CLI
Lệnh clarkcant
Hỏi Clark, đọc và tạo cuộc trò chuyện, dừng việc đang chạy hoặc gọi bất kỳ route nào, từ terminal hay từ script.
Chạy từ bản checkout
CLI là workspace package @clarkcant/cli trong apps/cli. Nó chưa được phát hành lên npm, nên hãy chạy từ một bản checkout của ClarkCant:
# The CLI is the workspace package @clarkcant/cli (apps/cli). It is not on npm yet.
git clone https://github.com/digitopvn/clarkcant.git
cd clarkcant
corepack enable && pnpm install
node apps/cli/src/main.ts ask "hello"
pnpm clarkcant ask "hello"
# Optional: a shell alias so the examples below work as written
alias clarkcant="node $PWD/apps/cli/src/main.ts"
Phát triển widget
Lệnh tác giả package clark hỗ trợ toàn bộ vòng đời widget từ một bản checkout. Không cần tài khoản runtime hay provider:
node packages/widget-cli/src/cli.ts widget init ./my-widget --template form
node packages/widget-cli/src/cli.ts widget dev ./my-widget
node packages/widget-cli/src/cli.ts widget test ./my-widget
node packages/widget-cli/src/cli.ts widget pack ./my-widget
Đây là clark widget init/dev/test/pack khi workspace bin có trong PATH. Dev chạy widget trong browser host cô lập, chỉ lắng nghe loopback. Bảng dịch vụ mô phỏng từng capability đã khai báo ở trạng thái loading, ready, blocked hoặc unhealthy; bạn có thể thử offline, degraded và phục hồi sau khi khởi động lại bằng fixture của package. Công cụ không chạy dịch vụ thật hay gọi provider.
Binding tới một capability chạy dưới dạng job của package dùng fixture job trong fixtures/dev-host-services.json thay cho outcome: các bước tiến độ, output khi hoàn tất và lỗi khi thất bại. Khi đó một lần bấm khởi động một job mô phỏng, và danh sách Simulated jobs cho phép đi từng bước bằng Next step, Complete hoặc Fail; lệnh huỷ của chính widget cũng hoạt động. Mọi kết thúc đều ghi “(simulated by clark widget dev)”, không có gì được ghi xuống đĩa, và clark widget test kiểm tra rằng chỉ đúng các capability job mới có fixture job.
Bảng ngữ nghĩa hiển thị đề xuất đã chuẩn hoá mà runtime lưu, phần bị cắt hoặc loại bỏ, delta, ghi chú ngữ cảnh và kết quả inspect_ui. Bảng composition gửi các event đã khai báo qua cùng bộ kiểm tra hợp đồng và ghi lại event do widget phát ra. Event sai hoặc chưa khai báo sẽ bị từ chối; mô phỏng chỉ chạy cục bộ và không thể gọi capability. clark widget test kiểm tra giới hạn semantic của fixture và schema event đã khai báo trước khi pack tạo artifact cùng digest.
Hiện tại --template nhận blank, form, dashboard, pure-ui, ai-generator, ui-with-service, media-tool và connected-app. pure-ui sao chép trình soạn thảo văn bản mẫu, mang id, facet id và tên riêng của package mới, không kèm các kiểm thử của trình soạn thảo, nên bạn bắt đầu từ một ứng dụng chạy được và qua được clark widget test. ai-generator và ui-with-service sao chép trình tạo ảnh mẫu theo cách tương tự: ai-generator giữ provider, với origin giữ chỗ https://images.example.com cần thay, còn ui-with-service có một service tự vẽ ảnh, không khai báo provider và chỉ đọc. media-tool sao chép công cụ dựng media mẫu, gồm một widget và một service, mang id riêng của package mới, không kèm các kiểm thử của công cụ. connected-app sao chép ứng dụng kết nối tài khoản mẫu, mang các id và tên riêng của package mới: một widget, một service có các capability nêu scope chúng cần trên một kết nối tài khoản đã khai báo, các skill, fake connector dùng để kiểm thử nó, và tệp kiểm thử di động dev/service.test.mjs của service. Hãy thay provider, client id, scope và endpoint bằng của provider của bạn trước khi phát hành. Template editor, media và MCP App adapter chưa được triển khai.
node packages/widget-cli/src/cli.ts widget init ./my-editor --template pure-ui
Đóng gói và phát hành widget lên npm
Khi package có package.json, clark widget pack build archive npm của nó bằng pnpm pack vào dist/<name>-<version>.tgz. Sau đó lệnh giải nén archive bằng đúng bộ đọc mà node của bạn dùng khi cài đặt, và từ chối archive nếu nó không tự qua được bộ kiểm tra tuân thủ, không giữ cùng clarkcant.json, hoặc chứa thứ có dạng thông tin xác thực như .npmrc, tệp .env, .git-credentials, khóa riêng, node_modules hay .git. Một danh sách files bỏ sót thứ widget cần sẽ bị phát hiện ở đây, chứ không phải trên máy người khác. clark widget init ghi package.json cho mọi template. Package không có tệp này vẫn là package local hoặc git, và pack không build archive.
Pack từ chối package.json vi phạm các quy tắc sau, và nêu tên từng quy tắc:
namelà tên npm hợp lệ, vàversiontrùng version trongclarkcant.json.licensekhớppublisher.licensecủa manifest.keywordsgồmclarkcantvà một keyword cho mỗi loại facet, nhưclarkcant-widgethayclarkcant-service. Marketplace tìm package qua các keyword này.- Có danh sách
filestường minh, không có dependency, và không có scriptpreinstall,installhaypostinstall. Package mang theo đúng thứ nó chạy. - Không có script
prepack,preparehaypostpack. Pack không chạy code của package, nên hãy đưa sẵn các tệp mà script lẽ ra sinh ra.
package.json được tạo mang tên theo phần cuối của package id, và tên đó có thể đã có người dùng trên npm. Hãy đổi tên, hoặc dùng scope của bạn như @you/my-widget, trước khi phát hành. Pack cần pnpm (corepack enable pnpm). Thêm package.json vào một package đã pack sẽ đổi author digest của nó, nên hãy tăng version khi thêm.
dist/artifact.json ghi ba digest, mỗi digest trả lời một câu hỏi:
npm.integrity: đây có phải các byte registry phục vụ không? Đó là sha512 của archive theo npm.npm.contentDigest: thứ node của bạn giải nén có đúng package mà listing nêu không? Node tính nó sau khi tải đúng version npm đó, và từ chối cài đặt khi không khớp.authorDigest: các tệp của version này có đổi từ lúc pack không? Pack từ chối pack lại một version đã đổi tệp; hãy tăng version.
clark widget publish chuẩn bị directory entry và không upload gì. Entry nêu đúng version npm và content digest của archive. Lệnh in riêng ba kết quả (prepared: yes; published to npm: no; Marketplace submission: no) và lệnh phát hành đúng archive đã được kiểm tra:
node packages/widget-cli/src/cli.ts widget pack ./my-widget
node packages/widget-cli/src/cli.ts widget publish ./my-widget
npm publish ./my-widget/dist/my-widget-0.1.0.tgz
Hãy phát hành tệp đó thay vì chạy npm publish trong thư mục package, để registry phục vụ đúng các byte mà entry nêu. Sau đó Marketplace liệt kê các package npm mang keyword clarkcant. --source local chuẩn bị entry cho chính thư mục của package, không cần tài khoản npm.
Ứng dụng mẫu: trình soạn thảo văn bản
examples/reference-apps/text-editor là một widget hoàn chỉnh chỉ dựng trên các hợp đồng widget công khai: một facet UI cách ly, không có service, không xin quyền nào và không yêu cầu capability nào. Bạn mở một tệp văn bản, sửa, lưu và nhờ Clark viết lại một đoạn đang chọn, còn widget không bao giờ biết tệp nằm ở đâu.
- Mở tệp. Hộp chọn tệp của chính ứng dụng, nằm ngoài widget, nhận văn bản thuần, Markdown, CSV và JSON. Trình soạn thảo từ chối tệp lớn hơn 1 MiB trước khi đọc, đọc phần còn lại theo từng đoạn 256 KiB, và từ chối byte không phải UTF-8 hợp lệ kèm lý do, thay vì hiển thị ký tự thay thế mà một lần lưu sẽ ghi ngược lại vào tệp.
- Sửa. Bản nháp chưa lưu được giữ trong state của widget, nên tải lại trang, một bản đã ghim hay một thiết bị khác đều thấy cùng bản nháp. Bản nháp quá lớn so với state không bị cắt: trình soạn thảo nói rõ nó sẽ không còn sau khi tải lại. Khi hai chỗ xem cùng một trình soạn thảo đều đã đổi bản nháp, nó đưa ra Giữ bản của tôi và Dùng bản kia, và không vứt bản nào.
- Lưu. Trình soạn thảo ghi một bản sao hoàn chỉnh và giao cho ứng dụng (tệp mà widget giữ). Trong ứng dụng máy tính, bạn có thể ghi đè tệp gốc, nhưng chỉ trong widget nơi bạn đã chọn tệp và chỉ cho đến khi nó được tải lại; sau đó, hoặc trong một bản đã ghim hay một cửa sổ tách riêng, ứng dụng máy tính đưa ra Lưu thành. Trên trình duyệt, bản sao được tải xuống. Ctrl+S (Cmd+S trên macOS) để lưu, và Đính kèm đưa một bản sao vào ô soạn tin.
- Những gì Clark thấy. Một câu tóm tắt ngắn, tên tệp, số dòng, có thay đổi chưa lưu hay không, và khoảng đang chọn cùng một đoạn trích dài tối đa 200 đơn vị UTF-16. Máy của bạn giới hạn, che thông tin nhạy cảm và đánh dấu tất cả là lời của chính widget.
- Viết lại đoạn chọn. Nút riêng của trình soạn thảo bấm một binding
agentđọc đoạn đang chọn và widget (contextRefs: ["selection", "widget"]), được nêu tên qua proprewriteBinding. Nó chỉ hỏi về một dòng dài tối đa 200 đơn vị UTF-16 mà ứng dụng sẽ chuyển cho Clark nguyên vẹn, nên đoạn chọn có ký tự ẩn, khoảng trắng đặc biệt hoặc nội dung ứng dụng che vì có thể là thông tin riêng sẽ bị từ chối kèm lý do. Trước khi lần bấm chạy, ứng dụng bảo đảm Clark đọc đúng đoạn chọn mà trình soạn thảo vừa công bố (lần bấm đọc điều widget đang hiển thị). Câu trả lời của Clark chỉ được coi là đề xuất khi nó là đúng một khối có rào đã đóng. Trình soạn thảo hiển thị đề xuất cạnh đoạn văn bản nó sẽ thay, và chỉ thay đổi văn bản khi bạn chấp nhận và khoảng đã chọn vẫn còn đúng đoạn Clark đã đọc; Ctrl+Z hoàn tác được.
Trong một bản cài thật, Clark gắn nút viết lại khi đặt trình soạn thảo kèm nút đó, qua tool place_widget của nó; không có binding thì nút vẫn bị tắt và hiện lý do. Trình soạn thảo còn cung cấp cho Clark hành động replaceSelection (hành động Clark nhờ một widget thực hiện), nên một yêu cầu gõ trong ô soạn tin, chẳng hạn “rút ngắn dòng thứ hai”, có thể thay đoạn đang chọn qua chính sách thực thi của bạn. Trình soạn thảo từ chối khi đoạn chọn không còn đúng đoạn Clark đã đọc. Thay đổi là một chỉnh sửa chưa lưu, và việc lưu vẫn do bạn làm. Nói nhãn của hành động khi trình soạn thảo đang được chọn sẽ chạy hành động đó qua cùng đường và cùng chính sách thực thi như khi nhờ Clark trong ô soạn tin, trên một trang tới được frame của trình soạn thảo (digitopvn/clarkcant#444). Khi chính sách của bạn yêu cầu hỏi trước, thẻ duyệt hiện trong cuộc trò chuyện và bạn có thể trả lời bằng cách bấm hoặc nói. Câu nói chỉ quyết định khi mọi từ đều là từ đồng ý, như “yes”, “ok” hay “đồng ý”, hoặc mọi từ đều là từ từ chối, như “no”, “cancel” hay “không”, ngoài các từ đệm như “please” hay “nhé”. Một câu hỏi, một câu lẫn lộn hay bất cứ câu nào khác, như “not ok” hay “chưa được”, sẽ được hỏi lại. Sau đó giọng nói cho biết việc đã diễn ra thế nào, và câu trả lời của widget được đọc như lời của chính widget. Một câu ngụ ý hành động mà không nói nhãn của nó, như “rút ngắn đoạn này”, thì giọng nói chưa khớp được; hãy nhờ Clark thay vào đó.
Kiểm tra ứng dụng mẫu từ một bản checkout:
node packages/widget-cli/src/cli.ts widget test examples/reference-apps/text-editor
node packages/widget-cli/src/cli.ts widget pack examples/reference-apps/text-editor
Ứng dụng mẫu: bảng tính
examples/reference-apps/spreadsheet là widget hoàn chỉnh thứ hai chỉ dựng trên các hợp đồng widget công khai: một facet UI cách ly và không có service. Nó làm việc với một tệp, giữ một bảng lớn trong giới hạn, tự mô tả cho Clark và áp dụng một thay đổi do Clark chọn.
- Tệp. Nhập CSV/TSV mở hộp chọn tệp của chính ứng dụng (tệp mà widget giữ), và bảng tính đọc tệp theo từng đoạn 256 KiB mà không bao giờ biết tệp nằm ở đâu. Ứng dụng giờ nhận tệp phân tách bằng tab (
text/tab-separated-values,.tsvhoặc.tab) với cùng các bước kiểm tra như CSV; một tệp nhị phân được khai là TSV vẫn bị từ chối. Xuất CSV và Xuất TSV ghi một tệp mới có dấu BOM, giống chức năng xuất bảng của chính ứng dụng. XLSX không được hỗ trợ. - Giới hạn. Tải tối đa 25.000 ô, 64 cột và 5.000 hàng, và không tệp nào được đọc quá 8 MiB. Giới hạn ô tính theo hình chữ nhật mà các hàng tạo thành, nên một tệp có hàng dài ngắn không đều bị cắt ở chỗ hình chữ nhật đó hết vừa. Một thông báo cho biết đã hiện bao nhiêu và khi xuất chỉ ghi phần đó. Hàng và cột chỉ được vẽ khi đang nằm trong vùng nhìn thấy, nên một bảng lớn vẫn phản hồi nhanh.
- Những gì được giữ. State của widget giữ tham chiếu tới tệp nguồn, các sửa đổi từ đó và các định dạng, không bao giờ giữ chính bảng; ô hiện tại và vùng chọn chỉ ở lại trong frame. Ngay sau khi nhập, bảng được ghi vào một tệp riêng của widget, vì quyền trên một tệp bạn đã chọn chỉ kéo dài 24 giờ. Khi các sửa đổi vượt 10 KiB trong 16 KiB mà state của một widget được giữ, toàn bộ bảng được ghi vào một tệp riêng mới theo cách đó, và widget yêu cầu ứng dụng huỷ tệp mà nó thay thế. Nếu lần ghi như vậy thất bại, dòng trạng thái nói rõ và lần sửa tiếp theo sẽ thử lại.
- Mở bảng. Khi được gắn vào, bảng được đọc lại từ nguồn. Cho tới lúc đó, lưới không nhận sửa đổi và các nút phải chờ. Nếu không đọc được nguồn, dòng trạng thái nói rõ, lưới không nhận sửa đổi và không có gì được lưu, nên bảng đã lưu vẫn còn ở lần sau. Nhập một tệp sẽ bắt đầu lại từ tệp ấy.
- Công thức. Một tập đóng: số học, tham chiếu ô và vùng, và
SUM,AVERAGE,MIN,MAXvàCOUNT. Một bộ phân tích dựng cây và widget duyệt cây đó, nên không văn bản nào được chạy như mã. Lỗi là giá trị (#DIV/0!,#VALUE!,#REF!,#NAME?,#PARSE!,#NUM!,#LIMIT!), và tham chiếu vòng là#CIRC!, với các ô trên vòng được nêu tên trong thông báo.#LIMIT!chỉ đánh dấu công thức đọc quá xa và các công thức đọc nó; một tổng luỹ kế như=SUM($A$1:A3000)kéo xuống 3.000 hàng vẫn vừa. - Xuất an toàn. Tệp xuất mang giá trị đã tính, không bao giờ mang công thức. Văn bản bắt đầu bằng
=,+,-,@, tab hoặc ký tự CR được ghi kèm dấu'ở đầu, giống chức năng xuất bảng của ứng dụng, và văn bản mà bảng tính sẽ đọc lại thành thứ khác, như007hoặc1e3, cũng vậy. Bảng tính đọc dấu'ở đầu là văn bản, nên một tệp đã xuất nhập lại vẫn ra cùng giá trị. Một ứng dụng bảng tính khác hiện dấu'đó như một phần của văn bản. - Những gì Clark thấy. Vùng đang chọn dạng A1, một đoạn trích tối đa 12 hàng × 8 cột, công thức và giá trị của ô hiện tại, và kích thước bảng, vừa trong giới hạn của ứng dụng nên không gì bị cắt.
- Định dạng qua Clark. Nhờ Clark định dạng phần trăm bấm một binding
agentđọc vùng chọn và widget (contextRefs: ["selection", "widget"]), được nêu tên qua propformatBinding; không có prop đó thì nút bị tắt. Lần bấm không gửi gì: ứng dụng đọc vùng từ điều widget đã công bố (lần bấm đọc điều widget đang hiển thị) và yêu cầu Clark trả lời đúng một dòng,format: percent <vùng>. Widget coi câu trả lời là không đáng tin. Nó chỉ nhậnformat: percent|number|plain <vùng>, và chỉ áp dụng khi vùng đúng là vùng đã chọn lúc bấm, vùng này bị khoá cho tới khi có câu trả lời; nếu không, nó nói rõ và không thay đổi gì. Bảng tính giữ tối đa 32 định dạng và nêu tên định dạng phải bỏ. Hoàn tác định dạng, hoặc Ctrl+Z trong lưới, rút lại thay đổi của Clark. - Bàn phím, chuột và cảm ứng. Lưới là một điểm dừng Tab duy nhất, và Tab cùng Shift+Tab rời lưới, nên vẫn tới được các nút. Phím mũi tên để di chuyển, Shift mở rộng vùng chọn, Home/End và Ctrl+Home/End để nhảy, Page Up/Down để lật trang, Enter hoặc F2 để sửa, gõ phím để bắt đầu sửa, Escape huỷ lần sửa hoặc thu vùng chọn lại, và Delete xoá vùng chọn. Chuột chọn bằng cách bấm, Shift+bấm hoặc kéo. Trên màn hình cảm ứng, chạm để chọn một ô, vuốt để cuộn, và Chọn vùng làm các lần chạm sau mở rộng vùng chọn. Các nút cao ít nhất 40 px.
Trong một bản cài thật, tool place_widget của Clark đặt bảng tính và gắn cả hành động format mà bảng tính cung cấp lẫn, khi được yêu cầu, nút định dạng. Bảng tính cung cấp cho Clark hành động format ({ format: percent | number | plain, range? }, xem hành động Clark nhờ một widget thực hiện), nên một yêu cầu gõ trong ô soạn tin, chẳng hạn “định dạng chỗ này thành phần trăm”, sẽ định dạng vùng được nêu, hoặc vùng đang chọn khi không nêu vùng nào, qua chính sách thực thi của bạn. Bảng tính từ chối khi đang bận hoặc chỉ đọc, khi định dạng không xác định, và khi vùng không đọc được hoặc vượt quá giới hạn của bảng. “Hoàn tác định dạng” lùi lại định dạng của Clark như mọi định dạng khác.
Kiểm tra ứng dụng mẫu từ một bản checkout:
node packages/widget-cli/src/cli.ts widget test examples/reference-apps/spreadsheet
node packages/widget-cli/src/cli.ts widget pack examples/reference-apps/spreadsheet
Ứng dụng mẫu: trình tạo ảnh
examples/reference-apps/image-generator là một package có một facet UI cách ly và một facet service. Widget khởi động một việc kéo dài trên service của nó, theo dõi việc đó như một job và nhận lại một ảnh dưới dạng tệp, trong khi service tới provider bằng một khoá mà nó không bao giờ giữ.
- Capability. Service cung cấp
com.clarkcant.reference.image-generator.image.generate@1, chạy dưới dạng job và được khai báoexternal-write. Một lần bấm trả lời ngay bằng một JobRef; ảnh được tạo bởi một job mà node của bạn sở hữu, nên việc vẫn tiếp tục khi widget bị đóng, tải lại hoặc mở trên thiết bị khác. - Vì sao là
external-write. Nhờ provider vẽ là làm việc trên dịch vụ của người khác và tiêu hạn mức của bạn ở đó. Đó cũng là điều cho phép service bắt đầu ảnh bằng một POST có prompt nằm trong body JSON: với capabilityread, node của bạn chỉ gửi GET và HEAD, và một prompt nằm trong URL sẽ vào nhiều log hơn so với nằm trong body. Theo chính sách tự chủ mặc định, lần bấm chạy luôn; nếu chính sách của bạn hỏi trước các thao tác ghi ra bên ngoài, thẻ phê duyệt của ứng dụng hiện trước và widget cho biết lần bấm đang chờ. - Provider và khoá. Package khai báo một origin và một bí mật,
IMAGE_PROVIDER_KEY. Service bắt đầu một ảnh, đọc trạng thái mỗi bước một lần và tải PNG chỉ qua egress của node, nơi thêm khoá vào dưới dạng header bearer. Service, container của nó và widget không bao giờ nhận được khoá. Cho tới khi bạn lưu khoá cho package, nút bị tắt kèm lý do của node. Provider trong repository là một provider giả trong các kiểm thử của package: nó chỉ trả lời các yêu cầu có khoá, từ chối prompt nằm trong URL và trả về một ảnh tất định. - Tiến độ và Dừng. Mỗi bước provider hoàn tất được service báo lại, và widget hiển thị đúng điều đó; nó không ước lượng gì. Mỗi job đang chạy có khung riêng với tiến độ và nút Dừng riêng, nút này huỷ đúng job đó, và service thôi hỏi provider.
- Lỗi. Khi service báo lỗi, job thất bại giữ chính lời của service, được trích trong câu của node (job thất bại có thể mang chính lời của service). Widget hiển thị những lời đó trong ngoặc kép như lời của service, bên trong câu của chính nó. Khoá mà provider gửi lại hiện thành
[redacted]. - Bộ sưu tập. Khi ứng dụng cung cấp
jobs.list@1, widget liệt kê các job của chính nó, theo dõi các job còn mở và đọc ảnh đã xong dưới dạngartifactReftheo từng đoạn 256 KiB, nên khi tải lại hay trên thiết bị khác vẫn thấy cùng các job đó. Trên ứng dụng không có extension này, bộ sưu tập chỉ giữ các job được khởi động trong lúc widget đang mở và nói rõ điều đó. Mỗi ảnh có thể được đính kèm vào hội thoại hoặc xuất ra; widget chỉ biết ứng dụng có nhận hay không. - Widget, Clark và giọng nói. Tạo ảnh bấm một binding
invokeđược nêu tên qua propgenerateBinding, lấy prompt từ bản nháp trong state của widget, hoặc từ điều bạn nói khi có input. Ctrl/Cmd+Enter để tạo ảnh. Nói nhãn của nút khi widget đang mở sẽ bấm nút đó, và câu trả lời cho biết job đã bắt đầu. Công cụinvoke_capabilitycủa Clark khởi động job qua widget trong hội thoại có binding tới capability đó, nên chính widget ấy theo dõi job; nếu không có widget nào, không có gì chạy và Clark nói rõ, còn nếu có nhiều widget, Clark chọn một widget theo id instance và id binding. Clark bị từ chối với binding sẽ hỏi chính Clark, vì lần bấm đó sẽ được gửi như tin nhắn của chính bạn. - Ngôn ngữ. Giống trình soạn thảo văn bản, chữ của chính widget chỉ có tiếng Việt; ứng dụng dịch phần khung của chính nó, không dịch chữ của widget.
Clark đặt trình tạo ảnh kèm nút Tạo ảnh qua tool place_widget, tool này gắn nút với capability image.generate của chính package (digitopvn/clarkcant#445). Một nút chỉ được gắn với capability do chính package chứa widget cung cấp, và chỉ gửi những đầu vào mà capability đó khai báo. Cho tới khi khoá của provider được lưu, nút bị tắt kèm lý do của node. Chưa có provider ảnh thật nào được nối vào; đó là digitopvn/clarkcant#321. Thẻ của ảnh đã đính kèm ghi untitled.png, vì tệp kết quả của job không mang tên.
Kiểm tra ứng dụng mẫu từ một bản checkout, hoặc bắt đầu ứng dụng của riêng bạn từ nó:
node packages/widget-cli/src/cli.ts widget test examples/reference-apps/image-generator
node packages/widget-cli/src/cli.ts widget pack examples/reference-apps/image-generator
node packages/widget-cli/src/cli.ts widget init ./my-generator --template ai-generator
Ứng dụng mẫu: công cụ dựng media
examples/reference-apps/media-render là một package có một facet UI cách ly và một facet service. Nó dựng một đoạn WAV bạn chọn, với thay đổi độ lợi và cắt bớt, dưới dạng một job mà widget theo dõi và bạn có thể dừng. Service đọc đoạn âm thanh từ node của bạn theo từng phần và không bao giờ nhận đường dẫn hay handle tới nó (tệp mà một service đọc).
- Capability. Service cung cấp
com.clarkcant.reference.media-render.render@1, chạy dưới dạng job và được khai báoread. Nó nêu đối sốsourcelà một tệp tronginputArtifacts, và nhận độ lợi từ −24 tới +12 dB cùng phần cắt đầu và cắt cuối tuỳ chọn tính bằng mili giây. Service không khai báo egress, và một lệnh gọireadđang giữ tệp cũng không thể dùng egress nào. - Resource profile. Package xin
background-computetheo tên (tài nguyên mà một gói được chạy cùng). Node của bạn từ chối đoạn âm thanh vượt giới hạn đầu vào 25 MiB của profile đó trước khi lệnh gọi được gửi đi. Service từ chối đoạn dài hơn một giờ của profile, hoặc bản dựng lớn hơn mức một kết quả được phép mang, trước khi dựng bất cứ thứ gì. - Chọn và dựng. Chọn tệp WAV mở hộp chọn tệp của chính ứng dụng, và widget chỉ giữ
artifactRefcủa tệp (tệp mà widget giữ). Dựng bấm một bindinginvokeđược nêu tên qua proprenderBinding, kèm id của đoạn âm thanh và các thông số, và widget giữ JobRef trong state của nó, nên một frame tải lại vẫn theo đúng bản dựng đó. - Tiến độ và Dừng. Tiến độ là của chính service, tính bằng số byte đã dựng; widget không ước lượng gì. Dừng dựng, hoặc Escape, huỷ job: service thôi đọc và không trả lời, nên không giữ lại tệp dở dang nào. Lần dừng mà node của bạn từ chối sẽ nói lý do và đưa lại Dừng và Escape. Một lần bấm mới chỉ thay bản dựng đang hiện khi node của bạn chấp nhận nó, nên lần bấm bị từ chối vẫn giữ kết quả trước đó.
- Kết quả. Chỉ job đã hoàn tất mới đưa ra tệp của nó. Widget đọc lại tệp và vẽ dạng sóng, kèm thời lượng, định dạng và digest sha256 của node. Không có trình phát âm thanh, vì chính sách của frame widget không cho phép nguồn media nào. Đính kèm vào cuộc trò chuyện đưa tệp vào ô soạn tin, còn Lưu bản dựng đi qua hộp lưu tệp của ứng dụng. Bản dựng đã hoàn tất mà node của bạn không giữ được tệp sẽ nói điều gì thất bại và rằng tệp nguồn vẫn nguyên vẹn.
- Profile không khả dụng. Khi node của bạn không cấp được
background-compute, vì một quy tắc chính sách từ chối hoặc container engine quá nhỏ, service không được khởi động. Nút Dựng bị tắt và lý do của node hiện ở vị trí của nó. - Cảm ứng. Ô độ lợi yêu cầu bàn phím văn bản, vì bàn phím số thập phân trên iOS không có phím dấu trừ; ô này nhận dấu trừ kiểu chữ in và dấu phẩy thập phân. Mọi nút và ô nhập đều cao ít nhất 44 px.
- Ngôn ngữ. Giống các ứng dụng mẫu khác, chữ của chính widget chỉ có tiếng Việt.
Chỉ WAV PCM 16-bit, mono hoặc stereo, được dựng; không có codec nào khác. Clark đặt widget kèm nút Dựng qua place_widget, gắn với capability dựng của chính package (digitopvn/clarkcant#445). Các kiểm thử trình duyệt của nó cần một container engine chạy được container Linux.
Kiểm tra ứng dụng mẫu từ một bản checkout, hoặc bắt đầu ứng dụng của riêng bạn từ nó:
node packages/widget-cli/src/cli.ts widget test examples/reference-apps/media-render
node packages/widget-cli/src/cli.ts widget pack examples/reference-apps/media-render
node packages/widget-cli/src/cli.ts widget init ./my-render --template media-tool
Ứng dụng mẫu: ứng dụng kết nối tài khoản
examples/reference-apps/connected-app là một package làm việc trên tài khoản của bạn ở một provider mà không bao giờ giữ tài khoản đó: một widget liệt kê công việc và đổi tên một công việc, một service gọi provider, và các skill hướng dẫn Clark cách dùng. Node của bạn kết nối tài khoản và ký các yêu cầu của service; widget chỉ thấy trạng thái (kết nối một tài khoản).
- Một kết nối được khai báo. Facet tools khai báo
connection: provider, client id công khai, các endpoint ủy quyền, token và thu hồi, các scope kèm mục đích của từng scope, các endpoint mà service được phép gọi bằng tài khoản, và một probe. - Capability.
com.clarkcant.reference.connected-app.list-tasks@1làreadvà cầntasks.read.com.clarkcant.reference.connected-app.update-task@1đổi tên một công việc, làexternal-writevà cầntasks.write, nên chính sách thực thi của bạn quyết định nó. - Widget. Tải công việc liệt kê công việc và Lưu lưu tên mới, qua các binding được nêu tên bằng prop
listBindingvàupdateBinding. Khi một capability chưa sẵn sàng, widget hiện lý do của node, ví dụ kết nối đã bị thu hồi hoặc không cấptasks.write. Giống các ứng dụng mẫu khác, chữ của chính widget chỉ có tiếng Việt. - Một capability cho widget, Clark và giọng nói. Các nút của widget,
invoke_capabilitycủa Clark và một câu lệnh nói đều tới cùng các capability, qua cùng chính sách và cùng nhật ký kiểm toán. Skill dặn Clark không bao giờ hỏi bạn mật khẩu, token hay mã, nói lý do của node khi một capability chưa sẵn sàng, và không gửi lại một lần đổi tên chưa rõ kết quả. - Fake connector.
dev/fake-connector.mjslà một fixture kiểm thử và phát triển, không phải provider thật: một máy chủ OAuth và API công việc nhỏ trên cổng loopback 8880, cùng một listener quản trị trên cổng 8881 chỉ dành cho kiểm thử. Nó không có tài khoản thật và không có client secret, và mọi mã và token nó phát ra đều là giá trị kiểm thử ngẫu nhiên chỉ nằm trong bộ nhớ của nó. Node của bạn chỉ gọi tới các endpoint loopback của nó khi chạy vớiCC_EGRESS_ALLOW_PRIVATE_NETWORK=1.
Chưa có provider thật nào; việc này được theo dõi ở digitopvn/clarkcant#333. Ứng dụng desktop chỉ mở địa chỉ HTTPS trong trình duyệt hệ thống, nên bạn kết nối fake connector chạy trên loopback từ trình duyệt; provider thật dùng HTTPS. Clark đặt widget kèm nút liệt kê và nút đổi tên qua place_widget, gắn với các capability của chính package (digitopvn/clarkcant#445).
Kiểm tra ứng dụng mẫu từ một bản checkout, thử nó với fake connector, hoặc bắt đầu ứng dụng của riêng bạn từ nó:
node packages/widget-cli/src/cli.ts widget test examples/reference-apps/connected-app
node packages/widget-cli/src/cli.ts widget pack examples/reference-apps/connected-app
node --test examples/reference-apps/connected-app/dev/service.test.mjs
node examples/reference-apps/connected-app/dev/fake-connector.mjs
node packages/widget-cli/src/cli.ts widget init ./my-tasks --template connected-app
Tạo một chủ đề
Lệnh tác giả package clark cũng hỗ trợ facet chủ đề chỉ chứa dữ liệu. Chạy từ checkout; không cần tài khoản runtime hay provider:
node packages/widget-cli/src/cli.ts theme init ./my-theme
node packages/widget-cli/src/cli.ts theme dev ./my-theme
node packages/widget-cli/src/cli.ts theme test ./my-theme
node packages/widget-cli/src/cli.ts theme pack ./my-theme
Đây là clark theme init/dev/test/pack khi workspace bin có trong PATH. Init tạo manifest package tổng quát và chủ đề JSON hợp lệ. Dev lắng nghe tại 127.0.0.1:4319 (đổi bằng --port) và nạp lại dữ liệu khi sửa, giữ draft xem trước. Dừng bằng Ctrl-C. Theme Lab dùng component sản phẩm cho ví dụ hội thoại, composer, widget, nút/ô nhập, Cài đặt, modal, phê duyệt, lỗi, trạng thái và Orb; thao tác ví dụ không vận hành runtime. Chuyển sáng/tối, kích thước thường/điện thoại/gọn và giảm chuyển động; xem token và recipe đã biên dịch.
Test kiểm tra tài liệu chủ đề bất kỳ ở cả hai chế độ: tương phản chữ/focus, trạng thái được bảo vệ và đường viền, typography có giới hạn, giảm chuyển động, asset là tệp thường trong gói, manifest và không thực thi mã. Chủ đề không nhận CSS, HTML, script, tài nguyên bên ngoài hay URL font. Symlink trong gói bị từ chối. Kiểm tra bố cục browser và bàn phím vẫn ghi rõ requires-dev-host; kiểm tra token không chứng nhận hành trình browser. Pack dùng định dạng artifact bất biến hiện có, chứa digest tài liệu chủ đề, ghi các kiểm tra chưa thực hiện và từ chối thay đổi nội dung cùng phiên bản. Tăng phiên bản trước khi pack bản sửa.
Kết nối
| Cờ | Biến môi trường | Mặc định |
|---|---|---|
--url | CLARKCANT_URL | http://127.0.0.1:8765 |
--token | CLARKCANT_TOKEN | Đọc từ identity.json trong thư mục dữ liệu, chỉ khi node ở trên máy này (localhost, 127.x, ::1); --url trỏ tới máy khác cần --token, nên token cục bộ không bao giờ rời khỏi máy |
--data-dir | CLARKCANT_DATA_DIR | ~/.clarkcant |
--json | – | In JSON thô |
Trên chính máy chạy node, với thư mục dữ liệu mặc định, không cần cờ nào. Với node ở nơi khác, đặt URL và token:
export CLARKCANT_URL="https://clark.example.com"
export CLARKCANT_TOKEN="<token>"
clarkcant status
Các lệnh
| Lệnh | Chức năng |
|---|---|
clarkcant ask "<text>" [-c <conversationId>] | Stream câu trả lời của Clark ra stdout. Không có -c thì tạo cuộc trò chuyện mới và in id của nó ra stderr. Khi tin nhắn được nhập (steer) vào câu trả lời Clark đang viết (resolution: "steered"), lệnh báo điều đó ra stderr và thoát với mã 0; hãy đọc cuộc trò chuyện để xem câu trả lời. |
clarkcant status | Tình trạng node. |
clarkcant conversations | Liệt kê cuộc trò chuyện. |
clarkcant new [title] | Tạo cuộc trò chuyện. |
clarkcant read <conversationId> | In cuộc trò chuyện. |
clarkcant stop | Dừng khẩn cấp. |
clarkcant api <METHOD> <path> [jsonBody] | Gọi thô tới bất kỳ route REST nào, trừ các quyết định của con người (phê duyệt hành động cần kiểm soát, quyết định năng lực của package, xác nhận ý định với ứng dụng, báo cáo ứng dụng đã làm gì với một hành động agent yêu cầu, tin cậy một peer đã ghép cặp hoặc cấp quyền, cài bản cập nhật mà một thông báo nêu) hoặc việc xuất CSV của một bảng, trả về 403 PERSON_ONLY. |
clarkcant mcp | MCP server qua stdio, nối tới /mcp của node (xem MCP). |
clarkcant discover | In /.well-known/clarkcant.json. |
clarkcant instructions check [file|folder] | Kiểm tra một tệp hướng dẫn dự án theo contract dùng chung. Đây là lệnh duy nhất chạy ngoại tuyến, không cần node. |
Ví dụ
clarkcant status
clarkcant discover
clarkcant ask "how do I connect Cursor to you?" # new conversation; its id is printed on stderr
clarkcant ask "and Claude Desktop?" -c <conversationId>
clarkcant conversations
clarkcant new "Release notes"
clarkcant read <conversationId>
clarkcant api GET /node
clarkcant api POST /conversations '{ "title": "From the CLI" }'
clarkcant stop
Vì câu trả lời ra stdout còn id cuộc trò chuyện mới ra stderr, ask kết hợp được với pipe và file như mọi lệnh khác.