orizu 0.5.23 → 0.5.25

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 (142) hide show
  1. package/README.md +38 -10
  2. package/dist/child-output-tee.d.ts +48 -0
  3. package/dist/child-output-tee.d.ts.map +1 -0
  4. package/dist/child-output-tee.js +512 -0
  5. package/dist/cli-auth-browser-page.d.ts +2 -0
  6. package/dist/cli-auth-browser-page.d.ts.map +1 -0
  7. package/dist/cli-auth-browser-page.js +107 -0
  8. package/dist/credentials.d.ts +15 -0
  9. package/dist/credentials.d.ts.map +1 -1
  10. package/dist/credentials.js +279 -59
  11. package/dist/github-setup.js +8 -8
  12. package/dist/headless-login.d.ts +17 -0
  13. package/dist/headless-login.d.ts.map +1 -0
  14. package/dist/headless-login.js +105 -0
  15. package/dist/help.d.ts.map +1 -1
  16. package/dist/help.js +33 -22
  17. package/dist/hosted-boot.js +3 -3
  18. package/dist/hosted-bootstrap.js +2 -2
  19. package/dist/hosted-commands.d.ts +2 -0
  20. package/dist/hosted-commands.d.ts.map +1 -1
  21. package/dist/hosted-commands.js +11 -0
  22. package/dist/hosted-environment.d.ts +3 -0
  23. package/dist/hosted-environment.d.ts.map +1 -0
  24. package/dist/hosted-environment.js +2 -0
  25. package/dist/hosted-gepa-cli.d.ts +3 -0
  26. package/dist/hosted-gepa-cli.d.ts.map +1 -1
  27. package/dist/hosted-gepa-cli.js +92 -5
  28. package/dist/hosted-loop.d.ts +1 -1
  29. package/dist/hosted-loop.d.ts.map +1 -1
  30. package/dist/hosted-loop.js +2 -1
  31. package/dist/hosted-optimization-cli.d.ts +7 -0
  32. package/dist/hosted-optimization-cli.d.ts.map +1 -0
  33. package/dist/hosted-optimization-cli.js +218 -0
  34. package/dist/hosted-optimization-refusals.d.ts +88 -0
  35. package/dist/hosted-optimization-refusals.d.ts.map +1 -0
  36. package/dist/hosted-optimization-refusals.js +86 -0
  37. package/dist/hosted-optimization.d.ts +6 -0
  38. package/dist/hosted-optimization.d.ts.map +1 -0
  39. package/dist/hosted-optimization.js +436 -0
  40. package/dist/hosted-provider-settings.d.ts +3 -0
  41. package/dist/hosted-provider-settings.d.ts.map +1 -1
  42. package/dist/hosted-provider-settings.js +9 -0
  43. package/dist/hosted-session-cli.d.ts.map +1 -1
  44. package/dist/hosted-session-cli.js +6 -1
  45. package/dist/hosted-skill-staging.d.ts +9 -9
  46. package/dist/hosted-skill-staging.d.ts.map +1 -1
  47. package/dist/hosted-skill-staging.js +13 -10
  48. package/dist/http.d.ts +20 -0
  49. package/dist/http.d.ts.map +1 -1
  50. package/dist/http.js +239 -19
  51. package/dist/index.d.ts +4 -4
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +421 -511
  54. package/dist/keyboard-select.d.ts +7 -0
  55. package/dist/keyboard-select.d.ts.map +1 -0
  56. package/dist/keyboard-select.js +72 -0
  57. package/dist/logout-diagnostic.d.ts +4 -0
  58. package/dist/logout-diagnostic.d.ts.map +1 -0
  59. package/dist/logout-diagnostic.js +26 -0
  60. package/dist/report-comments-cli.d.ts +1 -0
  61. package/dist/report-comments-cli.d.ts.map +1 -1
  62. package/dist/report-comments-cli.js +70 -4
  63. package/dist/runner-dir-verify.d.ts +5 -0
  64. package/dist/runner-dir-verify.d.ts.map +1 -1
  65. package/dist/runner-dir-verify.js +41 -9
  66. package/dist/runner-version-materialization.d.ts +6 -0
  67. package/dist/runner-version-materialization.d.ts.map +1 -0
  68. package/dist/runner-version-materialization.js +64 -0
  69. package/dist/setup-account.d.ts +11 -0
  70. package/dist/setup-account.d.ts.map +1 -0
  71. package/dist/setup-account.js +106 -0
  72. package/dist/setup-onboarding.d.ts +42 -0
  73. package/dist/setup-onboarding.d.ts.map +1 -0
  74. package/dist/setup-onboarding.js +143 -0
  75. package/dist/setup-summary.d.ts +12 -0
  76. package/dist/setup-summary.d.ts.map +1 -0
  77. package/dist/setup-summary.js +56 -0
  78. package/dist/skill-installer.d.ts +3 -3
  79. package/dist/skill-installer.d.ts.map +1 -1
  80. package/dist/skill-installer.js +39 -3
  81. package/dist/skilled-proposer-launch.d.ts +5 -1
  82. package/dist/skilled-proposer-launch.d.ts.map +1 -1
  83. package/dist/skilled-proposer-launch.js +27 -5
  84. package/dist/skilled-proposer-wire.d.ts +2 -0
  85. package/dist/skilled-proposer-wire.d.ts.map +1 -0
  86. package/dist/skilled-proposer-wire.js +25 -0
  87. package/dist/workspace.d.ts +14 -0
  88. package/dist/workspace.d.ts.map +1 -1
  89. package/dist/workspace.js +63 -4
  90. package/package.json +1 -1
  91. package/scripts/vendor-orizu-skill.mjs +3 -3
  92. package/src/child-output-tee.ts +587 -0
  93. package/src/cli-auth-browser-page.ts +108 -0
  94. package/src/credentials.ts +326 -57
  95. package/src/github-setup.ts +8 -8
  96. package/src/headless-login.ts +178 -0
  97. package/src/help.ts +35 -22
  98. package/src/hosted-boot.ts +3 -3
  99. package/src/hosted-bootstrap.ts +2 -2
  100. package/src/hosted-commands.ts +16 -0
  101. package/src/hosted-environment.ts +2 -0
  102. package/src/hosted-gepa-cli.ts +112 -4
  103. package/src/hosted-loop.ts +2 -1
  104. package/src/hosted-optimization-cli.ts +215 -0
  105. package/src/hosted-optimization-refusals.ts +101 -0
  106. package/src/hosted-optimization.ts +483 -0
  107. package/src/hosted-provider-settings.ts +12 -0
  108. package/src/hosted-session-cli.ts +6 -1
  109. package/src/hosted-skill-staging.ts +17 -14
  110. package/src/http.ts +325 -20
  111. package/src/index.ts +395 -482
  112. package/src/keyboard-select.ts +97 -0
  113. package/src/logout-diagnostic.ts +39 -0
  114. package/src/report-comments-cli.ts +83 -5
  115. package/src/runner-dir-verify.ts +74 -9
  116. package/src/runner-version-materialization.ts +56 -0
  117. package/src/setup-account.ts +129 -0
  118. package/src/setup-onboarding.ts +187 -0
  119. package/src/setup-summary.ts +77 -0
  120. package/src/skill-installer.ts +39 -3
  121. package/src/skilled-proposer-launch.ts +36 -5
  122. package/src/skilled-proposer-wire.ts +22 -0
  123. package/src/workspace.ts +65 -4
  124. package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +56 -21
  125. package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +76 -13
  126. package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +16 -2
  127. package/vendor/skills/{orizu-cli → orizu}/SKILL.md +7 -3
  128. package/vendor/skills/orizu/agents/openai.yaml +4 -0
  129. package/vendor/skills/{orizu-cli → orizu}/references/assess-and-plan.md +1 -1
  130. package/vendor/skills/{orizu-cli → orizu}/references/building-apps.md +1 -1
  131. package/vendor/skills/{orizu-cli → orizu}/references/cli-reference.md +4 -4
  132. package/vendor/skills/{orizu-cli → orizu}/references/migrate-existing-gepa-setup.md +1 -1
  133. package/vendor/skills/{orizu-cli → orizu}/references/optimization-with-gepa.md +17 -3
  134. package/vendor/skills/orizu-cli/agents/openai.yaml +0 -4
  135. /package/vendor/skills/{orizu-cli → orizu}/references/building-judges.md +0 -0
  136. /package/vendor/skills/{orizu-cli → orizu}/references/dataset-design.md +0 -0
  137. /package/vendor/skills/{orizu-cli → orizu}/references/eval-strategy.md +0 -0
  138. /package/vendor/skills/{orizu-cli → orizu}/references/instructions-after-prompts.md +0 -0
  139. /package/vendor/skills/{orizu-cli → orizu}/references/optimization-reports.md +0 -0
  140. /package/vendor/skills/{orizu-cli → orizu}/references/primer.md +0 -0
  141. /package/vendor/skills/{orizu-cli → orizu}/references/prompt-control-plane.md +0 -0
  142. /package/vendor/skills/{orizu-cli → orizu}/scripts/test-app.mjs +0 -0
