@goah/cli 0.13.0 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/dist/LICENSE +201 -0
  2. package/dist/THIRD-PARTY-NOTICES.md +2319 -0
  3. package/dist/anthropic-messages-WBFOHES5.js +7214 -0
  4. package/dist/azure-openai-responses-NZFH3PO6.js +253 -0
  5. package/dist/chunk-25XSBFX6.js +639 -0
  6. package/dist/chunk-2QBQZGXF.js +110 -0
  7. package/dist/chunk-2ZP2NBW3.js +345 -0
  8. package/dist/chunk-4KA63VNZ.js +2 -0
  9. package/dist/chunk-4M6TXTT5.js +86 -0
  10. package/dist/chunk-4US7ZHT3.js +20 -0
  11. package/dist/chunk-6M3OOCKO.js +11809 -0
  12. package/dist/chunk-7AXAFDPG.js +17 -0
  13. package/dist/chunk-7E7V6OIU.js +920 -0
  14. package/dist/chunk-BXA47DTF.js +548 -0
  15. package/dist/chunk-D2DVAH37.js +25 -0
  16. package/dist/chunk-FSBUS2BI.js +26 -0
  17. package/dist/chunk-GKDRAHLI.js +4787 -0
  18. package/dist/chunk-H462EFJP.js +89 -0
  19. package/dist/chunk-IIJQ3DW4.js +35 -0
  20. package/dist/chunk-JQD5EWJY.js +34 -0
  21. package/dist/chunk-K26BVR6O.js +61 -0
  22. package/dist/chunk-KAO7WIXG.js +31 -0
  23. package/dist/chunk-KJ4HJCTK.js +1178 -0
  24. package/dist/chunk-LBKI23CC.js +9810 -0
  25. package/dist/chunk-LKBKZEHT.js +41 -0
  26. package/dist/chunk-MD7TLTXZ.js +18 -0
  27. package/dist/chunk-MPXK7QBL.js +868 -0
  28. package/dist/chunk-NRLGWZJC.js +11 -0
  29. package/dist/chunk-QBD6EJ2Z.js +1746 -0
  30. package/dist/chunk-R6I3VKME.js +132 -0
  31. package/dist/chunk-TJON22J4.js +87 -0
  32. package/dist/chunk-ULY36ME4.js +33097 -0
  33. package/dist/chunk-XALZLRYK.js +9221 -0
  34. package/dist/chunk-XPQKM3L7.js +57 -0
  35. package/dist/chunk-YH6YJ2IB.js +1773 -0
  36. package/dist/cli.d.ts +0 -2
  37. package/dist/cli.js +13716 -580
  38. package/dist/console/assets/{index-Rtc_B7cs.js → index-B1vGMXKr.js} +11 -11
  39. package/dist/console/assets/{index-Rtc_B7cs.js.map → index-B1vGMXKr.js.map} +1 -1
  40. package/dist/console/index.html +1 -1
  41. package/dist/dist-65MDQA4Q.js +1126 -0
  42. package/dist/dist-KNR2XL6J.js +39 -0
  43. package/dist/execution.d.ts +557 -2
  44. package/dist/execution.js +37 -2
  45. package/dist/faux-runner-worker.js +128 -0
  46. package/dist/google-generative-ai-YMHXZSFE.js +422 -0
  47. package/dist/google-vertex-X6DB3ZQ6.js +471 -0
  48. package/dist/index.d.ts +853 -25
  49. package/dist/index.js +84 -385
  50. package/dist/kernel.d.ts +39 -2
  51. package/dist/kernel.js +21 -2
  52. package/dist/mistral-conversations-TY3MMD6G.js +718 -0
  53. package/dist/multipart-parser-F6M4NJZU.js +373 -0
  54. package/dist/openai-codex-responses-BRDUMLN5.js +1374 -0
  55. package/dist/openai-completions-CX634XN3.js +1139 -0
  56. package/dist/openai-responses-HMCXJIHT.js +300 -0
  57. package/dist/openrouter-images-KTWET2H2.js +148 -0
  58. package/dist/pi-messages-XL5OEC4K.js +307 -0
  59. package/dist/pi-worker.js +671 -0
  60. package/dist/runner-pi.d.ts +342 -2
  61. package/dist/runner-pi.js +46 -2
  62. package/dist/runner-registry-GD4BYCQC.js +22 -0
  63. package/dist/sqlite.d.ts +446 -2
  64. package/dist/sqlite.js +15 -2
  65. package/dist/src-F2KBSV4T.js +1398 -0
  66. package/dist/supervisor.d.ts +625 -2
  67. package/dist/supervisor.js +39 -2
  68. package/dist/testkit.d.ts +564 -2
  69. package/dist/testkit.js +127 -2
  70. package/dist/transcript.d.ts +84 -2
  71. package/dist/transcript.js +25 -2
  72. package/dist/verification-worker.js +72 -0
  73. package/package.json +2 -26
  74. package/dist/cli.d.ts.map +0 -1
  75. package/dist/cli.js.map +0 -1
  76. package/dist/control.d.ts +0 -106
  77. package/dist/control.d.ts.map +0 -1
  78. package/dist/control.js +0 -242
  79. package/dist/control.js.map +0 -1
  80. package/dist/execution.d.ts.map +0 -1
  81. package/dist/execution.js.map +0 -1
  82. package/dist/index.d.ts.map +0 -1
  83. package/dist/index.js.map +0 -1
  84. package/dist/kernel.d.ts.map +0 -1
  85. package/dist/kernel.js.map +0 -1
  86. package/dist/live-config.d.ts +0 -10
  87. package/dist/live-config.d.ts.map +0 -1
  88. package/dist/live-config.js +0 -44
  89. package/dist/live-config.js.map +0 -1
  90. package/dist/prompt-queue.d.ts +0 -13
  91. package/dist/prompt-queue.d.ts.map +0 -1
  92. package/dist/prompt-queue.js +0 -35
  93. package/dist/prompt-queue.js.map +0 -1
  94. package/dist/runner-pi.d.ts.map +0 -1
  95. package/dist/runner-pi.js.map +0 -1
  96. package/dist/runner-registry.d.ts +0 -8
  97. package/dist/runner-registry.d.ts.map +0 -1
  98. package/dist/runner-registry.js +0 -12
  99. package/dist/runner-registry.js.map +0 -1
  100. package/dist/searchable-select.d.ts +0 -24
  101. package/dist/searchable-select.d.ts.map +0 -1
  102. package/dist/searchable-select.js +0 -61
  103. package/dist/searchable-select.js.map +0 -1
  104. package/dist/secret-input.d.ts +0 -8
  105. package/dist/secret-input.d.ts.map +0 -1
  106. package/dist/secret-input.js +0 -14
  107. package/dist/secret-input.js.map +0 -1
  108. package/dist/setup-wizard.d.ts +0 -22
  109. package/dist/setup-wizard.d.ts.map +0 -1
  110. package/dist/setup-wizard.js +0 -154
  111. package/dist/setup-wizard.js.map +0 -1
  112. package/dist/sqlite.d.ts.map +0 -1
  113. package/dist/sqlite.js.map +0 -1
  114. package/dist/supervisor.d.ts.map +0 -1
  115. package/dist/supervisor.js.map +0 -1
  116. package/dist/testkit.d.ts.map +0 -1
  117. package/dist/testkit.js.map +0 -1
  118. package/dist/thread-inspect.d.ts +0 -47
  119. package/dist/thread-inspect.d.ts.map +0 -1
  120. package/dist/thread-inspect.js +0 -78
  121. package/dist/thread-inspect.js.map +0 -1
  122. package/dist/transcript.d.ts.map +0 -1
  123. package/dist/transcript.js.map +0 -1
  124. package/dist/tui-theme.d.ts +0 -17
  125. package/dist/tui-theme.d.ts.map +0 -1
  126. package/dist/tui-theme.js +0 -19
  127. package/dist/tui-theme.js.map +0 -1
  128. package/dist/tui.d.ts +0 -24
  129. package/dist/tui.d.ts.map +0 -1
  130. package/dist/tui.js +0 -784
  131. package/dist/tui.js.map +0 -1
  132. package/dist/update.d.ts +0 -16
  133. package/dist/update.d.ts.map +0 -1
  134. package/dist/update.js +0 -67
  135. package/dist/update.js.map +0 -1
  136. package/dist/web-console.d.ts +0 -33
  137. package/dist/web-console.d.ts.map +0 -1
  138. package/dist/web-console.js +0 -242
  139. package/dist/web-console.js.map +0 -1
  140. package/dist/welcome.d.ts +0 -30
  141. package/dist/welcome.d.ts.map +0 -1
  142. package/dist/welcome.js +0 -69
  143. package/dist/welcome.js.map +0 -1
  144. package/node_modules/goah-ledger-contract/dist/execution.d.ts +0 -544
  145. package/node_modules/goah-ledger-contract/dist/execution.d.ts.map +0 -1
  146. package/node_modules/goah-ledger-contract/dist/execution.js +0 -52
  147. package/node_modules/goah-ledger-contract/dist/execution.js.map +0 -1
  148. package/node_modules/goah-ledger-contract/dist/index.d.ts +0 -4
  149. package/node_modules/goah-ledger-contract/dist/index.d.ts.map +0 -1
  150. package/node_modules/goah-ledger-contract/dist/index.js +0 -4
  151. package/node_modules/goah-ledger-contract/dist/index.js.map +0 -1
  152. package/node_modules/goah-ledger-contract/dist/kernel.d.ts +0 -37
  153. package/node_modules/goah-ledger-contract/dist/kernel.d.ts.map +0 -1
  154. package/node_modules/goah-ledger-contract/dist/kernel.js +0 -8
  155. package/node_modules/goah-ledger-contract/dist/kernel.js.map +0 -1
  156. package/node_modules/goah-ledger-contract/dist/transcript.d.ts +0 -63
  157. package/node_modules/goah-ledger-contract/dist/transcript.d.ts.map +0 -1
  158. package/node_modules/goah-ledger-contract/dist/transcript.js +0 -115
  159. package/node_modules/goah-ledger-contract/dist/transcript.js.map +0 -1
  160. package/node_modules/goah-ledger-contract/package.json +0 -28
  161. package/node_modules/goah-ledger-sqlite/dist/index.d.ts +0 -86
  162. package/node_modules/goah-ledger-sqlite/dist/index.d.ts.map +0 -1
  163. package/node_modules/goah-ledger-sqlite/dist/index.js +0 -1254
  164. package/node_modules/goah-ledger-sqlite/dist/index.js.map +0 -1
  165. package/node_modules/goah-ledger-sqlite/package.json +0 -26
  166. package/node_modules/goah-runner-pi/dist/configurator.d.ts +0 -20
  167. package/node_modules/goah-runner-pi/dist/configurator.d.ts.map +0 -1
  168. package/node_modules/goah-runner-pi/dist/configurator.js +0 -423
  169. package/node_modules/goah-runner-pi/dist/configurator.js.map +0 -1
  170. package/node_modules/goah-runner-pi/dist/credential-store.d.ts +0 -14
  171. package/node_modules/goah-runner-pi/dist/credential-store.d.ts.map +0 -1
  172. package/node_modules/goah-runner-pi/dist/credential-store.js +0 -163
  173. package/node_modules/goah-runner-pi/dist/credential-store.js.map +0 -1
  174. package/node_modules/goah-runner-pi/dist/env-spec.d.ts +0 -17
  175. package/node_modules/goah-runner-pi/dist/env-spec.d.ts.map +0 -1
  176. package/node_modules/goah-runner-pi/dist/env-spec.js +0 -95
  177. package/node_modules/goah-runner-pi/dist/env-spec.js.map +0 -1
  178. package/node_modules/goah-runner-pi/dist/index.d.ts +0 -76
  179. package/node_modules/goah-runner-pi/dist/index.d.ts.map +0 -1
  180. package/node_modules/goah-runner-pi/dist/index.js +0 -326
  181. package/node_modules/goah-runner-pi/dist/index.js.map +0 -1
  182. package/node_modules/goah-runner-pi/dist/model-provider.d.ts +0 -30
  183. package/node_modules/goah-runner-pi/dist/model-provider.d.ts.map +0 -1
  184. package/node_modules/goah-runner-pi/dist/model-provider.js +0 -96
  185. package/node_modules/goah-runner-pi/dist/model-provider.js.map +0 -1
  186. package/node_modules/goah-runner-pi/dist/pi-worker.d.ts +0 -31
  187. package/node_modules/goah-runner-pi/dist/pi-worker.d.ts.map +0 -1
  188. package/node_modules/goah-runner-pi/dist/pi-worker.js +0 -579
  189. package/node_modules/goah-runner-pi/dist/pi-worker.js.map +0 -1
  190. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.d.ts +0 -2
  191. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.d.ts.map +0 -1
  192. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.js +0 -3
  193. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.js.map +0 -1
  194. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.d.ts +0 -2
  195. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.d.ts.map +0 -1
  196. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.js +0 -5
  197. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.js.map +0 -1
  198. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.d.ts +0 -2
  199. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.d.ts.map +0 -1
  200. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.js +0 -5
  201. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.js.map +0 -1
  202. package/node_modules/goah-runner-pi/dist/test-helpers.d.ts +0 -4
  203. package/node_modules/goah-runner-pi/dist/test-helpers.d.ts.map +0 -1
  204. package/node_modules/goah-runner-pi/dist/test-helpers.js +0 -13
  205. package/node_modules/goah-runner-pi/dist/test-helpers.js.map +0 -1
  206. package/node_modules/goah-runner-pi/dist/verification-worker.d.ts +0 -2
  207. package/node_modules/goah-runner-pi/dist/verification-worker.d.ts.map +0 -1
  208. package/node_modules/goah-runner-pi/dist/verification-worker.js +0 -46
  209. package/node_modules/goah-runner-pi/dist/verification-worker.js.map +0 -1
  210. package/node_modules/goah-runner-pi/package.json +0 -28
  211. package/node_modules/goah-supervisor/dist/context-view.d.ts +0 -30
  212. package/node_modules/goah-supervisor/dist/context-view.d.ts.map +0 -1
  213. package/node_modules/goah-supervisor/dist/context-view.js +0 -58
  214. package/node_modules/goah-supervisor/dist/context-view.js.map +0 -1
  215. package/node_modules/goah-supervisor/dist/index.d.ts +0 -96
  216. package/node_modules/goah-supervisor/dist/index.d.ts.map +0 -1
  217. package/node_modules/goah-supervisor/dist/index.js +0 -1075
  218. package/node_modules/goah-supervisor/dist/index.js.map +0 -1
  219. package/node_modules/goah-supervisor/dist/roles.d.ts +0 -4
  220. package/node_modules/goah-supervisor/dist/roles.d.ts.map +0 -1
  221. package/node_modules/goah-supervisor/dist/roles.js +0 -29
  222. package/node_modules/goah-supervisor/dist/roles.js.map +0 -1
  223. package/node_modules/goah-supervisor/dist/verification.d.ts +0 -61
  224. package/node_modules/goah-supervisor/dist/verification.d.ts.map +0 -1
  225. package/node_modules/goah-supervisor/dist/verification.js +0 -135
  226. package/node_modules/goah-supervisor/dist/verification.js.map +0 -1
  227. package/node_modules/goah-supervisor/package.json +0 -26
  228. package/node_modules/goah-testkit/dist/faux-runner-worker.d.ts +0 -2
  229. package/node_modules/goah-testkit/dist/faux-runner-worker.d.ts.map +0 -1
  230. package/node_modules/goah-testkit/dist/faux-runner-worker.js +0 -82
  231. package/node_modules/goah-testkit/dist/faux-runner-worker.js.map +0 -1
  232. package/node_modules/goah-testkit/dist/index.d.ts +0 -49
  233. package/node_modules/goah-testkit/dist/index.d.ts.map +0 -1
  234. package/node_modules/goah-testkit/dist/index.js +0 -96
  235. package/node_modules/goah-testkit/dist/index.js.map +0 -1
  236. package/node_modules/goah-testkit/package.json +0 -29
