@devstationlabs/cli 0.1.2 → 0.1.3
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 +9 -0
- package/devstation.js +205 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,15 @@ devstation config # what it will use, and where each value came fro
|
|
|
46
46
|
Exporting `ANTHROPIC_API_KEY` or `OPENROUTER_API_KEY` still works, and is what a
|
|
47
47
|
server or CI job should do.
|
|
48
48
|
|
|
49
|
+
## Upgrade
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
devstation upgrade
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
It upgrades however it was installed: through npm for an npm install, or by
|
|
56
|
+
downloading and verifying the new binary for the standalone one.
|
|
57
|
+
|
|
49
58
|
## Use it
|
|
50
59
|
|
|
51
60
|
```sh
|
package/devstation.js
CHANGED
|
@@ -12510,7 +12510,7 @@ import { existsSync as existsSync12 } from "fs";
|
|
|
12510
12510
|
|
|
12511
12511
|
// src/lib/agent/cli/args.ts
|
|
12512
12512
|
var CLI_NAME = "devstation";
|
|
12513
|
-
var VERSION = "0.1.
|
|
12513
|
+
var VERSION = "0.1.3";
|
|
12514
12514
|
var COMMANDS = new Set([
|
|
12515
12515
|
"chat",
|
|
12516
12516
|
"run",
|
|
@@ -12528,6 +12528,7 @@ var COMMANDS = new Set([
|
|
|
12528
12528
|
"config",
|
|
12529
12529
|
"login",
|
|
12530
12530
|
"logout",
|
|
12531
|
+
"upgrade",
|
|
12531
12532
|
"doctor",
|
|
12532
12533
|
"version",
|
|
12533
12534
|
"help"
|
|
@@ -12542,6 +12543,7 @@ var OFFLINE_COMMANDS = new Set([
|
|
|
12542
12543
|
"config",
|
|
12543
12544
|
"login",
|
|
12544
12545
|
"logout",
|
|
12546
|
+
"upgrade",
|
|
12545
12547
|
"doctor",
|
|
12546
12548
|
"version",
|
|
12547
12549
|
"help",
|
|
@@ -12558,6 +12560,7 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
12558
12560
|
json: false,
|
|
12559
12561
|
sandbox: (process.env.DEVSTATION_SANDBOX ?? "").toLowerCase() !== "off",
|
|
12560
12562
|
project: false,
|
|
12563
|
+
check: false,
|
|
12561
12564
|
root: cwd
|
|
12562
12565
|
};
|
|
12563
12566
|
const words = [];
|
|
@@ -12585,6 +12588,9 @@ function parseArgs(argv, cwd = process.cwd()) {
|
|
|
12585
12588
|
case "--project":
|
|
12586
12589
|
parsed.project = true;
|
|
12587
12590
|
break;
|
|
12591
|
+
case "--check":
|
|
12592
|
+
parsed.check = true;
|
|
12593
|
+
break;
|
|
12588
12594
|
case "-h":
|
|
12589
12595
|
case "--help":
|
|
12590
12596
|
parsed.command = "help";
|
|
@@ -12675,6 +12681,7 @@ var HELP = `DevStation, the coding agent.
|
|
|
12675
12681
|
${CLI_NAME} tools list the tools it can use, and which ones ask first
|
|
12676
12682
|
${CLI_NAME} login [provider] store an API key and choose a model
|
|
12677
12683
|
${CLI_NAME} logout [provider] remove stored API keys
|
|
12684
|
+
${CLI_NAME} upgrade [--check] update to the latest version (--check only reports)
|
|
12678
12685
|
${CLI_NAME} config show the settings a run would use, and where each came from
|
|
12679
12686
|
${CLI_NAME} config set <key> <value> [--project]
|
|
12680
12687
|
set provider, model or baseUrl
|
|
@@ -12696,6 +12703,7 @@ Options
|
|
|
12696
12703
|
--json JSON from config, sessions, checkpoints and tools
|
|
12697
12704
|
--no-sandbox run commands on this machine instead of in a container
|
|
12698
12705
|
--project with config set/unset: write this workspace's config, not the global one
|
|
12706
|
+
--check with upgrade: say whether a newer version exists, change nothing
|
|
12699
12707
|
-h, --help this
|
|
12700
12708
|
-v, --version the version
|
|
12701
12709
|
|
|
@@ -18154,8 +18162,9 @@ function readSettingsFile(path4, problems = []) {
|
|
|
18154
18162
|
const raw = JSON.parse(readFileSync3(path4, "utf8"));
|
|
18155
18163
|
const out = {};
|
|
18156
18164
|
if (raw.provider !== undefined) {
|
|
18157
|
-
|
|
18158
|
-
|
|
18165
|
+
const named = typeof raw.provider === "string" ? raw.provider.toLowerCase() : raw.provider;
|
|
18166
|
+
if (isProvider(named))
|
|
18167
|
+
out.provider = named;
|
|
18159
18168
|
else
|
|
18160
18169
|
problems.push(`${path4}: unknown provider "${String(raw.provider)}".`);
|
|
18161
18170
|
}
|
|
@@ -18228,8 +18237,9 @@ function resolveSettings(opts) {
|
|
|
18228
18237
|
let provider = null;
|
|
18229
18238
|
let providerSource = "not set";
|
|
18230
18239
|
if (env2.DEVSTATION_PROVIDER) {
|
|
18231
|
-
|
|
18232
|
-
|
|
18240
|
+
const named = env2.DEVSTATION_PROVIDER.toLowerCase();
|
|
18241
|
+
if (isProvider(named)) {
|
|
18242
|
+
provider = named;
|
|
18233
18243
|
providerSource = "DEVSTATION_PROVIDER";
|
|
18234
18244
|
} else {
|
|
18235
18245
|
warnings.push(`DEVSTATION_PROVIDER="${env2.DEVSTATION_PROVIDER}" is not a provider, so it was ignored.`);
|
|
@@ -19542,7 +19552,7 @@ var NEEDS = {
|
|
|
19542
19552
|
go: ["go"]
|
|
19543
19553
|
};
|
|
19544
19554
|
async function probeImage(workspace, image, runtime) {
|
|
19545
|
-
const ecosystems =
|
|
19555
|
+
const ecosystems = gatingEcosystems(detectManifests(workspace));
|
|
19546
19556
|
const wanted = [...new Set(ecosystems.flatMap((e) => NEEDS[e]))];
|
|
19547
19557
|
if (wanted.length === 0)
|
|
19548
19558
|
return { ok: true, missing: [], ecosystems };
|
|
@@ -19554,10 +19564,13 @@ async function probeImage(workspace, image, runtime) {
|
|
|
19554
19564
|
`).map((l) => l.trim()).filter(Boolean);
|
|
19555
19565
|
return { ok: missing.length === 0, missing, ecosystems };
|
|
19556
19566
|
}
|
|
19567
|
+
function gatingEcosystems(manifests) {
|
|
19568
|
+
return [...new Set(manifests.filter((m) => m.dir === "").map((m) => m.ecosystem))];
|
|
19569
|
+
}
|
|
19557
19570
|
function probeProblem(probe, image) {
|
|
19558
19571
|
if (probe.ok)
|
|
19559
19572
|
return null;
|
|
19560
|
-
return `
|
|
19573
|
+
return `The sandbox image ${image} has no ${probe.missing.join(", ")}, which this ${probe.ecosystems.join(" and ")} ` + "project uses, so commands that need it will fail inside the sandbox. Build an image that has it " + "and set DEVSTATION_SANDBOX_IMAGE, or run with --no-sandbox.";
|
|
19561
19574
|
}
|
|
19562
19575
|
function quote2(value) {
|
|
19563
19576
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
@@ -20911,7 +20924,8 @@ function renderSessions(records) {
|
|
|
20911
20924
|
}
|
|
20912
20925
|
|
|
20913
20926
|
// src/lib/agent/cli/commands.ts
|
|
20914
|
-
async function buildExecutor(root, sandbox) {
|
|
20927
|
+
async function buildExecutor(root, sandbox, warn = (message) => process.stderr.write(`warning: ${message}
|
|
20928
|
+
`)) {
|
|
20915
20929
|
if (!sandbox)
|
|
20916
20930
|
return { executor: hostExecutor() };
|
|
20917
20931
|
const readiness = await sandboxReadiness();
|
|
@@ -20921,7 +20935,7 @@ async function buildExecutor(root, sandbox) {
|
|
|
20921
20935
|
const probe = await probeImage(root, readiness.imageName, readiness.runtimeName);
|
|
20922
20936
|
const mismatch = probeProblem(probe, readiness.imageName);
|
|
20923
20937
|
if (mismatch)
|
|
20924
|
-
|
|
20938
|
+
warn(mismatch);
|
|
20925
20939
|
return { executor: sandboxExecutor({ workspace: root }) };
|
|
20926
20940
|
}
|
|
20927
20941
|
function isYes(answer) {
|
|
@@ -21254,6 +21268,14 @@ ${r.problem}`);
|
|
|
21254
21268
|
function home() {
|
|
21255
21269
|
return process.env.HOME ?? "";
|
|
21256
21270
|
}
|
|
21271
|
+
function homeDirectoryWarning(root, homeDir = process.env.HOME ?? "") {
|
|
21272
|
+
if (!homeDir)
|
|
21273
|
+
return null;
|
|
21274
|
+
const strip2 = (p) => p.replace(/\/+$/, "");
|
|
21275
|
+
if (strip2(root) !== strip2(homeDir))
|
|
21276
|
+
return null;
|
|
21277
|
+
return "You are in your home directory, so the agent treats everything under it as one project, " + "every repository inside it included. cd into the project you mean first.";
|
|
21278
|
+
}
|
|
21257
21279
|
function configEditCommand(context, rest, opts = {}) {
|
|
21258
21280
|
const [action, key, ...valueWords] = rest.trim().split(/\s+/);
|
|
21259
21281
|
const value = valueWords.join(" ").trim();
|
|
@@ -21289,7 +21311,7 @@ function configEditCommand(context, rest, opts = {}) {
|
|
|
21289
21311
|
context.terminal.err(`Give it a value: devstation config set ${name} <value>`);
|
|
21290
21312
|
return 2;
|
|
21291
21313
|
}
|
|
21292
|
-
if (name === "provider" && !PROVIDER_IDS.includes(value)) {
|
|
21314
|
+
if (name === "provider" && !PROVIDER_IDS.includes(value.toLowerCase())) {
|
|
21293
21315
|
context.terminal.err(`Unknown provider "${value}". Providers: ${PROVIDER_IDS.join(", ")}.`);
|
|
21294
21316
|
return 2;
|
|
21295
21317
|
}
|
|
@@ -21297,7 +21319,7 @@ function configEditCommand(context, rest, opts = {}) {
|
|
|
21297
21319
|
context.terminal.err("baseUrl must start with http:// or https://.");
|
|
21298
21320
|
return 2;
|
|
21299
21321
|
}
|
|
21300
|
-
current[name] = name === "baseUrl" ? value.replace(/\/+$/, "") : value;
|
|
21322
|
+
current[name] = name === "baseUrl" ? value.replace(/\/+$/, "") : name === "provider" ? value.toLowerCase() : value;
|
|
21301
21323
|
writeSettingsFile(path4, current);
|
|
21302
21324
|
context.terminal.out(`Set ${name} = ${current[name]} in ${path4}.`);
|
|
21303
21325
|
return 0;
|
|
@@ -21308,13 +21330,13 @@ function configEditCommand(context, rest, opts = {}) {
|
|
|
21308
21330
|
async function loginCommand(context, rest) {
|
|
21309
21331
|
const t = context.terminal;
|
|
21310
21332
|
const secret = t.askSecret ? (q) => t.askSecret(q) : (q) => t.ask(q);
|
|
21311
|
-
let provider = rest.trim().split(/\s+/)[0];
|
|
21333
|
+
let provider = (rest.trim().split(/\s+/)[0] ?? "").toLowerCase();
|
|
21312
21334
|
if (!provider) {
|
|
21313
21335
|
t.out("Which provider?");
|
|
21314
21336
|
t.out(" anthropic Claude, directly (prompt caching, native tool use)");
|
|
21315
21337
|
t.out(" openrouter one key for Claude, GPT, Gemini, DeepSeek and more");
|
|
21316
21338
|
t.out(" openai OpenAI, or any compatible server: Ollama, LM Studio, Groq, Together");
|
|
21317
|
-
provider = (await t.ask("provider [anthropic]: ")).trim() || "anthropic";
|
|
21339
|
+
provider = (await t.ask("provider [anthropic]: ")).trim().toLowerCase() || "anthropic";
|
|
21318
21340
|
}
|
|
21319
21341
|
if (!PROVIDER_IDS.includes(provider)) {
|
|
21320
21342
|
t.err(`Unknown provider "${provider}". Providers: ${PROVIDER_IDS.join(", ")}.`);
|
|
@@ -21372,7 +21394,7 @@ async function loginCommand(context, rest) {
|
|
|
21372
21394
|
}
|
|
21373
21395
|
function logoutCommand(context, rest) {
|
|
21374
21396
|
const h = home();
|
|
21375
|
-
const target = rest.trim().split(/\s+/)[0];
|
|
21397
|
+
const target = (rest.trim().split(/\s+/)[0] ?? "").toLowerCase();
|
|
21376
21398
|
if (target && !PROVIDER_IDS.includes(target)) {
|
|
21377
21399
|
context.terminal.err(`Unknown provider "${target}". Providers: ${PROVIDER_IDS.join(", ")}.`);
|
|
21378
21400
|
return 2;
|
|
@@ -22135,6 +22157,169 @@ function lineReader(rl, write) {
|
|
|
22135
22157
|
};
|
|
22136
22158
|
}
|
|
22137
22159
|
|
|
22160
|
+
// src/lib/agent/cli/upgrade.ts
|
|
22161
|
+
import { spawnSync } from "child_process";
|
|
22162
|
+
import { createHash as createHash2 } from "crypto";
|
|
22163
|
+
import { chmodSync as chmodSync2, mkdirSync as mkdirSync8, readFileSync as readFileSync9, renameSync as renameSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
22164
|
+
import { dirname as dirname8, join as join14 } from "path";
|
|
22165
|
+
var PACKAGE = "@devstationlabs/cli";
|
|
22166
|
+
var REPO = "linoxbt/dev-shipyard";
|
|
22167
|
+
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
22168
|
+
function installMethod(execPath = process.execPath, script = process.argv[1] ?? "") {
|
|
22169
|
+
const slash = (p) => p.replace(/\\/g, "/");
|
|
22170
|
+
const marker = `node_modules/${PACKAGE}/`;
|
|
22171
|
+
if (slash(execPath).includes(marker) || slash(script).includes(marker))
|
|
22172
|
+
return "npm";
|
|
22173
|
+
if (/\.(ts|tsx)$/.test(script) && /(^|\/)bun(\.exe)?$/.test(slash(execPath)))
|
|
22174
|
+
return "source";
|
|
22175
|
+
return "binary";
|
|
22176
|
+
}
|
|
22177
|
+
function compareVersions(a, b) {
|
|
22178
|
+
const parts = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
22179
|
+
const [x, y] = [parts(a), parts(b)];
|
|
22180
|
+
for (let i = 0;i < Math.max(x.length, y.length); i++) {
|
|
22181
|
+
const d = (x[i] ?? 0) - (y[i] ?? 0);
|
|
22182
|
+
if (d !== 0)
|
|
22183
|
+
return d > 0 ? 1 : -1;
|
|
22184
|
+
}
|
|
22185
|
+
return 0;
|
|
22186
|
+
}
|
|
22187
|
+
async function latestVersion(fetchImpl = fetch, timeoutMs = 4000) {
|
|
22188
|
+
try {
|
|
22189
|
+
const res = await fetchImpl(`https://registry.npmjs.org/${PACKAGE.replace("/", "%2f")}/latest`, {
|
|
22190
|
+
headers: { accept: "application/json" },
|
|
22191
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
22192
|
+
});
|
|
22193
|
+
if (!res.ok)
|
|
22194
|
+
return null;
|
|
22195
|
+
const body = await res.json();
|
|
22196
|
+
return typeof body.version === "string" ? body.version : null;
|
|
22197
|
+
} catch {
|
|
22198
|
+
return null;
|
|
22199
|
+
}
|
|
22200
|
+
}
|
|
22201
|
+
function targetFor(platform = process.platform, arch = process.arch) {
|
|
22202
|
+
const map = {
|
|
22203
|
+
"linux-x64": "devstation-linux-x64",
|
|
22204
|
+
"linux-arm64": "devstation-linux-arm64",
|
|
22205
|
+
"darwin-arm64": "devstation-darwin-arm64",
|
|
22206
|
+
"darwin-x64": "devstation-darwin-x64",
|
|
22207
|
+
"win32-x64": "devstation-windows-x64.exe"
|
|
22208
|
+
};
|
|
22209
|
+
return map[`${platform}-${arch}`] ?? null;
|
|
22210
|
+
}
|
|
22211
|
+
function runInherit(command, args) {
|
|
22212
|
+
const result = spawnSync(command, args, { stdio: "inherit" });
|
|
22213
|
+
if (result.error)
|
|
22214
|
+
return 127;
|
|
22215
|
+
return result.status ?? 1;
|
|
22216
|
+
}
|
|
22217
|
+
async function upgradeCommand(context, opts = {}) {
|
|
22218
|
+
const t = context.terminal;
|
|
22219
|
+
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
22220
|
+
const latest = await latestVersion(fetchImpl);
|
|
22221
|
+
if (!latest) {
|
|
22222
|
+
t.err("Could not reach the npm registry to check for a newer version. Nothing was changed.");
|
|
22223
|
+
return 1;
|
|
22224
|
+
}
|
|
22225
|
+
if (compareVersions(latest, VERSION) <= 0) {
|
|
22226
|
+
t.out(`devstation ${VERSION} is the latest version.`);
|
|
22227
|
+
return 0;
|
|
22228
|
+
}
|
|
22229
|
+
t.out(`devstation ${latest} is available (you have ${VERSION}).`);
|
|
22230
|
+
const method = installMethod(opts.execPath, opts.script);
|
|
22231
|
+
if (opts.check) {
|
|
22232
|
+
t.out(method === "source" ? "This is a source checkout: update it with git pull." : "Upgrade with: devstation upgrade");
|
|
22233
|
+
return 0;
|
|
22234
|
+
}
|
|
22235
|
+
switch (method) {
|
|
22236
|
+
case "npm": {
|
|
22237
|
+
t.out(`Installed through npm, so upgrading through npm: npm install -g ${PACKAGE}@${latest}`);
|
|
22238
|
+
const code = (opts.run ?? runInherit)("npm", ["install", "-g", `${PACKAGE}@${latest}`]);
|
|
22239
|
+
if (code !== 0) {
|
|
22240
|
+
t.err(`npm exited with ${code}. Nothing else was changed. Run it yourself: npm install -g ${PACKAGE}@latest`);
|
|
22241
|
+
return code === 0 ? 1 : code;
|
|
22242
|
+
}
|
|
22243
|
+
t.out(`Upgraded to ${latest}. If your shell still runs the old one, run: hash -r`);
|
|
22244
|
+
return 0;
|
|
22245
|
+
}
|
|
22246
|
+
case "source":
|
|
22247
|
+
t.out("This is running from a source checkout. Update it with git pull, not upgrade.");
|
|
22248
|
+
return 0;
|
|
22249
|
+
case "binary":
|
|
22250
|
+
return replaceBinary(t, latest, opts.execPath ?? process.execPath, fetchImpl);
|
|
22251
|
+
}
|
|
22252
|
+
}
|
|
22253
|
+
async function replaceBinary(t, latest, execPath, fetchImpl) {
|
|
22254
|
+
const target = targetFor();
|
|
22255
|
+
if (!target) {
|
|
22256
|
+
t.err(`There is no prebuilt binary for ${process.platform}-${process.arch}.`);
|
|
22257
|
+
return 1;
|
|
22258
|
+
}
|
|
22259
|
+
if (process.platform === "win32") {
|
|
22260
|
+
t.err(`A running .exe cannot replace itself on Windows. Download ${target} from https://github.com/${REPO}/releases/latest and swap it in.`);
|
|
22261
|
+
return 1;
|
|
22262
|
+
}
|
|
22263
|
+
const base = `https://github.com/${REPO}/releases/download/v${latest}`;
|
|
22264
|
+
t.out(`Downloading ${target} ${latest}\u2026`);
|
|
22265
|
+
try {
|
|
22266
|
+
const sums = await fetchImpl(`${base}/SHA256SUMS`, { redirect: "follow" });
|
|
22267
|
+
if (!sums.ok)
|
|
22268
|
+
throw new Error(`${sums.status} fetching the checksums`);
|
|
22269
|
+
const expected = (await sums.text()).split(`
|
|
22270
|
+
`).map((line) => line.trim().split(/\s+/)).find(([, name]) => name === target)?.[0];
|
|
22271
|
+
if (!expected)
|
|
22272
|
+
throw new Error(`no checksum is published for ${target}`);
|
|
22273
|
+
const bin = await fetchImpl(`${base}/${target}`, { redirect: "follow" });
|
|
22274
|
+
if (!bin.ok)
|
|
22275
|
+
throw new Error(`${bin.status} downloading ${target}`);
|
|
22276
|
+
const body = Buffer.from(await bin.arrayBuffer());
|
|
22277
|
+
const actual = createHash2("sha256").update(body).digest("hex");
|
|
22278
|
+
if (actual !== expected) {
|
|
22279
|
+
throw new Error(`the download does not match its checksum (expected ${expected}, got ${actual})`);
|
|
22280
|
+
}
|
|
22281
|
+
const partial = `${execPath}.partial`;
|
|
22282
|
+
writeFileSync7(partial, body);
|
|
22283
|
+
chmodSync2(partial, 493);
|
|
22284
|
+
renameSync3(partial, execPath);
|
|
22285
|
+
} catch (error2) {
|
|
22286
|
+
const message2 = error2 instanceof Error ? error2.message : String(error2);
|
|
22287
|
+
const permission = /EACCES|EPERM/.test(message2) ? " Run it with sudo, since the binary is in a system directory." : "";
|
|
22288
|
+
t.err(`Upgrade failed and the installed version was left untouched: ${message2}.${permission}`);
|
|
22289
|
+
return 1;
|
|
22290
|
+
}
|
|
22291
|
+
t.out(`Upgraded ${execPath} to ${latest}.`);
|
|
22292
|
+
return 0;
|
|
22293
|
+
}
|
|
22294
|
+
function notifyIfOutdated(terminal, opts = {}) {
|
|
22295
|
+
const env2 = opts.env ?? process.env;
|
|
22296
|
+
if (env2.DEVSTATION_NO_UPDATE_CHECK === "1" || env2.CI)
|
|
22297
|
+
return Promise.resolve();
|
|
22298
|
+
const homeDir = opts.home ?? env2.HOME ?? "";
|
|
22299
|
+
if (!homeDir)
|
|
22300
|
+
return Promise.resolve();
|
|
22301
|
+
const path4 = join14(homeDir, ".devstation", "update-check.json");
|
|
22302
|
+
let cached = {};
|
|
22303
|
+
try {
|
|
22304
|
+
cached = JSON.parse(readFileSync9(path4, "utf8"));
|
|
22305
|
+
} catch {}
|
|
22306
|
+
if (cached.latest && compareVersions(cached.latest, VERSION) > 0) {
|
|
22307
|
+
terminal.err(`devstation ${cached.latest} is available (you have ${VERSION}). Run: devstation upgrade`);
|
|
22308
|
+
}
|
|
22309
|
+
const now = opts.now ?? Date.now();
|
|
22310
|
+
if (cached.checkedAt && now - cached.checkedAt < DAY_MS)
|
|
22311
|
+
return Promise.resolve();
|
|
22312
|
+
return latestVersion(opts.fetchImpl ?? fetch, 2500).then((latest) => {
|
|
22313
|
+
if (!latest)
|
|
22314
|
+
return;
|
|
22315
|
+
try {
|
|
22316
|
+
mkdirSync8(dirname8(path4), { recursive: true, mode: 448 });
|
|
22317
|
+
writeFileSync7(path4, `${JSON.stringify({ checkedAt: now, latest })}
|
|
22318
|
+
`);
|
|
22319
|
+
} catch {}
|
|
22320
|
+
});
|
|
22321
|
+
}
|
|
22322
|
+
|
|
22138
22323
|
// src/lib/agent/cli/index.ts
|
|
22139
22324
|
async function main(argv) {
|
|
22140
22325
|
const parsed = parseArgs(argv);
|
|
@@ -22222,6 +22407,8 @@ Stopping. The session is saved; resume it with \`${CLI_NAME} resume\`.`);
|
|
|
22222
22407
|
return await loginCommand(offline, parsed.rest);
|
|
22223
22408
|
case "logout":
|
|
22224
22409
|
return logoutCommand(offline, parsed.rest);
|
|
22410
|
+
case "upgrade":
|
|
22411
|
+
return await upgradeCommand(offline, { check: parsed.check });
|
|
22225
22412
|
case "doctor":
|
|
22226
22413
|
return await doctorCommand(offline);
|
|
22227
22414
|
case "index":
|
|
@@ -22242,6 +22429,10 @@ Stopping. The session is saved; resume it with \`${CLI_NAME} resume\`.`);
|
|
|
22242
22429
|
terminal.err(`Run \`${CLI_NAME} doctor\` to see what else is missing.`);
|
|
22243
22430
|
return 2;
|
|
22244
22431
|
}
|
|
22432
|
+
const atHome = homeDirectoryWarning(root);
|
|
22433
|
+
if (atHome)
|
|
22434
|
+
terminal.err(`warning: ${atHome}`);
|
|
22435
|
+
notifyIfOutdated(terminal);
|
|
22245
22436
|
const ctx = context(root, terminal, parsed, provider);
|
|
22246
22437
|
ctx.signal = controller.signal;
|
|
22247
22438
|
if (parsed.command === "repo") {
|