@clinebot/shared 0.0.10 → 0.0.12
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/dist/db/index.js +80 -92
- package/dist/rpc/runtime.d.ts +1 -0
- package/dist/storage/index.d.ts +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/storage/paths.d.ts +5 -0
- package/package.json +1 -1
- package/src/db/sqlite-db.ts +244 -203
- package/src/rpc/runtime.ts +1 -0
- package/src/storage/index.ts +5 -0
- package/src/storage/paths.ts +167 -3
package/dist/db/index.js
CHANGED
|
@@ -1,92 +1,80 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
tokens_used INTEGER,
|
|
82
|
-
cost_usd REAL,
|
|
83
|
-
FOREIGN KEY (schedule_id) REFERENCES schedules(schedule_id) ON DELETE CASCADE,
|
|
84
|
-
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE SET NULL
|
|
85
|
-
);
|
|
86
|
-
`),S.exec(`
|
|
87
|
-
CREATE INDEX IF NOT EXISTS idx_schedule_executions_schedule
|
|
88
|
-
ON schedule_executions(schedule_id, triggered_at DESC);
|
|
89
|
-
`),S.exec(`
|
|
90
|
-
CREATE INDEX IF NOT EXISTS idx_schedules_next_run
|
|
91
|
-
ON schedules(enabled, next_run_at);
|
|
92
|
-
`),!g.includeLegacyMigrations)return;let z=S.prepare("PRAGMA table_info(sessions);").all(),B=(x)=>z.some((I)=>I.name===x);if(!B("workspace_root"))S.exec("ALTER TABLE sessions ADD COLUMN workspace_root TEXT;"),S.exec("UPDATE sessions SET workspace_root = cwd WHERE workspace_root IS NULL OR workspace_root = '';");if(!B("parent_session_id"))S.exec("ALTER TABLE sessions ADD COLUMN parent_session_id TEXT;");if(!B("parent_agent_id"))S.exec("ALTER TABLE sessions ADD COLUMN parent_agent_id TEXT;");if(!B("agent_id"))S.exec("ALTER TABLE sessions ADD COLUMN agent_id TEXT;");if(!B("conversation_id"))S.exec("ALTER TABLE sessions ADD COLUMN conversation_id TEXT;");if(!B("is_subagent"))S.exec("ALTER TABLE sessions ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0;");if(!B("messages_path"))S.exec("ALTER TABLE sessions ADD COLUMN messages_path TEXT;");if(!B("metadata_json"))S.exec("ALTER TABLE sessions ADD COLUMN metadata_json TEXT;");let D=S.prepare("PRAGMA table_info(schedules);").all(),j=(x)=>D.some((I)=>I.name===x);if(!j("claim_token"))S.exec("ALTER TABLE schedules ADD COLUMN claim_token TEXT;");if(!j("claim_started_at"))S.exec("ALTER TABLE schedules ADD COLUMN claim_started_at TEXT;");if(!j("claim_until_at"))S.exec("ALTER TABLE schedules ADD COLUMN claim_until_at TEXT;")}export{T as toBoolInt,Q as nowIso,X as loadSqliteDb,Y as ensureSessionSchema,U as asString,V as asOptionalString,W as asBool};
|
|
1
|
+
var J=Object.defineProperty;var K=(S)=>S;function Q(S,x){this[S]=K.bind(null,x)}var M=(S,x)=>{for(var B in x)J(S,B,{get:x[B],enumerable:!0,configurable:!0,set:Q.bind(x,B)})};import{mkdirSync as U}from"node:fs";import{createRequire as V}from"node:module";import{dirname as W}from"node:path";function X(){return new Date().toISOString()}function Z(S){return S?1:0}function $(S){return typeof S==="string"?S:""}function k(S){if(typeof S!=="string")return;let x=S.trim();return x.length>0?x:void 0}function y(S){return S===1||S===!0}function F(S){return{prepare:(x)=>S.query(x),exec:(x)=>S.exec(x)}}function H(S){return{prepare:(x)=>{let B=S.prepare(x);return{run:(...O)=>B.run(...O),get:(...O)=>B.get(...O)??null,all:(...O)=>B.all(...O)}},exec:(x)=>S.exec(x)}}function L(S){U(W(S),{recursive:!0});let x=V(import.meta.url);if(typeof globalThis.Bun<"u"){let{Database:O}=x("bun:sqlite");return F(new O(S,{create:!0}))}try{let O=process.emitWarning;process.emitWarning=(z,...I)=>{if((typeof z==="string"?z:z?.message??"").includes("SQLite"))return;return O.call(process,z,...I)};let{DatabaseSync:j}=x(["node",":sqlite"].join(""));return process.emitWarning=O,H(new j(S))}catch{}return new(x(["better","-sqlite3"].join("")))(S)}var Y=[`CREATE TABLE IF NOT EXISTS sessions (
|
|
2
|
+
session_id TEXT PRIMARY KEY,
|
|
3
|
+
source TEXT NOT NULL,
|
|
4
|
+
pid INTEGER NOT NULL,
|
|
5
|
+
started_at TEXT NOT NULL,
|
|
6
|
+
ended_at TEXT,
|
|
7
|
+
exit_code INTEGER,
|
|
8
|
+
status TEXT NOT NULL,
|
|
9
|
+
status_lock INTEGER NOT NULL DEFAULT 0,
|
|
10
|
+
interactive INTEGER NOT NULL,
|
|
11
|
+
provider TEXT NOT NULL,
|
|
12
|
+
model TEXT NOT NULL,
|
|
13
|
+
cwd TEXT NOT NULL,
|
|
14
|
+
workspace_root TEXT NOT NULL,
|
|
15
|
+
team_name TEXT,
|
|
16
|
+
enable_tools INTEGER NOT NULL,
|
|
17
|
+
enable_spawn INTEGER NOT NULL,
|
|
18
|
+
enable_teams INTEGER NOT NULL,
|
|
19
|
+
parent_session_id TEXT,
|
|
20
|
+
parent_agent_id TEXT,
|
|
21
|
+
agent_id TEXT,
|
|
22
|
+
conversation_id TEXT,
|
|
23
|
+
is_subagent INTEGER NOT NULL DEFAULT 0,
|
|
24
|
+
prompt TEXT,
|
|
25
|
+
metadata_json TEXT,
|
|
26
|
+
transcript_path TEXT NOT NULL,
|
|
27
|
+
hook_path TEXT NOT NULL,
|
|
28
|
+
messages_path TEXT,
|
|
29
|
+
updated_at TEXT NOT NULL
|
|
30
|
+
);`,`CREATE TABLE IF NOT EXISTS subagent_spawn_queue (
|
|
31
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
32
|
+
root_session_id TEXT NOT NULL,
|
|
33
|
+
parent_agent_id TEXT NOT NULL,
|
|
34
|
+
task TEXT,
|
|
35
|
+
system_prompt TEXT,
|
|
36
|
+
created_at TEXT NOT NULL,
|
|
37
|
+
consumed_at TEXT
|
|
38
|
+
);`,`CREATE TABLE IF NOT EXISTS schedules (
|
|
39
|
+
schedule_id TEXT PRIMARY KEY,
|
|
40
|
+
name TEXT NOT NULL,
|
|
41
|
+
cron_pattern TEXT NOT NULL,
|
|
42
|
+
prompt TEXT NOT NULL,
|
|
43
|
+
provider TEXT NOT NULL,
|
|
44
|
+
model TEXT NOT NULL,
|
|
45
|
+
mode TEXT NOT NULL DEFAULT 'act',
|
|
46
|
+
workspace_root TEXT,
|
|
47
|
+
cwd TEXT,
|
|
48
|
+
system_prompt TEXT,
|
|
49
|
+
max_iterations INTEGER,
|
|
50
|
+
timeout_seconds INTEGER,
|
|
51
|
+
max_parallel INTEGER NOT NULL DEFAULT 1,
|
|
52
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
53
|
+
created_at TEXT NOT NULL,
|
|
54
|
+
updated_at TEXT NOT NULL,
|
|
55
|
+
last_run_at TEXT,
|
|
56
|
+
next_run_at TEXT,
|
|
57
|
+
claim_token TEXT,
|
|
58
|
+
claim_started_at TEXT,
|
|
59
|
+
claim_until_at TEXT,
|
|
60
|
+
created_by TEXT,
|
|
61
|
+
tags TEXT,
|
|
62
|
+
metadata_json TEXT
|
|
63
|
+
);`,`CREATE TABLE IF NOT EXISTS schedule_executions (
|
|
64
|
+
execution_id TEXT PRIMARY KEY,
|
|
65
|
+
schedule_id TEXT NOT NULL,
|
|
66
|
+
session_id TEXT,
|
|
67
|
+
triggered_at TEXT NOT NULL,
|
|
68
|
+
started_at TEXT,
|
|
69
|
+
ended_at TEXT,
|
|
70
|
+
status TEXT NOT NULL,
|
|
71
|
+
exit_code INTEGER,
|
|
72
|
+
error_message TEXT,
|
|
73
|
+
iterations INTEGER,
|
|
74
|
+
tokens_used INTEGER,
|
|
75
|
+
cost_usd REAL,
|
|
76
|
+
FOREIGN KEY (schedule_id) REFERENCES schedules(schedule_id) ON DELETE CASCADE,
|
|
77
|
+
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE SET NULL
|
|
78
|
+
);`,`CREATE INDEX IF NOT EXISTS idx_schedule_executions_schedule
|
|
79
|
+
ON schedule_executions(schedule_id, triggered_at DESC);`,`CREATE INDEX IF NOT EXISTS idx_schedules_next_run
|
|
80
|
+
ON schedules(enabled, next_run_at);`],D=[{table:"sessions",column:"workspace_root",sql:"ALTER TABLE sessions ADD COLUMN workspace_root TEXT;"},{table:"sessions",column:"parent_session_id",sql:"ALTER TABLE sessions ADD COLUMN parent_session_id TEXT;"},{table:"sessions",column:"parent_agent_id",sql:"ALTER TABLE sessions ADD COLUMN parent_agent_id TEXT;"},{table:"sessions",column:"agent_id",sql:"ALTER TABLE sessions ADD COLUMN agent_id TEXT;"},{table:"sessions",column:"conversation_id",sql:"ALTER TABLE sessions ADD COLUMN conversation_id TEXT;"},{table:"sessions",column:"is_subagent",sql:"ALTER TABLE sessions ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0;"},{table:"sessions",column:"messages_path",sql:"ALTER TABLE sessions ADD COLUMN messages_path TEXT;"},{table:"sessions",column:"metadata_json",sql:"ALTER TABLE sessions ADD COLUMN metadata_json TEXT;"},{table:"schedules",column:"claim_token",sql:"ALTER TABLE schedules ADD COLUMN claim_token TEXT;"},{table:"schedules",column:"claim_started_at",sql:"ALTER TABLE schedules ADD COLUMN claim_started_at TEXT;"},{table:"schedules",column:"claim_until_at",sql:"ALTER TABLE schedules ADD COLUMN claim_until_at TEXT;"}];function G(S,x){return new Set(S.prepare(`PRAGMA table_info(${x});`).all().map((B)=>B.name))}function R(S,x={}){S.exec("PRAGMA journal_mode = WAL;"),S.exec("PRAGMA busy_timeout = 5000;");for(let j of Y)S.exec(j);if(!x.includeLegacyMigrations)return;let B=new Map,O=(j)=>{let z=B.get(j);if(!z)z=G(S,j),B.set(j,z);return z};for(let j of D)if(!O(j.table).has(j.column)){if(S.exec(j.sql),j.column==="workspace_root")S.exec("UPDATE sessions SET workspace_root = cwd WHERE workspace_root IS NULL OR workspace_root = '';")}}export{Z as toBoolInt,X as nowIso,L as loadSqliteDb,R as ensureSessionSchema,$ as asString,k as asOptionalString,y as asBool};
|
package/dist/rpc/runtime.d.ts
CHANGED
package/dist/storage/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { AGENT_CONFIG_DIRECTORY_NAME, CLINE_MCP_SETTINGS_FILE_NAME, ensureHookLogDir, ensureParentDir, HOOKS_CONFIG_DIRECTORY_NAME, RULES_CONFIG_DIRECTORY_NAME, resolveAgentConfigSearchPaths, resolveAgentsConfigDirPath, resolveClineDataDir, resolveClineDir, resolveDocumentsAgentConfigDirectoryPath, resolveDocumentsClineDirectoryPath, resolveDocumentsHooksDirectoryPath, resolveDocumentsRulesDirectoryPath, resolveDocumentsWorkflowsDirectoryPath, resolveHooksConfigSearchPaths, resolveMcpSettingsPath, resolvePluginConfigSearchPaths, resolveProviderSettingsPath, resolveRulesConfigSearchPaths, resolveSessionDataDir, resolveSkillsConfigSearchPaths, resolveTeamDataDir, resolveWorkflowsConfigSearchPaths, SKILLS_CONFIG_DIRECTORY_NAME, setClineDir, setClineDirIfUnset, setHomeDir, setHomeDirIfUnset, WORKFLOWS_CONFIG_DIRECTORY_NAME, } from "./paths";
|
|
1
|
+
export { AGENT_CONFIG_DIRECTORY_NAME, CLINE_MCP_SETTINGS_FILE_NAME, discoverPluginModulePaths, ensureHookLogDir, ensureParentDir, HOOKS_CONFIG_DIRECTORY_NAME, isPluginModulePath, RULES_CONFIG_DIRECTORY_NAME, resolveAgentConfigSearchPaths, resolveAgentsConfigDirPath, resolveClineDataDir, resolveClineDir, resolveConfiguredPluginModulePaths, resolveDocumentsAgentConfigDirectoryPath, resolveDocumentsClineDirectoryPath, resolveDocumentsHooksDirectoryPath, resolveDocumentsPluginsDirectoryPath, resolveDocumentsRulesDirectoryPath, resolveDocumentsWorkflowsDirectoryPath, resolveHooksConfigSearchPaths, resolveMcpSettingsPath, resolvePluginConfigSearchPaths, resolvePluginModuleEntries, resolveProviderSettingsPath, resolveRulesConfigSearchPaths, resolveSessionDataDir, resolveSkillsConfigSearchPaths, resolveTeamDataDir, resolveWorkflowsConfigSearchPaths, SKILLS_CONFIG_DIRECTORY_NAME, setClineDir, setClineDirIfUnset, setHomeDir, setHomeDirIfUnset, WORKFLOWS_CONFIG_DIRECTORY_NAME, } from "./paths";
|
package/dist/storage/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var s=Object.defineProperty;var n=(C)=>C;function w(C,D){this[C]=n.bind(null,D)}var EC=(C,D)=>{for(var N in D)s(C,N,{get:D[N],enumerable:!0,configurable:!0,set:w.bind(D,N)})};import{existsSync as g,mkdirSync as L,readdirSync as k,readFileSync as i,statSync as G}from"node:fs";import{dirname as U,join as S,resolve as H}from"node:path";var Y="agents",j="hooks",A="skills",q="rules",z="workflows",u="plugins",B="cline_mcp_settings.json",I=process?.env?.HOME||"~",Q=!1;function c(C){let D=C.trim();if(!D)return;I=D,Q=!0}function P(C){if(Q)return;let D=C.trim();if(!D)return;I=D}var T,V=!1;function d(C){let D=C.trim();if(!D)return;T=D,V=!0}function r(C){if(V)return;let D=C.trim();if(!D)return;T=D}function l(){if(T)return T;let C=process.env.CLINE_DIR?.trim();if(C)return C;return S(I,".cline")}function M(){return S(I,"Documents","Cline")}function Z(){return S(M(),"Agents")}function $(){return S(M(),"Hooks")}function f(){return S(M(),"Rules")}function X(){return S(M(),"Workflows")}function J(){return S(I,"Documents","Plugins")}function R(){let C=process.env.CLINE_DATA_DIR?.trim();if(C)return C;return S(l(),"data")}function h(){let C=process.env.CLINE_SESSION_DATA_DIR?.trim();if(C)return C;return S(R(),"sessions")}function p(){let C=process.env.CLINE_TEAM_DATA_DIR?.trim();if(C)return C;return S(R(),"teams")}function e(){let C=process.env.CLINE_PROVIDER_SETTINGS_PATH?.trim();if(C)return C;return S(R(),"settings","providers.json")}function t(){let C=process.env.CLINE_MCP_SETTINGS_PATH?.trim();if(C)return C;return S(R(),"settings",B)}function E(C){let D=new Set,N=[];for(let _ of C){if(!_||D.has(_))continue;D.add(_),N.push(_)}return N}function a(C){if(!C)return[];return[S(C,".clinerules",A),S(C,".cline",A),S(C,".claude",A),S(C,".agents",A)]}function x(){return S(R(),"settings",Y)}function CC(){return[Z(),x()]}function DC(C){return E([C?S(C,".clinerules",j):"",$()])}function SC(C){return E([...a(C),S(R(),"settings",A),S(l(),A),S(I,".agents",A)])}function vC(C){return E([C?S(C,".clinerules"):"",S(R(),"settings",q),f()])}function NC(C){return E([C?S(C,".clinerules","workflows"):"",S(R(),"settings",z),X()])}function OC(C){return E([C?S(C,".clinerules",u):"",S(l(),u),J()])}var b=new Set([".js",".mjs",".cjs",".ts",".mts",".cts"]),_C="package.json",RC=["index.ts","index.mts","index.cts","index.js","index.mjs","index.cjs"];function K(C){let D=C.lastIndexOf(".");if(D===-1)return!1;return b.has(C.slice(D))}function gC(C){try{let D=JSON.parse(i(C,"utf8"));if(!D.cline||typeof D.cline!=="object")return null;return D.cline}catch{return null}}function AC(C){let D=C?.plugins??C?.extensions;if(!Array.isArray(D))return[];return D.filter((N)=>typeof N==="string")}function o(C){let D=H(C);if(!g(D)||!G(D).isDirectory())return null;let N=S(D,_C);if(g(N)){let _=gC(N),O=AC(_).map((v)=>H(D,v)).filter((v)=>g(v)&&G(v).isFile()&&K(v));if(O.length>0)return O}for(let _ of RC){let O=S(D,_);if(g(O)&&G(O).isFile())return[O]}return null}function m(C){let D=H(C);if(!g(D))return[];let N=[],_=[D];while(_.length>0){let O=_.pop();if(!O)continue;for(let v of k(O,{withFileTypes:!0})){let F=S(O,v.name);if(v.isDirectory()){_.push(F);continue}if(v.name.startsWith("."))continue;if(v.isFile()&&K(F))N.push(F)}}return N.sort((O,v)=>O.localeCompare(v))}function IC(C,D){let N=[];for(let _ of C){let O=_.trim();if(!O)continue;let v=H(D,O);if(!g(v))throw Error(`Plugin path does not exist: ${v}`);if(G(v).isDirectory()){let W=o(v);if(W){N.push(...W);continue}N.push(...m(v));continue}if(!K(v))throw Error(`Plugin file must use a supported extension (${[...b].join(", ")}): ${v}`);N.push(v)}return N}function y(C){let D=U(C);if(!g(D))L(D,{recursive:!0})}function MC(C){if(C?.trim())return y(C),U(C);let D=S(R(),"hooks");if(!g(D))L(D,{recursive:!0});return D}export{P as setHomeDirIfUnset,c as setHomeDir,r as setClineDirIfUnset,d as setClineDir,NC as resolveWorkflowsConfigSearchPaths,p as resolveTeamDataDir,SC as resolveSkillsConfigSearchPaths,h as resolveSessionDataDir,vC as resolveRulesConfigSearchPaths,e as resolveProviderSettingsPath,o as resolvePluginModuleEntries,OC as resolvePluginConfigSearchPaths,t as resolveMcpSettingsPath,DC as resolveHooksConfigSearchPaths,X as resolveDocumentsWorkflowsDirectoryPath,f as resolveDocumentsRulesDirectoryPath,J as resolveDocumentsPluginsDirectoryPath,$ as resolveDocumentsHooksDirectoryPath,M as resolveDocumentsClineDirectoryPath,Z as resolveDocumentsAgentConfigDirectoryPath,IC as resolveConfiguredPluginModulePaths,l as resolveClineDir,R as resolveClineDataDir,x as resolveAgentsConfigDirPath,CC as resolveAgentConfigSearchPaths,K as isPluginModulePath,y as ensureParentDir,MC as ensureHookLogDir,m as discoverPluginModulePaths,z as WORKFLOWS_CONFIG_DIRECTORY_NAME,A as SKILLS_CONFIG_DIRECTORY_NAME,q as RULES_CONFIG_DIRECTORY_NAME,j as HOOKS_CONFIG_DIRECTORY_NAME,B as CLINE_MCP_SETTINGS_FILE_NAME,Y as AGENT_CONFIG_DIRECTORY_NAME};
|
package/dist/storage/paths.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare function resolveDocumentsAgentConfigDirectoryPath(): string;
|
|
|
15
15
|
export declare function resolveDocumentsHooksDirectoryPath(): string;
|
|
16
16
|
export declare function resolveDocumentsRulesDirectoryPath(): string;
|
|
17
17
|
export declare function resolveDocumentsWorkflowsDirectoryPath(): string;
|
|
18
|
+
export declare function resolveDocumentsPluginsDirectoryPath(): string;
|
|
18
19
|
export declare function resolveClineDataDir(): string;
|
|
19
20
|
export declare function resolveSessionDataDir(): string;
|
|
20
21
|
export declare function resolveTeamDataDir(): string;
|
|
@@ -27,5 +28,9 @@ export declare function resolveSkillsConfigSearchPaths(workspacePath?: string):
|
|
|
27
28
|
export declare function resolveRulesConfigSearchPaths(workspacePath?: string): string[];
|
|
28
29
|
export declare function resolveWorkflowsConfigSearchPaths(workspacePath?: string): string[];
|
|
29
30
|
export declare function resolvePluginConfigSearchPaths(workspacePath?: string): string[];
|
|
31
|
+
export declare function isPluginModulePath(path: string): boolean;
|
|
32
|
+
export declare function resolvePluginModuleEntries(directoryPath: string): string[] | null;
|
|
33
|
+
export declare function discoverPluginModulePaths(directoryPath: string): string[];
|
|
34
|
+
export declare function resolveConfiguredPluginModulePaths(pluginPaths: ReadonlyArray<string>, cwd: string): string[];
|
|
30
35
|
export declare function ensureParentDir(filePath: string): void;
|
|
31
36
|
export declare function ensureHookLogDir(filePath?: string): string;
|
package/package.json
CHANGED
package/src/db/sqlite-db.ts
CHANGED
|
@@ -13,26 +13,6 @@ export type SqliteDb = {
|
|
|
13
13
|
exec: (sql: string) => void;
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
type BunSqliteDb = {
|
|
17
|
-
query: (sql: string) => {
|
|
18
|
-
run: (...params: unknown[]) => { changes?: number };
|
|
19
|
-
get: (...params: unknown[]) => Record<string, unknown> | null;
|
|
20
|
-
all: (...params: unknown[]) => Record<string, unknown>[];
|
|
21
|
-
};
|
|
22
|
-
exec: (sql: string) => void;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
type NodeSqliteStatement = {
|
|
26
|
-
run: (...params: unknown[]) => { changes?: number };
|
|
27
|
-
get: (...params: unknown[]) => Record<string, unknown> | undefined;
|
|
28
|
-
all: (...params: unknown[]) => Record<string, unknown>[];
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
type NodeSqliteDb = {
|
|
32
|
-
prepare: (sql: string) => NodeSqliteStatement;
|
|
33
|
-
exec: (sql: string) => void;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
16
|
export function nowIso(): string {
|
|
37
17
|
return new Date().toISOString();
|
|
38
18
|
}
|
|
@@ -46,9 +26,7 @@ export function asString(value: unknown): string {
|
|
|
46
26
|
}
|
|
47
27
|
|
|
48
28
|
export function asOptionalString(value: unknown): string | undefined {
|
|
49
|
-
if (typeof value !== "string")
|
|
50
|
-
return undefined;
|
|
51
|
-
}
|
|
29
|
+
if (typeof value !== "string") return undefined;
|
|
52
30
|
const trimmed = value.trim();
|
|
53
31
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
54
32
|
}
|
|
@@ -57,59 +35,83 @@ export function asBool(value: unknown): boolean {
|
|
|
57
35
|
return value === 1 || value === true;
|
|
58
36
|
}
|
|
59
37
|
|
|
38
|
+
function wrapBunDb(db: {
|
|
39
|
+
query: (sql: string) => SqliteStatement;
|
|
40
|
+
exec: (sql: string) => void;
|
|
41
|
+
}): SqliteDb {
|
|
42
|
+
return {
|
|
43
|
+
prepare: (sql) => db.query(sql),
|
|
44
|
+
exec: (sql) => db.exec(sql),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function wrapNodeDb(db: {
|
|
49
|
+
prepare: (sql: string) => {
|
|
50
|
+
run: (...params: unknown[]) => { changes?: number };
|
|
51
|
+
get: (...params: unknown[]) => Record<string, unknown> | undefined;
|
|
52
|
+
all: (...params: unknown[]) => Record<string, unknown>[];
|
|
53
|
+
};
|
|
54
|
+
exec: (sql: string) => void;
|
|
55
|
+
}): SqliteDb {
|
|
56
|
+
return {
|
|
57
|
+
prepare: (sql) => {
|
|
58
|
+
const stmt = db.prepare(sql);
|
|
59
|
+
return {
|
|
60
|
+
run: (...params) => stmt.run(...params),
|
|
61
|
+
get: (...params) => stmt.get(...params) ?? null,
|
|
62
|
+
all: (...params) => stmt.all(...params),
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
exec: (sql) => db.exec(sql),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
export function loadSqliteDb(filePath: string): SqliteDb {
|
|
61
70
|
mkdirSync(dirname(filePath), { recursive: true });
|
|
62
71
|
const require = createRequire(import.meta.url);
|
|
63
|
-
const isBunRuntime =
|
|
64
|
-
typeof (globalThis as { Bun?: unknown }).Bun !== "undefined";
|
|
65
72
|
|
|
66
|
-
if (
|
|
73
|
+
if (typeof (globalThis as { Bun?: unknown }).Bun !== "undefined") {
|
|
67
74
|
const { Database } = require("bun:sqlite") as {
|
|
68
75
|
Database: new (
|
|
69
76
|
path: string,
|
|
70
|
-
options?: { create?: boolean
|
|
71
|
-
) =>
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
prepare: (sql: string): SqliteStatement => {
|
|
77
|
-
const query = db.query(sql);
|
|
78
|
-
return {
|
|
79
|
-
run: (...params: unknown[]) => query.run(...params),
|
|
80
|
-
get: (...params: unknown[]) => query.get(...params),
|
|
81
|
-
all: (...params: unknown[]) => query.all(...params),
|
|
82
|
-
};
|
|
83
|
-
},
|
|
84
|
-
exec: (sql: string) => db.exec(sql),
|
|
77
|
+
options?: { create?: boolean },
|
|
78
|
+
) => {
|
|
79
|
+
query: (sql: string) => SqliteStatement;
|
|
80
|
+
exec: (sql: string) => void;
|
|
81
|
+
};
|
|
85
82
|
};
|
|
83
|
+
return wrapBunDb(new Database(filePath, { create: true }));
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
try {
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
// Suppress "ExperimentalWarning: SQLite is an experimental feature"
|
|
88
|
+
const originalEmit = process.emitWarning;
|
|
89
|
+
process.emitWarning = ((warning: string | Error, ...args: unknown[]) => {
|
|
90
|
+
const msg =
|
|
91
|
+
typeof warning === "string" ? warning : (warning?.message ?? "");
|
|
92
|
+
if (msg.includes("SQLite")) return;
|
|
93
|
+
return (originalEmit as Function).call(process, warning, ...args);
|
|
94
|
+
}) as typeof process.emitWarning;
|
|
95
|
+
|
|
96
|
+
const { DatabaseSync } = require(["node", ":sqlite"].join("")) as {
|
|
97
|
+
DatabaseSync: new (
|
|
98
|
+
path: string,
|
|
99
|
+
) => {
|
|
100
|
+
prepare: (sql: string) => {
|
|
101
|
+
run: (...params: unknown[]) => { changes?: number };
|
|
102
|
+
get: (...params: unknown[]) => Record<string, unknown> | undefined;
|
|
103
|
+
all: (...params: unknown[]) => Record<string, unknown>[];
|
|
101
104
|
};
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
exec: (sql: string) => void;
|
|
106
|
+
};
|
|
104
107
|
};
|
|
108
|
+
process.emitWarning = originalEmit;
|
|
109
|
+
return wrapNodeDb(new DatabaseSync(filePath));
|
|
105
110
|
} catch {
|
|
106
|
-
// Fall through to better-sqlite3 for older Node runtimes
|
|
111
|
+
// Fall through to better-sqlite3 for older Node runtimes.
|
|
107
112
|
}
|
|
108
113
|
|
|
109
|
-
|
|
110
|
-
// better-sqlite3 when this Node-only path is not executed.
|
|
111
|
-
const betterSqlite3ModuleName = ["better", "-sqlite3"].join("");
|
|
112
|
-
const BetterSqlite3 = require(betterSqlite3ModuleName) as new (
|
|
114
|
+
const BetterSqlite3 = require(["better", "-sqlite3"].join("")) as new (
|
|
113
115
|
path: string,
|
|
114
116
|
) => SqliteDb;
|
|
115
117
|
return new BetterSqlite3(filePath);
|
|
@@ -119,156 +121,195 @@ export interface SessionSchemaOptions {
|
|
|
119
121
|
includeLegacyMigrations?: boolean;
|
|
120
122
|
}
|
|
121
123
|
|
|
124
|
+
const SCHEMA_STATEMENTS = [
|
|
125
|
+
`CREATE TABLE IF NOT EXISTS sessions (
|
|
126
|
+
session_id TEXT PRIMARY KEY,
|
|
127
|
+
source TEXT NOT NULL,
|
|
128
|
+
pid INTEGER NOT NULL,
|
|
129
|
+
started_at TEXT NOT NULL,
|
|
130
|
+
ended_at TEXT,
|
|
131
|
+
exit_code INTEGER,
|
|
132
|
+
status TEXT NOT NULL,
|
|
133
|
+
status_lock INTEGER NOT NULL DEFAULT 0,
|
|
134
|
+
interactive INTEGER NOT NULL,
|
|
135
|
+
provider TEXT NOT NULL,
|
|
136
|
+
model TEXT NOT NULL,
|
|
137
|
+
cwd TEXT NOT NULL,
|
|
138
|
+
workspace_root TEXT NOT NULL,
|
|
139
|
+
team_name TEXT,
|
|
140
|
+
enable_tools INTEGER NOT NULL,
|
|
141
|
+
enable_spawn INTEGER NOT NULL,
|
|
142
|
+
enable_teams INTEGER NOT NULL,
|
|
143
|
+
parent_session_id TEXT,
|
|
144
|
+
parent_agent_id TEXT,
|
|
145
|
+
agent_id TEXT,
|
|
146
|
+
conversation_id TEXT,
|
|
147
|
+
is_subagent INTEGER NOT NULL DEFAULT 0,
|
|
148
|
+
prompt TEXT,
|
|
149
|
+
metadata_json TEXT,
|
|
150
|
+
transcript_path TEXT NOT NULL,
|
|
151
|
+
hook_path TEXT NOT NULL,
|
|
152
|
+
messages_path TEXT,
|
|
153
|
+
updated_at TEXT NOT NULL
|
|
154
|
+
);`,
|
|
155
|
+
`CREATE TABLE IF NOT EXISTS subagent_spawn_queue (
|
|
156
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
157
|
+
root_session_id TEXT NOT NULL,
|
|
158
|
+
parent_agent_id TEXT NOT NULL,
|
|
159
|
+
task TEXT,
|
|
160
|
+
system_prompt TEXT,
|
|
161
|
+
created_at TEXT NOT NULL,
|
|
162
|
+
consumed_at TEXT
|
|
163
|
+
);`,
|
|
164
|
+
`CREATE TABLE IF NOT EXISTS schedules (
|
|
165
|
+
schedule_id TEXT PRIMARY KEY,
|
|
166
|
+
name TEXT NOT NULL,
|
|
167
|
+
cron_pattern TEXT NOT NULL,
|
|
168
|
+
prompt TEXT NOT NULL,
|
|
169
|
+
provider TEXT NOT NULL,
|
|
170
|
+
model TEXT NOT NULL,
|
|
171
|
+
mode TEXT NOT NULL DEFAULT 'act',
|
|
172
|
+
workspace_root TEXT,
|
|
173
|
+
cwd TEXT,
|
|
174
|
+
system_prompt TEXT,
|
|
175
|
+
max_iterations INTEGER,
|
|
176
|
+
timeout_seconds INTEGER,
|
|
177
|
+
max_parallel INTEGER NOT NULL DEFAULT 1,
|
|
178
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
179
|
+
created_at TEXT NOT NULL,
|
|
180
|
+
updated_at TEXT NOT NULL,
|
|
181
|
+
last_run_at TEXT,
|
|
182
|
+
next_run_at TEXT,
|
|
183
|
+
claim_token TEXT,
|
|
184
|
+
claim_started_at TEXT,
|
|
185
|
+
claim_until_at TEXT,
|
|
186
|
+
created_by TEXT,
|
|
187
|
+
tags TEXT,
|
|
188
|
+
metadata_json TEXT
|
|
189
|
+
);`,
|
|
190
|
+
`CREATE TABLE IF NOT EXISTS schedule_executions (
|
|
191
|
+
execution_id TEXT PRIMARY KEY,
|
|
192
|
+
schedule_id TEXT NOT NULL,
|
|
193
|
+
session_id TEXT,
|
|
194
|
+
triggered_at TEXT NOT NULL,
|
|
195
|
+
started_at TEXT,
|
|
196
|
+
ended_at TEXT,
|
|
197
|
+
status TEXT NOT NULL,
|
|
198
|
+
exit_code INTEGER,
|
|
199
|
+
error_message TEXT,
|
|
200
|
+
iterations INTEGER,
|
|
201
|
+
tokens_used INTEGER,
|
|
202
|
+
cost_usd REAL,
|
|
203
|
+
FOREIGN KEY (schedule_id) REFERENCES schedules(schedule_id) ON DELETE CASCADE,
|
|
204
|
+
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE SET NULL
|
|
205
|
+
);`,
|
|
206
|
+
`CREATE INDEX IF NOT EXISTS idx_schedule_executions_schedule
|
|
207
|
+
ON schedule_executions(schedule_id, triggered_at DESC);`,
|
|
208
|
+
`CREATE INDEX IF NOT EXISTS idx_schedules_next_run
|
|
209
|
+
ON schedules(enabled, next_run_at);`,
|
|
210
|
+
];
|
|
211
|
+
|
|
212
|
+
const LEGACY_MIGRATIONS: Array<{
|
|
213
|
+
table: string;
|
|
214
|
+
column: string;
|
|
215
|
+
sql: string;
|
|
216
|
+
}> = [
|
|
217
|
+
{
|
|
218
|
+
table: "sessions",
|
|
219
|
+
column: "workspace_root",
|
|
220
|
+
sql: "ALTER TABLE sessions ADD COLUMN workspace_root TEXT;",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
table: "sessions",
|
|
224
|
+
column: "parent_session_id",
|
|
225
|
+
sql: "ALTER TABLE sessions ADD COLUMN parent_session_id TEXT;",
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
table: "sessions",
|
|
229
|
+
column: "parent_agent_id",
|
|
230
|
+
sql: "ALTER TABLE sessions ADD COLUMN parent_agent_id TEXT;",
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
table: "sessions",
|
|
234
|
+
column: "agent_id",
|
|
235
|
+
sql: "ALTER TABLE sessions ADD COLUMN agent_id TEXT;",
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
table: "sessions",
|
|
239
|
+
column: "conversation_id",
|
|
240
|
+
sql: "ALTER TABLE sessions ADD COLUMN conversation_id TEXT;",
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
table: "sessions",
|
|
244
|
+
column: "is_subagent",
|
|
245
|
+
sql: "ALTER TABLE sessions ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0;",
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
table: "sessions",
|
|
249
|
+
column: "messages_path",
|
|
250
|
+
sql: "ALTER TABLE sessions ADD COLUMN messages_path TEXT;",
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
table: "sessions",
|
|
254
|
+
column: "metadata_json",
|
|
255
|
+
sql: "ALTER TABLE sessions ADD COLUMN metadata_json TEXT;",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
table: "schedules",
|
|
259
|
+
column: "claim_token",
|
|
260
|
+
sql: "ALTER TABLE schedules ADD COLUMN claim_token TEXT;",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
table: "schedules",
|
|
264
|
+
column: "claim_started_at",
|
|
265
|
+
sql: "ALTER TABLE schedules ADD COLUMN claim_started_at TEXT;",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
table: "schedules",
|
|
269
|
+
column: "claim_until_at",
|
|
270
|
+
sql: "ALTER TABLE schedules ADD COLUMN claim_until_at TEXT;",
|
|
271
|
+
},
|
|
272
|
+
];
|
|
273
|
+
|
|
274
|
+
function getColumnNames(db: SqliteDb, table: string): Set<string> {
|
|
275
|
+
return new Set(
|
|
276
|
+
db
|
|
277
|
+
.prepare(`PRAGMA table_info(${table});`)
|
|
278
|
+
.all()
|
|
279
|
+
.map((c) => c.name as string),
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
122
283
|
export function ensureSessionSchema(
|
|
123
284
|
db: SqliteDb,
|
|
124
285
|
options: SessionSchemaOptions = {},
|
|
125
286
|
): void {
|
|
126
287
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
127
288
|
db.exec("PRAGMA busy_timeout = 5000;");
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
session_id TEXT PRIMARY KEY,
|
|
131
|
-
source TEXT NOT NULL,
|
|
132
|
-
pid INTEGER NOT NULL,
|
|
133
|
-
started_at TEXT NOT NULL,
|
|
134
|
-
ended_at TEXT,
|
|
135
|
-
exit_code INTEGER,
|
|
136
|
-
status TEXT NOT NULL,
|
|
137
|
-
status_lock INTEGER NOT NULL DEFAULT 0,
|
|
138
|
-
interactive INTEGER NOT NULL,
|
|
139
|
-
provider TEXT NOT NULL,
|
|
140
|
-
model TEXT NOT NULL,
|
|
141
|
-
cwd TEXT NOT NULL,
|
|
142
|
-
workspace_root TEXT NOT NULL,
|
|
143
|
-
team_name TEXT,
|
|
144
|
-
enable_tools INTEGER NOT NULL,
|
|
145
|
-
enable_spawn INTEGER NOT NULL,
|
|
146
|
-
enable_teams INTEGER NOT NULL,
|
|
147
|
-
parent_session_id TEXT,
|
|
148
|
-
parent_agent_id TEXT,
|
|
149
|
-
agent_id TEXT,
|
|
150
|
-
conversation_id TEXT,
|
|
151
|
-
is_subagent INTEGER NOT NULL DEFAULT 0,
|
|
152
|
-
prompt TEXT,
|
|
153
|
-
metadata_json TEXT,
|
|
154
|
-
transcript_path TEXT NOT NULL,
|
|
155
|
-
hook_path TEXT NOT NULL,
|
|
156
|
-
messages_path TEXT,
|
|
157
|
-
updated_at TEXT NOT NULL
|
|
158
|
-
);
|
|
159
|
-
`);
|
|
160
|
-
db.exec(`
|
|
161
|
-
CREATE TABLE IF NOT EXISTS subagent_spawn_queue (
|
|
162
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
163
|
-
root_session_id TEXT NOT NULL,
|
|
164
|
-
parent_agent_id TEXT NOT NULL,
|
|
165
|
-
task TEXT,
|
|
166
|
-
system_prompt TEXT,
|
|
167
|
-
created_at TEXT NOT NULL,
|
|
168
|
-
consumed_at TEXT
|
|
169
|
-
);
|
|
170
|
-
`);
|
|
171
|
-
db.exec(`
|
|
172
|
-
CREATE TABLE IF NOT EXISTS schedules (
|
|
173
|
-
schedule_id TEXT PRIMARY KEY,
|
|
174
|
-
name TEXT NOT NULL,
|
|
175
|
-
cron_pattern TEXT NOT NULL,
|
|
176
|
-
prompt TEXT NOT NULL,
|
|
177
|
-
provider TEXT NOT NULL,
|
|
178
|
-
model TEXT NOT NULL,
|
|
179
|
-
mode TEXT NOT NULL DEFAULT 'act',
|
|
180
|
-
workspace_root TEXT,
|
|
181
|
-
cwd TEXT,
|
|
182
|
-
system_prompt TEXT,
|
|
183
|
-
max_iterations INTEGER,
|
|
184
|
-
timeout_seconds INTEGER,
|
|
185
|
-
max_parallel INTEGER NOT NULL DEFAULT 1,
|
|
186
|
-
enabled INTEGER NOT NULL DEFAULT 1,
|
|
187
|
-
created_at TEXT NOT NULL,
|
|
188
|
-
updated_at TEXT NOT NULL,
|
|
189
|
-
last_run_at TEXT,
|
|
190
|
-
next_run_at TEXT,
|
|
191
|
-
claim_token TEXT,
|
|
192
|
-
claim_started_at TEXT,
|
|
193
|
-
claim_until_at TEXT,
|
|
194
|
-
created_by TEXT,
|
|
195
|
-
tags TEXT,
|
|
196
|
-
metadata_json TEXT
|
|
197
|
-
);
|
|
198
|
-
`);
|
|
199
|
-
db.exec(`
|
|
200
|
-
CREATE TABLE IF NOT EXISTS schedule_executions (
|
|
201
|
-
execution_id TEXT PRIMARY KEY,
|
|
202
|
-
schedule_id TEXT NOT NULL,
|
|
203
|
-
session_id TEXT,
|
|
204
|
-
triggered_at TEXT NOT NULL,
|
|
205
|
-
started_at TEXT,
|
|
206
|
-
ended_at TEXT,
|
|
207
|
-
status TEXT NOT NULL,
|
|
208
|
-
exit_code INTEGER,
|
|
209
|
-
error_message TEXT,
|
|
210
|
-
iterations INTEGER,
|
|
211
|
-
tokens_used INTEGER,
|
|
212
|
-
cost_usd REAL,
|
|
213
|
-
FOREIGN KEY (schedule_id) REFERENCES schedules(schedule_id) ON DELETE CASCADE,
|
|
214
|
-
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE SET NULL
|
|
215
|
-
);
|
|
216
|
-
`);
|
|
217
|
-
db.exec(`
|
|
218
|
-
CREATE INDEX IF NOT EXISTS idx_schedule_executions_schedule
|
|
219
|
-
ON schedule_executions(schedule_id, triggered_at DESC);
|
|
220
|
-
`);
|
|
221
|
-
db.exec(`
|
|
222
|
-
CREATE INDEX IF NOT EXISTS idx_schedules_next_run
|
|
223
|
-
ON schedules(enabled, next_run_at);
|
|
224
|
-
`);
|
|
225
|
-
|
|
226
|
-
if (!options.includeLegacyMigrations) {
|
|
227
|
-
return;
|
|
289
|
+
for (const stmt of SCHEMA_STATEMENTS) {
|
|
290
|
+
db.exec(stmt);
|
|
228
291
|
}
|
|
229
292
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if (!hasColumn("is_subagent")) {
|
|
252
|
-
db.exec(
|
|
253
|
-
"ALTER TABLE sessions ADD COLUMN is_subagent INTEGER NOT NULL DEFAULT 0;",
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
if (!hasColumn("messages_path")) {
|
|
257
|
-
db.exec("ALTER TABLE sessions ADD COLUMN messages_path TEXT;");
|
|
258
|
-
}
|
|
259
|
-
if (!hasColumn("metadata_json")) {
|
|
260
|
-
db.exec("ALTER TABLE sessions ADD COLUMN metadata_json TEXT;");
|
|
261
|
-
}
|
|
262
|
-
const scheduleColumns = db.prepare("PRAGMA table_info(schedules);").all();
|
|
263
|
-
const scheduleHasColumn = (name: string): boolean =>
|
|
264
|
-
scheduleColumns.some((column) => column.name === name);
|
|
265
|
-
if (!scheduleHasColumn("claim_token")) {
|
|
266
|
-
db.exec("ALTER TABLE schedules ADD COLUMN claim_token TEXT;");
|
|
267
|
-
}
|
|
268
|
-
if (!scheduleHasColumn("claim_started_at")) {
|
|
269
|
-
db.exec("ALTER TABLE schedules ADD COLUMN claim_started_at TEXT;");
|
|
270
|
-
}
|
|
271
|
-
if (!scheduleHasColumn("claim_until_at")) {
|
|
272
|
-
db.exec("ALTER TABLE schedules ADD COLUMN claim_until_at TEXT;");
|
|
293
|
+
if (!options.includeLegacyMigrations) return;
|
|
294
|
+
|
|
295
|
+
const columnCache = new Map<string, Set<string>>();
|
|
296
|
+
const getColumns = (table: string) => {
|
|
297
|
+
let cols = columnCache.get(table);
|
|
298
|
+
if (!cols) {
|
|
299
|
+
cols = getColumnNames(db, table);
|
|
300
|
+
columnCache.set(table, cols);
|
|
301
|
+
}
|
|
302
|
+
return cols;
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
for (const migration of LEGACY_MIGRATIONS) {
|
|
306
|
+
if (!getColumns(migration.table).has(migration.column)) {
|
|
307
|
+
db.exec(migration.sql);
|
|
308
|
+
if (migration.column === "workspace_root") {
|
|
309
|
+
db.exec(
|
|
310
|
+
"UPDATE sessions SET workspace_root = cwd WHERE workspace_root IS NULL OR workspace_root = '';",
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
273
314
|
}
|
|
274
315
|
}
|
package/src/rpc/runtime.ts
CHANGED
package/src/storage/index.ts
CHANGED
|
@@ -1,22 +1,27 @@
|
|
|
1
1
|
export {
|
|
2
2
|
AGENT_CONFIG_DIRECTORY_NAME,
|
|
3
3
|
CLINE_MCP_SETTINGS_FILE_NAME,
|
|
4
|
+
discoverPluginModulePaths,
|
|
4
5
|
ensureHookLogDir,
|
|
5
6
|
ensureParentDir,
|
|
6
7
|
HOOKS_CONFIG_DIRECTORY_NAME,
|
|
8
|
+
isPluginModulePath,
|
|
7
9
|
RULES_CONFIG_DIRECTORY_NAME,
|
|
8
10
|
resolveAgentConfigSearchPaths,
|
|
9
11
|
resolveAgentsConfigDirPath,
|
|
10
12
|
resolveClineDataDir,
|
|
11
13
|
resolveClineDir,
|
|
14
|
+
resolveConfiguredPluginModulePaths,
|
|
12
15
|
resolveDocumentsAgentConfigDirectoryPath,
|
|
13
16
|
resolveDocumentsClineDirectoryPath,
|
|
14
17
|
resolveDocumentsHooksDirectoryPath,
|
|
18
|
+
resolveDocumentsPluginsDirectoryPath,
|
|
15
19
|
resolveDocumentsRulesDirectoryPath,
|
|
16
20
|
resolveDocumentsWorkflowsDirectoryPath,
|
|
17
21
|
resolveHooksConfigSearchPaths,
|
|
18
22
|
resolveMcpSettingsPath,
|
|
19
23
|
resolvePluginConfigSearchPaths,
|
|
24
|
+
resolvePluginModuleEntries,
|
|
20
25
|
resolveProviderSettingsPath,
|
|
21
26
|
resolveRulesConfigSearchPaths,
|
|
22
27
|
resolveSessionDataDir,
|
package/src/storage/paths.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
statSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
9
|
|
|
4
10
|
export const AGENT_CONFIG_DIRECTORY_NAME = "agents";
|
|
5
11
|
export const HOOKS_CONFIG_DIRECTORY_NAME = "hooks";
|
|
@@ -86,6 +92,10 @@ export function resolveDocumentsWorkflowsDirectoryPath(): string {
|
|
|
86
92
|
return join(resolveDocumentsClineDirectoryPath(), "Workflows");
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
export function resolveDocumentsPluginsDirectoryPath(): string {
|
|
96
|
+
return join(HOME_DIR, "Documents", "Plugins");
|
|
97
|
+
}
|
|
98
|
+
|
|
89
99
|
export function resolveClineDataDir(): string {
|
|
90
100
|
const explicitDir = process.env.CLINE_DATA_DIR?.trim();
|
|
91
101
|
if (explicitDir) {
|
|
@@ -212,10 +222,164 @@ export function resolvePluginConfigSearchPaths(
|
|
|
212
222
|
? join(workspacePath, ".clinerules", PLUGINS_DIRECTORY_NAME)
|
|
213
223
|
: "",
|
|
214
224
|
join(resolveClineDir(), PLUGINS_DIRECTORY_NAME),
|
|
215
|
-
|
|
225
|
+
resolveDocumentsPluginsDirectoryPath(),
|
|
216
226
|
]);
|
|
217
227
|
}
|
|
218
228
|
|
|
229
|
+
const PLUGIN_MODULE_EXTENSIONS = new Set([
|
|
230
|
+
".js",
|
|
231
|
+
".mjs",
|
|
232
|
+
".cjs",
|
|
233
|
+
".ts",
|
|
234
|
+
".mts",
|
|
235
|
+
".cts",
|
|
236
|
+
]);
|
|
237
|
+
const PLUGIN_PACKAGE_JSON_FILE_NAME = "package.json";
|
|
238
|
+
const PLUGIN_DIRECTORY_INDEX_CANDIDATES = [
|
|
239
|
+
"index.ts",
|
|
240
|
+
"index.mts",
|
|
241
|
+
"index.cts",
|
|
242
|
+
"index.js",
|
|
243
|
+
"index.mjs",
|
|
244
|
+
"index.cjs",
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
interface PluginPackageManifest {
|
|
248
|
+
plugins?: unknown;
|
|
249
|
+
extensions?: unknown;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export function isPluginModulePath(path: string): boolean {
|
|
253
|
+
const dot = path.lastIndexOf(".");
|
|
254
|
+
if (dot === -1) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
return PLUGIN_MODULE_EXTENSIONS.has(path.slice(dot));
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function readPluginPackageManifest(
|
|
261
|
+
packageJsonPath: string,
|
|
262
|
+
): PluginPackageManifest | null {
|
|
263
|
+
try {
|
|
264
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8")) as {
|
|
265
|
+
cline?: PluginPackageManifest;
|
|
266
|
+
};
|
|
267
|
+
if (!packageJson.cline || typeof packageJson.cline !== "object") {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
return packageJson.cline;
|
|
271
|
+
} catch {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function getManifestPluginEntries(
|
|
277
|
+
manifest: PluginPackageManifest | null,
|
|
278
|
+
): string[] {
|
|
279
|
+
const entries = manifest?.plugins ?? manifest?.extensions;
|
|
280
|
+
if (!Array.isArray(entries)) {
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
return entries.filter((entry): entry is string => typeof entry === "string");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function resolvePluginModuleEntries(
|
|
287
|
+
directoryPath: string,
|
|
288
|
+
): string[] | null {
|
|
289
|
+
const root = resolve(directoryPath);
|
|
290
|
+
if (!existsSync(root) || !statSync(root).isDirectory()) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const packageJsonPath = join(root, PLUGIN_PACKAGE_JSON_FILE_NAME);
|
|
295
|
+
if (existsSync(packageJsonPath)) {
|
|
296
|
+
const manifest = readPluginPackageManifest(packageJsonPath);
|
|
297
|
+
const entries = getManifestPluginEntries(manifest)
|
|
298
|
+
.map((entry) => resolve(root, entry))
|
|
299
|
+
.filter(
|
|
300
|
+
(entryPath) =>
|
|
301
|
+
existsSync(entryPath) &&
|
|
302
|
+
statSync(entryPath).isFile() &&
|
|
303
|
+
isPluginModulePath(entryPath),
|
|
304
|
+
);
|
|
305
|
+
if (entries.length > 0) {
|
|
306
|
+
return entries;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
for (const candidate of PLUGIN_DIRECTORY_INDEX_CANDIDATES) {
|
|
311
|
+
const entryPath = join(root, candidate);
|
|
312
|
+
if (existsSync(entryPath) && statSync(entryPath).isFile()) {
|
|
313
|
+
return [entryPath];
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function discoverPluginModulePaths(directoryPath: string): string[] {
|
|
321
|
+
const root = resolve(directoryPath);
|
|
322
|
+
if (!existsSync(root)) {
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
325
|
+
const discovered: string[] = [];
|
|
326
|
+
const stack = [root];
|
|
327
|
+
while (stack.length > 0) {
|
|
328
|
+
const current = stack.pop();
|
|
329
|
+
if (!current) {
|
|
330
|
+
continue;
|
|
331
|
+
}
|
|
332
|
+
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
333
|
+
const candidate = join(current, entry.name);
|
|
334
|
+
if (entry.isDirectory()) {
|
|
335
|
+
stack.push(candidate);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (entry.name.startsWith(".")) {
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (entry.isFile() && isPluginModulePath(candidate)) {
|
|
342
|
+
discovered.push(candidate);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
return discovered.sort((a, b) => a.localeCompare(b));
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function resolveConfiguredPluginModulePaths(
|
|
350
|
+
pluginPaths: ReadonlyArray<string>,
|
|
351
|
+
cwd: string,
|
|
352
|
+
): string[] {
|
|
353
|
+
const resolvedPaths: string[] = [];
|
|
354
|
+
for (const pluginPath of pluginPaths) {
|
|
355
|
+
const trimmed = pluginPath.trim();
|
|
356
|
+
if (!trimmed) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const absolutePath = resolve(cwd, trimmed);
|
|
360
|
+
if (!existsSync(absolutePath)) {
|
|
361
|
+
throw new Error(`Plugin path does not exist: ${absolutePath}`);
|
|
362
|
+
}
|
|
363
|
+
const stats = statSync(absolutePath);
|
|
364
|
+
if (stats.isDirectory()) {
|
|
365
|
+
const entries = resolvePluginModuleEntries(absolutePath);
|
|
366
|
+
if (entries) {
|
|
367
|
+
resolvedPaths.push(...entries);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
resolvedPaths.push(...discoverPluginModulePaths(absolutePath));
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (!isPluginModulePath(absolutePath)) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Plugin file must use a supported extension (${[...PLUGIN_MODULE_EXTENSIONS].join(", ")}): ${absolutePath}`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
resolvedPaths.push(absolutePath);
|
|
379
|
+
}
|
|
380
|
+
return resolvedPaths;
|
|
381
|
+
}
|
|
382
|
+
|
|
219
383
|
export function ensureParentDir(filePath: string): void {
|
|
220
384
|
const parent = dirname(filePath);
|
|
221
385
|
if (!existsSync(parent)) {
|