@karowanorg/orc-ops 0.1.0 → 0.1.2

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/guide.d.ts CHANGED
@@ -3,4 +3,4 @@
3
3
  * - CLI: `orc guide`
4
4
  * - MCP: the `orc_guide` tool
5
5
  */
6
- export declare const GUIDE = "# orc \u2014 how to write and run a program\n\norc runs a \"program\": a script that fans out AI-agent subtasks (\"leaves\") and\nrecords each one, so a run is reproducible, resumable, and observable. You write\na program, launch it, then watch it and collect the result.\n\n## 1. Write a program\n\nA program is a `.orc.ts` file with a single default-exported async function.\nIt receives an api object and returns the run's result:\n\n import type { Program } from \"@karowanorg/orc-sdk/program\"; // optional, type-only\n\n const program: Program = async ({ agent, parallel, phase }) => {\n const inventory = await agent(\"List the modules in this repo.\", {\n schema: { type: \"object\",\n properties: { modules: { type: \"array\", items: { type: \"string\" } } },\n required: [\"modules\"] },\n });\n\n // Ordinary async/await controls the flow. Awaiting a result before the\n // next call makes it a dependency; independent calls run concurrently.\n const plans = await Promise.all((inventory.modules as string[]).map(async (m) => {\n const findings = await agent(`Audit module ${m}`, { id: `audit-${m}` });\n return agent(`Plan a fix for ${m}: ${JSON.stringify(findings)}`, { id: `plan-${m}` });\n }));\n\n return phase(\"synthesis\", () => agent(`Merge these plans: ${JSON.stringify(plans)}`, { id: \"merge\" }));\n };\n export default program;\n\n## The api\n\nIMPORTANT \u2014 leaves are isolated. Each agent() runs a FRESH subagent that shares\nNONE of your context: it cannot see this program, your conversation, other\nleaves' prompts or results, or anything you know that you haven't written into\nits prompt (plus the shared `--brief`).\n\n- agent(prompt, opts?) => Promise<result>\n Dispatch one agent. Returns its result (matching `schema` if you pass one).\n A failed leaf rejects the promise, so wrap it in try/catch if you want to\n handle failure. Options:\n id a label shown in the monitor\n schema JSON Schema; the result is structured output matching it\n harness \"claude\" | \"codex\" | a configured harness (default: auto)\n model e.g. \"claude-fable-5\", \"gpt-5.6-sol\"\n reasoningEffort \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\"\n readOnly default true; set false for a leaf that edits files\n cwd working directory for this leaf (defaults to the run's)\n host SSH destination to run this leaf on a remote machine\n idleTimeout ms with no output before the leaf is killed (false = off)\n phase group this call under a phase label\n To see valid `harness`, `model`, and `reasoningEffort` values for your\n machine, run `orc capabilities` (or `orc capabilities --json`) \u2014 it lists\n each installed harness with its available models and reasoning levels.\n Omit any of them to use the default; an unknown value is rejected up front\n by `orc validate`.\n\n- parallel(specs[]) => Promise<outcomes[]>\n Run several agents at once from an array of option objects (each with a\n `prompt`). Every lane runs to completion INDEPENDENTLY \u2014 one lane failing\n never cancels the others. Outcomes come back in order as\n `{ status: \"ok\", value } | { status: \"error\", error }`, so you decide how to\n handle partial failure. (For fail-fast, use `Promise.all` over `agent()`,\n which rejects on the first failure.)\n\n- phase(name, fn) => Promise<result>\n Group every agent() call made inside `fn` under a phase, for a readable\n run timeline.\n\n- settle(promise) => { status: \"ok\", value } | { status: \"error\", error }\n Run a lane and capture its outcome instead of letting a failure stop the run\n \u2014 useful for \"do all of these, some may fail\" fan-outs.\n\n- log(message) \u2014 write a line to the run's live event feed.\n- ext.<name>(payload) \u2014 call a custom step you registered in orc.config.\n\nBecause leaves can't see each other, pass data between them as plain values:\nput a previous result into the next prompt with JSON.stringify so the receiving\nleaf actually has it. Use normal try/catch, Promise.all/allSettled, and bounded\nloops for control flow.\n\n## What a program may and may not do\n\n- No wall clock or randomness. `Date`, `Math.random`, timers, network, file\n access, and imports (beyond type-only) are unavailable \u2014 a program only\n decides which agents to run and how to combine their results.\n- Loops must be finite; an unbounded loop is stopped by a step limit.\n- These constraints let orc replay a program exactly when you resume it, so the\n same inputs always produce the same run.\n\n## Writing files\n\nBy default leaves are read-only. To let a leaf directly modify files or run\nmutating commands, set `readOnly: false` and launch the run with\n`--allow-writes`. Configured hooks and MCP tools are not disabled for read-only\nleaves; their side effects are outside this guarantee. A write leaf runs with\nwhatever permissions your own agent has; add `--sandbox` to confine its writes\nto the working directory. If a run stops on a write leaf, `orc resume` picks it\nback up (it re-checks the working-tree state first, never blindly redoing work).\n\n## 2. Validate and launch\n\n orc validate --program-path ./my.orc.ts # compile + preview, no run\n orc launch --program-path ./my.orc.ts --brief \"what this run is for\"\n\n`--brief` (required) is shared context added to every leaf. Common launch flags:\n`--allow-writes`, `--host <ssh>`, `--approval-mode manual|accept-edits|auto|bypass`,\n`--sandbox`, `--harness claude|codex`, `--budget <usd>` (fail the run after\nobserved estimated cost exceeds this; concurrent work may overshoot), `--wait`\n(block for the result instead of running in the background).\n\n## 3. Watch and collect\n\n orc status --run-id <id> # summary\n orc wait --run-id <id> # block until it finishes\n orc get-result --run-id <id> # the final result (or --seq N for one leaf)\n orc trace --run-id <id> # per-leaf and per-tool detail\n orc open --run-id <id> # open the live monitor, print its URL\n orc list # recent runs\n\n## 4. Approvals\n\nIn `manual` or `accept-edits` mode a leaf can pause to ask permission for a\ntool. Answer it from anywhere:\n\n orc approvals --run-id <id>\n orc respond --run-id <id> --approval-id <aid> --behavior allow|deny\n\n## Discover more\n\n orc --help # every command\n orc <command> --help # a command's flags\n orc commands # JSON catalog of all operations\n orc capabilities # available harnesses, models, and reasoning levels\n orc doctor --host <ssh> # check a local or remote machine is ready\n\nEvery command supports `--json`. Run state lives in `$ORC_HOME` (default\n`~/.orc`). Real runs need `claude` and/or `codex` installed and logged in.\n";
6
+ export declare const GUIDE = "# orc \u2014 how to write and run a program\n\norc runs a \"program\": a script that fans out AI-agent subtasks (\"leaves\") and\nrecords each one, so a run is reproducible, resumable, and observable. You write\na program, launch it, then watch it and collect the result.\n\n## 1. Write a program\n\nA program is a `.orc.ts` file with a single default-exported async function.\nIt receives an api object and returns the run's result:\n\n import type { Program } from \"@karowanorg/orc-sdk/program\"; // optional, type-only\n\n const program: Program = async ({ agent, parallel, phase }) => {\n const inventory = await agent(\"List the modules in this repo.\", {\n schema: { type: \"object\",\n properties: { modules: { type: \"array\", items: { type: \"string\" } } },\n required: [\"modules\"] },\n });\n\n // Ordinary async/await controls the flow. Awaiting a result before the\n // next call makes it a dependency; independent calls run concurrently.\n const plans = await Promise.all((inventory.modules as string[]).map(async (m) => {\n const findings = await agent(`Audit module ${m}`, { id: `audit-${m}` });\n return agent(`Plan a fix for ${m}: ${JSON.stringify(findings)}`, { id: `plan-${m}` });\n }));\n\n return phase(\"synthesis\", () => agent(`Merge these plans: ${JSON.stringify(plans)}`, { id: \"merge\" }));\n };\n export default program;\n\n## The api\n\nIMPORTANT \u2014 leaves are isolated. Each agent() runs a FRESH subagent that shares\nNONE of your context: it cannot see this program, your conversation, other\nleaves' prompts or results, or anything you know that you haven't written into\nits prompt (plus the shared `--brief`).\n\n- agent(prompt, opts?) => Promise<result>\n Dispatch one agent. Returns its result (matching `schema` if you pass one).\n A failed leaf rejects the promise, so wrap it in try/catch if you want to\n handle failure. Options:\n id a label shown in the monitor\n schema JSON Schema; the result is structured output matching it\n harness \"claude\" | \"codex\" | a configured harness (default: auto)\n model e.g. \"claude-fable-5\", \"gpt-5.6-sol\"\n reasoningEffort \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\"\n readOnly default true; set false for a leaf that edits files\n cwd working directory for this leaf (defaults to the run's)\n idleTimeout ms with no output before the leaf is killed (false = off)\n phase group this call under a phase label\n To see valid `harness`, `model`, and `reasoningEffort` values for your\n machine, run `orc capabilities` (or `orc capabilities --json`) \u2014 it lists\n each installed harness with its available models and reasoning levels.\n Omit any of them to use the default; an unknown value is rejected up front\n by `orc validate`.\n\n- parallel(specs[]) => Promise<outcomes[]>\n Run several agents at once from an array of option objects (each with a\n `prompt`). Every lane runs to completion INDEPENDENTLY \u2014 one lane failing\n never cancels the others. Outcomes come back in order as\n `{ status: \"ok\", value } | { status: \"error\", error }`, so you decide how to\n handle partial failure. (For fail-fast, use `Promise.all` over `agent()`,\n which rejects on the first failure.)\n\n- phase(name, fn) => Promise<result>\n Group every agent() call made inside `fn` under a phase, for a readable\n run timeline.\n\n- settle(promise) => { status: \"ok\", value } | { status: \"error\", error }\n Run a lane and capture its outcome instead of letting a failure stop the run\n \u2014 useful for \"do all of these, some may fail\" fan-outs.\n\n- log(message) \u2014 write a line to the run's live event feed.\n- ext.<name>(payload) \u2014 call a custom step you registered in orc.config.\n\nBecause leaves can't see each other, pass data between them as plain values:\nput a previous result into the next prompt with JSON.stringify so the receiving\nleaf actually has it. Use normal try/catch, Promise.all/allSettled, and bounded\nloops for control flow.\n\n## What a program may and may not do\n\n- No wall clock or randomness. `Date`, `Math.random`, timers, network, file\n access, and imports (beyond type-only) are unavailable \u2014 a program only\n decides which agents to run and how to combine their results.\n- Loops must be finite; an unbounded loop is stopped by a step limit.\n- These constraints let orc replay a program exactly when you resume it, so the\n same inputs always produce the same run.\n\n## Writing files\n\nBy default leaves are read-only. To let a leaf directly modify files or run\nmutating commands, set `readOnly: false` and launch the run with\n`--allow-writes`. Configured hooks and MCP tools are not disabled for read-only\nleaves; their side effects are outside this guarantee. A write leaf runs with\nwhatever permissions your own agent has; add `--sandbox` to confine its writes\nto the working directory. If a run stops on a write leaf, `orc resume` picks it\nback up (it re-checks the working-tree state first, never blindly redoing work).\n\n## 2. Validate and launch\n\n orc validate --program-path ./my.orc.ts # compile + preview, no run\n orc launch --program-path ./my.orc.ts --brief \"what this run is for\"\n\n`--brief` (required) is shared context added to every leaf. Common launch flags:\n`--allow-writes`, `--approval-mode manual|accept-edits|auto|bypass`,\n`--sandbox`, `--harness claude|codex`, `--budget <usd>` (fail the run after\nobserved estimated cost exceeds this; concurrent work may overshoot), `--wait`\n(block for the result instead of running in the background).\n\n## 3. Watch and collect\n\n orc status --run-id <id> # summary\n orc wait --run-id <id> # block until it finishes\n orc get-result --run-id <id> # the final result (or --seq N for one leaf)\n orc trace --run-id <id> # per-leaf and per-tool detail\n orc open --run-id <id> # open the live monitor, print its URL\n orc list # recent runs\n\n## 4. Approvals\n\nIn `manual` or `accept-edits` mode a leaf can pause to ask permission for a\ntool. Answer it from anywhere:\n\n orc approvals --run-id <id>\n orc respond --run-id <id> --approval-id <aid> --behavior allow|deny\n\n## Discover more\n\n orc --help # every command\n orc <command> --help # a command's flags\n orc commands # JSON catalog of all operations\n orc capabilities # available harnesses, models, and reasoning levels\n orc doctor # check this machine is ready\n\nEvery command supports `--json`. Run state lives in `$ORC_HOME` (default\n`~/.orc`). Real runs need `claude` and/or `codex` installed and logged in.\n";
package/dist/guide.js CHANGED
@@ -52,7 +52,6 @@ its prompt (plus the shared \`--brief\`).
52
52
  reasoningEffort "low" | "medium" | "high" | "xhigh" | "max"
53
53
  readOnly default true; set false for a leaf that edits files
54
54
  cwd working directory for this leaf (defaults to the run's)
55
- host SSH destination to run this leaf on a remote machine
56
55
  idleTimeout ms with no output before the leaf is killed (false = off)
57
56
  phase group this call under a phase label
58
57
  To see valid \`harness\`, \`model\`, and \`reasoningEffort\` values for your
@@ -110,7 +109,7 @@ back up (it re-checks the working-tree state first, never blindly redoing work).
110
109
  orc launch --program-path ./my.orc.ts --brief "what this run is for"
111
110
 
112
111
  \`--brief\` (required) is shared context added to every leaf. Common launch flags:
113
- \`--allow-writes\`, \`--host <ssh>\`, \`--approval-mode manual|accept-edits|auto|bypass\`,
112
+ \`--allow-writes\`, \`--approval-mode manual|accept-edits|auto|bypass\`,
114
113
  \`--sandbox\`, \`--harness claude|codex\`, \`--budget <usd>\` (fail the run after
115
114
  observed estimated cost exceeds this; concurrent work may overshoot), \`--wait\`
116
115
  (block for the result instead of running in the background).
@@ -138,7 +137,7 @@ tool. Answer it from anywhere:
138
137
  orc <command> --help # a command's flags
139
138
  orc commands # JSON catalog of all operations
140
139
  orc capabilities # available harnesses, models, and reasoning levels
141
- orc doctor --host <ssh> # check a local or remote machine is ready
140
+ orc doctor # check this machine is ready
142
141
 
143
142
  Every command supports \`--json\`. Run state lives in \`$ORC_HOME\` (default
144
143
  \`~/.orc\`). Real runs need \`claude\` and/or \`codex\` installed and logged in.
package/dist/ops.d.ts CHANGED
@@ -18,7 +18,6 @@ interface FirstFrontierCall {
18
18
  id?: string;
19
19
  readOnly: boolean;
20
20
  harness?: string;
21
- host?: string;
22
21
  model?: string;
23
22
  reasoningEffort?: string;
24
23
  schema?: Json;
@@ -27,7 +26,6 @@ interface FirstFrontierCall {
27
26
  export declare const launch: OpDef<z.ZodObject<{
28
27
  programPath: z.ZodString;
29
28
  cwd: z.ZodOptional<z.ZodString>;
30
- host: z.ZodOptional<z.ZodString>;
31
29
  brief: z.ZodString;
32
30
  allowWrites: z.ZodDefault<z.ZodBoolean>;
33
31
  approvalMode: z.ZodDefault<z.ZodEnum<{
@@ -38,6 +36,7 @@ export declare const launch: OpDef<z.ZodObject<{
38
36
  }>>;
39
37
  sandbox: z.ZodDefault<z.ZodBoolean>;
40
38
  sandboxDirs: z.ZodOptional<z.ZodArray<z.ZodString>>;
39
+ networkAccess: z.ZodDefault<z.ZodBoolean>;
41
40
  maxParallel: z.ZodOptional<z.ZodNumber>;
42
41
  idleTimeoutSeconds: z.ZodOptional<z.ZodNumber>;
43
42
  budget: z.ZodOptional<z.ZodNumber>;
@@ -79,7 +78,6 @@ export declare const validate: OpDef<z.ZodObject<{
79
78
  bypass: "bypass";
80
79
  }>>;
81
80
  harness: z.ZodOptional<z.ZodString>;
82
- host: z.ZodOptional<z.ZodString>;
83
81
  checkCapabilities: z.ZodDefault<z.ZodBoolean>;
84
82
  }, z.core.$strip>, {
85
83
  ok: boolean;
@@ -137,7 +135,6 @@ export declare const getTrace: OpDef<z.ZodObject<{
137
135
  harness?: string;
138
136
  model?: string;
139
137
  reasoningEffort?: string;
140
- host?: string;
141
138
  cwd?: string;
142
139
  readOnly: boolean;
143
140
  startMs: number;
@@ -159,7 +156,6 @@ export declare const list: OpDef<z.ZodObject<{
159
156
  name: string | undefined;
160
157
  state: string;
161
158
  createdAtMs: number;
162
- host: string | undefined;
163
159
  cwd: string;
164
160
  }[]>;
165
161
  export declare const cancel: OpDef<z.ZodObject<{
@@ -194,10 +190,8 @@ export declare const respondApproval: OpDef<z.ZodObject<{
194
190
  enqueued: boolean;
195
191
  }>;
196
192
  export declare const capabilities: OpDef<z.ZodObject<{
197
- host: z.ZodOptional<z.ZodString>;
198
193
  refresh: z.ZodDefault<z.ZodBoolean>;
199
194
  }, z.core.$strip>, {
200
- host: string;
201
195
  defaultHarness: string;
202
196
  harnesses: Record<string, unknown>;
203
197
  extensions: string[];
@@ -214,16 +208,13 @@ export declare const report: OpDef<z.ZodObject<{
214
208
  path: string;
215
209
  }>;
216
210
  export declare const guide: OpDef<z.ZodObject<{
217
- host: z.ZodOptional<z.ZodString>;
218
211
  probe: z.ZodDefault<z.ZodBoolean>;
219
212
  }, z.core.$strip>, {
220
213
  guide: string;
221
214
  }>;
222
215
  export declare const doctor: OpDef<z.ZodObject<{
223
- host: z.ZodOptional<z.ZodString>;
224
216
  cwd: z.ZodOptional<z.ZodString>;
225
217
  }, z.core.$strip>, {
226
- host: string;
227
218
  checks: Record<string, unknown>;
228
219
  }>;
229
220
  export declare const ALL_OPS: OpDef[];
package/dist/ops.js CHANGED
@@ -25,12 +25,12 @@ export const launch = defineOp({
25
25
  input: z.object({
26
26
  programPath: z.string().describe("path to the program (.orc.ts/.ts/.js)"),
27
27
  cwd: z.string().optional().describe("working directory (plain path); defaults to the caller's cwd"),
28
- host: z.string().optional().describe("SSH destination (separate field; e.g. 'build@ci-box' or an ssh-config alias)"),
29
28
  brief: z.string().describe("shared context injected into every leaf"),
30
29
  allowWrites: z.boolean().default(false).describe("grant write-declared leaves permission to mutate files"),
31
30
  approvalMode: ApprovalMode.default("auto").describe("manual | accept-edits | auto | bypass"),
32
31
  sandbox: z.boolean().default(false).describe("confine write leaves to cwd + sandboxDirs (default: unconfined, like the caller)"),
33
32
  sandboxDirs: z.array(z.string()).optional().describe("extra writable roots when sandboxed (e.g. cache dirs outside the workspace)"),
33
+ networkAccess: z.boolean().default(false).describe("permit outbound network while retaining filesystem sandboxing"),
34
34
  maxParallel: z.number().int().min(1).max(64).optional(),
35
35
  idleTimeoutSeconds: z.number().int().optional().describe("run default for the per-call output-idle watchdog; 0 disables"),
36
36
  budget: z.number().positive().optional().describe("reactive USD cap: fail the run once observed estimated cost exceeds this (may overshoot)"),
@@ -42,12 +42,12 @@ export const launch = defineOp({
42
42
  const manifest = await prepareRun({
43
43
  programPath: input.programPath,
44
44
  cwd: input.cwd,
45
- host: input.host,
46
45
  brief: input.brief,
47
46
  allowWrites: input.allowWrites,
48
47
  approvalMode: input.approvalMode,
49
48
  sandbox: input.sandbox,
50
49
  sandboxDirs: input.sandboxDirs,
50
+ networkAccess: input.networkAccess,
51
51
  maxParallel: input.maxParallel,
52
52
  idleTimeout: input.idleTimeoutSeconds === undefined
53
53
  ? undefined
@@ -87,7 +87,6 @@ export const validate = defineOp({
87
87
  allowWrites: z.boolean().default(false),
88
88
  approvalMode: ApprovalMode.default("auto").describe("check the first-frontier harnesses can honor this mode"),
89
89
  harness: z.string().optional().describe("default harness override (same precedence as launch)"),
90
- host: z.string().optional().describe("check capabilities on this remote host"),
91
90
  checkCapabilities: z.boolean().default(true).describe("probe referenced harnesses for model/effort/mode support (slower)"),
92
91
  }),
93
92
  async handler(input, ctx) {
@@ -105,7 +104,6 @@ export const validate = defineOp({
105
104
  id: spec.id,
106
105
  readOnly: spec.readOnly,
107
106
  harness: spec.harness,
108
- host: spec.host,
109
107
  model: spec.model,
110
108
  reasoningEffort: spec.reasoningEffort,
111
109
  schema: spec.schema,
@@ -127,19 +125,16 @@ export const validate = defineOp({
127
125
  finally {
128
126
  vm?.dispose();
129
127
  }
130
- // Host can change per leaf, so only share probes with the same effective
131
- // harness and executor destination.
132
128
  const capsCache = new Map();
133
- const capsFor = (harnessName, host) => {
134
- const key = JSON.stringify([harnessName, host]);
135
- const cached = capsCache.get(key);
129
+ const capsFor = (harnessName) => {
130
+ const cached = capsCache.get(harnessName);
136
131
  if (cached)
137
132
  return cached;
138
133
  const harness = ctx.registry.harnesses.get(harnessName);
139
134
  if (!harness)
140
135
  return Promise.reject(new Error(`unknown harness "${harnessName}"`));
141
- const pending = Promise.resolve().then(() => harness.discover({ executor: ctx.registry.executorFor(host) }));
142
- capsCache.set(key, pending);
136
+ const pending = Promise.resolve().then(() => harness.discover({ executor: ctx.registry.executor }));
137
+ capsCache.set(harnessName, pending);
143
138
  return pending;
144
139
  };
145
140
  for (const call of firstCalls) {
@@ -159,7 +154,6 @@ export const validate = defineOp({
159
154
  }
160
155
  if (call.kind === "agent") {
161
156
  const harnessName = call.harness ?? input.harness ?? ctx.registry.defaultHarness;
162
- const host = call.host ?? input.host;
163
157
  const harness = ctx.registry.harnesses.get(harnessName);
164
158
  if (!harness) {
165
159
  problems.push(`call ${call.seq} uses unknown harness "${harnessName}" (available: ${[...ctx.registry.harnesses.keys()].join(", ")})`);
@@ -177,10 +171,10 @@ export const validate = defineOp({
177
171
  if (harness && input.checkCapabilities) {
178
172
  let caps;
179
173
  try {
180
- caps = await capsFor(harnessName, host);
174
+ caps = await capsFor(harnessName);
181
175
  }
182
176
  catch (err) {
183
- problems.push(`call ${call.seq}: could not discover harness "${harnessName}"${host ? ` on ${host}` : ""} (${String(err instanceof Error ? err.message : err)})`);
177
+ problems.push(`call ${call.seq}: could not discover harness "${harnessName}" (${String(err instanceof Error ? err.message : err)})`);
184
178
  continue;
185
179
  }
186
180
  if (!caps.available) {
@@ -218,7 +212,7 @@ export const validate = defineOp({
218
212
  }
219
213
  const approvalMode = input.approvalMode ?? "auto";
220
214
  if (caps.approvalModes.length > 0 && !caps.approvalModes.includes(approvalMode)) {
221
- problems.push(`call ${call.seq}: harness "${harnessName}" cannot honor approval mode "${approvalMode}"${host ? " over ssh" : ""} (supports: ${caps.approvalModes.join(", ")})`);
215
+ problems.push(`call ${call.seq}: harness "${harnessName}" cannot honor approval mode "${approvalMode}" (supports: ${caps.approvalModes.join(", ")})`);
222
216
  }
223
217
  }
224
218
  }
@@ -322,13 +316,13 @@ export const list = defineOp({
322
316
  catch {
323
317
  /* partial run dir */
324
318
  }
325
- return { runId: m.runId, name: m.name, state, createdAtMs: m.createdAtMs, host: m.host, cwd: m.cwd };
319
+ return { runId: m.runId, name: m.name, state, createdAtMs: m.createdAtMs, cwd: m.cwd };
326
320
  });
327
321
  },
328
322
  });
329
323
  export const cancel = defineOp({
330
324
  name: "cancel",
331
- doc: "Queue cancellation. Local leaves get TERM→KILL; plain SSH only confirms channel teardown, so remote process state may be unknown.",
325
+ doc: "Queue cancellation. Leaves get TERM→KILL on their whole process group.",
332
326
  readOnly: false,
333
327
  input: z.object({ runId: RunId }),
334
328
  async handler(input) {
@@ -401,11 +395,10 @@ export const capabilities = defineOp({
401
395
  doc: "List harnesses, models, and reasoning levels — each discovered through the harness's native mechanism.",
402
396
  readOnly: true,
403
397
  input: z.object({
404
- host: z.string().optional().describe("probe a remote host instead of local"),
405
398
  refresh: z.boolean().default(false),
406
399
  }),
407
400
  async handler(input, ctx) {
408
- const executor = ctx.registry.executorFor(input.host);
401
+ const executor = ctx.registry.executor;
409
402
  const out = {};
410
403
  for (const [name, harness] of ctx.registry.harnesses) {
411
404
  out[name] = await harness.discover({ executor }).catch((err) => ({
@@ -414,7 +407,6 @@ export const capabilities = defineOp({
414
407
  }));
415
408
  }
416
409
  return {
417
- host: input.host ?? "(local)",
418
410
  defaultHarness: ctx.registry.defaultHarness,
419
411
  harnesses: out,
420
412
  extensions: [...ctx.registry.extensions.keys()],
@@ -450,7 +442,6 @@ export const guide = defineOp({
450
442
  doc: "How to write and run an orc program — the authoring + usage guide, with this machine's live harness/model catalog appended.",
451
443
  readOnly: true,
452
444
  input: z.object({
453
- host: z.string().optional().describe("probe this remote host's harnesses instead of local"),
454
445
  probe: z.boolean().default(true).describe("append the live capability catalog (set false for the static doc only)"),
455
446
  }),
456
447
  async handler(input, ctx) {
@@ -461,7 +452,7 @@ export const guide = defineOp({
461
452
  // values in hand instead of guessing. Never let a slow or missing harness
462
453
  // break the guide — degrade to the static doc, which already points at
463
454
  // `orc capabilities`.
464
- const caps = await withDeadline(capabilities.handler({ host: input.host, refresh: false }, ctx), 15_000).catch(() => null);
455
+ const caps = await withDeadline(capabilities.handler({ refresh: false }, ctx), 15_000).catch(() => null);
465
456
  return { guide: GUIDE + (caps ? renderCapabilitiesForGuide(caps) : "") };
466
457
  },
467
458
  });
@@ -483,7 +474,7 @@ function renderCapabilitiesForGuide(caps) {
483
474
  "",
484
475
  "## Available on this machine",
485
476
  "",
486
- `Discovered live${c.host && c.host !== "(local)" ? ` on ${c.host}` : ""} — these are the valid \`harness\`, \`model\`, and`,
477
+ "Discovered live — these are the valid \`harness\`, \`model\`, and",
487
478
  `\`reasoningEffort\` values right now, so you don't have to guess. Default harness: **${c.defaultHarness}**.`,
488
479
  "Omit any of them to take the default.",
489
480
  ];
