@north-light/crouter 0.3.321 → 0.3.322

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.
Files changed (66) hide show
  1. package/bin/runtime-selector.mjs +52 -3
  2. package/dist/api/client.d.ts +29 -16
  3. package/dist/api/client.js +2 -2
  4. package/dist/api/dto/modelauth.d.ts +15 -0
  5. package/dist/api/errors.d.ts +6 -3
  6. package/dist/api/errors.js +1 -1
  7. package/dist/api/index.d.ts +2 -2
  8. package/dist/api/index.js +1 -1
  9. package/dist/api/node-transport.d.ts +18 -0
  10. package/dist/api/node-transport.js +1 -0
  11. package/dist/api/routes.d.ts +1 -0
  12. package/dist/api/routes.js +1 -1
  13. package/dist/builtin-memory/crouter-sdk.md +86 -0
  14. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.js +1 -1
  15. package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +1 -1
  16. package/dist/cli.js +8 -2
  17. package/dist/clients/attach/render/chat-view.d.ts +5 -1
  18. package/dist/clients/attach/render/chat-view.js +1 -1
  19. package/dist/clients/attach/session/frames.d.ts +1 -0
  20. package/dist/clients/attach/session/frames.js +1 -1
  21. package/dist/clients/attach/viewer.js +583 -583
  22. package/dist/commands/api-client.js +3 -3
  23. package/dist/commands/memory/read.js +2 -2
  24. package/dist/commands/sys/branch.js +1 -1
  25. package/dist/commands/sys/connect.d.ts +1 -0
  26. package/dist/commands/sys/connect.js +3 -0
  27. package/dist/commands/sys/daemon.js +1 -1
  28. package/dist/commands/sys/panels/provider-panel.js +1 -1
  29. package/dist/commands/sys/provider-login.d.ts +8 -0
  30. package/dist/commands/sys/provider-login.js +9 -0
  31. package/dist/commands/sys/setup-core.d.ts +4 -0
  32. package/dist/commands/sys/setup-core.js +4 -4
  33. package/dist/commands/sys.js +1 -1
  34. package/dist/core/canvas/paths.d.ts +4 -4
  35. package/dist/core/config.js +1 -1
  36. package/dist/core/runtime/broker/daemon-ops.js +1 -1
  37. package/dist/core/secrets.d.ts +3 -0
  38. package/dist/core/secrets.js +2 -2
  39. package/dist/core/subscription-state.d.ts +1 -1
  40. package/dist/core/subscription-state.js +3 -3
  41. package/dist/core/termrender/termrender.d.ts +3 -1
  42. package/dist/core/termrender/termrender.js +13 -13
  43. package/dist/core/user-settings.d.ts +4 -0
  44. package/dist/core/user-settings.js +1 -1
  45. package/dist/daemon/api/handlers/health.d.ts +1 -1
  46. package/dist/daemon/api/handlers/health.js +3 -3
  47. package/dist/daemon/api/handlers/modelauth.js +1 -1
  48. package/dist/daemon/api/server.d.ts +5 -1
  49. package/dist/daemon/api/server.js +4 -4
  50. package/dist/daemon/crtrd-cli.js +1 -1
  51. package/dist/daemon/manage.d.ts +1 -0
  52. package/dist/daemon/manage.js +2 -2
  53. package/dist/types.d.ts +7 -0
  54. package/dist/types.js +1 -1
  55. package/docs/sdk/README.md +54 -0
  56. package/docs/sdk/client.md +92 -0
  57. package/docs/sdk/docker.md +63 -0
  58. package/docs/sdk/errors.md +85 -0
  59. package/docs/sdk/getting-started.md +160 -0
  60. package/docs/sdk/memory.md +107 -0
  61. package/docs/sdk/migration.md +108 -0
  62. package/docs/sdk/nodes.md +193 -0
  63. package/docs/sdk/resources.md +71 -0
  64. package/docs/sdk/streaming.md +124 -0
  65. package/package.json +1 -1
  66. package/runtime.lock.json +6 -6
@@ -15,12 +15,57 @@ function holdGenerationEntryLease(entryPath) {
15
15
  if (!generationEntryLeases.has(entryPath)) generationEntryLeases.set(entryPath, openSync(entryPath, 'r'));
16
16
  }
17
17
 
18
+ function isSourceCheckout() {
19
+ return existsSync(join(PACKAGE_ROOT, '.git'));
20
+ }
21
+
18
22
  function remediation() {
19
- return existsSync(join(PACKAGE_ROOT, '.git'))
23
+ return isSourceCheckout()
20
24
  ? 'Run `npm run build && npm run install-runtime` in the crouter source checkout, then retry.'
21
25
  : 'Reinstall crouter, then retry.';
22
26
  }
23
27
 
