poe-code 3.0.191 → 3.0.192

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 (70) hide show
  1. package/dist/cli/commands/configure-payload.d.ts +1 -1
  2. package/dist/cli/commands/configure-payload.js +16 -9
  3. package/dist/cli/commands/configure-payload.js.map +1 -1
  4. package/dist/cli/commands/configure.d.ts +7 -0
  5. package/dist/cli/commands/configure.js +25 -5
  6. package/dist/cli/commands/configure.js.map +1 -1
  7. package/dist/cli/commands/ensure-isolated-config.js +3 -2
  8. package/dist/cli/commands/ensure-isolated-config.js.map +1 -1
  9. package/dist/cli/commands/experiment.js +38 -39
  10. package/dist/cli/commands/experiment.js.map +1 -1
  11. package/dist/cli/commands/memory.js +20 -15
  12. package/dist/cli/commands/memory.js.map +1 -1
  13. package/dist/cli/commands/pipeline-loop-agent.d.ts +1 -0
  14. package/dist/cli/commands/pipeline-loop-agent.js +2 -0
  15. package/dist/cli/commands/pipeline-loop-agent.js.map +1 -0
  16. package/dist/cli/commands/pipeline.js +42 -71
  17. package/dist/cli/commands/pipeline.js.map +1 -1
  18. package/dist/cli/commands/ralph.js +37 -20
  19. package/dist/cli/commands/ralph.js.map +1 -1
  20. package/dist/cli/commands/shared.d.ts +1 -0
  21. package/dist/cli/commands/shared.js +30 -1
  22. package/dist/cli/commands/shared.js.map +1 -1
  23. package/dist/cli/commands/test.js +5 -2
  24. package/dist/cli/commands/test.js.map +1 -1
  25. package/dist/cli/poe-code-command-runner.js +2 -7
  26. package/dist/cli/poe-code-command-runner.js.map +1 -1
  27. package/dist/cli/program.js +55 -46
  28. package/dist/cli/program.js.map +1 -1
  29. package/dist/cli/service-registry.d.ts +7 -1
  30. package/dist/cli/service-registry.js.map +1 -1
  31. package/dist/index.js +9189 -2108
  32. package/dist/index.js.map +4 -4
  33. package/dist/providers/claude-code.js +18 -1
  34. package/dist/providers/claude-code.js.map +3 -3
  35. package/dist/providers/codex.js +18 -1
  36. package/dist/providers/codex.js.map +3 -3
  37. package/dist/providers/create-provider.d.ts +1 -0
  38. package/dist/providers/create-provider.js +3 -0
  39. package/dist/providers/create-provider.js.map +1 -1
  40. package/dist/providers/goose.js +19 -2
  41. package/dist/providers/goose.js.map +3 -3
  42. package/dist/providers/kimi.js +18 -1
  43. package/dist/providers/kimi.js.map +3 -3
  44. package/dist/providers/opencode.js +18 -1
  45. package/dist/providers/opencode.js.map +3 -3
  46. package/dist/providers/poe-agent.js +723 -300
  47. package/dist/providers/poe-agent.js.map +4 -4
  48. package/dist/providers/tiny-http-mcp-server.d.ts +22 -0
  49. package/dist/providers/tiny-http-mcp-server.js +1471 -0
  50. package/dist/providers/tiny-http-mcp-server.js.map +7 -0
  51. package/dist/templates/pipeline/SKILL_plan.md +64 -52
  52. package/package.json +10 -2
  53. package/packages/memory/dist/explain.cli.d.ts +1 -2
  54. package/packages/memory/dist/explain.cli.js +1 -2
  55. package/packages/memory/dist/explain.d.ts +1 -2
  56. package/packages/memory/dist/explain.js +19 -12
  57. package/packages/memory/dist/handle.d.ts +37 -0
  58. package/packages/memory/dist/handle.js +41 -0
  59. package/packages/memory/dist/index.d.ts +3 -1
  60. package/packages/memory/dist/index.js +1474 -452
  61. package/packages/memory/dist/index.js.map +4 -4
  62. package/packages/memory/dist/ingest.d.ts +12 -1
  63. package/packages/memory/dist/ingest.js +23 -12
  64. package/packages/memory/dist/mcp.d.ts +4 -2
  65. package/packages/memory/dist/mcp.js +6 -10
  66. package/packages/memory/dist/query.js +2 -8
  67. package/packages/memory/dist/types.d.ts +0 -21
  68. package/packages/tiny-oauth-test-server/dist/cli.js +191 -0
  69. package/packages/tiny-stdio-mcp-server/dist/server.js +9 -1
  70. package/packages/tiny-stdio-mcp-server/dist/types.d.ts +1 -1
