terminal-pilot 0.0.6 → 0.0.7
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.js +5708 -1261
- package/dist/cli.js.map +4 -4
- package/dist/commands/close-session.js +249 -4
- package/dist/commands/close-session.js.map +4 -4
- package/dist/commands/create-session.js +249 -4
- package/dist/commands/create-session.js.map +4 -4
- package/dist/commands/fill.js +249 -4
- package/dist/commands/fill.js.map +4 -4
- package/dist/commands/get-session.js +249 -4
- package/dist/commands/get-session.js.map +4 -4
- package/dist/commands/index.js +2376 -184
- package/dist/commands/index.js.map +4 -4
- package/dist/commands/install.js +1377 -16
- package/dist/commands/install.js.map +4 -4
- package/dist/commands/installer.js +181 -15
- package/dist/commands/installer.js.map +4 -4
- package/dist/commands/list-sessions.js +249 -4
- package/dist/commands/list-sessions.js.map +4 -4
- package/dist/commands/press-key.js +249 -4
- package/dist/commands/press-key.js.map +4 -4
- package/dist/commands/read-history.js +249 -4
- package/dist/commands/read-history.js.map +4 -4
- package/dist/commands/read-screen.js +249 -4
- package/dist/commands/read-screen.js.map +4 -4
- package/dist/commands/resize.js +249 -4
- package/dist/commands/resize.js.map +4 -4
- package/dist/commands/runtime.js +8 -2
- package/dist/commands/runtime.js.map +3 -3
- package/dist/commands/screenshot.js +1014 -8
- package/dist/commands/screenshot.js.map +4 -4
- package/dist/commands/send-signal.js +249 -4
- package/dist/commands/send-signal.js.map +4 -4
- package/dist/commands/type.js +249 -4
- package/dist/commands/type.js.map +4 -4
- package/dist/commands/uninstall.js +421 -16
- package/dist/commands/uninstall.js.map +4 -4
- package/dist/commands/wait-for-exit.js +249 -4
- package/dist/commands/wait-for-exit.js.map +4 -4
- package/dist/commands/wait-for.js +249 -4
- package/dist/commands/wait-for.js.map +4 -4
- package/dist/testing/cli-repl.js +5633 -1185
- package/dist/testing/cli-repl.js.map +4 -4
- package/dist/testing/qa-cli.js +5674 -1226
- package/dist/testing/qa-cli.js.map +4 -4
- package/package.json +17 -8
package/dist/commands/index.js
CHANGED
|
@@ -1,11 +1,342 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
1
|
+
// ../cmdkit/src/index.ts
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
3
|
|
|
4
|
-
// src/
|
|
5
|
-
|
|
4
|
+
// ../cmdkit-schema/src/index.ts
|
|
5
|
+
function assertValidEnumValues(values) {
|
|
6
|
+
if (values.length === 0) {
|
|
7
|
+
throw new Error("Enum schema requires at least one value");
|
|
8
|
+
}
|
|
9
|
+
const uniqueValues = new Set(values);
|
|
10
|
+
if (uniqueValues.size !== values.length) {
|
|
11
|
+
throw new Error("Enum schema values must be unique");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
var S = {
|
|
15
|
+
String(options = {}) {
|
|
16
|
+
return {
|
|
17
|
+
kind: "string",
|
|
18
|
+
...options
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
Number(options = {}) {
|
|
22
|
+
return {
|
|
23
|
+
kind: "number",
|
|
24
|
+
...options
|
|
25
|
+
};
|
|
26
|
+
},
|
|
27
|
+
Boolean(options = {}) {
|
|
28
|
+
return {
|
|
29
|
+
kind: "boolean",
|
|
30
|
+
...options
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
Enum(values, options = {}) {
|
|
34
|
+
assertValidEnumValues(values);
|
|
35
|
+
return {
|
|
36
|
+
kind: "enum",
|
|
37
|
+
values,
|
|
38
|
+
...options
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
Array(item, options = {}) {
|
|
42
|
+
return {
|
|
43
|
+
kind: "array",
|
|
44
|
+
item,
|
|
45
|
+
...options
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
Object(shape) {
|
|
49
|
+
return {
|
|
50
|
+
kind: "object",
|
|
51
|
+
shape
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
Optional(inner) {
|
|
55
|
+
return {
|
|
56
|
+
kind: "optional",
|
|
57
|
+
inner
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
};
|
|
6
61
|
|
|
7
|
-
// src/
|
|
8
|
-
|
|
62
|
+
// ../cmdkit/src/index.ts
|
|
63
|
+
var commandConfigSymbol = /* @__PURE__ */ Symbol("cmdkit.command.config");
|
|
64
|
+
var groupConfigSymbol = /* @__PURE__ */ Symbol("cmdkit.group.config");
|
|
65
|
+
var commandSourcePathSymbol = /* @__PURE__ */ Symbol("cmdkit.command.sourcePath");
|
|
66
|
+
var UserError = class extends Error {
|
|
67
|
+
constructor(message) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "UserError";
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
function cloneScope(scope) {
|
|
73
|
+
return scope === void 0 ? void 0 : [...scope];
|
|
74
|
+
}
|
|
75
|
+
function cloneSecretDefinition(secret) {
|
|
76
|
+
return {
|
|
77
|
+
env: secret.env,
|
|
78
|
+
description: secret.description,
|
|
79
|
+
optional: secret.optional
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function cloneSecrets(secrets) {
|
|
83
|
+
if (secrets === void 0) {
|
|
84
|
+
return {};
|
|
85
|
+
}
|
|
86
|
+
return Object.fromEntries(
|
|
87
|
+
Object.entries(secrets).map(([key, secret]) => [key, cloneSecretDefinition(secret)])
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
function cloneRequires(requires) {
|
|
91
|
+
if (requires === void 0) {
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
auth: requires.auth,
|
|
96
|
+
apiVersion: requires.apiVersion,
|
|
97
|
+
check: requires.check
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function parseStackPath(candidate) {
|
|
101
|
+
if (candidate.startsWith("file://")) {
|
|
102
|
+
try {
|
|
103
|
+
return fileURLToPath(candidate);
|
|
104
|
+
} catch {
|
|
105
|
+
return void 0;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (candidate.startsWith("/")) {
|
|
109
|
+
return candidate;
|
|
110
|
+
}
|
|
111
|
+
return void 0;
|
|
112
|
+
}
|
|
113
|
+
function extractStackPath(line) {
|
|
114
|
+
const trimmed = line.trim();
|
|
115
|
+
const fileIndex = trimmed.indexOf("file://");
|
|
116
|
+
if (fileIndex >= 0) {
|
|
117
|
+
const location2 = trimmed.slice(fileIndex);
|
|
118
|
+
const separatorIndex2 = location2.lastIndexOf(":");
|
|
119
|
+
const previousSeparatorIndex2 = separatorIndex2 >= 0 ? location2.lastIndexOf(":", separatorIndex2 - 1) : -1;
|
|
120
|
+
const candidate2 = separatorIndex2 >= 0 && previousSeparatorIndex2 >= 0 ? location2.slice(0, previousSeparatorIndex2) : location2;
|
|
121
|
+
return parseStackPath(candidate2);
|
|
122
|
+
}
|
|
123
|
+
const slashIndex = trimmed.indexOf("/");
|
|
124
|
+
if (slashIndex < 0) {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
const location = trimmed.slice(slashIndex);
|
|
128
|
+
const separatorIndex = location.lastIndexOf(":");
|
|
129
|
+
const previousSeparatorIndex = separatorIndex >= 0 ? location.lastIndexOf(":", separatorIndex - 1) : -1;
|
|
130
|
+
const candidate = separatorIndex >= 0 && previousSeparatorIndex >= 0 ? location.slice(0, previousSeparatorIndex) : location;
|
|
131
|
+
return parseStackPath(candidate);
|
|
132
|
+
}
|
|
133
|
+
function inferCommandSourcePath() {
|
|
134
|
+
const stack = new Error().stack;
|
|
135
|
+
if (typeof stack !== "string") {
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
for (const line of stack.split("\n").slice(1)) {
|
|
139
|
+
const candidate = extractStackPath(line);
|
|
140
|
+
if (candidate === void 0) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (candidate.includes("/packages/cmdkit/src/index.ts") || candidate.includes("/packages/cmdkit/dist/index.js")) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
return candidate;
|
|
147
|
+
}
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
function composeChecks(parentCheck, childCheck) {
|
|
151
|
+
if (parentCheck === void 0) {
|
|
152
|
+
return childCheck;
|
|
153
|
+
}
|
|
154
|
+
if (childCheck === void 0) {
|
|
155
|
+
return parentCheck;
|
|
156
|
+
}
|
|
157
|
+
return async (ctx) => {
|
|
158
|
+
const parentResult = await parentCheck(ctx);
|
|
159
|
+
if (!parentResult.ok) {
|
|
160
|
+
return parentResult;
|
|
161
|
+
}
|
|
162
|
+
return childCheck(ctx);
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
function mergeRequires(parent, child) {
|
|
166
|
+
if (parent === void 0 && child === void 0) {
|
|
167
|
+
return void 0;
|
|
168
|
+
}
|
|
169
|
+
const merged = {
|
|
170
|
+
auth: child?.auth ?? parent?.auth,
|
|
171
|
+
apiVersion: child?.apiVersion ?? parent?.apiVersion,
|
|
172
|
+
check: composeChecks(parent?.check, child?.check)
|
|
173
|
+
};
|
|
174
|
+
if (merged.auth === void 0 && merged.apiVersion === void 0 && merged.check === void 0) {
|
|
175
|
+
return void 0;
|
|
176
|
+
}
|
|
177
|
+
return merged;
|
|
178
|
+
}
|
|
179
|
+
function mergeSecrets(parent, child) {
|
|
180
|
+
return cloneSecrets({
|
|
181
|
+
...parent,
|
|
182
|
+
...child
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function resolveCommandScope(ownScope, inheritedScope) {
|
|
186
|
+
return cloneScope(ownScope ?? inheritedScope) ?? ["cli", "sdk"];
|
|
187
|
+
}
|
|
188
|
+
function resolveGroupScope(ownScope, inheritedScope) {
|
|
189
|
+
return cloneScope(ownScope ?? inheritedScope);
|
|
190
|
+
}
|
|
191
|
+
function createBaseCommand(config) {
|
|
192
|
+
const command = {
|
|
193
|
+
kind: "command",
|
|
194
|
+
name: config.name,
|
|
195
|
+
description: config.description,
|
|
196
|
+
aliases: [...config.aliases ?? []],
|
|
197
|
+
positional: [...config.positional ?? []],
|
|
198
|
+
params: config.params,
|
|
199
|
+
secrets: cloneSecrets(config.secrets),
|
|
200
|
+
scope: resolveCommandScope(config.scope, void 0),
|
|
201
|
+
confirm: config.confirm ?? false,
|
|
202
|
+
requires: cloneRequires(config.requires),
|
|
203
|
+
handler: config.handler,
|
|
204
|
+
render: config.render
|
|
205
|
+
};
|
|
206
|
+
Object.defineProperty(command, commandConfigSymbol, {
|
|
207
|
+
value: {
|
|
208
|
+
scope: cloneScope(config.scope),
|
|
209
|
+
secrets: cloneSecrets(config.secrets),
|
|
210
|
+
requires: cloneRequires(config.requires),
|
|
211
|
+
sourcePath: inferCommandSourcePath()
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
return command;
|
|
215
|
+
}
|
|
216
|
+
function createBaseGroup(config) {
|
|
217
|
+
const group = {
|
|
218
|
+
kind: "group",
|
|
219
|
+
name: config.name,
|
|
220
|
+
description: config.description,
|
|
221
|
+
aliases: [...config.aliases ?? []],
|
|
222
|
+
scope: resolveGroupScope(config.scope, void 0),
|
|
223
|
+
secrets: cloneSecrets(config.secrets),
|
|
224
|
+
requires: cloneRequires(config.requires),
|
|
225
|
+
children: [],
|
|
226
|
+
default: void 0
|
|
227
|
+
};
|
|
228
|
+
Object.defineProperty(group, groupConfigSymbol, {
|
|
229
|
+
value: {
|
|
230
|
+
scope: cloneScope(config.scope),
|
|
231
|
+
secrets: cloneSecrets(config.secrets),
|
|
232
|
+
requires: cloneRequires(config.requires),
|
|
233
|
+
children: [...config.children],
|
|
234
|
+
default: config.default
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
return group;
|
|
238
|
+
}
|
|
239
|
+
function getInternalCommandConfig(command) {
|
|
240
|
+
return command[commandConfigSymbol];
|
|
241
|
+
}
|
|
242
|
+
function getInternalGroupConfig(group) {
|
|
243
|
+
return group[groupConfigSymbol];
|
|
244
|
+
}
|
|
245
|
+
function materializeCommand(command, inherited) {
|
|
246
|
+
const internal = getInternalCommandConfig(command);
|
|
247
|
+
const materialized = {
|
|
248
|
+
kind: "command",
|
|
249
|
+
name: command.name,
|
|
250
|
+
description: command.description,
|
|
251
|
+
aliases: [...command.aliases],
|
|
252
|
+
positional: [...command.positional],
|
|
253
|
+
params: command.params,
|
|
254
|
+
secrets: mergeSecrets(inherited.secrets, internal.secrets),
|
|
255
|
+
scope: resolveCommandScope(internal.scope, inherited.scope),
|
|
256
|
+
confirm: command.confirm,
|
|
257
|
+
requires: mergeRequires(inherited.requires, internal.requires),
|
|
258
|
+
handler: command.handler,
|
|
259
|
+
render: command.render
|
|
260
|
+
};
|
|
261
|
+
Object.defineProperty(materialized, commandConfigSymbol, {
|
|
262
|
+
value: {
|
|
263
|
+
scope: cloneScope(internal.scope),
|
|
264
|
+
secrets: cloneSecrets(internal.secrets),
|
|
265
|
+
requires: cloneRequires(internal.requires),
|
|
266
|
+
sourcePath: internal.sourcePath
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
Object.defineProperty(materialized, commandSourcePathSymbol, {
|
|
270
|
+
value: internal.sourcePath
|
|
271
|
+
});
|
|
272
|
+
return materialized;
|
|
273
|
+
}
|
|
274
|
+
function materializeGroup(group, inherited) {
|
|
275
|
+
const internal = getInternalGroupConfig(group);
|
|
276
|
+
const scope = resolveGroupScope(internal.scope, inherited.scope);
|
|
277
|
+
const secrets = mergeSecrets(inherited.secrets, internal.secrets);
|
|
278
|
+
const requires = mergeRequires(inherited.requires, internal.requires);
|
|
279
|
+
const materializedChildren = internal.children.map(
|
|
280
|
+
(child) => materializeNode(child, {
|
|
281
|
+
scope,
|
|
282
|
+
secrets,
|
|
283
|
+
requires
|
|
284
|
+
})
|
|
285
|
+
);
|
|
286
|
+
let defaultChild;
|
|
287
|
+
if (internal.default !== void 0) {
|
|
288
|
+
const defaultIndex = internal.children.indexOf(internal.default);
|
|
289
|
+
if (defaultIndex === -1) {
|
|
290
|
+
throw new UserError(`Default command "${internal.default.name}" must be listed in children.`);
|
|
291
|
+
}
|
|
292
|
+
const resolvedDefault = materializedChildren[defaultIndex];
|
|
293
|
+
if (resolvedDefault?.kind !== "command") {
|
|
294
|
+
throw new UserError(`Default child "${internal.default.name}" must be a command.`);
|
|
295
|
+
}
|
|
296
|
+
defaultChild = resolvedDefault;
|
|
297
|
+
}
|
|
298
|
+
const materialized = {
|
|
299
|
+
kind: "group",
|
|
300
|
+
name: group.name,
|
|
301
|
+
description: group.description,
|
|
302
|
+
aliases: [...group.aliases],
|
|
303
|
+
scope,
|
|
304
|
+
secrets,
|
|
305
|
+
requires,
|
|
306
|
+
children: materializedChildren,
|
|
307
|
+
default: defaultChild
|
|
308
|
+
};
|
|
309
|
+
Object.defineProperty(materialized, groupConfigSymbol, {
|
|
310
|
+
value: {
|
|
311
|
+
scope: cloneScope(internal.scope),
|
|
312
|
+
secrets: cloneSecrets(internal.secrets),
|
|
313
|
+
requires: cloneRequires(internal.requires),
|
|
314
|
+
children: [...internal.children],
|
|
315
|
+
default: internal.default
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
return materialized;
|
|
319
|
+
}
|
|
320
|
+
function materializeNode(node, inherited) {
|
|
321
|
+
if (node.kind === "command") {
|
|
322
|
+
return materializeCommand(node, inherited);
|
|
323
|
+
}
|
|
324
|
+
return materializeGroup(node, inherited);
|
|
325
|
+
}
|
|
326
|
+
function defineCommand(config) {
|
|
327
|
+
return materializeCommand(createBaseCommand(config), {
|
|
328
|
+
scope: void 0,
|
|
329
|
+
secrets: {},
|
|
330
|
+
requires: void 0
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
function defineGroup(config) {
|
|
334
|
+
return materializeGroup(createBaseGroup(config), {
|
|
335
|
+
scope: void 0,
|
|
336
|
+
secrets: {},
|
|
337
|
+
requires: void 0
|
|
338
|
+
});
|
|
339
|
+
}
|
|
9
340
|
|
|
10
341
|
// src/terminal-pilot.ts
|
|
11
342
|
import { randomUUID } from "node:crypto";
|
|
@@ -1263,17 +1594,16 @@ var closeSession = defineCommand({
|
|
|
1263
1594
|
});
|
|
1264
1595
|
|
|
1265
1596
|
// src/commands/create-session.ts
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
observe: S2.Optional(S2.Boolean({ description: "Mirror PTY output to stderr" }))
|
|
1597
|
+
var params2 = S.Object({
|
|
1598
|
+
command: S.String({ description: "Command to execute" }),
|
|
1599
|
+
args: S.Optional(S.Array(S.String(), { description: "Command arguments" })),
|
|
1600
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" })),
|
|
1601
|
+
cwd: S.Optional(S.String({ description: "Working directory" })),
|
|
1602
|
+
cols: S.Optional(S.Number({ description: "Terminal width in columns" })),
|
|
1603
|
+
rows: S.Optional(S.Number({ description: "Terminal height in rows" })),
|
|
1604
|
+
observe: S.Optional(S.Boolean({ description: "Mirror PTY output to stderr" }))
|
|
1275
1605
|
});
|
|
1276
|
-
var createSession =
|
|
1606
|
+
var createSession = defineCommand({
|
|
1277
1607
|
name: "create-session",
|
|
1278
1608
|
description: "Spawn an interactive CLI in a PTY",
|
|
1279
1609
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1286,12 +1616,11 @@ var createSession = defineCommand2({
|
|
|
1286
1616
|
});
|
|
1287
1617
|
|
|
1288
1618
|
// src/commands/fill.ts
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
session: S3.Optional(S3.String({ short: "s", description: "Session name" }))
|
|
1619
|
+
var params3 = S.Object({
|
|
1620
|
+
text: S.String({ description: "Text to write to the session" }),
|
|
1621
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" }))
|
|
1293
1622
|
});
|
|
1294
|
-
var fill =
|
|
1623
|
+
var fill = defineCommand({
|
|
1295
1624
|
name: "fill",
|
|
1296
1625
|
description: "Write text to an active terminal session all at once (replaces \\n with \\r)",
|
|
1297
1626
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1305,11 +1634,10 @@ var fill = defineCommand3({
|
|
|
1305
1634
|
});
|
|
1306
1635
|
|
|
1307
1636
|
// src/commands/get-session.ts
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
session: S4.Optional(S4.String({ short: "s", description: "Session name" }))
|
|
1637
|
+
var params4 = S.Object({
|
|
1638
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" }))
|
|
1311
1639
|
});
|
|
1312
|
-
var getSession =
|
|
1640
|
+
var getSession = defineCommand({
|
|
1313
1641
|
name: "get-session",
|
|
1314
1642
|
description: "Get session metadata (name, pid, command, exitCode)",
|
|
1315
1643
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1325,131 +1653,1245 @@ var getSession = defineCommand4({
|
|
|
1325
1653
|
}
|
|
1326
1654
|
});
|
|
1327
1655
|
|
|
1328
|
-
// src/
|
|
1329
|
-
import { installSkill } from "@poe-code/agent-skill-config";
|
|
1330
|
-
import { defineCommand as defineCommand5, S as S5 } from "@poe-code/cmdkit";
|
|
1331
|
-
|
|
1332
|
-
// src/commands/installer.ts
|
|
1656
|
+
// ../agent-skill-config/src/configs.ts
|
|
1333
1657
|
import os from "node:os";
|
|
1334
1658
|
import path from "node:path";
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1659
|
+
|
|
1660
|
+
// ../agent-defs/src/agents/claude-code.ts
|
|
1661
|
+
var claudeCodeAgent = {
|
|
1662
|
+
id: "claude-code",
|
|
1663
|
+
name: "claude-code",
|
|
1664
|
+
label: "Claude Code",
|
|
1665
|
+
summary: "Configure Claude Code to route through Poe.",
|
|
1666
|
+
aliases: ["claude"],
|
|
1667
|
+
binaryName: "claude",
|
|
1668
|
+
configPath: "~/.claude/settings.json",
|
|
1669
|
+
branding: {
|
|
1670
|
+
colors: {
|
|
1671
|
+
dark: "#C15F3C",
|
|
1672
|
+
light: "#C15F3C"
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
|
|
1677
|
+
// ../agent-defs/src/agents/claude-desktop.ts
|
|
1678
|
+
var claudeDesktopAgent = {
|
|
1679
|
+
id: "claude-desktop",
|
|
1680
|
+
name: "claude-desktop",
|
|
1681
|
+
label: "Claude Desktop",
|
|
1682
|
+
summary: "Anthropic's official desktop application for Claude",
|
|
1683
|
+
configPath: "~/.claude/settings.json",
|
|
1684
|
+
branding: {
|
|
1685
|
+
colors: {
|
|
1686
|
+
dark: "#D97757",
|
|
1687
|
+
light: "#D97757"
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
// ../agent-defs/src/agents/codex.ts
|
|
1693
|
+
var codexAgent = {
|
|
1694
|
+
id: "codex",
|
|
1695
|
+
name: "codex",
|
|
1696
|
+
label: "Codex",
|
|
1697
|
+
summary: "Configure Codex to use Poe as the model provider.",
|
|
1698
|
+
binaryName: "codex",
|
|
1699
|
+
configPath: "~/.codex/config.toml",
|
|
1700
|
+
branding: {
|
|
1701
|
+
colors: {
|
|
1702
|
+
dark: "#D5D9DF",
|
|
1703
|
+
light: "#7A7F86"
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
|
|
1708
|
+
// ../agent-defs/src/agents/opencode.ts
|
|
1709
|
+
var openCodeAgent = {
|
|
1710
|
+
id: "opencode",
|
|
1711
|
+
name: "opencode",
|
|
1712
|
+
label: "OpenCode CLI",
|
|
1713
|
+
summary: "Configure OpenCode CLI to use the Poe API.",
|
|
1714
|
+
binaryName: "opencode",
|
|
1715
|
+
configPath: "~/.config/opencode/config.json",
|
|
1716
|
+
branding: {
|
|
1717
|
+
colors: {
|
|
1718
|
+
dark: "#4A4F55",
|
|
1719
|
+
light: "#2F3338"
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
};
|
|
1723
|
+
|
|
1724
|
+
// ../agent-defs/src/agents/kimi.ts
|
|
1725
|
+
var kimiAgent = {
|
|
1726
|
+
id: "kimi",
|
|
1727
|
+
name: "kimi",
|
|
1728
|
+
label: "Kimi",
|
|
1729
|
+
summary: "Configure Kimi CLI to use Poe API",
|
|
1730
|
+
aliases: ["kimi-cli"],
|
|
1731
|
+
binaryName: "kimi",
|
|
1732
|
+
configPath: "~/.kimi/config.toml",
|
|
1733
|
+
branding: {
|
|
1734
|
+
colors: {
|
|
1735
|
+
dark: "#7B68EE",
|
|
1736
|
+
light: "#6A5ACD"
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
};
|
|
1740
|
+
|
|
1741
|
+
// ../agent-defs/src/registry.ts
|
|
1742
|
+
var allAgents = [
|
|
1743
|
+
claudeCodeAgent,
|
|
1744
|
+
claudeDesktopAgent,
|
|
1745
|
+
codexAgent,
|
|
1746
|
+
openCodeAgent,
|
|
1747
|
+
kimiAgent
|
|
1748
|
+
];
|
|
1749
|
+
var lookup = /* @__PURE__ */ new Map();
|
|
1750
|
+
for (const agent of allAgents) {
|
|
1751
|
+
const values = [agent.id, agent.name, ...agent.aliases ?? []];
|
|
1752
|
+
for (const value of values) {
|
|
1753
|
+
const normalized = value.toLowerCase();
|
|
1754
|
+
if (!lookup.has(normalized)) {
|
|
1755
|
+
lookup.set(normalized, agent.id);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1349
1758
|
}
|
|
1350
|
-
function
|
|
1759
|
+
function resolveAgentId(input) {
|
|
1760
|
+
if (!input) {
|
|
1761
|
+
return void 0;
|
|
1762
|
+
}
|
|
1763
|
+
return lookup.get(input.toLowerCase());
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// ../agent-skill-config/src/configs.ts
|
|
1767
|
+
var agentSkillConfigs = {
|
|
1768
|
+
"claude-code": {
|
|
1769
|
+
globalSkillDir: "~/.claude/skills",
|
|
1770
|
+
localSkillDir: ".claude/skills"
|
|
1771
|
+
},
|
|
1772
|
+
codex: {
|
|
1773
|
+
globalSkillDir: "~/.codex/skills",
|
|
1774
|
+
localSkillDir: ".codex/skills"
|
|
1775
|
+
},
|
|
1776
|
+
opencode: {
|
|
1777
|
+
globalSkillDir: "~/.config/opencode/skills",
|
|
1778
|
+
localSkillDir: ".opencode/skills"
|
|
1779
|
+
}
|
|
1780
|
+
};
|
|
1781
|
+
var supportedAgents = Object.keys(agentSkillConfigs);
|
|
1782
|
+
function resolveAgentSupport(input, registry = agentSkillConfigs) {
|
|
1783
|
+
const resolvedId = resolveAgentId(input);
|
|
1784
|
+
if (!resolvedId) {
|
|
1785
|
+
return { status: "unknown", input };
|
|
1786
|
+
}
|
|
1787
|
+
const config = registry[resolvedId];
|
|
1788
|
+
if (!config) {
|
|
1789
|
+
return { status: "unsupported", input, id: resolvedId };
|
|
1790
|
+
}
|
|
1791
|
+
return { status: "supported", input, id: resolvedId, config };
|
|
1792
|
+
}
|
|
1793
|
+
function getAgentConfig(agentId) {
|
|
1794
|
+
const support = resolveAgentSupport(agentId);
|
|
1795
|
+
return support.status === "supported" ? support.config : void 0;
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
// ../config-mutations/src/mutations/file-mutation.ts
|
|
1799
|
+
function ensureDirectory(options) {
|
|
1351
1800
|
return {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
platform: installer?.platform ?? process.platform
|
|
1801
|
+
kind: "ensureDirectory",
|
|
1802
|
+
path: options.path,
|
|
1803
|
+
label: options.label
|
|
1356
1804
|
};
|
|
1357
1805
|
}
|
|
1358
|
-
function
|
|
1359
|
-
|
|
1806
|
+
function remove(options) {
|
|
1807
|
+
return {
|
|
1808
|
+
kind: "removeFile",
|
|
1809
|
+
target: options.target,
|
|
1810
|
+
whenEmpty: options.whenEmpty,
|
|
1811
|
+
whenContentMatches: options.whenContentMatches,
|
|
1812
|
+
label: options.label
|
|
1813
|
+
};
|
|
1360
1814
|
}
|
|
1361
|
-
function
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1815
|
+
function removeDirectory(options) {
|
|
1816
|
+
return {
|
|
1817
|
+
kind: "removeDirectory",
|
|
1818
|
+
path: options.path,
|
|
1819
|
+
force: options.force,
|
|
1820
|
+
label: options.label
|
|
1821
|
+
};
|
|
1367
1822
|
}
|
|
1368
|
-
function
|
|
1369
|
-
|
|
1370
|
-
|
|
1823
|
+
function chmod(options) {
|
|
1824
|
+
return {
|
|
1825
|
+
kind: "chmod",
|
|
1826
|
+
target: options.target,
|
|
1827
|
+
mode: options.mode,
|
|
1828
|
+
label: options.label
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
function backup(options) {
|
|
1832
|
+
return {
|
|
1833
|
+
kind: "backup",
|
|
1834
|
+
target: options.target,
|
|
1835
|
+
label: options.label
|
|
1836
|
+
};
|
|
1837
|
+
}
|
|
1838
|
+
var fileMutation = {
|
|
1839
|
+
ensureDirectory,
|
|
1840
|
+
remove,
|
|
1841
|
+
removeDirectory,
|
|
1842
|
+
chmod,
|
|
1843
|
+
backup
|
|
1844
|
+
};
|
|
1845
|
+
|
|
1846
|
+
// ../config-mutations/src/mutations/template-mutation.ts
|
|
1847
|
+
function write(options) {
|
|
1848
|
+
return {
|
|
1849
|
+
kind: "templateWrite",
|
|
1850
|
+
target: options.target,
|
|
1851
|
+
templateId: options.templateId,
|
|
1852
|
+
context: options.context,
|
|
1853
|
+
label: options.label
|
|
1854
|
+
};
|
|
1855
|
+
}
|
|
1856
|
+
function mergeToml(options) {
|
|
1857
|
+
return {
|
|
1858
|
+
kind: "templateMergeToml",
|
|
1859
|
+
target: options.target,
|
|
1860
|
+
templateId: options.templateId,
|
|
1861
|
+
context: options.context,
|
|
1862
|
+
label: options.label
|
|
1863
|
+
};
|
|
1864
|
+
}
|
|
1865
|
+
function mergeJson(options) {
|
|
1866
|
+
return {
|
|
1867
|
+
kind: "templateMergeJson",
|
|
1868
|
+
target: options.target,
|
|
1869
|
+
templateId: options.templateId,
|
|
1870
|
+
context: options.context,
|
|
1871
|
+
label: options.label
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
var templateMutation = {
|
|
1875
|
+
write,
|
|
1876
|
+
mergeToml,
|
|
1877
|
+
mergeJson
|
|
1878
|
+
};
|
|
1879
|
+
|
|
1880
|
+
// ../config-mutations/src/execution/apply-mutation.ts
|
|
1881
|
+
import Mustache from "mustache";
|
|
1882
|
+
|
|
1883
|
+
// ../config-mutations/src/formats/json.ts
|
|
1884
|
+
import * as jsonc from "jsonc-parser";
|
|
1885
|
+
function isConfigObject(value) {
|
|
1886
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1887
|
+
}
|
|
1888
|
+
function parse2(content) {
|
|
1889
|
+
if (!content || content.trim() === "") {
|
|
1890
|
+
return {};
|
|
1891
|
+
}
|
|
1892
|
+
const errors = [];
|
|
1893
|
+
const parsed = jsonc.parse(content, errors, {
|
|
1894
|
+
allowTrailingComma: true,
|
|
1895
|
+
disallowComments: false
|
|
1896
|
+
});
|
|
1897
|
+
if (errors.length > 0) {
|
|
1898
|
+
throw new Error(`JSON parse error: ${jsonc.printParseErrorCode(errors[0].error)}`);
|
|
1371
1899
|
}
|
|
1372
|
-
if (
|
|
1373
|
-
return
|
|
1900
|
+
if (parsed === null || parsed === void 0) {
|
|
1901
|
+
return {};
|
|
1374
1902
|
}
|
|
1375
|
-
if (
|
|
1376
|
-
|
|
1903
|
+
if (!isConfigObject(parsed)) {
|
|
1904
|
+
throw new Error("Expected JSON object.");
|
|
1377
1905
|
}
|
|
1378
|
-
return
|
|
1906
|
+
return parsed;
|
|
1379
1907
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1908
|
+
function serialize(obj) {
|
|
1909
|
+
return `${JSON.stringify(obj, null, 2)}
|
|
1910
|
+
`;
|
|
1911
|
+
}
|
|
1912
|
+
function merge(base, patch) {
|
|
1913
|
+
const result = { ...base };
|
|
1914
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1915
|
+
if (value === void 0) {
|
|
1916
|
+
continue;
|
|
1917
|
+
}
|
|
1918
|
+
const existing = result[key];
|
|
1919
|
+
if (isConfigObject(existing) && isConfigObject(value)) {
|
|
1920
|
+
result[key] = merge(existing, value);
|
|
1921
|
+
continue;
|
|
1922
|
+
}
|
|
1923
|
+
result[key] = value;
|
|
1384
1924
|
}
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
for (const
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1925
|
+
return result;
|
|
1926
|
+
}
|
|
1927
|
+
function prune(obj, shape) {
|
|
1928
|
+
let changed = false;
|
|
1929
|
+
const result = { ...obj };
|
|
1930
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
1931
|
+
if (!(key in result)) {
|
|
1932
|
+
continue;
|
|
1933
|
+
}
|
|
1934
|
+
const current = result[key];
|
|
1935
|
+
if (isConfigObject(pattern) && Object.keys(pattern).length === 0) {
|
|
1936
|
+
delete result[key];
|
|
1937
|
+
changed = true;
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
if (isConfigObject(pattern) && isConfigObject(current)) {
|
|
1941
|
+
const { changed: childChanged, result: childResult } = prune(
|
|
1942
|
+
current,
|
|
1943
|
+
pattern
|
|
1944
|
+
);
|
|
1945
|
+
if (childChanged) {
|
|
1946
|
+
changed = true;
|
|
1947
|
+
}
|
|
1948
|
+
if (Object.keys(childResult).length === 0) {
|
|
1949
|
+
delete result[key];
|
|
1950
|
+
} else {
|
|
1951
|
+
result[key] = childResult;
|
|
1397
1952
|
}
|
|
1953
|
+
continue;
|
|
1398
1954
|
}
|
|
1955
|
+
delete result[key];
|
|
1956
|
+
changed = true;
|
|
1399
1957
|
}
|
|
1400
|
-
|
|
1958
|
+
return { changed, result };
|
|
1401
1959
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1960
|
+
var jsonFormat = {
|
|
1961
|
+
parse: parse2,
|
|
1962
|
+
serialize,
|
|
1963
|
+
merge,
|
|
1964
|
+
prune
|
|
1965
|
+
};
|
|
1966
|
+
|
|
1967
|
+
// ../config-mutations/src/formats/toml.ts
|
|
1968
|
+
import { parse as parseToml, stringify as stringifyToml } from "smol-toml";
|
|
1969
|
+
function isConfigObject2(value) {
|
|
1970
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1971
|
+
}
|
|
1972
|
+
function parse3(content) {
|
|
1973
|
+
if (!content || content.trim() === "") {
|
|
1974
|
+
return {};
|
|
1405
1975
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1976
|
+
const parsed = parseToml(content);
|
|
1977
|
+
if (!isConfigObject2(parsed)) {
|
|
1978
|
+
throw new Error("Expected TOML document to be a table.");
|
|
1408
1979
|
}
|
|
1409
|
-
return
|
|
1980
|
+
return parsed;
|
|
1410
1981
|
}
|
|
1411
|
-
function
|
|
1412
|
-
const
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
}
|
|
1416
|
-
return {
|
|
1417
|
-
displayPath: path.join(
|
|
1418
|
-
scope === "global" ? config.globalSkillDir : config.localSkillDir,
|
|
1419
|
-
TERMINAL_PILOT_SKILL_NAME
|
|
1420
|
-
),
|
|
1421
|
-
fullPath: path.join(
|
|
1422
|
-
scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path.resolve(cwd, config.localSkillDir),
|
|
1423
|
-
TERMINAL_PILOT_SKILL_NAME
|
|
1424
|
-
)
|
|
1425
|
-
};
|
|
1982
|
+
function serialize2(obj) {
|
|
1983
|
+
const serialized = stringifyToml(obj);
|
|
1984
|
+
return serialized.endsWith("\n") ? serialized : `${serialized}
|
|
1985
|
+
`;
|
|
1426
1986
|
}
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
return false;
|
|
1987
|
+
function merge2(base, patch) {
|
|
1988
|
+
const result = { ...base };
|
|
1989
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
1990
|
+
if (value === void 0) {
|
|
1991
|
+
continue;
|
|
1433
1992
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1993
|
+
const existing = result[key];
|
|
1994
|
+
if (isConfigObject2(existing) && isConfigObject2(value)) {
|
|
1995
|
+
result[key] = merge2(existing, value);
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1998
|
+
result[key] = value;
|
|
1438
1999
|
}
|
|
1439
|
-
|
|
1440
|
-
return true;
|
|
2000
|
+
return result;
|
|
1441
2001
|
}
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
2002
|
+
function prune2(obj, shape) {
|
|
2003
|
+
let changed = false;
|
|
2004
|
+
const result = { ...obj };
|
|
2005
|
+
for (const [key, pattern] of Object.entries(shape)) {
|
|
2006
|
+
if (!(key in result)) {
|
|
2007
|
+
continue;
|
|
2008
|
+
}
|
|
2009
|
+
const current = result[key];
|
|
2010
|
+
if (isConfigObject2(pattern) && Object.keys(pattern).length === 0) {
|
|
2011
|
+
delete result[key];
|
|
2012
|
+
changed = true;
|
|
2013
|
+
continue;
|
|
2014
|
+
}
|
|
2015
|
+
if (isConfigObject2(pattern) && isConfigObject2(current)) {
|
|
2016
|
+
const { changed: childChanged, result: childResult } = prune2(
|
|
2017
|
+
current,
|
|
2018
|
+
pattern
|
|
2019
|
+
);
|
|
2020
|
+
if (childChanged) {
|
|
2021
|
+
changed = true;
|
|
2022
|
+
}
|
|
2023
|
+
if (Object.keys(childResult).length === 0) {
|
|
2024
|
+
delete result[key];
|
|
2025
|
+
} else {
|
|
2026
|
+
result[key] = childResult;
|
|
2027
|
+
}
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
delete result[key];
|
|
2031
|
+
changed = true;
|
|
2032
|
+
}
|
|
2033
|
+
return { changed, result };
|
|
2034
|
+
}
|
|
2035
|
+
var tomlFormat = {
|
|
2036
|
+
parse: parse3,
|
|
2037
|
+
serialize: serialize2,
|
|
2038
|
+
merge: merge2,
|
|
2039
|
+
prune: prune2
|
|
2040
|
+
};
|
|
2041
|
+
|
|
2042
|
+
// ../config-mutations/src/formats/index.ts
|
|
2043
|
+
var formatRegistry = {
|
|
2044
|
+
json: jsonFormat,
|
|
2045
|
+
toml: tomlFormat
|
|
2046
|
+
};
|
|
2047
|
+
var extensionMap = {
|
|
2048
|
+
".json": "json",
|
|
2049
|
+
".toml": "toml"
|
|
2050
|
+
};
|
|
2051
|
+
function getConfigFormat(pathOrFormat) {
|
|
2052
|
+
if (pathOrFormat in formatRegistry) {
|
|
2053
|
+
return formatRegistry[pathOrFormat];
|
|
2054
|
+
}
|
|
2055
|
+
const ext = getExtension(pathOrFormat);
|
|
2056
|
+
const formatName = extensionMap[ext];
|
|
2057
|
+
if (!formatName) {
|
|
2058
|
+
throw new Error(
|
|
2059
|
+
`Unsupported config format. Cannot detect format from "${pathOrFormat}". Supported extensions: ${Object.keys(extensionMap).join(", ")}. Supported format names: ${Object.keys(formatRegistry).join(", ")}.`
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
2062
|
+
return formatRegistry[formatName];
|
|
2063
|
+
}
|
|
2064
|
+
function detectFormat(path4) {
|
|
2065
|
+
const ext = getExtension(path4);
|
|
2066
|
+
return extensionMap[ext];
|
|
2067
|
+
}
|
|
2068
|
+
function getExtension(path4) {
|
|
2069
|
+
const lastDot = path4.lastIndexOf(".");
|
|
2070
|
+
if (lastDot === -1) {
|
|
2071
|
+
return "";
|
|
2072
|
+
}
|
|
2073
|
+
return path4.slice(lastDot).toLowerCase();
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
// ../config-mutations/src/execution/path-utils.ts
|
|
2077
|
+
import path2 from "node:path";
|
|
2078
|
+
function expandHome(targetPath, homeDir) {
|
|
2079
|
+
if (!targetPath?.startsWith("~")) {
|
|
2080
|
+
return targetPath;
|
|
2081
|
+
}
|
|
2082
|
+
if (targetPath.startsWith("~./")) {
|
|
2083
|
+
targetPath = `~/.${targetPath.slice(3)}`;
|
|
2084
|
+
}
|
|
2085
|
+
let remainder = targetPath.slice(1);
|
|
2086
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
2087
|
+
remainder = remainder.slice(1);
|
|
2088
|
+
} else if (remainder.startsWith(".")) {
|
|
2089
|
+
remainder = remainder.slice(1);
|
|
2090
|
+
if (remainder.startsWith("/") || remainder.startsWith("\\")) {
|
|
2091
|
+
remainder = remainder.slice(1);
|
|
2092
|
+
}
|
|
2093
|
+
}
|
|
2094
|
+
return remainder.length === 0 ? homeDir : path2.join(homeDir, remainder);
|
|
2095
|
+
}
|
|
2096
|
+
function validateHomePath(targetPath) {
|
|
2097
|
+
if (typeof targetPath !== "string" || targetPath.length === 0) {
|
|
2098
|
+
throw new Error("Target path must be a non-empty string.");
|
|
2099
|
+
}
|
|
2100
|
+
if (!targetPath.startsWith("~")) {
|
|
2101
|
+
throw new Error(
|
|
2102
|
+
`All target paths must be home-relative (start with ~). Received: "${targetPath}"`
|
|
2103
|
+
);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
function resolvePath(rawPath, homeDir, pathMapper) {
|
|
2107
|
+
validateHomePath(rawPath);
|
|
2108
|
+
const expanded = expandHome(rawPath, homeDir);
|
|
2109
|
+
if (!pathMapper) {
|
|
2110
|
+
return expanded;
|
|
2111
|
+
}
|
|
2112
|
+
const rawDirectory = path2.dirname(expanded);
|
|
2113
|
+
const mappedDirectory = pathMapper.mapTargetDirectory({
|
|
2114
|
+
targetDirectory: rawDirectory
|
|
2115
|
+
});
|
|
2116
|
+
const filename = path2.basename(expanded);
|
|
2117
|
+
return filename.length === 0 ? mappedDirectory : path2.join(mappedDirectory, filename);
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
// ../config-mutations/src/fs-utils.ts
|
|
2121
|
+
function isNotFound(error) {
|
|
2122
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2123
|
+
}
|
|
2124
|
+
async function readFileIfExists(fs, target) {
|
|
2125
|
+
try {
|
|
2126
|
+
return await fs.readFile(target, "utf8");
|
|
2127
|
+
} catch (error) {
|
|
2128
|
+
if (isNotFound(error)) {
|
|
2129
|
+
return null;
|
|
2130
|
+
}
|
|
2131
|
+
throw error;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
async function pathExists(fs, target) {
|
|
2135
|
+
try {
|
|
2136
|
+
await fs.stat(target);
|
|
2137
|
+
return true;
|
|
2138
|
+
} catch (error) {
|
|
2139
|
+
if (isNotFound(error)) {
|
|
2140
|
+
return false;
|
|
2141
|
+
}
|
|
2142
|
+
throw error;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
function createTimestamp() {
|
|
2146
|
+
return (/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-").replaceAll(".", "-");
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
// ../config-mutations/src/execution/apply-mutation.ts
|
|
2150
|
+
function resolveValue(resolver, options) {
|
|
2151
|
+
if (typeof resolver === "function") {
|
|
2152
|
+
return resolver(options);
|
|
2153
|
+
}
|
|
2154
|
+
return resolver;
|
|
2155
|
+
}
|
|
2156
|
+
function createInvalidDocumentBackupPath(targetPath) {
|
|
2157
|
+
const ext = targetPath.includes(".") ? targetPath.split(".").pop() : "bak";
|
|
2158
|
+
return `${targetPath}.invalid-${createTimestamp()}.${ext}`;
|
|
2159
|
+
}
|
|
2160
|
+
async function backupInvalidDocument(fs, targetPath, content) {
|
|
2161
|
+
const backupPath = createInvalidDocumentBackupPath(targetPath);
|
|
2162
|
+
await fs.writeFile(backupPath, content, { encoding: "utf8" });
|
|
2163
|
+
}
|
|
2164
|
+
function describeMutation(kind, targetPath) {
|
|
2165
|
+
const displayPath = targetPath ?? "target";
|
|
2166
|
+
switch (kind) {
|
|
2167
|
+
case "ensureDirectory":
|
|
2168
|
+
return `Create ${displayPath}`;
|
|
2169
|
+
case "removeDirectory":
|
|
2170
|
+
return `Remove directory ${displayPath}`;
|
|
2171
|
+
case "backup":
|
|
2172
|
+
return `Backup ${displayPath}`;
|
|
2173
|
+
case "templateWrite":
|
|
2174
|
+
return `Write ${displayPath}`;
|
|
2175
|
+
case "chmod":
|
|
2176
|
+
return `Set permissions on ${displayPath}`;
|
|
2177
|
+
case "removeFile":
|
|
2178
|
+
return `Remove ${displayPath}`;
|
|
2179
|
+
case "configMerge":
|
|
2180
|
+
case "configPrune":
|
|
2181
|
+
case "configTransform":
|
|
2182
|
+
case "templateMergeToml":
|
|
2183
|
+
case "templateMergeJson":
|
|
2184
|
+
return `Update ${displayPath}`;
|
|
2185
|
+
default:
|
|
2186
|
+
return "Operation";
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
function pruneKeysByPrefix(table, prefix) {
|
|
2190
|
+
const result = {};
|
|
2191
|
+
for (const [key, value] of Object.entries(table)) {
|
|
2192
|
+
if (!key.startsWith(prefix)) {
|
|
2193
|
+
result[key] = value;
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
return result;
|
|
2197
|
+
}
|
|
2198
|
+
function isConfigObject3(value) {
|
|
2199
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2200
|
+
}
|
|
2201
|
+
function mergeWithPruneByPrefix(base, patch, pruneByPrefix) {
|
|
2202
|
+
const result = { ...base };
|
|
2203
|
+
const prefixMap = pruneByPrefix ?? {};
|
|
2204
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
2205
|
+
const current = result[key];
|
|
2206
|
+
const prefix = prefixMap[key];
|
|
2207
|
+
if (isConfigObject3(current) && isConfigObject3(value)) {
|
|
2208
|
+
if (prefix) {
|
|
2209
|
+
const pruned = pruneKeysByPrefix(current, prefix);
|
|
2210
|
+
result[key] = { ...pruned, ...value };
|
|
2211
|
+
} else {
|
|
2212
|
+
result[key] = mergeWithPruneByPrefix(
|
|
2213
|
+
current,
|
|
2214
|
+
value,
|
|
2215
|
+
prefixMap
|
|
2216
|
+
);
|
|
2217
|
+
}
|
|
2218
|
+
continue;
|
|
2219
|
+
}
|
|
2220
|
+
result[key] = value;
|
|
2221
|
+
}
|
|
2222
|
+
return result;
|
|
2223
|
+
}
|
|
2224
|
+
async function applyMutation(mutation, context, options) {
|
|
2225
|
+
switch (mutation.kind) {
|
|
2226
|
+
case "ensureDirectory":
|
|
2227
|
+
return applyEnsureDirectory(mutation, context, options);
|
|
2228
|
+
case "removeDirectory":
|
|
2229
|
+
return applyRemoveDirectory(mutation, context, options);
|
|
2230
|
+
case "removeFile":
|
|
2231
|
+
return applyRemoveFile(mutation, context, options);
|
|
2232
|
+
case "chmod":
|
|
2233
|
+
return applyChmod(mutation, context, options);
|
|
2234
|
+
case "backup":
|
|
2235
|
+
return applyBackup(mutation, context, options);
|
|
2236
|
+
case "configMerge":
|
|
2237
|
+
return applyConfigMerge(mutation, context, options);
|
|
2238
|
+
case "configPrune":
|
|
2239
|
+
return applyConfigPrune(mutation, context, options);
|
|
2240
|
+
case "configTransform":
|
|
2241
|
+
return applyConfigTransform(mutation, context, options);
|
|
2242
|
+
case "templateWrite":
|
|
2243
|
+
return applyTemplateWrite(mutation, context, options);
|
|
2244
|
+
case "templateMergeToml":
|
|
2245
|
+
return applyTemplateMerge(mutation, context, options, "toml");
|
|
2246
|
+
case "templateMergeJson":
|
|
2247
|
+
return applyTemplateMerge(mutation, context, options, "json");
|
|
2248
|
+
default: {
|
|
2249
|
+
const never = mutation;
|
|
2250
|
+
throw new Error(`Unknown mutation kind: ${never.kind}`);
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
async function applyEnsureDirectory(mutation, context, options) {
|
|
2255
|
+
const rawPath = resolveValue(mutation.path, options);
|
|
2256
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2257
|
+
const details = {
|
|
2258
|
+
kind: mutation.kind,
|
|
2259
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2260
|
+
targetPath
|
|
2261
|
+
};
|
|
2262
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
2263
|
+
if (!context.dryRun) {
|
|
2264
|
+
await context.fs.mkdir(targetPath, { recursive: true });
|
|
2265
|
+
}
|
|
2266
|
+
return {
|
|
2267
|
+
outcome: {
|
|
2268
|
+
changed: !existed,
|
|
2269
|
+
effect: "mkdir",
|
|
2270
|
+
detail: existed ? "noop" : "create"
|
|
2271
|
+
},
|
|
2272
|
+
details
|
|
2273
|
+
};
|
|
2274
|
+
}
|
|
2275
|
+
async function applyRemoveDirectory(mutation, context, options) {
|
|
2276
|
+
const rawPath = resolveValue(mutation.path, options);
|
|
2277
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2278
|
+
const details = {
|
|
2279
|
+
kind: mutation.kind,
|
|
2280
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2281
|
+
targetPath
|
|
2282
|
+
};
|
|
2283
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
2284
|
+
if (!existed) {
|
|
2285
|
+
return {
|
|
2286
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2287
|
+
details
|
|
2288
|
+
};
|
|
2289
|
+
}
|
|
2290
|
+
if (typeof context.fs.rm !== "function") {
|
|
2291
|
+
return {
|
|
2292
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2293
|
+
details
|
|
2294
|
+
};
|
|
2295
|
+
}
|
|
2296
|
+
if (mutation.force) {
|
|
2297
|
+
if (!context.dryRun) {
|
|
2298
|
+
await context.fs.rm(targetPath, { recursive: true, force: true });
|
|
2299
|
+
}
|
|
2300
|
+
return {
|
|
2301
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
2302
|
+
details
|
|
2303
|
+
};
|
|
2304
|
+
}
|
|
2305
|
+
const entries = await context.fs.readdir(targetPath);
|
|
2306
|
+
if (entries.length > 0) {
|
|
2307
|
+
return {
|
|
2308
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2309
|
+
details
|
|
2310
|
+
};
|
|
2311
|
+
}
|
|
2312
|
+
if (!context.dryRun) {
|
|
2313
|
+
await context.fs.rm(targetPath, { recursive: true, force: true });
|
|
2314
|
+
}
|
|
2315
|
+
return {
|
|
2316
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
2317
|
+
details
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
async function applyRemoveFile(mutation, context, options) {
|
|
2321
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2322
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2323
|
+
const details = {
|
|
2324
|
+
kind: mutation.kind,
|
|
2325
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2326
|
+
targetPath
|
|
2327
|
+
};
|
|
2328
|
+
try {
|
|
2329
|
+
const content = await context.fs.readFile(targetPath, "utf8");
|
|
2330
|
+
const trimmed = content.trim();
|
|
2331
|
+
if (mutation.whenContentMatches && !mutation.whenContentMatches.test(trimmed)) {
|
|
2332
|
+
return {
|
|
2333
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2334
|
+
details
|
|
2335
|
+
};
|
|
2336
|
+
}
|
|
2337
|
+
if (mutation.whenEmpty && trimmed.length > 0) {
|
|
2338
|
+
return {
|
|
2339
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2340
|
+
details
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
if (!context.dryRun) {
|
|
2344
|
+
await context.fs.unlink(targetPath);
|
|
2345
|
+
}
|
|
2346
|
+
return {
|
|
2347
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
2348
|
+
details
|
|
2349
|
+
};
|
|
2350
|
+
} catch (error) {
|
|
2351
|
+
if (isNotFound(error)) {
|
|
2352
|
+
return {
|
|
2353
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2354
|
+
details
|
|
2355
|
+
};
|
|
2356
|
+
}
|
|
2357
|
+
throw error;
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
async function applyChmod(mutation, context, options) {
|
|
2361
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2362
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2363
|
+
const details = {
|
|
2364
|
+
kind: mutation.kind,
|
|
2365
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2366
|
+
targetPath
|
|
2367
|
+
};
|
|
2368
|
+
if (typeof context.fs.chmod !== "function") {
|
|
2369
|
+
return {
|
|
2370
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2371
|
+
details
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
try {
|
|
2375
|
+
const stat = await context.fs.stat(targetPath);
|
|
2376
|
+
const currentMode = typeof stat.mode === "number" ? stat.mode & 511 : null;
|
|
2377
|
+
if (currentMode === mutation.mode) {
|
|
2378
|
+
return {
|
|
2379
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2380
|
+
details
|
|
2381
|
+
};
|
|
2382
|
+
}
|
|
2383
|
+
if (!context.dryRun) {
|
|
2384
|
+
await context.fs.chmod(targetPath, mutation.mode);
|
|
2385
|
+
}
|
|
2386
|
+
return {
|
|
2387
|
+
outcome: { changed: true, effect: "chmod", detail: "update" },
|
|
2388
|
+
details
|
|
2389
|
+
};
|
|
2390
|
+
} catch (error) {
|
|
2391
|
+
if (isNotFound(error)) {
|
|
2392
|
+
return {
|
|
2393
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2394
|
+
details
|
|
2395
|
+
};
|
|
2396
|
+
}
|
|
2397
|
+
throw error;
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
async function applyBackup(mutation, context, options) {
|
|
2401
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2402
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2403
|
+
const details = {
|
|
2404
|
+
kind: mutation.kind,
|
|
2405
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2406
|
+
targetPath
|
|
2407
|
+
};
|
|
2408
|
+
const content = await readFileIfExists(context.fs, targetPath);
|
|
2409
|
+
if (content === null) {
|
|
2410
|
+
return {
|
|
2411
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2412
|
+
details
|
|
2413
|
+
};
|
|
2414
|
+
}
|
|
2415
|
+
if (!context.dryRun) {
|
|
2416
|
+
const backupPath = `${targetPath}.backup-${createTimestamp()}`;
|
|
2417
|
+
await context.fs.writeFile(backupPath, content, { encoding: "utf8" });
|
|
2418
|
+
}
|
|
2419
|
+
return {
|
|
2420
|
+
outcome: { changed: true, effect: "copy", detail: "backup" },
|
|
2421
|
+
details
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
async function applyConfigMerge(mutation, context, options) {
|
|
2425
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2426
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2427
|
+
const details = {
|
|
2428
|
+
kind: mutation.kind,
|
|
2429
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2430
|
+
targetPath
|
|
2431
|
+
};
|
|
2432
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
2433
|
+
if (!formatName) {
|
|
2434
|
+
throw new Error(
|
|
2435
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
2436
|
+
);
|
|
2437
|
+
}
|
|
2438
|
+
const format = getConfigFormat(formatName);
|
|
2439
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
2440
|
+
let current;
|
|
2441
|
+
try {
|
|
2442
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
2443
|
+
} catch {
|
|
2444
|
+
if (rawContent !== null) {
|
|
2445
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
2446
|
+
}
|
|
2447
|
+
current = {};
|
|
2448
|
+
}
|
|
2449
|
+
const value = resolveValue(mutation.value, options);
|
|
2450
|
+
let merged;
|
|
2451
|
+
if (mutation.pruneByPrefix) {
|
|
2452
|
+
merged = mergeWithPruneByPrefix(current, value, mutation.pruneByPrefix);
|
|
2453
|
+
} else {
|
|
2454
|
+
merged = format.merge(current, value);
|
|
2455
|
+
}
|
|
2456
|
+
const serialized = format.serialize(merged);
|
|
2457
|
+
const changed = serialized !== rawContent;
|
|
2458
|
+
if (changed && !context.dryRun) {
|
|
2459
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
2460
|
+
}
|
|
2461
|
+
return {
|
|
2462
|
+
outcome: {
|
|
2463
|
+
changed,
|
|
2464
|
+
effect: changed ? "write" : "none",
|
|
2465
|
+
detail: changed ? rawContent === null ? "create" : "update" : "noop"
|
|
2466
|
+
},
|
|
2467
|
+
details
|
|
2468
|
+
};
|
|
2469
|
+
}
|
|
2470
|
+
async function applyConfigPrune(mutation, context, options) {
|
|
2471
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2472
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2473
|
+
const details = {
|
|
2474
|
+
kind: mutation.kind,
|
|
2475
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2476
|
+
targetPath
|
|
2477
|
+
};
|
|
2478
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
2479
|
+
if (rawContent === null) {
|
|
2480
|
+
return {
|
|
2481
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2482
|
+
details
|
|
2483
|
+
};
|
|
2484
|
+
}
|
|
2485
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
2486
|
+
if (!formatName) {
|
|
2487
|
+
throw new Error(
|
|
2488
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
2489
|
+
);
|
|
2490
|
+
}
|
|
2491
|
+
const format = getConfigFormat(formatName);
|
|
2492
|
+
let current;
|
|
2493
|
+
try {
|
|
2494
|
+
current = format.parse(rawContent);
|
|
2495
|
+
} catch {
|
|
2496
|
+
return {
|
|
2497
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2498
|
+
details
|
|
2499
|
+
};
|
|
2500
|
+
}
|
|
2501
|
+
if (mutation.onlyIf && !mutation.onlyIf(current, options)) {
|
|
2502
|
+
return {
|
|
2503
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2504
|
+
details
|
|
2505
|
+
};
|
|
2506
|
+
}
|
|
2507
|
+
const shape = resolveValue(mutation.shape, options);
|
|
2508
|
+
const { changed, result } = format.prune(current, shape);
|
|
2509
|
+
if (!changed) {
|
|
2510
|
+
return {
|
|
2511
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2512
|
+
details
|
|
2513
|
+
};
|
|
2514
|
+
}
|
|
2515
|
+
if (Object.keys(result).length === 0) {
|
|
2516
|
+
if (!context.dryRun) {
|
|
2517
|
+
await context.fs.unlink(targetPath);
|
|
2518
|
+
}
|
|
2519
|
+
return {
|
|
2520
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
2521
|
+
details
|
|
2522
|
+
};
|
|
2523
|
+
}
|
|
2524
|
+
const serialized = format.serialize(result);
|
|
2525
|
+
if (!context.dryRun) {
|
|
2526
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
2527
|
+
}
|
|
2528
|
+
return {
|
|
2529
|
+
outcome: { changed: true, effect: "write", detail: "update" },
|
|
2530
|
+
details
|
|
2531
|
+
};
|
|
2532
|
+
}
|
|
2533
|
+
async function applyConfigTransform(mutation, context, options) {
|
|
2534
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2535
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2536
|
+
const details = {
|
|
2537
|
+
kind: mutation.kind,
|
|
2538
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2539
|
+
targetPath
|
|
2540
|
+
};
|
|
2541
|
+
const formatName = mutation.format ?? detectFormat(rawPath);
|
|
2542
|
+
if (!formatName) {
|
|
2543
|
+
throw new Error(
|
|
2544
|
+
`Cannot detect config format for "${rawPath}". Provide explicit format option.`
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
const format = getConfigFormat(formatName);
|
|
2548
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
2549
|
+
let current;
|
|
2550
|
+
try {
|
|
2551
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
2552
|
+
} catch {
|
|
2553
|
+
if (rawContent !== null) {
|
|
2554
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
2555
|
+
}
|
|
2556
|
+
current = {};
|
|
2557
|
+
}
|
|
2558
|
+
const { content: transformed, changed } = mutation.transform(current, options);
|
|
2559
|
+
if (!changed) {
|
|
2560
|
+
return {
|
|
2561
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2562
|
+
details
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
if (transformed === null) {
|
|
2566
|
+
if (rawContent === null) {
|
|
2567
|
+
return {
|
|
2568
|
+
outcome: { changed: false, effect: "none", detail: "noop" },
|
|
2569
|
+
details
|
|
2570
|
+
};
|
|
2571
|
+
}
|
|
2572
|
+
if (!context.dryRun) {
|
|
2573
|
+
await context.fs.unlink(targetPath);
|
|
2574
|
+
}
|
|
2575
|
+
return {
|
|
2576
|
+
outcome: { changed: true, effect: "delete", detail: "delete" },
|
|
2577
|
+
details
|
|
2578
|
+
};
|
|
2579
|
+
}
|
|
2580
|
+
const serialized = format.serialize(transformed);
|
|
2581
|
+
if (!context.dryRun) {
|
|
2582
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
2583
|
+
}
|
|
2584
|
+
return {
|
|
2585
|
+
outcome: {
|
|
2586
|
+
changed: true,
|
|
2587
|
+
effect: "write",
|
|
2588
|
+
detail: rawContent === null ? "create" : "update"
|
|
2589
|
+
},
|
|
2590
|
+
details
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
async function applyTemplateWrite(mutation, context, options) {
|
|
2594
|
+
if (!context.templates) {
|
|
2595
|
+
throw new Error(
|
|
2596
|
+
"Template mutations require a templates loader. Provide templates function to runMutations context."
|
|
2597
|
+
);
|
|
2598
|
+
}
|
|
2599
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2600
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2601
|
+
const details = {
|
|
2602
|
+
kind: mutation.kind,
|
|
2603
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2604
|
+
targetPath
|
|
2605
|
+
};
|
|
2606
|
+
const template = await context.templates(mutation.templateId);
|
|
2607
|
+
const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
|
|
2608
|
+
const rendered = Mustache.render(template, templateContext);
|
|
2609
|
+
const existed = await pathExists(context.fs, targetPath);
|
|
2610
|
+
if (!context.dryRun) {
|
|
2611
|
+
await context.fs.writeFile(targetPath, rendered, { encoding: "utf8" });
|
|
2612
|
+
}
|
|
2613
|
+
return {
|
|
2614
|
+
outcome: {
|
|
2615
|
+
changed: true,
|
|
2616
|
+
effect: "write",
|
|
2617
|
+
detail: existed ? "update" : "create"
|
|
2618
|
+
},
|
|
2619
|
+
details
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
async function applyTemplateMerge(mutation, context, options, formatName) {
|
|
2623
|
+
if (!context.templates) {
|
|
2624
|
+
throw new Error(
|
|
2625
|
+
"Template mutations require a templates loader. Provide templates function to runMutations context."
|
|
2626
|
+
);
|
|
2627
|
+
}
|
|
2628
|
+
const rawPath = resolveValue(mutation.target, options);
|
|
2629
|
+
const targetPath = resolvePath(rawPath, context.homeDir, context.pathMapper);
|
|
2630
|
+
const details = {
|
|
2631
|
+
kind: mutation.kind,
|
|
2632
|
+
label: mutation.label ?? describeMutation(mutation.kind, targetPath),
|
|
2633
|
+
targetPath
|
|
2634
|
+
};
|
|
2635
|
+
const format = getConfigFormat(formatName);
|
|
2636
|
+
const template = await context.templates(mutation.templateId);
|
|
2637
|
+
const templateContext = mutation.context ? resolveValue(mutation.context, options) : {};
|
|
2638
|
+
const rendered = Mustache.render(template, templateContext);
|
|
2639
|
+
let templateDoc;
|
|
2640
|
+
try {
|
|
2641
|
+
templateDoc = format.parse(rendered);
|
|
2642
|
+
} catch (error) {
|
|
2643
|
+
throw new Error(
|
|
2644
|
+
`Failed to parse rendered template "${mutation.templateId}" as ${formatName.toUpperCase()}: ${error}`,
|
|
2645
|
+
{ cause: error }
|
|
2646
|
+
);
|
|
2647
|
+
}
|
|
2648
|
+
const rawContent = await readFileIfExists(context.fs, targetPath);
|
|
2649
|
+
let current;
|
|
2650
|
+
try {
|
|
2651
|
+
current = rawContent === null ? {} : format.parse(rawContent);
|
|
2652
|
+
} catch {
|
|
2653
|
+
if (rawContent !== null) {
|
|
2654
|
+
await backupInvalidDocument(context.fs, targetPath, rawContent);
|
|
2655
|
+
}
|
|
2656
|
+
current = {};
|
|
2657
|
+
}
|
|
2658
|
+
const merged = format.merge(current, templateDoc);
|
|
2659
|
+
const serialized = format.serialize(merged);
|
|
2660
|
+
const changed = serialized !== rawContent;
|
|
2661
|
+
if (changed && !context.dryRun) {
|
|
2662
|
+
await context.fs.writeFile(targetPath, serialized, { encoding: "utf8" });
|
|
2663
|
+
}
|
|
2664
|
+
return {
|
|
2665
|
+
outcome: {
|
|
2666
|
+
changed,
|
|
2667
|
+
effect: changed ? "write" : "none",
|
|
2668
|
+
detail: changed ? rawContent === null ? "create" : "update" : "noop"
|
|
2669
|
+
},
|
|
2670
|
+
details
|
|
2671
|
+
};
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2674
|
+
// ../config-mutations/src/execution/run-mutations.ts
|
|
2675
|
+
async function runMutations(mutations, context, options) {
|
|
2676
|
+
const effects = [];
|
|
2677
|
+
let anyChanged = false;
|
|
2678
|
+
const resolverOptions = options ?? {};
|
|
2679
|
+
for (const mutation of mutations) {
|
|
2680
|
+
const { outcome } = await executeMutation(
|
|
2681
|
+
mutation,
|
|
2682
|
+
context,
|
|
2683
|
+
resolverOptions
|
|
2684
|
+
);
|
|
2685
|
+
effects.push(outcome);
|
|
2686
|
+
if (outcome.changed) {
|
|
2687
|
+
anyChanged = true;
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
return {
|
|
2691
|
+
changed: anyChanged,
|
|
2692
|
+
effects
|
|
2693
|
+
};
|
|
2694
|
+
}
|
|
2695
|
+
async function executeMutation(mutation, context, options) {
|
|
2696
|
+
context.observers?.onStart?.({
|
|
2697
|
+
kind: mutation.kind,
|
|
2698
|
+
label: mutation.label ?? mutation.kind,
|
|
2699
|
+
targetPath: void 0
|
|
2700
|
+
// Will be resolved during apply
|
|
2701
|
+
});
|
|
2702
|
+
try {
|
|
2703
|
+
const { outcome, details } = await applyMutation(mutation, context, options);
|
|
2704
|
+
context.observers?.onComplete?.(details, outcome);
|
|
2705
|
+
return { outcome, details };
|
|
2706
|
+
} catch (error) {
|
|
2707
|
+
context.observers?.onError?.(
|
|
2708
|
+
{
|
|
2709
|
+
kind: mutation.kind,
|
|
2710
|
+
label: mutation.label ?? mutation.kind,
|
|
2711
|
+
targetPath: void 0
|
|
2712
|
+
},
|
|
2713
|
+
error
|
|
2714
|
+
);
|
|
2715
|
+
throw error;
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
// ../config-mutations/src/template/render.ts
|
|
2720
|
+
import Mustache2 from "mustache";
|
|
2721
|
+
var originalEscape = Mustache2.escape;
|
|
2722
|
+
|
|
2723
|
+
// ../agent-skill-config/src/templates.ts
|
|
2724
|
+
import { readFile } from "node:fs/promises";
|
|
2725
|
+
|
|
2726
|
+
// ../agent-skill-config/src/apply.ts
|
|
2727
|
+
var UnsupportedAgentError = class extends Error {
|
|
2728
|
+
constructor(agentId) {
|
|
2729
|
+
super(`Unsupported agent: ${agentId}`);
|
|
2730
|
+
this.name = "UnsupportedAgentError";
|
|
2731
|
+
}
|
|
2732
|
+
};
|
|
2733
|
+
function toHomeRelative(localSkillDir) {
|
|
2734
|
+
if (localSkillDir.startsWith("~/") || localSkillDir === "~") {
|
|
2735
|
+
return localSkillDir;
|
|
2736
|
+
}
|
|
2737
|
+
const normalized = localSkillDir.startsWith("./") ? localSkillDir.slice(2) : localSkillDir;
|
|
2738
|
+
return `~/${normalized}`;
|
|
2739
|
+
}
|
|
2740
|
+
var SKILL_TEMPLATE_ID = "__skill_content__";
|
|
2741
|
+
async function installSkill(agentId, skill, options) {
|
|
2742
|
+
const support = resolveAgentSupport(agentId);
|
|
2743
|
+
if (support.status !== "supported") {
|
|
2744
|
+
throw new UnsupportedAgentError(agentId);
|
|
2745
|
+
}
|
|
2746
|
+
const scope = options.scope ?? "local";
|
|
2747
|
+
const config = support.config;
|
|
2748
|
+
const skillDir = scope === "global" ? config.globalSkillDir : toHomeRelative(config.localSkillDir);
|
|
2749
|
+
const skillFolderPath = `${skillDir}/${skill.name}`;
|
|
2750
|
+
const skillFilePath = `${skillFolderPath}/SKILL.md`;
|
|
2751
|
+
const displayPath = `${scope === "global" ? config.globalSkillDir : config.localSkillDir}/${skill.name}/SKILL.md`;
|
|
2752
|
+
await runMutations(
|
|
2753
|
+
[
|
|
2754
|
+
fileMutation.ensureDirectory({
|
|
2755
|
+
path: skillFolderPath,
|
|
2756
|
+
label: `Ensure skill directory ${skill.name}`
|
|
2757
|
+
}),
|
|
2758
|
+
templateMutation.write({
|
|
2759
|
+
target: skillFilePath,
|
|
2760
|
+
templateId: SKILL_TEMPLATE_ID,
|
|
2761
|
+
label: `Write skill ${skill.name}`
|
|
2762
|
+
})
|
|
2763
|
+
],
|
|
2764
|
+
{
|
|
2765
|
+
fs: options.fs,
|
|
2766
|
+
homeDir: scope === "global" ? options.homeDir : options.cwd,
|
|
2767
|
+
dryRun: options.dryRun,
|
|
2768
|
+
observers: options.observers,
|
|
2769
|
+
templates: async (templateId) => {
|
|
2770
|
+
if (templateId === SKILL_TEMPLATE_ID) {
|
|
2771
|
+
return skill.content;
|
|
2772
|
+
}
|
|
2773
|
+
throw new Error(`Unknown template: ${templateId}`);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
);
|
|
2777
|
+
return { skillPath: skillFilePath, displayPath };
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
// src/commands/installer.ts
|
|
2781
|
+
import os2 from "node:os";
|
|
2782
|
+
import path3 from "node:path";
|
|
2783
|
+
import * as nodeFs from "node:fs/promises";
|
|
2784
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
2785
|
+
var DEFAULT_INSTALL_AGENT = "claude-code";
|
|
2786
|
+
var DEFAULT_INSTALL_SCOPE = "local";
|
|
2787
|
+
var TERMINAL_PILOT_SKILL_NAME = "terminal-pilot";
|
|
2788
|
+
var installableAgents = supportedAgents;
|
|
2789
|
+
function isNotFoundError(error) {
|
|
2790
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
2791
|
+
}
|
|
2792
|
+
function resolveInstallerServices(installer) {
|
|
2793
|
+
return {
|
|
2794
|
+
fs: installer?.fs ?? nodeFs,
|
|
2795
|
+
cwd: installer?.cwd ?? process.cwd(),
|
|
2796
|
+
homeDir: installer?.homeDir ?? os2.homedir(),
|
|
2797
|
+
platform: installer?.platform ?? process.platform
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
function throwUnsupportedAgent(agent) {
|
|
2801
|
+
throw new UserError(`Unsupported agent: ${agent}`);
|
|
2802
|
+
}
|
|
2803
|
+
function resolveInstallableAgent(agent) {
|
|
2804
|
+
const skillSupport = resolveAgentSupport(agent);
|
|
2805
|
+
if (skillSupport.status !== "supported" || !skillSupport.id) {
|
|
2806
|
+
throwUnsupportedAgent(agent);
|
|
2807
|
+
}
|
|
2808
|
+
return skillSupport.id;
|
|
2809
|
+
}
|
|
2810
|
+
function resolveInstallScope(input) {
|
|
2811
|
+
if (input.local && input.global) {
|
|
2812
|
+
throw new UserError("Use either --local or --global, not both.");
|
|
2813
|
+
}
|
|
2814
|
+
if (input.local) {
|
|
2815
|
+
return "local";
|
|
2816
|
+
}
|
|
2817
|
+
if (input.global) {
|
|
2818
|
+
return "global";
|
|
2819
|
+
}
|
|
2820
|
+
return DEFAULT_INSTALL_SCOPE;
|
|
2821
|
+
}
|
|
2822
|
+
var terminalPilotTemplateCache;
|
|
2823
|
+
async function loadTerminalPilotTemplate() {
|
|
2824
|
+
if (terminalPilotTemplateCache !== void 0) {
|
|
2825
|
+
return terminalPilotTemplateCache;
|
|
2826
|
+
}
|
|
2827
|
+
const candidates = [
|
|
2828
|
+
new URL("./templates/terminal-pilot.md", import.meta.url),
|
|
2829
|
+
new URL("../templates/terminal-pilot.md", import.meta.url),
|
|
2830
|
+
new URL("../../../agent-skill-config/src/templates/terminal-pilot.md", import.meta.url)
|
|
2831
|
+
];
|
|
2832
|
+
for (const candidate of candidates) {
|
|
2833
|
+
try {
|
|
2834
|
+
terminalPilotTemplateCache = await readFile2(candidate, "utf8");
|
|
2835
|
+
return terminalPilotTemplateCache;
|
|
2836
|
+
} catch (error) {
|
|
2837
|
+
if (!isNotFoundError(error)) {
|
|
2838
|
+
throw error;
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
throw new UserError("terminal-pilot skill template is missing.");
|
|
2843
|
+
}
|
|
2844
|
+
function resolveHomeRelativePath(targetPath, homeDir) {
|
|
2845
|
+
if (targetPath === "~") {
|
|
2846
|
+
return homeDir;
|
|
2847
|
+
}
|
|
2848
|
+
if (targetPath.startsWith("~/")) {
|
|
2849
|
+
return path3.join(homeDir, targetPath.slice(2));
|
|
2850
|
+
}
|
|
2851
|
+
return targetPath;
|
|
2852
|
+
}
|
|
2853
|
+
function getSkillFolderWithHome(agent, scope, cwd, homeDir) {
|
|
2854
|
+
const config = getAgentConfig(agent);
|
|
2855
|
+
if (!config) {
|
|
2856
|
+
throwUnsupportedAgent(agent);
|
|
2857
|
+
}
|
|
2858
|
+
return {
|
|
2859
|
+
displayPath: path3.join(
|
|
2860
|
+
scope === "global" ? config.globalSkillDir : config.localSkillDir,
|
|
2861
|
+
TERMINAL_PILOT_SKILL_NAME
|
|
2862
|
+
),
|
|
2863
|
+
fullPath: path3.join(
|
|
2864
|
+
scope === "global" ? resolveHomeRelativePath(config.globalSkillDir, homeDir) : path3.resolve(cwd, config.localSkillDir),
|
|
2865
|
+
TERMINAL_PILOT_SKILL_NAME
|
|
2866
|
+
)
|
|
2867
|
+
};
|
|
2868
|
+
}
|
|
2869
|
+
async function removeSkillFolder(fs, folderPath) {
|
|
2870
|
+
try {
|
|
2871
|
+
await fs.stat(folderPath);
|
|
2872
|
+
} catch (error) {
|
|
2873
|
+
if (isNotFoundError(error)) {
|
|
2874
|
+
return false;
|
|
2875
|
+
}
|
|
2876
|
+
throw error;
|
|
2877
|
+
}
|
|
2878
|
+
if (typeof fs.rm !== "function") {
|
|
2879
|
+
throw new UserError("The configured filesystem does not support removing directories.");
|
|
2880
|
+
}
|
|
2881
|
+
await fs.rm(folderPath, { recursive: true, force: true });
|
|
2882
|
+
return true;
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2885
|
+
// src/commands/install.ts
|
|
2886
|
+
var params5 = S.Object({
|
|
2887
|
+
agent: S.Enum(installableAgents, {
|
|
2888
|
+
description: "Agent to install terminal-pilot for",
|
|
2889
|
+
default: DEFAULT_INSTALL_AGENT
|
|
2890
|
+
}),
|
|
2891
|
+
local: S.Optional(S.Boolean({ description: "Install the skill in the current project" })),
|
|
2892
|
+
global: S.Optional(S.Boolean({ description: "Install the skill in the user home directory" }))
|
|
2893
|
+
});
|
|
2894
|
+
var install = defineCommand({
|
|
1453
2895
|
name: "install",
|
|
1454
2896
|
description: "Install the terminal-pilot CLI skill.",
|
|
1455
2897
|
scope: ["cli"],
|
|
@@ -1482,9 +2924,8 @@ var install = defineCommand5({
|
|
|
1482
2924
|
});
|
|
1483
2925
|
|
|
1484
2926
|
// src/commands/list-sessions.ts
|
|
1485
|
-
|
|
1486
|
-
var
|
|
1487
|
-
var listSessions = defineCommand6({
|
|
2927
|
+
var params6 = S.Object({});
|
|
2928
|
+
var listSessions = defineCommand({
|
|
1488
2929
|
name: "list-sessions",
|
|
1489
2930
|
description: "List active terminal sessions",
|
|
1490
2931
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1502,12 +2943,11 @@ var listSessions = defineCommand6({
|
|
|
1502
2943
|
});
|
|
1503
2944
|
|
|
1504
2945
|
// src/commands/press-key.ts
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
session: S7.Optional(S7.String({ short: "s", description: "Session name" }))
|
|
2946
|
+
var params7 = S.Object({
|
|
2947
|
+
key: S.String({ description: "Named key to press" }),
|
|
2948
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" }))
|
|
1509
2949
|
});
|
|
1510
|
-
var pressKey =
|
|
2950
|
+
var pressKey = defineCommand({
|
|
1511
2951
|
name: "press-key",
|
|
1512
2952
|
description: "Send a named key press to an active terminal session",
|
|
1513
2953
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1521,12 +2961,11 @@ var pressKey = defineCommand7({
|
|
|
1521
2961
|
});
|
|
1522
2962
|
|
|
1523
2963
|
// src/commands/read-history.ts
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
last: S8.Optional(S8.Number({ short: "n", description: "Return only the last N lines" }))
|
|
2964
|
+
var params8 = S.Object({
|
|
2965
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" })),
|
|
2966
|
+
last: S.Optional(S.Number({ short: "n", description: "Return only the last N lines" }))
|
|
1528
2967
|
});
|
|
1529
|
-
var readHistory =
|
|
2968
|
+
var readHistory = defineCommand({
|
|
1530
2969
|
name: "read-history",
|
|
1531
2970
|
description: "Read terminal output history",
|
|
1532
2971
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1539,11 +2978,10 @@ var readHistory = defineCommand8({
|
|
|
1539
2978
|
});
|
|
1540
2979
|
|
|
1541
2980
|
// src/commands/read-screen.ts
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
session: S9.Optional(S9.String({ short: "s", description: "Session name" }))
|
|
2981
|
+
var params9 = S.Object({
|
|
2982
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" }))
|
|
1545
2983
|
});
|
|
1546
|
-
var readScreen =
|
|
2984
|
+
var readScreen = defineCommand({
|
|
1547
2985
|
name: "read-screen",
|
|
1548
2986
|
description: "Read the current visible terminal screen",
|
|
1549
2987
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1561,13 +2999,12 @@ var readScreen = defineCommand9({
|
|
|
1561
2999
|
});
|
|
1562
3000
|
|
|
1563
3001
|
// src/commands/resize.ts
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
session: S10.Optional(S10.String({ short: "s", description: "Session name" }))
|
|
3002
|
+
var params10 = S.Object({
|
|
3003
|
+
cols: S.Number({ description: "Terminal width in columns" }),
|
|
3004
|
+
rows: S.Number({ description: "Terminal height in rows" }),
|
|
3005
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" }))
|
|
1569
3006
|
});
|
|
1570
|
-
var resize =
|
|
3007
|
+
var resize = defineCommand({
|
|
1571
3008
|
name: "resize",
|
|
1572
3009
|
description: "Resize an active terminal session",
|
|
1573
3010
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1579,16 +3016,776 @@ var resize = defineCommand10({
|
|
|
1579
3016
|
}
|
|
1580
3017
|
});
|
|
1581
3018
|
|
|
3019
|
+
// ../terminal-png/src/index.ts
|
|
3020
|
+
import { writeFile } from "node:fs/promises";
|
|
3021
|
+
|
|
3022
|
+
// ../terminal-png/src/ansi-parser.ts
|
|
3023
|
+
var ESC2 = "\x1B";
|
|
3024
|
+
function createDefaultStyle() {
|
|
3025
|
+
return {
|
|
3026
|
+
fg: null,
|
|
3027
|
+
bg: null,
|
|
3028
|
+
bold: false,
|
|
3029
|
+
italic: false,
|
|
3030
|
+
underline: false,
|
|
3031
|
+
strikethrough: false,
|
|
3032
|
+
dim: false
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
3035
|
+
function cloneStyle(style) {
|
|
3036
|
+
return { ...style };
|
|
3037
|
+
}
|
|
3038
|
+
function stylesEqual(left, right) {
|
|
3039
|
+
return left.bold === right.bold && left.italic === right.italic && left.underline === right.underline && left.strikethrough === right.strikethrough && left.dim === right.dim && colorsEqual(left.fg, right.fg) && colorsEqual(left.bg, right.bg);
|
|
3040
|
+
}
|
|
3041
|
+
function colorsEqual(left, right) {
|
|
3042
|
+
if (left === right) {
|
|
3043
|
+
return true;
|
|
3044
|
+
}
|
|
3045
|
+
if (!left || !right || left.type !== right.type) {
|
|
3046
|
+
return false;
|
|
3047
|
+
}
|
|
3048
|
+
if (left.type === "rgb" && right.type === "rgb") {
|
|
3049
|
+
return left.r === right.r && left.g === right.g && left.b === right.b;
|
|
3050
|
+
}
|
|
3051
|
+
if ((left.type === "ansi4" || left.type === "ansi8") && left.type === right.type) {
|
|
3052
|
+
return left.index === right.index;
|
|
3053
|
+
}
|
|
3054
|
+
return false;
|
|
3055
|
+
}
|
|
3056
|
+
function pushRun(runs, style, text) {
|
|
3057
|
+
if (text.length === 0) {
|
|
3058
|
+
return;
|
|
3059
|
+
}
|
|
3060
|
+
const previous = runs.at(-1);
|
|
3061
|
+
if (previous && text !== "\n" && previous.text !== "\n" && stylesEqual(previous, style)) {
|
|
3062
|
+
previous.text += text;
|
|
3063
|
+
return;
|
|
3064
|
+
}
|
|
3065
|
+
runs.push({
|
|
3066
|
+
text,
|
|
3067
|
+
fg: style.fg,
|
|
3068
|
+
bg: style.bg,
|
|
3069
|
+
bold: style.bold,
|
|
3070
|
+
italic: style.italic,
|
|
3071
|
+
underline: style.underline,
|
|
3072
|
+
strikethrough: style.strikethrough,
|
|
3073
|
+
dim: style.dim
|
|
3074
|
+
});
|
|
3075
|
+
}
|
|
3076
|
+
function parseCsi(input, start) {
|
|
3077
|
+
let index = start + 2;
|
|
3078
|
+
while (index < input.length) {
|
|
3079
|
+
const code = input.charCodeAt(index);
|
|
3080
|
+
if (code >= 64 && code <= 126) {
|
|
3081
|
+
return {
|
|
3082
|
+
end: index + 1,
|
|
3083
|
+
final: input[index],
|
|
3084
|
+
params: input.slice(start + 2, index)
|
|
3085
|
+
};
|
|
3086
|
+
}
|
|
3087
|
+
index += 1;
|
|
3088
|
+
}
|
|
3089
|
+
return {
|
|
3090
|
+
end: input.length,
|
|
3091
|
+
final: null,
|
|
3092
|
+
params: input.slice(start + 2)
|
|
3093
|
+
};
|
|
3094
|
+
}
|
|
3095
|
+
function toInteger(value) {
|
|
3096
|
+
if (!value || value.length === 0) {
|
|
3097
|
+
return null;
|
|
3098
|
+
}
|
|
3099
|
+
for (const char of value) {
|
|
3100
|
+
if (char < "0" || char > "9") {
|
|
3101
|
+
return null;
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
return Number.parseInt(value, 10);
|
|
3105
|
+
}
|
|
3106
|
+
function clampByte(value) {
|
|
3107
|
+
if (value < 0) {
|
|
3108
|
+
return 0;
|
|
3109
|
+
}
|
|
3110
|
+
if (value > 255) {
|
|
3111
|
+
return 255;
|
|
3112
|
+
}
|
|
3113
|
+
return value;
|
|
3114
|
+
}
|
|
3115
|
+
function applyExtendedColor(params17, start) {
|
|
3116
|
+
const mode = params17[start];
|
|
3117
|
+
if (mode === 5) {
|
|
3118
|
+
const index = params17[start + 1];
|
|
3119
|
+
if (index === void 0) {
|
|
3120
|
+
return null;
|
|
3121
|
+
}
|
|
3122
|
+
return {
|
|
3123
|
+
color: { type: "ansi8", index: clampByte(index) },
|
|
3124
|
+
consumed: 2
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
if (mode === 2) {
|
|
3128
|
+
const r = params17[start + 1];
|
|
3129
|
+
const g = params17[start + 2];
|
|
3130
|
+
const b = params17[start + 3];
|
|
3131
|
+
if (r === void 0 || g === void 0 || b === void 0) {
|
|
3132
|
+
return null;
|
|
3133
|
+
}
|
|
3134
|
+
return {
|
|
3135
|
+
color: { type: "rgb", r: clampByte(r), g: clampByte(g), b: clampByte(b) },
|
|
3136
|
+
consumed: 4
|
|
3137
|
+
};
|
|
3138
|
+
}
|
|
3139
|
+
return null;
|
|
3140
|
+
}
|
|
3141
|
+
function applySgr(style, paramsText) {
|
|
3142
|
+
const nextStyle = cloneStyle(style);
|
|
3143
|
+
const rawParams = paramsText.length === 0 ? ["0"] : paramsText.split(";");
|
|
3144
|
+
const params17 = [];
|
|
3145
|
+
for (const rawParam of rawParams) {
|
|
3146
|
+
const value = toInteger(rawParam);
|
|
3147
|
+
if (value === null) {
|
|
3148
|
+
return nextStyle;
|
|
3149
|
+
}
|
|
3150
|
+
params17.push(value);
|
|
3151
|
+
}
|
|
3152
|
+
for (let index = 0; index < params17.length; index += 1) {
|
|
3153
|
+
const value = params17[index];
|
|
3154
|
+
if (value === 0) {
|
|
3155
|
+
Object.assign(nextStyle, createDefaultStyle());
|
|
3156
|
+
continue;
|
|
3157
|
+
}
|
|
3158
|
+
if (value === 1) {
|
|
3159
|
+
nextStyle.bold = true;
|
|
3160
|
+
continue;
|
|
3161
|
+
}
|
|
3162
|
+
if (value === 2) {
|
|
3163
|
+
nextStyle.dim = true;
|
|
3164
|
+
continue;
|
|
3165
|
+
}
|
|
3166
|
+
if (value === 22) {
|
|
3167
|
+
nextStyle.bold = false;
|
|
3168
|
+
nextStyle.dim = false;
|
|
3169
|
+
continue;
|
|
3170
|
+
}
|
|
3171
|
+
if (value === 3) {
|
|
3172
|
+
nextStyle.italic = true;
|
|
3173
|
+
continue;
|
|
3174
|
+
}
|
|
3175
|
+
if (value === 23) {
|
|
3176
|
+
nextStyle.italic = false;
|
|
3177
|
+
continue;
|
|
3178
|
+
}
|
|
3179
|
+
if (value === 4) {
|
|
3180
|
+
nextStyle.underline = true;
|
|
3181
|
+
continue;
|
|
3182
|
+
}
|
|
3183
|
+
if (value === 24) {
|
|
3184
|
+
nextStyle.underline = false;
|
|
3185
|
+
continue;
|
|
3186
|
+
}
|
|
3187
|
+
if (value === 9) {
|
|
3188
|
+
nextStyle.strikethrough = true;
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3191
|
+
if (value === 29) {
|
|
3192
|
+
nextStyle.strikethrough = false;
|
|
3193
|
+
continue;
|
|
3194
|
+
}
|
|
3195
|
+
if (value === 39) {
|
|
3196
|
+
nextStyle.fg = null;
|
|
3197
|
+
continue;
|
|
3198
|
+
}
|
|
3199
|
+
if (value === 49) {
|
|
3200
|
+
nextStyle.bg = null;
|
|
3201
|
+
continue;
|
|
3202
|
+
}
|
|
3203
|
+
if (value >= 30 && value <= 37) {
|
|
3204
|
+
nextStyle.fg = { type: "ansi4", index: value - 30 };
|
|
3205
|
+
continue;
|
|
3206
|
+
}
|
|
3207
|
+
if (value >= 90 && value <= 97) {
|
|
3208
|
+
nextStyle.fg = { type: "ansi4", index: value - 90 + 8 };
|
|
3209
|
+
continue;
|
|
3210
|
+
}
|
|
3211
|
+
if (value >= 40 && value <= 47) {
|
|
3212
|
+
nextStyle.bg = { type: "ansi4", index: value - 40 };
|
|
3213
|
+
continue;
|
|
3214
|
+
}
|
|
3215
|
+
if (value >= 100 && value <= 107) {
|
|
3216
|
+
nextStyle.bg = { type: "ansi4", index: value - 100 + 8 };
|
|
3217
|
+
continue;
|
|
3218
|
+
}
|
|
3219
|
+
if (value === 38 || value === 48) {
|
|
3220
|
+
const extended = applyExtendedColor(params17, index + 1);
|
|
3221
|
+
if (!extended) {
|
|
3222
|
+
continue;
|
|
3223
|
+
}
|
|
3224
|
+
if (value === 38) {
|
|
3225
|
+
nextStyle.fg = extended.color;
|
|
3226
|
+
} else {
|
|
3227
|
+
nextStyle.bg = extended.color;
|
|
3228
|
+
}
|
|
3229
|
+
index += extended.consumed;
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3232
|
+
return nextStyle;
|
|
3233
|
+
}
|
|
3234
|
+
function parseAnsi(input) {
|
|
3235
|
+
const runs = [];
|
|
3236
|
+
let style = createDefaultStyle();
|
|
3237
|
+
let textStart = 0;
|
|
3238
|
+
let index = 0;
|
|
3239
|
+
while (index < input.length) {
|
|
3240
|
+
const char = input[index];
|
|
3241
|
+
if (char === "\n") {
|
|
3242
|
+
pushRun(runs, style, input.slice(textStart, index));
|
|
3243
|
+
pushRun(runs, style, "\n");
|
|
3244
|
+
index += 1;
|
|
3245
|
+
textStart = index;
|
|
3246
|
+
continue;
|
|
3247
|
+
}
|
|
3248
|
+
if (char === ESC2 && input[index + 1] === "[") {
|
|
3249
|
+
pushRun(runs, style, input.slice(textStart, index));
|
|
3250
|
+
const sequence = parseCsi(input, index);
|
|
3251
|
+
if (sequence.final === "m") {
|
|
3252
|
+
style = applySgr(style, sequence.params);
|
|
3253
|
+
}
|
|
3254
|
+
index = sequence.end;
|
|
3255
|
+
textStart = index;
|
|
3256
|
+
continue;
|
|
3257
|
+
}
|
|
3258
|
+
index += 1;
|
|
3259
|
+
}
|
|
3260
|
+
pushRun(runs, style, input.slice(textStart));
|
|
3261
|
+
return runs;
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
// ../terminal-png/src/png-renderer.ts
|
|
3265
|
+
import { Resvg } from "@resvg/resvg-js";
|
|
3266
|
+
|
|
3267
|
+
// ../terminal-png/src/font.ts
|
|
3268
|
+
import { readFileSync } from "node:fs";
|
|
3269
|
+
import { createRequire } from "node:module";
|
|
3270
|
+
import { dirname, join } from "node:path";
|
|
3271
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3272
|
+
var require2 = createRequire(import.meta.url);
|
|
3273
|
+
var fontPackageRoot = dirname(require2.resolve("jetbrains-mono/package.json"));
|
|
3274
|
+
var webfontRoot = join(fontPackageRoot, "fonts/webfonts");
|
|
3275
|
+
function readWebfontBase64(filename) {
|
|
3276
|
+
return readFileSync(join(webfontRoot, filename)).toString("base64");
|
|
3277
|
+
}
|
|
3278
|
+
function resolveAssetPath(filename) {
|
|
3279
|
+
return fileURLToPath2(new URL(`../assets/${filename}`, import.meta.url));
|
|
3280
|
+
}
|
|
3281
|
+
function createFontFace(base64, weight, style) {
|
|
3282
|
+
return `@font-face {
|
|
3283
|
+
font-family: 'JetBrains Mono';
|
|
3284
|
+
font-style: ${style};
|
|
3285
|
+
font-weight: ${weight};
|
|
3286
|
+
src: url('data:font/woff2;base64,${base64}') format('woff2');
|
|
3287
|
+
}`;
|
|
3288
|
+
}
|
|
3289
|
+
var JETBRAINS_MONO_BASE64 = readWebfontBase64("JetBrainsMono-Regular.woff2");
|
|
3290
|
+
var JETBRAINS_MONO_FONT_FILES = [
|
|
3291
|
+
resolveAssetPath("jetbrains-mono-400-normal.ttf"),
|
|
3292
|
+
resolveAssetPath("jetbrains-mono-700-normal.ttf"),
|
|
3293
|
+
resolveAssetPath("jetbrains-mono-400-italic.ttf"),
|
|
3294
|
+
resolveAssetPath("jetbrains-mono-700-italic.ttf")
|
|
3295
|
+
];
|
|
3296
|
+
var JETBRAINS_MONO_TTF_PATH = JETBRAINS_MONO_FONT_FILES[0];
|
|
3297
|
+
var FONT_FACE_CSS = [
|
|
3298
|
+
createFontFace(JETBRAINS_MONO_BASE64, 400, "normal"),
|
|
3299
|
+
createFontFace(readWebfontBase64("JetBrainsMono-Bold.woff2"), 700, "normal"),
|
|
3300
|
+
createFontFace(readWebfontBase64("JetBrainsMono-Italic.woff2"), 400, "italic"),
|
|
3301
|
+
createFontFace(readWebfontBase64("JetBrainsMono-BoldItalic.woff2"), 700, "italic")
|
|
3302
|
+
].join("\n");
|
|
3303
|
+
|
|
3304
|
+
// ../terminal-png/src/png-renderer.ts
|
|
3305
|
+
function renderPng(svg) {
|
|
3306
|
+
const resvg = new Resvg(svg, {
|
|
3307
|
+
font: {
|
|
3308
|
+
defaultFontFamily: "JetBrains Mono",
|
|
3309
|
+
fontFiles: [...JETBRAINS_MONO_FONT_FILES],
|
|
3310
|
+
loadSystemFonts: false,
|
|
3311
|
+
monospaceFamily: "JetBrains Mono"
|
|
3312
|
+
},
|
|
3313
|
+
fitTo: {
|
|
3314
|
+
mode: "zoom",
|
|
3315
|
+
value: 4
|
|
3316
|
+
}
|
|
3317
|
+
});
|
|
3318
|
+
const png = resvg.render();
|
|
3319
|
+
return Buffer.from(png.asPng());
|
|
3320
|
+
}
|
|
3321
|
+
|
|
3322
|
+
// ../terminal-png/src/svg-renderer.ts
|
|
3323
|
+
var ANSI_16_PALETTE = [
|
|
3324
|
+
"#282a2e",
|
|
3325
|
+
"#D74E6F",
|
|
3326
|
+
"#31BB71",
|
|
3327
|
+
"#D3E561",
|
|
3328
|
+
"#8056FF",
|
|
3329
|
+
"#ED61D7",
|
|
3330
|
+
"#04D7D7",
|
|
3331
|
+
"#C5C8C6",
|
|
3332
|
+
"#4B4B4B",
|
|
3333
|
+
"#FE5F86",
|
|
3334
|
+
"#00D787",
|
|
3335
|
+
"#EBFF71",
|
|
3336
|
+
"#8F69FF",
|
|
3337
|
+
"#FF7AEA",
|
|
3338
|
+
"#00FEFE",
|
|
3339
|
+
"#FFFFFF"
|
|
3340
|
+
];
|
|
3341
|
+
var XTERM_256_PALETTE = [
|
|
3342
|
+
"#000000",
|
|
3343
|
+
"#800000",
|
|
3344
|
+
"#008000",
|
|
3345
|
+
"#808000",
|
|
3346
|
+
"#000080",
|
|
3347
|
+
"#800080",
|
|
3348
|
+
"#008080",
|
|
3349
|
+
"#c0c0c0",
|
|
3350
|
+
"#808080",
|
|
3351
|
+
"#ff0000",
|
|
3352
|
+
"#00ff00",
|
|
3353
|
+
"#ffff00",
|
|
3354
|
+
"#0000ff",
|
|
3355
|
+
"#ff00ff",
|
|
3356
|
+
"#00ffff",
|
|
3357
|
+
"#ffffff",
|
|
3358
|
+
"#000000",
|
|
3359
|
+
"#00005f",
|
|
3360
|
+
"#000087",
|
|
3361
|
+
"#0000af",
|
|
3362
|
+
"#0000d7",
|
|
3363
|
+
"#0000ff",
|
|
3364
|
+
"#005f00",
|
|
3365
|
+
"#005f5f",
|
|
3366
|
+
"#005f87",
|
|
3367
|
+
"#005faf",
|
|
3368
|
+
"#005fd7",
|
|
3369
|
+
"#005fff",
|
|
3370
|
+
"#008700",
|
|
3371
|
+
"#00875f",
|
|
3372
|
+
"#008787",
|
|
3373
|
+
"#0087af",
|
|
3374
|
+
"#0087d7",
|
|
3375
|
+
"#0087ff",
|
|
3376
|
+
"#00af00",
|
|
3377
|
+
"#00af5f",
|
|
3378
|
+
"#00af87",
|
|
3379
|
+
"#00afaf",
|
|
3380
|
+
"#00afd7",
|
|
3381
|
+
"#00afff",
|
|
3382
|
+
"#00d700",
|
|
3383
|
+
"#00d75f",
|
|
3384
|
+
"#00d787",
|
|
3385
|
+
"#00d7af",
|
|
3386
|
+
"#00d7d7",
|
|
3387
|
+
"#00d7ff",
|
|
3388
|
+
"#00ff00",
|
|
3389
|
+
"#00ff5f",
|
|
3390
|
+
"#00ff87",
|
|
3391
|
+
"#00ffaf",
|
|
3392
|
+
"#00ffd7",
|
|
3393
|
+
"#00ffff",
|
|
3394
|
+
"#5f0000",
|
|
3395
|
+
"#5f005f",
|
|
3396
|
+
"#5f0087",
|
|
3397
|
+
"#5f00af",
|
|
3398
|
+
"#5f00d7",
|
|
3399
|
+
"#5f00ff",
|
|
3400
|
+
"#5f5f00",
|
|
3401
|
+
"#5f5f5f",
|
|
3402
|
+
"#5f5f87",
|
|
3403
|
+
"#5f5faf",
|
|
3404
|
+
"#5f5fd7",
|
|
3405
|
+
"#5f5fff",
|
|
3406
|
+
"#5f8700",
|
|
3407
|
+
"#5f875f",
|
|
3408
|
+
"#5f8787",
|
|
3409
|
+
"#5f87af",
|
|
3410
|
+
"#5f87d7",
|
|
3411
|
+
"#5f87ff",
|
|
3412
|
+
"#5faf00",
|
|
3413
|
+
"#5faf5f",
|
|
3414
|
+
"#5faf87",
|
|
3415
|
+
"#5fafaf",
|
|
3416
|
+
"#5fafd7",
|
|
3417
|
+
"#5fafff",
|
|
3418
|
+
"#5fd700",
|
|
3419
|
+
"#5fd75f",
|
|
3420
|
+
"#5fd787",
|
|
3421
|
+
"#5fd7af",
|
|
3422
|
+
"#5fd7d7",
|
|
3423
|
+
"#5fd7ff",
|
|
3424
|
+
"#5fff00",
|
|
3425
|
+
"#5fff5f",
|
|
3426
|
+
"#5fff87",
|
|
3427
|
+
"#5fffaf",
|
|
3428
|
+
"#5fffd7",
|
|
3429
|
+
"#5fffff",
|
|
3430
|
+
"#870000",
|
|
3431
|
+
"#87005f",
|
|
3432
|
+
"#870087",
|
|
3433
|
+
"#8700af",
|
|
3434
|
+
"#8700d7",
|
|
3435
|
+
"#8700ff",
|
|
3436
|
+
"#875f00",
|
|
3437
|
+
"#875f5f",
|
|
3438
|
+
"#875f87",
|
|
3439
|
+
"#875faf",
|
|
3440
|
+
"#875fd7",
|
|
3441
|
+
"#875fff",
|
|
3442
|
+
"#878700",
|
|
3443
|
+
"#87875f",
|
|
3444
|
+
"#878787",
|
|
3445
|
+
"#8787af",
|
|
3446
|
+
"#8787d7",
|
|
3447
|
+
"#8787ff",
|
|
3448
|
+
"#87af00",
|
|
3449
|
+
"#87af5f",
|
|
3450
|
+
"#87af87",
|
|
3451
|
+
"#87afaf",
|
|
3452
|
+
"#87afd7",
|
|
3453
|
+
"#87afff",
|
|
3454
|
+
"#87d700",
|
|
3455
|
+
"#87d75f",
|
|
3456
|
+
"#87d787",
|
|
3457
|
+
"#87d7af",
|
|
3458
|
+
"#87d7d7",
|
|
3459
|
+
"#87d7ff",
|
|
3460
|
+
"#87ff00",
|
|
3461
|
+
"#87ff5f",
|
|
3462
|
+
"#87ff87",
|
|
3463
|
+
"#87ffaf",
|
|
3464
|
+
"#87ffd7",
|
|
3465
|
+
"#87ffff",
|
|
3466
|
+
"#af0000",
|
|
3467
|
+
"#af005f",
|
|
3468
|
+
"#af0087",
|
|
3469
|
+
"#af00af",
|
|
3470
|
+
"#af00d7",
|
|
3471
|
+
"#af00ff",
|
|
3472
|
+
"#af5f00",
|
|
3473
|
+
"#af5f5f",
|
|
3474
|
+
"#af5f87",
|
|
3475
|
+
"#af5faf",
|
|
3476
|
+
"#af5fd7",
|
|
3477
|
+
"#af5fff",
|
|
3478
|
+
"#af8700",
|
|
3479
|
+
"#af875f",
|
|
3480
|
+
"#af8787",
|
|
3481
|
+
"#af87af",
|
|
3482
|
+
"#af87d7",
|
|
3483
|
+
"#af87ff",
|
|
3484
|
+
"#afaf00",
|
|
3485
|
+
"#afaf5f",
|
|
3486
|
+
"#afaf87",
|
|
3487
|
+
"#afafaf",
|
|
3488
|
+
"#afafd7",
|
|
3489
|
+
"#afafff",
|
|
3490
|
+
"#afd700",
|
|
3491
|
+
"#afd75f",
|
|
3492
|
+
"#afd787",
|
|
3493
|
+
"#afd7af",
|
|
3494
|
+
"#afd7d7",
|
|
3495
|
+
"#afd7ff",
|
|
3496
|
+
"#afff00",
|
|
3497
|
+
"#afff5f",
|
|
3498
|
+
"#afff87",
|
|
3499
|
+
"#afffaf",
|
|
3500
|
+
"#afffd7",
|
|
3501
|
+
"#afffff",
|
|
3502
|
+
"#d70000",
|
|
3503
|
+
"#d7005f",
|
|
3504
|
+
"#d70087",
|
|
3505
|
+
"#d700af",
|
|
3506
|
+
"#d700d7",
|
|
3507
|
+
"#d700ff",
|
|
3508
|
+
"#d75f00",
|
|
3509
|
+
"#d75f5f",
|
|
3510
|
+
"#d75f87",
|
|
3511
|
+
"#d75faf",
|
|
3512
|
+
"#d75fd7",
|
|
3513
|
+
"#d75fff",
|
|
3514
|
+
"#d78700",
|
|
3515
|
+
"#d7875f",
|
|
3516
|
+
"#d78787",
|
|
3517
|
+
"#d787af",
|
|
3518
|
+
"#d787d7",
|
|
3519
|
+
"#d787ff",
|
|
3520
|
+
"#d7af00",
|
|
3521
|
+
"#d7af5f",
|
|
3522
|
+
"#d7af87",
|
|
3523
|
+
"#d7afaf",
|
|
3524
|
+
"#d7afd7",
|
|
3525
|
+
"#d7afff",
|
|
3526
|
+
"#d7d700",
|
|
3527
|
+
"#d7d75f",
|
|
3528
|
+
"#d7d787",
|
|
3529
|
+
"#d7d7af",
|
|
3530
|
+
"#d7d7d7",
|
|
3531
|
+
"#d7d7ff",
|
|
3532
|
+
"#d7ff00",
|
|
3533
|
+
"#d7ff5f",
|
|
3534
|
+
"#d7ff87",
|
|
3535
|
+
"#d7ffaf",
|
|
3536
|
+
"#d7ffd7",
|
|
3537
|
+
"#d7ffff",
|
|
3538
|
+
"#ff0000",
|
|
3539
|
+
"#ff005f",
|
|
3540
|
+
"#ff0087",
|
|
3541
|
+
"#ff00af",
|
|
3542
|
+
"#ff00d7",
|
|
3543
|
+
"#ff00ff",
|
|
3544
|
+
"#ff5f00",
|
|
3545
|
+
"#ff5f5f",
|
|
3546
|
+
"#ff5f87",
|
|
3547
|
+
"#ff5faf",
|
|
3548
|
+
"#ff5fd7",
|
|
3549
|
+
"#ff5fff",
|
|
3550
|
+
"#ff8700",
|
|
3551
|
+
"#ff875f",
|
|
3552
|
+
"#ff8787",
|
|
3553
|
+
"#ff87af",
|
|
3554
|
+
"#ff87d7",
|
|
3555
|
+
"#ff87ff",
|
|
3556
|
+
"#ffaf00",
|
|
3557
|
+
"#ffaf5f",
|
|
3558
|
+
"#ffaf87",
|
|
3559
|
+
"#ffafaf",
|
|
3560
|
+
"#ffafd7",
|
|
3561
|
+
"#ffafff",
|
|
3562
|
+
"#ffd700",
|
|
3563
|
+
"#ffd75f",
|
|
3564
|
+
"#ffd787",
|
|
3565
|
+
"#ffd7af",
|
|
3566
|
+
"#ffd7d7",
|
|
3567
|
+
"#ffd7ff",
|
|
3568
|
+
"#ffff00",
|
|
3569
|
+
"#ffff5f",
|
|
3570
|
+
"#ffff87",
|
|
3571
|
+
"#ffffaf",
|
|
3572
|
+
"#ffffd7",
|
|
3573
|
+
"#ffffff",
|
|
3574
|
+
"#080808",
|
|
3575
|
+
"#121212",
|
|
3576
|
+
"#1c1c1c",
|
|
3577
|
+
"#262626",
|
|
3578
|
+
"#303030",
|
|
3579
|
+
"#3a3a3a",
|
|
3580
|
+
"#444444",
|
|
3581
|
+
"#4e4e4e",
|
|
3582
|
+
"#585858",
|
|
3583
|
+
"#606060",
|
|
3584
|
+
"#666666",
|
|
3585
|
+
"#767676",
|
|
3586
|
+
"#808080",
|
|
3587
|
+
"#8a8a8a",
|
|
3588
|
+
"#949494",
|
|
3589
|
+
"#9e9e9e",
|
|
3590
|
+
"#a8a8a8",
|
|
3591
|
+
"#b2b2b2",
|
|
3592
|
+
"#bcbcbc",
|
|
3593
|
+
"#c6c6c6",
|
|
3594
|
+
"#d0d0d0",
|
|
3595
|
+
"#dadada",
|
|
3596
|
+
"#e4e4e4",
|
|
3597
|
+
"#eeeeee"
|
|
3598
|
+
];
|
|
3599
|
+
var BACKGROUND = "#171717";
|
|
3600
|
+
var DEFAULT_FOREGROUND = "#c4c4c4";
|
|
3601
|
+
var FONT_FAMILY = "JetBrains Mono";
|
|
3602
|
+
var FONT_SIZE = 14;
|
|
3603
|
+
var LINE_HEIGHT = 1.2;
|
|
3604
|
+
var CHARACTER_WIDTH = 8.412666666666667;
|
|
3605
|
+
var TEXT_BOTTOM_PADDING = 11.6;
|
|
3606
|
+
var DEFAULT_PADDING = {
|
|
3607
|
+
top: 20,
|
|
3608
|
+
right: 40,
|
|
3609
|
+
bottom: 20,
|
|
3610
|
+
left: 20
|
|
3611
|
+
};
|
|
3612
|
+
var WINDOW_BAR_HEIGHT = 15;
|
|
3613
|
+
function renderSvg(runs, options = {}) {
|
|
3614
|
+
const padding = options.padding === void 0 ? DEFAULT_PADDING : {
|
|
3615
|
+
top: options.padding,
|
|
3616
|
+
right: options.padding,
|
|
3617
|
+
bottom: options.padding,
|
|
3618
|
+
left: options.padding
|
|
3619
|
+
};
|
|
3620
|
+
const showWindow = options.window ?? true;
|
|
3621
|
+
const titleBarHeight = showWindow ? WINDOW_BAR_HEIGHT : 0;
|
|
3622
|
+
const textStartX = padding.left;
|
|
3623
|
+
const lineHeightPx = FONT_SIZE * LINE_HEIGHT;
|
|
3624
|
+
const textStartY = titleBarHeight + padding.top + lineHeightPx;
|
|
3625
|
+
const lines = splitIntoLines(runs);
|
|
3626
|
+
const contentWidth = measureLines(lines);
|
|
3627
|
+
const width = padding.left + contentWidth + padding.right;
|
|
3628
|
+
const height = titleBarHeight + padding.top + lines.length * lineHeightPx + padding.bottom + TEXT_BOTTOM_PADDING;
|
|
3629
|
+
const svgWidth = formatNumber(width);
|
|
3630
|
+
const svgHeight = formatNumber(height);
|
|
3631
|
+
const viewBox = `0 0 ${svgWidth} ${svgHeight}`;
|
|
3632
|
+
const textElements = renderLines(lines, textStartX, textStartY, lineHeightPx);
|
|
3633
|
+
return [
|
|
3634
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="${svgWidth}" height="${svgHeight}" viewBox="${viewBox}">`,
|
|
3635
|
+
"<defs>",
|
|
3636
|
+
"<style><![CDATA[",
|
|
3637
|
+
FONT_FACE_CSS,
|
|
3638
|
+
"]]></style>",
|
|
3639
|
+
"</defs>",
|
|
3640
|
+
`<rect x="0" y="0" width="${svgWidth}" height="${svgHeight}" fill="${BACKGROUND}" />`,
|
|
3641
|
+
showWindow ? renderWindowControls() : "",
|
|
3642
|
+
`<g font-family="${FONT_FAMILY}" font-size="${formatNumber(FONT_SIZE)}px" fill="${DEFAULT_FOREGROUND}">`,
|
|
3643
|
+
textElements,
|
|
3644
|
+
"</g>",
|
|
3645
|
+
"</svg>"
|
|
3646
|
+
].join("");
|
|
3647
|
+
}
|
|
3648
|
+
function splitIntoLines(runs) {
|
|
3649
|
+
const lines = [[]];
|
|
3650
|
+
for (const run of runs) {
|
|
3651
|
+
if (run.text === "\n") {
|
|
3652
|
+
lines.push([]);
|
|
3653
|
+
continue;
|
|
3654
|
+
}
|
|
3655
|
+
lines[lines.length - 1]?.push(run);
|
|
3656
|
+
}
|
|
3657
|
+
return lines;
|
|
3658
|
+
}
|
|
3659
|
+
function measureLines(lines) {
|
|
3660
|
+
return Math.max(
|
|
3661
|
+
...lines.map((line) => line.reduce((width, run) => width + displayWidth(run.text) * CHARACTER_WIDTH, 0)),
|
|
3662
|
+
0
|
|
3663
|
+
);
|
|
3664
|
+
}
|
|
3665
|
+
function displayWidth(text) {
|
|
3666
|
+
const segmenter = new Intl.Segmenter();
|
|
3667
|
+
let width = 0;
|
|
3668
|
+
for (const { segment } of segmenter.segment(text)) {
|
|
3669
|
+
const cp = segment.codePointAt(0);
|
|
3670
|
+
width += cp !== void 0 && isWideCodePoint(cp) ? 2 : 1;
|
|
3671
|
+
}
|
|
3672
|
+
return width;
|
|
3673
|
+
}
|
|
3674
|
+
function isWideCodePoint(cp) {
|
|
3675
|
+
return cp >= 4352 && cp <= 4447 || // Hangul Jamo
|
|
3676
|
+
cp === 9001 || cp === 9002 || // Angle brackets
|
|
3677
|
+
cp >= 11904 && cp <= 12350 || // CJK Radicals, Kangxi
|
|
3678
|
+
cp >= 12353 && cp <= 13247 || // Hiragana, Katakana, CJK symbols
|
|
3679
|
+
cp >= 13312 && cp <= 19903 || // CJK Extension A
|
|
3680
|
+
cp >= 19968 && cp <= 42191 || // CJK Unified Ideographs
|
|
3681
|
+
cp >= 43360 && cp <= 43391 || // Hangul Jamo Extended-A
|
|
3682
|
+
cp >= 44032 && cp <= 55215 || // Hangul Syllables
|
|
3683
|
+
cp >= 63744 && cp <= 64255 || // CJK Compatibility Ideographs
|
|
3684
|
+
cp >= 65040 && cp <= 65049 || // Vertical Forms
|
|
3685
|
+
cp >= 65072 && cp <= 65135 || // CJK Compatibility Forms
|
|
3686
|
+
cp >= 65280 && cp <= 65376 || // Fullwidth Latin, Halfwidth Katakana
|
|
3687
|
+
cp >= 65504 && cp <= 65510 || // Fullwidth Signs
|
|
3688
|
+
cp >= 110592 && cp <= 110847 || // Kana Supplement
|
|
3689
|
+
cp >= 126980 && cp <= 126980 || // Mahjong tile
|
|
3690
|
+
cp >= 127183 && cp <= 127183 || // Playing card black joker
|
|
3691
|
+
cp >= 127488 && cp <= 131069 || // Enclosed CJK + Emoji
|
|
3692
|
+
cp >= 131072 && cp <= 196605 || // CJK Extension B–F
|
|
3693
|
+
cp >= 196608 && cp <= 262141;
|
|
3694
|
+
}
|
|
3695
|
+
function renderLines(lines, textStartX, textStartY, lineHeightPx) {
|
|
3696
|
+
return lines.map((line, index) => {
|
|
3697
|
+
const y = formatNumber(textStartY + index * lineHeightPx);
|
|
3698
|
+
if (line.length === 0) {
|
|
3699
|
+
return `<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve"/>`;
|
|
3700
|
+
}
|
|
3701
|
+
return [
|
|
3702
|
+
`<text x="${formatNumber(textStartX)}" y="${y}" xml:space="preserve">`,
|
|
3703
|
+
line.map(renderRun).join(""),
|
|
3704
|
+
"</text>"
|
|
3705
|
+
].join("");
|
|
3706
|
+
}).join("");
|
|
3707
|
+
}
|
|
3708
|
+
function renderRun(run) {
|
|
3709
|
+
const attributes = ['xml:space="preserve"'];
|
|
3710
|
+
const color = resolveColor(run.fg);
|
|
3711
|
+
const textDecorations = [];
|
|
3712
|
+
if (color !== DEFAULT_FOREGROUND) {
|
|
3713
|
+
attributes.push(`fill="${escapeXmlAttribute(color)}"`);
|
|
3714
|
+
}
|
|
3715
|
+
if (run.bold) {
|
|
3716
|
+
attributes.push('font-weight="bold"');
|
|
3717
|
+
}
|
|
3718
|
+
if (run.italic) {
|
|
3719
|
+
attributes.push('font-style="italic"');
|
|
3720
|
+
}
|
|
3721
|
+
if (run.underline) {
|
|
3722
|
+
textDecorations.push("underline");
|
|
3723
|
+
}
|
|
3724
|
+
if (run.strikethrough) {
|
|
3725
|
+
textDecorations.push("line-through");
|
|
3726
|
+
}
|
|
3727
|
+
if (textDecorations.length > 0) {
|
|
3728
|
+
attributes.push(`text-decoration="${textDecorations.join(" ")}"`);
|
|
3729
|
+
}
|
|
3730
|
+
if (run.dim) {
|
|
3731
|
+
attributes.push('opacity="0.7"');
|
|
3732
|
+
}
|
|
3733
|
+
return `<tspan ${attributes.join(" ")}>${escapeXmlText(run.text)}</tspan>`;
|
|
3734
|
+
}
|
|
3735
|
+
function renderWindowControls() {
|
|
3736
|
+
return [
|
|
3737
|
+
'<circle cx="13.5" cy="12" r="5.5" fill="#FF5A54" />',
|
|
3738
|
+
'<circle cx="32.5" cy="12" r="5.5" fill="#E6BF29" />',
|
|
3739
|
+
'<circle cx="51.5" cy="12" r="5.5" fill="#52C12B" />'
|
|
3740
|
+
].join("");
|
|
3741
|
+
}
|
|
3742
|
+
function resolveColor(color) {
|
|
3743
|
+
if (color === null) {
|
|
3744
|
+
return DEFAULT_FOREGROUND;
|
|
3745
|
+
}
|
|
3746
|
+
if (color.type === "ansi4") {
|
|
3747
|
+
return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
|
|
3748
|
+
}
|
|
3749
|
+
if (color.type === "ansi8") {
|
|
3750
|
+
if (color.index < ANSI_16_PALETTE.length) {
|
|
3751
|
+
return ANSI_16_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
|
|
3752
|
+
}
|
|
3753
|
+
return XTERM_256_PALETTE[color.index] ?? DEFAULT_FOREGROUND;
|
|
3754
|
+
}
|
|
3755
|
+
return `rgb(${color.r},${color.g},${color.b})`;
|
|
3756
|
+
}
|
|
3757
|
+
function escapeXmlText(value) {
|
|
3758
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
3759
|
+
}
|
|
3760
|
+
function escapeXmlAttribute(value) {
|
|
3761
|
+
return escapeXmlText(value).replaceAll('"', """).replaceAll("'", "'");
|
|
3762
|
+
}
|
|
3763
|
+
function formatNumber(value) {
|
|
3764
|
+
return value.toFixed(2);
|
|
3765
|
+
}
|
|
3766
|
+
|
|
3767
|
+
// ../terminal-png/src/index.ts
|
|
3768
|
+
async function renderTerminalPng(ansiText, options = {}) {
|
|
3769
|
+
const runs = parseAnsi(ansiText);
|
|
3770
|
+
const svg = renderSvg(runs, {
|
|
3771
|
+
padding: options.padding,
|
|
3772
|
+
window: options.window
|
|
3773
|
+
});
|
|
3774
|
+
const png = renderPng(svg);
|
|
3775
|
+
if (options.output) {
|
|
3776
|
+
await writeFile(options.output, png);
|
|
3777
|
+
}
|
|
3778
|
+
return png;
|
|
3779
|
+
}
|
|
3780
|
+
|
|
1582
3781
|
// src/commands/screenshot.ts
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
window: S11.Optional(S11.Boolean({ description: "Include terminal window chrome", default: true })),
|
|
1589
|
-
padding: S11.Optional(S11.Number({ short: "p", description: "Padding around terminal content" }))
|
|
3782
|
+
var params11 = S.Object({
|
|
3783
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" })),
|
|
3784
|
+
output: S.String({ short: "o", description: "Path to the output PNG file" }),
|
|
3785
|
+
window: S.Optional(S.Boolean({ description: "Include terminal window chrome", default: true })),
|
|
3786
|
+
padding: S.Optional(S.Number({ short: "p", description: "Padding around terminal content" }))
|
|
1590
3787
|
});
|
|
1591
|
-
var screenshot =
|
|
3788
|
+
var screenshot = defineCommand({
|
|
1592
3789
|
name: "screenshot",
|
|
1593
3790
|
description: "Capture the current terminal screen as a PNG image",
|
|
1594
3791
|
scope: ["cli"],
|
|
@@ -1606,12 +3803,11 @@ var screenshot = defineCommand11({
|
|
|
1606
3803
|
});
|
|
1607
3804
|
|
|
1608
3805
|
// src/commands/send-signal.ts
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
session: S12.Optional(S12.String({ short: "s", description: "Session name" }))
|
|
3806
|
+
var params12 = S.Object({
|
|
3807
|
+
signal: S.String({ description: "Signal to send to the session process" }),
|
|
3808
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" }))
|
|
1613
3809
|
});
|
|
1614
|
-
var sendSignal =
|
|
3810
|
+
var sendSignal = defineCommand({
|
|
1615
3811
|
name: "send-signal",
|
|
1616
3812
|
description: "Send a process signal to an active terminal session",
|
|
1617
3813
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1625,12 +3821,11 @@ var sendSignal = defineCommand12({
|
|
|
1625
3821
|
});
|
|
1626
3822
|
|
|
1627
3823
|
// src/commands/type.ts
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
session: S13.Optional(S13.String({ short: "s", description: "Session name" }))
|
|
3824
|
+
var params13 = S.Object({
|
|
3825
|
+
text: S.String({ description: "Text to write to the session" }),
|
|
3826
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" }))
|
|
1632
3827
|
});
|
|
1633
|
-
var type =
|
|
3828
|
+
var type = defineCommand({
|
|
1634
3829
|
name: "type",
|
|
1635
3830
|
description: "Write text to an active terminal session character-by-character with delay",
|
|
1636
3831
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1644,14 +3839,13 @@ var type = defineCommand13({
|
|
|
1644
3839
|
});
|
|
1645
3840
|
|
|
1646
3841
|
// src/commands/uninstall.ts
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
agent: S14.Enum(installableAgents, {
|
|
3842
|
+
var params14 = S.Object({
|
|
3843
|
+
agent: S.Enum(installableAgents, {
|
|
1650
3844
|
description: "Agent to uninstall terminal-pilot from",
|
|
1651
3845
|
default: DEFAULT_INSTALL_AGENT
|
|
1652
3846
|
})
|
|
1653
3847
|
});
|
|
1654
|
-
var uninstall =
|
|
3848
|
+
var uninstall = defineCommand({
|
|
1655
3849
|
name: "uninstall",
|
|
1656
3850
|
description: "Remove the terminal-pilot CLI skill.",
|
|
1657
3851
|
scope: ["cli"],
|
|
@@ -1687,19 +3881,18 @@ var uninstall = defineCommand14({
|
|
|
1687
3881
|
});
|
|
1688
3882
|
|
|
1689
3883
|
// src/commands/wait-for.ts
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
S15.Boolean({
|
|
3884
|
+
var params15 = S.Object({
|
|
3885
|
+
pattern: S.String({ description: "Regular expression pattern to wait for" }),
|
|
3886
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" })),
|
|
3887
|
+
timeout: S.Optional(S.Number({ short: "t", description: "Maximum wait time in milliseconds" })),
|
|
3888
|
+
literal: S.Optional(
|
|
3889
|
+
S.Boolean({
|
|
1697
3890
|
short: "l",
|
|
1698
3891
|
description: "When true, treat pattern as a literal string instead of a regex"
|
|
1699
3892
|
})
|
|
1700
3893
|
)
|
|
1701
3894
|
});
|
|
1702
|
-
var waitFor =
|
|
3895
|
+
var waitFor = defineCommand({
|
|
1703
3896
|
name: "wait-for",
|
|
1704
3897
|
description: "Wait for terminal output to match a pattern",
|
|
1705
3898
|
scope: ["cli", "mcp", "sdk"],
|
|
@@ -1714,12 +3907,11 @@ var waitFor = defineCommand15({
|
|
|
1714
3907
|
});
|
|
1715
3908
|
|
|
1716
3909
|
// src/commands/wait-for-exit.ts
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
timeout: S16.Optional(S16.Number({ short: "t", description: "Maximum wait time in milliseconds" }))
|
|
3910
|
+
var params16 = S.Object({
|
|
3911
|
+
session: S.Optional(S.String({ short: "s", description: "Session name" })),
|
|
3912
|
+
timeout: S.Optional(S.Number({ short: "t", description: "Maximum wait time in milliseconds" }))
|
|
1721
3913
|
});
|
|
1722
|
-
var waitForExit2 =
|
|
3914
|
+
var waitForExit2 = defineCommand({
|
|
1723
3915
|
name: "wait-for-exit",
|
|
1724
3916
|
description: "Wait for a terminal session process to finish",
|
|
1725
3917
|
scope: ["cli", "mcp", "sdk"],
|