@ats-cx/cx-cli 0.1.0

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 (57) hide show
  1. package/.env.example +8 -0
  2. package/README.md +119 -0
  3. package/bin/cx-cli.cjs +90 -0
  4. package/config/apm-provider.json +7 -0
  5. package/config/apm-provider.template.jsonc +48 -0
  6. package/config/diagnostic-rules.json +54 -0
  7. package/config/diagnostic-rules.template.jsonc +82 -0
  8. package/config/event-semantics.json +828 -0
  9. package/config/event-semantics.template.jsonc +13 -0
  10. package/config/toolkit.json +3 -0
  11. package/dist/apm-help.d.ts +5 -0
  12. package/dist/apm-help.js +108 -0
  13. package/dist/apm-output.d.ts +49 -0
  14. package/dist/apm-output.js +115 -0
  15. package/dist/budget.d.ts +14 -0
  16. package/dist/budget.js +16 -0
  17. package/dist/cli.d.ts +20 -0
  18. package/dist/cli.js +596 -0
  19. package/dist/context.d.ts +12 -0
  20. package/dist/context.js +20 -0
  21. package/dist/contract.d.ts +11 -0
  22. package/dist/contract.js +4 -0
  23. package/dist/errors.d.ts +4 -0
  24. package/dist/errors.js +8 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +8 -0
  27. package/dist/init.d.ts +17 -0
  28. package/dist/init.js +194 -0
  29. package/dist/output.d.ts +7 -0
  30. package/dist/output.js +7 -0
  31. package/dist/project-pull.d.ts +32 -0
  32. package/dist/project-pull.js +81 -0
  33. package/dist/report.d.ts +3 -0
  34. package/dist/report.js +42 -0
  35. package/dist/skills.d.ts +12 -0
  36. package/dist/skills.js +430 -0
  37. package/dist/tools/apm-tools.d.ts +115 -0
  38. package/dist/tools/apm-tools.js +329 -0
  39. package/dist/tools/conclusion-tools.d.ts +24 -0
  40. package/dist/tools/conclusion-tools.js +61 -0
  41. package/dist/tools/create-run.d.ts +61 -0
  42. package/dist/tools/create-run.js +273 -0
  43. package/dist/tools/log-tools.d.ts +68 -0
  44. package/dist/tools/log-tools.js +116 -0
  45. package/dist/tools/project-tools.d.ts +82 -0
  46. package/dist/tools/project-tools.js +139 -0
  47. package/dist/workspace.d.ts +51 -0
  48. package/dist/workspace.js +104 -0
  49. package/package.json +37 -0
  50. package/skills/apm-query/SKILL.md +304 -0
  51. package/skills/apm-query/agents/openai.yaml +10 -0
  52. package/skills/cx-cli-setup/SKILL.md +120 -0
  53. package/skills/cx-cli-setup/agents/openai.yaml +11 -0
  54. package/skills/editor-diagnostic/SKILL.md +255 -0
  55. package/skills/editor-diagnostic/agents/openai.yaml +13 -0
  56. package/skills/semantics-curation/SKILL.md +151 -0
  57. package/skills/semantics-curation/agents/openai.yaml +12 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ // 生产语义表位于宿主仓库的 .cx-cli/event-semantics.json;