@@ -0,0 +1,1471 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // src/templates/py-poe-spawn/env.mustache
28
+ var require_env = __commonJS({
29
+ "src/templates/py-poe-spawn/env.mustache"(exports, module) {
30
+ module.exports = "POE_API_KEY={{apiKey}}\nPOE_BASE_URL=https://api.poe.com/v1\nMODEL={{model}}\n";
31
+ }
32
+ });
33
+
34
+ // src/templates/py-poe-spawn/main.py.mustache
35
+ var require_main_py = __commonJS({
36
+ "src/templates/py-poe-spawn/main.py.mustache"(exports, module) {
37
+ module.exports = 'import os\nfrom openai import OpenAI\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nclient = OpenAI(\n api_key=os.getenv("POE_API_KEY"),\n base_url=os.getenv("POE_BASE_URL")\n)\n\nresponse = client.chat.completions.create(\n model=os.getenv("MODEL", "{{model}}"),\n messages=[{"role": "user", "content": "Tell me a joke"}]\n)\n\nprint(response.choices[0].message.content)\n';
38
+ }
39
+ });
40
+
41
+ // src/templates/py-poe-spawn/requirements.txt.mustache
42
+ var require_requirements_txt = __commonJS({
43
+ "src/templates/py-poe-spawn/requirements.txt.mustache"(exports, module) {
44
+ module.exports = "openai>=1.0.0\npython-dotenv>=1.0.0\n";
45
+ }
46
+ });
47
+
48
+ // src/templates/codex/config.toml.mustache
49
+ var require_config_toml = __commonJS({
50
+ "src/templates/codex/config.toml.mustache"(exports, module) {
51
+ module.exports = 'model_provider = "poe"\n\n[profiles."{{{profileName}}}"]\nmodel = "{{{model}}}"\nmodel_provider = "poe"\nmodel_reasoning_effort = "{{reasoningEffort}}"\nmodel_verbosity = "medium"\n\n[model_providers.poe]\nname = "poe"\nbase_url = "{{{baseUrl}}}"\nwire_api = "responses"\nexperimental_bearer_token = "{{apiKey}}"\nrequires_openai_auth = false\nsupports_websockets = false\n';
52
+ }
53
+ });
54
+
55
+ // src/templates/tiny-http-mcp-server/server.mjs.mustache
56
+ var require_server_mjs = __commonJS({
57
+ "src/templates/tiny-http-mcp-server/server.mjs.mustache"(exports, module) {
58
+ module.exports = 'import path from "node:path";\nimport { readFile } from "node:fs/promises";\nimport { fileURLToPath, pathToFileURL } from "node:url";\nimport { createHttpServer } from "tiny-http-mcp-server";\n\nfunction isWindowsAbsolutePath(value) {\n if (value.length < 3) {\n return false;\n }\n\n const drive = value.charCodeAt(0);\n const separator = value[2];\n const isLetter =\n (drive >= 65 && drive <= 90) || (drive >= 97 && drive <= 122);\n\n return isLetter && value[1] === ":" && (separator === "\\\\" || separator === "/");\n}\n\nfunction resolveModuleSpecifier(baseDir, value) {\n if (value.startsWith("file:")) {\n return value;\n }\n\n if (value.startsWith(".") || value.startsWith("/") || isWindowsAbsolutePath(value)) {\n const resolvedPath = path.isAbsolute(value)\n ? value\n : path.resolve(baseDir, value);\n return pathToFileURL(resolvedPath).href;\n }\n\n return value;\n}\n\nconst directory = path.dirname(fileURLToPath(import.meta.url));\nconst config = JSON.parse(\n await readFile(new URL("./config.json", import.meta.url), "utf8")\n);\n\nconst verifierModule = await import(\n resolveModuleSpecifier(directory, config.oauth.verifierModule)\n);\nconst verifier = verifierModule[config.oauth.verifierExport ?? "default"];\n\nif (!verifier || typeof verifier.verify !== "function") {\n throw new Error("Verifier module must export an object with a verify() method.");\n}\n\nconst server = createHttpServer({\n name: config.name,\n version: config.version,\n oauth: {\n resource: config.oauth.resource,\n authorizationServers: config.oauth.authorizationServers,\n requiredScopes: config.oauth.requiredScopes,\n scopesSupported: config.oauth.scopesSupported,\n bearerMethodsSupported: config.oauth.bearerMethodsSupported,\n verifier,\n },\n});\n\nconst handle = await server.listenHttp(config.listen);\n\nconsole.log(handle.url);\n\nconst shutdown = async () => {\n await handle.close();\n process.exit(0);\n};\n\nprocess.once("SIGINT", () => {\n void shutdown();\n});\n\nprocess.once("SIGTERM", () => {\n void shutdown();\n});\n';
59
+ }
60
+ });
61
+
62
+ // src/templates/tiny-http-mcp-server/verify-token.mjs.mustache
63
+ var require_verify_token_mjs = __commonJS({
64
+ "src/templates/tiny-http-mcp-server/verify-token.mjs.mustache"(exports, module) {
65
+ module.exports = 'import { TokenVerificationError } from "tiny-http-mcp-server";\n\nexport default {\n async verify(_input) {\n throw new TokenVerificationError({\n error: "invalid_token",\n errorDescription: "Replace ~/.poe-code/tiny-http-mcp-server/verify-token.mjs with your token verifier.",\n });\n },\n};\n';
66
+ }
67
+ });
68
+
69
+ // packages/config-mutations/src/mutations/config-mutation.ts
70
+ function merge(options) {
71
+ return {
72
+ kind: "configMerge",
73
+ target: options.target,
74
+ value: options.value,
75
+ format: options.format,
76
+ pruneByPrefix: options.pruneByPrefix,
77
+ label: options.label
78
+ };
79
+ }
80
+ function prune(options) {
81
+ return {
82
+ kind: "configPrune",
83
+ target: options.target,
84
+ shape: options.shape,
85
+ format: options.format,
86
+ onlyIf: options.onlyIf,
87
+ label: options.label
88
+ };
89
+ }
90
+ function transform(options) {
91
+ return {
92
+ kind: "configTransform",
93
+ target: options.target,
94
+ format: options.format,
95
+ transform: options.transform,
96
+ label: options.label
97
+ };
98
+ }
99
+ var configMutation = {
100
+ merge,
101
+ prune,
102
+ transform
103
+ };
104
+
105
+ // packages/config-mutations/src/mutations/file-mutation.ts
106
+ function ensureDirectory(options) {
107
+ return {
108
+ kind: "ensureDirectory",
109
+ path: options.path,
110
+ label: options.label
111
+ };
112
+ }
113
+ function remove(options) {
114
+ return {
115
+ kind: "removeFile",
116
+ target: options.target,
117
+ whenEmpty: options.whenEmpty,
118
+ whenContentMatches: options.whenContentMatches,
119
+ label: options.label
120
+ };
121
+ }
122
+ function removeDirectory(options) {
123
+ return {
124
+ kind: "removeDirectory",
125
+ path: options.path,
126
+ force: options.force,
127
+ label: options.label
128
+ };
129
+ }
130
+ function chmod(options) {
131
+ return {
132
+ kind: "chmod",
133
+ target: options.target,
134
+ mode: options.mode,
135
+ label: options.label
136
+ };
137
+ }
138
+ function backup(options) {
139
+ return {
140
+ kind: "backup",
141
+ target: options.target,
142
+ label: options.label
143
+ };
144
+ }
145
+ var fileMutation = {
146
+ ensureDirectory,
147
+ remove,
148
+ removeDirectory,
149
+ chmod,
150
+ backup
151
+ };
152
+
153
+ // packages/config-mutations/src/mutations/template-mutation.ts
154
+ function write(options) {
155
+ return {
156
+ kind: "templateWrite",
157
+ target: options.target,
158
+ templateId: options.templateId,
159
+ context: options.context,
160
+ label: options.label
161
+ };
162
+ }
163
+ function mergeToml(options) {
164
+ return {
165
+ kind: "templateMergeToml",
166
+ target: options.target,
167
+ templateId: options.templateId,
168
+ context: options.context,
169
+ label: options.label
170
+ };
171
+ }
172
+ function mergeJson(options) {
173
+ return {
174
+ kind: "templateMergeJson",
175
+ target: options.target,
176
+ templateId: options.templateId,
177
+ context: options.context,
178
+ label: options.label
179
+ };
180
+ }
181
+ var templateMutation = {
182
+ write,
183
+ mergeToml,
184
+ mergeJson
185
+ };
186
+
187
+ // packages/config-mutations/src/execution/apply-mutation.ts
188
+ import Mustache from "mustache";
189
+
190
+ // packages/config-mutations/src/formats/json.ts
191
+ import * as jsonc from "jsonc-parser";
192
+ function isConfigObject(value) {
193
+ return typeof value === "object" && value !== null && !Array.isArray(value);
194
+ }
195
+ function parse2(content) {
196
+ if (!content || content.trim() === "") {
197
+ return {};
198
+ }
199
+ const errors = [];
200
+ const parsed = jsonc.parse(content, errors, {
201
+ allowTrailingComma: true,
202
+ disallowComments: false
203
+ });
204
+ if (errors.length > 0) {
205
+ throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
206
+ }
207
+ if (parsed === null || parsed === void 0) {
208
+ return {};
209
+ }
210
+ if (!isConfigObject(parsed)) {
211
+ throw new Error("Expected JSON object.");
212
+ }
213
+ return parsed;
214
+ }
215
+ function serialize(obj) {
216
+ return `${JSON.stringify(obj, null, 2)}
217
+ `;
218
+ }
219
+ function merge2(base, patch) {
220
+ const result = { ...base };
221
+ for (const [key, value] of Object.entries(patch)) {
222
+ if (value === void 0) {
223
+ continue;
224
+ }
225
+ const existing = result[key];
226
+ if (isConfigObject(existing) && isConfigObject(value)) {
227
+ result[key] = merge2(existing, value);
228
+ continue;
229
+ }
230
+ result[key] = value;
231
+ }
232
+ return result;
233
+ }
234
+ function prune2(obj, shape) {
235
+ let changed = false;
236
+ const result = { ...obj };
237
+ for (const [key, pattern] of Object.entries(shape)) {
238
+ if (!(key in result)) {
239
+ continue;
240
+ }
241
+ const current = result[key];
242
+ if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
243
+ delete result[key];
244
+ changed = true;
245
+ continue;
246
+ }
247
+ if (isConfigObject(pattern) && isConfigObject(current)) {
248
+ const { changed: childChanged, result: childResult } = prune2(
249
+ current,
250
+ pattern
251
+ );
252
+ if (childChanged) {
253
+ changed = true;
254
+ }
255
+ if (Object.keys(childResult).length === 0) {
256
+ delete result[key];
257
+ } else {
258
+ result[key] = childResult;
259
+ }
260
+ continue;
261
+ }
262
+ delete result[key];
263
+ changed = true;
264
+ }
265
+ return { changed, result };
266
+ }
267
+ var jsonFormat = {
268
+ parse: parse2,
269
+ serialize,
270
+ merge: merge2,
271
+ prune: prune2
272
+ };
273
+
274
+ // packages/config-mutations/src/formats/toml.ts
275
+ import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
276
+ function isConfigObject2(value) {
277
+ return typeof value === "object" && value !== null && !Array.isArray(value);
278
+ }
279
+ function parse3(content) {
280
+ if (!content || content.trim() === "") {
281
+ return {};
282
+ }
283
+ const parsed = parseToml(content);
284
+ if (!isConfigObject2(parsed)) {
285
+ throw new Error("Expected TOML document to be a table.");
286
+ }
287
+ return parsed;
288
+ }
289
+ function serialize2(obj) {
290
+ const serialized = stringifyToml(obj);
291
+ return serialized.endsWith("\n") ? serialized : `${serialized}
292
+ `;
293
+ }
294
+ function merge3(base, patch) {
295
+ const result = { ...base };
296
+ for (const [key, value] of Object.entries(patch)) {
297
+ if (value === void 0) {
298
+ continue;
299
+ }
300
+ const existing = result[key];
301
+ if (isConfigObject2(existing) && isConfigObject2(value)) {
302
+ result[key] = merge3(existing, value);
303
+ continue;
304
+ }
305
+ result[key] = value;
306
+ }
307
+ return result;
308
+ }
309
+ function prune3(obj, shape) {
310
+ let changed = false;
311
+ const result = { ...obj };
312
+ for (const [key, pattern] of Object.entries(shape)) {
313
+ if (!(key in result)) {
314
+ continue;
315
+ }
316
+ const current = result[key];
317
+ if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
318
+ delete result[key];
319
+ changed = true;
320
+ continue;
321
+ }
322
+ if (isConfigObject2(pattern) && isConfigObject2(current)) {
323
+ const { changed: childChanged, result: childResult } = prune3(
324
+ current,
325
+ pattern
326
+ );
327
+ if (childChanged) {
328
+ changed = true;
329
+ }
330
+ if (Object.keys(childResult).length === 0) {
331
+ delete result[key];
332
+ } else {
333
+ result[key] = childResult;
334
+ }
335
+ continue;
336
+ }
337
+ delete result[key];
338
+ changed = true;
339
+ }
340
+ return { changed, result };
341
+ }
342
+ var tomlFormat = {
343
+ parse: parse3,
344
+ serialize: serialize2,
345
+ merge: merge3,
346
+ prune: prune3
347
+ };
348
+
349
+ // packages/config-mutations/src/formats/yaml.ts
350
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
351
+ function isConfigObject3(value) {
352
+ return typeof value === "object" && value !== null && !Array.isArray(value);
353
+ }
354
+ function parse4(content) {
355
+ if (!content || content.trim() === "") {
356
+ return {};
357
+ }
358
+ const parsed = parseYaml(content);
359
+ if (parsed === null || parsed === void 0) {
360
+ return {};
361
+ }
362
+ if (!isConfigObject3(parsed)) {
363
+ throw new Error("Expected YAML object.");
364
+ }
365
+ return parsed;
366
+ }
367
+ function serialize3(obj) {
368
+ const serialized = stringifyYaml(obj);
369
+ return serialized.endsWith("\n") ? serialized : `${serialized}
370
+ `;
371
+ }
372
+ function merge4(base, patch) {
373
+ const result = { ...base };
374
+ for (const [key, value] of Object.entries(patch)) {
375
+ if (value === void 0) {
376
+ continue;
377
+ }
378
+ const existing = result[key];
379
+ if (isConfigObject3(existing) && isConfigObject3(value)) {
380
+ result[key] = merge4(existing, value);
381
+ continue;
382
+ }
383
+ result[key] = value;
384
+ }
385
+ return result;
386
+ }
387
+ function prune4(obj, shape) {
388
+ let changed = false;
389
+ const result = { ...obj };
390
+ for (const [key, pattern] of Object.entries(shape)) {
391
+ if (!(key in result)) {
392
+ continue;
393
+ }
394
+ const current = result[key];
395
+ if (isConfigObject3(pattern) && Object.keys(pattern).length === 0) {
396
+ delete result[key];
397
+ changed = true;
398
+ continue;
399
+ }
400
+ if (isConfigObject3(pattern) && isConfigObject3(current)) {
401
+ const { changed: childChanged, result: childResult } = prune4(current, pattern);
402
+ if (childChanged) {
403
+ changed = true;
404
+ }
405
+ if (Object.keys(childResult).length === 0) {
406
+ delete result[key];
407
+ } else {
408
+ result[key] = childResult;
409
+ }
410
+ continue;
411
+ }
412
+ delete result[key];
413
+ changed = true;
414
+ }
415
+ return { changed, result };
416
+ }
417
+ var yamlFormat = {
418
+ parse: parse4,
419
+ serialize: serialize3,
420
+ merge: merge4,
421
+ prune: prune4
422
+ };
423
+
424
+ // packages/config-mutations/src/formats/index.ts
425
+ var formatRegistry = {
426
+ json: jsonFormat,
427
+ toml: tomlFormat,
428
+ yaml: yamlFormat
429
+ };
430
+ var extensionMap = {
431
+ ".json": "json",
432
+ ".toml": "toml",
433
+ ".yaml": "yaml",
434
+ ".yml": "yaml"
435
+ };
436
+ function getConfigFormat(pathOrFormat) {
437
+ if (pathOrFormat in formatRegistry) {
438
+ return formatRegistry[pathOrFormat];
439
+ }
440
+ const ext = getExtension(pathOrFormat);
441
+ const formatName = extensionMap[ext];
442
+ if (!formatName) {
443
+ throw new Error(
444
+ `Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
445
+ );
446
+ }
447
+ return formatRegistry[formatName];
448
+ }
449
+ function detectFormat(path2) {
450
+ const ext = getExtension(path2);
451
+ return extensionMap[ext];
452
+ }
453
+ function getExtension(path2) {
454
+ const lastDot = path2.lastIndexOf(".");
455
+ if (lastDot === -1) {
456
+ return "";
457
+ }
458
+ return path2.slice(lastDot).toLowerCase();
459
+ }
460
+
461
+ // packages/config-mutations/src/execution/path-utils.ts
462
+ import path from "node:path";
463
+ function expandHome(targetPath, homeDir) {
464
+ if (!targetPath?.startsWith("~")) {
465
+ return targetPath;
466
+ }
467
+ if (targetPath.startsWith("~./")) {
468
+ targetPath = `~/.${targetPath.slice(3)}`;
469
+ }
470
+ let remainder = targetPath.slice(1);
471
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
472
+ remainder = remainder.slice(1);
473
+ } else if (remainder.startsWith(".")) {
474
+ remainder = remainder.slice(1);
475
+ if (remainder.startsWith("/") || remainder.startsWith("\\")) {
476
+ remainder = remainder.slice(1);
477
+ }
478
+ }
479
+ return remainder.length === 0 ? homeDir : path.join(homeDir, remainder);
480
+ }
481
+ function validateHomePath(targetPath) {
482
+ if (typeof targetPath !== "string" || targetPath.length === 0) {
483
+ throw new Error("Target path must be a non-empty string.");
484
+ }
485
+ if (!targetPath.startsWith("~")) {
486
+ throw new Error(
487
+ `All target paths must be home-relative (start with ~). Received: "${targetPath}"`
488
+ );
489
+ }
490
+ }
491
+ function resolvePath(rawPath, homeDir, pathMapper) {
492
+ validateHomePath(rawPath);
493
+ const expanded = expandHome(rawPath, homeDir);
494
+ if (!pathMapper) {
495
+ return expanded;
496
+ }
497
+ const rawDirectory = path.dirname(expanded);
498
+ const mappedDirectory = pathMapper.mapTargetDirectory({
499
+ targetDirectory: rawDirectory
500
+ });
501
+ const filename = path.basename(expanded);
502
+ return filename.length === 0 ? mappedDirectory : path.join(mappedDirectory, filename);
503
+ }
504
+
505
+ // packages/config-mutations/src/fs-utils.ts
506
+ function isNotFound(error) {
507
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
508
+ }
509
+ async function readFileIfExists(fs, target) {
510
+ try {
511
+ return await fs.readFile(target, "utf8");
512
+ } catch (error) {
513
+ if (isNotFound(error)) {
514
+ return null;
515
+ }
516
+ throw error;
517
+ }
518
+ }
519
+ async function pathExists(fs, target) {
520
+ try {
521
+ await fs.stat(target);
522
+ return true;
523
+ } catch (error) {
524
+ if (isNotFound(error)) {
525
+ return false;
526
+ }
527
+ throw error;
528
+ }
529
+ }
530
+ function createTimestamp() {
531
+ return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
532
+ }
533
+
534
+ // packages/config-mutations/src/execution/apply-mutation.ts
535
+ function resolveValue(resolver, options) {
536
+ if (typeof resolver === "function") {
537
+ return resolver(options);
538
+ }
539
+ return resolver;
540
+ }
541
+ function createInvalidDocumentBackupPath(targetPath) {
542
+ const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
543
+ return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
544
+ }
545
+ async function backupInvalidDocument(fs, targetPath, content) {
546
+ const backupPath = createInvalidDocumentBackupPath(targetPath);
547
+ await fs.writeFile(backupPath, content, { encoding: "utf8" });
548
+ }
549
+ function describeMutation(kind, targetPath) {
550
+ const displayPath = targetPath ?? "target";
551
+ switch (kind) {
552
+ case "ensureDirectory":
553
+ return `Create ${displayPath}`;
554
+ case "removeDirectory":
555
+ return `Remove directory ${displayPath}`;
556
+ case "backup":
557
+ return `Backup ${displayPath}`;
558
+ case "templateWrite":
559
+ return `Write ${displayPath}`;
560
+ case "chmod":
561
+ return `Set permissions on ${displayPath}`;
562
+ case "removeFile":
563
+ return `Remove ${displayPath}`;
564
+ case "configMerge":
565
+ case "configPrune":
566
+ case "configTransform":
567
+ case "templateMergeToml":
568
+ case "templateMergeJson":
569
+ return `Update ${displayPath}`;
570
+ default:
571
+ return "Operation";
572
+ }
573
+ }
574
+ function pruneKeysByPrefix(table, prefix) {
575
+ const result = {};
576
+ for (const [key, value] of Object.entries(table)) {
577
+ if (!key.startsWith(prefix)) {
578
+ result[key] = value;
579
+ }
580
+ }
581
+ return result;
582
+ }
583
+ function isConfigObject4(value) {
584
+ return typeof value === "object" && value !== null && !Array.isArray(value);
585
+ }
586
+ function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
587
+ const result = { ...base };
588
+ const prefixMap = pruneByPrefix ?? {};
589
+ for (const [key, value] of Object.entries(patch)) {
590
+ const current = result[key];
591
+ const prefix = prefixMap[key];
592
+ if (isConfigObject4(current) && isConfigObject4(value)) {
593
+ if (prefix) {
594
+ const pruned = pruneKeysByPrefix(current, prefix);
595
+ result[key] = { ...pruned, ...value };
596
+ } else {
597
+ result[key] = mergeWithPruneByPrefix(
598
+ current,
599
+ value,
600
+ prefixMap
601
+ );
602
+ }
603
+ continue;
604
+ }
605
+ result[key] = value;
606
+ }
607
+ return result;
608
+ }
609
+ async function applyMutation(mutation, context, options) {
610
+ switch (mutation.kind) {
611
+ case "ensureDirectory":
612
+ return applyEnsureDirectory(mutation, context, options);
613
+ case "removeDirectory":
614
+ return applyRemoveDirectory(mutation, context, options);
615
+ case "removeFile":
616
+ return applyRemoveFile(mutation, context, options);
617
+ case "chmod":
618
+ return applyChmod(mutation, context, options);
619
+ case "backup":
620
+ return applyBackup(mutation, context, options);
621
+ case "configMerge":
622
+ return applyConfigMerge(mutation, context, options);
623
+ case "configPrune":
624
+ return applyConfigPrune(mutation, context, options);
625
+ case "configTransform":
626
+ return applyConfigTransform(mutation, context, options);
627
+ case "templateWrite":
628
+ return applyTemplateWrite(mutation, context, options);
629
+ case "templateMergeToml":
630
+ return applyTemplateMerge(mutation, context, options, "toml");
631
+ case "templateMergeJson":
632
+ return applyTemplateMerge(mutation, context, options, "json");
633
+ default: {
634
+ const never = mutation;
635
+ throw new Error(`Unknown mutation kind: ${never.kind}`);
636
+ }
637
+ }
638
+ }
639
+ async function applyEnsureDirectory(mutation, context, options) {
640
+ const rawPath = resolveValue(mutation.path, options);
641
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
642
+ const details = {
643
+ kind: mutation.kind,
644
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
645
+ targetPath
646
+ };
647
+ const existed = await pathExists(context.fs, targetPath);
648
+ if (!context.dryRun) {
649
+ await context.fs.mkdir(targetPath, { recursive: true });
650
+ }
651
+ return {
652
+ outcome: {
653
+ changed: !existed,
654
+ effect: "mkdir",
655
+ detail: existed ? "noop" : "create"
656
+ },
657
+ details
658
+ };
659
+ }
660
+ async function applyRemoveDirectory(mutation, context, options) {
661
+ const rawPath = resolveValue(mutation.path, options);
662
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
663
+ const details = {
664
+ kind: mutation.kind,
665
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
666
+ targetPath
667
+ };
668
+ const existed = await pathExists(context.fs, targetPath);
669
+ if (!existed) {
670
+ return {
671
+ outcome: { changed: false, effect: "none", detail: "noop" },
672
+ details
673
+ };
674
+ }
675
+ if (typeof context.fs.rm !== "function") {
676
+ return {
677
+ outcome: { changed: false, effect: "none", detail: "noop" },
678
+ details
679
+ };
680
+ }
681
+ if (mutation.force) {
682
+ if (!context.dryRun) {
683
+ await context.fs.rm(targetPath, { recursive: true, force: true });
684
+ }
685
+ return {
686
+ outcome: { changed: true, effect: "delete", detail: "delete" },
687
+ details
688
+ };
689
+ }
690
+ const entries = await context.fs.readdir(targetPath);
691
+ if (entries.length > 0) {
692
+ return {
693
+ outcome: { changed: false, effect: "none", detail: "noop" },
694
+ details
695
+ };
696
+ }
697
+ if (!context.dryRun) {
698
+ await context.fs.rm(targetPath, { recursive: true, force: true });
699
+ }
700
+ return {
701
+ outcome: { changed: true, effect: "delete", detail: "delete" },
702
+ details
703
+ };
704
+ }
705
+ async function applyRemoveFile(mutation, context, options) {
706
+ const rawPath = resolveValue(mutation.target, options);
707
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
708
+ const details = {
709
+ kind: mutation.kind,
710
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
711
+ targetPath
712
+ };
713
+ try {
714
+ const content = await context.fs.readFile(targetPath, "utf8");
715
+ const trimmed = content.trim();
716
+ if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
717
+ return {
718
+ outcome: { changed: false, effect: "none", detail: "noop" },
719
+ details
720
+ };
721
+ }
722
+ if (mutation.whenEmpty && trimmed.length > 0) {
723
+ return {
724
+ outcome: { changed: false, effect: "none", detail: "noop" },
725
+ details
726
+ };
727
+ }
728
+ if (!context.dryRun) {
729
+ await context.fs.unlink(targetPath);
730
+ }
731
+ return {
732
+ outcome: { changed: true, effect: "delete", detail: "delete" },
733
+ details
734
+ };
735
+ } catch (error) {
736
+ if (isNotFound(error)) {
737
+ return {
738
+ outcome: { changed: false, effect: "none", detail: "noop" },
739
+ details
740
+ };
741
+ }
742
+ throw error;
743
+ }
744
+ }
745
+ async function applyChmod(mutation, context, options) {
746
+ const rawPath = resolveValue(mutation.target, options);
747
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
748
+ const details = {
749
+ kind: mutation.kind,
750
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
751
+ targetPath
752
+ };
753
+ if (typeof context.fs.chmod !== "function") {
754
+ return {
755
+ outcome: { changed: false, effect: "none", detail: "noop" },
756
+ details
757
+ };
758
+ }
759
+ try {
760
+ const stat = await context.fs.stat(targetPath);
761
+ const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
762
+ if (currentMode === mutation.mode) {
763
+ return {
764
+ outcome: { changed: false, effect: "none", detail: "noop" },
765
+ details
766
+ };
767
+ }
768
+ if (!context.dryRun) {
769
+ await context.fs.chmod(targetPath, mutation.mode);
770
+ }
771
+ return {
772
+ outcome: { changed: true, effect: "chmod", detail: "update" },
773
+ details
774
+ };
775
+ } catch (error) {
776
+ if (isNotFound(error)) {
777
+ return {
778
+ outcome: { changed: false, effect: "none", detail: "noop" },
779
+ details
780
+ };
781
+ }
782
+ throw error;
783
+ }
784
+ }
785
+ async function applyBackup(mutation, context, options) {
786
+ const rawPath = resolveValue(mutation.target, options);
787
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
788
+ const details = {
789
+ kind: mutation.kind,
790
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
791
+ targetPath
792
+ };
793
+ const content = await readFileIfExists(context.fs, targetPath);
794
+ if (content === null) {
795
+ return {
796
+ outcome: { changed: false, effect: "none", detail: "noop" },
797
+ details
798
+ };
799
+ }
800
+ if (!context.dryRun) {
801
+ const backupPath = `${targetPath}.backup-${createTimestamp()}`;
802
+ await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
803
+ }
804
+ return {
805
+ outcome: { changed: true, effect: "copy", detail: "backup" },
806
+ details
807
+ };
808
+ }
809
+ async function applyConfigMerge(mutation, context, options) {
810
+ const rawPath = resolveValue(mutation.target, options);
811
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
812
+ const details = {
813
+ kind: mutation.kind,
814
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
815
+ targetPath
816
+ };
817
+ const formatName = mutation.format ?? detectFormat(rawPath);
818
+ if (!formatName) {
819
+ throw new Error(
820
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
821
+ );
822
+ }
823
+ const format = getConfigFormat(formatName);
824
+ const rawContent = await readFileIfExists(context.fs, targetPath);
825
+ let current;
826
+ try {
827
+ current = rawContent === null ? {} : format.parse(rawContent);
828
+ } catch {
829
+ if (rawContent !== null) {
830
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
831
+ }
832
+ current = {};
833
+ }
834
+ const value = resolveValue(mutation.value, options);
835
+ let merged;
836
+ if (mutation.pruneByPrefix) {
837
+ merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
838
+ } else {
839
+ merged = format.merge(current, value);
840
+ }
841
+ const serialized = format.serialize(merged);
842
+ const changed = serialized !== rawContent;
843
+ if (changed && !context.dryRun) {
844
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
845
+ }
846
+ return {
847
+ outcome: {
848
+ changed,
849
+ effect: changed ? "write" : "none",
850
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
851
+ },
852
+ details
853
+ };
854
+ }
855
+ async function applyConfigPrune(mutation, context, options) {
856
+ const rawPath = resolveValue(mutation.target, options);
857
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
858
+ const details = {
859
+ kind: mutation.kind,
860
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
861
+ targetPath
862
+ };
863
+ const rawContent = await readFileIfExists(context.fs, targetPath);
864
+ if (rawContent === null) {
865
+ return {
866
+ outcome: { changed: false, effect: "none", detail: "noop" },
867
+ details
868
+ };
869
+ }
870
+ const formatName = mutation.format ?? detectFormat(rawPath);
871
+ if (!formatName) {
872
+ throw new Error(
873
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
874
+ );
875
+ }
876
+ const format = getConfigFormat(formatName);
877
+ let current;
878
+ try {
879
+ current = format.parse(rawContent);
880
+ } catch {
881
+ return {
882
+ outcome: { changed: false, effect: "none", detail: "noop" },
883
+ details
884
+ };
885
+ }
886
+ if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
887
+ return {
888
+ outcome: { changed: false, effect: "none", detail: "noop" },
889
+ details
890
+ };
891
+ }
892
+ const shape = resolveValue(mutation.shape, options);
893
+ const { changed, result } = format.prune(current, shape);
894
+ if (!changed) {
895
+ return {
896
+ outcome: { changed: false, effect: "none", detail: "noop" },
897
+ details
898
+ };
899
+ }
900
+ if (Object.keys(result).length === 0) {
901
+ if (!context.dryRun) {
902
+ await context.fs.unlink(targetPath);
903
+ }
904
+ return {
905
+ outcome: { changed: true, effect: "delete", detail: "delete" },
906
+ details
907
+ };
908
+ }
909
+ const serialized = format.serialize(result);
910
+ if (!context.dryRun) {
911
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
912
+ }
913
+ return {
914
+ outcome: { changed: true, effect: "write", detail: "update" },
915
+ details
916
+ };
917
+ }
918
+ async function applyConfigTransform(mutation, context, options) {
919
+ const rawPath = resolveValue(mutation.target, options);
920
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
921
+ const details = {
922
+ kind: mutation.kind,
923
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
924
+ targetPath
925
+ };
926
+ const formatName = mutation.format ?? detectFormat(rawPath);
927
+ if (!formatName) {
928
+ throw new Error(
929
+ `Cannot detect config format for "${rawPath}". Provide explicit format option.`
930
+ );
931
+ }
932
+ const format = getConfigFormat(formatName);
933
+ const rawContent = await readFileIfExists(context.fs, targetPath);
934
+ let current;
935
+ try {
936
+ current = rawContent === null ? {} : format.parse(rawContent);
937
+ } catch {
938
+ if (rawContent !== null) {
939
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
940
+ }
941
+ current = {};
942
+ }
943
+ const { content: transformed, changed } = mutation.transform(current, options);
944
+ if (!changed) {
945
+ return {
946
+ outcome: { changed: false, effect: "none", detail: "noop" },
947
+ details
948
+ };
949
+ }
950
+ if (transformed === null) {
951
+ if (rawContent === null) {
952
+ return {
953
+ outcome: { changed: false, effect: "none", detail: "noop" },
954
+ details
955
+ };
956
+ }
957
+ if (!context.dryRun) {
958
+ await context.fs.unlink(targetPath);
959
+ }
960
+ return {
961
+ outcome: { changed: true, effect: "delete", detail: "delete" },
962
+ details
963
+ };
964
+ }
965
+ const serialized = format.serialize(transformed);
966
+ if (!context.dryRun) {
967
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
968
+ }
969
+ return {
970
+ outcome: {
971
+ changed: true,
972
+ effect: "write",
973
+ detail: rawContent === null ? "create" : "update"
974
+ },
975
+ details
976
+ };
977
+ }
978
+ async function applyTemplateWrite(mutation, context, options) {
979
+ if (!context.templates) {
980
+ throw new Error(
981
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
982
+ );
983
+ }
984
+ const rawPath = resolveValue(mutation.target, options);
985
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
986
+ const details = {
987
+ kind: mutation.kind,
988
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
989
+ targetPath
990
+ };
991
+ const template = await context.templates(mutation.templateId);
992
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
993
+ const rendered = Mustache.render(template, templateContext);
994
+ const existed = await pathExists(context.fs, targetPath);
995
+ if (!context.dryRun) {
996
+ await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
997
+ }
998
+ return {
999
+ outcome: {
1000
+ changed: true,
1001
+ effect: "write",
1002
+ detail: existed ? "update" : "create"
1003
+ },
1004
+ details
1005
+ };
1006
+ }
1007
+ async function applyTemplateMerge(mutation, context, options, formatName) {
1008
+ if (!context.templates) {
1009
+ throw new Error(
1010
+ "Template mutations require a templates loader. Provide templates function to runMutations context."
1011
+ );
1012
+ }
1013
+ const rawPath = resolveValue(mutation.target, options);
1014
+ const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
1015
+ const details = {
1016
+ kind: mutation.kind,
1017
+ label: mutation.label ?? describeMutation(mutation.kind, targetPath),
1018
+ targetPath
1019
+ };
1020
+ const format = getConfigFormat(formatName);
1021
+ const template = await context.templates(mutation.templateId);
1022
+ const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
1023
+ const rendered = Mustache.render(template, templateContext);
1024
+ let templateDoc;
1025
+ try {
1026
+ templateDoc = format.parse(rendered);
1027
+ } catch (error) {
1028
+ throw new Error(
1029
+ `Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
1030
+ { cause: error }
1031
+ );
1032
+ }
1033
+ const rawContent = await readFileIfExists(context.fs, targetPath);
1034
+ let current;
1035
+ try {
1036
+ current = rawContent === null ? {} : format.parse(rawContent);
1037
+ } catch {
1038
+ if (rawContent !== null) {
1039
+ await backupInvalidDocument(context.fs, targetPath, rawContent);
1040
+ }
1041
+ current = {};
1042
+ }
1043
+ const merged = format.merge(current, templateDoc);
1044
+ const serialized = format.serialize(merged);
1045
+ const changed = serialized !== rawContent;
1046
+ if (changed && !context.dryRun) {
1047
+ await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
1048
+ }
1049
+ return {
1050
+ outcome: {
1051
+ changed,
1052
+ effect: changed ? "write" : "none",
1053
+ detail: changed ? rawContent === null ? "create" : "update" : "noop"
1054
+ },
1055
+ details
1056
+ };
1057
+ }
1058
+
1059
+ // packages/config-mutations/src/execution/run-mutations.ts
1060
+ async function runMutations(mutations, context, options) {
1061
+ const effects = [];
1062
+ let anyChanged = false;
1063
+ const resolverOptions = options ?? {};
1064
+ for (const mutation of mutations) {
1065
+ const { outcome } = await executeMutation(
1066
+ mutation,
1067
+ context,
1068
+ resolverOptions
1069
+ );
1070
+ effects.push(outcome);
1071
+ if (outcome.changed) {
1072
+ anyChanged = true;
1073
+ }
1074
+ }
1075
+ return {
1076
+ changed: anyChanged,
1077
+ effects
1078
+ };
1079
+ }
1080
+ async function executeMutation(mutation, context, options) {
1081
+ context.observers?.onStart?.({
1082
+ kind: mutation.kind,
1083
+ label: mutation.label ?? mutation.kind,
1084
+ targetPath: void 0
1085
+ // Will be resolved during apply
1086
+ });
1087
+ try {
1088
+ const { outcome, details } = await applyMutation(mutation, context, options);
1089
+ context.observers?.onComplete?.(details, outcome);
1090
+ return { outcome, details };
1091
+ } catch (error) {
1092
+ context.observers?.onError?.(
1093
+ {
1094
+ kind: mutation.kind,
1095
+ label: mutation.label ?? mutation.kind,
1096
+ targetPath: void 0
1097
+ },
1098
+ error
1099
+ );
1100
+ throw error;
1101
+ }
1102
+ }
1103
+
1104
+ // packages/config-mutations/src/template/render.ts
1105
+ import Mustache2 from "mustache";
1106
+ var originalEscape = Mustache2.escape;
1107
+
1108
+ // src/services/service-install.ts
1109
+ async function runServiceInstall(definition, context) {
1110
+ const checkContext = {
1111
+ isDryRun: context.isDryRun,
1112
+ runCommand: context.runCommand
1113
+ };
1114
+ let needsInstall = false;
1115
+ try {
1116
+ await definition.check.run(checkContext);
1117
+ context.logger(`${definition.summary} already installed.`);
1118
+ } catch (error) {
1119
+ const detail = error instanceof Error ? error.message : String(error);
1120
+ context.logger(`${definition.summary} not detected: ${detail}`);
1121
+ needsInstall = true;
1122
+ }
1123
+ if (!needsInstall) {
1124
+ return false;
1125
+ }
1126
+ if (context.isDryRun) {
1127
+ logInstallDryRun(definition, context);
1128
+ return true;
1129
+ }
1130
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
1131
+ for (const step of platformSteps) {
1132
+ await runInstallStep(step, context);
1133
+ }
1134
+ await definition.check.run(checkContext);
1135
+ if (definition.postChecks) {
1136
+ for (const postCheck of definition.postChecks) {
1137
+ await postCheck.run(checkContext);
1138
+ }
1139
+ }
1140
+ context.logger(
1141
+ definition.successMessage ?? `${definition.summary} installed.`
1142
+ );
1143
+ return true;
1144
+ }
1145
+ function describeInstallCommand(step) {
1146
+ return `[${step.id}] ${formatCommand(step.command, step.args)}`;
1147
+ }
1148
+ function formatCommand(command, args) {
1149
+ return [command, ...args.map(quoteIfNeeded)].join(" ");
1150
+ }
1151
+ function quoteIfNeeded(value) {
1152
+ if (value.length === 0) {
1153
+ return '""';
1154
+ }
1155
+ if (value.includes(" ") || value.includes(" ") || value.includes("\n")) {
1156
+ return `"${value.replaceAll('"', '\\"')}"`;
1157
+ }
1158
+ return value;
1159
+ }
1160
+ function filterStepsByPlatform(steps, platform) {
1161
+ return steps.filter(
1162
+ (step) => !step.platforms || step.platforms.includes(platform)
1163
+ );
1164
+ }
1165
+ function logInstallDryRun(definition, context) {
1166
+ context.logger(`Dry run: would install ${definition.summary}.`);
1167
+ const platformSteps = filterStepsByPlatform(definition.steps, context.platform);
1168
+ for (const step of platformSteps) {
1169
+ context.logger(`Dry run: ${describeInstallCommand(step)}`);
1170
+ }
1171
+ }
1172
+ async function runInstallStep(step, context) {
1173
+ context.logger(`Running ${describeInstallCommand(step)}`);
1174
+ const result = await context.runCommand(step.command, step.args);
1175
+ if (result.exitCode !== 0) {
1176
+ const stderr = result.stderr.trim();
1177
+ const suffix = stderr.length > 0 ? `: ${stderr}` : "";
1178
+ throw new Error(
1179
+ `${describeInstallCommand(step)} failed with exit code ${result.exitCode}${suffix}`
1180
+ );
1181
+ }
1182
+ }
1183
+
1184
+ // src/providers/create-provider.ts
1185
+ var templateImports = {
1186
+ "py-poe-spawn/env.mustache": () => Promise.resolve().then(() => __toESM(require_env(), 1)),
1187
+ "py-poe-spawn/main.py.mustache": () => Promise.resolve().then(() => __toESM(require_main_py(), 1)),
1188
+ "py-poe-spawn/requirements.txt.mustache": () => Promise.resolve().then(() => __toESM(require_requirements_txt(), 1)),
1189
+ "codex/config.toml.mustache": () => Promise.resolve().then(() => __toESM(require_config_toml(), 1)),
1190
+ "tiny-http-mcp-server/server.mjs.mustache": () => Promise.resolve().then(() => __toESM(require_server_mjs(), 1)),
1191
+ "tiny-http-mcp-server/verify-token.mjs.mustache": () => Promise.resolve().then(() => __toESM(require_verify_token_mjs(), 1))
1192
+ };
1193
+ async function loadTemplate(templateId) {
1194
+ const loader = templateImports[templateId];
1195
+ if (!loader) {
1196
+ throw new Error(`Template not found: ${templateId}`);
1197
+ }
1198
+ const module = await loader();
1199
+ return module.default;
1200
+ }
1201
+ function createProvider(opts) {
1202
+ const provider2 = {
1203
+ id: opts.id,
1204
+ summary: opts.summary,
1205
+ name: opts.name,
1206
+ aliases: opts.aliases,
1207
+ label: opts.label,
1208
+ branding: opts.branding,
1209
+ disabled: opts.disabled,
1210
+ supportsStdinPrompt: opts.supportsStdinPrompt,
1211
+ supportsMcpSpawn: opts.supportsMcpSpawn,
1212
+ requiresProvider: opts.requiresProvider ?? true,
1213
+ configurePrompts: opts.configurePrompts,
1214
+ postConfigureMessages: opts.postConfigureMessages,
1215
+ extendConfigurePayload: opts.extendConfigurePayload,
1216
+ isolatedEnv: opts.isolatedEnv,
1217
+ async configure(context, runOptions) {
1218
+ await runMutations(opts.manifest.configure, {
1219
+ fs: context.fs,
1220
+ homeDir: context.env.homeDir,
1221
+ observers: runOptions?.observers,
1222
+ templates: loadTemplate,
1223
+ pathMapper: context.pathMapper
1224
+ }, context.options);
1225
+ context.command.flushDryRun({ emitIfEmpty: false });
1226
+ },
1227
+ async unconfigure(context, runOptions) {
1228
+ if (!opts.manifest.unconfigure) {
1229
+ return false;
1230
+ }
1231
+ const result = await runMutations(opts.manifest.unconfigure, {
1232
+ fs: context.fs,
1233
+ homeDir: context.env.homeDir,
1234
+ observers: runOptions?.observers,
1235
+ templates: loadTemplate,
1236
+ pathMapper: context.pathMapper
1237
+ }, context.options);
1238
+ context.command.flushDryRun({ emitIfEmpty: false });
1239
+ return result.changed;
1240
+ }
1241
+ };
1242
+ if (opts.install) {
1243
+ provider2.install = createInstallRunner(opts.install);
1244
+ }
1245
+ if (opts.test) {
1246
+ provider2.test = opts.test;
1247
+ }
1248
+ if (opts.spawn) {
1249
+ provider2.spawn = opts.spawn;
1250
+ }
1251
+ return provider2;
1252
+ }
1253
+ function createInstallRunner(definition) {
1254
+ return async (context) => {
1255
+ await runServiceInstall(definition, {
1256
+ isDryRun: context.logger.context.dryRun,
1257
+ runCommand: context.command.runCommand,
1258
+ logger: (message) => context.logger.verbose(message),
1259
+ platform: context.env.platform
1260
+ });
1261
+ };
1262
+ }
1263
+
1264
+ // src/providers/tiny-http-mcp-server.ts
1265
+ var SCAFFOLD_DIRECTORY = "~/.poe-code/tiny-http-mcp-server";
1266
+ var CONFIG_FILE = `${SCAFFOLD_DIRECTORY}/config.json`;
1267
+ var SERVER_FILE = `${SCAFFOLD_DIRECTORY}/server.mjs`;
1268
+ var VERIFIER_FILE = `${SCAFFOLD_DIRECTORY}/verify-token.mjs`;
1269
+ var DEFAULT_CONFIG = {
1270
+ name: "oauth-http-server",
1271
+ version: "1.0.0",
1272
+ listen: {
1273
+ hostname: "127.0.0.1",
1274
+ path: "/mcp",
1275
+ port: 3e3
1276
+ },
1277
+ oauth: {
1278
+ resource: "https://example.com/mcp",
1279
+ authorizationServers: ["https://auth.example.com"],
1280
+ scopesSupported: ["mcp.read", "mcp.write"],
1281
+ requiredScopes: ["mcp.read"],
1282
+ bearerMethodsSupported: ["header"],
1283
+ verifierModule: "./verify-token.mjs",
1284
+ verifierExport: "default"
1285
+ }
1286
+ };
1287
+ function normalizeString(value) {
1288
+ return value.trim();
1289
+ }
1290
+ function normalizeStringList(values) {
1291
+ const normalized = [];
1292
+ for (const value of values) {
1293
+ const trimmed = normalizeString(value);
1294
+ if (trimmed.length === 0 || normalized.includes(trimmed)) {
1295
+ continue;
1296
+ }
1297
+ normalized.push(trimmed);
1298
+ }
1299
+ return normalized;
1300
+ }
1301
+ function splitCommaSeparated(value) {
1302
+ return normalizeStringList(value.split(","));
1303
+ }
1304
+ function readStringOption(context, key) {
1305
+ const value = context.commandOptions[key];
1306
+ return typeof value === "string" && value.trim().length > 0 ? normalizeString(value) : void 0;
1307
+ }
1308
+ function readStringArrayOption(context, key) {
1309
+ const value = context.commandOptions[key];
1310
+ if (!Array.isArray(value)) {
1311
+ return void 0;
1312
+ }
1313
+ const normalized = normalizeStringList(
1314
+ value.filter((entry) => typeof entry === "string")
1315
+ );
1316
+ return normalized.length > 0 ? normalized : void 0;
1317
+ }
1318
+ async function promptForValue(context, input) {
1319
+ const response = await context.prompts({
1320
+ name: input.key,
1321
+ message: input.message,
1322
+ type: "text",
1323
+ initial: input.defaultValue
1324
+ });
1325
+ const value = response[input.key];
1326
+ if (typeof value !== "string" || value.trim().length === 0) {
1327
+ throw new Error(`Missing value for ${input.message}.`);
1328
+ }
1329
+ return normalizeString(value);
1330
+ }
1331
+ async function resolveStringField(context, input) {
1332
+ const configured = readStringOption(context, input.optionKey);
1333
+ if (configured !== void 0) {
1334
+ return configured;
1335
+ }
1336
+ if (context.assumeYes) {
1337
+ return input.defaultValue;
1338
+ }
1339
+ return promptForValue(context, {
1340
+ key: input.promptKey,
1341
+ message: input.message,
1342
+ defaultValue: input.defaultValue
1343
+ });
1344
+ }
1345
+ async function resolveStringListField(context, input) {
1346
+ const configured = readStringArrayOption(context, input.optionKey);
1347
+ if (configured !== void 0) {
1348
+ return configured;
1349
+ }
1350
+ if (context.assumeYes) {
1351
+ return [...input.defaultValue];
1352
+ }
1353
+ const response = await promptForValue(context, {
1354
+ key: input.promptKey,
1355
+ message: input.message,
1356
+ defaultValue: input.defaultValue.join(", ")
1357
+ });
1358
+ return splitCommaSeparated(response);
1359
+ }
1360
+ async function resolveTinyHttpMcpServerConfig(context) {
1361
+ return {
1362
+ name: DEFAULT_CONFIG.name,
1363
+ version: DEFAULT_CONFIG.version,
1364
+ listen: { ...DEFAULT_CONFIG.listen },
1365
+ oauth: {
1366
+ resource: await resolveStringField(context, {
1367
+ optionKey: "oauthResource",
1368
+ promptKey: "oauthResource",
1369
+ message: "OAuth protected resource URI",
1370
+ defaultValue: DEFAULT_CONFIG.oauth.resource
1371
+ }),
1372
+ authorizationServers: await resolveStringListField(context, {
1373
+ optionKey: "oauthAuthorizationServer",
1374
+ promptKey: "oauthAuthorizationServers",
1375
+ message: "OAuth authorization server issuers (comma-separated)",
1376
+ defaultValue: DEFAULT_CONFIG.oauth.authorizationServers
1377
+ }),
1378
+ scopesSupported: await resolveStringListField(context, {
1379
+ optionKey: "oauthSupportedScope",
1380
+ promptKey: "oauthSupportedScopes",
1381
+ message: "OAuth supported scopes (comma-separated)",
1382
+ defaultValue: DEFAULT_CONFIG.oauth.scopesSupported
1383
+ }),
1384
+ requiredScopes: await resolveStringListField(context, {
1385
+ optionKey: "oauthRequiredScope",
1386
+ promptKey: "oauthRequiredScopes",
1387
+ message: "OAuth required scopes (comma-separated)",
1388
+ defaultValue: DEFAULT_CONFIG.oauth.requiredScopes
1389
+ }),
1390
+ bearerMethodsSupported: await resolveStringListField(context, {
1391
+ optionKey: "oauthBearerMethod",
1392
+ promptKey: "oauthBearerMethods",
1393
+ message: "OAuth bearer methods (comma-separated)",
1394
+ defaultValue: DEFAULT_CONFIG.oauth.bearerMethodsSupported
1395
+ }),
1396
+ verifierModule: await resolveStringField(context, {
1397
+ optionKey: "oauthVerifierModule",
1398
+ promptKey: "oauthVerifierModule",
1399
+ message: "OAuth verifier module path or specifier",
1400
+ defaultValue: DEFAULT_CONFIG.oauth.verifierModule
1401
+ }),
1402
+ verifierExport: await resolveStringField(context, {
1403
+ optionKey: "oauthVerifierExport",
1404
+ promptKey: "oauthVerifierExport",
1405
+ message: "OAuth verifier export name",
1406
+ defaultValue: DEFAULT_CONFIG.oauth.verifierExport
1407
+ })
1408
+ }
1409
+ };
1410
+ }
1411
+ var tinyHttpMcpServerService = createProvider({
1412
+ id: "tiny-http-mcp-server",
1413
+ name: "tiny-http-mcp-server",
1414
+ label: "tiny-http-mcp-server",
1415
+ summary: "Scaffold an OAuth-protected tiny-http-mcp-server example.",
1416
+ requiresProvider: false,
1417
+ async extendConfigurePayload(context) {
1418
+ return resolveTinyHttpMcpServerConfig(context);
1419
+ },
1420
+ manifest: {
1421
+ configure: [
1422
+ fileMutation.ensureDirectory({ path: SCAFFOLD_DIRECTORY }),
1423
+ configMutation.merge({
1424
+ target: CONFIG_FILE,
1425
+ value: (ctx) => {
1426
+ const options = ctx;
1427
+ return {
1428
+ name: options.name,
1429
+ version: options.version,
1430
+ listen: {
1431
+ hostname: options.listen.hostname,
1432
+ path: options.listen.path,
1433
+ port: options.listen.port
1434
+ },
1435
+ oauth: {
1436
+ resource: options.oauth.resource,
1437
+ authorizationServers: options.oauth.authorizationServers,
1438
+ scopesSupported: options.oauth.scopesSupported,
1439
+ requiredScopes: options.oauth.requiredScopes,
1440
+ bearerMethodsSupported: options.oauth.bearerMethodsSupported,
1441
+ verifierModule: options.oauth.verifierModule,
1442
+ verifierExport: options.oauth.verifierExport
1443
+ }
1444
+ };
1445
+ }
1446
+ }),
1447
+ fileMutation.backup({ target: SERVER_FILE }),
1448
+ templateMutation.write({
1449
+ target: SERVER_FILE,
1450
+ templateId: "tiny-http-mcp-server/server.mjs.mustache"
1451
+ }),
1452
+ fileMutation.backup({ target: VERIFIER_FILE }),
1453
+ templateMutation.write({
1454
+ target: VERIFIER_FILE,
1455
+ templateId: "tiny-http-mcp-server/verify-token.mjs.mustache"
1456
+ })
1457
+ ],
1458
+ unconfigure: [
1459
+ fileMutation.remove({ target: CONFIG_FILE }),
1460
+ fileMutation.remove({ target: SERVER_FILE }),
1461
+ fileMutation.remove({ target: VERIFIER_FILE }),
1462
+ fileMutation.removeDirectory({ path: SCAFFOLD_DIRECTORY })
1463
+ ]
1464
+ }
1465
+ });
1466
+ var provider = tinyHttpMcpServerService;
1467
+ export {
1468
+ provider,
1469
+ tinyHttpMcpServerService
1470
+ };
1471
+ //# sourceMappingURL=tiny-http-mcp-server.js.map