@0xgeorgemathew/zcode 0.3.1 → 0.3.2

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
@@ -213,9 +213,9 @@ Event types on the way in:
213
213
 
214
214
  ## Under the hood
215
215
 
216
- **Key handling.** `ZAI_API_KEY` env var, or `zcode login` which stores it at `~/.zcode/credentials.json` (mode 600). The key is never printed or logged. The only file it ever lands in is the scratch provider config, which the engine requires to register the provider.
216
+ **Key handling.** `ZAI_API_KEY` env var, or `zcode login` which stores it at `~/.zcode-cli/credentials.json` (mode 600). The key is never printed or logged. The only file it ever lands in is the scratch provider config, which the engine requires to register the provider.
217
217
 
218
- **Isolation.** The engine runs with `HOME` pointed at a scratch copy this package owns (`~/.zcode/home`, mode 700, mtime-cached). Your live `~/.zcode`, which the desktop app is using right now, is never written to. Your OAuth `credentials.json` is deliberately never copied. The scratch keeps exactly the `zai-api` provider rule with your key, your agents, and your skills.
218
+ **Isolation.** This package's own state lives under `~/.zcode-cli` (never under `~/.zcode`, which belongs to the desktop app; if an older 0.3.x left files there, they are migrated out automatically on first run). The engine runs with `HOME` pointed at a scratch copy we own (`~/.zcode-cli/home`, mode 700, mtime-cached). Your live `~/.zcode`, which the desktop app is using right now, is never written to. Your OAuth `credentials.json` is deliberately never copied. The scratch keeps exactly the `zai-api` provider rule with your key, your agents, and your skills.
219
219
 
220
220
  **Yolo mode.** Sessions run with auto-approved tool side effects, matching the engine's own headless default; `--cwd` is the declared blast radius. A headless run that requests interactive user input fails fast with exit 5 rather than hanging.
221
221
 
