@bridge_gpt/mcp-server 0.2.28 → 0.2.29
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 +11 -4
- package/build/agent-registry.js +5 -0
- package/build/conductor-bin.js +1 -1
- package/build/index.js +13 -12
- package/build/install-bridge.js +165 -32
- package/build/readme.generated.js +1 -1
- package/build/start-tickets.js +19 -9
- package/build/version.generated.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,11 +31,14 @@ npx -y @bridge_gpt/mcp-server@latest install-bridge
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Run bare like that in a terminal and it starts by asking
|
|
34
|
-
**`Do you have a Bridge API key? [Y/n]`**:
|
|
34
|
+
**`Do you have a Bridge API key or invite? [Y/n]`**:
|
|
35
35
|
|
|
36
36
|
- **Yes** (or just press Enter) — the existing-key flow. It asks for your **API key**
|
|
37
37
|
(generate one on the Bridge API web UI **Security** page) and a **repo name**
|
|
38
|
-
matching your server-side registration; everything else is derived.
|
|
38
|
+
matching your server-side registration; everything else is derived. A
|
|
39
|
+
`bapi_inv_…` credential entered here instead of a full API key is automatically
|
|
40
|
+
detected and redeemed as a **bootstrap invite** — it creates a brand-new project
|
|
41
|
+
and mints your admin API key rather than looking up an existing repository.
|
|
39
42
|
- **No** — the **self-serve** flow. It asks for an **email**, then a name for your new
|
|
40
43
|
Bridge project, and creates the workspace and your own admin API key for you. No
|
|
41
44
|
account, no key, and no invite needed beforehand. Same as passing
|
|
@@ -112,7 +115,11 @@ In this **existing-key** flow the only inputs are an **API key** and a **repo na
|
|
|
112
115
|
prompt. Generate one first on the Bridge API web UI **Security** page (see
|
|
113
116
|
[Generate an API Key](#2-generate-an-api-key)); in this flow the command consumes
|
|
114
117
|
a key, it never mints one — **`--email` and `--invite` are the two exceptions**
|
|
115
|
-
(below), and each mints your first key.
|
|
118
|
+
(below), and each mints your first key. All three of `--api-key`, `BAPI_API_KEY`,
|
|
119
|
+
and the hidden prompt also accept a bootstrap-invite value (`bapi_inv_…`) —
|
|
120
|
+
detected automatically and redeemed the same way `--invite` is, skipping
|
|
121
|
+
repository lookup entirely. `--invite` and `--email` remain the preferred,
|
|
122
|
+
explicit entry points for a new project. The key is **never printed or logged**.
|
|
116
123
|
- **Repo name:** `--repo <name>` and `BAPI_REPO_NAME` remain the deterministic
|
|
117
124
|
short-circuits and compatibility fallbacks — when either is set it is used
|
|
118
125
|
directly, with no network round-trip. When **neither** is set, a compatible
|
|
@@ -141,7 +148,7 @@ shown**.
|
|
|
141
148
|
The email may instead come from the `BAPI_SIGNUP_EMAIL` environment variable or a
|
|
142
149
|
**visible** interactive prompt (email is not a secret, so it is echoed as you type —
|
|
143
150
|
unlike the API key and the invite token, which use a hidden prompt). That prompt is
|
|
144
|
-
what answering **no** to `Do you have a Bridge API key? [Y/n]` on a bare run reaches,
|
|
151
|
+
what answering **no** to `Do you have a Bridge API key or invite? [Y/n]` on a bare run reaches,
|
|
145
152
|
so `install-bridge --email you@example.com` and a bare `install-bridge` + "no" land
|
|
146
153
|
in the same place. The email is still **never written to a log line**. No email
|
|
147
154
|
verification is performed and no message is sent to the address — it only labels your
|
package/build/agent-registry.js
CHANGED
|
@@ -76,6 +76,11 @@ export const AGENT_REGISTRY = {
|
|
|
76
76
|
basic: "claude-4.6-sonnet-medium",
|
|
77
77
|
premium: "claude-opus-4-8-thinking-high",
|
|
78
78
|
},
|
|
79
|
+
// BAPI-662: cursor-agent's interactive TUI blocks on a workspace-trust
|
|
80
|
+
// prompt the spawned tab/session can't answer, hanging the launch. Interactive
|
|
81
|
+
// builders already `cd`/`Set-Location` into the target worktree before
|
|
82
|
+
// launching, so `--workspace` (headless-only) is not needed here.
|
|
83
|
+
interactiveLaunchArgs: ["--trust"],
|
|
79
84
|
},
|
|
80
85
|
};
|
|
81
86
|
/** The default agent used when `--agent` is omitted. */
|
package/build/conductor-bin.js
CHANGED
|
@@ -123,7 +123,7 @@ ${stderr}`.matchAll(/^job\s+(\d+)\s+at\b/gim)];return matches.length>0?matches[m
|
|
|
123
123
|
`);lines.push(["ID","COMMAND","RUN_AT","BACKEND","AGENT","NATIVE","LATEST","UNIT_PATH"].join(" "));for(let e of report.entries)lines.push([e.metadata.id,scheduleCommandLabel(e.metadata),e.metadata.run_at_iso,e.metadata.backend,e.metadata.agent,e.status,latestRunStatus(e.metadata)||"-",e.metadata.unit_path??"-"].join(" "));return lines.join(`
|
|
124
124
|
`)}async function orchestrateScheduleCancel(options,deps){let metadata=await readScheduleMetadata(options.id,deps.homeDir,deps.platform);if(!metadata)return{ok:!1,notFound:!0,error:`No schedule found with id '${options.id}'.`};if(options.agent!==void 0&&metadata.agent!==options.agent)return{ok:!1,notFound:!0,error:`Schedule '${options.id}' does not match agent filter '${options.agent}'.`};if(options.backend!==void 0&&metadata.backend!==options.backend)return{ok:!1,notFound:!0,error:`Schedule '${options.id}' does not match backend filter '${options.backend}'.`};let backend=getSchedulerBackendByName(metadata.backend);if(!backend)return{ok:!1,error:`Unknown backend '${metadata.backend}' recorded for '${options.id}'.`};let cancelResult=await backend.cancel({deps,metadata});if(!cancelResult.ok)return{ok:!1,error:cancelResult.error??"Backend cancel failed."};let canceledAtIso=new Date(deps.now?deps.now():Date.now()).toISOString();return await appendScheduleRunEvent(options.id,{status:"canceled",at:canceledAtIso},deps.homeDir,deps.platform).catch(()=>{}),await deleteScheduleMetadata(options.id,deps.homeDir,deps.platform),{ok:!0,id:options.id,backend:metadata.backend,nativeRemoved:cancelResult.nativeRemoved,stale:cancelResult.stale,metadataRemoved:!0}}function formatScheduleCancelResult(result){return result.ok?[`Schedule '${result.id}' canceled.`,` backend: ${result.backend}`,` native removed: ${result.nativeRemoved?"yes":`no${result.stale?" (stale)":""}`}`,` metadata removed: ${result.metadataRemoved?"yes":"no"}`," logs: preserved"].join(`
|
|
125
125
|
`):`Error: ${result.error}`}async function orchestrateScheduleDoctor(deps){let platformResult=getSchedulerBackendsForPlatform(deps.platform),envPath=deps.env.PATH??deps.env.Path??"",claudeResolved=!!await resolveCommandOnPath("claude",envPath,deps),cursorResolved=!!await resolveCommandOnPath("cursor-agent",envPath,deps),npxResolved=!!await resolveCommandOnPath("npx",envPath,deps),cursorApiKeyPresent=!!deps.env.CURSOR_API_KEY,bridgeCredentialResolved=deps.bridgeCredentialResolved?.()??!!deps.env.BAPI_API_KEY;if(!platformResult.ok)return{platform:deps.platform,platformSupported:!1,candidateBackends:[],backendAvailability:[],claudeResolved,cursorResolved,npxResolved,cursorApiKeyPresent,bridgeCredentialResolved,unsupportedMessage:platformResult.error};let candidateBackends=platformResult.backends.map(b=>b.name),backendAvailability=[];for(let backend of platformResult.backends)backendAvailability.push({backend:backend.name,available:await backend.isAvailable(deps)});return{platform:deps.platform,platformSupported:!0,candidateBackends,backendAvailability,claudeResolved,cursorResolved,npxResolved,cursorApiKeyPresent,bridgeCredentialResolved}}function formatScheduleDoctorReport(report,json){if(json)return JSON.stringify(report,null,2);let lines=["schedule-run doctor (read-only diagnostics)",`Platform: ${report.platform}`];if(!report.platformSupported)lines.push(report.unsupportedMessage??unsupportedSchedulerPlatformMessage(report.platform));else{lines.push(`Candidate backends (in order): ${report.candidateBackends.join(", ")}`);for(let a of report.backendAvailability)lines.push(` ${a.available?"AVAILABLE ":"UNAVAILABLE"} ${a.backend}`)}return lines.push(`claude on PATH: ${report.claudeResolved?"yes":"no"}`),lines.push(`cursor-agent on PATH: ${report.cursorResolved?"yes":"no"}`),lines.push(`npx on PATH: ${report.npxResolved?"yes":"no"}`),lines.push(`CURSOR_API_KEY set: ${report.cursorApiKeyPresent?"yes":"no"}`),lines.push(`Bridge credential: ${report.bridgeCredentialResolved?"resolved":"not resolved"}`),lines.join(`
|
|
126
|
-
`)}function nowIso(deps){return new Date(deps.now?deps.now():Date.now()).toISOString()}async function orchestrateScheduleExecute(options,deps,io){let metadata=await readScheduleMetadata(options.id,deps.homeDir,deps.platform);if(!metadata)return{ok:!1,exitCode:1,error:`No schedule found with id '${options.id}'.`};let agentInvocation=metadata.agent_invocation??metadata.invocation;if(!agentInvocation||!agentInvocation.exe)return await appendScheduleRunEvent(options.id,{status:"failed",at:nowIso(deps),message:"missing agent_invocation"},deps.homeDir,deps.platform).catch(()=>{}),{ok:!1,exitCode:1,error:`Schedule '${options.id}' has no agent invocation to run.`};await appendScheduleRunEvent(options.id,{status:"started",at:nowIso(deps)},deps.homeDir,deps.platform).catch(()=>{});let env={...deps.env};metadata.env_path&&(env.PATH=metadata.env_path,deps.platform==="win32"&&(env.Path=metadata.env_path)),env.BRIDGE_GPT_SCHEDULE_ID=metadata.id,metadata.command&&(env.BRIDGE_GPT_COMMAND=metadata.command),metadata.args&&(env.BRIDGE_GPT_COMMAND_ARGS_JSON=JSON.stringify(metadata.args)),metadata.repo_path&&(env.BRIDGE_GPT_REPO_PATH=metadata.repo_path),metadata.agent&&(env.BRIDGE_GPT_AGENT=metadata.agent),metadata.agent_path&&(env.BRIDGE_GPT_AGENT_PATH=metadata.agent_path),metadata.idea_file&&(env.BRIDGE_GPT_IDEA_FILE=metadata.idea_file);let result;try{result=await deps.runCommand(agentInvocation.exe,agentInvocation.args,{cwd:metadata.repo_path,env})}catch(error){let msg=error instanceof Error?error.message:String(error);return await appendScheduleRunEvent(options.id,{status:"failed",at:nowIso(deps),message:`agent launch failed: ${msg}`},deps.homeDir,deps.platform).catch(()=>{}),{ok:!1,exitCode:1,error:`Failed to launch agent: ${msg}`}}return result.stdout&&io.writeStdout(result.stdout),result.stderr&&io.writeStderr(result.stderr),result.exitCode===0?(await appendScheduleRunEvent(options.id,{status:"completed",at:nowIso(deps),exit_code:0},deps.homeDir,deps.platform).catch(()=>{}),{ok:!0,exitCode:0}):(await appendScheduleRunEvent(options.id,{status:"failed",at:nowIso(deps),exit_code:result.exitCode},deps.homeDir,deps.platform).catch(()=>{}),{ok:!1,exitCode:result.exitCode})}async function runScheduleRunCli(argv,overrides={}){let log=overrides.log??(m=>console.log(m)),errorLog=overrides.errorLog??(m=>console.error(m)),parsed=parseScheduleRunArgs(argv);if(parsed.status==="help")return log(parsed.usage),0;if(parsed.status==="error")return errorLog(`Error: ${parsed.message}`),errorLog(""),errorLog(getScheduleRunUsage()),1;let deps=overrides.deps??createDefaultScheduleRunDeps();try{switch(parsed.subcommand){case"create":{let result=await orchestrateScheduleCreate(parsed.options,deps);return result.ok?(log(formatScheduleCreateResult(result)),0):(errorLog(formatScheduleCreateResult(result)),1)}case"list":{let report=await orchestrateScheduleList(parsed.options,deps);return log(formatScheduleListResult(report,parsed.options.json)),0}case"cancel":{let result=await orchestrateScheduleCancel(parsed.options,deps);return result.ok?(log(formatScheduleCancelResult(result)),0):(errorLog(formatScheduleCancelResult(result)),1)}case"doctor":{let report=await orchestrateScheduleDoctor(deps);return log(formatScheduleDoctorReport(report,parsed.options.json)),report.platformSupported?0:1}case"_execute":{let io={writeStdout:overrides.writeStdout??(chunk=>process.stdout.write(chunk)),writeStderr:overrides.writeStderr??(chunk=>process.stderr.write(chunk))},result=await orchestrateScheduleExecute(parsed.options,deps,io);return!result.ok&&result.error&&errorLog(`Error: ${result.error}`),result.exitCode}}}catch(error){let detail=error instanceof Error?error.message:String(error);return errorLog(`Internal error: ${detail}`),errorLog("Error: schedule-run failed unexpectedly. See the message above for local diagnostics."),1}return 1}var VALID_BACKEND_NAMES,SCHEDULE_ID_PATTERN,init_schedule_run=__esm({"src/schedule-run.ts"(){"use strict";init_scheduler_backends();init_schedule_store();init_agent_launchers();init_claude();init_command_catalog();init_scheduled_prompt();VALID_BACKEND_NAMES=["launchd","task-scheduler","systemd-user","at-fallback"],SCHEDULE_ID_PATTERN=/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/}});function listAgentNames(){return Object.keys(AGENT_REGISTRY)}function isAgentName(value){return listAgentNames().includes(value)}function resolveAgentSpec(name){let resolved=name??DEFAULT_AGENT_NAME;return isAgentName(resolved)?AGENT_REGISTRY[resolved]:null}var AGENT_REGISTRY,DEFAULT_AGENT_NAME,init_agent_registry=__esm({"src/agent-registry.ts"(){"use strict";AGENT_REGISTRY={claude:{name:"claude",command:"claude",promptArgStyle:"positional",installHint:{darwin:"npm install -g @anthropic-ai/claude-code",linux:"npm install -g @anthropic-ai/claude-code",win32:"npm install -g @anthropic-ai/claude-code"},authNote:"Claude Code authenticates interactively on first run \u2014 follow its login/auth prompt if asked.",supportsModelOverride:!0,modelFlag:"--model",tierModels:{cheap:"haiku",basic:"sonnet",premium:"opus"},staticModelAliasAllowlist:["haiku","sonnet","opus"]},"cursor-agent":{name:"cursor-agent",command:"cursor-agent",promptArgStyle:"positional",installHint:{darwin:"curl https://cursor.com/install -fsSL | bash",linux:"curl https://cursor.com/install -fsSL | bash",win32:"irm 'https://cursor.com/install?win32=true' | iex"},authNote:"Run cursor-agent login to authenticate; doctor checks PATH presence only, not login state.",supportsModelOverride:!0,modelFlag:"--model",tierModels:{cheap:"auto",basic:"claude-4.6-sonnet-medium",premium:"claude-opus-4-8-thinking-high"}}},DEFAULT_AGENT_NAME="claude"}});var DEFAULT_PROBE_TIMEOUT_MS,init_types2=__esm({"src/agent-capabilities/types.ts"(){"use strict";DEFAULT_PROBE_TIMEOUT_MS=9e4}});import{join}from"node:path";function buildHeadlessArgs(agentName,opts){let fmt=opts.outputFormat??"text";if(agentName==="cursor-agent")return["-p","--output-format",fmt,"--trust","--workspace",opts.cwd,opts.prompt];let args=["-p"];return opts.skipPermissions===!0&&args.push("--dangerously-skip-permissions"),typeof opts.model=="string"&&opts.model.trim().length>0&&args.push("--model",opts.model),fmt==="json"?args.push("--output-format","json"):fmt==="stream-json"&&args.push("--output-format","stream-json","--verbose"),args.push(opts.prompt),args}async function createProbeContext(deps,agent,defaultTimeoutMs=DEFAULT_PROBE_TIMEOUT_MS){let launcherDeps={platform:deps.platform,env:deps.env,runCommand:(file,args,options)=>deps.runCommand(file,args,options)},resolvedBinary=await resolveCommandOnPath(agent.command,deps.env.PATH??"",launcherDeps),createdDirs=[],counter=0;return{ctx:{agent,deps,resolvedBinary,marker(name){return`${name}_${deps.uniqueSuffix()}_${counter++}`},async makeTempProject(seed){let dir=await deps.mkdtemp(join(deps.tmpRoot,"agent-cap-"));return createdDirs.push(dir),seed&&await seed(dir),dir},async runHeadless(opts){let exe=resolvedBinary??agent.command,args=buildHeadlessArgs(agent.name,opts),timeoutMs=opts.timeoutMs??defaultTimeoutMs,controller=new AbortController,timedOut=!1,timer=setTimeout(()=>{timedOut=!0,controller.abort()},timeoutMs),start=deps.now();try{let result=await deps.runCommand(exe,args,{cwd:opts.cwd,env:deps.env,signal:controller.signal}),elapsedMs=deps.now()-start;return timedOut?{kind:"hang",elapsedMs,partialStdout:result.stdout??""}:{kind:"exited",exitCode:result.exitCode,stdout:result.stdout??"",stderr:result.stderr??"",elapsedMs}}catch(err){let elapsedMs=deps.now()-start;return timedOut?{kind:"hang",elapsedMs,partialStdout:""}:{kind:"spawn-error",message:err instanceof Error?err.message:String(err)}}finally{clearTimeout(timer)}}},cleanup:async()=>{for(let dir of createdDirs.splice(0))try{await deps.rm(dir,{recursive:!0,force:!0})}catch{}}}}var init_probe_context=__esm({"src/agent-capabilities/probe-context.ts"(){"use strict";init_claude();init_types2()}});import{execFile as execFile2}from"node:child_process";import{mkdtemp,rm,writeFile,mkdir}from"node:fs/promises";import os2 from"node:os";import{randomBytes}from"node:crypto";function createDefaultAgentCapabilitiesDeps(){let runCommand=(file,args,options)=>new Promise(resolve2=>{execFile2(file,args,{cwd:options?.cwd,env:options?.env??process.env,signal:options?.signal,killSignal:"SIGKILL",maxBuffer:67108864,encoding:"utf-8"},(error,stdout,stderr)=>{let exitCode=error&&typeof error.code=="number"?error.code:error?1:0;resolve2({stdout:stdout??"",stderr:stderr??"",exitCode})})});return{platform:process.platform,env:process.env,runCommand,tmpRoot:os2.tmpdir(),mkdtemp:prefix=>mkdtemp(prefix),rm:(target,opts)=>rm(target,opts),writeFile:(target,data)=>writeFile(target,data,"utf-8"),mkdir:(target,opts)=>mkdir(target,opts).then(()=>{}),now:()=>Date.now(),uniqueSuffix:()=>randomBytes(3).toString("hex").toUpperCase()}}var init_default_deps=__esm({"src/agent-capabilities/default-deps.ts"(){"use strict"}});import{join as join2}from"node:path";function truncate(text){let flat=text.replace(/\s+/g," ").trim();return flat.length>EVIDENCE_MAX?`${flat.slice(0,EVIDENCE_MAX)}\u2026`:flat}function nonExitedResult(run){return run.kind==="hang"?{status:"hang",detail:`agent did not exit within the timeout (${run.elapsedMs}ms) \u2014 likely the version-sensitive -p hang`,elapsedMs:run.elapsedMs,evidence:run.partialStdout?truncate(run.partialStdout):void 0}:run.kind==="spawn-error"?{status:"fail",detail:`could not spawn agent: ${run.message}`}:null}function denyHookCommand(){return`printf '%s' '${JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"agent-capability deny-enforcement probe fallback: tool call denied."}})}'`}async function seedDenyTargetFile(ctx,dir,marker){await ctx.deps.writeFile(join2(dir,DENY_TARGET_FILE),`${marker}
|
|
126
|
+
`)}function nowIso(deps){return new Date(deps.now?deps.now():Date.now()).toISOString()}async function orchestrateScheduleExecute(options,deps,io){let metadata=await readScheduleMetadata(options.id,deps.homeDir,deps.platform);if(!metadata)return{ok:!1,exitCode:1,error:`No schedule found with id '${options.id}'.`};let agentInvocation=metadata.agent_invocation??metadata.invocation;if(!agentInvocation||!agentInvocation.exe)return await appendScheduleRunEvent(options.id,{status:"failed",at:nowIso(deps),message:"missing agent_invocation"},deps.homeDir,deps.platform).catch(()=>{}),{ok:!1,exitCode:1,error:`Schedule '${options.id}' has no agent invocation to run.`};await appendScheduleRunEvent(options.id,{status:"started",at:nowIso(deps)},deps.homeDir,deps.platform).catch(()=>{});let env={...deps.env};metadata.env_path&&(env.PATH=metadata.env_path,deps.platform==="win32"&&(env.Path=metadata.env_path)),env.BRIDGE_GPT_SCHEDULE_ID=metadata.id,metadata.command&&(env.BRIDGE_GPT_COMMAND=metadata.command),metadata.args&&(env.BRIDGE_GPT_COMMAND_ARGS_JSON=JSON.stringify(metadata.args)),metadata.repo_path&&(env.BRIDGE_GPT_REPO_PATH=metadata.repo_path),metadata.agent&&(env.BRIDGE_GPT_AGENT=metadata.agent),metadata.agent_path&&(env.BRIDGE_GPT_AGENT_PATH=metadata.agent_path),metadata.idea_file&&(env.BRIDGE_GPT_IDEA_FILE=metadata.idea_file);let result;try{result=await deps.runCommand(agentInvocation.exe,agentInvocation.args,{cwd:metadata.repo_path,env})}catch(error){let msg=error instanceof Error?error.message:String(error);return await appendScheduleRunEvent(options.id,{status:"failed",at:nowIso(deps),message:`agent launch failed: ${msg}`},deps.homeDir,deps.platform).catch(()=>{}),{ok:!1,exitCode:1,error:`Failed to launch agent: ${msg}`}}return result.stdout&&io.writeStdout(result.stdout),result.stderr&&io.writeStderr(result.stderr),result.exitCode===0?(await appendScheduleRunEvent(options.id,{status:"completed",at:nowIso(deps),exit_code:0},deps.homeDir,deps.platform).catch(()=>{}),{ok:!0,exitCode:0}):(await appendScheduleRunEvent(options.id,{status:"failed",at:nowIso(deps),exit_code:result.exitCode},deps.homeDir,deps.platform).catch(()=>{}),{ok:!1,exitCode:result.exitCode})}async function runScheduleRunCli(argv,overrides={}){let log=overrides.log??(m=>console.log(m)),errorLog=overrides.errorLog??(m=>console.error(m)),parsed=parseScheduleRunArgs(argv);if(parsed.status==="help")return log(parsed.usage),0;if(parsed.status==="error")return errorLog(`Error: ${parsed.message}`),errorLog(""),errorLog(getScheduleRunUsage()),1;let deps=overrides.deps??createDefaultScheduleRunDeps();try{switch(parsed.subcommand){case"create":{let result=await orchestrateScheduleCreate(parsed.options,deps);return result.ok?(log(formatScheduleCreateResult(result)),0):(errorLog(formatScheduleCreateResult(result)),1)}case"list":{let report=await orchestrateScheduleList(parsed.options,deps);return log(formatScheduleListResult(report,parsed.options.json)),0}case"cancel":{let result=await orchestrateScheduleCancel(parsed.options,deps);return result.ok?(log(formatScheduleCancelResult(result)),0):(errorLog(formatScheduleCancelResult(result)),1)}case"doctor":{let report=await orchestrateScheduleDoctor(deps);return log(formatScheduleDoctorReport(report,parsed.options.json)),report.platformSupported?0:1}case"_execute":{let io={writeStdout:overrides.writeStdout??(chunk=>process.stdout.write(chunk)),writeStderr:overrides.writeStderr??(chunk=>process.stderr.write(chunk))},result=await orchestrateScheduleExecute(parsed.options,deps,io);return!result.ok&&result.error&&errorLog(`Error: ${result.error}`),result.exitCode}}}catch(error){let detail=error instanceof Error?error.message:String(error);return errorLog(`Internal error: ${detail}`),errorLog("Error: schedule-run failed unexpectedly. See the message above for local diagnostics."),1}return 1}var VALID_BACKEND_NAMES,SCHEDULE_ID_PATTERN,init_schedule_run=__esm({"src/schedule-run.ts"(){"use strict";init_scheduler_backends();init_schedule_store();init_agent_launchers();init_claude();init_command_catalog();init_scheduled_prompt();VALID_BACKEND_NAMES=["launchd","task-scheduler","systemd-user","at-fallback"],SCHEDULE_ID_PATTERN=/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/}});function listAgentNames(){return Object.keys(AGENT_REGISTRY)}function isAgentName(value){return listAgentNames().includes(value)}function resolveAgentSpec(name){let resolved=name??DEFAULT_AGENT_NAME;return isAgentName(resolved)?AGENT_REGISTRY[resolved]:null}var AGENT_REGISTRY,DEFAULT_AGENT_NAME,init_agent_registry=__esm({"src/agent-registry.ts"(){"use strict";AGENT_REGISTRY={claude:{name:"claude",command:"claude",promptArgStyle:"positional",installHint:{darwin:"npm install -g @anthropic-ai/claude-code",linux:"npm install -g @anthropic-ai/claude-code",win32:"npm install -g @anthropic-ai/claude-code"},authNote:"Claude Code authenticates interactively on first run \u2014 follow its login/auth prompt if asked.",supportsModelOverride:!0,modelFlag:"--model",tierModels:{cheap:"haiku",basic:"sonnet",premium:"opus"},staticModelAliasAllowlist:["haiku","sonnet","opus"]},"cursor-agent":{name:"cursor-agent",command:"cursor-agent",promptArgStyle:"positional",installHint:{darwin:"curl https://cursor.com/install -fsSL | bash",linux:"curl https://cursor.com/install -fsSL | bash",win32:"irm 'https://cursor.com/install?win32=true' | iex"},authNote:"Run cursor-agent login to authenticate; doctor checks PATH presence only, not login state.",supportsModelOverride:!0,modelFlag:"--model",tierModels:{cheap:"auto",basic:"claude-4.6-sonnet-medium",premium:"claude-opus-4-8-thinking-high"},interactiveLaunchArgs:["--trust"]}},DEFAULT_AGENT_NAME="claude"}});var DEFAULT_PROBE_TIMEOUT_MS,init_types2=__esm({"src/agent-capabilities/types.ts"(){"use strict";DEFAULT_PROBE_TIMEOUT_MS=9e4}});import{join}from"node:path";function buildHeadlessArgs(agentName,opts){let fmt=opts.outputFormat??"text";if(agentName==="cursor-agent")return["-p","--output-format",fmt,"--trust","--workspace",opts.cwd,opts.prompt];let args=["-p"];return opts.skipPermissions===!0&&args.push("--dangerously-skip-permissions"),typeof opts.model=="string"&&opts.model.trim().length>0&&args.push("--model",opts.model),fmt==="json"?args.push("--output-format","json"):fmt==="stream-json"&&args.push("--output-format","stream-json","--verbose"),args.push(opts.prompt),args}async function createProbeContext(deps,agent,defaultTimeoutMs=DEFAULT_PROBE_TIMEOUT_MS){let launcherDeps={platform:deps.platform,env:deps.env,runCommand:(file,args,options)=>deps.runCommand(file,args,options)},resolvedBinary=await resolveCommandOnPath(agent.command,deps.env.PATH??"",launcherDeps),createdDirs=[],counter=0;return{ctx:{agent,deps,resolvedBinary,marker(name){return`${name}_${deps.uniqueSuffix()}_${counter++}`},async makeTempProject(seed){let dir=await deps.mkdtemp(join(deps.tmpRoot,"agent-cap-"));return createdDirs.push(dir),seed&&await seed(dir),dir},async runHeadless(opts){let exe=resolvedBinary??agent.command,args=buildHeadlessArgs(agent.name,opts),timeoutMs=opts.timeoutMs??defaultTimeoutMs,controller=new AbortController,timedOut=!1,timer=setTimeout(()=>{timedOut=!0,controller.abort()},timeoutMs),start=deps.now();try{let result=await deps.runCommand(exe,args,{cwd:opts.cwd,env:deps.env,signal:controller.signal}),elapsedMs=deps.now()-start;return timedOut?{kind:"hang",elapsedMs,partialStdout:result.stdout??""}:{kind:"exited",exitCode:result.exitCode,stdout:result.stdout??"",stderr:result.stderr??"",elapsedMs}}catch(err){let elapsedMs=deps.now()-start;return timedOut?{kind:"hang",elapsedMs,partialStdout:""}:{kind:"spawn-error",message:err instanceof Error?err.message:String(err)}}finally{clearTimeout(timer)}}},cleanup:async()=>{for(let dir of createdDirs.splice(0))try{await deps.rm(dir,{recursive:!0,force:!0})}catch{}}}}var init_probe_context=__esm({"src/agent-capabilities/probe-context.ts"(){"use strict";init_claude();init_types2()}});import{execFile as execFile2}from"node:child_process";import{mkdtemp,rm,writeFile,mkdir}from"node:fs/promises";import os2 from"node:os";import{randomBytes}from"node:crypto";function createDefaultAgentCapabilitiesDeps(){let runCommand=(file,args,options)=>new Promise(resolve2=>{execFile2(file,args,{cwd:options?.cwd,env:options?.env??process.env,signal:options?.signal,killSignal:"SIGKILL",maxBuffer:67108864,encoding:"utf-8"},(error,stdout,stderr)=>{let exitCode=error&&typeof error.code=="number"?error.code:error?1:0;resolve2({stdout:stdout??"",stderr:stderr??"",exitCode})})});return{platform:process.platform,env:process.env,runCommand,tmpRoot:os2.tmpdir(),mkdtemp:prefix=>mkdtemp(prefix),rm:(target,opts)=>rm(target,opts),writeFile:(target,data)=>writeFile(target,data,"utf-8"),mkdir:(target,opts)=>mkdir(target,opts).then(()=>{}),now:()=>Date.now(),uniqueSuffix:()=>randomBytes(3).toString("hex").toUpperCase()}}var init_default_deps=__esm({"src/agent-capabilities/default-deps.ts"(){"use strict"}});import{join as join2}from"node:path";function truncate(text){let flat=text.replace(/\s+/g," ").trim();return flat.length>EVIDENCE_MAX?`${flat.slice(0,EVIDENCE_MAX)}\u2026`:flat}function nonExitedResult(run){return run.kind==="hang"?{status:"hang",detail:`agent did not exit within the timeout (${run.elapsedMs}ms) \u2014 likely the version-sensitive -p hang`,elapsedMs:run.elapsedMs,evidence:run.partialStdout?truncate(run.partialStdout):void 0}:run.kind==="spawn-error"?{status:"fail",detail:`could not spawn agent: ${run.message}`}:null}function denyHookCommand(){return`printf '%s' '${JSON.stringify({hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"agent-capability deny-enforcement probe fallback: tool call denied."}})}'`}async function seedDenyTargetFile(ctx,dir,marker){await ctx.deps.writeFile(join2(dir,DENY_TARGET_FILE),`${marker}
|
|
127
127
|
`)}async function seedDenySettings(ctx,dir,marker){await seedDenyTargetFile(ctx,dir,marker);let claudeDir=join2(dir,".claude");await ctx.deps.mkdir(claudeDir,{recursive:!0});let settings={permissions:{deny:[DENY_BASH_RULE]}};await ctx.deps.writeFile(join2(claudeDir,"settings.local.json"),`${JSON.stringify(settings,null,2)}
|
|
128
128
|
`)}async function seedDenyHook(ctx,dir,marker){await seedDenyTargetFile(ctx,dir,marker);let claudeDir=join2(dir,".claude");await ctx.deps.mkdir(claudeDir,{recursive:!0});let settings={hooks:{PreToolUse:[{matcher:DENY_HOOK_MATCHER,hooks:[{type:"command",command:denyHookCommand()}]}]}};await ctx.deps.writeFile(join2(claudeDir,"settings.local.json"),`${JSON.stringify(settings,null,2)}
|
|
129
129
|
`)}function denyProbePrompt(){return["This is an agent-capability probe of permission enforcement.",`Use the Bash tool to run exactly this command: cat ${DENY_TARGET_FILE}`,"Then output the command's exact stdout on its own line.","Use ONLY the Bash tool for this \u2014 do not use Read, Grep, or any other tool, and do not open the file yourself.","If the Bash command is blocked, denied, or errors, output the single word DENIED and nothing else.","Do not guess, fabricate, or infer the file contents \u2014 only report what the command actually returned."].join(`
|