crewx 0.8.1 → 0.8.2-rc.2
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/README.md +268 -268
- package/bin/cli-commands.js +34 -0
- package/bin/crewx-lib.js +213 -108
- package/bin/crewx-ui.js +83 -83
- package/bin/crewx.js +219 -147
- package/bin/launcher-flags.js +29 -0
- package/bin/package.json +1 -1
- package/dist/assets/MarketPage-DptjaFpT.js +36 -0
- package/dist/assets/{PromptTab-DVKc7hJY.js → PromptTab-DZha2_v1.js} +1 -1
- package/dist/assets/{_baseUniq-wjlVo2E6.js → _baseUniq-jd6NubI3.js} +1 -1
- package/dist/assets/{arc-BfPgRtzW.js → arc-C2te3-8P.js} +1 -1
- package/dist/assets/{architectureDiagram-Q4EWVU46-ewcueFAG.js → architectureDiagram-Q4EWVU46-CbIQua02.js} +1 -1
- package/dist/assets/{blockDiagram-DXYQGD6D-TxlbbvKn.js → blockDiagram-DXYQGD6D-Cg7qkpSM.js} +1 -1
- package/dist/assets/{c4Diagram-AHTNJAMY-C1lT_bl_.js → c4Diagram-AHTNJAMY-BkffDY1F.js} +1 -1
- package/dist/assets/channel-beae0DeI.js +1 -0
- package/dist/assets/chatgpt-logo-dark.svg +15 -15
- package/dist/assets/chatgpt-logo.svg +15 -15
- package/dist/assets/{chunk-4BX2VUAB-C41j2mCL.js → chunk-4BX2VUAB-BxHe9wPE.js} +1 -1
- package/dist/assets/{chunk-4TB4RGXK-HNNsUbz0.js → chunk-4TB4RGXK--f1tN90O.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-qtCgO0r2.js → chunk-55IACEB6-B5QXfPXQ.js} +1 -1
- package/dist/assets/{chunk-EDXVE4YY-BSnDYtsd.js → chunk-EDXVE4YY-DqKhblOg.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-DyHRLQqX.js → chunk-FMBD7UC4-DZ1w_G02.js} +1 -1
- package/dist/assets/{chunk-OYMX7WX6-CCjfi6WS.js → chunk-OYMX7WX6-BqAgQpv8.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-COLty8kd.js → chunk-QZHKN3VN-DPqnGqVi.js} +1 -1
- package/dist/assets/{chunk-YZCP3GAM-CHUUnGeN.js → chunk-YZCP3GAM-x-ZYSQLd.js} +1 -1
- package/dist/assets/classDiagram-6PBFFD2Q-BquWrs1y.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-BquWrs1y.js +1 -0
- package/dist/assets/clone-C9wSPtDN.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-CSip-V2g.js → cose-bilkent-S5V4N54A-4VCNRP-E.js} +1 -1
- package/dist/assets/{dagre-KV5264BT-DkdpnWhv.js → dagre-KV5264BT-DucVi1QS.js} +1 -1
- package/dist/assets/{diagram-5BDNPKRD-PH4qc6PV.js → diagram-5BDNPKRD-ChdRA8bE.js} +1 -1
- package/dist/assets/{diagram-G4DWMVQ6-Cg5xZcjx.js → diagram-G4DWMVQ6-B1-97yHr.js} +1 -1
- package/dist/assets/{diagram-MMDJMWI5-soKmeTCW.js → diagram-MMDJMWI5-CQs3cO7G.js} +1 -1
- package/dist/assets/{diagram-TYMM5635-Daq5Mihu.js → diagram-TYMM5635-CuNCxDfO.js} +1 -1
- package/dist/assets/{erDiagram-SMLLAGMA-kr2OtY0Y.js → erDiagram-SMLLAGMA-DdR8v8g-.js} +1 -1
- package/dist/assets/{flowDiagram-DWJPFMVM-DQZCb8gm.js → flowDiagram-DWJPFMVM-Dt02upId.js} +1 -1
- package/dist/assets/{ganttDiagram-T4ZO3ILL-BHkn485T.js → ganttDiagram-T4ZO3ILL-cG_k9VOa.js} +1 -1
- package/dist/assets/{gitGraphDiagram-UUTBAWPF-FaCyYFmC.js → gitGraphDiagram-UUTBAWPF-Dz1DjhKq.js} +1 -1
- package/dist/assets/{graph-BVJlrP6V.js → graph-CF2NtM33.js} +1 -1
- package/dist/assets/{infoDiagram-42DDH7IO-DJOWkKdM.js → infoDiagram-42DDH7IO-tQWKrYM6.js} +1 -1
- package/dist/assets/{ishikawaDiagram-UXIWVN3A-VfpvNaIf.js → ishikawaDiagram-UXIWVN3A-CLuUMkF0.js} +1 -1
- package/dist/assets/{journeyDiagram-VCZTEJTY-CPzsak-v.js → journeyDiagram-VCZTEJTY-a6JenLCk.js} +1 -1
- package/dist/assets/{kanban-definition-6JOO6SKY-DFqLDBU0.js → kanban-definition-6JOO6SKY-rqOxTzYb.js} +1 -1
- package/dist/assets/{layout-CCSbNPHm.js → layout-Dvic1Hpy.js} +1 -1
- package/dist/assets/{linear-C4T7PCKE.js → linear-CfMV1S6a.js} +1 -1
- package/dist/assets/main-05K4ggqd.css +10 -0
- package/dist/assets/main-CCM1gtr8.js +1165 -0
- package/dist/assets/{min-CGQNEYGh.js → min-GpF3DZux.js} +1 -1
- package/dist/assets/{mindmap-definition-QFDTVHPH-AuU1EqwS.js → mindmap-definition-QFDTVHPH-Cg80z0Jx.js} +1 -1
- package/dist/assets/{pieDiagram-DEJITSTG-CopkCZwp.js → pieDiagram-DEJITSTG-BrtK7lAq.js} +1 -1
- package/dist/assets/{quadrantDiagram-34T5L4WZ-lMKrSv_t.js → quadrantDiagram-34T5L4WZ-BL2txAAS.js} +1 -1
- package/dist/assets/{requirementDiagram-MS252O5E-dWUpHOFb.js → requirementDiagram-MS252O5E-Co3wpBnu.js} +1 -1
- package/dist/assets/{sankeyDiagram-XADWPNL6-C8UQx9Bb.js → sankeyDiagram-XADWPNL6-B4KJXdQ4.js} +1 -1
- package/dist/assets/{sequenceDiagram-FGHM5R23-CUVNIItJ.js → sequenceDiagram-FGHM5R23-xs5OuzvV.js} +1 -1
- package/dist/assets/{stateDiagram-FHFEXIEX-Ct0GamGl.js → stateDiagram-FHFEXIEX-bbEP20JD.js} +1 -1
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-XYh9U1A7.js +1 -0
- package/dist/assets/{timeline-definition-GMOUNBTQ-ul8Po7f7.js → timeline-definition-GMOUNBTQ-CTc2wVwC.js} +1 -1
- package/dist/assets/{vennDiagram-DHZGUBPP-B4AOWQnP.js → vennDiagram-DHZGUBPP-oJpXott7.js} +1 -1
- package/dist/assets/{wardley-RL74JXVD-Dr7Wp3AJ.js → wardley-RL74JXVD-aTmOXkKh.js} +1 -1
- package/dist/assets/{wardleyDiagram-NUSXRM2D-Ck70faXX.js → wardleyDiagram-NUSXRM2D-C83SOkig.js} +1 -1
- package/dist/assets/{xychartDiagram-5P7HB3ND-Bsy5-cNt.js → xychartDiagram-5P7HB3ND-BScws0tF.js} +1 -1
- package/dist/index.html +13 -13
- package/dist-electron/main.js +153 -116
- package/dist-electron/overlay.js +102 -65
- package/dist-electron/package.json +1 -0
- package/dist-electron/preload.js +8 -8
- package/dist-server/bootstrap/crewx-server.js +19 -10
- package/dist-server/domain/agent/agent.service.js +12 -0
- package/dist-server/domain/agent/dto/update-agent.dto.js +20 -1
- package/dist-server/domain/mcp/crewx-tool.factory.js +44 -113
- package/dist-server/domain/mcp/mcp.module.js +2 -0
- package/dist-server/domain/mcp/mcp.service.js +37 -12
- package/dist-server/domain/message/message.service.js +21 -13
- package/dist-server/domain/skill/skill.service.js +63 -43
- package/dist-server/domain/task/task.module.js +2 -0
- package/dist-server/domain/task/task.service.js +17 -10
- package/dist-server/domain/thread/dto/update-thread.dto.js +23 -0
- package/dist-server/domain/thread/thread.controller.js +16 -0
- package/dist-server/domain/thread/thread.service.js +9 -0
- package/dist-server/main.js +1 -1
- package/dist-server/modules/crewx.module.js +16 -1
- package/dist-server/repository/box.repository.js +20 -20
- package/dist-server/repository/project.repository.js +13 -13
- package/dist-server/repository/request-log.repository.js +10 -10
- package/dist-server/repository/task.repository.js +72 -72
- package/dist-server/repository/thread.repository.js +78 -58
- package/package.json +6 -6
- package/packages/cli/dist/bootstrap/crewx-cli.js +12 -0
- package/packages/cli/dist/commands/agent.js +23 -23
- package/packages/cli/dist/commands/init.js +19 -19
- package/packages/cli/dist/commands/parse-common-flags.d.ts +19 -3
- package/packages/cli/dist/commands/parse-common-flags.js +46 -6
- package/packages/cli/dist/commands/registry.d.ts +13 -0
- package/packages/cli/dist/commands/registry.js +29 -0
- package/packages/cli/dist/commands/task-db.js +7 -7
- package/packages/cli/dist/examples/deny-secrets-plugin.d.ts +22 -0
- package/packages/cli/dist/examples/deny-secrets-plugin.js +40 -0
- package/packages/cli/dist/main.js +134 -68
- package/packages/cli/dist/plugins/examples/echo-hook.d.ts +24 -0
- package/packages/cli/dist/plugins/examples/echo-hook.js +60 -0
- package/packages/cli/dist/plugins/examples/verify-echo-hook.d.ts +8 -0
- package/packages/cli/dist/plugins/examples/verify-echo-hook.js +47 -0
- package/packages/cli/dist/plugins/sqlite-tracing.d.ts +11 -0
- package/packages/cli/dist/plugins/sqlite-tracing.js +19 -0
- package/packages/cli/dist/schema/tasks.d.ts +7 -0
- package/packages/cli/dist/schema/tasks.js +48 -0
- package/packages/cli/package.json +52 -52
- package/scripts/analyze-task-logs.mjs +569 -0
- package/scripts/build-manual.mjs +266 -266
- package/scripts/emit-dist-server-package-json.mjs +7 -7
- package/scripts/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/scripts/postinstall.mjs +44 -44
- package/scripts/smoke-tarball.mjs +285 -285
- package/scripts/snapshot-msg-list.sh +52 -52
- package/server.js +167 -164
- package/dist/assets/MarketPage-Dwsg6K-B.js +0 -31
- package/dist/assets/channel-BP4PNMmz.js +0 -1
- package/dist/assets/classDiagram-6PBFFD2Q-Upr3UAcM.js +0 -1
- package/dist/assets/classDiagram-v2-HSJHXN6E-Upr3UAcM.js +0 -1
- package/dist/assets/clone-B8BP7ReZ.js +0 -1
- package/dist/assets/main-CELBpK6r.js +0 -1166
- package/dist/assets/main-CmP-VosD.css +0 -10
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-CFBLQDEx.js +0 -1
- package/dist-server/domain/task/dto/project-usage.dto.js +0 -38
- package/dist-server/domain/thread/dto/send-message.dto.js +0 -10
|
@@ -29,12 +29,12 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
29
29
|
? `WHERE (workspace_id = ? OR workspace_id IS NULL)`
|
|
30
30
|
: '';
|
|
31
31
|
const queryParams = workspaceId ? [workspaceId] : [];
|
|
32
|
-
const fetched = db.all(`
|
|
33
|
-
SELECT id, workspace_id, platform, title, first_message, last_message,
|
|
34
|
-
message_count, created_at, updated_at, metadata
|
|
35
|
-
FROM threads
|
|
36
|
-
${whereClause}
|
|
37
|
-
ORDER BY updated_at DESC
|
|
32
|
+
const fetched = db.all(`
|
|
33
|
+
SELECT id, workspace_id, platform, title, first_message, last_message,
|
|
34
|
+
message_count, created_at, updated_at, metadata
|
|
35
|
+
FROM threads
|
|
36
|
+
${whereClause}
|
|
37
|
+
ORDER BY updated_at DESC
|
|
38
38
|
`, queryParams);
|
|
39
39
|
for (const row of fetched) {
|
|
40
40
|
if (!seenIds.has(row.id)) {
|
|
@@ -66,10 +66,10 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
66
66
|
continue;
|
|
67
67
|
const wsFilter = workspaceId ? ` AND (workspace_id = ? OR workspace_id IS NULL)` : '';
|
|
68
68
|
const params = workspaceId ? [threadId, workspaceId] : [threadId];
|
|
69
|
-
const row = db.get(`
|
|
70
|
-
SELECT id, workspace_id, platform, title, first_message, last_message,
|
|
71
|
-
message_count, created_at, updated_at, metadata
|
|
72
|
-
FROM threads WHERE id = ?${wsFilter}
|
|
69
|
+
const row = db.get(`
|
|
70
|
+
SELECT id, workspace_id, platform, title, first_message, last_message,
|
|
71
|
+
message_count, created_at, updated_at, metadata
|
|
72
|
+
FROM threads WHERE id = ?${wsFilter}
|
|
73
73
|
`, params);
|
|
74
74
|
if (row)
|
|
75
75
|
return row;
|
|
@@ -129,22 +129,22 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
129
129
|
continue;
|
|
130
130
|
const wsFilter = workspaceId ? ` AND workspace_id = ?` : '';
|
|
131
131
|
const statsParams = workspaceId ? [threadId, workspaceId] : [threadId];
|
|
132
|
-
const stats = db.get(`
|
|
133
|
-
SELECT COUNT(*) as cnt,
|
|
134
|
-
COALESCE(SUM(input_tokens), 0) as total_input,
|
|
135
|
-
COALESCE(SUM(output_tokens), 0) as total_output,
|
|
136
|
-
COALESCE(SUM(cost_usd), 0) as total_cost
|
|
137
|
-
FROM tasks
|
|
138
|
-
WHERE thread_id = ? AND (parent_task_id IS NULL OR parent_task_id = '')${wsFilter}
|
|
132
|
+
const stats = db.get(`
|
|
133
|
+
SELECT COUNT(*) as cnt,
|
|
134
|
+
COALESCE(SUM(input_tokens), 0) as total_input,
|
|
135
|
+
COALESCE(SUM(output_tokens), 0) as total_output,
|
|
136
|
+
COALESCE(SUM(cost_usd), 0) as total_cost
|
|
137
|
+
FROM tasks
|
|
138
|
+
WHERE thread_id = ? AND (parent_task_id IS NULL OR parent_task_id = '')${wsFilter}
|
|
139
139
|
`, statsParams);
|
|
140
140
|
taskCount += stats.cnt;
|
|
141
141
|
inputTokens += stats.total_input;
|
|
142
142
|
outputTokens += stats.total_output;
|
|
143
143
|
costUsd += stats.total_cost;
|
|
144
|
-
const agentRows = db.all(`
|
|
145
|
-
SELECT DISTINCT agent_id FROM tasks
|
|
146
|
-
WHERE thread_id = ? AND agent_id IS NOT NULL AND agent_id != ''
|
|
147
|
-
AND (parent_task_id IS NULL OR parent_task_id = '')${wsFilter}
|
|
144
|
+
const agentRows = db.all(`
|
|
145
|
+
SELECT DISTINCT agent_id FROM tasks
|
|
146
|
+
WHERE thread_id = ? AND agent_id IS NOT NULL AND agent_id != ''
|
|
147
|
+
AND (parent_task_id IS NULL OR parent_task_id = '')${wsFilter}
|
|
148
148
|
`, statsParams);
|
|
149
149
|
for (const row of agentRows) {
|
|
150
150
|
agents.add(row.agent_id);
|
|
@@ -175,13 +175,13 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
175
175
|
continue;
|
|
176
176
|
const wsFilter = workspaceId ? ` AND workspace_id = ?` : '';
|
|
177
177
|
const taskParams = workspaceId ? [threadId, workspaceId] : [threadId];
|
|
178
|
-
const tasks = db.all(`
|
|
179
|
-
SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
180
|
-
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
181
|
-
error, parent_task_id, caller_agent_id
|
|
182
|
-
FROM tasks
|
|
183
|
-
WHERE thread_id = ? AND (parent_task_id IS NULL OR parent_task_id = '')${wsFilter}
|
|
184
|
-
ORDER BY started_at ASC
|
|
178
|
+
const tasks = db.all(`
|
|
179
|
+
SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
180
|
+
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
181
|
+
error, parent_task_id, caller_agent_id
|
|
182
|
+
FROM tasks
|
|
183
|
+
WHERE thread_id = ? AND (parent_task_id IS NULL OR parent_task_id = '')${wsFilter}
|
|
184
|
+
ORDER BY started_at ASC
|
|
185
185
|
`, taskParams);
|
|
186
186
|
for (const t of tasks) {
|
|
187
187
|
if (!seenIds.has(t.id)) {
|
|
@@ -215,12 +215,12 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
215
215
|
continue;
|
|
216
216
|
const wsFilter = workspaceId ? ` AND workspace_id = ?` : '';
|
|
217
217
|
const taskParams = workspaceId ? [threadId, workspaceId] : [threadId];
|
|
218
|
-
const tasks = db.all(`
|
|
219
|
-
SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
220
|
-
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
221
|
-
error, parent_task_id, caller_agent_id
|
|
222
|
-
FROM tasks WHERE thread_id = ?${wsFilter}
|
|
223
|
-
ORDER BY started_at ASC
|
|
218
|
+
const tasks = db.all(`
|
|
219
|
+
SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
220
|
+
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
221
|
+
error, parent_task_id, caller_agent_id
|
|
222
|
+
FROM tasks WHERE thread_id = ?${wsFilter}
|
|
223
|
+
ORDER BY started_at ASC
|
|
224
224
|
`, taskParams);
|
|
225
225
|
for (const t of tasks) {
|
|
226
226
|
if (!seenIds.has(t.id)) {
|
|
@@ -250,17 +250,17 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
250
250
|
try {
|
|
251
251
|
const wsFilter = workspaceId ? ` AND workspace_id = ?` : '';
|
|
252
252
|
const findParams = workspaceId ? [taskId, threadId, workspaceId] : [taskId, threadId];
|
|
253
|
-
const row = db.get(`
|
|
254
|
-
SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
255
|
-
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
256
|
-
error, parent_task_id, caller_agent_id
|
|
257
|
-
FROM tasks WHERE id = ? AND thread_id = ?${wsFilter}
|
|
253
|
+
const row = db.get(`
|
|
254
|
+
SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
255
|
+
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
256
|
+
error, parent_task_id, caller_agent_id
|
|
257
|
+
FROM tasks WHERE id = ? AND thread_id = ?${wsFilter}
|
|
258
258
|
`, findParams);
|
|
259
259
|
if (!row)
|
|
260
260
|
continue;
|
|
261
|
-
const children = db.all(`SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
262
|
-
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
263
|
-
error, parent_task_id, caller_agent_id
|
|
261
|
+
const children = db.all(`SELECT id, agent_id, prompt, result, status, started_at, completed_at,
|
|
262
|
+
duration_ms, thread_id, logs, input_tokens, output_tokens, cost_usd,
|
|
263
|
+
error, parent_task_id, caller_agent_id
|
|
264
264
|
FROM tasks WHERE parent_task_id = ? ORDER BY started_at ASC`, [row.id]);
|
|
265
265
|
return { task: row, children };
|
|
266
266
|
}
|
|
@@ -287,13 +287,13 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
287
287
|
continue;
|
|
288
288
|
const db = this.openReadonly(dbPath);
|
|
289
289
|
try {
|
|
290
|
-
const rows = db.all(`
|
|
291
|
-
SELECT id, agent_id, status, started_at, completed_at,
|
|
292
|
-
duration_ms, thread_id, input_tokens, output_tokens, cost_usd, error
|
|
293
|
-
FROM tasks
|
|
294
|
-
WHERE thread_id IN (${placeholders})
|
|
295
|
-
AND (parent_task_id IS NULL OR parent_task_id = '')
|
|
296
|
-
ORDER BY started_at ASC
|
|
290
|
+
const rows = db.all(`
|
|
291
|
+
SELECT id, agent_id, status, started_at, completed_at,
|
|
292
|
+
duration_ms, thread_id, input_tokens, output_tokens, cost_usd, error
|
|
293
|
+
FROM tasks
|
|
294
|
+
WHERE thread_id IN (${placeholders})
|
|
295
|
+
AND (parent_task_id IS NULL OR parent_task_id = '')
|
|
296
|
+
ORDER BY started_at ASC
|
|
297
297
|
`, threadIds);
|
|
298
298
|
for (const row of rows) {
|
|
299
299
|
const tid = row.thread_id;
|
|
@@ -313,6 +313,26 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
313
313
|
}
|
|
314
314
|
return result;
|
|
315
315
|
}
|
|
316
|
+
/** Update thread title and set title_locked=1 to prevent auto-overwrite.
|
|
317
|
+
* Returns true if the thread was found and updated, false if not found. */
|
|
318
|
+
updateThreadTitle(threadId, title, workspaceId) {
|
|
319
|
+
const dbPath = this.resolveDbPath();
|
|
320
|
+
if (!(0, fs_1.existsSync)(dbPath))
|
|
321
|
+
return false;
|
|
322
|
+
const db = this.openReadWrite(dbPath);
|
|
323
|
+
try {
|
|
324
|
+
const wsFilter = workspaceId ? ` AND (workspace_id = ? OR workspace_id IS NULL)` : '';
|
|
325
|
+
const params = workspaceId ? [threadId, workspaceId] : [threadId];
|
|
326
|
+
const existing = db.get(`SELECT id FROM threads WHERE id = ?${wsFilter}`, params);
|
|
327
|
+
if (!existing)
|
|
328
|
+
return false;
|
|
329
|
+
db.run(`UPDATE threads SET title = ?, title_locked = 1, updated_at = datetime('now') WHERE id = ?`, [title, threadId]);
|
|
330
|
+
return true;
|
|
331
|
+
}
|
|
332
|
+
finally {
|
|
333
|
+
db.close();
|
|
334
|
+
}
|
|
335
|
+
}
|
|
316
336
|
/** UPSERT thread (used by MSG.SEND).
|
|
317
337
|
* workspace_id is required on INSERT to prevent NULL workspace rows. */
|
|
318
338
|
upsertThread(threadId, opts) {
|
|
@@ -323,18 +343,18 @@ let ThreadRepository = class ThreadRepository extends base_sqlite_repository_js_
|
|
|
323
343
|
try {
|
|
324
344
|
const existing = db.get('SELECT id, message_count FROM threads WHERE id = ?', [threadId]);
|
|
325
345
|
if (existing) {
|
|
326
|
-
db.run(`
|
|
327
|
-
UPDATE threads
|
|
328
|
-
SET updated_at = datetime('now'), message_count = message_count + 1
|
|
329
|
-
WHERE id = ?
|
|
346
|
+
db.run(`
|
|
347
|
+
UPDATE threads
|
|
348
|
+
SET updated_at = datetime('now'), message_count = message_count + 1
|
|
349
|
+
WHERE id = ?
|
|
330
350
|
`, [threadId]);
|
|
331
351
|
}
|
|
332
352
|
else {
|
|
333
|
-
db.run(`
|
|
334
|
-
INSERT OR REPLACE INTO threads
|
|
335
|
-
(id, workspace_id, platform, title, first_message, message_count, created_at, updated_at)
|
|
336
|
-
VALUES (?, ?,
|
|
337
|
-
`, [threadId, opts.workspaceId ?? null, opts.title, opts.firstMessage]);
|
|
353
|
+
db.run(`
|
|
354
|
+
INSERT OR REPLACE INTO threads
|
|
355
|
+
(id, workspace_id, platform, title, first_message, message_count, created_at, updated_at)
|
|
356
|
+
VALUES (?, ?, ?, ?, ?, 1, datetime('now'), datetime('now'))
|
|
357
|
+
`, [threadId, opts.workspaceId ?? null, opts.platform ?? 'cli', opts.title, opts.firstMessage]);
|
|
338
358
|
}
|
|
339
359
|
}
|
|
340
360
|
finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crewx",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2-rc.2",
|
|
4
4
|
"description": "CrewX — AI agent team dashboard with Electron UI and CLI (Web + Electron + Global CLI)",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|
|
@@ -97,17 +97,17 @@
|
|
|
97
97
|
"yargs": "^17.7.0",
|
|
98
98
|
"zod": "^3.22.0",
|
|
99
99
|
"zustand": "^4.5.0",
|
|
100
|
+
"@crewx/cli": "0.8.2-rc.2",
|
|
100
101
|
"@crewx/cron": "0.1.7",
|
|
101
|
-
"@crewx/cli": "0.8.1",
|
|
102
102
|
"@crewx/doc": "0.1.7",
|
|
103
103
|
"@crewx/knowledge-core": "0.1.5",
|
|
104
104
|
"@crewx/memory": "0.1.9",
|
|
105
|
+
"@crewx/sdk": "0.8.2-rc.2",
|
|
105
106
|
"@crewx/search": "0.1.8",
|
|
106
107
|
"@crewx/shared": "0.0.4",
|
|
107
|
-
"@crewx/
|
|
108
|
-
"@crewx/sdk": "0.8.1",
|
|
108
|
+
"@crewx/skill": "0.1.7",
|
|
109
109
|
"@crewx/wbs": "0.1.8",
|
|
110
|
-
"@crewx/
|
|
110
|
+
"@crewx/workflow": "0.3.8"
|
|
111
111
|
},
|
|
112
112
|
"devDependencies": {
|
|
113
113
|
"@nestjs/cli": "^11.0.16",
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
"typescript": "^5.3.3",
|
|
146
146
|
"vite": "^5.0.10",
|
|
147
147
|
"vite-plugin-electron": "^0.29.0",
|
|
148
|
-
"vitest": "^
|
|
148
|
+
"vitest": "^3.2.4"
|
|
149
149
|
},
|
|
150
150
|
"optionalDependencies": {
|
|
151
151
|
"electron": "^39.2.7"
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.createCliCrewx = createCliCrewx;
|
|
4
7
|
const fs_1 = require("fs");
|
|
5
8
|
const path_1 = require("path");
|
|
9
|
+
const os_1 = require("os");
|
|
10
|
+
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
6
11
|
const sdk_1 = require("@crewx/sdk");
|
|
7
12
|
const plugins_1 = require("@crewx/sdk/plugins");
|
|
8
13
|
const register_builtin_tools_1 = require("../register-builtin-tools");
|
|
9
14
|
const version_1 = require("../utils/version");
|
|
15
|
+
const tasks_1 = require("../schema/tasks");
|
|
10
16
|
/**
|
|
11
17
|
* Build a Crewx instance with CLI-standard plugins (FileLogger + SqliteTracing)
|
|
12
18
|
* and built-in tools registered. Use this from any CLI command that needs a
|
|
@@ -39,6 +45,12 @@ async function createCliCrewx(configPath = process.env.CREWX_CONFIG ?? 'crewx.ya
|
|
|
39
45
|
// No explicit config and file doesn't exist — built-in-only mode
|
|
40
46
|
yamlPath = undefined;
|
|
41
47
|
}
|
|
48
|
+
// Ensure ~/.crewx/crewx.db schema exists before SqliteTracingPlugin opens it.
|
|
49
|
+
const dbDir = (0, path_1.join)((0, os_1.homedir)(), '.crewx');
|
|
50
|
+
(0, fs_1.mkdirSync)(dbDir, { recursive: true });
|
|
51
|
+
const schemaDb = new better_sqlite3_1.default((0, path_1.join)(dbDir, 'crewx.db'));
|
|
52
|
+
(0, tasks_1.ensureTasksSchema)(schemaDb);
|
|
53
|
+
schemaDb.close();
|
|
42
54
|
// Pass ourselves back to the SDK: when this Crewx encounters a file:// remote
|
|
43
55
|
// agent, the SDK uses this factory to bootstrap the target Crewx instance
|
|
44
56
|
// with the same plugin set (FileLogger + SqliteTracing + built-in tools).
|
|
@@ -219,28 +219,28 @@ function loadAgentSkills() {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
function printAgentHelp() {
|
|
222
|
-
console.log(`
|
|
223
|
-
CrewX Agent Management
|
|
224
|
-
|
|
225
|
-
Usage:
|
|
226
|
-
crewx agent # List configured agents (default)
|
|
227
|
-
crewx agent ls # List configured agents
|
|
228
|
-
crewx agent list # Alias for ls
|
|
229
|
-
crewx agent prompt <id> # Inspect rendered agent prompt
|
|
230
|
-
|
|
231
|
-
Filter Options (for agent ls):
|
|
232
|
-
--role <value> Filter by agent role (comma-separated for multiple: PM,Dev)
|
|
233
|
-
--team <value> Filter by agent team (comma-separated for multiple)
|
|
234
|
-
--provider <value> Filter by provider (comma-separated, partial match: claude)
|
|
235
|
-
|
|
236
|
-
Examples:
|
|
237
|
-
crewx agent
|
|
238
|
-
crewx agent ls
|
|
239
|
-
crewx agent ls --role=PM
|
|
240
|
-
crewx agent ls --team="CrewX Core 개발팀"
|
|
241
|
-
crewx agent ls --provider=claude
|
|
242
|
-
crewx agent ls --role=PM --provider=claude
|
|
243
|
-
crewx agent ls --role=PM,general
|
|
244
|
-
CREWX_CONFIG=./crewx.yaml crewx agent list
|
|
222
|
+
console.log(`
|
|
223
|
+
CrewX Agent Management
|
|
224
|
+
|
|
225
|
+
Usage:
|
|
226
|
+
crewx agent # List configured agents (default)
|
|
227
|
+
crewx agent ls # List configured agents
|
|
228
|
+
crewx agent list # Alias for ls
|
|
229
|
+
crewx agent prompt <id> # Inspect rendered agent prompt
|
|
230
|
+
|
|
231
|
+
Filter Options (for agent ls):
|
|
232
|
+
--role <value> Filter by agent role (comma-separated for multiple: PM,Dev)
|
|
233
|
+
--team <value> Filter by agent team (comma-separated for multiple)
|
|
234
|
+
--provider <value> Filter by provider (comma-separated, partial match: claude)
|
|
235
|
+
|
|
236
|
+
Examples:
|
|
237
|
+
crewx agent
|
|
238
|
+
crewx agent ls
|
|
239
|
+
crewx agent ls --role=PM
|
|
240
|
+
crewx agent ls --team="CrewX Core 개발팀"
|
|
241
|
+
crewx agent ls --provider=claude
|
|
242
|
+
crewx agent ls --role=PM --provider=claude
|
|
243
|
+
crewx agent ls --role=PM,general
|
|
244
|
+
CREWX_CONFIG=./crewx.yaml crewx agent list
|
|
245
245
|
`.trim());
|
|
246
246
|
}
|
|
@@ -56,27 +56,27 @@ function escapeYamlBlock(text) {
|
|
|
56
56
|
}
|
|
57
57
|
function generateDefaultYaml(agents) {
|
|
58
58
|
const agentBlocks = agents
|
|
59
|
-
.map((a) => ` - id: "${a.id}"
|
|
60
|
-
name: "${a.name}"
|
|
61
|
-
role: "${a.role}"
|
|
62
|
-
team: "${a.team}"
|
|
63
|
-
provider: "${a.provider}"
|
|
64
|
-
default_model: "${a.default_model}"
|
|
65
|
-
working_directory: "${a.working_directory}"
|
|
66
|
-
description: "${a.description}"
|
|
67
|
-
system_prompt: |
|
|
59
|
+
.map((a) => ` - id: "${a.id}"
|
|
60
|
+
name: "${a.name}"
|
|
61
|
+
role: "${a.role}"
|
|
62
|
+
team: "${a.team}"
|
|
63
|
+
provider: "${a.provider}"
|
|
64
|
+
default_model: "${a.default_model}"
|
|
65
|
+
working_directory: "${a.working_directory}"
|
|
66
|
+
description: "${a.description}"
|
|
67
|
+
system_prompt: |
|
|
68
68
|
${escapeYamlBlock(a.system_prompt)}`)
|
|
69
69
|
.join('\n\n');
|
|
70
|
-
return `# CrewX Agents Configuration
|
|
71
|
-
# Generated by 'crewx init'
|
|
72
|
-
|
|
73
|
-
agents:
|
|
74
|
-
${agentBlocks}
|
|
75
|
-
|
|
76
|
-
# Usage examples:
|
|
77
|
-
# crewx query "@planner analyze this codebase"
|
|
78
|
-
# crewx execute "@developer implement the feature described above"
|
|
79
|
-
# crewx agent ls
|
|
70
|
+
return `# CrewX Agents Configuration
|
|
71
|
+
# Generated by 'crewx init'
|
|
72
|
+
|
|
73
|
+
agents:
|
|
74
|
+
${agentBlocks}
|
|
75
|
+
|
|
76
|
+
# Usage examples:
|
|
77
|
+
# crewx query "@planner analyze this codebase"
|
|
78
|
+
# crewx execute "@developer implement the feature described above"
|
|
79
|
+
# crewx agent ls
|
|
80
80
|
`;
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Supports both `--flag=value` and `--flag value` forms.
|
|
5
5
|
* Handles: --thread, --provider, --metadata, --verbose, --config/-c,
|
|
6
|
-
* --output-format, --effort.
|
|
6
|
+
* --output-format, --effort, --prompt-file/-f.
|
|
7
|
+
*
|
|
8
|
+
* Strict mode: unknown --xxx tokens after known flags are consumed throw an
|
|
9
|
+
* error instead of silently leaking into the message prompt.
|
|
10
|
+
* Use `--` sentinel to pass literal flag-like tokens as positional args.
|
|
7
11
|
*/
|
|
8
12
|
export interface CommonFlags {
|
|
9
13
|
/** Thread name for conversation continuity. */
|
|
@@ -22,12 +26,24 @@ export interface CommonFlags {
|
|
|
22
26
|
effort?: string;
|
|
23
27
|
/** Path to a file whose content is used as the prompt body (-f/--prompt-file). */
|
|
24
28
|
promptFile?: string;
|
|
25
|
-
/** Remaining non-flag arguments. */
|
|
29
|
+
/** Remaining non-flag positional arguments. */
|
|
26
30
|
rest: string[];
|
|
27
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Thrown by parseCommonFlags when an unrecognized --xxx flag is encountered.
|
|
34
|
+
* Callers can distinguish this from other errors to map to exit code 2.
|
|
35
|
+
*/
|
|
36
|
+
export declare class UnknownOptionError extends Error {
|
|
37
|
+
constructor(message: string);
|
|
38
|
+
}
|
|
28
39
|
/**
|
|
29
40
|
* Parse common CLI flags from an args array.
|
|
30
|
-
*
|
|
41
|
+
*
|
|
42
|
+
* Strict mode: any unconsumed `--xxx` token (outside a `--` escape block)
|
|
43
|
+
* throws `UnknownOptionError: Unknown option: --xxx.`
|
|
44
|
+
*
|
|
45
|
+
* Tokens after `--` are treated as positional and included in `rest` as-is.
|
|
46
|
+
* The `--` sentinel itself is NOT included in `rest`.
|
|
31
47
|
*/
|
|
32
48
|
export declare function parseCommonFlags(args: string[]): CommonFlags;
|
|
33
49
|
/**
|
|
@@ -4,9 +4,14 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Supports both `--flag=value` and `--flag value` forms.
|
|
6
6
|
* Handles: --thread, --provider, --metadata, --verbose, --config/-c,
|
|
7
|
-
* --output-format, --effort.
|
|
7
|
+
* --output-format, --effort, --prompt-file/-f.
|
|
8
|
+
*
|
|
9
|
+
* Strict mode: unknown --xxx tokens after known flags are consumed throw an
|
|
10
|
+
* error instead of silently leaking into the message prompt.
|
|
11
|
+
* Use `--` sentinel to pass literal flag-like tokens as positional args.
|
|
8
12
|
*/
|
|
9
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.UnknownOptionError = void 0;
|
|
10
15
|
exports.parseCommonFlags = parseCommonFlags;
|
|
11
16
|
exports.parseMetadata = parseMetadata;
|
|
12
17
|
/**
|
|
@@ -35,9 +40,25 @@ function parseFlag(args, ...names) {
|
|
|
35
40
|
function hasFlag(args, ...names) {
|
|
36
41
|
return names.some(name => args.includes(name));
|
|
37
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Thrown by parseCommonFlags when an unrecognized --xxx flag is encountered.
|
|
45
|
+
* Callers can distinguish this from other errors to map to exit code 2.
|
|
46
|
+
*/
|
|
47
|
+
class UnknownOptionError extends Error {
|
|
48
|
+
constructor(message) {
|
|
49
|
+
super(message);
|
|
50
|
+
this.name = 'UnknownOptionError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.UnknownOptionError = UnknownOptionError;
|
|
38
54
|
/**
|
|
39
55
|
* Parse common CLI flags from an args array.
|
|
40
|
-
*
|
|
56
|
+
*
|
|
57
|
+
* Strict mode: any unconsumed `--xxx` token (outside a `--` escape block)
|
|
58
|
+
* throws `UnknownOptionError: Unknown option: --xxx.`
|
|
59
|
+
*
|
|
60
|
+
* Tokens after `--` are treated as positional and included in `rest` as-is.
|
|
61
|
+
* The `--` sentinel itself is NOT included in `rest`.
|
|
41
62
|
*/
|
|
42
63
|
function parseCommonFlags(args) {
|
|
43
64
|
const thread = parseFlag(args, '--thread');
|
|
@@ -48,7 +69,7 @@ function parseCommonFlags(args) {
|
|
|
48
69
|
const effort = parseFlag(args, '--effort');
|
|
49
70
|
const promptFile = parseFlag(args, '--prompt-file', '-f');
|
|
50
71
|
const verbose = hasFlag(args, '--verbose');
|
|
51
|
-
// Collect
|
|
72
|
+
// Collect consumed positions for known flags
|
|
52
73
|
const consumed = new Set();
|
|
53
74
|
const flagPairs = [
|
|
54
75
|
{ names: ['--thread'] },
|
|
@@ -61,12 +82,10 @@ function parseCommonFlags(args) {
|
|
|
61
82
|
];
|
|
62
83
|
for (let i = 0; i < args.length; i++) {
|
|
63
84
|
const arg = args[i];
|
|
64
|
-
// Boolean flags
|
|
65
85
|
if (arg === '--verbose') {
|
|
66
86
|
consumed.add(i);
|
|
67
87
|
continue;
|
|
68
88
|
}
|
|
69
|
-
// Value flags
|
|
70
89
|
for (const { names } of flagPairs) {
|
|
71
90
|
for (const name of names) {
|
|
72
91
|
if (arg.startsWith(`${name}=`)) {
|
|
@@ -80,7 +99,28 @@ function parseCommonFlags(args) {
|
|
|
80
99
|
}
|
|
81
100
|
}
|
|
82
101
|
}
|
|
83
|
-
|
|
102
|
+
// Build rest with strict unknown-flag detection and -- escape support
|
|
103
|
+
const rest = [];
|
|
104
|
+
let escapeMode = false;
|
|
105
|
+
for (let i = 0; i < args.length; i++) {
|
|
106
|
+
if (consumed.has(i))
|
|
107
|
+
continue;
|
|
108
|
+
const token = args[i];
|
|
109
|
+
if (!escapeMode && token === '--') {
|
|
110
|
+
escapeMode = true; // consume -- sentinel; everything after is positional
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (!escapeMode && token.startsWith('--')) {
|
|
114
|
+
throw new UnknownOptionError(`Unknown option: ${token}. Use "--" to pass literal flag-like tokens as message.\n` +
|
|
115
|
+
` Did you mean: crewx serve ${token}\n` +
|
|
116
|
+
` or: crewx q -- ${token} <message> (to include in message)`);
|
|
117
|
+
}
|
|
118
|
+
// Reject short -x flags that are not numeric (e.g. -f was consumed above)
|
|
119
|
+
if (!escapeMode && token.startsWith('-') && token.length > 1 && !/^-?\d/.test(token)) {
|
|
120
|
+
throw new UnknownOptionError(`Unknown option: ${token}`);
|
|
121
|
+
}
|
|
122
|
+
rest.push(token);
|
|
123
|
+
}
|
|
84
124
|
return { thread, provider, metadata, verbose, config, outputFormat, effort, promptFile, rest };
|
|
85
125
|
}
|
|
86
126
|
/**
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI command registry — SSOT for commands handled by the CLI engine.
|
|
3
|
+
*
|
|
4
|
+
* Consumed by:
|
|
5
|
+
* - main.ts dispatch (to avoid switch/case literal drift)
|
|
6
|
+
* - bin/__tests__/crewx-entrypoint.test.ts (SSOT assertion vs CLI_SUBCOMMANDS)
|
|
7
|
+
*/
|
|
8
|
+
/** Core commands handled by the main.ts switch/case or hook routing. */
|
|
9
|
+
export declare const KNOWN_COMMANDS: Set<string>;
|
|
10
|
+
/** Built-in tool commands routed via handleBuiltin(). */
|
|
11
|
+
export declare const BUILTIN_COMMAND_NAMES: Set<string>;
|
|
12
|
+
/** Commands not yet migrated from cli-bak — show a migration message. */
|
|
13
|
+
export declare const NOT_YET_MIGRATED: Set<string>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CLI command registry — SSOT for commands handled by the CLI engine.
|
|
4
|
+
*
|
|
5
|
+
* Consumed by:
|
|
6
|
+
* - main.ts dispatch (to avoid switch/case literal drift)
|
|
7
|
+
* - bin/__tests__/crewx-entrypoint.test.ts (SSOT assertion vs CLI_SUBCOMMANDS)
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.NOT_YET_MIGRATED = exports.BUILTIN_COMMAND_NAMES = exports.KNOWN_COMMANDS = void 0;
|
|
11
|
+
/** Core commands handled by the main.ts switch/case or hook routing. */
|
|
12
|
+
exports.KNOWN_COMMANDS = new Set([
|
|
13
|
+
'q', 'query',
|
|
14
|
+
'x', 'execute',
|
|
15
|
+
'agent',
|
|
16
|
+
'ps', 'kill', 'result', 'log',
|
|
17
|
+
'doctor', 'init',
|
|
18
|
+
'help',
|
|
19
|
+
'slack', 'slack:files',
|
|
20
|
+
'hook', 'hook-dispatch',
|
|
21
|
+
]);
|
|
22
|
+
/** Built-in tool commands routed via handleBuiltin(). */
|
|
23
|
+
exports.BUILTIN_COMMAND_NAMES = new Set([
|
|
24
|
+
'memory', 'search', 'doc', 'wbs', 'cron', 'workflow', 'skill',
|
|
25
|
+
]);
|
|
26
|
+
/** Commands not yet migrated from cli-bak — show a migration message. */
|
|
27
|
+
exports.NOT_YET_MIGRATED = new Set([
|
|
28
|
+
'template', 'templates', 'chat', 'mcp',
|
|
29
|
+
]);
|
|
@@ -33,8 +33,8 @@ function getRunningTasks(dbRoot) {
|
|
|
33
33
|
if (!db)
|
|
34
34
|
return [];
|
|
35
35
|
try {
|
|
36
|
-
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
37
|
-
result, error, duration_ms
|
|
36
|
+
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
37
|
+
result, error, duration_ms
|
|
38
38
|
FROM tasks WHERE status = 'running' ORDER BY started_at DESC`).all();
|
|
39
39
|
}
|
|
40
40
|
finally {
|
|
@@ -47,8 +47,8 @@ function getAllTasks(dbRoot) {
|
|
|
47
47
|
if (!db)
|
|
48
48
|
return [];
|
|
49
49
|
try {
|
|
50
|
-
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
51
|
-
result, error, duration_ms
|
|
50
|
+
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
51
|
+
result, error, duration_ms
|
|
52
52
|
FROM tasks ORDER BY started_at DESC`).all();
|
|
53
53
|
}
|
|
54
54
|
finally {
|
|
@@ -61,8 +61,8 @@ function getTask(id, dbRoot) {
|
|
|
61
61
|
if (!db)
|
|
62
62
|
return undefined;
|
|
63
63
|
try {
|
|
64
|
-
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
65
|
-
result, error, duration_ms
|
|
64
|
+
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
65
|
+
result, error, duration_ms
|
|
66
66
|
FROM tasks WHERE id = ?`).get(id);
|
|
67
67
|
}
|
|
68
68
|
finally {
|
|
@@ -97,7 +97,7 @@ function killTask(id, dbRoot) {
|
|
|
97
97
|
// ESRCH = process already gone — still clean up the record
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
|
-
db.prepare(`UPDATE tasks SET status='failed', error='killed by user',
|
|
100
|
+
db.prepare(`UPDATE tasks SET status='failed', error='killed by user',
|
|
101
101
|
completed_at=?, pid=NULL WHERE id=?`).run(new Date().toISOString(), id);
|
|
102
102
|
return { ok: true, message: `Killed task ${id}${task.pid ? ` (PID: ${task.pid})` : ''}` };
|
|
103
103
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DenyIfTouchesSecretsPlugin — Phase 0 demo HookPlugin.
|
|
3
|
+
*
|
|
4
|
+
* Denies Bash tool calls whose command string contains ".env".
|
|
5
|
+
* Pure string matching — trivially bypassable (see README).
|
|
6
|
+
*
|
|
7
|
+
* SECURITY NOTE:
|
|
8
|
+
* Do NOT include tool.input content in deny reasons.
|
|
9
|
+
* Use static messages only to prevent prompt injection.
|
|
10
|
+
* ✅ return ctx.deny('Secrets-related command');
|
|
11
|
+
* ❌ return ctx.deny(`Blocked: ${cmd}`);
|
|
12
|
+
*/
|
|
13
|
+
import { HookPlugin } from '@crewx/sdk/hooks';
|
|
14
|
+
import type { HookContext, HookResult } from '@crewx/sdk/hooks';
|
|
15
|
+
export declare class DenyIfTouchesSecretsPlugin extends HookPlugin {
|
|
16
|
+
readonly name = "deny-secrets";
|
|
17
|
+
readonly version = "0.1.0";
|
|
18
|
+
readonly capabilities: {
|
|
19
|
+
required: readonly ["deny"];
|
|
20
|
+
};
|
|
21
|
+
run(ctx: HookContext): Promise<HookResult>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DenyIfTouchesSecretsPlugin — Phase 0 demo HookPlugin.
|
|
4
|
+
*
|
|
5
|
+
* Denies Bash tool calls whose command string contains ".env".
|
|
6
|
+
* Pure string matching — trivially bypassable (see README).
|
|
7
|
+
*
|
|
8
|
+
* SECURITY NOTE:
|
|
9
|
+
* Do NOT include tool.input content in deny reasons.
|
|
10
|
+
* Use static messages only to prevent prompt injection.
|
|
11
|
+
* ✅ return ctx.deny('Secrets-related command');
|
|
12
|
+
* ❌ return ctx.deny(`Blocked: ${cmd}`);
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.DenyIfTouchesSecretsPlugin = void 0;
|
|
16
|
+
const hooks_1 = require("@crewx/sdk/hooks");
|
|
17
|
+
const SECRET_PATTERNS = ['.env', 'credentials.json', 'service-account-key.json'];
|
|
18
|
+
const SHELL_TOOL_NAMES = new Set(['Bash', 'shell', 'local_shell']);
|
|
19
|
+
class DenyIfTouchesSecretsPlugin extends hooks_1.HookPlugin {
|
|
20
|
+
name = 'deny-secrets';
|
|
21
|
+
version = '0.1.0';
|
|
22
|
+
capabilities = { required: ['deny'] };
|
|
23
|
+
async run(ctx) {
|
|
24
|
+
if (!SHELL_TOOL_NAMES.has(ctx.tool.rawName) && ctx.tool.name !== 'shell') {
|
|
25
|
+
return ctx.pass();
|
|
26
|
+
}
|
|
27
|
+
const input = ctx.tool.input;
|
|
28
|
+
const command = input?.command;
|
|
29
|
+
if (typeof command !== 'string') {
|
|
30
|
+
return ctx.pass();
|
|
31
|
+
}
|
|
32
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
33
|
+
if (command.includes(pattern)) {
|
|
34
|
+
return ctx.deny('Secrets-related command');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return ctx.pass();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.DenyIfTouchesSecretsPlugin = DenyIfTouchesSecretsPlugin;
|