@automagik/genie 4.260504.14 → 4.260504.16

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/genie.js CHANGED
@@ -4282,10 +4282,10 @@ Genie Scheduler Daemon`),console.log("\u2500".repeat(50)),console.log(` Status:
4282
4282
  (showing last ${lines} of ${allLines.length} entries)`)}async function tailFollow(filePath,initialLines){let{watch:watch2}=await import("fs");tailStatic(filePath,initialLines),console.log(`
4283
4283
  --- following (Ctrl+C to exit) ---
4284
4284
  `);let lastSize=existsSync56(filePath)?readFileSync37(filePath).length:0,watcher2=watch2(filePath,()=>{try{let content=readFileSync37(filePath,"utf-8");if(content.length>lastSize){let newLines=content.slice(lastSize).trim().split(`
4285
- `).filter(Boolean);for(let line of newLines)printLogLine(line);lastSize=content.length}}catch{}});process.on("SIGINT",()=>{watcher2.close(),process.exit(0)}),await new Promise(()=>{})}function printLogLine(raw){try{let entry2=JSON.parse(raw),ts3=entry2.timestamp?new Date(entry2.timestamp).toLocaleTimeString("en-US",{hour12:!1}):"??:??:??",level=(entry2.level??"info").toUpperCase().padEnd(5),event=entry2.event??"unknown",extras=Object.entries(entry2).filter(([k])=>!["timestamp","level","event"].includes(k)).map(([k,v])=>`${k}=${typeof v==="object"?JSON.stringify(v):v}`).join(" ");console.log(`${ts3} ${level} ${event}${extras?` ${extras}`:""}`)}catch{console.log(raw)}}function formatUptime(ms){let seconds=Math.floor(ms/1000),minutes=Math.floor(seconds/60),hours=Math.floor(minutes/60),days=Math.floor(hours/24);if(days>0)return`${days}d ${hours%24}h ${minutes%60}m`;if(hours>0)return`${hours}h ${minutes%60}m`;if(minutes>0)return`${minutes}m ${seconds%60}s`;return`${seconds}s`}function registerDaemonCommands(program2){let daemon=program2.command("daemon").description("Manage scheduler daemon lifecycle (redirects to genie serve --headless)");daemon.command("install").description("Generate systemd service unit and enable it").action(async()=>{await daemonInstallCommand()}),daemon.command("start").description("Start the scheduler daemon (alias for genie serve --headless)").option("--foreground","Run in foreground (for systemd ExecStart)").action(async(options)=>{await daemonStartCommand(options)}),daemon.command("stop").description("Stop genie serve gracefully").action(async()=>{await daemonStopCommand()}),daemon.command("status").description("Show daemon state, PID, uptime, and trigger stats").action(async()=>{await daemonStatusCommand()}),daemon.command("logs").description("Tail structured JSON scheduler log").option("--follow, -f","Follow log output").option("--lines <n>","Number of lines to show (default: 20)",Number.parseInt).action(async(options)=>{await daemonLogsCommand(options)})}init_cron();import{existsSync as existsSync58,readFileSync as readFileSync39}from"fs";import{dirname as dirname24,join as join70}from"path";import{createInterface as createInterface3}from"readline";init_db();init_wish_state();import{spawnSync as spawnSync8}from"child_process";import{existsSync as existsSync57,mkdirSync as mkdirSync28,readFileSync as readFileSync38,renameSync as renameSync10,statSync as statSync13,writeFileSync as writeFileSync27}from"fs";import{homedir as homedir46}from"os";import{basename as basename10,join as join69,relative as relative4,resolve as resolve13}from"path";import{gunzipSync,gzipSync}from"zlib";var SNAPSHOT_FILE="snapshot.sql.gz";function getSnapshotPath(cwd){let repoRoot=resolveRepoPath(cwd),genieHome6=process.env.GENIE_HOME??join69(homedir46(),".genie");return join69(genieHome6,"backups",basename10(repoRoot),SNAPSHOT_FILE)}function assertOutsideRepo(snapshotPath,cwd){let repoRoot=resolveRepoPath(cwd),rel=relative4(repoRoot,resolve13(snapshotPath));if(!rel.startsWith("..")&&rel!==""&&!rel.startsWith("/"))throw Error(`Refusing to write snapshot inside repo tree: ${snapshotPath}. Snapshots must live outside the repo (default: ~/.genie/backups/<repo>/).`)}function pgEnv(database){let forceTcp=process.env.GENIE_PG_FORCE_TCP==="1",testPort=process.env.GENIE_TEST_PG_PORT,useSocket=!forceTcp&&!testPort,resolvedDatabase=database??resolveDatabaseName();if(useSocket)return{...process.env,PGHOST:resolvePgserveSocketDir(),PGUSER:DB_NAME,PGPASSWORD:resolvePgserveAuthPassword(),PGDATABASE:resolvedDatabase};let port=testPort&&testPort.length>0?testPort:String(getActivePort());return{...process.env,PGHOST:"127.0.0.1",PGPORT:port,PGUSER:DB_NAME,PGPASSWORD:resolveTcpPgPassword(),PGDATABASE:resolvedDatabase}}function backup(cwd){let snapshotPath=getSnapshotPath(cwd);assertOutsideRepo(snapshotPath,cwd);let snapshotDir=snapshotPath.slice(0,snapshotPath.lastIndexOf("/")),tmpPath=`${snapshotPath}.tmp`;mkdirSync28(snapshotDir,{recursive:!0});let result2=spawnSync8("pg_dump",["--no-owner","--no-acl","--clean","--if-exists"],{env:pgEnv(),stdio:["pipe","pipe","pipe"],timeout:120000,maxBuffer:1073741824});if(result2.status!==0){let stderr=result2.stderr?.toString().trim()||"unknown error";throw Error(`pg_dump failed (exit ${result2.status}): ${stderr}`)}let compressed=gzipSync(result2.stdout);writeFileSync27(tmpPath,compressed),renameSync10(tmpPath,snapshotPath);let compressedBytes=statSync13(snapshotPath).size,uncompressedBytes=0;try{let sizeResult=spawnSync8("psql",["-t","-A","-c","SELECT pg_database_size(current_database())"],{env:pgEnv(),encoding:"utf-8",timeout:1e4});if(sizeResult.status===0)uncompressedBytes=Number.parseInt(sizeResult.stdout.trim(),10)||0}catch{}return{path:snapshotPath,compressedBytes,uncompressedBytes}}function restore(snapshotFile,cwd){let filePath=snapshotFile??getSnapshotPath(cwd);if(!existsSync57(filePath))throw Error(`Snapshot not found: ${filePath}`);let env=pgEnv();spawnSync8("psql",["-c","SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid()"],{env,stdio:["pipe","pipe","pipe"],timeout:1e4});let compressed=readFileSync38(filePath),sql=gunzipSync(compressed),restoreResult=spawnSync8("psql",["-v","ON_ERROR_STOP=1"],{env,input:sql,stdio:["pipe","pipe","pipe"],timeout:300000,maxBuffer:1073741824});if(restoreResult.status!==0)throw Error(`psql restore failed (exit ${restoreResult.status}): ${restoreResult.stderr?.toString().trim()}`)}init_db_migrations();init_db();init_term_format();init_src();init_db();var PG_USER2="postgres",PG_PASS2="postgres",PG_HOST2="127.0.0.1",SYSTEM_DBS=new Set(["postgres","template0","template1"]),V2_TENANT_DB_PATTERN=/^app_[a-z0-9_]{1,50}_[0-9a-f]{12}$/;async function dbLsCommand(options){if(!await isAvailable())console.error("Database is not running."),process.exit(1);let portRows=await(await getConnection()).unsafe("SHOW port"),port=Number(portRows[0]?.port);if(!Number.isFinite(port)||port<=0)console.error("Could not resolve pgserve TCP port."),await shutdown(),process.exit(1);let admin=src_default({host:PG_HOST2,port,username:PG_USER2,password:PG_PASS2,database:"postgres",max:1,onnotice:()=>{},idle_timeout:5});try{let filtered=await loadAllDbs(admin);if(!options.all)filtered=filtered.filter((r)=>!r.isSystem);if(options.orphans)filtered=filtered.filter((r)=>!r.isV2Pattern&&!r.isSystem&&!r.fingerprint);if(options.counts)for(let row2 of filtered){if(row2.isSystem)continue;try{let c=src_default({host:PG_HOST2,port,username:PG_USER2,password:PG_PASS2,database:row2.name,max:1,onnotice:()=>{},idle_timeout:1,connect_timeout:3}),probe=await c`
4286
- SELECT relname, n_live_tup FROM pg_stat_user_tables
4287
- WHERE schemaname='public' AND relname IN ('tasks','wishes','teams','sessions')
4288
- `,counts={};for(let p of probe){let n=Number(p.n_live_tup);if(p.relname==="tasks")counts.tasks=n;else if(p.relname==="wishes")counts.wishes=n;else if(p.relname==="teams")counts.teams=n;else if(p.relname==="sessions")counts.sessions=n}row2.counts=counts,await c.end({timeout:1})}catch{}}if(options.json)console.log(JSON.stringify(filtered,null,2));else printTable(filtered,{showCounts:!!options.counts})}finally{await admin.end({timeout:5})}await shutdown()}async function loadAllDbs(admin){return(await admin`
4285
+ `).filter(Boolean);for(let line of newLines)printLogLine(line);lastSize=content.length}}catch{}});process.on("SIGINT",()=>{watcher2.close(),process.exit(0)}),await new Promise(()=>{})}function printLogLine(raw){try{let entry2=JSON.parse(raw),ts3=entry2.timestamp?new Date(entry2.timestamp).toLocaleTimeString("en-US",{hour12:!1}):"??:??:??",level=(entry2.level??"info").toUpperCase().padEnd(5),event=entry2.event??"unknown",extras=Object.entries(entry2).filter(([k])=>!["timestamp","level","event"].includes(k)).map(([k,v])=>`${k}=${typeof v==="object"?JSON.stringify(v):v}`).join(" ");console.log(`${ts3} ${level} ${event}${extras?` ${extras}`:""}`)}catch{console.log(raw)}}function formatUptime(ms){let seconds=Math.floor(ms/1000),minutes=Math.floor(seconds/60),hours=Math.floor(minutes/60),days=Math.floor(hours/24);if(days>0)return`${days}d ${hours%24}h ${minutes%60}m`;if(hours>0)return`${hours}h ${minutes%60}m`;if(minutes>0)return`${minutes}m ${seconds%60}s`;return`${seconds}s`}function registerDaemonCommands(program2){let daemon=program2.command("daemon").description("Manage scheduler daemon lifecycle (redirects to genie serve --headless)");daemon.command("install").description("Generate systemd service unit and enable it").action(async()=>{await daemonInstallCommand()}),daemon.command("start").description("Start the scheduler daemon (alias for genie serve --headless)").option("--foreground","Run in foreground (for systemd ExecStart)").action(async(options)=>{await daemonStartCommand(options)}),daemon.command("stop").description("Stop genie serve gracefully").action(async()=>{await daemonStopCommand()}),daemon.command("status").description("Show daemon state, PID, uptime, and trigger stats").action(async()=>{await daemonStatusCommand()}),daemon.command("logs").description("Tail structured JSON scheduler log").option("--follow, -f","Follow log output").option("--lines <n>","Number of lines to show (default: 20)",Number.parseInt).action(async(options)=>{await daemonLogsCommand(options)})}init_cron();import{existsSync as existsSync58,readFileSync as readFileSync39}from"fs";import{dirname as dirname24,join as join70}from"path";import{createInterface as createInterface3}from"readline";init_db();init_wish_state();import{spawnSync as spawnSync8}from"child_process";import{existsSync as existsSync57,mkdirSync as mkdirSync28,readFileSync as readFileSync38,renameSync as renameSync10,statSync as statSync13,writeFileSync as writeFileSync27}from"fs";import{homedir as homedir46}from"os";import{basename as basename10,join as join69,relative as relative4,resolve as resolve13}from"path";import{gunzipSync,gzipSync}from"zlib";var SNAPSHOT_FILE="snapshot.sql.gz";function getSnapshotPath(cwd){let repoRoot=resolveRepoPath(cwd),genieHome6=process.env.GENIE_HOME??join69(homedir46(),".genie");return join69(genieHome6,"backups",basename10(repoRoot),SNAPSHOT_FILE)}function assertOutsideRepo(snapshotPath,cwd){let repoRoot=resolveRepoPath(cwd),rel=relative4(repoRoot,resolve13(snapshotPath));if(!rel.startsWith("..")&&rel!==""&&!rel.startsWith("/"))throw Error(`Refusing to write snapshot inside repo tree: ${snapshotPath}. Snapshots must live outside the repo (default: ~/.genie/backups/<repo>/).`)}function pgEnv(database){let forceTcp=process.env.GENIE_PG_FORCE_TCP==="1",testPort=process.env.GENIE_TEST_PG_PORT,useSocket=!forceTcp&&!testPort,resolvedDatabase=database??resolveDatabaseName();if(useSocket)return{...process.env,PGHOST:resolvePgserveSocketDir(),PGUSER:DB_NAME,PGPASSWORD:resolvePgserveAuthPassword(),PGDATABASE:resolvedDatabase};let port=testPort&&testPort.length>0?testPort:String(getActivePort());return{...process.env,PGHOST:"127.0.0.1",PGPORT:port,PGUSER:DB_NAME,PGPASSWORD:resolveTcpPgPassword(),PGDATABASE:resolvedDatabase}}function backup(cwd){let snapshotPath=getSnapshotPath(cwd);assertOutsideRepo(snapshotPath,cwd);let snapshotDir=snapshotPath.slice(0,snapshotPath.lastIndexOf("/")),tmpPath=`${snapshotPath}.tmp`;mkdirSync28(snapshotDir,{recursive:!0});let result2=spawnSync8("pg_dump",["--no-owner","--no-acl","--clean","--if-exists"],{env:pgEnv(),stdio:["pipe","pipe","pipe"],timeout:120000,maxBuffer:1073741824});if(result2.status!==0){let stderr=result2.stderr?.toString().trim()||"unknown error";throw Error(`pg_dump failed (exit ${result2.status}): ${stderr}`)}let compressed=gzipSync(result2.stdout);writeFileSync27(tmpPath,compressed),renameSync10(tmpPath,snapshotPath);let compressedBytes=statSync13(snapshotPath).size,uncompressedBytes=0;try{let sizeResult=spawnSync8("psql",["-t","-A","-c","SELECT pg_database_size(current_database())"],{env:pgEnv(),encoding:"utf-8",timeout:1e4});if(sizeResult.status===0)uncompressedBytes=Number.parseInt(sizeResult.stdout.trim(),10)||0}catch{}return{path:snapshotPath,compressedBytes,uncompressedBytes}}function restore(snapshotFile,cwd){let filePath=snapshotFile??getSnapshotPath(cwd);if(!existsSync57(filePath))throw Error(`Snapshot not found: ${filePath}`);let env=pgEnv();spawnSync8("psql",["-c","SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = current_database() AND pid <> pg_backend_pid()"],{env,stdio:["pipe","pipe","pipe"],timeout:1e4});let compressed=readFileSync38(filePath),sql=gunzipSync(compressed),restoreResult=spawnSync8("psql",["-v","ON_ERROR_STOP=1"],{env,input:sql,stdio:["pipe","pipe","pipe"],timeout:300000,maxBuffer:1073741824});if(restoreResult.status!==0)throw Error(`psql restore failed (exit ${restoreResult.status}): ${restoreResult.stderr?.toString().trim()}`)}init_db_migrations();init_db();init_term_format();init_src();init_db();var PG_USER2="postgres",PG_PASS2="postgres",PG_HOST2="127.0.0.1",SYSTEM_DBS=new Set(["postgres","template0","template1"]),V2_TENANT_DB_PATTERN=/^app_[a-z0-9_]{1,50}_[0-9a-f]{12}$/;async function dbLsCommand(options){if(!await isAvailable())console.error("Database is not running."),process.exit(1);let v2=await getConnection(),port=await resolveAdminPort(v2),admin=openAdminClient(port);try{let rows=await loadAllDbs(admin),filtered=applyFilters(rows,options);if(options.counts)await enrichWithCounts(filtered,port);if(options.json)console.log(JSON.stringify(filtered,null,2));else printTable(filtered,{showCounts:!!options.counts})}finally{await admin.end({timeout:5})}await shutdown()}async function resolveAdminPort(v2){let portRows=await v2.unsafe("SHOW port"),port=Number(portRows[0]?.port);if(!Number.isFinite(port)||port<=0)console.error("Could not resolve pgserve TCP port."),await shutdown(),process.exit(1);return port}function openAdminClient(port){return src_default({host:PG_HOST2,port,username:PG_USER2,password:PG_PASS2,database:"postgres",max:1,onnotice:()=>{},idle_timeout:5})}function applyFilters(rows,options){let filtered=rows;if(!options.all)filtered=filtered.filter((r)=>!r.isSystem);if(options.orphans)filtered=filtered.filter((r)=>!r.isV2Pattern&&!r.isSystem&&!r.fingerprint);return filtered}async function enrichWithCounts(rows,port){for(let row2 of rows){if(row2.isSystem)continue;await probeRowCounts(row2,port)}}async function probeRowCounts(row2,port){try{let c=src_default({host:PG_HOST2,port,username:PG_USER2,password:PG_PASS2,database:row2.name,max:1,onnotice:()=>{},idle_timeout:1,connect_timeout:3}),probe=await c`
4286
+ SELECT relname, n_live_tup FROM pg_stat_user_tables
4287
+ WHERE schemaname='public' AND relname IN ('tasks','wishes','teams','sessions')
4288
+ `,counts={};for(let p of probe){let n=Number(p.n_live_tup);if(p.relname==="tasks")counts.tasks=n;else if(p.relname==="wishes")counts.wishes=n;else if(p.relname==="teams")counts.teams=n;else if(p.relname==="sessions")counts.sessions=n}row2.counts=counts,await c.end({timeout:1})}catch{}}async function loadAllDbs(admin){return(await admin`
4289
4289
  SELECT
