diffcat-cli 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/README.md +135 -0
  3. package/connectors/workbuddy/cli.json +11 -0
  4. package/connectors/workbuddy/connector-meta.json +22 -0
  5. package/connectors/workbuddy/icon.svg +11 -0
  6. package/connectors/workbuddy/skills/diffcat/SKILL.md +94 -0
  7. package/connectors/workbuddy/skills/diffcat/references/operations.md +64 -0
  8. package/connectors/workbuddy/skills/diffcat/references/safety.md +37 -0
  9. package/dist/client.d.ts +38 -0
  10. package/dist/client.js +199 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/config-store.d.ts +8 -0
  13. package/dist/config-store.js +100 -0
  14. package/dist/config-store.js.map +1 -0
  15. package/dist/credential-store.d.ts +4 -0
  16. package/dist/credential-store.js +37 -0
  17. package/dist/credential-store.js.map +1 -0
  18. package/dist/deployment.d.ts +5 -0
  19. package/dist/deployment.js +6 -0
  20. package/dist/deployment.js.map +1 -0
  21. package/dist/device-login.d.ts +6 -0
  22. package/dist/device-login.js +93 -0
  23. package/dist/device-login.js.map +1 -0
  24. package/dist/dpop.d.ts +12 -0
  25. package/dist/dpop.js +25 -0
  26. package/dist/dpop.js.map +1 -0
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.js +296 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/json-input.d.ts +1 -0
  31. package/dist/json-input.js +28 -0
  32. package/dist/json-input.js.map +1 -0
  33. package/dist/output.d.ts +2 -0
  34. package/dist/output.js +7 -0
  35. package/dist/output.js.map +1 -0
  36. package/dist/types.d.ts +37 -0
  37. package/dist/types.js +2 -0
  38. package/dist/types.js.map +1 -0
  39. package/package.json +42 -0
  40. package/plugins/diffcat/.codex-plugin/plugin.json +24 -0
  41. package/plugins/diffcat/README.md +45 -0
  42. package/plugins/diffcat/skills/diffcat/SKILL.md +41 -0
  43. package/plugins/diffcat/skills/diffcat/agents/openai.yaml +6 -0
  44. package/plugins/diffcat/skills/diffcat/references/authentication.md +75 -0
  45. package/plugins/diffcat/skills/diffcat/references/capabilities.md +75 -0
  46. package/plugins/diffcat/skills/diffcat/references/drafts-and-approvals.md +58 -0
  47. package/plugins/diffcat/skills/diffcat/references/querying.md +74 -0
  48. package/plugins/diffcat/skills/diffcat/references/recovery.md +44 -0
  49. package/scripts/codex-plugin.mjs +116 -0
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "diffcat-cli",
3
+ "interface": {
4
+ "displayName": "DiffCat CLI"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "diffcat",
9
+ "source": {
10
+ "source": "local",
11
+ "path": "./plugins/diffcat"
12
+ },
13
+ "policy": {
14
+ "installation": "AVAILABLE",
15
+ "authentication": "ON_INSTALL"
16
+ },
17
+ "category": "Productivity"
18
+ }
19
+ ]
20
+ }
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # diffcat-cli
2
+
3
+ 供 AI 智能体和本地自动化使用的 Diffcat CLI。CLI 只能调用服务端登记的能力,不能传入任意 URL 或 SQL。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install -g diffcat-cli
9
+ ```
10
+
11
+ 要求 Node.js 22 或更高版本,并需要可用的系统凭据库(Windows Credential Manager、macOS Keychain 或 Linux Secret Service)。首期不支持把长期授权以明文文件形式放入容器或 CI。
12
+
13
+ npm 包同时包含 Agent 通用操作指南、DiffCat Codex Plugin 和 WorkBuddy CLI Connector。安装脚本会在检测到 Codex 时自动登记插件;如果安装时未检测到 Codex、npm 使用了 `--ignore-scripts`,或需要修复安装,可执行:
14
+
15
+ ```bash
16
+ diffcat setup codex
17
+ ```
18
+
19
+ 安装或升级后应新建 Agent 会话,以加载当前 npm 包内的 DiffCat Skill。CLI、各 Agent 适配清单和 Skill 使用同一个 npm 版本发布,不需要从其他仓库重复安装。
20
+
21
+ 当前包按公开 npm 包配置,以便 WorkBuddy 托管运行时无需私有 Registry 凭据即可一键安装。正式发布由流水线执行 `pnpm build && pnpm test && pnpm publish`,不要把 Registry Token 打进安装包。npm tarball 必须同时包含 `plugins/diffcat`、`.agents/plugins/marketplace.json`、`connectors/workbuddy` 和安装脚本。
22
+
23
+ ### Agent 集成
24
+
25
+ - **通用 Agent**:只要能够执行本机命令并读取 `SKILL.md`,即可使用 `diffcat` 的 JSON 命令、动态能力目录和指南,不依赖 Codex API。
26
+ - **Codex**:npm `postinstall` 自动登记随包 Plugin;也可运行 `diffcat setup codex` 修复。
27
+ - **WorkBuddy**:`connectors/workbuddy` 是符合[官方 CLI + Skill 规范](https://open.workbuddy.cn/docs/connector)的连接器提交目录。连接器市场审核发布后,用户点击一次即可由 WorkBuddy 托管 Node 22 并安装本 npm 包。正式环境默认连接生产 API;只有用户明确选择测试或私有部署时,Skill 才通过 `--server` 覆盖地址并使用独立 profile。
28
+
29
+ WorkBuddy 连接器的版本必须与 npm 包版本同步。提交 WorkBuddy 审核前,应先发布对应 npm 版本,再将 `connectors/workbuddy` 目录打包提交。
30
+
31
+ ## 最小权限登录
32
+
33
+ 正式环境地址如下:
34
+
35
+ | 用途 | 地址 |
36
+ | ----------------------------- | ---------------------------------- |
37
+ | CLI / 服务端 API | `https://diffcat-api.howcat.cn` |
38
+ | 后台 PC 端与 CLI 浏览器授权页 | `https://diffcat.howcat.cn` |
39
+ | 移动端 | `https://diffcat-mobile.howcat.cn` |
40
+
41
+ CLI 默认使用正式 API,连接其他部署时可显式传入 `--server <url>`。
42
+
43
+ ```bash
44
+ diffcat auth login \
45
+ --capability pm.projects.read \
46
+ --capability sales.orders.read
47
+ ```
48
+
49
+ CLI 会输出浏览器授权地址。只有用户在网页中确认的能力才会被授予;用户当前业务权限或管理员策略不允许的能力会从授权中剔除。
50
+
51
+ ## 常用命令
52
+
53
+ ```bash
54
+ diffcat --pretty auth status
55
+ diffcat --pretty capability list
56
+ diffcat --pretty capability catalog
57
+ diffcat auth login --preset master-data-read
58
+ diffcat resource list md.customers --query '{"pageSize":20}'
59
+ diffcat resource get md.materials 123 --include bomLines
60
+ diffcat document list inv.purchase_requests --query '{"status":"DRAFT"}'
61
+ diffcat document create-draft inv.purchase_requests --input @draft.json
62
+ diffcat project list --query '{"pageSize":20}'
63
+ diffcat sales-order get 123
64
+ diffcat sales-order create-draft --input @draft.json
65
+ diffcat stats query --input @widgets.json
66
+ diffcat task list --query '{"status":"PENDING"}'
67
+ diffcat task approve 456 --comment "同意"
68
+ diffcat operation resume <operation-id>
69
+ ```
70
+
71
+ 标准输出始终是 JSON,登录提示和进度写入标准错误。写操作自动携带幂等键;也可以通过 `--idempotency-key` 指定。高风险审批第一次调用只创建五分钟有效的确认意图,用户在网页二次认证后再运行 `operation resume`。
72
+
73
+ ## 凭据与协议
74
+
75
+ - 登录采用 OAuth 2.0 Device Authorization Grant 风格的设备授权流程;CLI 不能读取浏览器登录态。
76
+ - 每台设备生成独立 ES256 密钥,设备授权、令牌签发和能力调用都携带 DPoP proof。
77
+ - 十分钟访问令牌、轮换刷新令牌和设备私钥只进入系统凭据库;普通配置文件不含任何令牌或私钥。
78
+ - 刷新令牌连续 30 天未使用失效,且最长 90 天必须重新授权;检测到旧刷新令牌复用时整条授权立即撤销。
79
+ - `auth logout` 同时请求服务端撤销授权并清除本机凭据;用户也可在网页“已连接 Agent”中远程撤销。
80
+ - 同一 profile 的刷新和凭据更新通过跨进程锁串行化,支持多个 AI 进程并发调用而不误触发刷新令牌复用告警。
81
+
82
+ ## 面向 AI 的稳定输出
83
+
84
+ 所有业务结果只写到 stdout,且始终是单个 JSON 文档;提示、浏览器授权地址和轮询进度写到 stderr。命令非零退出码表示请求未成功,AI 不应从自然语言日志推断成功。
85
+
86
+ 写操作应让 CLI 自动生成幂等键,或由调用者提供稳定的 `--idempotency-key`。网络中断后不要盲目重试高风险操作,应先运行:
87
+
88
+ ```bash
89
+ diffcat operation status <operation-id>
90
+ diffcat operation resume <operation-id>
91
+ ```
92
+
93
+ ## 资源名称
94
+
95
+ `resource` 支持以下基础资料资源:
96
+
97
+ ```text
98
+ sys.users sys.departments
99
+ sys.roles sys.dict_types
100
+ sys.dict_entries md.companies
101
+ md.currencies fin.exchange_rates
102
+ md.uoms md.encoding_rules
103
+ md.fee_subjects md.customers
104
+ md.customer_contacts md.customer_invoice_profiles
105
+ md.stores md.contracts
106
+ md.products md.price_lists
107
+ md.price_list_lines md.warehouses
108
+ md.warehouse_members md.suppliers
109
+ md.supplier_contacts md.materials
110
+ md.bom_lines adm.supplies
111
+ fin.bank_accounts sales.crowdfunding_platforms
112
+ ```
113
+
114
+ `document` 支持以下单据资源,每种都可 `list`、`get` 和 `create-draft`:
115
+
116
+ ```text
117
+ sales.orders sales.direct_orders
118
+ sales.consignment_reconciliations sales.crowdfunding_orders
119
+ inv.purchase_requests inv.inbound_orders
120
+ inv.outbound_orders inv.stocktake_orders
121
+ fin.expense_requests fin.invoice_requests
122
+ fin.refund_requests adm.supply_entry_orders
123
+ adm.supply_issue_requests adm.supply_purchase_requests
124
+ adm.supply_stocktake_orders
125
+ ```
126
+
127
+ `create-draft` 会复用对应业务模块的 Create DTO 校验并在创建后回查状态;只有状态确认为 `DRAFT` 才报告成功。它不会提交单据、发起流程、记账或变更库存。
128
+
129
+ ## 当前约束
130
+
131
+ - 不接受任意 HTTP URL、GraphQL、SQL 或脚本,只能调用服务端能力目录中的已登记能力。
132
+ - `resource` 与 `document` 的资源名会映射为固定能力 ID,最终仍由服务端目录、用户实时权限、组织策略和授权范围共同校验。
133
+ - 首期不支持无交互 CI/容器凭据;需要机器身份时应另行建设 workload identity,而不是导出个人刷新令牌。
134
+ - 高风险确认当前使用 Diffcat 本地账号密码进行二次校验;仅使用飞书或 NAS 登录且没有本地密码的用户暂时不能确认此类操作。
135
+ - Device Flow 适合不能稳定接收浏览器回调的 CLI;在浏览器与 CLI 同机且能监听 loopback 回调的场景,后续版本应优先增加 Authorization Code + PKCE,Device Flow 保留为受限的 headless fallback。
@@ -0,0 +1,11 @@
1
+ {
2
+ "runtime": {
3
+ "type": "node",
4
+ "version": "22"
5
+ },
6
+ "init": {
7
+ "darwin": "npm install -g diffcat-cli@0.2.1",
8
+ "linux": "npm install -g diffcat-cli@0.2.1",
9
+ "win32": "npm install -g diffcat-cli@0.2.1"
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "DiffCat",
3
+ "name_zh": "DiffCat 企业管理",
4
+ "name_en": "DiffCat",
5
+ "description": "Securely query authorized DiffCat data and perform controlled business operations.",
6
+ "description_zh": "通过最小授权查询 DiffCat 基础资料和单据,并创建草稿、办理受控审批。",
7
+ "description_en": "Query authorized DiffCat master data and documents, create drafts, and handle controlled approvals.",
8
+ "source": "diffcat-cli",
9
+ "type": "cli",
10
+ "version": "0.2.1",
11
+ "minWorkbuddyVersion": "4.24.0",
12
+ "examples_zh": [
13
+ "查询我有权查看的客户和最近销售订单",
14
+ "根据现有基础资料创建一张采购申请草稿并核对结果",
15
+ "查看我的采购申请待办,在我明确确认后再审批"
16
+ ],
17
+ "examples_en": [
18
+ "List the customers and recent sales orders I can access",
19
+ "Create a purchase request draft from existing master data and verify it",
20
+ "Show my purchase request tasks and approve only after I explicitly confirm"
21
+ ]
22
+ }
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="DiffCat">
2
+ <defs>
3
+ <linearGradient id="background" x1="8" y1="6" x2="56" y2="58" gradientUnits="userSpaceOnUse">
4
+ <stop stop-color="#165DFF" />
5
+ <stop offset="1" stop-color="#722ED1" />
6
+ </linearGradient>
7
+ </defs>
8
+ <rect width="64" height="64" rx="15" fill="url(#background)" />
9
+ <path d="M18 17h13c10.5 0 18 5.8 18 15s-7.5 15-18 15H18V17Zm12.5 23c6.8 0 10.5-2.7 10.5-8s-3.7-8-10.5-8H26v16h4.5Z" fill="#fff" />
10
+ <path d="m45.5 13 1.8 4.1 4.2.6-3.1 3 .8 4.3-3.7-2-3.7 2 .8-4.3-3.1-3 4.2-.6 1.8-4.1Z" fill="#69E0FF" />
11
+ </svg>
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: diffcat
3
+ display_name: DiffCat 企业管理
4
+ display_name_en: DiffCat Business Operations
5
+ description: Use the DiffCat CLI for authorized business queries, document drafts, and controlled workflow approvals.
6
+ description_zh: 使用 DiffCat CLI 查询获授权的基础资料和业务单据、创建单据草稿,并办理受控流程审批。
7
+ description_en: Query authorized DiffCat master data and documents, create drafts, and handle controlled workflow approvals with the DiffCat CLI.
8
+ category: productivity
9
+ version: 0.2.1
10
+ author: DiffCat
11
+ ---
12
+
13
+ # DiffCat CLI
14
+
15
+ Use the installed `diffcat` command as the only DiffCat business execution surface. Do not bypass it with internal REST endpoints, SQL, browser automation, or direct database access.
16
+
17
+ ## Inputs
18
+
19
+ 1. The user's requested DiffCat business task.
20
+ 2. A DiffCat HTTPS API root for the target deployment when the `workbuddy` profile is not logged in.
21
+ 3. Explicit user intent and complete business fields before any draft creation or approval.
22
+
23
+ ## Output
24
+
25
+ Return the requested authorized data or a verified operation result. For writes, include the operation ID, affected entity ID, terminal status, and independent post-write verification outcome.
26
+
27
+ ## Process
28
+
29
+ ### 1. Check installation and authorization
30
+
31
+ Run:
32
+
33
+ ```bash
34
+ diffcat --version
35
+ diffcat --profile workbuddy --pretty auth status
36
+ ```
37
+
38
+ If the profile is not logged in, use the CLI's default production API unless the user explicitly requests another environment. Discover the public catalog, select only the capabilities needed for the user's workflow, and start Device Flow:
39
+
40
+ ```bash
41
+ diffcat --pretty capability catalog
42
+ diffcat --profile workbuddy auth login --capability md.customers.read
43
+ ```
44
+
45
+ The default production API is `https://diffcat-api.howcat.cn`; use `--server <url>` only when the user explicitly chooses another deployment. The user must inspect and approve the browser authorization at `https://diffcat.howcat.cn`. Never request all capabilities by default, operate the authorization page for the user, or ask them to paste tokens, passwords, cookies, or DPoP keys into chat.
46
+
47
+ After authorization, run `auth status`, `grant list`, and `capability list`. A public capability is not automatically granted. Effective access is the intersection of the registered capability, explicit grant, live menu/action permissions, administrator policy constraints, and object-level business checks.
48
+
49
+ ### 2. Discover the live contract
50
+
51
+ Before constructing unfamiliar input, run:
52
+
53
+ ```bash
54
+ diffcat --profile workbuddy --pretty capability describe <capability-id>
55
+ ```
56
+
57
+ Treat the returned `inputSchema`, operations, risk level, and confirmation flag as authoritative. Read @references/operations.md for command routing and implemented resources.
58
+
59
+ ### 3. Execute reads conservatively
60
+
61
+ Use authorized reads to resolve human names to IDs. Never guess a customer, supplier, company, department, product, material, warehouse, account, currency, project, document, or workflow-task ID. If multiple valid matches remain, show stable identifiers and scope fields, then ask the user to choose.
62
+
63
+ Follow pagination deliberately. State when the result is partial because of page limits, authorization scope, or filters. Use only query and relation fields allowed by the live schema.
64
+
65
+ ### 4. Execute writes only on clear intent
66
+
67
+ A request to inspect, plan, prepare, explain, or validate does not authorize a mutation. Before writing, read @references/safety.md.
68
+
69
+ Keep quantities, prices, exchange rates, and money values as JSON strings. Generate one stable idempotency key before the first write and reuse it only for the identical capability and logical input. Prefer `--input @file` or stdin for nontrivial JSON; never place credentials in payload files.
70
+
71
+ ### 5. Verify and report
72
+
73
+ Treat stdout as a single JSON document and stderr as progress. Require exit code zero and operation status `succeeded`. Independently re-read every created or affected object and compare its status, scope, parties, currency, totals, and line identifiers/count as applicable.
74
+
75
+ Never report success for `running`, `unknown`, `awaiting_confirmation`, `failed`, `verification_failed`, `rejected`, or `expired`. Use `operation status` and the recovery rules in @references/safety.md rather than replaying a write.
76
+
77
+ ## Examples
78
+
79
+ - “查询客户海猫科技最近的销售订单” → verify grant, resolve the unique customer ID, query authorized sales orders, and report pagination coverage.
80
+ - “创建采购申请草稿” → describe the create capability, resolve material/company/department data, ask for missing business choices, create with a stable idempotency key, then re-read and verify `DRAFT`.
81
+ - “审批 456” → read the task and related purchase request, show decision-relevant details, invoke only after explicit approval intent, wait for user browser confirmation, resume the same operation, and verify completion.
82
+
83
+ ## Troubleshooting
84
+
85
+ - Missing capability: explain the blocked operation and request only that additional capability after user agreement.
86
+ - Schema or DTO error: re-run `capability describe`; correct only authoritative, user-supported fields.
87
+ - Network interruption or unknown state: query the known operation ID; never retry with a new idempotency key.
88
+ - Verification failure: data may have changed. Re-read the entity, report uncertainty, and require human review instead of automatic retry.
89
+ - Disconnect request: run `diffcat --profile workbuddy auth logout`; do not delete or copy credential-store records manually.
90
+
91
+ ## Reference
92
+
93
+ - @references/operations.md — available resources, commands, and query routes.
94
+ - @references/safety.md — least privilege, draft, approval, idempotency, recovery, and secret-handling rules.
@@ -0,0 +1,64 @@
1
+ # DiffCat operations
2
+
3
+ The live output of `capability list` and `capability describe` overrides this routing snapshot.
4
+
5
+ ## Discovery and credentials
6
+
7
+ ```bash
8
+ diffcat --profile workbuddy --pretty auth status
9
+ diffcat --pretty capability catalog --server <https-api-root>
10
+ diffcat --profile workbuddy --pretty capability list
11
+ diffcat --profile workbuddy --pretty capability describe <capability-id>
12
+ diffcat --profile workbuddy grant list
13
+ diffcat --profile workbuddy auth logout
14
+ ```
15
+
16
+ ## Base and master data
17
+
18
+ Use `resource list <resource> --query <json>` and, when the live schema permits `get`, `resource get <resource> <id>`.
19
+
20
+ ```text
21
+ sys.users sys.departments
22
+ sys.roles sys.dict_types
23
+ sys.dict_entries md.companies
24
+ md.currencies fin.exchange_rates
25
+ md.uoms md.encoding_rules
26
+ md.fee_subjects md.customers
27
+ md.customer_contacts md.customer_invoice_profiles
28
+ md.stores md.contracts
29
+ md.products md.price_lists
30
+ md.price_list_lines md.warehouses
31
+ md.warehouse_members md.suppliers
32
+ md.supplier_contacts md.materials
33
+ md.bom_lines adm.supplies
34
+ fin.bank_accounts sales.crowdfunding_platforms
35
+ ```
36
+
37
+ `md.warehouse_members` currently supports list only. Each base-data capability ID is `<resource>.read`.
38
+
39
+ ## Business documents
40
+
41
+ Use `document list`, `document get`, and `document create-draft` after confirming the live capability. Each resource has `<resource>.read` and `<resource>.create_draft` in the current implementation.
42
+
43
+ ```text
44
+ sales.orders sales.direct_orders
45
+ sales.consignment_reconciliations sales.crowdfunding_orders
46
+ inv.purchase_requests inv.inbound_orders
47
+ inv.outbound_orders inv.stocktake_orders
48
+ fin.expense_requests fin.invoice_requests
49
+ fin.refund_requests adm.supply_entry_orders
50
+ adm.supply_issue_requests adm.supply_purchase_requests
51
+ adm.supply_stocktake_orders
52
+ ```
53
+
54
+ All current create abilities produce `DRAFT` only. They do not submit, start approval, post finance, or change inventory.
55
+
56
+ ## Specialized routes
57
+
58
+ - `project list/get` → `pm.projects.read`.
59
+ - `sales-order list/get/create-draft` → the same capabilities as generic `document sales.orders`.
60
+ - `stats list/query` → `data_center.stats.read`; registered widgets only, never SQL.
61
+ - `task list/get` → `flow.tasks.read` when allowed by the live schema.
62
+ - `task approve` → `flow.tasks.approve`; currently limited to the current user's pending purchase-request `APPROVE/APPROVAL` task.
63
+
64
+ List handlers default to 20 rows and cap a page at 100; a grant can impose a lower limit. Project or statistics queries can additionally require allowed project/department IDs.
@@ -0,0 +1,37 @@
1
+ # DiffCat safety and recovery
2
+
3
+ ## Least privilege
4
+
5
+ - Prefer individual `--capability` values. Use `master-data-read`, `document-read`, or `document-draft` presets only for a clearly requested recurring workflow.
6
+ - Never switch profiles or environments to evade a denial.
7
+ - Do not export personal credentials for CI or containers. Credentials and DPoP keys stay in the operating-system credential store.
8
+
9
+ ## Draft creation
10
+
11
+ 1. Require explicit intent to create the identified document.
12
+ 2. Describe `<resource>.create_draft` and resolve all referenced IDs through authorized reads.
13
+ 3. Ask about every missing business choice; do not invent scope, party, warehouse, account, currency, date, quantity, price, rate, or line data.
14
+ 4. Generate a stable idempotency key before the first attempt.
15
+ 5. Run `document create-draft <resource> --input @draft.json --idempotency-key <key>`.
16
+ 6. Require `succeeded`, then re-read the returned ID and compare `DRAFT` plus relevant business fields.
17
+
18
+ ## Approval
19
+
20
+ Approval is irreversible. The current high-risk capability only accepts a `PENDING` purchase-request task with grant function `APPROVE`, node type `APPROVAL`, and the current user as handler.
21
+
22
+ 1. Read the task and related purchase request; present decision-relevant scope, requester, lines, quantities, and amounts.
23
+ 2. Invoke only when the user explicitly chooses approval.
24
+ 3. The first call creates an `awaiting_confirmation` intent. Present its operation ID and URL; never open or operate the page or enter the user's password.
25
+ 4. After the user says they confirmed, run `operation status <id>` and then `operation resume <id>` using the same `workbuddy` profile.
26
+ 5. Re-read the task and document. Never replace resume with a new approve call.
27
+
28
+ ## Failure handling
29
+
30
+ - Access denied: inspect current grant and live capability list; request additional access only with user agreement.
31
+ - Validation failure: correct against the live schema and business source data; reuse the same key only for identical logical input.
32
+ - `running` or `unknown`: poll the known operation at a reasonable interval; do not replay.
33
+ - `verification_failed`: execution may have changed data. Re-read and escalate uncertainty; do not claim success or automatically retry.
34
+ - `rejected` or `expired`: do not resume. A new attempt needs renewed explicit intent and a new key.
35
+ - Rate limit: reduce pagination/concurrency and wait; do not rotate identities.
36
+
37
+ Keep request and operation IDs for diagnosis, but redact bounded error details. Never print passwords, tokens, cookies, authorization headers, DPoP proofs/keys, credential-store records, or complete sensitive payloads.
@@ -0,0 +1,38 @@
1
+ import type { OperationResponse, PendingOperation } from './types.js';
2
+ export declare class DiffcatApiError extends Error {
3
+ readonly status: number;
4
+ readonly body: unknown;
5
+ constructor(message: string, status: number, body: unknown);
6
+ }
7
+ export declare class DiffcatClient {
8
+ readonly profile: string;
9
+ readonly server: string;
10
+ readonly keyringAccount: string;
11
+ private credential;
12
+ private constructor();
13
+ static load(profile: string): Promise<DiffcatClient>;
14
+ getStatus(): {
15
+ profile: string;
16
+ server: string;
17
+ authenticated: boolean;
18
+ accessExpiresAt: string;
19
+ scope: string[];
20
+ pendingOperationIds: string[];
21
+ };
22
+ getRefreshToken(): string;
23
+ get<T>(path: string, query?: Record<string, string>): Promise<T>;
24
+ post<T>(path: string, body: Record<string, unknown>, headers?: Record<string, string>): Promise<T>;
25
+ invoke(capabilityId: string, input: Record<string, unknown>, options?: {
26
+ operationId?: string;
27
+ idempotencyKey?: string;
28
+ }): Promise<OperationResponse>;
29
+ getPendingOperation(id: string): PendingOperation | null;
30
+ private apiRequest;
31
+ private ensureAccessToken;
32
+ private refresh;
33
+ private fetchWithDpop;
34
+ private applyToken;
35
+ private persistPendingOperation;
36
+ }
37
+ export declare function parseResponse(response: Response): Promise<unknown>;
38
+ export declare function errorMessage(payload: unknown, status: number): string;
package/dist/client.js ADDED
@@ -0,0 +1,199 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { buildServerUrl, getProfileConfig, normalizeServer, withProfileLock, } from './config-store.js';
3
+ import { loadCredential, saveCredential } from './credential-store.js';
4
+ import { createDpopProof } from './dpop.js';
5
+ export class DiffcatApiError extends Error {
6
+ status;
7
+ body;
8
+ constructor(message, status, body) {
9
+ super(message);
10
+ this.status = status;
11
+ this.body = body;
12
+ this.name = 'DiffcatApiError';
13
+ }
14
+ }
15
+ export class DiffcatClient {
16
+ profile;
17
+ server;
18
+ keyringAccount;
19
+ credential;
20
+ constructor(profile, server, keyringAccount, credential) {
21
+ this.profile = profile;
22
+ this.server = server;
23
+ this.keyringAccount = keyringAccount;
24
+ this.credential = credential;
25
+ }
26
+ static async load(profile) {
27
+ const config = await getProfileConfig(profile);
28
+ if (!config)
29
+ throw new Error(`配置 ${profile} 尚未登录,请先运行 diffcat auth login`);
30
+ const credential = loadCredential(config.keyringAccount);
31
+ if (!credential)
32
+ throw new Error(`系统凭据库中没有配置 ${profile} 的授权`);
33
+ return new DiffcatClient(profile, normalizeServer(config.server), config.keyringAccount, credential);
34
+ }
35
+ getStatus() {
36
+ return {
37
+ profile: this.profile,
38
+ server: this.server,
39
+ authenticated: true,
40
+ accessExpiresAt: this.credential.accessExpiresAt,
41
+ scope: this.credential.scope,
42
+ pendingOperationIds: Object.keys(this.credential.pendingOperations ?? {}),
43
+ };
44
+ }
45
+ getRefreshToken() {
46
+ return this.credential.refreshToken;
47
+ }
48
+ async get(path, query) {
49
+ const suffix = query ? `?${new URLSearchParams(query).toString()}` : '';
50
+ return this.apiRequest('GET', `${path}${suffix}`);
51
+ }
52
+ async post(path, body, headers = {}) {
53
+ return this.apiRequest('POST', path, body, headers);
54
+ }
55
+ async invoke(capabilityId, input, options = {}) {
56
+ const idempotencyKey = options.idempotencyKey ?? randomUUID();
57
+ const result = await this.post('/agent/v1/commands/execute', { capabilityId, input, operationId: options.operationId }, { 'Idempotency-Key': idempotencyKey });
58
+ if (result.status === 'awaiting_confirmation') {
59
+ await this.persistPendingOperation(result.operationId, {
60
+ capabilityId,
61
+ input,
62
+ idempotencyKey,
63
+ createdAt: new Date().toISOString(),
64
+ });
65
+ }
66
+ else if (['succeeded', 'failed', 'verification_failed', 'rejected', 'expired'].includes(result.status)) {
67
+ await this.persistPendingOperation(result.operationId, null);
68
+ }
69
+ return result;
70
+ }
71
+ getPendingOperation(id) {
72
+ return this.credential.pendingOperations?.[id] ?? null;
73
+ }
74
+ async apiRequest(method, path, body, headers = {}, retry = true) {
75
+ await this.ensureAccessToken();
76
+ const response = await this.fetchWithDpop(method, path, this.credential.accessToken, body, headers);
77
+ if (response.status === 401 && retry) {
78
+ await this.refresh(this.credential.accessToken);
79
+ return this.apiRequest(method, path, body, headers, false);
80
+ }
81
+ const payload = await parseResponse(response);
82
+ if (!response.ok) {
83
+ throw new DiffcatApiError(errorMessage(payload, response.status), response.status, payload);
84
+ }
85
+ const envelope = payload;
86
+ if (envelope &&
87
+ typeof envelope === 'object' &&
88
+ 'success' in envelope &&
89
+ envelope.success === false) {
90
+ throw new DiffcatApiError(typeof envelope.msg === 'string' ? envelope.msg : 'Diffcat 业务调用失败', response.status, payload);
91
+ }
92
+ return envelope && typeof envelope === 'object' && 'data' in envelope
93
+ ? envelope.data
94
+ : payload;
95
+ }
96
+ async ensureAccessToken() {
97
+ if (new Date(this.credential.accessExpiresAt).getTime() > Date.now() + 30_000)
98
+ return;
99
+ await this.refresh();
100
+ }
101
+ async refresh(rejectedAccessToken) {
102
+ await withProfileLock(this.profile, async () => {
103
+ const latest = loadCredential(this.keyringAccount);
104
+ if (!latest)
105
+ throw new Error(`系统凭据库中没有配置 ${this.profile} 的授权`);
106
+ this.credential = latest;
107
+ const latestIsUsable = new Date(latest.accessExpiresAt).getTime() > Date.now() + 30_000 &&
108
+ (!rejectedAccessToken || latest.accessToken !== rejectedAccessToken);
109
+ if (latestIsUsable)
110
+ return;
111
+ const path = '/agent/v1/oauth/token';
112
+ const url = buildServerUrl(this.server, path);
113
+ const proof = await createDpopProof({
114
+ method: 'POST',
115
+ htu: url,
116
+ privateJwk: this.credential.privateJwk,
117
+ publicJwk: this.credential.publicJwk,
118
+ });
119
+ const response = await fetch(url, {
120
+ method: 'POST',
121
+ headers: { 'Content-Type': 'application/json', DPoP: proof },
122
+ body: JSON.stringify({
123
+ grant_type: 'refresh_token',
124
+ client_id: 'diffcat-cli',
125
+ refresh_token: this.credential.refreshToken,
126
+ }),
127
+ });
128
+ const payload = await parseResponse(response);
129
+ if (!response.ok) {
130
+ throw new DiffcatApiError(errorMessage(payload, response.status), response.status, payload);
131
+ }
132
+ this.applyToken(payload);
133
+ saveCredential(this.keyringAccount, this.credential);
134
+ });
135
+ }
136
+ async fetchWithDpop(method, path, accessToken, body, headers = {}) {
137
+ const url = new URL(buildServerUrl(this.server, path));
138
+ const proof = await createDpopProof({
139
+ method,
140
+ htu: `${url.origin}${url.pathname}`,
141
+ privateJwk: this.credential.privateJwk,
142
+ publicJwk: this.credential.publicJwk,
143
+ accessToken,
144
+ });
145
+ return fetch(url, {
146
+ method,
147
+ headers: {
148
+ Accept: 'application/json',
149
+ ...(body ? { 'Content-Type': 'application/json' } : {}),
150
+ Authorization: `DPoP ${accessToken}`,
151
+ DPoP: proof,
152
+ ...headers,
153
+ },
154
+ body: body ? JSON.stringify(body) : undefined,
155
+ });
156
+ }
157
+ applyToken(token) {
158
+ this.credential.accessToken = token.access_token;
159
+ this.credential.refreshToken = token.refresh_token;
160
+ this.credential.accessExpiresAt = new Date(Date.now() + token.expires_in * 1000).toISOString();
161
+ this.credential.scope = token.scope.split(' ').filter(Boolean);
162
+ }
163
+ async persistPendingOperation(operationId, pending) {
164
+ await withProfileLock(this.profile, async () => {
165
+ const latest = loadCredential(this.keyringAccount);
166
+ if (!latest)
167
+ throw new Error(`系统凭据库中没有配置 ${this.profile} 的授权`);
168
+ latest.pendingOperations ??= {};
169
+ if (pending)
170
+ latest.pendingOperations[operationId] = pending;
171
+ else
172
+ delete latest.pendingOperations[operationId];
173
+ saveCredential(this.keyringAccount, latest);
174
+ this.credential = latest;
175
+ });
176
+ }
177
+ }
178
+ export async function parseResponse(response) {
179
+ const text = await response.text();
180
+ if (!text)
181
+ return null;
182
+ try {
183
+ return JSON.parse(text);
184
+ }
185
+ catch {
186
+ return { error: 'invalid_response', error_description: text.slice(0, 500) };
187
+ }
188
+ }
189
+ export function errorMessage(payload, status) {
190
+ if (payload && typeof payload === 'object') {
191
+ const value = payload;
192
+ for (const key of ['error_description', 'msg', 'message', 'error']) {
193
+ if (typeof value[key] === 'string')
194
+ return value[key];
195
+ }
196
+ }
197
+ return `Diffcat 请求失败(HTTP ${status})`;
198
+ }
199
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}