poe-code 3.0.196 → 3.0.198
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/configure.d.ts +0 -7
- package/dist/cli/commands/configure.js +11 -14
- package/dist/cli/commands/configure.js.map +1 -1
- package/dist/cli/commands/provider.js +8 -1
- package/dist/cli/commands/provider.js.map +1 -1
- package/dist/index.js +263 -269
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code.js +6 -20
- package/dist/providers/claude-code.js.map +3 -3
- package/dist/providers/codex.js +6 -20
- package/dist/providers/codex.js.map +3 -3
- package/dist/providers/create-provider.js +0 -2
- package/dist/providers/create-provider.js.map +1 -1
- package/dist/providers/goose.js +14 -25
- package/dist/providers/goose.js.map +3 -3
- package/dist/providers/kimi.js +6 -20
- package/dist/providers/kimi.js.map +3 -3
- package/dist/providers/opencode.js +6 -20
- package/dist/providers/opencode.js.map +3 -3
- package/dist/providers/poe-agent.js +66 -84
- package/dist/providers/poe-agent.js.map +4 -4
- package/dist/utils/command-checks.d.ts +2 -1
- package/dist/utils/command-checks.js +3 -1
- package/dist/utils/command-checks.js.map +1 -1
- package/package.json +4 -1
- package/packages/memory/dist/cache.js +1 -1
- package/packages/memory/dist/explain.js +1 -1
- package/packages/memory/dist/index.js +18 -7
- package/packages/memory/dist/index.js.map +3 -3
- package/packages/memory/dist/query.js +1 -1
- package/packages/memory/dist/tokens.js +1 -1
- package/packages/superintendent/dist/cli.d.ts +2 -0
- package/packages/superintendent/dist/cli.js +41 -0
- package/packages/superintendent/dist/commands/builder-group.d.ts +52 -0
- package/packages/superintendent/dist/commands/builder-group.js +73 -0
- package/packages/superintendent/dist/commands/complete.d.ts +19 -0
- package/packages/superintendent/dist/commands/complete.js +54 -0
- package/packages/superintendent/dist/commands/index.d.ts +4 -0
- package/packages/superintendent/dist/commands/index.js +4 -0
- package/packages/superintendent/dist/commands/inspector-group.d.ts +115 -0
- package/packages/superintendent/dist/commands/inspector-group.js +133 -0
- package/packages/superintendent/dist/commands/install.d.ts +31 -0
- package/packages/superintendent/dist/commands/install.js +148 -0
- package/packages/superintendent/dist/commands/plan-path.d.ts +9 -0
- package/packages/superintendent/dist/commands/plan-path.js +40 -0
- package/packages/superintendent/dist/commands/poe-agent-runner.d.ts +5 -0
- package/packages/superintendent/dist/commands/poe-agent-runner.js +27 -0
- package/packages/superintendent/dist/commands/run.d.ts +86 -0
- package/packages/superintendent/dist/commands/run.js +945 -0
- package/packages/superintendent/dist/commands/superintendent-group.d.ts +325 -0
- package/packages/superintendent/dist/commands/superintendent-group.js +238 -0
- package/packages/superintendent/dist/config-scope.d.ts +8 -0
- package/packages/superintendent/dist/config-scope.js +9 -0
- package/packages/superintendent/dist/direct-execution.d.ts +1 -0
- package/packages/superintendent/dist/direct-execution.js +20 -0
- package/packages/superintendent/dist/document/parse.d.ts +59 -0
- package/packages/superintendent/dist/document/parse.js +409 -0
- package/packages/superintendent/dist/document/tasks.d.ts +12 -0
- package/packages/superintendent/dist/document/tasks.js +96 -0
- package/packages/superintendent/dist/document/write.d.ts +6 -0
- package/packages/superintendent/dist/document/write.js +156 -0
- package/packages/superintendent/dist/index.d.ts +12 -0
- package/packages/superintendent/dist/index.js +15 -0
- package/packages/superintendent/dist/mcp.d.ts +24 -0
- package/packages/superintendent/dist/mcp.js +202 -0
- package/packages/superintendent/dist/runtime/agentic-tools.d.ts +33 -0
- package/packages/superintendent/dist/runtime/agentic-tools.js +74 -0
- package/packages/superintendent/dist/runtime/loop.d.ts +88 -0
- package/packages/superintendent/dist/runtime/loop.js +446 -0
- package/packages/superintendent/dist/runtime/resolve-cwd.d.ts +2 -0
- package/packages/superintendent/dist/runtime/resolve-cwd.js +10 -0
- package/packages/superintendent/dist/runtime/run-builder.d.ts +13 -0
- package/packages/superintendent/dist/runtime/run-builder.js +102 -0
- package/packages/superintendent/dist/runtime/run-inspector.d.ts +16 -0
- package/packages/superintendent/dist/runtime/run-inspector.js +119 -0
- package/packages/superintendent/dist/runtime/run-owner-review.d.ts +18 -0
- package/packages/superintendent/dist/runtime/run-owner-review.js +208 -0
- package/packages/superintendent/dist/runtime/run-superintendent.d.ts +13 -0
- package/packages/superintendent/dist/runtime/run-superintendent.js +208 -0
- package/packages/superintendent/dist/runtime/system-prompt.d.ts +17 -0
- package/packages/superintendent/dist/runtime/system-prompt.js +54 -0
- package/packages/superintendent/dist/runtime/templates.d.ts +22 -0
- package/packages/superintendent/dist/runtime/templates.js +23 -0
- package/packages/superintendent/dist/runtime/types.d.ts +4 -0
- package/packages/superintendent/dist/runtime/types.js +1 -0
- package/packages/superintendent/dist/runtime/workflow-tool.d.ts +29 -0
- package/packages/superintendent/dist/runtime/workflow-tool.js +83 -0
- package/packages/superintendent/dist/state/machine.d.ts +14 -0
- package/packages/superintendent/dist/state/machine.js +53 -0
- package/packages/superintendent/dist/templates/SKILL_superintendent.md +193 -0
- package/packages/superintendent/dist/testing/index.d.ts +2 -0
- package/packages/superintendent/dist/testing/index.js +1 -0
- package/packages/superintendent/dist/testing/simulation.d.ts +57 -0
- package/packages/superintendent/dist/testing/simulation.js +346 -0
- package/dist/providers/tiny-http-mcp-server.d.ts +0 -22
- package/dist/providers/tiny-http-mcp-server.js +0 -1471
- package/dist/providers/tiny-http-mcp-server.js.map +0 -7
|
@@ -1,1471 +0,0 @@
|
|
|
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
|