@zergai/ztc 0.2.2-dev.35681806994 → 0.2.3-dev.35760740177
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 +17 -0
- package/dist/zlm-embed.mjs +1 -1
- package/npm-cli/npm-process.cjs +21 -0
- package/npm-cli/run.cjs +36 -0
- package/npm-cli/update.cjs +161 -0
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -1038,3 +1038,20 @@ the ZTC 0.1.x releases.
|
|
|
1038
1038
|
---
|
|
1039
1039
|
|
|
1040
1040
|
Part of the [Zerg](https://github.com/idanbeck/zerg) autonomous AI framework.
|
|
1041
|
+
|
|
1042
|
+
## Updating the npm CLI
|
|
1043
|
+
|
|
1044
|
+
Run `ztc update` or `ztc --update` to install a newer npm
|
|
1045
|
+
release. `ztc update --check` prints JSON without installing. The
|
|
1046
|
+
installed channel is preserved (`dev`, `next`, or `latest`); select a channel
|
|
1047
|
+
explicitly with `update --channel dev|next|latest`. Updates never downgrade.
|
|
1048
|
+
|
|
1049
|
+
Startup checks only notify on stderr when it is a terminal, never prompt or
|
|
1050
|
+
install, and are cached for one hour. CI, redirected stderr, and help/version
|
|
1051
|
+
requests skip checks. Set `ZERGAI_NO_UPDATE_CHECK=1` to disable startup checks.
|
|
1052
|
+
Self-installation requires the matching macOS/Linux global npm prefix; local,
|
|
1053
|
+
linked, npx, or other package-manager installs must use their original manager.
|
|
1054
|
+
Existing releases need one ordinary npm upgrade before these commands exist.
|
|
1055
|
+
|
|
1056
|
+
See [shared updater policy](https://github.com/Epoch-ML/zerg/blob/development/docs/npm-cli-updates.md)
|
|
1057
|
+
for installation safety, channel switching, and packaging details.
|
package/dist/zlm-embed.mjs
CHANGED
|
@@ -12130,7 +12130,7 @@ var invokeSkillTool = makeInvokeSkillTool(resolveAmbientSkills);
|
|
|
12130
12130
|
// src/zlm.ts
|
|
12131
12131
|
var ZLM_EMBED_API_VERSION = "zerglang.ztc-embed/1";
|
|
12132
12132
|
var ZLM_STDIO_PROTOCOL = "zerglang.ztc-host/1";
|
|
12133
|
-
var ZLM_ZTC_VERSION = true ? "0.2.
|
|
12133
|
+
var ZLM_ZTC_VERSION = true ? "0.2.3-dev.35760740177" : "development";
|
|
12134
12134
|
var ZLM_MAX_HOST_TOOLS = 64;
|
|
12135
12135
|
var ZLM_MAX_SYSTEM_PROMPT_BYTES = 128 * 1024;
|
|
12136
12136
|
var ZLM_HOST_APPROVAL_CAPABILITIES = Object.freeze([
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const { execFile, spawn } = require('node:child_process');
|
|
3
|
+
const { promisify } = require('node:util');
|
|
4
|
+
|
|
5
|
+
// Kept separate so OS process contracts can be exercised independently from the
|
|
6
|
+
// version/cache policy. This is copied alongside the updater in each artifact.
|
|
7
|
+
module.exports = async function runNpm(args, options) {
|
|
8
|
+
if (args[0] === 'prefix') return promisify(execFile)('npm', args, { ...options, encoding: 'utf8', timeout: 5000, maxBuffer: 16384 });
|
|
9
|
+
await new Promise((done, fail) => {
|
|
10
|
+
const child = spawn('npm', args, { ...options, stdio: ['ignore', 'inherit', 'inherit'], shell: false });
|
|
11
|
+
// npm shares the foreground group; a terminal has already signalled it.
|
|
12
|
+
const relay = ![process.stdin, process.stdout, process.stderr].some(stream => stream.isTTY);
|
|
13
|
+
const interrupt = () => { if (relay) child.kill('SIGINT'); };
|
|
14
|
+
const terminate = () => { if (relay) child.kill('SIGTERM'); };
|
|
15
|
+
process.on('SIGINT', interrupt); process.on('SIGTERM', terminate);
|
|
16
|
+
const cleanup = () => { process.off('SIGINT', interrupt); process.off('SIGTERM', terminate); };
|
|
17
|
+
child.on('error', error => { cleanup(); fail(error); });
|
|
18
|
+
child.on('exit', (code, signal) => { cleanup(); if (code === 0) done(); else fail(new Error(`npm exited ${signal || code}`)); });
|
|
19
|
+
});
|
|
20
|
+
return { stdout: '' };
|
|
21
|
+
};
|
package/npm-cli/run.cjs
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
const { spawn } = require('node:child_process');
|
|
4
|
+
const { join } = require('node:path');
|
|
5
|
+
const { handleUpdate } = require('./update.cjs');
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
const root = join(__dirname, '..');
|
|
9
|
+
const args = process.argv.slice(2);
|
|
10
|
+
const result = await handleUpdate({ root, args });
|
|
11
|
+
if (result !== null) { process.exitCode = result; return; }
|
|
12
|
+
if (args.length === 1 && ['--help', '-h', 'help'].includes(args[0])) {
|
|
13
|
+
process.stdout.write('Updates: update | --update [--check] [--channel dev|next|latest]\n');
|
|
14
|
+
}
|
|
15
|
+
const metadata = require('../package.json');
|
|
16
|
+
// ZTC's old TUI updaters must not install silently behind this launch policy.
|
|
17
|
+
const env = { ...process.env };
|
|
18
|
+
if (['@zergai/ztc', 'zerg-ztc'].includes(metadata.name)) {
|
|
19
|
+
env.ZTC_NO_AUTO_UPDATE = '1'; env.ZTC_DISABLE_UPDATE = '1';
|
|
20
|
+
}
|
|
21
|
+
const child = spawn(process.execPath, [join(root, metadata.zergCli.entry), ...args], { stdio: 'inherit', env });
|
|
22
|
+
// Attached terminals signal the foreground group, including the child. Relay
|
|
23
|
+
// only for non-TTY supervisors; keep the group intact for terminal resize.
|
|
24
|
+
const relay = ![process.stdin, process.stdout, process.stderr].some(stream => stream.isTTY);
|
|
25
|
+
const interrupt = () => { if (relay) child.kill('SIGINT'); };
|
|
26
|
+
const terminate = () => { if (relay) child.kill('SIGTERM'); };
|
|
27
|
+
process.on('SIGINT', interrupt); process.on('SIGTERM', terminate);
|
|
28
|
+
const cleanup = () => { process.off('SIGINT', interrupt); process.off('SIGTERM', terminate); };
|
|
29
|
+
child.on('error', error => { cleanup(); console.error(`Unable to launch ${metadata.name}: ${error.message}`); process.exitCode = 1; });
|
|
30
|
+
child.on('exit', (code, signal) => {
|
|
31
|
+
cleanup();
|
|
32
|
+
if (signal) process.kill(process.pid, signal);
|
|
33
|
+
else process.exitCode = code ?? 1;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
main().catch(error => { console.error(error.message); process.exitCode = 1; });
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Copied into each npm CLI at pack time. No repo paths or runtime dependencies.
|
|
4
|
+
const runNpm = require('./npm-process.cjs');
|
|
5
|
+
const { readFile, realpath, lstat, mkdir, mkdtemp, writeFile, rename, rm } = require('node:fs/promises');
|
|
6
|
+
const { homedir } = require('node:os');
|
|
7
|
+
const { join, resolve, isAbsolute } = require('node:path');
|
|
8
|
+
const registry = 'https://registry.npmjs.org';
|
|
9
|
+
const cacheLifetime = 60 * 60 * 1000;
|
|
10
|
+
|
|
11
|
+
function parseVersion(version) {
|
|
12
|
+
const match = typeof version === 'string' && version.length <= 256 && /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/.exec(version);
|
|
13
|
+
if (!match || match.slice(1, 4).some(n => !Number.isSafeInteger(Number(n))) || (match[4] || '').split('.').some(n => /^0\d+$/.test(n))) {
|
|
14
|
+
throw new Error('Invalid semantic version');
|
|
15
|
+
}
|
|
16
|
+
return { core: match.slice(1, 4).map(Number), pre: match[4] ? match[4].split('.') : [] };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function compareVersions(left, right) {
|
|
20
|
+
const a = parseVersion(left), b = parseVersion(right);
|
|
21
|
+
for (let i = 0; i < 3; i++) if (a.core[i] !== b.core[i]) return a.core[i] > b.core[i] ? 1 : -1;
|
|
22
|
+
if (!a.pre.length && b.pre.length) return 1;
|
|
23
|
+
if (a.pre.length && !b.pre.length) return -1;
|
|
24
|
+
for (let i = 0; i < Math.max(a.pre.length, b.pre.length); i++) {
|
|
25
|
+
if (a.pre[i] === b.pre[i]) continue;
|
|
26
|
+
if (a.pre[i] === undefined) return -1;
|
|
27
|
+
if (b.pre[i] === undefined) return 1;
|
|
28
|
+
const an = /^\d+$/.test(a.pre[i]), bn = /^\d+$/.test(b.pre[i]);
|
|
29
|
+
if (an !== bn) return an ? -1 : 1;
|
|
30
|
+
return (an ? BigInt(a.pre[i]) > BigInt(b.pre[i]) : a.pre[i] > b.pre[i]) ? 1 : -1;
|
|
31
|
+
}
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function releaseChannel(version) {
|
|
36
|
+
const { pre } = parseVersion(version);
|
|
37
|
+
return pre[0] === 'dev' ? 'dev' : pre.length ? 'next' : 'latest';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function identity(metadata) {
|
|
41
|
+
if (!/^@zergai\/[a-z0-9][a-z0-9-]*$/.test(metadata.name) && !['zerg-ztc', 'zerg-cyberdeck'].includes(metadata.name)) {
|
|
42
|
+
throw new Error('Unsupported CLI package identity');
|
|
43
|
+
}
|
|
44
|
+
parseVersion(metadata.version);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function checkUpdate(metadata, { fetch = globalThis.fetch, channel = releaseChannel(metadata.version), timeoutMs = 5000 } = {}) {
|
|
48
|
+
identity(metadata);
|
|
49
|
+
if (!['dev', 'next', 'latest'].includes(channel)) throw new Error('Invalid update channel');
|
|
50
|
+
const response = await fetch(`${registry}/${encodeURIComponent(metadata.name)}/${channel}`, {
|
|
51
|
+
headers: { Accept: 'application/json' }, redirect: 'error', signal: AbortSignal.timeout(timeoutMs),
|
|
52
|
+
});
|
|
53
|
+
if (!response.ok) throw new Error(`npm registry check failed (${response.status})`);
|
|
54
|
+
const reader = response.body.getReader();
|
|
55
|
+
const chunks = []; let size = 0;
|
|
56
|
+
try {
|
|
57
|
+
while (true) {
|
|
58
|
+
const { done, value } = await reader.read();
|
|
59
|
+
if (done) break;
|
|
60
|
+
size += value.byteLength;
|
|
61
|
+
if (size > 1024 * 1024) throw new Error('npm registry response too large');
|
|
62
|
+
chunks.push(Buffer.from(value));
|
|
63
|
+
}
|
|
64
|
+
} finally { await reader.cancel(); }
|
|
65
|
+
const data = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
66
|
+
if (data.name !== metadata.name) throw new Error('npm registry package identity mismatch');
|
|
67
|
+
if (releaseChannel(data.version) !== channel) throw new Error('npm registry version does not match the requested channel');
|
|
68
|
+
return { name: metadata.name, current: metadata.version, latest: data.version, channel, hasUpdate: compareVersions(data.version, metadata.version) > 0 };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function updateOptions(args) {
|
|
72
|
+
const options = { check: false };
|
|
73
|
+
for (let i = 1; i < args.length; i++) {
|
|
74
|
+
const arg = args[i];
|
|
75
|
+
if (arg === '--check' && !options.check) options.check = true;
|
|
76
|
+
else if (arg === '--channel' && !options.channel && ['dev', 'next', 'latest'].includes(args[i + 1])) options.channel = args[++i];
|
|
77
|
+
else if (['--help', '-h'].includes(arg) && args.length === 2) options.help = true;
|
|
78
|
+
else throw new Error('Usage: update [--check] [--channel dev|next|latest]');
|
|
79
|
+
}
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function install(root, metadata, info, npm, env) {
|
|
84
|
+
const { stdout } = await npm(['prefix', '--global'], { env });
|
|
85
|
+
const prefix = stdout.trim();
|
|
86
|
+
if (!isAbsolute(prefix)) throw new Error('Cannot identify the global npm installation');
|
|
87
|
+
const expected = join(prefix, 'lib/node_modules', metadata.name);
|
|
88
|
+
// Compare real paths AND reject npm link. Never turn npx/local/checkouts into a
|
|
89
|
+
// global install or update a different Node-version manager's global prefix.
|
|
90
|
+
const matches = await (async () => !(await lstat(expected)).isSymbolicLink() && await realpath(root) === await realpath(expected))().catch(() => false);
|
|
91
|
+
if (!matches) {
|
|
92
|
+
throw new Error('Not this global npm installation. Update using the package manager/prefix that installed this CLI.');
|
|
93
|
+
}
|
|
94
|
+
await npm(['install', '--global', '--prefix', prefix, `--registry=${registry}`, `--@zergai:registry=${registry}`, '--no-audit', '--no-fund', `${metadata.name}@${info.latest}`], { cwd: prefix, env });
|
|
95
|
+
const installed = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
|
|
96
|
+
if (installed.name !== metadata.name || installed.version !== info.latest) throw new Error('npm did not install the requested package version');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function startupCheck(metadata, env, fetch) {
|
|
100
|
+
const channel = releaseChannel(metadata.version);
|
|
101
|
+
const directory = join(env.XDG_CACHE_HOME || join(homedir(), '.cache'), 'zergai-cli-updates');
|
|
102
|
+
const path = join(directory, `${encodeURIComponent(metadata.name)}-${channel}.json`);
|
|
103
|
+
try {
|
|
104
|
+
const cached = JSON.parse(await readFile(path, 'utf8'));
|
|
105
|
+
const age = Date.now() - cached.checkedAt;
|
|
106
|
+
if (Number.isFinite(age) && age >= 0 && age < cacheLifetime && cached.current === metadata.version) {
|
|
107
|
+
if (cached.latest === null) return null;
|
|
108
|
+
if (releaseChannel(cached.latest) === channel) return { latest: cached.latest, hasUpdate: compareVersions(cached.latest, metadata.version) > 0 };
|
|
109
|
+
}
|
|
110
|
+
} catch { /* Missing, stale, or malformed cache: do a bounded public check. */ }
|
|
111
|
+
let info = null;
|
|
112
|
+
try { info = await checkUpdate(metadata, { fetch, timeoutMs: 800 }); }
|
|
113
|
+
catch { /* A registry outage must not prevent use of a CLI. */ }
|
|
114
|
+
let staging;
|
|
115
|
+
try {
|
|
116
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
117
|
+
staging = await mkdtemp(join(directory, '.check-'));
|
|
118
|
+
const source = join(staging, 'record');
|
|
119
|
+
await writeFile(source, JSON.stringify({ current: metadata.version, latest: info?.latest ?? null, checkedAt: Date.now() }), { flag: 'wx', mode: 0o600 });
|
|
120
|
+
await rename(source, path);
|
|
121
|
+
} catch { /* Read-only home/cache is supported. */ }
|
|
122
|
+
finally { if (staging) await rm(staging, { recursive: true, force: true }).catch(() => {}); }
|
|
123
|
+
return info;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function handleUpdate({ root, args, env = process.env, stdout = process.stdout, stderr = process.stderr, fetch = globalThis.fetch, npm = runNpm }) {
|
|
127
|
+
const explicit = ['update', '--update'].includes(args[0]);
|
|
128
|
+
if (!explicit && (env.CI || env.ZERGAI_NO_UPDATE_CHECK === '1' || !stderr.isTTY || args.some(arg => ['help', '--help', '-h', 'version', '--version', '-V'].includes(arg)))) return null;
|
|
129
|
+
let options;
|
|
130
|
+
try { options = explicit ? updateOptions(args) : {}; }
|
|
131
|
+
catch (error) { stderr.write(`${error.message}\n`); return 2; }
|
|
132
|
+
if (options.help) {
|
|
133
|
+
stdout.write('Check npm and update this globally installed CLI\nUsage: update | --update [--check] [--channel dev|next|latest]\n--check prints JSON without installing. Channels are preserved by default.\nStartup only notifies; set ZERGAI_NO_UPDATE_CHECK=1 to disable checks.\n');
|
|
134
|
+
return 0;
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
const metadata = JSON.parse(await readFile(join(root, 'package.json'), 'utf8'));
|
|
138
|
+
identity(metadata);
|
|
139
|
+
if (!explicit) {
|
|
140
|
+
const info = await startupCheck(metadata, env, fetch);
|
|
141
|
+
if (info?.hasUpdate) stderr.write(`Update available for ${metadata.name}: ${metadata.version} → ${info.latest}. Run ${Object.keys(metadata.bin || {})[0] || metadata.name} update.\n`);
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
// Explicit checks/install always consult the registry; cache is advisory only.
|
|
145
|
+
const info = await checkUpdate(metadata, { fetch, ...options });
|
|
146
|
+
if (options.check) stdout.write(`${JSON.stringify(info)}\n`);
|
|
147
|
+
else if (!info.hasUpdate) stderr.write(`No newer ${info.channel} release of ${metadata.name} (installed ${metadata.version}).\n`);
|
|
148
|
+
else {
|
|
149
|
+
stderr.write(`Updating ${metadata.name}: ${metadata.version} → ${info.latest} (${info.channel})…\n`);
|
|
150
|
+
await install(resolve(root), metadata, info, npm, env);
|
|
151
|
+
stderr.write(`Updated ${metadata.name} to ${info.latest}. The next launch uses the new version.\n`);
|
|
152
|
+
}
|
|
153
|
+
return 0;
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (!explicit) return null;
|
|
156
|
+
stderr.write(`Update failed: ${error.message}\n`);
|
|
157
|
+
return 1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = { compareVersions, releaseChannel, checkUpdate, handleUpdate };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zergai/ztc",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3-dev.35760740177",
|
|
4
4
|
"description": "Zerg Terminal Client - CLI agent for continual AI systems",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,12 @@
|
|
|
38
38
|
"falkordblite"
|
|
39
39
|
]
|
|
40
40
|
},
|
|
41
|
+
"zergCli": {
|
|
42
|
+
"entry": "dist/cli_main.js"
|
|
43
|
+
},
|
|
41
44
|
"bin": {
|
|
42
|
-
"ztc": "
|
|
43
|
-
"zerg": "
|
|
45
|
+
"ztc": "npm-cli/run.cjs",
|
|
46
|
+
"zerg": "npm-cli/run.cjs"
|
|
44
47
|
},
|
|
45
48
|
"exports": {
|
|
46
49
|
".": {
|
|
@@ -129,7 +132,7 @@
|
|
|
129
132
|
"package:prepare": "./scripts/prepare-package.sh",
|
|
130
133
|
"pack:dry": "npm pack --dry-run --cache ${NPM_CONFIG_CACHE:-/tmp/ztc-npm-cache}",
|
|
131
134
|
"pack:tarball": "npm pack --cache ${NPM_CONFIG_CACHE:-/tmp/ztc-npm-cache}",
|
|
132
|
-
"prepack": "npm run package:prepare",
|
|
135
|
+
"prepack": "npm run package:prepare && node ../scripts/npm/prepare-cli.mjs",
|
|
133
136
|
"predev": "npm run clean",
|
|
134
137
|
"dev": "npm run build:rust && tsx src/cli_main.ts",
|
|
135
138
|
"dev:ink": "tsx src/cli.tsx",
|
|
@@ -161,6 +164,7 @@
|
|
|
161
164
|
"release:preflight-governance": "node scripts/preflight-release-governance.mjs"
|
|
162
165
|
},
|
|
163
166
|
"files": [
|
|
167
|
+
"npm-cli/*.cjs",
|
|
164
168
|
"LICENSE.md",
|
|
165
169
|
"COMMERCIAL-LICENSING.md",
|
|
166
170
|
"NOTICE",
|
|
@@ -234,7 +238,7 @@
|
|
|
234
238
|
"zergRelease": {
|
|
235
239
|
"branch": "development",
|
|
236
240
|
"channel": "dev",
|
|
237
|
-
"sourceCommit": "
|
|
241
|
+
"sourceCommit": "4b068dffbc8a4dd0debe7e14c61674e5d4670ff4",
|
|
238
242
|
"sourceVersion": "0.2.0-canary.1"
|
|
239
243
|
},
|
|
240
244
|
"publishConfig": {
|