pantheon-opencode 1.6.0-beta.7 → 1.6.0-beta.8
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/AGENTS.md +58 -15
- package/CHANGELOG.md +37 -100
- package/README.md +78 -102
- package/README.pt-BR.md +19 -37
- package/package.json +1 -2
- package/plugin.json +2 -2
- package/pyproject.toml +1 -1
- package/scripts/build-agents-md.mjs +2 -3
- package/scripts/doctor.mjs +22 -104
- package/scripts/hooks/scan-secrets.sh +97 -0
- package/scripts/install/config-migration.mjs +9 -42
- package/scripts/install/interactive.mjs +1 -1
- package/scripts/install/opencode.mjs +114 -43
- package/scripts/install/strings.mjs +0 -8
- package/scripts/probe-pantheon-cost.mjs +3 -16
- package/scripts/test-opencode-v2-sandbox.sh +203 -786
- package/scripts/validate-sandbox-install.mjs +138 -0
- package/skills-lock.json +3 -4
- package/src/agents/aphrodite.md +2 -9
- package/src/agents/demeter.md +0 -9
- package/src/agents/gaia.md +1 -0
- package/src/agents/hephaestus.md +0 -9
- package/src/agents/hermes.md +2 -9
- package/src/agents/mnemosyne.md +14 -42
- package/src/agents/prometheus.md +0 -9
- package/src/agents/themis.md +0 -1
- package/src/agents/zeus.md +180 -19
- package/src/instructions/agent-return-format.instructions.md +48 -3
- package/src/instructions/memory-protocol.instructions.md +8 -9
- package/src/instructions/zeus-anti-stall.instructions.md +54 -6
- package/src/instructions/zeus-council-synthesis.instructions.md +71 -0
- package/src/instructions/zeus-memory-operations.instructions.md +4 -4
- package/src/instructions/zeus-timeout-retry.instructions.md +81 -7
- package/src/pantheon/v2-delegation-enforce.ts +2 -1
- package/src/plugin-v2.ts +251 -168
- package/src/plugins/pantheon-hooks.ts +35 -82
- package/src/plugins/tui/dist/tui.d.ts.map +1 -1
- package/src/plugins/tui/dist/tui.js +26 -50
- package/src/plugins/tui/package-lock.json +2 -2
- package/src/plugins/tui/package.json +1 -1
- package/src/plugins/tui/src/index.tsx +25 -60
- package/src/routing.yml +4 -2
- package/src/skills/README.md +7 -12
- package/src/skills/agent-coordination/SKILL.md +83 -13
- package/src/skills/auto-continue/SKILL.md +13 -14
- package/src/skills/context-compression/SKILL.md +26 -879
- package/src/pantheon/secret-scanner.ts +0 -489
- package/src/plugins/tui/README.md +0 -14
- package/src/skills/council-synthesis/SKILL.md +0 -29
|
@@ -135,6 +135,74 @@ export const MANAGED_FIELDS = Object.freeze([
|
|
|
135
135
|
'disable_model_invocation',
|
|
136
136
|
])
|
|
137
137
|
|
|
138
|
+
const PANTHEON_MCP_SERVERS = Object.freeze([
|
|
139
|
+
'pantheon-resources',
|
|
140
|
+
'pantheon-memory',
|
|
141
|
+
'pantheon-code-mode',
|
|
142
|
+
'pantheon-persistence',
|
|
143
|
+
'pantheon-vision',
|
|
144
|
+
])
|
|
145
|
+
|
|
146
|
+
function normalizeMcpPermissionSegment(value) {
|
|
147
|
+
return value.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Translate an agent's `mcp_tools` frontmatter into per-tool native
|
|
152
|
+
* permissions. V1 matches the original server-tool names; V2 normalizes
|
|
153
|
+
* punctuation to underscores. Emitting both spellings keeps the same source
|
|
154
|
+
* metadata valid for either host. Unlisted bundled Pantheon servers default to
|
|
155
|
+
* deny; unrelated user MCP servers are not touched.
|
|
156
|
+
*
|
|
157
|
+
* @param {Record<string, 'all' | string[]>} mcpTools
|
|
158
|
+
* @returns {Record<string, 'allow' | 'deny'>}
|
|
159
|
+
*/
|
|
160
|
+
export function deriveNativeMcpPermissions(mcpTools) {
|
|
161
|
+
if (mcpTools === null || typeof mcpTools !== 'object' || Array.isArray(mcpTools)) {
|
|
162
|
+
throw new TypeError('mcp_tools must be an object keyed by MCP server name')
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const servers = new Set([...PANTHEON_MCP_SERVERS, ...Object.keys(mcpTools)])
|
|
166
|
+
const permissions = {}
|
|
167
|
+
|
|
168
|
+
for (const server of servers) {
|
|
169
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(server)) {
|
|
170
|
+
throw new TypeError(`mcp_tools server name is invalid: ${server}`)
|
|
171
|
+
}
|
|
172
|
+
const tools = Object.hasOwn(mcpTools, server) ? mcpTools[server] : []
|
|
173
|
+
if (
|
|
174
|
+
tools !== 'all' &&
|
|
175
|
+
(!Array.isArray(tools) ||
|
|
176
|
+
tools.some((tool) => typeof tool !== 'string' || !/^[a-zA-Z0-9._-]+$/.test(tool)))
|
|
177
|
+
) {
|
|
178
|
+
throw new TypeError(`mcp_tools.${server} must be "all" or an array of tool names`)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const serverAliases = new Set([server, normalizeMcpPermissionSegment(server)])
|
|
182
|
+
for (const alias of serverAliases) permissions[`${alias}_*`] = 'deny'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const server of servers) {
|
|
186
|
+
const tools = Object.hasOwn(mcpTools, server) ? mcpTools[server] : []
|
|
187
|
+
const serverAliases = new Set([server, normalizeMcpPermissionSegment(server)])
|
|
188
|
+
if (tools === 'all') {
|
|
189
|
+
for (const alias of serverAliases) permissions[`${alias}_*`] = 'allow'
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const tool of tools) {
|
|
194
|
+
const toolAliases = new Set([tool, normalizeMcpPermissionSegment(tool)])
|
|
195
|
+
for (const serverAlias of serverAliases) {
|
|
196
|
+
for (const toolAlias of toolAliases) {
|
|
197
|
+
permissions[`${serverAlias}_${toolAlias}`] = 'allow'
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return permissions
|
|
204
|
+
}
|
|
205
|
+
|
|
138
206
|
function deepClone(value) {
|
|
139
207
|
return JSON.parse(JSON.stringify(value))
|
|
140
208
|
}
|
|
@@ -226,26 +294,23 @@ function mergeMissing(target, source) {
|
|
|
226
294
|
}
|
|
227
295
|
|
|
228
296
|
/**
|
|
229
|
-
* Resolve a plugin ref from the repository-root opencode.json to
|
|
230
|
-
* installed
|
|
297
|
+
* Resolve a plugin ref from the repository-root opencode.json to a path INSIDE
|
|
298
|
+
* the installed package.
|
|
231
299
|
*
|
|
232
300
|
* Only exact Pantheon plugin identities are resolved into the installed
|
|
233
301
|
* package. In particular, a third-party absolute path ending in
|
|
234
302
|
* `src/plugin.ts` or `src/plugins/pantheon-hooks.ts` is not evidence that the
|
|
235
|
-
* plugin belongs to Pantheon and must be preserved verbatim.
|
|
236
|
-
* of package-local paths; V2 uses an exact npm version so it is independent of
|
|
237
|
-
* the installer's global prefix or transient npx cache.
|
|
303
|
+
* plugin belongs to Pantheon and must be preserved verbatim.
|
|
238
304
|
*/
|
|
239
305
|
export function resolveInstalledPlugin(plugin) {
|
|
240
306
|
if (typeof plugin !== 'string') return plugin
|
|
241
307
|
const normalized = normalizePluginRef(plugin)
|
|
242
|
-
if (normalized === PANTHEON_V2_PLUGIN || normalized === PANTHEON_V2_LEGACY_FILE) {
|
|
243
|
-
return PANTHEON_V2_PACKAGE_SPEC
|
|
244
|
-
}
|
|
245
308
|
if (PANTHEON_PLUGIN_IDENTITIES.has(normalized)) return join(ROOT, normalized)
|
|
309
|
+
// Legacy V2 file ref (pre-directory-contract): migrate to the installed
|
|
310
|
+
// plugin directory so the beta loader accepts the entry.
|
|
311
|
+
if (normalized === PANTHEON_V2_LEGACY_FILE) return join(ROOT, PANTHEON_V2_PLUGIN)
|
|
246
312
|
const identity = managedPluginIdentity(plugin)
|
|
247
313
|
if (identity !== null && isAbsolute(normalized)) {
|
|
248
|
-
if (identity === PANTHEON_V2_PLUGIN) return PANTHEON_V2_PACKAGE_SPEC
|
|
249
314
|
return INSTALLED_PANTHEON_PLUGIN_PATHS.get(identity) ?? plugin
|
|
250
315
|
}
|
|
251
316
|
return plugin
|
|
@@ -267,14 +332,7 @@ const PANTHEON_V2_PLUGIN = 'src/plugin-v2'
|
|
|
267
332
|
const PANTHEON_V2_LEGACY_FILE = 'src/plugin-v2.ts'
|
|
268
333
|
const PANTHEON_V1_EXPORT = 'pantheon-opencode/plugin'
|
|
269
334
|
const PANTHEON_V2_EXPORT = 'pantheon-opencode/plugin-v2'
|
|
270
|
-
const
|
|
271
|
-
const PANTHEON_V2_PACKAGE_SPEC = `${PACKAGE_MANIFEST.name}@${PACKAGE_MANIFEST.version}`
|
|
272
|
-
// Installation origin probe: npm copies (global prefix, npx cache) always live
|
|
273
|
-
// under a `node_modules` directory; a repo checkout does not. From a checkout —
|
|
274
|
-
// or between a version bump and its publish — the V2 pin above names a version
|
|
275
|
-
// that may not exist on the npm registry yet, and the host cannot resolve it
|
|
276
|
-
// until publish. Advisory only: the install itself is structurally correct.
|
|
277
|
-
const RUNNING_FROM_NPM_INSTALL = import.meta.url.includes('/node_modules/')
|
|
335
|
+
const PANTHEON_PACKAGE_REF = /^pantheon-opencode(?:@[^/]+)?(?:\/(plugin|plugin-v2))?$/
|
|
278
336
|
const PANTHEON_PLUGIN_IDENTITIES = new Set([
|
|
279
337
|
PANTHEON_V1_PLUGIN,
|
|
280
338
|
PANTHEON_V1_HOOKS,
|
|
@@ -303,14 +361,22 @@ function managedPluginIdentity(ref) {
|
|
|
303
361
|
const normalized = normalizePluginRef(ref)
|
|
304
362
|
if (typeof normalized !== 'string') return null
|
|
305
363
|
|
|
364
|
+
// Older installs registered the package root itself (often pinned to a
|
|
365
|
+
// beta version) in OpenCode's plural `plugins` list. Treat all exact npm
|
|
366
|
+
// specs for this package as managed so an update removes the old cached
|
|
367
|
+
// copy before registering the current package path. Do not match similarly
|
|
368
|
+
// named third-party packages such as `pantheon-opencode-fork`.
|
|
369
|
+
const packageRef = normalized.match(PANTHEON_PACKAGE_REF)
|
|
370
|
+
if (packageRef !== null) {
|
|
371
|
+
if (packageRef[1] === 'plugin') return PANTHEON_V1_PLUGIN
|
|
372
|
+
return PANTHEON_V2_PLUGIN
|
|
373
|
+
}
|
|
374
|
+
|
|
306
375
|
if (PANTHEON_PLUGIN_IDENTITIES.has(normalized)) return normalized
|
|
307
376
|
if (normalized === PANTHEON_V2_LEGACY_FILE) return PANTHEON_V2_PLUGIN
|
|
308
377
|
if (normalized === PANTHEON_V1_EXPORT || normalized.startsWith(`${PANTHEON_V1_EXPORT}@`)) {
|
|
309
378
|
return PANTHEON_V1_PLUGIN
|
|
310
379
|
}
|
|
311
|
-
if (normalized === PACKAGE_MANIFEST.name || normalized.startsWith(`${PACKAGE_MANIFEST.name}@`)) {
|
|
312
|
-
return PANTHEON_V2_PLUGIN
|
|
313
|
-
}
|
|
314
380
|
if (normalized === PANTHEON_V2_EXPORT || normalized.startsWith(`${PANTHEON_V2_EXPORT}@`)) {
|
|
315
381
|
return PANTHEON_V2_PLUGIN
|
|
316
382
|
}
|
|
@@ -1154,6 +1220,12 @@ export async function installOpenCode(
|
|
|
1154
1220
|
if (fm.permission) {
|
|
1155
1221
|
agent.permission = JSON.parse(JSON.stringify(fm.permission))
|
|
1156
1222
|
}
|
|
1223
|
+
if (fm.mcp_tools !== undefined) {
|
|
1224
|
+
agent.permission = {
|
|
1225
|
+
...(agent.permission ?? {}),
|
|
1226
|
+
...deriveNativeMcpPermissions(fm.mcp_tools),
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1157
1229
|
|
|
1158
1230
|
config[name] = agent
|
|
1159
1231
|
}
|
|
@@ -1266,12 +1338,16 @@ export async function installOpenCode(
|
|
|
1266
1338
|
// --------------------------------------------------------------------
|
|
1267
1339
|
// B.5.1 Hermetic plugin resolution (packaging fix)
|
|
1268
1340
|
// --------------------------------------------------------------------
|
|
1269
|
-
// A developer running from a checkout
|
|
1270
|
-
//
|
|
1271
|
-
//
|
|
1272
|
-
//
|
|
1273
|
-
//
|
|
1274
|
-
//
|
|
1341
|
+
// A developer running from a checkout has a repository-root opencode.json
|
|
1342
|
+
// that may reference plugins via developer-machine absolute paths (e.g.
|
|
1343
|
+
// <dev>/pantheon/.../src/plugin.ts or
|
|
1344
|
+
// <dev>/pantheon/.../src/plugins/pantheon-hooks.ts). Copying those
|
|
1345
|
+
// verbatim into the user's config would break every global install on any
|
|
1346
|
+
// other machine. resolveInstalledPlugin() rewrites each entry to the plugin
|
|
1347
|
+
// file inside the INSTALLED package (derived from ROOT — never hardcoded),
|
|
1348
|
+
// so both dev and global installs resolve to a path that actually exists.
|
|
1349
|
+
// The source opencode.json keeps its dev path for local development; the
|
|
1350
|
+
// transform happens at install/sync time only.
|
|
1275
1351
|
|
|
1276
1352
|
if (version === 'v1') {
|
|
1277
1353
|
// V1 loads the singular `plugin` list. Remove every Pantheon V2 entry from
|
|
@@ -1332,13 +1408,18 @@ export async function installOpenCode(
|
|
|
1332
1408
|
}
|
|
1333
1409
|
}
|
|
1334
1410
|
}
|
|
1335
|
-
//
|
|
1336
|
-
//
|
|
1337
|
-
//
|
|
1338
|
-
//
|
|
1339
|
-
//
|
|
1340
|
-
// directory
|
|
1341
|
-
//
|
|
1411
|
+
// The beta resolves every `plugins` entry through npm install, so an
|
|
1412
|
+
// `a/b` shorthand (e.g. the published `pantheon-opencode/plugin-v2`
|
|
1413
|
+
// export) is treated as a GitHub repo and dies with NpmInstallFailedError
|
|
1414
|
+
// (beta log evidence). The beta loader additionally requires each entry
|
|
1415
|
+
// to be a DIRECTORY with a real index.js/index.ts — a file path warns
|
|
1416
|
+
// "must be a directory" and a symlink is ignored (sandbox probe
|
|
1417
|
+
// evidence). Register the V2 plugin DIRECTORY inside the INSTALLED
|
|
1418
|
+
// package — exactly like V1's ensurePantheonPlugin, but pointing at
|
|
1419
|
+
// src/plugin-v2 whose index.ts shim re-exports the real entry.
|
|
1420
|
+
// Legacy refs (npm shorthand, the old src/plugin-v2.ts file path) are
|
|
1421
|
+
// recognized via managedPluginIdentity and already stripped above, so
|
|
1422
|
+
// they migrate here.
|
|
1342
1423
|
const resolvedV2 = resolveInstalledPlugin(PANTHEON_V2_PLUGIN)
|
|
1343
1424
|
config.plugins = config.plugins.filter(
|
|
1344
1425
|
(plugin) => pluginReferenceIdentity(plugin) !== pluginReferenceIdentity(resolvedV2),
|
|
@@ -1522,16 +1603,6 @@ export async function installOpenCode(
|
|
|
1522
1603
|
configToWrite.plugins,
|
|
1523
1604
|
config.context_sandbox,
|
|
1524
1605
|
)
|
|
1525
|
-
|
|
1526
|
-
// Bootstrap chicken-and-egg: the pin names the CURRENT package version,
|
|
1527
|
-
// which exists on the registry only after publish. From an npm install
|
|
1528
|
-
// (global prefix / npx cache) that is the published version by
|
|
1529
|
-
// construction; from a repo checkout the pin may point at an unpublished
|
|
1530
|
-
// version the host cannot resolve. Warn instead of failing — the config is
|
|
1531
|
-
// structurally correct and the pin realigns once the version is out.
|
|
1532
|
-
if (!RUNNING_FROM_NPM_INSTALL) {
|
|
1533
|
-
warning(S.v2UnpublishedPin(PANTHEON_V2_PACKAGE_SPEC))
|
|
1534
|
-
}
|
|
1535
1606
|
}
|
|
1536
1607
|
|
|
1537
1608
|
const configContent = `${JSON.stringify(configToWrite, null, 2)}\n`
|
|
@@ -47,10 +47,6 @@ const TABLE = {
|
|
|
47
47
|
prereqMissing:
|
|
48
48
|
'runtime prerequisites missing — install the tools above, or re-run with --no-mcp to install without Python MCP servers',
|
|
49
49
|
v2Migrating: 'Migrating config to V2 native format...',
|
|
50
|
-
v2UnpublishedPin: (spec) =>
|
|
51
|
-
`V2 plugin is pinned to ${spec}, but this installer is running from a source checkout — ` +
|
|
52
|
-
'that version may not be published to npm yet, so OpenCode cannot resolve the pin until it is. ' +
|
|
53
|
-
'Run the installer from the published package (npx pantheon-opencode init) to avoid this.',
|
|
54
50
|
// summary (printSummary)
|
|
55
51
|
summaryInstalled: (target) => `OpenCode installed in ${target}`,
|
|
56
52
|
summaryWithErrors: (target) => `Installation finished WITH ERRORS in ${target}`,
|
|
@@ -99,10 +95,6 @@ const TABLE = {
|
|
|
99
95
|
prereqMissing:
|
|
100
96
|
'pré-requisitos do runtime ausentes — instale as ferramentas acima, ou rode de novo com --no-mcp para instalar sem os MCPs Python',
|
|
101
97
|
v2Migrating: 'Migrando config para o formato nativo V2...',
|
|
102
|
-
v2UnpublishedPin: (spec) =>
|
|
103
|
-
`Plugin V2 fixado em ${spec}, mas este installer está rodando de um checkout do código-fonte — ` +
|
|
104
|
-
'essa versão pode ainda não estar publicada no npm, então o OpenCode não resolve o pin até a publicação. ' +
|
|
105
|
-
'Rode o installer pelo pacote publicado (npx pantheon-opencode init) para evitar isso.',
|
|
106
98
|
// summary (printSummary)
|
|
107
99
|
summaryInstalled: (target) => `OpenCode instalado em ${target}`,
|
|
108
100
|
summaryWithErrors: (target) => `Instalação concluída COM ERROS em ${target}`,
|
|
@@ -8,10 +8,9 @@
|
|
|
8
8
|
* temporary synthetic opencode database. A missing fixture runtime is also
|
|
9
9
|
* AMBIENTAL rather than a false PASS.
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { existsSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs'
|
|
12
12
|
import { tmpdir } from 'node:os'
|
|
13
13
|
import { join } from 'node:path'
|
|
14
|
-
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
15
14
|
|
|
16
15
|
const requestedVersion = valueFor('--version')
|
|
17
16
|
const jsonOutput = process.argv.includes('--json')
|
|
@@ -152,27 +151,19 @@ async function main() {
|
|
|
152
151
|
return
|
|
153
152
|
}
|
|
154
153
|
|
|
155
|
-
const root = mkdtempSync(join(tmpdir(), 'pantheon-cost-probe-'))
|
|
156
154
|
let DatabaseSync
|
|
157
155
|
let createCostCommand
|
|
158
156
|
try {
|
|
159
157
|
;({ DatabaseSync } = await import('node:sqlite'))
|
|
160
|
-
|
|
161
|
-
// node_modules. Exercise the shipped command from an isolated temp copy
|
|
162
|
-
// while resolving its runtime dependencies through the installed package.
|
|
163
|
-
const runtimeSource = join(root, 'cost-command.ts')
|
|
164
|
-
const packageNodeModules = fileURLToPath(new URL('../node_modules/', import.meta.url))
|
|
165
|
-
copyFileSync(fileURLToPath(source), runtimeSource)
|
|
166
|
-
symlinkSync(packageNodeModules, join(root, 'node_modules'), 'dir')
|
|
167
|
-
;({ createCostCommand } = await import(pathToFileURL(runtimeSource).href))
|
|
158
|
+
;({ createCostCommand } = await import(source.href))
|
|
168
159
|
} catch (error) {
|
|
169
|
-
rmSync(root, { recursive: true, force: true })
|
|
170
160
|
ambiental(
|
|
171
161
|
`pantheon_cost fixture runtime unavailable: ${error instanceof Error ? error.message : String(error)}`,
|
|
172
162
|
)
|
|
173
163
|
return
|
|
174
164
|
}
|
|
175
165
|
|
|
166
|
+
const root = mkdtempSync(join(tmpdir(), 'pantheon-cost-probe-'))
|
|
176
167
|
const dataHome = join(root, 'data')
|
|
177
168
|
const opencodeDir = join(dataHome, 'opencode')
|
|
178
169
|
// Every real installation writes `opencode.db`; the v1/v2 distinction is the
|
|
@@ -263,7 +254,6 @@ async function main() {
|
|
|
263
254
|
|
|
264
255
|
const restore = setEnvironment({
|
|
265
256
|
XDG_DATA_HOME: dataHome,
|
|
266
|
-
OPENCODE_DB: undefined,
|
|
267
257
|
PANTHEON_OPENCODE_VERSION: requestedVersion,
|
|
268
258
|
PANTHEON_COST_DB: undefined,
|
|
269
259
|
})
|
|
@@ -294,7 +284,6 @@ async function main() {
|
|
|
294
284
|
|
|
295
285
|
const envRestore = setEnvironment({
|
|
296
286
|
XDG_DATA_HOME: dataHome,
|
|
297
|
-
OPENCODE_DB: undefined,
|
|
298
287
|
PANTHEON_OPENCODE_VERSION: requestedVersion,
|
|
299
288
|
PANTHEON_COST_DB: envDb,
|
|
300
289
|
})
|
|
@@ -319,7 +308,6 @@ async function main() {
|
|
|
319
308
|
|
|
320
309
|
const explicitRestore = setEnvironment({
|
|
321
310
|
XDG_DATA_HOME: dataHome,
|
|
322
|
-
OPENCODE_DB: undefined,
|
|
323
311
|
PANTHEON_OPENCODE_VERSION: requestedVersion,
|
|
324
312
|
PANTHEON_COST_DB: envDb,
|
|
325
313
|
})
|
|
@@ -341,7 +329,6 @@ async function main() {
|
|
|
341
329
|
|
|
342
330
|
const missingRestore = setEnvironment({
|
|
343
331
|
XDG_DATA_HOME: join(root, 'missing-data'),
|
|
344
|
-
OPENCODE_DB: undefined,
|
|
345
332
|
PANTHEON_OPENCODE_VERSION: requestedVersion,
|
|
346
333
|
PANTHEON_COST_DB: undefined,
|
|
347
334
|
})
|