@kitman-sh/cli 0.7.5 → 0.8.0

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
@@ -1,11 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  import {
4
- identity,
5
- isUndefined,
6
- isNotUndefined,
7
- isNotNull,
8
- isNullish,
9
4
  toJsonValue,
10
5
  isOneOf,
11
6
  toJsonObject,
@@ -14,34 +9,19 @@ import {
14
9
  isObject,
15
10
  isJsonObject,
16
11
  isString2,
17
- match,
18
12
  getOrElse,
19
- fromUndefinedOr,
20
13
  getOrUndefined,
21
- Service,
22
14
  Reference,
23
- of,
24
- toMillis,
25
15
  TaggedError,
26
16
  squash,
27
- Done,
28
- done,
29
- UnknownError,
30
- succeed2,
31
- fail2,
32
- makeUnsafe,
17
+ make5,
33
18
  _await,
34
- isDone,
35
- isDoneUnsafe,
36
- doneUnsafe,
37
- addFinalizer,
38
- succeed4,
39
- effect,
40
- mergeAll,
19
+ fail3,
41
20
  provide2,
42
21
  provideMerge,
43
- catchDone,
44
- findFirst,
22
+ min,
23
+ exponential,
24
+ spaced,
45
25
  forEach,
46
26
  promise,
47
27
  tryPromise,
@@ -50,7 +30,6 @@ import {
50
30
  sync,
51
31
  void_2,
52
32
  undefined_,
53
- callback,
54
33
  gen,
55
34
  fail4,
56
35
  try_,
@@ -61,103 +40,88 @@ import {
61
40
  as,
62
41
  asVoid,
63
42
  catch_,
43
+ catchTag,
64
44
  catchIf,
65
45
  mapError,
46
+ tapError,
66
47
  tapCause,
48
+ retry,
67
49
  ignore,
68
50
  orElseSucceed,
51
+ timeoutOrElse,
69
52
  sleep,
70
53
  raceFirst,
71
54
  filterOrFail,
72
55
  context,
73
56
  provide3,
74
57
  provideContext,
58
+ service,
75
59
  provideService,
60
+ scope,
76
61
  scoped,
77
62
  acquireRelease,
78
- addFinalizer2,
79
63
  ensuring,
80
- uninterruptible,
81
- forever,
64
+ onError,
65
+ forkIn,
82
66
  forkScoped,
83
- fnUntraced,
84
67
  logWarning,
85
68
  logError,
86
69
  runMain,
87
- systemError,
88
- badArgument,
89
- makeUnsafe3,
90
- make8,
91
- make9,
92
- offerUnsafe,
93
- endUnsafe,
94
70
  take,
95
- poll,
96
- fromTransform,
97
- mapDone,
98
- isSink,
99
- fromChannel,
100
- drain,
101
- make10,
102
- get2,
103
- isStream,
104
- fromChannel2,
105
- empty2,
106
- unwrap,
107
- merge2,
108
- transduce,
71
+ tap2,
72
+ mapError2,
109
73
  decodeText,
110
74
  run,
111
75
  mkString,
112
76
  FileSystem,
113
77
  Path,
114
78
  encode,
115
- ExitCode,
116
- ProcessId,
117
- makeHandle,
118
- make12,
119
79
  ChildProcessSpawner,
120
- make13,
121
- parseFdName,
122
- fdName,
123
- handleErrnoException,
80
+ make8,
124
81
  Crypto,
82
+ Stdio,
83
+ is,
84
+ decodeUnknownEffect,
85
+ String2,
86
+ Struct,
87
+ ArraySchema,
88
+ isPattern,
89
+ Terminal,
125
90
  layer,
126
- layer2,
127
- layer3,
128
- tag,
129
- Error2,
130
91
  error,
131
92
  log,
132
93
  guard,
133
94
  toIso,
134
95
  currentDate,
135
- ProposalStatuses,
136
- ProposalTargetTypes,
137
- ContextPackModes,
138
- DefaultContextPackMode,
139
- MemoryStatuses,
140
- MemoryScopes,
141
- MaxMemoryListLimit,
96
+ redactDurableSurface,
97
+ AgentHarnessIds,
142
98
  RepoBindingProviders,
143
99
  RepoBindingCredentialProviderTypes,
144
- AgentHarnessIds,
145
- redactDurableSurface,
100
+ ContextPackModes,
101
+ DefaultContextPackMode,
102
+ ProposalStatuses,
103
+ ProposalTargetTypes,
146
104
  RunDisplayStates,
147
- KitmanAgentCapabilityNames,
148
- DecisionStatuses,
149
- FixFlowRecoveryActions,
150
- createId,
105
+ defaultRunnerCapabilities,
106
+ isSyncKitHashValid,
107
+ isSyncKitSafe,
108
+ resolveSyncKit,
151
109
  KitKinds,
152
110
  KitInstallationScopes,
153
111
  KitCreate,
154
112
  KitUpdate,
155
- runDisplayLabels,
113
+ MemoryStatuses,
114
+ MemoryScopes,
115
+ MaxMemoryListLimit,
116
+ DecisionStatuses,
117
+ FixFlowRecoveryActions,
118
+ KitmanAgentCapabilityNames,
119
+ isRunFileRefusalReason,
120
+ runFileRefusalWords,
156
121
  repositoryKey,
157
- defaultRunnerCapabilities,
158
- isSyncKitHashValid,
159
- isSyncKitSafe,
160
- resolveSyncKit,
122
+ createId,
123
+ digestSha256,
124
+ sha256Hasher,
161
125
  decodeJsonString,
162
126
  decodeJsonStringOption,
163
127
  encodeJsonStringSync,
@@ -165,6 +129,17 @@ import {
165
129
  encodeUnknownJsonStringSync,
166
130
  option2,
167
131
  String3,
132
+ raw,
133
+ make9,
134
+ get2,
135
+ put,
136
+ HttpClient,
137
+ execute,
138
+ Fetch,
139
+ layer2,
140
+ runDisplayLabels,
141
+ KitmanClientError,
142
+ KitmanEffectClient,
168
143
  CliError,
169
144
  DefaultApiUrl,
170
145
  resolveConfig,
@@ -183,15 +158,12 @@ import {
183
158
  removeAllSessions,
184
159
  agentCredentialForApi,
185
160
  storeAgentCredential,
186
- raw,
187
- make14,
188
- get3,
189
- put,
190
- execute,
191
- Fetch,
192
- layer4,
193
- KitmanEffectClient,
194
- digestSha256,
161
+ removeAgentCredential,
162
+ normalizeApiUrl,
163
+ isStoredAgentCredential,
164
+ RpcClientError,
165
+ RunnerHeaders,
166
+ RunnerRpcError,
195
167
  parseCallbackUrl,
196
168
  revokeToken,
197
169
  toStoredSession,
@@ -202,941 +174,286 @@ import {
202
174
  refreshSession,
203
175
  createAgentAuthBearerToken,
204
176
  createAgentAuthSignedFetch,
205
- RunnerHeaders,
206
177
  setupMcpAgentCredential,
207
178
  generateAgentKeyPair,
179
+ CommandFailed2,
208
180
  runCommand2,
209
181
  detectHarnesses2,
210
182
  herdrRunnerCapabilities2
211
- } from "./chunk-p2wk2v39.js";
212
-
213
- // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/NodeChildProcessSpawner.js
214
- import * as NodeChildProcess from "child_process";
215
- import { PassThrough } from "stream";
216
-
217
- // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/internal/nodeChildProcessSpawner.js
218
- var buildSpawnOptions = (options, base, platform) => {
219
- const detached = options.detached ?? platform !== "win32";
220
- return {
221
- ...base,
222
- detached,
223
- shell: options.shell,
224
- windowsHide: options.windowsHide ?? !detached
225
- };
226
- };
227
-
228
- // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/NodeSink.js
229
- var fromWritable = (options) => fromChannel(mapDone(fromWritableChannel(options), (_) => [_]));
230
- var fromWritableChannel = (options) => fromTransform((pull) => {
231
- const writable = options.evaluate();
232
- return succeed5(pullIntoWritable({
233
- ...options,
234
- writable,
235
- pull
236
- }));
237
- });
238
- var pullIntoWritable = (options) => options.pull.pipe(flatMap2((chunk) => {
239
- let i = 0;
240
- return callback((resume) => {
241
- let cancelled = false;
242
- const loop = () => {
243
- for (;i < chunk.length; ) {
244
- if (cancelled) {
245
- return;
246
- }
247
- const success = options.writable.write(chunk[i++], options.encoding);
248
- if (!success) {
249
- if (!cancelled) {
250
- options.writable.once("drain", loop);
251
- }
252
- return;
253
- }
254
- }
255
- if (!cancelled) {
256
- resume(void_2);
257
- }
258
- };
259
- loop();
260
- return sync(() => {
261
- cancelled = true;
262
- options.writable.off("drain", loop);
263
- });
264
- });
265
- }), forever({
266
- disableYield: true
267
- }), options.endOnDone !== false ? catchDone((_) => {
268
- if ("closed" in options.writable && options.writable.closed) {
269
- return done(_);
270
- }
271
- return callback((resume) => {
272
- const onFinish = () => resume(done(_));
273
- options.writable.once("finish", onFinish);
274
- options.writable.end();
275
- return sync(() => {
276
- options.writable.off("finish", onFinish);
277
- });
278
- });
279
- }) : identity, raceFirst(callback((resume) => {
280
- const onError = (error) => resume(fail4(options.onError(error)));
281
- options.writable.once("error", onError);
282
- return sync(() => {
283
- options.writable.off("error", onError);
183
+ } from "./chunk-fpyy4rpd.js";
184
+ // apps/cli/src/herdr-runner-credential.ts
185
+ function herdrRunnerCredentialName(runnerId) {
186
+ return `herdr-runner:${runnerId}`;
187
+ }
188
+ function storedHerdrRunnerCredential(apiUrl, runnerId) {
189
+ return map3(readAuthStore(), (auth) => agentCredentialForApi(auth, apiUrl, herdrRunnerCredentialName(runnerId)));
190
+ }
191
+ function writeRunnerCredentialFile(path, file) {
192
+ return gen(function* () {
193
+ const fs = yield* FileSystem;
194
+ yield* fs.makeDirectory((yield* Path).dirname(path), { recursive: true, mode: 448 });
195
+ yield* fs.writeFileString(path, `${encodeJsonStringSync(file)}
196
+ `, { mode: 384 });
197
+ yield* fs.chmod(path, 384);
284
198
  });
285
- })));
286
-
287
- // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/NodeStream.js
288
- var fromReadable = (options) => fromChannel2(fromReadableChannel(options));
289
- var fromReadableChannel = (options) => fromTransform((_, scope) => readableToPullUnsafe({
290
- scope,
291
- readable: options.evaluate(),
292
- onError: options.onError ?? defaultOnError,
293
- chunkSize: options.chunkSize,
294
- closeOnDone: options.closeOnDone
295
- }));
296
- var readableToPullUnsafe = (options) => {
297
- const readable = options.readable;
298
- const closeOnDone = options.closeOnDone ?? true;
299
- const exit = options.exit ?? make8(undefined);
300
- const latch = options.latch ?? makeUnsafe3(false);
301
- function onReadable() {
302
- latch.openUnsafe();
303
- }
304
- function onError(error) {
305
- exit.current = fail2(options.onError(error));
306
- latch.openUnsafe();
307
- }
308
- function onEnd() {
309
- exit.current = fail2(Done());
310
- latch.openUnsafe();
311
- }
312
- readable.on("readable", onReadable);
313
- readable.once("error", onError);
314
- readable.once("end", onEnd);
315
- const pull = suspend(function loop() {
316
- let item = options.readable.read(options.chunkSize);
317
- if (item === null) {
318
- if (exit.current) {
319
- return exit.current;
320
- }
321
- if (readable.readableEnded) {
322
- return fail4(Done());
323
- }
324
- latch.closeUnsafe();
325
- return flatMap2(latch.await, loop);
326
- }
327
- const chunk = of(item);
328
- while (true) {
329
- item = options.readable.read(options.chunkSize);
330
- if (item === null)
331
- break;
332
- chunk.push(item);
333
- }
334
- return succeed5(chunk);
199
+ }
200
+ function readRunnerCredentialFile(path) {
201
+ return gen(function* () {
202
+ const text = yield* flatMap2(FileSystem, (fs) => fs.readFileString(path));
203
+ const file = yield* decodeJsonString(text);
204
+ if (!isJsonObject(file) || !isStoredAgentCredential(file) || !isString2(file.apiUrl) || !isString2(file.runnerId) || file.hostId === undefined)
205
+ return yield* new CliError({ message: `${path} holds no Runner credential; pair again with --credential-file` });
206
+ return { apiUrl: file.apiUrl, runnerId: file.runnerId, credential: file };
335
207
  });
336
- return as(addFinalizer(options.scope, sync(() => {
337
- readable.off("readable", onReadable);
338
- readable.off("error", onError);
339
- readable.off("end", onEnd);
340
- if (closeOnDone && "closed" in options.readable && !options.readable.closed) {
341
- options.readable.destroy();
342
- }
343
- })), pull);
344
- };
345
- var defaultOnError = (error) => new UnknownError(error);
346
-
347
- // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/NodeChildProcessSpawner.js
348
- var toError = (error) => error instanceof globalThis.Error ? error : new globalThis.Error(String(error));
349
- var toPlatformError = (method, error, command) => {
350
- const {
351
- commands
352
- } = flattenCommand(command);
353
- const commandStr = commands.reduce((acc, curr) => {
354
- const cmd = `${curr.command} ${curr.args.join(" ")}`;
355
- return acc.length === 0 ? cmd : `${acc} | ${cmd}`;
356
- }, "");
357
- return handleErrnoException("ChildProcess", method)(error, [commandStr]);
358
- };
359
- var processGroupGraceMillis = 1000;
360
- var processGroupPollIntervalMillis = 10;
361
- var handleInputErrors = (writable) => {
362
- const onError = () => {};
363
- writable.on("error", onError);
364
- writable.once("close", () => writable.off("error", onError));
365
- };
366
- var isProcessAlive = (childProcess, exitSignal) => {
367
- if (!isDoneUnsafe(exitSignal)) {
368
- return true;
369
- }
370
- if (globalThis.process.platform === "win32") {
371
- return false;
372
- }
373
- try {
374
- globalThis.process.kill(-childProcess.pid, 0);
375
- return true;
376
- } catch {
377
- return false;
378
- }
379
- };
380
- var taskkill = (childProcess, onExit = () => {}) => {
381
- if (childProcess.exitCode !== null || childProcess.signalCode !== null) {
382
- onExit(null);
383
- return;
384
- }
385
- NodeChildProcess.execFile("taskkill", ["/pid", String(childProcess.pid), "/T", "/F"], {
386
- windowsHide: true
387
- }, onExit);
388
- };
389
- var make = /* @__PURE__ */ gen(function* () {
390
- const fs = yield* FileSystem;
391
- const path = yield* Path;
392
- const resolveWorkingDirectory = fnUntraced(function* (options) {
393
- if (isUndefined(options.cwd))
394
- return;
395
- yield* fs.access(options.cwd);
396
- return path.resolve(options.cwd);
208
+ }
209
+ function herdrRunnerCredentialCheck(apiUrl, runnerId) {
210
+ return gen(function* () {
211
+ const credential = yield* storedHerdrRunnerCredential(apiUrl, runnerId);
212
+ if (credential === undefined)
213
+ return "missing";
214
+ const { client } = yield* createHerdrRunnerClient({ apiUrl, runnerId, credential });
215
+ return yield* client.heartbeatRunner(runnerId, {}).pipe(as("accepted"), catchIf((error) => error.status === 401 || error.status === 403, () => succeed5("refused")));
397
216
  });
398
- const resolveEnvironment = (options) => {
399
- return options.extendEnv ? {
400
- ...globalThis.process.env,
401
- ...options.env
402
- } : options.env;
403
- };
404
- const inputToStdioOption = (input) => isStream(input) ? "pipe" : input;
405
- const outputToStdioOption = (input) => isSink(input) ? "pipe" : input;
406
- const resolveStdinOption = (options) => {
407
- const defaultConfig = {
408
- stream: "pipe",
409
- encoding: "utf-8",
410
- endOnDone: true
411
- };
412
- if (isUndefined(options.stdin)) {
413
- return defaultConfig;
414
- }
415
- if (typeof options.stdin === "string") {
416
- return {
417
- ...defaultConfig,
418
- stream: options.stdin
419
- };
420
- }
421
- if (isStream(options.stdin)) {
422
- return {
423
- ...defaultConfig,
424
- stream: options.stdin
425
- };
426
- }
427
- return {
428
- stream: options.stdin.stream,
429
- encoding: options.stdin.encoding ?? defaultConfig.encoding,
430
- endOnDone: options.stdin.endOnDone ?? defaultConfig.endOnDone
431
- };
432
- };
433
- const resolveOutputOption = (options, streamName) => {
434
- const option = options[streamName];
435
- if (isUndefined(option)) {
436
- return {
437
- stream: "pipe"
438
- };
439
- }
440
- if (typeof option === "string") {
441
- return {
442
- stream: option
443
- };
444
- }
445
- if (isSink(option)) {
446
- return {
447
- stream: option
448
- };
449
- }
450
- return {
451
- stream: option.stream
452
- };
453
- };
454
- const resolveAdditionalFds = (options) => {
455
- if (isUndefined(options.additionalFds)) {
456
- return [];
457
- }
458
- const result = [];
459
- for (const [name, config] of Object.entries(options.additionalFds)) {
460
- const fd = parseFdName(name);
461
- if (isNotUndefined(fd)) {
462
- result.push({
463
- fd,
464
- config
465
- });
466
- }
467
- }
468
- return result.sort((a, b) => a.fd - b.fd);
469
- };
470
- const buildStdioArray = (stdinConfig, stdoutConfig, stderrConfig, additionalFds) => {
471
- const stdio = [inputToStdioOption(stdinConfig.stream), outputToStdioOption(stdoutConfig.stream), outputToStdioOption(stderrConfig.stream)];
472
- if (additionalFds.length === 0) {
473
- return stdio;
474
- }
475
- const maxFd = additionalFds.reduce((max, {
476
- fd
477
- }) => Math.max(max, fd), 2);
478
- for (let i = 3;i <= maxFd; i++) {
479
- stdio[i] = "ignore";
480
- }
481
- for (const {
482
- fd
483
- } of additionalFds) {
484
- stdio[fd] = "pipe";
485
- }
486
- return stdio;
487
- };
488
- const setupAdditionalFds = fnUntraced(function* (command, childProcess, additionalFds) {
489
- if (additionalFds.length === 0) {
490
- return {
491
- getInputFd: () => drain,
492
- getOutputFd: () => empty2
493
- };
494
- }
495
- const inputSinks = new Map;
496
- const outputStreams = new Map;
497
- for (const {
498
- config,
499
- fd
500
- } of additionalFds) {
501
- const nodeStream = childProcess.stdio[fd];
502
- switch (config.type) {
503
- case "input": {
504
- let sink = drain;
505
- if (nodeStream && "write" in nodeStream) {
506
- handleInputErrors(nodeStream);
507
- sink = fromWritable({
508
- evaluate: () => nodeStream,
509
- onError: (error) => toPlatformError(`fromWritable(fd${fd})`, toError(error), command)
510
- });
511
- }
512
- if (config.stream) {
513
- yield* forkScoped(run(config.stream, sink));
514
- }
515
- inputSinks.set(fd, sink);
516
- break;
517
- }
518
- case "output": {
519
- let stream = empty2;
520
- if (nodeStream && "read" in nodeStream) {
521
- const passThrough = new PassThrough;
522
- nodeStream.on("error", (error) => passThrough.destroy(error));
523
- nodeStream.pipe(passThrough);
524
- stream = fromReadable({
525
- evaluate: () => passThrough,
526
- onError: (error) => toPlatformError(`fromReadable(fd${fd})`, toError(error), command)
527
- });
528
- }
529
- if (config.sink) {
530
- stream = transduce(stream, config.sink);
531
- }
532
- outputStreams.set(fd, stream);
533
- break;
534
- }
535
- }
536
- }
537
- return {
538
- getInputFd: (fd) => inputSinks.get(fd) ?? drain,
539
- getOutputFd: (fd) => outputStreams.get(fd) ?? empty2
217
+ }
218
+ function provisionHerdrRunnerCredential(input) {
219
+ return gen(function* () {
220
+ const keyPair = yield* generateAgentKeyPair;
221
+ const client = yield* createSdkClient({
222
+ apiUrl: input.apiUrl,
223
+ session: { accessToken: input.accessToken },
224
+ ...input.fetch === undefined ? undefined : { fetch: input.fetch }
225
+ });
226
+ const provisioned = yield* client.provisionRunnerCredential(input.runner.id, {
227
+ publicKey: keyPair.publicKeyJwk
228
+ }).pipe(mapError(() => new CliError({ message: "herdr_runner_credential_unavailable" })));
229
+ if (provisioned.credentialHostId === undefined)
230
+ return yield* new CliError({ message: "herdr_runner_credential_unavailable" });
231
+ const credential = {
232
+ agentId: provisioned.agentIdentityId,
233
+ hostId: provisioned.credentialHostId,
234
+ privateKeyJwk: keyPair.privateKeyJwk,
235
+ publicKeyJwk: keyPair.publicKeyJwk
540
236
  };
237
+ yield* storeAgentCredential(input.apiUrl, herdrRunnerCredentialName(input.runner.id), credential, input.authPath);
238
+ return credential;
541
239
  });
542
- const setupChildStdin = (command, childProcess, config) => suspend(() => {
543
- let sink = drain;
544
- if (isNotNull(childProcess.stdin)) {
545
- handleInputErrors(childProcess.stdin);
546
- sink = fromWritable({
547
- evaluate: () => childProcess.stdin,
548
- onError: (error) => toPlatformError("fromWritable(stdin)", toError(error), command),
549
- endOnDone: config.endOnDone,
550
- encoding: config.encoding
551
- });
552
- }
553
- if (isStream(config.stream)) {
554
- return as(forkScoped(run(config.stream, sink)), sink);
555
- }
556
- return succeed5(sink);
557
- });
558
- const setupChildOutputStreams = (command, childProcess, stdoutConfig, stderrConfig) => {
559
- let stdout = childProcess.stdout ? (() => {
560
- const passThrough = new PassThrough;
561
- childProcess.stdout.on("error", (error) => passThrough.destroy(error));
562
- childProcess.stdout.pipe(passThrough);
563
- return fromReadable({
564
- evaluate: () => passThrough,
565
- onError: (error) => toPlatformError("fromReadable(stdout)", toError(error), command)
566
- });
567
- })() : empty2;
568
- let stderr = childProcess.stderr ? (() => {
569
- const passThrough = new PassThrough;
570
- childProcess.stderr.on("error", (error) => passThrough.destroy(error));
571
- childProcess.stderr.pipe(passThrough);
572
- return fromReadable({
573
- evaluate: () => passThrough,
574
- onError: (error) => toPlatformError("fromReadable(stderr)", toError(error), command)
575
- });
576
- })() : empty2;
577
- if (isSink(stdoutConfig.stream)) {
578
- stdout = transduce(stdout, stdoutConfig.stream);
579
- }
580
- if (isSink(stderrConfig.stream)) {
581
- stderr = transduce(stderr, stderrConfig.stream);
582
- }
583
- const all = merge2(stdout, stderr);
584
- return {
585
- stdout,
586
- stderr,
587
- all
588
- };
589
- };
590
- const spawn2 = (command, spawnOptions) => callback((resume) => {
591
- const deferred = makeUnsafe();
592
- const handle = NodeChildProcess.spawn(command.command, command.args, spawnOptions);
593
- handle.on("error", (error) => {
594
- resume(fail4(toPlatformError("spawn", error, command)));
595
- });
596
- handle.on("exit", (...args) => {
597
- doneUnsafe(deferred, succeed2(args));
598
- });
599
- handle.on("spawn", () => {
600
- resume(succeed5([handle, deferred]));
240
+ }
241
+ function createHerdrRunnerClient(input) {
242
+ return gen(function* () {
243
+ const credential = input.credential;
244
+ const signedFetch = createAgentAuthSignedFetch({
245
+ apiUrl: input.apiUrl,
246
+ credential,
247
+ fetch: input.fetch ?? (yield* Fetch)
601
248
  });
602
- return sync(() => {
603
- handle.kill("SIGTERM");
249
+ const runnerFetch = (requestInput, init) => {
250
+ const request = new Request(requestInput, init);
251
+ request.headers.set(RunnerHeaders.runnerId, input.runnerId);
252
+ return signedFetch(request);
253
+ };
254
+ const client = yield* createSdkClient({
255
+ apiUrl: input.apiUrl,
256
+ fetch: runnerFetch,
257
+ session: { accessToken: "runner-agent-auth" }
604
258
  });
605
- });
606
- const killProcessGroup = (command, childProcess, signal) => {
607
- if (globalThis.process.platform === "win32") {
608
- return callback((resume) => {
609
- taskkill(childProcess, (error) => {
610
- if (error) {
611
- resume(fail4(toPlatformError("kill", toError(error), command)));
612
- } else {
613
- resume(void_2);
614
- }
615
- });
616
- });
617
- }
618
- return try_({
619
- try: () => {
620
- globalThis.process.kill(-childProcess.pid, signal);
621
- },
622
- catch: (error) => toPlatformError("kill", toError(error), command)
259
+ const rpcBearer = createAgentAuthBearerToken({
260
+ credential,
261
+ audience: new URL("/v1/runners/rpc", input.apiUrl).toString(),
262
+ capabilities: ["kitman_api"]
623
263
  });
624
- };
625
- const killProcessGroupOnExit = (childProcess, signal) => {
626
- if (globalThis.process.platform === "win32") {
627
- taskkill(childProcess);
628
- return;
629
- }
630
- try {
631
- globalThis.process.kill(-childProcess.pid, signal);
632
- } catch {}
633
- };
634
- const killProcess = (command, childProcess, signal) => suspend(() => {
635
- const killed = childProcess.kill(signal);
636
- if (!killed) {
637
- const error = new globalThis.Error("Failed to kill child process");
638
- return fail4(toPlatformError("kill", error, command));
639
- }
640
- return void_2;
641
- });
642
- const awaitProcessExit = (childProcess, exitSignal, timeoutMillis) => callback((resume) => {
643
- const deadline = Date.now() + timeoutMillis;
644
- let timer;
645
- const stop = () => {
646
- clearTimeout(timer);
647
- childProcess.removeListener("exit", poll);
648
- };
649
- const poll = () => {
650
- clearTimeout(timer);
651
- if (Date.now() >= deadline || !isProcessAlive(childProcess, exitSignal)) {
652
- stop();
653
- resume(void_2);
654
- return;
655
- }
656
- timer = setTimeout(poll, processGroupPollIntervalMillis);
657
- };
658
- childProcess.on("exit", poll);
659
- poll();
660
- return sync(stop);
661
- });
662
- const terminateProcessGroup = fnUntraced(function* (command, childProcess, exitSignal, options) {
663
- const signalGroup = (signal) => killProcessGroup(command, childProcess, signal).pipe(catch_(() => killProcess(command, childProcess, signal)));
664
- yield* signalGroup(options?.killSignal ?? "SIGTERM");
665
- if (isUndefined(options?.forceKillAfter)) {
666
- yield* awaitProcessExit(childProcess, exitSignal, processGroupGraceMillis);
667
- } else {
668
- yield* awaitProcessExit(childProcess, exitSignal, toMillis(options.forceKillAfter));
669
- if (isProcessAlive(childProcess, exitSignal)) {
670
- yield* signalGroup("SIGKILL");
671
- yield* awaitProcessExit(childProcess, exitSignal, processGroupGraceMillis);
672
- }
673
- }
674
- yield* _await(exitSignal);
675
- });
676
- const getSourceStream = (handle, from) => {
677
- const fromOption = from ?? "stdout";
678
- switch (fromOption) {
679
- case "stdout":
680
- return handle.stdout;
681
- case "stderr":
682
- return handle.stderr;
683
- case "all":
684
- return handle.all;
685
- default: {
686
- const fd = parseFdName(fromOption);
687
- if (isNotUndefined(fd)) {
688
- return handle.getOutputFd(fd);
689
- }
690
- return handle.stdout;
691
- }
692
- }
693
- };
694
- const spawnCommand = fnUntraced(function* (cmd) {
695
- switch (cmd._tag) {
696
- case "StandardCommand": {
697
- const stdinConfig = resolveStdinOption(cmd.options);
698
- const stdoutConfig = resolveOutputOption(cmd.options, "stdout");
699
- const stderrConfig = resolveOutputOption(cmd.options, "stderr");
700
- const resolvedAdditionalFds = resolveAdditionalFds(cmd.options);
701
- let isReferenced = true;
702
- const cwd = yield* resolveWorkingDirectory(cmd.options);
703
- const env = resolveEnvironment(cmd.options);
704
- const stdio = buildStdioArray(stdinConfig, stdoutConfig, stderrConfig, resolvedAdditionalFds);
705
- const [childProcess, exitSignal] = yield* acquireRelease(spawn2(cmd, buildSpawnOptions(cmd.options, {
706
- cwd,
707
- env,
708
- stdio
709
- }, process.platform)), fnUntraced(function* ([childProcess, exitSignal]) {
710
- const exited = yield* isDone(exitSignal);
711
- if (exited) {
712
- const [code] = yield* _await(exitSignal);
713
- if (code !== 0 && isNotNull(code)) {
714
- yield* ignore(terminateProcessGroup(cmd, childProcess, exitSignal, cmd.options));
715
- } else if (isReferenced && process.platform !== "win32" && cmd.options.detached !== false) {
716
- yield* ignore(terminateProcessGroup(cmd, childProcess, exitSignal, cmd.options));
717
- }
718
- return;
719
- }
720
- if (!isReferenced) {
721
- return;
722
- }
723
- yield* ignore(terminateProcessGroup(cmd, childProcess, exitSignal, cmd.options));
724
- }));
725
- const pid = ProcessId(childProcess.pid);
726
- childProcess.on("exit", (code) => {
727
- if (code !== 0 && isNotNull(code)) {
728
- killProcessGroupOnExit(childProcess, cmd.options.killSignal ?? "SIGTERM");
729
- }
730
- });
731
- const reref = sync(() => {
732
- if (!isReferenced) {
733
- childProcess.ref();
734
- isReferenced = true;
735
- }
736
- });
737
- const unref = sync(() => {
738
- if (isReferenced) {
739
- childProcess.unref();
740
- isReferenced = false;
741
- }
742
- return reref;
743
- });
744
- const stdin = yield* setupChildStdin(cmd, childProcess, stdinConfig);
745
- const {
746
- all,
747
- stderr,
748
- stdout
749
- } = setupChildOutputStreams(cmd, childProcess, stdoutConfig, stderrConfig);
750
- const {
751
- getInputFd,
752
- getOutputFd
753
- } = yield* setupAdditionalFds(cmd, childProcess, resolvedAdditionalFds);
754
- const isRunning = map3(isDone(exitSignal), (done) => !done);
755
- const exitCode = flatMap2(_await(exitSignal), ([code, signal]) => {
756
- if (isNotNull(code)) {
757
- return succeed5(ExitCode(code));
758
- }
759
- const error = new globalThis.Error(`Process interrupted due to receipt of signal: '${signal}'`);
760
- return fail4(toPlatformError("exitCode", error, cmd));
761
- });
762
- const kill = (options) => terminateProcessGroup(cmd, childProcess, exitSignal, options);
763
- return makeHandle({
764
- pid,
765
- exitCode,
766
- isRunning,
767
- kill,
768
- stdin,
769
- stdout,
770
- stderr,
771
- all,
772
- getInputFd,
773
- getOutputFd,
774
- unref
775
- });
776
- }
777
- case "PipedCommand": {
778
- const {
779
- commands,
780
- pipeOptions
781
- } = flattenCommand(cmd);
782
- const [root, ...pipeline] = commands;
783
- const handles = [yield* spawnCommand(root)];
784
- for (let i = 0;i < pipeline.length; i++) {
785
- const command = pipeline[i];
786
- const options = pipeOptions[i] ?? {};
787
- const stdinConfig = resolveStdinOption(command.options);
788
- const sourceStream = unwrap(succeed5(getSourceStream(handles[handles.length - 1], options.from)));
789
- const toOption = options.to ?? "stdin";
790
- if (toOption === "stdin") {
791
- handles.push(yield* spawnCommand(make13(command.command, command.args, {
792
- ...command.options,
793
- stdin: {
794
- ...stdinConfig,
795
- stream: sourceStream
796
- }
797
- })));
798
- } else {
799
- const fd = parseFdName(toOption);
800
- if (isNotUndefined(fd)) {
801
- const fdName2 = fdName(fd);
802
- const existingFds = command.options.additionalFds ?? {};
803
- handles.push(yield* spawnCommand(make13(command.command, command.args, {
804
- ...command.options,
805
- additionalFds: {
806
- ...existingFds,
807
- [fdName2]: {
808
- type: "input",
809
- stream: sourceStream
810
- }
811
- }
812
- })));
813
- } else {
814
- handles.push(yield* spawnCommand(make13(command.command, command.args, {
815
- ...command.options,
816
- stdin: {
817
- ...stdinConfig,
818
- stream: sourceStream
819
- }
820
- })));
821
- }
822
- }
823
- }
824
- const handle = handles[handles.length - 1];
825
- const kill = (options) => forEach([...handles].reverse(), (handle) => ignore(handle.kill(options)), {
826
- discard: true
827
- });
828
- const unref = gen(function* () {
829
- const rerefs = [];
830
- for (const handle of handles) {
831
- rerefs.push(yield* handle.unref);
832
- }
833
- return forEach([...rerefs].reverse(), (reref) => reref, {
834
- discard: true
835
- });
836
- });
837
- return makeHandle({
838
- pid: handle.pid,
839
- exitCode: handle.exitCode,
840
- isRunning: handle.isRunning,
841
- kill,
842
- stdin: handles[0].stdin,
843
- stdout: handle.stdout,
844
- stderr: handle.stderr,
845
- all: handle.all,
846
- getInputFd: handle.getInputFd,
847
- getOutputFd: handle.getOutputFd,
848
- unref
849
- });
850
- }
851
- }
264
+ return { client, rpcBearer };
852
265
  });
853
- return make12(spawnCommand);
266
+ }
267
+
268
+ // apps/cli/src/kitman-version.ts
269
+ var kitmanVersion = undefined;
270
+
271
+ // apps/cli/src/runner-startup.ts
272
+ class RunnerStopped extends TaggedError("RunnerStopped") {
273
+ }
274
+ var pairFix = "Run `kitman pair` on this machine. Not signed in there? Mint a Pairing Code on the Runners page and run the command it shows.";
275
+ var credentialMissing = (runnerId) => ({
276
+ code: "credential_missing",
277
+ message: `This machine holds no credential for Runner ${runnerId}.`,
278
+ fix: pairFix
854
279
  });
855
- var layer5 = /* @__PURE__ */ effect(ChildProcessSpawner, make);
856
- var flattenCommand = (command) => {
857
- const commands = [];
858
- const pipeOptions = [];
859
- const flatten = (cmd) => {
860
- switch (cmd._tag) {
861
- case "StandardCommand": {
862
- commands.push(cmd);
863
- break;
864
- }
865
- case "PipedCommand": {
866
- flatten(cmd.left);
867
- pipeOptions.push(cmd.options);
868
- flatten(cmd.right);
869
- break;
870
- }
871
- }
872
- };
873
- flatten(command);
874
- if (commands.length === 0) {
875
- throw new Error("flattenCommand produced empty commands array");
876
- }
877
- const [first, ...rest] = commands;
878
- const nonEmptyCommands = [first, ...rest];
879
- return {
880
- commands: nonEmptyCommands,
881
- pipeOptions
882
- };
280
+ var credentialRefused = {
281
+ code: "credential_refused",
282
+ message: "Kitman refused this Runner's credential.",
283
+ fix: pairFix
883
284
  };
884
-
885
- // node_modules/.bun/effect@4.0.4/node_modules/effect/dist/Stdio.js
886
- var TypeId = "~effect/Stdio";
887
- var Stdio = /* @__PURE__ */ Service(TypeId);
888
- var make2 = (options) => ({
889
- [TypeId]: TypeId,
890
- stdinIsTerminal: succeed5(false),
891
- stdoutIsTerminal: succeed5(false),
892
- stderrIsTerminal: succeed5(false),
893
- ...options
285
+ var runnerRemoved = {
286
+ code: "runner_removed",
287
+ message: "This Runner no longer exists in Kitman.",
288
+ fix: "Run `kitman pair` to set it up again, or `kitman runner remove` to clean up this machine."
289
+ };
290
+ var sandboxInstall = {
291
+ srt: process.platform === "darwin" ? "Install what it needs with `brew install ripgrep`" : "Install what it needs with `sudo apt install bubblewrap socat ripgrep` (or your distribution\u2019s packages)",
292
+ docker: "Install and start Docker (https://docs.docker.com/engine/install/), and set KITMAN_RUNNER_DOCKER_IMAGE to an image pinned by digest"
293
+ };
294
+ var sandboxMissing = (sandbox, missing) => ({
295
+ code: "sandbox_missing",
296
+ message: `\`${sandbox}\` is not available on this host: ${missing.join("; ")}.`.slice(0, 500),
297
+ fix: `${sandboxInstall[sandbox]}, or unset KITMAN_RUNNER_SANDBOX to run agents on the host.`
298
+ });
299
+ var stopOnRefusal = (call) => mapError(call, (error) => {
300
+ if (!(error instanceof KitmanClientError) && !is(RunnerRpcError)(error))
301
+ return error;
302
+ if (error.status === 401 || error.status === 403)
303
+ return new RunnerStopped({ problem: credentialRefused });
304
+ return error.status === 404 ? new RunnerStopped({ problem: runnerRemoved }) : error;
894
305
  });
306
+ var stopWhenRefused = (replica) => mapError(replica, (error) => error.kind === "unauthorized" ? new RunnerStopped({ problem: credentialRefused }) : error);
307
+ var unreachable = (error) => error instanceof KitmanClientError && (error.status === undefined || error.status >= 500) || is(RunnerRpcError)(error) && error.status >= 500 || is(RpcClientError)(error);
308
+ var backoff = min([exponential("1 second"), spaced("1 minute")]);
895
309
 
896
- // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/NodeStdio.js
897
- var layer6 = /* @__PURE__ */ succeed4(Stdio, /* @__PURE__ */ make2({
898
- args: /* @__PURE__ */ sync(() => process.argv.slice(2)),
899
- stdinIsTerminal: /* @__PURE__ */ sync(() => process.stdin.isTTY === true),
900
- stdoutIsTerminal: /* @__PURE__ */ sync(() => process.stdout.isTTY === true),
901
- stderrIsTerminal: /* @__PURE__ */ sync(() => process.stderr.isTTY === true),
902
- stdout: (options) => fromWritable({
903
- evaluate: () => process.stdout,
904
- onError: (cause) => systemError({
905
- module: "Stdio",
906
- method: "stdout",
907
- _tag: "Unknown",
908
- cause
909
- }),
910
- endOnDone: options?.endOnDone ?? false
911
- }),
912
- stderr: (options) => fromWritable({
913
- evaluate: () => process.stderr,
914
- onError: (cause) => systemError({
915
- module: "Stdio",
916
- method: "stderr",
917
- _tag: "Unknown",
918
- cause
919
- }),
920
- endOnDone: options?.endOnDone ?? false
921
- }),
922
- stdin: /* @__PURE__ */ fromReadable({
923
- evaluate: () => process.stdin,
924
- onError: (cause) => systemError({
925
- module: "Stdio",
926
- method: "stdin",
927
- _tag: "Unknown",
928
- cause
929
- }),
930
- closeOnDone: false
931
- })
310
+ class StepTimedOut extends TaggedError("StepTimedOut") {
311
+ }
312
+ var startupStep = (step, bound, attempt) => attempt.pipe(timeoutOrElse({ duration: bound, orElse: () => fail4(new StepTimedOut) }), tapError((error2) => unreachable(error2) ? error(`Runner start: ${step} failed, trying again: ${error2.message}`) : void_2), retry({ schedule: backoff, while: unreachable }), mapError((error) => {
313
+ if (error instanceof RunnerStopped)
314
+ return error;
315
+ if (error instanceof StepTimedOut)
316
+ return new CliError({ message: `Runner start: ${step} did not finish within ${bound}` });
317
+ return new CliError({
318
+ message: `Runner start: ${step} failed: ${error instanceof Error ? error.message : String(error)}`
319
+ });
932
320
  }));
933
321
 
934
- // node_modules/.bun/@effect+platform-bun@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-bun/dist/BunStdio.js
935
- var layer7 = layer6;
936
-
937
- // node_modules/.bun/effect@4.0.4/node_modules/effect/dist/Terminal.js
938
- var TypeId2 = "~effect/Terminal";
939
- var QuitErrorTypeId = "~effect/Terminal/QuitError";
940
-
941
- class QuitError extends (/* @__PURE__ */ Error2("QuitError")({
942
- _tag: /* @__PURE__ */ tag("QuitError")
943
- })) {
944
- [QuitErrorTypeId] = QuitErrorTypeId;
945
- }
946
- var Terminal = /* @__PURE__ */ Service("effect/Terminal");
947
- var make3 = (impl) => Terminal.of({
948
- ...impl,
949
- [TypeId2]: TypeId2
322
+ // apps/cli/src/managed-install.ts
323
+ var RunnerReleaseManifest = Struct({
324
+ version: String2.check(isPattern(/^[\w.+-]+$/u)),
325
+ platforms: ArraySchema(Struct({ os: String2, architecture: String2, sha256: String2, url: String2 }))
950
326
  });
951
-
952
- // node_modules/.bun/@effect+platform-node-shared@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-node-shared/dist/NodeTerminal.js
953
- import * as readline from "readline";
954
- var make4 = /* @__PURE__ */ fnUntraced(function* (shouldQuit = defaultShouldQuit) {
955
- const stdin = process.stdin;
956
- const stdout = process.stdout;
957
- const lines = yield* make9();
958
- let inputEnded = stdin.readableEnded;
959
- let readlineActive = false;
960
- const onStdinEnd = () => {
961
- inputEnded = true;
962
- if (!readlineActive) {
963
- endUnsafe(lines);
964
- }
965
- };
966
- stdin.once("end", onStdinEnd);
967
- yield* addFinalizer2(() => sync(() => stdin.off("end", onStdinEnd)));
968
- const rlRef = yield* make10({
969
- acquire: acquireRelease(sync(() => {
970
- const rl = readline.createInterface({
971
- input: stdin,
972
- escapeCodeTimeout: 50
973
- });
974
- const onLine = (line) => offerUnsafe(lines, line);
975
- const onClose = () => {
976
- readlineActive = false;
977
- endUnsafe(lines);
978
- };
979
- readlineActive = true;
980
- readline.emitKeypressEvents(stdin, rl);
981
- rl.on("line", onLine);
982
- rl.once("close", onClose);
983
- if (stdin.isTTY) {
984
- stdin.setRawMode(true);
985
- }
986
- return {
987
- rl,
988
- onClose,
989
- onLine
990
- };
991
- }), ({
992
- rl,
993
- onClose,
994
- onLine
995
- }) => sync(() => {
996
- readlineActive = false;
997
- rl.off("line", onLine);
998
- rl.off("close", onClose);
999
- if (stdin.isTTY) {
1000
- stdin.setRawMode(false);
1001
- }
1002
- rl.close();
1003
- if (inputEnded) {
1004
- endUnsafe(lines);
1005
- }
1006
- })),
1007
- idleTimeToLive: "10 millis"
1008
- });
1009
- const columns = sync(() => stdout.columns ?? 0);
1010
- const rows = sync(() => stdout.rows ?? 0);
1011
- const readInput = gen(function* () {
1012
- const queue = yield* make9();
1013
- const handleKeypress = (s, k) => {
1014
- const userInput = {
1015
- input: fromUndefinedOr(s),
1016
- key: {
1017
- name: k.name ?? "",
1018
- ctrl: !!k.ctrl,
1019
- meta: !!k.meta,
1020
- shift: !!k.shift
1021
- }
1022
- };
1023
- offerUnsafe(queue, userInput);
1024
- if (shouldQuit(userInput)) {
1025
- endUnsafe(queue);
1026
- }
1027
- };
1028
- const keepAlive = setInterval(() => {}, 2147483647);
1029
- const handleEnd = () => {
1030
- clearInterval(keepAlive);
1031
- endUnsafe(queue);
1032
- };
1033
- yield* addFinalizer2(() => sync(() => {
1034
- clearInterval(keepAlive);
1035
- stdin.off("keypress", handleKeypress);
1036
- stdin.off("end", handleEnd);
1037
- }));
1038
- stdin.on("keypress", handleKeypress);
1039
- if (inputEnded) {
1040
- handleEnd();
1041
- } else {
1042
- yield* get2(rlRef);
1043
- stdin.once("end", handleEnd);
1044
- }
1045
- return queue;
1046
- });
1047
- const readLine = suspend(() => poll(lines).pipe(flatMap2(match({
1048
- onNone: () => scoped(andThen(get2(rlRef), take(lines))),
1049
- onSome: succeed5
1050
- })), mapError(() => new QuitError({}))));
1051
- const display = (prompt2) => uninterruptible(callback((resume) => {
1052
- stdout.write(prompt2, (err) => isNullish(err) ? resume(void_2) : resume(fail4(badArgument({
1053
- module: "Terminal",
1054
- method: "display",
1055
- description: "Failed to write prompt to stdout",
1056
- cause: err
1057
- }))));
327
+ var versionsDirectory = (platform) => gen(function* () {
328
+ const path = yield* Path;
329
+ const home = yield* String3("HOME");
330
+ const xdgData = getOrUndefined(yield* option2(String3("XDG_DATA_HOME")));
331
+ const data = platform === "darwin" ? path.join(home, "Library", "Application Support") : xdgData ?? path.join(home, ".local", "share");
332
+ return path.join(data, "kitman", "versions");
333
+ });
334
+ var managedKitmanPath = gen(function* () {
335
+ const path = yield* Path;
336
+ return path.join(yield* String3("HOME"), ".local", "bin", "kitman");
337
+ });
338
+ var installedKitmanVersion = gen(function* () {
339
+ const fs = yield* FileSystem;
340
+ const path = yield* Path;
341
+ return yield* fs.readLink(yield* managedKitmanPath).pipe(map3((target) => path.basename(path.dirname(target))), orElseSucceed(() => {
342
+ return;
1058
343
  }));
1059
- return make3({
1060
- columns,
1061
- rows,
1062
- readInput,
1063
- readLine,
1064
- display
344
+ });
345
+ var runningManagedInstall = (platform) => gen(function* () {
346
+ if (process.argv[1]?.startsWith("/$bunfs/") !== true)
347
+ return false;
348
+ const fs = yield* FileSystem;
349
+ const versions = yield* versionsDirectory(platform);
350
+ const executable = yield* fs.realPath(process.execPath).pipe(orElseSucceed(() => process.execPath));
351
+ return executable.startsWith(`${versions}/`);
352
+ });
353
+ var downloadManagedKitman = (apiUrl, platform) => gen(function* () {
354
+ const fs = yield* FileSystem;
355
+ const path = yield* Path;
356
+ const fetch = yield* Fetch;
357
+ const get = (url) => tryPromise({
358
+ try: () => fetch(url),
359
+ catch: (error) => new CliError({ message: `Fetching ${url} failed: ${String(error)}` })
1065
360
  });
361
+ const answer = yield* get(new URL("/v1/runner-release", apiUrl).toString());
362
+ if (answer.status === 404)
363
+ return;
364
+ if (!answer.ok)
365
+ return yield* new CliError({ message: `${apiUrl} answered ${answer.status} for its kitman release` });
366
+ const manifest = yield* tryPromise(() => answer.json()).pipe(flatMap2(decodeUnknownEffect(RunnerReleaseManifest)), mapError(() => new CliError({ message: `${apiUrl} served a kitman release this kitman cannot read` })));
367
+ const os = platform === "darwin" ? "darwin" : "linux";
368
+ const architecture = process.arch === "x64" ? "amd64" : process.arch;
369
+ const release = manifest.platforms.find((entry) => entry.os === os && entry.architecture === architecture && entry.sha256 !== "");
370
+ if (release === undefined)
371
+ return yield* new CliError({ message: `${apiUrl} serves no kitman for ${os}/${architecture}` });
372
+ const versions = yield* versionsDirectory(platform);
373
+ const kitman = path.join(versions, manifest.version, "kitman");
374
+ const installed = !(yield* fs.exists(kitman));
375
+ if (installed) {
376
+ const binary = yield* get(release.url).pipe(filterOrFail((downloaded) => downloaded.ok, (downloaded) => new CliError({ message: `Downloading kitman ${manifest.version} answered ${downloaded.status}` })), flatMap2((downloaded) => tryPromise({
377
+ try: () => downloaded.arrayBuffer(),
378
+ catch: (error) => new CliError({ message: `Downloading kitman ${manifest.version} failed: ${String(error)}` })
379
+ })), map3((bytes) => new Uint8Array(bytes)));
380
+ if (digestSha256(binary, "hex") !== release.sha256)
381
+ return yield* new CliError({
382
+ message: `The downloaded kitman ${manifest.version} for ${os}/${architecture} does not match its sha256; nothing was installed`
383
+ });
384
+ const download = path.join(versions, `.kitman-${manifest.version}.download`);
385
+ yield* fs.makeDirectory(path.dirname(kitman), { recursive: true });
386
+ yield* fs.writeFile(download, binary, { mode: 493 });
387
+ yield* fs.rename(download, kitman);
388
+ }
389
+ return { version: manifest.version, kitman, installed };
390
+ });
391
+ var linkManagedKitman = (kitman) => gen(function* () {
392
+ const fs = yield* FileSystem;
393
+ const path = yield* Path;
394
+ const link = yield* managedKitmanPath;
395
+ const nextLink = `${link}.next`;
396
+ yield* fs.makeDirectory(path.dirname(link), { recursive: true });
397
+ yield* ignore(fs.remove(nextLink));
398
+ yield* fs.symlink(kitman, nextLink);
399
+ yield* fs.rename(nextLink, link);
400
+ });
401
+ var installManagedKitman = (apiUrl, platform) => gen(function* () {
402
+ const downloaded = yield* downloadManagedKitman(apiUrl, platform);
403
+ if (downloaded === undefined)
404
+ return;
405
+ yield* linkManagedKitman(downloaded.kitman);
406
+ return { version: downloaded.version, installed: downloaded.installed };
1066
407
  });
1067
- var layer8 = /* @__PURE__ */ effect(Terminal, /* @__PURE__ */ make4(defaultShouldQuit));
1068
- function defaultShouldQuit(input) {
1069
- const key = input.key;
1070
- return key.name === "escape" || key.ctrl && (key.name === "c" || key.name === "d");
1071
- }
1072
-
1073
- // node_modules/.bun/@effect+platform-bun@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-bun/dist/BunTerminal.js
1074
- var layer9 = layer8;
1075
408
 
1076
- // node_modules/.bun/@effect+platform-bun@4.0.4+b8cbb99ebc524863/node_modules/@effect/platform-bun/dist/BunServices.js
1077
- var layer10 = /* @__PURE__ */ layer5.pipe(/* @__PURE__ */ provideMerge(/* @__PURE__ */ mergeAll(layer2, layer, layer3, layer7, layer9)));
1078
- // apps/cli/src/herdr-runner-credential.ts
1079
- function herdrRunnerCredentialName(runnerId) {
1080
- return `herdr-runner:${runnerId}`;
409
+ // apps/cli/src/runner-update.ts
410
+ class RunnerUpdated extends TaggedError("RunnerUpdated") {
411
+ get message() {
412
+ return `Updated to kitman ${this.version}; exiting so the Runner service restarts on it`;
413
+ }
1081
414
  }
1082
- function ensureHerdrRunnerCredential(input) {
1083
- return gen(function* () {
1084
- const hostId = input.runner.credentialHostId;
1085
- if (hostId === undefined)
1086
- return yield* new CliError({ message: "herdr_runner_credential_unavailable" });
1087
- const credentialName = herdrRunnerCredentialName(input.runner.id);
1088
- const auth = yield* readAuthStore(input.authPath);
1089
- const existing = agentCredentialForApi(auth, input.apiUrl, credentialName);
1090
- const credential = existing ?? (yield* newCredential(input.runner.agentIdentityId, hostId));
1091
- if (credential.agentId !== input.runner.agentIdentityId || credential.hostId !== hostId)
1092
- return yield* new CliError({ message: "herdr_runner_credential_mismatch" });
1093
- if (existing === undefined)
1094
- yield* storeAgentCredential(input.apiUrl, credentialName, credential, input.authPath);
1095
- const client = yield* createSdkClient({
1096
- apiUrl: input.apiUrl,
1097
- session: { accessToken: input.accessToken },
1098
- ...input.fetch === undefined ? undefined : { fetch: input.fetch }
1099
- });
1100
- yield* client.provisionRunnerCredential(input.runner.id, {
1101
- publicKey: credential.publicKeyJwk
1102
- }).pipe(mapError(() => new CliError({ message: "herdr_runner_credential_unavailable" })));
1103
- return credential;
415
+
416
+ class UpdateStepFailed extends TaggedError("UpdateStepFailed") {
417
+ }
418
+ var step = (name) => (effect) => mapError(effect, (error) => new UpdateStepFailed({ step: name, reason: error.message }));
419
+ var runnerUpdater = (input) => {
420
+ let claiming = true;
421
+ let updating = false;
422
+ let problems = [];
423
+ const failed = new Set;
424
+ const attempt = gen(function* () {
425
+ const downloaded = yield* downloadManagedKitman(input.apiUrl, input.platform).pipe(filterOrFail((found) => found !== undefined, () => new UpdateStepFailed({ step: "download", reason: `${input.apiUrl} serves no kitman` })), step("download"));
426
+ const reported = (yield* runCommand2(downloaded.kitman, ["--version"], { timeout: "30 seconds" }).pipe(step("start"))).trim();
427
+ if (reported !== downloaded.version)
428
+ return yield* new UpdateStepFailed({ step: "start", reason: `it reported version ${reported || "none"}` });
429
+ claiming = false;
430
+ while ((yield* input.inFlight) > 0)
431
+ yield* sleep("1 second");
432
+ yield* linkManagedKitman(downloaded.kitman).pipe(step("switch"));
433
+ return yield* new RunnerUpdated({ version: downloaded.version });
1104
434
  });
1105
- }
1106
- function createHerdrRunnerClient(input) {
1107
- return gen(function* () {
1108
- const credential = input.credential;
1109
- const signedFetch = createAgentAuthSignedFetch({
1110
- apiUrl: input.apiUrl,
1111
- credential,
1112
- ...input.fetch === undefined ? undefined : { fetch: input.fetch }
1113
- });
1114
- const runnerFetch = (requestInput, init) => {
1115
- const request = new Request(requestInput, init);
1116
- request.headers.set(RunnerHeaders.runnerId, input.runnerId);
1117
- return signedFetch(request);
1118
- };
1119
- const client = yield* createSdkClient({
1120
- apiUrl: input.apiUrl,
1121
- fetch: runnerFetch,
1122
- session: { accessToken: "runner-agent-auth" }
1123
- });
1124
- const rpcBearer = createAgentAuthBearerToken({
1125
- credential,
1126
- audience: new URL("/v1/runners/rpc", input.apiUrl).toString(),
1127
- capabilities: ["kitman_api"]
1128
- });
1129
- return { client, rpcBearer };
435
+ const update = (version) => suspend(() => {
436
+ if (updating || failed.has(version))
437
+ return void_2;
438
+ updating = true;
439
+ return attempt.pipe(catchTag("UpdateStepFailed", ({ step: failedStep, reason }) => sync(() => {
440
+ failed.add(version);
441
+ claiming = true;
442
+ problems = [
443
+ {
444
+ code: "update_failed",
445
+ message: `Updating kitman to ${version} failed at the ${failedStep} step: ${reason}`.slice(0, 500),
446
+ fix: `It keeps running its current version. Check that this machine reaches ${input.apiUrl}, then run \`kitman runner restart ${input.runnerId}\` here to try again.`.slice(0, 500)
447
+ }
448
+ ];
449
+ })), ensuring(sync(() => updating = false)));
1130
450
  });
1131
- }
1132
- function newCredential(agentId, hostId) {
1133
- return generateAgentKeyPair.pipe(map3((keyPair) => ({
1134
- agentId,
1135
- hostId,
1136
- privateKeyJwk: keyPair.privateKeyJwk,
1137
- publicKeyJwk: keyPair.publicKeyJwk
1138
- })));
1139
- }
451
+ return {
452
+ claiming: () => claiming,
453
+ problems: () => problems,
454
+ update
455
+ };
456
+ };
1140
457
 
1141
458
  // apps/cli/src/harness-sync.ts
1142
459
  function buildHarnessSyncPlan(input) {
@@ -1145,11 +462,11 @@ function buildHarnessSyncPlan(input) {
1145
462
  version: 1,
1146
463
  apiUrl: input.apiUrl,
1147
464
  workspaceId: input.workspaceId,
1148
- ...input.projectId === undefined ? undefined : { projectId: input.projectId },
465
+ ...input.teamId === undefined ? undefined : { teamId: input.teamId },
1149
466
  harnesses: harnesses.map((harness) => ({ harness, configScope: "user", managedBy: "kitman" })),
1150
467
  sources: [
1151
468
  { kind: "workspace", id: input.workspaceId },
1152
- ...input.projectId === undefined ? [] : [{ kind: "project", id: input.projectId }],
469
+ ...input.teamId === undefined ? [] : [{ kind: "team", id: input.teamId }],
1153
470
  { kind: "user-preferences", id: "default" }
1154
471
  ],
1155
472
  materialization: (input.materializationPlans ?? []).map(renderMaterializationPlan),
@@ -1232,7 +549,7 @@ function hashIdentity(value) {
1232
549
 
1233
550
  // apps/cli/src/assets.ts
1234
551
  function send(request, failure) {
1235
- return execute(request).pipe(mapError((error) => failure(error.cause ?? error)), provide3(layer4));
552
+ return execute(request).pipe(mapError((error) => failure(error.cause ?? error)), provide3(layer2));
1236
553
  }
1237
554
  function putPresignedAsset(upload, bytes) {
1238
555
  return gen(function* () {
@@ -1245,6 +562,30 @@ function putPresignedAsset(upload, bytes) {
1245
562
  return yield* new CliError({ message: `Asset upload failed with ${response.status}` });
1246
563
  });
1247
564
  }
565
+ var localFileName = (name, fallback) => {
566
+ const last = name.split(/[\\/]/u).at(-1) ?? "";
567
+ return last === "" || last === "." || last === ".." ? fallback : last;
568
+ };
569
+ function downloadVerified(url, path, sha256) {
570
+ return gen(function* () {
571
+ const fs = yield* FileSystem;
572
+ const failed = (cause) => new CliError({ message: `Download failed: ${String(cause)}` });
573
+ const response = yield* send(get2(url), failed);
574
+ if (response.status < 200 || response.status >= 300)
575
+ return yield* new CliError({ message: `Download failed with ${response.status}` });
576
+ const partial = `${path}.partial`;
577
+ const hash = sha256Hasher();
578
+ yield* response.stream.pipe(tap2((chunk) => sync(() => hash.update(chunk))), mapError2((error) => failed(error.message)), run(fs.sink(partial)), onError(() => ignore(fs.remove(partial))));
579
+ const actual = hash.digest("hex");
580
+ if (actual !== sha256) {
581
+ yield* fs.remove(partial);
582
+ return yield* new CliError({
583
+ message: `The download's SHA-256 is ${actual}, not the ${sha256} Kitman recorded; nothing was kept`
584
+ });
585
+ }
586
+ yield* fs.rename(partial, path);
587
+ });
588
+ }
1248
589
 
1249
590
  // apps/cli/src/output.ts
1250
591
  function printJson(value) {
@@ -1382,6 +723,162 @@ function declaredChecks(manifest, rootFiles) {
1382
723
  // apps/cli/src/init.ts
1383
724
  import { hostname } from "os";
1384
725
 
726
+ // apps/cli/src/machine-runners.ts
727
+ var serviceFilePattern = (platform) => platform === "darwin" ? /^dev\.kitman\.kitman-runner-(.+)\.plist$/u : /^kitman-runner-(.+)\.service$/u;
728
+ var unescapeXml = (text) => text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
729
+ var serviceCommand = (platform, definition) => {
730
+ if (platform === "darwin") {
731
+ const programArguments = /<key>ProgramArguments<\/key>\s*<array>(.*?)<\/array>/su.exec(definition)?.[1] ?? "";
732
+ return Array.from(programArguments.matchAll(/<string>(.*?)<\/string>/gsu), ([, word = ""]) => unescapeXml(word));
733
+ }
734
+ const execStart = /^ExecStart=(.*)$/mu.exec(definition)?.[1] ?? "";
735
+ return Array.from(execStart.matchAll(/"([^"]*)"|(\S+)/gu), ([, quoted, word]) => quoted ?? word ?? "");
736
+ };
737
+ var machineRunnerServices = gen(function* () {
738
+ const fs = yield* FileSystem;
739
+ const path = yield* Path;
740
+ const { platform } = yield* ServiceManager;
741
+ const directory = path.dirname(yield* runnerServiceFile(platform, runnerServiceName("")));
742
+ const pattern = serviceFilePattern(platform);
743
+ const files = yield* fs.readDirectory(directory).pipe(orElseSucceed(() => []));
744
+ return yield* forEach(files.flatMap((file) => {
745
+ const runnerId = pattern.exec(file)?.[1];
746
+ return runnerId === undefined ? [] : [{ runnerId, file: path.join(directory, file) }];
747
+ }), ({ runnerId, file }) => map3(fs.readFileString(file), (definition) => ({
748
+ runnerId,
749
+ command: serviceCommand(platform, definition)
750
+ })));
751
+ });
752
+ var serviceApiUrl = (service, fallback) => {
753
+ const index = service.command.indexOf("--api-url");
754
+ return normalizeApiUrl((index < 0 ? undefined : service.command[index + 1]) ?? fallback);
755
+ };
756
+ var ownersRunners = (apiUrl) => gen(function* () {
757
+ const session = yield* sessionForApiWithRefresh(apiUrl);
758
+ if (session === undefined)
759
+ return;
760
+ const client = yield* clientFor(apiUrl, session);
761
+ const workspaces = (yield* client.listWorkspaces()).items;
762
+ const lists = yield* forEach(workspaces, (workspace) => map3(client.listRunners(workspace.id), (list) => list.items));
763
+ return new Map(lists.flat().map((runner) => [runner.id, runner]));
764
+ }).pipe(orElseSucceed(() => {
765
+ return;
766
+ }));
767
+ var runnerAsItself = (apiUrl, runnerId) => gen(function* () {
768
+ const credential = yield* storedHerdrRunnerCredential(apiUrl, runnerId);
769
+ if (credential === undefined)
770
+ return;
771
+ const { client } = yield* createHerdrRunnerClient({ apiUrl, runnerId, credential });
772
+ return yield* client.getRunner(runnerId).pipe(catchIf((error) => error instanceof KitmanClientError && [401, 403, 404].includes(error.status ?? 0), () => succeed5("gone")));
773
+ }).pipe(orElseSucceed(() => {
774
+ return;
775
+ }));
776
+ var removeAsItself = (apiUrl, runnerId) => gen(function* () {
777
+ const credential = yield* storedHerdrRunnerCredential(apiUrl, runnerId);
778
+ if (credential === undefined)
779
+ return;
780
+ const { client } = yield* createHerdrRunnerClient({ apiUrl, runnerId, credential });
781
+ return yield* client.removeRunner(runnerId).pipe(as("removed"), catchIf((error) => error.status === 404, () => succeed5("removed")), catchIf((error) => error.status === 401 || error.status === 403, () => succeed5("refused")));
782
+ });
783
+ var adoptRunnerServices = (apiUrl) => gen(function* () {
784
+ const fs = yield* FileSystem;
785
+ const services = yield* machineRunnerServices;
786
+ const managed = yield* managedKitmanPath;
787
+ const installed = yield* fs.exists(managed);
788
+ const known = new Map;
789
+ for (const service of services) {
790
+ const url = serviceApiUrl(service, apiUrl);
791
+ if (!known.has(url))
792
+ known.set(url, yield* ownersRunners(url));
793
+ }
794
+ const everyListRead = Array.from(known.values()).every((runners) => runners !== undefined);
795
+ for (const service of services) {
796
+ const url = serviceApiUrl(service, apiUrl);
797
+ const runners = known.get(url);
798
+ const name = runnerServiceName(service.runnerId);
799
+ if (everyListRead && runners?.has(service.runnerId) === false) {
800
+ yield* removeRunnerService(name);
801
+ yield* removeAgentCredential(url, `herdr-runner:${service.runnerId}`);
802
+ yield* log(`Runner service: removed ${name} (Kitman no longer has Runner ${service.runnerId})`);
803
+ } else if (installed && service.command[0] !== managed && (yield* storedHerdrRunnerCredential(url, service.runnerId)) !== undefined) {
804
+ yield* runnerService(yield* managedRunnerService(service.runnerId, url));
805
+ yield* log(`Runner service: moved ${name} onto ${managed}`);
806
+ }
807
+ }
808
+ return known.get(normalizeApiUrl(apiUrl)) ?? (yield* ownersRunners(apiUrl));
809
+ });
810
+ var stateText = (state) => {
811
+ if (state.kind === "needs_attention")
812
+ return "needs attention";
813
+ return state.kind === "offline" && state.since !== undefined ? `offline since ${state.since}` : state.kind;
814
+ };
815
+ var chosenService = (services, runnerId) => {
816
+ if (runnerId !== undefined)
817
+ return succeed5(services.find((service) => service.runnerId === runnerId));
818
+ const [only, ...others] = services;
819
+ if (only === undefined)
820
+ return fail4(new CliError({ message: "No Runner service runs on this machine" }));
821
+ if (others.length > 0)
822
+ return fail4(new CliError({
823
+ message: `This machine runs several Runners; choose one with ${services.map((service) => `--runner ${service.runnerId}`).join(" or ")}`
824
+ }));
825
+ return succeed5(only);
826
+ };
827
+ var serviceFor = (runnerId) => gen(function* () {
828
+ const service = yield* chosenService(yield* machineRunnerServices, runnerId);
829
+ if (service === undefined)
830
+ return yield* new CliError({ message: `No service for Runner ${runnerId} runs on this machine` });
831
+ return service;
832
+ });
833
+ var machineRunnerCommand = (action, apiUrl, options) => gen(function* () {
834
+ if (action === "logs")
835
+ return yield* showRunnerServiceLog(runnerServiceName((yield* serviceFor(options.runnerId)).runnerId), options.follow);
836
+ if (action === "restart") {
837
+ const { runnerId } = yield* serviceFor(options.runnerId);
838
+ yield* restartRunnerService(runnerServiceName(runnerId));
839
+ return yield* log(`Restarted Runner ${runnerId}`);
840
+ }
841
+ if (action === "remove") {
842
+ const service = yield* chosenService(yield* machineRunnerServices, options.runnerId);
843
+ const runnerId = options.runnerId ?? service?.runnerId;
844
+ if (runnerId === undefined)
845
+ return yield* new CliError({ message: "runner remove requires --runner <runner-id>" });
846
+ const url = service === undefined ? normalizeApiUrl(apiUrl) : serviceApiUrl(service, apiUrl);
847
+ const session = yield* sessionForApiWithRefresh(url);
848
+ const removal = session === undefined ? yield* removeAsItself(url, runnerId) : yield* (yield* clientFor(url, session)).removeRunner(runnerId).pipe(as("removed"), catchIf((error) => error instanceof KitmanClientError && error.status === 404, () => succeed5("removed")));
849
+ if (removal === undefined)
850
+ return yield* new CliError({
851
+ message: `This machine holds no credential for Runner ${runnerId}, and nobody is signed in to ${url} here to remove it. Remove it on the Runners page, or sign in with \`kitman login --api-url ${url}\`, then run this again.`
852
+ });
853
+ if (service !== undefined)
854
+ yield* removeRunnerService(runnerServiceName(runnerId));
855
+ yield* removeAgentCredential(url, `herdr-runner:${runnerId}`);
856
+ if (removal === "refused")
857
+ return yield* log(`Removed Runner ${runnerId} from this machine. Kitman refused this machine's credential for it, so it may still exist: if the Runners page lists it, remove it there.`);
858
+ return yield* log(`Removed Runner ${runnerId} from Kitman and this machine`);
859
+ }
860
+ const runners = yield* adoptRunnerServices(apiUrl);
861
+ const services = yield* machineRunnerServices;
862
+ if (services.length === 0)
863
+ return yield* log("No Runner service runs on this machine. Pair one with the command the Runners page shows.");
864
+ const version = yield* installedKitmanVersion;
865
+ for (const service of services) {
866
+ const { runnerId } = service;
867
+ const shown = runners?.get(runnerId) ?? (yield* runnerAsItself(serviceApiUrl(service, apiUrl), runnerId));
868
+ const runner = shown === "gone" ? undefined : shown;
869
+ const problems = runner?.state.kind === "needs_attention" ? runner.state.problems : [];
870
+ const state = shown === "gone" ? "Kitman refuses its credential; run `kitman pair` here, or `kitman runner remove` to clean up" : runner === undefined ? "unknown; Kitman could not be asked" : stateText(runner.state);
871
+ yield* log([
872
+ `${runner?.name ?? "Runner"} (${runnerId})`,
873
+ ` state: ${state}`,
874
+ ...problems.flatMap((problem) => [` ${problem.code}: ${problem.message}`, ` fix: ${problem.fix}`]),
875
+ ` kitman: ${version ?? "not installed"}`,
876
+ ` service: ${(yield* runnerServiceState(runnerServiceName(runnerId))) ?? "unsupported"}`
877
+ ].join(`
878
+ `));
879
+ }
880
+ });
881
+
1385
882
  // apps/cli/src/repo-binding-setup.ts
1386
883
  var Git = Reference("kitman/cli/Git", {
1387
884
  defaultValue: () => (args) => runCommand2("git", args).pipe(map3((stdout) => stdout.trim()))
@@ -1494,7 +991,17 @@ function stripPathSuffix(value) {
1494
991
 
1495
992
  // apps/cli/src/init.ts
1496
993
  var ServiceManager = Reference("kitman/cli/ServiceManager", {
1497
- defaultValue: () => ({ platform: process.platform, run: (command, args) => runCommand2(command, args) })
994
+ defaultValue: () => ({
995
+ platform: process.platform,
996
+ run: (command, args) => runCommand2(command, args),
997
+ attach: (command, args) => gen(function* () {
998
+ const spawner = yield* ChildProcessSpawner;
999
+ const handle = yield* spawner.spawn(make8(command, args, { stdin: "inherit", stdout: "inherit", stderr: "inherit" }));
1000
+ const exitCode = yield* handle.exitCode;
1001
+ if (exitCode !== 0)
1002
+ return yield* new CommandFailed2({ command, reason: `exit code ${exitCode}` });
1003
+ }).pipe(scoped)
1004
+ })
1498
1005
  });
1499
1006
  var Login = Reference("kitman/cli/Login", {
1500
1007
  defaultValue: () => browserSession
@@ -1536,52 +1043,103 @@ function initCommand(args) {
1536
1043
  return yield* new CliError({ message: "No Workspace to use" });
1537
1044
  yield* writeUserConfig({ ...user, apiUrl, activeWorkspaceId: workspace.id });
1538
1045
  yield* step("Workspace", current === undefined ? "set_up" : "already", workspace.name);
1539
- const project = yield* projectFor(client, workspace.id, remoteUrl, remote.name, local?.activeProjectId);
1540
- yield* writeUserConfig({ ...local, apiUrl, activeWorkspaceId: workspace.id, activeProjectId: project.id }, yield* localConfigPath);
1541
- yield* step("Project", project.outcome, project.name);
1542
- const bindings = (yield* client.listRepoBindings(project.id)).items;
1046
+ yield* writeUserConfig({ ...local, apiUrl, activeWorkspaceId: workspace.id }, yield* localConfigPath);
1047
+ const bindings = (yield* client.listRepoBindings(workspace.id)).items;
1543
1048
  const bound = bindings.find((binding) => matchesRemote(binding, remoteUrl));
1544
- const binding = bound ?? (yield* client.createRepoBinding(project.id, yield* repoBindingInputFromCurrentRepo));
1545
- yield* step("Repository", bound === undefined ? project.outcome === "already" ? "repaired" : "set_up" : "already", `${remote.owner}/${remote.name}`);
1049
+ const binding = bound ?? (yield* client.createRepoBinding(workspace.id, yield* repoBindingInputFromCurrentRepo));
1050
+ yield* step("Repository", bound === undefined ? current === undefined ? "set_up" : "repaired" : "already", `${remote.owner}/${remote.name}`);
1546
1051
  yield* runnerStep(client, { apiUrl, session, workspaceId: workspace.id, userId: me.userId }, step);
1547
1052
  const syncPath = yield* userHarnessSyncPath;
1548
1053
  const before = yield* readText(syncPath);
1549
1054
  yield* harnessSync(["sync"]);
1550
1055
  const after = yield* readText(syncPath);
1551
1056
  yield* step("Harness", before === undefined ? "set_up" : before === after ? "already" : "repaired", "synced");
1552
- yield* verificationStep(client, project.id, binding, confirm, step);
1057
+ yield* verificationStep(client, workspace.id, binding, confirm, step);
1553
1058
  const changed = steps.filter((entry) => entry.outcome === "set_up" || entry.outcome === "repaired");
1554
1059
  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>".`);
1555
1060
  });
1556
1061
  }
1557
1062
  function pairCommand(args) {
1558
1063
  return gen(function* () {
1559
- const [code, ...rest] = args;
1560
- if (code === undefined || code.startsWith("-"))
1561
- return yield* new CliError({ message: "Usage: kitman pair <pairing code> [--api-url <url>]" });
1064
+ const code = args[0]?.startsWith("-") === false ? args[0] : undefined;
1065
+ const { credentialFile, rest } = credentialFileFlag(code === undefined ? args : args.slice(1));
1562
1066
  const flags = parseInitFlags(rest);
1563
1067
  const apiUrl = yield* resolveApiUrl(flags.apiUrl);
1564
1068
  const step = (name, outcome, detail) => log(`${name}: ${outcomeLabel[outcome]} (${detail})`);
1069
+ if (code === undefined) {
1070
+ if (credentialFile !== undefined)
1071
+ return yield* new CliError({ message: "--credential-file needs a Pairing Code minted for a Runner" });
1072
+ return yield* pairAsOwner(apiUrl, yield* storedPairing(apiUrl), "already", step);
1073
+ }
1074
+ const keys = yield* generateAgentKeyPair;
1565
1075
  const anonymous = new KitmanEffectClient({ baseUrl: apiUrl, fetch: yield* Fetch });
1566
- const credentials = yield* anonymous.redeemPairingCode({ code });
1567
- const session = toStoredSession({
1568
- access_token: credentials.accessToken,
1569
- ...credentials.refreshToken === undefined ? undefined : { refresh_token: credentials.refreshToken },
1570
- ...credentials.expiresIn === undefined ? undefined : { expires_in: credentials.expiresIn }
1076
+ const redeemed = yield* anonymous.redeemPairingCode({
1077
+ code,
1078
+ publicKey: keys.publicKeyJwk
1571
1079
  });
1572
- yield* storeSession(apiUrl, session);
1573
- yield* writeUserConfig({ ...yield* readUserConfig(), apiUrl, activeWorkspaceId: credentials.workspaceId });
1574
- yield* step("Login", "set_up", apiUrl);
1575
- const client = yield* clientFor(apiUrl, session);
1576
- yield* runnerStep(client, { apiUrl, session, workspaceId: credentials.workspaceId, userId: credentials.userId }, step);
1577
- const detected = yield* detectHarnesses2;
1578
- if (detected.harnesses.length === 0)
1579
- return yield* step("Harness", "skipped", `none logged in: ${detected.unavailable.map((harness) => harness.missing.join(", ")).join("; ")}`);
1580
- const harnesses = detected.harnesses.map((harness) => harness === "claude" ? "claude-code" : harness);
1581
- yield* harnessSync(["sync", ...harnesses]);
1582
- yield* step("Harness", "set_up", `${harnesses.join(", ")} synced`);
1080
+ if ("runnerId" in redeemed) {
1081
+ const credential = { agentId: redeemed.agentIdentityId, hostId: redeemed.credentialHostId, ...keys };
1082
+ if (credentialFile !== undefined) {
1083
+ yield* writeRunnerCredentialFile(credentialFile, { apiUrl, runnerId: redeemed.runnerId, ...credential });
1084
+ return yield* step("Runner credential", "set_up", `${credentialFile}; start each replica with kitman runner start --credential-file ${credentialFile}`);
1085
+ }
1086
+ yield* storeAgentCredential(apiUrl, herdrRunnerCredentialName(redeemed.runnerId), credential);
1087
+ yield* writeUserConfig({ ...yield* readUserConfig(), apiUrl });
1088
+ yield* step("Runner", "set_up", redeemed.runnerId);
1089
+ return yield* runnerServiceStep(apiUrl, redeemed.runnerId, { fresh: true, keyed: true }, step);
1090
+ }
1091
+ const pairing = yield* storeRedeemedSession(apiUrl, redeemed);
1092
+ if (credentialFile !== undefined)
1093
+ return yield* new CliError({
1094
+ message: "This Pairing Code signed you in here and pairs no Runner, so there is no Runner credential to write. Run kitman pair to pair this machine as you, or mint a code with Pair a shared Runner."
1095
+ });
1096
+ yield* pairAsOwner(apiUrl, pairing, "set_up", step);
1583
1097
  });
1584
1098
  }
1099
+ var pairAsOwner = (apiUrl, pairing, login, step) => gen(function* () {
1100
+ yield* step("Login", login, apiUrl);
1101
+ yield* runnerStep(yield* clientFor(apiUrl, pairing.session), { apiUrl, ...pairing }, step);
1102
+ yield* adoptRunnerServices(apiUrl);
1103
+ const detected = yield* detectHarnesses2;
1104
+ if (detected.harnesses.length === 0)
1105
+ return yield* step("Harness", "skipped", `none logged in: ${detected.unavailable.map((harness) => harness.missing.join(", ")).join("; ")}`);
1106
+ const harnesses = detected.harnesses.map((harness) => harness === "claude" ? "claude-code" : harness);
1107
+ yield* harnessSync(["sync", ...harnesses]);
1108
+ yield* step("Harness", "set_up", `${harnesses.join(", ")} synced`);
1109
+ });
1110
+ var credentialFileFlag = (args) => {
1111
+ const at = args.indexOf("--credential-file");
1112
+ if (at === -1)
1113
+ return { credentialFile: undefined, rest: args };
1114
+ const credentialFile = args[at + 1];
1115
+ if (!credentialFile)
1116
+ throw new Error("--credential-file requires a path");
1117
+ return { credentialFile, rest: [...args.slice(0, at), ...args.slice(at + 2)] };
1118
+ };
1119
+ var storeRedeemedSession = (apiUrl, credentials) => gen(function* () {
1120
+ const session = toStoredSession({
1121
+ access_token: credentials.accessToken,
1122
+ ...credentials.refreshToken === undefined ? undefined : { refresh_token: credentials.refreshToken },
1123
+ ...credentials.expiresIn === undefined ? undefined : { expires_in: credentials.expiresIn }
1124
+ });
1125
+ yield* storeSession(apiUrl, session);
1126
+ yield* writeUserConfig({ ...yield* readUserConfig(), apiUrl, activeWorkspaceId: credentials.workspaceId });
1127
+ return { session, workspaceId: credentials.workspaceId, userId: credentials.userId };
1128
+ });
1129
+ var storedPairing = (apiUrl) => gen(function* () {
1130
+ const session = yield* sessionForApiWithRefresh(apiUrl);
1131
+ if (session === undefined)
1132
+ return yield* new CliError({
1133
+ message: `kitman pair needs a Pairing Code: nobody is signed in to ${apiUrl} here. Copy the pairing command from the Runners page in Kitman; it carries a new code.`
1134
+ });
1135
+ const me = yield* (yield* clientFor(apiUrl, session)).whoami();
1136
+ if (me.actorType !== "human")
1137
+ return yield* new CliError({ message: "kitman pair needs a person signed in" });
1138
+ const workspaceId = (yield* readUserConfig())?.activeWorkspaceId ?? me.activeWorkspaceId ?? me.workspaceId;
1139
+ if (workspaceId === undefined)
1140
+ return yield* new CliError({ message: "No active Workspace. Run kitman workspace use <workspace>." });
1141
+ return { session, workspaceId, userId: me.userId };
1142
+ });
1585
1143
  var parseInitFlags = (args) => {
1586
1144
  const flags = { yes: false };
1587
1145
  for (let index = 0;index < args.length; index += 1) {
@@ -1606,54 +1164,59 @@ var pickWorkspace = (workspaces, ask) => gen(function* () {
1606
1164
  return workspaces.find((workspace) => workspace.slug === answer || workspace.name === answer) ?? first;
1607
1165
  });
1608
1166
  var matchesRemote = (binding, remoteUrl) => binding.status === "active" && repositoryKey(binding.repositoryUrl ?? `https://${binding.host}/${binding.owner}/${binding.name}`) === repositoryKey(remoteUrl);
1609
- var projectFor = (client, workspaceId, remoteUrl, repositoryName, configured) => gen(function* () {
1610
- const projects = (yield* client.listProjects(workspaceId)).items;
1611
- const known = projects.find((project) => project.id === configured);
1612
- if (known !== undefined)
1613
- return { id: known.id, name: known.name, outcome: "already" };
1614
- const boundHere = yield* findFirst(projects, (project) => client.listRepoBindings(project.id).pipe(map3((bindings) => bindings.items.some((binding) => matchesRemote(binding, remoteUrl))), orElseSucceed(() => false)));
1615
- const found = getOrUndefined(boundHere) ?? projects.find((project) => project.slug === repositoryName || project.name === repositoryName);
1616
- if (found === undefined) {
1617
- const created = yield* client.createProject(workspaceId, { name: repositoryName });
1618
- return { id: created.id, name: created.name, outcome: "set_up" };
1619
- }
1620
- return { id: found.id, name: found.name, outcome: "set_up" };
1621
- });
1622
1167
  var runnerStep = (client, input, step) => gen(function* () {
1623
1168
  const name = yield* machineName;
1624
1169
  const runners = (yield* client.listRunners(input.workspaceId)).items;
1625
- const existing = runners.find((runner) => runner.ownership.kind === "personal" && runner.ownership.ownerUserId === input.userId && runner.name === name && runner.status === "active");
1626
- const runner = existing ?? (yield* client.registerRunner(input.workspaceId, {
1170
+ const listed = runners.find((runner) => runner.ownership.kind === "personal" && runner.ownership.ownerUserId === input.userId && runner.name === name && runner.status === "active");
1171
+ const check = listed === undefined ? undefined : yield* herdrRunnerCredentialCheck(input.apiUrl, listed.id);
1172
+ const runner = listed ?? (yield* client.registerRunner(input.workspaceId, {
1627
1173
  name,
1628
1174
  capabilities: herdrRunnerCapabilities2({ harnesses: yield* detectHarnesses2 }),
1629
1175
  labels: ["kitman-runner", "kitman-loop", "herdr"]
1630
1176
  })).runner;
1631
- const credentialMissing = agentCredentialForApi(yield* readAuthStore(), input.apiUrl, `herdr-runner:${runner.id}`) === undefined;
1632
- if (credentialMissing)
1633
- yield* ensureHerdrRunnerCredential({ apiUrl: input.apiUrl, accessToken: input.session.accessToken, runner });
1634
- yield* step("Runner", existing === undefined ? "set_up" : credentialMissing ? "repaired" : "already", `${name}${credentialMissing && existing !== undefined ? ", credential restored" : ""}`);
1635
- const home = yield* String3("HOME");
1636
- const service = yield* runnerService({
1637
- name: runnerServiceName(runner.id),
1638
- command: [...kitmanCommand(), "runner", "start", runner.id, "--api-url", input.apiUrl],
1639
- workingDirectory: home,
1640
- path: getOrElse(yield* option2(String3("PATH")), () => "/usr/local/bin:/usr/bin:/bin")
1641
- });
1177
+ const keyed = check !== "accepted";
1178
+ if (keyed)
1179
+ yield* provisionHerdrRunnerCredential({ apiUrl: input.apiUrl, accessToken: input.session.accessToken, runner });
1180
+ yield* step("Runner", check === undefined ? "set_up" : keyed ? "repaired" : "already", `${name}${check === undefined ? "" : credentialNote[check]}`);
1181
+ yield* runnerServiceStep(input.apiUrl, runner.id, { fresh: listed === undefined, keyed }, step);
1182
+ });
1183
+ var runnerServiceStep = (apiUrl, runnerId, change, step) => gen(function* () {
1184
+ const manager = yield* ServiceManager;
1185
+ if (manager.platform === "linux" || manager.platform === "darwin") {
1186
+ const managed = (yield* runningManagedInstall(manager.platform)) ? "running" : yield* installManagedKitman(apiUrl, manager.platform);
1187
+ if (managed === undefined)
1188
+ return yield* step("Runner service", "skipped", `${apiUrl} serves no kitman to install; start it yourself with kitman runner start --runner ${runnerId}`);
1189
+ if (managed !== "running")
1190
+ yield* step("kitman", managed.installed ? "set_up" : "already", managed.version);
1191
+ }
1192
+ const spec = yield* managedRunnerService(runnerId, apiUrl);
1193
+ const service = yield* runnerService(spec);
1642
1194
  if (service === "unsupported")
1643
- yield* step("Runner service", "skipped", `start it yourself with kitman runner start ${runner.id}`);
1644
- else
1645
- yield* step("Runner service", service === "running" ? "already" : existing === undefined ? "set_up" : "repaired", service === "running" ? "running" : "started");
1195
+ return yield* step("Runner service", "skipped", `start it yourself with kitman runner start ${runnerId}`);
1196
+ if (service === "running" && !change.keyed)
1197
+ return yield* step("Runner service", "already", "running");
1198
+ if (service === "running")
1199
+ yield* restartRunnerService(spec.name);
1200
+ yield* step("Runner service", change.fresh ? "set_up" : "repaired", "started");
1646
1201
  });
1202
+ var credentialNote = { missing: ", credential restored", refused: ", credential re-keyed", accepted: "" };
1647
1203
  var machineName = map3(option2(String3("KITMAN_RUNNER_NAME")), (name) => getOrElse(name, () => hostname()));
1648
- var kitmanCommand = () => {
1649
- const [runtime, script] = process.argv;
1650
- return script === undefined || script.startsWith("/$bunfs/") ? [process.execPath] : [runtime ?? process.execPath, script];
1651
- };
1204
+ var managedRunnerService = (runnerId, apiUrl) => gen(function* () {
1205
+ const kitman = yield* managedKitmanPath;
1206
+ const bin = (yield* Path).dirname(kitman);
1207
+ const path = getOrElse(yield* option2(String3("PATH")), () => "/usr/local/bin:/usr/bin:/bin");
1208
+ return {
1209
+ name: runnerServiceName(runnerId),
1210
+ command: [kitman, "runner", "start", "--runner", runnerId, "--api-url", apiUrl],
1211
+ workingDirectory: yield* String3("HOME"),
1212
+ path: [bin, ...path.split(":").filter((entry) => entry !== bin)].join(":")
1213
+ };
1214
+ });
1652
1215
  var readText = (path) => flatMap2(FileSystem, (fs) => fs.readFileString(path)).pipe(orElseSucceed(() => {
1653
1216
  return;
1654
1217
  }));
1655
- var verificationStep = (client, projectId, binding, confirm, step) => gen(function* () {
1656
- const approved = (yield* client.listApprovedVerifications(projectId, binding.id)).items;
1218
+ var verificationStep = (client, workspaceId, binding, confirm, step) => gen(function* () {
1219
+ const approved = (yield* client.listApprovedVerifications(workspaceId, binding.id)).items;
1657
1220
  const suggested = (yield* scriptChecks).filter((check) => !approved.some((entry) => entry.name === check.name));
1658
1221
  if (suggested.length === 0)
1659
1222
  return yield* step("Verifications", approved.length === 0 ? "skipped" : "already", approved.length === 0 ? "no check scripts found; add one with kitman verify add" : approved.map((check) => check.name).join(", "));
@@ -1662,7 +1225,7 @@ var verificationStep = (client, projectId, binding, confirm, step) => gen(functi
1662
1225
  if (!(yield* confirm("Approve these verifications as evidence for every Run", true)))
1663
1226
  return yield* step("Verifications", "skipped", "not approved; run kitman init again or kitman verify add");
1664
1227
  for (const check of suggested)
1665
- yield* client.approveVerification(projectId, binding.id, check);
1228
+ yield* client.approveVerification(workspaceId, binding.id, check);
1666
1229
  yield* step("Verifications", "set_up", suggested.map((check) => check.name).join(", "));
1667
1230
  });
1668
1231
  var scriptChecks = gen(function* () {
@@ -1684,13 +1247,15 @@ var systemdUnit = (spec) => [
1684
1247
  `Environment="PATH=${spec.path}"`,
1685
1248
  "Restart=on-failure",
1686
1249
  "RestartSec=5",
1250
+ "RestartSteps=10",
1251
+ "RestartMaxDelaySec=300",
1687
1252
  "",
1688
1253
  "[Install]",
1689
1254
  "WantedBy=default.target",
1690
1255
  ""
1691
1256
  ].join(`
1692
1257
  `);
1693
- var launchdPlist = (label, spec) => [
1258
+ var launchdPlist = (label, spec, log) => [
1694
1259
  '<?xml version="1.0" encoding="UTF-8"?>',
1695
1260
  '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
1696
1261
  '<plist version="1.0"><dict>',
@@ -1698,8 +1263,10 @@ var launchdPlist = (label, spec) => [
1698
1263
  `<key>ProgramArguments</key><array>${spec.command.map((word) => `<string>${escapeXml(word)}</string>`).join("")}</array>`,
1699
1264
  `<key>WorkingDirectory</key><string>${escapeXml(spec.workingDirectory)}</string>`,
1700
1265
  `<key>EnvironmentVariables</key><dict><key>PATH</key><string>${escapeXml(spec.path)}</string></dict>`,
1266
+ `<key>StandardOutPath</key><string>${escapeXml(log)}</string>`,
1267
+ `<key>StandardErrorPath</key><string>${escapeXml(log)}</string>`,
1701
1268
  "<key>RunAtLoad</key><true/>",
1702
- "<key>KeepAlive</key><true/>",
1269
+ "<key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>",
1703
1270
  "</dict></plist>",
1704
1271
  ""
1705
1272
  ].join(`
@@ -1707,6 +1274,44 @@ var launchdPlist = (label, spec) => [
1707
1274
  var escapeXml = (text) => text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
1708
1275
  var runnerServiceName = (runnerId) => `kitman-runner-${runnerId}`;
1709
1276
  var launchdLabel = (name) => `dev.kitman.${name}`;
1277
+ var runnerServiceFile = (platform, name) => gen(function* () {
1278
+ const path = yield* Path;
1279
+ const home = yield* String3("HOME");
1280
+ return platform === "darwin" ? path.join(home, "Library", "LaunchAgents", `${launchdLabel(name)}.plist`) : path.join(home, ".config", "systemd", "user", `${name}.service`);
1281
+ });
1282
+ var runnerServiceLog = (name) => gen(function* () {
1283
+ const path = yield* Path;
1284
+ return path.join(yield* String3("HOME"), "Library", "Logs", "kitman", `${name}.log`);
1285
+ });
1286
+ var restartRunnerService = (name) => gen(function* () {
1287
+ const manager = yield* ServiceManager;
1288
+ if (manager.platform === "darwin")
1289
+ return yield* manager.run("launchctl", ["kickstart", "-k", `${yield* launchdDomain}/${launchdLabel(name)}`]);
1290
+ return yield* manager.run("systemctl", ["--user", "restart", `${name}.service`]);
1291
+ });
1292
+ var showRunnerServiceLog = (name, follow) => gen(function* () {
1293
+ const manager = yield* ServiceManager;
1294
+ if (manager.platform === "darwin")
1295
+ return yield* manager.attach("tail", ["-n", "200", ...follow ? ["-f"] : [], yield* runnerServiceLog(name)]);
1296
+ return yield* manager.attach("journalctl", [
1297
+ "--user",
1298
+ "--unit",
1299
+ `${name}.service`,
1300
+ ...follow ? ["--follow"] : ["--no-pager"]
1301
+ ]);
1302
+ });
1303
+ var removeRunnerService = (name) => gen(function* () {
1304
+ const fs = yield* FileSystem;
1305
+ const manager = yield* ServiceManager;
1306
+ const file = yield* runnerServiceFile(manager.platform, name);
1307
+ if (manager.platform === "darwin")
1308
+ yield* ignore(manager.run("launchctl", ["bootout", `${yield* launchdDomain}/${launchdLabel(name)}`]));
1309
+ else
1310
+ yield* ignore(manager.run("systemctl", ["--user", "disable", "--now", `${name}.service`]));
1311
+ yield* fs.remove(file, { force: true });
1312
+ if (manager.platform !== "darwin")
1313
+ yield* ignore(manager.run("systemctl", ["--user", "daemon-reload"]));
1314
+ });
1710
1315
  var launchdDomain = gen(function* () {
1711
1316
  const manager = yield* ServiceManager;
1712
1317
  return `gui/${(yield* manager.run("id", ["-u"])).trim()}`;
@@ -1726,7 +1331,6 @@ function runnerService(spec) {
1726
1331
  const fs = yield* FileSystem;
1727
1332
  const path = yield* Path;
1728
1333
  const manager = yield* ServiceManager;
1729
- const home = yield* String3("HOME");
1730
1334
  const define = (file, content) => gen(function* () {
1731
1335
  if ((yield* readText(file)) === content)
1732
1336
  return false;
@@ -1736,7 +1340,7 @@ function runnerService(spec) {
1736
1340
  });
1737
1341
  if (manager.platform === "linux") {
1738
1342
  const unit = `${spec.name}.service`;
1739
- const changed = yield* define(path.join(home, ".config", "systemd", "user", unit), systemdUnit(spec));
1343
+ const changed = yield* define(yield* runnerServiceFile(manager.platform, spec.name), systemdUnit(spec));
1740
1344
  if (!changed && (yield* runnerServiceState(spec.name)) === "systemd: active")
1741
1345
  return "running";
1742
1346
  yield* manager.run("systemctl", ["--user", "daemon-reload"]);
@@ -1746,8 +1350,10 @@ function runnerService(spec) {
1746
1350
  }
1747
1351
  if (manager.platform === "darwin") {
1748
1352
  const label = launchdLabel(spec.name);
1749
- const file = path.join(home, "Library", "LaunchAgents", `${label}.plist`);
1750
- const changed = yield* define(file, launchdPlist(label, spec));
1353
+ const file = yield* runnerServiceFile(manager.platform, spec.name);
1354
+ const log = yield* runnerServiceLog(spec.name);
1355
+ yield* fs.makeDirectory(path.dirname(log), { recursive: true });
1356
+ const changed = yield* define(file, launchdPlist(label, spec, log));
1751
1357
  if (!changed && (yield* runnerServiceState(spec.name)) === "launchd: running")
1752
1358
  return "running";
1753
1359
  const domain = yield* launchdDomain;
@@ -1788,7 +1394,7 @@ var package_default = {
1788
1394
  };
1789
1395
 
1790
1396
  // apps/cli/src/scaffold.ts
1791
- var cliVersion = "0.7.5";
1397
+ var cliVersion = "0.8.0";
1792
1398
  var packageJson = (name, scaffold) => `${JSON.stringify({
1793
1399
  name,
1794
1400
  private: true,
@@ -1898,7 +1504,7 @@ var readme = (name, scaffold) => [
1898
1504
  "1. `bun install`, then `bun test`.",
1899
1505
  "2. `bunx kitman deploy --plan` shows what deploying would change.",
1900
1506
  "3. Push this repository to GitLab, then let its CI deploy the default branch: a Workspace owner or admin runs",
1901
- " `bunx kitman deploy bind --branch main` (add `--project <project>` to deploy to one Project).",
1507
+ " `bunx kitman deploy bind --branch main` (add `--team <team>` to deploy to one Team).",
1902
1508
  "4. Merge requests print the plan, and merging deploys. `.gitlab-ci.yml` names the Workspace to deploy to.",
1903
1509
  ""
1904
1510
  ].join(`
@@ -1908,7 +1514,7 @@ var scaffoldPackage = (directory, scaffold = {}) => gen(function* () {
1908
1514
  const path = yield* Path;
1909
1515
  const root = path.resolve(directory);
1910
1516
  if (scaffold.fork !== undefined) {
1911
- const { shippedLoops } = yield* promise(() => import("./chunk-0pre510a.js"));
1517
+ const { shippedLoops } = yield* promise(() => import("./chunk-64et31rh.js"));
1912
1518
  if (!(scaffold.fork in shippedLoops))
1913
1519
  return yield* new CliError({
1914
1520
  message: `Kitman ships no Loop ${scaffold.fork}; fork one of ${Object.keys(shippedLoops).join(", ")}`
@@ -1940,30 +1546,38 @@ var scaffoldPackage = (directory, scaffold = {}) => gen(function* () {
1940
1546
  // apps/cli/src/commands.ts
1941
1547
  var ClientId = "kitman-cli";
1942
1548
  var Usage = [
1943
- "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]",
1549
+ "Usage: kitman <init|pair|run|inbox|loop|agent|deploy|package|verify|secret|team|repo|constraint|mcp|sync|whoami|config|kit|task|memory|proposal|decision|context|schedule|delivery|reconcile|lifecycle|logout> [options]",
1944
1550
  "",
1945
1551
  "Set up, in a repository (run it again to repair what is missing):",
1946
1552
  " kitman init [--yes] [--api-url <url>]",
1947
- " kitman pair <pairing code> [--api-url <url>] (what the command the web shows runs)",
1553
+ " kitman pair [<pairing code>] [--api-url <url>] (what the command the web shows runs; no code finishes or re-keys with your session)",
1554
+ " kitman pair <runner pairing code> --credential-file <path> (writes a shared Runner's credential for a container or its replicas to mount)",
1555
+ " kitman runner start --credential-file <path> (or KITMAN_RUNNER_CREDENTIAL_FILE; starts the Runner the file holds)",
1948
1556
  "",
1949
1557
  "Runs:",
1950
- ' kitman run start <loop> "<words>" [--input <json>] [--project <project>] [--type <task type>] [--constraint <text>]...',
1558
+ ' kitman run start <loop> "<words>" [--input <json>] [--team <team>] [--type <task type>] [--constraint <text>]...',
1951
1559
  " kitman run show <run>",
1952
1560
  " kitman run intent <run> [--set-goal <text>] [--add-constraint <text>] [--remove-constraint <id>] [--corrections on|off]",
1953
1561
  " kitman run drift <run> [--dismiss <constraint> --reason <text>]",
1954
- " kitman run list [--project <id>] [--loop <loop>] [--state <state>[,<state>\u2026]] [--limit <n>]",
1562
+ " kitman run files <run> [--step <step>]",
1563
+ " kitman run file get <run> <id> [--out <path>]",
1564
+ " kitman run list [--team <id>] [--loop <loop>] [--state <state>[,<state>\u2026]] [--limit <n>]",
1955
1565
  " kitman run wait <run> [--until attention|finished]",
1956
1566
  ' kitman run answer <run> "<option or words>" [--question <name>]',
1957
1567
  ' kitman run steer <run> "<note for the agent>"',
1568
+ " kitman run follow <run> (prints its agents' Transcripts as they grow, until the Run settles)",
1569
+ " kitman transcript <run> [--step <step>] [--attempt <n>]",
1958
1570
  " kitman run stop <run>",
1959
1571
  " kitman run resume <run>",
1960
1572
  " kitman run close <run> --complete|--abandoned [--reason <text>]",
1961
1573
  " kitman run actions <run>",
1962
1574
  " kitman run action <run> <action>",
1575
+ " kitman task assign <task> --to <person> (by email, name or id)",
1576
+ " kitman task assign <task> --loop <loop> (starts a Run of the Loop on the Task; you stay its owner)",
1963
1577
  " kitman inbox [--unread|--snoozed|--done]",
1964
- " kitman inbox my-teams [--project <project>]",
1965
- " kitman inbox read|unread|done|undone|unsnooze <task:id|run:id|proposal:id>...",
1966
- " kitman inbox snooze <task:id|run:id|proposal:id>... --until <time>",
1578
+ " kitman inbox my-teams [--team <team>]",
1579
+ " kitman inbox read|unread|done|undone|unsnooze <task:id|run:id|proposal:id|project_update:id>...",
1580
+ " kitman inbox snooze <task:id|run:id|proposal:id|project_update:id>... --until <time>",
1967
1581
  " kitman inbox unsubscribe <task>...",
1968
1582
  "",
1969
1583
  "",
@@ -1985,24 +1599,37 @@ var Usage = [
1985
1599
  ' kitman constraint add "<constraint>"',
1986
1600
  " kitman constraint remove <constraint>",
1987
1601
  "",
1988
- "Secrets, for the Workspace or with --project for one Project; values are write-only:",
1989
- " <command> | kitman secret set <name> [--project <project>] (reads the value from stdin)",
1990
- " kitman secret delete <name> [--project <project>]",
1991
- " kitman secret list [--project <project>]",
1602
+ "Secrets, for the Workspace or with --team for one Team; values are write-only:",
1603
+ " <command> | kitman secret set <name> [--team <team>] (reads the value from stdin)",
1604
+ " kitman secret delete <name> [--team <team>]",
1605
+ " kitman secret list [--team <team>]",
1606
+ " kitman secret move <name> [--team <team>] --to <team>|workspace",
1992
1607
  "",
1993
- "Packages, built from the current directory and deployed to the Workspace or with --project to one Project:",
1994
- " kitman deploy [--plan] [--project <project>] (--plan prints what would change; deploying needs a clean tree)",
1995
- ' kitman dev <loop> "<words>" [--input <json>] [--project <project>] (runs the working tree on your personal Runner)',
1996
- " kitman package list [--project <project>]",
1997
- " kitman package show <name> [--project <project>]",
1998
- " kitman package remove <name> [--project <project>] [--yes] (prints the plan; removes only with --yes)",
1608
+ "A private Team, and all it holds, is seen by its members alone:",
1609
+ " kitman team private <team>",
1610
+ " kitman team open <team>",
1611
+ " kitman team invite <team> <person>",
1612
+ "",
1613
+ "Repositories, Memory and Decisions belong to the Workspace or one Team:",
1614
+ " kitman repo list",
1615
+ " kitman repo add [--from-current-repo] [--team <team>]",
1616
+ " kitman repo move <repository> --to <team>|workspace",
1617
+ " kitman memory move <memory> --to <team>|workspace",
1618
+ " kitman decision move <decision> --to <team>|workspace",
1619
+ "",
1620
+ "Packages, built from the current directory and deployed to the Workspace or with --team to one Team:",
1621
+ " kitman deploy [--plan] [--team <team>] (--plan prints what would change; deploying needs a clean tree)",
1622
+ ' kitman dev <loop> "<words>" [--input <json>] [--team <team>] (runs the working tree on your personal Runner)',
1623
+ " kitman package list [--team <team>]",
1624
+ " kitman package show <name> [--team <team>]",
1625
+ " kitman package remove <name> [--team <team>] [--yes] (prints the plan; removes only with --yes)",
1999
1626
  "",
2000
1627
  "Every run, loop, agent, verify, constraint, secret, deploy, dev and package command takes --json."
2001
1628
  ].join(`
2002
1629
  `);
2003
1630
  var KitCreateValidator = guard(KitCreate);
2004
1631
  var KitUpdateValidator = guard(KitUpdate);
2005
- var agentRuntime = promise(() => import("./chunk-vz3v0f4j.js"));
1632
+ var agentRuntime = promise(() => import("./chunk-m6yymqfr.js"));
2006
1633
  var TuiLauncher = Reference("kitman/cli/TuiLauncher", {
2007
1634
  defaultValue: () => gen(function* () {
2008
1635
  const tui = yield* tryPromise({
@@ -2024,6 +1651,8 @@ function runEffect(args) {
2024
1651
  yield* log(Usage);
2025
1652
  return;
2026
1653
  }
1654
+ if (args[0] === "--version")
1655
+ return yield* log(kitmanVersion ?? "dev");
2027
1656
  const [command, ...rest] = args;
2028
1657
  if (command === "init" && rest[0] === "--package")
2029
1658
  return yield* scaffoldPackage(rest[1] ?? ".");
@@ -2041,8 +1670,10 @@ function runEffect(args) {
2041
1670
  return yield* configCommand(args);
2042
1671
  if (command === "workspace")
2043
1672
  return yield* workspaceCommand(args);
2044
- if (command === "project")
2045
- return yield* projectCommand(args);
1673
+ if (command === "team")
1674
+ return yield* teamCommand(args);
1675
+ if (command === "repo")
1676
+ return yield* repoCommand(args);
2046
1677
  if (command === "runner")
2047
1678
  return yield* runnerCommand(args);
2048
1679
  if (command === "mcp")
@@ -2065,6 +1696,8 @@ function runEffect(args) {
2065
1696
  return yield* agentCommand(args);
2066
1697
  if (command === "run")
2067
1698
  return yield* runCommand(args);
1699
+ if (command === "transcript")
1700
+ return yield* transcriptCommand(["transcript", "read", ...rest]);
2068
1701
  if (command === "inbox")
2069
1702
  return yield* inboxCommand(args);
2070
1703
  if (command === "verify")
@@ -2100,6 +1733,10 @@ var isInteractiveTty = gen(function* () {
2100
1733
  });
2101
1734
  var tuiModulePath = "./tui/index";
2102
1735
  var solidTransformModulePath = "@opentui/solid/preload";
1736
+ var renamedOptions = new Map([
1737
+ ["--project", "--team"],
1738
+ ["--project-access", "--team-access"]
1739
+ ]);
2103
1740
  function parseCommand(args) {
2104
1741
  const group = args[0];
2105
1742
  const action = args[1];
@@ -2180,18 +1817,16 @@ function parseCommand(args) {
2180
1817
  if (flags.closure !== undefined && flags.closure !== kind)
2181
1818
  throw new Error("run close takes one of --complete or --abandoned");
2182
1819
  flags.closure = kind;
2183
- } else if (arg === "--project" && group === "sync")
2184
- flags.projectScope = true;
2185
- else if (arg === "--runner") {
1820
+ } else if (arg === "--team" && group === "sync")
1821
+ flags.teamScope = true;
1822
+ else if (arg === "--runner" || arg === "--credential-file") {
2186
1823
  const value = args[++index];
2187
1824
  if (!value)
2188
1825
  throw new Error(`${arg} requires a value`);
2189
- flags.runnerId = value;
2190
- } else if (arg === "--agent-identity") {
2191
- const value = args[++index];
2192
- if (!value)
2193
- throw new Error("--agent-identity requires a value");
2194
- flags.agentIdentityId = value;
1826
+ if (arg === "--runner")
1827
+ flags.runnerId = value;
1828
+ else
1829
+ flags.credentialFile = value;
2195
1830
  } else if (arg === "--iterations") {
2196
1831
  if (group === "runner" && action === "start")
2197
1832
  throw new Error("runner start accepts only a Runner identity");
@@ -2199,6 +1834,11 @@ function parseCommand(args) {
2199
1834
  if (!value)
2200
1835
  throw new Error("--iterations requires a value");
2201
1836
  flags.iterations = parsePositiveIntegerFlag("--iterations", value);
1837
+ } else if (arg === "--attempt") {
1838
+ const value = args[++index];
1839
+ if (!value)
1840
+ throw new Error("--attempt requires a value");
1841
+ flags.attempt = parsePositiveIntegerFlag("--attempt", value);
2202
1842
  } else if (arg === "--poll-interval-ms") {
2203
1843
  if (group === "runner" && action === "start")
2204
1844
  throw new Error("runner start accepts only a Runner identity");
@@ -2269,7 +1909,7 @@ function parseCommand(args) {
2269
1909
  if (!value)
2270
1910
  throw new Error("--capabilities requires a value");
2271
1911
  flags.capabilities = validateAgentCapabilities(value);
2272
- } else if (arg === "--name" || arg === "--title" || arg === "--description" || arg === "--summary" || arg === "--rationale" || arg === "--kind" || arg === "--content" || arg === "--source" || arg === "--content-type" || arg === "--filename" || arg === "--session" || arg === "--project" || arg === "--workspace" || arg === "--branch" || arg === "--team" || arg === "--type" || arg === "--status" || arg === "--loop" || arg === "--state" || arg === "--reason" || arg === "--dismiss" || arg === "--action" || arg === "--step-key" || arg === "--risk" || arg === "--target-type" || arg === "--scope-type" || arg === "--scope-id" || arg === "--project-access" || arg === "--provider" || arg === "--task" || arg === "--input" || arg === "--idempotency-key" || arg === "--key" || arg === "--after" || arg === "--version" || arg === "--alias" || arg === "--profile" || arg === "--event" || arg === "--cascade" || arg === "--run" || arg === "--run-id" || arg === "--replica" || arg === "--work-unit-kind" || arg === "--host" || arg === "--owner" || arg === "--repository-url" || arg === "--default-branch" || arg === "--expected-branch" || arg === "--expected-commit-sha" || arg === "--credential") {
1912
+ } else if (arg === "--name" || arg === "--title" || arg === "--description" || arg === "--summary" || arg === "--rationale" || arg === "--kind" || arg === "--content" || arg === "--source" || arg === "--content-type" || arg === "--filename" || arg === "--session" || arg === "--team" || arg === "--workspace" || arg === "--branch" || arg === "--team" || arg === "--type" || arg === "--status" || arg === "--loop" || arg === "--state" || arg === "--reason" || arg === "--dismiss" || arg === "--action" || arg === "--step-key" || arg === "--step" || arg === "--risk" || arg === "--target-type" || arg === "--scope-type" || arg === "--scope-id" || arg === "--team-access" || arg === "--provider" || arg === "--task" || arg === "--input" || arg === "--idempotency-key" || arg === "--key" || arg === "--after" || arg === "--version" || arg === "--alias" || arg === "--profile" || arg === "--event" || arg === "--cascade" || arg === "--run" || arg === "--run-id" || arg === "--out" || arg === "--replica" || arg === "--work-unit-kind" || arg === "--host" || arg === "--owner" || arg === "--repository-url" || arg === "--default-branch" || arg === "--expected-branch" || arg === "--expected-commit-sha" || arg === "--credential" || arg === "--to") {
2273
1913
  const value = args[++index];
2274
1914
  if (!value)
2275
1915
  throw new Error(`${arg} requires a value`);
@@ -2295,14 +1935,12 @@ function parseCommand(args) {
2295
1935
  flags.filename = value;
2296
1936
  else if (arg === "--session")
2297
1937
  flags.sessionId = value;
2298
- else if (arg === "--project")
2299
- flags.project = value;
1938
+ else if (arg === "--team")
1939
+ flags.team = value;
2300
1940
  else if (arg === "--workspace")
2301
1941
  flags.workspace = value;
2302
1942
  else if (arg === "--branch")
2303
1943
  flags.branch = value;
2304
- else if (arg === "--team")
2305
- flags.team = value;
2306
1944
  else if (arg === "--type")
2307
1945
  flags.taskType = value;
2308
1946
  else if (arg === "--status")
@@ -2317,7 +1955,7 @@ function parseCommand(args) {
2317
1955
  flags.dismiss = value;
2318
1956
  else if (arg === "--action")
2319
1957
  flags.fixFlowAction = validateFixFlowAction(value);
2320
- else if (arg === "--step-key")
1958
+ else if (arg === "--step-key" || arg === "--step")
2321
1959
  flags.stepKey = value;
2322
1960
  else if (arg === "--target-type")
2323
1961
  flags.targetType = validateProposalTargetType(value);
@@ -2325,10 +1963,10 @@ function parseCommand(args) {
2325
1963
  flags.scopeType = value;
2326
1964
  else if (arg === "--scope-id")
2327
1965
  flags.scopeId = value;
2328
- else if (arg === "--project-access") {
1966
+ else if (arg === "--team-access") {
2329
1967
  if (value !== "all" && value !== "selected")
2330
- throw new Error("--project-access must be all or selected");
2331
- flags.projectAccess = value;
1968
+ throw new Error("--team-access must be all or selected");
1969
+ flags.teamAccess = value;
2332
1970
  } else if (arg === "--provider")
2333
1971
  flags.provider = validateRepoBindingProvider(value);
2334
1972
  else if (arg === "--task")
@@ -2353,6 +1991,8 @@ function parseCommand(args) {
2353
1991
  flags.runId = value;
2354
1992
  else if (arg === "--run-id")
2355
1993
  flags.runId = value;
1994
+ else if (arg === "--out")
1995
+ flags.out = value;
2356
1996
  else if (arg === "--replica")
2357
1997
  flags.replicaId = value;
2358
1998
  else if (arg === "--work-unit-kind")
@@ -2369,9 +2009,13 @@ function parseCommand(args) {
2369
2009
  flags.expectedBranch = value;
2370
2010
  else if (arg === "--expected-commit-sha")
2371
2011
  flags.expectedCommitSha = value;
2012
+ else if (arg === "--to")
2013
+ flags.to = value;
2372
2014
  else
2373
2015
  flags.credentialRefs = [...flags.credentialRefs ?? [], parseRepoBindingCredentialRef(value)];
2374
- } else if (arg?.startsWith("--"))
2016
+ } else if (arg !== undefined && renamedOptions.has(arg))
2017
+ throw new Error(`${arg} is now ${renamedOptions.get(arg)}`);
2018
+ else if (arg?.startsWith("--"))
2375
2019
  throw new Error(`Unknown option: ${arg}`);
2376
2020
  else if (arg)
2377
2021
  values.push(arg);
@@ -2387,7 +2031,7 @@ function syncCommand(args) {
2387
2031
  materializationPlans,
2388
2032
  configPlans,
2389
2033
  userConfigPlans,
2390
- projectConfigPlans,
2034
+ teamConfigPlans,
2391
2035
  syncKits,
2392
2036
  outputPath,
2393
2037
  applied: shouldApply
@@ -2404,8 +2048,8 @@ function syncCommand(args) {
2404
2048
  else {
2405
2049
  yield* log(`Harness sync ${shouldApply ? "applied" : "preview"}.`);
2406
2050
  yield* log(`Workspace: ${plan.workspaceId}`);
2407
- if (plan.projectId)
2408
- yield* log(`Project: ${plan.projectId}`);
2051
+ if (plan.teamId)
2052
+ yield* log(`Team: ${plan.teamId}`);
2409
2053
  yield* log(`Harnesses: ${plan.harnesses.map((item) => item.harness).join(", ")}`);
2410
2054
  yield* log(`Secrets: ${plan.secrets}`);
2411
2055
  yield* log(`Plan path: ${outputPath}`);
@@ -2417,43 +2061,45 @@ function syncCommand(args) {
2417
2061
  for (const configPlan of userConfigPlans) {
2418
2062
  yield* log(`${configPlan.harness} user writes: ${configPlan.operations.map((operation) => operation.path).join(", ") || "none"}`);
2419
2063
  }
2420
- for (const configPlan of projectConfigPlans) {
2421
- yield* log(`${configPlan.harness} project writes: ${configPlan.operations.map((operation) => operation.path).join(", ") || "none"}`);
2064
+ for (const configPlan of teamConfigPlans) {
2065
+ yield* log(`${configPlan.harness} team writes: ${configPlan.operations.map((operation) => operation.path).join(", ") || "none"}`);
2422
2066
  }
2423
2067
  }
2424
2068
  });
2425
2069
  }
2426
- function harnessSync(args) {
2070
+ function harnessSync(args, runnerClient) {
2427
2071
  const command = parseSyncCommand(args);
2428
2072
  return gen(function* () {
2429
2073
  const { applyAgentEnvironmentPlans, composeAgentEnvironmentSyncPlan, resolveAgentEnvironmentFromSources } = yield* agentRuntime;
2430
2074
  const { config } = yield* resolveCommandConfig(command.flags);
2431
2075
  if (!config.activeWorkspaceId)
2432
2076
  return yield* new CliError({ message: "No active workspace. Run kitman workspace use <workspace>." });
2433
- if (command.flags.projectScope && !config.activeProjectId)
2077
+ if (command.flags.teamScope && !config.activeTeamId)
2434
2078
  return yield* new CliError({
2435
- message: "No active project. Run kitman project use <project> before project sync."
2079
+ message: "No active team. Run kitman team use <team> before team sync."
2436
2080
  });
2437
- const activeProjectId = command.flags.projectScope ? config.activeProjectId : undefined;
2438
- const session = yield* sessionForApiWithRefresh(config.apiUrl);
2439
- if (!session)
2440
- return yield* new CliError({ message: `Not logged in for ${config.apiUrl}. Run kitman login.` });
2441
- const client = yield* clientFor(config.apiUrl, session);
2081
+ const activeTeamId = command.flags.teamScope ? config.activeTeamId : undefined;
2082
+ const client = runnerClient ?? (yield* gen(function* () {
2083
+ const session = yield* sessionForApiWithRefresh(config.apiUrl);
2084
+ if (!session)
2085
+ return yield* new CliError({ message: `Not logged in for ${config.apiUrl}. Run kitman login.` });
2086
+ return yield* clientFor(config.apiUrl, session);
2087
+ }));
2442
2088
  const outputPath = yield* userHarnessSyncPath;
2443
2089
  const workspaceId = config.activeWorkspaceId;
2444
2090
  const selectedHarnesses = syncTargetHarnesses(command, config);
2445
2091
  const workspaceKits = yield* client.listKits(workspaceId).pipe(catchIf((error) => error.status === 404, () => succeed5({ items: Array() })));
2446
- const projectKits = activeProjectId === undefined ? { items: Array() } : yield* client.listProjectKits(activeProjectId, { workspaceId });
2092
+ const teamKits = activeTeamId === undefined ? { items: Array() } : yield* client.listTeamKits(activeTeamId, { workspaceId });
2447
2093
  const userPreferences = config.userAgentPreferences;
2448
2094
  const syncKits = selectedHarnesses.map((harness) => resolveSyncKit({
2449
2095
  defaultHarness: harness,
2450
2096
  activeKits: syncKitActiveKits({
2451
2097
  workspaceId,
2452
- ...activeProjectId === undefined ? undefined : { projectId: activeProjectId },
2098
+ ...activeTeamId === undefined ? undefined : { teamId: activeTeamId },
2453
2099
  ...userPreferences === undefined ? undefined : { userPreferences },
2454
2100
  harnesses: [harness],
2455
2101
  workspaceKits: workspaceKits.items,
2456
- projectKits: projectKits.items
2102
+ teamKits: teamKits.items
2457
2103
  })
2458
2104
  }));
2459
2105
  const blockedSyncKit = syncKits.find((syncKit) => syncKit.status === "blocked");
@@ -2461,13 +2107,13 @@ function harnessSync(args) {
2461
2107
  return yield* new CliError({ message: `Sync Kit is blocked: ${blockedSyncKit.blockers.join("; ")}` });
2462
2108
  const fragments = syncFragments({
2463
2109
  workspaceId,
2464
- ...activeProjectId === undefined ? undefined : { projectId: activeProjectId },
2110
+ ...activeTeamId === undefined ? undefined : { teamId: activeTeamId },
2465
2111
  userId: "default",
2466
2112
  workspaceKits: workspaceKits.items,
2467
- projectKits: projectKits.items
2113
+ teamKits: teamKits.items
2468
2114
  });
2469
- const syncProfile = syncProfileForCommand(command, config, workspaceId, activeProjectId);
2470
- const projectContext = activeProjectId === undefined ? undefined : { workspaceId, projectId: activeProjectId };
2115
+ const syncProfile = syncProfileForCommand(command, config, workspaceId, activeTeamId);
2116
+ const teamContext = activeTeamId === undefined ? undefined : { workspaceId, teamId: activeTeamId };
2471
2117
  const environments = selectedHarnesses.map((harness) => {
2472
2118
  const environment = resolveAgentEnvironmentFromSources({
2473
2119
  userId: "default",
@@ -2475,27 +2121,27 @@ function harnessSync(args) {
2475
2121
  ...userPreferences === undefined ? undefined : { userPreferences },
2476
2122
  syncProfile,
2477
2123
  fragments,
2478
- ...projectContext === undefined ? undefined : { projectContext }
2124
+ ...teamContext === undefined ? undefined : { teamContext }
2479
2125
  });
2480
2126
  return {
2481
2127
  ...environment,
2482
2128
  warnings: [
2483
2129
  ...environment.warnings,
2484
- ...(projectKits.policyOptOuts ?? []).map((optOut) => `Workspace-default Kit ${optOut.kitId} revision ${optOut.policyRevision} is opted out for this Project.`)
2130
+ ...(teamKits.policyOptOuts ?? []).map((optOut) => `Workspace-default Kit ${optOut.kitId} revision ${optOut.policyRevision} is opted out for this Team.`)
2485
2131
  ]
2486
2132
  };
2487
2133
  });
2488
- const projectRoot = command.flags.projectScope ? yield* resolveProjectWriteRoot : undefined;
2489
- const targetPlans = yield* previewEnvironmentPlans(environments, command, projectRoot, config.mcpCredentialName, syncKits, (yield* readUserConfig()) ?? {});
2134
+ const teamRoot = command.flags.teamScope ? yield* resolveTeamWriteRoot : undefined;
2135
+ const targetPlans = yield* previewEnvironmentPlans(environments, command, teamRoot, config.mcpCredentialName, syncKits, (yield* readUserConfig()) ?? {});
2490
2136
  const environmentPlan = composeAgentEnvironmentSyncPlan(targetPlans);
2491
2137
  const materializationPlans = targetPlans.map((target) => target.skills);
2492
- const userConfigPlans = command.flags.projectScope ? [] : targetPlans.map((target) => target.harnessConfig);
2493
- const projectConfigPlans = projectRoot === undefined ? [] : targetPlans.map((target) => target.harnessConfig);
2494
- const configPlans = [...userConfigPlans, ...projectConfigPlans];
2138
+ const userConfigPlans = command.flags.teamScope ? [] : targetPlans.map((target) => target.harnessConfig);
2139
+ const teamConfigPlans = teamRoot === undefined ? [] : targetPlans.map((target) => target.harnessConfig);
2140
+ const configPlans = [...userConfigPlans, ...teamConfigPlans];
2495
2141
  const plan = buildHarnessSyncPlan({
2496
2142
  apiUrl: config.apiUrl,
2497
2143
  workspaceId,
2498
- ...activeProjectId === undefined ? undefined : { projectId: activeProjectId },
2144
+ ...activeTeamId === undefined ? undefined : { teamId: activeTeamId },
2499
2145
  harnesses: selectedHarnesses,
2500
2146
  outputPath,
2501
2147
  materializationPlans,
@@ -2513,7 +2159,7 @@ function harnessSync(args) {
2513
2159
  materializationPlans: resultPlan.targets.map((target) => target.skills),
2514
2160
  configPlans,
2515
2161
  userConfigPlans,
2516
- projectConfigPlans,
2162
+ teamConfigPlans,
2517
2163
  syncKits,
2518
2164
  outputPath,
2519
2165
  applied
@@ -2546,12 +2192,12 @@ function syncTargetHarnesses(command, config) {
2546
2192
  return [config.userAgentPreferences.defaultHarness];
2547
2193
  return AgentHarnessIds;
2548
2194
  }
2549
- function syncProfileForCommand(command, config, workspaceId, projectId) {
2195
+ function syncProfileForCommand(command, config, workspaceId, teamId) {
2550
2196
  const harnesses = [...syncTargetHarnesses(command, config)];
2551
2197
  const existing = config.agentSyncProfile;
2552
2198
  const sources = existing?.sources ?? [
2553
2199
  { kind: "personal_kit_defaults", enabled: true },
2554
- ...projectId === undefined ? [] : [{ kind: "project_kits", projectId, enabled: true }]
2200
+ ...teamId === undefined ? [] : [{ kind: "team_kits", teamId, enabled: true }]
2555
2201
  ];
2556
2202
  return {
2557
2203
  id: existing?.id ?? "default",
@@ -2564,7 +2210,7 @@ function syncProfileForCommand(command, config, workspaceId, projectId) {
2564
2210
  }
2565
2211
  function syncKitActiveKits(input) {
2566
2212
  const legacyKit = (kit, scope, index) => {
2567
- const active = scope === "project" ? true : isInstallDefaultKit(kit) || input.harnesses.some((harness) => kitEnabledForHarness(input.userPreferences, harness, kit.id));
2213
+ const active = scope === "team" ? true : isInstallDefaultKit(kit) || input.harnesses.some((harness) => kitEnabledForHarness(input.userPreferences, harness, kit.id));
2568
2214
  const metadata = kit.metadata ?? {};
2569
2215
  const version = isString2(metadata.version) && metadata.version.length > 0 ? metadata.version : "legacy";
2570
2216
  return {
@@ -2573,11 +2219,11 @@ function syncKitActiveKits(input) {
2573
2219
  kitRef: `kit:${kit.slug}`,
2574
2220
  version,
2575
2221
  sha256: digestSha256(encodeUnknownJsonStringSync(kit), "hex"),
2576
- precedence: scope === "project" ? 20 + index : 10 + index
2222
+ precedence: scope === "team" ? 20 + index : 10 + index
2577
2223
  },
2578
2224
  activation: {
2579
2225
  activationId: `legacy:${kit.id}:${scope}`,
2580
- activationType: scope === "project" ? "project_attachment" : "personal_default"
2226
+ activationType: scope === "team" ? "team_attachment" : "personal_default"
2581
2227
  },
2582
2228
  enabled: active,
2583
2229
  gates: kit.agentEnvironment === undefined ? [] : [
@@ -2591,7 +2237,7 @@ function syncKitActiveKits(input) {
2591
2237
  };
2592
2238
  return [
2593
2239
  ...input.workspaceKits.map((kit, index) => legacyKit(kit, "workspace", index)),
2594
- ...input.projectId === undefined ? [] : input.projectKits.map((kit, index) => legacyKit(kit, "project", index))
2240
+ ...input.teamId === undefined ? [] : input.teamKits.map((kit, index) => legacyKit(kit, "team", index))
2595
2241
  ];
2596
2242
  }
2597
2243
  function syncFragments(input) {
@@ -2612,27 +2258,27 @@ function syncFragments(input) {
2612
2258
  fragment: kit.agentEnvironment
2613
2259
  });
2614
2260
  }
2615
- for (const kit of input.projectKits) {
2616
- if (kit.agentEnvironment === undefined || input.projectId === undefined)
2261
+ for (const kit of input.teamKits) {
2262
+ if (kit.agentEnvironment === undefined || input.teamId === undefined)
2617
2263
  continue;
2618
2264
  fragments.push({
2619
- sourceKind: "project_kit",
2265
+ sourceKind: "team_kit",
2620
2266
  workspaceId: input.workspaceId,
2621
- projectId: input.projectId,
2267
+ teamId: input.teamId,
2622
2268
  source: {
2623
- kind: "project_kit",
2269
+ kind: "team_kit",
2624
2270
  kitId: kit.id,
2625
2271
  label: kit.name,
2626
- ...projectKitPolicyMetadata(kit),
2627
- ...kitSourceMetadata(kit, "project_attachment")
2272
+ ...teamKitPolicyMetadata(kit),
2273
+ ...kitSourceMetadata(kit, "team_attachment")
2628
2274
  },
2629
2275
  fragment: kit.agentEnvironment
2630
2276
  });
2631
2277
  }
2632
2278
  return fragments;
2633
2279
  }
2634
- function projectKitPolicyMetadata(kit) {
2635
- const attachment = kit.projectAttachment;
2280
+ function teamKitPolicyMetadata(kit) {
2281
+ const attachment = kit.teamAttachment;
2636
2282
  if (attachment === undefined)
2637
2283
  return {};
2638
2284
  return {
@@ -2657,14 +2303,14 @@ function isInstallDefaultKit(kit) {
2657
2303
  const rollout = isObject(marketplace) && marketplace !== null && "rollout" in marketplace ? marketplace.rollout : undefined;
2658
2304
  return isObject(rollout) && rollout !== null && "installDefault" in rollout && rollout.installDefault === true;
2659
2305
  }
2660
- function previewEnvironmentPlans(environments, command, projectRoot, mcpCredentialName, syncKits = [], userConfig = {}) {
2306
+ function previewEnvironmentPlans(environments, command, teamRoot, mcpCredentialName, syncKits = [], userConfig = {}) {
2661
2307
  const previews = forEach(environments, (environment, index) => gen(function* () {
2662
- const rootDir = command.flags.projectScope ? projectRoot : getOrUndefined(yield* option2(String3("HOME")));
2308
+ const rootDir = command.flags.teamScope ? teamRoot : getOrUndefined(yield* option2(String3("HOME")));
2663
2309
  const { previewAgentEnvironmentPlan } = yield* agentRuntime;
2664
2310
  return yield* previewAgentEnvironmentPlan({
2665
2311
  environment,
2666
2312
  ...syncKits[index] === undefined ? undefined : { syncKit: syncKits[index] },
2667
- scope: command.flags.projectScope ? "project" : "user",
2313
+ scope: command.flags.teamScope ? "project" : "user",
2668
2314
  ...rootDir === undefined ? undefined : { rootDir },
2669
2315
  ...command.flags.noInstall === undefined ? undefined : { noInstall: command.flags.noInstall },
2670
2316
  ...userConfig.approvedSkillSources === undefined ? undefined : { approvedSkillSources: userConfig.approvedSkillSources },
@@ -2811,14 +2457,14 @@ function mixedKitState(kit, harnesses, preferences) {
2811
2457
  return "disabled";
2812
2458
  return "mixed";
2813
2459
  }
2814
- var resolveProjectWriteRoot = gen(function* () {
2460
+ var resolveTeamWriteRoot = gen(function* () {
2815
2461
  const fs = yield* FileSystem;
2816
2462
  const path = yield* Path;
2817
2463
  const cwd = path.resolve(".");
2818
2464
  if ((yield* fs.exists(path.join(cwd, ".git"))) || (yield* fs.exists(path.join(cwd, ".kitman/config.json"))))
2819
2465
  return cwd;
2820
2466
  return yield* new CliError({
2821
- message: "Project sync requires a repo root or local project config in the current working directory."
2467
+ message: "Team sync requires a repo root or local team config in the current working directory."
2822
2468
  });
2823
2469
  });
2824
2470
  function parseHarnesses(values) {
@@ -2945,7 +2591,7 @@ function whoami(args) {
2945
2591
  user: "userId" in me ? me.userId : me.actorId,
2946
2592
  apiUrl,
2947
2593
  activeWorkspace: me.activeWorkspaceId ?? config.activeWorkspaceId ?? me.workspaceId ?? null,
2948
- activeProject: config.activeProjectId ?? null,
2594
+ activeTeam: config.activeTeamId ?? null,
2949
2595
  workspaceRole: me.workspaceRole ?? null,
2950
2596
  credentialType: me.credentialType,
2951
2597
  tokenExpires: session.expiresAt ?? null
@@ -2957,7 +2603,7 @@ function whoami(args) {
2957
2603
  yield* log(`User: ${output.user}`);
2958
2604
  yield* log(`API URL: ${output.apiUrl}`);
2959
2605
  yield* log(`Active workspace: ${output.activeWorkspace ?? "none"}`);
2960
- yield* log(`Active project: ${output.activeProject ?? "none"}`);
2606
+ yield* log(`Active team: ${output.activeTeam ?? "none"}`);
2961
2607
  yield* log(`Workspace role: ${output.workspaceRole ?? "unknown"}`);
2962
2608
  yield* log(`Credential type: ${output.credentialType}`);
2963
2609
  yield* log(`Token expires: ${output.tokenExpires ?? "unknown"}`);
@@ -2973,7 +2619,7 @@ function configCommand(args) {
2973
2619
  const output = {
2974
2620
  apiUrl: normalizeResolvedApiUrl(config.apiUrl),
2975
2621
  activeWorkspace: config.activeWorkspaceId ?? null,
2976
- activeProject: config.activeProjectId ?? null,
2622
+ activeTeam: config.activeTeamId ?? null,
2977
2623
  sources: resolved.sources,
2978
2624
  userConfigPath: resolved.userConfigPath,
2979
2625
  ...resolved.localConfigPath ? { localConfigPath: resolved.localConfigPath } : undefined
@@ -2987,8 +2633,8 @@ function configCommand(args) {
2987
2633
  "API URL": output.apiUrl,
2988
2634
  "Active workspace": output.activeWorkspace ?? "none",
2989
2635
  "Active workspace source": output.sources.activeWorkspace,
2990
- "Active project": output.activeProject ?? "none",
2991
- "Active project source": output.sources.activeProject,
2636
+ "Active team": output.activeTeam ?? "none",
2637
+ "Active team source": output.sources.activeTeam,
2992
2638
  "API URL source": output.sources.apiUrl,
2993
2639
  "User config path": output.userConfigPath,
2994
2640
  ...output.localConfigPath ? { "Local config path": output.localConfigPath } : undefined
@@ -3033,52 +2679,80 @@ function workspaceCommand(args) {
3033
2679
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
3034
2680
  });
3035
2681
  }
3036
- function projectCommand(args) {
2682
+ function teamCommand(args) {
3037
2683
  return gen(function* () {
3038
2684
  const command = parseCommand(args);
3039
2685
  const apiUrl = yield* resolveApiUrl(command.flags.apiUrl);
3040
- const session = yield* requiredSessionForApi(apiUrl);
3041
- const client = yield* clientFor(apiUrl, session);
3042
- if (command.action === "list") {
3043
- const workspaceId = yield* activeWorkspaceId(command.flags);
3044
- const projects = yield* client.listProjects(workspaceId);
3045
- return yield* printRows(projects.items.map(projectRow), command.flags.json);
3046
- }
3047
- if (command.action === "create") {
3048
- const workspaceId = yield* activeWorkspaceId(command.flags);
3049
- const name = requiredArg(command, "project name");
3050
- const project = yield* client.createProject(workspaceId, { name });
3051
- yield* writeActiveProject(apiUrl, project.workspaceId, project.id);
3052
- return yield* printRows([projectRow(project)], command.flags.json);
3053
- }
3054
- if (command.action === "show") {
3055
- const projectRef = requiredArg(command, "project");
3056
- const workspaceId = yield* optionalActiveWorkspaceId(command.flags);
3057
- const project = yield* client.getProject(projectRef, workspaceId ? { workspaceId } : undefined);
3058
- return yield* printRows([projectRow(project)], command.flags.json);
3059
- }
2686
+ const client = yield* clientFor(apiUrl, yield* requiredSessionForApi(apiUrl));
2687
+ const workspaceId = yield* activeWorkspaceId(command.flags);
2688
+ if (command.action === "list")
2689
+ return yield* printRows((yield* client.listTeams(workspaceId)).items.map(teamRow), command.flags.json);
3060
2690
  if (command.action === "use") {
3061
- const projectRef = requiredArg(command, "project");
3062
- const workspaceId = yield* optionalActiveWorkspaceId(command.flags);
3063
- const project = yield* client.getProject(projectRef, workspaceId ? { workspaceId } : undefined);
3064
- yield* writeActiveProject(apiUrl, project.workspaceId, project.id, command.flags.local);
2691
+ const team = yield* teamNamed(client, workspaceId, requiredArg(command, "team"));
2692
+ yield* writeActiveTeam(apiUrl, workspaceId, team.id, command.flags.local);
3065
2693
  if (command.flags.json)
3066
- return yield* printRows([projectRow(project)], true);
3067
- yield* log(`Active project: ${project.name}`);
2694
+ return yield* printRows([teamRow(team)], true);
2695
+ yield* log(`Active team: ${team.name}`);
3068
2696
  return;
3069
2697
  }
3070
- if (command.action === "repo-bindings") {
3071
- const projectRef = command.args[0] ?? (yield* activeProjectId(command.flags));
3072
- const repoBindings = yield* client.listRepoBindings(projectRef);
2698
+ if (command.action === "private" || command.action === "open") {
2699
+ const team = yield* teamNamed(client, workspaceId, requiredArg(command, "team"));
2700
+ const visibility = command.action === "private" ? "private" : "workspace";
2701
+ yield* client.updateTeam(workspaceId, team.id, { visibility });
2702
+ return yield* log(visibility === "private" ? `${team.key} is private: only its members see it` : `${team.key} is open to the Workspace`);
2703
+ }
2704
+ if (command.action === "invite") {
2705
+ const team = yield* teamNamed(client, workspaceId, requiredArg(command, "team"));
2706
+ const userId = command.args[1];
2707
+ if (userId === undefined)
2708
+ return yield* new CliError({ message: "team invite requires <team> <person>" });
2709
+ yield* client.addTeamMember(workspaceId, team.id, { userId });
2710
+ return yield* log(`Invited ${userId} to ${team.key}`);
2711
+ }
2712
+ return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
2713
+ });
2714
+ }
2715
+ var teamIn = (teams, ref) => {
2716
+ const team = teams.find((candidate) => candidate.id === ref || candidate.key === ref.toUpperCase());
2717
+ return team === undefined ? fail4(new CliError({ message: `No Team ${ref} in the Workspace` })) : succeed5(team);
2718
+ };
2719
+ var teamNamed = (client, workspaceId, ref) => flatMap2(client.listTeams(workspaceId), (teams) => teamIn(teams.items, ref));
2720
+ var ownerTo = (client, workspaceId, command) => gen(function* () {
2721
+ const to = command.flags.to;
2722
+ if (to === undefined)
2723
+ return yield* new CliError({ message: `${command.group} move requires --to <team> or --to workspace` });
2724
+ if (to === "workspace")
2725
+ return { change: { team: null }, label: "the Workspace" };
2726
+ const team = yield* teamNamed(client, workspaceId, to);
2727
+ return { change: { team: team.id }, label: team.key };
2728
+ });
2729
+ var printMoved = (command, moved, label) => command.flags.json ? printJson(moved) : log(`Moved ${moved.id} to ${label}`);
2730
+ function repoCommand(args) {
2731
+ return gen(function* () {
2732
+ const command = parseCommand(args);
2733
+ const apiUrl = yield* resolveApiUrl(command.flags.apiUrl);
2734
+ const client = yield* clientFor(apiUrl, yield* requiredSessionForApi(apiUrl));
2735
+ const workspaceId = yield* activeWorkspaceId(command.flags);
2736
+ const teams = (yield* client.listTeams(workspaceId)).items;
2737
+ const ownerOf = (teamId) => teams.find((team) => team.id === teamId)?.key ?? "Workspace";
2738
+ if (command.action === "list") {
2739
+ const repoBindings = yield* client.listRepoBindings(workspaceId);
3073
2740
  if (command.flags.json)
3074
2741
  return yield* printJson(repoBindings);
3075
- return yield* printRows(repoBindings.items.map(repoBindingRow), false);
2742
+ return yield* printRows(repoBindings.items.map((binding) => ({ ...repoBindingRow(binding), team: ownerOf(binding.teamId) })), false);
3076
2743
  }
3077
- if (command.action === "add-repo-binding") {
3078
- const projectRef = command.args[0] ?? (yield* activeProjectId(command.flags));
2744
+ if (command.action === "add") {
2745
+ const team = command.flags.team === undefined ? undefined : yield* teamIn(teams, command.flags.team);
3079
2746
  const input = command.flags.fromCurrentRepo === true ? yield* repoBindingInputFromCurrentRepo : repoBindingInput2(command);
3080
- const repoBinding = yield* client.createRepoBinding(projectRef, input);
3081
- return yield* printRows([repoBindingRow(repoBinding)], command.flags.json);
2747
+ const repoBinding = yield* client.createRepoBinding(workspaceId, {
2748
+ ...input,
2749
+ ...team === undefined ? undefined : { teamId: team.id }
2750
+ });
2751
+ return yield* printRows([{ ...repoBindingRow(repoBinding), team: ownerOf(repoBinding.teamId) }], command.flags.json);
2752
+ }
2753
+ if (command.action === "move") {
2754
+ const owner = yield* ownerTo(client, workspaceId, command);
2755
+ return yield* printMoved(command, yield* client.moveRepoBinding(workspaceId, requiredArg(command, "a repository"), owner.change), owner.label);
3082
2756
  }
3083
2757
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
3084
2758
  });
@@ -3109,7 +2783,30 @@ function runnerCommand(args) {
3109
2783
  yield* log(`Runner bootstrap prepared for ${output.profile}; no canonical state was created.`);
3110
2784
  return;
3111
2785
  }
2786
+ if (command.action === "start") {
2787
+ if (command.flags.backend !== undefined)
2788
+ return yield* new CliError({ message: "runner start accepts only a Runner identity" });
2789
+ const teamId = command.flags.team === undefined ? undefined : { teamId: command.flags.team };
2790
+ const credentialFile = command.flags.credentialFile ?? getOrUndefined(yield* option2(String3("KITMAN_RUNNER_CREDENTIAL_FILE")));
2791
+ if (credentialFile !== undefined)
2792
+ return yield* startCanonicalHerdrRunnerCommand({
2793
+ ...yield* readRunnerCredentialFile(credentialFile),
2794
+ ...teamId
2795
+ });
2796
+ return yield* startCanonicalHerdrRunnerCommand({
2797
+ apiUrl: yield* resolveApiUrl(command.flags.apiUrl),
2798
+ runnerId: command.flags.runnerId ?? requiredArg(command, "Runner"),
2799
+ ...teamId
2800
+ });
2801
+ }
3112
2802
  const apiUrl = yield* resolveApiUrl(command.flags.apiUrl);
2803
+ if (command.action === "status" || command.action === "logs" || command.action === "restart" || command.action === "remove") {
2804
+ const runnerId = command.flags.runnerId ?? command.args[0];
2805
+ return yield* machineRunnerCommand(command.action, apiUrl, {
2806
+ follow: command.flags.follow === true,
2807
+ ...runnerId === undefined ? undefined : { runnerId }
2808
+ });
2809
+ }
3113
2810
  const session = yield* requiredSessionForApi(apiUrl);
3114
2811
  const workspaceId = yield* activeWorkspaceId(command.flags);
3115
2812
  const client = yield* clientFor(apiUrl, session);
@@ -3149,21 +2846,21 @@ function runnerCommand(args) {
3149
2846
  return yield* new CliError({ message: "runner grant requires add, list, or remove and --runner <runner-id>" });
3150
2847
  const runner = yield* resolveRunnerForCommand(client, workspaceId, runnerRef);
3151
2848
  if (grantAction === "list") {
3152
- const response = yield* client.listRunnerProjectGrants(runner.id);
2849
+ const response = yield* client.listRunnerTeamGrants(runner.id);
3153
2850
  return yield* printRows(response.items.map((grant) => ({
3154
2851
  id: grant.id,
3155
2852
  runnerId: grant.runnerId,
3156
2853
  workspaceId: grant.workspaceId,
3157
- projectId: grant.projectId,
2854
+ teamId: grant.teamId,
3158
2855
  createdAt: grant.createdAt
3159
2856
  })), command.flags.json);
3160
2857
  }
3161
2858
  if (grantAction === "add" || grantAction === "remove") {
3162
- const projectId = command.flags.project ?? command.flags.scopeId ?? command.args[2];
3163
- if (!projectId)
3164
- return yield* new CliError({ message: `runner grant ${grantAction} requires --project <project-id>` });
2859
+ const teamId = command.flags.team ?? command.flags.scopeId ?? command.args[2];
2860
+ if (!teamId)
2861
+ return yield* new CliError({ message: `runner grant ${grantAction} requires --team <team-id>` });
3165
2862
  if (grantAction === "add") {
3166
- const response = yield* client.addRunnerProjectGrant(runner.id, { projectId });
2863
+ const response = yield* client.addRunnerTeamGrant(runner.id, { teamId });
3167
2864
  if (command.flags.json)
3168
2865
  return yield* printJson(response);
3169
2866
  return yield* printRows([
@@ -3171,15 +2868,15 @@ function runnerCommand(args) {
3171
2868
  id: response.id,
3172
2869
  runnerId: response.runnerId,
3173
2870
  workspaceId: response.workspaceId,
3174
- projectId: response.projectId,
2871
+ teamId: response.teamId,
3175
2872
  status: "granted"
3176
2873
  }
3177
2874
  ], false);
3178
2875
  }
3179
- const response = yield* client.removeRunnerProjectGrant(runner.id, projectId);
2876
+ const response = yield* client.removeRunnerTeamGrant(runner.id, teamId);
3180
2877
  if (command.flags.json)
3181
2878
  return yield* printJson(response);
3182
- return yield* printRows([{ runnerId: runner.id, projectId, status: response.removed ? "removed" : "not_found" }], false);
2879
+ return yield* printRows([{ runnerId: runner.id, teamId, status: response.removed ? "removed" : "not_found" }], false);
3183
2880
  }
3184
2881
  return yield* new CliError({ message: `Unknown runner grant action: ${grantAction}` });
3185
2882
  }
@@ -3192,9 +2889,8 @@ function runnerCommand(args) {
3192
2889
  const ownership = runnerOwnershipFromCommand(command, false);
3193
2890
  const response = yield* client.registerRunner(workspaceId, {
3194
2891
  name,
3195
- ...command.flags.agentIdentityId === undefined ? undefined : { agentIdentityId: command.flags.agentIdentityId },
3196
2892
  ...ownership === undefined ? undefined : { ownership },
3197
- ...command.flags.project === undefined ? undefined : { projectId: command.flags.project },
2893
+ ...command.flags.team === undefined ? undefined : { teamId: command.flags.team },
3198
2894
  capabilities: herdr ? (yield* agentRuntime).herdrRunnerCapabilities({ harnesses: yield* (yield* agentRuntime).detectHarnesses }) : loopRunnerCapabilities(),
3199
2895
  labels: herdr ? ["kitman-runner", "kitman-loop", "herdr"] : ["kitman-runner", "kitman-loop"]
3200
2896
  });
@@ -3218,26 +2914,11 @@ function runnerCommand(args) {
3218
2914
  const runnerRef = command.flags.runnerId ?? requiredArg(command, "Runner");
3219
2915
  const runner = yield* resolveRunnerForCommand(client, workspaceId, runnerRef);
3220
2916
  const response = yield* client.heartbeatRunner(runner.id, {
3221
- healthStatus: "healthy",
3222
2917
  capabilities: runner.capabilities,
3223
2918
  labels: runner.labels
3224
2919
  });
3225
2920
  return yield* printRows([runnerRow(response)], command.flags.json);
3226
2921
  }
3227
- if (command.action === "start") {
3228
- if (command.flags.backend !== undefined)
3229
- return yield* new CliError({ message: "runner start accepts only a Runner identity" });
3230
- const runnerRef = command.flags.runnerId ?? requiredArg(command, "Runner");
3231
- const runner = yield* resolveRunnerForCommand(client, workspaceId, runnerRef);
3232
- return yield* startCanonicalHerdrRunnerCommand({
3233
- apiUrl,
3234
- accessToken: session.accessToken,
3235
- workspaceId,
3236
- ...command.flags.project === undefined ? undefined : { projectId: command.flags.project },
3237
- runner,
3238
- client
3239
- });
3240
- }
3241
2922
  if (command.action === "doctor") {
3242
2923
  const runners = yield* client.listRunners(workspaceId);
3243
2924
  if (command.flags.runnerId === undefined && command.args.length === 0 && command.flags.backend === undefined)
@@ -3261,6 +2942,16 @@ function runnerCommand(args) {
3261
2942
  var SandboxHostProbe = Reference("kitman/cli/SandboxHostProbe", {
3262
2943
  defaultValue: () => flatMap2(agentRuntime, (runtime) => runtime.commandHostProbe)
3263
2944
  });
2945
+ var OpenRunnerRpc = Reference("kitman/cli/OpenRunnerRpc", {
2946
+ defaultValue: () => (options) => gen(function* () {
2947
+ const { makeRunnerRpcClient, runnerRpcUrl, runnerWebSocketProtocol } = yield* promise(() => import("./chunk-nmgr9w6v.js"));
2948
+ return yield* makeRunnerRpcClient({
2949
+ runnerId: options.runnerId,
2950
+ bearer: options.bearer,
2951
+ protocol: runnerWebSocketProtocol(runnerRpcUrl(options.apiUrl))
2952
+ });
2953
+ })
2954
+ });
3264
2955
  var runnerSettings = gen(function* () {
3265
2956
  const optional = (name) => String3(name).pipe(option2, map3((value) => getOrUndefined(value)?.trim()));
3266
2957
  const concurrency = Number(yield* optional("KITMAN_RUNNER_CONCURRENCY")) || 1;
@@ -3315,27 +3006,40 @@ function startCanonicalHerdrRunnerCommand(input) {
3315
3006
  superviseReplica,
3316
3007
  verificationExecutor
3317
3008
  } = yield* agentRuntime;
3318
- const { makeRunnerRpcClient, runnerRpcUrl, runnerWebSocketProtocol } = yield* promise(() => import("./chunk-xnzxpert.js"));
3009
+ const openRunnerRpc = yield* OpenRunnerRpc;
3319
3010
  const settings = yield* runnerSettings;
3320
3011
  const { concurrency, fullAccess, sshAgentAvailable } = settings;
3321
- const personal = input.runner.ownership.kind === "personal";
3322
- const host = yield* probeSandboxHost(yield* yield* SandboxHostProbe, undefined, settings.dockerImage === undefined ? {} : { dockerImage: settings.dockerImage });
3012
+ const fs = yield* FileSystem;
3013
+ const container = input.credential !== undefined || (yield* fs.exists("/.dockerenv").pipe(orElseSucceed(() => false))) || (yield* fs.exists("/run/.containerenv").pipe(orElseSucceed(() => false)));
3014
+ const reported = { ...kitmanVersion === undefined ? undefined : { version: kitmanVersion }, container };
3015
+ const credential = input.credential ?? (yield* startupStep("load the Runner credential", "10 seconds", storedHerdrRunnerCredential(input.apiUrl, input.runnerId)));
3016
+ if (credential === undefined)
3017
+ return yield* new RunnerStopped({ problem: credentialMissing(input.runnerId) });
3018
+ const controlPlane = yield* createHerdrRunnerClient({ apiUrl: input.apiUrl, runnerId: input.runnerId, credential });
3019
+ const runner = yield* startupStep("first heartbeat", "30 seconds", stopOnRefusal(controlPlane.client.heartbeatRunner(input.runnerId, reported)));
3020
+ const personal = runner.ownership.kind === "personal";
3021
+ const host = yield* startupStep("check the sandbox", "30 seconds", probeSandboxHost(yield* yield* SandboxHostProbe, undefined, settings.dockerImage === undefined ? {} : { dockerImage: settings.dockerImage }));
3323
3022
  const sandbox = personal ? settings.sandbox : host.backends.includes("docker") ? "docker" : "srt";
3324
- if (personal && !host.backends.includes(sandbox))
3325
- return yield* new CliError({
3326
- 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.`
3327
- });
3328
- const repoBinding = input.projectId === undefined ? undefined : (yield* input.client.listRepoBindings(input.projectId)).items.find((binding) => binding.status === "active");
3023
+ if (personal && sandbox !== "host" && !host.backends.includes(sandbox)) {
3024
+ const problem = sandboxMissing(sandbox, host.unavailable.find((entry) => entry.backend === sandbox)?.missing ?? ["unsupported"]);
3025
+ yield* startupStep("report the Runner Problem", "30 seconds", stopOnRefusal(controlPlane.client.heartbeatRunner(input.runnerId, { ...reported, problems: [problem] })));
3026
+ return yield* new RunnerStopped({ problem });
3027
+ }
3028
+ const teamId = input.teamId;
3029
+ const repoBinding = teamId === undefined ? undefined : (yield* startupStep("read the repo binding", "30 seconds", controlPlane.client.listRepoBindings(runner.workspaceId))).items.find((binding) => binding.status === "active" && binding.teamId === teamId);
3329
3030
  const harnesses = yield* detectHarnesses;
3330
3031
  if (harnesses.harnesses.length > 0)
3331
3032
  yield* harnessSync([
3332
3033
  "sync",
3333
3034
  ...harnesses.harnesses.map((harness) => harness === "claude" ? "claude-code" : harness),
3334
3035
  "--workspace",
3335
- input.workspaceId,
3036
+ runner.workspaceId,
3336
3037
  "--api-url",
3337
3038
  input.apiUrl
3338
- ]).pipe(catch_((error2) => error(`Harness sync failed; the Runner starts on the previous one: ${error2.message}`)));
3039
+ ], controlPlane.client).pipe(timeoutOrElse({
3040
+ duration: "60 seconds",
3041
+ orElse: () => fail4(new CliError({ message: "harness sync did not finish within 60 seconds" }))
3042
+ }), catch_((error2) => error(`Harness sync failed; the Runner starts on the previous one: ${error2.message}`)));
3339
3043
  const advertisedCapabilities = repoBinding === undefined ? herdrRunnerCapabilities({ harnesses, fullAccess, sandbox: host, shared: !personal }) : herdrRunnerCapabilitiesForRepoBinding(repoBinding, {
3340
3044
  harnesses,
3341
3045
  fullAccess,
@@ -3348,23 +3052,16 @@ function startCanonicalHerdrRunnerCommand(input) {
3348
3052
  resourceLimits: { ...advertisedCapabilities.resourceLimits, maxConcurrentRuns: concurrency }
3349
3053
  };
3350
3054
  assertHerdrRunnerCapability(capabilities);
3351
- const credential = yield* ensureHerdrRunnerCredential({
3352
- apiUrl: input.apiUrl,
3353
- accessToken: input.accessToken,
3354
- runner: input.runner
3355
- });
3356
- const controlPlane = yield* createHerdrRunnerClient({ apiUrl: input.apiUrl, runnerId: input.runner.id, credential });
3357
- const fs = yield* FileSystem;
3358
3055
  const path = yield* Path;
3359
3056
  const runnerRootDir = yield* herdrRunnerRootDir;
3360
3057
  yield* fs.makeDirectory(path.join(runnerRootDir, ".kitman", "worktrees"), { recursive: true });
3361
3058
  yield* fs.makeDirectory(bundleCacheDir, { recursive: true });
3362
- const replica = yield* controlPlane.client.registerRunnerReplica(input.runner.id, {
3059
+ const replica = yield* startupStep("register the replica", "30 seconds", stopOnRefusal(controlPlane.client.registerRunnerReplica(input.runnerId, {
3363
3060
  name: `replica-${createId()}`,
3364
3061
  capacity: { maxConcurrentRuns: concurrency },
3365
3062
  telemetry: { activeWorkUnits: 0 }
3366
- });
3367
- const backend = herdrExecutorSurface({ runnerId: input.runner.id, session: personal ? "default" : "own" });
3063
+ })));
3064
+ const backend = herdrExecutorSurface({ runnerId: input.runnerId, session: personal ? "default" : "own" });
3368
3065
  const uid = process.getuid?.();
3369
3066
  const gid = process.getgid?.();
3370
3067
  const docker = host.backends.includes("docker") && settings.dockerImage !== undefined ? {
@@ -3375,41 +3072,46 @@ function startCanonicalHerdrRunnerCommand(input) {
3375
3072
  const root = herdrAgentBackendLayer({
3376
3073
  client: () => backend.executionClient,
3377
3074
  sandboxBackends: host.backends,
3378
- ...docker === undefined ? undefined : { docker: { ...docker, runnerId: input.runner.id } }
3379
- }).pipe(provide2(herdrHarnessLayer({ client: () => backend.executionClient })), provide2(layer10));
3075
+ ...docker === undefined ? undefined : { docker: { ...docker, runnerId: input.runnerId } }
3076
+ }).pipe(provide2(herdrHarnessLayer({ client: () => backend.executionClient })), provide2(layer));
3380
3077
  const activeSlots = new Set;
3078
+ let claimsInFlight = 0;
3079
+ const updater = runnerUpdater({
3080
+ apiUrl: input.apiUrl,
3081
+ runnerId: input.runnerId,
3082
+ platform: process.platform,
3083
+ inFlight: sync(() => activeSlots.size + claimsInFlight)
3084
+ });
3381
3085
  return yield* gen(function* () {
3382
3086
  const agentBackend = yield* AgentBackend;
3383
3087
  const spawner = yield* ChildProcessSpawner;
3384
- yield* controlPlane.client.heartbeatRunner(input.runner.id, {
3385
- healthStatus: "healthy",
3386
- capabilities,
3387
- labels: input.runner.labels
3388
- });
3389
- yield* controlPlane.client.heartbeatRunnerReplica(input.runner.id, replica.id, {
3088
+ yield* startupStep("heartbeat", "30 seconds", stopOnRefusal(controlPlane.client.heartbeatRunner(input.runnerId, { ...reported, problems: [], capabilities, labels: runner.labels }).pipe(andThen(controlPlane.client.heartbeatRunnerReplica(input.runnerId, replica.id, {
3390
3089
  healthStatus: "healthy",
3391
3090
  drainState: "accepting",
3392
3091
  capacity: { maxConcurrentRuns: concurrency },
3393
3092
  telemetry: { activeWorkUnits: 0 }
3394
- });
3093
+ })))));
3395
3094
  return yield* scoped(gen(function* () {
3396
- const rpc = yield* makeRunnerRpcClient({
3397
- runnerId: input.runner.id,
3398
- bearer: controlPlane.rpcBearer.pipe(provideService(Crypto, yield* Crypto)),
3399
- protocol: runnerWebSocketProtocol(runnerRpcUrl(input.apiUrl))
3400
- });
3095
+ const rpc = yield* startupStep("open the Runner RPC", "30 seconds", stopOnRefusal(openRunnerRpc({
3096
+ apiUrl: input.apiUrl,
3097
+ runnerId: input.runnerId,
3098
+ bearer: controlPlane.rpcBearer.pipe(provideService(Crypto, yield* Crypto))
3099
+ }).pipe(tap((opened) => opened.settledRuns([])))));
3401
3100
  const signals = yield* makeReplicaSignals;
3402
3101
  const bundles = yield* kitmanBundles({
3403
3102
  fetch: (digest) => rpc.bundle(digest).pipe(map3((found) => found.bundle))
3404
3103
  });
3405
3104
  const checkouts = runCheckouts(path.join(runnerRootDir, ".kitman"));
3406
3105
  const platform = yield* context();
3106
+ const updated = yield* make5();
3107
+ const scope2 = yield* scope;
3407
3108
  yield* listenForSignals({
3408
3109
  rpc,
3409
3110
  runnerReplicaId: replica.id,
3410
3111
  signals,
3411
3112
  checkouts,
3412
- reconnectAfter: "2 seconds"
3113
+ reconnectAfter: "2 seconds",
3114
+ update: (version) => container ? void_2 : updater.update(version).pipe(catch_((error) => fail3(updated, error)), provideContext(platform), forkIn(scope2), asVoid)
3413
3115
  }).pipe(forkScoped);
3414
3116
  const authStore = yield* userAuthPath;
3415
3117
  const runnerState = {
@@ -3420,7 +3122,7 @@ function startCanonicalHerdrRunnerCommand(input) {
3420
3122
  };
3421
3123
  const sharedSandbox = personal ? undefined : {
3422
3124
  runner: runnerState,
3423
- ...docker !== undefined ? { backend: "docker", docker, runnerId: input.runner.id } : { backend: host.backends.includes("srt") ? "srt" : undefined }
3125
+ ...docker !== undefined ? { backend: "docker", docker, runnerId: input.runnerId } : { backend: host.backends.includes("srt") ? "srt" : undefined }
3424
3126
  };
3425
3127
  const runPackageProgram = programProcessExecutor({
3426
3128
  bundles,
@@ -3435,12 +3137,14 @@ function startCanonicalHerdrRunnerCommand(input) {
3435
3137
  preflight: (mcp) => kitmanMcpPreflight(mcp).pipe(provideService(ChildProcessSpawner, spawner))
3436
3138
  });
3437
3139
  const heartbeatOf = (work) => rpc.heartbeat({ workUnitId: work.id, lease: leaseOf(work) });
3140
+ const storage = yield* provide3(service(HttpClient), layer2);
3438
3141
  const runAgentStep = agentStepExecutor({
3439
3142
  rootDir: path.join(runnerRootDir, ".kitman"),
3440
3143
  heartbeat: heartbeatOf,
3441
- checkpoints: rpcStepCheckpoints(rpc, signals),
3442
- runAgent: (work, worktree, session) => agentBackend.execute({
3443
- runnerId: input.runner.id,
3144
+ checkpoints: rpcStepCheckpoints(rpc, signals, storage),
3145
+ transcripts: (work, entries) => rpc.appendTranscript({ workUnitId: work.id, entries }),
3146
+ runAgent: (work, worktree, session, steered) => agentBackend.execute({
3147
+ runnerId: input.runnerId,
3444
3148
  sharedRunner: !personal,
3445
3149
  attempt: runnerStepAttemptFromAgentStep(work),
3446
3150
  workspace: { path: worktree },
@@ -3456,38 +3160,49 @@ function startCanonicalHerdrRunnerCommand(input) {
3456
3160
  settled: heartbeatOf(work).pipe(map3((state) => state.settled || state.outputSubmitted === true)),
3457
3161
  steering: {
3458
3162
  ...signals.steers(work.id),
3459
- report: (note, delivered) => rpc.reportSteer({ workUnitId: work.id, messageId: note.messageId, delivered }).pipe(catch_((error) => logWarning("runner_steer_report_failed", error.message)))
3163
+ report: (note, delivered) => andThen(delivered ? steered(note) : void_2, rpc.reportSteer({ workUnitId: work.id, messageId: note.messageId, delivered }).pipe(catch_((error) => logWarning("runner_steer_report_failed", error.message))))
3460
3164
  }
3461
3165
  }).pipe(catch_((refusal) => succeed5({ status: "failed", reason: refusal.reason, summary: refusal.summary })), map3(agentStepRunOutcome))
3462
3166
  });
3463
3167
  const runVerification = verificationExecutor({
3464
3168
  rootDir: path.join(runnerRootDir, ".kitman"),
3465
3169
  known: rpcKnownEvidence(rpc),
3170
+ runFiles: { client: rpc, storage },
3466
3171
  ...sharedSandbox === undefined ? undefined : { sandbox: { ...sharedSandbox, apiUrl: input.apiUrl } }
3467
3172
  });
3468
3173
  yield* acquireRelease(backend.start(), () => backend.stop());
3469
3174
  const control = (capacitySlot) => ({
3470
- claim: () => rpc.claim({
3471
- ...input.projectId === undefined ? undefined : { projectId: input.projectId },
3472
- runnerReplicaId: replica.id,
3473
- capacitySlot,
3474
- idempotencyKey: `${replica.id}:${createId()}`
3475
- }).pipe(tap((result) => sync(() => result.workUnit === undefined ? activeSlots.delete(capacitySlot) : activeSlots.add(capacitySlot)))),
3476
- heartbeatReplica: () => controlPlane.client.heartbeatRunner(input.runner.id, {
3477
- healthStatus: "healthy",
3175
+ claim: () => suspend(() => {
3176
+ if (!updater.claiming())
3177
+ return succeed5({});
3178
+ claimsInFlight += 1;
3179
+ return rpc.claim({
3180
+ ...input.teamId === undefined ? undefined : { teamId: input.teamId },
3181
+ runnerReplicaId: replica.id,
3182
+ capacitySlot,
3183
+ idempotencyKey: `${replica.id}:${createId()}`
3184
+ }).pipe(tap((result) => sync(() => {
3185
+ if (result.workUnit !== undefined)
3186
+ activeSlots.add(capacitySlot);
3187
+ })), ensuring(sync(() => claimsInFlight -= 1)));
3188
+ }),
3189
+ finished: () => sync(() => activeSlots.delete(capacitySlot)),
3190
+ heartbeatReplica: () => controlPlane.client.heartbeatRunner(input.runnerId, {
3191
+ ...reported,
3192
+ problems: updater.problems(),
3478
3193
  capabilities,
3479
- labels: input.runner.labels
3480
- }).pipe(flatMap2(() => controlPlane.client.heartbeatRunnerReplica(input.runner.id, replica.id, {
3194
+ labels: runner.labels
3195
+ }).pipe(flatMap2(() => controlPlane.client.heartbeatRunnerReplica(input.runnerId, replica.id, {
3481
3196
  healthStatus: "healthy",
3482
- drainState: "accepting",
3197
+ drainState: updater.claiming() ? "accepting" : "draining",
3483
3198
  capacity: { maxConcurrentRuns: concurrency },
3484
3199
  telemetry: { activeWorkUnits: activeSlots.size }
3485
3200
  }).pipe(asVoid))),
3486
3201
  heartbeatWork: (workUnitId, lease) => rpc.heartbeat({ workUnitId, lease }),
3487
- complete: (completion) => rpc.complete(completion).pipe(tap(() => sync(() => activeSlots.delete(completion.lease.capacitySlot)))),
3488
- release: (workUnitId, lease) => rpc.release({ workUnitId, lease }).pipe(tap(() => sync(() => activeSlots.delete(lease.capacitySlot))))
3202
+ complete: (completion) => rpc.complete(completion),
3203
+ release: (workUnitId, lease) => rpc.release({ workUnitId, lease })
3489
3204
  });
3490
- return yield* superviseReplica({
3205
+ const supervised = superviseReplica({
3491
3206
  slots: Array.from({ length: concurrency }, (_, index) => `slot-${index + 1}`),
3492
3207
  control,
3493
3208
  executor: {
@@ -3510,9 +3225,11 @@ function startCanonicalHerdrRunnerCommand(input) {
3510
3225
  },
3511
3226
  signals
3512
3227
  });
3513
- }).pipe(ensuring(controlPlane.client.stopRunnerReplica(input.runner.id, replica.id).pipe(catch_(() => void_2), asVoid))));
3514
- }).pipe(provide3(provideMerge(root, layer10)));
3515
- });
3228
+ return yield* raceFirst(stopWhenRefused(supervised), _await(updated));
3229
+ }));
3230
+ }).pipe(ensuring(ignore(controlPlane.client.stopRunnerReplica(input.runnerId, replica.id))), provide3(provideMerge(root, layer)));
3231
+ }).pipe(catchTag("RunnerStopped", ({ problem }) => error(`Runner stopped (${problem.code}): ${problem.message}
3232
+ Fix: ${problem.fix}`)));
3516
3233
  }
3517
3234
  function checkHerdrRunnerReadiness(runner) {
3518
3235
  const notReady = (code) => ({ backend: "herdr", status: "not_ready", code, runnerId: runner.id });
@@ -3542,7 +3259,7 @@ function mcpCommand(args) {
3542
3259
  if (command.action === "run") {
3543
3260
  if (command.args.length > 0)
3544
3261
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
3545
- const { runMcpServerCommand } = yield* promise(() => import("./chunk-64cz9grx.js"));
3262
+ const { runMcpServerCommand } = yield* promise(() => import("./chunk-m1086bf9.js"));
3546
3263
  return yield* runMcpServerCommand({ flags: command.flags });
3547
3264
  }
3548
3265
  if (command.action !== "setup")
@@ -3741,7 +3458,7 @@ function kitCommand(args) {
3741
3458
  const source = requiredArg(command, "kit source");
3742
3459
  const version = command.args[1];
3743
3460
  const scopeType = kitInstallScope(command.flags.scopeType);
3744
- const scopeId = command.flags.scopeId ?? (scopeType === "project" ? yield* activeProjectId(command.flags) : undefined);
3461
+ const scopeId = command.flags.scopeId ?? (scopeType === "team" ? yield* activeTeamId(command.flags) : undefined);
3745
3462
  if ((scopeType === "user" || scopeType === "team") && scopeId === undefined)
3746
3463
  return yield* new CliError({ message: `kit install --scope-type ${scopeType} requires --scope-id` });
3747
3464
  const install = yield* client.installKit(workspaceId, {
@@ -3778,32 +3495,57 @@ function kitCommand(args) {
3778
3495
  return;
3779
3496
  }
3780
3497
  if (command.action === "attach") {
3781
- const projectRef = requiredArg(command, "project");
3498
+ const teamRef = requiredArg(command, "team");
3782
3499
  const kitRef = command.args[1];
3783
3500
  if (!kitRef)
3784
3501
  return yield* new CliError({ message: "kit attach requires kit" });
3785
3502
  const workspaceId = yield* optionalActiveWorkspaceId(command.flags);
3786
- const result = yield* client.attachKit(projectRef, kitRef, workspaceId ? { workspaceId } : undefined);
3503
+ const result = yield* client.attachKit(teamRef, kitRef, workspaceId ? { workspaceId } : undefined);
3787
3504
  if (command.flags.json)
3788
- return yield* printRows([{ projectId: result.projectId, kitId: result.kitId, status: result.status }], true);
3789
- yield* log(`Kit attached: ${result.kitId} to ${result.projectId}`);
3505
+ return yield* printRows([{ teamId: result.teamId, kitId: result.kitId, status: result.status }], true);
3506
+ yield* log(`Kit attached: ${result.kitId} to ${result.teamId}`);
3790
3507
  return;
3791
3508
  }
3792
3509
  if (command.action === "detach") {
3793
- const projectRef = requiredArg(command, "project");
3510
+ const teamRef = requiredArg(command, "team");
3794
3511
  const kitRef = command.args[1];
3795
3512
  if (!kitRef)
3796
3513
  return yield* new CliError({ message: "kit detach requires kit" });
3797
3514
  const workspaceId = yield* optionalActiveWorkspaceId(command.flags);
3798
- const result = yield* client.detachKit(projectRef, kitRef, workspaceId ? { workspaceId } : undefined);
3515
+ const result = yield* client.detachKit(teamRef, kitRef, workspaceId ? { workspaceId } : undefined);
3799
3516
  if (command.flags.json)
3800
- return yield* printRows([{ projectId: result.projectId, kitId: result.kitId, status: result.status }], true);
3801
- yield* log(`Kit detached: ${result.kitId} from ${result.projectId}`);
3517
+ return yield* printRows([{ teamId: result.teamId, kitId: result.kitId, status: result.status }], true);
3518
+ yield* log(`Kit detached: ${result.kitId} from ${result.teamId}`);
3802
3519
  return;
3803
3520
  }
3804
3521
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
3805
3522
  });
3806
3523
  }
3524
+ function assignTask(client, command) {
3525
+ return gen(function* () {
3526
+ const { to, loop, json } = command.flags;
3527
+ if (to === undefined === (loop === undefined))
3528
+ return yield* new CliError({ message: "task assign takes one of --to <person> or --loop <loop>" });
3529
+ const workspaceId = yield* activeWorkspaceId(command.flags);
3530
+ const task = yield* client.getTeamTask(workspaceId, requiredArg(command, "task"));
3531
+ if (loop !== undefined) {
3532
+ const loops = yield* client.listProgramLoops(workspaceId, { teamId: task.team.id });
3533
+ if (!loops.items.some((candidate) => candidate.id === loop))
3534
+ return yield* new CliError({
3535
+ message: `${task.key}'s Team has no Loop ${loop}. Its Loops: ${loops.items.map((candidate) => candidate.id).join(", ") || "none"}`
3536
+ });
3537
+ const started = yield* client.assignTaskLoop(workspaceId, task.id, { loop });
3538
+ return yield* json ? printJson(started) : log(`Started Run ${started.run.id} of ${loop} for ${task.key}`);
3539
+ }
3540
+ const team = yield* client.getTeam(workspaceId, task.team.id);
3541
+ const named = (to ?? "").toLowerCase();
3542
+ const person = [...team.members, ...team.nonMembers].find((candidate) => candidate.id === to || candidate.email.toLowerCase() === named || candidate.name.toLowerCase() === named);
3543
+ if (person === undefined)
3544
+ return yield* new CliError({ message: `${to} is not a member of this Workspace` });
3545
+ const assigned = yield* client.assignTeamTask(workspaceId, task.id, { assigneeId: person.id });
3546
+ return yield* json ? printJson(assigned) : log(`Assigned ${task.key} to ${person.name}`);
3547
+ });
3548
+ }
3807
3549
  function taskCommand(args) {
3808
3550
  return gen(function* () {
3809
3551
  const command = parseCommand(args);
@@ -3811,16 +3553,18 @@ function taskCommand(args) {
3811
3553
  const session = yield* requiredSessionForApi(apiUrl);
3812
3554
  const client = yield* clientFor(apiUrl, session);
3813
3555
  if (command.action === "list") {
3814
- const projectId = yield* activeProjectId(command.flags);
3556
+ const teamId = yield* activeTeamId(command.flags);
3815
3557
  const tasks = [];
3816
- let page = yield* client.listTasks(projectId);
3558
+ let page = yield* client.listTasks(teamId);
3817
3559
  tasks.push(...page.items);
3818
3560
  while (command.flags.status !== undefined && page.nextCursor !== undefined) {
3819
- page = yield* client.listTasks(projectId, { cursor: page.nextCursor, limit: 100 });
3561
+ page = yield* client.listTasks(teamId, { cursor: page.nextCursor, limit: 100 });
3820
3562
  tasks.push(...page.items);
3821
3563
  }
3822
3564
  return yield* printRows(tasks.flatMap((task) => inState(task, command.flags.status) ? [taskRow(task)] : []), command.flags.json);
3823
3565
  }
3566
+ if (command.action === "assign")
3567
+ return yield* assignTask(client, command);
3824
3568
  if (command.action === "show") {
3825
3569
  const taskRef = requiredArg(command, "task");
3826
3570
  const task = yield* client.getTask(taskRef);
@@ -3975,6 +3719,11 @@ function memoryCommand(args) {
3975
3719
  yield* log(`Memory ${memoryTransitionPastTense(command.action)}: ${response.memory.id}`);
3976
3720
  return;
3977
3721
  }
3722
+ if (command.action === "move") {
3723
+ const workspaceId = yield* activeWorkspaceId(command.flags);
3724
+ const owner = yield* ownerTo(client, workspaceId, command);
3725
+ return yield* printMoved(command, yield* client.moveMemory(workspaceId, requiredArg(command, "memory"), owner.change), owner.label);
3726
+ }
3978
3727
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
3979
3728
  });
3980
3729
  }
@@ -3985,8 +3734,8 @@ function proposalCommand(args) {
3985
3734
  const session = yield* requiredSessionForApi(apiUrl);
3986
3735
  const client = yield* clientFor(apiUrl, session);
3987
3736
  if (command.action === "list") {
3988
- const projectId = command.flags.project ?? (yield* activeProjectId(command.flags));
3989
- const response = yield* client.listProposals(projectId, {
3737
+ const teamId = command.flags.team ?? (yield* activeTeamId(command.flags));
3738
+ const response = yield* client.listProposals(teamId, {
3990
3739
  ...command.flags.status === undefined ? undefined : { status: validateProposalStatus(command.flags.status) },
3991
3740
  ...command.flags.targetType === undefined ? undefined : { targetType: command.flags.targetType },
3992
3741
  ...command.flags.limit === undefined ? undefined : { limit: command.flags.limit }
@@ -4024,8 +3773,8 @@ function decisionCommand(args) {
4024
3773
  const session = yield* requiredSessionForApi(apiUrl);
4025
3774
  const client = yield* clientFor(apiUrl, session);
4026
3775
  if (command.action === "list") {
4027
- const projectId = command.flags.project ?? (yield* activeProjectId(command.flags));
4028
- const response = yield* client.listDecisions("project", projectId, {
3776
+ const teamId = command.flags.team ?? (yield* activeTeamId(command.flags));
3777
+ const response = yield* client.listDecisions("team", teamId, {
4029
3778
  status: command.flags.status === undefined ? "active" : validateDecisionStatus(command.flags.status),
4030
3779
  ...command.flags.limit === undefined ? undefined : { limit: command.flags.limit }
4031
3780
  });
@@ -4036,6 +3785,11 @@ function decisionCommand(args) {
4036
3785
  const decision = yield* client.getDecision(decisionId);
4037
3786
  return yield* printRows([decisionRow(decision)], command.flags.json);
4038
3787
  }
3788
+ if (command.action === "move") {
3789
+ const workspaceId = yield* activeWorkspaceId(command.flags);
3790
+ const owner = yield* ownerTo(client, workspaceId, command);
3791
+ return yield* printMoved(command, yield* client.moveDecision(workspaceId, requiredArg(command, "decision"), owner.change), owner.label);
3792
+ }
4039
3793
  return yield* new CliError({ message: `Unknown command: ${args.join(" ")}` });
4040
3794
  });
4041
3795
  }
@@ -4071,18 +3825,18 @@ function programLoopCommand(command) {
4071
3825
  const client = yield* loopClientFor(yield* resolveApiUrl(command.flags.apiUrl));
4072
3826
  const print = (value, lines) => command.flags.json ? printJson(value) : forEach(lines(value), (line) => log(line), { discard: true });
4073
3827
  if (command.action === "list") {
4074
- const projectId = yield* optionalActiveProjectId(command.flags);
4075
- const listed = yield* client.listProgramLoops(yield* activeWorkspaceId(command.flags), projectId === undefined ? {} : { projectId });
3828
+ const teamId = yield* optionalActiveTeamId(command.flags);
3829
+ const listed = yield* client.listProgramLoops(yield* activeWorkspaceId(command.flags), teamId === undefined ? {} : { teamId });
4076
3830
  return yield* print(listed, (value) => value.items.flatMap(programLoopLines));
4077
3831
  }
4078
3832
  if (command.action === "starts") {
4079
- const started = yield* client.listAutomaticStarts(yield* activeProjectId(command.flags), command.flags.limit === undefined ? {} : { limit: command.flags.limit });
3833
+ const started = yield* client.listAutomaticStarts(yield* activeTeamId(command.flags), command.flags.limit === undefined ? {} : { limit: command.flags.limit });
4080
3834
  return yield* print(started, (value) => value.items.length === 0 ? ["No Event has started a Loop here yet"] : value.items.map((start) => `${start.at} ${start.eventType} -> ${start.loop}: ${start.runId === undefined ? `not started: ${start.rejection ?? "unknown"}` : `Run ${start.runId}`}`));
4081
3835
  }
4082
3836
  if (command.action === "show") {
4083
3837
  const loop = requiredArg(command, "a Loop");
4084
- const projectId = yield* optionalActiveProjectId(command.flags);
4085
- const shown = yield* client.showProgramLoop(yield* activeWorkspaceId(command.flags), loop, projectId === undefined ? {} : { projectId });
3838
+ const teamId = yield* optionalActiveTeamId(command.flags);
3839
+ const shown = yield* client.showProgramLoop(yield* activeWorkspaceId(command.flags), loop, teamId === undefined ? {} : { teamId });
4086
3840
  return yield* print(shown, (value) => value.resolved === undefined ? [`${loop} resolves to nothing here`] : programLoopLines(value.resolved));
4087
3841
  }
4088
3842
  return yield* new CliError({ message: `Unknown loop command: ${command.action}` });
@@ -4108,8 +3862,8 @@ function agentCommand(args) {
4108
3862
  resolvedField(agent, "effort")
4109
3863
  ].filter((field) => field !== "").join(" ");
4110
3864
  if (command.action === "list") {
4111
- const projectId = yield* optionalActiveProjectId(command.flags);
4112
- const listed = yield* client.listProgramAgents(yield* activeWorkspaceId(command.flags), projectId === undefined ? {} : { projectId });
3865
+ const teamId = yield* optionalActiveTeamId(command.flags);
3866
+ const listed = yield* client.listProgramAgents(yield* activeWorkspaceId(command.flags), teamId === undefined ? {} : { teamId });
4113
3867
  return yield* print(listed, (value) => value.items.map(agentLine));
4114
3868
  }
4115
3869
  return yield* new CliError({ message: `Unknown agent command: ${command.action}. Use kitman agent list.` });
@@ -4132,21 +3886,21 @@ function loopCatalogCommand(args) {
4132
3886
  const command = parseCommand(args);
4133
3887
  const apiUrl = yield* resolveApiUrl(command.flags.apiUrl);
4134
3888
  const client = yield* loopClientFor(apiUrl);
4135
- const projectId = yield* activeProjectId(command.flags);
3889
+ const teamId = yield* activeTeamId(command.flags);
4136
3890
  if (command.action === "list" || command.action === "catalog") {
4137
- const response = yield* client.requestJson("listLoops", `/loops${loopListQuery(projectId, command.flags.limit)}`);
3891
+ const response = yield* client.requestJson("listLoops", `/loops${loopListQuery(teamId, command.flags.limit)}`);
4138
3892
  return yield* printOperationalResult(command, response);
4139
3893
  }
4140
3894
  const loopRef = requiredArg(command, "Loop");
4141
3895
  if (command.action === "show") {
4142
- const response = yield* client.requestJson("getLoop", `/loops/${encodeURIComponent(loopRef)}?projectId=${encodeURIComponent(projectId)}`);
3896
+ const response = yield* client.requestJson("getLoop", `/loops/${encodeURIComponent(loopRef)}?teamId=${encodeURIComponent(teamId)}`);
4143
3897
  return yield* printOperationalResult(command, response);
4144
3898
  }
4145
3899
  if (command.action === "validate" || command.action === "test") {
4146
3900
  const input = yield* operationalInput(command, `loop ${command.action}`);
4147
3901
  const response = yield* client.requestJson(`loop${capitalize(command.action)}`, `/loops/${encodeURIComponent(loopRef)}/${command.action}`, {
4148
3902
  method: "POST",
4149
- body: { projectId, loopRef, ...input }
3903
+ body: { teamId, loopRef, ...input }
4150
3904
  });
4151
3905
  return yield* printOperationalResult(command, response);
4152
3906
  }
@@ -4164,9 +3918,9 @@ var launchContext = (flags) => gen(function* () {
4164
3918
  const read = (args) => git(args).pipe(orElseSucceed(() => ""));
4165
3919
  const remoteUrl = yield* read(["remote", "get-url", "origin"]);
4166
3920
  const branch = yield* read(["branch", "--show-current"]);
4167
- const projectId = yield* optionalActiveProjectId(flags);
3921
+ const teamId = yield* optionalActiveTeamId(flags);
4168
3922
  return {
4169
- ...projectId === undefined ? undefined : { projectId },
3923
+ ...teamId === undefined ? undefined : { teamId },
4170
3924
  ...remoteUrl === "" ? undefined : { remoteUrl },
4171
3925
  ...branch === "" ? undefined : { branch }
4172
3926
  };
@@ -4297,12 +4051,14 @@ var inboxItemLine = (item) => {
4297
4051
  `${item.subject.kind}:${item.subject.id}`
4298
4052
  ].join(" ");
4299
4053
  };
4300
- var inboxSubjectKinds = ["task", "run", "proposal"];
4054
+ var inboxSubjectKinds = ["task", "run", "proposal", "memory", "project_update", "runner"];
4301
4055
  var inboxSubjectOf = (ref) => {
4302
4056
  const separator = ref.indexOf(":");
4303
4057
  const kind = inboxSubjectKinds.find((known) => known === ref.slice(0, separator));
4304
4058
  const id = ref.slice(separator + 1);
4305
- return kind !== undefined && id !== "" ? succeed5({ kind, id }) : fail4(new CliError({ message: `${ref} is not an Inbox item; name it as task:<id>, run:<id> or proposal:<id>` }));
4059
+ return kind !== undefined && id !== "" ? succeed5({ kind, id }) : fail4(new CliError({
4060
+ message: `${ref} is not an Inbox item; name it as task:<id>, run:<id>, proposal:<id> or project_update:<id>`
4061
+ }));
4306
4062
  };
4307
4063
  var inboxActions = new Set([
4308
4064
  "list",
@@ -4322,7 +4078,7 @@ function inboxCommand(args) {
4322
4078
  const client = yield* loopClientFor(yield* resolveApiUrl(command.flags.apiUrl));
4323
4079
  const workspaceId = yield* activeWorkspaceId(command.flags);
4324
4080
  if (command.action === "my-teams") {
4325
- const view = yield* client.showMyTeams(workspaceId, command.flags.project === undefined ? {} : { projectId: command.flags.project });
4081
+ const view = yield* client.showMyTeams(workspaceId, command.flags.team === undefined ? {} : { teamId: command.flags.team });
4326
4082
  if (command.flags.json)
4327
4083
  return yield* printJson(view);
4328
4084
  return yield* forEach(myTeamsSections.flatMap(([section, title]) => [
@@ -4365,6 +4121,93 @@ function inboxCommand(args) {
4365
4121
  return yield* command.flags.json ? printJson(counts) : log(`${counts.items} in your Inbox, ${counts.unread} unread`);
4366
4122
  });
4367
4123
  }
4124
+ var transcriptEventLine = (event) => {
4125
+ switch (event.kind) {
4126
+ case "message":
4127
+ return `${event.role}: ${event.text}`;
4128
+ case "tool_call":
4129
+ return `tool ${event.name} ${encodeJsonStringSync(event.arguments)}`;
4130
+ case "tool_result":
4131
+ return `${event.isError === true ? "tool error" : "tool result"}: ${event.output}`;
4132
+ case "file":
4133
+ return `${event.change} ${event.path}`;
4134
+ case "question":
4135
+ case "answer":
4136
+ case "steer":
4137
+ return `${event.kind}: ${event.text}`;
4138
+ case "lifecycle":
4139
+ return `session ${event.state}`;
4140
+ }
4141
+ };
4142
+ var transcriptLine = (entry) => `${entry.sequence} ${transcriptEventLine(entry.event)}`;
4143
+ var transcriptAfter = (client, runId, stepAttemptId, after) => gen(function* () {
4144
+ const entries = [];
4145
+ for (let cursor = after;cursor !== undefined; ) {
4146
+ const page = yield* client.readTranscript(runId, stepAttemptId, cursor === 0 ? {} : { after: cursor });
4147
+ entries.push(...page.items);
4148
+ cursor = page.next;
4149
+ }
4150
+ return entries;
4151
+ });
4152
+ var askedAttempts = (attempts, step, attempt) => {
4153
+ const latest = new Map;
4154
+ for (const candidate of attempts)
4155
+ if ((step === undefined || candidate.stepKey === step) && (attempt === undefined || candidate.attempt === attempt) && candidate.attempt >= (latest.get(candidate.stepKey)?.attempt ?? 0))
4156
+ latest.set(candidate.stepKey, candidate);
4157
+ return [...latest.values()].sort((left, right) => left.sequence - right.sequence);
4158
+ };
4159
+ function transcriptCommand(args) {
4160
+ return gen(function* () {
4161
+ const command = parseCommand(args);
4162
+ const runId = requiredArg(command, "a Run");
4163
+ const client = yield* loopClientFor(yield* resolveApiUrl(command.flags.apiUrl));
4164
+ const { stepKey, attempt } = command.flags;
4165
+ const asked = askedAttempts((yield* client.listStepAttempts(runId)).items, stepKey, attempt);
4166
+ if (asked.length === 0)
4167
+ return yield* new CliError({
4168
+ message: `Run ${runId} has no ${stepKey === undefined ? "Step attempts" : `attempt of ${stepKey}`}${attempt === undefined ? "" : ` numbered ${attempt}`}`
4169
+ });
4170
+ const items = yield* forEach(asked, (found) => map3(transcriptAfter(client, runId, found.id, 0), (entries) => ({
4171
+ stepKey: found.stepKey,
4172
+ attempt: found.attempt,
4173
+ stepAttemptId: found.id,
4174
+ entries
4175
+ })));
4176
+ if (command.flags.json)
4177
+ return yield* log(encodeJsonStringSync({ items }));
4178
+ for (const item of items) {
4179
+ yield* log(`Step ${item.stepKey}, attempt ${item.attempt}`);
4180
+ yield* forEach(item.entries, (entry) => log(transcriptLine(entry)), { discard: true });
4181
+ }
4182
+ });
4183
+ }
4184
+ var settledRunStatuses = new Set(["completed", "failed", "canceled", "expired"]);
4185
+ var followTranscripts = (client, runId, options) => gen(function* () {
4186
+ const read = new Map;
4187
+ const refused = new Set;
4188
+ let status;
4189
+ const show = (value, line) => log(options.json ? encodeJsonStringSync(value) : line);
4190
+ for (;; ) {
4191
+ const run = yield* client.getRun(runId);
4192
+ if (run.status !== status) {
4193
+ status = run.status;
4194
+ yield* show({ run: { id: run.id, status: run.status } }, `Run ${run.id}: ${run.status}`);
4195
+ }
4196
+ for (const attempt of (yield* client.listStepAttempts(runId)).items) {
4197
+ if (refused.has(attempt.id))
4198
+ continue;
4199
+ const step = { stepKey: attempt.stepKey, attempt: attempt.attempt };
4200
+ const entries = yield* transcriptAfter(client, runId, attempt.id, read.get(attempt.id) ?? 0).pipe(catchIf((error) => error instanceof KitmanClientError && error.status === 403, (error) => as(andThen(sync(() => refused.add(attempt.id)), show({ ...step, stepAttemptId: attempt.id, refused: error.responseMessage ?? error.message }, `${attempt.stepKey} #${attempt.attempt}: ${error.responseMessage ?? error.message}`)), [])));
4201
+ for (const entry of entries) {
4202
+ read.set(attempt.id, entry.sequence);
4203
+ yield* show({ ...step, ...entry }, `${attempt.stepKey} #${attempt.attempt} ${transcriptLine(entry)}`);
4204
+ }
4205
+ }
4206
+ if (settledRunStatuses.has(run.status))
4207
+ return;
4208
+ yield* sleep(options.pollIntervalMs);
4209
+ }
4210
+ });
4368
4211
  function runCommand(args) {
4369
4212
  return gen(function* () {
4370
4213
  const command = parseCommand(args);
@@ -4396,17 +4239,35 @@ function runCommand(args) {
4396
4239
  ]);
4397
4240
  }
4398
4241
  if (command.action === "list") {
4399
- const { project, loop, states, limit } = command.flags;
4242
+ const { team, loop, states, limit } = command.flags;
4400
4243
  const listed = yield* client.listRuns(yield* activeWorkspaceId(command.flags), {
4401
- ...project === undefined ? undefined : { projectId: project },
4244
+ ...team === undefined ? undefined : { teamId: team },
4402
4245
  ...loop === undefined ? undefined : { loop },
4403
4246
  ...states === undefined ? undefined : { states },
4404
4247
  ...limit === undefined ? undefined : { limit }
4405
4248
  });
4406
4249
  return yield* print(listed, (value) => value.items.length === 0 ? ["No Runs"] : value.items.map(runListLine));
4407
4250
  }
4251
+ if (command.action === "file") {
4252
+ const [verb, fileRunId, fileId] = command.args;
4253
+ if (verb !== "get" || fileRunId === undefined || fileId === undefined)
4254
+ return yield* new CliError({ message: "Usage: kitman run file get <run> <id> [--out path]" });
4255
+ const listed = yield* client.listRunFiles(fileRunId);
4256
+ const file = "files" in listed ? listed.files.find((candidate) => candidate.id === fileId) : undefined;
4257
+ if (file === undefined)
4258
+ return yield* new CliError({ message: `Run ${fileRunId} has no file ${fileId} you can read` });
4259
+ const { urls } = yield* client.runFileUrls(fileRunId, { ids: [fileId], use: "download" });
4260
+ const url = urls.find((candidate) => candidate.id === fileId)?.url;
4261
+ if (url === undefined)
4262
+ return yield* new CliError({ message: `Kitman gave no link to file ${fileId}` });
4263
+ const out = command.flags.out ?? localFileName(file.name, file.id);
4264
+ yield* downloadVerified(url, out, file.sha256);
4265
+ return yield* print({ id: file.id, path: out }, (value) => [`Wrote ${value.id} to ${value.path}`]);
4266
+ }
4408
4267
  const runId = requiredArg(command, "a Run");
4409
4268
  switch (command.action) {
4269
+ case "files":
4270
+ return yield* print(yield* client.listRunFiles(runId, command.flags.stepKey === undefined ? {} : { step: command.flags.stepKey }), runFileLines);
4410
4271
  case "show": {
4411
4272
  const shown = {
4412
4273
  ...yield* client.showRun(runId),
@@ -4461,6 +4322,11 @@ function runCommand(args) {
4461
4322
  });
4462
4323
  return yield* print(answered, (question) => [`Answered ${question.stepKey}: ${value}`]);
4463
4324
  }
4325
+ case "follow":
4326
+ return yield* followTranscripts(client, runId, {
4327
+ json: command.flags.json === true,
4328
+ pollIntervalMs: command.flags.pollIntervalMs ?? 1000
4329
+ });
4464
4330
  case "steer": {
4465
4331
  const text = command.args.slice(1).join(" ");
4466
4332
  if (text === "")
@@ -4506,38 +4372,56 @@ function runCommand(args) {
4506
4372
  }
4507
4373
  });
4508
4374
  }
4509
- var currentRepoBinding = (client, projectId) => gen(function* () {
4375
+ var byteSize = (bytes) => {
4376
+ const units = ["B", "KiB", "MiB", "GiB"];
4377
+ const exponent = Math.min(units.length - 1, Math.floor(Math.log(Math.max(bytes, 1)) / Math.log(1024)));
4378
+ return exponent === 0 ? `${bytes} B` : `${(bytes / 1024 ** exponent).toFixed(1)} ${units[exponent]}`;
4379
+ };
4380
+ var runFileLines = (listed) => {
4381
+ if (!("files" in listed))
4382
+ return [`${listed.count} files; reading them needs access to the content of the Run's repositories`];
4383
+ if (listed.files.length === 0)
4384
+ return ["No files"];
4385
+ return listed.files.map((file) => [
4386
+ file.id,
4387
+ file.name,
4388
+ file.madeBy,
4389
+ file.kind,
4390
+ byteSize(file.byteSize),
4391
+ file.reason === undefined ? file.status : `${file.status}: ${file.status === "refused" && isRunFileRefusalReason(file.reason) ? runFileRefusalWords({ ...file, reason: file.reason }) : file.reason}`
4392
+ ].join(" "));
4393
+ };
4394
+ var currentRepoBinding = (client, workspaceId) => gen(function* () {
4510
4395
  const remote = yield* (yield* Git)(["remote", "get-url", "origin"]).pipe(orElseSucceed(() => ""));
4511
4396
  if (remote === "")
4512
4397
  return yield* new CliError({ message: "Run this inside a git checkout with an origin" });
4513
- const bindings = yield* client.listRepoBindings(projectId);
4514
- const binding = bindings.items.find((candidate) => repositoryKey(candidate.repositoryUrl ?? `https://${candidate.host}/${candidate.owner}/${candidate.name}`) === repositoryKey(remote));
4398
+ const binding = (yield* client.listRepoBindings(workspaceId)).items.find((candidate) => candidate.status === "active" && repositoryKey(candidate.repositoryUrl ?? `https://${candidate.host}/${candidate.owner}/${candidate.name}`) === repositoryKey(remote));
4515
4399
  if (binding === undefined)
4516
- return yield* new CliError({ message: `No repository of the Project matches ${remote.trim()}` });
4400
+ return yield* new CliError({ message: `No repository of the Workspace matches ${remote.trim()}` });
4517
4401
  return binding;
4518
4402
  });
4519
4403
  function verifyCommand(args) {
4520
4404
  return gen(function* () {
4521
4405
  const command = parseCommand(args.length === 1 ? [...args, "list"] : args);
4522
4406
  const client = yield* loopClientFor(yield* resolveApiUrl(command.flags.apiUrl));
4523
- const projectId = yield* activeProjectId(command.flags);
4524
- const binding = yield* currentRepoBinding(client, projectId);
4407
+ const workspaceId = yield* activeWorkspaceId(command.flags);
4408
+ const binding = yield* currentRepoBinding(client, workspaceId);
4525
4409
  const print = (value, lines) => command.flags.json ? printJson(value) : forEach(lines(value), (line) => log(line), { discard: true });
4526
4410
  switch (command.action) {
4527
4411
  case "list":
4528
- return yield* print(yield* client.listApprovedVerifications(projectId, binding.id), (listed) => listed.items.length === 0 ? [`No approved verifications for ${binding.owner}/${binding.name}`] : listed.items.map((check) => `${check.name}: ${check.command}${check.workingDirectory === undefined ? "" : ` (in ${check.workingDirectory})`}`));
4412
+ return yield* print(yield* client.listApprovedVerifications(workspaceId, binding.id), (listed) => listed.items.length === 0 ? [`No approved verifications for ${binding.owner}/${binding.name}`] : listed.items.map((check) => `${check.name}: ${check.command}${check.workingDirectory === undefined ? "" : ` (in ${check.workingDirectory})`}`));
4529
4413
  case "add": {
4530
4414
  const [name, ...words] = command.args;
4531
4415
  if (name === undefined || words.length === 0)
4532
4416
  return yield* new CliError({ message: "verify add requires a name and a command" });
4533
- return yield* print(yield* client.approveVerification(projectId, binding.id, {
4417
+ return yield* print(yield* client.approveVerification(workspaceId, binding.id, {
4534
4418
  name,
4535
4419
  command: words.join(" "),
4536
4420
  ...command.flags.workingDirectory === undefined ? undefined : { workingDirectory: command.flags.workingDirectory }
4537
4421
  }), (check) => [`Approved ${check.name}: ${check.command}`]);
4538
4422
  }
4539
4423
  case "remove":
4540
- return yield* print(yield* client.removeVerification(projectId, binding.id, requiredArg(command, "a verification")), (removed) => [`Removed ${removed.removed}`]);
4424
+ return yield* print(yield* client.removeVerification(workspaceId, binding.id, requiredArg(command, "a verification")), (removed) => [`Removed ${removed.removed}`]);
4541
4425
  default:
4542
4426
  return yield* new CliError({ message: `Unknown verify command: ${command.action}` });
4543
4427
  }
@@ -4549,13 +4433,13 @@ function secretCommand(args) {
4549
4433
  const command = parseCommand(args.length === 1 ? [...args, "list"] : args);
4550
4434
  const client = yield* loopClientFor(yield* resolveApiUrl(command.flags.apiUrl));
4551
4435
  const workspaceId = yield* activeWorkspaceId(command.flags);
4552
- const projectId = command.flags.project === undefined ? undefined : (yield* client.getProject(command.flags.project, { workspaceId })).id;
4553
- const scope = projectId === undefined ? {} : { projectId };
4554
- const where = projectId === undefined ? "the Workspace" : `Project ${command.flags.project}`;
4436
+ const teamId = command.flags.team === undefined ? undefined : (yield* teamNamed(client, workspaceId, command.flags.team)).id;
4437
+ const scope = teamId === undefined ? {} : { teamId };
4438
+ const where = teamId === undefined ? "the Workspace" : `Team ${command.flags.team}`;
4555
4439
  const print = (value, lines) => command.flags.json ? printJson(value) : forEach(lines(value), (line) => log(line), { discard: true });
4556
4440
  switch (command.action) {
4557
4441
  case "list":
4558
- return yield* print(yield* client.listSecrets(workspaceId, scope), (listed) => listed.items.length === 0 ? [`No Secrets in ${where}`] : listed.items.map((secret) => `${secret.name} ${secret.scope === "workspace" ? "workspace" : `project ${secret.projectId}`} set by ${secret.setByActorId} at ${secret.setAt}`));
4442
+ return yield* print(yield* client.listSecrets(workspaceId, scope), (listed) => listed.items.length === 0 ? [`No Secrets in ${where}`] : listed.items.map((secret) => `${secret.name} ${secret.scope === "workspace" ? "workspace" : `team ${secret.teamId}`} set by ${secret.setByActorId} at ${secret.setAt}`));
4559
4443
  case "set": {
4560
4444
  const name = requiredArg(command, "a Secret name");
4561
4445
  if (command.args.length > 1)
@@ -4571,13 +4455,17 @@ function secretCommand(args) {
4571
4455
  }
4572
4456
  case "delete":
4573
4457
  return yield* print(yield* client.deleteSecret(workspaceId, requiredArg(command, "a Secret name"), scope), (removed) => [`Deleted ${removed.removed} from ${where}`]);
4458
+ case "move": {
4459
+ const owner = yield* ownerTo(client, workspaceId, command);
4460
+ return yield* printMoved(command, yield* client.moveSecret(workspaceId, requiredArg(command, "a Secret name"), owner.change, scope), owner.label);
4461
+ }
4574
4462
  default:
4575
4463
  return yield* new CliError({ message: `Unknown secret command: ${command.action}` });
4576
4464
  }
4577
4465
  });
4578
4466
  }
4579
4467
  var PackageBuilder = Reference("kitman/cli/PackageBuilder", {
4580
- defaultValue: () => (directory) => flatMap2(promise(() => import("./chunk-hpfcwnd9.js")), (build) => build.buildPackage(directory))
4468
+ defaultValue: () => (directory) => flatMap2(promise(() => import("./chunk-djtdn35f.js")), (build) => build.buildPackage(directory))
4581
4469
  });
4582
4470
  var optionalEnv = (name) => String3(name).pipe(option2, map3(getOrUndefined));
4583
4471
  var ciIdToken = (apiUrl) => gen(function* () {
@@ -4591,7 +4479,7 @@ var ciIdToken = (apiUrl) => gen(function* () {
4591
4479
  const url = new URL(requestUrl);
4592
4480
  const refused = (reason) => new CliError({ message: `GitHub Actions issued no ID token: ${reason}` });
4593
4481
  url.searchParams.set("audience", apiUrl.replace(/\/+$/, ""));
4594
- const response = yield* send(get3(url, { headers: { authorization: `Bearer ${requestToken}` } }), (cause) => refused(String(cause)));
4482
+ const response = yield* send(get2(url, { headers: { authorization: `Bearer ${requestToken}` } }), (cause) => refused(String(cause)));
4595
4483
  if (response.status < 200 || response.status >= 300)
4596
4484
  return yield* refused(`${response.status}; does the job have id-token: write?`);
4597
4485
  const body = yield* response.text.pipe(mapError((error) => refused(error.message)));
@@ -4604,12 +4492,12 @@ var packageScope = (command, options = {}) => gen(function* () {
4604
4492
  const token = options.ci === true ? yield* ciIdToken(apiUrl) : undefined;
4605
4493
  const client = token === undefined ? yield* loopClientFor(apiUrl) : yield* createSdkClient({ apiUrl, session: { accessToken: token }, clientId: ClientId });
4606
4494
  const workspaceId = yield* activeWorkspaceId(command.flags);
4607
- const projectId = command.flags.project === undefined || token !== undefined ? command.flags.project : (yield* client.getProject(command.flags.project, { workspaceId })).id;
4495
+ const teamId = command.flags.team === undefined || token !== undefined ? command.flags.team : (yield* teamNamed(client, workspaceId, command.flags.team)).id;
4608
4496
  return {
4609
4497
  client,
4610
4498
  workspaceId,
4611
- scope: projectId === undefined ? {} : { projectId },
4612
- where: projectId !== undefined ? `Project ${command.flags.project}` : token === undefined ? "the Workspace" : "the Deploy Binding's layer"
4499
+ scope: teamId === undefined ? {} : { teamId },
4500
+ where: teamId !== undefined ? `Team ${command.flags.team}` : token === undefined ? "the Workspace" : "the Deploy Binding's layer"
4613
4501
  };
4614
4502
  });
4615
4503
  var changeLines = (resource, changes) => [
@@ -4618,9 +4506,9 @@ var changeLines = (resource, changes) => [
4618
4506
  ...changes.removed.map((id) => ` - ${resource} ${id}`)
4619
4507
  ];
4620
4508
  var scheduleProblems = {
4621
- missing: "names no Project, which a Workspace Package must",
4622
- not_allowed: "names a Project, though a Project Package's Schedules run in its own",
4623
- unknown: "names a Project this Workspace does not have"
4509
+ missing: "names no Team, which a Workspace Package must",
4510
+ not_allowed: "names a Team, though a Team Package's Schedules run in its own",
4511
+ unknown: "names a Team this Workspace does not have"
4624
4512
  };
4625
4513
  var removedKitRecords = (shown, kind) => ({
4626
4514
  created: [],
@@ -4637,9 +4525,9 @@ var conflictLine = (conflict) => {
4637
4525
  switch (conflict.kind) {
4638
4526
  case "id_taken":
4639
4527
  return ` conflict: ${conflict.resource} ${conflict.id} is declared by Package ${conflict.package} in this layer`;
4640
- case "not_for_project":
4641
- return ` conflict: a Project Package cannot declare ${conflict.declaration}`;
4642
- case "schedule_project":
4528
+ case "not_for_team":
4529
+ return ` conflict: a Team Package cannot declare ${conflict.declaration}`;
4530
+ case "schedule_team":
4643
4531
  return ` conflict: schedule ${conflict.key} ${scheduleProblems[conflict.problem]}`;
4644
4532
  case "loop_unresolved":
4645
4533
  return ` conflict: schedule ${conflict.key} starts Loop ${conflict.loop}, which will not resolve after this deploy`;
@@ -4649,8 +4537,8 @@ var conflictLine = (conflict) => {
4649
4537
  return ` conflict: kit ${conflict.key} ${kitSourceProblems[conflict.problem]}`;
4650
4538
  case "kit_policy_disabled":
4651
4539
  return ` conflict: kit assignment ${conflict.key} sets Workspace policy, which this Workspace's rollout does not allow`;
4652
- case "kit_project_unknown":
4653
- return ` conflict: kit assignment ${conflict.key} names Project ${conflict.project}, which this Workspace does not have`;
4540
+ case "kit_team_unknown":
4541
+ return ` conflict: kit assignment ${conflict.key} names Team ${conflict.team}, which this Workspace does not have`;
4654
4542
  }
4655
4543
  };
4656
4544
  var driftValue = (drift, side) => encodeJsonStringSync(toJsonValue(drift[side]));
@@ -4767,19 +4655,6 @@ function devCommand(args) {
4767
4655
  return yield* new CliError({ message: `Run ${settled.run.id} ${settled.reason}` });
4768
4656
  });
4769
4657
  }
4770
- var workspaceRepoBinding = (client, workspaceId, projectId) => gen(function* () {
4771
- const remote = yield* (yield* Git)(["remote", "get-url", "origin"]).pipe(orElseSucceed(() => ""));
4772
- if (remote === "")
4773
- return yield* new CliError({ message: "Run kitman deploy bind inside a git checkout with an origin" });
4774
- const projects = (yield* client.listProjects(workspaceId)).items.map((project) => project.id);
4775
- const ordered = projectId === undefined ? projects : [projectId, ...projects.filter((id) => id !== projectId)];
4776
- for (const id of ordered) {
4777
- const binding = (yield* client.listRepoBindings(id)).items.find((candidate) => repositoryKey(candidate.repositoryUrl ?? `https://${candidate.host}/${candidate.owner}/${candidate.name}`) === repositoryKey(remote));
4778
- if (binding !== undefined)
4779
- return binding;
4780
- }
4781
- return yield* new CliError({ message: `No repository of the Workspace matches ${remote.trim()}` });
4782
- });
4783
4658
  function deployBindingCommand(args) {
4784
4659
  return gen(function* () {
4785
4660
  const command = parseCommand(args);
@@ -4787,12 +4662,12 @@ function deployBindingCommand(args) {
4787
4662
  const print = (value, lines) => command.flags.json ? printJson(value) : forEach(lines(value), (line) => log(line), { discard: true });
4788
4663
  switch (command.action) {
4789
4664
  case "bindings":
4790
- return yield* print(yield* client.listDeployBindings(workspaceId), (listed) => listed.items.length === 0 ? ["No Deploy Bindings in the Workspace"] : listed.items.map((binding) => `${binding.id} ${binding.packageName} ${binding.layer === "workspace" ? "workspace" : `project ${binding.projectId}`} ${binding.repository} ${binding.branch} by ${binding.createdByUserId}`));
4665
+ return yield* print(yield* client.listDeployBindings(workspaceId), (listed) => listed.items.length === 0 ? ["No Deploy Bindings in the Workspace"] : listed.items.map((binding) => `${binding.id} ${binding.packageName} ${binding.layer === "workspace" ? "workspace" : `team ${binding.teamId}`} ${binding.repository} ${binding.branch} by ${binding.createdByUserId}`));
4791
4666
  case "bind": {
4792
4667
  const branch = command.flags.branch;
4793
4668
  if (branch === undefined)
4794
4669
  return yield* new CliError({ message: "deploy bind requires --branch <branch>" });
4795
- const repoBinding = yield* workspaceRepoBinding(client, workspaceId, scope.projectId);
4670
+ const repoBinding = yield* currentRepoBinding(client, workspaceId);
4796
4671
  const built = yield* (yield* PackageBuilder)(".");
4797
4672
  return yield* print(yield* client.createDeployBinding(workspaceId, {
4798
4673
  repoBindingId: repoBinding.id,
@@ -4868,22 +4743,22 @@ function constraintCommand(args) {
4868
4743
  return gen(function* () {
4869
4744
  const command = parseCommand(args.length === 1 ? [...args, "list"] : args);
4870
4745
  const client = yield* loopClientFor(yield* resolveApiUrl(command.flags.apiUrl));
4871
- const projectId = yield* activeProjectId(command.flags);
4872
- const binding = yield* currentRepoBinding(client, projectId);
4746
+ const workspaceId = yield* activeWorkspaceId(command.flags);
4747
+ const binding = yield* currentRepoBinding(client, workspaceId);
4873
4748
  const print = (value, lines) => command.flags.json ? printJson(value) : forEach(lines(value), (line) => log(line), { discard: true });
4874
4749
  switch (command.action) {
4875
4750
  case "list":
4876
- return yield* print(yield* client.listStandingConstraints(projectId, binding.id), (listed) => listed.items.length === 0 ? [`No standing constraints for ${binding.owner}/${binding.name}`] : listed.items.map((constraint) => `${constraint.id}: ${constraint.text}`));
4751
+ return yield* print(yield* client.listStandingConstraints(workspaceId, binding.id), (listed) => listed.items.length === 0 ? [`No standing constraints for ${binding.owner}/${binding.name}`] : listed.items.map((constraint) => `${constraint.id}: ${constraint.text}`));
4877
4752
  case "add": {
4878
4753
  const text = command.args.join(" ");
4879
4754
  if (text.trim() === "")
4880
4755
  return yield* new CliError({ message: "constraint add requires a constraint" });
4881
- return yield* print(yield* client.addStandingConstraint(projectId, binding.id, text), (added) => [
4756
+ return yield* print(yield* client.addStandingConstraint(workspaceId, binding.id, text), (added) => [
4882
4757
  `Added ${added.id}: ${added.text}`
4883
4758
  ]);
4884
4759
  }
4885
4760
  case "remove":
4886
- return yield* print(yield* client.removeStandingConstraint(projectId, binding.id, requiredArg(command, "a constraint")), (removed) => [`Removed ${removed.removed}`]);
4761
+ return yield* print(yield* client.removeStandingConstraint(workspaceId, binding.id, requiredArg(command, "a constraint")), (removed) => [`Removed ${removed.removed}`]);
4887
4762
  default:
4888
4763
  return yield* new CliError({ message: `Unknown constraint command: ${command.action}` });
4889
4764
  }
@@ -4915,10 +4790,10 @@ function scheduleCommand(args) {
4915
4790
  const apiUrl = yield* resolveApiUrl(command.flags.apiUrl);
4916
4791
  const client = yield* loopClientFor(apiUrl);
4917
4792
  const operational = createOperationalCliClient(client);
4918
- const projectId = yield* optionalActiveProjectId(command.flags);
4793
+ const teamId = yield* optionalActiveTeamId(command.flags);
4919
4794
  const response = yield* operational.publishSchedule(scheduleId, {
4920
4795
  workspaceId: yield* activeWorkspaceId(command.flags),
4921
- ...projectId === undefined ? undefined : { projectId },
4796
+ ...teamId === undefined ? undefined : { teamId },
4922
4797
  ...yield* operationalInput(command, "schedule")
4923
4798
  }, yield* idempotencyKey(command.flags.idempotencyKey, "schedule-publish"));
4924
4799
  return yield* printOperationalResult(command, response);
@@ -5036,8 +4911,8 @@ function readString(value, keys) {
5036
4911
  function readNumber(value, keys) {
5037
4912
  return fieldValues(value, keys).find(isNumber);
5038
4913
  }
5039
- function loopListQuery(projectId, limit) {
5040
- const query = new URLSearchParams({ projectId });
4914
+ function loopListQuery(teamId, limit) {
4915
+ const query = new URLSearchParams({ teamId });
5041
4916
  if (limit !== undefined)
5042
4917
  query.set("limit", String(limit));
5043
4918
  return `?${query.toString()}`;
@@ -5062,30 +4937,30 @@ function runnerOwnershipFromCommand(command, required) {
5062
4937
  if (scopeType === undefined) {
5063
4938
  if (required)
5064
4939
  throw new Error("runner rescope requires --scope-type");
5065
- if (command.flags.scopeId !== undefined || command.flags.projectAccess !== undefined)
5066
- throw new Error("--scope-id and --project-access require --scope-type");
4940
+ if (command.flags.scopeId !== undefined || command.flags.teamAccess !== undefined)
4941
+ throw new Error("--scope-id and --team-access require --scope-type");
5067
4942
  return;
5068
4943
  }
5069
4944
  if (scopeType === "workspace") {
5070
4945
  if (command.flags.scopeId !== undefined)
5071
- throw new Error("workspace runner scope does not accept --scope-id; use --project-access selected and grants");
5072
- return { kind: "workspace", projectAccess: command.flags.projectAccess ?? "all" };
4946
+ throw new Error("workspace runner scope does not accept --scope-id; use --team-access selected and grants");
4947
+ return { kind: "workspace", teamAccess: command.flags.teamAccess ?? "all" };
5073
4948
  }
5074
- if (scopeType === "project") {
5075
- const projectId = command.flags.scopeId ?? command.flags.project;
5076
- if (!projectId)
5077
- throw new Error("project runner scope requires --scope-id <project-id>");
5078
- if (command.flags.projectAccess !== undefined)
5079
- throw new Error("project runner scope does not accept --project-access");
5080
- return { kind: "project", projectId };
4949
+ if (scopeType === "team") {
4950
+ const teamId = command.flags.scopeId ?? command.flags.team;
4951
+ if (!teamId)
4952
+ throw new Error("team runner scope requires --scope-id <team-id>");
4953
+ if (command.flags.teamAccess !== undefined)
4954
+ throw new Error("team runner scope does not accept --team-access");
4955
+ return { kind: "team", teamId };
5081
4956
  }
5082
4957
  if (scopeType === "personal") {
5083
4958
  const ownerUserId = command.flags.scopeId;
5084
4959
  if (!ownerUserId)
5085
4960
  throw new Error("personal runner scope requires --scope-id <owner-user-id>");
5086
- return { kind: "personal", ownerUserId, projectAccess: command.flags.projectAccess ?? "all" };
4961
+ return { kind: "personal", ownerUserId, teamAccess: command.flags.teamAccess ?? "all" };
5087
4962
  }
5088
- throw new Error("--scope-type must be workspace, project, or personal");
4963
+ throw new Error("--scope-type must be workspace, team, or personal");
5089
4964
  }
5090
4965
  function loopRunnerCapabilities() {
5091
4966
  const capabilities = defaultRunnerCapabilities();
@@ -5146,7 +5021,7 @@ function resolveCommandConfig(flags = {}) {
5146
5021
  const localConfig = yield* readUserConfig(localConfigPathValue);
5147
5022
  const configFlags = {
5148
5023
  ...flags.apiUrl ? { apiUrl: flags.apiUrl } : undefined,
5149
- ...flags.project ? { project: flags.project } : undefined,
5024
+ ...flags.team ? { team: flags.team } : undefined,
5150
5025
  ...flags.workspace ? { workspace: flags.workspace } : undefined
5151
5026
  };
5152
5027
  const envApiUrl = yield* apiUrlFromEnv;
@@ -5169,10 +5044,10 @@ function resolveCommandConfig(flags = {}) {
5169
5044
  ["localConfig", localConfig?.activeWorkspaceId],
5170
5045
  ["userConfig", userConfig?.activeWorkspaceId]
5171
5046
  ], "none"),
5172
- activeProject: firstConfigSource([
5173
- ["flag", flags.project],
5174
- ["localConfig", localConfig?.activeProjectId],
5175
- ["userConfig", userConfig?.activeProjectId]
5047
+ activeTeam: firstConfigSource([
5048
+ ["flag", flags.team],
5049
+ ["localConfig", localConfig?.activeTeamId],
5050
+ ["userConfig", userConfig?.activeTeamId]
5176
5051
  ], "none")
5177
5052
  },
5178
5053
  userConfigPath: yield* userConfigPath,
@@ -5197,36 +5072,36 @@ function optionalActiveWorkspaceId(flags) {
5197
5072
  return config.activeWorkspaceId;
5198
5073
  });
5199
5074
  }
5200
- function activeProjectId(flags) {
5075
+ function activeTeamId(flags) {
5201
5076
  return gen(function* () {
5202
5077
  const { config } = yield* resolveCommandConfig(flags);
5203
- if (!config.activeProjectId) {
5078
+ if (!config.activeTeamId) {
5204
5079
  return yield* new CliError({
5205
- message: "No active Project. Run kitman project use <project> or pass --project <project>."
5080
+ message: "No active Team. Run kitman team use <team> or pass --team <team>."
5206
5081
  });
5207
5082
  }
5208
- return config.activeProjectId;
5083
+ return config.activeTeamId;
5209
5084
  });
5210
5085
  }
5211
- function optionalActiveProjectId(flags) {
5086
+ function optionalActiveTeamId(flags) {
5212
5087
  return gen(function* () {
5213
5088
  const { config } = yield* resolveCommandConfig(flags);
5214
- return config.activeProjectId;
5089
+ return config.activeTeamId;
5215
5090
  });
5216
5091
  }
5217
5092
  function writeActiveWorkspace(apiUrl, workspaceId, local) {
5218
5093
  return gen(function* () {
5219
5094
  const path = local ? yield* localConfigPath : undefined;
5220
5095
  const existing = yield* readUserConfig(path);
5221
- const { activeProjectId: _activeProjectId, ...rest } = existing ?? {};
5096
+ const { activeTeamId: _activeTeamId, ...rest } = existing ?? {};
5222
5097
  yield* writeUserConfig({ ...rest, apiUrl, activeWorkspaceId: workspaceId }, path);
5223
5098
  });
5224
5099
  }
5225
- function writeActiveProject(apiUrl, workspaceId, projectId, local) {
5100
+ function writeActiveTeam(apiUrl, workspaceId, teamId, local) {
5226
5101
  return gen(function* () {
5227
5102
  const path = local ? yield* localConfigPath : undefined;
5228
5103
  const existing = yield* readUserConfig(path);
5229
- yield* writeUserConfig({ ...existing, apiUrl, activeWorkspaceId: workspaceId, activeProjectId: projectId }, path);
5104
+ yield* writeUserConfig({ ...existing, apiUrl, activeWorkspaceId: workspaceId, activeTeamId: teamId }, path);
5230
5105
  });
5231
5106
  }
5232
5107
  function resolveWorkspace(client, workspaceRef) {
@@ -5244,14 +5119,8 @@ function requiredArg(command, name) {
5244
5119
  throw new Error(`${command.group} ${command.action} requires ${name}`);
5245
5120
  return value;
5246
5121
  }
5247
- function projectRow(project) {
5248
- return {
5249
- id: project.id,
5250
- name: project.name,
5251
- slug: project.slug,
5252
- open: project.openTaskCount,
5253
- blocked: project.blockedTaskCount
5254
- };
5122
+ function teamRow(team) {
5123
+ return { id: team.id, key: team.key, name: team.name, members: team.memberCount };
5255
5124
  }
5256
5125
  function kitRow(kit) {
5257
5126
  return { id: kit.id, name: kit.name, slug: kit.slug, kind: kit.kind, description: kit.description };
@@ -5338,18 +5207,19 @@ function repoBindingCredentialRefLabel(ref) {
5338
5207
  }
5339
5208
  function runnerRow(runner) {
5340
5209
  const ownership = runner.ownership;
5341
- let effectiveProjectAccess;
5342
- if (ownership.kind === "project")
5343
- effectiveProjectAccess = `project:${ownership.projectId}`;
5344
- else if (ownership.projectAccess === "selected")
5345
- effectiveProjectAccess = runner.projectGrantIds.join(",") || "selected_none";
5210
+ let effectiveTeamAccess;
5211
+ if (ownership.kind === "team")
5212
+ effectiveTeamAccess = `team:${ownership.teamId}`;
5213
+ else if (ownership.teamAccess === "selected")
5214
+ effectiveTeamAccess = runner.teamGrantIds.join(",") || "selected_none";
5346
5215
  else
5347
- effectiveProjectAccess = ownership.kind === "workspace" ? "workspace_all" : `personal:${ownership.ownerUserId}`;
5216
+ effectiveTeamAccess = ownership.kind === "workspace" ? "workspace_all" : `personal:${ownership.ownerUserId}`;
5348
5217
  return {
5349
5218
  id: runner.id,
5350
5219
  name: runner.name,
5351
5220
  status: runner.status,
5352
- health: runner.healthStatus,
5221
+ state: runner.state.kind,
5222
+ credentialStatus: runner.credentialStatus,
5353
5223
  agentIdentityId: runner.agentIdentityId,
5354
5224
  harnesses: runner.capabilities.harnesses.join(","),
5355
5225
  defaultHarness: runner.capabilities.defaultHarness ?? "",
@@ -5358,10 +5228,10 @@ function runnerRow(runner) {
5358
5228
  sandboxes: runner.capabilities.sandboxBackends.join(","),
5359
5229
  labels: runner.labels.join(","),
5360
5230
  ownership: ownership.kind,
5361
- projectAccess: ownership.kind === "project" ? undefined : ownership.projectAccess,
5231
+ teamAccess: ownership.kind === "team" ? undefined : ownership.teamAccess,
5362
5232
  ownerUserId: ownership.kind === "personal" ? ownership.ownerUserId : undefined,
5363
- scopedProjectId: ownership.kind === "project" ? ownership.projectId : undefined,
5364
- effectiveProjectAccess,
5233
+ scopedTeamId: ownership.kind === "team" ? ownership.teamId : undefined,
5234
+ effectiveTeamAccess,
5365
5235
  managementBoundary: "workspace_operator_only",
5366
5236
  schedulingBoundary: "scope_and_identity_gated",
5367
5237
  lastHeartbeatAt: runner.lastHeartbeatAt
@@ -5384,7 +5254,8 @@ function runnerDoctorRow(runner) {
5384
5254
  id: runner.id,
5385
5255
  name: runner.name,
5386
5256
  status: runner.status,
5387
- health: runner.healthStatus,
5257
+ state: runner.state.kind,
5258
+ credentialStatus: runner.credentialStatus,
5388
5259
  runs: runner.capabilities.runs?.supported === true,
5389
5260
  harnesses: runner.capabilities.harnesses.join(","),
5390
5261
  defaultHarness: runner.capabilities.defaultHarness ?? "",
@@ -5448,8 +5319,8 @@ function sessionUpdateRow(update) {
5448
5319
  function dependencyTaskLabel(task) {
5449
5320
  if (task.redacted) {
5450
5321
  const taskId = task.taskId ?? "hidden-task";
5451
- const project = task.projectId === undefined ? "" : ` (${task.projectId})`;
5452
- return `[redacted] ${taskId}${project}`;
5322
+ const team = task.teamId === undefined ? "" : ` (${task.teamId})`;
5323
+ return `[redacted] ${taskId}${team}`;
5453
5324
  }
5454
5325
  return `${task.taskKey} ${task.title} [${task.state.name}]`;
5455
5326
  }
@@ -5477,7 +5348,7 @@ function downloadPresignedAsset(download, outputPath) {
5477
5348
  return gen(function* () {
5478
5349
  const fs = yield* FileSystem;
5479
5350
  const failed = (cause) => new CliError({ message: `Asset download failed: ${String(cause)}` });
5480
- const response = yield* send(make14(download.method)(download.url), failed);
5351
+ const response = yield* send(make9(download.method)(download.url), failed);
5481
5352
  if (response.status < 200 || response.status >= 300)
5482
5353
  return yield* new CliError({ message: `Asset download failed with ${response.status}` });
5483
5354
  const body = yield* response.arrayBuffer.pipe(mapError((error) => failed(error.message)));
@@ -5634,15 +5505,15 @@ function parseRepoBindingCredentialRef(value) {
5634
5505
  }
5635
5506
  function repoBindingInput2(command) {
5636
5507
  if (!command.flags.provider)
5637
- throw new Error("project add-repo-binding requires --provider");
5508
+ throw new Error("repo add requires --provider");
5638
5509
  if (!command.flags.host)
5639
- throw new Error("project add-repo-binding requires --host");
5510
+ throw new Error("repo add requires --host");
5640
5511
  if (!command.flags.owner)
5641
- throw new Error("project add-repo-binding requires --owner");
5512
+ throw new Error("repo add requires --owner");
5642
5513
  if (!command.flags.name)
5643
- throw new Error("project add-repo-binding requires --name");
5514
+ throw new Error("repo add requires --name");
5644
5515
  if (!command.flags.defaultBranch)
5645
- throw new Error("project add-repo-binding requires --default-branch");
5516
+ throw new Error("repo add requires --default-branch");
5646
5517
  return {
5647
5518
  provider: command.flags.provider,
5648
5519
  host: command.flags.host,
@@ -5869,5 +5740,5 @@ function cliErrorJson(error) {
5869
5740
  var program = runEffect(args).pipe(tapCause((cause) => {
5870
5741
  const error2 = squash(cause);
5871
5742
  return args.includes("--json") ? printJson(cliErrorJson(error2)) : error(error2 instanceof Error ? error2.message : String(error2));
5872
- }), provide3(layer10));
5743
+ }), provide3(layer));
5873
5744
  runMain(program, { disableErrorReporting: true });