@deeplake/hivemind 0.7.35 → 0.7.36
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/bundle/cli.js +152 -67
- package/codex/bundle/capture.js +152 -67
- package/codex/bundle/commands/auth-login.js +152 -67
- package/codex/bundle/pre-tool-use.js +152 -67
- package/codex/bundle/session-start-setup.js +152 -67
- package/codex/bundle/session-start.js +152 -67
- package/codex/bundle/shell/deeplake-shell.js +152 -67
- package/codex/bundle/skillify-worker.js +146 -14
- package/codex/bundle/stop.js +152 -67
- package/cursor/bundle/capture.js +152 -67
- package/cursor/bundle/commands/auth-login.js +152 -67
- package/cursor/bundle/pre-tool-use.js +152 -67
- package/cursor/bundle/session-start.js +152 -67
- package/cursor/bundle/shell/deeplake-shell.js +152 -67
- package/cursor/bundle/skillify-worker.js +146 -14
- package/hermes/bundle/capture.js +152 -67
- package/hermes/bundle/commands/auth-login.js +152 -67
- package/hermes/bundle/pre-tool-use.js +152 -67
- package/hermes/bundle/session-start.js +152 -67
- package/hermes/bundle/shell/deeplake-shell.js +152 -67
- package/hermes/bundle/skillify-worker.js +146 -14
- package/mcp/bundle/server.js +154 -69
- package/openclaw/dist/index.js +151 -74
- package/openclaw/dist/skillify-worker.js +146 -14
- package/openclaw/openclaw.plugin.json +1 -1
- package/openclaw/package.json +1 -1
- package/package.json +1 -1
package/openclaw/dist/index.js
CHANGED
|
@@ -106,7 +106,121 @@ function sqlIdent(name) {
|
|
|
106
106
|
|
|
107
107
|
// src/embeddings/columns.ts
|
|
108
108
|
var SUMMARY_EMBEDDING_COL = "summary_embedding";
|
|
109
|
-
|
|
109
|
+
|
|
110
|
+
// src/deeplake-schema.ts
|
|
111
|
+
var MEMORY_COLUMNS = Object.freeze([
|
|
112
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
113
|
+
{ name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
114
|
+
{ name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
115
|
+
{ name: "summary", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
116
|
+
{ name: "summary_embedding", sql: "FLOAT4[]" },
|
|
117
|
+
{ name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
118
|
+
{ name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'text/plain'" },
|
|
119
|
+
{ name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
120
|
+
{ name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
121
|
+
{ name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
122
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
123
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
124
|
+
{ name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
125
|
+
{ name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
126
|
+
]);
|
|
127
|
+
var SESSIONS_COLUMNS = Object.freeze([
|
|
128
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
129
|
+
{ name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
130
|
+
{ name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
131
|
+
{ name: "message", sql: "JSONB" },
|
|
132
|
+
{ name: "message_embedding", sql: "FLOAT4[]" },
|
|
133
|
+
{ name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
134
|
+
{ name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'application/json'" },
|
|
135
|
+
{ name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
136
|
+
{ name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
137
|
+
{ name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
138
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
139
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
140
|
+
{ name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
141
|
+
{ name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
142
|
+
]);
|
|
143
|
+
var SKILLS_COLUMNS = Object.freeze([
|
|
144
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
145
|
+
{ name: "name", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
146
|
+
{ name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
147
|
+
{ name: "project_key", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
148
|
+
{ name: "local_path", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
149
|
+
{ name: "install", sql: "TEXT NOT NULL DEFAULT 'project'" },
|
|
150
|
+
{ name: "source_sessions", sql: "TEXT NOT NULL DEFAULT '[]'" },
|
|
151
|
+
{ name: "source_agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
152
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
|
|
153
|
+
{ name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
154
|
+
{ name: "contributors", sql: "TEXT NOT NULL DEFAULT '[]'" },
|
|
155
|
+
{ name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
156
|
+
{ name: "trigger_text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
157
|
+
{ name: "body", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
158
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
159
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
160
|
+
{ name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
161
|
+
]);
|
|
162
|
+
function validateSchema(label, cols) {
|
|
163
|
+
const seen = /* @__PURE__ */ new Set();
|
|
164
|
+
for (const col of cols) {
|
|
165
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(col.name)) {
|
|
166
|
+
throw new Error(`${label}: column name "${col.name}" is not a valid SQL identifier`);
|
|
167
|
+
}
|
|
168
|
+
if (seen.has(col.name)) {
|
|
169
|
+
throw new Error(`${label}: duplicate column "${col.name}"`);
|
|
170
|
+
}
|
|
171
|
+
seen.add(col.name);
|
|
172
|
+
const notNull = /\bNOT\s+NULL\b/i.test(col.sql);
|
|
173
|
+
const hasDefault = /\bDEFAULT\b/i.test(col.sql);
|
|
174
|
+
if (notNull && !hasDefault) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`${label}: column "${col.name}" is NOT NULL but has no DEFAULT \u2014 ALTER TABLE ADD COLUMN on a populated table would fail.`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
|
|
182
|
+
validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
|
|
183
|
+
validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
|
|
184
|
+
function buildCreateTableSql(tableName, cols) {
|
|
185
|
+
const safe = sqlIdent(tableName);
|
|
186
|
+
const colSql = cols.map((c) => `${c.name} ${c.sql}`).join(", ");
|
|
187
|
+
return `CREATE TABLE IF NOT EXISTS "${safe}" (${colSql}) USING deeplake`;
|
|
188
|
+
}
|
|
189
|
+
function buildIntrospectionSql(tableName, workspaceId) {
|
|
190
|
+
return `SELECT column_name FROM information_schema.columns WHERE table_name = '${sqlStr(tableName)}' AND table_schema = '${sqlStr(workspaceId)}'`;
|
|
191
|
+
}
|
|
192
|
+
async function healMissingColumns(args) {
|
|
193
|
+
const safeTable = sqlIdent(args.tableName);
|
|
194
|
+
const introspectSql = buildIntrospectionSql(args.tableName, args.workspaceId);
|
|
195
|
+
const rows = await args.query(introspectSql);
|
|
196
|
+
const existing = /* @__PURE__ */ new Set();
|
|
197
|
+
for (const row of rows) {
|
|
198
|
+
const v = row?.column_name;
|
|
199
|
+
if (typeof v === "string") existing.add(v.toLowerCase());
|
|
200
|
+
}
|
|
201
|
+
const missingCols = args.columns.filter((c) => !existing.has(c.name.toLowerCase()));
|
|
202
|
+
const missing = missingCols.map((c) => c.name);
|
|
203
|
+
if (missingCols.length === 0) return { missing, altered: [] };
|
|
204
|
+
const altered = [];
|
|
205
|
+
for (const col of missingCols) {
|
|
206
|
+
try {
|
|
207
|
+
await args.query(`ALTER TABLE "${safeTable}" ADD COLUMN ${col.name} ${col.sql}`);
|
|
208
|
+
altered.push(col.name);
|
|
209
|
+
args.log?.(`schema-heal: added "${args.tableName}"."${col.name}"`);
|
|
210
|
+
} catch (e) {
|
|
211
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
212
|
+
if (!/already exists/i.test(msg)) throw e;
|
|
213
|
+
const recheck = await args.query(introspectSql);
|
|
214
|
+
const present = recheck.some((r) => {
|
|
215
|
+
const v = r?.column_name;
|
|
216
|
+
return typeof v === "string" && v.toLowerCase() === col.name.toLowerCase();
|
|
217
|
+
});
|
|
218
|
+
if (!present) throw e;
|
|
219
|
+
args.log?.(`schema-heal: "${args.tableName}"."${col.name}" appeared via race, treating as success`);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return { missing, altered };
|
|
223
|
+
}
|
|
110
224
|
|
|
111
225
|
// src/notifications/queue.ts
|
|
112
226
|
import { readFileSync, writeFileSync, renameSync, mkdirSync, openSync, closeSync, unlinkSync, statSync } from "node:fs";
|
|
@@ -469,61 +583,33 @@ var DeeplakeApi = class {
|
|
|
469
583
|
}
|
|
470
584
|
}
|
|
471
585
|
/**
|
|
472
|
-
*
|
|
586
|
+
* Heal any missing columns on a table so it matches one of the schema
|
|
587
|
+
* definitions in `deeplake-schema.ts`. One SELECT against
|
|
588
|
+
* `information_schema.columns` per call, then `ALTER TABLE ADD COLUMN`
|
|
589
|
+
* only the genuinely missing ones — never blanket, never `IF NOT
|
|
590
|
+
* EXISTS`.
|
|
473
591
|
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
480
|
-
*
|
|
481
|
-
*
|
|
482
|
-
*
|
|
483
|
-
*
|
|
484
|
-
*
|
|
485
|
-
*
|
|
486
|
-
* column_name = C. Read-only, idempotent, can't tickle the post-ALTER
|
|
487
|
-
* bug. If the column is present → mark + return.
|
|
488
|
-
* 3. Only if step 2 says the column is missing, fall back to ALTER ADD
|
|
489
|
-
* COLUMN IF NOT EXISTS. Mark on success, also mark if Deeplake reports
|
|
490
|
-
* "already exists" (race: another client added it between our SELECT
|
|
491
|
-
* and ALTER).
|
|
492
|
-
*
|
|
493
|
-
* Marker uses the same dir / TTL as ensureLookupIndex so both schema
|
|
494
|
-
* caches share an opt-out (HIVEMIND_INDEX_MARKER_DIR) and a TTL knob.
|
|
495
|
-
*/
|
|
496
|
-
async ensureEmbeddingColumn(table, column) {
|
|
497
|
-
await this.ensureColumn(table, column, "FLOAT4[]");
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Generic marker-gated column migration. Same SELECT-then-ALTER flow as
|
|
501
|
-
* ensureEmbeddingColumn, parameterized by SQL type so it can patch up any
|
|
502
|
-
* column that was added to the schema after the table was originally
|
|
503
|
-
* created. Used today for `summary_embedding`, `message_embedding`, and
|
|
504
|
-
* the `agent` column (added 2026-04-11) — the latter has no fallback if
|
|
505
|
-
* a user upgraded over a pre-2026-04-11 table, so every INSERT fails
|
|
506
|
-
* with `column "agent" does not exist`.
|
|
592
|
+
* History: an earlier path used a local marker file (`col_<name>` under
|
|
593
|
+
* the index-marker dir) to skip even the SELECT after the first
|
|
594
|
+
* confirmation, plus per-column ALTERs for `summary_embedding`,
|
|
595
|
+
* `message_embedding`, `agent`, `plugin_version`. The marker existed
|
|
596
|
+
* because Deeplake used to expose a ~30s post-ALTER bug where
|
|
597
|
+
* subsequent INSERTs failed, so we wanted to keep ALTER traffic to a
|
|
598
|
+
* minimum. The bug was re-verified on 2026-05-18 against
|
|
599
|
+
* `api.deeplake.ai` (`test_plugin` org) and no longer reproduces
|
|
600
|
+
* (71/71 INSERTs OK, first success 2ms after ALTER). The single SELECT
|
|
601
|
+
* + targeted ALTER pattern survives the marker removal because: each
|
|
602
|
+
* ALTER still costs ~800ms (so blanket sweeps are wasteful) and the
|
|
603
|
+
* diff produces clearer logs than "ALTER all with IF NOT EXISTS".
|
|
507
604
|
*/
|
|
508
|
-
async
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
return;
|
|
517
|
-
}
|
|
518
|
-
try {
|
|
519
|
-
await this.query(`ALTER TABLE "${table}" ADD COLUMN ${column} ${sqlType}`);
|
|
520
|
-
} catch (e) {
|
|
521
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
522
|
-
if (!/already exists/i.test(msg)) throw e;
|
|
523
|
-
const recheck = await this.query(colCheck);
|
|
524
|
-
if (recheck.length === 0) throw e;
|
|
525
|
-
}
|
|
526
|
-
markers.writeIndexMarker(markerPath);
|
|
605
|
+
async healSchema(table, columns) {
|
|
606
|
+
await healMissingColumns({
|
|
607
|
+
query: (sql) => this.query(sql),
|
|
608
|
+
tableName: table,
|
|
609
|
+
workspaceId: this.workspaceId,
|
|
610
|
+
columns,
|
|
611
|
+
log: log3
|
|
612
|
+
});
|
|
527
613
|
}
|
|
528
614
|
/** List all tables in the workspace (with retry). */
|
|
529
615
|
async listTables(forceRefresh = false) {
|
|
@@ -592,22 +678,20 @@ var DeeplakeApi = class {
|
|
|
592
678
|
}
|
|
593
679
|
throw lastErr;
|
|
594
680
|
}
|
|
595
|
-
/** Create the memory table if it doesn't already exist.
|
|
681
|
+
/** Create the memory table if it doesn't already exist. Heal missing columns on existing tables. */
|
|
596
682
|
async ensureTable(name) {
|
|
683
|
+
if (!MEMORY_COLUMNS.some((c) => c.name === SUMMARY_EMBEDDING_COL)) {
|
|
684
|
+
throw new Error(`MEMORY_COLUMNS missing "${SUMMARY_EMBEDDING_COL}" (embeddings/columns.ts drift)`);
|
|
685
|
+
}
|
|
597
686
|
const tbl = sqlIdent(name ?? this.tableName);
|
|
598
687
|
const tables = await this.listTables();
|
|
599
688
|
if (!tables.includes(tbl)) {
|
|
600
689
|
log3(`table "${tbl}" not found, creating`);
|
|
601
|
-
await this.createTableWithRetry(
|
|
602
|
-
`CREATE TABLE IF NOT EXISTS "${tbl}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', summary TEXT NOT NULL DEFAULT '', summary_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'text/plain', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`,
|
|
603
|
-
tbl
|
|
604
|
-
);
|
|
690
|
+
await this.createTableWithRetry(buildCreateTableSql(tbl, MEMORY_COLUMNS), tbl);
|
|
605
691
|
log3(`table "${tbl}" created`);
|
|
606
692
|
if (!tables.includes(tbl)) this._tablesCache = [...tables, tbl];
|
|
607
693
|
}
|
|
608
|
-
await this.
|
|
609
|
-
await this.ensureColumn(tbl, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
610
|
-
await this.ensureColumn(tbl, "plugin_version", "TEXT NOT NULL DEFAULT ''");
|
|
694
|
+
await this.healSchema(tbl, MEMORY_COLUMNS);
|
|
611
695
|
}
|
|
612
696
|
/** Create the sessions table (uses JSONB for message since every row is a JSON event). */
|
|
613
697
|
async ensureSessionsTable(name) {
|
|
@@ -615,16 +699,11 @@ var DeeplakeApi = class {
|
|
|
615
699
|
const tables = await this.listTables();
|
|
616
700
|
if (!tables.includes(safe)) {
|
|
617
701
|
log3(`table "${safe}" not found, creating`);
|
|
618
|
-
await this.createTableWithRetry(
|
|
619
|
-
`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', path TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL DEFAULT '', message JSONB, message_embedding FLOAT4[], author TEXT NOT NULL DEFAULT '', mime_type TEXT NOT NULL DEFAULT 'application/json', size_bytes BIGINT NOT NULL DEFAULT 0, project TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', agent TEXT NOT NULL DEFAULT '', plugin_version TEXT NOT NULL DEFAULT '', creation_date TEXT NOT NULL DEFAULT '', last_update_date TEXT NOT NULL DEFAULT '') USING deeplake`,
|
|
620
|
-
safe
|
|
621
|
-
);
|
|
702
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, SESSIONS_COLUMNS), safe);
|
|
622
703
|
log3(`table "${safe}" created`);
|
|
623
704
|
if (!tables.includes(safe)) this._tablesCache = [...tables, safe];
|
|
624
705
|
}
|
|
625
|
-
await this.
|
|
626
|
-
await this.ensureColumn(safe, "agent", "TEXT NOT NULL DEFAULT ''");
|
|
627
|
-
await this.ensureColumn(safe, "plugin_version", "TEXT NOT NULL DEFAULT ''");
|
|
706
|
+
await this.healSchema(safe, SESSIONS_COLUMNS);
|
|
628
707
|
await this.ensureLookupIndex(safe, "path_creation_date", `("path", "creation_date")`);
|
|
629
708
|
}
|
|
630
709
|
/**
|
|
@@ -642,13 +721,11 @@ var DeeplakeApi = class {
|
|
|
642
721
|
const tables = await this.listTables();
|
|
643
722
|
if (!tables.includes(safe)) {
|
|
644
723
|
log3(`table "${safe}" not found, creating`);
|
|
645
|
-
await this.createTableWithRetry(
|
|
646
|
-
`CREATE TABLE IF NOT EXISTS "${safe}" (id TEXT NOT NULL DEFAULT '', name TEXT NOT NULL DEFAULT '', project TEXT NOT NULL DEFAULT '', project_key TEXT NOT NULL DEFAULT '', local_path TEXT NOT NULL DEFAULT '', install TEXT NOT NULL DEFAULT 'project', source_sessions TEXT NOT NULL DEFAULT '[]', source_agent TEXT NOT NULL DEFAULT '', scope TEXT NOT NULL DEFAULT 'me', author TEXT NOT NULL DEFAULT '', description TEXT NOT NULL DEFAULT '', trigger_text TEXT NOT NULL DEFAULT '', body TEXT NOT NULL DEFAULT '', version BIGINT NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT '', updated_at TEXT NOT NULL DEFAULT '') USING deeplake`,
|
|
647
|
-
safe
|
|
648
|
-
);
|
|
724
|
+
await this.createTableWithRetry(buildCreateTableSql(safe, SKILLS_COLUMNS), safe);
|
|
649
725
|
log3(`table "${safe}" created`);
|
|
650
726
|
if (!tables.includes(safe)) this._tablesCache = [...tables, safe];
|
|
651
727
|
}
|
|
728
|
+
await this.healSchema(safe, SKILLS_COLUMNS);
|
|
652
729
|
await this.ensureLookupIndex(safe, "project_key_name", `("project_key", "name")`);
|
|
653
730
|
}
|
|
654
731
|
};
|
|
@@ -1466,7 +1543,7 @@ function extractLatestVersion(body) {
|
|
|
1466
1543
|
return typeof v === "string" && v.length > 0 ? v : null;
|
|
1467
1544
|
}
|
|
1468
1545
|
function getInstalledVersion() {
|
|
1469
|
-
return "0.7.
|
|
1546
|
+
return "0.7.36".length > 0 ? "0.7.36" : null;
|
|
1470
1547
|
}
|
|
1471
1548
|
function isNewer(latest, current) {
|
|
1472
1549
|
const parse = (v) => v.replace(/-.*$/, "").split(".").map(Number);
|
|
@@ -317,6 +317,9 @@ ${s.body}
|
|
|
317
317
|
import { randomUUID } from "node:crypto";
|
|
318
318
|
|
|
319
319
|
// dist/src/utils/sql.js
|
|
320
|
+
function sqlStr(value) {
|
|
321
|
+
return value.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/\0/g, "").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
322
|
+
}
|
|
320
323
|
function sqlIdent(name) {
|
|
321
324
|
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
322
325
|
throw new Error(`Invalid SQL identifier: ${JSON.stringify(name)}`);
|
|
@@ -324,29 +327,142 @@ function sqlIdent(name) {
|
|
|
324
327
|
return name;
|
|
325
328
|
}
|
|
326
329
|
|
|
327
|
-
// dist/src/
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
330
|
+
// dist/src/deeplake-schema.js
|
|
331
|
+
var MEMORY_COLUMNS = Object.freeze([
|
|
332
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
333
|
+
{ name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
334
|
+
{ name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
335
|
+
{ name: "summary", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
336
|
+
{ name: "summary_embedding", sql: "FLOAT4[]" },
|
|
337
|
+
{ name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
338
|
+
{ name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'text/plain'" },
|
|
339
|
+
{ name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
340
|
+
{ name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
341
|
+
{ name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
342
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
343
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
344
|
+
{ name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
345
|
+
{ name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
346
|
+
]);
|
|
347
|
+
var SESSIONS_COLUMNS = Object.freeze([
|
|
348
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
349
|
+
{ name: "path", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
350
|
+
{ name: "filename", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
351
|
+
{ name: "message", sql: "JSONB" },
|
|
352
|
+
{ name: "message_embedding", sql: "FLOAT4[]" },
|
|
353
|
+
{ name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
354
|
+
{ name: "mime_type", sql: "TEXT NOT NULL DEFAULT 'application/json'" },
|
|
355
|
+
{ name: "size_bytes", sql: "BIGINT NOT NULL DEFAULT 0" },
|
|
356
|
+
{ name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
357
|
+
{ name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
358
|
+
{ name: "agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
359
|
+
{ name: "plugin_version", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
360
|
+
{ name: "creation_date", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
361
|
+
{ name: "last_update_date", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
362
|
+
]);
|
|
363
|
+
var SKILLS_COLUMNS = Object.freeze([
|
|
364
|
+
{ name: "id", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
365
|
+
{ name: "name", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
366
|
+
{ name: "project", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
367
|
+
{ name: "project_key", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
368
|
+
{ name: "local_path", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
369
|
+
{ name: "install", sql: "TEXT NOT NULL DEFAULT 'project'" },
|
|
370
|
+
{ name: "source_sessions", sql: "TEXT NOT NULL DEFAULT '[]'" },
|
|
371
|
+
{ name: "source_agent", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
372
|
+
{ name: "scope", sql: "TEXT NOT NULL DEFAULT 'me'" },
|
|
373
|
+
{ name: "author", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
374
|
+
{ name: "contributors", sql: "TEXT NOT NULL DEFAULT '[]'" },
|
|
375
|
+
{ name: "description", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
376
|
+
{ name: "trigger_text", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
377
|
+
{ name: "body", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
378
|
+
{ name: "version", sql: "BIGINT NOT NULL DEFAULT 1" },
|
|
379
|
+
{ name: "created_at", sql: "TEXT NOT NULL DEFAULT ''" },
|
|
380
|
+
{ name: "updated_at", sql: "TEXT NOT NULL DEFAULT ''" }
|
|
381
|
+
]);
|
|
382
|
+
function validateSchema(label, cols) {
|
|
383
|
+
const seen = /* @__PURE__ */ new Set();
|
|
384
|
+
for (const col of cols) {
|
|
385
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(col.name)) {
|
|
386
|
+
throw new Error(`${label}: column name "${col.name}" is not a valid SQL identifier`);
|
|
387
|
+
}
|
|
388
|
+
if (seen.has(col.name)) {
|
|
389
|
+
throw new Error(`${label}: duplicate column "${col.name}"`);
|
|
390
|
+
}
|
|
391
|
+
seen.add(col.name);
|
|
392
|
+
const notNull = /\bNOT\s+NULL\b/i.test(col.sql);
|
|
393
|
+
const hasDefault = /\bDEFAULT\b/i.test(col.sql);
|
|
394
|
+
if (notNull && !hasDefault) {
|
|
395
|
+
throw new Error(`${label}: column "${col.name}" is NOT NULL but has no DEFAULT \u2014 ALTER TABLE ADD COLUMN on a populated table would fail.`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
331
398
|
}
|
|
332
|
-
|
|
399
|
+
validateSchema("MEMORY_COLUMNS", MEMORY_COLUMNS);
|
|
400
|
+
validateSchema("SESSIONS_COLUMNS", SESSIONS_COLUMNS);
|
|
401
|
+
validateSchema("SKILLS_COLUMNS", SKILLS_COLUMNS);
|
|
402
|
+
function buildCreateTableSql(tableName, cols) {
|
|
333
403
|
const safe = sqlIdent(tableName);
|
|
334
|
-
|
|
404
|
+
const colSql = cols.map((c) => `${c.name} ${c.sql}`).join(", ");
|
|
405
|
+
return `CREATE TABLE IF NOT EXISTS "${safe}" (${colSql}) USING deeplake`;
|
|
335
406
|
}
|
|
336
|
-
function
|
|
337
|
-
return
|
|
407
|
+
function buildIntrospectionSql(tableName, workspaceId) {
|
|
408
|
+
return `SELECT column_name FROM information_schema.columns WHERE table_name = '${sqlStr(tableName)}' AND table_schema = '${sqlStr(workspaceId)}'`;
|
|
409
|
+
}
|
|
410
|
+
async function healMissingColumns(args) {
|
|
411
|
+
const safeTable = sqlIdent(args.tableName);
|
|
412
|
+
const introspectSql = buildIntrospectionSql(args.tableName, args.workspaceId);
|
|
413
|
+
const rows = await args.query(introspectSql);
|
|
414
|
+
const existing = /* @__PURE__ */ new Set();
|
|
415
|
+
for (const row of rows) {
|
|
416
|
+
const v = row?.column_name;
|
|
417
|
+
if (typeof v === "string")
|
|
418
|
+
existing.add(v.toLowerCase());
|
|
419
|
+
}
|
|
420
|
+
const missingCols = args.columns.filter((c) => !existing.has(c.name.toLowerCase()));
|
|
421
|
+
const missing = missingCols.map((c) => c.name);
|
|
422
|
+
if (missingCols.length === 0)
|
|
423
|
+
return { missing, altered: [] };
|
|
424
|
+
const altered = [];
|
|
425
|
+
for (const col of missingCols) {
|
|
426
|
+
try {
|
|
427
|
+
await args.query(`ALTER TABLE "${safeTable}" ADD COLUMN ${col.name} ${col.sql}`);
|
|
428
|
+
altered.push(col.name);
|
|
429
|
+
args.log?.(`schema-heal: added "${args.tableName}"."${col.name}"`);
|
|
430
|
+
} catch (e) {
|
|
431
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
432
|
+
if (!/already exists/i.test(msg))
|
|
433
|
+
throw e;
|
|
434
|
+
const recheck = await args.query(introspectSql);
|
|
435
|
+
const present = recheck.some((r) => {
|
|
436
|
+
const v = r?.column_name;
|
|
437
|
+
return typeof v === "string" && v.toLowerCase() === col.name.toLowerCase();
|
|
438
|
+
});
|
|
439
|
+
if (!present)
|
|
440
|
+
throw e;
|
|
441
|
+
args.log?.(`schema-heal: "${args.tableName}"."${col.name}" appeared via race, treating as success`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return { missing, altered };
|
|
338
445
|
}
|
|
339
446
|
function isMissingTableError(message) {
|
|
340
447
|
if (!message)
|
|
341
448
|
return false;
|
|
449
|
+
if (/permission denied|must be owner/i.test(message))
|
|
450
|
+
return false;
|
|
342
451
|
if (/\bcolumn\b/i.test(message))
|
|
343
452
|
return false;
|
|
344
453
|
return /Table does not exist|relation .* does not exist|no such table/i.test(message);
|
|
345
454
|
}
|
|
346
|
-
function
|
|
455
|
+
function isMissingColumnError(message) {
|
|
347
456
|
if (!message)
|
|
348
457
|
return false;
|
|
349
|
-
|
|
458
|
+
if (/permission denied|must be owner/i.test(message))
|
|
459
|
+
return false;
|
|
460
|
+
return /column ["']?[A-Za-z_][A-Za-z0-9_]*["']? .*does not exist/i.test(message) || /unknown column/i.test(message) || /no such column/i.test(message);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// dist/src/skillify/skills-table.js
|
|
464
|
+
function esc(s) {
|
|
465
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "''").replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, "");
|
|
350
466
|
}
|
|
351
467
|
async function insertSkillRow(args) {
|
|
352
468
|
const id = args.id ?? randomUUID();
|
|
@@ -355,14 +471,29 @@ async function insertSkillRow(args) {
|
|
|
355
471
|
const sql = `INSERT INTO "${sqlIdent(args.tableName)}" (id, name, project, project_key, local_path, install, source_sessions, source_agent, scope, author, contributors, description, trigger_text, body, version, created_at, updated_at) VALUES ('${esc(id)}', '${esc(args.name)}', '${esc(args.project)}', '${esc(args.projectKey)}', '${esc(args.localPath)}', '${esc(args.install)}', '${esc(sourceSessionsJson)}', '${esc(args.sourceAgent)}', '${esc(args.scope)}', '${esc(args.author)}', '${esc(contributorsJson)}', '${esc(args.description)}', '${esc(args.trigger ?? "")}', '${esc(args.body)}', ${args.version}, '${esc(args.createdAt)}', '${esc(args.updatedAt)}')`;
|
|
356
472
|
try {
|
|
357
473
|
await args.query(sql);
|
|
474
|
+
return;
|
|
358
475
|
} catch (e) {
|
|
359
|
-
|
|
360
|
-
|
|
476
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
477
|
+
if (isMissingTableError(msg)) {
|
|
478
|
+
await args.query(buildCreateTableSql(args.tableName, SKILLS_COLUMNS));
|
|
479
|
+
await healMissingColumns({
|
|
480
|
+
query: args.query,
|
|
481
|
+
tableName: args.tableName,
|
|
482
|
+
workspaceId: args.workspaceId,
|
|
483
|
+
columns: SKILLS_COLUMNS
|
|
484
|
+
});
|
|
361
485
|
await args.query(sql);
|
|
362
486
|
return;
|
|
363
487
|
}
|
|
364
|
-
if (
|
|
365
|
-
await
|
|
488
|
+
if (isMissingColumnError(msg)) {
|
|
489
|
+
const result = await healMissingColumns({
|
|
490
|
+
query: args.query,
|
|
491
|
+
tableName: args.tableName,
|
|
492
|
+
workspaceId: args.workspaceId,
|
|
493
|
+
columns: SKILLS_COLUMNS
|
|
494
|
+
});
|
|
495
|
+
if (result.missing.length === 0)
|
|
496
|
+
throw e;
|
|
366
497
|
await args.query(sql);
|
|
367
498
|
return;
|
|
368
499
|
}
|
|
@@ -1002,6 +1133,7 @@ async function main() {
|
|
|
1002
1133
|
await insertSkillRow({
|
|
1003
1134
|
query,
|
|
1004
1135
|
tableName: cfg.skillsTable,
|
|
1136
|
+
workspaceId: cfg.workspaceId,
|
|
1005
1137
|
name: verdict2.name,
|
|
1006
1138
|
project: cfg.project,
|
|
1007
1139
|
projectKey: cfg.projectKey,
|
package/openclaw/package.json
CHANGED