opencode-navigator 0.22.0 → 0.23.0

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/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "opencode-navigator",
4
- "version": "0.22.0",
4
+ "version": "0.23.0",
5
5
  "description": "Search, navigate, and manage OpenCode sessions from the TUI",
6
6
  "type": "module",
7
- "packageManager": "bun@1.4.2",
7
+ "packageManager": "bun@1.4.4",
8
8
  "license": "MIT",
9
9
  "author": "St1ggy",
10
10
  "repository": {
@@ -33,9 +33,11 @@
33
33
  "scripts/install-font.mjs",
34
34
  "scripts/update-local-snapshot.mjs",
35
35
  "scripts/snapshot-runtime.mjs",
36
+ "scripts/snapshot-verifier.mjs",
36
37
  "scripts/save-project-profile.mjs",
37
38
  "README.md",
38
39
  "PROVIDERS.md",
40
+ "HOST_API.md",
39
41
  "CHANGELOG.md",
40
42
  "LICENSE"
41
43
  ],
@@ -58,7 +60,7 @@
58
60
  "access": "public"
59
61
  },
60
62
  "engines": {
61
- "opencode": ">=1.18.30"
63
+ "opencode": "1.19.5 || 2.2.0"
62
64
  },
63
65
  "scripts": {
64
66
  "build": "bun run build.ts",
@@ -81,8 +83,8 @@
81
83
  "check": "bun run test && bun run typecheck && bun run lint && bun run bundle:size && bun run format:check"
82
84
  },
83
85
  "peerDependencies": {
84
- "@opencode-ai/plugin": ">=1.18.30",
85
- "@opencode/plugin": ">=2.0.11",
86
+ "@opencode-ai/plugin": "1.19.5",
87
+ "@opencode/plugin": "2.2.0",
86
88
  "@opentui/core": ">=0.4.5",
87
89
  "@opentui/solid": ">=0.4.5",
88
90
  "solid-js": ">=1.9.12"
@@ -105,12 +107,12 @@
105
107
  }
106
108
  },
