chainlesschain 0.45.80 → 0.46.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 (48) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/.build-hash +1 -1
  3. package/src/assets/web-panel/assets/{Analytics--dpzs0oZ.js → Analytics-C1AnPdMx.js} +2 -2
  4. package/src/assets/web-panel/assets/{AppLayout-DWXapZbP.js → AppLayout-BnvARObz.js} +1 -1
  5. package/src/assets/web-panel/assets/{Backup-BoUUzGFw.js → Backup-D31iZX3l.js} +1 -1
  6. package/src/assets/web-panel/assets/{Chat-CkSlXBzN.js → Chat-DiXJ3TuK.js} +1 -1
  7. package/src/assets/web-panel/assets/Cowork-B8ZDdRm4.js +7 -0
  8. package/src/assets/web-panel/assets/Cowork-CXuhlHew.css +1 -0
  9. package/src/assets/web-panel/assets/{Cron-xiBL6XfP.js → Cron-DBt1ueXh.js} +2 -2
  10. package/src/assets/web-panel/assets/{Dashboard-CmgEtUKl.js → Dashboard-jt6XPIjB.js} +1 -1
  11. package/src/assets/web-panel/assets/{Git-DCDjvp5Z.js → Git-hwQ1oZHj.js} +2 -2
  12. package/src/assets/web-panel/assets/{Logs-Qz-GLplC.js → Logs-4D9p6PRM.js} +1 -1
  13. package/src/assets/web-panel/assets/{McpTools-qYf_sT-Y.js → McpTools-CyAUjbbs.js} +1 -1
  14. package/src/assets/web-panel/assets/{Memory-BxoM2XNZ.js → Memory-BMqOR7S-.js} +2 -2
  15. package/src/assets/web-panel/assets/{Notes-DltR8wq4.js → Notes-Cmas8i4E.js} +2 -2
  16. package/src/assets/web-panel/assets/{Organization-7m_PX3yo.js → Organization-DnSa58Tl.js} +4 -4
  17. package/src/assets/web-panel/assets/{P2P-e88KqFBm.js → P2P-BxksIBWs.js} +2 -2
  18. package/src/assets/web-panel/assets/{Permissions-DAY4Xy1h.js → Permissions-Bq5Qn2s3.js} +4 -4
  19. package/src/assets/web-panel/assets/{Projects-ylUhg9th.js → Projects-B7EM0uPg.js} +1 -1
  20. package/src/assets/web-panel/assets/{Providers-DNIlBWLm.js → Providers-DAwgG5KV.js} +2 -2
  21. package/src/assets/web-panel/assets/{RssFeed-Dr_6vD69.js → RssFeed-HSZoRXvS.js} +2 -2
  22. package/src/assets/web-panel/assets/{Security-U57Q-VOj.js → Security-Cz17qBny.js} +3 -3
  23. package/src/assets/web-panel/assets/{Services-BUfO-jvr.js → Services-D2EsLq-v.js} +1 -1
  24. package/src/assets/web-panel/assets/{Skills-D0NYT7Q8.js → Skills-C9v-f3vZ.js} +1 -1
  25. package/src/assets/web-panel/assets/{Tasks-WXqKX58l.js → Tasks-yMEcU0n7.js} +1 -1
  26. package/src/assets/web-panel/assets/{Templates-B1zfqNTe.js → Templates-l7SvlKuB.js} +1 -1
  27. package/src/assets/web-panel/assets/{Wallet-CUSPGN3F.js → Wallet-BHWhLWn9.js} +4 -4
  28. package/src/assets/web-panel/assets/{WebAuthn-ZGz__UJi.js → WebAuthn-kWhFYaUK.js} +4 -4
  29. package/src/assets/web-panel/assets/{antd-BQNxIyr-.js → antd-D6h4fDFf.js} +82 -82
  30. package/src/assets/web-panel/assets/{index-BYqeR6ME.js → index-C1SPm_5l.js} +2 -2
  31. package/src/assets/web-panel/assets/{markdown-BeVIhIzs.js → markdown-BZsB-Dsv.js} +1 -1
  32. package/src/assets/web-panel/index.html +2 -2
  33. package/src/commands/cowork.js +695 -0
  34. package/src/gateways/ws/action-protocol.js +143 -2
  35. package/src/gateways/ws/message-dispatcher.js +3 -0
  36. package/src/gateways/ws/ws-server.js +18 -0
  37. package/src/lib/cowork-cron.js +474 -0
  38. package/src/lib/cowork-learning.js +438 -0
  39. package/src/lib/cowork-mcp-tools.js +182 -0
  40. package/src/lib/cowork-share.js +218 -0
  41. package/src/lib/cowork-task-runner.js +364 -4
  42. package/src/lib/cowork-task-templates.js +203 -12
  43. package/src/lib/cowork-template-marketplace.js +205 -0
  44. package/src/lib/cowork-workflow.js +571 -0
  45. package/src/lib/sub-agent-context.js +66 -0
  46. package/src/lib/workflow-expr.js +318 -0
  47. package/src/assets/web-panel/assets/Cowork-CPqYhoMI.css +0 -1
  48. package/src/assets/web-panel/assets/Cowork-DjAJ5ymV.js +0 -48