4290
4290
  d.datname AS datname,
4291
4291
  pg_database_size(d.datname) AS size_bytes,
@@ -4297,7 +4297,7 @@ Genie Scheduler Daemon`),console.log("\u2500".repeat(50)),console.log(` Status:
4297
4297
  FROM pg_database d
4298
4298
  LEFT JOIN pgserve_meta m ON m.database_name = d.datname
4299
4299
  ORDER BY pg_database_size(d.datname) DESC
4300
- `).map((r)=>({name:r.datname,sizeBytes:Number(r.size_bytes),fingerprint:r.fingerprint,persist:!!r.persist,lastConnectionAt:r.last_connection_at,packageRealpath:r.package_realpath,livenessPid:r.liveness_pid,isV2Pattern:V2_TENANT_DB_PATTERN.test(r.datname),isSystem:SYSTEM_DBS.has(r.datname)}))}function printTable(rows,opts){if(rows.length===0){console.log("(no databases)");return}let cols=[{header:"NAME",getter:(r)=>r.name},{header:"SIZE",getter:(r)=>formatBytes(r.sizeBytes)},{header:"PERSIST",getter:(r)=>r.persist?"yes":"no"},{header:"LAST CONNECT",getter:(r)=>formatDate2(r.lastConnectionAt)},{header:"FINGERPRINT",getter:(r)=>r.fingerprint??"(no meta row)"},{header:"PACKAGE",getter:(r)=>r.packageRealpath??"(script-mode)"}];if(opts.showCounts)cols.push({header:"TASKS / WISHES / TEAMS / SESSIONS",getter:(r)=>{if(!r.counts)return"-";return`${r.counts.tasks??"-"} / ${r.counts.wishes??"-"} / ${r.counts.teams??"-"} / ${r.counts.sessions??"-"}`}});let widths=cols.map((c)=>Math.max(c.header.length,...rows.map((r)=>c.getter(r).length))),sep2=widths.map((w)=>"-".repeat(w)).join(" ");console.log(cols.map((c,i2)=>c.header.padEnd(widths[i2])).join(" ")),console.log(sep2);for(let r of rows)console.log(cols.map((c,i2)=>c.getter(r).padEnd(widths[i2])).join(" "));console.log();let total=rows.reduce((acc,r)=>acc+r.sizeBytes,0),persistCount=rows.filter((r)=>r.persist).length,orphanCount=rows.filter((r)=>!r.fingerprint&&!r.isSystem).length,v2Count=rows.filter((r)=>r.isV2Pattern).length;console.log(`${rows.length} databases (${formatBytes(total)} total) \u2014 `+`${persistCount} persist, ${orphanCount} orphans, ${v2Count} v2-pattern`)}function formatBytes(n){if(n<1024)return`${n}B`;let u=["KB","MB","GB","TB"],v=n/1024,i2=0;while(v>=1024&&i2<u.length-1)v/=1024,i2++;return`${v.toFixed(1)} ${u[i2]}`}function formatDate2(d){if(!d)return"-";let elapsedMs=Date.now()-d.getTime(),sec=Math.floor(elapsedMs/1000);if(sec<60)return`${sec}s ago`;let min=Math.floor(sec/60);if(min<60)return`${min}m ago`;let hr=Math.floor(min/60);if(hr<24)return`${hr}h ago`;return`${Math.floor(hr/24)}d ago`}function registerDbLsCommand(db){db.command("ls").description("List all pgserve databases on this host").option("--json","Machine-readable JSON output").option("--counts","Probe each DB for tasks/wishes/teams/sessions row counts (slower)").option("--all","Include system DBs (postgres, template0, template1)").option("--orphans","Only show DBs without a pgserve_meta row (legacy / unmanaged)").action(dbLsCommand)}init_src();init_db();init_v1_migration_prompt();var V1_DB_NAME2="genie",V1_USER="postgres",V1_PASS="postgres",V1_HOST="127.0.0.1",FOUNDATION_TABLES=["organizations","projects","executors","agents","agent_projects","teams","board_templates","boards","task_types","wishes","tasks","task_actors","task_dependencies","task_stage_log","tags","task_tags","assignments"],NEVER_MIGRATE=new Set(["tool_events","genie_runtime_events","heartbeats","machine_snapshots","genie_bridge_sessions","mailbox","messages"]);async function dbMigrateV1Command(options){if(!await isAvailable())console.error("Database is not running. Start it with: genie db status"),process.exit(1);let v2=await getConnection(),portRows=await v2.unsafe("SHOW port"),port=Number(portRows[0]?.port);if(!Number.isFinite(port)||port<=0)console.error("Could not resolve pgserve TCP port from runtime connection."),await shutdown(),process.exit(1);console.log("[genie db migrate-v1] Detecting v1 data\u2026");let detected=await detectV1(port);if(!detected){console.log(" No v1 `genie` database found. Nothing to migrate."),await shutdown();return}let targetDb=(await v2.unsafe("SELECT current_database() AS db"))[0]?.db;console.log(` Source: ${V1_DB_NAME2} (v1, port ${port}, ${formatBytes2(detected.totalSizeBytes)} on disk)`),console.log(` Target: ${targetDb} (your v2 workspace)`),console.log();let sessionDays=parseDayWindow(options.includeSessions,30),auditDays=parseDayWindow(options.includeAudit,0);console.log("Row counts in v1:");for(let t of FOUNDATION_TABLES){let c=detected.rowCounts[t];if(typeof c==="number")console.log(` ${t.padEnd(22)} ${c}`)}if(sessionDays>0)console.log(` ${"sessions".padEnd(22)} ${detected.rowCounts.sessions??0} (last ${sessionDays} days)`);if(auditDays>0)console.log(` ${"audit_events".padEnd(22)} ${detected.rowCounts.audit_events??0} (last ${auditDays} days)`);if(options.includeContent)console.log(` ${"session_content".padEnd(22)} ${detected.rowCounts.session_content??0} (last ${sessionDays} days, --include-content)`);if(console.log(),console.log(`Will SKIP: ${[...NEVER_MIGRATE].join(", ")}`),options.archive!==!1)console.log(`Will ARCHIVE: source DB renamed to ${archiveName()} (kept indefinitely; persist=true)`);if(console.log(),options.dryRun){console.log("--dry-run set, exiting."),await shutdown();return}if(!options.yes&&!await confirm("Proceed with migration?")){console.log("Aborted."),await shutdown();return}let v1=src_default({host:V1_HOST,port,username:V1_USER,password:V1_PASS,database:V1_DB_NAME2,max:1,onnotice:()=>{},idle_timeout:5}),v2Bypass=src_default({host:V1_HOST,port,username:V1_USER,password:V1_PASS,database:targetDb,max:1,onnotice:()=>{},idle_timeout:5,connection:{session_replication_role:"replica"}}),summary={},failures=[];try{for(let table of FOUNDATION_TABLES){if(NEVER_MIGRATE.has(table))continue;let r=await migrateOne(v1,v2Bypass,table);if(report(table,r),r.failed)failures.push(`${table}: ${r.failed}`);else summary[table]=r.copied}if(sessionDays>0){let r=await migrateOne(v1,v2Bypass,"sessions",{dateColumn:"created_at",days:sessionDays});if(report("sessions",r),r.failed)failures.push(`sessions: ${r.failed}`);else summary.sessions=r.copied}if(auditDays>0){let r=await migrateOne(v1,v2Bypass,"audit_events",{dateColumn:"created_at",days:auditDays});if(report("audit_events",r),r.failed)failures.push(`audit_events: ${r.failed}`);else summary.audit_events=r.copied}if(options.includeContent&&sessionDays>0){let r=await migrateOne(v1,v2Bypass,"session_content",{dateColumn:"created_at",days:sessionDays});if(report("session_content",r),r.failed)failures.push(`session_content: ${r.failed}`);else summary.session_content=r.copied}}finally{await v1.end({timeout:5}),await v2Bypass.end({timeout:5})}console.log();let total=Object.values(summary).reduce((a,b2)=>a+b2,0);console.log(`Migrated ${total} rows across ${Object.keys(summary).length} tables. ${failures.length} failed.`);for(let f of failures)console.log(` - ${f}`);if(failures.length===0){if(await recordMigrationComplete(v2,V1_DB_NAME2,summary),console.log("Migration recorded in _genie_migration_state \u2014 startup prompt will be silenced."),options.archive!==!1){let archive=archiveName();console.log(`Archiving v1 \u2192 ${archive}`),await archiveV1(port,archive),console.log(`Done. ${archive} renamed and marked persist=true.`)}}else console.log(`Skipping archive due to ${failures.length} failures. Re-run after resolving (idempotent).`);await shutdown()}async function detectV1(port){let v1=src_default({host:V1_HOST,port,username:V1_USER,password:V1_PASS,database:V1_DB_NAME2,max:1,onnotice:()=>{},connect_timeout:5,idle_timeout:1});try{let tableRows=await v1`
4300
+ `).map((r)=>({name:r.datname,sizeBytes:Number(r.size_bytes),fingerprint:r.fingerprint,persist:!!r.persist,lastConnectionAt:r.last_connection_at,packageRealpath:r.package_realpath,livenessPid:r.liveness_pid,isV2Pattern:V2_TENANT_DB_PATTERN.test(r.datname),isSystem:SYSTEM_DBS.has(r.datname)}))}function printTable(rows,opts){if(rows.length===0){console.log("(no databases)");return}let cols=[{header:"NAME",getter:(r)=>r.name},{header:"SIZE",getter:(r)=>formatBytes(r.sizeBytes)},{header:"PERSIST",getter:(r)=>r.persist?"yes":"no"},{header:"LAST CONNECT",getter:(r)=>formatDate2(r.lastConnectionAt)},{header:"FINGERPRINT",getter:(r)=>r.fingerprint??"(no meta row)"},{header:"PACKAGE",getter:(r)=>r.packageRealpath??"(script-mode)"}];if(opts.showCounts)cols.push({header:"TASKS / WISHES / TEAMS / SESSIONS",getter:(r)=>{if(!r.counts)return"-";return`${r.counts.tasks??"-"} / ${r.counts.wishes??"-"} / ${r.counts.teams??"-"} / ${r.counts.sessions??"-"}`}});let widths=cols.map((c)=>Math.max(c.header.length,...rows.map((r)=>c.getter(r).length))),sep2=widths.map((w)=>"-".repeat(w)).join(" ");console.log(cols.map((c,i2)=>c.header.padEnd(widths[i2])).join(" ")),console.log(sep2);for(let r of rows)console.log(cols.map((c,i2)=>c.getter(r).padEnd(widths[i2])).join(" "));console.log();let total=rows.reduce((acc,r)=>acc+r.sizeBytes,0),persistCount=rows.filter((r)=>r.persist).length,orphanCount=rows.filter((r)=>!r.fingerprint&&!r.isSystem).length,v2Count=rows.filter((r)=>r.isV2Pattern).length;console.log(`${rows.length} databases (${formatBytes(total)} total) \u2014 `+`${persistCount} persist, ${orphanCount} orphans, ${v2Count} v2-pattern`)}function formatBytes(n){if(n<1024)return`${n}B`;let u=["KB","MB","GB","TB"],v=n/1024,i2=0;while(v>=1024&&i2<u.length-1)v/=1024,i2++;return`${v.toFixed(1)} ${u[i2]}`}function formatDate2(d){if(!d)return"-";let elapsedMs=Date.now()-d.getTime(),sec=Math.floor(elapsedMs/1000);if(sec<60)return`${sec}s ago`;let min=Math.floor(sec/60);if(min<60)return`${min}m ago`;let hr=Math.floor(min/60);if(hr<24)return`${hr}h ago`;return`${Math.floor(hr/24)}d ago`}function registerDbLsCommand(db){db.command("ls").description("List all pgserve databases on this host").option("--json","Machine-readable JSON output").option("--counts","Probe each DB for tasks/wishes/teams/sessions row counts (slower)").option("--all","Include system DBs (postgres, template0, template1)").option("--orphans","Only show DBs without a pgserve_meta row (legacy / unmanaged)").action(dbLsCommand)}init_src();init_db();init_v1_migration_prompt();var V1_DB_NAME2="genie",V1_USER="postgres",V1_PASS="postgres",V1_HOST="127.0.0.1",FOUNDATION_TABLES=["organizations","projects","executors","agents","agent_projects","teams","board_templates","boards","task_types","wishes","tasks","task_actors","task_dependencies","task_stage_log","tags","task_tags","assignments"],NEVER_MIGRATE=new Set(["tool_events","genie_runtime_events","heartbeats","machine_snapshots","genie_bridge_sessions","mailbox","messages"]);async function dbMigrateV1Command(options){if(!await isAvailable())console.error("Database is not running. Start it with: genie db status"),process.exit(1);let v2=await getConnection(),port=await resolveV1Port(v2);console.log("[genie db migrate-v1] Detecting v1 data\u2026");let detected=await detectV1(port);if(!detected){console.log(" No v1 `genie` database found. Nothing to migrate."),await shutdown();return}let targetDb=(await v2.unsafe("SELECT current_database() AS db"))[0]?.db??"",sessionDays=parseDayWindow(options.includeSessions,30),auditDays=parseDayWindow(options.includeAudit,0);if(printMigrationPreview(detected,port,targetDb,options,sessionDays,auditDays),options.dryRun){console.log("--dry-run set, exiting."),await shutdown();return}if(!options.yes&&!await confirm("Proceed with migration?")){console.log("Aborted."),await shutdown();return}let{v1,v2Bypass}=openMigrationClients(port,targetDb),summary={},failures=[];try{await runMigrations2(v1,v2Bypass,options,sessionDays,auditDays,summary,failures)}finally{await v1.end({timeout:5}),await v2Bypass.end({timeout:5})}await finalizeMigration(v2,summary,failures,port,options),await shutdown()}async function resolveV1Port(v2){let portRows=await v2.unsafe("SHOW port"),port=Number(portRows[0]?.port);if(!Number.isFinite(port)||port<=0)console.error("Could not resolve pgserve TCP port from runtime connection."),await shutdown(),process.exit(1);return port}function printMigrationPreview(detected,port,targetDb,options,sessionDays,auditDays){console.log(` Source: ${V1_DB_NAME2} (v1, port ${port}, ${formatBytes2(detected.totalSizeBytes)} on disk)`),console.log(` Target: ${targetDb} (your v2 workspace)`),console.log(),console.log("Row counts in v1:");for(let t of FOUNDATION_TABLES){let c=detected.rowCounts[t];if(typeof c==="number")console.log(` ${t.padEnd(22)} ${c}`)}if(sessionDays>0)console.log(` ${"sessions".padEnd(22)} ${detected.rowCounts.sessions??0} (last ${sessionDays} days)`);if(auditDays>0)console.log(` ${"audit_events".padEnd(22)} ${detected.rowCounts.audit_events??0} (last ${auditDays} days)`);if(options.includeContent)console.log(` ${"session_content".padEnd(22)} ${detected.rowCounts.session_content??0} (last ${sessionDays} days, --include-content)`);if(console.log(),console.log(`Will SKIP: ${[...NEVER_MIGRATE].join(", ")}`),options.archive!==!1)console.log(`Will ARCHIVE: source DB renamed to ${archiveName()} (kept indefinitely; persist=true)`);console.log()}function openMigrationClients(port,targetDb){let v1=src_default({host:V1_HOST,port,username:V1_USER,password:V1_PASS,database:V1_DB_NAME2,max:1,onnotice:()=>{},idle_timeout:5}),v2Bypass=src_default({host:V1_HOST,port,username:V1_USER,password:V1_PASS,database:targetDb,max:1,onnotice:()=>{},idle_timeout:5,connection:{session_replication_role:"replica"}});return{v1,v2Bypass}}async function runMigrations2(v1,v2Bypass,options,sessionDays,auditDays,summary,failures){for(let table of FOUNDATION_TABLES){if(NEVER_MIGRATE.has(table))continue;await runOne(v1,v2Bypass,table,summary,failures)}if(sessionDays>0)await runOne(v1,v2Bypass,"sessions",summary,failures,{dateColumn:"created_at",days:sessionDays});if(auditDays>0)await runOne(v1,v2Bypass,"audit_events",summary,failures,{dateColumn:"created_at",days:auditDays});if(options.includeContent&&sessionDays>0)await runOne(v1,v2Bypass,"session_content",summary,failures,{dateColumn:"created_at",days:sessionDays})}async function runOne(v1,v2Bypass,table,summary,failures,windowOpt){let r=await migrateOne(v1,v2Bypass,table,windowOpt);if(report(table,r),r.failed)failures.push(`${table}: ${r.failed}`);else summary[table]=r.copied}async function finalizeMigration(v2,summary,failures,port,options){console.log();let total=Object.values(summary).reduce((a,b2)=>a+b2,0);console.log(`Migrated ${total} rows across ${Object.keys(summary).length} tables. ${failures.length} failed.`);for(let f of failures)console.log(` - ${f}`);if(failures.length>0){console.log(`Skipping archive due to ${failures.length} failures. Re-run after resolving (idempotent).`);return}if(await recordMigrationComplete(v2,V1_DB_NAME2,summary),console.log("Migration recorded in _genie_migration_state \u2014 startup prompt will be silenced."),options.archive!==!1){let archive=archiveName();console.log(`Archiving v1 \u2192 ${archive}`),await archiveV1(port,archive),console.log(`Done. ${archive} renamed and marked persist=true.`)}}async function detectV1(port){let v1=src_default({host:V1_HOST,port,username:V1_USER,password:V1_PASS,database:V1_DB_NAME2,max:1,onnotice:()=>{},connect_timeout:5,idle_timeout:1});try{let tableRows=await v1`
4301
4301
  SELECT relname, n_live_tup FROM pg_stat_user_tables WHERE schemaname='public'