107
109
  "devDependencies": {
108
- "@opencode-ai/plugin": "1.19.3",
109
- "@opencode-ai/sdk": "1.19.3",
110
- "@opencode/plugin": "2.1.0",
111
- "@opentui/core": "0.6.6",
112
- "@opentui/keymap": "0.6.6",
113
- "@opentui/solid": "0.6.6",
110
+ "@opencode-ai/plugin": "1.19.5",
111
+ "@opencode-ai/sdk": "1.19.5",
112
+ "@opencode/plugin": "2.2.0",
113
+ "@opentui/core": "0.7.0",
114
+ "@opentui/keymap": "0.7.0",
115
+ "@opentui/solid": "0.7.0",
114
116
  "@st1ggy/linter-config": "8.0.0",
115
117
  "@types/bun": "1.4.2",
116
118
  "eslint-plugin-solid": "0.18.0",
@@ -0,0 +1,36 @@
1
+ import { readFile } from 'node:fs/promises'
2
+ import { join } from 'node:path'
3
+
4
+ function supports(version, minimum) {
5
+ const value = version.replace(/^v/, '').split('.').map(Number)
6
+ const required = minimum.split('.').map(Number)
7
+
8
+ for (let index = 0; index < 3; index++) {
9
+ if (value[index] !== required[index]) return value[index] > required[index]
10
+ }
11
+
12
+ return true
13
+ }
14
+
15
+ export async function snapshotVerifier(directory, probe) {
16
+ const source = await readFile(join(directory, 'dist', 'runtime-requirements.json'), 'utf8').catch((error) => {
17
+ if (error.code !== 'ENOENT') throw error
18
+ })
19
+
20
+ if (!source) return 'node'
21
+
22
+ const requirements = JSON.parse(source)
23
+
24
+ if (requirements.version !== 1 || ['bun', 'node'].some((name) => !/^\d+\.\d+\.\d+$/.test(requirements[name])))
25
+ throw new Error('Navigator package has invalid runtime requirements')
26
+
27
+ for (const runtime of ['bun', 'node']) {
28
+ const version = await probe(runtime).catch(() => '')
29
+
30
+ if (/^v?\d+\.\d+\.\d+$/.test(version.trim()) && supports(version.trim(), requirements[runtime])) return runtime
31
+ }
32
+
33
+ throw new Error(
34
+ `Snapshot verification requires Bun >=${requirements.bun} or Node >=${requirements.node}; the installed snapshot was preserved`,
35
+ )
36
+ }
@@ -6,17 +6,22 @@ import { basename, dirname, join, resolve } from 'node:path'
6
6
  import { pathToFileURL } from 'node:url'
7
7
 
8
8
  import { snapshotRuntime } from './snapshot-runtime.mjs'
9
+ import { snapshotVerifier } from './snapshot-verifier.mjs'
9
10
 
10
- function command(program, args) {
11
+ function command(program, args, capture = false) {
11
12
  return new Promise((resolveCommand, reject) => {
12
- const child = spawn(program, args, { stdio: ['ignore', 'ignore', 'pipe'] })
13
+ const child = spawn(program, args, { stdio: ['ignore', capture ? 'pipe' : 'ignore', 'pipe'] })
13
14
  let error = ''
15
+ let output = ''
16
+
17
+ child.stdout?.setEncoding('utf8')
18
+ child.stdout?.on('data', (chunk) => (output += chunk))
14
19
 
15
20
  child.stderr.setEncoding('utf8')
16
21
  child.stderr.on('data', (chunk) => (error += chunk))
17
22
  child.on('error', reject)
18
23
  child.on('close', (code) => {
19
- if (code === 0) resolveCommand()
24
+ if (code === 0) resolveCommand(output.trim())
20
25
  else reject(new Error(error.trim() || `${program} exited with code ${code}`))
21
26
  })
22
27
  })
@@ -61,6 +66,17 @@ async function validate(directory, version, { checkRuntime = true } = {}) {
61
66
  throw new Error(`Snapshot ${version} has mismatched runtime dependencies`)
62
67
  }
63
68
 
69
+ async function verifySnapshot(directory, runtime) {
70
+ const verifier = runtime ?? (await snapshotVerifier(directory, (program) => command(program, ['--version'], true)))
71
+ const importURL = JSON.stringify(pathToFileURL(join(directory, 'dist', 'tui.js')).href)
72
+
73
+ await command(verifier, [
74
+ '--input-type=module',
75
+ '--eval',
76
+ `const plugin=(await import(${importURL})).default;if(plugin?.id!=='opencode-navigator')throw Error('Invalid Navigator plugin bundle')`,
77
+ ])
78
+ }
79
+
64
80
  export async function updateLocalSnapshot(wrapper, target, operations = {}) {
65
81
  if (!/^\d+\.\d+\.\d+$/.test(target)) throw new Error('Invalid Navigator update version')
66
82
 
@@ -81,8 +97,11 @@ export async function updateLocalSnapshot(wrapper, target, operations = {}) {
81
97
  const replacement = join(root, `.tui-${randomUUID()}.tmp`)
82
98
 
83
99
  try {
84
- if (existsSync(next)) await validate(next, target)
85
- else {
100
+ if (existsSync(next)) {
101
+ await validate(next, target)
102
+
103
+ await (operations.verify ? operations.verify(next) : verifySnapshot(next))
104
+ } else {
86
105
  await mkdir(staging)
87
106
 
88
107
  if (operations.stage) await operations.stage(staging, target)
@@ -103,6 +122,10 @@ export async function updateLocalSnapshot(wrapper, target, operations = {}) {
103
122
  if (metadata.name !== 'opencode-navigator' || metadata.version !== target)
104
123
  throw new Error('Downloaded Navigator package does not match the requested version')
105
124
 
125
+ const verifier = operations.verify
126
+ ? undefined
127
+ : await snapshotVerifier(staging, (runtime) => command(runtime, ['--version'], true))
128
+
106
129
  await writeFile(
107
130
  join(staging, 'package.json'),
108
131
  JSON.stringify({
@@ -129,17 +152,7 @@ export async function updateLocalSnapshot(wrapper, target, operations = {}) {
129
152
 
130
153
  await validate(staging, target)
131
154
 
132
- if (operations.verify) await operations.verify(staging)
133
- else {
134
- const bundleURL = pathToFileURL(join(staging, 'dist', 'tui.js')).href
135
- const importURL = JSON.stringify(bundleURL)
136
-
137
- await command('node', [
138
- '--input-type=module',
139
- '--eval',
140
- `const plugin=(await import(${importURL})).default;if(plugin?.id!=='opencode-navigator')throw Error('Invalid Navigator plugin bundle')`,
141
- ])
142
- }
155
+ await (operations.verify ? operations.verify(staging) : verifySnapshot(staging, verifier))
143
156
 
144
157
  await rename(staging, next)
145
158
  }