chainlesschain 0.45.81 → 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.
- package/bin/chainlesschain.js +0 -0
- package/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-YdvJBMHH.js → AppLayout-BnvARObz.js} +1 -1
- package/src/assets/web-panel/assets/Cowork-B8ZDdRm4.js +7 -0
- package/src/assets/web-panel/assets/Cowork-CXuhlHew.css +1 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
- package/src/assets/web-panel/assets/{Dashboard-HPh9FcPt.js → Dashboard-jt6XPIjB.js} +2 -2
- package/src/assets/web-panel/assets/{index-ByUk2Wmr.js → index-C1SPm_5l.js} +2 -2
- package/src/assets/web-panel/index.html +1 -1
- package/src/commands/cowork.js +695 -0
- package/src/gateways/ws/action-protocol.js +42 -2
- package/src/lib/cowork-cron.js +474 -0
- package/src/lib/cowork-learning.js +438 -0
- package/src/lib/cowork-mcp-tools.js +182 -0
- package/src/lib/cowork-share.js +218 -0
- package/src/lib/cowork-task-runner.js +317 -3
- package/src/lib/cowork-task-templates.js +101 -13
- package/src/lib/cowork-template-marketplace.js +205 -0
- package/src/lib/cowork-workflow.js +571 -0
- package/src/lib/sub-agent-context.js +40 -0
- package/src/lib/workflow-expr.js +318 -0
- package/src/assets/web-panel/assets/Cowork-BnrHWwZw.js +0 -7
- package/src/assets/web-panel/assets/Cowork-CcSoS3eX.css +0 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
|
@@ -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 (
|
|
472
|
-
return
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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,7 +546,7 @@ ${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)];
|
|
495
550
|
}
|
|
496
551
|
|
|
497
552
|
// ─── UI Metadata ─────────────────────────────────────────────────────────────
|
|
@@ -571,6 +626,15 @@ const UI_METADATA = {
|
|
|
571
626
|
description: "文档翻译、内容总结、论文分析",
|
|
572
627
|
examples: ["翻译 PDF 摘要", "总结长文档要点", "解释代码工作原理"],
|
|
573
628
|
},
|
|
629
|
+
"code-review": {
|
|
630
|
+
icon: "SafetyCertificateOutlined",
|
|
631
|
+
description: "多视角评审(性能 / 安全 / 可维护性),综合裁决",
|
|
632
|
+
examples: [
|
|
633
|
+
"评审这个函数的安全问题",
|
|
634
|
+
"从性能角度审查这段代码",
|
|
635
|
+
"评审 PR 的可维护性",
|
|
636
|
+
],
|
|
637
|
+
},
|
|
574
638
|
};
|
|
575
639
|
|
|
576
640
|
/**
|
|
@@ -580,8 +644,17 @@ const UI_METADATA = {
|
|
|
580
644
|
* @returns {object[]}
|
|
581
645
|
*/
|
|
582
646
|
export function getTemplatesForUI() {
|
|
583
|
-
|
|
647
|
+
const builtIn = Object.values(TASK_TEMPLATES).map((tpl) => {
|
|
584
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 }) => {
|
|
585
658
|
return {
|
|
586
659
|
id: tpl.id,
|
|
587
660
|
name: tpl.name,
|
|
@@ -589,10 +662,25 @@ export function getTemplatesForUI() {
|
|
|
589
662
|
category: tpl.category,
|
|
590
663
|
description: ui.description || "",
|
|
591
664
|
examples: ui.examples || [],
|
|
665
|
+
source,
|
|
592
666
|
acceptsFiles: tpl.acceptsFiles,
|
|
667
|
+
parallelStrategy: tpl.parallelStrategy || "none",
|
|
668
|
+
mode: tpl.mode || "agent",
|
|
669
|
+
...(tpl.debatePerspectives
|
|
670
|
+
? { debatePerspectives: tpl.debatePerspectives }
|
|
671
|
+
: {}),
|
|
593
672
|
...(tpl.shellPolicyOverrides
|
|
594
673
|
? { shellPolicyOverrides: tpl.shellPolicyOverrides }
|
|
595
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
|
+
: {}),
|
|
596
684
|
};
|
|
597
685
|
});
|
|
598
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
|
+
}
|