4302
4302
  `;if(tableRows.length===0)return null;let counts={};for(let r of tableRows)counts[r.relname]=Number(r.n_live_tup);let sizeRows=await v1`SELECT pg_database_size(${V1_DB_NAME2}) AS size`;return{rowCounts:counts,totalSizeBytes:Number(sizeRows[0]?.size??0)}}catch(err){let code=err.code;if(["3D000","08001","08006","ECONNREFUSED"].includes(code??""))return null;throw err}finally{try{await v1.end({timeout:1})}catch{}}}async function getInsertableColumns(client,table){return client`
4303
4303
  SELECT a.attname AS name, a.attidentity::text AS identity, a.attgenerated::text AS generated
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/genie",
3
- "version": "4.260504.14",
3
+ "version": "4.260504.16",
4
4
  "description": "Collaborative terminal toolkit for human + AI workflows. NOTE: the npm distribution is being soft-deprecated — the canonical install is `curl -fsSL https://get.automagik.dev/genie | bash` (cosign + SLSA verified). See https://automagik.dev/genie/security/distribution-sovereignty",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie",
3
- "version": "4.260504.14",
3
+ "version": "4.260504.16",
4
4
  "description": "Human-AI partnership for Claude Code. Share a terminal, orchestrate workers, evolve together. Brainstorm ideas, turn them into wishes, execute with /work, validate with /review, and ship as one team.",
5
5
  "author": {
6
6
  "name": "Namastex Labs"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genie-plugin",
3
- "version": "4.260504.14",
3
+ "version": "4.260504.16",
4
4
  "private": true,
5
5
  "description": "Runtime dependencies for genie bundled CLIs",
6
6
  "type": "module",