@@ -158,6 +158,7 @@ ${ERROR_RECOVERY_PROMPT}`,
158
158
  name: "数据分析",
159
159
  category: "data",
160
160
  acceptsFiles: true,
161
+ parallelStrategy: "auto",
161
162
  fileTypes: [".csv", ".xlsx", ".xls", ".json", ".tsv", ".sqlite", ".db"],
162
163
  systemPromptExtension: `你是数据分析专家。
163
164
 
@@ -194,6 +195,7 @@ ${ERROR_RECOVERY_PROMPT}`,
194
195
  category: "research",
195
196
  acceptsFiles: false,
196
197
  fileTypes: [],
198
+ parallelStrategy: "auto",
197
199
  shellPolicyOverrides: ["network-download"],
198
200
  systemPromptExtension: `你是信息检索与调研专家。
199
201
 
@@ -270,6 +272,7 @@ ${ERROR_RECOVERY_PROMPT}`,
270
272
  name: "代码辅助",
271
273
  category: "development",
272
274
  acceptsFiles: true,
275
+ parallelStrategy: "auto",
273
276
  fileTypes: [
274
277
  ".js",
275
278
  ".ts",
@@ -460,6 +463,32 @@ ${OPEN_SOURCE_FIRST_PROMPT}
460
463
  ${FILE_HANDLING_PROMPT}
461
464
  ${ERROR_RECOVERY_PROMPT}`,
462
465
  },
466
+
467
+ "code-review": {
468
+ id: "code-review",
469
+ name: "代码评审",
470
+ category: "development",
471
+ acceptsFiles: true,
472
+ fileTypes: [
473
+ ".js",
474
+ ".ts",
475
+ ".py",
476
+ ".go",
477
+ ".rs",
478
+ ".java",
479
+ ".kt",
480
+ ".cpp",
481
+ ".c",
482
+ ".rb",
483
+ ".php",
484
+ ".vue",
485
+ ],
486
+ mode: "debate",
487
+ debatePerspectives: ["performance", "security", "maintainability"],
488
+ systemPromptExtension: `你是多视角代码评审的协调者。
489
+ 将代码分发给多个专业评审员独立评审,最后综合输出裁决。
490
+ `,
491
+ },
463
492
  };
464
493
 
465
494
  /**
@@ -467,23 +496,49 @@ ${ERROR_RECOVERY_PROMPT}`,
467
496
  * @param {string|null} templateId
468
497
  * @returns {object} Template definition
469
498
  */