28
+ /**
29
+ * First-run self-install. A published install relies on npm running
30
+ * `scripts/postinstall.mjs` to stage the first runtime generation, and npm no
31
+ * longer does that for a global install (11.19 skips install scripts, and its
32
+ * own allow-scripts remediations skip them too). Upgrades never depended on
33
+ * npm — self-update calls install-runtime.mjs directly — so first install was
34
+ * the only path still trusting it. This closes that gap the same way: when no
35
+ * generation is selected at all and this shim sits in a published package
36
+ * tree, stage one from that tree and continue.
37
+ *
38
+ * Only the missing-selection case qualifies. A selection that exists but
39
+ * fails validation is a real defect to report, not something to paper over,
40
+ * and a source checkout keeps its explicit `npm run install-runtime` because
41
+ * its `dist/` may be stale or absent.
42
+ */
43
+ function installFirstGeneration(command) {
44
+ if (isSourceCheckout() || !existsSync(join(PACKAGE_ROOT, 'dist', 'index.js'))) return false;
45
+ process.stderr.write(`${command}: first run — installing the runtime generation (one-time)\n`);
46
+ const result = spawnSync(process.execPath, [join(PACKAGE_ROOT, 'scripts', 'install-runtime.mjs'), PACKAGE_ROOT], { cwd: PACKAGE_ROOT, stdio: 'inherit' });
47
+ return result.status === 0;
48
+ }
49
+
50
+ /**
51
+ * The other half of what postinstall did: provision the pinned termrender
52
+ * venv, from the generation that was just selected so the code that renders
53
+ * is the code that provisioned. Best-effort, exactly as postinstall was — a
54
+ * renderer that will not provision (no `uv`, no network) is reported and the
55
+ * command still runs; every render path re-attempts on first use.
56
+ */
57
+ async function provisionFirstRenderer(command, entryPath) {
58
+ try {
59
+ const mod = await import(pathToFileURL(join(dirname(entryPath), 'core', 'termrender', 'termrender.js')).href);
60
+ if (mod.isRendererReady()) return;
61
+ process.stderr.write(`${command}: first run — provisioning the markdown renderer (one-time)\n`);
62
+ mod.ensureRenderer();
63
+ if (!mod.isRendererReady()) process.stderr.write(`${command}: renderer not provisioned; it will be retried on first render.\n`);
64
+ } catch (error) {
65
+ process.stderr.write(`${command}: renderer provisioning skipped: ${errorMessage(error)}\n`);
66
+ }
67
+ }
68
+
24
69
  function fail(command, detail) {
25
70
  process.stderr.write(`${command}: invalid runtime selection: ${detail}\n${remediation()}\n`);
26
71
  process.exitCode = 1;
@@ -41,7 +86,7 @@ function readSelection() {
41
86
  try {
42
87
  target = readlinkSync(selected);
43
88
  } catch {
44
- return failure(`missing ${selected}`);
89
+ return failure(`missing ${selected}`, 'missing-selection');
45
90
  }
46
91
  const id = parseSelectionTarget(target);
47
92
  if (!id) return failure(`selected must target generations/<id>, got ${JSON.stringify(target)}`);
@@ -367,7 +412,11 @@ export function selectRuntime(command, entryName) {
367
412
  }
368
413
 
369
414
  export async function runSelected(command, entryName) {
370
- const result = tryResolveRuntime(entryName);
415
+ let result = tryResolveRuntime(entryName);
416
+ if (!result.ok && result.code === 'missing-selection' && installFirstGeneration(command)) {
417
+ result = tryResolveRuntime(entryName);
418
+ if (result.ok) await provisionFirstRenderer(command, result.runtime.entryPath);
419
+ }
371
420
  if (result.ok) {
372
421
  holdGenerationEntryLease(result.runtime.entryPath);
373
422
  if (result.runtime.notice !== undefined) process.stderr.write(`${command}: ${result.runtime.notice}\n`);
@@ -14,7 +14,7 @@ import type { DeleteProfileRequest, DeleteProfileResultDTO, EnsureProfileRequest
14
14
  import type { FilePeekDTO } from './dto/files.js';
15
15
  import type { MemoryDocRefDTO } from './dto/memory.js';
16
16
  import type { ChatInventoryDTO, ProspectiveChatInventoryDTO, ProspectiveChatInventoryQuery } from './dto/chat-inventory.js';
17
- import type { CredentialRemovalResultDTO, CredentialResultDTO, InstallCredentialRequest, ModelAuthListDTO } from './dto/modelauth.js';
17
+ import type { CredentialRemovalResultDTO, CredentialResultDTO, InstallCredentialRequest, ModelAuthListDTO, ModelAuthReadinessDTO, ModelAuthReadinessQuery } from './dto/modelauth.js';
18
18
  import type { CancelReviewRequest, CreateReviewRequest, ListReviewsQuery, ReviewCancelResultDTO, ReviewDocumentBaseDTO, ReviewDTO, ReviewListDTO, ReviewSubmitResultDTO } from './dto/reviews.js';
19
19
  import type { CreateReviewCommentRequest, EditReviewCommentRequest, ListReviewCommentsQuery, ReadReviewCommentEventsQuery, ReviewCommentActionRequest, ReviewCommentDetailDTO, ReviewCommentEventsDTO, ReviewCommentForkDTO, ReviewCommentListDTO, ReviewCommentMutationDTO, ReviewCommentRangeBatchRequest, ReviewCommentRangeBatchResultDTO } from './dto/review-comments.js';
20
20
  import type { CancelInboxTicketRequest, CanceledTicketResultDTO, InboxListDTO, InboxPageDTO, InboxPageHistoryDTO, InboxPageResponseDTO, InboxTicketIdDTO, PageFeedbackResolutionDTO, PageResponsesDTO, PageTicketResultDTO, RespondInboxPageRequest } from './dto/inbox.js';
@@ -25,16 +25,18 @@ import type { BrokerExtensionStateDTO, BrokerExecutionRequest, BrokerGeneratedNa
25
25
  import type { AcknowledgeMailRequest, AcknowledgeMailResultDTO, ClaimMailRequest, ClaimMailResultDTO } from './dto/mail.js';
26
26
  import type { BrokerFaultInputDTO, BrokerFaultRequest, BrokerFaultResultDTO, BrokerProviderRetryRequest, BrokerProviderRetryResultDTO, BrokerTurnRequest, BrokerTurnResultDTO, NodeFaultClearResultDTO, RecoveryStateDTO } from './dto/recovery.js';
27
27
  export interface CrtrClientOptions {
28
- /** Unix socket path (default local transport). Exactly one of socketPath|baseUrl. */
29
- socketPath?: string;
30
- /** `http(s)://host:port` for TCP/remote transport. */
31
- baseUrl?: string;
28
+ /** `http(s)://host:port` for a TCP/remote transport. */
29
+ baseUrl: string;
30
+ /** Fetch implementation. The global Web fetch is used unless a caller supplies one. */
31
+ fetch?: typeof fetch;
32
32
  /** Extra headers, e.g. `{ authorization: 'Bearer <token>' }` when the target
33
33
  * crtrd's TCP listener has `CRTRD_TOKEN` set (unix-socket transport is
34
34
  * never checked, and a TCP daemon with no token set ignores this too). */
35
35
  headers?: Record<string, string>;
36
- /** Autostart on a cold socket (default true for socketPath, false for baseUrl). */
36
+ /** Retry a cold local connection through this hook once when enabled. */
37
37
  autostart?: boolean;
38
+ /** Maximum retries for transient GET, HEAD, and DELETE requests. Defaults to 2. */
39
+ maxRetries?: number;
38
40
  /** Per-request timeout in ms (default 30_000). */
39
41
  timeoutMs?: number;
40
42
  /** Injected daemon-start hook (spec §7.1). Called once on a cold socket when
@@ -64,22 +66,26 @@ export declare function waitForDaemonAvailability({ windowMs, probe, initialErro
64
66
  now?: () => number;
65
67
  sleep?: (ms: number) => Promise<void> | void;
66
68
  }): Promise<void>;
69
+ export interface CrtrRequestOptions {
70
+ headers?: Record<string, string>;
71
+ signal?: AbortSignal;
72
+ timeout?: number;
73
+ maxRetries?: number;
74
+ }
67
75
  export declare class CrtrClient {
68
- private readonly socketPath?;
69
- private readonly baseUrl?;
76
+ private readonly baseUrl;
77
+ private readonly fetch;
70
78
  private readonly headers;
71
79
  private readonly autostart;
72
80
  private readonly timeoutMs;
81
+ private readonly maxRetries;
82
+ private readonly localSocketTransport;
73
83
  private readonly onColdSocket?;
74
84
  private readonly coldStartDiagnostic?;
75
85
  private readonly coldStartPollWindowMs;
76
86
  /** Guards against invoking the daemon-start hook more than once per client. */
77
87
  private coldStartAttempted;
78
88
  constructor(opts: CrtrClientOptions);
79
- /** Construct a client bound to the default local socket with autostart on. Pass
80
- * `onColdSocket` to enable the daemon-spawn hook (spec §7.1); without it a cold
81
- * socket fails loud with `daemon_unavailable`. */
82
- static forLocalSocket(opts?: Omit<CrtrClientOptions, 'socketPath' | 'baseUrl'>): CrtrClient;
83
89
  healthz(): Promise<HealthDTO>;
84
90
  /** One `/healthz` observation without cold-socket recovery. Availability
85
91
  * waiters own retry policy and pass their remaining wall-clock budget here. */
@@ -219,6 +225,7 @@ export declare class CrtrClient {
219
225
  /** Force-delete or detach one profile by exact id or unique name. */
220
226
  deleteProfile(name: string, req: DeleteProfileRequest): Promise<DeleteProfileResultDTO>;
221
227
  listModelAuth(): Promise<ModelAuthListDTO>;
228
+ getModelAuthReadiness(query?: ModelAuthReadinessQuery): Promise<ModelAuthReadinessDTO>;
222
229
  installCredential(provider: string, req: InstallCredentialRequest): Promise<CredentialResultDTO>;
223
230
  removeCredential(provider: string): Promise<CredentialRemovalResultDTO>;
224
231
  createReview(req: CreateReviewRequest): Promise<ReviewDTO>;
@@ -310,10 +317,12 @@ export declare class CrtrClient {
310
317
  canvasRoster(): Promise<RosterDTO>;
311
318
  prune(req: PruneRequest): Promise<PruneResultDTO>;
312
319
  /** Raw request for routes not yet method-wrapped. Applies the same
313
- * autostart + error-mapping semantics. */
314
- request<T>(method: string, path: string, body?: unknown, timeoutMs?: number): Promise<T>;
315
- /** The unparsed request: cold-socket and interrupted-response recovery, no
316
- * JSON parse. Every wrapper goes through here; only non-JSON routes call it directly. */
320
+ * autostart + retry + error-mapping semantics. */
321
+ request<T>(method: string, path: string, body?: unknown, opts?: CrtrRequestOptions): Promise<T>;
322
+ /** The unparsed request: cold-socket and interrupted-response recovery, plus
323
+ * the §7 retry policy (connection errors and 429/5xx on GET/HEAD/DELETE
324
+ * only — POST/PATCH are never replayed once a request has actually been
325
+ * sent). No JSON parse; every wrapper goes through here. */
317
326
  private send;
318
327
  private nodePath;
319
328
  /** Validate a background job id before route construction. Job ids arrive
@@ -343,7 +352,11 @@ export declare class CrtrClient {
343
352
  * agent argv, so a bad one is a plausible request the server would also
344
353
  * reject, not a caller-bug `TypeError` like `ticketId`. */
345
354
  private commentPath;
355
+ /** The one fetch path (§1). Resolves as soon as headers arrive — the body
356
+ * stays an unread `ReadableStream` on the returned `Response`, so a caller
357
+ * that wants to stream (SSE, later) never waits on a buffered body. */
346
358
  private transport;
359
+ private retainRequestLifetime;
347
360
  private isColdSocketError;
348
361
  /** A connection torn down MID-request (Node's "socket hang up" / a broken
349
362
  * pipe). It establishes only that the response was interrupted, not why. */
@@ -1,2 +1,2 @@
1
- var b=Object.defineProperty;var d=(n,e)=>b(n,"name",{value:e,configurable:!0});import{envHomeOverride as C}from"../shared/env.js";import{request as G}from"node:http";import{request as N}from"node:https";import{homedir as _}from"node:os";import{join as k}from"node:path";import{ApiError as u,isErrorBody as A}from"./errors.js";import{routes as r}from"./routes.js";import{isSafeNodeId as p}from"./dto/common.js";import{isSafeBashJobId as D}from"./dto/bash-jobs.js";import{isSafeCronId as I}from"./dto/crons.js";const x=".crouter",M="crtrd.sock";function B(){const n=C(),e=n!==void 0&&n!==""?n:k(_(),x,"canvas");return k(e,M)}d(B,"defaultSocketPath");const H=3e4,$=1e4,U=200;async function L({windowMs:n,probe:e,initialError:t,pollIntervalMs:s=U,retry:o=d(()=>!0,"retry"),now:a=Date.now,sleep:h=F}){const P=a()+n;let c=t;for(;;){const m=P-a();if(m<=0)throw c??new Error("crtrd availability window expired without a probe failure");try{await e(m);return}catch(f){if(c=f,!o(f))throw f}const l=P-a();if(l<=0)throw c;await h(Math.min(s,l))}}d(L,"waitForDaemonAvailability");class R{static{d(this,"CrtrClient")}socketPath;baseUrl;headers;autostart;timeoutMs;onColdSocket;coldStartDiagnostic;coldStartPollWindowMs;coldStartAttempted=!1;constructor(e){const t=e.socketPath!==void 0&&e.socketPath!=="",s=e.baseUrl!==void 0&&e.baseUrl!=="";if(t===s)throw new TypeError("CrtrClient requires exactly one of socketPath | baseUrl");t&&(this.socketPath=e.socketPath),s&&(this.baseUrl=new URL(e.baseUrl)),this.headers={...e.headers},this.autostart=e.autostart??t,this.timeoutMs=e.timeoutMs??H,e.onColdSocket!==void 0&&(this.onColdSocket=e.onColdSocket),e.coldStartDiagnostic!==void 0&&(this.coldStartDiagnostic=e.coldStartDiagnostic),this.coldStartPollWindowMs=e.coldStartPollWindowMs??$}static forLocalSocket(e){return new R({socketPath:B(),autostart:!0,...e})}healthz(){return this.request("GET",r.healthz())}async probeHealthz(e){const t=await this.transport("GET",r.healthz(),void 0,void 0,e);if(t.status===503)try{const s=JSON.parse(t.text);if(typeof s.startup_blocked=="string")return s}catch{}return y(t)}status(){return this.request("GET",r.status())}restartDaemon(){return this.request("POST",r.daemonRestart())}admitDaemon(){return this.request("POST",r.daemonAdmit())}migrateState(e){return this.request("POST",r.daemonMigrate(),e)}createNode(e){return this.request("POST",r.nodes(),e)}listNodes(e){return this.request("GET",i(r.nodes(),e))}getNode(e){return this.request("GET",r.node(this.nodePath(e)))}getNodeOutcome(e,{waitSeconds:t}={}){if(t!==void 0&&(!Number.isInteger(t)||t<0||t>25))throw new RangeError("waitSeconds must be an integer between 0 and 25");return this.request("GET",i(r.nodeOutcome(this.nodePath(e)),{wait:t}),void 0,(t??0)*1e3+this.timeoutMs)}registerOutcomeDelivery(e,t){return this.request("PUT",r.nodeOutcomeDelivery(this.nodePath(e)),t)}getOutcomeDelivery(e){return this.request("GET",r.nodeOutcomeDelivery(this.nodePath(e)))}async disarmOutcomeDelivery(e){await this.request("DELETE",r.nodeOutcomeDelivery(this.nodePath(e)))}listBashJobs(e){return this.request("GET",r.nodeJobs(this.nodePath(e)))}stopBashJob(e,t){return this.request("DELETE",r.nodeJob(this.nodePath(e),this.jobPath(t)))}sendMessage(e,t){return this.request("POST",r.nodeMessages(this.nodePath(e)),t)}interruptNode(e){return this.request("POST",r.nodeInterrupt(this.nodePath(e)),{})}pushReport(e,t){return this.request("POST",r.nodeReports(this.nodePath(e)),t)}submitResult(e,t){return this.request("POST",r.nodeResult(this.nodePath(e)),t)}forkNode(e){return this.request("POST",r.nodeFork(this.nodePath(e)),{})}reviveNode(e,t){return this.request("POST",r.nodeRevive(this.nodePath(e)),t??{})}relaunchRoot(e){return this.request("POST",r.nodeRelaunchRoot(this.nodePath(e)),{})}bindBrokerSession(e,t){return this.request("POST",r.nodeBrokerSessionBound(this.nodePath(e)),t)}settleBroker(e,t){return this.request("POST",r.nodeBrokerSettle(this.nodePath(e)),t)}completeBrokerPark(e,t){return this.request("POST",r.nodeBrokerParkComplete(this.nodePath(e)),t)}recordBrokerParkActivity(e,t){return this.request("POST",r.nodeBrokerParkActivity(this.nodePath(e)),t)}claimNodeMail(e,t){return this.request("POST",r.nodeMailClaim(this.nodePath(e)),t)}acknowledgeNodeMail(e,t){return this.request("POST",r.nodeMailAcknowledge(this.nodePath(e)),t)}recordBrokerTurn(e,t){return this.request("POST",r.brokerTurn(this.nodePath(e)),t)}mutateBrokerProviderRetry(e,t){return this.request("POST",r.brokerProviderRetry(this.nodePath(e)),t)}mutateBrokerFault(e,t){return this.request("POST",r.brokerFault(this.nodePath(e)),t)}getBrokerRecovery(e,t){return this.request("GET",`${r.brokerRecovery(this.nodePath(e))}?expected_execution_id=${encodeURIComponent(t)}`)}recordNodeFault(e,t){return this.request("POST",r.nodeFault(this.nodePath(e)),t)}clearNodeFault(e,t={}){const s=new URLSearchParams;t.link!==void 0&&s.set("link",t.link),t.preserve_episode!==void 0&&s.set("preserve_episode",String(t.preserve_episode));const o=s.size===0?"":`?${s.toString()}`;return this.request("DELETE",`${r.nodeFault(this.nodePath(e))}${o}`)}commitBrokerModel(e,t){return this.request("POST",r.nodeBrokerModel(this.nodePath(e)),t)}brokerExtensionState(e){return this.request("GET",r.nodeBrokerExtensionState(this.nodePath(e)))}commitBrokerGeneratedName(e,t){return this.request("POST",r.nodeBrokerGeneratedName(this.nodePath(e)),t)}commitBrokerPersonaAck(e,t){return this.request("POST",r.nodeBrokerPersonaAck(this.nodePath(e)),t)}closeNode(e,t){return this.request("POST",r.nodeClose(this.nodePath(e)),t??{})}recycleNode(e){return this.request("POST",r.nodeRecycle(this.nodePath(e)),{})}demoteNode(e){return this.request("POST",r.nodeDemote(this.nodePath(e)),{})}promoteNode(e,t){return this.request("POST",r.nodePromote(this.nodePath(e)),t)}yieldNode(e,t){return this.request("POST",r.nodeYield(this.nodePath(e)),t)}waitNode(e,t){return this.request("POST",r.nodeWait(this.nodePath(e)),t)}patchConfig(e,t){return this.request("PATCH",r.nodeConfig(this.nodePath(e)),t)}closeWorktree(e){return this.request("POST",r.nodeWorktreeClose(this.nodePath(e)),{})}abandonWorktree(e,t){return this.request("POST",r.nodeWorktreeAbandon(this.nodePath(e)),t)}listQuarantinedWorktrees(){return this.request("GET",r.quarantinedWorktrees())}subscribe(e,t){return this.request("POST",r.nodeSubscriptions(this.nodePath(e)),t)}listFocuses(){return this.request("GET",r.focuses())}focusOf(e){return this.request("GET",i(r.focusByNode(),{node_id:e}))}focusByPane(e){return this.request("GET",i(r.focusByPane(),{pane:e}))}registerFocus(e){return this.request("POST",r.focuses(),e)}async setFocusPane(e,t){await this.request("PATCH",r.focus(e),t)}async closeFocus(e){await this.request("DELETE",r.focus(e))}async unsubscribe(e,t){await this.request("DELETE",r.nodeSubscription(this.nodePath(e),this.nodePath(t)))}armCron(e){return this.request("POST",r.crons(),e)}listCrons(e){return this.request("GET",i(r.crons(),e))}showCron(e,t){return this.request("GET",i(r.cron(this.cronPath(e)),t))}pauseCron(e,t){return this.request("POST",i(r.cronPause(this.cronPath(e)),t),{})}resumeCron(e,t){return this.request("POST",i(r.cronResume(this.cronPath(e)),t),{})}runCron(e,t){return this.request("POST",i(r.cronRun(this.cronPath(e)),t),{})}async cancelCron(e,t){await this.request("DELETE",i(r.cron(this.cronPath(e)),t))}pokeCrons(){return this.request("POST",r.cronsPoke(),{})}ensureAttach(e,t){return this.request("POST",r.nodeAttach(this.nodePath(e)),t??{})}getReports(e,t){return this.request("GET",i(r.nodeReports(this.nodePath(e)),t))}getTranscript(e,t){return this.request("GET",i(r.nodeTranscript(this.nodePath(e)),t))}getSnapshot(e){return this.request("GET",r.nodeSnapshot(this.nodePath(e)))}nodeSubject(e){return this.request("GET",r.nodeSubject(this.nodePath(e)))}getNodeMessages(e,t){return this.request("GET",i(r.nodeMessages(this.nodePath(e)),t))}getSession(e){return this.request("GET",r.nodeSession(this.nodePath(e)))}getChatInventory(e){return this.request("GET",r.nodeChatInventory(this.nodePath(e)))}getProspectiveChatInventory(e){return this.request("GET",i(r.prospectiveChatInventory(),e))}getArtifacts(e,t){return this.request("GET",i(r.nodeArtifacts(this.nodePath(e)),t))}getContext(e){return this.request("GET",r.nodeContext(this.nodePath(e)))}peekFile(e){return this.request("GET",i(r.filePeek(),{path:e}))}resolveMemoryDoc(e,t){return this.request("GET",i(r.memoryResolve(),{name:e,node:t}))}ensureProfile(e,t){return this.request("PUT",r.profile(e),t??{})}listProfiles(){return this.request("GET",r.profiles())}getProfile(e){return this.request("GET",r.profile(e))}pauseProfile(e){return this.request("POST",r.profilePause(e),{})}resumeProfile(e){return this.request("POST",r.profileResume(e),{})}updateProfileMetadata(e,t){return this.request("PATCH",r.profileMetadata(e),t)}deleteProfile(e,t){return this.request("DELETE",r.profile(e),t)}listModelAuth(){return this.request("GET",r.modelAuths())}installCredential(e,t){return this.request("PUT",r.modelAuth(e),t)}removeCredential(e){return this.request("DELETE",r.modelAuth(e))}createReview(e){return this.request("POST",r.humanReviews(),e)}listReviews(e){return this.request("GET",i(r.humanReviews(),e))}getReview(e){return this.request("GET",r.humanReview(this.reviewPath(e)))}submitReview(e){return this.request("POST",r.humanReviewSubmit(this.reviewPath(e)),{})}cancelReview(e,t={}){return this.request("POST",r.humanReviewCancel(this.reviewPath(e)),t)}getReviewDocumentBase(e){return this.request("GET",r.humanReviewDocument(this.reviewPath(e)))}createReviewComment(e,t){const s=e===void 0?"self":this.reviewPath(e);return this.request("POST",r.humanReviewComments(s),t)}listReviewComments(e,t){const s=e===void 0?"self":this.reviewPath(e);return this.request("GET",i(r.humanReviewComments(s),t))}readReviewCommentEvents(e,t){return this.request("GET",i(r.humanReviewCommentEvents(this.reviewPath(e)),t))}updateReviewCommentRanges(e,t){return this.request("POST",r.humanReviewCommentRanges(this.reviewPath(e)),t)}getReviewComment(e){return this.request("GET",r.humanComment(this.commentPath(e)))}editReviewComment(e,t){return this.request("POST",r.humanCommentEdit(this.commentPath(e)),t)}resolveReviewComment(e,t={}){return this.request("POST",r.humanCommentResolve(this.commentPath(e)),t)}reopenReviewComment(e,t={}){return this.request("POST",r.humanCommentReopen(this.commentPath(e)),t)}deleteReviewComment(e,t={}){return this.request("POST",r.humanCommentDelete(this.commentPath(e)),t)}forkReviewComment(e){return this.request("POST",r.humanCommentFork(this.commentPath(e)),{})}listHumanInbox(){return this.request("GET",r.humanInbox())}getHumanInboxPage(e){return this.request("GET",r.humanInboxTicket(this.ticketId(e)))}getInboxHistory(e){if(e===void 0)return this.request("GET",r.humanInbox()+"/history");if(typeof e!="string"||e==="")throw new TypeError(`invalid node id: ${JSON.stringify(e)}`);return this.request("GET",i(r.humanInbox()+"/history",{node_id:e}))}respondHumanInboxPage(e,t){return this.request("POST",r.humanInboxRespond(this.ticketId(e)),t)}postInboxProgress(e,t){return this.request("POST",r.humanInboxProgress(this.ticketId(e)),{responses:t})}getInboxResponse(e){return this.request("GET",r.humanInboxResponse(this.ticketId(e)))}cancelHumanInboxTicket(e,t){return this.request("POST",r.humanInboxCancel(this.ticketId(e)),t??{})}createHumanRequest(e){return this.request("POST",r.humanRequests(),e)}getHumanRequest(e){return this.request("GET",r.humanRequest(this.requestPath(e)))}replaceHumanRequest(e,t){return this.request("POST",r.humanRequestReplace(this.requestPath(e)),t)}respondHumanRequest(e,t){return this.request("POST",r.humanRequestRespond(this.requestPath(e)),t)}dismissHumanRequest(e,t={}){return this.request("POST",r.humanRequestDismiss(this.requestPath(e)),t)}cancelHumanRequest(e,t={}){return this.request("POST",r.humanRequestCancel(this.requestPath(e)),t)}resolvePageFeedbackComment(e,t,s){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(t))throw new TypeError(`invalid feedback comment id: ${JSON.stringify(t)}`);return this.request("POST",r.humanInboxFeedbackResolve(this.ticketId(e),t),{node_id:s})}async dashboard(e){const t=e?.under!==void 0?{under:e.under}:void 0,[s,o]=await Promise.all([this.listNodes(t),this.status()]);return{nodes:s,counts:o.node_counts,generated_at:new Date().toISOString()}}attention(){return this.request("GET",r.canvasAttention())}attentionCounts(e){const t={node_ids:e};return this.request("POST",r.canvasAttentionCounts(),t)}historySearch(e){return this.request("POST",r.canvasHistorySearch(),e)}historyGrep(e){return this.request("POST",r.canvasHistoryGrep(),e)}historyRead(e){return this.request("GET",i(r.canvasHistoryRead(),e))}historyStats(e){return this.request("POST",r.canvasHistoryStats(),e)}canvasSnapshot(){return this.request("GET",r.canvasSnapshot())}canvasRoster(){return this.request("GET",r.canvasRoster())}prune(e){return this.request("POST",r.canvasPrune(),e)}async request(e,t,s,o){return y(await this.send(e,t,s,void 0,o))}async send(e,t,s,o,a){try{return await this.transport(e,t,s,o,a)}catch(h){if(this.isColdSocketError(h))try{return await this.handleColdSocket(h),await this.transport(e,t,s,o,a)}catch(P){throw S(P)}if(this.isInterruptedSocketError(h))return await this.rideOutInterruptedRequest(e,t,s,o,a);throw S(h)}}nodePath(e){if(!p(e))throw new u(400,"invalid_node_id",`invalid node id: ${JSON.stringify(e)}`);return e}jobPath(e){if(!D(e))throw new u(400,"invalid_job_id",`invalid background job id: ${JSON.stringify(e)}`);return e}cronPath(e){if(!I(e))throw new u(400,"invalid_cron_id",`invalid cron id: ${JSON.stringify(e)}`);return e}requestPath(e){if(!p(e))throw new u(400,"invalid_request_id",`invalid request id: ${JSON.stringify(e)}`);return e}ticketId(e){if(!/^[a-f0-9]{64}$/.test(e))throw new TypeError(`invalid inbox ticket id: ${JSON.stringify(e)}`);return e}reviewPath(e){if(!p(e))throw new u(400,"invalid_review_id",`invalid review id: ${JSON.stringify(e)}`);return e}commentPath(e){if(!/^[a-f0-9]{32}$/.test(e))throw new u(400,"invalid_comment_id",`invalid comment id: ${JSON.stringify(e)}`);return e}transport(e,t,s,o,a=this.timeoutMs){const P=this.baseUrl?.protocol==="https:"?N:G,c=s===void 0?void 0:JSON.stringify(s),m={accept:"application/json",...this.headers,...o};c!==void 0&&(m["content-type"]="application/json",m["content-length"]=String(Buffer.byteLength(c)));const l={method:e,path:t,headers:m,timeout:a};return this.socketPath!==void 0?l.socketPath=this.socketPath:this.baseUrl!==void 0&&(l.protocol=this.baseUrl.protocol,l.hostname=this.baseUrl.hostname,this.baseUrl.port!==""&&(l.port=this.baseUrl.port)),new Promise((f,v)=>{const q=P(l,T=>{const O=[];T.on("data",E=>O.push(E)),T.on("end",()=>{const E={};for(const[g,w]of Object.entries(T.headers))w!==void 0&&(E[g]=Array.isArray(w)?w.join(", "):w);f({status:T.statusCode??0,text:Buffer.concat(O).toString("utf8"),headers:E})}),T.on("error",v)});q.on("error",v),q.on("timeout",()=>{q.destroy(Object.assign(new Error("request timed out"),{code:"ETIMEDOUT"}))}),c!==void 0&&q.write(c),q.end()})}isColdSocketError(e){if(this.socketPath===void 0)return!1;const t=e?.code;return t==="ECONNREFUSED"||t==="ENOENT"||t==="ENOTSOCK"}isInterruptedSocketError(e){if(this.socketPath===void 0)return!1;const t=e?.code;return t==="ECONNRESET"||t==="EPIPE"}async rideOutInterruptedRequest(e,t,s,o,a){try{await this.awaitAvailability()}catch(h){throw S(h)}if(e!=="GET"&&e!=="HEAD")throw new u(503,"daemon_request_interrupted",`crtrd connection ended before this ${e} response; the request may or may not have been applied.`);try{return await this.transport(e,t,s,o,a)}catch(h){throw S(h)}}async awaitAvailability(e){await L({windowMs:this.coldStartPollWindowMs,initialError:e,probe:d(async t=>{const s=await this.transport("GET",r.healthz(),void 0,void 0,t);if(!(s.status>=200&&s.status<300)){try{if(typeof JSON.parse(s.text).startup_blocked=="string")return}catch{}throw new u(s.status,"daemon_health_unavailable",`crtrd health check returned HTTP ${s.status}: ${s.text.slice(0,500)}`)}},"probe")})}async handleColdSocket(e){const t=this.autostart&&this.onColdSocket!==void 0&&!this.coldStartAttempted;t&&(this.coldStartAttempted=!0,await this.onColdSocket());try{await this.awaitAvailability(e)}catch(s){if(t){const o=J(this.coldStartDiagnostic);if(o!==void 0){const a=S(s);throw new u(a.status,a.code,`${a.message}
2
- ${o}`)}}throw s}}}function i(n,e){if(e===void 0)return n;const t=new URLSearchParams;for(const[o,a]of Object.entries(e))a!=null&&t.set(o,String(a));const s=t.toString();return s===""?n:`${n}?${s}`}d(i,"withQuery");function y(n){const e=n.status>=200&&n.status<300,t=n.text.trim();let s;if(t!=="")try{s=JSON.parse(t)}catch{if(e)return;throw new u(n.status,"invalid_response",n.text.slice(0,500))}if(e)return s;throw A(s)?new u(n.status,s.error.code,s.error.message,s.error.details):new u(n.status,"internal",`request failed with status ${n.status}`)}d(y,"parse");function S(n){if(n instanceof u)return n;const e=n?.code,t=n instanceof Error?n.message:String(n);return e==="ECONNREFUSED"||e==="ENOENT"||e==="ENOTSOCK"?new u(503,"daemon_unavailable",`crtrd is not reachable: ${t}`):e==="ETIMEDOUT"?new u(504,"request_timeout",`crtrd request timed out: ${t}`):new u(503,"transport_error",t)}d(S,"toTransportApiError");function F(n){return new Promise(e=>setTimeout(e,n))}d(F,"sleepMs");function J(n){if(n!==void 0)try{return n()}catch{return}}d(J,"safeColdStartDiagnostic");export{R as CrtrClient,J as safeColdStartDiagnostic,L as waitForDaemonAvailability};
1
+ var p=Object.defineProperty;var d=(s,e)=>p(s,"name",{value:e,configurable:!0});import{ApiError as h,isErrorBody as b}from"./errors.js";import{routes as r}from"./routes.js";import{isSafeNodeId as S}from"./dto/common.js";import{isSafeBashJobId as R}from"./dto/bash-jobs.js";import{isSafeCronId as g}from"./dto/crons.js";const k=3e4,C=Symbol.for("@north-light/crouter-api/local-socket-fetch"),G=1e4,N=200;async function _({windowMs:s,probe:e,initialError:t,pollIntervalMs:n=N,retry:i=d(()=>!0,"retry"),now:o=Date.now,sleep:P=w}){const l=o()+s;let u=t;for(;;){const c=l-o();if(c<=0)throw u??new Error("crtrd availability window expired without a probe failure");try{await e(c);return}catch(f){if(u=f,!i(f))throw f}const m=l-o();if(m<=0)throw u;await P(Math.min(n,m))}}d(_,"waitForDaemonAvailability");class U{static{d(this,"CrtrClient")}baseUrl;fetch;headers;autostart;timeoutMs;maxRetries;localSocketTransport;onColdSocket;coldStartDiagnostic;coldStartPollWindowMs;coldStartAttempted=!1;constructor(e){if(e.baseUrl==="")throw new TypeError("CrtrClient requires baseUrl");this.baseUrl=new URL(e.baseUrl),this.fetch=e.fetch??globalThis.fetch,this.headers={...e.headers},this.autostart=e.autostart??!1,this.timeoutMs=e.timeoutMs??k,this.maxRetries=e.maxRetries??2,this.localSocketTransport=this.fetch[C]===!0,e.onColdSocket!==void 0&&(this.onColdSocket=e.onColdSocket),e.coldStartDiagnostic!==void 0&&(this.coldStartDiagnostic=e.coldStartDiagnostic),this.coldStartPollWindowMs=e.coldStartPollWindowMs??G}healthz(){return this.request("GET",r.healthz())}async probeHealthz(e){const t=await this.transport("GET",r.healthz(),void 0,{timeout:e});if(t.status===503)try{const n=await t.clone().json();if(typeof n.startup_blocked=="string")return n}catch{}return O(t)}status(){return this.request("GET",r.status())}restartDaemon(){return this.request("POST",r.daemonRestart())}admitDaemon(){return this.request("POST",r.daemonAdmit())}migrateState(e){return this.request("POST",r.daemonMigrate(),e)}createNode(e){return this.request("POST",r.nodes(),e)}listNodes(e){return this.request("GET",a(r.nodes(),e))}getNode(e){return this.request("GET",r.node(this.nodePath(e)))}getNodeOutcome(e,{waitSeconds:t}={}){if(t!==void 0&&(!Number.isInteger(t)||t<0||t>25))throw new RangeError("waitSeconds must be an integer between 0 and 25");return this.request("GET",a(r.nodeOutcome(this.nodePath(e)),{wait:t}),void 0,{timeout:(t??0)*1e3+this.timeoutMs})}registerOutcomeDelivery(e,t){return this.request("PUT",r.nodeOutcomeDelivery(this.nodePath(e)),t)}getOutcomeDelivery(e){return this.request("GET",r.nodeOutcomeDelivery(this.nodePath(e)))}async disarmOutcomeDelivery(e){await this.request("DELETE",r.nodeOutcomeDelivery(this.nodePath(e)))}listBashJobs(e){return this.request("GET",r.nodeJobs(this.nodePath(e)))}stopBashJob(e,t){return this.request("DELETE",r.nodeJob(this.nodePath(e),this.jobPath(t)))}sendMessage(e,t){return this.request("POST",r.nodeMessages(this.nodePath(e)),t)}interruptNode(e){return this.request("POST",r.nodeInterrupt(this.nodePath(e)),{})}pushReport(e,t){return this.request("POST",r.nodeReports(this.nodePath(e)),t)}submitResult(e,t){return this.request("POST",r.nodeResult(this.nodePath(e)),t)}forkNode(e){return this.request("POST",r.nodeFork(this.nodePath(e)),{})}reviveNode(e,t){return this.request("POST",r.nodeRevive(this.nodePath(e)),t??{})}relaunchRoot(e){return this.request("POST",r.nodeRelaunchRoot(this.nodePath(e)),{})}bindBrokerSession(e,t){return this.request("POST",r.nodeBrokerSessionBound(this.nodePath(e)),t)}settleBroker(e,t){return this.request("POST",r.nodeBrokerSettle(this.nodePath(e)),t)}completeBrokerPark(e,t){return this.request("POST",r.nodeBrokerParkComplete(this.nodePath(e)),t)}recordBrokerParkActivity(e,t){return this.request("POST",r.nodeBrokerParkActivity(this.nodePath(e)),t)}claimNodeMail(e,t){return this.request("POST",r.nodeMailClaim(this.nodePath(e)),t)}acknowledgeNodeMail(e,t){return this.request("POST",r.nodeMailAcknowledge(this.nodePath(e)),t)}recordBrokerTurn(e,t){return this.request("POST",r.brokerTurn(this.nodePath(e)),t)}mutateBrokerProviderRetry(e,t){return this.request("POST",r.brokerProviderRetry(this.nodePath(e)),t)}mutateBrokerFault(e,t){return this.request("POST",r.brokerFault(this.nodePath(e)),t)}getBrokerRecovery(e,t){return this.request("GET",`${r.brokerRecovery(this.nodePath(e))}?expected_execution_id=${encodeURIComponent(t)}`)}recordNodeFault(e,t){return this.request("POST",r.nodeFault(this.nodePath(e)),t)}clearNodeFault(e,t={}){const n=new URLSearchParams;t.link!==void 0&&n.set("link",t.link),t.preserve_episode!==void 0&&n.set("preserve_episode",String(t.preserve_episode));const i=n.size===0?"":`?${n.toString()}`;return this.request("DELETE",`${r.nodeFault(this.nodePath(e))}${i}`)}commitBrokerModel(e,t){return this.request("POST",r.nodeBrokerModel(this.nodePath(e)),t)}brokerExtensionState(e){return this.request("GET",r.nodeBrokerExtensionState(this.nodePath(e)))}commitBrokerGeneratedName(e,t){return this.request("POST",r.nodeBrokerGeneratedName(this.nodePath(e)),t)}commitBrokerPersonaAck(e,t){return this.request("POST",r.nodeBrokerPersonaAck(this.nodePath(e)),t)}closeNode(e,t){return this.request("POST",r.nodeClose(this.nodePath(e)),t??{})}recycleNode(e){return this.request("POST",r.nodeRecycle(this.nodePath(e)),{})}demoteNode(e){return this.request("POST",r.nodeDemote(this.nodePath(e)),{})}promoteNode(e,t){return this.request("POST",r.nodePromote(this.nodePath(e)),t)}yieldNode(e,t){return this.request("POST",r.nodeYield(this.nodePath(e)),t)}waitNode(e,t){return this.request("POST",r.nodeWait(this.nodePath(e)),t)}patchConfig(e,t){return this.request("PATCH",r.nodeConfig(this.nodePath(e)),t)}closeWorktree(e){return this.request("POST",r.nodeWorktreeClose(this.nodePath(e)),{})}abandonWorktree(e,t){return this.request("POST",r.nodeWorktreeAbandon(this.nodePath(e)),t)}listQuarantinedWorktrees(){return this.request("GET",r.quarantinedWorktrees())}subscribe(e,t){return this.request("POST",r.nodeSubscriptions(this.nodePath(e)),t)}listFocuses(){return this.request("GET",r.focuses())}focusOf(e){return this.request("GET",a(r.focusByNode(),{node_id:e}))}focusByPane(e){return this.request("GET",a(r.focusByPane(),{pane:e}))}registerFocus(e){return this.request("POST",r.focuses(),e)}async setFocusPane(e,t){await this.request("PATCH",r.focus(e),t)}async closeFocus(e){await this.request("DELETE",r.focus(e))}async unsubscribe(e,t){await this.request("DELETE",r.nodeSubscription(this.nodePath(e),this.nodePath(t)))}armCron(e){return this.request("POST",r.crons(),e)}listCrons(e){return this.request("GET",a(r.crons(),e))}showCron(e,t){return this.request("GET",a(r.cron(this.cronPath(e)),t))}pauseCron(e,t){return this.request("POST",a(r.cronPause(this.cronPath(e)),t),{})}resumeCron(e,t){return this.request("POST",a(r.cronResume(this.cronPath(e)),t),{})}runCron(e,t){return this.request("POST",a(r.cronRun(this.cronPath(e)),t),{})}async cancelCron(e,t){await this.request("DELETE",a(r.cron(this.cronPath(e)),t))}pokeCrons(){return this.request("POST",r.cronsPoke(),{})}ensureAttach(e,t){return this.request("POST",r.nodeAttach(this.nodePath(e)),t??{})}getReports(e,t){return this.request("GET",a(r.nodeReports(this.nodePath(e)),t))}getTranscript(e,t){return this.request("GET",a(r.nodeTranscript(this.nodePath(e)),t))}getSnapshot(e){return this.request("GET",r.nodeSnapshot(this.nodePath(e)))}nodeSubject(e){return this.request("GET",r.nodeSubject(this.nodePath(e)))}getNodeMessages(e,t){return this.request("GET",a(r.nodeMessages(this.nodePath(e)),t))}getSession(e){return this.request("GET",r.nodeSession(this.nodePath(e)))}getChatInventory(e){return this.request("GET",r.nodeChatInventory(this.nodePath(e)))}getProspectiveChatInventory(e){return this.request("GET",a(r.prospectiveChatInventory(),e))}getArtifacts(e,t){return this.request("GET",a(r.nodeArtifacts(this.nodePath(e)),t))}getContext(e){return this.request("GET",r.nodeContext(this.nodePath(e)))}peekFile(e){return this.request("GET",a(r.filePeek(),{path:e}))}resolveMemoryDoc(e,t){return this.request("GET",a(r.memoryResolve(),{name:e,node:t}))}ensureProfile(e,t){return this.request("PUT",r.profile(e),t??{})}listProfiles(){return this.request("GET",r.profiles())}getProfile(e){return this.request("GET",r.profile(e))}pauseProfile(e){return this.request("POST",r.profilePause(e),{})}resumeProfile(e){return this.request("POST",r.profileResume(e),{})}updateProfileMetadata(e,t){return this.request("PATCH",r.profileMetadata(e),t)}deleteProfile(e,t){return this.request("DELETE",r.profile(e),t)}listModelAuth(){return this.request("GET",r.modelAuths())}getModelAuthReadiness(e){return this.request("GET",a(r.modelAuthReadiness(),e))}installCredential(e,t){return this.request("PUT",r.modelAuth(e),t)}removeCredential(e){return this.request("DELETE",r.modelAuth(e))}createReview(e){return this.request("POST",r.humanReviews(),e)}listReviews(e){return this.request("GET",a(r.humanReviews(),e))}getReview(e){return this.request("GET",r.humanReview(this.reviewPath(e)))}submitReview(e){return this.request("POST",r.humanReviewSubmit(this.reviewPath(e)),{})}cancelReview(e,t={}){return this.request("POST",r.humanReviewCancel(this.reviewPath(e)),t)}getReviewDocumentBase(e){return this.request("GET",r.humanReviewDocument(this.reviewPath(e)))}createReviewComment(e,t){const n=e===void 0?"self":this.reviewPath(e);return this.request("POST",r.humanReviewComments(n),t)}listReviewComments(e,t){const n=e===void 0?"self":this.reviewPath(e);return this.request("GET",a(r.humanReviewComments(n),t))}readReviewCommentEvents(e,t){return this.request("GET",a(r.humanReviewCommentEvents(this.reviewPath(e)),t))}updateReviewCommentRanges(e,t){return this.request("POST",r.humanReviewCommentRanges(this.reviewPath(e)),t)}getReviewComment(e){return this.request("GET",r.humanComment(this.commentPath(e)))}editReviewComment(e,t){return this.request("POST",r.humanCommentEdit(this.commentPath(e)),t)}resolveReviewComment(e,t={}){return this.request("POST",r.humanCommentResolve(this.commentPath(e)),t)}reopenReviewComment(e,t={}){return this.request("POST",r.humanCommentReopen(this.commentPath(e)),t)}deleteReviewComment(e,t={}){return this.request("POST",r.humanCommentDelete(this.commentPath(e)),t)}forkReviewComment(e){return this.request("POST",r.humanCommentFork(this.commentPath(e)),{})}listHumanInbox(){return this.request("GET",r.humanInbox())}getHumanInboxPage(e){return this.request("GET",r.humanInboxTicket(this.ticketId(e)))}getInboxHistory(e){if(e===void 0)return this.request("GET",r.humanInbox()+"/history");if(typeof e!="string"||e==="")throw new TypeError(`invalid node id: ${JSON.stringify(e)}`);return this.request("GET",a(r.humanInbox()+"/history",{node_id:e}))}respondHumanInboxPage(e,t){return this.request("POST",r.humanInboxRespond(this.ticketId(e)),t)}postInboxProgress(e,t){return this.request("POST",r.humanInboxProgress(this.ticketId(e)),{responses:t})}getInboxResponse(e){return this.request("GET",r.humanInboxResponse(this.ticketId(e)))}cancelHumanInboxTicket(e,t){return this.request("POST",r.humanInboxCancel(this.ticketId(e)),t??{})}createHumanRequest(e){return this.request("POST",r.humanRequests(),e)}getHumanRequest(e){return this.request("GET",r.humanRequest(this.requestPath(e)))}replaceHumanRequest(e,t){return this.request("POST",r.humanRequestReplace(this.requestPath(e)),t)}respondHumanRequest(e,t){return this.request("POST",r.humanRequestRespond(this.requestPath(e)),t)}dismissHumanRequest(e,t={}){return this.request("POST",r.humanRequestDismiss(this.requestPath(e)),t)}cancelHumanRequest(e,t={}){return this.request("POST",r.humanRequestCancel(this.requestPath(e)),t)}resolvePageFeedbackComment(e,t,n){if(!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(t))throw new TypeError(`invalid feedback comment id: ${JSON.stringify(t)}`);return this.request("POST",r.humanInboxFeedbackResolve(this.ticketId(e),t),{node_id:n})}async dashboard(e){const t=e?.under!==void 0?{under:e.under}:void 0,[n,i]=await Promise.all([this.listNodes(t),this.status()]);return{nodes:n,counts:i.node_counts,generated_at:new Date().toISOString()}}attention(){return this.request("GET",r.canvasAttention())}attentionCounts(e){const t={node_ids:e};return this.request("POST",r.canvasAttentionCounts(),t)}historySearch(e){return this.request("POST",r.canvasHistorySearch(),e)}historyGrep(e){return this.request("POST",r.canvasHistoryGrep(),e)}historyRead(e){return this.request("GET",a(r.canvasHistoryRead(),e))}historyStats(e){return this.request("POST",r.canvasHistoryStats(),e)}canvasSnapshot(){return this.request("GET",r.canvasSnapshot())}canvasRoster(){return this.request("GET",r.canvasRoster())}prune(e){return this.request("POST",r.canvasPrune(),e)}async request(e,t,n,i){try{return await O(await this.send(e,t,n,i))}catch(o){throw T(o,i?.signal)}}async send(e,t,n,i={}){const o=e==="GET"||e==="HEAD"||e==="DELETE",P=i.maxRetries??this.maxRetries;for(let l=0;;l++){let u;try{u=await this.transport(e,t,n,i)}catch(c){if(this.isColdSocketError(c))try{await this.handleColdSocket(c),u=await this.transport(e,t,n,i)}catch(m){throw T(m,i.signal)}else{if(this.isInterruptedSocketError(c)&&this.localSocketTransport&&(!o||l<P))return await this.rideOutInterruptedRequest(e,t,n,i);if(o&&l<P&&!A(c,i.signal)&&(!this.localSocketTransport||!this.isInterruptedSocketError(c))){await w(v(l+1));continue}else throw T(c,i.signal)}}if(o&&l<P&&x(u.status)){await D(u),await w(v(l+1));continue}return u}}nodePath(e){if(!S(e))throw new h(400,"invalid_node_id",`invalid node id: ${JSON.stringify(e)}`);return e}jobPath(e){if(!R(e))throw new h(400,"invalid_job_id",`invalid background job id: ${JSON.stringify(e)}`);return e}cronPath(e){if(!g(e))throw new h(400,"invalid_cron_id",`invalid cron id: ${JSON.stringify(e)}`);return e}requestPath(e){if(!S(e))throw new h(400,"invalid_request_id",`invalid request id: ${JSON.stringify(e)}`);return e}ticketId(e){if(!/^[a-f0-9]{64}$/.test(e))throw new TypeError(`invalid inbox ticket id: ${JSON.stringify(e)}`);return e}reviewPath(e){if(!S(e))throw new h(400,"invalid_review_id",`invalid review id: ${JSON.stringify(e)}`);return e}commentPath(e){if(!/^[a-f0-9]{32}$/.test(e))throw new h(400,"invalid_comment_id",`invalid comment id: ${JSON.stringify(e)}`);return e}transport(e,t,n,i={}){const o=n===void 0?void 0:JSON.stringify(n),P={accept:"application/json",...this.headers,...i.headers};o!==void 0&&(P["content-type"]="application/json");const l=i.timeout??this.timeoutMs,u=new AbortController,c=l>0?setTimeout(()=>u.abort(new DOMException("request timed out","TimeoutError")),l):void 0,m=i.signal,f=d(()=>u.abort(m?.reason),"onExternalAbort");m!==void 0&&(m.aborted?u.abort(m.reason):m.addEventListener("abort",f,{once:!0}));const y=d(()=>{c!==void 0&&clearTimeout(c),m?.removeEventListener("abort",f)},"finish");return this.fetch(new URL(t,this.baseUrl),{method:e,headers:P,body:o,signal:u.signal}).then(q=>this.retainRequestLifetime(q,y,()=>u.signal.aborted?u.signal.reason:void 0),q=>{throw y(),q})}retainRequestLifetime(e,t,n){if(e.body===null)return t(),e;const i=e.body.getReader();let o=!1;const P=d(()=>{o||(o=!0,t())},"close"),l=new ReadableStream({async pull(u){try{const{done:c,value:m}=await i.read();c?(P(),u.close()):u.enqueue(m)}catch(c){P(),u.error(n()??c)}},async cancel(u){P(),await i.cancel(u)}});return new Response(l,{status:e.status,statusText:e.statusText,headers:e.headers})}isColdSocketError(e){const t=E(e);return t==="ECONNREFUSED"||t==="ENOENT"||t==="ENOTSOCK"}isInterruptedSocketError(e){const t=E(e);return t==="ECONNRESET"||t==="EPIPE"}async rideOutInterruptedRequest(e,t,n,i={}){try{await this.awaitAvailability()}catch(o){throw T(o,i.signal)}if(e!=="GET"&&e!=="HEAD")throw new h(503,"daemon_request_interrupted",`crtrd connection ended before this ${e} response; the request may or may not have been applied.`);try{return await this.transport(e,t,n,i)}catch(o){throw T(o,i.signal)}}async awaitAvailability(e){await _({windowMs:this.coldStartPollWindowMs,initialError:e,probe:d(async t=>{const n=await this.transport("GET",r.healthz(),void 0,{timeout:t});if(n.status>=200&&n.status<300)return;const i=await n.text();try{if(typeof JSON.parse(i).startup_blocked=="string")return}catch{}throw new h(n.status,"daemon_health_unavailable",`crtrd health check returned HTTP ${n.status}: ${i.slice(0,500)}`)},"probe")})}async handleColdSocket(e){const t=this.autostart&&this.onColdSocket!==void 0&&!this.coldStartAttempted;t&&(this.coldStartAttempted=!0,await this.onColdSocket());try{await this.awaitAvailability(e)}catch(n){if(t){const i=M(this.coldStartDiagnostic);if(i!==void 0){const o=T(n);throw new h(o.status,o.code,`${o.message}
2
+ ${i}`)}}throw n}}}function a(s,e){if(e===void 0)return s;const t=new URLSearchParams;for(const[i,o]of Object.entries(e))o!=null&&t.set(i,String(o));const n=t.toString();return n===""?s:`${s}?${n}`}d(a,"withQuery");async function O(s){const e=s.status>=200&&s.status<300,t=(await s.text()).trim();let n;if(t!=="")try{n=JSON.parse(t)}catch{if(e)return;throw new h(s.status,"invalid_response",t.slice(0,500),void 0,s.headers)}if(e)return n;throw b(n)?new h(s.status,n.error.code,n.error.message,n.error.details,s.headers):new h(s.status,"internal",`request failed with status ${s.status}`,void 0,s.headers)}d(O,"parse");function x(s){return s===429||s>=500}d(x,"isRetryableStatus");async function D(s){try{await s.body?.cancel()}catch{}}d(D,"drainResponseBody");function v(s){return Math.min(2e3,250*2**(s-1))}d(v,"retryDelayMs");function E(s){if(typeof s!="object"||s===null)return;const e=s.code;if(typeof e=="string")return e;const t=s.cause;if(typeof t=="object"&&t!==null){const n=t.code;if(typeof n=="string")return n}}d(E,"transportErrorCode");function A(s,e){return e?.aborted?!0:s instanceof DOMException&&(s.name==="AbortError"||s.name==="TimeoutError")}d(A,"isAbortError");function T(s,e){if(e?.aborted)return new h(0,"request_aborted","request aborted by caller signal");if(s instanceof h)return s;if(s instanceof DOMException&&s.name==="TimeoutError")return new h(504,"request_timeout",`crtrd request timed out: ${s.message}`);if(s instanceof DOMException&&s.name==="AbortError")return new h(0,"request_aborted","request aborted by caller signal");const t=E(s),n=s instanceof Error?s.message:String(s);return t==="ECONNREFUSED"||t==="ENOENT"||t==="ENOTSOCK"?new h(503,"daemon_unavailable",`crtrd is not reachable: ${n}`):t==="ETIMEDOUT"?new h(504,"request_timeout",`crtrd request timed out: ${n}`):new h(503,"transport_error",n)}d(T,"toTransportApiError");function w(s){return new Promise(e=>setTimeout(e,s))}d(w,"sleepMs");function M(s){if(s!==void 0)try{return s()}catch{return}}d(M,"safeColdStartDiagnostic");export{U as CrtrClient,M as safeColdStartDiagnostic,_ as waitForDaemonAvailability};
@@ -66,6 +66,21 @@ export type ModelAuthProviderStatusDTO = {
66
66
  export interface ModelAuthListDTO {
67
67
  providers: ModelAuthProviderStatusDTO[];
68
68
  }
69
+ /** Inputs that select the exact provider a new root node would use. */
70
+ export interface ModelAuthReadinessQuery {
71
+ profile?: string;
72
+ cwd?: string;
73
+ kind?: string;
74
+ model?: string;
75
+ }
76
+ /** `GET /v1/model-auth/readiness` — credential state for the provider selected by a prospective root launch. */
77
+ export interface ModelAuthReadinessDTO {
78
+ provider: string;
79
+ model: string;
80
+ credential: 'ready' | 'missing' | 'unusable';
81
+ reason?: 'invalid_grant';
82
+ rate_limited_until?: string | null;
83
+ }
69
84
  /** `DELETE /v1/model-auth/{provider}` — all user credentials removed for one provider. */
70
85
  export interface CredentialRemovalResultDTO {
71
86
  provider: string;
@@ -9,14 +9,17 @@ export interface ErrorBody {
9
9
  /** Thrown by `CrtrClient` on a non-2xx response (or a synthesized transport
10
10
  * failure such as an unreachable daemon). Carries the HTTP status, the stable
11
11
  * machine `code` from the `ErrorBody`, and optional structured `details`. */
12
- export declare class ApiError extends Error {
12
+ export declare class APIError extends Error {
13
13
  readonly status: number;
14
14
  readonly code: string;
15
15
  readonly details?: unknown;
16
- constructor(status: number, code: string, message: string, details?: unknown);
16
+ readonly headers: Headers;
17
+ constructor(status: number, code: string, message: string, details?: unknown, headers?: HeadersInit);
17
18
  }
19
+ /** @deprecated Use APIError. Kept as the in-repo compatibility alias. */
20
+ export { APIError as ApiError };
18
21
  /** A local API transport failure has stable status/code identity, independent
19
22
  * of message prose. */
20
- export declare function isDaemonTransportApiError(error: unknown): error is ApiError;
23
+ export declare function isDaemonTransportApiError(error: unknown): error is APIError;
21
24
  /** Type guard for an `ErrorBody`-shaped parsed payload. */
22
25
  export declare function isErrorBody(value: unknown): value is ErrorBody;
@@ -1 +1 @@
1
- var a=Object.defineProperty;var s=(t,e)=>a(t,"name",{value:e,configurable:!0});class i extends Error{static{s(this,"ApiError")}status;code;details;constructor(e,n,o,r){super(o),this.name="ApiError",this.status=e,this.code=n,r!==void 0&&(this.details=r)}}function c(t){return t instanceof i?t.status===503&&(t.code==="daemon_unavailable"||t.code==="transport_error"||t.code==="daemon_request_interrupted")||t.status===504&&t.code==="request_timeout"||t.status>=500&&t.code==="daemon_health_unavailable":!1}s(c,"isDaemonTransportApiError");function d(t){if(typeof t!="object"||t===null)return!1;const e=t.error;if(typeof e!="object"||e===null)return!1;const{code:n,message:o}=e;return typeof n=="string"&&typeof o=="string"}s(d,"isErrorBody");export{i as ApiError,c as isDaemonTransportApiError,d as isErrorBody};
1
+ var u=Object.defineProperty;var s=(t,e)=>u(t,"name",{value:e,configurable:!0});class a extends Error{static{s(this,"APIError")}status;code;details;headers;constructor(e,o,n,r,i){super(n),this.name="APIError",this.status=e,this.code=o,this.headers=new Headers(i),r!==void 0&&(this.details=r)}}function c(t){return t instanceof a?t.status===503&&(t.code==="daemon_unavailable"||t.code==="transport_error"||t.code==="daemon_request_interrupted")||t.status===504&&t.code==="request_timeout"||t.status>=500&&t.code==="daemon_health_unavailable":!1}s(c,"isDaemonTransportApiError");function f(t){if(typeof t!="object"||t===null)return!1;const e=t.error;if(typeof e!="object"||e===null)return!1;const{code:o,message:n}=e;return typeof o=="string"&&typeof n=="string"}s(f,"isErrorBody");export{a as APIError,a as ApiError,c as isDaemonTransportApiError,f as isErrorBody};
@@ -1,6 +1,6 @@
1
1
  export { CrtrClient, waitForDaemonAvailability } from './client.js';
2
- export type { CrtrClientOptions } from './client.js';
3
- export { ApiError, isErrorBody } from './errors.js';
2
+ export type { CrtrClientOptions, CrtrRequestOptions } from './client.js';
3
+ export { APIError, ApiError, isErrorBody } from './errors.js';
4
4
  export type { ErrorBody } from './errors.js';
5
5
  export { API_VERSION, routes } from './routes.js';
6
6
  export * from '../shared/generated-context.js';
package/dist/api/index.js CHANGED
@@ -1 +1 @@
1
- import{CrtrClient as t,waitForDaemonAvailability as e}from"./client.js";import{ApiError as p,isErrorBody as f}from"./errors.js";import{API_VERSION as i,routes as a}from"./routes.js";export*from"../shared/generated-context.js";export*from"./dto/common.js";export*from"./dto/health.js";export*from"./dto/nodes.js";export*from"./dto/node-outcomes.js";export*from"./dto/bash-jobs.js";export*from"./dto/messages.js";export*from"./dto/reports.js";export*from"./dto/lifecycle.js";export*from"./dto/subscriptions.js";export*from"./dto/focus.js";export*from"./dto/crons.js";export*from"./dto/config.js";export*from"./dto/attach.js";export*from"./dto/broker.js";export*from"./dto/profiles.js";export*from"./dto/modelauth.js";export*from"./dto/canvas.js";export*from"./dto/worktree.js";export*from"./dto/human.js";export*from"./dto/files.js";export*from"./dto/memory.js";export*from"./dto/inbox.js";export*from"./dto/human-requests.js";export*from"./dto/reviews.js";export*from"./dto/review-comments.js";export*from"./dto/chat-inventory.js";export{i as API_VERSION,p as ApiError,t as CrtrClient,f as isErrorBody,a as routes,e as waitForDaemonAvailability};
1
+ import{CrtrClient as t,waitForDaemonAvailability as e}from"./client.js";import{APIError as p,ApiError as f,isErrorBody as x}from"./errors.js";import{API_VERSION as a,routes as A}from"./routes.js";export*from"../shared/generated-context.js";export*from"./dto/common.js";export*from"./dto/health.js";export*from"./dto/nodes.js";export*from"./dto/node-outcomes.js";export*from"./dto/bash-jobs.js";export*from"./dto/messages.js";export*from"./dto/reports.js";export*from"./dto/lifecycle.js";export*from"./dto/subscriptions.js";export*from"./dto/focus.js";export*from"./dto/crons.js";export*from"./dto/config.js";export*from"./dto/attach.js";export*from"./dto/broker.js";export*from"./dto/profiles.js";export*from"./dto/modelauth.js";export*from"./dto/canvas.js";export*from"./dto/worktree.js";export*from"./dto/human.js";export*from"./dto/files.js";export*from"./dto/memory.js";export*from"./dto/inbox.js";export*from"./dto/human-requests.js";export*from"./dto/reviews.js";export*from"./dto/review-comments.js";export*from"./dto/chat-inventory.js";export{p as APIError,a as API_VERSION,f as ApiError,t as CrtrClient,x as isErrorBody,A as routes,e as waitForDaemonAvailability};
@@ -0,0 +1,18 @@
1
+ import { CrtrClient, type CrtrClientOptions } from './client.js';
2
+ /** Resolve crtrd's default unix socket path the same way `apiSocketPath()` does,
3
+ * via Node built-ins only (no `core/canvas/paths.ts` import). */
4
+ export declare function defaultSocketPath(): string;
5
+ /** A standard `fetch` bound to one unix socket, built on `node:http` with
6
+ * `{ socketPath }`. The returned `Response`'s body is a live `ReadableStream`
7
+ * fed as bytes arrive — never buffered before the caller sees headers —
8
+ * which is what lets `CrtrClient` serve a streaming route (SSE) later without
9
+ * a second transport. Connection failures are wrapped as `TypeError('fetch
10
+ * failed', { cause })`, matching the shape native `fetch` throws, so
11
+ * `CrtrClient`'s cold-socket/interrupted-request classification (`err.cause.code`)
12
+ * reads identically off either transport. */
13
+ export declare function socketFetch(socketPath: string): typeof fetch;
14
+ /** Construct a client bound to the default local socket with autostart on.
15
+ * Without `onColdSocket`, a cold socket fails with `daemon_unavailable`. */
16
+ export declare function localClient(opts?: Omit<CrtrClientOptions, 'baseUrl' | 'fetch'> & {
17
+ socketPath?: string;
18
+ }): CrtrClient;
@@ -0,0 +1 @@
1
+ var C=Object.defineProperty;var a=(e,o)=>C(e,"name",{value:o,configurable:!0});import{request as v}from"node:http";import{homedir as A}from"node:os";import{join as b}from"node:path";import{CrtrClient as R}from"./client.js";import{envHomeOverride as S}from"../shared/env.js";const T=".crouter",O="crtrd.sock",k=Symbol.for("@north-light/crouter-api/local-socket-fetch");function L(){const e=S(),o=e!==void 0&&e!==""?e:b(A(),T,"canvas");return b(o,O)}a(L,"defaultSocketPath");function x(e){const o=a(function(n,s={}){const m=n instanceof URL?n:new URL(typeof n=="string"?n:n.url),w=s.method??"GET",h={};if(s.headers!==void 0)for(const[l,u]of new Headers(s.headers).entries())h[l]=u;const f=typeof s.body=="string"?s.body:void 0;return f!==void 0&&(h["content-length"]=String(Buffer.byteLength(f))),new Promise((l,u)=>{const i=v({socketPath:e,path:m.pathname+m.search,method:w,headers:h},t=>{const E=new ReadableStream({start(d){t.on("data",r=>d.enqueue(new Uint8Array(r))),t.on("end",()=>d.close()),t.on("error",r=>d.error(r))},cancel(){t.destroy()}}),y=new Headers;for(const[d,r]of Object.entries(t.headers))if(r!==void 0)for(const g of Array.isArray(r)?r:[r])y.append(d,g);l(new Response(E,{status:t.statusCode??0,statusText:t.statusMessage,headers:y}))});i.on("error",t=>u(new TypeError("fetch failed",{cause:t})));const c=s.signal,p=a(()=>{i.destroy(),u(c?.reason??new DOMException("The operation was aborted.","AbortError"))},"abort");if(c!=null){if(c.aborted){p();return}c.addEventListener("abort",p,{once:!0})}f!==void 0&&i.write(f),i.end()})},"fetchOverSocket");return o[k]=!0,o}a(x,"socketFetch");function B(e={}){const{socketPath:o,...n}=e;return new R({baseUrl:"http://localhost",fetch:x(o??L()),autostart:!0,...n})}a(B,"localClient");export{L as defaultSocketPath,B as localClient,x as socketFetch};
@@ -109,6 +109,7 @@ export declare const routes: {
109
109
  readonly profileResume: (name: string) => string;
110
110
  readonly profileMetadata: (name: string) => string;
111
111
  readonly modelAuths: () => string;
112
+ readonly modelAuthReadiness: () => string;
112
113
  readonly modelAuth: (provider: string) => string;
113
114
  readonly filePeek: () => string;
114
115
  readonly memoryResolve: () => string;
@@ -1 +1 @@
1
- var s=Object.defineProperty;var o=(e,n)=>s(e,"name",{value:n,configurable:!0});const a="v1",$="/v1",t={healthz:o(()=>"/healthz","healthz"),status:o(()=>"/v1/status","status"),daemonRestart:o(()=>"/v1/daemon/restart","daemonRestart"),daemonMigrate:o(()=>"/v1/daemon/migrate","daemonMigrate"),daemonAdmit:o(()=>"/v1/daemon/admit","daemonAdmit"),nodes:o(()=>"/v1/nodes","nodes"),reviveAll:o(()=>"/v1/nodes/revive-all","reviveAll"),node:o(e=>`/v1/nodes/${e}`,"node"),nodeOutcome:o(e=>`/v1/nodes/${e}/outcome`,"nodeOutcome"),nodeOutcomeDelivery:o(e=>`/v1/nodes/${e}/outcome-delivery`,"nodeOutcomeDelivery"),nodeSnapshot:o(e=>`/v1/nodes/${e}/snapshot`,"nodeSnapshot"),nodeSubject:o(e=>`/v1/nodes/${e}/subject`,"nodeSubject"),nodeSession:o(e=>`/v1/nodes/${e}/session`,"nodeSession"),nodeChatInventory:o(e=>`/v1/nodes/${e}/chat-inventory`,"nodeChatInventory"),prospectiveChatInventory:o(()=>"/v1/prospective-chat-inventory","prospectiveChatInventory"),nodeTranscript:o(e=>`/v1/nodes/${e}/transcript`,"nodeTranscript"),nodeContext:o(e=>`/v1/nodes/${e}/context`,"nodeContext"),nodeArtifacts:o(e=>`/v1/nodes/${e}/artifacts`,"nodeArtifacts"),nodeReports:o(e=>`/v1/nodes/${e}/reports`,"nodeReports"),nodeResult:o(e=>`/v1/nodes/${e}/result`,"nodeResult"),nodeJobs:o(e=>`/v1/nodes/${e}/jobs`,"nodeJobs"),nodeJob:o((e,n)=>`/v1/nodes/${e}/jobs/${n}`,"nodeJob"),nodeMessages:o(e=>`/v1/nodes/${e}/messages`,"nodeMessages"),nodeInterrupt:o(e=>`/v1/nodes/${e}/interrupt`,"nodeInterrupt"),nodeFork:o(e=>`/v1/nodes/${e}/fork`,"nodeFork"),nodeRevive:o(e=>`/v1/nodes/${e}/revive`,"nodeRevive"),nodeRelaunchRoot:o(e=>`/v1/nodes/${e}/relaunch-root`,"nodeRelaunchRoot"),nodeBrokerSessionBound:o(e=>`/v1/nodes/${e}/broker/session-bound`,"nodeBrokerSessionBound"),nodeBrokerSettle:o(e=>`/v1/nodes/${e}/broker/settle`,"nodeBrokerSettle"),nodeBrokerParkComplete:o(e=>`/v1/nodes/${e}/broker/park-complete`,"nodeBrokerParkComplete"),nodeBrokerParkActivity:o(e=>`/v1/nodes/${e}/broker/park-activity`,"nodeBrokerParkActivity"),nodeMailClaim:o(e=>`/v1/nodes/${e}/mail/claim`,"nodeMailClaim"),nodeMailAcknowledge:o(e=>`/v1/nodes/${e}/mail/acknowledge`,"nodeMailAcknowledge"),nodeBrokerModel:o(e=>`/v1/nodes/${e}/broker/model`,"nodeBrokerModel"),nodeBrokerExtensionState:o(e=>`/v1/nodes/${e}/broker/extension-state`,"nodeBrokerExtensionState"),nodeBrokerGeneratedName:o(e=>`/v1/nodes/${e}/broker/generated-name`,"nodeBrokerGeneratedName"),nodeBrokerPersonaAck:o(e=>`/v1/nodes/${e}/broker/persona-ack`,"nodeBrokerPersonaAck"),brokerTurn:o(e=>`/v1/broker/${e}/turn`,"brokerTurn"),brokerProviderRetry:o(e=>`/v1/broker/${e}/provider-retry`,"brokerProviderRetry"),brokerFault:o(e=>`/v1/broker/${e}/fault`,"brokerFault"),brokerRecovery:o(e=>`/v1/broker/${e}/recovery`,"brokerRecovery"),nodeFault:o(e=>`/v1/nodes/${e}/fault`,"nodeFault"),nodeClose:o(e=>`/v1/nodes/${e}/close`,"nodeClose"),nodeRecycle:o(e=>`/v1/nodes/${e}/recycle`,"nodeRecycle"),nodeDemote:o(e=>`/v1/nodes/${e}/demote`,"nodeDemote"),nodePromote:o(e=>`/v1/nodes/${e}/promote`,"nodePromote"),nodeYield:o(e=>`/v1/nodes/${e}/yield`,"nodeYield"),nodeWait:o(e=>`/v1/nodes/${e}/wait`,"nodeWait"),nodeConfig:o(e=>`/v1/nodes/${e}/config`,"nodeConfig"),nodeWorktreeClose:o(e=>`/v1/nodes/${e}/worktree/close`,"nodeWorktreeClose"),nodeWorktreeAbandon:o(e=>`/v1/nodes/${e}/worktree/abandon`,"nodeWorktreeAbandon"),quarantinedWorktrees:o(()=>"/v1/worktrees/quarantined","quarantinedWorktrees"),nodeAttach:o(e=>`/v1/nodes/${e}/attach`,"nodeAttach"),focuses:o(()=>"/v1/focuses","focuses"),focusByNode:o(()=>"/v1/focuses/by-node","focusByNode"),focusByPane:o(()=>"/v1/focuses/by-pane","focusByPane"),focus:o(e=>`/v1/focuses/${e}`,"focus"),nodeSubscriptions:o(e=>`/v1/nodes/${e}/subscriptions`,"nodeSubscriptions"),nodeSubscription:o((e,n)=>`/v1/nodes/${e}/subscriptions/${n}`,"nodeSubscription"),crons:o(()=>"/v1/crons","crons"),cron:o(e=>`/v1/crons/${e}`,"cron"),cronPause:o(e=>`/v1/crons/${e}/pause`,"cronPause"),cronResume:o(e=>`/v1/crons/${e}/resume`,"cronResume"),cronRun:o(e=>`/v1/crons/${e}/run`,"cronRun"),cronsPoke:o(()=>"/v1/crons/poke","cronsPoke"),canvasAttention:o(()=>"/v1/canvas/attention","canvasAttention"),canvasAttentionCounts:o(()=>"/v1/canvas/attention/counts","canvasAttentionCounts"),canvasHistorySearch:o(()=>"/v1/canvas/history/search","canvasHistorySearch"),canvasHistoryGrep:o(()=>"/v1/canvas/history/grep","canvasHistoryGrep"),canvasHistoryRead:o(()=>"/v1/canvas/history/read","canvasHistoryRead"),canvasHistoryStats:o(()=>"/v1/canvas/history/stats","canvasHistoryStats"),canvasSnapshot:o(()=>"/v1/canvas/snapshot","canvasSnapshot"),canvasRoster:o(()=>"/v1/canvas/roster","canvasRoster"),canvasPrune:o(()=>"/v1/canvas/prune","canvasPrune"),humanReviews:o(()=>"/v1/human/reviews","humanReviews"),humanReview:o(e=>`/v1/human/reviews/${e}`,"humanReview"),humanReviewSubmit:o(e=>`/v1/human/reviews/${e}/submit`,"humanReviewSubmit"),humanReviewCancel:o(e=>`/v1/human/reviews/${e}/cancel`,"humanReviewCancel"),humanReviewDocument:o(e=>`/v1/human/reviews/${e}/document`,"humanReviewDocument"),humanReviewComments:o(e=>`/v1/human/reviews/${e}/comments`,"humanReviewComments"),humanReviewCommentEvents:o(e=>`/v1/human/reviews/${e}/comment-events`,"humanReviewCommentEvents"),humanReviewCommentRanges:o(e=>`/v1/human/reviews/${e}/comment-ranges`,"humanReviewCommentRanges"),humanComment:o(e=>`/v1/human/comments/${e}`,"humanComment"),humanCommentEdit:o(e=>`/v1/human/comments/${e}/edit`,"humanCommentEdit"),humanCommentResolve:o(e=>`/v1/human/comments/${e}/resolve`,"humanCommentResolve"),humanCommentReopen:o(e=>`/v1/human/comments/${e}/reopen`,"humanCommentReopen"),humanCommentDelete:o(e=>`/v1/human/comments/${e}/delete`,"humanCommentDelete"),humanCommentFork:o(e=>`/v1/human/comments/${e}/fork`,"humanCommentFork"),humanInbox:o(()=>"/v1/human/inbox","humanInbox"),humanInboxTicket:o(e=>`/v1/human/inbox/${e}`,"humanInboxTicket"),humanInboxRespond:o(e=>`/v1/human/inbox/${e}/respond`,"humanInboxRespond"),humanInboxProgress:o(e=>`/v1/human/inbox/${e}/progress`,"humanInboxProgress"),humanInboxResponse:o(e=>`/v1/human/inbox/${e}/response`,"humanInboxResponse"),humanInboxCancel:o(e=>`/v1/human/inbox/${e}/cancel`,"humanInboxCancel"),humanInboxFeedbackResolve:o((e,n)=>`/v1/human/inbox/${e}/feedback-comments/${n}/resolve`,"humanInboxFeedbackResolve"),humanRequests:o(()=>"/v1/human/requests","humanRequests"),humanRequest:o(e=>`/v1/human/requests/${e}`,"humanRequest"),humanRequestReplace:o(e=>`/v1/human/requests/${e}/replace`,"humanRequestReplace"),humanRequestRespond:o(e=>`/v1/human/requests/${e}/respond`,"humanRequestRespond"),humanRequestDismiss:o(e=>`/v1/human/requests/${e}/dismiss`,"humanRequestDismiss"),humanRequestCancel:o(e=>`/v1/human/requests/${e}/cancel`,"humanRequestCancel"),profiles:o(()=>"/v1/profiles","profiles"),profile:o(e=>`/v1/profiles/${e}`,"profile"),profilePause:o(e=>`/v1/profiles/${e}/pause`,"profilePause"),profileResume:o(e=>`/v1/profiles/${e}/resume`,"profileResume"),profileMetadata:o(e=>`/v1/profiles/${e}/metadata`,"profileMetadata"),modelAuths:o(()=>"/v1/model-auth","modelAuths"),modelAuth:o(e=>`/v1/model-auth/${e}`,"modelAuth"),filePeek:o(()=>"/v1/files/peek","filePeek"),memoryResolve:o(()=>"/v1/memory/resolve","memoryResolve")};export{a as API_VERSION,t as routes};
1
+ var s=Object.defineProperty;var o=(e,n)=>s(e,"name",{value:n,configurable:!0});const a="v1",$="/v1",t={healthz:o(()=>"/healthz","healthz"),status:o(()=>"/v1/status","status"),daemonRestart:o(()=>"/v1/daemon/restart","daemonRestart"),daemonMigrate:o(()=>"/v1/daemon/migrate","daemonMigrate"),daemonAdmit:o(()=>"/v1/daemon/admit","daemonAdmit"),nodes:o(()=>"/v1/nodes","nodes"),reviveAll:o(()=>"/v1/nodes/revive-all","reviveAll"),node:o(e=>`/v1/nodes/${e}`,"node"),nodeOutcome:o(e=>`/v1/nodes/${e}/outcome`,"nodeOutcome"),nodeOutcomeDelivery:o(e=>`/v1/nodes/${e}/outcome-delivery`,"nodeOutcomeDelivery"),nodeSnapshot:o(e=>`/v1/nodes/${e}/snapshot`,"nodeSnapshot"),nodeSubject:o(e=>`/v1/nodes/${e}/subject`,"nodeSubject"),nodeSession:o(e=>`/v1/nodes/${e}/session`,"nodeSession"),nodeChatInventory:o(e=>`/v1/nodes/${e}/chat-inventory`,"nodeChatInventory"),prospectiveChatInventory:o(()=>"/v1/prospective-chat-inventory","prospectiveChatInventory"),nodeTranscript:o(e=>`/v1/nodes/${e}/transcript`,"nodeTranscript"),nodeContext:o(e=>`/v1/nodes/${e}/context`,"nodeContext"),nodeArtifacts:o(e=>`/v1/nodes/${e}/artifacts`,"nodeArtifacts"),nodeReports:o(e=>`/v1/nodes/${e}/reports`,"nodeReports"),nodeResult:o(e=>`/v1/nodes/${e}/result`,"nodeResult"),nodeJobs:o(e=>`/v1/nodes/${e}/jobs`,"nodeJobs"),nodeJob:o((e,n)=>`/v1/nodes/${e}/jobs/${n}`,"nodeJob"),nodeMessages:o(e=>`/v1/nodes/${e}/messages`,"nodeMessages"),nodeInterrupt:o(e=>`/v1/nodes/${e}/interrupt`,"nodeInterrupt"),nodeFork:o(e=>`/v1/nodes/${e}/fork`,"nodeFork"),nodeRevive:o(e=>`/v1/nodes/${e}/revive`,"nodeRevive"),nodeRelaunchRoot:o(e=>`/v1/nodes/${e}/relaunch-root`,"nodeRelaunchRoot"),nodeBrokerSessionBound:o(e=>`/v1/nodes/${e}/broker/session-bound`,"nodeBrokerSessionBound"),nodeBrokerSettle:o(e=>`/v1/nodes/${e}/broker/settle`,"nodeBrokerSettle"),nodeBrokerParkComplete:o(e=>`/v1/nodes/${e}/broker/park-complete`,"nodeBrokerParkComplete"),nodeBrokerParkActivity:o(e=>`/v1/nodes/${e}/broker/park-activity`,"nodeBrokerParkActivity"),nodeMailClaim:o(e=>`/v1/nodes/${e}/mail/claim`,"nodeMailClaim"),nodeMailAcknowledge:o(e=>`/v1/nodes/${e}/mail/acknowledge`,"nodeMailAcknowledge"),nodeBrokerModel:o(e=>`/v1/nodes/${e}/broker/model`,"nodeBrokerModel"),nodeBrokerExtensionState:o(e=>`/v1/nodes/${e}/broker/extension-state`,"nodeBrokerExtensionState"),nodeBrokerGeneratedName:o(e=>`/v1/nodes/${e}/broker/generated-name`,"nodeBrokerGeneratedName"),nodeBrokerPersonaAck:o(e=>`/v1/nodes/${e}/broker/persona-ack`,"nodeBrokerPersonaAck"),brokerTurn:o(e=>`/v1/broker/${e}/turn`,"brokerTurn"),brokerProviderRetry:o(e=>`/v1/broker/${e}/provider-retry`,"brokerProviderRetry"),brokerFault:o(e=>`/v1/broker/${e}/fault`,"brokerFault"),brokerRecovery:o(e=>`/v1/broker/${e}/recovery`,"brokerRecovery"),nodeFault:o(e=>`/v1/nodes/${e}/fault`,"nodeFault"),nodeClose:o(e=>`/v1/nodes/${e}/close`,"nodeClose"),nodeRecycle:o(e=>`/v1/nodes/${e}/recycle`,"nodeRecycle"),nodeDemote:o(e=>`/v1/nodes/${e}/demote`,"nodeDemote"),nodePromote:o(e=>`/v1/nodes/${e}/promote`,"nodePromote"),nodeYield:o(e=>`/v1/nodes/${e}/yield`,"nodeYield"),nodeWait:o(e=>`/v1/nodes/${e}/wait`,"nodeWait"),nodeConfig:o(e=>`/v1/nodes/${e}/config`,"nodeConfig"),nodeWorktreeClose:o(e=>`/v1/nodes/${e}/worktree/close`,"nodeWorktreeClose"),nodeWorktreeAbandon:o(e=>`/v1/nodes/${e}/worktree/abandon`,"nodeWorktreeAbandon"),quarantinedWorktrees:o(()=>"/v1/worktrees/quarantined","quarantinedWorktrees"),nodeAttach:o(e=>`/v1/nodes/${e}/attach`,"nodeAttach"),focuses:o(()=>"/v1/focuses","focuses"),focusByNode:o(()=>"/v1/focuses/by-node","focusByNode"),focusByPane:o(()=>"/v1/focuses/by-pane","focusByPane"),focus:o(e=>`/v1/focuses/${e}`,"focus"),nodeSubscriptions:o(e=>`/v1/nodes/${e}/subscriptions`,"nodeSubscriptions"),nodeSubscription:o((e,n)=>`/v1/nodes/${e}/subscriptions/${n}`,"nodeSubscription"),crons:o(()=>"/v1/crons","crons"),cron:o(e=>`/v1/crons/${e}`,"cron"),cronPause:o(e=>`/v1/crons/${e}/pause`,"cronPause"),cronResume:o(e=>`/v1/crons/${e}/resume`,"cronResume"),cronRun:o(e=>`/v1/crons/${e}/run`,"cronRun"),cronsPoke:o(()=>"/v1/crons/poke","cronsPoke"),canvasAttention:o(()=>"/v1/canvas/attention","canvasAttention"),canvasAttentionCounts:o(()=>"/v1/canvas/attention/counts","canvasAttentionCounts"),canvasHistorySearch:o(()=>"/v1/canvas/history/search","canvasHistorySearch"),canvasHistoryGrep:o(()=>"/v1/canvas/history/grep","canvasHistoryGrep"),canvasHistoryRead:o(()=>"/v1/canvas/history/read","canvasHistoryRead"),canvasHistoryStats:o(()=>"/v1/canvas/history/stats","canvasHistoryStats"),canvasSnapshot:o(()=>"/v1/canvas/snapshot","canvasSnapshot"),canvasRoster:o(()=>"/v1/canvas/roster","canvasRoster"),canvasPrune:o(()=>"/v1/canvas/prune","canvasPrune"),humanReviews:o(()=>"/v1/human/reviews","humanReviews"),humanReview:o(e=>`/v1/human/reviews/${e}`,"humanReview"),humanReviewSubmit:o(e=>`/v1/human/reviews/${e}/submit`,"humanReviewSubmit"),humanReviewCancel:o(e=>`/v1/human/reviews/${e}/cancel`,"humanReviewCancel"),humanReviewDocument:o(e=>`/v1/human/reviews/${e}/document`,"humanReviewDocument"),humanReviewComments:o(e=>`/v1/human/reviews/${e}/comments`,"humanReviewComments"),humanReviewCommentEvents:o(e=>`/v1/human/reviews/${e}/comment-events`,"humanReviewCommentEvents"),humanReviewCommentRanges:o(e=>`/v1/human/reviews/${e}/comment-ranges`,"humanReviewCommentRanges"),humanComment:o(e=>`/v1/human/comments/${e}`,"humanComment"),humanCommentEdit:o(e=>`/v1/human/comments/${e}/edit`,"humanCommentEdit"),humanCommentResolve:o(e=>`/v1/human/comments/${e}/resolve`,"humanCommentResolve"),humanCommentReopen:o(e=>`/v1/human/comments/${e}/reopen`,"humanCommentReopen"),humanCommentDelete:o(e=>`/v1/human/comments/${e}/delete`,"humanCommentDelete"),humanCommentFork:o(e=>`/v1/human/comments/${e}/fork`,"humanCommentFork"),humanInbox:o(()=>"/v1/human/inbox","humanInbox"),humanInboxTicket:o(e=>`/v1/human/inbox/${e}`,"humanInboxTicket"),humanInboxRespond:o(e=>`/v1/human/inbox/${e}/respond`,"humanInboxRespond"),humanInboxProgress:o(e=>`/v1/human/inbox/${e}/progress`,"humanInboxProgress"),humanInboxResponse:o(e=>`/v1/human/inbox/${e}/response`,"humanInboxResponse"),humanInboxCancel:o(e=>`/v1/human/inbox/${e}/cancel`,"humanInboxCancel"),humanInboxFeedbackResolve:o((e,n)=>`/v1/human/inbox/${e}/feedback-comments/${n}/resolve`,"humanInboxFeedbackResolve"),humanRequests:o(()=>"/v1/human/requests","humanRequests"),humanRequest:o(e=>`/v1/human/requests/${e}`,"humanRequest"),humanRequestReplace:o(e=>`/v1/human/requests/${e}/replace`,"humanRequestReplace"),humanRequestRespond:o(e=>`/v1/human/requests/${e}/respond`,"humanRequestRespond"),humanRequestDismiss:o(e=>`/v1/human/requests/${e}/dismiss`,"humanRequestDismiss"),humanRequestCancel:o(e=>`/v1/human/requests/${e}/cancel`,"humanRequestCancel"),profiles:o(()=>"/v1/profiles","profiles"),profile:o(e=>`/v1/profiles/${e}`,"profile"),profilePause:o(e=>`/v1/profiles/${e}/pause`,"profilePause"),profileResume:o(e=>`/v1/profiles/${e}/resume`,"profileResume"),profileMetadata:o(e=>`/v1/profiles/${e}/metadata`,"profileMetadata"),modelAuths:o(()=>"/v1/model-auth","modelAuths"),modelAuthReadiness:o(()=>"/v1/model-auth/readiness","modelAuthReadiness"),modelAuth:o(e=>`/v1/model-auth/${e}`,"modelAuth"),filePeek:o(()=>"/v1/files/peek","filePeek"),memoryResolve:o(()=>"/v1/memory/resolve","memoryResolve")};export{a as API_VERSION,t as routes};
@@ -0,0 +1,86 @@
1
+ ---
2
+ kind: knowledge
3
+ description: Driving a crouter daemon from an application with @north-light/crouter-sdk
4
+ when-and-why-to-read: When you are writing or reviewing application code that runs a crtr agent — installing @north-light/crouter-sdk, constructing a client, creating a node and reading its result, or connecting a browser to a daemon — this reference should be read because `generate()` and `local()` were deleted, and phase-2 streaming and phase-3 memory methods do not exist yet.
5
+ short-form: The application-facing client for a crtrd daemon. Construction and transports, `client.nodes` create/wait/parse, the returned-not-thrown outcome union, the error classes, and which parts of the surface have not shipped.
6
+ surfaces:
7
+ - on: boot
8
+ at: name
9
+ ---
10
+
11
+ # `@north-light/crouter-sdk`
12
+
13
+ One client an application installs to drive a crouter daemon over its `/v1` API: create agent runs, wait for typed results, and reach the rest of the daemon. Resource methods issue `/v1` requests; `createAndWait`, `parse`, and `auth.status` compose requests. crtrd stays the sole owner of canvas state. The runtime model behind the objects — nodes, the canvas, lifecycle, reports — is `internal/nodes-and-canvas`.
14
+
15
+ ## Check what has shipped before you write against it
16
+
17
+ The surface ships in three cuts, and **phases 2 and 3 do not exist**. Streaming (`client.nodes.stream`, `client.nodes.events`, `GET /v1/nodes/{id}/events`) is phase 2. Memory (`client.memory` and every `/v1/memory` route but `resolve`) is phase 3. Writing code against a phase-2 or phase-3 method produces something that typechecks against the design document and fails at runtime.
18
+
19
+ Phase 1 is client construction and transports, `client.nodes` create / retrieve / list / outcome / waitForOutcome / createAndWait / parse / message / cancel / interrupt, `nodes.reports.list`, `profiles`, `system.status`, `files.peek`, the error hierarchy, and per-request options.
20
+
21
+ Confirm against the installed package's types rather than against any document, including this one.
22
+
23
+ ## The published README is stale
24
+
25
+ `generate()`, `local()`, and the `Environment` interface were **deleted**, not deprecated — a hard cut with no shim. Code written from the npm README or from training data will call `generate({ prompt, schema, env: local() })` and will not compile. The replacement is `new Crouter()` plus `client.nodes.parse({ prompt, output_schema })`.
26
+
27
+ ## A run, end to end
28
+
29
+ An agent run needs a daemon. `npm i -g @north-light/crouter` installs `crtr` and `crtrd`; `new Crouter()` starts the daemon itself on a cold socket, so nothing has to be running first.
30
+
31
+ ```ts
32
+ import Crouter from '@north-light/crouter-sdk';
33
+ import { z } from 'zod';
34
+
35
+ const client = new Crouter();
36
+
37
+ const run = await client.nodes.parse({
38
+ prompt: 'Read package.json here and report its name and version.',
39
+ cwd: '/path/to/repo',
40
+ output_schema: z.object({ name: z.string(), version: z.string() }),
41
+ root: true,
42
+ root_lifecycle: 'terminal',
43
+ });
44
+ ```
45
+
46
+ `root: true` means the run has no parent node and reports to nobody, which is what an application's run is. `root_lifecycle: 'terminal'` finishes and reaps; `'resident'` keeps the node for a person to open. Wire fields are `snake_case` and the SDK does not camelize them.
47
+
48
+ ## The agent's own outcome is returned, never thrown
49
+
50
+ A run that refused the schema, hit its deadline, or crashed comes back as a value. Wrapping the call in `try`/`catch` and treating the catch as the failure path silently drops every agent-side outcome, because none of them throw.
51
+
52
+ ```ts
53
+ if (run.kind === 'result') use(run.output_parsed);
54
+ else if (run.reason === 'declined') handleRefusal(run.declined?.reason, run.declined?.retryable);
55
+ else handleFailure(run.reason, run.detail);
56
+ ```
57
+
58
+ A decline is `kind: 'failure'` with `reason: 'declined'` — narrow on `reason`, not on `kind`. `output_parsed` is non-null exactly when `kind === 'result'`. A Zod schema supplies its inferred output type; a Standard Schema supplies `~standard.types.output`; a JSON-Schema literal or any other `{ toJSONSchema() }` object makes it `unknown`. The SDK does not re-validate the result because the daemon already enforced the schema when the agent submitted.
59
+
60
+ Exceptions are for the layer underneath: `BadRequestError`, `AuthenticationError`, `PermissionDeniedError`, `NotFoundError`, `ConflictError`, `UnprocessableEntityError`, `RateLimitError`, `InternalServerError`, `APIConnectionError`, `APIConnectionTimeoutError`, `APIUserAbortError`, all extending `APIError` with `status`, `code`, `message`, `details`, `headers`. Branch on `code` — it is the stable machine-readable identity (`node_id_exists`, `daemon_unavailable`, `node_dormant`).
61
+
62
+ ## A retried create spawns a second agent unless you pass `node_id`
63
+
64
+ `maxRetries` (default 2) covers connection errors and 429/5xx on `GET`, `HEAD`, and `DELETE`. **`POST` and `PATCH` are never retried**, because a mutation whose response was interrupted may already have been applied. A caller that wants a retry-safe create passes `node_id`: the duplicate then fails with `409 node_id_exists`, which the caller catches and turns into a `waitForOutcome` on the run that already exists.
65
+
66
+ ## `waitForOutcome` has no time bound
67
+
68
+ It polls until the node settles, however long that takes. Bound it from one side or the other: a `deadline` on `create` makes the daemon cancel the run, a `signal` makes your client stop waiting. Aborting the signal stops the client, not the agent — `client.nodes.cancel(id)` stops the agent, `client.nodes.interrupt(id)` stops only the current turn.
69
+
70
+ Before streaming ships, progress comes from `client.nodes.reports.list(id)`.
71
+
72
+ ## Reaching a daemon that is not on this machine
73
+
74
+ `new Crouter()` uses the owner's unix socket, which authenticates by filesystem permission. A browser has no unix sockets and a remote process has no access to that one, so both use the TCP listener with a bearer token: `new Crouter({ baseURL, token })`.
75
+
76
+ The listener is off until someone runs `crtr sys connect` on the daemon's machine, which stores the address and token and hands the daemon over to a successor that boots with it on, then prints both to paste into the application. The token is the owner credential — whoever holds it reaches the whole surface. Cross-origin calls work only when a token is set; an unauthenticated listener stays same-origin so that no page the user happens to visit can drive their daemon. A page served over HTTPS calling `http://localhost:<port>` also needs Chrome's Local Network Access permission, which the browser prompts for once per site and which requires no response header.
77
+
78
+ `baseURL` and `socketPath` are mutually exclusive — passing both throws `TypeError` at construction. Options fall back to `CRTR_BASE_URL`, `CRTR_SOCKET`, `CRTR_HOME`, and `CRTRD_TOKEN`, read once at construction.
79
+
80
+ ## Reaching a route the SDK does not wrap
81
+
82
+ `client.request(method, path, body?, options?)` carries the same authentication, retry policy, and error mapping as every generated method. Some routes are deliberately outside the typed surface — tmux focuses, a node's own inbox mail claim, local attach, and the broker's control plane — because an external caller invoking them either cannot use the result or becomes a second writer to state the broker and daemon coordinate between themselves.
83
+
84
+ ## The full reference
85
+
86
+ Page-per-topic detail — the complete create-parameter table, the streaming event table, the memory scope object, the resource map with its phase markers, and the migration table — lives in the crouter repository under `docs/sdk/`, published at <https://github.com/vallum-security/crouter/tree/main/docs/sdk>. The remaining `TODO(verify)` markers belong only to the phase-2 streaming and phase-3 memory pages.
@@ -668,7 +668,7 @@ async function runManagedProvider(state, model, context, options, stream) {
668
668
  }
669
669
  if (!reauthenticated) {
670
670
  const attemptAt = Date.now();
671
- await recordSubscriptionMutation(`recording ${getProviderLabel(providerId)} authentication failure`, () => markSubscriptionAuthenticationFailed(providerId, credential.label, INVALID_REFRESH_TOKEN_BACKOFF_MS, attemptAt));
671
+ await recordSubscriptionMutation(`recording ${getProviderLabel(providerId)} authentication failure`, () => markSubscriptionAuthenticationFailed(providerId, credential.label, credential, INVALID_REFRESH_TOKEN_BACKOFF_MS, attemptAt));
672
672
  authFailures.add(`${providerId}/${credential.label}`);
673
673
  continue;
674
674
  }
@@ -864,7 +864,7 @@ async function runManagedProvider(
864
864
  }
865
865
  if (!reauthenticated) {
866
866
  const attemptAt = Date.now();
867
- await recordSubscriptionMutation(`recording ${getProviderLabel(providerId)} authentication failure`, () => markSubscriptionAuthenticationFailed(providerId, credential.label, INVALID_REFRESH_TOKEN_BACKOFF_MS, attemptAt));
867
+ await recordSubscriptionMutation(`recording ${getProviderLabel(providerId)} authentication failure`, () => markSubscriptionAuthenticationFailed(providerId, credential.label, credential, INVALID_REFRESH_TOKEN_BACKOFF_MS, attemptAt));
868
868
  authFailures.add(`${providerId}/${credential.label}`);
869
869
  continue;
870
870
  }
package/dist/cli.js CHANGED
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
- var s=Object.defineProperty;var e=(o,t)=>s(o,"name",{value:t,configurable:!0});import"./suppress-experimental-warnings.js";import{openSync as a}from"node:fs";import{enableCompileCache as c}from"node:module";import{fileURLToPath as p}from"node:url";import{isDaemonControlInvocation as m,resolveDaemonControlRoot as n,resolveRoot as f}from"./build-root.js";import{maybeBootRoot as l}from"./core/runtime/front-door.js";import{maybeAutoUpdate as d}from"./core/auto-update.js";import{ensureProjectScope as v}from"./core/bootstrap.js";import{provisionExports as g,pruneLegacyExports as u}from"./core/host-exports/export.js";import{revalidateBundlePlugins as b}from"./core/command-plugins/revalidate.js";import{runCli as h}from"./core/command.js";import{dispatchWithStaleRecovery as y}from"./core/manifest-recovery.js";import{installStdoutErrorPolicy as w}from"./core/stdout-errors.js";import{handle as E}from"./core/io.js";import{CrtrError as C}from"./core/errors.js";import{ApiError as x}from"./api/index.js";import{mark as r}from"./core/timing.js";a(p(import.meta.url),"r"),w(),r("cli.entry",{cmd:process.argv[2]??"(bare)"});try{c()}catch{}r("cli.compile_cache");async function _(){if(u(process.argv),await l(process.argv),r("cli.front_door_passed"),m(process.argv)){await h(await n(),process.argv);return}await b(process.argv),r("cli.plugins_revalidated");const o=await f(process.argv[2]);r("cli.root_resolved"),g(o,process.argv),v(process.argv),d(process.argv),r("cli.dispatch_start"),await y(o,process.argv),r("cli.dispatch_done")}e(_,"main"),_().catch(o=>{(o instanceof Error&&o.name==="AbortError"||o instanceof Error&&/operation was aborted/i.test(o.message))&&(process.stderr.write(`crtr: Interrupted
3
- `),process.exit(130)),(o instanceof x||o instanceof C)&&(E(o),process.exit(typeof process.exitCode=="number"?process.exitCode:1));const i=o instanceof Error?o.message:String(o);process.stderr.write(`crtr: ${i}
2
+ var l=Object.defineProperty;var i=(r,t)=>l(r,"name",{value:t,configurable:!0});import"./suppress-experimental-warnings.js";import{mkdirSync as d,openSync as u,renameSync as y,rmSync as g,statSync as a,writeFileSync as w}from"node:fs";import{homedir as v}from"node:os";import{dirname as p,join as c}from"node:path";import{enableCompileCache as h}from"node:module";import{fileURLToPath as m}from"node:url";import{isDaemonControlInvocation as R,resolveDaemonControlRoot as S,resolveRoot as E}from"./build-root.js";import{maybeBootRoot as _}from"./core/runtime/front-door.js";import{maybeAutoUpdate as b}from"./core/auto-update.js";import{ensureProjectScope as A}from"./core/bootstrap.js";import{provisionExports as C,pruneLegacyExports as $}from"./core/host-exports/export.js";import{revalidateBundlePlugins as k}from"./core/command-plugins/revalidate.js";import{runCli as I}from"./core/command.js";import{dispatchWithStaleRecovery as x}from"./core/manifest-recovery.js";import{installStdoutErrorPolicy as N}from"./core/stdout-errors.js";import{handle as O}from"./core/io.js";import{CrtrError as T}from"./core/errors.js";import{ApiError as F}from"./api/index.js";import{mark as o}from"./core/timing.js";u(m(import.meta.url),"r"),N(),o("cli.entry",{cmd:process.argv[2]??"(bare)"});try{h()}catch{}o("cli.compile_cache");const P=c(p(m(import.meta.url)),".."),n=c(v(),".crouter","first-run.json");function s(r){return(r instanceof Error?r.message:String(r)).split(/\r?\n/,1)[0]??""}i(s,"errorMessage");function D(r){const t=p(n);d(t,{recursive:!0});const e=`${n}.${process.pid}.${Date.now()}.tmp`;try{w(e,JSON.stringify({packageVersion:r,completedAt:new Date().toISOString()})+`
3
+ `,"utf8"),y(e,n)}finally{g(e,{force:!0})}}i(D,"writeFirstRunMarker");async function U(){if(process.env.CRTR_NO_AUTO_INIT==="1")return;try{if(a(n,{throwIfNoEntry:!1})!==void 0)return}catch(t){process.stderr.write(`crtr: first-run reconcile marker check failed: ${s(t)}
4
+ `);return}try{if(a(c(P,".git"),{throwIfNoEntry:!1})!==void 0)return}catch(t){process.stderr.write(`crtr: first-run reconcile source check failed: ${s(t)}
5
+ `);return}let r;try{const{reconcileFirstRun:t}=await import("./commands/sys/setup-core.js");r=await t((e,f)=>{process.stderr.write(`crtr: first-run reconcile ${e} failed: ${s(f)}
6
+ `)})}catch(t){process.stderr.write(`crtr: first-run reconcile failed: ${s(t)}
7
+ `)}finally{if(r!==void 0)try{D(r)}catch(t){process.stderr.write(`crtr: first-run reconcile marker failed: ${s(t)}
8
+ `)}}}i(U,"maybeReconcileFirstRun");async function j(){if($(process.argv),await U(),await _(process.argv),o("cli.front_door_passed"),R(process.argv)){await I(await S(),process.argv);return}await k(process.argv),o("cli.plugins_revalidated");const r=await E(process.argv[2]);o("cli.root_resolved"),C(r,process.argv),A(process.argv),b(process.argv),o("cli.dispatch_start"),await x(r,process.argv),o("cli.dispatch_done")}i(j,"main"),j().catch(r=>{(r instanceof Error&&r.name==="AbortError"||r instanceof Error&&/operation was aborted/i.test(r.message))&&(process.stderr.write(`crtr: Interrupted
9
+ `),process.exit(130)),(r instanceof F||r instanceof T)&&(O(r),process.exit(typeof process.exitCode=="number"?process.exitCode:1));const e=r instanceof Error?r.message:String(r);process.stderr.write(`crtr: ${e}
4
10
  `),process.exit(1)});
@@ -317,7 +317,11 @@ export declare class ChatView {
317
317
  /** Swap the single active activity indicator (working/compaction/retry). */
318
318
  private setActivity;
319
319
  private showStatus;
320
- private showError;
320
+ /** Append an error to the transcript. pi's own error events land here, and so
321
+ * do the broker's `error` frames: their text is the engine's own and is
322
+ * routinely multi-line (pi's login help is three lines), which the one-row
323
+ * status notice cannot show. */
324
+ showError(message: string): void;
321
325
  private crtrOutputTheme;
322
326
  /** Extract the tool-call content items from an assistant message. */
323
327
  private assistantToolCalls;