Governed substrate for autonomous agents: scoped identity (passports), audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
108 lines
4.6 KiB
JavaScript
108 lines
4.6 KiB
JavaScript
#!/usr/bin/env node
|
|
// One-off: backfill space 7798 (owner user 8944) with ADR-0079 layout.
|
|
// Drops the empty Password Manager project, then runs applyStarterPack
|
|
// under a temporary flag flip. Idempotent on re-run.
|
|
//
|
|
// Usage: node scripts/backfill-adr-0079-space-7798.mjs
|
|
|
|
import { dbGet, dbAll, dbRun } from '../backend/database/connection.js';
|
|
import { applyStarterPack } from '../backend/services/starter-pack/StarterPackService.js';
|
|
|
|
const SPACE_ID = 7798;
|
|
const USER_ID = 8944;
|
|
const PM_PROJECT_ID = 8375;
|
|
const FLAG_KEY = 'starter_pack_enabled';
|
|
|
|
async function main() {
|
|
console.log(`[backfill-7798] start — space=${SPACE_ID} user=${USER_ID}`);
|
|
|
|
// 1. Sanity: confirm space + owner match.
|
|
const space = await dbGet('SELECT id, owner_id, type FROM spaces WHERE id = ?', [SPACE_ID]);
|
|
if (!space) throw new Error(`space ${SPACE_ID} not found`);
|
|
if (space.owner_id !== USER_ID) throw new Error(`space ${SPACE_ID}.owner_id=${space.owner_id}, expected ${USER_ID}`);
|
|
if (space.type !== 'personal') throw new Error(`space ${SPACE_ID}.type=${space.type}, expected 'personal'`);
|
|
|
|
// 2. Drop Password Manager project (CASCADE clears its tables / 0 rows).
|
|
const pm = await dbGet('SELECT id, name FROM projects WHERE id = ? AND space_id = ?', [PM_PROJECT_ID, SPACE_ID]);
|
|
if (pm) {
|
|
if (pm.name !== 'Password Manager') {
|
|
throw new Error(`refusing to drop project ${PM_PROJECT_ID}: name="${pm.name}" (expected Password Manager)`);
|
|
}
|
|
const rowCheck = await dbAll(
|
|
`SELECT ut.id, ut.name, (SELECT COUNT(*) FROM table_rows tr WHERE tr.table_id = ut.id) AS rows
|
|
FROM universal_tables ut WHERE ut.project_id = ? AND ut.deleted_at IS NULL`,
|
|
[PM_PROJECT_ID]
|
|
);
|
|
const dirty = rowCheck.filter(r => Number(r.rows) > 0);
|
|
if (dirty.length) {
|
|
throw new Error(`refusing to drop project ${PM_PROJECT_ID}: tables have data: ${JSON.stringify(dirty)}`);
|
|
}
|
|
await dbRun('DELETE FROM projects WHERE id = ?', [PM_PROJECT_ID]);
|
|
console.log(`[backfill-7798] dropped Password Manager project ${PM_PROJECT_ID} (${rowCheck.length} empty tables cascaded)`);
|
|
} else {
|
|
console.log(`[backfill-7798] PM project ${PM_PROJECT_ID} already gone — skipping drop`);
|
|
}
|
|
|
|
// 3. Save current flag value, force-enable for this run.
|
|
const before = await dbGet('SELECT value FROM _app_settings WHERE key = ?', [FLAG_KEY]);
|
|
const prevValue = before?.value ?? null;
|
|
await dbRun(
|
|
`INSERT INTO _app_settings(key, value, updated_at)
|
|
VALUES (?, 'true'::jsonb, NOW())
|
|
ON CONFLICT (key) DO UPDATE SET value = 'true'::jsonb, updated_at = NOW()`,
|
|
[FLAG_KEY]
|
|
);
|
|
console.log(`[backfill-7798] flag '${FLAG_KEY}' temporarily forced to true (was ${prevValue})`);
|
|
|
|
let outcome = null;
|
|
try {
|
|
outcome = await applyStarterPack(USER_ID);
|
|
console.log(`[backfill-7798] applyStarterPack result:`, JSON.stringify(outcome, null, 2));
|
|
} finally {
|
|
// 4. Restore original flag value (or keep null → delete row if it was missing).
|
|
if (prevValue === null) {
|
|
await dbRun(`DELETE FROM _app_settings WHERE key = ?`, [FLAG_KEY]);
|
|
} else {
|
|
await dbRun(
|
|
`UPDATE _app_settings SET value = ?::jsonb, updated_at = NOW() WHERE key = ?`,
|
|
[JSON.stringify(prevValue), FLAG_KEY]
|
|
);
|
|
}
|
|
const after = await dbGet('SELECT value FROM _app_settings WHERE key = ?', [FLAG_KEY]);
|
|
console.log(`[backfill-7798] flag '${FLAG_KEY}' restored: ${after?.value ?? '(deleted)'}`);
|
|
}
|
|
|
|
// 5. Verify post-state.
|
|
const projects = await dbAll('SELECT id, name, icon FROM projects WHERE space_id = ? ORDER BY id', [SPACE_ID]);
|
|
const tables = await dbAll(
|
|
`SELECT ut.id, ut.name, ut.project_id
|
|
FROM universal_tables ut
|
|
JOIN projects p ON p.id = ut.project_id
|
|
WHERE p.space_id = ? AND ut.deleted_at IS NULL
|
|
ORDER BY ut.id`,
|
|
[SPACE_ID]
|
|
);
|
|
const widgets = await dbAll(
|
|
`SELECT w.id, w.preset_name, w.title, w.dashboard_id
|
|
FROM widgets w
|
|
JOIN dashboards d ON d.id = w.dashboard_id
|
|
JOIN projects p ON p.id = d.project_id
|
|
WHERE p.space_id = ? AND w.preset_name = 'welcome_dashboard'`,
|
|
[SPACE_ID]
|
|
);
|
|
|
|
console.log(`[backfill-7798] verification:`);
|
|
console.log(` projects: ${JSON.stringify(projects)}`);
|
|
console.log(` starter tables: ${tables.length}`);
|
|
for (const t of tables) console.log(` - ${t.id}: ${t.name} (project ${t.project_id})`);
|
|
console.log(` welcome_dashboard widget(s): ${widgets.length}`);
|
|
for (const w of widgets) console.log(` - widget ${w.id}: ${w.title}`);
|
|
|
|
console.log(`[backfill-7798] done`);
|
|
process.exit(0);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('[backfill-7798] FATAL:', err?.stack || err);
|
|
process.exit(1);
|
|
});
|