@@ -0,0 +1,920 @@
1
+ import { createRequire as __goahCreateRequire } from "node:module";
2
+ const require = __goahCreateRequire(import.meta.url);
3
+ import {
4
+ JsonCredentialStore,
5
+ builtinModels,
6
+ createPiModel,
7
+ defaultAuthFile,
8
+ modelCatalog,
9
+ providerCatalog
10
+ } from "./chunk-6M3OOCKO.js";
11
+ import {
12
+ assertTurnOutput
13
+ } from "./chunk-TJON22J4.js";
14
+
15
+ // node_modules/goah-runner-pi/dist/env-spec.js
16
+ import { readFileSync } from "node:fs";
17
+ import { homedir } from "node:os";
18
+ import { join } from "node:path";
19
+ var ENV_REFERENCE_PREFIX = "env:";
20
+ var OPTIONAL_ENV_REFERENCE_PREFIX = "env?:";
21
+ function parseEnvFile(text) {
22
+ const values = {};
23
+ for (const rawLine of text.split("\n")) {
24
+ const line = rawLine.trim();
25
+ if (line === "" || line.startsWith("#"))
26
+ continue;
27
+ const equals = line.indexOf("=");
28
+ if (equals <= 0)
29
+ continue;
30
+ const key = line.slice(0, equals).trim();
31
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
32
+ continue;
33
+ let value = line.slice(equals + 1).trim();
34
+ if (value.startsWith('"') && value.endsWith('"') && value.length >= 2 || value.startsWith("'") && value.endsWith("'") && value.length >= 2)
35
+ value = value.slice(1, -1);
36
+ values[key] = value;
37
+ }
38
+ return values;
39
+ }
40
+ function readEnvFile(path) {
41
+ try {
42
+ return parseEnvFile(readFileSync(path, "utf8"));
43
+ } catch {
44
+ return {};
45
+ }
46
+ }
47
+ function envFileChain(source) {
48
+ if (!source)
49
+ return [];
50
+ const files = [join(source.root, ".env"), join(homedir(), ".goah", ".env")];
51
+ const merged = {};
52
+ for (let index = files.length - 1; index >= 0; index -= 1)
53
+ Object.assign(merged, readEnvFile(files[index]));
54
+ return [merged];
55
+ }
56
+ function resolveEnvSpec(spec, source) {
57
+ if (!spec)
58
+ return {};
59
+ const [fileLayer] = envFileChain(source);
60
+ const resolved = {};
61
+ for (const [key, value] of Object.entries(spec)) {
62
+ if (value.startsWith(OPTIONAL_ENV_REFERENCE_PREFIX)) {
63
+ const name2 = value.slice(OPTIONAL_ENV_REFERENCE_PREFIX.length);
64
+ const optional = process.env[name2] ?? fileLayer?.[name2];
65
+ if (optional !== void 0)
66
+ resolved[key] = optional;
67
+ continue;
68
+ }
69
+ if (!value.startsWith(ENV_REFERENCE_PREFIX)) {
70
+ resolved[key] = value;
71
+ continue;
72
+ }
73
+ const name = value.slice(ENV_REFERENCE_PREFIX.length);
74
+ const fromProcess = process.env[name];
75
+ if (fromProcess !== void 0) {
76
+ resolved[key] = fromProcess;
77
+ continue;
78
+ }
79
+ const fromFile = fileLayer?.[name];
80
+ if (fromFile !== void 0) {
81
+ resolved[key] = fromFile;
82
+ continue;
83
+ }
84
+ throw new Error(`environment variable is missing: ${name} (set it in your shell, <workspace>/.env, or ~/.goah/.env)`);
85
+ }
86
+ return resolved;
87
+ }
88
+
89
+ // node_modules/goah-runner-pi/dist/configurator.js
90
+ import { dirname, join as join2 } from "node:path";
91
+ import { tmpdir } from "node:os";
92
+ import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
93
+ import { existsSync } from "node:fs";
94
+
95
+ // node_modules/goah-runner-pi/dist/index.js
96
+ import { spawn } from "node:child_process";
97
+ import { createInterface } from "node:readline";
98
+ import { fileURLToPath } from "node:url";
99
+ var PiRunnerAdapter = class {
100
+ driver;
101
+ constructor(driver) {
102
+ this.driver = driver;
103
+ }
104
+ prepare(request) {
105
+ let started = false;
106
+ let resolveResult;
107
+ const result = new Promise((resolve) => {
108
+ resolveResult = resolve;
109
+ });
110
+ return {
111
+ pid: null,
112
+ begin: () => {
113
+ if (started)
114
+ return;
115
+ started = true;
116
+ void this.#run(request).then(resolveResult, (error) => resolveResult({ outcome: "abnormal", reason: error instanceof Error ? error.message : String(error) }));
117
+ },
118
+ result,
119
+ terminate: async () => void 0
120
+ };
121
+ }
122
+ async terminateProcess(pid) {
123
+ await terminatePid(pid, 500);
124
+ }
125
+ async #run(request) {
126
+ const runnerSession = await this.driver.createRunnerSession(request);
127
+ try {
128
+ while (true) {
129
+ const step = await runnerSession.step();
130
+ for (const trace of step.trace ?? [])
131
+ request.emit(trace);
132
+ if (step.response)
133
+ return { outcome: "completed", response: step.response };
134
+ if (step.handoff) {
135
+ const validation = await request.rpc?.("goal.handoff.validate", { handoff: step.handoff.handoff, candidateMessage: step.handoff.response.content });
136
+ if (!validation)
137
+ return { outcome: "abnormal", reason: "Handoff validation is unavailable" };
138
+ request.emit({ type: "message.assistant.completed", data: { message: { id: `adapter:${request.execution.id}:handoff:${validation.attemptId}`, role: "assistant", content: [{ type: "text", text: step.handoff.response.content }], stopReason: "toolUse" }, commitState: "provisional" } });
139
+ if (!validation.accepted) {
140
+ if (validation.fatal)
141
+ return { outcome: "abnormal", reason: validation.issues.map((issue) => issue.message).join("; ") };
142
+ await runnerSession.feedback(validation);
143
+ request.emit({ type: "runner.handoff_rejected", data: { attemptId: validation.attemptId, issues: validation.issues } });
144
+ continue;
145
+ }
146
+ const output = { validationAttemptId: validation.attemptId, validationToken: validation.token, handoff: step.handoff.handoff };
147
+ assertTurnOutput(output);
148
+ return { outcome: "completed", response: step.handoff.response, handoff: output };
149
+ }
150
+ if (step.stopped)
151
+ return { outcome: "abnormal", reason: request.turn.goalCommitment ? "runner stopped without a readable response and valid handoff" : "runner stopped without a response" };
152
+ }
153
+ } catch (error) {
154
+ return { outcome: "abnormal", reason: error instanceof Error ? error.message : String(error) };
155
+ } finally {
156
+ await runnerSession.close();
157
+ }
158
+ }
159
+ };
160
+ function piWorkerPath() {
161
+ return fileURLToPath(new URL("./pi-worker.js", import.meta.url));
162
+ }
163
+ function verificationWorkerPath() {
164
+ return fileURLToPath(new URL("./verification-worker.js", import.meta.url));
165
+ }
166
+ var ProcessRunner = class {
167
+ options;
168
+ isolation = "process";
169
+ constructor(options) {
170
+ this.options = options;
171
+ }
172
+ prepare(request) {
173
+ const explicit = this.options.envSpec ? resolveEnvSpec(this.options.envSpec, { root: this.options.cwd ?? process.cwd() }) : this.options.env;
174
+ const child = spawn(this.options.command, this.options.args ?? [], {
175
+ detached: process.platform !== "win32",
176
+ ...this.options.cwd ? { cwd: this.options.cwd } : {},
177
+ env: childEnvironment(explicit, this.options.inheritEnv)
178
+ });
179
+ let started = false;
180
+ let timedOut = false;
181
+ let messageResult = null;
182
+ let protocolError = null;
183
+ let stderr = "";
184
+ let timer;
185
+ let settled = false;
186
+ let resolveStartReady;
187
+ const startReady = new Promise((resolve) => {
188
+ resolveStartReady = resolve;
189
+ });
190
+ let resolveResult;
191
+ const pendingSteering = /* @__PURE__ */ new Map();
192
+ const result = new Promise((resolve) => {
193
+ resolveResult = resolve;
194
+ });
195
+ const settle = (value) => {
196
+ if (!settled) {
197
+ settled = true;
198
+ resolveResult(value);
199
+ }
200
+ };
201
+ child.stderr?.on("data", (chunk) => {
202
+ stderr = `${stderr}${chunk.toString()}`.slice(-65536);
203
+ });
204
+ let protocolLineBytes = 0;
205
+ child.stdout?.on("data", (chunk) => {
206
+ for (const byte of chunk) {
207
+ if (byte === 10)
208
+ protocolLineBytes = 0;
209
+ else if (++protocolLineBytes > 1e6 && !protocolError) {
210
+ protocolError = "runner protocol line exceeded 1 MB";
211
+ void terminate();
212
+ break;
213
+ }
214
+ }
215
+ });
216
+ const lines = createInterface({ input: child.stdout });
217
+ lines.on("line", (line) => {
218
+ if (protocolError)
219
+ return;
220
+ try {
221
+ const message = JSON.parse(line);
222
+ if (message.type === "trace")
223
+ request.emit(message.event);
224
+ else if (message.type === "steer_ack") {
225
+ const pending = pendingSteering.get(message.id);
226
+ if (pending) {
227
+ pendingSteering.delete(message.id);
228
+ clearTimeout(pending.timer);
229
+ if (message.accepted)
230
+ pending.resolve();
231
+ else
232
+ pending.reject(new Error("runner is no longer accepting steering messages"));
233
+ }
234
+ } else if (message.type === "rpc_request") {
235
+ void Promise.resolve(request.rpc?.(message.method, message.params) ?? Promise.reject(new Error("runner RPC is unavailable"))).then((result2) => child.stdin?.write(`${JSON.stringify({ type: "rpc_response", id: message.id, result: result2 })}
236
+ `)).catch((error) => child.stdin?.write(`${JSON.stringify({ type: "rpc_response", id: message.id, error: error instanceof Error ? error.message : String(error) })}
237
+ `));
238
+ } else
239
+ messageResult = message.result;
240
+ } catch (error) {
241
+ protocolError = error instanceof Error ? error.message : String(error);
242
+ void terminate();
243
+ }
244
+ });
245
+ child.once("error", (error) => {
246
+ resolveStartReady();
247
+ if (timer)
248
+ clearTimeout(timer);
249
+ settle({ outcome: "abnormal", reason: error.message });
250
+ });
251
+ child.once("close", (code, signal) => {
252
+ resolveStartReady();
253
+ for (const pending of pendingSteering.values()) {
254
+ clearTimeout(pending.timer);
255
+ pending.reject(new Error("runner closed before acknowledging the steering message"));
256
+ }
257
+ pendingSteering.clear();
258
+ if (timer)
259
+ clearTimeout(timer);
260
+ if (protocolError)
261
+ settle({ outcome: "abnormal", reason: `runner protocol error: ${protocolError}` });
262
+ else if (messageResult)
263
+ settle(messageResult);
264
+ else if (timedOut)
265
+ settle({ outcome: "abnormal", reason: "runner-specific timeout exceeded and process was killed" });
266
+ else
267
+ settle({ outcome: "abnormal", reason: stderr.trim() || `runner exited without a result (${code ?? signal ?? "unknown"})` });
268
+ });
269
+ const terminate = async () => {
270
+ if (child.pid)
271
+ await terminateOwnedChild(child, this.options.killGraceMs ?? 500);
272
+ };
273
+ return {
274
+ pid: child.pid ?? null,
275
+ begin: () => {
276
+ if (started)
277
+ return;
278
+ started = true;
279
+ void (async () => {
280
+ try {
281
+ const runtime = await this.options.prepareRuntime?.(request);
282
+ const serializable = { agent: request.agent, execution: request.execution, ...request.sourceWake ? { sourceWake: request.sourceWake } : {}, ...request.sourceWakeTriggers ? { sourceWakeTriggers: request.sourceWakeTriggers } : {}, turn: request.turn, context: request.context, ...runtime !== void 0 ? { runtime } : {} };
283
+ child.stdin?.write(`${JSON.stringify({ type: "start", request: serializable })}
284
+ `);
285
+ resolveStartReady();
286
+ if (this.options.timeoutMs)
287
+ timer = setTimeout(() => {
288
+ timedOut = true;
289
+ void terminate();
290
+ }, this.options.timeoutMs);
291
+ } catch (error) {
292
+ protocolError = error instanceof Error ? error.message : String(error);
293
+ resolveStartReady();
294
+ await terminate();
295
+ }
296
+ })();
297
+ },
298
+ result,
299
+ ...this.options.steering ? { steer: async (message) => {
300
+ if (!started)
301
+ throw new Error("runner is not accepting steering messages");
302
+ await startReady;
303
+ if (settled || protocolError || !child.stdin?.writable)
304
+ throw new Error("runner is not accepting steering messages");
305
+ const id = crypto.randomUUID();
306
+ await new Promise((resolve, reject) => {
307
+ const timer2 = setTimeout(() => {
308
+ pendingSteering.delete(id);
309
+ reject(new Error("runner did not acknowledge the steering message in time"));
310
+ }, this.options.steerAckTimeoutMs ?? 2e3);
311
+ timer2.unref();
312
+ pendingSteering.set(id, { resolve, reject, timer: timer2 });
313
+ child.stdin.write(`${JSON.stringify({ type: "steer", id, message })}
314
+ `, (error) => {
315
+ if (error) {
316
+ const pending = pendingSteering.get(id);
317
+ if (pending)
318
+ clearTimeout(pending.timer);
319
+ pendingSteering.delete(id);
320
+ reject(error);
321
+ }
322
+ });
323
+ });
324
+ } } : {},
325
+ terminate
326
+ };
327
+ }
328
+ async terminateProcess(pid) {
329
+ await terminatePid(pid, this.options.killGraceMs ?? 500);
330
+ }
331
+ };
332
+ async function runProcessWorker(run) {
333
+ const parent = process.ppid;
334
+ const monitor = setInterval(() => {
335
+ if (process.ppid !== parent || !isAlive(parent))
336
+ process.exit(70);
337
+ }, 250);
338
+ monitor.unref();
339
+ const input = createInterface({ input: process.stdin });
340
+ const iterator = input[Symbol.asyncIterator]();
341
+ const first = await iterator.next();
342
+ if (first.done)
343
+ throw new Error("runner parent closed before start");
344
+ const start = JSON.parse(first.value);
345
+ if (start.type !== "start")
346
+ throw new Error("first runner message must be start");
347
+ const pending = /* @__PURE__ */ new Map();
348
+ const queuedSteering = [];
349
+ let steerListener;
350
+ const deliverSteer = (id, message) => {
351
+ let accepted = false;
352
+ try {
353
+ accepted = steerListener?.(message) === true;
354
+ } catch {
355
+ }
356
+ process.stdout.write(`${JSON.stringify({ type: "steer_ack", id, accepted })}
357
+ `);
358
+ };
359
+ void (async () => {
360
+ for await (const line of { [Symbol.asyncIterator]: () => iterator }) {
361
+ const message = JSON.parse(line);
362
+ if (message.type === "steer") {
363
+ if (steerListener)
364
+ deliverSteer(message.id, message.message);
365
+ else
366
+ queuedSteering.push(message);
367
+ continue;
368
+ }
369
+ if (message.type !== "rpc_response")
370
+ continue;
371
+ const waiter = pending.get(message.id);
372
+ if (!waiter)
373
+ continue;
374
+ pending.delete(message.id);
375
+ if (message.error)
376
+ waiter.reject(new Error(message.error));
377
+ else
378
+ waiter.resolve(message.result ?? null);
379
+ }
380
+ })();
381
+ const rpc = (method, params) => new Promise((resolve, reject) => {
382
+ const id = crypto.randomUUID();
383
+ pending.set(id, { resolve, reject });
384
+ process.stdout.write(`${JSON.stringify({ type: "rpc_request", id, method, params })}
385
+ `);
386
+ });
387
+ const controls = { onSteer: (listener) => {
388
+ steerListener = listener;
389
+ for (const message of queuedSteering.splice(0))
390
+ deliverSteer(message.id, message.message);
391
+ } };
392
+ const result = await run(start.request, (event) => process.stdout.write(`${JSON.stringify({ type: "trace", event })}
393
+ `), rpc, controls);
394
+ process.stdout.write(`${JSON.stringify({ type: "result", result })}
395
+ `);
396
+ input.close();
397
+ process.stdin.unref();
398
+ clearInterval(monitor);
399
+ }
400
+ async function terminateOwnedChild(child, graceMs) {
401
+ if (!child.pid || child.exitCode !== null || child.signalCode !== null)
402
+ return;
403
+ signalPid(child.pid, "SIGTERM");
404
+ await Promise.race([new Promise((resolve) => child.once("close", () => resolve())), delay(graceMs)]);
405
+ if (child.exitCode === null && child.signalCode === null) {
406
+ signalPid(child.pid, "SIGKILL");
407
+ await new Promise((resolve) => child.once("close", () => resolve()));
408
+ }
409
+ }
410
+ async function terminatePid(pid, graceMs) {
411
+ if (!isAlive(pid))
412
+ return;
413
+ signalPid(pid, "SIGTERM");
414
+ const deadline = Date.now() + graceMs;
415
+ while (isAlive(pid) && Date.now() < deadline)
416
+ await delay(25);
417
+ if (isAlive(pid))
418
+ signalPid(pid, "SIGKILL");
419
+ const killDeadline = Date.now() + graceMs;
420
+ while (isAlive(pid) && Date.now() < killDeadline)
421
+ await delay(25);
422
+ if (isAlive(pid))
423
+ throw new Error(`runner process ${pid} did not exit after SIGKILL`);
424
+ }
425
+ function signalPid(pid, signal) {
426
+ try {
427
+ process.kill(process.platform === "win32" ? pid : -pid, signal);
428
+ } catch {
429
+ }
430
+ }
431
+ function isAlive(pid) {
432
+ try {
433
+ process.kill(pid, 0);
434
+ return true;
435
+ } catch {
436
+ return false;
437
+ }
438
+ }
439
+ function delay(ms) {
440
+ return new Promise((resolve) => setTimeout(resolve, ms));
441
+ }
442
+ function childEnvironment(explicit = {}, inherited = []) {
443
+ const names = /* @__PURE__ */ new Set(["PATH", "TMPDIR", "TMP", "TEMP", "SYSTEMROOT", ...inherited]);
444
+ const env = {};
445
+ for (const name of names)
446
+ if (process.env[name] !== void 0)
447
+ env[name] = process.env[name];
448
+ return { ...env, ...explicit };
449
+ }
450
+
451
+ // node_modules/goah-runner-pi/dist/configurator.js
452
+ function piRunnerConfigurator() {
453
+ return {
454
+ describe: () => ({ id: "pi", name: "Pi", description: "Direct multi-provider model runner", commands: [{ name: "model", description: "List or switch provider/model" }, { name: "auth", description: "Inspect, login, or logout provider credentials" }] }),
455
+ beginSetup: beginPiSetup,
456
+ setup: setupPi,
457
+ doctor: doctorPi,
458
+ summarize: summarizePi,
459
+ runCommand: runPiCommand
460
+ };
461
+ }
462
+ async function beginPiSetup(current, interaction) {
463
+ const before = current ? piConfig(current) : null;
464
+ const authFile = before?.authFile ?? defaultAuthFile();
465
+ const baseline = await readFile(authFile).catch((error) => error.code === "ENOENT" ? null : Promise.reject(error));
466
+ const stagingDirectory = await mkdtemp(join2(tmpdir(), "goah-auth-stage-"));
467
+ const stagingAuthFile = join2(stagingDirectory, "auth.json");
468
+ if (baseline)
469
+ await writeFile(stagingAuthFile, baseline, { mode: 384 });
470
+ let config;
471
+ try {
472
+ config = await setupPi(current, interaction, "full", stagingAuthFile);
473
+ } catch (error) {
474
+ await rm(stagingDirectory, { recursive: true, force: true });
475
+ throw error;
476
+ }
477
+ let state = "open";
478
+ let committed = null;
479
+ return {
480
+ config,
481
+ commit: async () => {
482
+ if (state === "committed")
483
+ return config && typeof config === "object" && !Array.isArray(config) ? { ...config, authFile } : config;
484
+ if (state === "rolled_back")
485
+ throw new Error("Runner setup transaction was already rolled back");
486
+ const staged = await readFile(stagingAuthFile).catch((error) => error.code === "ENOENT" ? null : Promise.reject(error));
487
+ try {
488
+ await new JsonCredentialStore(authFile).replaceFileIfUnchanged(baseline, staged);
489
+ committed = staged;
490
+ state = "committed";
491
+ } catch (error) {
492
+ state = "rolled_back";
493
+ await rm(stagingDirectory, { recursive: true, force: true });
494
+ throw error;
495
+ }
496
+ await rm(stagingDirectory, { recursive: true, force: true });
497
+ return config && typeof config === "object" && !Array.isArray(config) ? { ...config, authFile } : config;
498
+ },
499
+ rollback: async () => {
500
+ if (state === "rolled_back")
501
+ return;
502
+ if (state === "committed")
503
+ await new JsonCredentialStore(authFile).replaceFileIfUnchanged(committed, baseline);
504
+ state = "rolled_back";
505
+ await rm(stagingDirectory, { recursive: true, force: true });
506
+ }
507
+ };
508
+ }
509
+ function createPiProcessRunner(configValue, root) {
510
+ const config = piConfig(configValue);
511
+ createPiModel(config.provider, config.model, piEnvironment(config));
512
+ const privateEnv = piEnvironment(config);
513
+ return new ProcessRunner({
514
+ command: process.execPath,
515
+ args: [piWorkerPath()],
516
+ cwd: root,
517
+ steering: true,
518
+ envSpec: workerEnvironment(config),
519
+ prepareRuntime: async () => {
520
+ const resolved = resolveEnvSpec(privateEnv, { root });
521
+ const configured = createPiModel(config.provider, config.model, resolved);
522
+ const auth = await configured.models.getAuth(config.provider);
523
+ if (!auth && config.provider !== "faux" && !["ollama", "lm-studio", "llama.cpp"].includes(config.provider))
524
+ throw new Error(`No credentials for ${config.provider}. Run \`goah auth login ${config.provider}\` or configure ${config.apiKeyEnv ?? defaultApiKeyEnv(config.provider)}.`);
525
+ const authFile = config.authFile ?? defaultAuthFile();
526
+ return { ...auth?.auth ?? {}, protectedPaths: [.../* @__PURE__ */ new Set([dirname(defaultAuthFile()), dirname(authFile)])] };
527
+ }
528
+ });
529
+ }
530
+ async function setupPi(current, interaction, scope = "full", setupAuthFile = defaultAuthFile()) {
531
+ const legacyCredential = Boolean(current && typeof current === "object" && !Array.isArray(current) && typeof current.apiKeyEnv === "string" && !isShellIdentifier(current.apiKeyEnv));
532
+ const before = current ? piConfig(current) : null;
533
+ const providers = [...providerCatalog(), { id: "custom", name: "Custom endpoint", modelCount: 0, oauth: false, apiKey: true, local: false }, { id: "faux", name: "Offline demo", modelCount: 1, oauth: false, apiKey: false, local: true }];
534
+ const provider = await interaction.select({ title: "Choose a provider", description: "Search by company, provider ID, or capability.", progress: { current: 2, total: 5 }, choices: providers.map((entry) => ({ value: entry.id, label: entry.name, description: `${entry.id}${entry.oauth ? " \xB7 OAuth" : ""}${entry.local ? " \xB7 local" : ` \xB7 ${entry.modelCount} models`}` })) });
535
+ if (!provider)
536
+ return null;
537
+ const providerId = provider === "custom" ? await interaction.input({ title: "Custom provider", description: "A stable ID used only inside this Runner Profile.", prompt: "Provider ID", initial: before?.provider ?? "custom", progress: { current: 2, total: 5 } }) : provider;
538
+ if (!providerId)
539
+ return null;
540
+ const catalog = ["ollama", "lm-studio", "llama.cpp"].includes(providerId) ? await discoverLocalModels(providerId) : modelCatalog(providerId);
541
+ const selected = catalog.length ? await interaction.select({ title: "Choose a model", description: `${providerId} \xB7 search by model name or ID.`, progress: { current: 3, total: 5 }, choices: [...catalog.map((model2) => ({ value: model2.id, label: model2.name, description: `${model2.id} \xB7 ${Math.round(model2.contextWindow / 1e3)}k${model2.reasoning ? " \xB7 reasoning" : ""}` })), { value: "__custom__", label: "Custom model\u2026" }, { value: "__back__", label: "Back to providers" }] }) : "__custom__";
542
+ if (!selected)
543
+ return null;
544
+ if (selected === "__back__")
545
+ return setupPi(current, interaction, scope, setupAuthFile);
546
+ const model = selected === "__custom__" ? await interaction.input({ title: "Model ID", description: `Enter the model identifier accepted by ${providerId}.`, prompt: "Model", progress: { current: 3, total: 5 }, ...before?.provider === providerId ? { initial: before.model } : {} }) : selected;
547
+ if (!model)
548
+ return null;
549
+ const config = scope === "model" && before?.provider === providerId ? { ...before, model, authFile: before.authFile ?? setupAuthFile } : { provider: providerId, model, thinking: before?.thinking ?? (providerId === "faux" ? "off" : "medium"), authFile: scope === "full" ? setupAuthFile : before?.authFile ?? setupAuthFile, authMode: providerId === "faux" || ["ollama", "lm-studio", "llama.cpp"].includes(providerId) ? "local" : "unconfigured" };
550
+ const descriptor = providers.find((entry) => entry.id === provider);
551
+ if (!descriptor.local && provider !== "faux") {
552
+ if (scope === "model") {
553
+ const authenticated = await requireAuthentication(config, descriptor, interaction, legacyCredential);
554
+ if (authenticated === "back")
555
+ return setupPi(current, interaction, scope, setupAuthFile);
556
+ Object.assign(config, authenticated);
557
+ } else {
558
+ const auth = await configureAuthentication(providerId, descriptor, config.authFile, interaction, legacyCredential);
559
+ if (auth === "back")
560
+ return setupPi(current, interaction, scope, setupAuthFile);
561
+ Object.assign(config, auth);
562
+ }
563
+ }
564
+ if (["ollama", "lm-studio", "llama.cpp"].includes(provider)) {
565
+ const baseUrl = await interaction.input({ title: "Local endpoint", prompt: "OpenAI-compatible base URL", initial: localBaseUrl(provider), progress: { current: 4, total: 5 } });
566
+ if (baseUrl)
567
+ config.baseUrl = baseUrl;
568
+ config.contextWindowTokens = positive(await interaction.input({ title: "Context window", prompt: "Tokens", initial: "128000", progress: { current: 4, total: 5 } }), 128e3);
569
+ config.maxOutputTokens = positive(await interaction.input({ title: "Max output", prompt: "Tokens", initial: "16384", progress: { current: 4, total: 5 } }), 16384);
570
+ }
571
+ if (provider === "custom") {
572
+ const baseUrl = await interaction.input({ title: "Custom endpoint", prompt: "Base URL", progress: { current: 4, total: 5 } });
573
+ if (!baseUrl)
574
+ return null;
575
+ config.baseUrl = baseUrl;
576
+ const api = await interaction.select({ title: "Wire API", progress: { current: 4, total: 5 }, choices: [{ value: "openai-completions", label: "OpenAI Chat Completions" }, { value: "openai-responses", label: "OpenAI Responses" }, { value: "anthropic-messages", label: "Anthropic Messages" }] });
577
+ if (!api)
578
+ return null;
579
+ config.api = api;
580
+ config.contextWindowTokens = positive(await interaction.input({ title: "Context window", prompt: "Tokens", initial: "128000", progress: { current: 4, total: 5 } }), 128e3);
581
+ config.maxOutputTokens = positive(await interaction.input({ title: "Max output", prompt: "Tokens", initial: "16384", progress: { current: 4, total: 5 } }), 16384);
582
+ }
583
+ return config;
584
+ }
585
+ async function configureAuthentication(provider, descriptor, authFile, interaction, legacyCredential, allowLater = true, allowEnvironment = true) {
586
+ const store = new JsonCredentialStore(authFile);
587
+ const existing = await store.read(provider);
588
+ const choices = [
589
+ ...existing ? [{ value: "existing", label: "Use saved credentials", description: existing.type === "oauth" ? "OAuth session" : "Stored API key" }] : [],
590
+ ...descriptor.oauth ? [{ value: "oauth", label: "Sign in with OAuth", description: "Opens the provider login flow" }] : [],
591
+ ...descriptor.apiKey ? [{ value: "key", label: "Paste an API key", description: "Stored privately and masked while typing" }] : [],
592
+ ...allowEnvironment ? [{ value: "env", label: "Use an environment variable", description: `Default: ${defaultApiKeyEnv(provider)}` }] : [],
593
+ ...allowLater ? [{ value: "later", label: "Configure later", description: "Save now; runs will remain blocked" }] : [],
594
+ { value: "back", label: "Back to providers" }
595
+ ];
596
+ const description = legacyCredential ? "A pasted key was found in the old environment-variable field. It will be removed from config; rotate that key, then authenticate again here." : "Choose how this runner should authenticate. Secrets never enter goah.config.json.";
597
+ const choice = await interaction.select({ title: "Authentication", description, progress: { current: 4, total: 5 }, choices });
598
+ if (!choice || choice === "back")
599
+ return "back";
600
+ if (choice === "existing")
601
+ return { authMode: existing?.type === "oauth" ? "oauth" : "stored-key" };
602
+ if (choice === "oauth") {
603
+ await oauthLogin(provider, authFile, interaction);
604
+ return { authMode: "oauth" };
605
+ }
606
+ if (choice === "key") {
607
+ const key = await interaction.input({ title: "API key", description: "The value is masked and saved only in the Runner credential store.", prompt: "Paste key", secret: true, progress: { current: 4, total: 5 } });
608
+ if (!key)
609
+ return "back";
610
+ await store.modify(provider, async () => ({ type: "api_key", key }));
611
+ return { authMode: "stored-key" };
612
+ }
613
+ if (choice === "env") {
614
+ const name = await interaction.input({ title: "Environment variable", description: "Enter a variable name, not the key itself.", prompt: "Variable", initial: defaultApiKeyEnv(provider), progress: { current: 4, total: 5 } });
615
+ if (!name)
616
+ return "back";
617
+ if (!isShellIdentifier(name))
618
+ throw new Error("Environment variable names use letters, numbers, and underscores, and cannot contain an API key. Choose \u201CPaste an API key\u201D for a secret value.");
619
+ return { authMode: "environment", apiKeyEnv: name };
620
+ }
621
+ return { authMode: "unconfigured" };
622
+ }
623
+ async function requireAuthentication(config, descriptor, interaction, legacyCredential = false) {
624
+ if (descriptor.local || config.provider === "faux" || ["ollama", "lm-studio", "llama.cpp"].includes(config.provider)) {
625
+ const local = { ...config, authMode: "local" };
626
+ delete local.apiKeyEnv;
627
+ return local;
628
+ }
629
+ const authFile = config.authFile ?? defaultAuthFile();
630
+ const stored = await new JsonCredentialStore(authFile).read(config.provider);
631
+ if (stored) {
632
+ const saved = { ...config, authFile, authMode: stored.type === "oauth" ? "oauth" : "stored-key" };
633
+ delete saved.apiKeyEnv;
634
+ return saved;
635
+ }
636
+ if (environmentAuthenticationAvailable(config))
637
+ return { ...config, authFile, authMode: "environment", apiKeyEnv: config.apiKeyEnv ?? defaultApiKeyEnv(config.provider) };
638
+ const auth = await configureAuthentication(config.provider, descriptor, authFile, interaction, legacyCredential, false, true);
639
+ if (auth === "back")
640
+ return "back";
641
+ const next = { ...config, ...auth, authFile };
642
+ if (auth.authMode !== "environment")
643
+ delete next.apiKeyEnv;
644
+ if (next.authMode === "environment" && !environmentAuthenticationAvailable(next))
645
+ throw new Error(`Environment variable ${next.apiKeyEnv ?? defaultApiKeyEnv(next.provider)} is not set. Set it first or choose \u201CPaste an API key\u201D.`);
646
+ return next;
647
+ }
648
+ function environmentAuthenticationAvailable(config) {
649
+ const key = config.api ? "GOAH_PI_API_KEY" : defaultApiKeyEnv(config.provider);
650
+ try {
651
+ return Boolean(resolveEnvSpec(piEnvironment(config), { root: process.cwd() })[key]);
652
+ } catch {
653
+ return false;
654
+ }
655
+ }
656
+ async function doctorPi(value, context) {
657
+ const config = piConfig(value);
658
+ const env = resolveEnvSpec(piEnvironment(config), { root: context?.root ?? process.cwd() });
659
+ const configured = createPiModel(config.provider, config.model, env);
660
+ const auth = config.provider === "faux" || ["ollama", "lm-studio", "llama.cpp"].includes(config.provider) ? { source: "local" } : await configured.models.getAuth(config.provider);
661
+ const sandbox = bashSandboxCapability();
662
+ const sandboxSmoke = sandbox.available ? await (await import("./pi-worker.js")).runBashCommand(context?.root ?? process.cwd(), { command: "node --version && npm --version && git --version", timeoutMs: 15e3 }, void 0, [dirname(defaultAuthFile())]) : null;
663
+ const sandboxReady = sandbox.available && sandboxSmoke?.isError !== true;
664
+ return [
665
+ { ok: true, name: "model", detail: `${config.provider}/${config.model} \xB7 context ${configured.model.contextWindow} \xB7 output ${configured.model.maxTokens}` },
666
+ { ok: Boolean(auth), name: "auth", detail: auth?.source ?? `No credentials for ${config.provider}; use runner auth login or configure ${config.apiKeyEnv ?? defaultApiKeyEnv(config.provider)}` },
667
+ { ok: sandboxReady, name: "bash-sandbox", detail: sandboxReady ? `${sandbox.detail} \xB7 node/npm/git ready` : sandboxSmoke?.content[0]?.text.trim() || sandbox.detail }
668
+ ];
669
+ }
670
+ function summarizePi(value) {
671
+ const config = piConfig(value);
672
+ const auth = config.authMode === "environment" ? `Environment \xB7 ${config.apiKeyEnv ?? defaultApiKeyEnv(config.provider)}` : config.authMode === "local" ? "Local \xB7 no credentials" : "Managed separately \xB7 use auth status";
673
+ return [
674
+ { label: "Runner", value: "Pi" },
675
+ { label: "Provider", value: config.provider },
676
+ { label: "Model", value: config.model },
677
+ { label: "Thinking", value: config.thinking ?? "off" },
678
+ { label: "Authentication", value: auth },
679
+ { label: "Bash sandbox", value: bashSandboxDescription() },
680
+ ...config.baseUrl ? [{ label: "Endpoint", value: config.baseUrl }] : []
681
+ ];
682
+ }
683
+ function bashSandboxDescription() {
684
+ return bashSandboxCapability().detail;
685
+ }
686
+ function bashSandboxCapability() {
687
+ if (process.platform === "darwin")
688
+ return existsSync("/usr/bin/sandbox-exec") ? { available: true, detail: "sandbox-exec" } : { available: false, detail: "unavailable \u2014 Bash fails closed" };
689
+ if (process.platform === "linux")
690
+ return ["/usr/bin/bwrap", "/bin/bwrap"].some(existsSync) ? { available: true, detail: "bubblewrap" } : { available: false, detail: "unavailable \u2014 install bubblewrap; Bash fails closed" };
691
+ return { available: false, detail: "unavailable on this platform \u2014 Bash fails closed" };
692
+ }
693
+ async function runPiCommand(command, args, value, interaction) {
694
+ const config = piConfig(value);
695
+ if (command === "model") {
696
+ if (!args.length) {
697
+ const selected = await setupPi(value, interaction, "model", config.authFile ?? defaultAuthFile());
698
+ return selected === null ? { output: ["No change."] } : { config: selected, output: [`Pi target changed to ${piConfig(selected).provider}/${piConfig(selected).model}`] };
699
+ }
700
+ if (args[0] === "list") {
701
+ const provider2 = args[1] ?? config.provider;
702
+ if (provider2 === "faux")
703
+ return { output: [`faux/${config.provider === "faux" ? config.model : "faux-goah"}`] };
704
+ if (provider2 === config.provider && config.baseUrl && !providerCatalog().some((entry) => entry.id === provider2))
705
+ return { output: [`${provider2}/${config.model}`] };
706
+ const models = ["ollama", "lm-studio", "llama.cpp"].includes(provider2) ? await discoverLocalModels(provider2) : modelCatalog(provider2);
707
+ return { output: models.map((model2) => `${provider2}/${model2.id}`) };
708
+ }
709
+ const explicitProvider = args[0] === "use";
710
+ const target = explicitProvider ? args[1] : args[0];
711
+ if (!target)
712
+ throw new Error("usage: goah model use PROVIDER/MODEL");
713
+ const slash = target.indexOf("/");
714
+ if (explicitProvider && slash <= 0)
715
+ throw new Error("usage: goah model use PROVIDER/MODEL");
716
+ const provider = explicitProvider ? target.slice(0, slash) : slash > 0 && providerCatalog().some((entry) => entry.id === target.slice(0, slash)) ? target.slice(0, slash) : config.provider;
717
+ const model = explicitProvider || provider !== config.provider || slash >= 0 ? target.slice(slash + 1) : target;
718
+ const knownProvider = provider === "faux" || providerCatalog().some((entry) => entry.id === provider);
719
+ if (provider !== config.provider && !knownProvider)
720
+ throw new Error(`Unknown provider: ${provider}. Use \`goah model\` to configure a custom endpoint.`);
721
+ const existing = provider === config.provider || provider === "faux" || ["ollama", "lm-studio", "llama.cpp"].includes(provider) ? void 0 : await new JsonCredentialStore(config.authFile ?? defaultAuthFile()).read(provider);
722
+ const next = provider === config.provider ? { ...config, model } : { provider, model, thinking: config.thinking ?? (provider === "faux" ? "off" : "medium"), authFile: config.authFile ?? defaultAuthFile(), authMode: provider === "faux" || ["ollama", "lm-studio", "llama.cpp"].includes(provider) ? "local" : existing?.type === "oauth" ? "oauth" : existing ? "stored-key" : "unconfigured" };
723
+ createPiModel(provider, model, piEnvironment(next));
724
+ const descriptor = providerCatalog().find((entry) => entry.id === provider) ?? { id: provider, name: provider, oauth: false, apiKey: true, local: false, modelCount: 0 };
725
+ const authenticated = await requireAuthentication(next, descriptor, interaction);
726
+ if (authenticated === "back")
727
+ return { output: ["No change."] };
728
+ return { config: authenticated, output: [`Pi target changed to ${provider}/${model}`] };
729
+ }
730
+ if (command === "auth") {
731
+ const authFile = config.authFile ?? defaultAuthFile();
732
+ const store = new JsonCredentialStore(authFile);
733
+ const catalog = providerCatalog();
734
+ if (!catalog.some((entry) => entry.id === config.provider))
735
+ catalog.unshift({ id: config.provider, name: config.provider === "faux" ? "Offline demo" : config.provider, oauth: false, apiKey: config.provider !== "faux", local: config.provider === "faux", modelCount: config.provider === "faux" ? 1 : 0 });
736
+ const storedProviders = await store.list();
737
+ for (const stored of storedProviders)
738
+ if (!catalog.some((entry) => entry.id === stored.providerId))
739
+ catalog.push({ id: stored.providerId, name: stored.providerId, oauth: stored.type === "oauth", apiKey: stored.type === "api_key", local: false, modelCount: 0 });
740
+ let action = args[0];
741
+ if (!action)
742
+ action = await interaction.select({ title: "Authentication", description: "Credentials are stored by the Pi Runner, outside workspace config.", choices: [{ value: "login", label: "Add or sign in", description: "OAuth, API key, or environment variable" }, { value: "logout", label: "Sign out or remove", description: "Remove stored credentials" }, { value: "status", label: "View status", description: "Inspect configured providers" }] }) ?? "cancel";
743
+ if (action === "cancel")
744
+ return { output: ["No change."] };
745
+ if (action === "add")
746
+ action = "login";
747
+ if (action === "remove")
748
+ action = "logout";
749
+ let provider = args[1];
750
+ if (!provider && (action === "login" || action === "logout")) {
751
+ const saved = new Set(storedProviders.map((item) => item.providerId));
752
+ const choices = catalog.filter((entry) => action === "login" ? !entry.local : saved.has(entry.id)).map((entry) => ({ value: entry.id, label: entry.name, description: `${entry.id}${saved.has(entry.id) ? " \xB7 signed in" : entry.oauth ? " \xB7 OAuth available" : " \xB7 API key"}` }));
753
+ if (!choices.length)
754
+ return { output: ["No stored credentials."] };
755
+ provider = await interaction.select({ title: action === "login" ? "Sign in to a provider" : "Sign out of a provider", choices }) ?? void 0;
756
+ if (!provider)
757
+ return { output: ["No change."] };
758
+ }
759
+ provider ??= config.provider;
760
+ const descriptor = catalog.find((item) => item.id === provider);
761
+ if (!descriptor)
762
+ throw new Error(`Unknown provider: ${provider}`);
763
+ if (action === "status" || action === "list") {
764
+ const saved = new Set(storedProviders.map((item) => item.providerId));
765
+ const rows = catalog.filter((item) => action === "list" || item.id === provider).map((item) => `${saved.has(item.id) || item.id === config.provider && config.authMode === "environment" ? "\u2713" : "\xB7"} ${item.id}${item.id === config.provider && config.authMode === "environment" ? ` \xB7 env:${config.apiKeyEnv ?? defaultApiKeyEnv(item.id)}` : item.oauth ? " \xB7 OAuth available" : ""}`);
766
+ return { output: rows.length ? rows : ["No configured credentials."] };
767
+ }
768
+ if (action === "login") {
769
+ if (descriptor.local)
770
+ return { output: [`${provider} is local and does not require authentication.`] };
771
+ const auth = await stagedProviderMutation(authFile, provider, (staged) => configureAuthentication(provider, descriptor, staged, interaction, false, false, provider === config.provider));
772
+ if (auth === "back")
773
+ return { output: ["No change."] };
774
+ return { ...provider === config.provider ? { config: { ...config, ...auth, ...auth.authMode === "environment" ? {} : { apiKeyEnv: void 0 } } } : {}, output: [`Authentication configured for ${provider}`] };
775
+ }
776
+ if (action === "logout") {
777
+ if (descriptor.local)
778
+ return { output: [`${provider} is local and does not use stored credentials.`] };
779
+ const storedCredential = await store.read(provider);
780
+ if (!storedCredential && provider === config.provider && config.authMode === "environment")
781
+ return { output: [`${provider} uses ${config.apiKeyEnv ?? defaultApiKeyEnv(provider)} from your environment; unset it in your shell to sign out.`] };
782
+ await stagedProviderMutation(authFile, provider, async (staged) => {
783
+ await new JsonCredentialStore(staged).delete(provider);
784
+ });
785
+ return { ...provider === config.provider && config.authMode !== "environment" ? { config: { ...config, authMode: "unconfigured", apiKeyEnv: void 0 } } : {}, output: [`Removed stored credentials for ${provider}. Environment variables, if set, remain available to the provider.`] };
786
+ }
787
+ throw new Error(`Unknown Pi auth command: ${action}`);
788
+ }
789
+ throw new Error(`Unknown Pi runner command: ${command}`);
790
+ }
791
+ async function stagedProviderMutation(path, provider, operation) {
792
+ const real = new JsonCredentialStore(path);
793
+ const before = await real.read(provider);
794
+ const directory = await mkdtemp(join2(tmpdir(), "goah-auth-command-"));
795
+ const stagedPath = join2(directory, "auth.json");
796
+ const raw = await readFile(path).catch((error) => error.code === "ENOENT" ? null : Promise.reject(error));
797
+ if (raw)
798
+ await writeFile(stagedPath, raw, { mode: 384 });
799
+ try {
800
+ const value = await operation(stagedPath);
801
+ const after = await new JsonCredentialStore(stagedPath).read(provider);
802
+ if (JSON.stringify(before) !== JSON.stringify(after))
803
+ await real.replaceProviderIfUnchanged(provider, before, after);
804
+ return value;
805
+ } finally {
806
+ await rm(directory, { recursive: true, force: true });
807
+ }
808
+ }
809
+ async function oauthLogin(providerId, authFile, interaction) {
810
+ const models = builtinModels({ credentials: new JsonCredentialStore(authFile) });
811
+ const provider = models.getProvider(providerId);
812
+ if (!provider?.auth.oauth)
813
+ throw new Error(`${providerId} does not provide OAuth login.`);
814
+ await models.login(providerId, "oauth", {
815
+ prompt: (prompt) => authPrompt(prompt, interaction),
816
+ notify: (event) => authEvent(event, interaction)
817
+ });
818
+ }
819
+ async function authPrompt(prompt, interaction) {
820
+ if (prompt.type === "select") {
821
+ const answer2 = await interaction.select({ title: "Authentication", description: prompt.message, choices: prompt.options.map((option) => ({ value: option.id, label: option.label, ...option.description ? { description: option.description } : {} })) });
822
+ if (!answer2)
823
+ throw new Error("Authentication cancelled.");
824
+ return answer2;
825
+ }
826
+ const answer = await interaction.input({ title: "Authentication", prompt: prompt.message, ...prompt.placeholder ? { initial: prompt.placeholder } : {} });
827
+ if (answer === null)
828
+ throw new Error("Authentication cancelled.");
829
+ return answer;
830
+ }
831
+ function authEvent(event, interaction) {
832
+ if (event.type === "auth_url") {
833
+ interaction.notify(`${event.instructions ?? "Complete sign-in in your browser."}
834
+ ${event.url}`);
835
+ interaction.openUrl?.(event.url);
836
+ } else if (event.type === "device_code") {
837
+ interaction.notify(`Open ${event.verificationUri} and enter ${event.userCode}`);
838
+ interaction.openUrl?.(event.verificationUri);
839
+ } else
840
+ interaction.notify(event.message);
841
+ }
842
+ function piConfig(value) {
843
+ if (!value || typeof value !== "object" || Array.isArray(value))
844
+ throw new Error("Pi runner configuration is missing.");
845
+ const config = { ...value };
846
+ if (!config.provider || !config.model)
847
+ throw new Error("Pi runner requires provider and model. Run `goah runner setup`.");
848
+ if (config.apiKeyEnv && !isShellIdentifier(config.apiKeyEnv))
849
+ delete config.apiKeyEnv;
850
+ if (!config.authMode)
851
+ config.authMode = ["faux", "ollama", "lm-studio", "llama.cpp"].includes(config.provider) ? "local" : config.apiKeyEnv ? "environment" : "unconfigured";
852
+ return config;
853
+ }
854
+ function piEnvironment(config) {
855
+ const env = { GOAH_PI_PROVIDER: config.provider, GOAH_PI_MODEL: config.model, GOAH_PI_AUTH_FILE: config.authFile ?? defaultAuthFile() };
856
+ if (config.provider === "faux")
857
+ env.GOAH_PI_FAUX_HANDOFF = JSON.stringify(config.fauxHandoff ?? { outcome: "progress" });
858
+ if (config.provider !== "faux" && !["ollama", "lm-studio", "llama.cpp"].includes(config.provider)) {
859
+ const key = config.api ? "GOAH_PI_API_KEY" : defaultApiKeyEnv(config.provider);
860
+ env[key] = `env?:${config.apiKeyEnv ?? defaultApiKeyEnv(config.provider)}`;
861
+ }
862
+ if (config.baseUrl)
863
+ env.GOAH_PI_BASE_URL = config.baseUrl;
864
+ if (config.api)
865
+ env.GOAH_PI_API = config.api;
866
+ if (config.contextWindowTokens)
867
+ env.GOAH_PI_CONTEXT_WINDOW_TOKENS = String(config.contextWindowTokens);
868
+ if (config.maxOutputTokens)
869
+ env.GOAH_PI_MAX_OUTPUT_TOKENS = String(config.maxOutputTokens);
870
+ return env;
871
+ }
872
+ function workerEnvironment(config) {
873
+ const env = piEnvironment(config);
874
+ for (const key of Object.keys(env))
875
+ if (key.endsWith("API_KEY") || env[key]?.startsWith("env?:") || env[key]?.startsWith("env:"))
876
+ delete env[key];
877
+ env.GOAH_PI_AUTH_FILE = "";
878
+ return env;
879
+ }
880
+ function defaultApiKeyEnv(provider) {
881
+ const map = { anthropic: "ANTHROPIC_API_KEY", openai: "OPENAI_API_KEY", google: "GEMINI_API_KEY", openrouter: "OPENROUTER_API_KEY", xai: "XAI_API_KEY", groq: "GROQ_API_KEY", mistral: "MISTRAL_API_KEY", deepseek: "DEEPSEEK_API_KEY", nvidia: "NVIDIA_API_KEY", "github-copilot": "COPILOT_GITHUB_TOKEN", "kimi-coding": "KIMI_API_KEY" };
882
+ return map[provider] ?? `${provider.toUpperCase().replace(/[^A-Z0-9]+/g, "_")}_API_KEY`;
883
+ }
884
+ function isShellIdentifier(value) {
885
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
886
+ }
887
+ function localBaseUrl(provider) {
888
+ return provider === "ollama" ? "http://127.0.0.1:11434/v1" : provider === "lm-studio" ? "http://127.0.0.1:1234/v1" : "http://127.0.0.1:8080/v1";
889
+ }
890
+ function positive(value, fallback) {
891
+ const parsed = Number(value);
892
+ return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
893
+ }
894
+ async function discoverLocalModels(provider) {
895
+ const base = localBaseUrl(provider);
896
+ const url = provider === "ollama" ? `${base.replace(/\/v1\/?$/, "")}/api/tags` : `${base.replace(/\/$/, "")}/models`;
897
+ try {
898
+ const response = await fetch(url, { signal: AbortSignal.timeout(1500) });
899
+ if (!response.ok)
900
+ return [];
901
+ const body = await response.json();
902
+ const ids = provider === "ollama" ? (body.models ?? []).map((item) => item.name) : (body.data ?? []).map((item) => item.id);
903
+ return ids.filter((id) => Boolean(id)).map((id) => ({ provider, id, name: id, contextWindow: 128e3, maxTokens: 16384, reasoning: false }));
904
+ } catch {
905
+ return [];
906
+ }
907
+ }
908
+
909
+ export {
910
+ resolveEnvSpec,
911
+ piRunnerConfigurator,
912
+ createPiProcessRunner,
913
+ piConfig,
914
+ piEnvironment,
915
+ PiRunnerAdapter,
916
+ piWorkerPath,
917
+ verificationWorkerPath,
918
+ ProcessRunner,
919
+ runProcessWorker
920
+ };