3
+ // 工具箱 config/event-semantics.json 仅是 cx-cli init 的种子表。
4
+ // 字段、值域与条件约束的代码权威是 packages/core/src/semantics/table-schema.ts。
5
+ // 下例是一个可通过当前 strict schema 的完整条目;表内不要加入证据或备注字段。
6
+ "transformElement": {
7
+ "category": "mutation",
8
+ "action": "transform_element",
9
+ "entityType": "element",
10
+ "stateChange": "definite",
11
+ "tags": ["geometry_change"]
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "logSource": { "type": "apm" }
3
+ }
@@ -0,0 +1,5 @@
1
+ /** 命令面契约:`.scratch/apm-command-group/assets/01-apm-help-mock.md`。 */
2
+ export declare const APM_QUERY_HELP = "Usage: cx-cli apm query [\u9009\u9879]\n\n\u67E5\u8BE2 APM \u57CB\u70B9\u65E5\u5FD7\uFF08\u7ECF APM \u7F51\u9875\u7AEF /api/log/query\uFF08\u6839\u5730\u5740\u53D6\u914D\u7F6E baseUrl\uFF09\uFF0C\u53EA\u8BFB\uFF1B\u4E0D\u6536 SQL\uFF09\u3002\n\n\u8FC7\u6EE4\uFF08\u7CBE\u786E\u7B49\u503C\uFF0C\u53EF\u7EC4\u5408\uFF0CAND\uFF09:\n --type <\u8868> \u65E5\u5FD7\u7C7B\u578B\uFF08\u9ED8\u8BA4 user_behav\uFF09: error | performance | api | paint |\n user_behav | network | server_error | footprint | server_api\n --project <id> project_id\n --user <id> user_id\n --collection <id> collection_id\n --session <id> session_id\n --event <\u540D> \u4E8B\u4EF6\u540D data_name\uFF0C\u6A21\u7CCA\u5339\u914D %\u540D%\uFF08\u4EC5 user_behav \u6709\u8BE5\u5217\uFF1B\u63A5\u53E3\u53EA\u6709\u6A21\u7CCA\u3001\u65E0\u7B49\u503C\uFF09\n --sub-type <\u503C> sub_type\uFF08performance: navigation | memory | resource\uFF09\n --where <\u5217=\u503C> \u6309\u5217\u7B49\u503C\uFF0C\u53EF\u91CD\u590D\uFF1B\u5217\u540D\u53EA\u5141\u8BB8 [a-z0-9_]\uFF0C\u503C\u4E0D\u5141\u8BB8\u542B\u5F15\u53F7\u6216\u53CD\u659C\u6760\uFF1B\u5217\u4E0D\u5B58\u5728\u7531\u670D\u52A1\u7AEF\u62A5\u9519\u4E0A\u6D6E\uFF08\u65E0\u539F\u56E0\u3001\u7EA6 1 s\uFF09\uFF1Bdata_name \u4E0D\u53EF\u7528\u4E8E --where\uFF08\u7528 --event\uFF09\uFF1Btype / page / pageSize / order \u662F\u56FA\u5B9A\u8BF7\u6C42\u952E\uFF0C\u4E0D\u53EF\u7528\u4E8E --where\uFF08type \u5217\u7ECF\u63A5\u53E3\u65E0\u6CD5\u8FC7\u6EE4\uFF0C\u5148\u843D\u76D8\u518D\u7528 jq \u7B5B\uFF09\n --app <id> app_id\uFF08\u9ED8\u8BA4\u53D6\u914D\u7F6E defaults.app_id\uFF0C\u5DE5\u5177\u7BB1\u9ED8\u8BA4 10001\uFF09\n --bizline <id> bizline_id\uFF08\u9ED8\u8BA4\u53D6\u914D\u7F6E defaults.bizline_id\uFF0C\u5DE5\u5177\u7BB1\u9ED8\u8BA4 US\uFF09\n --from <\u65F6\u95F4> log_time \u4E0B\u754C\uFF0C\u5FC5\u586B\uFF1BYYYY-MM-DD[ HH:mm:ss] \u6216 ISO\n --to <\u65F6\u95F4> log_time \u4E0A\u754C\uFF08\u9ED8\u8BA4\u4E0D\u8BBE\uFF09\n\n\u8FC7\u6EE4\uFF08\u7F51\u9875\u540C\u6B3E\uFF0C\u5BF9\u7167\u7528\uFF09:\n --item <\u503C> \u590D\u523B\u7F51\u9875\u300C\u7528\u6237/\u9879\u76EE/\u76F8\u518CID\u300D\u6846\uFF1Auser_id / project_id / collection_id \u4E09\u5217 OR\uFF1B\n oNNN \u8BA2\u5355\u53F7\u7531\u670D\u52A1\u7AEF\u89E3\u6790\u4E3A project_id IN (...)\uFF1B\u90AE\u7BB1/\u624B\u673A\u4E0D\u89E3\u6790\uFF0C\u5148 apm resolve\u3002\n \u4E0E --project / --user / --collection \u4E92\u65A5\uFF08\u540C\u7ED9 exit 2\uFF09\n --search <\u6587\u672C> \u590D\u523B\u7F51\u9875\u300CHost,Url,Status...\u300D\u6846\uFF1A\u9017\u53F7\u5206\u9694\uFF0C\u6BCF\u6BB5\u6309\u8868\u5728 host / client_ip /\n terminal_id\uFF08\u7B49\u503C\uFF09\u4E0E page_url\uFF08\u6A21\u7CCA\uFF09\u7B49\u5217\u4E0A OR\uFF0C\u6BB5\u95F4 AND\n\n\u6392\u5E8F:\n --order <ASC|DESC> \u6309 log_time \u6392\u5E8F\uFF08\u9ED8\u8BA4 DESC\uFF1B\u53EA\u63A5\u53D7\u8FD9\u4E24\u4E2A\u503C\uFF09\n\n\u8F93\u51FA\uFF0802 \u5B9A\u7A3F\uFF09:\n --limit <n> \u6700\u591A\u4ECE\u670D\u52A1\u7AEF\u53D6 n \u884C\uFF08\u6309 --order \u53D6\u524D n \u884C\uFF1B\u7701\u65F6\u7701\u91CF\uFF09\n --count \u53EA\u56DE\u603B\u6570 { total }\uFF0C\u4E0D\u53D6\u884C\u3001\u4E0D\u843D\u76D8\n --preview <n> stdout \u5185\u8054\u7684\u9884\u89C8\u884C\u6570\uFF08\u9ED8\u8BA4 20\uFF0C\u4E0A\u9650 200\uFF09\uFF1B\u5B8C\u6574\u7ED3\u679C\u770B\u67E5\u8BE2\u843D\u76D8\u6587\u4EF6\n --full \u9884\u89C8\u4E0D\u7626\u8EAB\uFF08\u9ED8\u8BA4\u9690\u85CF extra_data / agent_type \u4E24\u5217\uFF1B\u6587\u4EF6\u6C38\u8FDC\u5168\u5217\uFF09\n --out <\u6587\u4EF6> \u67E5\u8BE2\u843D\u76D8\u8DEF\u5F84\uFF08\u9ED8\u8BA4 <outDir>/apm/query.<type>.<YYYYMMDD-HHmmss>.<\u968F\u673A>.json\uFF09\n --run <runId> \u628A\u67E5\u8BE2\u843D\u76D8\u5199\u8FDB\u8BE5 run \u7684 artifacts/apm/ \u5E76\u8BB0 trace\uFF0C\u884C\u53EF\u4F5C\u8BC1\u636E apm:<\u6587\u4EF6\u540D>:<\u4E0B\u6807>\uFF0803\uFF09\uFF1B\u4E0E --out \u4E92\u65A5\n\n\u540C\u6B65\uFF0804 \u5B9A\u7A3F\uFF09:\n --no-flush \u4E0D\u5728\u67E5\u8BE2\u524D\u8C03 GET /api/log/flush\uFF08\u9ED8\u8BA4\u5148\u540C\u6B65\u4E00\u6B21\uFF0C\u8BA9\u7F51\u9875\u7AEF\u6512\u6279\u672A\u5199\u5165\u7684\u4E8B\u4EF6\u843D\u5E93\uFF1B\u8FD4\u56DE\u5373\u53EF\u89C1\uFF09\n\n\u884C\u4E3A:\n \u67E5\u8BE2\u524D\u5148\u540C\u6B65\u4E00\u6B21\uFF08--no-flush \u53EF\u5173\uFF1B\u540C\u6B65\u5931\u8D25\u53EA stderr \u8B66\u544A\u3001\u7167\u5E38\u67E5\uFF0C\u7ED3\u679C meta.flush \u8BB0\u5F55\uFF09\u3002\n \u4E00\u6B21\u8C03\u7528\u62C9\u5B8C\u5339\u914D\u96C6\u5E76\u843D\u76D8\uFF08\u53D6\u6570\u9875\u7531\u914D\u7F6E fetch.pageSize \u51B3\u5B9A\uFF0C\u4E32\u884C\u7FFB\u9875\u3001fetch.maxPages \u515C\u5E95\uFF09\uFF0C\n stdout \u53EA\u6709\u6458\u8981 + \u9884\u89C8\uFF0C\u6CA1\u6709 --cursor\u2014\u2014\u518D\u5F80\u4E0B\u770B\u7528\u81EA\u5E26\u5DE5\u5177\u8BFB\u6587\u4EF6\u3002\u65E0 --limit \u65F6\u670D\u52A1\u7AEF\u6309 ASC \u62C9\u5168\u91CF\u3001\n \u672C\u5730\u6309 --order \u6392\u5E8F\uFF1B\u591A\u9875\u9010\u9875\u6838\u5BF9 total\uFF0C\u53D8\u5316 stderr \u8B66\u544A\uFF1B\u7A7A\u7ED3\u679C exit 0\u2014\u2014\u5DF2\u540C\u6B65\u65F6\u63D0\u793A\u6362\u6761\u4EF6\u6216\n --item \u5BF9\u7167\u4E09\u5217 OR\uFF0C\u672A\u540C\u6B65 / \u540C\u6B65\u5931\u8D25\u65F6\u624D\u63D0\u793A\u5148 apm flush\u3002\n\n\u793A\u4F8B:\n cx-cli apm query --project 6478815 --from 2026-08-20 # \u8BE5\u9879\u76EE 8/20 \u8D77\u7684\u7528\u6237\u884C\u4E3A\u4E8B\u4EF6\n cx-cli apm query --project 6478815 --event saveProject --from 2026-08-20 # \u53EA\u770B\u4FDD\u5B58\u7C7B\u4E8B\u4EF6\n cx-cli apm query --type error --project 6478815 --from 2026-08-20 # \u540C\u9879\u76EE\u7684\u524D\u7AEF\u62A5\u9519\n cx-cli apm query --type api --where data_http_status=500 --from 2026-08-27\n cx-cli apm query --item 6478815 --from 2026-08-20 # \u590D\u73B0\u7F51\u9875\u770B\u5230\u7684\u7ED3\u679C\n cx-cli apm query --item o1234567 --from 2026-08-01 # \u6309\u8BA2\u5355\u53F7\uFF08\u670D\u52A1\u7AEF\u89E3\u6790\uFF09\n cx-cli apm query --project 6478815 --from 2026-08-20 --count # \u5148\u63A2\u89C4\u6A21\uFF081 \u4E2A\u8BF7\u6C42\uFF0C\u4E0D\u843D\u76D8\uFF09\n cx-cli apm query --project 6478815 --from 2026-08-20 --limit 20 # \u6700\u8FD1 20 \u6761\uFF08\u670D\u52A1\u7AEF DESC \u5355\u8BF7\u6C42\uFF09\n cx-cli apm query --type error --project 6478815 --from 2026-08-20 --run r-20260829-120000-ab12 # \u843D\u8FDB run\uFF0C\u884C\u5F15\u7528\u4E3A apm:<\u6587\u4EF6\u540D>:<\u4E0B\u6807>\n";
3
+ export declare const APM_RESOLVE_HELP = "Usage: cx-cli apm resolve [\u9009\u9879]\n\n\u7ECF\u7F51\u9875\u7AEF portal \u514D\u767B\u5F55\u8DEF\u7531\u89E3\u6790 ID\uFF08\u4E8C\u9009\u4E00\uFF0C\u8F93\u51FA\u89E3\u6790\u7ED3\u679C\u4F9B\u4E0B\u4E00\u6B65\u67E5\u8BE2\u7528\uFF09\uFF1Bcx-cli \u4E0D\u6301\u4EFB\u4F55\u51ED\u636E\u3002\u8BA2\u5355\u53F7 \u2192 projectIds \u65E0\u514D\u767B\u5F55\u8DEF\u7531\uFF0814 \u5B9A\u4E0D\u52A0\uFF09\uFF0C\u7528 apm query --item oNNN\u3002\n\n --email <\u90AE\u7BB1> \u2192 userId\n --phone <\u624B\u673A\u53F7> \u2192 userId\n --host <\u7AD9\u70B9 host> \u7AD9\u70B9 host\uFF0C\u5373\u65E5\u5FD7 host \u5217\u7684\u503C\uFF08\u987B\u5E26\u5B50\u57DF\uFF0C\u5982 www.example.com\uFF1B\n \u9ED8\u8BA4\u53D6\u914D\u7F6E defaults.host\uFF09\uFF1B\u4E0D\u8DB3\u4E09\u6BB5 exit 2\n\n\u8F93\u51FA\uFF0802\uFF09:\n { userId, email | phone, host }\uFF1B\u67E5\u4E0D\u5230\u662F\u63A5\u53E3 {success:false} \u2192 exit 1\uFF08\u6587\u6848\u540C\u65F6\u63D0\u300C\u65E0\u6B64\u8D26\u53F7\u300D\u4E0E\u300C\u63A5\u53E3\u4E0D\u53EF\u7528\u300D\uFF09\n\n\u793A\u4F8B:\n cx-cli apm resolve --email user@example.com\n";
4
+ export declare const APM_PROJECT_HELP = "Usage: cx-cli apm project [\u9009\u9879] <projectId>\n\n\u62C9\u53D6\u73B0\u7F51 project JSON\uFF08portal \u5F53\u524D\u7EBF\u4E0A\u7248\u672C\uFF1B\u4E0E `project pull` \u7684\u5931\u8D25\u5FEB\u7167\u662F\u4E24\u4E2A\u6765\u6E90\uFF09\u3002\u7ECF\u7F51\u9875\u7AEF\u514D\u767B\u5F55\u8DEF\u7531\uFF0Ccx-cli \u4E0D\u6301\u4EFB\u4F55\u51ED\u636E\u3002\n\n\u53C2\u6570:\n projectId \u9879\u76EE ID\uFF08\u53EA\u6536\u6570\u5B57\uFF0C\u5426\u5219 exit 2\uFF09\n\n\u9009\u9879:\n --host <\u7AD9\u70B9 host> \u7AD9\u70B9 host\uFF0C\u5373\u65E5\u5FD7 host \u5217\u7684\u503C\uFF08\u987B\u5E26\u5B50\u57DF\uFF0C\u5982 www.example.com\uFF1B\n \u9ED8\u8BA4\u53D6\u914D\u7F6E defaults.host\uFF09\uFF1B\u4E0D\u8DB3\u4E09\u6BB5 exit 2\n --out <\u6587\u4EF6> \u843D\u76D8\u8DEF\u5F84\uFF08\u9ED8\u8BA4 <outDir>/apm/project.<projectId>.<YYYYMMDD-HHmmss>.<\u968F\u673A>.json\uFF09\n --run <runId> \u843D\u8FDB\u8BE5 run \u7684 artifacts/apm/ \u5E76\u8BB0 trace\uFF0C\u6574\u6587\u4EF6\u53EF\u4F5C\u8BC1\u636E apm:<\u6587\u4EF6\u540D>\uFF1B\u4E0E --out \u4E92\u65A5\n\n\u8F93\u51FA\uFF0802 / 07\uFF09:\n \u5FC5\u843D\u76D8\uFF1A\u54CD\u5E94\u4F53\u539F\u6837\u5199\u5165\uFF08\u9876\u5C42 {images, effects, project}\uFF0C\u4E0E\u5FEB\u7167\u6587\u4EF6\u540C\u5F62\u72B6\u3001\u53EF\u4E92\u6362\uFF1B\u4E0D\u88F9 meta\uFF09\uFF0C\n stdout \u53EA\u7ED9 { projectId, host, file, bytes, updatedDate }\uFF1B\u4E0D\u63D0\u4F9B --print\uFF0CAgent \u76F4\u63A5\u8BFB\u6587\u4EF6\u3002\n \u9879\u76EE\u4E0D\u5B58\u5728 / host \u4E0D\u5BF9 / portal \u767B\u5F55\u5931\u8D25 \u2192 \u63A5\u53E3\u56DE HTTP 200 + \u6B63\u6587 [object Object] \u2192 exit 1 kind:\"invalid_response\"\uFF08\u6587\u6848\u5217\u51FA\u4E09\u79CD\u53EF\u80FD\uFF09\uFF1B\n \u8FD4\u56DE\u4F53\u65E0 project \u952E\uFF08package \u9879\u76EE\uFF09\u2192 \u7167\u5E38\u843D\u76D8\u3001updatedDate null\u3001stderr \u8B66\u544A\u3001exit 0\u3002\n\n\u793A\u4F8B:\n cx-cli apm project 6478815 # \u73B0\u7F51\u7248\u672C\u843D <outDir>/apm/\uFF0C\u4E0E\u6700\u65B0\u5FEB\u7167\u6BD4 updatedDate\n cx-cli apm project 6478815 --run r-20260829-120000-ab12 # \u843D\u8FDB run\uFF0C\u8BC1\u636E apm:project.6478815.20260829-120000.k3f9.json\n";
5
+ export declare const APM_FLUSH_HELP = "Usage: cx-cli apm flush\n\n\u8BA9\u7F51\u9875\u7AEF\u628A\u5185\u5B58\u4E2D\u6512\u6279\u672A\u5199\u5165\u7684\u4E8B\u4EF6\u5F3A\u5236\u843D\u5E93\uFF08= \u7F51\u9875\u300C\u540C\u6B65\u300D\u6309\u94AE\uFF0CGET /api/log/flush\uFF0C\u514D\u9274\u6743\uFF09\u3002\n\u4E0D\u540C\u6B65\u65F6\u6700\u8FD1\u4E8B\u4EF6\u53EF\u80FD\u67E5\u4E0D\u5230\uFF08\u6BCF\u8868\u81F3\u591A 1000 \u6761\u6EDE\u540E\uFF09\u3002\u8FD4\u56DE\u5373\u53EF\u89C1\uFF0C\u4E0D\u9700\u8981\u7B49\u5F85\u3002\napm query \u4E0E run new \u76F4\u62C9\u9ED8\u8BA4\u5DF2\u5728\u67E5\u8BE2\u524D\u81EA\u52A8\u540C\u6B65\uFF0804\uFF09\uFF1B\u672C\u547D\u4EE4\u7ED9 --no-flush / --logs \u5DE5\u4F5C\u6D41\uFF0C\n\u4EE5\u53CA stderr \u51FA\u73B0\u300C\u540C\u6B65\u5931\u8D25\u300D\u8B66\u544A\u540E\u7684\u624B\u52A8\u91CD\u8BD5\u7528\u3002\n\n\u8F93\u51FA\uFF0802\uFF09:\n { flushed: true, message }\uFF1B\u63A5\u53E3 {success:false} \u2192 exit 1\n";
@@ -0,0 +1,108 @@
1
+ /** 命令面契约:`.scratch/apm-command-group/assets/01-apm-help-mock.md`。 */
2
+ export const APM_QUERY_HELP = `Usage: cx-cli apm query [选项]
3
+
4
+ 查询 APM 埋点日志(经 APM 网页端 /api/log/query(根地址取配置 baseUrl),只读;不收 SQL)。
5
+
6
+ 过滤(精确等值,可组合,AND):
7
+ --type <表> 日志类型(默认 user_behav): error | performance | api | paint |
8
+ user_behav | network | server_error | footprint | server_api
9
+ --project <id> project_id
10
+ --user <id> user_id
11
+ --collection <id> collection_id
12
+ --session <id> session_id
13
+ --event <名> 事件名 data_name,模糊匹配 %名%(仅 user_behav 有该列;接口只有模糊、无等值)
14
+ --sub-type <值> sub_type(performance: navigation | memory | resource)
15
+ --where <列=值> 按列等值,可重复;列名只允许 [a-z0-9_],值不允许含引号或反斜杠;列不存在由服务端报错上浮(无原因、约 1 s);data_name 不可用于 --where(用 --event);type / page / pageSize / order 是固定请求键,不可用于 --where(type 列经接口无法过滤,先落盘再用 jq 筛)
16
+ --app <id> app_id(默认取配置 defaults.app_id,工具箱默认 10001)
17
+ --bizline <id> bizline_id(默认取配置 defaults.bizline_id,工具箱默认 US)
18
+ --from <时间> log_time 下界,必填;YYYY-MM-DD[ HH:mm:ss] 或 ISO
19
+ --to <时间> log_time 上界(默认不设)
20
+
21
+ 过滤(网页同款,对照用):
22
+ --item <值> 复刻网页「用户/项目/相册ID」框:user_id / project_id / collection_id 三列 OR;
23
+ oNNN 订单号由服务端解析为 project_id IN (...);邮箱/手机不解析,先 apm resolve。
24
+ 与 --project / --user / --collection 互斥(同给 exit 2)
25
+ --search <文本> 复刻网页「Host,Url,Status...」框:逗号分隔,每段按表在 host / client_ip /
26
+ terminal_id(等值)与 page_url(模糊)等列上 OR,段间 AND
27
+
28
+ 排序:
29
+ --order <ASC|DESC> 按 log_time 排序(默认 DESC;只接受这两个值)
30
+
31
+ 输出(02 定稿):
32
+ --limit <n> 最多从服务端取 n 行(按 --order 取前 n 行;省时省量)
33
+ --count 只回总数 { total },不取行、不落盘
34
+ --preview <n> stdout 内联的预览行数(默认 20,上限 200);完整结果看查询落盘文件
35
+ --full 预览不瘦身(默认隐藏 extra_data / agent_type 两列;文件永远全列)
36
+ --out <文件> 查询落盘路径(默认 <outDir>/apm/query.<type>.<YYYYMMDD-HHmmss>.<随机>.json)
37
+ --run <runId> 把查询落盘写进该 run 的 artifacts/apm/ 并记 trace,行可作证据 apm:<文件名>:<下标>(03);与 --out 互斥
38
+
39
+ 同步(04 定稿):
40
+ --no-flush 不在查询前调 GET /api/log/flush(默认先同步一次,让网页端攒批未写入的事件落库;返回即可见)
41
+
42
+ 行为:
43
+ 查询前先同步一次(--no-flush 可关;同步失败只 stderr 警告、照常查,结果 meta.flush 记录)。
44
+ 一次调用拉完匹配集并落盘(取数页由配置 fetch.pageSize 决定,串行翻页、fetch.maxPages 兜底),
45
+ stdout 只有摘要 + 预览,没有 --cursor——再往下看用自带工具读文件。无 --limit 时服务端按 ASC 拉全量、
46
+ 本地按 --order 排序;多页逐页核对 total,变化 stderr 警告;空结果 exit 0——已同步时提示换条件或
47
+ --item 对照三列 OR,未同步 / 同步失败时才提示先 apm flush。
48
+
49
+ 示例:
50
+ cx-cli apm query --project 6478815 --from 2026-08-20 # 该项目 8/20 起的用户行为事件
51
+ cx-cli apm query --project 6478815 --event saveProject --from 2026-08-20 # 只看保存类事件
52
+ cx-cli apm query --type error --project 6478815 --from 2026-08-20 # 同项目的前端报错
53
+ cx-cli apm query --type api --where data_http_status=500 --from 2026-08-27
54
+ cx-cli apm query --item 6478815 --from 2026-08-20 # 复现网页看到的结果
55
+ cx-cli apm query --item o1234567 --from 2026-08-01 # 按订单号(服务端解析)
56
+ cx-cli apm query --project 6478815 --from 2026-08-20 --count # 先探规模(1 个请求,不落盘)
57
+ cx-cli apm query --project 6478815 --from 2026-08-20 --limit 20 # 最近 20 条(服务端 DESC 单请求)
58
+ cx-cli apm query --type error --project 6478815 --from 2026-08-20 --run r-20260829-120000-ab12 # 落进 run,行引用为 apm:<文件名>:<下标>
59
+ `;
60
+ export const APM_RESOLVE_HELP = `Usage: cx-cli apm resolve [选项]
61
+
62
+ 经网页端 portal 免登录路由解析 ID(二选一,输出解析结果供下一步查询用);cx-cli 不持任何凭据。订单号 → projectIds 无免登录路由(14 定不加),用 apm query --item oNNN。
63
+
64
+ --email <邮箱> → userId
65
+ --phone <手机号> → userId
66
+ --host <站点 host> 站点 host,即日志 host 列的值(须带子域,如 www.example.com;
67
+ 默认取配置 defaults.host);不足三段 exit 2
68
+
69
+ 输出(02):
70
+ { userId, email | phone, host };查不到是接口 {success:false} → exit 1(文案同时提「无此账号」与「接口不可用」)
71
+
72
+ 示例:
73
+ cx-cli apm resolve --email user@example.com
74
+ `;
75
+ export const APM_PROJECT_HELP = `Usage: cx-cli apm project [选项] <projectId>
76
+
77
+ 拉取现网 project JSON(portal 当前线上版本;与 \`project pull\` 的失败快照是两个来源)。经网页端免登录路由,cx-cli 不持任何凭据。
78
+
79
+ 参数:
80
+ projectId 项目 ID(只收数字,否则 exit 2)
81
+
82
+ 选项:
83
+ --host <站点 host> 站点 host,即日志 host 列的值(须带子域,如 www.example.com;
84
+ 默认取配置 defaults.host);不足三段 exit 2
85
+ --out <文件> 落盘路径(默认 <outDir>/apm/project.<projectId>.<YYYYMMDD-HHmmss>.<随机>.json)
86
+ --run <runId> 落进该 run 的 artifacts/apm/ 并记 trace,整文件可作证据 apm:<文件名>;与 --out 互斥
87
+
88
+ 输出(02 / 07):
89
+ 必落盘:响应体原样写入(顶层 {images, effects, project},与快照文件同形状、可互换;不裹 meta),
90
+ stdout 只给 { projectId, host, file, bytes, updatedDate };不提供 --print,Agent 直接读文件。
91
+ 项目不存在 / host 不对 / portal 登录失败 → 接口回 HTTP 200 + 正文 [object Object] → exit 1 kind:"invalid_response"(文案列出三种可能);
92
+ 返回体无 project 键(package 项目)→ 照常落盘、updatedDate null、stderr 警告、exit 0。
93
+
94
+ 示例:
95
+ cx-cli apm project 6478815 # 现网版本落 <outDir>/apm/,与最新快照比 updatedDate
96
+ cx-cli apm project 6478815 --run r-20260829-120000-ab12 # 落进 run,证据 apm:project.6478815.20260829-120000.k3f9.json
97
+ `;
98
+ export const APM_FLUSH_HELP = `Usage: cx-cli apm flush
99
+
100
+ 让网页端把内存中攒批未写入的事件强制落库(= 网页「同步」按钮,GET /api/log/flush,免鉴权)。
101
+ 不同步时最近事件可能查不到(每表至多 1000 条滞后)。返回即可见,不需要等待。
102
+ apm query 与 run new 直拉默认已在查询前自动同步(04);本命令给 --no-flush / --logs 工作流,
103
+ 以及 stderr 出现「同步失败」警告后的手动重试用。
104
+
105
+ 输出(02):
106
+ { flushed: true, message };接口 {success:false} → exit 1
107
+ `;
108
+ //# sourceMappingURL=apm-help.js.map
@@ -0,0 +1,49 @@
1
+ import type { ApmOrder, FlushRecord, LogType } from "@ats-cx/cx-core";
2
+ export interface QueryDumpMeta {
3
+ command: "apm query";
4
+ fetchedAt: string;
5
+ request: {
6
+ url: string;
7
+ body: Record<string, unknown>;
8
+ };
9
+ flush: FlushRecord;
10
+ total: number;
11
+ fetched: number;
12
+ pagesFetched: number;
13
+ warnings: string[];
14
+ }
15
+ export interface QueryPreview {
16
+ total: number;
17
+ fetched: number;
18
+ pagesFetched: number;
19
+ truncated: boolean;
20
+ file: string;
21
+ columns: string[];
22
+ hiddenColumns: string[];
23
+ hint: string | null;
24
+ rows: unknown[];
25
+ }
26
+ export declare function createQueryFileName(type: LogType, now?: Date): string;
27
+ export declare function createProjectFileName(projectId: string, now?: Date): string;
28
+ export declare function resolveApmOutputFile(options: {
29
+ out?: string;
30
+ run?: string;
31
+ runsDir: string;
32
+ outDir: string;
33
+ fileName: string;
34
+ }): string;
35
+ export declare function writeQueryDump(file: string, rows: unknown[], meta: Omit<QueryDumpMeta, "command" | "fetchedAt"> & {
36
+ fetchedAt: Date;
37
+ }): void;
38
+ export declare function writeRawApmFile(file: string, text: string): number;
39
+ export declare function buildQueryPreview(options: {
40
+ rows: unknown[];
41
+ total: number;
42
+ pagesFetched: number;
43
+ file: string;
44
+ order: ApmOrder;
45
+ preview: number;
46
+ full: boolean;
47
+ alreadySorted: boolean;
48
+ flush: FlushRecord;
49
+ }): QueryPreview;
@@ -0,0 +1,115 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { randomInt } from "node:crypto";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { loadRun } from "@ats-cx/cx-core";
5
+ const HIDDEN_PREVIEW_COLUMNS = ["extra_data", "agent_type"];
6
+ const RANDOM_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
7
+ function pad(value) {
8
+ return String(value).padStart(2, "0");
9
+ }
10
+ function localTimestamp(now) {
11
+ return [now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate())].join("") +
12
+ `-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
13
+ }
14
+ function randomSuffix() {
15
+ return Array.from({ length: 4 }, () => RANDOM_ALPHABET[randomInt(RANDOM_ALPHABET.length)]).join("");
16
+ }
17
+ export function createQueryFileName(type, now = new Date()) {
18
+ return `query.${type}.${localTimestamp(now)}.${randomSuffix()}.json`;
19
+ }
20
+ export function createProjectFileName(projectId, now = new Date()) {
21
+ return `project.${projectId}.${localTimestamp(now)}.${randomSuffix()}.json`;
22
+ }
23
+ export function resolveApmOutputFile(options) {
24
+ if (options.out !== undefined) {
25
+ return resolve(options.out);
26
+ }
27
+ if (options.run !== undefined) {
28
+ const run = loadRun(options.runsDir, options.run);
29
+ return join(run.runDir, "artifacts", "apm", options.fileName);
30
+ }
31
+ return join(options.outDir, "apm", options.fileName);
32
+ }
33
+ export function writeQueryDump(file, rows, meta) {
34
+ mkdirSync(dirname(file), { recursive: true });
35
+ const content = {
36
+ meta: {
37
+ command: "apm query",
38
+ fetchedAt: meta.fetchedAt.toISOString(),
39
+ request: meta.request,
40
+ flush: meta.flush,
41
+ total: meta.total,
42
+ fetched: meta.fetched,
43
+ pagesFetched: meta.pagesFetched,
44
+ warnings: meta.warnings,
45
+ },
46
+ result: rows,
47
+ };
48
+ writeFileSync(file, JSON.stringify(content, null, 2));
49
+ }
50
+ export function writeRawApmFile(file, text) {
51
+ mkdirSync(dirname(file), { recursive: true });
52
+ writeFileSync(file, text, "utf8");
53
+ return Buffer.byteLength(text, "utf8");
54
+ }
55
+ function isRecord(value) {
56
+ return typeof value === "object" && value !== null && !Array.isArray(value);
57
+ }
58
+ function parsePreviewString(value) {
59
+ try {
60
+ const parsed = JSON.parse(value);
61
+ return typeof parsed === "string" || (typeof parsed === "object" && parsed !== null) ? parsed : value;
62
+ }
63
+ catch {
64
+ return value;
65
+ }
66
+ }
67
+ function preparePreviewRow(row, hiddenColumns) {
68
+ if (!isRecord(row))
69
+ return row;
70
+ return Object.fromEntries(Object.entries(row).flatMap(([key, value]) => {
71
+ if (hiddenColumns.includes(key))
72
+ return [];
73
+ return [[key, typeof value === "string" ? parsePreviewString(value) : value]];
74
+ }));
75
+ }
76
+ function sortRows(rows, order) {
77
+ return [...rows].sort((left, right) => {
78
+ const leftTime = isRecord(left) && typeof left.log_time === "string" ? left.log_time : "";
79
+ const rightTime = isRecord(right) && typeof right.log_time === "string" ? right.log_time : "";
80
+ const comparison = leftTime.localeCompare(rightTime);
81
+ return order === "ASC" ? comparison : -comparison;
82
+ });
83
+ }
84
+ function emptyHint(flush) {
85
+ return flush.ok === true
86
+ ? "未查到结果;换过滤条件,或用 --item <id> 对照三列 OR 是否命中"
87
+ : "未查到结果;先 apm flush 让最近事件落库再查,或换过滤条件";
88
+ }
89
+ export function buildQueryPreview(options) {
90
+ const fetched = options.rows.length;
91
+ const ordered = options.alreadySorted ? options.rows : sortRows(options.rows, options.order);
92
+ const hiddenColumns = options.full ? [] : [...HIDDEN_PREVIEW_COLUMNS];
93
+ const rows = ordered
94
+ .slice(0, options.preview)
95
+ .map(row => preparePreviewRow(row, hiddenColumns));
96
+ const truncated = rows.length < fetched;
97
+ const columns = isRecord(options.rows[0]) ? Object.keys(options.rows[0]) : [];
98
+ const hint = truncated
99
+ ? `完整结果在 ${options.file},用 jq / grep 读,或加过滤条件缩小范围`
100
+ : fetched === 0
101
+ ? emptyHint(options.flush)
102
+ : null;
103
+ return {
104
+ total: options.total,
105
+ fetched,
106
+ pagesFetched: options.pagesFetched,
107
+ truncated,
108
+ file: options.file,
109
+ columns,
110
+ hiddenColumns,
111
+ hint,
112
+ rows,
113
+ };
114
+ }
115
+ //# sourceMappingURL=apm-output.js.map
@@ -0,0 +1,14 @@
1
+ export declare const DEFAULT_PAGE_SIZE = 50;
2
+ export interface Page<T> {
3
+ items: T[];
4
+ total: number;
5
+ cursor: number;
6
+ nextCursor: number | null;
7
+ truncated: boolean;
8
+ /** 截断时提示下钻方式,直接展示给宿主 Agent。 */
9
+ hint: string | null;
10
+ }
11
+ export declare function paginate<T>(all: T[], options?: {
12
+ pageSize?: number;
13
+ cursor?: number;
14
+ }): Page<T>;
package/dist/budget.js ADDED
@@ -0,0 +1,16 @@
1
+ export const DEFAULT_PAGE_SIZE = 50;
2
+ export function paginate(all, options = {}) {
3
+ const pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;
4
+ const cursor = options.cursor ?? 0;
5
+ const items = all.slice(cursor, cursor + pageSize);
6
+ const nextCursor = cursor + items.length < all.length ? cursor + items.length : null;
7
+ return {
8
+ items,
9
+ total: all.length,
10
+ cursor,
11
+ nextCursor,
12
+ truncated: nextCursor !== null,
13
+ hint: nextCursor !== null ? `结果已截断(共 ${all.length} 条),用 --cursor ${nextCursor} 继续,或加过滤参数缩小范围` : null,
14
+ };
15
+ }
16
+ //# sourceMappingURL=budget.js.map
package/dist/cli.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { ConfigSource } from "@ats-cx/cx-core";
2
+ import { Command } from "commander";
3
+ import type { CliResult } from "./output.js";
4
+ import type { Workspace } from "./workspace.js";
5
+ export declare const TOOLKIT_ROOT: string;
6
+ export interface CliSink {
7
+ write(chunk: string): void;
8
+ emit(result: CliResult): void;
9
+ }
10
+ export interface Execution {
11
+ configDir: string;
12
+ configSource: ConfigSource;
13
+ /** 命中的诊断工作区;显式 --config 时不消费工作区,恒为 null。 */
14
+ workspace: Workspace | null;
15
+ }
16
+ export declare function assertConfigDirExists(path: string, label: string): void;
17
+ /** 配置解析优先级(map 决策 8):显式 --config > cwd 向上发现工作区 > $CX_CLI_CONFIG_DIR > 工具箱 config/。 */
18
+ export declare function resolveExecution(explicit: unknown, cwd?: string): Execution;
19
+ export declare function createProgram(sink: CliSink): Command;
20
+ export declare function runCli(argv: string[]): Promise<CliResult>;