@textcortex/zenocode 0.1.15 → 0.1.16
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/README.md
CHANGED
|
@@ -46,9 +46,9 @@ Zenocode releases its launcher and managed runtime together. Update both with:
|
|
|
46
46
|
npm install -g @textcortex/zenocode@latest
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
The terminal runtime
|
|
49
|
+
The terminal runtime never checks for, offers, or installs upstream OpenCode updates: the startup update check, the in-app "Update Available" prompt, and the runtime's `/global/upgrade` API are all removed at build time. Running `zenocode upgrade` prints the managed update command above.
|
|
50
50
|
|
|
51
|
-
Runtime package versions include a Zenocode revision suffix (for example, `1.18.10-zenocode.
|
|
51
|
+
Runtime package versions include a Zenocode revision suffix (for example, `1.18.10-zenocode.2`). Increment that revision when the Zenocode source overlay changes without an upstream version bump, so an immutable npm runtime release can always be replaced deliberately.
|
|
52
52
|
|
|
53
53
|
## Models
|
|
54
54
|
|
package/package.json
CHANGED
|
@@ -20,7 +20,7 @@ test('branded OpenCode build defaults to the latest stable upstream release', ()
|
|
|
20
20
|
assert.equal(defaultOpenCodeForkUrl, 'https://github.com/sst/opencode');
|
|
21
21
|
assert.equal(defaultOpenCodeRef, 'v1.18.10');
|
|
22
22
|
assert.equal(defaultOpenCodeVersion, '1.18.10');
|
|
23
|
-
assert.equal(zenocodeRuntimeVersion, '1.18.10-zenocode.
|
|
23
|
+
assert.equal(zenocodeRuntimeVersion, '1.18.10-zenocode.2');
|
|
24
24
|
assert.deepEqual(defaultOpenCodeBuildArgs, []);
|
|
25
25
|
});
|
|
26
26
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const openCodeVersion = '1.18.10';
|
|
2
2
|
export const openCodeRef = `v${openCodeVersion}`;
|
|
3
3
|
export const zenocodeRuntimePackageName = '@textcortex/zenocode-ai';
|
|
4
|
-
export const zenocodeRuntimeRevision =
|
|
4
|
+
export const zenocodeRuntimeRevision = 2;
|
|
5
5
|
export const zenocodeRuntimeVersion = `${openCodeVersion}-zenocode.${zenocodeRuntimeRevision}`;
|
|
6
6
|
export const zenocodeRuntimePackageSpec = `${zenocodeRuntimePackageName}@${zenocodeRuntimeVersion}`;
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
test('Zenocode launches the pinned TextCortex-owned runtime by default', () => {
|
|
31
31
|
assert.equal(
|
|
32
32
|
resolveLaunchPackage(),
|
|
33
|
-
'@textcortex/zenocode-ai@1.18.10-zenocode.
|
|
33
|
+
'@textcortex/zenocode-ai@1.18.10-zenocode.2'
|
|
34
34
|
);
|
|
35
35
|
});
|
|
36
36
|
|
|
@@ -316,13 +316,13 @@ test('buildPackageLauncherInvocation launches the owned runtime package unchange
|
|
|
316
316
|
const invocation = buildPackageLauncherInvocation({
|
|
317
317
|
runner: {
|
|
318
318
|
command: 'pnpm',
|
|
319
|
-
args: ['dlx', '@textcortex/zenocode-ai@1.18.10-zenocode.
|
|
319
|
+
args: ['dlx', '@textcortex/zenocode-ai@1.18.10-zenocode.2', '--version'],
|
|
320
320
|
},
|
|
321
321
|
});
|
|
322
322
|
|
|
323
323
|
assert.deepEqual(invocation, {
|
|
324
324
|
command: 'pnpm',
|
|
325
|
-
args: ['dlx', '@textcortex/zenocode-ai@1.18.10-zenocode.
|
|
325
|
+
args: ['dlx', '@textcortex/zenocode-ai@1.18.10-zenocode.2', '--version'],
|
|
326
326
|
});
|
|
327
327
|
});
|
|
328
328
|
|
|
@@ -112,6 +112,109 @@ const zenocodeUpgradeSource = `/**
|
|
|
112
112
|
export async function upgrade() {}
|
|
113
113
|
`;
|
|
114
114
|
|
|
115
|
+
/** Pinned upstream TUI handler that offers an in-app runtime upgrade. */
|
|
116
|
+
export const openCodeUpdatePromptSource = ` event.on("installation.update-available", async (evt) => {
|
|
117
|
+
console.log("installation.update-available", evt)
|
|
118
|
+
const version = evt.properties.version
|
|
119
|
+
|
|
120
|
+
const skipped = kv.get("skipped_version")
|
|
121
|
+
if (skipped && !isVersionGreater(version, skipped)) return
|
|
122
|
+
|
|
123
|
+
const choice = await DialogConfirm.show(
|
|
124
|
+
dialog,
|
|
125
|
+
\`Update Available\`,
|
|
126
|
+
\`A new release v\${version} is available. Would you like to update now?\`,
|
|
127
|
+
"skip",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if (choice === false) {
|
|
131
|
+
kv.set("skipped_version", version)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (choice !== true) return
|
|
136
|
+
|
|
137
|
+
toast.show({
|
|
138
|
+
variant: "info",
|
|
139
|
+
message: \`Updating to v\${version}...\`,
|
|
140
|
+
duration: 30000,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const result = await sdk.client.global.upgrade({ target: version })
|
|
144
|
+
|
|
145
|
+
if (result.error || !result.data?.success) {
|
|
146
|
+
toast.show({
|
|
147
|
+
variant: "error",
|
|
148
|
+
title: "Update Failed",
|
|
149
|
+
message: "Update failed",
|
|
150
|
+
duration: 10000,
|
|
151
|
+
})
|
|
152
|
+
return
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await DialogAlert.show(
|
|
156
|
+
dialog,
|
|
157
|
+
"Update Complete",
|
|
158
|
+
\`Successfully updated to OpenCode v\${result.data.version}. Please restart the application.\`,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
void exit()
|
|
162
|
+
})
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
// Zenocode pins its runtime package, so an in-app upgrade can never take effect
|
|
166
|
+
// and the "restart to finish updating" prompt would loop on every launch.
|
|
167
|
+
const zenocodeUpdatePromptSource = ` // Zenocode owns runtime updates; see apps/zenocode/scripts/source-branding.mjs.
|
|
168
|
+
event.on("installation.update-available", () => {})
|
|
169
|
+
`;
|
|
170
|
+
|
|
171
|
+
/** Pinned upstream server handler behind POST /global/upgrade. */
|
|
172
|
+
export const openCodeServerUpgradeSource = ` const upgrade = Effect.fn("GlobalHttpApi.upgrade")(function* (ctx: { payload: typeof GlobalUpgradeInput.Type }) {
|
|
173
|
+
const method = yield* installation.method()
|
|
174
|
+
if (method === "unknown") {
|
|
175
|
+
return {
|
|
176
|
+
status: 400,
|
|
177
|
+
body: { success: false as const, error: "Unknown installation method" },
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const target = ctx.payload.target || (yield* installation.latest(method))
|
|
181
|
+
const result = yield* installation.upgrade(method, target).pipe(
|
|
182
|
+
Effect.as({ status: 200, body: { success: true as const, version: target } }),
|
|
183
|
+
Effect.catch((err) =>
|
|
184
|
+
Effect.succeed({
|
|
185
|
+
status: 500,
|
|
186
|
+
body: {
|
|
187
|
+
success: false as const,
|
|
188
|
+
error: err instanceof Error ? err.message : String(err),
|
|
189
|
+
},
|
|
190
|
+
}),
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
if (!result.body.success) return result
|
|
194
|
+
GlobalBus.emit("event", {
|
|
195
|
+
directory: "global",
|
|
196
|
+
payload: {
|
|
197
|
+
type: Installation.Event.Updated.type,
|
|
198
|
+
properties: { version: target },
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
return result
|
|
202
|
+
})
|
|
203
|
+
`;
|
|
204
|
+
|
|
205
|
+
const zenocodeServerUpgradeSource = ` const upgrade = Effect.fn("GlobalHttpApi.upgrade")(function* (_ctx: {
|
|
206
|
+
payload: typeof GlobalUpgradeInput.Type
|
|
207
|
+
}) {
|
|
208
|
+
return {
|
|
209
|
+
status: 400,
|
|
210
|
+
body: {
|
|
211
|
+
success: false as const,
|
|
212
|
+
error: "Zenocode manages runtime updates. Reinstall Zenocode to update.",
|
|
213
|
+
},
|
|
214
|
+
}
|
|
215
|
+
})
|
|
216
|
+
`;
|
|
217
|
+
|
|
115
218
|
function decodeLogoLine(line) {
|
|
116
219
|
return line.replace(/\\u([0-9a-fA-F]{4})/g, (_, codePoint) =>
|
|
117
220
|
String.fromCharCode(Number.parseInt(codePoint, 16))
|
|
@@ -205,8 +308,18 @@ const sourceBrandingRules = new Map([
|
|
|
205
308
|
count: 1,
|
|
206
309
|
},
|
|
207
310
|
{
|
|
208
|
-
from:
|
|
209
|
-
to:
|
|
311
|
+
from: openCodeUpdatePromptSource,
|
|
312
|
+
to: zenocodeUpdatePromptSource,
|
|
313
|
+
count: 1,
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
],
|
|
317
|
+
[
|
|
318
|
+
'packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts',
|
|
319
|
+
[
|
|
320
|
+
{
|
|
321
|
+
from: openCodeServerUpgradeSource,
|
|
322
|
+
to: zenocodeServerUpgradeSource,
|
|
210
323
|
count: 1,
|
|
211
324
|
},
|
|
212
325
|
],
|
|
@@ -3,6 +3,8 @@ import test from 'node:test';
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
brandOpenCodeSourceFile,
|
|
6
|
+
openCodeServerUpgradeSource,
|
|
7
|
+
openCodeUpdatePromptSource,
|
|
6
8
|
sourceBrandingFiles,
|
|
7
9
|
} from './source-branding.mjs';
|
|
8
10
|
|
|
@@ -26,17 +28,45 @@ renderer.setTerminalTitle("OpenCode")
|
|
|
26
28
|
renderer.setTerminalTitle("OpenCode")
|
|
27
29
|
renderer.setTerminalTitle(\`OC | \${title}\`)
|
|
28
30
|
renderer.setTerminalTitle(\`OC | \${route.data.id}\`)
|
|
29
|
-
|
|
30
|
-
`;
|
|
31
|
+
${openCodeUpdatePromptSource}`;
|
|
31
32
|
|
|
32
33
|
const branded = brandOpenCodeSourceFile('packages/tui/src/app.tsx', source);
|
|
33
34
|
|
|
34
35
|
assert.match(branded, /setTerminalTitle\("Zenocode"\)/);
|
|
35
36
|
assert.match(branded, /setTerminalTitle\(`ZC \| \$\{title\}`\)/);
|
|
36
|
-
assert.match(branded, /Successfully updated to Zenocode/);
|
|
37
37
|
assert.doesNotMatch(branded, /OpenCode/);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
test('source branding removes the in-app update prompt', () => {
|
|
41
|
+
const branded = brandOpenCodeSourceFile(
|
|
42
|
+
'packages/tui/src/app.tsx',
|
|
43
|
+
`renderer.setTerminalTitle("OpenCode")
|
|
44
|
+
renderer.setTerminalTitle("OpenCode")
|
|
45
|
+
renderer.setTerminalTitle(\`OC | \${title}\`)
|
|
46
|
+
renderer.setTerminalTitle(\`OC | \${route.data.id}\`)
|
|
47
|
+
${openCodeUpdatePromptSource}`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
assert.match(
|
|
51
|
+
branded,
|
|
52
|
+
/event\.on\("installation\.update-available", \(\) => \{\}\)/
|
|
53
|
+
);
|
|
54
|
+
assert.doesNotMatch(branded, /Update Available/);
|
|
55
|
+
assert.doesNotMatch(branded, /global\.upgrade/);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('source branding refuses runtime upgrades through the server API', () => {
|
|
59
|
+
const branded = brandOpenCodeSourceFile(
|
|
60
|
+
'packages/opencode/src/server/routes/instance/httpapi/handlers/global.ts',
|
|
61
|
+
openCodeServerUpgradeSource
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
assert.match(branded, /Zenocode manages runtime updates/);
|
|
65
|
+
assert.match(branded, /status: 400/);
|
|
66
|
+
assert.doesNotMatch(branded, /installation\.upgrade\(/);
|
|
67
|
+
assert.doesNotMatch(branded, /installation\.latest\(/);
|
|
68
|
+
});
|
|
69
|
+
|
|
40
70
|
test('source branding replaces the terminal network identity', () => {
|
|
41
71
|
const source = `
|
|
42
72
|
describe: "custom domain name for mDNS service (default: opencode.local)",
|