@automagik/genie 4.260424.2 → 4.260424.4
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/README.md +1 -8
- package/dist/genie.js +94 -1
- package/package.json +1 -1
- package/plugins/genie/.claude-plugin/plugin.json +1 -1
- package/plugins/genie/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,14 +25,7 @@
|
|
|
25
25
|
-->
|
|
26
26
|
|
|
27
27
|
<!-- METRICS:START -->
|
|
28
|
-
**🚀
|
|
29
|
-
|
|
30
|
-
| Metric | Value (7d) |
|
|
31
|
-
|--------|-----------|
|
|
32
|
-
| PRs merged | 142 (20/day) |
|
|
33
|
-
| Avg merge time | 0.7h |
|
|
34
|
-
| SHIP rate | 99% |
|
|
35
|
-
| Releases (24h) | 0 |
|
|
28
|
+
**🚀 122 commits** this week · **7 releases** · **+28.1K LoC** · **6 contributors**
|
|
36
29
|
|
|
37
30
|

|
|
38
31
|
|
package/dist/genie.js
CHANGED
|
@@ -4165,7 +4165,100 @@ Team: ${teamName}
|
|
|
4165
4165
|
History for "${schedule.name}":
|
|
4166
4166
|
`);let tableRows=rows.map((r)=>[formatTimestamp2(r.due_at),r.trigger_status,r.run_status??"-",formatDuration2(r.duration_ms),r.error?r.error.slice(0,60):"-"]);printTable2(["DUE AT","TRIGGER","RUN","DURATION","ERROR"],tableRows),await shutdown()}catch(error2){let message=error2 instanceof Error?error2.message:String(error2);console.error(`Error: ${message}`),process.exit(1)}}function registerScheduleCommands(program2){let schedule=program2.command("schedule").description("Manage scheduled triggers");schedule.command("create <name>").description("Create a new schedule").requiredOption("--command <cmd>",'Command to execute (e.g., "genie spawn reviewer")').option("--at <time>","One-time schedule at absolute time (ISO 8601)").option("--every <interval>","Repeating schedule: duration (10m, 2h, 24h) or cron expression").option("--after <duration>","One-time schedule after delay (10m, 2h)").option("--timezone <tz>","Timezone for schedule (default: UTC)","UTC").option("--lease-timeout <duration>","Lease timeout for runs (default: 5m)").action(async(name,options)=>{await scheduleCreateCommand(name,options)}),schedule.command("list").description("List schedules with next due trigger").option("--json","Output as JSON").option("--watch","Refresh every 2s").action(async(options)=>{await scheduleListCommand(options)}),schedule.command("cancel <name>").description("Cancel a schedule and skip pending triggers").option("--filter <expr>","Filter expression (e.g., status=pending)").action(async(name,options)=>{await scheduleCancelCommand(name,options)}),schedule.command("retry <name>").description("Reset a failed trigger to pending").action(async(name)=>{await scheduleRetryCommand(name)}),schedule.command("history <name>").description("Show past executions for a schedule").option("--limit <n>","Max rows to show (default: 20)",Number.parseInt).action(async(name,options)=>{await scheduleHistoryCommand(name,options)})}import{spawnSync as spawnSync7}from"child_process";import{existsSync as existsSync54,readFileSync as readFileSync35,readdirSync as readdirSync11,realpathSync as realpathSync6}from"fs";import{dirname as dirname14,join as join65,resolve as resolve12}from"path";var defaultDeps5={existsSync:existsSync54,realpathSync:realpathSync6,readFileSync:(path3,encoding)=>readFileSync35(path3,encoding),spawnSync:(command,args,options)=>spawnSync7(command,args,options),setExitCode:(exitCode)=>{process.exitCode=exitCode},stdout:(line)=>process.stdout.write(`${line}
|
|
4167
4167
|
`),stderr:(line)=>process.stderr.write(`${line}
|
|
4168
|
-
`),now:()=>new Date};function collectRepeatedOption(value,previous){return[...previous,value]}function collectKillPid(value,previous){let pid=Number.parseInt(value,10);if(!Number.isFinite(pid)||pid<=0)throw Error(`--kill-pid expects a positive integer, got "${value}"`);return[...previous,pid]}function resolveGenieRoot2(argv1=process.argv[1],deps=defaultDeps5){try{if(argv1){let scriptDir=dirname14(deps.realpathSync(argv1)),candidates=[resolve12(scriptDir,".."),resolve12(scriptDir,"..","..")];for(let candidate of candidates)if(deps.existsSync(join65(candidate,"package.json")))return candidate}}catch{}return resolve12(import.meta.dir,"..","..")}function resolveSecScanScript(argv1=process.argv[1],deps=defaultDeps5){let root=resolveGenieRoot2(argv1,deps),scriptPath=join65(root,"scripts","sec-scan.cjs");if(!deps.existsSync(scriptPath))throw Error(`Security scanner payload not found at ${scriptPath}`);return scriptPath}function resolveSecRemediateScript(argv1=process.argv[1],deps=defaultDeps5){let root=resolveGenieRoot2(argv1,deps),scriptPath=join65(root,"scripts","sec-remediate.cjs");if(!deps.existsSync(scriptPath))throw Error(`Security remediation payload not found at ${scriptPath}`);return scriptPath}var BOOLEAN_FLAG_MAP=[["json","--json"],["allHomes","--all-homes"],["noProgress","--no-progress"],["quiet","--quiet"],["verbose","--verbose"],["progressJson","--progress-json"],["redact","--redact"],["impactSurface","--impact-surface"]],REPEATED_FLAG_MAP=[["home","--home"],["root","--root"],["phaseBudget","--phase-budget"]],STRING_FLAG_MAP=[["progressInterval","--progress-interval"],["eventsFile","--events-file"]];function buildSecScanArgv(options){let args=[];for(let[key,flag]of BOOLEAN_FLAG_MAP)if(options[key])args.push(flag);for(let[key,flag]of REPEATED_FLAG_MAP){let values2=options[key]??[];for(let value of values2)args.push(flag,value)}for(let[key,flag]of STRING_FLAG_MAP){let value=options[key];if(value)args.push(flag,value)}if(options.persist===!1)args.push("--no-persist");return args}function buildSecRemediateArgv(options){let args=[];if(options.dryRun)args.push("--dry-run");if(options.apply)args.push("--apply");if(options.resume)args.push("--resume",options.resume);if(options.scanReport)args.push("--scan-report",options.scanReport);if(options.scanId)args.push("--scan-id",options.scanId);if(options.plan)args.push("--plan",options.plan);if(options.quarantineDir)args.push("--quarantine-dir",options.quarantineDir);if(options.unsafeUnverified)args.push("--unsafe-unverified",options.unsafeUnverified);if(options.remediatePartial)args.push("--remediate-partial");if(options.confirmIncompleteScan)args.push("--confirm-incomplete-scan",options.confirmIncompleteScan);for(let pid of options.killPid??[])args.push("--kill-pid",String(pid));if(options.autoConfirmFrom)args.push("--auto-confirm-from",options.autoConfirmFrom);if(options.json)args.push("--json");return args}function runSecScan(options,deps=defaultDeps5){let args=[resolveSecScanScript(process.argv[1],deps),...buildSecScanArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecRemediate(options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecRemediateArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecRestore(quarantineId,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),"--restore",quarantineId],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function buildSecRollbackArgv(scanId,options){let args=["--rollback",scanId];if(options.json)args.push("--json");return args}function buildSecQuarantineListArgv(options){let args=["--quarantine-list"];if(options.json)args.push("--json");return args}function buildSecQuarantineGcArgv(options){let args=["--quarantine-gc"];if(options.olderThan)args.push("--older-than",options.olderThan);if(options.confirmGc)args.push("--confirm-gc",options.confirmGc);if(options.json)args.push("--json");return args}function runSecRollback(scanId,options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecRollbackArgv(scanId,options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecQuarantineList(options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecQuarantineListArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecQuarantineGc(options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecQuarantineGcArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function applySecScanExitCode(exitCode,deps=defaultDeps5){if(exitCode!==0)deps.setExitCode(exitCode)}var VERIFY_EXIT={VERIFIED:0,SIGNATURE_INVALID:2,SIGNER_IDENTITY_MISMATCH:3,PROVENANCE_INVALID:4,NO_SIGNATURE_MATERIAL:5,MISSING_BINARY:127},SIGNER_IDENTITY_REGEXP="^https://github.com/automagik-dev/genie/.github/workflows/release.yml@",SIGNER_OIDC_ISSUER="https://token.actions.githubusercontent.com",PROVENANCE_SOURCE_URI="github.com/automagik-dev/genie",COSIGN_NO_KEY_SENTINEL="BEGIN COSIGN NO-PINNED-KEY SENTINEL";function discoverSignatureBundle(bundleDir,deps=defaultDeps5){if(!deps.existsSync(bundleDir))return null;let candidates=[];try{for(let entry2 of readdirSync11(bundleDir))if(entry2.endsWith(".tgz"))candidates.push(entry2)}catch{return null}for(let tarballName of candidates){let tarball=join65(bundleDir,tarballName),signature=`${tarball}.sig`,certificate=`${tarball}.cert`;if(!deps.existsSync(signature))continue;if(!deps.existsSync(certificate))continue;let provenancePath=join65(bundleDir,"provenance.intoto.jsonl"),provenance=deps.existsSync(provenancePath)?provenancePath:null;return{tarball,signature,certificate,provenance}}return null}function readsAsCosignSentinel(path3,deps=defaultDeps5){if(!deps.existsSync(path3))return!1;try{return deps.readFileSync(path3,"utf8").includes(COSIGN_NO_KEY_SENTINEL)}catch{return!1}}function ensureBinary(name,deps){let result2=deps.spawnSync(name,["--version"],{stdio:"pipe",encoding:"utf8"});if(result2.error)return{ok:!1,binary:name,reason:result2.error.message};if((result2.status??1)!==0)return{ok:!1,binary:name,reason:`${name} --version exited non-zero`};return{ok:!0,binary:name}}function buildVerifyResult(exitCode,ctx){return{exitCode,json:{verified:exitCode===VERIFY_EXIT.VERIFIED,exit_code:exitCode,signer_identity:SIGNER_IDENTITY_REGEXP,signer_oidc_issuer:SIGNER_OIDC_ISSUER,signature_source:ctx.bundle?.signature??null,provenance_source:ctx.bundle?.provenance??null,tarball_path:ctx.bundle?.tarball??null,verified_at:ctx.verifiedAt,pinned_key_fingerprint:null,signing_mode:"cosign-keyless",offline:ctx.offline,errors:ctx.errors}}}function classifyCosignFailure(stderr){let lower=stderr.toLowerCase();return lower.includes("certificate identity")||lower.includes("subject does not match")||lower.includes("oidc issuer")?VERIFY_EXIT.SIGNER_IDENTITY_MISMATCH:VERIFY_EXIT.SIGNATURE_INVALID}function runCosignStep(bundle,offline,errors3,deps){let cosignCheck=ensureBinary("cosign",deps);if(!cosignCheck.ok)return errors3.push(`cosign not available in PATH (${cosignCheck.reason??"unknown"}). Install from https://docs.sigstore.dev/cosign/installation/.`),VERIFY_EXIT.MISSING_BINARY;let cosignArgs=["verify-blob","--certificate-identity-regexp",SIGNER_IDENTITY_REGEXP,"--certificate-oidc-issuer",SIGNER_OIDC_ISSUER,"--signature",bundle.signature,"--certificate",bundle.certificate,bundle.tarball];if(offline)cosignArgs.push("--insecure-ignore-tlog","--offline");let result2=deps.spawnSync("cosign",cosignArgs,{stdio:"pipe",encoding:"utf8"});if(result2.error)return errors3.push(`cosign spawn failed: ${result2.error.message}`),VERIFY_EXIT.MISSING_BINARY;if((result2.status??1)===0)return VERIFY_EXIT.VERIFIED;let stderr=typeof result2.stderr==="string"?result2.stderr:"";if(stderr)errors3.push(stderr.trim());return classifyCosignFailure(stderr)}function runSlsaStep(bundle,errors3,deps){if(!bundle.provenance)return errors3.push(`provenance.intoto.jsonl missing alongside ${bundle.tarball} \u2014 cosign passed but SLSA provenance cannot be checked.`),VERIFY_EXIT.PROVENANCE_INVALID;let slsaCheck=ensureBinary("slsa-verifier",deps);if(!slsaCheck.ok)return errors3.push(`slsa-verifier not available in PATH (${slsaCheck.reason??"unknown"}). Install from https://github.com/slsa-framework/slsa-verifier.`),VERIFY_EXIT.MISSING_BINARY;let result2=deps.spawnSync("slsa-verifier",["verify-artifact",bundle.tarball,"--provenance-path",bundle.provenance,"--source-uri",PROVENANCE_SOURCE_URI],{stdio:"pipe",encoding:"utf8"});if(result2.error)return errors3.push(`slsa-verifier spawn failed: ${result2.error.message}`),VERIFY_EXIT.MISSING_BINARY;if((result2.status??1)===0)return VERIFY_EXIT.VERIFIED;let stderr=typeof result2.stderr==="string"?result2.stderr:"";if(stderr)errors3.push(stderr.trim());return VERIFY_EXIT.PROVENANCE_INVALID}function resolveBundleDir(options,genieRoot){if(options.bundleDir)return options.bundleDir;if(options.tarball)return dirname14(resolve12(options.tarball));return resolve12(genieRoot)}function runVerifyInstall(options,deps=defaultDeps5){let errors3=[],verifiedAt=deps.now().toISOString(),offline=options.offline===!0,genieRoot=resolveGenieRoot2(process.argv[1],deps),bundleDir=resolveBundleDir(options,genieRoot),bundle=discoverSignatureBundle(bundleDir,deps),ctx={bundle,verifiedAt,offline,errors:errors3};if(!bundle){if(errors3.push(`No signed release bundle found under ${bundleDir}. Expected <pkg>.tgz + .sig + .cert + provenance.intoto.jsonl.`),readsAsCosignSentinel(join65(genieRoot,".github","cosign.pub"),deps))errors3.push(".github/cosign.pub is the documented NO-KEY sentinel \u2014 release signing is cosign KEYLESS ONLY; there is no public key to pin.");return buildVerifyResult(VERIFY_EXIT.NO_SIGNATURE_MATERIAL,ctx)}let cosignExit=runCosignStep(bundle,offline,errors3,deps);if(cosignExit!==VERIFY_EXIT.VERIFIED)return buildVerifyResult(cosignExit,ctx);let slsaExit=runSlsaStep(bundle,errors3,deps);return buildVerifyResult(slsaExit,ctx)}function emitHumanReport(result2,options,deps){let{json:json2,exitCode}=result2,status=json2.verified?"OK":"FAIL";if(deps.stdout(`verify-install: ${status} (exit ${exitCode})`),deps.stdout(` signing mode: ${json2.signing_mode}`),deps.stdout(` signer identity: ${json2.signer_identity}`),deps.stdout(` OIDC issuer: ${json2.signer_oidc_issuer}`),deps.stdout(` provenance source: ${PROVENANCE_SOURCE_URI}`),deps.stdout(` tarball: ${json2.tarball_path??"(not found)"}`),deps.stdout(` signature: ${json2.signature_source??"(not found)"}`),deps.stdout(` provenance: ${json2.provenance_source??"(not found)"}`),deps.stdout(` verified_at: ${json2.verified_at}`),deps.stdout(` offline: ${json2.offline?"yes (skips Rekor tlog)":"no"}`),options.offline)deps.stdout(" warning: offline mode skips the Rekor transparency log; revoked certs are not detected.");if(json2.errors.length>0){deps.stderr("verify-install errors:");for(let err of json2.errors)deps.stderr(` - ${err}`)}}function runVerifyInstallCommand(options,deps=defaultDeps5){let result2=runVerifyInstall(options,deps);if(options.json)deps.stdout(JSON.stringify(result2.json));else emitHumanReport(result2,options,deps);return result2.exitCode}function registerSecCommands(program2,deps=defaultDeps5){let sec=program2.command("sec").description("Security tooling \u2014 host compromise triage and IOC hunts");sec.command("scan",{isDefault:!0}).description("Scan host for TeamPCP/CanisterWorm-style package compromise indicators").option("--json","Output as JSON envelope").option("--all-homes","Scan /root, /home/*, /Users/*, and WSL Windows homes when present").option("--home <path>","Add a specific home directory to scan",collectRepeatedOption,[]).option("--root <path>","Add an application root to scan for project evidence",collectRepeatedOption,[]).option("--no-progress","Suppress progress output on stderr").option("--quiet","Suppress progress and banners on stderr").option("--verbose","Emit extra diagnostics on stderr").option("--progress-json","Emit progress as NDJSON events to stderr").option("--progress-interval <ms>","Progress tick interval in milliseconds").option("--events-file <path>","Append structured NDJSON events to a 0600-mode file").option("--redact","Hash $HOME-prefixed paths; scrub AWS/GitHub/npm/JWT patterns").option("--no-persist","Do not persist the report to $GENIE_HOME/sec-scan/").option("--impact-surface","Scan for at-risk local material (secrets, wallets, browsers)").option("--phase-budget <name=ms>","Budget (ms) for a named phase (repeatable)",collectRepeatedOption,[]).action((options)=>{let exitCode=runSecScan(options,deps);applySecScanExitCode(exitCode,deps)}),sec.command("remediate").description("Reversibly remediate findings from a sec scan (dry-run by default)").option("--dry-run","Generate a plan manifest without mutating anything (default mode)").option("--apply","Execute a previously-generated plan (requires --plan)").option("--resume <path>","Resume a previously-aborted apply from its resume file").option("--scan-report <path>","Path to a scan JSON report (use with --dry-run)").option("--scan-id <ulid>","ULID of a persisted scan in $GENIE_HOME/sec-scan/").option("--plan <path>","Path to a frozen plan manifest (required with --apply)").option("--quarantine-dir <path>","Override quarantine root (must be on same device as targets)").option("--unsafe-unverified <id>","Bypass binary signature requirement (logs incident id + ack)").option("--remediate-partial","Allow remediation against a coverage-capped scan (requires typed ack)").option("--confirm-incomplete-scan <ack>","Typed ack for --remediate-partial").option("--kill-pid <pid>","Authorize SIGTERM to a PID matching a plan entry",collectKillPid,[]).option("--auto-confirm-from <path>","Non-interactive consent map (testing only)").option("--json","Emit JSON summary to stdout").action((options)=>{let normalized={...options};if(!normalized.dryRun&&!normalized.apply&&!normalized.resume)normalized.dryRun=!0;let exitCode=runSecRemediate(normalized,deps);applySecScanExitCode(exitCode,deps)}),sec.command("restore <quarantine-id>").description("Restore every action under a quarantine id (sha256-verified per file)").action((quarantineId)=>{let exitCode=runSecRestore(quarantineId,deps);applySecScanExitCode(exitCode,deps)}),sec.command("rollback <scan-id>").description("Bulk undo every quarantined action for a scan (walks audit log in reverse)").option("--json","Emit JSON summary to stdout").action((scanId,options)=>{let exitCode=runSecRollback(scanId,options,deps);applySecScanExitCode(exitCode,deps)});let quarantine=sec.command("quarantine").description("Quarantine lifecycle (list, gc)");quarantine.command("list").description("List quarantines with id, timestamp, size, status, scan_id").option("--json","Emit JSON rows to stdout").action((options)=>{let exitCode=runSecQuarantineList(options,deps);applySecScanExitCode(exitCode,deps)}),quarantine.command("gc").description("Delete restored/abandoned quarantines older than <duration> (refuses active)").requiredOption("--older-than <duration>","Duration threshold, e.g. 30d, 24h, 15m").option("--confirm-gc <token>","Typed ack: CONFIRM-GC-<6-hex>").option("--json","Emit JSON summary to stdout").action((options)=>{let exitCode=runSecQuarantineGc(options,deps);applySecScanExitCode(exitCode,deps)}),sec.command("verify-install").description("Verify the cosign signature + SLSA provenance of the running @automagik/genie release.").option("--offline","Skip the Rekor transparency-log check (signature + cert still verified)").option("--json","Emit machine-readable verification report on stdout").option("--tarball <path>","Point at a specific release tarball (for local verification)").option("--bundle-dir <path>","Directory containing <pkg>.tgz + .sig + .cert + provenance").action((options)=>{let exitCode=runVerifyInstallCommand(options,deps);applySecScanExitCode(exitCode,deps)})}init_serve();init_db();init_session_backfill();init_term_format();function resolveAgentLabel(r){if(r.agent_name)return r.agent_name;if(r.executor_id)return r.executor_id.slice(0,12);if(r.agent_id)return r.agent_id;return"(orphaned)"}async function sessionsListCommand(options){if(!await isAvailable())console.error("Database not available."),process.exit(1);let sql=await getConnection(),limit=Number(options.limit)||50,sourceFilter=options.source?sql`AND e.metadata->>'source' = ${options.source}`:sql``,rows;if(options.active)rows=await sql`
|
|
4168
|
+
`),now:()=>new Date};function collectRepeatedOption(value,previous){return[...previous,value]}function collectKillPid(value,previous){let pid=Number.parseInt(value,10);if(!Number.isFinite(pid)||pid<=0)throw Error(`--kill-pid expects a positive integer, got "${value}"`);return[...previous,pid]}function resolveGenieRoot2(argv1=process.argv[1],deps=defaultDeps5){try{if(argv1){let scriptDir=dirname14(deps.realpathSync(argv1)),candidates=[resolve12(scriptDir,".."),resolve12(scriptDir,"..","..")];for(let candidate of candidates)if(deps.existsSync(join65(candidate,"package.json")))return candidate}}catch{}return resolve12(import.meta.dir,"..","..")}function resolveSecScanScript(argv1=process.argv[1],deps=defaultDeps5){let root=resolveGenieRoot2(argv1,deps),scriptPath=join65(root,"scripts","sec-scan.cjs");if(!deps.existsSync(scriptPath))throw Error(`Security scanner payload not found at ${scriptPath}`);return scriptPath}function resolveSecRemediateScript(argv1=process.argv[1],deps=defaultDeps5){let root=resolveGenieRoot2(argv1,deps),scriptPath=join65(root,"scripts","sec-remediate.cjs");if(!deps.existsSync(scriptPath))throw Error(`Security remediation payload not found at ${scriptPath}`);return scriptPath}var BOOLEAN_FLAG_MAP=[["json","--json"],["allHomes","--all-homes"],["noProgress","--no-progress"],["quiet","--quiet"],["verbose","--verbose"],["progressJson","--progress-json"],["redact","--redact"],["impactSurface","--impact-surface"]],REPEATED_FLAG_MAP=[["home","--home"],["root","--root"],["phaseBudget","--phase-budget"]],STRING_FLAG_MAP=[["progressInterval","--progress-interval"],["eventsFile","--events-file"]];function buildSecScanArgv(options){let args=[];for(let[key,flag]of BOOLEAN_FLAG_MAP)if(options[key])args.push(flag);for(let[key,flag]of REPEATED_FLAG_MAP){let values2=options[key]??[];for(let value of values2)args.push(flag,value)}for(let[key,flag]of STRING_FLAG_MAP){let value=options[key];if(value)args.push(flag,value)}if(options.persist===!1)args.push("--no-persist");return args}function buildSecRemediateArgv(options){let args=[];if(options.dryRun)args.push("--dry-run");if(options.apply)args.push("--apply");if(options.resume)args.push("--resume",options.resume);if(options.scanReport)args.push("--scan-report",options.scanReport);if(options.scanId)args.push("--scan-id",options.scanId);if(options.plan)args.push("--plan",options.plan);if(options.quarantineDir)args.push("--quarantine-dir",options.quarantineDir);if(options.unsafeUnverified)args.push("--unsafe-unverified",options.unsafeUnverified);if(options.remediatePartial)args.push("--remediate-partial");if(options.confirmIncompleteScan)args.push("--confirm-incomplete-scan",options.confirmIncompleteScan);for(let pid of options.killPid??[])args.push("--kill-pid",String(pid));if(options.autoConfirmFrom)args.push("--auto-confirm-from",options.autoConfirmFrom);if(options.json)args.push("--json");return args}function runSecScan(options,deps=defaultDeps5){let args=[resolveSecScanScript(process.argv[1],deps),...buildSecScanArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecRemediate(options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecRemediateArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecRestore(quarantineId,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),"--restore",quarantineId],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function buildSecRollbackArgv(scanId,options){let args=["--rollback",scanId];if(options.json)args.push("--json");return args}function buildSecQuarantineListArgv(options){let args=["--quarantine-list"];if(options.json)args.push("--json");return args}function buildSecQuarantineGcArgv(options){let args=["--quarantine-gc"];if(options.olderThan)args.push("--older-than",options.olderThan);if(options.confirmGc)args.push("--confirm-gc",options.confirmGc);if(options.json)args.push("--json");return args}function runSecRollback(scanId,options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecRollbackArgv(scanId,options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecQuarantineList(options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecQuarantineListArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function runSecQuarantineGc(options,deps=defaultDeps5){let args=[resolveSecRemediateScript(process.argv[1],deps),...buildSecQuarantineGcArgv(options)],result2=deps.spawnSync(process.execPath,args,{stdio:"inherit"});if(result2.error)throw result2.error;return result2.status??1}function applySecScanExitCode(exitCode,deps=defaultDeps5){if(exitCode!==0)deps.setExitCode(exitCode)}var VERIFY_EXIT={VERIFIED:0,SIGNATURE_INVALID:2,SIGNER_IDENTITY_MISMATCH:3,PROVENANCE_INVALID:4,NO_SIGNATURE_MATERIAL:5,MISSING_BINARY:127},SIGNER_IDENTITY_REGEXP="^https://github.com/automagik-dev/genie/.github/workflows/release.yml@",SIGNER_OIDC_ISSUER="https://token.actions.githubusercontent.com",PROVENANCE_SOURCE_URI="github.com/automagik-dev/genie",COSIGN_NO_KEY_SENTINEL="BEGIN COSIGN NO-PINNED-KEY SENTINEL";function discoverSignatureBundle(bundleDir,deps=defaultDeps5){if(!deps.existsSync(bundleDir))return null;let candidates=[];try{for(let entry2 of readdirSync11(bundleDir))if(entry2.endsWith(".tgz"))candidates.push(entry2)}catch{return null}for(let tarballName of candidates){let tarball=join65(bundleDir,tarballName),signature=`${tarball}.sig`,certificate=`${tarball}.cert`;if(!deps.existsSync(signature))continue;if(!deps.existsSync(certificate))continue;let provenancePath=join65(bundleDir,"provenance.intoto.jsonl"),provenance=deps.existsSync(provenancePath)?provenancePath:null;return{tarball,signature,certificate,provenance}}return null}function readsAsCosignSentinel(path3,deps=defaultDeps5){if(!deps.existsSync(path3))return!1;try{return deps.readFileSync(path3,"utf8").includes(COSIGN_NO_KEY_SENTINEL)}catch{return!1}}function ensureBinary(name,deps){let result2=deps.spawnSync(name,["--version"],{stdio:"pipe",encoding:"utf8"});if(result2.error)return{ok:!1,binary:name,reason:result2.error.message};if((result2.status??1)!==0)return{ok:!1,binary:name,reason:`${name} --version exited non-zero`};return{ok:!0,binary:name}}function buildVerifyResult(exitCode,ctx){return{exitCode,json:{verified:exitCode===VERIFY_EXIT.VERIFIED,exit_code:exitCode,signer_identity:SIGNER_IDENTITY_REGEXP,signer_oidc_issuer:SIGNER_OIDC_ISSUER,signature_source:ctx.bundle?.signature??null,provenance_source:ctx.bundle?.provenance??null,tarball_path:ctx.bundle?.tarball??null,verified_at:ctx.verifiedAt,pinned_key_fingerprint:null,signing_mode:"cosign-keyless",offline:ctx.offline,errors:ctx.errors}}}function classifyCosignFailure(stderr){let lower=stderr.toLowerCase();return lower.includes("certificate identity")||lower.includes("subject does not match")||lower.includes("oidc issuer")?VERIFY_EXIT.SIGNER_IDENTITY_MISMATCH:VERIFY_EXIT.SIGNATURE_INVALID}function runCosignStep(bundle,offline,errors3,deps){let cosignCheck=ensureBinary("cosign",deps);if(!cosignCheck.ok)return errors3.push(`cosign not available in PATH (${cosignCheck.reason??"unknown"}). Install from https://docs.sigstore.dev/cosign/installation/.`),VERIFY_EXIT.MISSING_BINARY;let cosignArgs=["verify-blob","--certificate-identity-regexp",SIGNER_IDENTITY_REGEXP,"--certificate-oidc-issuer",SIGNER_OIDC_ISSUER,"--signature",bundle.signature,"--certificate",bundle.certificate,bundle.tarball];if(offline)cosignArgs.push("--insecure-ignore-tlog","--offline");let result2=deps.spawnSync("cosign",cosignArgs,{stdio:"pipe",encoding:"utf8"});if(result2.error)return errors3.push(`cosign spawn failed: ${result2.error.message}`),VERIFY_EXIT.MISSING_BINARY;if((result2.status??1)===0)return VERIFY_EXIT.VERIFIED;let stderr=typeof result2.stderr==="string"?result2.stderr:"";if(stderr)errors3.push(stderr.trim());return classifyCosignFailure(stderr)}function runSlsaStep(bundle,errors3,deps){if(!bundle.provenance)return errors3.push(`provenance.intoto.jsonl missing alongside ${bundle.tarball} \u2014 cosign passed but SLSA provenance cannot be checked.`),VERIFY_EXIT.PROVENANCE_INVALID;let slsaCheck=ensureBinary("slsa-verifier",deps);if(!slsaCheck.ok)return errors3.push(`slsa-verifier not available in PATH (${slsaCheck.reason??"unknown"}). Install from https://github.com/slsa-framework/slsa-verifier.`),VERIFY_EXIT.MISSING_BINARY;let result2=deps.spawnSync("slsa-verifier",["verify-artifact",bundle.tarball,"--provenance-path",bundle.provenance,"--source-uri",PROVENANCE_SOURCE_URI],{stdio:"pipe",encoding:"utf8"});if(result2.error)return errors3.push(`slsa-verifier spawn failed: ${result2.error.message}`),VERIFY_EXIT.MISSING_BINARY;if((result2.status??1)===0)return VERIFY_EXIT.VERIFIED;let stderr=typeof result2.stderr==="string"?result2.stderr:"";if(stderr)errors3.push(stderr.trim());return VERIFY_EXIT.PROVENANCE_INVALID}function resolveBundleDir(options,genieRoot){if(options.bundleDir)return options.bundleDir;if(options.tarball)return dirname14(resolve12(options.tarball));return resolve12(genieRoot)}function runVerifyInstall(options,deps=defaultDeps5){let errors3=[],verifiedAt=deps.now().toISOString(),offline=options.offline===!0,genieRoot=resolveGenieRoot2(process.argv[1],deps),bundleDir=resolveBundleDir(options,genieRoot),bundle=discoverSignatureBundle(bundleDir,deps),ctx={bundle,verifiedAt,offline,errors:errors3};if(!bundle){if(errors3.push(`No signed release bundle found under ${bundleDir}. Expected <pkg>.tgz + .sig + .cert + provenance.intoto.jsonl.`),readsAsCosignSentinel(join65(genieRoot,".github","cosign.pub"),deps))errors3.push(".github/cosign.pub is the documented NO-KEY sentinel \u2014 release signing is cosign KEYLESS ONLY; there is no public key to pin.");return buildVerifyResult(VERIFY_EXIT.NO_SIGNATURE_MATERIAL,ctx)}let cosignExit=runCosignStep(bundle,offline,errors3,deps);if(cosignExit!==VERIFY_EXIT.VERIFIED)return buildVerifyResult(cosignExit,ctx);let slsaExit=runSlsaStep(bundle,errors3,deps);return buildVerifyResult(slsaExit,ctx)}function emitHumanReport(result2,options,deps){let{json:json2,exitCode}=result2,status=json2.verified?"OK":"FAIL";if(deps.stdout(`verify-install: ${status} (exit ${exitCode})`),deps.stdout(` signing mode: ${json2.signing_mode}`),deps.stdout(` signer identity: ${json2.signer_identity}`),deps.stdout(` OIDC issuer: ${json2.signer_oidc_issuer}`),deps.stdout(` provenance source: ${PROVENANCE_SOURCE_URI}`),deps.stdout(` tarball: ${json2.tarball_path??"(not found)"}`),deps.stdout(` signature: ${json2.signature_source??"(not found)"}`),deps.stdout(` provenance: ${json2.provenance_source??"(not found)"}`),deps.stdout(` verified_at: ${json2.verified_at}`),deps.stdout(` offline: ${json2.offline?"yes (skips Rekor tlog)":"no"}`),options.offline)deps.stdout(" warning: offline mode skips the Rekor transparency log; revoked certs are not detected.");if(json2.errors.length>0){deps.stderr("verify-install errors:");for(let err of json2.errors)deps.stderr(` - ${err}`)}}function runVerifyInstallCommand(options,deps=defaultDeps5){let result2=runVerifyInstall(options,deps);if(options.json)deps.stdout(JSON.stringify(result2.json));else emitHumanReport(result2,options,deps);return result2.exitCode}function registerSecCommands(program2,deps=defaultDeps5){let sec=program2.command("sec").description("Security tooling \u2014 host compromise triage and IOC hunts");sec.command("scan",{isDefault:!0}).description("Scan host for TeamPCP/CanisterWorm-style package compromise indicators (read-only)").option("--json","Emit machine-readable report on stdout (for archival, CI, or piping to jq)").option("--all-homes","Blast-radius flag \u2014 scan every /root, /home/*, /Users/*, and WSL Windows home found on the host. When to reach for this: incident-response on a multi-tenant box or CI runner where per-user material must all be assessed in one pass.").option("--home <path>","Add one extra home directory to scan. When to reach for this: the scanner did not auto-discover a non-standard home (e.g. /var/lib/service-user) but you know it holds at-risk material.",collectRepeatedOption,[]).option("--root <path>","Blast-radius flag \u2014 add an application root (repeatable) to scan for lockfiles, node_modules, and project evidence. When to reach for this: a multi-service host where each service lives under its own prefix (e.g. --root /srv/app --root /opt/worker).",collectRepeatedOption,[]).option("--no-progress","Suppress progress output on stderr").option("--quiet","Suppress progress and banners on stderr").option("--verbose","Emit extra diagnostics on stderr").option("--progress-json","Emit progress as NDJSON events to stderr").option("--progress-interval <ms>","Progress tick interval in milliseconds").option("--events-file <path>","Append structured NDJSON events to a 0600-mode file").option("--redact","Hash $HOME-prefixed paths; scrub AWS/GitHub/npm/JWT patterns").option("--no-persist","Do not persist the report to $GENIE_HOME/sec-scan/").option("--impact-surface","Scan for at-risk local material (secrets, wallets, browsers)").option("--phase-budget <name=ms>","Budget (ms) for a named phase (repeatable)",collectRepeatedOption,[]).addHelpText("after",`
|
|
4169
|
+
Examples:
|
|
4170
|
+
# Quick triage of the current project and every home on the host.
|
|
4171
|
+
$ genie sec scan --all-homes --root "$PWD"
|
|
4172
|
+
|
|
4173
|
+
# Full-host incident sweep \u2014 archive JSON for the audit trail.
|
|
4174
|
+
$ genie sec scan --all-homes --root / --json > /tmp/scan-$(date -u +%Y%m%dT%H%M%SZ).json
|
|
4175
|
+
|
|
4176
|
+
# Targeted multi-service scan without touching other homes.
|
|
4177
|
+
$ genie sec scan --root /srv/api --root /srv/worker --root /opt/vendor
|
|
4178
|
+
|
|
4179
|
+
The scanner is read-only by design \u2014 it never mutates the host. See:
|
|
4180
|
+
docs/incident-response/canisterworm.md for the LIKELY COMPROMISED / AFFECTED / OBSERVED decision tree.
|
|
4181
|
+
`.trimStart()).action((options)=>{let exitCode=runSecScan(options,deps);applySecScanExitCode(exitCode,deps)}),sec.command("remediate").description("Reversibly remediate findings from a sec scan (dry-run by default; only mutating verb)").option("--dry-run","Generate a plan manifest without mutating anything (default mode)").option("--apply","Blast-radius flag \u2014 execute a previously-generated plan manifest. When to reach for this: you have already reviewed $GENIE_HOME/sec-scan/<id>/plan.json and you are ready to quarantine, kill, or disable the listed entries. Requires --plan.").option("--resume <path>","Blast-radius flag \u2014 resume a previously-aborted apply from its resume file. When to reach for this: --apply was interrupted (SIGINT, power loss, OOM) and you need to finish the remaining actions without replaying completed ones.").option("--scan-report <path>","Path to a scan JSON report (use with --dry-run)").option("--scan-id <ulid>","ULID of a persisted scan in $GENIE_HOME/sec-scan/").option("--plan <path>","Blast-radius flag \u2014 path to a frozen plan manifest (required with --apply). When to reach for this: always, when applying. The plan is generated once, reviewed once, applied once \u2014 never hand --apply a plan that was not generated by a dry-run of the same scan id.").option("--quarantine-dir <path>","Override quarantine root (must be on same device as targets)").option("--unsafe-unverified <id>","Blast-radius flag \u2014 bypass the genie sec verify-install gate, logging the incident id + typed ack to $GENIE_SEC_AUDIT_LOG. When to reach for this: exactly the three legitimate contexts in docs/incident-response/canisterworm.md (burned signing identity, pre-signing release channel, integration test harness). Every other use is an audit finding.").option("--remediate-partial","Blast-radius flag \u2014 allow remediation against a coverage-capped scan (requires typed ack via --confirm-incomplete-scan). When to reach for this: the scan was scoped to a subset (e.g. a single --root) but you need to act on its findings now, accepting that unscanned areas may still carry evidence.").option("--confirm-incomplete-scan <ack>","Typed ack for --remediate-partial").option("--kill-pid <pid>","Blast-radius flag \u2014 authorize SIGTERM to a PID matching a plan entry (repeatable). When to reach for this: the scanner flagged a live process and the plan asks for explicit per-PID consent before killing it. List each PID the plan references; omit PIDs you do not want touched.",collectKillPid,[]).option("--auto-confirm-from <path>","Non-interactive consent map (testing only \u2014 not for production hosts)").option("--json","Emit JSON summary to stdout").addHelpText("after",`
|
|
4182
|
+
Examples:
|
|
4183
|
+
# Generate a dry-run plan from a persisted scan id and review it before applying.
|
|
4184
|
+
$ genie sec remediate --dry-run --scan-id 01HW3RKX...
|
|
4185
|
+
$ jq '.actions[] | {type, target, reason}' "$GENIE_HOME/sec-scan/01HW3RKX.../plan.json"
|
|
4186
|
+
|
|
4187
|
+
# Apply the reviewed plan. Typed per-action consent is required interactively.
|
|
4188
|
+
$ genie sec remediate --apply --plan "$GENIE_HOME/sec-scan/01HW3RKX.../plan.json"
|
|
4189
|
+
|
|
4190
|
+
# Apply with PID-kill authorization for two live processes the plan flagged.
|
|
4191
|
+
$ genie sec remediate --apply --plan ./plan.json --kill-pid 42 --kill-pid 1337
|
|
4192
|
+
|
|
4193
|
+
# Incident-only: burned signing identity \u2014 run against a known rotation id.
|
|
4194
|
+
$ genie sec remediate --apply --plan ./plan.json \\
|
|
4195
|
+
--unsafe-unverified "SIGNING_CERT_IDENTITY_20260423"
|
|
4196
|
+
# Typed ack prompt: I_ACKNOWLEDGE_UNSIGNED_GENIE_SIGNING_CERT_IDENTITY_20260423
|
|
4197
|
+
|
|
4198
|
+
Nothing is deleted without a recoverable copy under $GENIE_SEC_QUARANTINE_DIR. Undo with:
|
|
4199
|
+
genie sec restore <quarantine-id> # one item
|
|
4200
|
+
genie sec rollback <scan-id> # every action for a scan
|
|
4201
|
+
`.trimStart()).action((options)=>{let normalized={...options};if(!normalized.dryRun&&!normalized.apply&&!normalized.resume)normalized.dryRun=!0;let exitCode=runSecRemediate(normalized,deps);applySecScanExitCode(exitCode,deps)}),sec.command("restore <quarantine-id>").description("Restore every action under a quarantine id (sha256-verified per file)").addHelpText("after",`
|
|
4202
|
+
Examples:
|
|
4203
|
+
# List quarantines, then restore a specific one by id.
|
|
4204
|
+
$ genie sec quarantine list
|
|
4205
|
+
$ genie sec restore Q01HW3RKX...
|
|
4206
|
+
|
|
4207
|
+
When to reach for this: a single quarantined item needs to come back (a legitimate config
|
|
4208
|
+
file was swept up, a service needs its original artefact restored). For bulk undo across
|
|
4209
|
+
an entire remediation run, use 'genie sec rollback <scan-id>' instead.
|
|
4210
|
+
`.trimStart()).action((quarantineId)=>{let exitCode=runSecRestore(quarantineId,deps);applySecScanExitCode(exitCode,deps)}),sec.command("rollback <scan-id>").description("Bulk undo every quarantined action for a scan (walks audit log in reverse)").option("--json","Emit JSON summary to stdout").addHelpText("after",`
|
|
4211
|
+
Examples:
|
|
4212
|
+
# Undo an entire --apply that broke the host.
|
|
4213
|
+
$ genie sec rollback 01HW3RKX...
|
|
4214
|
+
|
|
4215
|
+
# Emit a JSON summary for the post-mortem.
|
|
4216
|
+
$ genie sec rollback 01HW3RKX... --json | jq '.restored, .failed'
|
|
4217
|
+
|
|
4218
|
+
When to reach for this: 'genie sec remediate --apply' completed but the host is now
|
|
4219
|
+
broken (a service fails to start, a dependency is gone). Rollback is safe \u2014 it only
|
|
4220
|
+
touches items the audit log recorded under this scan id.
|
|
4221
|
+
`.trimStart()).action((scanId,options)=>{let exitCode=runSecRollback(scanId,options,deps);applySecScanExitCode(exitCode,deps)});let quarantine=sec.command("quarantine").description("Quarantine lifecycle (list, gc)");quarantine.command("list").description("List quarantines with id, timestamp, size, status, scan_id").option("--json","Emit JSON rows to stdout").addHelpText("after",`
|
|
4222
|
+
Examples:
|
|
4223
|
+
# Human-readable inventory.
|
|
4224
|
+
$ genie sec quarantine list
|
|
4225
|
+
|
|
4226
|
+
# Pipe to jq for automation.
|
|
4227
|
+
$ genie sec quarantine list --json | jq '.[] | select(.status=="active") | .id'
|
|
4228
|
+
|
|
4229
|
+
When to reach for this: you need to locate the quarantine id of a specific item before
|
|
4230
|
+
running 'genie sec restore', or audit what is still under quarantine before running gc.
|
|
4231
|
+
`.trimStart()).action((options)=>{let exitCode=runSecQuarantineList(options,deps);applySecScanExitCode(exitCode,deps)}),quarantine.command("gc").description("Delete restored/abandoned quarantines older than <duration> (refuses active)").requiredOption("--older-than <duration>","Blast-radius flag \u2014 duration threshold, e.g. 30d, 24h, 15m. Anything younger is refused. When to reach for this: set a conservative threshold (\u226530d) for routine cleanup; older if your incident review process takes longer.").option("--confirm-gc <token>","Typed ack of the form CONFIRM-GC-<6-hex>. The command prints the expected token when it refuses; echo it back verbatim.").option("--json","Emit JSON summary to stdout").addHelpText("after",`
|
|
4232
|
+
Examples:
|
|
4233
|
+
# Routine cleanup \u2014 safe default threshold.
|
|
4234
|
+
$ genie sec quarantine gc --older-than 30d
|
|
4235
|
+
# First run prints: refused \u2014 re-run with --confirm-gc CONFIRM-GC-a1b2c3
|
|
4236
|
+
$ genie sec quarantine gc --older-than 30d --confirm-gc CONFIRM-GC-a1b2c3
|
|
4237
|
+
|
|
4238
|
+
Active quarantines (referenced by an unfinished remediation) are always refused \u2014 gc
|
|
4239
|
+
only prunes restored or abandoned quarantines.
|
|
4240
|
+
`.trimStart()).action((options)=>{let exitCode=runSecQuarantineGc(options,deps);applySecScanExitCode(exitCode,deps)}),sec.command("verify-install").description("Verify the cosign signature + SLSA provenance of the running @automagik/genie release.").option("--offline","Blast-radius flag \u2014 skip the Rekor transparency-log check (signature + cert still verified). When to reach for this: an air-gapped or locked-down incident-response host with no outbound network. Revoked certs will not be detected; never use on a production gate.").option("--json","Emit machine-readable verification report on stdout").option("--tarball <path>","Point at a specific release tarball (for local verification). When to reach for this: you downloaded the release assets manually and want to verify them before unpacking.").option("--bundle-dir <path>","Directory containing <pkg>.tgz + .sig + .cert + provenance. When to reach for this: the bundle lives somewhere other than the genie repo root (e.g. a CI artifact directory).").addHelpText("after",`
|
|
4241
|
+
Examples:
|
|
4242
|
+
# Verify the currently installed binary.
|
|
4243
|
+
$ genie sec verify-install
|
|
4244
|
+
|
|
4245
|
+
# Verify a downloaded tarball against the pinned identity.
|
|
4246
|
+
$ genie sec verify-install --tarball ./automagik-genie-4.260422.4.tgz
|
|
4247
|
+
|
|
4248
|
+
# Air-gapped host: skip Rekor transparency log (signature + cert still checked).
|
|
4249
|
+
$ genie sec verify-install --offline --bundle-dir ./release-assets
|
|
4250
|
+
|
|
4251
|
+
Exit codes:
|
|
4252
|
+
0 \u2014 verified (signature + provenance both pass)
|
|
4253
|
+
2 \u2014 cosign signature verification failed
|
|
4254
|
+
3 \u2014 signer identity does not match the pinned regex (SECURITY.md \xA7 Release Signing)
|
|
4255
|
+
4 \u2014 SLSA provenance verification failed
|
|
4256
|
+
5 \u2014 no signature material found (expected .sig + .cert + provenance.intoto.jsonl)
|
|
4257
|
+
127 \u2014 cosign or slsa-verifier not installed (see docs.sigstore.dev)
|
|
4258
|
+
|
|
4259
|
+
Must return exit 0 before 'genie sec remediate --apply' will proceed. See
|
|
4260
|
+
docs/incident-response/canisterworm.md for the legitimate --unsafe-unverified contexts.
|
|
4261
|
+
`.trimStart()).action((options)=>{let exitCode=runVerifyInstallCommand(options,deps);applySecScanExitCode(exitCode,deps)})}init_serve();init_db();init_session_backfill();init_term_format();function resolveAgentLabel(r){if(r.agent_name)return r.agent_name;if(r.executor_id)return r.executor_id.slice(0,12);if(r.agent_id)return r.agent_id;return"(orphaned)"}async function sessionsListCommand(options){if(!await isAvailable())console.error("Database not available."),process.exit(1);let sql=await getConnection(),limit=Number(options.limit)||50,sourceFilter=options.source?sql`AND e.metadata->>'source' = ${options.source}`:sql``,rows;if(options.active)rows=await sql`
|
|
4169
4262
|
SELECT s.*, a.custom_name as agent_name
|
|
4170
4263
|
FROM sessions s
|
|
4171
4264
|
LEFT JOIN executors e ON s.executor_id = e.id
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genie",
|
|
3
|
-
"version": "4.260424.
|
|
3
|
+
"version": "4.260424.4",
|
|
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"
|