@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 does not install upstream OpenCode updates. Running `zenocode upgrade` prints the managed update command above.
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.1`). 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.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@textcortex/zenocode",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "description": "Secure, TextCortex-managed coding agent for customers that runs in your terminal, edits files, runs scripts, and more.",
5
5
  "keywords": [
6
6
  "ai",
@@ -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.1');
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 = 1;
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.1'
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.1', '--version'],
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.1', '--version'],
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: 'Successfully updated to OpenCode',
209
- to: 'Successfully updated to Zenocode',
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
- \`Successfully updated to OpenCode v\${result.data.version}. Please restart the application.\`
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)",