@osovv/vvcode 0.5.0 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +12 -0
  2. package/dist/vvcode.js +157 -30
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -92,6 +92,18 @@ vvcode opencode --preset <name>
92
92
  vvcode --preset <name>
93
93
  ```
94
94
 
95
+ By default `vvcode` starts the shell with:
96
+
97
+ ```bash
98
+ opencode
99
+ ```
100
+
101
+ You can override the shell command, for example:
102
+
103
+ ```bash
104
+ OPENCODE_SHELL_COMMAND='bun x opencode-ai' vvcode --preset openai
105
+ ```
106
+
95
107
  Options:
96
108
 
97
109
  1. `--preset`, `-p` for the preset name.
package/dist/vvcode.js CHANGED
@@ -547,7 +547,7 @@ import { join as join3, resolve } from "path";
547
547
  // package.json
548
548
  var package_default = {
549
549
  name: "@osovv/vvcode",
550
- version: "0.5.0",
550
+ version: "0.5.1",
551
551
  description: "Packaged CLI for launching OpenCode with vvcode runtime semantics.",
552
552
  license: "MIT",
553
553
  homepage: "https://github.com/osovv/vvcode",
@@ -565,7 +565,7 @@ var package_default = {
565
565
  "dist"
566
566
  ],
567
567
  bin: {
568
- vvcode: "./bin/vvcode"
568
+ vvcode: "bin/vvcode"
569
569
  },
570
570
  publishConfig: {
571
571
  access: "public"
@@ -1359,7 +1359,8 @@ function createTraceWriteError(message, cause) {
1359
1359
  // src/cli/pivv-opencode.ts
1360
1360
  var LAUNCHER_LOG_PREFIX = "[VvcodeLauncher]";
1361
1361
  var BLOCK_LOAD_ACTIVE_PRESET = "BLOCK_LOAD_ACTIVE_PRESET";
1362
- var OPENCODE_ENTRYPOINT = "bun x opencode-ai";
1362
+ var DEFAULT_OPENCODE_SHELL_COMMAND = "opencode";
1363
+ var OPENCODE_SHELL_COMMAND_ENV = "OPENCODE_SHELL_COMMAND";
1363
1364
  function buildRuntimeEnvironment(runtimeOverlay, baseEnvironment = process.env) {
1364
1365
  const runtimeEnvironment = {};
1365
1366
  for (const [key, value] of Object.entries(baseEnvironment)) {
@@ -1368,7 +1369,12 @@ function buildRuntimeEnvironment(runtimeOverlay, baseEnvironment = process.env)
1368
1369
  }
1369
1370
  }
1370
1371
  runtimeEnvironment.OPENCODE_CONFIG_DIR = runtimeOverlay.overlayDirPath;
1371
- runtimeEnvironment.OPENCODE_CONFIG_CONTENT = JSON.stringify(runtimeOverlay);
1372
+ runtimeEnvironment.OPENCODE_CONFIG_CONTENT = JSON.stringify({
1373
+ provider: normalizeProviderConfig(runtimeOverlay.providers),
1374
+ agent: runtimeOverlay.agents,
1375
+ command: normalizeCommandConfig(runtimeOverlay.commands),
1376
+ tools: runtimeOverlay.tools
1377
+ });
1372
1378
  return runtimeEnvironment;
1373
1379
  }
1374
1380
  async function runPivvOpencode(cliArgs) {
@@ -1383,6 +1389,8 @@ async function runPivvOpencode(cliArgs) {
1383
1389
  let aliasModelCount = 0;
1384
1390
  let providerIds = [];
1385
1391
  let lifecycleStage = "config.load";
1392
+ const openCodeShellCommand = resolveOpenCodeShellCommand();
1393
+ const openCodeEntrypoint = openCodeShellCommand.entrypoint;
1386
1394
  if (!traceRecorder) {
1387
1395
  try {
1388
1396
  resolvedProjectRoot = await findProjectRoot(cliArgs.projectRoot);
@@ -1401,7 +1409,7 @@ async function runPivvOpencode(cliArgs) {
1401
1409
  preset: cliArgs.preset,
1402
1410
  projectRoot: resolvedProjectRoot,
1403
1411
  cwd: resolvedProjectRoot,
1404
- entrypoint: OPENCODE_ENTRYPOINT
1412
+ entrypoint: openCodeEntrypoint
1405
1413
  }
1406
1414
  });
1407
1415
  try {
@@ -1415,7 +1423,7 @@ async function runPivvOpencode(cliArgs) {
1415
1423
  preset: cliArgs.preset,
1416
1424
  projectRoot: resolvedProjectRoot,
1417
1425
  cwd: resolvedProjectRoot,
1418
- entrypoint: OPENCODE_ENTRYPOINT
1426
+ entrypoint: openCodeEntrypoint
1419
1427
  }
1420
1428
  });
1421
1429
  const projectConfig = await loadPivvConfig(cliArgs.globalConfigPath, cliArgs.projectRoot);
@@ -1430,7 +1438,7 @@ async function runPivvOpencode(cliArgs) {
1430
1438
  preset: cliArgs.preset,
1431
1439
  projectRoot: resolvedProjectRoot,
1432
1440
  cwd: resolvedProjectRoot,
1433
- entrypoint: OPENCODE_ENTRYPOINT
1441
+ entrypoint: openCodeEntrypoint
1434
1442
  }
1435
1443
  });
1436
1444
  lifecycleStage = "preset.resolve";
@@ -1444,7 +1452,7 @@ async function runPivvOpencode(cliArgs) {
1444
1452
  preset: cliArgs.preset,
1445
1453
  coreSlotsResolved: 0,
1446
1454
  cwd: resolvedProjectRoot,
1447
- entrypoint: OPENCODE_ENTRYPOINT
1455
+ entrypoint: openCodeEntrypoint
1448
1456
  }
1449
1457
  });
1450
1458
  const resolvedPreset = resolvePreset(cliArgs.preset, projectConfig);
@@ -1459,7 +1467,7 @@ async function runPivvOpencode(cliArgs) {
1459
1467
  profileCount: resolvedPreset.profileCount,
1460
1468
  coreSlotsResolved: Object.keys(resolvedPreset.coreProfiles).length,
1461
1469
  cwd: resolvedProjectRoot,
1462
- entrypoint: OPENCODE_ENTRYPOINT
1470
+ entrypoint: openCodeEntrypoint
1463
1471
  }
1464
1472
  });
1465
1473
  lifecycleStage = "overlay.build";
@@ -1474,7 +1482,7 @@ async function runPivvOpencode(cliArgs) {
1474
1482
  aliasModelCount,
1475
1483
  providerIds,
1476
1484
  cwd: resolvedProjectRoot,
1477
- entrypoint: OPENCODE_ENTRYPOINT
1485
+ entrypoint: openCodeEntrypoint
1478
1486
  }
1479
1487
  });
1480
1488
  const runtimeOverlay = await buildRuntimeOverlay(resolvedPreset, projectConfig, baseOpenCodeInputs);
@@ -1493,7 +1501,7 @@ async function runPivvOpencode(cliArgs) {
1493
1501
  aliasModelCount,
1494
1502
  providerIds,
1495
1503
  cwd: resolvedProjectRoot,
1496
- entrypoint: OPENCODE_ENTRYPOINT
1504
+ entrypoint: openCodeEntrypoint
1497
1505
  }
1498
1506
  });
1499
1507
  lifecycleStage = "launch";
@@ -1511,7 +1519,7 @@ async function runPivvOpencode(cliArgs) {
1511
1519
  aliasModelCount,
1512
1520
  providerIds,
1513
1521
  cwd: resolvedProjectRoot,
1514
- entrypoint: OPENCODE_ENTRYPOINT
1522
+ entrypoint: openCodeEntrypoint
1515
1523
  }
1516
1524
  });
1517
1525
  const launchResult = await launchOpenCodeProcess({
@@ -1519,7 +1527,8 @@ async function runPivvOpencode(cliArgs) {
1519
1527
  projectRoot: resolvedProjectRoot,
1520
1528
  runtimeOverlay,
1521
1529
  runtimeEnvironment,
1522
- traceContext
1530
+ traceContext,
1531
+ openCodeShellCommand
1523
1532
  });
1524
1533
  if (launchResult.exitCode !== 0) {
1525
1534
  const cleanupResult = await cleanupRuntimeOverlay(runtimeOverlayPath);
@@ -1531,7 +1540,8 @@ async function runPivvOpencode(cliArgs) {
1531
1540
  exitCode: launchResult.exitCode,
1532
1541
  cleanupResult,
1533
1542
  aliasModelCount,
1534
- providerIds
1543
+ providerIds,
1544
+ entrypoint: openCodeEntrypoint
1535
1545
  });
1536
1546
  return failureResult;
1537
1547
  }
@@ -1548,7 +1558,7 @@ async function runPivvOpencode(cliArgs) {
1548
1558
  aliasModelCount,
1549
1559
  providerIds,
1550
1560
  cwd: resolvedProjectRoot,
1551
- entrypoint: OPENCODE_ENTRYPOINT
1561
+ entrypoint: openCodeEntrypoint
1552
1562
  }
1553
1563
  });
1554
1564
  const runFinishTrace = await persistLaunchTrace(traceRecorder, traceContext, {
@@ -1563,7 +1573,7 @@ async function runPivvOpencode(cliArgs) {
1563
1573
  projectRoot: resolvedProjectRoot,
1564
1574
  runtimeOverlayPath,
1565
1575
  cwd: resolvedProjectRoot,
1566
- entrypoint: OPENCODE_ENTRYPOINT
1576
+ entrypoint: openCodeEntrypoint
1567
1577
  }
1568
1578
  });
1569
1579
  console.info(`${LAUNCHER_LOG_PREFIX}[runPivvOpencode][${BLOCK_LOAD_ACTIVE_PRESET}] launch completed`, {
@@ -1593,7 +1603,7 @@ async function runPivvOpencode(cliArgs) {
1593
1603
  preset: cliArgs.preset,
1594
1604
  projectRoot: resolvedProjectRoot,
1595
1605
  cwd: resolvedProjectRoot,
1596
- entrypoint: OPENCODE_ENTRYPOINT
1606
+ entrypoint: openCodeEntrypoint
1597
1607
  }
1598
1608
  });
1599
1609
  }
@@ -1609,7 +1619,7 @@ async function runPivvOpencode(cliArgs) {
1609
1619
  projectRoot: resolvedProjectRoot,
1610
1620
  coreSlotsResolved: 0,
1611
1621
  cwd: resolvedProjectRoot,
1612
- entrypoint: OPENCODE_ENTRYPOINT
1622
+ entrypoint: openCodeEntrypoint
1613
1623
  }
1614
1624
  });
1615
1625
  }
@@ -1627,7 +1637,7 @@ async function runPivvOpencode(cliArgs) {
1627
1637
  aliasModelCount,
1628
1638
  providerIds,
1629
1639
  cwd: resolvedProjectRoot,
1630
- entrypoint: OPENCODE_ENTRYPOINT
1640
+ entrypoint: openCodeEntrypoint
1631
1641
  }
1632
1642
  });
1633
1643
  }
@@ -1640,7 +1650,8 @@ async function runPivvOpencode(cliArgs) {
1640
1650
  code: failureCode,
1641
1651
  cleanupResult,
1642
1652
  aliasModelCount,
1643
- providerIds
1653
+ providerIds,
1654
+ entrypoint: openCodeEntrypoint
1644
1655
  });
1645
1656
  }
1646
1657
  }
@@ -1700,7 +1711,7 @@ async function finalizeLaunchFailure(traceRecorder, traceContext, context) {
1700
1711
  aliasModelCount: context.aliasModelCount,
1701
1712
  providerIds: context.providerIds,
1702
1713
  cwd: context.projectRoot,
1703
- entrypoint: OPENCODE_ENTRYPOINT
1714
+ entrypoint: context.entrypoint
1704
1715
  }
1705
1716
  });
1706
1717
  const runFailTrace = await persistLaunchTrace(traceRecorder, traceContext, {
@@ -1717,7 +1728,7 @@ async function finalizeLaunchFailure(traceRecorder, traceContext, context) {
1717
1728
  code: context.code,
1718
1729
  exitCode: context.exitCode,
1719
1730
  cwd: context.projectRoot,
1720
- entrypoint: OPENCODE_ENTRYPOINT
1731
+ entrypoint: context.entrypoint
1721
1732
  }
1722
1733
  });
1723
1734
  console.info(`${LAUNCHER_LOG_PREFIX}[runPivvOpencode][${BLOCK_LOAD_ACTIVE_PRESET}] launch failed`, {
@@ -1739,7 +1750,7 @@ async function finalizeLaunchFailure(traceRecorder, traceContext, context) {
1739
1750
  }
1740
1751
  async function launchOpenCodeProcess(request) {
1741
1752
  return await new Promise((resolve, reject) => {
1742
- const launchedProcess = spawn("bun", ["x", "opencode-ai"], {
1753
+ const launchedProcess = spawn(request.openCodeShellCommand.executable, request.openCodeShellCommand.args, {
1743
1754
  cwd: request.projectRoot,
1744
1755
  env: request.runtimeEnvironment,
1745
1756
  stdio: "inherit"
@@ -1754,6 +1765,122 @@ async function launchOpenCodeProcess(request) {
1754
1765
  });
1755
1766
  });
1756
1767
  }
1768
+ function resolveOpenCodeShellCommand(baseEnvironment = process.env) {
1769
+ const configuredCommand = baseEnvironment[OPENCODE_SHELL_COMMAND_ENV]?.trim();
1770
+ const entrypoint = configuredCommand && configuredCommand.length > 0 ? configuredCommand : DEFAULT_OPENCODE_SHELL_COMMAND;
1771
+ const tokens = splitShellCommand(entrypoint);
1772
+ if (tokens.length === 0) {
1773
+ return {
1774
+ executable: DEFAULT_OPENCODE_SHELL_COMMAND,
1775
+ args: [],
1776
+ entrypoint: DEFAULT_OPENCODE_SHELL_COMMAND
1777
+ };
1778
+ }
1779
+ return {
1780
+ executable: tokens[0],
1781
+ args: tokens.slice(1),
1782
+ entrypoint
1783
+ };
1784
+ }
1785
+ function splitShellCommand(command) {
1786
+ const tokens = [];
1787
+ let current = "";
1788
+ let quote = null;
1789
+ for (let index = 0;index < command.length; index += 1) {
1790
+ const character = command[index];
1791
+ if (quote === null) {
1792
+ if (character === '"' || character === "'") {
1793
+ quote = character === '"' ? "double" : "single";
1794
+ continue;
1795
+ }
1796
+ if (/\s/.test(character)) {
1797
+ if (current.length > 0) {
1798
+ tokens.push(current);
1799
+ current = "";
1800
+ }
1801
+ continue;
1802
+ }
1803
+ } else if (quote === "double" && character === '"' || quote === "single" && character === "'") {
1804
+ quote = null;
1805
+ continue;
1806
+ }
1807
+ current += character;
1808
+ }
1809
+ if (current.length > 0) {
1810
+ tokens.push(current);
1811
+ }
1812
+ return tokens;
1813
+ }
1814
+ function normalizeCommandConfig(commands) {
1815
+ const normalizedCommands = {};
1816
+ for (const [commandName, commandConfig] of Object.entries(commands)) {
1817
+ const template = typeof commandConfig.prompt === "string" ? commandConfig.prompt : undefined;
1818
+ if (!template) {
1819
+ continue;
1820
+ }
1821
+ normalizedCommands[commandName] = {
1822
+ template,
1823
+ ...typeof commandConfig.description === "string" ? { description: commandConfig.description } : {},
1824
+ ...typeof commandConfig.agent === "string" ? { agent: commandConfig.agent } : {},
1825
+ ...typeof commandConfig.model === "string" ? { model: commandConfig.model } : {},
1826
+ ...typeof commandConfig.subtask === "boolean" ? { subtask: commandConfig.subtask } : {}
1827
+ };
1828
+ }
1829
+ return normalizedCommands;
1830
+ }
1831
+ function normalizeProviderConfig(providers) {
1832
+ const normalizedProviders = {};
1833
+ for (const [providerId, providerConfig] of Object.entries(providers)) {
1834
+ const normalizedProvider = {};
1835
+ for (const key of ["api", "name", "env", "id", "npm", "whitelist", "blacklist", "options"]) {
1836
+ if (key in providerConfig) {
1837
+ normalizedProvider[key] = providerConfig[key];
1838
+ }
1839
+ }
1840
+ if (isRecord2(providerConfig.models)) {
1841
+ const normalizedModels = {};
1842
+ for (const [modelId, modelConfig] of Object.entries(providerConfig.models)) {
1843
+ if (!isRecord2(modelConfig)) {
1844
+ continue;
1845
+ }
1846
+ const normalizedModel = {};
1847
+ for (const key of [
1848
+ "id",
1849
+ "name",
1850
+ "family",
1851
+ "release_date",
1852
+ "attachment",
1853
+ "reasoning",
1854
+ "temperature",
1855
+ "tool_call",
1856
+ "interleaved",
1857
+ "cost",
1858
+ "limit",
1859
+ "modalities",
1860
+ "experimental",
1861
+ "status",
1862
+ "options",
1863
+ "headers",
1864
+ "variants"
1865
+ ]) {
1866
+ if (key in modelConfig) {
1867
+ normalizedModel[key] = modelConfig[key];
1868
+ }
1869
+ }
1870
+ if (isRecord2(modelConfig.provider)) {
1871
+ normalizedModel.provider = modelConfig.provider;
1872
+ }
1873
+ normalizedModels[modelId] = normalizedModel;
1874
+ }
1875
+ normalizedProvider.models = normalizedModels;
1876
+ }
1877
+ normalizedProviders[providerId] = normalizedProvider;
1878
+ }
1879
+ return normalizedProviders;
1880
+ }
1881
+ function isRecord2(value) {
1882
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1883
+ }
1757
1884
 
1758
1885
  // src/cli/vvcode.ts
1759
1886
  var CLI_LOG_PREFIX = "[VvcodeCli]";
@@ -1908,7 +2035,7 @@ function createVvcodeCommand(dependencyOverrides = {}) {
1908
2035
  const snapshot = await loadProjectConfigSnapshot(String(args.globalConfig), String(args.projectRoot));
1909
2036
  const globalPresets = toObjectBucket(snapshot.globalConfig?.presets);
1910
2037
  const globalProfiles = toObjectBucket(snapshot.globalConfig?.profiles);
1911
- if (!(presetName in globalPresets) || !isRecord2(globalPresets[presetName])) {
2038
+ if (!(presetName in globalPresets) || !isRecord3(globalPresets[presetName])) {
1912
2039
  throw new Error(`Preset "${presetName}" was not found in global config ${snapshot.globalConfigPath}`);
1913
2040
  }
1914
2041
  const projectConfig = snapshot.projectConfig ?? createEmptyRawVvcodeConfig();
@@ -1927,7 +2054,7 @@ function createVvcodeCommand(dependencyOverrides = {}) {
1927
2054
  }
1928
2055
  const profileName = profileNameValue;
1929
2056
  const globalProfile = globalProfiles[profileName];
1930
- if (!isRecord2(globalProfile)) {
2057
+ if (!isRecord3(globalProfile)) {
1931
2058
  throw new Error(`Global preset "${presetName}" references missing profile "${profileName}"`);
1932
2059
  }
1933
2060
  if (profileName in projectProfiles) {
@@ -1969,13 +2096,13 @@ function createVvcodeCommand(dependencyOverrides = {}) {
1969
2096
  const projectPresets = toMutableBucket(projectConfig, "presets");
1970
2097
  const projectProfiles = toMutableBucket(projectConfig, "profiles");
1971
2098
  const removedPreset = projectPresets[presetName];
1972
- if (!isRecord2(removedPreset)) {
2099
+ if (!isRecord3(removedPreset)) {
1973
2100
  throw new Error(`Preset "${presetName}" was not found in project config ${projectConfigPath}`);
1974
2101
  }
1975
2102
  delete projectPresets[presetName];
1976
2103
  const stillReferencedProfiles = new Set;
1977
2104
  for (const presetValue of Object.values(projectPresets)) {
1978
- if (!isRecord2(presetValue)) {
2105
+ if (!isRecord3(presetValue)) {
1979
2106
  continue;
1980
2107
  }
1981
2108
  for (const profileNameValue of Object.values(presetValue)) {
@@ -2086,7 +2213,7 @@ function describePresetSource(snapshot, presetName) {
2086
2213
  }
2087
2214
  function toMutableBucket(config, key) {
2088
2215
  const bucket = config[key];
2089
- if (isRecord2(bucket)) {
2216
+ if (isRecord3(bucket)) {
2090
2217
  return bucket;
2091
2218
  }
2092
2219
  const nextBucket = {};
@@ -2094,9 +2221,9 @@ function toMutableBucket(config, key) {
2094
2221
  return nextBucket;
2095
2222
  }
2096
2223
  function toObjectBucket(value) {
2097
- return isRecord2(value) ? value : {};
2224
+ return isRecord3(value) ? value : {};
2098
2225
  }
2099
- function isRecord2(value) {
2226
+ function isRecord3(value) {
2100
2227
  return typeof value === "object" && value !== null && !Array.isArray(value);
2101
2228
  }
2102
2229
  function areJsonValuesEqual(left, right) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@osovv/vvcode",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Packaged CLI for launching OpenCode with vvcode runtime semantics.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/osovv/vvcode",
@@ -18,7 +18,7 @@
18
18
  "dist"
19
19
  ],
20
20
  "bin": {
21
- "vvcode": "./bin/vvcode"
21
+ "vvcode": "bin/vvcode"
22
22
  },
23
23
  "publishConfig": {
24
24
  "access": "public"