@osovv/vvcode 0.4.1

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/vvcode.js ADDED
@@ -0,0 +1,1825 @@
1
+ // @bun
2
+ // node_modules/citty/dist/_chunks/libs/scule.mjs
3
+ var NUMBER_CHAR_RE = /\d/;
4
+ var STR_SPLITTERS = [
5
+ "-",
6
+ "_",
7
+ "/",
8
+ "."
9
+ ];
10
+ function isUppercase(char = "") {
11
+ if (NUMBER_CHAR_RE.test(char))
12
+ return;
13
+ return char !== char.toLowerCase();
14
+ }
15
+ function splitByCase(str, separators) {
16
+ const splitters = separators ?? STR_SPLITTERS;
17
+ const parts = [];
18
+ if (!str || typeof str !== "string")
19
+ return parts;
20
+ let buff = "";
21
+ let previousUpper;
22
+ let previousSplitter;
23
+ for (const char of str) {
24
+ const isSplitter = splitters.includes(char);
25
+ if (isSplitter === true) {
26
+ parts.push(buff);
27
+ buff = "";
28
+ previousUpper = undefined;
29
+ continue;
30
+ }
31
+ const isUpper = isUppercase(char);
32
+ if (previousSplitter === false) {
33
+ if (previousUpper === false && isUpper === true) {
34
+ parts.push(buff);
35
+ buff = char;
36
+ previousUpper = isUpper;
37
+ continue;
38
+ }
39
+ if (previousUpper === true && isUpper === false && buff.length > 1) {
40
+ const lastChar = buff.at(-1);
41
+ parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
42
+ buff = lastChar + char;
43
+ previousUpper = isUpper;
44
+ continue;
45
+ }
46
+ }
47
+ buff += char;
48
+ previousUpper = isUpper;
49
+ previousSplitter = isSplitter;
50
+ }
51
+ parts.push(buff);
52
+ return parts;
53
+ }
54
+ function upperFirst(str) {
55
+ return str ? str[0].toUpperCase() + str.slice(1) : "";
56
+ }
57
+ function lowerFirst(str) {
58
+ return str ? str[0].toLowerCase() + str.slice(1) : "";
59
+ }
60
+ function pascalCase(str, opts) {
61
+ return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => upperFirst(opts?.normalize ? p.toLowerCase() : p)).join("") : "";
62
+ }
63
+ function camelCase(str, opts) {
64
+ return lowerFirst(pascalCase(str || "", opts));
65
+ }
66
+ function kebabCase(str, joiner) {
67
+ return str ? (Array.isArray(str) ? str : splitByCase(str)).map((p) => p.toLowerCase()).join(joiner ?? "-") : "";
68
+ }
69
+ function snakeCase(str) {
70
+ return kebabCase(str || "", "_");
71
+ }
72
+
73
+ // node_modules/citty/dist/index.mjs
74
+ import { parseArgs as parseArgs$1 } from "util";
75
+ function toArray(val) {
76
+ if (Array.isArray(val))
77
+ return val;
78
+ return val === undefined ? [] : [val];
79
+ }
80
+ function formatLineColumns(lines, linePrefix = "") {
81
+ const maxLength = [];
82
+ for (const line of lines)
83
+ for (const [i, element] of line.entries())
84
+ maxLength[i] = Math.max(maxLength[i] || 0, element.length);
85
+ return lines.map((l) => l.map((c, i) => linePrefix + c[i === 0 ? "padStart" : "padEnd"](maxLength[i])).join(" ")).join(`
86
+ `);
87
+ }
88
+ function resolveValue(input) {
89
+ return typeof input === "function" ? input() : input;
90
+ }
91
+ var CLIError = class extends Error {
92
+ code;
93
+ constructor(message, code) {
94
+ super(message);
95
+ this.name = "CLIError";
96
+ this.code = code;
97
+ }
98
+ };
99
+ function parseRawArgs(args = [], opts = {}) {
100
+ const booleans = new Set(opts.boolean || []);
101
+ const strings = new Set(opts.string || []);
102
+ const aliasMap = opts.alias || {};
103
+ const defaults = opts.default || {};
104
+ const aliasToMain = /* @__PURE__ */ new Map;
105
+ const mainToAliases = /* @__PURE__ */ new Map;
106
+ for (const [key, value] of Object.entries(aliasMap)) {
107
+ const targets = value;
108
+ for (const target of targets) {
109
+ aliasToMain.set(key, target);
110
+ if (!mainToAliases.has(target))
111
+ mainToAliases.set(target, []);
112
+ mainToAliases.get(target).push(key);
113
+ aliasToMain.set(target, key);
114
+ if (!mainToAliases.has(key))
115
+ mainToAliases.set(key, []);
116
+ mainToAliases.get(key).push(target);
117
+ }
118
+ }
119
+ const options = {};
120
+ function getType(name) {
121
+ if (booleans.has(name))
122
+ return "boolean";
123
+ const aliases = mainToAliases.get(name) || [];
124
+ for (const alias of aliases)
125
+ if (booleans.has(alias))
126
+ return "boolean";
127
+ return "string";
128
+ }
129
+ function isStringType(name) {
130
+ if (strings.has(name))
131
+ return true;
132
+ const aliases = mainToAliases.get(name) || [];
133
+ for (const alias of aliases)
134
+ if (strings.has(alias))
135
+ return true;
136
+ return false;
137
+ }
138
+ const allOptions = new Set([
139
+ ...booleans,
140
+ ...strings,
141
+ ...Object.keys(aliasMap),
142
+ ...Object.values(aliasMap).flat(),
143
+ ...Object.keys(defaults)
144
+ ]);
145
+ for (const name of allOptions)
146
+ if (!options[name])
147
+ options[name] = {
148
+ type: getType(name),
149
+ default: defaults[name]
150
+ };
151
+ for (const [alias, main] of aliasToMain.entries())
152
+ if (alias.length === 1 && options[main] && !options[main].short)
153
+ options[main].short = alias;
154
+ const processedArgs = [];
155
+ const negatedFlags = {};
156
+ for (let i = 0;i < args.length; i++) {
157
+ const arg = args[i];
158
+ if (arg === "--") {
159
+ processedArgs.push(...args.slice(i));
160
+ break;
161
+ }
162
+ if (arg.startsWith("--no-")) {
163
+ const flagName = arg.slice(5);
164
+ negatedFlags[flagName] = true;
165
+ continue;
166
+ }
167
+ processedArgs.push(arg);
168
+ }
169
+ let parsed;
170
+ try {
171
+ parsed = parseArgs$1({
172
+ args: processedArgs,
173
+ options: Object.keys(options).length > 0 ? options : undefined,
174
+ allowPositionals: true,
175
+ strict: false
176
+ });
177
+ } catch {
178
+ parsed = {
179
+ values: {},
180
+ positionals: processedArgs
181
+ };
182
+ }
183
+ const out = { _: [] };
184
+ out._ = parsed.positionals;
185
+ for (const [key, value] of Object.entries(parsed.values)) {
186
+ let coerced = value;
187
+ if (getType(key) === "boolean" && typeof value === "string")
188
+ coerced = value !== "false";
189
+ else if (isStringType(key) && typeof value === "boolean")
190
+ coerced = "";
191
+ out[key] = coerced;
192
+ }
193
+ for (const [name] of Object.entries(negatedFlags)) {
194
+ out[name] = false;
195
+ const mainName = aliasToMain.get(name);
196
+ if (mainName)
197
+ out[mainName] = false;
198
+ const aliases = mainToAliases.get(name);
199
+ if (aliases)
200
+ for (const alias of aliases)
201
+ out[alias] = false;
202
+ }
203
+ for (const [alias, main] of aliasToMain.entries()) {
204
+ if (out[alias] !== undefined && out[main] === undefined)
205
+ out[main] = out[alias];
206
+ if (out[main] !== undefined && out[alias] === undefined)
207
+ out[alias] = out[main];
208
+ if (out[alias] !== out[main] && defaults[main] === out[main])
209
+ out[main] = out[alias];
210
+ }
211
+ return out;
212
+ }
213
+ var noColor = /* @__PURE__ */ (() => {
214
+ const env = globalThis.process?.env ?? {};
215
+ return env.NO_COLOR === "1" || env.TERM === "dumb" || env.TEST || env.CI;
216
+ })();
217
+ var _c = (c, r = 39) => (t) => noColor ? t : `\x1B[${c}m${t}\x1B[${r}m`;
218
+ var bold = /* @__PURE__ */ _c(1, 22);
219
+ var cyan = /* @__PURE__ */ _c(36);
220
+ var gray = /* @__PURE__ */ _c(90);
221
+ var underline = /* @__PURE__ */ _c(4, 24);
222
+ function parseArgs(rawArgs, argsDef) {
223
+ const parseOptions = {
224
+ boolean: [],
225
+ string: [],
226
+ alias: {},
227
+ default: {}
228
+ };
229
+ const args = resolveArgs(argsDef);
230
+ for (const arg of args) {
231
+ if (arg.type === "positional")
232
+ continue;
233
+ if (arg.type === "string" || arg.type === "enum")
234
+ parseOptions.string.push(arg.name);
235
+ else if (arg.type === "boolean")
236
+ parseOptions.boolean.push(arg.name);
237
+ if (arg.default !== undefined)
238
+ parseOptions.default[arg.name] = arg.default;
239
+ if (arg.alias)
240
+ parseOptions.alias[arg.name] = arg.alias;
241
+ const camelName = camelCase(arg.name);
242
+ const kebabName = kebabCase(arg.name);
243
+ if (camelName !== arg.name || kebabName !== arg.name) {
244
+ const existingAliases = toArray(parseOptions.alias[arg.name] || []);
245
+ if (camelName !== arg.name && !existingAliases.includes(camelName))
246
+ existingAliases.push(camelName);
247
+ if (kebabName !== arg.name && !existingAliases.includes(kebabName))
248
+ existingAliases.push(kebabName);
249
+ if (existingAliases.length > 0)
250
+ parseOptions.alias[arg.name] = existingAliases;
251
+ }
252
+ }
253
+ const parsed = parseRawArgs(rawArgs, parseOptions);
254
+ const [...positionalArguments] = parsed._;
255
+ const parsedArgsProxy = new Proxy(parsed, { get(target, prop) {
256
+ return target[prop] ?? target[camelCase(prop)] ?? target[kebabCase(prop)];
257
+ } });
258
+ for (const [, arg] of args.entries())
259
+ if (arg.type === "positional") {
260
+ const nextPositionalArgument = positionalArguments.shift();
261
+ if (nextPositionalArgument !== undefined)
262
+ parsedArgsProxy[arg.name] = nextPositionalArgument;
263
+ else if (arg.default === undefined && arg.required !== false)
264
+ throw new CLIError(`Missing required positional argument: ${arg.name.toUpperCase()}`, "EARG");
265
+ else
266
+ parsedArgsProxy[arg.name] = arg.default;
267
+ } else if (arg.type === "enum") {
268
+ const argument = parsedArgsProxy[arg.name];
269
+ const options = arg.options || [];
270
+ if (argument !== undefined && options.length > 0 && !options.includes(argument))
271
+ throw new CLIError(`Invalid value for argument: ${cyan(`--${arg.name}`)} (${cyan(argument)}). Expected one of: ${options.map((o) => cyan(o)).join(", ")}.`, "EARG");
272
+ } else if (arg.required && parsedArgsProxy[arg.name] === undefined)
273
+ throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
274
+ return parsedArgsProxy;
275
+ }
276
+ function resolveArgs(argsDef) {
277
+ const args = [];
278
+ for (const [name, argDef] of Object.entries(argsDef || {}))
279
+ args.push({
280
+ ...argDef,
281
+ name,
282
+ alias: toArray(argDef.alias)
283
+ });
284
+ return args;
285
+ }
286
+ async function resolvePlugins(plugins) {
287
+ return Promise.all(plugins.map((p) => resolveValue(p)));
288
+ }
289
+ function defineCommand(def) {
290
+ return def;
291
+ }
292
+ async function runCommand(cmd, opts) {
293
+ const cmdArgs = await resolveValue(cmd.args || {});
294
+ const parsedArgs = parseArgs(opts.rawArgs, cmdArgs);
295
+ const context = {
296
+ rawArgs: opts.rawArgs,
297
+ args: parsedArgs,
298
+ data: opts.data,
299
+ cmd
300
+ };
301
+ const plugins = await resolvePlugins(cmd.plugins ?? []);
302
+ let result;
303
+ let runError;
304
+ try {
305
+ for (const plugin of plugins)
306
+ await plugin.setup?.(context);
307
+ if (typeof cmd.setup === "function")
308
+ await cmd.setup(context);
309
+ const subCommands = await resolveValue(cmd.subCommands);
310
+ if (subCommands && Object.keys(subCommands).length > 0) {
311
+ const subCommandArgIndex = findSubCommandIndex(opts.rawArgs, cmdArgs);
312
+ const explicitName = opts.rawArgs[subCommandArgIndex];
313
+ if (explicitName) {
314
+ const subCommand = await _findSubCommand(subCommands, explicitName);
315
+ if (!subCommand)
316
+ throw new CLIError(`Unknown command ${cyan(explicitName)}`, "E_UNKNOWN_COMMAND");
317
+ await runCommand(subCommand, { rawArgs: opts.rawArgs.slice(subCommandArgIndex + 1) });
318
+ } else {
319
+ const defaultSubCommand = await resolveValue(cmd.default);
320
+ if (defaultSubCommand) {
321
+ if (cmd.run)
322
+ throw new CLIError(`Cannot specify both 'run' and 'default' on the same command.`, "E_DEFAULT_CONFLICT");
323
+ const subCommand = await _findSubCommand(subCommands, defaultSubCommand);
324
+ if (!subCommand)
325
+ throw new CLIError(`Default sub command ${cyan(defaultSubCommand)} not found in subCommands.`, "E_UNKNOWN_COMMAND");
326
+ await runCommand(subCommand, { rawArgs: opts.rawArgs });
327
+ } else if (!cmd.run)
328
+ throw new CLIError(`No command specified.`, "E_NO_COMMAND");
329
+ }
330
+ }
331
+ if (typeof cmd.run === "function")
332
+ result = await cmd.run(context);
333
+ } catch (error) {
334
+ runError = error;
335
+ }
336
+ const cleanupErrors = [];
337
+ if (typeof cmd.cleanup === "function")
338
+ try {
339
+ await cmd.cleanup(context);
340
+ } catch (error) {
341
+ cleanupErrors.push(error);
342
+ }
343
+ for (const plugin of [...plugins].reverse())
344
+ try {
345
+ await plugin.cleanup?.(context);
346
+ } catch (error) {
347
+ cleanupErrors.push(error);
348
+ }
349
+ if (runError)
350
+ throw runError;
351
+ if (cleanupErrors.length === 1)
352
+ throw cleanupErrors[0];
353
+ if (cleanupErrors.length > 1)
354
+ throw new Error("Multiple cleanup errors", { cause: cleanupErrors });
355
+ return { result };
356
+ }
357
+ async function resolveSubCommand(cmd, rawArgs, parent) {
358
+ const subCommands = await resolveValue(cmd.subCommands);
359
+ if (subCommands && Object.keys(subCommands).length > 0) {
360
+ const subCommandArgIndex = findSubCommandIndex(rawArgs, await resolveValue(cmd.args || {}));
361
+ const subCommandName = rawArgs[subCommandArgIndex];
362
+ const subCommand = await _findSubCommand(subCommands, subCommandName);
363
+ if (subCommand)
364
+ return resolveSubCommand(subCommand, rawArgs.slice(subCommandArgIndex + 1), cmd);
365
+ }
366
+ return [cmd, parent];
367
+ }
368
+ async function _findSubCommand(subCommands, name) {
369
+ if (name in subCommands)
370
+ return resolveValue(subCommands[name]);
371
+ for (const sub of Object.values(subCommands)) {
372
+ const resolved = await resolveValue(sub);
373
+ const meta = await resolveValue(resolved?.meta);
374
+ if (meta?.alias) {
375
+ if (toArray(meta.alias).includes(name))
376
+ return resolved;
377
+ }
378
+ }
379
+ }
380
+ function findSubCommandIndex(rawArgs, argsDef) {
381
+ for (let i = 0;i < rawArgs.length; i++) {
382
+ const arg = rawArgs[i];
383
+ if (arg === "--")
384
+ return -1;
385
+ if (arg.startsWith("-")) {
386
+ if (!arg.includes("=") && _isValueFlag(arg, argsDef))
387
+ i++;
388
+ continue;
389
+ }
390
+ return i;
391
+ }
392
+ return -1;
393
+ }
394
+ function _isValueFlag(flag, argsDef) {
395
+ const name = flag.replace(/^-{1,2}/, "");
396
+ const normalized = camelCase(name);
397
+ for (const [key, def] of Object.entries(argsDef)) {
398
+ if (def.type !== "string" && def.type !== "enum")
399
+ continue;
400
+ if (normalized === camelCase(key))
401
+ return true;
402
+ if ((Array.isArray(def.alias) ? def.alias : def.alias ? [def.alias] : []).includes(name))
403
+ return true;
404
+ }
405
+ return false;
406
+ }
407
+ async function showUsage(cmd, parent) {
408
+ try {
409
+ console.log(await renderUsage(cmd, parent) + `
410
+ `);
411
+ } catch (error) {
412
+ console.error(error);
413
+ }
414
+ }
415
+ var negativePrefixRe = /^no[-A-Z]/;
416
+ async function renderUsage(cmd, parent) {
417
+ const cmdMeta = await resolveValue(cmd.meta || {});
418
+ const cmdArgs = resolveArgs(await resolveValue(cmd.args || {}));
419
+ const parentMeta = await resolveValue(parent?.meta || {});
420
+ const commandName = `${parentMeta.name ? `${parentMeta.name} ` : ""}` + (cmdMeta.name || process.argv[1]);
421
+ const argLines = [];
422
+ const posLines = [];
423
+ const commandsLines = [];
424
+ const usageLine = [];
425
+ for (const arg of cmdArgs)
426
+ if (arg.type === "positional") {
427
+ const name = arg.name.toUpperCase();
428
+ const isRequired = arg.required !== false && arg.default === undefined;
429
+ posLines.push([cyan(name + renderValueHint(arg)), renderDescription(arg, isRequired)]);
430
+ usageLine.push(isRequired ? `<${name}>` : `[${name}]`);
431
+ } else {
432
+ const isRequired = arg.required === true && arg.default === undefined;
433
+ const argStr = [...(arg.alias || []).map((a) => `-${a}`), `--${arg.name}`].join(", ") + renderValueHint(arg);
434
+ argLines.push([cyan(argStr), renderDescription(arg, isRequired)]);
435
+ if (arg.type === "boolean" && (arg.default === true || arg.negativeDescription) && !negativePrefixRe.test(arg.name)) {
436
+ const negativeArgStr = [...(arg.alias || []).map((a) => `--no-${a}`), `--no-${arg.name}`].join(", ");
437
+ argLines.push([cyan(negativeArgStr), [arg.negativeDescription, isRequired ? gray("(Required)") : ""].filter(Boolean).join(" ")]);
438
+ }
439
+ if (isRequired)
440
+ usageLine.push(`--${arg.name}` + renderValueHint(arg));
441
+ }
442
+ if (cmd.subCommands) {
443
+ const commandNames = [];
444
+ const subCommands = await resolveValue(cmd.subCommands);
445
+ for (const [name, sub] of Object.entries(subCommands)) {
446
+ const meta = await resolveValue((await resolveValue(sub))?.meta);
447
+ if (meta?.hidden)
448
+ continue;
449
+ const aliases = toArray(meta?.alias);
450
+ const label = [name, ...aliases].join(", ");
451
+ commandsLines.push([cyan(label), meta?.description || ""]);
452
+ commandNames.push(name, ...aliases);
453
+ }
454
+ usageLine.push(commandNames.join("|"));
455
+ }
456
+ const usageLines = [];
457
+ const version = cmdMeta.version || parentMeta.version;
458
+ usageLines.push(gray(`${cmdMeta.description} (${commandName + (version ? ` v${version}` : "")})`), "");
459
+ const hasOptions = argLines.length > 0 || posLines.length > 0;
460
+ usageLines.push(`${underline(bold("USAGE"))} ${cyan(`${commandName}${hasOptions ? " [OPTIONS]" : ""} ${usageLine.join(" ")}`)}`, "");
461
+ if (posLines.length > 0) {
462
+ usageLines.push(underline(bold("ARGUMENTS")), "");
463
+ usageLines.push(formatLineColumns(posLines, " "));
464
+ usageLines.push("");
465
+ }
466
+ if (argLines.length > 0) {
467
+ usageLines.push(underline(bold("OPTIONS")), "");
468
+ usageLines.push(formatLineColumns(argLines, " "));
469
+ usageLines.push("");
470
+ }
471
+ if (commandsLines.length > 0) {
472
+ usageLines.push(underline(bold("COMMANDS")), "");
473
+ usageLines.push(formatLineColumns(commandsLines, " "));
474
+ usageLines.push("", `Use ${cyan(`${commandName} <command> --help`)} for more information about a command.`);
475
+ }
476
+ return usageLines.filter((l) => typeof l === "string").join(`
477
+ `);
478
+ }
479
+ function renderValueHint(arg) {
480
+ const valueHint = arg.valueHint ? `=<${arg.valueHint}>` : "";
481
+ const fallbackValueHint = valueHint || `=<${snakeCase(arg.name)}>`;
482
+ if (!arg.type || arg.type === "positional" || arg.type === "boolean")
483
+ return valueHint;
484
+ if (arg.type === "enum" && arg.options?.length)
485
+ return `=<${arg.options.join("|")}>`;
486
+ return fallbackValueHint;
487
+ }
488
+ function renderDescription(arg, required) {
489
+ const requiredHint = required ? gray("(Required)") : "";
490
+ const defaultHint = arg.default === undefined ? "" : gray(`(Default: ${arg.default})`);
491
+ return [
492
+ arg.description,
493
+ requiredHint,
494
+ defaultHint
495
+ ].filter(Boolean).join(" ");
496
+ }
497
+ async function runMain(cmd, opts = {}) {
498
+ const rawArgs = opts.rawArgs || process.argv.slice(2);
499
+ const showUsage$1 = opts.showUsage || showUsage;
500
+ try {
501
+ const builtinFlags = await _resolveBuiltinFlags(cmd);
502
+ if (builtinFlags.help.length > 0 && rawArgs.some((arg) => builtinFlags.help.includes(arg))) {
503
+ await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
504
+ process.exit(0);
505
+ } else if (rawArgs.length === 1 && builtinFlags.version.includes(rawArgs[0])) {
506
+ const meta = typeof cmd.meta === "function" ? await cmd.meta() : await cmd.meta;
507
+ if (!meta?.version)
508
+ throw new CLIError("No version specified", "E_NO_VERSION");
509
+ console.log(meta.version);
510
+ } else
511
+ await runCommand(cmd, { rawArgs });
512
+ } catch (error) {
513
+ if (error instanceof CLIError) {
514
+ await showUsage$1(...await resolveSubCommand(cmd, rawArgs));
515
+ console.error(error.message);
516
+ } else
517
+ console.error(error, `
518
+ `);
519
+ process.exit(1);
520
+ }
521
+ }
522
+ async function _resolveBuiltinFlags(cmd) {
523
+ const argsDef = await resolveValue(cmd.args || {});
524
+ const userNames = /* @__PURE__ */ new Set;
525
+ const userAliases = /* @__PURE__ */ new Set;
526
+ for (const [name, def] of Object.entries(argsDef)) {
527
+ userNames.add(name);
528
+ for (const alias of toArray(def.alias))
529
+ userAliases.add(alias);
530
+ }
531
+ return {
532
+ help: _getBuiltinFlags("help", "h", userNames, userAliases),
533
+ version: _getBuiltinFlags("version", "v", userNames, userAliases)
534
+ };
535
+ }
536
+ function _getBuiltinFlags(long, short, userNames, userAliases) {
537
+ if (userNames.has(long) || userAliases.has(long))
538
+ return [];
539
+ if (userNames.has(short) || userAliases.has(short))
540
+ return [`--${long}`];
541
+ return [`--${long}`, `-${short}`];
542
+ }
543
+
544
+ // src/cli/vvcode.ts
545
+ import { homedir } from "os";
546
+ import { join as join3 } from "path";
547
+ // package.json
548
+ var package_default = {
549
+ name: "@osovv/vvcode",
550
+ version: "0.4.1",
551
+ description: "Packaged CLI for launching OpenCode with vvcode runtime semantics.",
552
+ license: "MIT",
553
+ homepage: "https://github.com/osovv/vvcode",
554
+ repository: {
555
+ type: "git",
556
+ url: "git+https://github.com/osovv/vvcode.git"
557
+ },
558
+ bugs: {
559
+ url: "https://github.com/osovv/vvcode/issues"
560
+ },
561
+ type: "module",
562
+ packageManager: "bun@1.3.8",
563
+ files: [
564
+ "bin",
565
+ "dist"
566
+ ],
567
+ bin: {
568
+ vvcode: "./bin/vvcode"
569
+ },
570
+ publishConfig: {
571
+ access: "public"
572
+ },
573
+ engines: {
574
+ bun: ">=1.3.8"
575
+ },
576
+ scripts: {
577
+ build: "bun build ./src/cli/vvcode-entry.ts --target bun --format esm --outfile ./dist/vvcode.js",
578
+ prepack: "bun run build",
579
+ cli: "bun run build && bun run ./bin/vvcode",
580
+ typecheck: "tsc --noEmit",
581
+ test: "vitest run",
582
+ "test:unit": "vitest run tests/unit",
583
+ "test:integration": "vitest run tests/integration",
584
+ "test:scenarios": "vitest run tests/scenarios"
585
+ },
586
+ dependencies: {
587
+ "@opencode-ai/sdk": "1.3.13",
588
+ citty: "^0.2.2",
589
+ "opencode-ai": "1.3.13"
590
+ },
591
+ devDependencies: {
592
+ "@types/node": "25.5.0",
593
+ typescript: "5.9.3",
594
+ vitest: "4.1.2"
595
+ },
596
+ keywords: [
597
+ "vvcode",
598
+ "bun",
599
+ "cli",
600
+ "opencode",
601
+ "agentic"
602
+ ]
603
+ };
604
+
605
+ // src/cli/pivv-opencode.ts
606
+ import { rm } from "fs/promises";
607
+ import { spawn } from "child_process";
608
+
609
+ // src/config/load-pivv-config.ts
610
+ class PivvConfigError extends Error {
611
+ code;
612
+ constructor(code, message) {
613
+ super(message);
614
+ this.code = code;
615
+ this.name = "PivvConfigError";
616
+ }
617
+ }
618
+ var PROJECT_CONFIG_PATH = ".vvcode/config.jsonc";
619
+ var FS_PROMISES_IMPORT = "node:fs/promises";
620
+ async function loadPivvConfig(globalConfigPath, projectRoot) {
621
+ const runId = createRunId();
622
+ const normalizedProjectRoot = await findProjectRoot(projectRoot);
623
+ const sourceCandidates = [
624
+ { path: resolvePath(globalConfigPath), sourceType: "global" },
625
+ { path: joinPath(normalizedProjectRoot, PROJECT_CONFIG_PATH), sourceType: "project" }
626
+ ];
627
+ const sourceEntries = [];
628
+ for (const sourceCandidate of sourceCandidates) {
629
+ const sourcePath = sourceCandidate.path;
630
+ const sourceExists = await pathExists(sourcePath);
631
+ if (!sourceExists && sourceCandidate.sourceType === "project") {
632
+ throw new PivvConfigError("CONFIG_NOT_FOUND", `Project config not found at ${sourcePath}`);
633
+ }
634
+ if (!sourceExists) {
635
+ continue;
636
+ }
637
+ sourceEntries.push({
638
+ path: sourcePath,
639
+ config: await readJsoncConfig(sourcePath)
640
+ });
641
+ }
642
+ if (sourceEntries.length === 0) {
643
+ throw new PivvConfigError("CONFIG_NOT_FOUND", "No vvcode config sources were found");
644
+ }
645
+ const mergedRawConfig = sourceEntries.reduce((accumulator, entry) => {
646
+ return deepMerge(accumulator, entry.config);
647
+ }, {});
648
+ console.info("[VvcodeConfig][loadPivvConfig][BLOCK_VALIDATE_CONFIG] validating configuration", {
649
+ runId,
650
+ projectRoot: normalizedProjectRoot,
651
+ configSourceCount: sourceEntries.length
652
+ });
653
+ const normalizedConfig = normalizeConfig(mergedRawConfig, normalizedProjectRoot, sourceEntries.map((entry) => entry.path));
654
+ console.info("[VvcodeConfig][loadPivvConfig][BLOCK_RETURN_NORMALIZED_CONFIG] returning normalized config", {
655
+ runId,
656
+ projectRoot: normalizedProjectRoot,
657
+ configSourceCount: sourceEntries.length
658
+ });
659
+ return normalizedConfig;
660
+ }
661
+ async function findProjectRoot(projectRoot) {
662
+ let currentPath = resolvePath(projectRoot);
663
+ while (true) {
664
+ const hasPivvDirectory = await pathExists(joinPath(currentPath, ".vvcode"));
665
+ const hasOpenCodeDirectory = await pathExists(joinPath(currentPath, ".opencode"));
666
+ if (hasPivvDirectory || hasOpenCodeDirectory) {
667
+ return currentPath;
668
+ }
669
+ const parentPath = getParentPath(currentPath);
670
+ if (parentPath === currentPath) {
671
+ break;
672
+ }
673
+ currentPath = parentPath;
674
+ }
675
+ throw new PivvConfigError("CONFIG_NOT_FOUND", `Unable to resolve project root from ${projectRoot}`);
676
+ }
677
+ function normalizeConfig(rawConfig, projectRoot, configSources) {
678
+ if (!isRecord(rawConfig)) {
679
+ throw new PivvConfigError("CONFIG_INVALID", "Config root must be an object");
680
+ }
681
+ const rawProfiles = rawConfig.profiles ?? {};
682
+ if (!isRecord(rawProfiles)) {
683
+ throw new PivvConfigError("CONFIG_INVALID", 'Config "profiles" must be an object');
684
+ }
685
+ const profiles = {};
686
+ for (const [profileName, profileValue] of Object.entries(rawProfiles)) {
687
+ if (!isRecord(profileValue)) {
688
+ throw new PivvConfigError("CONFIG_INVALID", `Profile "${profileName}" must be an object`);
689
+ }
690
+ if (typeof profileValue.provider !== "string" || profileValue.provider.length === 0) {
691
+ throw new PivvConfigError("CONFIG_INVALID", `Profile "${profileName}" is missing a string "provider"`);
692
+ }
693
+ if (typeof profileValue.model !== "string" || profileValue.model.length === 0) {
694
+ throw new PivvConfigError("CONFIG_INVALID", `Profile "${profileName}" is missing a string "model"`);
695
+ }
696
+ profiles[profileName] = {
697
+ provider: profileValue.provider,
698
+ model: profileValue.model,
699
+ options: isRecord(profileValue.options) ? profileValue.options : undefined
700
+ };
701
+ }
702
+ const rawPresets = rawConfig.presets ?? {};
703
+ if (!isRecord(rawPresets)) {
704
+ throw new PivvConfigError("CONFIG_INVALID", 'Config "presets" must be an object');
705
+ }
706
+ const presets = {};
707
+ for (const [presetName, presetValue] of Object.entries(rawPresets)) {
708
+ if (!isRecord(presetValue)) {
709
+ throw new PivvConfigError("CONFIG_INVALID", `Preset "${presetName}" must be an object`);
710
+ }
711
+ const normalizedPreset = {};
712
+ for (const [slotName, profileName] of Object.entries(presetValue)) {
713
+ if (typeof profileName !== "string" || profileName.length === 0) {
714
+ throw new PivvConfigError("CONFIG_INVALID", `Preset "${presetName}" slot "${slotName}" must reference a non-empty profile name`);
715
+ }
716
+ if (!(profileName in profiles)) {
717
+ throw new PivvConfigError("CONFIG_INVALID", `Preset "${presetName}" slot "${slotName}" references unknown profile "${profileName}"`);
718
+ }
719
+ normalizedPreset[slotName] = profileName;
720
+ }
721
+ presets[presetName] = normalizedPreset;
722
+ }
723
+ const memory = isRecord(rawConfig.memory) ? rawConfig.memory : {};
724
+ const overlay = isRecord(rawConfig.overlay) ? rawConfig.overlay : {};
725
+ return {
726
+ profiles,
727
+ presets,
728
+ memory,
729
+ overlay,
730
+ projectRoot,
731
+ configSources
732
+ };
733
+ }
734
+ async function pathExists(path) {
735
+ try {
736
+ const fsPromises = await getFsPromises();
737
+ await fsPromises.access(path);
738
+ return true;
739
+ } catch {
740
+ return false;
741
+ }
742
+ }
743
+ async function readJsoncConfig(path) {
744
+ try {
745
+ const fsPromises = await getFsPromises();
746
+ const content = await fsPromises.readFile(path, "utf-8");
747
+ const parsed = parseJsonc(content);
748
+ if (!isRecord(parsed)) {
749
+ throw new PivvConfigError("CONFIG_INVALID", `Config at ${path} must be an object`);
750
+ }
751
+ return parsed;
752
+ } catch (error) {
753
+ if (error instanceof PivvConfigError) {
754
+ throw error;
755
+ }
756
+ throw new PivvConfigError("CONFIG_INVALID", `Invalid JSONC in ${path}`);
757
+ }
758
+ }
759
+ function createRunId() {
760
+ return `run-${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
761
+ }
762
+ function parseJsonc(jsonc) {
763
+ const withoutComments = stripJsoncComments(jsonc);
764
+ const normalized = stripJsoncTrailingCommas(withoutComments);
765
+ return JSON.parse(normalized);
766
+ }
767
+ function stripJsoncComments(jsonc) {
768
+ let result = "";
769
+ let inString = false;
770
+ let inSingleLineComment = false;
771
+ let inMultiLineComment = false;
772
+ for (let index = 0;index < jsonc.length; index += 1) {
773
+ const currentChar = jsonc[index];
774
+ const nextChar = jsonc[index + 1];
775
+ if (inSingleLineComment) {
776
+ if (currentChar === `
777
+ `) {
778
+ inSingleLineComment = false;
779
+ result += currentChar;
780
+ }
781
+ continue;
782
+ }
783
+ if (inMultiLineComment) {
784
+ if (currentChar === "*" && nextChar === "/") {
785
+ inMultiLineComment = false;
786
+ index += 1;
787
+ }
788
+ continue;
789
+ }
790
+ if (currentChar === '"' && !isEscapedCharacter(jsonc, index)) {
791
+ inString = !inString;
792
+ result += currentChar;
793
+ continue;
794
+ }
795
+ if (!inString && currentChar === "/" && nextChar === "/") {
796
+ inSingleLineComment = true;
797
+ index += 1;
798
+ continue;
799
+ }
800
+ if (!inString && currentChar === "/" && nextChar === "*") {
801
+ inMultiLineComment = true;
802
+ index += 1;
803
+ continue;
804
+ }
805
+ result += currentChar;
806
+ }
807
+ return result;
808
+ }
809
+ function stripJsoncTrailingCommas(jsonc) {
810
+ let result = "";
811
+ let inString = false;
812
+ for (let index = 0;index < jsonc.length; index += 1) {
813
+ const currentChar = jsonc[index];
814
+ if (currentChar === '"' && !isEscapedCharacter(jsonc, index)) {
815
+ inString = !inString;
816
+ result += currentChar;
817
+ continue;
818
+ }
819
+ if (!inString && currentChar === ",") {
820
+ const nextSignificantChar = findNextSignificantCharacter(jsonc, index + 1);
821
+ if (nextSignificantChar === "}" || nextSignificantChar === "]") {
822
+ continue;
823
+ }
824
+ }
825
+ result += currentChar;
826
+ }
827
+ return result;
828
+ }
829
+ function findNextSignificantCharacter(content, startIndex) {
830
+ for (let index = startIndex;index < content.length; index += 1) {
831
+ const currentChar = content[index];
832
+ if (!isWhitespace(currentChar)) {
833
+ return currentChar;
834
+ }
835
+ }
836
+ return null;
837
+ }
838
+ function isEscapedCharacter(content, quoteIndex) {
839
+ let backslashCount = 0;
840
+ for (let index = quoteIndex - 1;index >= 0 && content[index] === "\\"; index -= 1) {
841
+ backslashCount += 1;
842
+ }
843
+ return backslashCount % 2 === 1;
844
+ }
845
+ function isWhitespace(char) {
846
+ return char === " " || char === `
847
+ ` || char === "\r" || char === "\t";
848
+ }
849
+ function isRecord(value) {
850
+ return typeof value === "object" && value !== null && !Array.isArray(value);
851
+ }
852
+ function deepMerge(baseValue, overrideValue) {
853
+ if (!isRecord(baseValue) || !isRecord(overrideValue)) {
854
+ return overrideValue;
855
+ }
856
+ const mergedValue = { ...baseValue };
857
+ for (const [key, value] of Object.entries(overrideValue)) {
858
+ const baseEntry = mergedValue[key];
859
+ mergedValue[key] = isRecord(baseEntry) && isRecord(value) ? deepMerge(baseEntry, value) : value;
860
+ }
861
+ return mergedValue;
862
+ }
863
+ var fsPromisesCache = null;
864
+ async function getFsPromises() {
865
+ if (fsPromisesCache !== null) {
866
+ return fsPromisesCache;
867
+ }
868
+ const fsPromises = await import(FS_PROMISES_IMPORT);
869
+ fsPromisesCache = fsPromises;
870
+ return fsPromises;
871
+ }
872
+ function resolvePath(path) {
873
+ if (path.startsWith("/")) {
874
+ return normalizePath(path);
875
+ }
876
+ return normalizePath(`${process.cwd()}/${path}`);
877
+ }
878
+ function joinPath(basePath, relativePath) {
879
+ const normalizedBase = normalizePath(basePath).replace(/\/$/, "");
880
+ const normalizedRelative = relativePath.replace(/^\/+/, "");
881
+ return normalizePath(`${normalizedBase}/${normalizedRelative}`);
882
+ }
883
+ function getParentPath(path) {
884
+ const normalized = normalizePath(path);
885
+ if (normalized === "/") {
886
+ return "/";
887
+ }
888
+ const lastSlashIndex = normalized.lastIndexOf("/");
889
+ if (lastSlashIndex <= 0) {
890
+ return "/";
891
+ }
892
+ return normalized.slice(0, lastSlashIndex);
893
+ }
894
+ function normalizePath(path) {
895
+ const withSingleSlashes = path.replace(/\/+/g, "/");
896
+ if (withSingleSlashes.length > 1 && withSingleSlashes.endsWith("/")) {
897
+ return withSingleSlashes.slice(0, -1);
898
+ }
899
+ return withSingleSlashes;
900
+ }
901
+
902
+ // src/opencode/build-runtime-overlay.ts
903
+ import { mkdir, writeFile } from "fs/promises";
904
+ import { join } from "path";
905
+ var OVERLAY_LOG_PREFIX = "[VvcodeOverlay]";
906
+ var BLOCK_GENERATE_ALIAS_MODELS = "BLOCK_GENERATE_ALIAS_MODELS";
907
+
908
+ class PivvOverlayError extends Error {
909
+ code;
910
+ constructor(code, message) {
911
+ super(message);
912
+ this.name = "PivvOverlayError";
913
+ this.code = code;
914
+ }
915
+ }
916
+ function generateAliasModels(resolvedPreset) {
917
+ const aliasModelsByProvider = Object.create(null);
918
+ for (const resolvedProfile of Object.values(resolvedPreset.profiles)) {
919
+ const providerBucket = aliasModelsByProvider[resolvedProfile.provider] ?? (aliasModelsByProvider[resolvedProfile.provider] = Object.create(null));
920
+ const aliasModelId = toAliasModelId(resolvedProfile.alias);
921
+ if (aliasModelId in providerBucket) {
922
+ throw new PivvOverlayError("PROFILE_ALIAS_GENERATION_FAILED", `Alias model generation collided for provider "${resolvedProfile.provider}" alias "${resolvedProfile.alias}"`);
923
+ }
924
+ providerBucket[aliasModelId] = createAliasModelDefinition(aliasModelId, resolvedProfile);
925
+ }
926
+ const providerIds = Object.keys(aliasModelsByProvider).sort();
927
+ const aliasModelCount = providerIds.reduce((count, providerId) => count + Object.keys(aliasModelsByProvider[providerId]).length, 0);
928
+ console.info(`${OVERLAY_LOG_PREFIX}[generateAliasModels][${BLOCK_GENERATE_ALIAS_MODELS}] generated alias models`, {
929
+ preset: resolvedPreset.presetName,
930
+ aliasModelCount,
931
+ providerIds
932
+ });
933
+ return aliasModelsByProvider;
934
+ }
935
+ async function buildRuntimeOverlay(resolvedPreset, projectConfig, baseOpenCodeInputs) {
936
+ const projectOverlay = toProjectOverlayFragments(projectConfig.overlay);
937
+ const allowCustomNameCollisions = projectOverlay.allowCustomNameCollisions === true || baseOpenCodeInputs.collisionPolicy?.allowCustomNameCollisions === true;
938
+ assertNoCustomNameCollisions("agents", Object.keys(projectOverlay.agents), Object.keys(baseOpenCodeInputs.agents), new Set(baseOpenCodeInputs.collisionPolicy?.builtInNames?.agents ?? []), allowCustomNameCollisions);
939
+ assertNoCustomNameCollisions("commands", Object.keys(projectOverlay.commands), Object.keys(baseOpenCodeInputs.commands), new Set(baseOpenCodeInputs.collisionPolicy?.builtInNames?.commands ?? []), allowCustomNameCollisions);
940
+ assertNoCustomNameCollisions("tools", Object.keys(projectOverlay.tools), Object.keys(baseOpenCodeInputs.tools), new Set(baseOpenCodeInputs.collisionPolicy?.builtInNames?.tools ?? []), allowCustomNameCollisions);
941
+ const aliasModels = generateAliasModels(resolvedPreset);
942
+ const providerIds = Object.keys(aliasModels).sort();
943
+ const aliasModelCount = providerIds.reduce((count, providerId) => count + Object.keys(aliasModels[providerId]).length, 0);
944
+ const providersWithAliases = mergeProvidersWithAliasModels(baseOpenCodeInputs.providers, aliasModels);
945
+ const overlayDirPath = join(projectConfig.projectRoot, ".vvcode", "local", "runtime", "overlays");
946
+ const overlayFilePath = join(overlayDirPath, `${toOverlayFilename(resolvedPreset.presetName)}-runtime-overlay.json`);
947
+ const sourcePaths = dedupeSourcePaths([
948
+ ...projectConfig.configSources,
949
+ ...resolvedPreset.configSources,
950
+ ...baseOpenCodeInputs.sourcePaths ?? []
951
+ ]);
952
+ const runtimeOverlay = {
953
+ overlayDirPath,
954
+ overlayFilePath,
955
+ preset: resolvedPreset.presetName,
956
+ aliasModelCount,
957
+ providerIds,
958
+ aliasModels,
959
+ providers: providersWithAliases,
960
+ agents: {
961
+ ...baseOpenCodeInputs.agents,
962
+ ...projectOverlay.agents
963
+ },
964
+ commands: {
965
+ ...baseOpenCodeInputs.commands,
966
+ ...projectOverlay.commands
967
+ },
968
+ tools: {
969
+ ...baseOpenCodeInputs.tools,
970
+ ...projectOverlay.tools
971
+ },
972
+ metadata: {
973
+ sourcePaths,
974
+ collisionPolicy: {
975
+ allowCustomNameCollisions
976
+ }
977
+ }
978
+ };
979
+ await mkdir(overlayDirPath, { recursive: true });
980
+ await writeFile(overlayFilePath, `${JSON.stringify(runtimeOverlay, null, 2)}
981
+ `, "utf8");
982
+ console.info(`${OVERLAY_LOG_PREFIX}[buildRuntimeOverlay][${BLOCK_GENERATE_ALIAS_MODELS}] compiled runtime overlay`, {
983
+ preset: resolvedPreset.presetName,
984
+ aliasModelCount,
985
+ providerIds
986
+ });
987
+ return runtimeOverlay;
988
+ }
989
+ function toProjectOverlayFragments(overlay) {
990
+ return {
991
+ agents: toNamedEntityMap(overlay.agents),
992
+ commands: toNamedEntityMap(overlay.commands),
993
+ tools: toNamedEntityMap(overlay.tools),
994
+ allowCustomNameCollisions: typeof overlay.allowCustomNameCollisions === "boolean" ? overlay.allowCustomNameCollisions : undefined
995
+ };
996
+ }
997
+ function mergeProvidersWithAliasModels(baseProviders, aliasModels) {
998
+ const mergedProviders = { ...baseProviders };
999
+ for (const [providerId, providerAliasModels] of Object.entries(aliasModels)) {
1000
+ const providerRecord = toRecord(mergedProviders[providerId]);
1001
+ const baseProviderModels = toRecord(providerRecord.models);
1002
+ for (const aliasModelId of Object.keys(providerAliasModels)) {
1003
+ if (aliasModelId in baseProviderModels) {
1004
+ throw new PivvOverlayError("PROFILE_ALIAS_GENERATION_FAILED", `Alias model id "${aliasModelId}" for provider "${providerId}" collides with existing OpenCode model id`);
1005
+ }
1006
+ }
1007
+ mergedProviders[providerId] = {
1008
+ ...providerRecord,
1009
+ id: providerRecord.id ?? providerId,
1010
+ models: {
1011
+ ...baseProviderModels,
1012
+ ...providerAliasModels
1013
+ }
1014
+ };
1015
+ }
1016
+ return mergedProviders;
1017
+ }
1018
+ function createAliasModelDefinition(aliasModelId, resolvedProfile) {
1019
+ return {
1020
+ id: aliasModelId,
1021
+ name: toFriendlyAliasName(resolvedProfile.alias),
1022
+ provider: resolvedProfile.provider,
1023
+ model: resolvedProfile.model,
1024
+ profileName: resolvedProfile.profileName,
1025
+ alias: resolvedProfile.alias,
1026
+ options: resolvedProfile.options
1027
+ };
1028
+ }
1029
+ function toAliasModelId(alias) {
1030
+ const trimmedAlias = alias.trim();
1031
+ const normalizedAlias = trimmedAlias.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
1032
+ if (!normalizedAlias) {
1033
+ throw new PivvOverlayError("PROFILE_ALIAS_GENERATION_FAILED", `Alias model generation failed because alias "${alias}" is empty after normalization`);
1034
+ }
1035
+ return `vvcode-${normalizedAlias}`;
1036
+ }
1037
+ function toFriendlyAliasName(alias) {
1038
+ return `vvcode ${alias.trim()}`;
1039
+ }
1040
+ function assertNoCustomNameCollisions(kind, pivvNames, baseNames, builtInNames, allowCustomNameCollisions) {
1041
+ if (allowCustomNameCollisions) {
1042
+ return;
1043
+ }
1044
+ const baseNameSet = new Set(baseNames);
1045
+ for (const pivvName of pivvNames) {
1046
+ if (baseNameSet.has(pivvName) && !builtInNames.has(pivvName)) {
1047
+ throw new PivvOverlayError("CUSTOM_NAME_COLLISION", `Custom ${kind} name collision for "${pivvName}" between .vvcode overlay and .opencode inputs`);
1048
+ }
1049
+ }
1050
+ }
1051
+ function toNamedEntityMap(value) {
1052
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1053
+ return Object.create(null);
1054
+ }
1055
+ const entries = Object.entries(value);
1056
+ const result = Object.create(null);
1057
+ for (const [name, entryValue] of entries) {
1058
+ result[name] = toRecord(entryValue);
1059
+ }
1060
+ return result;
1061
+ }
1062
+ function toRecord(value) {
1063
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1064
+ return Object.create(null);
1065
+ }
1066
+ return value;
1067
+ }
1068
+ function dedupeSourcePaths(paths) {
1069
+ const seen = new Set;
1070
+ const deduped = [];
1071
+ for (const path of paths) {
1072
+ if (seen.has(path)) {
1073
+ continue;
1074
+ }
1075
+ seen.add(path);
1076
+ deduped.push(path);
1077
+ }
1078
+ return deduped;
1079
+ }
1080
+ function sanitizeForFilename(value) {
1081
+ const sanitized = value.replaceAll(/[^a-zA-Z0-9-]+/g, "-").replaceAll(/^-+|-+$/g, "");
1082
+ return sanitized || "runtime";
1083
+ }
1084
+ function toOverlayFilename(presetName) {
1085
+ return `${sanitizeForFilename(presetName)}-${toPresetNameFingerprint(presetName)}`;
1086
+ }
1087
+ function toPresetNameFingerprint(value) {
1088
+ let hash = 2166136261;
1089
+ for (let index = 0;index < value.length; index += 1) {
1090
+ hash ^= value.charCodeAt(index);
1091
+ hash = Math.imul(hash, 16777619);
1092
+ }
1093
+ return (hash >>> 0).toString(16).padStart(8, "0");
1094
+ }
1095
+
1096
+ // src/preset/resolve-preset.ts
1097
+ var PRESET_LOG_PREFIX = "[VvcodePreset]";
1098
+ var BLOCK_RESOLVE_PROFILE_BINDINGS = "BLOCK_RESOLVE_PROFILE_BINDINGS";
1099
+ var BLOCK_RETURN_RESOLVED_PRESET = "BLOCK_RETURN_RESOLVED_PRESET";
1100
+ var CORE_SLOTS = ["default", "smart", "small", "vision"];
1101
+
1102
+ class PivvPresetError extends Error {
1103
+ code;
1104
+ constructor(code, message) {
1105
+ super(message);
1106
+ this.name = "PivvPresetError";
1107
+ this.code = code;
1108
+ }
1109
+ }
1110
+ function resolvePreset(presetName, config) {
1111
+ const preset = config.presets[presetName];
1112
+ if (!preset) {
1113
+ throw new PivvPresetError("PRESET_NOT_FOUND", `Preset "${presetName}" was not found`);
1114
+ }
1115
+ const resolvedProfiles = Object.create(null);
1116
+ const canonicalAliasMap = new Map;
1117
+ for (const [alias, profileName] of Object.entries(preset)) {
1118
+ const canonicalAlias = toCanonicalAlias(alias);
1119
+ const existingAlias = canonicalAliasMap.get(canonicalAlias);
1120
+ if (canonicalAliasMap.has(canonicalAlias) && existingAlias !== alias) {
1121
+ throw new PivvPresetError("PROFILE_ALIAS_CONFLICT", `Preset "${presetName}" includes conflicting aliases "${existingAlias}" and "${alias}"`);
1122
+ }
1123
+ canonicalAliasMap.set(canonicalAlias, alias);
1124
+ resolvedProfiles[alias] = resolveProfile(alias, profileName, config.profiles);
1125
+ }
1126
+ const missingCoreSlot = CORE_SLOTS.find((slot) => !(slot in resolvedProfiles));
1127
+ if (missingCoreSlot) {
1128
+ throw new PivvPresetError("PROFILE_MISSING_CORE_SLOT", `Preset "${presetName}" is missing required core slot "${missingCoreSlot}"`);
1129
+ }
1130
+ const coreProfiles = {
1131
+ default: resolvedProfiles.default,
1132
+ smart: resolvedProfiles.smart,
1133
+ small: resolvedProfiles.small,
1134
+ vision: resolvedProfiles.vision
1135
+ };
1136
+ const profileCount = Object.keys(resolvedProfiles).length;
1137
+ console.info(`${PRESET_LOG_PREFIX}[resolvePreset][${BLOCK_RESOLVE_PROFILE_BINDINGS}] resolved preset profile bindings`, {
1138
+ preset: presetName,
1139
+ profileCount,
1140
+ coreSlotsResolved: CORE_SLOTS.length
1141
+ });
1142
+ const resolvedPreset = {
1143
+ presetName,
1144
+ coreProfiles,
1145
+ profiles: resolvedProfiles,
1146
+ profileCount,
1147
+ projectRoot: config.projectRoot,
1148
+ configSources: config.configSources,
1149
+ memory: config.memory,
1150
+ overlay: config.overlay
1151
+ };
1152
+ console.info(`${PRESET_LOG_PREFIX}[resolvePreset][${BLOCK_RETURN_RESOLVED_PRESET}] returning resolved preset`, {
1153
+ preset: presetName,
1154
+ profileCount,
1155
+ coreSlotsResolved: CORE_SLOTS.length
1156
+ });
1157
+ return resolvedPreset;
1158
+ }
1159
+ function resolveProfile(alias, profileName, profiles) {
1160
+ const profile = profiles[profileName];
1161
+ if (!profile) {
1162
+ throw new PivvPresetError("PROFILE_ALIAS_CONFLICT", `Alias "${alias}" references unknown profile "${profileName}"`);
1163
+ }
1164
+ return {
1165
+ alias,
1166
+ profileName,
1167
+ provider: profile.provider,
1168
+ model: profile.model,
1169
+ options: profile.options
1170
+ };
1171
+ }
1172
+ function toCanonicalAlias(alias) {
1173
+ return alias.trim().toLowerCase();
1174
+ }
1175
+
1176
+ // src/trace/trace-recorder.ts
1177
+ import { appendFile, mkdir as mkdir2 } from "fs/promises";
1178
+ import { join as join2 } from "path";
1179
+ var TRACE_LOG_PREFIX = "[VvcodeTrace]";
1180
+ var TRACE_BLOCK_PERSIST_EVENT = "BLOCK_PERSIST_TRACE_EVENT";
1181
+ var TRACE_WRITE_FAILED = "TRACE_WRITE_FAILED";
1182
+ var REDACTION_TOKEN = "[REDACTED]";
1183
+ var TERMINAL_EVENTS = new Set(["run.finish", "run.fail"]);
1184
+ var SENSITIVE_KEY_MATCHERS = ["token", "apikey", "authorization", "secret", "password", "credential"];
1185
+ function redactTracePayload(payload) {
1186
+ const seen = new WeakSet;
1187
+ const redactInner = (value) => {
1188
+ if (value === null || value === undefined) {
1189
+ return value;
1190
+ }
1191
+ if (typeof value !== "object") {
1192
+ return value;
1193
+ }
1194
+ if (seen.has(value)) {
1195
+ return "[REDACTED_CIRCULAR_REFERENCE]";
1196
+ }
1197
+ seen.add(value);
1198
+ if (Array.isArray(value)) {
1199
+ return value.map((item) => redactInner(item));
1200
+ }
1201
+ const redactedRecord = {};
1202
+ for (const [key, nestedValue] of Object.entries(value)) {
1203
+ if (isSensitiveKey(key)) {
1204
+ redactedRecord[key] = REDACTION_TOKEN;
1205
+ continue;
1206
+ }
1207
+ redactedRecord[key] = redactInner(nestedValue);
1208
+ }
1209
+ return redactedRecord;
1210
+ };
1211
+ return redactInner(payload);
1212
+ }
1213
+ function createTraceRecorder(options = {}) {
1214
+ const now = options.now ?? (() => new Date);
1215
+ const projectRoot = options.projectRoot ?? process.cwd();
1216
+ const log = options.logger ?? (() => {
1217
+ return;
1218
+ });
1219
+ const runTerminalState = new Map;
1220
+ const runOperationQueue = new Map;
1221
+ const enqueueRunOperation = (runId, operation) => {
1222
+ const previous = runOperationQueue.get(runId) ?? Promise.resolve();
1223
+ const next = previous.catch(() => {
1224
+ return;
1225
+ }).then(operation);
1226
+ const settled = next.then(() => {
1227
+ return;
1228
+ }, () => {
1229
+ return;
1230
+ });
1231
+ runOperationQueue.set(runId, settled);
1232
+ return next.finally(() => {
1233
+ if (runOperationQueue.get(runId) === settled) {
1234
+ runOperationQueue.delete(runId);
1235
+ }
1236
+ });
1237
+ };
1238
+ return {
1239
+ persistTraceEvent(event, traceContext) {
1240
+ return enqueueRunOperation(traceContext.runId, async () => {
1241
+ const isTerminalEvent = event.terminal === true || TERMINAL_EVENTS.has(event.event);
1242
+ const traceDir = join2(projectRoot, ".vvcode", "local", "runtime", "traces");
1243
+ const traceFilePath = join2(traceDir, `${toTraceFileName(traceContext.runId)}.jsonl`);
1244
+ const functionName = event.function ?? "persistTraceEvent";
1245
+ try {
1246
+ if (isTerminalEvent && runTerminalState.get(traceContext.runId) === true) {
1247
+ throw createTraceWriteError(`runId ${traceContext.runId} already contains a terminal event`);
1248
+ }
1249
+ const persistedTrace = {
1250
+ event: event.event,
1251
+ ts: event.ts ?? now().toISOString(),
1252
+ runId: traceContext.runId,
1253
+ sessionId: traceContext.sessionId,
1254
+ turnId: traceContext.turnId,
1255
+ toolCallId: traceContext.toolCallId,
1256
+ correlationId: traceContext.correlationId,
1257
+ module: event.module,
1258
+ function: functionName,
1259
+ block: event.block,
1260
+ preset: traceContext.preset,
1261
+ profile: traceContext.profile,
1262
+ outcome: event.outcome,
1263
+ payload: redactTracePayload(event.payload)
1264
+ };
1265
+ const traceLine = `${JSON.stringify(persistedTrace)}
1266
+ `;
1267
+ const bytesWritten = new TextEncoder().encode(traceLine).byteLength;
1268
+ await mkdir2(traceDir, { recursive: true });
1269
+ await appendFile(traceFilePath, traceLine, "utf8");
1270
+ if (isTerminalEvent) {
1271
+ runTerminalState.set(traceContext.runId, true);
1272
+ }
1273
+ log({
1274
+ message: `${TRACE_LOG_PREFIX}[persistTraceEvent][${TRACE_BLOCK_PERSIST_EVENT}] trace event persisted`,
1275
+ runId: traceContext.runId,
1276
+ module: event.module,
1277
+ function: functionName,
1278
+ block: event.block,
1279
+ outcome: event.outcome,
1280
+ event: event.event,
1281
+ traceFilePath,
1282
+ bytesWritten
1283
+ });
1284
+ return {
1285
+ ok: true,
1286
+ traceFilePath,
1287
+ bytesWritten,
1288
+ persistedTrace
1289
+ };
1290
+ } catch (error) {
1291
+ const writeError = isTraceRecorderError(error) ? error : createTraceWriteError("failed to persist trace event", error);
1292
+ log({
1293
+ message: `${TRACE_LOG_PREFIX}[persistTraceEvent][${TRACE_BLOCK_PERSIST_EVENT}] trace persist failed`,
1294
+ runId: traceContext.runId,
1295
+ module: event.module,
1296
+ function: functionName,
1297
+ block: event.block,
1298
+ outcome: "failure",
1299
+ event: event.event,
1300
+ errorCode: writeError.code
1301
+ });
1302
+ throw writeError;
1303
+ }
1304
+ });
1305
+ }
1306
+ };
1307
+ }
1308
+ function toTraceFileName(runId) {
1309
+ let encoded = "";
1310
+ for (let index = 0;index < runId.length; index += 1) {
1311
+ const codeUnit = runId.charCodeAt(index);
1312
+ encoded += codeUnit.toString(16).padStart(4, "0");
1313
+ }
1314
+ return `run-${encoded}`;
1315
+ }
1316
+ function isSensitiveKey(key) {
1317
+ const normalized = key.toLowerCase().replaceAll(/[^a-z]/g, "");
1318
+ return SENSITIVE_KEY_MATCHERS.some((matcher) => normalized.includes(matcher));
1319
+ }
1320
+ function isTraceRecorderError(error) {
1321
+ return error instanceof Error && "code" in error && error.code === TRACE_WRITE_FAILED;
1322
+ }
1323
+ function createTraceWriteError(message, cause) {
1324
+ const error = new Error(message);
1325
+ error.name = "TraceRecorderError";
1326
+ error.code = TRACE_WRITE_FAILED;
1327
+ error.cause = cause;
1328
+ return error;
1329
+ }
1330
+
1331
+ // src/cli/pivv-opencode.ts
1332
+ var LAUNCHER_LOG_PREFIX = "[VvcodeLauncher]";
1333
+ var BLOCK_LOAD_ACTIVE_PRESET = "BLOCK_LOAD_ACTIVE_PRESET";
1334
+ var OPENCODE_ENTRYPOINT = "bun x opencode-ai";
1335
+ function buildRuntimeEnvironment(runtimeOverlay, baseEnvironment = process.env) {
1336
+ const runtimeEnvironment = {};
1337
+ for (const [key, value] of Object.entries(baseEnvironment)) {
1338
+ if (typeof value === "string") {
1339
+ runtimeEnvironment[key] = value;
1340
+ }
1341
+ }
1342
+ runtimeEnvironment.OPENCODE_CONFIG_DIR = runtimeOverlay.overlayDirPath;
1343
+ runtimeEnvironment.OPENCODE_CONFIG_CONTENT = JSON.stringify(runtimeOverlay);
1344
+ return runtimeEnvironment;
1345
+ }
1346
+ async function runPivvOpencode(cliArgs) {
1347
+ let traceRecorder = cliArgs.traceRecorder;
1348
+ const traceContext = {
1349
+ runId: cliArgs.traceRunId ?? createLaunchRunId(),
1350
+ preset: cliArgs.preset
1351
+ };
1352
+ const baseOpenCodeInputs = cliArgs.baseOpenCodeInputs ?? createEmptyOpenCodeBaseInputs();
1353
+ let runtimeOverlayPath = null;
1354
+ let resolvedProjectRoot = cliArgs.projectRoot;
1355
+ let aliasModelCount = 0;
1356
+ let providerIds = [];
1357
+ let lifecycleStage = "config.load";
1358
+ if (!traceRecorder) {
1359
+ try {
1360
+ resolvedProjectRoot = await findProjectRoot(cliArgs.projectRoot);
1361
+ } catch {
1362
+ resolvedProjectRoot = cliArgs.projectRoot;
1363
+ }
1364
+ traceRecorder = createTraceRecorder({ projectRoot: resolvedProjectRoot });
1365
+ }
1366
+ await persistLaunchTrace(traceRecorder, traceContext, {
1367
+ event: "run.start",
1368
+ module: "M-LAUNCHER",
1369
+ function: "runPivvOpencode",
1370
+ block: BLOCK_LOAD_ACTIVE_PRESET,
1371
+ outcome: "success",
1372
+ payload: {
1373
+ preset: cliArgs.preset,
1374
+ projectRoot: resolvedProjectRoot,
1375
+ cwd: resolvedProjectRoot,
1376
+ entrypoint: OPENCODE_ENTRYPOINT
1377
+ }
1378
+ });
1379
+ try {
1380
+ await persistLaunchTrace(traceRecorder, traceContext, {
1381
+ event: "config.load.start",
1382
+ module: "M-CONFIG",
1383
+ function: "loadPivvConfig",
1384
+ block: "BLOCK_LOAD_CONFIG_SOURCES",
1385
+ outcome: "success",
1386
+ payload: {
1387
+ preset: cliArgs.preset,
1388
+ projectRoot: resolvedProjectRoot,
1389
+ cwd: resolvedProjectRoot,
1390
+ entrypoint: OPENCODE_ENTRYPOINT
1391
+ }
1392
+ });
1393
+ const projectConfig = await loadPivvConfig(cliArgs.globalConfigPath, cliArgs.projectRoot);
1394
+ resolvedProjectRoot = projectConfig.projectRoot;
1395
+ await persistLaunchTrace(traceRecorder, traceContext, {
1396
+ event: "config.load.finish",
1397
+ module: "M-CONFIG",
1398
+ function: "loadPivvConfig",
1399
+ block: "BLOCK_VALIDATE_CONFIG",
1400
+ outcome: "success",
1401
+ payload: {
1402
+ preset: cliArgs.preset,
1403
+ projectRoot: resolvedProjectRoot,
1404
+ cwd: resolvedProjectRoot,
1405
+ entrypoint: OPENCODE_ENTRYPOINT
1406
+ }
1407
+ });
1408
+ lifecycleStage = "preset.resolve";
1409
+ await persistLaunchTrace(traceRecorder, traceContext, {
1410
+ event: "preset.resolve.start",
1411
+ module: "M-PRESET",
1412
+ function: "resolvePreset",
1413
+ block: "BLOCK_RESOLVE_PROFILE_BINDINGS",
1414
+ outcome: "success",
1415
+ payload: {
1416
+ preset: cliArgs.preset,
1417
+ coreSlotsResolved: 0,
1418
+ cwd: resolvedProjectRoot,
1419
+ entrypoint: OPENCODE_ENTRYPOINT
1420
+ }
1421
+ });
1422
+ const resolvedPreset = resolvePreset(cliArgs.preset, projectConfig);
1423
+ await persistLaunchTrace(traceRecorder, traceContext, {
1424
+ event: "preset.resolve.finish",
1425
+ module: "M-PRESET",
1426
+ function: "resolvePreset",
1427
+ block: "BLOCK_RESOLVE_PROFILE_BINDINGS",
1428
+ outcome: "success",
1429
+ payload: {
1430
+ preset: cliArgs.preset,
1431
+ profileCount: resolvedPreset.profileCount,
1432
+ coreSlotsResolved: Object.keys(resolvedPreset.coreProfiles).length,
1433
+ cwd: resolvedProjectRoot,
1434
+ entrypoint: OPENCODE_ENTRYPOINT
1435
+ }
1436
+ });
1437
+ lifecycleStage = "overlay.build";
1438
+ await persistLaunchTrace(traceRecorder, traceContext, {
1439
+ event: "overlay.build.start",
1440
+ module: "M-OVERLAY",
1441
+ function: "buildRuntimeOverlay",
1442
+ block: "BLOCK_GENERATE_ALIAS_MODELS",
1443
+ outcome: "success",
1444
+ payload: {
1445
+ preset: cliArgs.preset,
1446
+ aliasModelCount,
1447
+ providerIds,
1448
+ cwd: resolvedProjectRoot,
1449
+ entrypoint: OPENCODE_ENTRYPOINT
1450
+ }
1451
+ });
1452
+ const runtimeOverlay = await buildRuntimeOverlay(resolvedPreset, projectConfig, baseOpenCodeInputs);
1453
+ runtimeOverlayPath = runtimeOverlay.overlayFilePath;
1454
+ aliasModelCount = runtimeOverlay.aliasModelCount;
1455
+ providerIds = runtimeOverlay.providerIds;
1456
+ await persistLaunchTrace(traceRecorder, traceContext, {
1457
+ event: "overlay.build.finish",
1458
+ module: "M-OVERLAY",
1459
+ function: "buildRuntimeOverlay",
1460
+ block: "BLOCK_GENERATE_ALIAS_MODELS",
1461
+ outcome: "success",
1462
+ payload: {
1463
+ preset: cliArgs.preset,
1464
+ runtimeOverlayPath,
1465
+ aliasModelCount,
1466
+ providerIds,
1467
+ cwd: resolvedProjectRoot,
1468
+ entrypoint: OPENCODE_ENTRYPOINT
1469
+ }
1470
+ });
1471
+ lifecycleStage = "launch";
1472
+ const runtimeEnvironment = buildRuntimeEnvironment(runtimeOverlay);
1473
+ await persistLaunchTrace(traceRecorder, traceContext, {
1474
+ event: "launch.start",
1475
+ module: "M-LAUNCHER",
1476
+ function: "runPivvOpencode",
1477
+ block: BLOCK_LOAD_ACTIVE_PRESET,
1478
+ outcome: "success",
1479
+ payload: {
1480
+ preset: cliArgs.preset,
1481
+ projectRoot: resolvedProjectRoot,
1482
+ runtimeOverlayPath,
1483
+ aliasModelCount,
1484
+ providerIds,
1485
+ cwd: resolvedProjectRoot,
1486
+ entrypoint: OPENCODE_ENTRYPOINT
1487
+ }
1488
+ });
1489
+ const launchResult = await launchOpenCodeProcess({
1490
+ preset: cliArgs.preset,
1491
+ projectRoot: resolvedProjectRoot,
1492
+ runtimeOverlay,
1493
+ runtimeEnvironment,
1494
+ traceContext
1495
+ });
1496
+ if (launchResult.exitCode !== 0) {
1497
+ const cleanupResult = await cleanupRuntimeOverlay(runtimeOverlayPath);
1498
+ const failureResult = await finalizeLaunchFailure(traceRecorder, traceContext, {
1499
+ preset: cliArgs.preset,
1500
+ projectRoot: resolvedProjectRoot,
1501
+ runtimeOverlayPath,
1502
+ code: "LAUNCH_OPENCODE_FAILED",
1503
+ exitCode: launchResult.exitCode,
1504
+ cleanupResult,
1505
+ aliasModelCount,
1506
+ providerIds
1507
+ });
1508
+ return failureResult;
1509
+ }
1510
+ const launchReadyTrace = await persistLaunchTrace(traceRecorder, traceContext, {
1511
+ event: "launch.ready",
1512
+ module: "M-LAUNCHER",
1513
+ function: "runPivvOpencode",
1514
+ block: BLOCK_LOAD_ACTIVE_PRESET,
1515
+ outcome: "success",
1516
+ payload: {
1517
+ preset: cliArgs.preset,
1518
+ projectRoot: resolvedProjectRoot,
1519
+ runtimeOverlayPath,
1520
+ aliasModelCount,
1521
+ providerIds,
1522
+ cwd: resolvedProjectRoot,
1523
+ entrypoint: OPENCODE_ENTRYPOINT
1524
+ }
1525
+ });
1526
+ const runFinishTrace = await persistLaunchTrace(traceRecorder, traceContext, {
1527
+ event: "run.finish",
1528
+ module: "M-LAUNCHER",
1529
+ function: "runPivvOpencode",
1530
+ block: BLOCK_LOAD_ACTIVE_PRESET,
1531
+ outcome: "success",
1532
+ terminal: true,
1533
+ payload: {
1534
+ preset: cliArgs.preset,
1535
+ projectRoot: resolvedProjectRoot,
1536
+ runtimeOverlayPath,
1537
+ cwd: resolvedProjectRoot,
1538
+ entrypoint: OPENCODE_ENTRYPOINT
1539
+ }
1540
+ });
1541
+ console.info(`${LAUNCHER_LOG_PREFIX}[runPivvOpencode][${BLOCK_LOAD_ACTIVE_PRESET}] launch completed`, {
1542
+ preset: cliArgs.preset,
1543
+ projectRoot: resolvedProjectRoot,
1544
+ runtimeOverlayPath,
1545
+ outcome: "launch-ready"
1546
+ });
1547
+ return {
1548
+ ok: true,
1549
+ preset: cliArgs.preset,
1550
+ projectRoot: resolvedProjectRoot,
1551
+ runtimeOverlayPath,
1552
+ outcome: "launch-ready",
1553
+ exitCode: launchResult.exitCode,
1554
+ traceFilePath: runFinishTrace?.traceFilePath ?? launchReadyTrace?.traceFilePath
1555
+ };
1556
+ } catch (error) {
1557
+ if (lifecycleStage === "config.load") {
1558
+ await persistLaunchTrace(traceRecorder, traceContext, {
1559
+ event: "config.load.fail",
1560
+ module: "M-CONFIG",
1561
+ function: "loadPivvConfig",
1562
+ block: "BLOCK_VALIDATE_CONFIG",
1563
+ outcome: "failure",
1564
+ payload: {
1565
+ preset: cliArgs.preset,
1566
+ projectRoot: resolvedProjectRoot,
1567
+ cwd: resolvedProjectRoot,
1568
+ entrypoint: OPENCODE_ENTRYPOINT
1569
+ }
1570
+ });
1571
+ }
1572
+ if (lifecycleStage === "preset.resolve") {
1573
+ await persistLaunchTrace(traceRecorder, traceContext, {
1574
+ event: "preset.resolve.fail",
1575
+ module: "M-PRESET",
1576
+ function: "resolvePreset",
1577
+ block: "BLOCK_RESOLVE_PROFILE_BINDINGS",
1578
+ outcome: "failure",
1579
+ payload: {
1580
+ preset: cliArgs.preset,
1581
+ projectRoot: resolvedProjectRoot,
1582
+ coreSlotsResolved: 0,
1583
+ cwd: resolvedProjectRoot,
1584
+ entrypoint: OPENCODE_ENTRYPOINT
1585
+ }
1586
+ });
1587
+ }
1588
+ if (lifecycleStage === "overlay.build") {
1589
+ await persistLaunchTrace(traceRecorder, traceContext, {
1590
+ event: "overlay.build.fail",
1591
+ module: "M-OVERLAY",
1592
+ function: "buildRuntimeOverlay",
1593
+ block: "BLOCK_GENERATE_ALIAS_MODELS",
1594
+ outcome: "failure",
1595
+ payload: {
1596
+ preset: cliArgs.preset,
1597
+ projectRoot: resolvedProjectRoot,
1598
+ runtimeOverlayPath,
1599
+ aliasModelCount,
1600
+ providerIds,
1601
+ cwd: resolvedProjectRoot,
1602
+ entrypoint: OPENCODE_ENTRYPOINT
1603
+ }
1604
+ });
1605
+ }
1606
+ const failureCode = isPresetInvalidError(error) ? "LAUNCH_PRESET_INVALID" : "LAUNCH_OPENCODE_FAILED";
1607
+ const cleanupResult = failureCode === "LAUNCH_OPENCODE_FAILED" && runtimeOverlayPath ? await cleanupRuntimeOverlay(runtimeOverlayPath) : undefined;
1608
+ return finalizeLaunchFailure(traceRecorder, traceContext, {
1609
+ preset: cliArgs.preset,
1610
+ projectRoot: resolvedProjectRoot,
1611
+ runtimeOverlayPath,
1612
+ code: failureCode,
1613
+ cleanupResult,
1614
+ aliasModelCount,
1615
+ providerIds
1616
+ });
1617
+ }
1618
+ }
1619
+ function createLaunchRunId() {
1620
+ return `launch-${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
1621
+ }
1622
+ function createEmptyOpenCodeBaseInputs() {
1623
+ return {
1624
+ providers: {},
1625
+ agents: {},
1626
+ commands: {},
1627
+ tools: {}
1628
+ };
1629
+ }
1630
+ function isPresetInvalidError(error) {
1631
+ if (!error || typeof error !== "object") {
1632
+ return false;
1633
+ }
1634
+ const errorCode = error.code;
1635
+ return errorCode === "PRESET_NOT_FOUND" || errorCode === "PROFILE_MISSING_CORE_SLOT" || errorCode === "PROFILE_ALIAS_CONFLICT" || errorCode === "CUSTOM_NAME_COLLISION" || errorCode === "PROFILE_ALIAS_GENERATION_FAILED" || errorCode === "CONFIG_NOT_FOUND" || errorCode === "CONFIG_INVALID";
1636
+ }
1637
+ async function cleanupRuntimeOverlay(runtimeOverlayPath) {
1638
+ try {
1639
+ await rm(runtimeOverlayPath, { force: true });
1640
+ return {
1641
+ cleanupFailed: false
1642
+ };
1643
+ } catch (error) {
1644
+ return {
1645
+ cleanupFailed: true,
1646
+ cleanupErrorMessage: error instanceof Error ? error.message : "unknown cleanup error"
1647
+ };
1648
+ }
1649
+ }
1650
+ async function persistLaunchTrace(traceRecorder, traceContext, event) {
1651
+ try {
1652
+ return await traceRecorder.persistTraceEvent(event, traceContext);
1653
+ } catch {
1654
+ return;
1655
+ }
1656
+ }
1657
+ async function finalizeLaunchFailure(traceRecorder, traceContext, context) {
1658
+ const traceResult = await persistLaunchTrace(traceRecorder, traceContext, {
1659
+ event: "launch.fail",
1660
+ module: "M-LAUNCHER",
1661
+ function: "runPivvOpencode",
1662
+ block: BLOCK_LOAD_ACTIVE_PRESET,
1663
+ outcome: "failure",
1664
+ payload: {
1665
+ preset: context.preset,
1666
+ projectRoot: context.projectRoot,
1667
+ runtimeOverlayPath: context.runtimeOverlayPath,
1668
+ code: context.code,
1669
+ exitCode: context.exitCode,
1670
+ cleanupFailed: context.cleanupResult?.cleanupFailed === true,
1671
+ cleanupErrorMessage: context.cleanupResult?.cleanupErrorMessage,
1672
+ aliasModelCount: context.aliasModelCount,
1673
+ providerIds: context.providerIds,
1674
+ cwd: context.projectRoot,
1675
+ entrypoint: OPENCODE_ENTRYPOINT
1676
+ }
1677
+ });
1678
+ const runFailTrace = await persistLaunchTrace(traceRecorder, traceContext, {
1679
+ event: "run.fail",
1680
+ module: "M-LAUNCHER",
1681
+ function: "runPivvOpencode",
1682
+ block: BLOCK_LOAD_ACTIVE_PRESET,
1683
+ outcome: "failure",
1684
+ terminal: true,
1685
+ payload: {
1686
+ preset: context.preset,
1687
+ projectRoot: context.projectRoot,
1688
+ runtimeOverlayPath: context.runtimeOverlayPath,
1689
+ code: context.code,
1690
+ exitCode: context.exitCode,
1691
+ cwd: context.projectRoot,
1692
+ entrypoint: OPENCODE_ENTRYPOINT
1693
+ }
1694
+ });
1695
+ console.info(`${LAUNCHER_LOG_PREFIX}[runPivvOpencode][${BLOCK_LOAD_ACTIVE_PRESET}] launch failed`, {
1696
+ preset: context.preset,
1697
+ projectRoot: context.projectRoot,
1698
+ runtimeOverlayPath: context.runtimeOverlayPath,
1699
+ outcome: "launch-failed"
1700
+ });
1701
+ return {
1702
+ ok: false,
1703
+ preset: context.preset,
1704
+ projectRoot: context.projectRoot,
1705
+ runtimeOverlayPath: context.runtimeOverlayPath,
1706
+ outcome: "launch-failed",
1707
+ code: context.code,
1708
+ exitCode: context.exitCode,
1709
+ traceFilePath: runFailTrace?.traceFilePath ?? traceResult?.traceFilePath
1710
+ };
1711
+ }
1712
+ async function launchOpenCodeProcess(request) {
1713
+ return await new Promise((resolve, reject) => {
1714
+ const launchedProcess = spawn("bun", ["x", "opencode-ai"], {
1715
+ cwd: request.projectRoot,
1716
+ env: request.runtimeEnvironment,
1717
+ stdio: "inherit"
1718
+ });
1719
+ launchedProcess.once("error", (error) => {
1720
+ reject(error);
1721
+ });
1722
+ launchedProcess.once("close", (exitCode) => {
1723
+ resolve({
1724
+ exitCode: typeof exitCode === "number" ? exitCode : 1
1725
+ });
1726
+ });
1727
+ });
1728
+ }
1729
+
1730
+ // src/cli/vvcode.ts
1731
+ var CLI_LOG_PREFIX = "[VvcodeCli]";
1732
+ var BLOCK_RUN_OPENCODE_COMMAND = "BLOCK_RUN_OPENCODE_COMMAND";
1733
+ function resolveDefaultGlobalConfigPath() {
1734
+ return join3(homedir(), ".config", "vvcode", "config.jsonc");
1735
+ }
1736
+ function createVvcodeCommand(dependencyOverrides = {}) {
1737
+ const dependencies = resolveVvcodeCliDependencies(dependencyOverrides);
1738
+ const opencodeArgs = {
1739
+ preset: {
1740
+ type: "string",
1741
+ description: "Preset name defined in .vvcode/config.jsonc.",
1742
+ alias: ["p"],
1743
+ required: true
1744
+ },
1745
+ "global-config": {
1746
+ type: "string",
1747
+ description: "Path to the global vvcode config file.",
1748
+ alias: ["c"],
1749
+ default: dependencies.defaultGlobalConfigPath()
1750
+ },
1751
+ "project-root": {
1752
+ type: "string",
1753
+ description: "Working directory or nested path inside the target .vvcode project.",
1754
+ alias: ["r"],
1755
+ default: dependencies.cwd()
1756
+ }
1757
+ };
1758
+ return defineCommand({
1759
+ meta: {
1760
+ name: "vvcode",
1761
+ version: package_default.version,
1762
+ description: "Packaged CLI for launching OpenCode with vvcode runtime semantics."
1763
+ },
1764
+ args: opencodeArgs,
1765
+ default: "opencode",
1766
+ subCommands: {
1767
+ opencode: defineCommand({
1768
+ meta: {
1769
+ name: "opencode",
1770
+ description: "Launch OpenCode with a vvcode preset-backed runtime overlay."
1771
+ },
1772
+ args: opencodeArgs,
1773
+ async run({ args }) {
1774
+ console.info(`${CLI_LOG_PREFIX}[runOpencodeCommand][${BLOCK_RUN_OPENCODE_COMMAND}] dispatching vvcode launcher`, {
1775
+ preset: String(args.preset),
1776
+ globalConfigPath: String(args.globalConfig),
1777
+ projectRoot: String(args.projectRoot)
1778
+ });
1779
+ const launchResult = await dependencies.launchOpenCode({
1780
+ preset: String(args.preset),
1781
+ globalConfigPath: String(args.globalConfig),
1782
+ projectRoot: String(args.projectRoot)
1783
+ });
1784
+ if (!launchResult.ok) {
1785
+ dependencies.writeStderr(formatLaunchFailure(launchResult));
1786
+ dependencies.setExitCode(launchResult.exitCode ?? 1);
1787
+ }
1788
+ return launchResult;
1789
+ }
1790
+ })
1791
+ }
1792
+ });
1793
+ }
1794
+ async function runVvcodeCli(rawArgs = process.argv.slice(2), dependencyOverrides = {}) {
1795
+ const vvcodeCommand = createVvcodeCommand(dependencyOverrides);
1796
+ await runMain(vvcodeCommand, { rawArgs });
1797
+ }
1798
+ function resolveVvcodeCliDependencies(dependencyOverrides) {
1799
+ return {
1800
+ launchOpenCode: dependencyOverrides.launchOpenCode ?? runPivvOpencode,
1801
+ cwd: dependencyOverrides.cwd ?? (() => process.cwd()),
1802
+ defaultGlobalConfigPath: dependencyOverrides.defaultGlobalConfigPath ?? resolveDefaultGlobalConfigPath,
1803
+ writeStderr: dependencyOverrides.writeStderr ?? ((message) => console.error(message)),
1804
+ setExitCode: dependencyOverrides.setExitCode ?? ((code) => {
1805
+ process.exitCode = code;
1806
+ })
1807
+ };
1808
+ }
1809
+ function formatLaunchFailure(launchResult) {
1810
+ const failureFields = [
1811
+ `vvcode: launch failed (${launchResult.code ?? "LAUNCH_OPENCODE_FAILED"})`,
1812
+ `preset="${launchResult.preset}"`,
1813
+ `projectRoot="${launchResult.projectRoot}"`
1814
+ ];
1815
+ if (launchResult.traceFilePath) {
1816
+ failureFields.push(`traceFilePath="${launchResult.traceFilePath}"`);
1817
+ }
1818
+ if (launchResult.runtimeOverlayPath) {
1819
+ failureFields.push(`runtimeOverlayPath="${launchResult.runtimeOverlayPath}"`);
1820
+ }
1821
+ return failureFields.join(" ");
1822
+ }
1823
+
1824
+ // src/cli/vvcode-entry.ts
1825
+ await runVvcodeCli();