@standardagents/code 0.5.1 → 0.6.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/dist/index.js +152 -58
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { stdout, stdin } from 'process';
|
|
|
8
8
|
import fsp from 'fs/promises';
|
|
9
9
|
import crypto from 'crypto';
|
|
10
10
|
import readline from 'readline';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
11
12
|
|
|
12
13
|
// src/api.ts
|
|
13
14
|
function classifyConnectError(err, endpoint) {
|
|
@@ -4096,8 +4097,10 @@ var PKG_NAME = "@standardagents/code";
|
|
|
4096
4097
|
var REGISTRY_URL = `https://registry.npmjs.org/${encodeURIComponent(PKG_NAME)}`;
|
|
4097
4098
|
var CACHE_REL_DIR = ".config/standardagents-cli";
|
|
4098
4099
|
var CACHE_FILE = "update-check.json";
|
|
4100
|
+
var STATE_FILE = "auto-update.json";
|
|
4099
4101
|
var CACHE_TTL_MS = 1e3 * 60 * 60 * 24;
|
|
4100
4102
|
var CHECK_TIMEOUT_MS = 4e3;
|
|
4103
|
+
var IN_FLIGHT_TTL_MS = 15 * 60 * 1e3;
|
|
4101
4104
|
function cacheDir() {
|
|
4102
4105
|
return path3.join(homedir(), CACHE_REL_DIR);
|
|
4103
4106
|
}
|
|
@@ -4120,16 +4123,86 @@ function writeCache(latest) {
|
|
|
4120
4123
|
} catch {
|
|
4121
4124
|
}
|
|
4122
4125
|
}
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
return { current: currentVersion, latest: cached.latest };
|
|
4130
|
-
}
|
|
4126
|
+
function readAutoUpdateState(dir = cacheDir()) {
|
|
4127
|
+
try {
|
|
4128
|
+
const raw = fs4.readFileSync(path3.join(dir, STATE_FILE), "utf-8");
|
|
4129
|
+
const state = JSON.parse(raw);
|
|
4130
|
+
return typeof state?.version === "string" ? state : null;
|
|
4131
|
+
} catch {
|
|
4131
4132
|
return null;
|
|
4132
4133
|
}
|
|
4134
|
+
}
|
|
4135
|
+
function writeAutoUpdateState(state, dir = cacheDir()) {
|
|
4136
|
+
try {
|
|
4137
|
+
if (!fs4.existsSync(dir)) fs4.mkdirSync(dir, { recursive: true });
|
|
4138
|
+
fs4.writeFileSync(path3.join(dir, STATE_FILE), JSON.stringify(state));
|
|
4139
|
+
} catch {
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
function clearAutoUpdateState(dir = cacheDir()) {
|
|
4143
|
+
try {
|
|
4144
|
+
fs4.unlinkSync(path3.join(dir, STATE_FILE));
|
|
4145
|
+
} catch {
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
function detectPackageManager(selfPath = fileURLToPath(import.meta.url), env = process.env) {
|
|
4149
|
+
const norm = selfPath.split(/[\\/]/).join("/");
|
|
4150
|
+
if (!norm.includes("node_modules/@standardagents/code")) return null;
|
|
4151
|
+
const pnpmHome = (env.PNPM_HOME || "").split(/[\\/]/).join("/");
|
|
4152
|
+
if (pnpmHome && norm.startsWith(pnpmHome)) return "pnpm";
|
|
4153
|
+
if (norm.includes("/.pnpm/") || /\/pnpm\/global\//.test(norm)) return "pnpm";
|
|
4154
|
+
if (norm.includes("/.bun/")) return "bun";
|
|
4155
|
+
if (norm.includes("/.config/yarn/") || norm.includes("/.yarn/")) return "yarn";
|
|
4156
|
+
return "npm";
|
|
4157
|
+
}
|
|
4158
|
+
function updateCommand(pm) {
|
|
4159
|
+
switch (pm) {
|
|
4160
|
+
case "pnpm":
|
|
4161
|
+
return { cmd: "pnpm", args: ["add", "-g", `${PKG_NAME}@latest`], display: `pnpm add -g ${PKG_NAME}@latest` };
|
|
4162
|
+
case "yarn":
|
|
4163
|
+
return { cmd: "yarn", args: ["global", "add", `${PKG_NAME}@latest`], display: `yarn global add ${PKG_NAME}@latest` };
|
|
4164
|
+
case "bun":
|
|
4165
|
+
return { cmd: "bun", args: ["add", "-g", `${PKG_NAME}@latest`], display: `bun add -g ${PKG_NAME}@latest` };
|
|
4166
|
+
default:
|
|
4167
|
+
return { cmd: "npm", args: ["i", "-g", `${PKG_NAME}@latest`], display: `npm i -g ${PKG_NAME}@latest` };
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
function decideAutoUpdate(info, opts) {
|
|
4171
|
+
const env = opts.env ?? process.env;
|
|
4172
|
+
if (env.STANDARD_CODE_NO_AUTO_UPDATE) return "disabled";
|
|
4173
|
+
if (!opts.pm) return "dev_checkout";
|
|
4174
|
+
const state = opts.state;
|
|
4175
|
+
if (state && state.version === info.latest) {
|
|
4176
|
+
if (state.exitCode === null) {
|
|
4177
|
+
const now = opts.now ?? Date.now();
|
|
4178
|
+
return now - state.startedAt < IN_FLIGHT_TTL_MS ? "in_flight" : "start";
|
|
4179
|
+
}
|
|
4180
|
+
return state.exitCode === 0 ? "path_shadowed" : "already_failed";
|
|
4181
|
+
}
|
|
4182
|
+
return "start";
|
|
4183
|
+
}
|
|
4184
|
+
function startBackgroundUpdate(latest, pm, dir = cacheDir()) {
|
|
4185
|
+
const startedAt = Date.now();
|
|
4186
|
+
writeAutoUpdateState({ version: latest, startedAt, exitCode: null }, dir);
|
|
4187
|
+
const stateFile = path3.join(dir, STATE_FILE);
|
|
4188
|
+
const { cmd, args } = updateCommand(pm);
|
|
4189
|
+
const script = `const cp=require('child_process');const fs=require('fs');const r=cp.spawnSync(${JSON.stringify(cmd)},${JSON.stringify(args)},{shell:process.platform==='win32',encoding:'utf8'});const out=((r.stdout||'')+(r.stderr||'')).slice(-2000);fs.writeFileSync(${JSON.stringify(stateFile)},JSON.stringify({version:${JSON.stringify(latest)},startedAt:${startedAt},exitCode:r.status==null?-1:r.status,finishedAt:Date.now(),output:out}));`;
|
|
4190
|
+
try {
|
|
4191
|
+
const child = spawn(process.execPath, ["-e", script], { detached: true, stdio: "ignore" });
|
|
4192
|
+
child.unref();
|
|
4193
|
+
return true;
|
|
4194
|
+
} catch {
|
|
4195
|
+
writeAutoUpdateState({ version: latest, startedAt, exitCode: -1, finishedAt: Date.now() }, dir);
|
|
4196
|
+
return false;
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
function consumeAppliedUpdate(currentVersion, dir = cacheDir()) {
|
|
4200
|
+
const state = readAutoUpdateState(dir);
|
|
4201
|
+
if (!state || state.version !== currentVersion) return null;
|
|
4202
|
+
clearAutoUpdateState(dir);
|
|
4203
|
+
return state.exitCode === 0 ? state : null;
|
|
4204
|
+
}
|
|
4205
|
+
async function fetchLatest(currentVersion) {
|
|
4133
4206
|
const controller = new AbortController();
|
|
4134
4207
|
const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
|
|
4135
4208
|
try {
|
|
@@ -4142,55 +4215,48 @@ async function checkForUpdate(currentVersion) {
|
|
|
4142
4215
|
});
|
|
4143
4216
|
if (!res.ok) return null;
|
|
4144
4217
|
const data = await res.json();
|
|
4145
|
-
|
|
4146
|
-
if (!latest) return null;
|
|
4147
|
-
writeCache(latest);
|
|
4148
|
-
if (latest !== currentVersion) {
|
|
4149
|
-
return { current: currentVersion, latest };
|
|
4150
|
-
}
|
|
4151
|
-
return null;
|
|
4218
|
+
return data["dist-tags"]?.latest ?? null;
|
|
4152
4219
|
} catch {
|
|
4153
4220
|
return null;
|
|
4154
4221
|
} finally {
|
|
4155
4222
|
clearTimeout(timeout);
|
|
4156
4223
|
}
|
|
4157
4224
|
}
|
|
4158
|
-
async function
|
|
4225
|
+
async function checkForUpdate(currentVersion) {
|
|
4159
4226
|
if (!currentVersion) return null;
|
|
4160
4227
|
if (process.env.NO_UPDATE_NOTIFIER || process.env.CI) return null;
|
|
4161
|
-
const
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
signal: controller.signal,
|
|
4166
|
-
headers: {
|
|
4167
|
-
Accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*",
|
|
4168
|
-
"User-Agent": `${PKG_NAME}/${currentVersion}`
|
|
4169
|
-
}
|
|
4170
|
-
});
|
|
4171
|
-
if (!res.ok) return null;
|
|
4172
|
-
const data = await res.json();
|
|
4173
|
-
const latest = data["dist-tags"]?.latest;
|
|
4174
|
-
if (!latest) return null;
|
|
4175
|
-
writeCache(latest);
|
|
4176
|
-
if (latest !== currentVersion) {
|
|
4177
|
-
return { current: currentVersion, latest };
|
|
4228
|
+
const cached = readCache();
|
|
4229
|
+
if (cached && Date.now() - cached.timestamp < CACHE_TTL_MS) {
|
|
4230
|
+
if (cached.latest !== currentVersion) {
|
|
4231
|
+
return { current: currentVersion, latest: cached.latest };
|
|
4178
4232
|
}
|
|
4179
4233
|
return null;
|
|
4180
|
-
} catch {
|
|
4181
|
-
return null;
|
|
4182
|
-
} finally {
|
|
4183
|
-
clearTimeout(timeout);
|
|
4184
4234
|
}
|
|
4235
|
+
const latest = await fetchLatest(currentVersion);
|
|
4236
|
+
if (!latest) return null;
|
|
4237
|
+
writeCache(latest);
|
|
4238
|
+
return latest !== currentVersion ? { current: currentVersion, latest } : null;
|
|
4239
|
+
}
|
|
4240
|
+
async function forceCheckForUpdate(currentVersion) {
|
|
4241
|
+
if (!currentVersion) return null;
|
|
4242
|
+
if (process.env.NO_UPDATE_NOTIFIER || process.env.CI) return null;
|
|
4243
|
+
const latest = await fetchLatest(currentVersion);
|
|
4244
|
+
if (!latest) return null;
|
|
4245
|
+
writeCache(latest);
|
|
4246
|
+
return latest !== currentVersion ? { current: currentVersion, latest } : null;
|
|
4185
4247
|
}
|
|
4186
|
-
function
|
|
4248
|
+
function runUpdate(pm) {
|
|
4187
4249
|
return new Promise((resolve) => {
|
|
4188
|
-
const
|
|
4189
|
-
|
|
4190
|
-
shell:
|
|
4250
|
+
const { cmd, args } = updateCommand(pm);
|
|
4251
|
+
const child = spawn(cmd, args, {
|
|
4252
|
+
shell: process.platform === "win32",
|
|
4253
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
4191
4254
|
});
|
|
4192
|
-
|
|
4193
|
-
child.on("
|
|
4255
|
+
let out = "";
|
|
4256
|
+
child.stdout?.on("data", (d) => out += d);
|
|
4257
|
+
child.stderr?.on("data", (d) => out += d);
|
|
4258
|
+
child.on("close", (code) => resolve({ ok: code === 0, output: out }));
|
|
4259
|
+
child.on("error", (err) => resolve({ ok: false, output: String(err) }));
|
|
4194
4260
|
});
|
|
4195
4261
|
}
|
|
4196
4262
|
|
|
@@ -4483,13 +4549,36 @@ ${c.dim}Press Control-C again to exit${c.reset}
|
|
|
4483
4549
|
const loading = startLoader("Checking for updates");
|
|
4484
4550
|
updateAvailable = await checkForUpdate(version);
|
|
4485
4551
|
loading.stop();
|
|
4552
|
+
const applied = consumeAppliedUpdate(version);
|
|
4553
|
+
if (applied) {
|
|
4554
|
+
stdout.write(` ${c.green}\u2713${c.reset} ${c.dim}Standard Code updated to v${version}.${c.reset}
|
|
4555
|
+
|
|
4556
|
+
`);
|
|
4557
|
+
}
|
|
4486
4558
|
if (updateAvailable) {
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4559
|
+
const pm = detectPackageManager();
|
|
4560
|
+
const decision = decideAutoUpdate(updateAvailable, { state: readAutoUpdateState(), pm });
|
|
4561
|
+
if (decision === "start" && pm && startBackgroundUpdate(updateAvailable.latest, pm)) {
|
|
4562
|
+
stdout.write(
|
|
4563
|
+
` ${c.teal}\u27F3${c.reset} ${c.dim}Standard Code ${c.reset}${c.bold}v${updateAvailable.latest}${c.reset}${c.dim} is installing in the background \u2014 it applies on your next launch.${c.reset}
|
|
4490
4564
|
|
|
4491
4565
|
`
|
|
4492
|
-
|
|
4566
|
+
);
|
|
4567
|
+
} else if (decision === "in_flight") {
|
|
4568
|
+
stdout.write(
|
|
4569
|
+
` ${c.teal}\u27F3${c.reset} ${c.dim}Standard Code v${updateAvailable.latest} is still installing in the background.${c.reset}
|
|
4570
|
+
|
|
4571
|
+
`
|
|
4572
|
+
);
|
|
4573
|
+
} else {
|
|
4574
|
+
const display = updateCommand(pm ?? "npm").display;
|
|
4575
|
+
stdout.write(
|
|
4576
|
+
` ${c.teal}\u25C7${c.reset} ${c.dim}Update available:${c.reset} ${c.dim}v${updateAvailable.current}${c.reset} \u2192 ${c.bold}v${updateAvailable.latest}${c.reset}
|
|
4577
|
+
${c.dim}Run ${c.reset}${c.bold}${display}${c.reset}${c.dim} to update${c.reset}
|
|
4578
|
+
|
|
4579
|
+
`
|
|
4580
|
+
);
|
|
4581
|
+
}
|
|
4493
4582
|
}
|
|
4494
4583
|
}
|
|
4495
4584
|
const stored = getCredential(endpoint);
|
|
@@ -5048,7 +5137,7 @@ ${c.bold}why: ${req.requestPermission}${c.reset}` : ""}`,
|
|
|
5048
5137
|
},
|
|
5049
5138
|
{ name: "background", label: "Background processes", hint: "list / stop", run: () => runProcessMenu(tui, bgMgr) },
|
|
5050
5139
|
{ name: "keybindings", label: "Keyboard shortcuts", run: () => showKeybindings(tui) },
|
|
5051
|
-
{ name: "update", label: "Check for updates", hint: "check
|
|
5140
|
+
{ name: "update", label: "Check for updates", hint: "check for a newer version", run: () => runUpdateCommand(tui) },
|
|
5052
5141
|
{ name: "logout", label: "Sign out", hint: "delete the saved token & quit", run: () => logout() },
|
|
5053
5142
|
{ name: "quit", label: "Quit", run: () => quit() }
|
|
5054
5143
|
]);
|
|
@@ -5315,21 +5404,26 @@ async function runUpdateCommand(tui) {
|
|
|
5315
5404
|
const { latest } = result;
|
|
5316
5405
|
tui.print(`
|
|
5317
5406
|
${c.yellow}\u27F3${c.reset} Update available: ${c.gray}v${version}${c.reset} \u2192 ${c.green}v${latest}${c.reset}`);
|
|
5318
|
-
const
|
|
5407
|
+
const pm = detectPackageManager();
|
|
5408
|
+
if (!pm) {
|
|
5409
|
+
tui.print(` ${c.gray}This is a source checkout \u2014 pull the repo to update.${c.reset}`);
|
|
5410
|
+
return;
|
|
5411
|
+
}
|
|
5412
|
+
const { display } = updateCommand(pm);
|
|
5413
|
+
const choice = await tui.select(`Update now with \`${display}\`?`, [
|
|
5319
5414
|
{ label: "Yes, update now", value: "yes" },
|
|
5320
5415
|
{ label: "No, skip", value: "no" }
|
|
5321
5416
|
]);
|
|
5322
5417
|
if (choice === "yes") {
|
|
5323
|
-
tui.print(` ${c.gray}Running
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
}
|
|
5329
|
-
|
|
5418
|
+
tui.print(` ${c.gray}Running ${display}\u2026${c.reset}`);
|
|
5419
|
+
const { ok, output: pmOutput } = await runUpdate(pm);
|
|
5420
|
+
if (ok) {
|
|
5421
|
+
tui.print(` ${c.green}\u2713${c.reset} Updated to v${latest}. Restart to use the new version.`);
|
|
5422
|
+
} else {
|
|
5423
|
+
tui.print(` ${c.red}\u2717${c.reset} Update failed:`);
|
|
5424
|
+
for (const line of pmOutput.trim().split("\n").slice(-6)) {
|
|
5425
|
+
tui.print(` ${c.dim}${line}${c.reset}`);
|
|
5330
5426
|
}
|
|
5331
|
-
} catch (e) {
|
|
5332
|
-
tui.print(` ${c.red}\u2717${c.reset} Update failed: ${e}`);
|
|
5333
5427
|
}
|
|
5334
5428
|
} else {
|
|
5335
5429
|
tui.print(` ${c.gray}Skipped. Run /update later.${c.reset}`);
|