499
+ /**
500
+ * Extra template registry for installed user templates. The marketplace
501
+ * loader populates this at CLI startup via `setUserTemplates()`. Keeping
502
+ * it local lets `getTemplate()` / `getTemplatesForUI()` stay synchronous
503
+ * while still returning user-installed templates.
504
+ */
505
+ let _userTemplates = {};
506
+
507
+ /** Called by the marketplace / CLI to register installed user templates. */
508
+ export function setUserTemplates(templates) {
509
+ _userTemplates = {};
510
+ if (Array.isArray(templates)) {
511
+ for (const tpl of templates) {
512
+ if (tpl?.id) _userTemplates[tpl.id] = tpl;
513
+ }
514
+ }
515
+ }
516
+
517
+ /** Read-only accessor for tests. */
518
+ export function getUserTemplates() {
519
+ return { ..._userTemplates };
520
+ }
521
+
470
522
  export function getTemplate(templateId) {
471
- if (!templateId || !TASK_TEMPLATES[templateId]) {
472
- return {
473
- id: "free",
474
- name: "自由模式",
475
- category: "general",
476
- acceptsFiles: true,
477
- fileTypes: [],
478
- systemPromptExtension: `你是一个全能助手,可以处理用户提出的任何日常任务。
523
+ if (templateId && TASK_TEMPLATES[templateId]) {
524
+ return TASK_TEMPLATES[templateId];
525
+ }
526
+ if (templateId && _userTemplates[templateId]) {
527
+ return _userTemplates[templateId];
528
+ }
529
+ return {
530
+ id: "free",
531
+ name: "自由模式",
532
+ category: "general",
533
+ acceptsFiles: true,
534
+ fileTypes: [],
535
+ systemPromptExtension: `你是一个全能助手,可以处理用户提出的任何日常任务。
479
536
  根据任务类型自动选择最合适的工具和方法。
480
537
 
481
538
  ${OPEN_SOURCE_FIRST_PROMPT}
482
539
  ${FILE_HANDLING_PROMPT}
483
540
  ${ERROR_RECOVERY_PROMPT}`,
484
- };
485
- }
486
- return TASK_TEMPLATES[templateId];
541
+ };
487
542
  }
488
543
 
489
544
  /**
@@ -491,5 +546,141 @@ ${ERROR_RECOVERY_PROMPT}`,
491
546
  * @returns {string[]}
492
547
  */
493
548
  export function listTemplateIds() {
494
- return Object.keys(TASK_TEMPLATES);
549
+ return [...Object.keys(TASK_TEMPLATES), ...Object.keys(_userTemplates)];
550
+ }
551
+
552
+ // ─── UI Metadata ─────────────────────────────────────────────────────────────
553
+
554
+ const UI_METADATA = {
555
+ "doc-convert": {
556
+ icon: "FileTextOutlined",
557
+ description: "Word、Markdown、HTML、PDF 之间的格式互转",
558
+ examples: [
559
+ "把 report.docx 转成 PDF",
560
+ "合并多个 Markdown 为一个文档",
561
+ "把 Excel 导出为 PDF",
562
+ ],
563
+ },
564
+ "media-process": {
565
+ icon: "PlayCircleOutlined",
566
+ description: "视频压缩、音频提取、格式转换、剪辑",
567
+ examples: [
568
+ "提取 MP4 的音频",
569
+ "压缩视频到 50MB 以内",
570
+ "剪辑 10:30 到 25:00 的片段",
571
+ ],
572
+ },
573
+ "data-analysis": {
574
+ icon: "BarChartOutlined",
575
+ description: "CSV/Excel 分析、统计、可视化图表",
576
+ examples: [
577
+ "分析 sales.csv 的月度趋势",
578
+ "清洗数据去重修复格式",
579
+ "比较两个 CSV 的差异",
580
+ ],
581
+ },
582
+ "web-research": {
583
+ icon: "SearchOutlined",
584
+ description: "网页抓取、API 调用、多源信息汇总",
585
+ examples: ["调研 AI Agent 框架对比", "查询实时汇率", "抓取网页内容并翻译"],
586
+ },
587
+ "image-process": {
588
+ icon: "PictureOutlined",
589
+ description: "批量压缩、格式转换、加水印、OCR",
590
+ examples: ["批量压缩到 500KB", "加水印文字", "识别图上的文字 (OCR)"],
591
+ },
592
+ "code-helper": {
593
+ icon: "CodeOutlined",
594
+ description: "生成脚本、调试代码、自动化任务",
595
+ examples: [
596
+ "写一个批量重命名脚本",
597
+ "调试这段报错代码",
598
+ "生成 REST API 脚手架",
599
+ ],
600
+ },
601
+ "system-admin": {
602
+ icon: "DesktopOutlined",
603
+ description: "磁盘分析、进程管理、日志分析",
604
+ examples: [
605
+ "查看磁盘使用情况",
606
+ "找出最大的 10 个文件",
607
+ "列出占用端口的进程",
608
+ ],
609
+ },
610
+ "file-organize": {
611
+ icon: "FolderOpenOutlined",
612
+ description: "批量重命名、分类整理、查找重复",
613
+ examples: [
614
+ "按文件类型分类整理",
615
+ "批量重命名去空格",
616
+ "打包排除 node_modules",
617
+ ],
618
+ },
619
+ "network-tools": {
620
+ icon: "GlobalOutlined",
621
+ description: "API 调试、网页抓取、网络诊断",
622
+ examples: ["测试 API 接口", "抓取网页图片链接", "ping 测试网络延迟"],
623
+ },
624
+ "learning-assist": {
625
+ icon: "ReadOutlined",
626
+ description: "文档翻译、内容总结、论文分析",
627
+ examples: ["翻译 PDF 摘要", "总结长文档要点", "解释代码工作原理"],
628
+ },
629
+ "code-review": {
630
+ icon: "SafetyCertificateOutlined",
631
+ description: "多视角评审(性能 / 安全 / 可维护性),综合裁决",
632
+ examples: [
633
+ "评审这个函数的安全问题",
634
+ "从性能角度审查这段代码",
635
+ "评审 PR 的可维护性",
636
+ ],
637
+ },
638
+ };
639
+
640
+ /**
641
+ * Get all templates formatted for UI consumption.
642
+ * Returns an array of template objects with id, name, icon, category,
643
+ * description, examples, acceptsFiles, and optional shellPolicyOverrides.
644
+ * @returns {object[]}
645
+ */
646
+ export function getTemplatesForUI() {
647
+ const builtIn = Object.values(TASK_TEMPLATES).map((tpl) => {
648
+ const ui = UI_METADATA[tpl.id] || {};
649
+ return { tpl, ui, source: "builtin" };
650
+ });
651
+ const userInstalled = Object.values(_userTemplates).map((tpl) => ({
652
+ tpl,
653
+ // User templates carry their own ui metadata inline
654
+ ui: { icon: tpl.icon, description: tpl.description, examples: tpl.examples },
655
+ source: "user",
656
+ }));
657
+ return [...builtIn, ...userInstalled].map(({ tpl, ui, source }) => {
658
+ return {
659
+ id: tpl.id,
660
+ name: tpl.name,
661
+ icon: ui.icon || "AppstoreOutlined",
662
+ category: tpl.category,
663
+ description: ui.description || "",
664
+ examples: ui.examples || [],
665
+ source,
666
+ acceptsFiles: tpl.acceptsFiles,
667
+ parallelStrategy: tpl.parallelStrategy || "none",
668
+ mode: tpl.mode || "agent",
669
+ ...(tpl.debatePerspectives
670
+ ? { debatePerspectives: tpl.debatePerspectives }
671
+ : {}),
672
+ ...(tpl.shellPolicyOverrides
673
+ ? { shellPolicyOverrides: tpl.shellPolicyOverrides }
674
+ : {}),
675
+ ...(Array.isArray(tpl.mcpServers) && tpl.mcpServers.length > 0
676
+ ? {
677
+ mcpServers: tpl.mcpServers.map((s) => ({
678
+ name: s.name,
679
+ command: s.command,
680
+ args: Array.isArray(s.args) ? s.args : [],
681
+ })),
682
+ }
683
+ : {}),
684
+ };
685
+ });
495
686
  }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Cowork Template Marketplace — share and install Cowork templates via EvoMap.
3
+ *
4
+ * A "Cowork template" is a serializable subset of the full template object
5
+ * (id, name, category, acceptsFiles, mode, systemPromptExtension,
6
+ * parallelStrategy, debatePerspectives, shellPolicyOverrides, mcpServers +
7
+ * UI fields: icon, description, examples).
8
+ *
9
+ * Published templates are wrapped as EvoMap "genes" with `category: "cowork-template"`
10
+ * so they sit alongside other gene types but can be filtered explicitly.
11
+ *
12
+ * Installed templates are persisted to `.chainlesschain/cowork/user-templates/<id>.json`
13
+ * and merged into the built-in template registry by `cowork-task-templates.js`.
14
+ *
15
+ * @module cowork-template-marketplace
16
+ */
17
+
18
+ import {
19
+ existsSync,
20
+ mkdirSync,
21
+ readFileSync,
22
+ writeFileSync,
23
+ readdirSync,
24
+ unlinkSync,
25
+ } from "node:fs";
26
+ import { join } from "node:path";
27
+
28
+ export const _deps = {
29
+ existsSync,
30
+ mkdirSync,
31
+ readFileSync,
32
+ writeFileSync,
33
+ readdirSync,
34
+ unlinkSync,
35
+ // Injected at runtime by CLI to avoid eager evomap-client load
36
+ evomapClient: null,
37
+ };
38
+
39
+ const EVOMAP_CATEGORY = "cowork-template";
40
+
41
+ // ─── Paths ───────────────────────────────────────────────────────────────────
42
+
43
+ function _userTemplatesDir(cwd) {
44
+ return join(cwd, ".chainlesschain", "cowork", "user-templates");
45
+ }
46
+
47
+ function _userTemplateFile(cwd, id) {
48
+ return join(_userTemplatesDir(cwd), `${id}.json`);
49
+ }
50
+
51
+ // ─── Serialization ───────────────────────────────────────────────────────────
52
+
53
+ const SHARED_FIELDS = [
54
+ "id",
55
+ "name",
56
+ "category",
57
+ "acceptsFiles",
58
+ "fileTypes",
59
+ "mode",
60
+ "parallelStrategy",
61
+ "debatePerspectives",
62
+ "systemPromptExtension",
63
+ "shellPolicyOverrides",
64
+ "mcpServers",
65
+ ];
66
+
67
+ const UI_FIELDS = ["icon", "description", "examples"];
68
+
69
+ /**
70
+ * Pick the shareable subset of a template (strips internal fields).
71
+ * @param {object} template - Template object from TASK_TEMPLATES or UI metadata
72
+ * @param {object} [uiMeta] - Optional { icon, description, examples }
73
+ */
74
+ export function toShareableTemplate(template, uiMeta = {}) {
75
+ const out = {};
76
+ for (const f of SHARED_FIELDS) {
77
+ if (template[f] !== undefined) out[f] = template[f];
78
+ }
79
+ for (const f of UI_FIELDS) {
80
+ if (uiMeta[f] !== undefined) out[f] = uiMeta[f];
81
+ }
82
+ return out;
83
+ }
84
+
85
+ /**
86
+ * Build an EvoMap gene payload for a template.
87
+ * @param {object} template - Shareable template (see toShareableTemplate)
88
+ * @param {object} meta - { author, version, description, tags }
89
+ */
90
+ export function buildTemplateGene(template, meta = {}) {
91
+ if (!template?.id) throw new Error("template.id is required");
92
+ if (!template?.name) throw new Error("template.name is required");
93
+ return {
94
+ id: `cowork-template-${template.id}`,
95
+ name: template.name,
96
+ description: meta.description || template.description || template.name,
97
+ category: EVOMAP_CATEGORY,
98
+ author: meta.author || "anonymous",
99
+ version: meta.version || "1.0.0",
100
+ tags: Array.isArray(meta.tags) ? meta.tags : [EVOMAP_CATEGORY],
101
+ content: JSON.stringify(template),
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Extract a template from a downloaded gene payload.
107
+ * Accepts either { gene, content } or a flat gene object.
108
+ */
109
+ export function templateFromGene(payload) {
110
+ if (!payload) throw new Error("empty gene payload");
111
+ const gene = payload.gene || payload;
112
+ const content = payload.content ?? gene.content;
113
+ if (!content || typeof content !== "string") {
114
+ throw new Error("gene is missing `content` string");
115
+ }
116
+ let parsed;
117
+ try {
118
+ parsed = JSON.parse(content);
119
+ } catch (err) {
120
+ throw new Error(`gene content is not valid JSON: ${err.message}`);
121
+ }
122
+ if (!parsed?.id || !parsed?.name) {
123
+ throw new Error("gene content is not a valid cowork template");
124
+ }
125
+ return parsed;
126
+ }
127
+
128
+ // ─── Local persistence ──────────────────────────────────────────────────────
129
+
130
+ /** List all installed user templates (as full objects). */
131
+ export function listUserTemplates(cwd) {
132
+ const dir = _userTemplatesDir(cwd);
133
+ if (!_deps.existsSync(dir)) return [];
134
+ const out = [];
135
+ for (const entry of _deps.readdirSync(dir)) {
136
+ if (!entry.endsWith(".json")) continue;
137
+ try {
138
+ const raw = _deps.readFileSync(join(dir, entry), "utf-8");
139
+ const tpl = JSON.parse(raw);
140
+ if (tpl?.id && tpl?.name) out.push({ ...tpl, source: "user" });
141
+ } catch (_e) {
142
+ // Skip malformed files — don't let one bad template break the list
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+
148
+ /** Save a template to the local user-templates directory. */
149
+ export function saveUserTemplate(cwd, template) {
150
+ if (!template?.id) throw new Error("template.id is required");
151
+ const dir = _userTemplatesDir(cwd);
152
+ _deps.mkdirSync(dir, { recursive: true });
153
+ _deps.writeFileSync(
154
+ _userTemplateFile(cwd, template.id),
155
+ JSON.stringify(template, null, 2),
156
+ "utf-8",
157
+ );
158
+ return template;
159
+ }
160
+
161
+ /** Remove an installed user template. Returns true if removed. */
162
+ export function removeUserTemplate(cwd, id) {
163
+ const file = _userTemplateFile(cwd, id);
164
+ if (!_deps.existsSync(file)) return false;
165
+ _deps.unlinkSync(file);
166
+ return true;
167
+ }
168
+
169
+ // ─── Marketplace operations ──────────────────────────────────────────────────
170
+
171
+ /**
172
+ * Search for templates on an EvoMap hub. Filters to category=cowork-template.
173
+ */
174
+ export async function searchTemplates(query, { limit = 20 } = {}) {
175
+ const client = _deps.evomapClient;
176
+ if (!client) throw new Error("EvoMap client not configured");
177
+ const results = await client.search(query || "", {
178
+ category: EVOMAP_CATEGORY,
179
+ limit,
180
+ });
181
+ return Array.isArray(results) ? results : [];
182
+ }
183
+
184
+ /**
185
+ * Install a template by gene id. Downloads, validates, and saves to local dir.
186
+ * @returns {object} The installed template definition.
187
+ */
188
+ export async function installTemplate(cwd, geneId) {
189
+ const client = _deps.evomapClient;
190
+ if (!client) throw new Error("EvoMap client not configured");
191
+ const payload = await client.download(geneId);
192
+ const template = templateFromGene(payload);
193
+ saveUserTemplate(cwd, template);
194
+ return template;
195
+ }
196
+
197
+ /**
198
+ * Publish a template. Caller provides the shareable template + metadata.
199
+ */
200
+ export async function publishTemplate(template, meta = {}) {
201
+ const client = _deps.evomapClient;
202
+ if (!client) throw new Error("EvoMap client not configured");
203
+ const gene = buildTemplateGene(template, meta);
204
+ return client.publish(gene);
205
+ }