@crewx/cli 0.8.7-rc.27 → 0.8.7-rc.28
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/package.json +5 -5
- package/dist/commands/task-db.d.ts +0 -33
- package/dist/commands/task-db.js +0 -107
- package/dist/examples/deny-secrets-plugin.d.ts +0 -22
- package/dist/examples/deny-secrets-plugin.js +0 -40
- package/dist/plugins/examples/echo-hook.d.ts +0 -24
- package/dist/plugins/examples/echo-hook.js +0 -60
- package/dist/plugins/examples/verify-echo-hook.d.ts +0 -8
- package/dist/plugins/examples/verify-echo-hook.js +0 -47
- package/dist/plugins/sqlite-tracing.d.ts +0 -11
- package/dist/plugins/sqlite-tracing.js +0 -19
- package/dist/repository/workspace.repository.d.ts +0 -26
- package/dist/repository/workspace.repository.js +0 -111
- package/dist/schema/tasks.d.ts +0 -7
- package/dist/schema/tasks.js +0 -48
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewx/cli",
|
|
3
|
-
"version": "0.8.7-rc.
|
|
3
|
+
"version": "0.8.7-rc.28",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.19.0"
|
|
@@ -23,16 +23,16 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@crewx/adapter-slack": "0.1.4",
|
|
25
25
|
"better-sqlite3": "*",
|
|
26
|
-
"@crewx/sdk": "0.8.7-rc.
|
|
26
|
+
"@crewx/sdk": "0.8.7-rc.28",
|
|
27
27
|
"@crewx/memory": "0.1.21",
|
|
28
|
+
"@crewx/search": "0.1.9",
|
|
28
29
|
"@crewx/doc": "0.1.8",
|
|
29
30
|
"@crewx/wbs": "0.1.9",
|
|
30
|
-
"@crewx/search": "0.1.9",
|
|
31
31
|
"@crewx/cron": "0.1.8",
|
|
32
32
|
"@crewx/workflow": "0.3.20",
|
|
33
|
+
"@crewx/skill": "0.1.19",
|
|
33
34
|
"@crewx/shared": "0.0.5",
|
|
34
|
-
"@crewx/wi": "0.1.9"
|
|
35
|
-
"@crewx/skill": "0.1.19"
|
|
35
|
+
"@crewx/wi": "0.1.9"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@types/better-sqlite3": "*",
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared SQLite helper for ps/kill/result CLI commands.
|
|
3
|
-
* Reads from (or writes to) the global ~/.crewx/crewx.db
|
|
4
|
-
* populated by SqliteTracingPlugin.
|
|
5
|
-
*/
|
|
6
|
-
import BetterSqlite3 from 'better-sqlite3';
|
|
7
|
-
export type TaskStatus = 'running' | 'success' | 'failed';
|
|
8
|
-
export interface TaskRow {
|
|
9
|
-
id: string;
|
|
10
|
-
agent_id: string;
|
|
11
|
-
prompt: string;
|
|
12
|
-
mode: string;
|
|
13
|
-
status: TaskStatus;
|
|
14
|
-
pid: number | null;
|
|
15
|
-
started_at: string;
|
|
16
|
-
completed_at: string | null;
|
|
17
|
-
result: string | null;
|
|
18
|
-
error: string | null;
|
|
19
|
-
duration_ms: number | null;
|
|
20
|
-
}
|
|
21
|
-
export declare function getDbPath(dbRoot?: string): string;
|
|
22
|
-
export declare function openDb(readonly?: boolean, dbRoot?: string): InstanceType<typeof BetterSqlite3> | null;
|
|
23
|
-
/** Return all tasks with status='running'. */
|
|
24
|
-
export declare function getRunningTasks(dbRoot?: string): TaskRow[];
|
|
25
|
-
/** Return all tasks ordered by started_at desc. */
|
|
26
|
-
export declare function getAllTasks(dbRoot?: string): TaskRow[];
|
|
27
|
-
/** Return a single task by id, or undefined if not found. */
|
|
28
|
-
export declare function getTask(id: string, dbRoot?: string): TaskRow | undefined;
|
|
29
|
-
/** Send SIGTERM to the task's pid and mark it failed. Returns ok/message. */
|
|
30
|
-
export declare function killTask(id: string, dbRoot?: string): {
|
|
31
|
-
ok: boolean;
|
|
32
|
-
message: string;
|
|
33
|
-
};
|
package/dist/commands/task-db.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Shared SQLite helper for ps/kill/result CLI commands.
|
|
4
|
-
* Reads from (or writes to) the global ~/.crewx/crewx.db
|
|
5
|
-
* populated by SqliteTracingPlugin.
|
|
6
|
-
*/
|
|
7
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
8
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
9
|
-
};
|
|
10
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.getDbPath = getDbPath;
|
|
12
|
-
exports.openDb = openDb;
|
|
13
|
-
exports.getRunningTasks = getRunningTasks;
|
|
14
|
-
exports.getAllTasks = getAllTasks;
|
|
15
|
-
exports.getTask = getTask;
|
|
16
|
-
exports.killTask = killTask;
|
|
17
|
-
const fs_1 = require("fs");
|
|
18
|
-
const path_1 = require("path");
|
|
19
|
-
const os_1 = require("os");
|
|
20
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
21
|
-
function getDbPath(dbRoot) {
|
|
22
|
-
return (0, path_1.join)(dbRoot ?? (0, os_1.homedir)(), '.crewx', 'crewx.db');
|
|
23
|
-
}
|
|
24
|
-
function openDb(readonly = true, dbRoot) {
|
|
25
|
-
const dbPath = getDbPath(dbRoot);
|
|
26
|
-
if (!(0, fs_1.existsSync)(dbPath))
|
|
27
|
-
return null;
|
|
28
|
-
return new better_sqlite3_1.default(dbPath, { readonly });
|
|
29
|
-
}
|
|
30
|
-
/** Return all tasks with status='running'. */
|
|
31
|
-
function getRunningTasks(dbRoot) {
|
|
32
|
-
const db = openDb(true, dbRoot);
|
|
33
|
-
if (!db)
|
|
34
|
-
return [];
|
|
35
|
-
try {
|
|
36
|
-
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
37
|
-
result, error, duration_ms
|
|
38
|
-
FROM tasks WHERE status = 'running' ORDER BY started_at DESC`).all();
|
|
39
|
-
}
|
|
40
|
-
finally {
|
|
41
|
-
db.close();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
/** Return all tasks ordered by started_at desc. */
|
|
45
|
-
function getAllTasks(dbRoot) {
|
|
46
|
-
const db = openDb(true, dbRoot);
|
|
47
|
-
if (!db)
|
|
48
|
-
return [];
|
|
49
|
-
try {
|
|
50
|
-
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
51
|
-
result, error, duration_ms
|
|
52
|
-
FROM tasks ORDER BY started_at DESC`).all();
|
|
53
|
-
}
|
|
54
|
-
finally {
|
|
55
|
-
db.close();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
/** Return a single task by id, or undefined if not found. */
|
|
59
|
-
function getTask(id, dbRoot) {
|
|
60
|
-
const db = openDb(true, dbRoot);
|
|
61
|
-
if (!db)
|
|
62
|
-
return undefined;
|
|
63
|
-
try {
|
|
64
|
-
return db.prepare(`SELECT id, agent_id, prompt, mode, status, pid, started_at, completed_at,
|
|
65
|
-
result, error, duration_ms
|
|
66
|
-
FROM tasks WHERE id = ?`).get(id);
|
|
67
|
-
}
|
|
68
|
-
finally {
|
|
69
|
-
db.close();
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/** Send SIGTERM to the task's pid and mark it failed. Returns ok/message. */
|
|
73
|
-
function killTask(id, dbRoot) {
|
|
74
|
-
const db = openDb(false, dbRoot);
|
|
75
|
-
if (!db)
|
|
76
|
-
return { ok: false, message: `crewx.db not found — no running tasks.` };
|
|
77
|
-
try {
|
|
78
|
-
const task = db.prepare(`SELECT id, status, pid FROM tasks WHERE id = ?`).get(id);
|
|
79
|
-
if (!task) {
|
|
80
|
-
return { ok: false, message: `Task not found: ${id}` };
|
|
81
|
-
}
|
|
82
|
-
if (task.status !== 'running') {
|
|
83
|
-
return { ok: false, message: `Task ${id} is not running (status: ${task.status})` };
|
|
84
|
-
}
|
|
85
|
-
if (task.pid) {
|
|
86
|
-
try {
|
|
87
|
-
process.kill(task.pid, 'SIGTERM');
|
|
88
|
-
}
|
|
89
|
-
catch (err) {
|
|
90
|
-
const code = err.code;
|
|
91
|
-
if (code !== 'ESRCH') {
|
|
92
|
-
return {
|
|
93
|
-
ok: false,
|
|
94
|
-
message: `Failed to kill PID ${task.pid}: ${err instanceof Error ? err.message : String(err)}`,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
// ESRCH = process already gone — still clean up the record
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
db.prepare(`UPDATE tasks SET status='failed', error='killed by user',
|
|
101
|
-
completed_at=?, pid=NULL WHERE id=?`).run(new Date().toISOString(), id);
|
|
102
|
-
return { ok: true, message: `Killed task ${id}${task.pid ? ` (PID: ${task.pid})` : ''}` };
|
|
103
|
-
}
|
|
104
|
-
finally {
|
|
105
|
-
db.close();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
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;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EchoObserverPlugin — Tool observer that echoes events to a JSONL log.
|
|
3
|
-
*
|
|
4
|
-
* Observes tool:before / tool:after events from the Crewx event bus
|
|
5
|
-
* and appends them as JSONL to ~/.crewx/logs/echo-hook.log.
|
|
6
|
-
* Pure observer — no flow control (deny/inject/modify not applicable).
|
|
7
|
-
*/
|
|
8
|
-
import { ToolObserverPlugin } from '@crewx/sdk/hooks';
|
|
9
|
-
import type { ObserverContext, ObserverResult } from '@crewx/sdk/hooks';
|
|
10
|
-
export declare class EchoHookPlugin extends ToolObserverPlugin {
|
|
11
|
-
readonly name = "echo-hook";
|
|
12
|
-
readonly version = "0.0.1";
|
|
13
|
-
readonly on: {
|
|
14
|
-
beforeTool: true;
|
|
15
|
-
afterTool: true;
|
|
16
|
-
beforePrompt: true;
|
|
17
|
-
sessionStart: true;
|
|
18
|
-
};
|
|
19
|
-
private readonly logPath;
|
|
20
|
-
constructor(logDir?: string);
|
|
21
|
-
run(ctx: ObserverContext): Promise<ObserverResult>;
|
|
22
|
-
private ensureLogDir;
|
|
23
|
-
private echo;
|
|
24
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* EchoObserverPlugin — Tool observer that echoes events to a JSONL log.
|
|
4
|
-
*
|
|
5
|
-
* Observes tool:before / tool:after events from the Crewx event bus
|
|
6
|
-
* and appends them as JSONL to ~/.crewx/logs/echo-hook.log.
|
|
7
|
-
* Pure observer — no flow control (deny/inject/modify not applicable).
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
exports.EchoHookPlugin = void 0;
|
|
11
|
-
const fs_1 = require("fs");
|
|
12
|
-
const path_1 = require("path");
|
|
13
|
-
const os_1 = require("os");
|
|
14
|
-
const hooks_1 = require("@crewx/sdk/hooks");
|
|
15
|
-
class EchoHookPlugin extends hooks_1.ToolObserverPlugin {
|
|
16
|
-
name = 'echo-hook';
|
|
17
|
-
version = '0.0.1';
|
|
18
|
-
on = {
|
|
19
|
-
beforeTool: true,
|
|
20
|
-
afterTool: true,
|
|
21
|
-
beforePrompt: true,
|
|
22
|
-
sessionStart: true,
|
|
23
|
-
};
|
|
24
|
-
logPath;
|
|
25
|
-
constructor(logDir) {
|
|
26
|
-
super();
|
|
27
|
-
this.logPath = (0, path_1.join)(logDir ?? (0, os_1.homedir)(), '.crewx', 'logs', 'echo-hook.log');
|
|
28
|
-
this.ensureLogDir();
|
|
29
|
-
}
|
|
30
|
-
async run(ctx) {
|
|
31
|
-
this.echo(ctx);
|
|
32
|
-
return ctx.pass();
|
|
33
|
-
}
|
|
34
|
-
ensureLogDir() {
|
|
35
|
-
const dir = (0, path_1.dirname)(this.logPath);
|
|
36
|
-
if (!(0, fs_1.existsSync)(dir)) {
|
|
37
|
-
(0, fs_1.mkdirSync)(dir, { recursive: true, mode: 0o700 });
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
echo(ctx) {
|
|
41
|
-
try {
|
|
42
|
-
const line = JSON.stringify({
|
|
43
|
-
timestamp: new Date().toISOString(),
|
|
44
|
-
event: ctx.event,
|
|
45
|
-
traceId: ctx.traceId,
|
|
46
|
-
agent: ctx.agent,
|
|
47
|
-
provider: ctx.provider,
|
|
48
|
-
thread: ctx.thread,
|
|
49
|
-
tool: ctx.tool,
|
|
50
|
-
cwd: ctx.cwd,
|
|
51
|
-
sessionId: ctx.sessionId,
|
|
52
|
-
});
|
|
53
|
-
(0, fs_1.appendFileSync)(this.logPath, line + '\n', { encoding: 'utf8', mode: 0o600 });
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
// Non-fatal
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
exports.EchoHookPlugin = EchoHookPlugin;
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
"use strict";
|
|
3
|
-
/**
|
|
4
|
-
* Phase 0 e2e verification — EchoHookPlugin logs Claude Code tool calls.
|
|
5
|
-
*
|
|
6
|
-
* Run: npx tsx packages/cli/src/plugins/examples/verify-echo-hook.ts
|
|
7
|
-
* Prereq: claude CLI installed, crewx.yaml with @claude agent
|
|
8
|
-
*/
|
|
9
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
-
const sdk_1 = require("@crewx/sdk");
|
|
11
|
-
const echo_hook_1 = require("./echo-hook");
|
|
12
|
-
const fs_1 = require("fs");
|
|
13
|
-
const path_1 = require("path");
|
|
14
|
-
const os_1 = require("os");
|
|
15
|
-
const LOG_PATH = (0, path_1.join)((0, os_1.homedir)(), '.crewx', 'logs', 'echo-hook.log');
|
|
16
|
-
async function main() {
|
|
17
|
-
if ((0, fs_1.existsSync)(LOG_PATH))
|
|
18
|
-
(0, fs_1.unlinkSync)(LOG_PATH);
|
|
19
|
-
const crewx = await sdk_1.Crewx.loadYaml('crewx.yaml');
|
|
20
|
-
await crewx.use(new echo_hook_1.EchoHookPlugin());
|
|
21
|
-
console.log('[verify] querying @claude with "ls"...');
|
|
22
|
-
await crewx.query('@claude', 'Run ls in the current directory. Use the Bash tool.');
|
|
23
|
-
await crewx.close();
|
|
24
|
-
if (!(0, fs_1.existsSync)(LOG_PATH)) {
|
|
25
|
-
console.error('[FAIL] echo-hook.log not created');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
const lines = (0, fs_1.readFileSync)(LOG_PATH, 'utf8').trim().split('\n');
|
|
29
|
-
const entries = lines.map((l) => JSON.parse(l));
|
|
30
|
-
const hasBeforeTool = entries.some((e) => e.event === 'beforeTool');
|
|
31
|
-
const hasTraceId = entries.every((e) => e.traceId?.startsWith('tsk_'));
|
|
32
|
-
const hasToolName = entries
|
|
33
|
-
.filter((e) => e.event === 'beforeTool')
|
|
34
|
-
.every((e) => typeof e.tool?.rawName === 'string' && e.tool.rawName.length > 0);
|
|
35
|
-
console.log(`[verify] entries: ${entries.length}`);
|
|
36
|
-
console.log(`[verify] beforeTool: ${hasBeforeTool}`);
|
|
37
|
-
console.log(`[verify] traceId: ${hasTraceId}`);
|
|
38
|
-
console.log(`[verify] toolName: ${hasToolName}`);
|
|
39
|
-
if (hasBeforeTool && hasTraceId && hasToolName) {
|
|
40
|
-
console.log('[PASS] Phase 0 e2e verification passed');
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
console.error('[FAIL] Some checks failed');
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
main().catch((err) => { console.error(err); process.exit(1); });
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export of SqliteTracingPlugin with a CLI-friendly constructor that
|
|
3
|
-
* accepts a plain dbRoot string (in addition to the SDK's options-object form).
|
|
4
|
-
*/
|
|
5
|
-
import { SqliteTracingPlugin as SdkSqliteTracingPlugin } from '@crewx/sdk/plugins';
|
|
6
|
-
export declare class SqliteTracingPlugin extends SdkSqliteTracingPlugin {
|
|
7
|
-
constructor(dbRootOrOpts?: string | {
|
|
8
|
-
dbRoot?: string;
|
|
9
|
-
version?: string;
|
|
10
|
-
});
|
|
11
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SqliteTracingPlugin = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Re-export of SqliteTracingPlugin with a CLI-friendly constructor that
|
|
6
|
-
* accepts a plain dbRoot string (in addition to the SDK's options-object form).
|
|
7
|
-
*/
|
|
8
|
-
const plugins_1 = require("@crewx/sdk/plugins");
|
|
9
|
-
class SqliteTracingPlugin extends plugins_1.SqliteTracingPlugin {
|
|
10
|
-
constructor(dbRootOrOpts) {
|
|
11
|
-
if (typeof dbRootOrOpts === 'string') {
|
|
12
|
-
super({ dbRoot: dbRootOrOpts });
|
|
13
|
-
}
|
|
14
|
-
else {
|
|
15
|
-
super(dbRootOrOpts);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
exports.SqliteTracingPlugin = SqliteTracingPlugin;
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import BetterSqlite3 from 'better-sqlite3';
|
|
2
|
-
export interface WorkspaceRepositoryOptions {
|
|
3
|
-
/** Root directory under which `.crewx/crewx.db` lives. Defaults to `os.homedir()`. */
|
|
4
|
-
dbRoot?: string;
|
|
5
|
-
}
|
|
6
|
-
export declare class WorkspaceRepository {
|
|
7
|
-
private readonly dbPath;
|
|
8
|
-
constructor(opts?: WorkspaceRepositoryOptions);
|
|
9
|
-
/**
|
|
10
|
-
* Register a CLI workspace in the global CrewX DB.
|
|
11
|
-
*
|
|
12
|
-
* This is intentionally CLI-owned, not SDK-owned: workspace registration is
|
|
13
|
-
* CrewX application metadata, while the SDK only needs pure workspace helpers.
|
|
14
|
-
* Registration is best-effort so `crewx init` remains usable even if tracing
|
|
15
|
-
* DB setup fails.
|
|
16
|
-
*/
|
|
17
|
-
registerWorkspace(workspacePath: string): void;
|
|
18
|
-
ensureSchema(db: InstanceType<typeof BetterSqlite3>): void;
|
|
19
|
-
resolveSlug(db: InstanceType<typeof BetterSqlite3>, workspaceId: string, workspacePath: string): string;
|
|
20
|
-
ensureRow(db: InstanceType<typeof BetterSqlite3>, opts: {
|
|
21
|
-
id: string;
|
|
22
|
-
slug: string;
|
|
23
|
-
name: string;
|
|
24
|
-
workspacePath: string;
|
|
25
|
-
}): void;
|
|
26
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.WorkspaceRepository = void 0;
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
|
-
const path_1 = require("path");
|
|
9
|
-
const os_1 = require("os");
|
|
10
|
-
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
|
|
11
|
-
const sdk_1 = require("@crewx/sdk");
|
|
12
|
-
class WorkspaceRepository {
|
|
13
|
-
dbPath;
|
|
14
|
-
constructor(opts = {}) {
|
|
15
|
-
this.dbPath = (0, path_1.join)(opts.dbRoot ?? (0, os_1.homedir)(), '.crewx', 'crewx.db');
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Register a CLI workspace in the global CrewX DB.
|
|
19
|
-
*
|
|
20
|
-
* This is intentionally CLI-owned, not SDK-owned: workspace registration is
|
|
21
|
-
* CrewX application metadata, while the SDK only needs pure workspace helpers.
|
|
22
|
-
* Registration is best-effort so `crewx init` remains usable even if tracing
|
|
23
|
-
* DB setup fails.
|
|
24
|
-
*/
|
|
25
|
-
registerWorkspace(workspacePath) {
|
|
26
|
-
const normalizedPath = (0, sdk_1.normalizeWorkspacePath)(workspacePath);
|
|
27
|
-
try {
|
|
28
|
-
const dbDir = (0, path_1.dirname)(this.dbPath);
|
|
29
|
-
if (!(0, fs_1.existsSync)(dbDir)) {
|
|
30
|
-
(0, fs_1.mkdirSync)(dbDir, { recursive: true });
|
|
31
|
-
}
|
|
32
|
-
const db = new better_sqlite3_1.default(this.dbPath);
|
|
33
|
-
try {
|
|
34
|
-
this.ensureSchema(db);
|
|
35
|
-
const id = (0, sdk_1.hashWorkspaceId)(normalizedPath);
|
|
36
|
-
const name = (0, path_1.basename)(normalizedPath);
|
|
37
|
-
const slug = this.resolveSlug(db, id, normalizedPath);
|
|
38
|
-
this.ensureRow(db, { id, slug, name, workspacePath: normalizedPath });
|
|
39
|
-
}
|
|
40
|
-
finally {
|
|
41
|
-
db.close();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// Non-fatal: workspace registration must never crash `crewx init`.
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
ensureSchema(db) {
|
|
49
|
-
db.exec(`
|
|
50
|
-
CREATE TABLE IF NOT EXISTS workspaces (
|
|
51
|
-
id TEXT PRIMARY KEY,
|
|
52
|
-
slug TEXT NOT NULL UNIQUE,
|
|
53
|
-
name TEXT NOT NULL,
|
|
54
|
-
workspace_path TEXT,
|
|
55
|
-
is_active INTEGER NOT NULL DEFAULT 1,
|
|
56
|
-
created_at TEXT NOT NULL,
|
|
57
|
-
updated_at TEXT NOT NULL
|
|
58
|
-
)
|
|
59
|
-
`);
|
|
60
|
-
db.exec(`
|
|
61
|
-
CREATE TABLE IF NOT EXISTS threads (
|
|
62
|
-
id TEXT PRIMARY KEY,
|
|
63
|
-
workspace_id TEXT,
|
|
64
|
-
platform TEXT NOT NULL DEFAULT 'cli',
|
|
65
|
-
title TEXT,
|
|
66
|
-
first_message TEXT,
|
|
67
|
-
last_message TEXT,
|
|
68
|
-
message_count INTEGER NOT NULL DEFAULT 0,
|
|
69
|
-
created_at TEXT NOT NULL,
|
|
70
|
-
updated_at TEXT NOT NULL,
|
|
71
|
-
metadata TEXT,
|
|
72
|
-
FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE SET NULL
|
|
73
|
-
)
|
|
74
|
-
`);
|
|
75
|
-
}
|
|
76
|
-
resolveSlug(db, workspaceId, workspacePath) {
|
|
77
|
-
const basenameSlug = (0, path_1.basename)(workspacePath);
|
|
78
|
-
const parentSlug = `${(0, path_1.basename)((0, path_1.dirname)(workspacePath))}/${basenameSlug}`;
|
|
79
|
-
const candidates = [basenameSlug, parentSlug];
|
|
80
|
-
try {
|
|
81
|
-
const slugExistsForOtherWorkspace = (slug) => !!db
|
|
82
|
-
.prepare('SELECT id FROM workspaces WHERE slug = ? AND id != ?')
|
|
83
|
-
.get(slug, workspaceId);
|
|
84
|
-
for (const slug of candidates) {
|
|
85
|
-
if (!slugExistsForOtherWorkspace(slug))
|
|
86
|
-
return slug;
|
|
87
|
-
}
|
|
88
|
-
for (let suffix = 2; suffix < 1000; suffix += 1) {
|
|
89
|
-
const slug = `${parentSlug}-${suffix}`;
|
|
90
|
-
if (!slugExistsForOtherWorkspace(slug))
|
|
91
|
-
return slug;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
// workspaces table may not yet exist during early migration.
|
|
96
|
-
}
|
|
97
|
-
return basenameSlug;
|
|
98
|
-
}
|
|
99
|
-
ensureRow(db, opts) {
|
|
100
|
-
const { id, slug, name, workspacePath } = opts;
|
|
101
|
-
const now = new Date().toISOString();
|
|
102
|
-
db.prepare(`
|
|
103
|
-
INSERT OR IGNORE INTO workspaces (id, slug, name, workspace_path, is_active, created_at, updated_at)
|
|
104
|
-
VALUES (?, ?, ?, ?, 1, ?, ?)
|
|
105
|
-
`).run(id, slug, name, workspacePath, now, now);
|
|
106
|
-
db.prepare(`
|
|
107
|
-
UPDATE workspaces SET workspace_path = ?, updated_at = ? WHERE id = ? AND workspace_path IS NULL
|
|
108
|
-
`).run(workspacePath, now, id);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
exports.WorkspaceRepository = WorkspaceRepository;
|
package/dist/schema/tasks.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type BetterSqlite3 from 'better-sqlite3';
|
|
2
|
-
/**
|
|
3
|
-
* Ensure the `tasks` table exists in the given database.
|
|
4
|
-
* Safe to call multiple times — CREATE TABLE IF NOT EXISTS is idempotent.
|
|
5
|
-
* Owns the canonical schema for the CLI-side tasks table.
|
|
6
|
-
*/
|
|
7
|
-
export declare function ensureTasksSchema(db: InstanceType<typeof BetterSqlite3>): void;
|
package/dist/schema/tasks.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensureTasksSchema = ensureTasksSchema;
|
|
4
|
-
/**
|
|
5
|
-
* Ensure the `tasks` table exists in the given database.
|
|
6
|
-
* Safe to call multiple times — CREATE TABLE IF NOT EXISTS is idempotent.
|
|
7
|
-
* Owns the canonical schema for the CLI-side tasks table.
|
|
8
|
-
*/
|
|
9
|
-
function ensureTasksSchema(db) {
|
|
10
|
-
db.exec(`
|
|
11
|
-
CREATE TABLE IF NOT EXISTS tasks (
|
|
12
|
-
id TEXT PRIMARY KEY,
|
|
13
|
-
agent_id TEXT,
|
|
14
|
-
user_id TEXT,
|
|
15
|
-
prompt TEXT,
|
|
16
|
-
mode TEXT,
|
|
17
|
-
status TEXT NOT NULL,
|
|
18
|
-
result TEXT,
|
|
19
|
-
error TEXT,
|
|
20
|
-
started_at TEXT NOT NULL,
|
|
21
|
-
completed_at TEXT,
|
|
22
|
-
duration_ms INTEGER,
|
|
23
|
-
metadata TEXT,
|
|
24
|
-
project_id TEXT,
|
|
25
|
-
project_name TEXT,
|
|
26
|
-
trace_id TEXT,
|
|
27
|
-
parent_task_id TEXT,
|
|
28
|
-
caller_agent_id TEXT,
|
|
29
|
-
model TEXT,
|
|
30
|
-
platform TEXT,
|
|
31
|
-
crewx_version TEXT,
|
|
32
|
-
input_tokens INTEGER DEFAULT 0,
|
|
33
|
-
output_tokens INTEGER DEFAULT 0,
|
|
34
|
-
cost_usd REAL,
|
|
35
|
-
pid INTEGER,
|
|
36
|
-
rendered_prompt TEXT,
|
|
37
|
-
command TEXT,
|
|
38
|
-
coding_agent_command TEXT,
|
|
39
|
-
exit_code INTEGER,
|
|
40
|
-
logs TEXT,
|
|
41
|
-
thread_id TEXT,
|
|
42
|
-
project_ref TEXT,
|
|
43
|
-
workspace_id TEXT,
|
|
44
|
-
workspace_name TEXT,
|
|
45
|
-
cached_input_tokens INTEGER DEFAULT 0
|
|
46
|
-
)
|
|
47
|
-
`);
|
|
48
|
-
}
|