@@ -85,7 +85,7 @@ module.exports = async function doctor(parsed) {
85
85
  (isolation.stamp && typeof isolation.stamp.skillsCopied === 'number'
86
86
  ? isolation.stamp.skillsCopied
87
87
  : 0) || 0,
88
- mcpServersInherited: Object.keys(effectiveMcpServers({})).length,
88
+ mcpServersInherited: Object.keys(effectiveMcpServers({ inherit: true })).length,
89
89
  experimental:
90
90
  isolation.mode === 'synthesized'
91
91
  ? 'synthesized config path is unproven; copy a real ~/.zcode/v2 or verify once on a clean HOME'
package/lib/isolation.js CHANGED
@@ -226,13 +226,62 @@ function buildScratch(home, key) {
226
226
 
227
227
  // --- public API -------------------------------------------------------------
228
228
 
229
+ // Root of zcode's own state. MUST NOT be ~/.zcode: that is the desktop
230
+ // app's live directory (0.3.0/0.3.1 regression — the package rename made
231
+ // rootDir collide with it, so login/scratch files landed inside the live
232
+ // config). Ours is ~/.zcode-cli.
229
233
  function rootDir(env = process.env) {
230
- return env.ZAI_CODE_HOME || path.join(os.homedir(), '.zcode');
234
+ return env.ZAI_CODE_HOME || path.join(os.homedir(), '.zcode-cli');
235
+ }
236
+
237
+ // The only file/dir names this package ever creates at its root.
238
+ // migrateRoot moves exactly these out of legacy locations and never
239
+ // touches anything else.
240
+ const OUR_ARTIFACTS = ['credentials.json', 'build-stamp.json', 'injected-skills.json', 'home'];
241
+
242
+ // 0.3.2 migration: vacate our artifacts from (a) the live ~/.zcode, where
243
+ // 0.3.0/0.3.1 wrongly wrote them, and (b) the pre-rename ~/.zai-code root,
244
+ // into the new ~/.zcode-cli root. Idempotent; never writes or deletes
245
+ // anything else in the live directory.
246
+ function migrateRoot(env = process.env) {
247
+ const root = rootDir(env);
248
+ try {
249
+ fs.mkdirSync(root, { recursive: true });
250
+ } catch {
251
+ return; // unwritable home — leave everything as-is; ensureIsolation surfaces it
252
+ }
253
+ const sources = [
254
+ LIVE_ZCODE, // live dir: move ours out, keep everything else
255
+ path.join(os.homedir(), '.zai-code'), // pre-rename root: wholly ours
256
+ ];
257
+ for (const dir of sources) {
258
+ let entries;
259
+ try {
260
+ entries = new Set(fs.readdirSync(dir));
261
+ } catch {
262
+ continue; // nothing to migrate from this source
263
+ }
264
+ for (const name of OUR_ARTIFACTS) {
265
+ if (!entries.has(name)) continue;
266
+ const src = path.join(dir, name);
267
+ const dest = path.join(root, name);
268
+ try {
269
+ if (fs.existsSync(dest)) {
270
+ fs.rmSync(src, { recursive: true, force: true });
271
+ } else {
272
+ fs.renameSync(src, dest);
273
+ }
274
+ } catch {
275
+ /* keep going; a missing artifact just rebuilds */
276
+ }
277
+ }
278
+ }
231
279
  }
232
280
 
233
281
  // Ensures the scratch HOME exists and is current. Returns
234
282
  // {root, home, mode, built, stamp}.
235
283
  function ensureIsolation(key, { fresh = false, env = process.env } = {}) {
284
+ migrateRoot(env);
236
285
  const root = rootDir(env);
237
286
  fs.mkdirSync(root, { recursive: true });
238
287
  fs.chmodSync(root, 0o700);
@@ -307,6 +356,7 @@ module.exports = {
307
356
  ensureIsolation,
308
357
  engineEnv,
309
358
  rootDir,
359
+ migrateRoot,
310
360
  keySha256,
311
361
  LIVE_ZCODE,
312
362
  LIVE_V2,
package/lib/key.js CHANGED
@@ -1,14 +1,19 @@
1
1
  'use strict';
2
- // API key resolution (DESIGN §5): ZAI_API_KEY env → ~/.zcode/credentials.json
3
- // ({"apiKey":"…"}, mode 600, written by `zcode login`). The key is never
4
- // printed, logged, or echoed by anything in this package.
2
+ // API key resolution (DESIGN §5): ZAI_API_KEY env →
3
+ // ~/.zcode-cli/credentials.json ({"apiKey":"…"}, mode 600, written by
4
+ // `zcode login`). The key is never printed, logged, or echoed by anything
5
+ // in this package.
5
6
  const fs = require('node:fs');
6
7
  const path = require('node:path');
7
8
  const os = require('node:os');
9
+ const { migrateRoot } = require('./isolation.js');
8
10
 
9
11
  // Root of zcode's own state (scratch HOME lives under <root>/home).
12
+ // MUST NOT be ~/.zcode — that is the desktop app's live directory the
13
+ // engine itself owns (0.3.0/0.3.1 regression: the rename made the CLI
14
+ // write credentials and the scratch HOME straight into it).
10
15
  function rootDir(env = process.env) {
11
- return env.ZAI_CODE_HOME || path.join(os.homedir(), '.zcode');
16
+ return env.ZAI_CODE_HOME || path.join(os.homedir(), '.zcode-cli');
12
17
  }
13
18
 
14
19
  function credentialsPath(env = process.env) {
@@ -41,6 +46,7 @@ function readCredentialsKey(file) {
41
46
  // Sources, in order: ZAI_API_KEY (non-empty) → credentials.json.
42
47
  // Throws NoKeyError when neither holds a key.
43
48
  function resolveKey(env = process.env) {
49
+ migrateRoot(env); // 0.3.2: vacate our artifacts out of the live ~/.zcode first
44
50
  const fromEnv = (env.ZAI_API_KEY || '').trim();
45
51
  if (fromEnv) return fromEnv;
46
52
  const fromFile = readCredentialsKey(credentialsPath(env));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0xgeorgemathew/zcode",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Headless GLM-5.3 / GLM-5.3-Flash coding CLI for agents: streaming thinking, subagents, MCP servers, skills, session continuity. Wraps the ZCode engine. Zero dependencies.",
5
5
  "license": "MIT",
6
6
  "private": false,