package/README.md CHANGED
@@ -86,12 +86,35 @@ Install the CLI globally with your package manager:
86
86
  | pnpm | `pnpm add -g orizu` |
87
87
  | Bun | `bun add -g orizu` |
88
88
 
89
- Then run the guided setup — it signs you in, initializes the local workspace
90
- contract, installs global coding-agent skill symlinks, and shows the next
91
- command for repo-specific Orizu adoption:
89
+ Then run the guided setup — it signs you in, asks you to choose or create a
90
+ team and project, initializes the local workspace contract, and optionally
91
+ hands off to Claude, Codex, Pi, OpenCode, and Cursor when their commands are
92
+ installed. Available commands appear in that order, with the first available
93
+ command selected by default. Other is always last. Enter launches the highlighted
94
+ agent; Esc skips agent launch and completes setup. Selecting Other prints the
95
+ complete prompt without launching anything. Explicit
96
+ `--launch claude|codex|pi|opencode|cursor` still requires an interactive terminal
97
+ and asks for confirmation unless `--yes` is supplied; non-interactive setup never
98
+ launches. Cursor uses the `agent` executable, not the Cursor editor `cursor` command.
99
+ The official [Cursor CLI overview](https://cursor.com/docs/cli/overview) and
100
+ [parameter reference](https://cursor.com/docs/cli/reference/parameters), retrieved
101
+ 2026-08-30, document direct `agent "prompt"` and `agent -p "prompt"` initial prompts;
102
+ setup uses the direct positional form without a shell.
103
+
104
+ If you accept skill installation, the picker starts with
105
+ `Universal (.agents — Codex and others) — ~/.agents/skills/orizu` and shows each
106
+ detected native target with its `~/.../orizu` destination on the same line before
107
+ any write. All visible choices start selected, and any choice—including Universal—
108
+ can be deselected. In non-interactive setup, `--agent` opts into skill installation.
109
+ Every accepted `--agent` always includes Universal, plus that agent's native
110
+ destination where needed. Duplicate destinations are removed. With no `--agent` flags, setup writes no skills.
111
+
112
+ For example:
92
113
 
93
114
  ```bash
94
115
  orizu setup
116
+ # Scripts use explicit choices after `orizu login --headless`:
117
+ orizu setup --team acme --project support-agent --agent codex --non-interactive
95
118
  ```
96
119
 
97
120
  To repair or customize skill installs later, run the companion skill installer
@@ -137,9 +160,8 @@ Agents are first-class users of this CLI: **every command supports `--json`**
137
160
  (as a prefix, `orizu --json teams list`, or a trailing flag) and emits a single
138
161
  machine-readable JSON document instead of formatted text. Discover the full
139
162
  command surface, including global options, with `orizu capabilities --json`.
140
- Hosted agents may read and sync instruction sets and prepare complete manifests. A local coding agent using the user's token runs instruction create, push, shape,
141
- archive, and restore itself after plan ratification. It gathers only read-only pointer-resolution and binding evidence; a human curator executes every production/default pointer command on every surface.
142
- In a hosted session, a human curator using the local CLI must run both categories.
163
+ Hosted agents may read and sync instruction sets, create profile seeds, run shape add/remove with team-scoped curator authority, and prepare complete manifests. A local coding agent using the user's token runs instruction create, push, shape add/remove, archive, and restore itself after plan ratification. It gathers only read-only pointer-resolution and binding evidence; a human curator executes every production/default pointer command on every surface.
164
+ In a hosted session, a human curator using the local CLI must run create/push, archive/restore, and every pointer command; shape add/remove stays agent-runnable because it creates unpromoted heads only.
143
165
 
144
166
  ## Authentication
145
167
 
@@ -152,6 +174,12 @@ orizu login
152
174
  This will open a browser tab for you to login with. You must either have an account on the [Orizu platform](https://orizu.ai) or it will help you create one.
153
175
  Approving the login creates a personal access token for the CLI and stores it in your local Orizu credentials file. You can revoke CLI tokens from the Personal Tokens page in Orizu.
154
176
 
177
+ In an SSH or display-less Linux session, the CLI automatically prints an approval URL and polls for completion instead of opening a local browser. Force this mode when needed with:
178
+
179
+ ```bash
180
+ orizu login --headless
181
+ ```
182
+
155
183
  **Check the signed-in user**
156
184
 
157
185
  ```bash
@@ -239,12 +267,12 @@ orizu logout
239
267
  <td colspan="2"><strong>Report comments</strong></td>
240
268
  </tr>
241
269
  <tr>
242
- <td>List report comments</td>
243
- <td><code>orizu comments list --prompt &lt;promptIdOrName&gt; --project ops-eval/support-qa</code><br><code>orizu comments list --run &lt;runId&gt;</code><br><code>orizu comments list --task &lt;taskId&gt;</code></td>
270
+ <td>List report or README comments</td>
271
+ <td><code>orizu comments list --prompt &lt;promptIdOrName&gt; --project ops-eval/support-qa</code><br><code>orizu comments list --run &lt;runId&gt;</code><br><code>orizu comments list --task &lt;taskId&gt;</code><br><code>orizu comments list --dataset &lt;datasetIdOrName&gt; --project ops-eval/support-qa</code></td>
244
272
  </tr>
245
273
  <tr>
246
274
  <td>Add or update comments</td>
247
- <td><code>orizu comments add --run &lt;runId&gt; --body @comment.md --anchor "Summary" --lines 4:6</code><br><code>orizu comments reply &lt;commentId&gt; --body "Fixed"</code><br><code>orizu comments resolve &lt;commentId&gt;</code></td>
275
+ <td><code>orizu comments add --run &lt;runId&gt; --body @comment.md --anchor "Summary" --lines 4:6</code><br><code>orizu comments add --dataset &lt;datasetIdOrName&gt; --project ops-eval/support-qa --body "Clarify this README" --anchor "Summary" --lines 4:6</code><br><code>orizu comments reply &lt;commentId&gt; --body "Fixed"</code><br><code>orizu comments resolve &lt;commentId&gt;</code></td>
248
276
  </tr>
249
277
  <tr>
250
278
  <td colspan="2"><strong>Diff comments</strong></td>
@@ -412,4 +440,4 @@ Use `--json` on supported task and app commands when automation needs structured
412
440
  ## More Documentation
413
441
 
414
442
  - [docs/cli.md](docs/cli.md): complete CLI guide and command reference
415
- - [skills/orizu-cli/references/cli-reference.md](skills/orizu-cli/references/cli-reference.md): compact command matrix and end-to-end flows
443
+ - [skills/orizu/references/cli-reference.md](skills/orizu/references/cli-reference.md): compact command matrix and end-to-end flows
@@ -0,0 +1,48 @@
1
+ import { Readable, Writable } from 'node:stream';
2
+ declare class FileDescriptorSink extends Writable {
3
+ private readonly descriptor;
4
+ private readonly activeWriteCancellations;
5
+ private isLiveOutputAvailable;
6
+ private relay;
7
+ constructor(descriptor: number);
8
+ _write(chunk: Buffer, _encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
9
+ prepare(): Promise<void>;
10
+ cancelPendingWrites(): void;
11
+ private relayProcess;
12
+ }
13
+ export declare const childOutputSinks: {
14
+ stdout: FileDescriptorSink;
15
+ stderr: FileDescriptorSink;
16
+ };
17
+ export interface PipedChild {
18
+ stdout: Readable;
19
+ stderr: Readable;
20
+ exited: Promise<number | null>;
21
+ signalCode: NodeJS.Signals | null;
22
+ kill(signal?: number | NodeJS.Signals): void;
23
+ }
24
+ export declare function spawnPipedChild(command: string, args: string[], environment: NodeJS.ProcessEnv): Promise<PipedChild>;
25
+ export declare function writeChildStderr(text: string): Promise<void>;
26
+ export interface ChildOutputForwarding {
27
+ completed: Promise<void>;
28
+ failure: Promise<Error>;
29
+ failureReason(): Error | undefined;
30
+ expectSourceClose(): void;
31
+ stopLiveOutput(): void;
32
+ }
33
+ export interface ChildOutputCapture {
34
+ stdout(chunk: string): void;
35
+ stderr(chunk: string): void;
36
+ }
37
+ export declare function completePipedChild(child: PipedChild, forwarding: readonly ChildOutputForwarding[]): Promise<number | null>;
38
+ export declare function runPipedChild(command: string, args: string[], environment: NodeJS.ProcessEnv, onSpawn: () => Promise<void>, capture: ChildOutputCapture, observeChild?: (child: PipedChild) => void): Promise<{
39
+ status: number | null;
40
+ error?: Error;
41
+ }>;
42
+ /**
43
+ * Forward a child stream live while retaining a bounded caller-owned copy.
44
+ * Transient sink slowness pushes back; a stall degrades safely to capture-only.
45
+ */
46
+ export declare function teeChildOutput(source: Readable, destination: Writable, capture: (chunk: string) => void): ChildOutputForwarding;
47
+ export {};
48
+ //# sourceMappingURL=child-output-tee.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-output-tee.d.ts","sourceRoot":"","sources":["../src/child-output-tee.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAsHhD,cAAM,kBAAmB,SAAQ,QAAQ;IAK3B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAJvC,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAwB;IACjE,OAAO,CAAC,qBAAqB,CAAO;IACpC,OAAO,CAAC,KAAK,CAA6B;gBAEb,UAAU,EAAE,MAAM;IAItC,MAAM,CACb,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,KAAK,IAAI,GACvC,IAAI;IAgCD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,mBAAmB,IAAI,IAAI;IAS3B,OAAO,CAAC,YAAY;CAiCrB;AAED,eAAO,MAAM,gBAAgB;;;CAG5B,CAAA;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC9B,UAAU,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;IACjC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;CAC7C;AAqDD,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,WAAW,EAAE,MAAM,CAAC,UAAU,GAC7B,OAAO,CAAC,UAAU,CAAC,CAiDrB;AAoCD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWlE;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;IACvB,aAAa,IAAI,KAAK,GAAG,SAAS,CAAA;IAClC,iBAAiB,IAAI,IAAI,CAAA;IACzB,cAAc,IAAI,IAAI,CAAA;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CAC5B;AAuCD,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,SAAS,qBAAqB,EAAE,GAC3C,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6CxB;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,WAAW,EAAE,MAAM,CAAC,UAAU,EAC9B,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC5B,OAAO,EAAE,kBAAkB,EAC3B,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GACzC,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC,CAqBnD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,QAAQ,EAChB,WAAW,EAAE,QAAQ,EACrB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAC/B,qBAAqB,CA2DvB"}
@@ -0,0 +1,512 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { closeSync, constants, mkdtempSync, openSync, read as readFileDescriptor, rmSync, } from 'node:fs';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { Readable, Writable } from 'node:stream';
6
+ const CHILD_OUTPUT_PREFETCH_MAX_BYTES = 64 * 1024;
7
+ const CHILD_OUTPUT_READ_BUFFER_BYTES = 16 * 1024;
8
+ const EMPTY_FIFO_INITIAL_RETRY_MS = 4;
9
+ const EMPTY_FIFO_MAX_RETRY_MS = 64;
10
+ const FORWARDING_FAILURE_TERMINATION_TIMEOUT_MS = 250, LIVE_OUTPUT_STALL_TIMEOUT_MS = 1_500;
11
+ const LIVE_OUTPUT_RELAY_SCRIPT = [
12
+ "const { writeSync } = require('node:fs')",
13
+ 'process.stdout._handle?.setBlocking(true)',
14
+ 'let buffered = Buffer.alloc(0)',
15
+ 'process.stdin.on(\'data\', chunk => {',
16
+ ' buffered = Buffer.concat([buffered, chunk])',
17
+ ' while (buffered.length >= 4) {',
18
+ ' const length = buffered.readUInt32BE(0)',
19
+ ' if (buffered.length < length + 4) return',
20
+ ' const payload = buffered.subarray(4, length + 4)',
21
+ ' buffered = buffered.subarray(length + 4)',
22
+ ' for (let offset = 0; offset < payload.length; )',
23
+ ' offset += writeSync(1, payload, offset, payload.length - offset)',
24
+ " writeSync(3, '1')",
25
+ ' }',
26
+ '})',
27
+ "writeSync(3, 'R')",
28
+ ].join('\n');
29
+ class FileDescriptorSource extends Readable {
30
+ descriptor;
31
+ readBuffer = Buffer.allocUnsafe(CHILD_OUTPUT_READ_BUFFER_BYTES);
32
+ emptyRetryMilliseconds = EMPTY_FIFO_INITIAL_RETRY_MS;
33
+ isReadPending = false;
34
+ retryTimer;
35
+ pendingDestroy;
36
+ constructor(descriptor) {
37
+ super({ highWaterMark: CHILD_OUTPUT_PREFETCH_MAX_BYTES });
38
+ this.descriptor = descriptor;
39
+ }
40
+ _read() {
41
+ if (this.isReadPending || this.retryTimer || this.descriptor === undefined)
42
+ return;
43
+ this.isReadPending = true;
44
+ readFileDescriptor(this.descriptor, this.readBuffer, 0, this.readBuffer.byteLength, null, (error, bytesRead) => {
45
+ this.isReadPending = false;
46
+ if (this.pendingDestroy) {
47
+ this.finishDestroy();
48
+ return;
49
+ }
50
+ if (error) {
51
+ if (error.code === 'EINTR') {
52
+ this._read();
53
+ return;
54
+ }
55
+ if (['EAGAIN', 'EWOULDBLOCK'].includes(error.code ?? '')) {
56
+ const delay = this.emptyRetryMilliseconds;
57
+ this.emptyRetryMilliseconds = Math.min(EMPTY_FIFO_MAX_RETRY_MS, this.emptyRetryMilliseconds * 2);
58
+ this.retryTimer = setTimeout(() => {
59
+ this.retryTimer = undefined;
60
+ this._read();
61
+ }, delay);
62
+ return;
63
+ }
64
+ this.destroy(error);
65
+ return;
66
+ }
67
+ if (bytesRead === 0) {
68
+ this.push(null);
69
+ return;
70
+ }
71
+ this.emptyRetryMilliseconds = EMPTY_FIFO_INITIAL_RETRY_MS;
72
+ this.push(Buffer.from(this.readBuffer.subarray(0, bytesRead)));
73
+ });
74
+ }
75
+ _destroy(error, callback) {
76
+ if (this.retryTimer)
77
+ clearTimeout(this.retryTimer);
78
+ this.retryTimer = undefined;
79
+ this.pendingDestroy = { error, callback };
80
+ if (!this.isReadPending)
81
+ this.finishDestroy();
82
+ }
83
+ finishDestroy() {
84
+ const pendingDestroy = this.pendingDestroy;
85
+ if (!pendingDestroy)
86
+ return;
87
+ this.pendingDestroy = undefined;
88
+ const descriptor = this.descriptor;
89
+ this.descriptor = undefined;
90
+ try {
91
+ if (descriptor !== undefined)
92
+ closeSync(descriptor);
93
+ pendingDestroy.callback(pendingDestroy.error);
94
+ }
95
+ catch (error) {
96
+ pendingDestroy.callback(error instanceof Error ? error : new Error(String(error)));
97
+ }
98
+ }
99
+ }
100
+ class FileDescriptorSink extends Writable {
101
+ descriptor;
102
+ activeWriteCancellations = new Set();
103
+ isLiveOutputAvailable = true;
104
+ relay;
105
+ constructor(descriptor) {
106
+ super({ highWaterMark: 16 * 1024 });
107
+ this.descriptor = descriptor;
108
+ }
109
+ _write(chunk, _encoding, callback) {
110
+ if (!this.isLiveOutputAvailable) {
111
+ callback();
112
+ return;
113
+ }
114
+ let relay;
115
+ let hasSettled = false;
116
+ const settle = (error) => {
117
+ if (hasSettled)
118
+ return;
119
+ hasSettled = true;
120
+ if (error)
121
+ this.isLiveOutputAvailable = false;
122
+ this.activeWriteCancellations.delete(cancel);
123
+ relay?.acknowledgements.off('data', acknowledge);
124
+ relay?.child.off('exit', relayExited);
125
+ callback(error);
126
+ };
127
+ const acknowledge = () => settle();
128
+ const relayExited = () => settle(new Error('live output relay exited before write acknowledgement'));
129
+ const cancel = () => settle();
130
+ this.activeWriteCancellations.add(cancel);
131
+ try {
132
+ relay = this.relayProcess();
133
+ }
134
+ catch (error) {
135
+ settle(errorFromUnknown(error));
136
+ return;
137
+ }
138
+ void relay.ready.then(() => {
139
+ if (hasSettled)
140
+ return;
141
+ relay.acknowledgements.once('data', acknowledge);
142
+ relay.child.once('exit', relayExited);
143
+ const frame = Buffer.allocUnsafe(chunk.byteLength + 4);
144
+ frame.writeUInt32BE(chunk.byteLength);
145
+ chunk.copy(frame, 4);
146
+ relay.input.write(frame, error => { if (error)
147
+ settle(error); });
148
+ }).catch(error => settle(errorFromUnknown(error)));
149
+ }
150
+ async prepare() {
151
+ if (!this.isLiveOutputAvailable)
152
+ return;
153
+ await this.relayProcess().ready;
154
+ }
155
+ cancelPendingWrites() {
156
+ this.isLiveOutputAvailable = false;
157
+ const relay = this.relay;
158
+ this.relay = undefined;
159
+ relay?.input.destroy();
160
+ relay?.child.kill('SIGKILL');
161
+ for (const cancel of [...this.activeWriteCancellations])
162
+ cancel();
163
+ }
164
+ relayProcess() {
165
+ if (this.relay)
166
+ return this.relay;
167
+ const child = spawn('node', ['-e', LIVE_OUTPUT_RELAY_SCRIPT], {
168
+ stdio: ['pipe', this.descriptor, 'ignore', 'pipe'],
169
+ });
170
+ const input = child.stdin;
171
+ const acknowledgements = child.stdio[3];
172
+ if (!input || !acknowledgements)
173
+ throw new Error('live output relay has no control pipes');
174
+ child.on('error', () => { });
175
+ input.on('error', () => { });
176
+ acknowledgements.on('error', () => { });
177
+ const ready = new Promise((resolve, reject) => {
178
+ acknowledgements.once('data', chunk => {
179
+ if (chunk.includes('R'.charCodeAt(0)))
180
+ resolve();
181
+ else
182
+ reject(new Error('live output relay sent an invalid readiness acknowledgement'));
183
+ });
184
+ child.once('exit', () => reject(new Error('live output relay exited before readiness')));
185
+ child.once('error', reject);
186
+ });
187
+ void ready.catch(() => { });
188
+ child.unref();
189
+ void ready.then(() => {
190
+ input.unref?.();
191
+ acknowledgements.unref?.();
192
+ }, () => { });
193
+ const relay = { child, input, acknowledgements, ready };
194
+ child.once('exit', () => {
195
+ if (this.relay?.child !== child)
196
+ return;
197
+ this.relay = undefined;
198
+ });
199
+ this.relay = relay;
200
+ return relay;
201
+ }
202
+ }
203
+ export const childOutputSinks = {
204
+ stdout: new FileDescriptorSink(1),
205
+ stderr: new FileDescriptorSink(2),
206
+ };
207
+ const POST_LEADER_CAPTURE_DRAIN_TIMEOUT_MS = 250;
208
+ function closeDescriptor(descriptor) {
209
+ if (descriptor === undefined)
210
+ return;
211
+ try {
212
+ closeSync(descriptor);
213
+ }
214
+ catch {
215
+ // Best-effort cleanup after another operation has already failed.
216
+ }
217
+ }
218
+ function createOutputFifo(name) {
219
+ const root = mkdtempSync(join(tmpdir(), 'orizu-child-output-'));
220
+ const fifoPath = join(root, name);
221
+ let anchorDescriptor;
222
+ let readDescriptor;
223
+ let writeDescriptor;
224
+ try {
225
+ const creation = spawnSync('mkfifo', [fifoPath], { stdio: 'ignore' });
226
+ if (creation.error)
227
+ throw creation.error;
228
+ if (creation.status !== 0) {
229
+ throw new Error(`mkfifo exited with status ${creation.status ?? 'unknown'}`);
230
+ }
231
+ // The temporary read/write anchor prevents either open from waiting for
232
+ // its peer. The child inherits the blocking write end; the parent polls a
233
+ // nonblocking read end so descendant-held descriptors remain cancellable.
234
+ anchorDescriptor = openSync(fifoPath, constants.O_RDWR | constants.O_NONBLOCK);
235
+ readDescriptor = openSync(fifoPath, constants.O_RDONLY | constants.O_NONBLOCK);
236
+ writeDescriptor = openSync(fifoPath, constants.O_WRONLY);
237
+ closeDescriptor(anchorDescriptor);
238
+ anchorDescriptor = undefined;
239
+ const source = new FileDescriptorSource(readDescriptor);
240
+ readDescriptor = undefined;
241
+ rmSync(root, { force: true, recursive: true });
242
+ return { source, writeDescriptor };
243
+ }
244
+ catch (error) {
245
+ closeDescriptor(writeDescriptor);
246
+ closeDescriptor(readDescriptor);
247
+ closeDescriptor(anchorDescriptor);
248
+ rmSync(root, { force: true, recursive: true });
249
+ throw error;
250
+ }
251
+ }
252
+ export async function spawnPipedChild(command, args, environment) {
253
+ const stdout = createOutputFifo('stdout');
254
+ let stderr;
255
+ try {
256
+ stderr = createOutputFifo('stderr');
257
+ }
258
+ catch (error) {
259
+ stdout.source.destroy();
260
+ closeDescriptor(stdout.writeDescriptor);
261
+ throw error;
262
+ }
263
+ let child;
264
+ try {
265
+ // Begin the bounded parent-side drain before an immediate child crash can
266
+ // strand bytes in the child's userland stdout/stderr buffers.
267
+ stdout.source.read(0);
268
+ stderr.source.read(0);
269
+ child = spawn(command, args, {
270
+ env: environment,
271
+ stdio: ['inherit', stdout.writeDescriptor, stderr.writeDescriptor],
272
+ });
273
+ }
274
+ catch (error) {
275
+ stdout.source.destroy();
276
+ stderr.source.destroy();
277
+ throw error;
278
+ }
279
+ finally {
280
+ closeDescriptor(stdout.writeDescriptor);
281
+ closeDescriptor(stderr.writeDescriptor);
282
+ }
283
+ // `close` also waits for descendant-held stdio descriptors. Observe the
284
+ // leader's exit so completePipedChild can enforce its bounded EOF grace.
285
+ const exited = new Promise(resolve => child.once('exit', resolve));
286
+ try {
287
+ await new Promise((resolve, reject) => {
288
+ child.once('spawn', resolve);
289
+ child.once('error', reject);
290
+ });
291
+ }
292
+ catch (error) {
293
+ stdout.source.destroy();
294
+ stderr.source.destroy();
295
+ throw error;
296
+ }
297
+ return {
298
+ stdout: stdout.source,
299
+ stderr: stderr.source,
300
+ exited,
301
+ get signalCode() { return child.signalCode; },
302
+ kill: signal => { child.kill(signal); },
303
+ };
304
+ }
305
+ const liveSinkStates = new WeakMap();
306
+ function liveSinkState(destination) {
307
+ const existing = liveSinkStates.get(destination);
308
+ if (existing)
309
+ return existing;
310
+ const state = { hasFailed: false };
311
+ destination.on('error', () => { state.hasFailed = true; });
312
+ liveSinkStates.set(destination, state);
313
+ return state;
314
+ }
315
+ async function writeLiveOutput(destination, state, text) {
316
+ if (state.hasFailed)
317
+ return;
318
+ await new Promise(resolve => {
319
+ try {
320
+ destination.write(text, error => {
321
+ if (error)
322
+ state.hasFailed = true;
323
+ resolve();
324
+ });
325
+ }
326
+ catch {
327
+ state.hasFailed = true;
328
+ resolve();
329
+ }
330
+ });
331
+ }
332
+ export async function writeChildStderr(text) {
333
+ const sink = childOutputSinks.stderr;
334
+ let timer;
335
+ const didForward = await Promise.race([
336
+ writeLiveOutput(sink, liveSinkState(sink), `${text}\n`).then(() => true),
337
+ new Promise(resolve => {
338
+ timer = setTimeout(() => resolve(false), LIVE_OUTPUT_STALL_TIMEOUT_MS);
339
+ }),
340
+ ]);
341
+ if (timer)
342
+ clearTimeout(timer);
343
+ if (!didForward)
344
+ sink.cancelPendingWrites();
345
+ }
346
+ function errorFromUnknown(cause) {
347
+ return cause instanceof Error ? cause : new Error(String(cause));
348
+ }
349
+ async function waitForChildExit(child, timeoutMilliseconds) {
350
+ let timer;
351
+ const didExit = await Promise.race([
352
+ child.exited.then(() => true),
353
+ new Promise(resolve => {
354
+ timer = setTimeout(() => resolve(false), timeoutMilliseconds);
355
+ }),
356
+ ]);
357
+ if (timer)
358
+ clearTimeout(timer);
359
+ return didExit;
360
+ }
361
+ async function terminateAfterForwardingFailure(child, forwarding, captureDrain, error) {
362
+ child.kill('SIGTERM');
363
+ for (const stream of forwarding)
364
+ stream.stopLiveOutput();
365
+ child.stdout.destroy();
366
+ child.stderr.destroy();
367
+ await captureDrain;
368
+ if (!await waitForChildExit(child, FORWARDING_FAILURE_TERMINATION_TIMEOUT_MS)) {
369
+ child.kill('SIGKILL');
370
+ await waitForChildExit(child, FORWARDING_FAILURE_TERMINATION_TIMEOUT_MS);
371
+ }
372
+ throw error;
373
+ }
374
+ export async function completePipedChild(child, forwarding) {
375
+ const captureDrain = Promise.allSettled(forwarding.map(stream => stream.completed));
376
+ const firstFailure = Promise.race(forwarding.map(stream => stream.failure));
377
+ const firstOutcome = await Promise.race([
378
+ child.exited.then(status => ({ kind: 'exit', status })),
379
+ firstFailure.then(error => ({ kind: 'failure', error })),
380
+ ]);
381
+ if (firstOutcome.kind === 'failure') {
382
+ return terminateAfterForwardingFailure(child, forwarding, captureDrain, firstOutcome.error);
383
+ }
384
+ const status = firstOutcome.status;
385
+ const waitForCaptureDrain = async () => {
386
+ let timer;
387
+ const didDrain = await Promise.race([
388
+ captureDrain.then(() => true),
389
+ new Promise(resolve => {
390
+ timer = setTimeout(() => resolve(false), POST_LEADER_CAPTURE_DRAIN_TIMEOUT_MS);
391
+ }),
392
+ ]);
393
+ if (timer)
394
+ clearTimeout(timer);
395
+ return didDrain;
396
+ };
397
+ const throwIfCaptureFailed = async () => {
398
+ await captureDrain;
399
+ const failure = forwarding.map(stream => stream.failureReason()).find(Boolean);
400
+ if (failure)
401
+ throw failure;
402
+ };
403
+ // Preserve ordinary live forwarding when EOF is already on its way. If a
404
+ // blocked sink or descendant-held descriptor prevents that bounded drain,
405
+ // switch to capture-only before enforcing the same bounded EOF grace.
406
+ if (await waitForCaptureDrain()) {
407
+ await throwIfCaptureFailed();
408
+ return status;
409
+ }
410
+ for (const stream of forwarding)
411
+ stream.stopLiveOutput();
412
+ if (await waitForCaptureDrain()) {
413
+ await throwIfCaptureFailed();
414
+ return status;
415
+ }
416
+ for (const stream of forwarding)
417
+ stream.expectSourceClose();
418
+ child.stdout.destroy();
419
+ child.stderr.destroy();
420
+ await throwIfCaptureFailed();
421
+ return status;
422
+ }
423
+ export async function runPipedChild(command, args, environment, onSpawn, capture, observeChild) {
424
+ await Promise.all([childOutputSinks.stdout.prepare(), childOutputSinks.stderr.prepare()]);
425
+ const child = await spawnPipedChild(command, args, environment);
426
+ observeChild?.(child);
427
+ const forwarding = [
428
+ teeChildOutput(child.stdout, childOutputSinks.stdout, capture.stdout),
429
+ teeChildOutput(child.stderr, childOutputSinks.stderr, capture.stderr),
430
+ ];
431
+ const completion = completePipedChild(child, forwarding);
432
+ const firstForwardingFailure = Promise.race(forwarding.map(stream => stream.failure)).then(error => { throw error; });
433
+ try {
434
+ await Promise.race([onSpawn(), firstForwardingFailure]);
435
+ const exitCode = await completion;
436
+ return { status: child.signalCode ? null : exitCode };
437
+ }
438
+ catch (cause) {
439
+ child.kill('SIGTERM');
440
+ await completion.catch(() => undefined);
441
+ throw cause;
442
+ }
443
+ }
444
+ /**
445
+ * Forward a child stream live while retaining a bounded caller-owned copy.
446
+ * Transient sink slowness pushes back; a stall degrades safely to capture-only.
447
+ */
448
+ export function teeChildOutput(source, destination, capture) {
449
+ let isLiveOutputEnabled = true;
450
+ let isSourceCloseExpected = false;
451
+ let unexpectedFailure;
452
+ let remainingLiveOutputWaitMilliseconds = LIVE_OUTPUT_STALL_TIMEOUT_MS;
453
+ let liveOutputWaitFinishedAt = performance.now();
454
+ let resolveLiveOutputStopped = () => { };
455
+ const liveOutputStopped = new Promise(resolve => { resolveLiveOutputStopped = resolve; });
456
+ const stopLiveOutput = () => {
457
+ if (!isLiveOutputEnabled)
458
+ return;
459
+ isLiveOutputEnabled = false;
460
+ if (destination instanceof FileDescriptorSink)
461
+ destination.cancelPendingWrites();
462
+ resolveLiveOutputStopped();
463
+ };
464
+ const sinkState = liveSinkState(destination);
465
+ const forward = async (text) => {
466
+ capture(text);
467
+ if (!isLiveOutputEnabled)
468
+ return;
469
+ const waitStartedAt = performance.now();
470
+ remainingLiveOutputWaitMilliseconds = Math.min(LIVE_OUTPUT_STALL_TIMEOUT_MS, remainingLiveOutputWaitMilliseconds + waitStartedAt - liveOutputWaitFinishedAt);
471
+ let stallTimer;
472
+ const wasForwarded = await Promise.race([
473
+ writeLiveOutput(destination, sinkState, text).then(() => true),
474
+ liveOutputStopped.then(() => false),
475
+ new Promise(resolve => {
476
+ stallTimer = setTimeout(() => resolve(false), remainingLiveOutputWaitMilliseconds);
477
+ }),
478
+ ]);
479
+ if (stallTimer)
480
+ clearTimeout(stallTimer);
481
+ liveOutputWaitFinishedAt = performance.now();
482
+ remainingLiveOutputWaitMilliseconds -= liveOutputWaitFinishedAt - waitStartedAt;
483
+ if (!wasForwarded || remainingLiveOutputWaitMilliseconds <= 0)
484
+ stopLiveOutput();
485
+ };
486
+ const completed = (async () => {
487
+ const decoder = new TextDecoder();
488
+ for await (const chunk of source) {
489
+ const text = decoder.decode(chunk, { stream: true });
490
+ if (text)
491
+ await forward(text);
492
+ }
493
+ const remainder = decoder.decode();
494
+ if (remainder)
495
+ await forward(remainder);
496
+ })();
497
+ let resolveFailure = () => { };
498
+ const failure = new Promise(resolve => { resolveFailure = resolve; });
499
+ void completed.catch(cause => {
500
+ if (isSourceCloseExpected)
501
+ return;
502
+ unexpectedFailure = errorFromUnknown(cause);
503
+ resolveFailure(unexpectedFailure);
504
+ });
505
+ return {
506
+ completed,
507
+ failure,
508
+ failureReason: () => unexpectedFailure,
509
+ expectSourceClose: () => { isSourceCloseExpected = true; },
510
+ stopLiveOutput,
511
+ };
512
+ }
@@ -0,0 +1,2 @@
1
+ export declare function renderCliAuthBrowserPage(status: 'success' | 'error'): string;
2
+ //# sourceMappingURL=cli-auth-browser-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-auth-browser-page.d.ts","sourceRoot":"","sources":["../src/cli-auth-browser-page.ts"],"names":[],"mappings":"AAAA,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,CA2G5E"}