@@ -502,19 +493,18 @@ function renderCapabilitiesForGuide(caps) {
502
493
  if (h.approvalModes?.length)
503
494
  lines.push(` - approval modes: ${h.approvalModes.join(", ")}`);
504
495
  }
505
- lines.push("", "Re-run `orc capabilities` any time, or `orc capabilities --host <ssh>` for another machine.");
496
+ lines.push("", "Re-run `orc capabilities` any time.");
506
497
  return lines.join("\n");
507
498
  }
508
499
  export const doctor = defineOp({
509
500
  name: "doctor",
510
- doc: "Preflight an executor: harness binaries, versions, cwd existence.",
501
+ doc: "Preflight this machine: harness binaries, versions, cwd existence.",
511
502
  readOnly: true,
512
503
  input: z.object({
513
- host: z.string().optional(),
514
504
  cwd: z.string().optional(),
515
505
  }),
516
506
  async handler(input, ctx) {
517
- const executor = ctx.registry.executorFor(input.host);
507
+ const executor = ctx.registry.executor;
518
508
  const checks = {};
519
509
  for (const [name, harness] of ctx.registry.harnesses) {
520
510
  const caps = await harness.discover({ executor }).catch(() => null);
@@ -523,7 +513,7 @@ export const doctor = defineOp({
523
513
  if (input.cwd) {
524
514
  checks.cwd = { path: input.cwd, exists: await executor.exists(input.cwd) };
525
515
  }
526
- return { host: input.host ?? "(local)", checks };
516
+ return { checks };
527
517
  },
528
518
  });
529
519
  // ---------------------------------------------------------------------------
package/dist/registry.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * come only from an explicit orc.config.(js|json).
6
6
  */
7
7
  import { callerAffinity, loadConfig } from "@karowanorg/orc-core";
8
- import { executorFor } from "@karowanorg/orc-executors";
8
+ import { LocalExecutor } from "@karowanorg/orc-executors";
9
9
  import { claudeHarness } from "@karowanorg/orc-harness-claude";
10
10
  import { codexHarness, createCodexHarness } from "@karowanorg/orc-harness-codex";
11
11
  import { makeExecHarness } from "./exec-harness.js";
@@ -31,5 +31,5 @@ export async function buildRegistry(opts = {}) {
31
31
  config.defaultHarness ??
32
32
  callerAffinity(opts.mcpClientName) ??
33
33
  "codex";
34
- return { harnesses, extensions, defaultHarness, executorFor };
34
+ return { harnesses, extensions, defaultHarness, executor: new LocalExecutor() };
35
35
  }
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@karowanorg/orc-ops",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "license": "0BSD",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "dependencies": {
8
- "@karowanorg/orc-core": "^0.1.0",
9
- "@karowanorg/orc-executors": "^0.1.0",
10
- "@karowanorg/orc-harness-claude": "^0.1.0",
11
- "@karowanorg/orc-harness-codex": "^0.1.0",
12
- "@karowanorg/orc-ui": "^0.1.0",
8
+ "@karowanorg/orc-core": "^0.1.2",
9
+ "@karowanorg/orc-executors": "^0.1.1",
10
+ "@karowanorg/orc-harness-claude": "^0.1.3",
11
+ "@karowanorg/orc-harness-codex": "^0.1.3",
12
+ "@karowanorg/orc-ui": "^0.1.1",
13
13
  "zod": "^4.1.0"
14
14
  },
15
15
  "description": "The zod-first operation registry orc's CLI, MCP server, and SDK all interpret.",