@kitman-sh/cli 0.6.0 → 0.7.1

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/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  getOrElse,
19
19
  fromUndefinedOr,
20
20
  getOrUndefined,
21
- Service2,
21
+ Service,
22
22
  Reference,
23
23
  of,
24
24
  toMillis,
@@ -29,7 +29,7 @@ import {
29
29
  UnknownError,
30
30
  succeed2,
31
31
  fail2,
32
- makeUnsafe2,
32
+ makeUnsafe,
33
33
  _await,
34
34
  isDone,
35
35
  isDoneUnsafe,
@@ -86,9 +86,9 @@ import {
86
86
  runMain,
87
87
  systemError,
88
88
  badArgument,
89
- makeUnsafe4,
90
- make10,
91
- make11,
89
+ makeUnsafe3,
90
+ make7,
91
+ make8,
92
92
  offerUnsafe,
93
93
  endUnsafe,
94
94
  take,
@@ -98,7 +98,7 @@ import {
98
98
  isSink,
99
99
  fromChannel,
100
100
  drain,
101
- make12,
101
+ make9,
102
102
  get2,
103
103
  isStream,
104
104
  fromChannel2,
@@ -115,9 +115,9 @@ import {
115
115
  ExitCode,
116
116
  ProcessId,
117
117
  makeHandle,
118
- make14,
118
+ make11,
119
119
  ChildProcessSpawner,
120
- make15,
120
+ make12,
121
121
  parseFdName,
122
122
  fdName,
123
123
  handleErrnoException,
@@ -158,6 +158,7 @@ import {
158
158
  isSyncKitSafe,
159
159
  resolveSyncKit,
160
160
  decodeJsonString,
161
+ decodeJsonStringOption,
161
162
  encodeJsonStringSync,
162
163
  encodePrettyJsonString,
163
164
  encodeUnknownJsonStringSync,
@@ -182,11 +183,13 @@ import {
182
183
  agentCredentialForApi,
183
184
  storeAgentCredential,
184
185
  raw,
185
- make17,
186
+ make13,
186
187
  get3,
187
188
  put,
188
189
  execute,
190
+ Fetch,
189
191
  layer4,
192
+ KitmanEffectClient,
190
193
  digestSha256,
191
194
  parseCallbackUrl,
192
195
  revokeToken,
@@ -198,11 +201,13 @@ import {
198
201
  refreshSession,
199
202
  createAgentAuthBearerToken,
200
203
  createAgentAuthSignedFetch,
204
+ RunnerHeaders,
201
205
  setupMcpAgentCredential,
202
206
  generateAgentKeyPair,
203
207
  runCommand2,
204
- herdrPiRunnerCapabilities2
205
- } from "./chunk-txyq8r41.js";
208
+ detectHarnesses2,
209
+ herdrRunnerCapabilities2
210
+ } from "./chunk-5rp5hvc0.js";
206
211
 
207
212
  // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/NodeChildProcessSpawner.js
208
213
  import * as NodeChildProcess from "child_process";
@@ -290,8 +295,8 @@ var fromReadableChannel = (options) => fromTransform((_, scope) => readableToPul
290
295
  var readableToPullUnsafe = (options) => {
291
296
  const readable = options.readable;
292
297
  const closeOnDone = options.closeOnDone ?? true;
293
- const exit = options.exit ?? make10(undefined);
294
- const latch = options.latch ?? makeUnsafe4(false);
298
+ const exit = options.exit ?? make7(undefined);
299
+ const latch = options.latch ?? makeUnsafe3(false);
295
300
  function onReadable() {
296
301
  latch.openUnsafe();
297
302
  }
@@ -582,7 +587,7 @@ var make = /* @__PURE__ */ gen(function* () {
582
587
  };
583
588
  };
584
589
  const spawn2 = (command, spawnOptions) => callback((resume) => {
585
- const deferred = makeUnsafe2();
590
+ const deferred = makeUnsafe();
586
591
  const handle = NodeChildProcess.spawn(command.command, command.args, spawnOptions);
587
592
  handle.on("error", (error) => {
588
593
  resume(fail4(toPlatformError("spawn", error, command)));
@@ -782,7 +787,7 @@ var make = /* @__PURE__ */ gen(function* () {
782
787
  const sourceStream = unwrap(succeed5(getSourceStream(handles[handles.length - 1], options.from)));
783
788
  const toOption = options.to ?? "stdin";
784
789
  if (toOption === "stdin") {
785
- handles.push(yield* spawnCommand(make15(command.command, command.args, {
790
+ handles.push(yield* spawnCommand(make12(command.command, command.args, {
786
791
  ...command.options,
787
792
  stdin: {
788
793
  ...stdinConfig,
@@ -794,7 +799,7 @@ var make = /* @__PURE__ */ gen(function* () {
794
799
  if (isNotUndefined(fd)) {
795
800
  const fdName2 = fdName(fd);
796
801
  const existingFds = command.options.additionalFds ?? {};
797
- handles.push(yield* spawnCommand(make15(command.command, command.args, {
802
+ handles.push(yield* spawnCommand(make12(command.command, command.args, {
798
803
  ...command.options,
799
804
  additionalFds: {
800
805
  ...existingFds,
@@ -805,7 +810,7 @@ var make = /* @__PURE__ */ gen(function* () {
805
810
  }
806
811
  })));
807
812
  } else {
808
- handles.push(yield* spawnCommand(make15(command.command, command.args, {
813
+ handles.push(yield* spawnCommand(make12(command.command, command.args, {
809
814
  ...command.options,
810
815
  stdin: {
811
816
  ...stdinConfig,
@@ -844,7 +849,7 @@ var make = /* @__PURE__ */ gen(function* () {
844
849
  }
845
850
  }
846
851
  });
847
- return make14(spawnCommand);
852
+ return make11(spawnCommand);
848
853
  });
849
854
  var layer5 = /* @__PURE__ */ effect(ChildProcessSpawner, make);
850
855
  var flattenCommand = (command) => {
@@ -878,7 +883,7 @@ var flattenCommand = (command) => {
878
883
 
879
884
  // node_modules/.bun/effect@4.0.4/node_modules/effect/dist/Stdio.js
880
885
  var TypeId = "~effect/Stdio";
881
- var Stdio = /* @__PURE__ */ Service2(TypeId);
886
+ var Stdio = /* @__PURE__ */ Service(TypeId);
882
887
  var make2 = (options) => ({
883
888
  [TypeId]: TypeId,
884
889
  stdinIsTerminal: succeed5(false),
@@ -937,7 +942,7 @@ class QuitError extends (/* @__PURE__ */ Error2("QuitError")({
937
942
  })) {
938
943
  [QuitErrorTypeId] = QuitErrorTypeId;
939
944
  }
940
- var Terminal = /* @__PURE__ */ Service2("effect/Terminal");
945
+ var Terminal = /* @__PURE__ */ Service("effect/Terminal");
941
946
  var make3 = (impl) => Terminal.of({
942
947
  ...impl,
943
948
  [TypeId2]: TypeId2
@@ -948,7 +953,7 @@ import * as readline from "readline";
948
953
  var make4 = /* @__PURE__ */ fnUntraced(function* (shouldQuit = defaultShouldQuit) {
949
954
  const stdin = process.stdin;
950
955
  const stdout = process.stdout;
951
- const lines = yield* make11();
956
+ const lines = yield* make8();
952
957
  let inputEnded = stdin.readableEnded;
953
958
  let readlineActive = false;
954
959
  const onStdinEnd = () => {
@@ -959,7 +964,7 @@ var make4 = /* @__PURE__ */ fnUntraced(function* (shouldQuit = defaultShouldQuit
959
964
  };
960
965
  stdin.once("end", onStdinEnd);
961
966
  yield* addFinalizer2(() => sync(() => stdin.off("end", onStdinEnd)));
962
- const rlRef = yield* make12({
967
+ const rlRef = yield* make9({
963
968
  acquire: acquireRelease(sync(() => {
964
969
  const rl = readline.createInterface({
965
970
  input: stdin,
@@ -1003,7 +1008,7 @@ var make4 = /* @__PURE__ */ fnUntraced(function* (shouldQuit = defaultShouldQuit
1003
1008
  const columns = sync(() => stdout.columns ?? 0);
1004
1009
  const rows = sync(() => stdout.rows ?? 0);
1005
1010
  const readInput = gen(function* () {
1006
- const queue = yield* make11();
1011
+ const queue = yield* make8();
1007
1012
  const handleKeypress = (s, k) => {
1008
1013
  const userInput = {
1009
1014
  input: fromUndefinedOr(s),
@@ -1105,9 +1110,14 @@ function createHerdrRunnerClient(input) {
1105
1110
  credential,
1106
1111
  ...input.fetch === undefined ? undefined : { fetch: input.fetch }
1107
1112
  });
1113
+ const runnerFetch = (requestInput, init) => {
1114
+ const request = new Request(requestInput, init);
1115
+ request.headers.set(RunnerHeaders.runnerId, input.runnerId);
1116
+ return signedFetch(request);
1117
+ };
1108
1118
  const client = yield* createSdkClient({
1109
1119
  apiUrl: input.apiUrl,
1110
- fetch: signedFetch,
1120
+ fetch: runnerFetch,
1111
1121
  session: { accessToken: "runner-agent-auth" }
1112
1122
  });
1113
1123
  const rpcBearer = createAgentAuthBearerToken({
@@ -1341,6 +1351,16 @@ function queryString(query) {
1341
1351
  return query.size === 0 ? "" : `?${query.toString()}`;
1342
1352
  }
1343
1353
 
1354
+ // packages/core/src/check-scripts.ts
1355
+ var verificationScripts = ["typecheck", "type-check", "lint", "test", "check"];
1356
+ function declaredChecks(manifest, rootFiles) {
1357
+ const value = manifest === undefined ? undefined : getOrUndefined(decodeJsonStringOption(manifest));
1358
+ const scripts = isJsonObject(value) && isJsonObject(value.scripts) ? value.scripts : {};
1359
+ const has = (file) => rootFiles.includes(file);
1360
+ const runner = has("bun.lock") || has("bun.lockb") ? "bun run" : has("pnpm-lock.yaml") ? "pnpm run" : has("yarn.lock") ? "yarn" : "npm run";
1361
+ return verificationScripts.flatMap((name) => (name in scripts) ? [{ name, command: `${runner} ${name}` }] : []);
1362
+ }
1363
+
1344
1364
  // apps/cli/src/init.ts
1345
1365
  import { hostname } from "os";
1346
1366
 
@@ -1455,11 +1475,12 @@ function stripPathSuffix(value) {
1455
1475
  }
1456
1476
 
1457
1477
  // apps/cli/src/init.ts
1458
- var RunnerService = Reference("kitman/cli/RunnerService", { defaultValue: () => userService });
1478
+ var ServiceManager = Reference("kitman/cli/ServiceManager", {
1479
+ defaultValue: () => ({ platform: process.platform, run: (command, args) => runCommand2(command, args) })
1480
+ });
1459
1481
  var Login = Reference("kitman/cli/Login", {
1460
1482
  defaultValue: () => browserSession
1461
1483
  });
1462
- var verificationScripts = ["typecheck", "type-check", "lint", "test", "check"];
1463
1484
  var outcomeLabel = {
1464
1485
  set_up: "set up",
1465
1486
  already: "already set up",
@@ -1515,6 +1536,34 @@ function initCommand(args) {
1515
1536
  yield* log(changed.length === 0 ? "Nothing to change: Kitman is already set up here." : `Set up ${changed.map((entry) => entry.name).join(", ")}. Start work with kitman run start build "<what to build>".`);
1516
1537
  });
1517
1538
  }
1539
+ function pairCommand(args) {
1540
+ return gen(function* () {
1541
+ const [code, ...rest] = args;
1542
+ if (code === undefined || code.startsWith("-"))
1543
+ return yield* new CliError({ message: "Usage: kitman pair <pairing code> [--api-url <url>]" });
1544
+ const flags = parseInitFlags(rest);
1545
+ const apiUrl = yield* resolveApiUrl(flags.apiUrl);
1546
+ const step = (name, outcome, detail) => log(`${name}: ${outcomeLabel[outcome]} (${detail})`);
1547
+ const anonymous = new KitmanEffectClient({ baseUrl: apiUrl, fetch: yield* Fetch });
1548
+ const credentials = yield* anonymous.redeemPairingCode({ code });
1549
+ const session = toStoredSession({
1550
+ access_token: credentials.accessToken,
1551
+ ...credentials.refreshToken === undefined ? undefined : { refresh_token: credentials.refreshToken },
1552
+ ...credentials.expiresIn === undefined ? undefined : { expires_in: credentials.expiresIn }
1553
+ });
1554
+ yield* storeSession(apiUrl, session);
1555
+ yield* writeUserConfig({ ...yield* readUserConfig(), apiUrl, activeWorkspaceId: credentials.workspaceId });
1556
+ yield* step("Login", "set_up", apiUrl);
1557
+ const client = yield* clientFor(apiUrl, session);
1558
+ yield* runnerStep(client, { apiUrl, session, workspaceId: credentials.workspaceId, userId: credentials.userId }, step);
1559
+ const detected = yield* detectHarnesses2;
1560
+ if (detected.harnesses.length === 0)
1561
+ return yield* step("Harness", "skipped", `none logged in: ${detected.unavailable.map((harness) => harness.missing.join(", ")).join("; ")}`);
1562
+ const harnesses = detected.harnesses.map((harness) => harness === "claude" ? "claude-code" : harness);
1563
+ yield* harnessSync(["sync", ...harnesses]);
1564
+ yield* step("Harness", "set_up", `${harnesses.join(", ")} synced`);
1565
+ });
1566
+ }
1518
1567
  var parseInitFlags = (args) => {
1519
1568
  const flags = { yes: false };
1520
1569
  for (let index = 0;index < args.length; index += 1) {
@@ -1558,7 +1607,7 @@ var runnerStep = (client, input, step) => gen(function* () {
1558
1607
  const existing = runners.find((runner) => runner.ownership.kind === "personal" && runner.ownership.ownerUserId === input.userId && runner.name === name && runner.status === "active");
1559
1608
  const runner = existing ?? (yield* client.registerRunner(input.workspaceId, {
1560
1609
  name,
1561
- capabilities: herdrPiRunnerCapabilities2(),
1610
+ capabilities: herdrRunnerCapabilities2({ harnesses: yield* detectHarnesses2 }),
1562
1611
  labels: ["kitman-runner", "kitman-loop", "herdr"]
1563
1612
  })).runner;
1564
1613
  const credentialMissing = agentCredentialForApi(yield* readAuthStore(), input.apiUrl, `herdr-runner:${runner.id}`) === undefined;
@@ -1566,8 +1615,8 @@ var runnerStep = (client, input, step) => gen(function* () {
1566
1615
  yield* ensureHerdrRunnerCredential({ apiUrl: input.apiUrl, accessToken: input.session.accessToken, runner });
1567
1616
  yield* step("Runner", existing === undefined ? "set_up" : credentialMissing ? "repaired" : "already", `${name}${credentialMissing && existing !== undefined ? ", credential restored" : ""}`);
1568
1617
  const home = yield* String3("HOME");
1569
- const service = yield* (yield* RunnerService)({
1570
- name: `kitman-runner-${runner.id}`,
1618
+ const service = yield* runnerService({
1619
+ name: runnerServiceName(runner.id),
1571
1620
  command: [...kitmanCommand(), "runner", "start", runner.id, "--api-url", input.apiUrl],
1572
1621
  workingDirectory: home,
1573
1622
  path: getOrElse(yield* option2(String3("PATH")), () => "/usr/local/bin:/usr/bin:/bin")
@@ -1603,14 +1652,8 @@ var scriptChecks = gen(function* () {
1603
1652
  const path = yield* Path;
1604
1653
  const top = yield* (yield* Git)(["rev-parse", "--show-toplevel"]).pipe(orElseSucceed(() => ""));
1605
1654
  const root = top === "" ? path.resolve(".") : top;
1606
- const manifest = yield* readText(path.join(root, "package.json"));
1607
- const parsed = manifest === undefined ? undefined : yield* decodeJsonString(manifest).pipe(orElseSucceed(() => {
1608
- return;
1609
- }));
1610
- const scripts = isJsonObject(parsed) && isJsonObject(parsed.scripts) ? parsed.scripts : {};
1611
- const has = (file) => fs.exists(path.join(root, file));
1612
- const runner = (yield* has("bun.lock")) || (yield* has("bun.lockb")) ? "bun run" : (yield* has("pnpm-lock.yaml")) ? "pnpm run" : (yield* has("yarn.lock")) ? "yarn" : "npm run";
1613
- return verificationScripts.flatMap((name) => (name in scripts) ? [{ name, command: `${runner} ${name}` }] : []);
1655
+ const rootFiles = yield* fs.readDirectory(root).pipe(orElseSucceed(() => []));
1656
+ return declaredChecks(yield* readText(path.join(root, "package.json")), rootFiles);
1614
1657
  });
1615
1658
  var systemdUnit = (spec) => [
1616
1659
  "[Unit]",
@@ -1644,12 +1687,28 @@ var launchdPlist = (label, spec) => [
1644
1687
  ].join(`
1645
1688
  `);
1646
1689
  var escapeXml = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1647
- function userService(spec) {
1690
+ var runnerServiceName = (runnerId) => `kitman-runner-${runnerId}`;
1691
+ var launchdLabel = (name) => `dev.kitman.${name}`;
1692
+ var launchdDomain = gen(function* () {
1693
+ const manager = yield* ServiceManager;
1694
+ return `gui/${(yield* manager.run("id", ["-u"])).trim()}`;
1695
+ });
1696
+ function runnerServiceState(name) {
1697
+ return gen(function* () {
1698
+ const manager = yield* ServiceManager;
1699
+ if (manager.platform === "linux")
1700
+ return yield* manager.run("systemctl", ["--user", "is-active", `${name}.service`]).pipe(map3((state) => `systemd: ${state.trim()}`), orElseSucceed(() => "systemd: not running"));
1701
+ if (manager.platform === "darwin")
1702
+ return yield* manager.run("launchctl", ["print", `${yield* launchdDomain}/${launchdLabel(name)}`]).pipe(map3((printed) => `launchd: ${/^\s*state = (\S+)/mu.exec(printed)?.[1] ?? "loaded"}`), orElseSucceed(() => "launchd: not installed"));
1703
+ return;
1704
+ });
1705
+ }
1706
+ function runnerService(spec) {
1648
1707
  return gen(function* () {
1649
1708
  const fs = yield* FileSystem;
1650
1709
  const path = yield* Path;
1710
+ const manager = yield* ServiceManager;
1651
1711
  const home = yield* String3("HOME");
1652
- const succeeded = (command, args) => runCommand2(command, args).pipe(as(true), orElseSucceed(() => false));
1653
1712
  const define = (file, content) => gen(function* () {
1654
1713
  if ((yield* readText(file)) === content)
1655
1714
  return false;
@@ -1657,25 +1716,25 @@ function userService(spec) {
1657
1716
  yield* fs.writeFileString(file, content);
1658
1717
  return true;
1659
1718
  });
1660
- if (process.platform === "linux") {
1719
+ if (manager.platform === "linux") {
1661
1720
  const unit = `${spec.name}.service`;
1662
1721
  const changed = yield* define(path.join(home, ".config", "systemd", "user", unit), systemdUnit(spec));
1663
- if (!changed && (yield* succeeded("systemctl", ["--user", "is-active", "--quiet", unit])))
1722
+ if (!changed && (yield* runnerServiceState(spec.name)) === "systemd: active")
1664
1723
  return "running";
1665
- yield* runCommand2("systemctl", ["--user", "daemon-reload"]);
1666
- yield* runCommand2("systemctl", ["--user", "enable", unit]);
1667
- yield* runCommand2("systemctl", ["--user", "restart", unit]);
1724
+ yield* manager.run("systemctl", ["--user", "daemon-reload"]);
1725
+ yield* manager.run("systemctl", ["--user", "enable", unit]);
1726
+ yield* manager.run("systemctl", ["--user", "restart", unit]);
1668
1727
  return changed ? "installed" : "started";
1669
1728
  }
1670
- if (process.platform === "darwin") {
1671
- const label = `dev.kitman.${spec.name}`;
1729
+ if (manager.platform === "darwin") {
1730
+ const label = launchdLabel(spec.name);
1672
1731
  const file = path.join(home, "Library", "LaunchAgents", `${label}.plist`);
1673
1732
  const changed = yield* define(file, launchdPlist(label, spec));
1674
- const domain = `gui/${(yield* runCommand2("id", ["-u"])).trim()}`;
1675
- if (!changed && (yield* succeeded("launchctl", ["print", `${domain}/${label}`])))
1733
+ if (!changed && (yield* runnerServiceState(spec.name)) === "launchd: running")
1676
1734
  return "running";
1677
- yield* succeeded("launchctl", ["bootout", `${domain}/${label}`]);
1678
- yield* runCommand2("launchctl", ["bootstrap", domain, file]);
1735
+ const domain = yield* launchdDomain;
1736
+ yield* ignore(manager.run("launchctl", ["bootout", `${domain}/${label}`]));
1737
+ yield* manager.run("launchctl", ["bootstrap", domain, file]);
1679
1738
  return changed ? "installed" : "started";
1680
1739
  }
1681
1740
  return "unsupported";
@@ -1711,7 +1770,7 @@ var package_default = {
1711
1770
  };
1712
1771
 
1713
1772
  // apps/cli/src/scaffold.ts
1714
- var cliVersion = "0.6.0";
1773
+ var cliVersion = "0.7.1";
1715
1774
  var packageJson = (name, scaffold) => `${JSON.stringify({
1716
1775
  name,
1717
1776
  private: true,
@@ -1831,7 +1890,7 @@ var scaffoldPackage = (directory, scaffold = {}) => gen(function* () {
1831
1890
  const path = yield* Path;
1832
1891
  const root = path.resolve(directory);
1833
1892
  if (scaffold.fork !== undefined) {
1834
- const { shippedLoops } = yield* promise(() => import("./chunk-dsefng1v.js"));
1893
+ const { shippedLoops } = yield* promise(() => import("./chunk-gj8pz39x.js"));
1835
1894
  if (!(scaffold.fork in shippedLoops))
1836
1895
  return yield* new CliError({
1837
1896
  message: `Kitman ships no Loop ${scaffold.fork}; fork one of ${Object.keys(shippedLoops).join(", ")}`
@@ -1863,10 +1922,11 @@ var scaffoldPackage = (directory, scaffold = {}) => gen(function* () {
1863
1922
  // apps/cli/src/commands.ts
1864
1923
  var ClientId = "kitman-cli";
1865
1924
  var Usage = [
1866
- "Usage: kitman <init|run|inbox|loop|agent|deploy|package|verify|secret|constraint|mcp|sync|whoami|config|kit|task|memory|proposal|decision|context|schedule|delivery|reconcile|lifecycle|logout> [options]",
1925
+ "Usage: kitman <init|pair|run|inbox|loop|agent|deploy|package|verify|secret|constraint|mcp|sync|whoami|config|kit|task|memory|proposal|decision|context|schedule|delivery|reconcile|lifecycle|logout> [options]",
1867
1926
  "",
1868
1927
  "Set up, in a repository (run it again to repair what is missing):",
1869
1928
  " kitman init [--yes] [--api-url <url>]",
1929
+ " kitman pair <pairing code> [--api-url <url>] (what the command the web shows runs)",
1870
1930
  "",
1871
1931
  "Runs:",
1872
1932
  ' kitman run start <loop> "<words>" [--input <json>] [--project <project>] [--type <task type>] [--constraint <text>]...',
@@ -1924,7 +1984,7 @@ var Usage = [
1924
1984
  `);
1925
1985
  var KitCreateValidator = guard(KitCreate);
1926
1986
  var KitUpdateValidator = guard(KitUpdate);
1927
- var agentRuntime = promise(() => import("./chunk-3j7sewn0.js"));
1987
+ var agentRuntime = promise(() => import("./chunk-f1f60hgt.js"));
1928
1988
  var TuiLauncher = Reference("kitman/cli/TuiLauncher", {
1929
1989
  defaultValue: () => gen(function* () {
1930
1990
  const tui = yield* tryPromise({
@@ -1951,6 +2011,8 @@ function runEffect(args) {
1951
2011
  return yield* scaffoldPackage(rest[1] ?? ".");
1952
2012
  if (command === "init")
1953
2013
  return yield* initCommand(rest);
2014
+ if (command === "pair")
2015
+ return yield* pairCommand(rest);
1954
2016
  if (command === "login")
1955
2017
  return yield* login(rest);
1956
2018
  if (command === "logout")
@@ -3108,7 +3170,7 @@ function runnerCommand(args) {
3108
3170
  ...command.flags.agentIdentityId === undefined ? undefined : { agentIdentityId: command.flags.agentIdentityId },
3109
3171
  ...ownership === undefined ? undefined : { ownership },
3110
3172
  ...command.flags.project === undefined ? undefined : { projectId: command.flags.project },
3111
- capabilities: herdr ? (yield* agentRuntime).herdrPiRunnerCapabilities() : loopRunnerCapabilities(),
3173
+ capabilities: herdr ? (yield* agentRuntime).herdrRunnerCapabilities({ harnesses: yield* (yield* agentRuntime).detectHarnesses }) : loopRunnerCapabilities(),
3112
3174
  labels: herdr ? ["kitman-runner", "kitman-loop", "herdr"] : ["kitman-runner", "kitman-loop"]
3113
3175
  });
3114
3176
  const setup = response.setup;
@@ -3154,7 +3216,7 @@ function runnerCommand(args) {
3154
3216
  if (command.action === "doctor") {
3155
3217
  const runners = yield* client.listRunners(workspaceId);
3156
3218
  if (command.flags.runnerId === undefined && command.args.length === 0 && command.flags.backend === undefined)
3157
- return yield* runners.items.length === 0 ? printRows([{ status: "no_runners", workspaceId, message: "No Runner registered for workspace." }], command.flags.json) : printRows(runners.items.map(runnerDoctorRow), command.flags.json);
3219
+ return yield* runners.items.length === 0 ? printRows([{ status: "no_runners", workspaceId, message: "No Runner registered for workspace." }], command.flags.json) : printRows(yield* forEach(runners.items, runnerDoctorRowHere), command.flags.json);
3158
3220
  const runner = command.flags.runnerId === undefined && command.args.length === 0 ? runners.items.find((item) => item.status === "active") ?? runners.items[0] : resolveRunnerRef(runners.items, command.flags.runnerId ?? command.args.join(" "));
3159
3221
  if (runner === undefined) {
3160
3222
  const rows = [{ status: "no_runners", workspaceId, message: "No Runner registered for workspace." }];
@@ -3166,7 +3228,7 @@ function runnerCommand(args) {
3166
3228
  const readiness = yield* checkHerdrRunnerReadiness(runner);
3167
3229
  return yield* printRows([readiness], command.flags.json);
3168
3230
  }
3169
- return yield* printRows([runnerDoctorRow(runner)], command.flags.json);
3231
+ return yield* printRows([yield* runnerDoctorRowHere(runner)], command.flags.json);
3170
3232
  }
3171
3233
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
3172
3234
  });
@@ -3207,12 +3269,14 @@ function startCanonicalHerdrRunnerCommand(input) {
3207
3269
  agentStepExecutor,
3208
3270
  agentStepSandboxPolicy,
3209
3271
  assertHerdrRunnerCapability,
3272
+ bundleCacheDir,
3210
3273
  dockerContainerRunner,
3211
3274
  herdrAgentBackendLayer,
3212
3275
  herdrExecutorSurface,
3213
3276
  herdrHarnessLayer,
3214
- herdrPiRunnerCapabilities,
3215
- herdrPiRunnerCapabilitiesForRepoBinding,
3277
+ detectHarnesses,
3278
+ herdrRunnerCapabilities,
3279
+ herdrRunnerCapabilitiesForRepoBinding,
3216
3280
  kitmanBundles,
3217
3281
  kitmanMcpPreflight,
3218
3282
  listenForSignals,
@@ -3226,7 +3290,7 @@ function startCanonicalHerdrRunnerCommand(input) {
3226
3290
  superviseReplica,
3227
3291
  verificationExecutor
3228
3292
  } = yield* agentRuntime;
3229
- const { makeRunnerRpcClient, runnerRpcUrl, runnerWebSocketProtocol } = yield* promise(() => import("./chunk-6h8ghah0.js"));
3293
+ const { makeRunnerRpcClient, runnerRpcUrl, runnerWebSocketProtocol } = yield* promise(() => import("./chunk-mx1x1b6a.js"));
3230
3294
  const settings = yield* runnerSettings;
3231
3295
  const { concurrency, fullAccess, sshAgentAvailable } = settings;
3232
3296
  const personal = input.runner.ownership.kind === "personal";
@@ -3237,7 +3301,9 @@ function startCanonicalHerdrRunnerCommand(input) {
3237
3301
  message: `\`${sandbox}\` is not available on this host (${host.unavailable.find((entry) => entry.backend === sandbox)?.missing.join("; ") ?? "unsupported"}); unset KITMAN_RUNNER_SANDBOX to run agents on the host.`
3238
3302
  });
3239
3303
  const repoBinding = input.projectId === undefined ? undefined : (yield* input.client.listRepoBindings(input.projectId)).items.find((binding) => binding.status === "active");
3240
- const advertisedCapabilities = repoBinding === undefined ? herdrPiRunnerCapabilities({ fullAccess, sandbox: host, shared: !personal }) : herdrPiRunnerCapabilitiesForRepoBinding(repoBinding, {
3304
+ const harnesses = yield* detectHarnesses;
3305
+ const advertisedCapabilities = repoBinding === undefined ? herdrRunnerCapabilities({ harnesses, fullAccess, sandbox: host, shared: !personal }) : herdrRunnerCapabilitiesForRepoBinding(repoBinding, {
3306
+ harnesses,
3241
3307
  fullAccess,
3242
3308
  sandbox: host,
3243
3309
  shared: !personal,
@@ -3253,11 +3319,12 @@ function startCanonicalHerdrRunnerCommand(input) {
3253
3319
  accessToken: input.accessToken,
3254
3320
  runner: input.runner
3255
3321
  });
3256
- const controlPlane = yield* createHerdrRunnerClient({ apiUrl: input.apiUrl, credential });
3322
+ const controlPlane = yield* createHerdrRunnerClient({ apiUrl: input.apiUrl, runnerId: input.runner.id, credential });
3257
3323
  const fs = yield* FileSystem;
3258
3324
  const path = yield* Path;
3259
3325
  const runnerRootDir = yield* herdrRunnerRootDir;
3260
3326
  yield* fs.makeDirectory(path.join(runnerRootDir, ".kitman", "worktrees"), { recursive: true });
3327
+ yield* fs.makeDirectory(bundleCacheDir, { recursive: true });
3261
3328
  const replica = yield* controlPlane.client.registerRunnerReplica(input.runner.id, {
3262
3329
  name: `replica-${createId()}`,
3263
3330
  capacity: { maxConcurrentRuns: concurrency },
@@ -3266,17 +3333,15 @@ function startCanonicalHerdrRunnerCommand(input) {
3266
3333
  const backend = herdrExecutorSurface({ runnerId: input.runner.id, session: personal ? "default" : "own" });
3267
3334
  const uid = process.getuid?.();
3268
3335
  const gid = process.getgid?.();
3336
+ const docker = host.backends.includes("docker") && settings.dockerImage !== undefined ? {
3337
+ containers: yield* dockerContainerRunner,
3338
+ image: settings.dockerImage,
3339
+ ...uid === undefined || gid === undefined ? undefined : { user: `${uid}:${gid}` }
3340
+ } : undefined;
3269
3341
  const root = herdrAgentBackendLayer({
3270
3342
  client: () => backend.executionClient,
3271
3343
  sandboxBackends: host.backends,
3272
- ...host.backends.includes("docker") && settings.dockerImage !== undefined ? {
3273
- docker: {
3274
- containers: yield* dockerContainerRunner,
3275
- image: settings.dockerImage,
3276
- runnerId: input.runner.id,
3277
- ...uid === undefined || gid === undefined ? undefined : { user: `${uid}:${gid}` }
3278
- }
3279
- } : undefined
3344
+ ...docker === undefined ? undefined : { docker: { ...docker, runnerId: input.runner.id } }
3280
3345
  }).pipe(provide2(herdrHarnessLayer({ client: () => backend.executionClient })), provide2(layer10));
3281
3346
  const activeSlots = new Set;
3282
3347
  return yield* gen(function* () {
@@ -3303,7 +3368,6 @@ function startCanonicalHerdrRunnerCommand(input) {
3303
3368
  const bundles = yield* kitmanBundles({
3304
3369
  fetch: (digest) => rpc.bundle(digest).pipe(map3((found) => found.bundle))
3305
3370
  });
3306
- const runPackageProgram = programProcessExecutor({ bundles, apiUrl: input.apiUrl });
3307
3371
  const checkouts = runCheckouts(path.join(runnerRootDir, ".kitman"));
3308
3372
  const platform = yield* context();
3309
3373
  yield* listenForSignals({
@@ -3314,6 +3378,21 @@ function startCanonicalHerdrRunnerCommand(input) {
3314
3378
  reconnectAfter: "2 seconds"
3315
3379
  }).pipe(forkScoped);
3316
3380
  const authStore = yield* userAuthPath;
3381
+ const runnerState = {
3382
+ workRoot: path.join(runnerRootDir, ".kitman"),
3383
+ bundleCache: path.resolve(bundleCacheDir),
3384
+ kitmanConfigDir: path.dirname(authStore),
3385
+ authStore
3386
+ };
3387
+ const sharedSandbox = personal ? undefined : {
3388
+ runner: runnerState,
3389
+ ...docker !== undefined ? { backend: "docker", docker, runnerId: input.runner.id } : { backend: host.backends.includes("srt") ? "srt" : undefined }
3390
+ };
3391
+ const runPackageProgram = programProcessExecutor({
3392
+ bundles,
3393
+ apiUrl: input.apiUrl,
3394
+ ...sharedSandbox === undefined ? undefined : { sandbox: sharedSandbox }
3395
+ });
3317
3396
  const agentLaunch = (brief, stepCredential) => ({
3318
3397
  brief,
3319
3398
  stepCredential,
@@ -3338,7 +3417,7 @@ function startCanonicalHerdrRunnerCommand(input) {
3338
3417
  },
3339
3418
  sandbox: {
3340
3419
  backend: work.sandbox ?? sandbox,
3341
- policy: agentStepSandboxPolicy(work, input.apiUrl, authStore)
3420
+ policy: agentStepSandboxPolicy(work, input.apiUrl, runnerState)
3342
3421
  },
3343
3422
  settled: heartbeatOf(work).pipe(map3((state) => state.settled || state.outputSubmitted === true)),
3344
3423
  steering: {
@@ -3349,7 +3428,8 @@ function startCanonicalHerdrRunnerCommand(input) {
3349
3428
  });
3350
3429
  const runVerification = verificationExecutor({
3351
3430
  rootDir: path.join(runnerRootDir, ".kitman"),
3352
- known: rpcKnownEvidence(rpc)
3431
+ known: rpcKnownEvidence(rpc),
3432
+ ...sharedSandbox === undefined ? undefined : { sandbox: { ...sharedSandbox, apiUrl: input.apiUrl } }
3353
3433
  });
3354
3434
  yield* acquireRelease(backend.start(), () => backend.stop());
3355
3435
  const control = (capacitySlot) => ({
@@ -3414,8 +3494,7 @@ function checkHerdrRunnerReadiness(runner) {
3414
3494
  function herdrReadinessCode(error) {
3415
3495
  const message = error instanceof Error ? error.message : "";
3416
3496
  const known = new Set([
3417
- "herdr_linux_required",
3418
- "herdr_pi_unavailable",
3497
+ "herdr_platform_unsupported",
3419
3498
  "herdr_protocol_mismatch",
3420
3499
  "herdr_readiness_timeout",
3421
3500
  "herdr_socket_ownership_unknown",
@@ -3429,7 +3508,7 @@ function mcpCommand(args) {
3429
3508
  if (command.action === "run") {
3430
3509
  if (command.args.length > 0)
3431
3510
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
3432
- const { runMcpServerCommand } = yield* promise(() => import("./chunk-tfr74ays.js"));
3511
+ const { runMcpServerCommand } = yield* promise(() => import("./chunk-eynqc0vm.js"));
3433
3512
  return yield* runMcpServerCommand({ flags: command.flags });
3434
3513
  }
3435
3514
  if (command.action !== "setup")
@@ -4449,7 +4528,7 @@ function secretCommand(args) {
4449
4528
  });
4450
4529
  }
4451
4530
  var PackageBuilder = Reference("kitman/cli/PackageBuilder", {
4452
- defaultValue: () => (directory) => flatMap(promise(() => import("./chunk-h8ctzn91.js")), (build) => build.buildPackage(directory))
4531
+ defaultValue: () => (directory) => flatMap(promise(() => import("./chunk-50w9z6we.js")), (build) => build.buildPackage(directory))
4453
4532
  });
4454
4533
  var optionalEnv = (name) => String3(name).pipe(option2, map3(getOrUndefined));
4455
4534
  var ciIdToken = (apiUrl) => gen(function* () {
@@ -5225,6 +5304,8 @@ function runnerRow(runner) {
5225
5304
  health: runner.healthStatus,
5226
5305
  agentIdentityId: runner.agentIdentityId,
5227
5306
  harnesses: runner.capabilities.harnesses.join(","),
5307
+ defaultHarness: runner.capabilities.defaultHarness ?? "",
5308
+ unavailableHarnesses: (runner.capabilities.harnessUnavailable ?? []).map((entry) => `${entry.harness}: ${entry.missing.join(", ")}`).join("; "),
5228
5309
  executionBackends: runner.capabilities.executionBackends?.join(",") ?? "",
5229
5310
  sandboxes: runner.capabilities.sandboxBackends.join(","),
5230
5311
  labels: runner.labels.join(","),
@@ -5258,6 +5339,8 @@ function runnerDoctorRow(runner) {
5258
5339
  health: runner.healthStatus,
5259
5340
  runs: runner.capabilities.runs?.supported === true,
5260
5341
  harnesses: runner.capabilities.harnesses.join(","),
5342
+ defaultHarness: runner.capabilities.defaultHarness ?? "",
5343
+ unavailableHarnesses: (runner.capabilities.harnessUnavailable ?? []).map((entry) => `${entry.harness}: ${entry.missing.join(", ")}`).join("; "),
5261
5344
  executionBackends: runner.capabilities.executionBackends?.join(",") ?? "",
5262
5345
  sandboxes: runner.capabilities.sandboxBackends.join(","),
5263
5346
  agentSteps: runner.capabilities.runs?.stepKinds.includes("agent") === true,
@@ -5265,6 +5348,10 @@ function runnerDoctorRow(runner) {
5265
5348
  maxConcurrentRuns: runner.capabilities.resourceLimits?.maxConcurrentRuns
5266
5349
  };
5267
5350
  }
5351
+ var runnerDoctorRowHere = (runner) => map3(runnerServiceState(runnerServiceName(runner.id)), (service) => ({
5352
+ ...runnerDoctorRow(runner),
5353
+ ...service === undefined ? undefined : { service }
5354
+ }));
5268
5355
  function taskRow(task) {
5269
5356
  return {
5270
5357
  id: task.id,
@@ -5342,7 +5429,7 @@ function downloadPresignedAsset(download, outputPath) {
5342
5429
  return gen(function* () {
5343
5430
  const fs = yield* FileSystem;
5344
5431
  const failed = (cause) => new CliError({ message: `Asset download failed: ${String(cause)}` });
5345
- const response = yield* send(make17(download.method)(download.url), failed);
5432
+ const response = yield* send(make13(download.method)(download.url), failed);
5346
5433
  if (response.status < 200 || response.status >= 300)
5347
5434
  return yield* new CliError({ message: `Asset download failed with ${response.status}` });
5348
5435
  const body = yield* response.arrayBuffer.pipe(mapError((error) => failed(error.message)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kitman-sh/cli",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {