@vibedeckx/linux-x64 0.3.9 → 0.3.11
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/bin.js +122 -5
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -185980,10 +185980,29 @@ var createCoreRepos = (kdb, h) => ({
|
|
|
185980
185980
|
const row = await readQuery.executeTakeFirst();
|
|
185981
185981
|
return row ? mapProject(row) : void 0;
|
|
185982
185982
|
},
|
|
185983
|
+
/**
|
|
185984
|
+
* Deleting a project cascades down two independent paths: to its sessions,
|
|
185985
|
+
* and to `workspaces` → `workspace_checkouts`. `remote_session_mappings`
|
|
185986
|
+
* sits on neither — it has no key to `projects` — so once a mapping's
|
|
185987
|
+
* checkout is a real foreign key, leaving those rows behind would strand
|
|
185988
|
+
* them against a checkout the same statement just deleted.
|
|
185989
|
+
*
|
|
185990
|
+
* The dependents are therefore removed explicitly, in order, inside one
|
|
185991
|
+
* transaction. The scope check happens first so an unauthorized caller
|
|
185992
|
+
* cannot delete another tenant's mappings on the way to a no-op.
|
|
185993
|
+
*/
|
|
185983
185994
|
delete: async (id, userId) => {
|
|
185984
|
-
|
|
185985
|
-
|
|
185986
|
-
|
|
185995
|
+
await kdb.transaction().execute(async (trx) => {
|
|
185996
|
+
let scope = trx.selectFrom("projects").select("id").where("id", "=", id);
|
|
185997
|
+
if (userId) scope = scope.where("user_id", "=", userId);
|
|
185998
|
+
if (!await scope.executeTakeFirst()) return;
|
|
185999
|
+
const mappings = await trx.selectFrom("remote_session_mappings").select(["remote_server_id", "remote_session_id"]).where("project_id", "=", id).execute();
|
|
186000
|
+
for (const mapping of mappings) {
|
|
186001
|
+
await trx.deleteFrom("notification_sync_cursors").where("remote_server_id", "=", mapping.remote_server_id).where("remote_session_id", "=", mapping.remote_session_id).execute();
|
|
186002
|
+
}
|
|
186003
|
+
await trx.deleteFrom("remote_session_mappings").where("project_id", "=", id).execute();
|
|
186004
|
+
await trx.deleteFrom("projects").where("id", "=", id).execute();
|
|
186005
|
+
});
|
|
185987
186006
|
},
|
|
185988
186007
|
// Deliberately unscoped: the notification importer runs without a request
|
|
185989
186008
|
// context and must DERIVE ownership from the mapped local project rather
|
|
@@ -187342,6 +187361,10 @@ var createAgentSessionRepos = (kdb, h) => ({
|
|
|
187342
187361
|
}
|
|
187343
187362
|
},
|
|
187344
187363
|
workspaceBindingMigration: {
|
|
187364
|
+
listUnboundLocalProjects: async () => {
|
|
187365
|
+
const rows = await kdb.selectFrom("agent_sessions as s").innerJoin("projects as p", "p.id", "s.project_id").select(["p.id", "p.path"]).where("s.workspace_checkout_id", "is", null).where("p.path", "is not", null).where("p.path", "<>", "").groupBy(["p.id", "p.path"]).execute();
|
|
187366
|
+
return rows.map((row) => ({ id: row.id, path: row.path }));
|
|
187367
|
+
},
|
|
187345
187368
|
backfill: async ({ kind, dryRun = true, batchSize = 100, afterId = "" }) => {
|
|
187346
187369
|
const limit = Math.max(1, Math.min(1e3, batchSize));
|
|
187347
187370
|
const source = kind === "local" ? await kdb.selectFrom("agent_sessions").select(["id", "project_id", "branch"]).where("workspace_checkout_id", "is", null).where("id", ">", afterId).orderBy("id", "asc").limit(limit).execute() : await kdb.selectFrom("remote_session_mappings").select(["local_session_id as id", "project_id", "branch", "remote_server_id"]).where("workspace_checkout_id", "is", null).where("local_session_id", ">", afterId).orderBy("local_session_id", "asc").limit(limit).execute();
|
|
@@ -203458,6 +203481,100 @@ var createWorkspaceRegistryRepo = (kdb, h) => ({
|
|
|
203458
203481
|
});
|
|
203459
203482
|
|
|
203460
203483
|
// src/storage/sqlite.ts
|
|
203484
|
+
var countDanglingCheckoutBindings = (db) => {
|
|
203485
|
+
const scalar = (sql2) => Number(db.prepare(sql2).get().count);
|
|
203486
|
+
return {
|
|
203487
|
+
sessions: scalar(`SELECT count(*) AS count FROM agent_sessions s
|
|
203488
|
+
LEFT JOIN workspace_checkouts c ON c.id = s.workspace_checkout_id
|
|
203489
|
+
WHERE s.workspace_checkout_id IS NOT NULL AND c.id IS NULL`),
|
|
203490
|
+
mappings: scalar(`SELECT count(*) AS count FROM remote_session_mappings m
|
|
203491
|
+
LEFT JOIN workspace_checkouts c ON c.id = m.workspace_checkout_id
|
|
203492
|
+
WHERE m.workspace_checkout_id IS NOT NULL AND c.id IS NULL`)
|
|
203493
|
+
};
|
|
203494
|
+
};
|
|
203495
|
+
var tightenWorkspaceCheckoutForeignKeys = (db) => {
|
|
203496
|
+
const ddl = (table) => db.prepare(
|
|
203497
|
+
"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = ?"
|
|
203498
|
+
).get(table)?.sql ?? "";
|
|
203499
|
+
const bound = (table) => /REFERENCES\s+workspace_checkouts/i.test(ddl(table));
|
|
203500
|
+
if (bound("agent_sessions") && bound("remote_session_mappings")) return;
|
|
203501
|
+
const indexDdl = (table) => db.prepare(
|
|
203502
|
+
"SELECT sql FROM sqlite_master WHERE type = 'index' AND tbl_name = ? AND sql IS NOT NULL"
|
|
203503
|
+
).all(table).map((row) => row.sql);
|
|
203504
|
+
const dangling = countDanglingCheckoutBindings(db);
|
|
203505
|
+
if (dangling.sessions > 0 || dangling.mappings > 0) {
|
|
203506
|
+
console.warn(
|
|
203507
|
+
`[Storage] Refusing to add the workspace_checkout foreign key: ${dangling.sessions} session(s) and ${dangling.mappings} remote mapping(s) reference a checkout that no longer exists. Resolve or clear those bindings, then restart to complete the migration.`
|
|
203508
|
+
);
|
|
203509
|
+
return;
|
|
203510
|
+
}
|
|
203511
|
+
const sessionIndexes = indexDdl("agent_sessions");
|
|
203512
|
+
const mappingIndexes = indexDdl("remote_session_mappings");
|
|
203513
|
+
db.transaction(() => {
|
|
203514
|
+
if (!bound("agent_sessions")) {
|
|
203515
|
+
db.exec(`
|
|
203516
|
+
CREATE TABLE agent_sessions_fk_new (
|
|
203517
|
+
id TEXT PRIMARY KEY,
|
|
203518
|
+
project_id TEXT NOT NULL,
|
|
203519
|
+
branch TEXT NOT NULL DEFAULT '',
|
|
203520
|
+
workspace_checkout_id TEXT DEFAULT NULL,
|
|
203521
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
203522
|
+
permission_mode TEXT DEFAULT 'edit',
|
|
203523
|
+
agent_type TEXT DEFAULT 'claude-code',
|
|
203524
|
+
title TEXT DEFAULT NULL,
|
|
203525
|
+
model TEXT DEFAULT NULL,
|
|
203526
|
+
created_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
|
203527
|
+
updated_at TEXT DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now')),
|
|
203528
|
+
activity_at INTEGER DEFAULT (cast((julianday('now') - 2440587.5) * 86400000 as integer)),
|
|
203529
|
+
last_user_message_at INTEGER DEFAULT NULL,
|
|
203530
|
+
last_completed_at INTEGER DEFAULT NULL,
|
|
203531
|
+
favorited_at INTEGER DEFAULT NULL,
|
|
203532
|
+
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
|
203533
|
+
FOREIGN KEY (workspace_checkout_id) REFERENCES workspace_checkouts(id)
|
|
203534
|
+
DEFERRABLE INITIALLY DEFERRED
|
|
203535
|
+
);
|
|
203536
|
+
INSERT INTO agent_sessions_fk_new
|
|
203537
|
+
(id, project_id, branch, workspace_checkout_id, status, permission_mode, agent_type,
|
|
203538
|
+
title, model, created_at, updated_at, activity_at, last_user_message_at,
|
|
203539
|
+
last_completed_at, favorited_at)
|
|
203540
|
+
SELECT id, project_id, branch, workspace_checkout_id, status, permission_mode, agent_type,
|
|
203541
|
+
title, model, created_at, updated_at, activity_at, last_user_message_at,
|
|
203542
|
+
last_completed_at, favorited_at
|
|
203543
|
+
FROM agent_sessions;
|
|
203544
|
+
DROP TABLE agent_sessions;
|
|
203545
|
+
ALTER TABLE agent_sessions_fk_new RENAME TO agent_sessions;
|
|
203546
|
+
`);
|
|
203547
|
+
for (const sql2 of sessionIndexes) db.exec(sql2);
|
|
203548
|
+
}
|
|
203549
|
+
if (!bound("remote_session_mappings")) {
|
|
203550
|
+
db.exec(`
|
|
203551
|
+
CREATE TABLE remote_session_mappings_fk_new (
|
|
203552
|
+
local_session_id TEXT PRIMARY KEY,
|
|
203553
|
+
project_id TEXT NOT NULL,
|
|
203554
|
+
remote_server_id TEXT NOT NULL,
|
|
203555
|
+
remote_session_id TEXT NOT NULL,
|
|
203556
|
+
branch TEXT,
|
|
203557
|
+
workspace_checkout_id TEXT DEFAULT NULL,
|
|
203558
|
+
title_resolved INTEGER NOT NULL DEFAULT 0,
|
|
203559
|
+
notification_sync_start TEXT NOT NULL DEFAULT 'from_now',
|
|
203560
|
+
notification_watch_until INTEGER,
|
|
203561
|
+
FOREIGN KEY (workspace_checkout_id) REFERENCES workspace_checkouts(id)
|
|
203562
|
+
DEFERRABLE INITIALLY DEFERRED
|
|
203563
|
+
);
|
|
203564
|
+
INSERT INTO remote_session_mappings_fk_new
|
|
203565
|
+
(local_session_id, project_id, remote_server_id, remote_session_id, branch,
|
|
203566
|
+
workspace_checkout_id, title_resolved, notification_sync_start, notification_watch_until)
|
|
203567
|
+
SELECT local_session_id, project_id, remote_server_id, remote_session_id, branch,
|
|
203568
|
+
workspace_checkout_id, title_resolved, notification_sync_start, notification_watch_until
|
|
203569
|
+
FROM remote_session_mappings;
|
|
203570
|
+
DROP TABLE remote_session_mappings;
|
|
203571
|
+
ALTER TABLE remote_session_mappings_fk_new RENAME TO remote_session_mappings;
|
|
203572
|
+
`);
|
|
203573
|
+
for (const sql2 of mappingIndexes) db.exec(sql2);
|
|
203574
|
+
}
|
|
203575
|
+
})();
|
|
203576
|
+
console.log("[Storage] workspace_checkout foreign keys are now enforced.");
|
|
203577
|
+
};
|
|
203461
203578
|
var createDatabase = (dbPath) => {
|
|
203462
203579
|
const db = new Database(dbPath);
|
|
203463
203580
|
db.pragma("journal_mode = WAL");
|
|
@@ -204954,6 +205071,7 @@ var createDatabase = (dbPath) => {
|
|
|
204954
205071
|
}
|
|
204955
205072
|
db.exec(`CREATE INDEX IF NOT EXISTS idx_remote_session_mappings_workspace_checkout
|
|
204956
205073
|
ON remote_session_mappings(workspace_checkout_id)`);
|
|
205074
|
+
tightenWorkspaceCheckoutForeignKeys(db);
|
|
204957
205075
|
const sessionSearchCacheInfo = db.prepare("PRAGMA table_info(session_search_cache)").all();
|
|
204958
205076
|
if (!sessionSearchCacheInfo.some((col) => col.name === "written_at")) {
|
|
204959
205077
|
db.exec("ALTER TABLE session_search_cache ADD COLUMN written_at INTEGER");
|
|
@@ -240128,9 +240246,8 @@ async function registerReportedWorktrees(storage, opts) {
|
|
|
240128
240246
|
}
|
|
240129
240247
|
}
|
|
240130
240248
|
async function syncLocalWorkspaceRegistry(storage) {
|
|
240131
|
-
const projects = await storage.
|
|
240249
|
+
const projects = await storage.workspaceBindingMigration.listUnboundLocalProjects();
|
|
240132
240250
|
for (const project of projects) {
|
|
240133
|
-
if (!project.path) continue;
|
|
240134
240251
|
try {
|
|
240135
240252
|
await getRegisteredWorktreeBranches(storage, project.id, project.path);
|
|
240136
240253
|
} catch (error48) {
|