@sealant/mend 0.34.4 → 0.35.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/{chunk-AAAXHRWL.js → chunk-SMH65F6Z.js} +59 -8
- package/dist/{dashboard-DWXXYFPE.js → dashboard-MGR4YUFD.js} +1 -1
- package/dist/main.js +401 -265
- package/man/mend-accounts.1 +1 -1
- package/man/mend-adopt.1 +1 -1
- package/man/mend-attach.1 +1 -1
- package/man/mend-codex.1 +1 -1
- package/man/mend-completions.1 +1 -1
- package/man/mend-connect.1 +8 -4
- package/man/mend-continue.1 +1 -1
- package/man/mend-doctor.1 +1 -1
- package/man/mend-dotfiles-repo.1 +1 -1
- package/man/mend-dotfiles-sync.1 +1 -1
- package/man/mend-dotfiles.1 +1 -1
- package/man/mend-env-cluster.1 +1 -1
- package/man/mend-env-load.1 +1 -1
- package/man/mend-env-show.1 +1 -1
- package/man/mend-folder-create.1 +1 -1
- package/man/mend-folder-list.1 +1 -1
- package/man/mend-folder-push.1 +1 -1
- package/man/mend-folder-rm.1 +1 -1
- package/man/mend-git-author.1 +1 -1
- package/man/mend-help.1 +1 -1
- package/man/mend-invite.1 +1 -1
- package/man/mend-keys-init.1 +1 -1
- package/man/mend-keys-mode.1 +1 -1
- package/man/mend-keys-share.1 +1 -1
- package/man/mend-keys-show.1 +1 -1
- package/man/mend-land.1 +1 -1
- package/man/mend-login.1 +1 -1
- package/man/mend-logout.1 +1 -1
- package/man/mend-man.1 +1 -1
- package/man/mend-members.1 +1 -1
- package/man/mend-operator-exposure.1 +1 -1
- package/man/mend-operator-gate.1 +1 -1
- package/man/mend-operator-grant-owner.1 +1 -1
- package/man/mend-operator-org-create.1 +1 -1
- package/man/mend-operator-org-invite-owner.1 +1 -1
- package/man/mend-operator-org-list.1 +1 -1
- package/man/mend-operator-org-rename.1 +1 -1
- package/man/mend-operator-reset-link.1 +1 -1
- package/man/mend-pair.1 +1 -1
- package/man/mend-projects.1 +1 -1
- package/man/mend-pull.1 +1 -1
- package/man/mend-refresh.1 +1 -1
- package/man/mend-rejoin.1 +1 -1
- package/man/mend-resume.1 +1 -1
- package/man/mend-run.1 +1 -1
- package/man/mend-server-logs.1 +1 -1
- package/man/mend-server-restart.1 +1 -1
- package/man/mend-server-setup.1 +1 -1
- package/man/mend-server-start.1 +1 -1
- package/man/mend-server-status.1 +1 -1
- package/man/mend-server-stop.1 +1 -1
- package/man/mend-server-upgrade.1 +1 -1
- package/man/mend-server.1 +1 -1
- package/man/mend-service-add.1 +1 -1
- package/man/mend-service-connect.1 +1 -1
- package/man/mend-service-init.1 +1 -1
- package/man/mend-service-list.1 +1 -1
- package/man/mend-service-logs.1 +1 -1
- package/man/mend-service-restart.1 +1 -1
- package/man/mend-service-run.1 +1 -1
- package/man/mend-service-stop.1 +1 -1
- package/man/mend-session-share.1 +1 -1
- package/man/mend-sessions.1 +1 -1
- package/man/mend-shell.1 +1 -1
- package/man/mend-skills-push.1 +1 -1
- package/man/mend-skills.1 +1 -1
- package/man/mend-snake.1 +1 -1
- package/man/mend-ssh-setup.1 +1 -1
- package/man/mend-ssh.1 +1 -1
- package/man/mend-stop.1 +1 -1
- package/man/mend-ui.1 +1 -1
- package/man/mend-uninstall.1 +1 -1
- package/man/mend-version.1 +1 -1
- package/man/mend-worktrees.1 +1 -1
- package/man/mend.1 +1 -1
- package/package.json +3 -3
package/dist/main.js
CHANGED
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
MendRequestError,
|
|
7
7
|
agentIsLive,
|
|
8
8
|
agentOutcome,
|
|
9
|
+
agentStartingFacts,
|
|
10
|
+
agentStartingLine,
|
|
9
11
|
captureLineOf,
|
|
10
12
|
claudeGrantFacts,
|
|
11
13
|
createSseParser,
|
|
@@ -32,15 +34,15 @@ import {
|
|
|
32
34
|
sessionDisplayName,
|
|
33
35
|
spoken,
|
|
34
36
|
trackBracketedPaste
|
|
35
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-SMH65F6Z.js";
|
|
36
38
|
|
|
37
39
|
// src/main.ts
|
|
38
|
-
import { spawn as spawn3, spawnSync as
|
|
39
|
-
import * as
|
|
40
|
+
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
41
|
+
import * as fs16 from "node:fs";
|
|
40
42
|
import { createRequire } from "node:module";
|
|
41
43
|
import * as net4 from "node:net";
|
|
42
|
-
import * as
|
|
43
|
-
import * as
|
|
44
|
+
import * as os11 from "node:os";
|
|
45
|
+
import * as path17 from "node:path";
|
|
44
46
|
|
|
45
47
|
// src/agent-share.ts
|
|
46
48
|
import * as net from "node:net";
|
|
@@ -175,6 +177,45 @@ var startAgentShare = (options) => {
|
|
|
175
177
|
};
|
|
176
178
|
};
|
|
177
179
|
|
|
180
|
+
// src/attach-starting.ts
|
|
181
|
+
var startingLabelOf = (harness) => harness === "run" ? "the command" : harness === "shell" ? "the shell" : harness;
|
|
182
|
+
var CLEAR_LINE = "\r\x1B[2K";
|
|
183
|
+
var startingLine = (label, facts, elapsedMs, cancelHint) => `${CLEAR_LINE} ${agentStartingLine(label, facts, elapsedMs)}${cancelHint === "" ? "" : ` \xB7 ${cancelHint}`}`;
|
|
184
|
+
var firstOutputGate = (options) => {
|
|
185
|
+
const attachedAt = options.now();
|
|
186
|
+
let facts = { startedAt: null, freshMachine: false };
|
|
187
|
+
let shown = false;
|
|
188
|
+
let done = false;
|
|
189
|
+
let interval = null;
|
|
190
|
+
const draw = () => {
|
|
191
|
+
if (done) return;
|
|
192
|
+
shown = true;
|
|
193
|
+
const since = facts.startedAt ?? attachedAt;
|
|
194
|
+
options.write(startingLine(options.label, facts, options.now() - since, options.cancelHint));
|
|
195
|
+
};
|
|
196
|
+
const grace = setTimeout(() => {
|
|
197
|
+
draw();
|
|
198
|
+
interval = setInterval(draw, options.intervalMs ?? 1e3);
|
|
199
|
+
}, options.graceMs ?? 400);
|
|
200
|
+
const finish = () => {
|
|
201
|
+
if (done) return;
|
|
202
|
+
done = true;
|
|
203
|
+
clearTimeout(grace);
|
|
204
|
+
if (interval !== null) clearInterval(interval);
|
|
205
|
+
if (shown) options.write(CLEAR_LINE);
|
|
206
|
+
shown = false;
|
|
207
|
+
};
|
|
208
|
+
return {
|
|
209
|
+
pass: finish,
|
|
210
|
+
stop: finish,
|
|
211
|
+
update: (next) => {
|
|
212
|
+
facts = next;
|
|
213
|
+
if (shown) draw();
|
|
214
|
+
},
|
|
215
|
+
shown: () => shown
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
|
|
178
219
|
// src/claude-grant.ts
|
|
179
220
|
import { spawnSync } from "node:child_process";
|
|
180
221
|
import { createHash } from "node:crypto";
|
|
@@ -301,15 +342,83 @@ var readClipboardImage = async (platform = process.platform, env = process.env)
|
|
|
301
342
|
return null;
|
|
302
343
|
};
|
|
303
344
|
|
|
304
|
-
// src/
|
|
305
|
-
import
|
|
345
|
+
// src/codex-grant.ts
|
|
346
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
347
|
+
import * as fs2 from "node:fs";
|
|
306
348
|
import * as os2 from "node:os";
|
|
307
|
-
import * as
|
|
349
|
+
import * as path2 from "node:path";
|
|
350
|
+
var codexCli = (environment = process.env) => ({
|
|
351
|
+
bin: environment["MEND_CODEX_BIN"] ?? "codex",
|
|
352
|
+
env: environment
|
|
353
|
+
});
|
|
354
|
+
var personalCodexHome = (environment = process.env) => {
|
|
355
|
+
const configured = environment["CODEX_HOME"];
|
|
356
|
+
return configured === void 0 || configured === "" ? path2.join(os2.homedir(), ".codex") : configured;
|
|
357
|
+
};
|
|
358
|
+
var runCodexDeviceLogin = (cli, codexHome) => {
|
|
359
|
+
const result = spawnSync2(cli.bin, ["login", "--device-auth"], {
|
|
360
|
+
stdio: "inherit",
|
|
361
|
+
env: { ...cli.env, CODEX_HOME: codexHome }
|
|
362
|
+
});
|
|
363
|
+
return result.error === void 0 && result.status === 0;
|
|
364
|
+
};
|
|
365
|
+
var refreshTokenOf = (authJson) => {
|
|
366
|
+
try {
|
|
367
|
+
const parsed = JSON.parse(authJson);
|
|
368
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
369
|
+
const tokens = parsed.tokens;
|
|
370
|
+
if (typeof tokens !== "object" || tokens === null) return null;
|
|
371
|
+
const refreshToken = tokens.refresh_token;
|
|
372
|
+
return typeof refreshToken === "string" && refreshToken !== "" ? refreshToken : null;
|
|
373
|
+
} catch {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
var sameCodexLogin = (a, b) => {
|
|
378
|
+
const first = refreshTokenOf(a);
|
|
379
|
+
return first !== null && first === refreshTokenOf(b);
|
|
380
|
+
};
|
|
381
|
+
var codexGrant = (input) => {
|
|
382
|
+
const home = fs2.mkdtempSync(path2.join(os2.tmpdir(), "mend-codex-grant-"));
|
|
383
|
+
fs2.chmodSync(home, 448);
|
|
384
|
+
try {
|
|
385
|
+
input.say(" Mend needs its own Codex login; it is sent to your server and not kept here");
|
|
386
|
+
input.say(" your own Codex login stays as it is");
|
|
387
|
+
if (!runCodexDeviceLogin(input.cli, home)) {
|
|
388
|
+
return { kind: "failed", reason: "codex: the login did not complete" };
|
|
389
|
+
}
|
|
390
|
+
const file = path2.join(home, "auth.json");
|
|
391
|
+
if (!fs2.existsSync(file)) {
|
|
392
|
+
return { kind: "failed", reason: `codex: logged in, but no auth.json appeared in ${home}` };
|
|
393
|
+
}
|
|
394
|
+
const secret = fs2.readFileSync(file, "utf8").trim();
|
|
395
|
+
if (refreshTokenOf(secret) === null) {
|
|
396
|
+
return {
|
|
397
|
+
kind: "failed",
|
|
398
|
+
reason: "codex: the login wrote no ChatGPT session (an API key login has nothing to refresh)"
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (input.personalAuthJson !== null && sameCodexLogin(secret, input.personalAuthJson)) {
|
|
402
|
+
return {
|
|
403
|
+
kind: "failed",
|
|
404
|
+
reason: "codex: that is the same login this machine's Codex holds, so both sides would race on refresh \u2014 connect it deliberately with --use-my-login"
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return { kind: "grant", secret };
|
|
408
|
+
} finally {
|
|
409
|
+
fs2.rmSync(home, { recursive: true, force: true });
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// src/doctor-bundle-collectors.ts
|
|
414
|
+
import * as fs4 from "node:fs";
|
|
415
|
+
import * as os3 from "node:os";
|
|
416
|
+
import * as path4 from "node:path";
|
|
308
417
|
|
|
309
418
|
// src/server-runtime.ts
|
|
310
419
|
import { spawn } from "node:child_process";
|
|
311
|
-
import * as
|
|
312
|
-
import * as
|
|
420
|
+
import * as fs3 from "node:fs";
|
|
421
|
+
import * as path3 from "node:path";
|
|
313
422
|
var serverProcessDeadlines = {
|
|
314
423
|
ordinary: 3e4,
|
|
315
424
|
pull: 10 * 6e4,
|
|
@@ -355,7 +464,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
355
464
|
return new Promise((resolve7) => {
|
|
356
465
|
let fd;
|
|
357
466
|
try {
|
|
358
|
-
if (options.stdoutFile !== void 0) fd =
|
|
467
|
+
if (options.stdoutFile !== void 0) fd = fs3.openSync(options.stdoutFile, "wx", 384);
|
|
359
468
|
const child = spawn(command, [...args], {
|
|
360
469
|
env,
|
|
361
470
|
detached: process.platform !== "win32",
|
|
@@ -406,12 +515,12 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
406
515
|
child.once("close", (status) => {
|
|
407
516
|
globalThis.clearTimeout(timer);
|
|
408
517
|
try {
|
|
409
|
-
if (fd !== void 0)
|
|
518
|
+
if (fd !== void 0) fs3.fsyncSync(fd);
|
|
410
519
|
} catch {
|
|
411
520
|
failure2 ??= "Could not durably write process output.";
|
|
412
521
|
} finally {
|
|
413
522
|
try {
|
|
414
|
-
if (fd !== void 0)
|
|
523
|
+
if (fd !== void 0) fs3.closeSync(fd);
|
|
415
524
|
} catch {
|
|
416
525
|
failure2 ??= "Could not close process output file.";
|
|
417
526
|
}
|
|
@@ -425,7 +534,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
425
534
|
});
|
|
426
535
|
} catch (cause) {
|
|
427
536
|
try {
|
|
428
|
-
if (fd !== void 0)
|
|
537
|
+
if (fd !== void 0) fs3.closeSync(fd);
|
|
429
538
|
} catch {
|
|
430
539
|
}
|
|
431
540
|
resolve7({
|
|
@@ -446,9 +555,9 @@ var serverComposeArgs = (installation, command) => [
|
|
|
446
555
|
"--project-directory",
|
|
447
556
|
installation.directory,
|
|
448
557
|
"--env-file",
|
|
449
|
-
|
|
558
|
+
path3.join(installation.directory, "server.env"),
|
|
450
559
|
"-f",
|
|
451
|
-
|
|
560
|
+
path3.join(installation.directory, "compose.yaml"),
|
|
452
561
|
...command
|
|
453
562
|
];
|
|
454
563
|
|
|
@@ -494,11 +603,11 @@ var mapLimit = async (items, limit, work) => {
|
|
|
494
603
|
return results;
|
|
495
604
|
};
|
|
496
605
|
var pathOf = (command, env = process.env) => {
|
|
497
|
-
for (const directory of (env["PATH"] ?? "").split(
|
|
606
|
+
for (const directory of (env["PATH"] ?? "").split(path4.delimiter)) {
|
|
498
607
|
if (directory === "") continue;
|
|
499
|
-
const candidate =
|
|
608
|
+
const candidate = path4.join(directory, command);
|
|
500
609
|
try {
|
|
501
|
-
|
|
610
|
+
fs4.accessSync(candidate, fs4.constants.X_OK);
|
|
502
611
|
return candidate;
|
|
503
612
|
} catch {
|
|
504
613
|
}
|
|
@@ -516,9 +625,9 @@ var cliCollector = (deps) => ({
|
|
|
516
625
|
node: process.versions.node,
|
|
517
626
|
platform: process.platform,
|
|
518
627
|
arch: process.arch,
|
|
519
|
-
osType:
|
|
520
|
-
osRelease:
|
|
521
|
-
hostname:
|
|
628
|
+
osType: os3.type(),
|
|
629
|
+
osRelease: os3.release(),
|
|
630
|
+
hostname: os3.hostname(),
|
|
522
631
|
term: deps.env["TERM"] ?? null,
|
|
523
632
|
shell: deps.env["SHELL"] ?? null,
|
|
524
633
|
lang: deps.env["LANG"] ?? null,
|
|
@@ -988,8 +1097,8 @@ var bundleCollectors = (deps) => {
|
|
|
988
1097
|
};
|
|
989
1098
|
|
|
990
1099
|
// src/doctor-bundle.ts
|
|
991
|
-
import * as
|
|
992
|
-
import * as
|
|
1100
|
+
import * as fs5 from "node:fs";
|
|
1101
|
+
import * as path5 from "node:path";
|
|
993
1102
|
import { gzipSync } from "node:zlib";
|
|
994
1103
|
|
|
995
1104
|
// src/help.ts
|
|
@@ -1038,19 +1147,20 @@ var COMMANDS = [
|
|
|
1038
1147
|
summary: "send this machine's provider credential to the platform",
|
|
1039
1148
|
synopsis: ["<claude|codex|github> [--use-my-login] [--from-stdin] [--remove]"],
|
|
1040
1149
|
description: [
|
|
1041
|
-
"Sessions run on the platform, so the platform needs your provider login.
|
|
1042
|
-
"Claude
|
|
1150
|
+
"Sessions run on the platform, so the platform needs your provider login. It is stored under your own user and used only for your work. Nothing is shared with other users.",
|
|
1151
|
+
"Claude and Codex get a login of Mend's own. Both rotate their refresh token, so two copies of one login fight and the one that refreshes second is signed out, and Mend's server refreshes on a schedule. So this logs in through the provider's own flow and your own login stays as it is: Claude against a directory Mend keeps, Codex with a device code, sent and not kept here. Run it again whenever Mend says a login needs reconnecting. GitHub sends gh's token."
|
|
1043
1152
|
],
|
|
1044
1153
|
options: [
|
|
1045
1154
|
{
|
|
1046
1155
|
flag: "--use-my-login",
|
|
1047
|
-
text: "claude: send the login this machine already uses; both sides then share
|
|
1156
|
+
text: "claude, codex: send the login this machine already uses; both sides then share it"
|
|
1048
1157
|
},
|
|
1049
1158
|
{ flag: "--from-stdin", text: "paste a credential instead of reading the provider's file" },
|
|
1050
1159
|
{ flag: "--remove", text: "disconnect the provider" }
|
|
1051
1160
|
],
|
|
1052
1161
|
examples: [
|
|
1053
1162
|
{ command: "mend connect claude", text: "a Claude login of Mend's own, through the browser" },
|
|
1163
|
+
{ command: "mend connect codex", text: "a Codex login of Mend's own, with a device code" },
|
|
1054
1164
|
{ command: "mend connect github --remove", text: "" }
|
|
1055
1165
|
],
|
|
1056
1166
|
see: ["accounts"]
|
|
@@ -2429,9 +2539,9 @@ var collectBundle = async (collectors) => {
|
|
|
2429
2539
|
};
|
|
2430
2540
|
var bundleFileName = (now) => `mend-bundle-${now.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-")}.tgz`;
|
|
2431
2541
|
var writeBundle = (files, outPath, now) => {
|
|
2432
|
-
const directory =
|
|
2433
|
-
|
|
2434
|
-
const root =
|
|
2542
|
+
const directory = path5.dirname(outPath);
|
|
2543
|
+
fs5.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
2544
|
+
const root = path5.basename(outPath).replace(/\.(?:tgz|tar\.gz)$/, "");
|
|
2435
2545
|
const entries = files.map((file) => ({
|
|
2436
2546
|
path: file.path,
|
|
2437
2547
|
content: Buffer.from(file.content, "utf8")
|
|
@@ -2440,8 +2550,8 @@ var writeBundle = (files, outPath, now) => {
|
|
|
2440
2550
|
entries.map((entry) => ({ path: `${root}/${entry.path}`, content: entry.content })),
|
|
2441
2551
|
now
|
|
2442
2552
|
);
|
|
2443
|
-
|
|
2444
|
-
|
|
2553
|
+
fs5.writeFileSync(outPath, bytes, { mode: 384 });
|
|
2554
|
+
fs5.chmodSync(outPath, 384);
|
|
2445
2555
|
return {
|
|
2446
2556
|
path: outPath,
|
|
2447
2557
|
bytes: bytes.length,
|
|
@@ -2502,7 +2612,7 @@ var doctorBundleCommand = async (args, deps) => {
|
|
|
2502
2612
|
return;
|
|
2503
2613
|
}
|
|
2504
2614
|
const now = deps.now();
|
|
2505
|
-
const outPath =
|
|
2615
|
+
const outPath = path5.resolve(parsed.args.out ?? path5.join(deps.defaultDir, bundleFileName(now)));
|
|
2506
2616
|
deps.warn("collecting \xB7 this can take a minute");
|
|
2507
2617
|
const files = await collectBundle(deps.collectors(parsed.args.tail));
|
|
2508
2618
|
const written = writeBundle(files, outPath, now);
|
|
@@ -2510,14 +2620,14 @@ var doctorBundleCommand = async (args, deps) => {
|
|
|
2510
2620
|
};
|
|
2511
2621
|
|
|
2512
2622
|
// src/doctor.ts
|
|
2513
|
-
import * as
|
|
2514
|
-
import * as
|
|
2623
|
+
import * as fs7 from "node:fs";
|
|
2624
|
+
import * as path7 from "node:path";
|
|
2515
2625
|
|
|
2516
2626
|
// src/docker-shutdown.ts
|
|
2517
|
-
import { spawnSync as
|
|
2518
|
-
import * as
|
|
2519
|
-
import * as
|
|
2520
|
-
import * as
|
|
2627
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2628
|
+
import * as fs6 from "node:fs";
|
|
2629
|
+
import * as os4 from "node:os";
|
|
2630
|
+
import * as path6 from "node:path";
|
|
2521
2631
|
var DOCKER_CAPTURE_STOP_GRACE_SECONDS = 3600;
|
|
2522
2632
|
var DOCKERD_DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 15;
|
|
2523
2633
|
var flagValue = (argv, name) => {
|
|
@@ -2542,12 +2652,12 @@ var daemonKindOf = (info) => {
|
|
|
2542
2652
|
var defaultConfigPath = (facts, daemon) => {
|
|
2543
2653
|
switch (daemon) {
|
|
2544
2654
|
case "desktop":
|
|
2545
|
-
return
|
|
2655
|
+
return path6.join(facts.home, ".docker", "daemon.json");
|
|
2546
2656
|
case "orbstack":
|
|
2547
|
-
return
|
|
2657
|
+
return path6.join(facts.home, ".orbstack", "config", "docker.json");
|
|
2548
2658
|
case "rootless":
|
|
2549
|
-
return
|
|
2550
|
-
facts.xdgConfigHome ??
|
|
2659
|
+
return path6.join(
|
|
2660
|
+
facts.xdgConfigHome ?? path6.join(facts.home, ".config"),
|
|
2551
2661
|
"docker",
|
|
2552
2662
|
"daemon.json"
|
|
2553
2663
|
);
|
|
@@ -2676,7 +2786,7 @@ var parseDockerInfo = (stdout) => {
|
|
|
2676
2786
|
};
|
|
2677
2787
|
var readHostFile = (file) => {
|
|
2678
2788
|
try {
|
|
2679
|
-
return { kind: "read", text:
|
|
2789
|
+
return { kind: "read", text: fs6.readFileSync(file, "utf8") };
|
|
2680
2790
|
} catch (cause) {
|
|
2681
2791
|
return cause instanceof Error && "code" in cause && cause.code === "ENOENT" ? { kind: "absent" } : { kind: "unreadable" };
|
|
2682
2792
|
}
|
|
@@ -2684,21 +2794,21 @@ var readHostFile = (file) => {
|
|
|
2684
2794
|
var hostProcView = {
|
|
2685
2795
|
list: (dir) => {
|
|
2686
2796
|
try {
|
|
2687
|
-
return
|
|
2797
|
+
return fs6.readdirSync(dir);
|
|
2688
2798
|
} catch {
|
|
2689
2799
|
return null;
|
|
2690
2800
|
}
|
|
2691
2801
|
},
|
|
2692
2802
|
read: (file) => {
|
|
2693
2803
|
try {
|
|
2694
|
-
return
|
|
2804
|
+
return fs6.readFileSync(file, "utf8");
|
|
2695
2805
|
} catch {
|
|
2696
2806
|
return null;
|
|
2697
2807
|
}
|
|
2698
2808
|
},
|
|
2699
2809
|
link: (file) => {
|
|
2700
2810
|
try {
|
|
2701
|
-
return
|
|
2811
|
+
return fs6.readlinkSync(file);
|
|
2702
2812
|
} catch {
|
|
2703
2813
|
return null;
|
|
2704
2814
|
}
|
|
@@ -2738,7 +2848,7 @@ var observeDockerd = (proc, host) => {
|
|
|
2738
2848
|
const cmdline = proc.read(`/proc/${entry}/cmdline`);
|
|
2739
2849
|
if (cmdline === null) continue;
|
|
2740
2850
|
const argv = cmdline.split("\0").filter((arg) => arg !== "");
|
|
2741
|
-
if (
|
|
2851
|
+
if (path6.basename(argv[0] ?? "") === "dockerd") daemons.push({ pid: entry, argv });
|
|
2742
2852
|
}
|
|
2743
2853
|
if (daemons.length === 0) return { kind: "none" };
|
|
2744
2854
|
const socket = unixSocketOf(host);
|
|
@@ -2798,7 +2908,7 @@ var observeDockerd = (proc, host) => {
|
|
|
2798
2908
|
var hostDockerEndpoint = () => {
|
|
2799
2909
|
const fromEnv = process.env["DOCKER_HOST"];
|
|
2800
2910
|
if (fromEnv !== void 0 && fromEnv.trim() !== "") return fromEnv.trim();
|
|
2801
|
-
const context =
|
|
2911
|
+
const context = spawnSync3(
|
|
2802
2912
|
"docker",
|
|
2803
2913
|
["context", "inspect", "--format", "{{.Endpoints.docker.Host}}"],
|
|
2804
2914
|
{
|
|
@@ -2824,11 +2934,11 @@ var hostDockerDaemonFacts = (infoStdout) => ({
|
|
|
2824
2934
|
info: infoStdout === null ? null : parseDockerInfo(infoStdout),
|
|
2825
2935
|
...dockerdFactsOf(observeDockerd(hostProcView, hostDockerEndpoint())),
|
|
2826
2936
|
readFile: readHostFile,
|
|
2827
|
-
home:
|
|
2937
|
+
home: os4.homedir(),
|
|
2828
2938
|
xdgConfigHome: process.env["XDG_CONFIG_HOME"] ?? null
|
|
2829
2939
|
});
|
|
2830
2940
|
var observeHostShutdownTimeout = () => {
|
|
2831
|
-
const info =
|
|
2941
|
+
const info = spawnSync3("docker", ["info", "--format", "{{json .}}"], {
|
|
2832
2942
|
encoding: "utf8",
|
|
2833
2943
|
timeout: 3e3,
|
|
2834
2944
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -2987,7 +3097,8 @@ var runChecks = async (config, probes) => {
|
|
|
2987
3097
|
} : {
|
|
2988
3098
|
label: provider,
|
|
2989
3099
|
state: "todo",
|
|
2990
|
-
|
|
3100
|
+
// A refused refresh ends the login (docs/adr/0008): say what happened, not the enum.
|
|
3101
|
+
detail: account.status === "invalid" ? "reconnect needed \xB7 the provider refused the login" : account.status,
|
|
2991
3102
|
fix: `mend connect ${provider}`
|
|
2992
3103
|
}
|
|
2993
3104
|
);
|
|
@@ -3057,10 +3168,10 @@ var runChecks = async (config, probes) => {
|
|
|
3057
3168
|
return checks;
|
|
3058
3169
|
};
|
|
3059
3170
|
var onPath = (command) => {
|
|
3060
|
-
for (const directory of (process.env["PATH"] ?? "").split(
|
|
3171
|
+
for (const directory of (process.env["PATH"] ?? "").split(path7.delimiter)) {
|
|
3061
3172
|
if (directory === "") continue;
|
|
3062
3173
|
try {
|
|
3063
|
-
|
|
3174
|
+
fs7.accessSync(path7.join(directory, command), fs7.constants.X_OK);
|
|
3064
3175
|
return true;
|
|
3065
3176
|
} catch {
|
|
3066
3177
|
}
|
|
@@ -3085,8 +3196,8 @@ var doctorCommand = async (config, localCredential2, claudeGrant2) => {
|
|
|
3085
3196
|
};
|
|
3086
3197
|
|
|
3087
3198
|
// src/dotfiles.ts
|
|
3088
|
-
import * as
|
|
3089
|
-
import * as
|
|
3199
|
+
import * as fs8 from "node:fs";
|
|
3200
|
+
import * as path8 from "node:path";
|
|
3090
3201
|
var DOTFILE_CANDIDATES = [
|
|
3091
3202
|
{
|
|
3092
3203
|
group: "shell",
|
|
@@ -3129,7 +3240,7 @@ var DOTFILE_CANDIDATES = [
|
|
|
3129
3240
|
var MAX_FILE_BYTES = 1024 * 1024;
|
|
3130
3241
|
var statFile = (home, relative4) => {
|
|
3131
3242
|
try {
|
|
3132
|
-
const stat =
|
|
3243
|
+
const stat = fs8.statSync(path8.join(home, relative4));
|
|
3133
3244
|
return stat.isFile() ? stat : null;
|
|
3134
3245
|
} catch {
|
|
3135
3246
|
return null;
|
|
@@ -3142,9 +3253,9 @@ var scanDotfileCandidates = (home) => DOTFILE_CANDIDATES.flatMap(
|
|
|
3142
3253
|
})
|
|
3143
3254
|
);
|
|
3144
3255
|
var homeRelativePath = (home, requested) => {
|
|
3145
|
-
const relative4 =
|
|
3146
|
-
const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${
|
|
3147
|
-
return outside ? null : relative4.split(
|
|
3256
|
+
const relative4 = path8.relative(home, path8.resolve(home, requested));
|
|
3257
|
+
const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${path8.sep}`) || path8.isAbsolute(relative4);
|
|
3258
|
+
return outside ? null : relative4.split(path8.sep).join("/");
|
|
3148
3259
|
};
|
|
3149
3260
|
var readSyncFiles = (home, paths_) => {
|
|
3150
3261
|
const files = [];
|
|
@@ -3164,7 +3275,7 @@ var readSyncFiles = (home, paths_) => {
|
|
|
3164
3275
|
}
|
|
3165
3276
|
files.push({
|
|
3166
3277
|
path: relative4,
|
|
3167
|
-
contentsBase64:
|
|
3278
|
+
contentsBase64: fs8.readFileSync(path8.join(home, relative4)).toString("base64"),
|
|
3168
3279
|
// eslint-disable-next-line no-bitwise -- permission bits are the point
|
|
3169
3280
|
mode: (stat.mode & 511).toString(8)
|
|
3170
3281
|
});
|
|
@@ -3263,10 +3374,10 @@ var parseGitAuthorArgs = (args) => {
|
|
|
3263
3374
|
var gitAuthorLine = (author) => `${author.name} <${author.email}> \xB7 ${author.source === "setting" ? "your setting" : "your account's name and email"}`;
|
|
3264
3375
|
|
|
3265
3376
|
// src/landing.ts
|
|
3266
|
-
import { spawnSync as
|
|
3267
|
-
import * as
|
|
3268
|
-
import * as
|
|
3269
|
-
import * as
|
|
3377
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
3378
|
+
import * as fs9 from "node:fs";
|
|
3379
|
+
import * as os5 from "node:os";
|
|
3380
|
+
import * as path9 from "node:path";
|
|
3270
3381
|
var paint = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
|
|
3271
3382
|
var dim = paint("2");
|
|
3272
3383
|
var green = paint("32");
|
|
@@ -3481,7 +3592,7 @@ var landCommand = async (api2, args) => {
|
|
|
3481
3592
|
if (!landingSucceeded(report)) process.exitCode = 1;
|
|
3482
3593
|
};
|
|
3483
3594
|
var git = (cwd, args) => {
|
|
3484
|
-
const result =
|
|
3595
|
+
const result = spawnSync4("git", args, { cwd, encoding: "utf8" });
|
|
3485
3596
|
return {
|
|
3486
3597
|
status: result.status ?? 1,
|
|
3487
3598
|
stdout: result.stdout ?? "",
|
|
@@ -3504,9 +3615,9 @@ var gitRemotes = (cwd) => {
|
|
|
3504
3615
|
var remoteForOrigin = (remotes, originUrl, cwd) => {
|
|
3505
3616
|
if (originUrl === null) return null;
|
|
3506
3617
|
const origin = normalizeRemoteUrl(originUrl);
|
|
3507
|
-
const originPath = origin === null ?
|
|
3618
|
+
const originPath = origin === null ? path9.resolve(originUrl.replace(/^file:\/\//, "")) : null;
|
|
3508
3619
|
return remotes.find(
|
|
3509
|
-
(remote) => origin === null ?
|
|
3620
|
+
(remote) => origin === null ? path9.resolve(cwd, remote.url.replace(/^file:\/\//, "")) === originPath : normalizeRemoteUrl(remote.url) === origin
|
|
3510
3621
|
) ?? null;
|
|
3511
3622
|
};
|
|
3512
3623
|
var LOG_LINES = 10;
|
|
@@ -3529,10 +3640,10 @@ var fetchBundle = (cwd, bundle) => {
|
|
|
3529
3640
|
const before = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
|
|
3530
3641
|
const previous = before.status === 0 ? before.stdout.trim() : null;
|
|
3531
3642
|
if (previous !== bundle.tip) {
|
|
3532
|
-
const dir =
|
|
3643
|
+
const dir = fs9.mkdtempSync(path9.join(os5.tmpdir(), "mend-pull-"));
|
|
3533
3644
|
try {
|
|
3534
|
-
const file =
|
|
3535
|
-
|
|
3645
|
+
const file = path9.join(dir, "change.bundle");
|
|
3646
|
+
fs9.writeFileSync(file, bundle.bytes, { mode: 384 });
|
|
3536
3647
|
const fetched = git(cwd, [
|
|
3537
3648
|
"fetch",
|
|
3538
3649
|
"--no-tags",
|
|
@@ -3542,7 +3653,7 @@ var fetchBundle = (cwd, bundle) => {
|
|
|
3542
3653
|
]);
|
|
3543
3654
|
if (fetched.status !== 0) return refusedWith(gitWords(fetched));
|
|
3544
3655
|
} finally {
|
|
3545
|
-
|
|
3656
|
+
fs9.rmSync(dir, { recursive: true, force: true });
|
|
3546
3657
|
}
|
|
3547
3658
|
}
|
|
3548
3659
|
const after = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]).stdout.trim();
|
|
@@ -3733,7 +3844,7 @@ var followStart = async (options) => {
|
|
|
3733
3844
|
|
|
3734
3845
|
// src/login.ts
|
|
3735
3846
|
import { spawn as spawn2 } from "node:child_process";
|
|
3736
|
-
import * as
|
|
3847
|
+
import * as os6 from "node:os";
|
|
3737
3848
|
|
|
3738
3849
|
// src/pair.ts
|
|
3739
3850
|
var paint2 = (code) => (text) => process.stdout.isTTY === true ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
@@ -3925,7 +4036,7 @@ var resolveServerUrl = async (args, deps) => {
|
|
|
3925
4036
|
};
|
|
3926
4037
|
var loginCommand = async (args, deps) => {
|
|
3927
4038
|
const base = await resolveServerUrl(args, deps);
|
|
3928
|
-
const started = await post(`${base}/api/cli/auth`, { name:
|
|
4039
|
+
const started = await post(`${base}/api/cli/auth`, { name: os6.hostname() });
|
|
3929
4040
|
if (started.status === 429) {
|
|
3930
4041
|
return fail3(
|
|
3931
4042
|
`the server asked for a pause \u2014 try again in ${retryAfterSecondsOf(started.json)}s`
|
|
@@ -3976,8 +4087,8 @@ var loginCommand = async (args, deps) => {
|
|
|
3976
4087
|
};
|
|
3977
4088
|
|
|
3978
4089
|
// src/organization.ts
|
|
3979
|
-
import * as
|
|
3980
|
-
import * as
|
|
4090
|
+
import * as fs10 from "node:fs";
|
|
4091
|
+
import * as path10 from "node:path";
|
|
3981
4092
|
var paint4 = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
|
|
3982
4093
|
var dim4 = paint4("2");
|
|
3983
4094
|
var green4 = paint4("32");
|
|
@@ -4041,7 +4152,7 @@ var scanFolder = (dir) => {
|
|
|
4041
4152
|
const files = [];
|
|
4042
4153
|
const skipped = [];
|
|
4043
4154
|
const walk = (relative4) => {
|
|
4044
|
-
const entries =
|
|
4155
|
+
const entries = fs10.readdirSync(path10.join(dir, relative4), { withFileTypes: true }).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
4045
4156
|
for (const entry of entries) {
|
|
4046
4157
|
const child = relative4 === "" ? entry.name : `${relative4}/${entry.name}`;
|
|
4047
4158
|
if (entry.isSymbolicLink()) {
|
|
@@ -4050,7 +4161,7 @@ var scanFolder = (dir) => {
|
|
|
4050
4161
|
if (SKIPPED_DIRECTORIES.has(entry.name)) skipped.push({ path: child, reason: "skipped" });
|
|
4051
4162
|
else walk(child);
|
|
4052
4163
|
} else if (entry.isFile()) {
|
|
4053
|
-
const size =
|
|
4164
|
+
const size = fs10.statSync(path10.join(dir, child)).size;
|
|
4054
4165
|
if (size > FOLDER_FILE_BYTES) skipped.push({ path: child, reason: "over 1 MiB" });
|
|
4055
4166
|
else files.push({ path: child, size });
|
|
4056
4167
|
}
|
|
@@ -4103,7 +4214,7 @@ var folderCommand = async (api2, args) => {
|
|
|
4103
4214
|
}
|
|
4104
4215
|
case "push": {
|
|
4105
4216
|
if (dir === void 0) return fail4("usage: mend folder push <name> <dir> [--replace]");
|
|
4106
|
-
if (!
|
|
4217
|
+
if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
|
|
4107
4218
|
return fail4(`${dir} is not a directory`);
|
|
4108
4219
|
}
|
|
4109
4220
|
const folder = await folderNamed(api2, name);
|
|
@@ -4118,7 +4229,7 @@ var folderCommand = async (api2, args) => {
|
|
|
4118
4229
|
files: batch.map((file) => ({
|
|
4119
4230
|
path: file.path,
|
|
4120
4231
|
// Read one batch at a time, so a large tree never sits in memory at once.
|
|
4121
|
-
contentsBase64:
|
|
4232
|
+
contentsBase64: fs10.readFileSync(path10.join(dir, file.path)).toString("base64")
|
|
4122
4233
|
})),
|
|
4123
4234
|
// --replace empties the folder with the first batch; the rest add to it.
|
|
4124
4235
|
merge: !(replace && index === 0)
|
|
@@ -4302,8 +4413,8 @@ var operatorCommand = async (api2, tryApi, baseUrl, args) => {
|
|
|
4302
4413
|
import { createHmac, randomBytes } from "node:crypto";
|
|
4303
4414
|
import { readFile } from "node:fs/promises";
|
|
4304
4415
|
import * as net2 from "node:net";
|
|
4305
|
-
import * as
|
|
4306
|
-
import * as
|
|
4416
|
+
import * as os8 from "node:os";
|
|
4417
|
+
import * as path12 from "node:path";
|
|
4307
4418
|
|
|
4308
4419
|
// src/server-docker-volumes.ts
|
|
4309
4420
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -4521,9 +4632,9 @@ var verifyServerDockerVolumes = (runtime, input) => ownership(runtime, input, "v
|
|
|
4521
4632
|
|
|
4522
4633
|
// src/server-store.ts
|
|
4523
4634
|
import { randomUUID } from "node:crypto";
|
|
4524
|
-
import * as
|
|
4525
|
-
import * as
|
|
4526
|
-
import * as
|
|
4635
|
+
import * as fs11 from "node:fs";
|
|
4636
|
+
import * as os7 from "node:os";
|
|
4637
|
+
import * as path11 from "node:path";
|
|
4527
4638
|
var ServerStoreError = class extends Error {
|
|
4528
4639
|
_tag = "ServerStoreError";
|
|
4529
4640
|
};
|
|
@@ -4544,28 +4655,28 @@ var attempt = (operation) => {
|
|
|
4544
4655
|
var hasCode = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
4545
4656
|
var readOptional = (file) => {
|
|
4546
4657
|
try {
|
|
4547
|
-
return
|
|
4658
|
+
return fs11.readFileSync(file, "utf8");
|
|
4548
4659
|
} catch (cause) {
|
|
4549
4660
|
if (hasCode(cause, "ENOENT")) return null;
|
|
4550
4661
|
throw cause;
|
|
4551
4662
|
}
|
|
4552
4663
|
};
|
|
4553
4664
|
var syncDirectory = (directory) => {
|
|
4554
|
-
const fd =
|
|
4665
|
+
const fd = fs11.openSync(directory, "r");
|
|
4555
4666
|
try {
|
|
4556
|
-
|
|
4667
|
+
fs11.fsyncSync(fd);
|
|
4557
4668
|
} finally {
|
|
4558
|
-
|
|
4669
|
+
fs11.closeSync(fd);
|
|
4559
4670
|
}
|
|
4560
4671
|
};
|
|
4561
4672
|
var writeDurable = (file, content, mode = 384) => {
|
|
4562
|
-
const fd =
|
|
4673
|
+
const fd = fs11.openSync(file, "wx", mode);
|
|
4563
4674
|
try {
|
|
4564
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4675
|
+
fs11.writeFileSync(fd, content, "utf8");
|
|
4676
|
+
fs11.fchmodSync(fd, mode);
|
|
4677
|
+
fs11.fsyncSync(fd);
|
|
4567
4678
|
} finally {
|
|
4568
|
-
|
|
4679
|
+
fs11.closeSync(fd);
|
|
4569
4680
|
}
|
|
4570
4681
|
};
|
|
4571
4682
|
var fileKeys = ["identity", "config", "env", "compose", "postgresInit"];
|
|
@@ -4585,11 +4696,11 @@ var parseLockOwner = (raw) => {
|
|
|
4585
4696
|
};
|
|
4586
4697
|
var describeOwner = (lockDir) => {
|
|
4587
4698
|
try {
|
|
4588
|
-
const raw = JSON.parse(
|
|
4699
|
+
const raw = JSON.parse(fs11.readFileSync(path11.join(lockDir, "owner.json"), "utf8"));
|
|
4589
4700
|
const owner = parseLockOwner(raw);
|
|
4590
4701
|
if (owner === null) return "owner metadata is invalid";
|
|
4591
4702
|
const label = `PID ${owner.pid} on ${owner.hostname}`;
|
|
4592
|
-
if (owner.hostname !==
|
|
4703
|
+
if (owner.hostname !== os7.hostname()) return label;
|
|
4593
4704
|
try {
|
|
4594
4705
|
process.kill(owner.pid, 0);
|
|
4595
4706
|
return `${label} is still live`;
|
|
@@ -4605,28 +4716,28 @@ var lockGuidance = (lockDir) => new ServerStoreError(
|
|
|
4605
4716
|
);
|
|
4606
4717
|
var acquireLock = (configDir, create) => {
|
|
4607
4718
|
if (create) {
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
} else if (!
|
|
4719
|
+
fs11.mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
4720
|
+
fs11.chmodSync(configDir, 448);
|
|
4721
|
+
} else if (!fs11.existsSync(configDir)) {
|
|
4611
4722
|
throw new ServerStoreError(
|
|
4612
4723
|
"No Mend server is configured. Run mend server setup explicitly to install one."
|
|
4613
4724
|
);
|
|
4614
4725
|
}
|
|
4615
|
-
const lockDir =
|
|
4726
|
+
const lockDir = path11.join(configDir, "server.lock");
|
|
4616
4727
|
try {
|
|
4617
|
-
|
|
4728
|
+
fs11.mkdirSync(lockDir, { mode: 448 });
|
|
4618
4729
|
} catch (cause) {
|
|
4619
4730
|
if (hasCode(cause, "EEXIST")) throw lockGuidance(lockDir);
|
|
4620
4731
|
throw cause;
|
|
4621
4732
|
}
|
|
4622
|
-
const owned =
|
|
4623
|
-
const ownerFile =
|
|
4624
|
-
const owner = `${JSON.stringify({ pid: process.pid, hostname:
|
|
4733
|
+
const owned = fs11.statSync(lockDir);
|
|
4734
|
+
const ownerFile = path11.join(lockDir, "owner.json");
|
|
4735
|
+
const owner = `${JSON.stringify({ pid: process.pid, hostname: os7.hostname(), token: randomUUID() })}
|
|
4625
4736
|
`;
|
|
4626
4737
|
let open = true;
|
|
4627
4738
|
const assertOwned = () => {
|
|
4628
|
-
const current =
|
|
4629
|
-
if (!open || current.dev !== owned.dev || current.ino !== owned.ino ||
|
|
4739
|
+
const current = fs11.statSync(lockDir);
|
|
4740
|
+
if (!open || current.dev !== owned.dev || current.ino !== owned.ino || fs11.readFileSync(ownerFile, "utf8") !== owner) {
|
|
4630
4741
|
throw new ServerStoreError(
|
|
4631
4742
|
"Server lock ownership was lost. Stop and check for another server command before retrying."
|
|
4632
4743
|
);
|
|
@@ -4642,8 +4753,8 @@ var acquireLock = (configDir, create) => {
|
|
|
4642
4753
|
release: () => {
|
|
4643
4754
|
const result = attempt(() => {
|
|
4644
4755
|
assertOwned();
|
|
4645
|
-
|
|
4646
|
-
|
|
4756
|
+
fs11.unlinkSync(ownerFile);
|
|
4757
|
+
fs11.rmdirSync(lockDir);
|
|
4647
4758
|
});
|
|
4648
4759
|
open = false;
|
|
4649
4760
|
return result;
|
|
@@ -4652,7 +4763,7 @@ var acquireLock = (configDir, create) => {
|
|
|
4652
4763
|
};
|
|
4653
4764
|
var readIdentity = (paths) => {
|
|
4654
4765
|
const identity = readOptional(paths.identity);
|
|
4655
|
-
if (identity === null && (
|
|
4766
|
+
if (identity === null && (fs11.existsSync(paths.active) || fs11.existsSync(paths.generations) && fs11.readdirSync(paths.generations).length > 0)) {
|
|
4656
4767
|
throw new ServerStoreError(
|
|
4657
4768
|
"Server identity.env is missing. Restore it from a retained generation or backup; refusing to generate replacement credentials."
|
|
4658
4769
|
);
|
|
@@ -4662,7 +4773,7 @@ var readIdentity = (paths) => {
|
|
|
4662
4773
|
var readActive = (paths) => {
|
|
4663
4774
|
let target;
|
|
4664
4775
|
try {
|
|
4665
|
-
target =
|
|
4776
|
+
target = fs11.readlinkSync(paths.active);
|
|
4666
4777
|
} catch (cause) {
|
|
4667
4778
|
if (hasCode(cause, "ENOENT")) return null;
|
|
4668
4779
|
throw cause;
|
|
@@ -4672,13 +4783,13 @@ var readActive = (paths) => {
|
|
|
4672
4783
|
"Server active pointer is invalid. Restore the symlink to a retained generation before retrying."
|
|
4673
4784
|
);
|
|
4674
4785
|
}
|
|
4675
|
-
const directory =
|
|
4786
|
+
const directory = path11.join(paths.configDir, target);
|
|
4676
4787
|
const files = {
|
|
4677
|
-
identity:
|
|
4678
|
-
config:
|
|
4679
|
-
env:
|
|
4680
|
-
compose:
|
|
4681
|
-
postgresInit:
|
|
4788
|
+
identity: fs11.readFileSync(path11.join(directory, fileNames.identity), "utf8"),
|
|
4789
|
+
config: fs11.readFileSync(path11.join(directory, fileNames.config), "utf8"),
|
|
4790
|
+
env: fs11.readFileSync(path11.join(directory, fileNames.env), "utf8"),
|
|
4791
|
+
compose: fs11.readFileSync(path11.join(directory, fileNames.compose), "utf8"),
|
|
4792
|
+
postgresInit: fs11.readFileSync(path11.join(directory, fileNames.postgresInit), "utf8")
|
|
4682
4793
|
};
|
|
4683
4794
|
if (readIdentity(paths) !== files.identity) {
|
|
4684
4795
|
throw new ServerStoreError(
|
|
@@ -4688,20 +4799,20 @@ var readActive = (paths) => {
|
|
|
4688
4799
|
return { directory, files };
|
|
4689
4800
|
};
|
|
4690
4801
|
var publishIdentity = (paths, identity) => {
|
|
4691
|
-
const temporary =
|
|
4802
|
+
const temporary = path11.join(paths.configDir, `.identity-${randomUUID()}`);
|
|
4692
4803
|
writeDurable(temporary, identity);
|
|
4693
|
-
|
|
4804
|
+
fs11.linkSync(temporary, paths.identity);
|
|
4694
4805
|
syncDirectory(paths.configDir);
|
|
4695
|
-
|
|
4806
|
+
fs11.unlinkSync(temporary);
|
|
4696
4807
|
};
|
|
4697
4808
|
var prepareGeneration = (paths, files) => {
|
|
4698
|
-
|
|
4809
|
+
fs11.mkdirSync(paths.generations, { recursive: true, mode: 448 });
|
|
4699
4810
|
const generationName = `gen-${randomUUID()}`;
|
|
4700
|
-
const directory =
|
|
4701
|
-
|
|
4811
|
+
const directory = path11.join(paths.generations, generationName);
|
|
4812
|
+
fs11.mkdirSync(directory, { mode: 448 });
|
|
4702
4813
|
for (const key of fileKeys) {
|
|
4703
4814
|
writeDurable(
|
|
4704
|
-
|
|
4815
|
+
path11.join(directory, fileNames[key]),
|
|
4705
4816
|
files[key],
|
|
4706
4817
|
key === "postgresInit" ? 493 : 384
|
|
4707
4818
|
);
|
|
@@ -4712,18 +4823,18 @@ var prepareGeneration = (paths, files) => {
|
|
|
4712
4823
|
return { directory, files };
|
|
4713
4824
|
};
|
|
4714
4825
|
var activateGeneration = (paths, generation) => {
|
|
4715
|
-
const relative4 =
|
|
4716
|
-
if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !==
|
|
4826
|
+
const relative4 = path11.relative(paths.configDir, generation.directory);
|
|
4827
|
+
if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !== fs11.readFileSync(path11.join(generation.directory, "identity.env"), "utf8")) {
|
|
4717
4828
|
throw new ServerStoreError("Cannot activate a generation outside this installation identity.");
|
|
4718
4829
|
}
|
|
4719
4830
|
for (const key of fileKeys) {
|
|
4720
|
-
if (
|
|
4831
|
+
if (fs11.readFileSync(path11.join(generation.directory, fileNames[key]), "utf8") !== generation.files[key]) {
|
|
4721
4832
|
throw new ServerStoreError("Cannot activate an incomplete or changed server generation.");
|
|
4722
4833
|
}
|
|
4723
4834
|
}
|
|
4724
|
-
const pointer =
|
|
4725
|
-
|
|
4726
|
-
|
|
4835
|
+
const pointer = path11.join(paths.configDir, `.active-${randomUUID()}`);
|
|
4836
|
+
fs11.symlinkSync(relative4, pointer);
|
|
4837
|
+
fs11.renameSync(pointer, paths.active);
|
|
4727
4838
|
syncDirectory(paths.configDir);
|
|
4728
4839
|
};
|
|
4729
4840
|
var prepareFiles = (paths, files) => {
|
|
@@ -4732,7 +4843,7 @@ var prepareFiles = (paths, files) => {
|
|
|
4732
4843
|
throw new ServerStoreError("Server identity already exists; refusing to replace credentials.");
|
|
4733
4844
|
}
|
|
4734
4845
|
const active = readActive(paths);
|
|
4735
|
-
if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (
|
|
4846
|
+
if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (fs11.statSync(path11.join(active.directory, fileNames.postgresInit)).mode & 4095) === 493)
|
|
4736
4847
|
return active;
|
|
4737
4848
|
if (identity === null) publishIdentity(paths, files.identity);
|
|
4738
4849
|
return prepareGeneration(paths, files);
|
|
@@ -4745,9 +4856,9 @@ var commitGeneration = (paths, files) => {
|
|
|
4745
4856
|
var createStore = (configDir, lock) => {
|
|
4746
4857
|
const paths = {
|
|
4747
4858
|
configDir,
|
|
4748
|
-
identity:
|
|
4749
|
-
generations:
|
|
4750
|
-
active:
|
|
4859
|
+
identity: path11.join(configDir, "identity.env"),
|
|
4860
|
+
generations: path11.join(configDir, "generations"),
|
|
4861
|
+
active: path11.join(configDir, "active")
|
|
4751
4862
|
};
|
|
4752
4863
|
const whileOwned = (operation) => attempt(() => {
|
|
4753
4864
|
lock.assertOwned();
|
|
@@ -4760,13 +4871,13 @@ var createStore = (configDir, lock) => {
|
|
|
4760
4871
|
prepare: (files) => whileOwned(() => prepareFiles(paths, files)),
|
|
4761
4872
|
activate: (generation) => whileOwned(() => activateGeneration(paths, generation)),
|
|
4762
4873
|
createBackup: (previous, target) => whileOwned(() => {
|
|
4763
|
-
const backups =
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
const directory =
|
|
4767
|
-
|
|
4874
|
+
const backups = path11.join(configDir, "backups");
|
|
4875
|
+
fs11.mkdirSync(backups, { recursive: true, mode: 448 });
|
|
4876
|
+
fs11.chmodSync(backups, 448);
|
|
4877
|
+
const directory = path11.join(backups, `upgrade-${randomUUID()}`);
|
|
4878
|
+
fs11.mkdirSync(directory, { mode: 448 });
|
|
4768
4879
|
writeDurable(
|
|
4769
|
-
|
|
4880
|
+
path11.join(directory, "recovery.json"),
|
|
4770
4881
|
`${JSON.stringify(
|
|
4771
4882
|
{
|
|
4772
4883
|
previousGeneration: previous.directory,
|
|
@@ -4782,21 +4893,21 @@ var createStore = (configDir, lock) => {
|
|
|
4782
4893
|
syncDirectory(directory);
|
|
4783
4894
|
syncDirectory(backups);
|
|
4784
4895
|
syncDirectory(configDir);
|
|
4785
|
-
const partialFile =
|
|
4896
|
+
const partialFile = path11.join(directory, "database.sql.partial");
|
|
4786
4897
|
return {
|
|
4787
4898
|
directory,
|
|
4788
4899
|
partialFile,
|
|
4789
4900
|
complete: () => whileOwned(() => {
|
|
4790
|
-
if (
|
|
4901
|
+
if (fs11.statSync(partialFile).size === 0)
|
|
4791
4902
|
throw new ServerStoreError("Database backup is empty; refusing target startup.");
|
|
4792
|
-
const fd =
|
|
4903
|
+
const fd = fs11.openSync(partialFile, "r");
|
|
4793
4904
|
try {
|
|
4794
|
-
|
|
4795
|
-
|
|
4905
|
+
fs11.fchmodSync(fd, 384);
|
|
4906
|
+
fs11.fsyncSync(fd);
|
|
4796
4907
|
} finally {
|
|
4797
|
-
|
|
4908
|
+
fs11.closeSync(fd);
|
|
4798
4909
|
}
|
|
4799
|
-
|
|
4910
|
+
fs11.renameSync(partialFile, path11.join(directory, "database.sql"));
|
|
4800
4911
|
syncDirectory(directory);
|
|
4801
4912
|
})
|
|
4802
4913
|
};
|
|
@@ -4805,7 +4916,7 @@ var createStore = (configDir, lock) => {
|
|
|
4805
4916
|
};
|
|
4806
4917
|
var refuseFlatLayout = (configDir) => {
|
|
4807
4918
|
if (["server.json", "server.env", "compose.yaml", "postgres-init.sh"].some(
|
|
4808
|
-
(name) =>
|
|
4919
|
+
(name) => fs11.existsSync(path11.join(configDir, name))
|
|
4809
4920
|
)) {
|
|
4810
4921
|
throw new ServerStoreError(
|
|
4811
4922
|
"Unreleased flat server configuration found. Preserve its credentials and volumes; migrate it to the generation layout before retrying. Setup will not replace this identity."
|
|
@@ -4813,7 +4924,7 @@ var refuseFlatLayout = (configDir) => {
|
|
|
4813
4924
|
}
|
|
4814
4925
|
};
|
|
4815
4926
|
var withServerStore = async (directory, operation, options = {}) => {
|
|
4816
|
-
const configDir =
|
|
4927
|
+
const configDir = path11.resolve(directory);
|
|
4817
4928
|
let lock;
|
|
4818
4929
|
let result;
|
|
4819
4930
|
try {
|
|
@@ -4830,9 +4941,9 @@ var withServerStore = async (directory, operation, options = {}) => {
|
|
|
4830
4941
|
};
|
|
4831
4942
|
|
|
4832
4943
|
// src/version.ts
|
|
4833
|
-
import * as
|
|
4944
|
+
import * as fs12 from "node:fs";
|
|
4834
4945
|
var cliVersion = () => {
|
|
4835
|
-
const raw =
|
|
4946
|
+
const raw = fs12.readFileSync(new URL("../package.json", import.meta.url), "utf8");
|
|
4836
4947
|
const parsed = JSON.parse(raw);
|
|
4837
4948
|
if (typeof parsed === "object" && parsed !== null && "version" in parsed) {
|
|
4838
4949
|
const version = parsed.version;
|
|
@@ -5535,7 +5646,7 @@ var resolveServerVersion = async (runtime, options, existing) => {
|
|
|
5535
5646
|
return version;
|
|
5536
5647
|
};
|
|
5537
5648
|
var parseDockerSocketPath = (socket) => {
|
|
5538
|
-
if (!
|
|
5649
|
+
if (!path12.isAbsolute(socket) || /[\r\n$'"#:\\]/.test(socket)) {
|
|
5539
5650
|
throw setupError(
|
|
5540
5651
|
"--docker-socket must be an absolute path without line breaks or Compose interpolation characters."
|
|
5541
5652
|
);
|
|
@@ -5561,8 +5672,8 @@ var resolveAssets = async (runtime, serverVersion, existing, store, options) =>
|
|
|
5561
5672
|
if (options.assetsDir !== void 0) {
|
|
5562
5673
|
try {
|
|
5563
5674
|
const [compose2, postgresInit2] = await Promise.all([
|
|
5564
|
-
readFile(
|
|
5565
|
-
readFile(
|
|
5675
|
+
readFile(path12.join(options.assetsDir, COMPOSE_ASSET), "utf8"),
|
|
5676
|
+
readFile(path12.join(options.assetsDir, POSTGRES_INIT_ASSET), "utf8")
|
|
5566
5677
|
]);
|
|
5567
5678
|
validateComposeAsset(compose2);
|
|
5568
5679
|
validatePostgresAsset(postgresInit2);
|
|
@@ -6088,10 +6199,10 @@ var manageServer = async (command, args, runtime, store) => {
|
|
|
6088
6199
|
};
|
|
6089
6200
|
var nodeServerRuntime = () => {
|
|
6090
6201
|
const environment = { ...process.env };
|
|
6091
|
-
const home =
|
|
6092
|
-
const configHome = environment["XDG_CONFIG_HOME"] ??
|
|
6202
|
+
const home = os8.homedir();
|
|
6203
|
+
const configHome = environment["XDG_CONFIG_HOME"] ?? path12.join(home, ".config");
|
|
6093
6204
|
return {
|
|
6094
|
-
configDir:
|
|
6205
|
+
configDir: path12.join(configHome, "mend"),
|
|
6095
6206
|
platform: process.platform,
|
|
6096
6207
|
cliVersion: cliVersion(),
|
|
6097
6208
|
run: (command, args, options) => runServerProcess(command, args, environment, options),
|
|
@@ -6544,8 +6655,8 @@ var createServiceTunnels = (options) => {
|
|
|
6544
6655
|
};
|
|
6545
6656
|
|
|
6546
6657
|
// src/skills.ts
|
|
6547
|
-
import * as
|
|
6548
|
-
import * as
|
|
6658
|
+
import * as fs13 from "node:fs";
|
|
6659
|
+
import * as path13 from "node:path";
|
|
6549
6660
|
var DEFAULT_SKILLS_DIR = ".agents/skills";
|
|
6550
6661
|
var MAX_SKILL_FILES = 64;
|
|
6551
6662
|
var MAX_SKILL_FILE_BYTES = 512 * 1024;
|
|
@@ -6568,12 +6679,12 @@ var readFrontmatterDescription = (contents) => {
|
|
|
6568
6679
|
var walkFiles = (root) => {
|
|
6569
6680
|
const found = [];
|
|
6570
6681
|
const visit = (dir) => {
|
|
6571
|
-
for (const entry of
|
|
6682
|
+
for (const entry of fs13.readdirSync(dir, { withFileTypes: true })) {
|
|
6572
6683
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6573
|
-
const absolute =
|
|
6684
|
+
const absolute = path13.join(dir, entry.name);
|
|
6574
6685
|
let stat;
|
|
6575
6686
|
try {
|
|
6576
|
-
stat =
|
|
6687
|
+
stat = fs13.statSync(absolute);
|
|
6577
6688
|
} catch {
|
|
6578
6689
|
continue;
|
|
6579
6690
|
}
|
|
@@ -6587,7 +6698,7 @@ var walkFiles = (root) => {
|
|
|
6587
6698
|
var scanSkillLibrary = (root) => {
|
|
6588
6699
|
let entries;
|
|
6589
6700
|
try {
|
|
6590
|
-
entries =
|
|
6701
|
+
entries = fs13.readdirSync(root, { withFileTypes: true });
|
|
6591
6702
|
} catch {
|
|
6592
6703
|
return { error: `${root} is not a readable directory` };
|
|
6593
6704
|
}
|
|
@@ -6595,23 +6706,23 @@ var scanSkillLibrary = (root) => {
|
|
|
6595
6706
|
const notes = [];
|
|
6596
6707
|
for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
6597
6708
|
if (entry.name.startsWith(".")) continue;
|
|
6598
|
-
const skillDir =
|
|
6709
|
+
const skillDir = path13.join(root, entry.name);
|
|
6599
6710
|
let stat;
|
|
6600
6711
|
try {
|
|
6601
|
-
stat =
|
|
6712
|
+
stat = fs13.statSync(skillDir);
|
|
6602
6713
|
} catch {
|
|
6603
6714
|
continue;
|
|
6604
6715
|
}
|
|
6605
6716
|
if (!stat.isDirectory()) continue;
|
|
6606
|
-
if (!
|
|
6717
|
+
if (!fs13.existsSync(path13.join(skillDir, "SKILL.md"))) {
|
|
6607
6718
|
notes.push({ skill: entry.name, message: "no SKILL.md \u2014 skipped" });
|
|
6608
6719
|
continue;
|
|
6609
6720
|
}
|
|
6610
6721
|
const files = [];
|
|
6611
6722
|
let bytes = 0;
|
|
6612
6723
|
for (const absolute of walkFiles(skillDir)) {
|
|
6613
|
-
const relative4 =
|
|
6614
|
-
const buffer =
|
|
6724
|
+
const relative4 = path13.relative(skillDir, absolute).split(path13.sep).join("/");
|
|
6725
|
+
const buffer = fs13.readFileSync(absolute);
|
|
6615
6726
|
if (buffer.byteLength > MAX_SKILL_FILE_BYTES) {
|
|
6616
6727
|
notes.push({ skill: entry.name, message: `${relative4} is over 512KB \u2014 skipped` });
|
|
6617
6728
|
continue;
|
|
@@ -6646,16 +6757,16 @@ var scanSkillLibrary = (root) => {
|
|
|
6646
6757
|
};
|
|
6647
6758
|
|
|
6648
6759
|
// src/ssh-setup.ts
|
|
6649
|
-
import * as
|
|
6650
|
-
import * as
|
|
6760
|
+
import * as os10 from "node:os";
|
|
6761
|
+
import * as path15 from "node:path";
|
|
6651
6762
|
|
|
6652
6763
|
// ../../packages/workspace-ssh/src/index.ts
|
|
6653
|
-
import { spawnSync as
|
|
6764
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
6654
6765
|
import { createHash as createHash3 } from "node:crypto";
|
|
6655
|
-
import * as
|
|
6766
|
+
import * as fs14 from "node:fs";
|
|
6656
6767
|
import { isIP as isIP2 } from "node:net";
|
|
6657
|
-
import * as
|
|
6658
|
-
import * as
|
|
6768
|
+
import * as os9 from "node:os";
|
|
6769
|
+
import * as path14 from "node:path";
|
|
6659
6770
|
var LEGACY_BLOCK_BEGIN = "# >>> mend workspace ssh (managed by `mend ssh setup`) >>>";
|
|
6660
6771
|
var LEGACY_BLOCK_END = "# <<< mend workspace ssh <<<";
|
|
6661
6772
|
var WorkspaceSshError = class extends Error {
|
|
@@ -6742,8 +6853,8 @@ var blockBegin = (alias) => `# >>> mend workspace ssh ${alias} (managed) >>>`;
|
|
|
6742
6853
|
var blockEnd = (alias) => `# <<< mend workspace ssh ${alias} <<<`;
|
|
6743
6854
|
var hasControls = (value) => /[\p{Cc}\p{Zl}\p{Zp}]/u.test(value);
|
|
6744
6855
|
var parseIdentityPath = (value) => {
|
|
6745
|
-
const resolved =
|
|
6746
|
-
value.startsWith("~/") ?
|
|
6856
|
+
const resolved = path14.resolve(
|
|
6857
|
+
value.startsWith("~/") ? path14.join(os9.homedir(), value.slice(2)) : value
|
|
6747
6858
|
);
|
|
6748
6859
|
if (value === "" || hasControls(value) || hasControls(resolved) || value.includes("${") || resolved.includes("${")) {
|
|
6749
6860
|
return failure(
|
|
@@ -6889,7 +7000,7 @@ var workspaceSshPublicKeyFingerprint = (publicKey) => {
|
|
|
6889
7000
|
var publicKeyIdentity = (publicKey) => publicKey.trim().split(/\s+/).slice(0, 2).join(" ");
|
|
6890
7001
|
var agentKeys = () => {
|
|
6891
7002
|
if (!process.env["SSH_AUTH_SOCK"]) return [];
|
|
6892
|
-
const listed =
|
|
7003
|
+
const listed = spawnSync5("ssh-add", ["-L"], {
|
|
6893
7004
|
encoding: "utf8",
|
|
6894
7005
|
timeout: 3e3,
|
|
6895
7006
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6899,7 +7010,7 @@ var agentKeys = () => {
|
|
|
6899
7010
|
};
|
|
6900
7011
|
var readText = (file) => {
|
|
6901
7012
|
try {
|
|
6902
|
-
return success(
|
|
7013
|
+
return success(fs14.readFileSync(file, "utf8"));
|
|
6903
7014
|
} catch (cause) {
|
|
6904
7015
|
const error = cause instanceof Error && "code" in cause ? cause.code : void 0;
|
|
6905
7016
|
return error === "ENOENT" ? success("") : failure(new WorkspaceSshError("key", `Could not read ${file}.`, cause));
|
|
@@ -6926,7 +7037,7 @@ var readRequiredKey = (keyPath, source) => {
|
|
|
6926
7037
|
const privatePath = publicPath.slice(0, -".pub".length);
|
|
6927
7038
|
const read = readText(publicPath);
|
|
6928
7039
|
if (!read.ok) return read;
|
|
6929
|
-
const derived =
|
|
7040
|
+
const derived = spawnSync5("ssh-keygen", ["-y", "-P", "", "-f", privatePath], {
|
|
6930
7041
|
encoding: "utf8",
|
|
6931
7042
|
timeout: 3e3,
|
|
6932
7043
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6957,19 +7068,19 @@ var readRequiredKey = (keyPath, source) => {
|
|
|
6957
7068
|
var readOptionalKey = (keyPath) => {
|
|
6958
7069
|
const parsed = parseIdentityPath(keyPath);
|
|
6959
7070
|
if (!parsed.ok) return parsed;
|
|
6960
|
-
if (!
|
|
7071
|
+
if (!fs14.existsSync(parsed.value) && !fs14.existsSync(publicKeyPath(parsed.value)))
|
|
6961
7072
|
return success(null);
|
|
6962
7073
|
return readRequiredKey(parsed.value, "existing");
|
|
6963
7074
|
};
|
|
6964
7075
|
var pinAgentKey = (configHome, publicKey) => {
|
|
6965
|
-
const identityFile =
|
|
7076
|
+
const identityFile = path14.resolve(
|
|
6966
7077
|
configHome,
|
|
6967
7078
|
"ssh",
|
|
6968
7079
|
`agent-${hashIdentity(publicKeyIdentity(publicKey))}.pub`
|
|
6969
7080
|
);
|
|
6970
7081
|
try {
|
|
6971
|
-
|
|
6972
|
-
|
|
7082
|
+
fs14.mkdirSync(path14.dirname(identityFile), { recursive: true, mode: 448 });
|
|
7083
|
+
fs14.writeFileSync(identityFile, `${publicKey}
|
|
6973
7084
|
`, { mode: 384 });
|
|
6974
7085
|
return success({ publicKey, identityFile, source: "agent" });
|
|
6975
7086
|
} catch (cause) {
|
|
@@ -6980,15 +7091,15 @@ var pinAgentKey = (configHome, publicKey) => {
|
|
|
6980
7091
|
};
|
|
6981
7092
|
var generateDedicatedKey = (privatePath) => {
|
|
6982
7093
|
try {
|
|
6983
|
-
|
|
7094
|
+
fs14.mkdirSync(path14.dirname(privatePath), { recursive: true, mode: 448 });
|
|
6984
7095
|
} catch (cause) {
|
|
6985
7096
|
return failure(
|
|
6986
|
-
new WorkspaceSshError("key", `Could not create ${
|
|
7097
|
+
new WorkspaceSshError("key", `Could not create ${path14.dirname(privatePath)}.`, cause)
|
|
6987
7098
|
);
|
|
6988
7099
|
}
|
|
6989
|
-
const generated =
|
|
7100
|
+
const generated = spawnSync5(
|
|
6990
7101
|
"ssh-keygen",
|
|
6991
|
-
["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${
|
|
7102
|
+
["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${os9.hostname()}`],
|
|
6992
7103
|
{ encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "pipe"] }
|
|
6993
7104
|
);
|
|
6994
7105
|
if (generated.status !== 0) {
|
|
@@ -7012,7 +7123,7 @@ var generateDedicatedKey = (privatePath) => {
|
|
|
7012
7123
|
return fresh;
|
|
7013
7124
|
};
|
|
7014
7125
|
var dedicatedKey = (configHome, create) => {
|
|
7015
|
-
const privatePath =
|
|
7126
|
+
const privatePath = path14.join(configHome, "ssh", "id_ed25519");
|
|
7016
7127
|
const existing = readOptionalKey(privatePath);
|
|
7017
7128
|
if (existing.ok === false) return failure(existing.error);
|
|
7018
7129
|
if (existing.value !== null || !create) return existing;
|
|
@@ -7060,10 +7171,10 @@ var writeWorkspaceSshConfig = (configFile, target, identityFile) => {
|
|
|
7060
7171
|
const reconciled = reconcileWorkspaceSshConfig(before.value, target, identityFile);
|
|
7061
7172
|
if (reconciled.ok === false) return failure(reconciled.error);
|
|
7062
7173
|
try {
|
|
7063
|
-
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
|
|
7174
|
+
fs14.mkdirSync(path14.dirname(configFile), { recursive: true, mode: 448 });
|
|
7175
|
+
fs14.chmodSync(path14.dirname(configFile), 448);
|
|
7176
|
+
fs14.writeFileSync(configFile, reconciled.value, { mode: 384 });
|
|
7177
|
+
fs14.chmodSync(configFile, 384);
|
|
7067
7178
|
return success(void 0);
|
|
7068
7179
|
} catch (cause) {
|
|
7069
7180
|
return failure(new WorkspaceSshError("config", `Could not write ${configFile}.`, cause));
|
|
@@ -7076,7 +7187,7 @@ var dim5 = ansi("2");
|
|
|
7076
7187
|
var green5 = ansi("32");
|
|
7077
7188
|
var warn = ansi("33");
|
|
7078
7189
|
var say5 = (line) => console.log(line);
|
|
7079
|
-
var sshConfigPath = () =>
|
|
7190
|
+
var sshConfigPath = () => path15.join(os10.homedir(), ".ssh", "config");
|
|
7080
7191
|
var flagValue2 = (args, flag) => {
|
|
7081
7192
|
const index = args.indexOf(flag);
|
|
7082
7193
|
return index === -1 || args[index + 1] === void 0 ? null : String(args[index + 1]);
|
|
@@ -7146,7 +7257,7 @@ var setup = async (api2, cliHome, serverUrl, args) => {
|
|
|
7146
7257
|
if (picked.value === null) return showFailure("No workspace SSH key is available.");
|
|
7147
7258
|
const registered = await api2("POST", "/workspace-ssh/keys", {
|
|
7148
7259
|
publicKey: picked.value.publicKey,
|
|
7149
|
-
name:
|
|
7260
|
+
name: os10.hostname()
|
|
7150
7261
|
});
|
|
7151
7262
|
const sourceLabel = {
|
|
7152
7263
|
explicit: "from --key",
|
|
@@ -7188,8 +7299,8 @@ var sshCommand = async (args, api2, cliHome, serverUrl) => {
|
|
|
7188
7299
|
};
|
|
7189
7300
|
|
|
7190
7301
|
// src/uninstall.ts
|
|
7191
|
-
import * as
|
|
7192
|
-
import * as
|
|
7302
|
+
import * as fs15 from "node:fs";
|
|
7303
|
+
import * as path16 from "node:path";
|
|
7193
7304
|
var UNINSTALL_USAGE = "usage: mend uninstall [--all | --server | --home] [--yes]";
|
|
7194
7305
|
var parseUninstallArgs = (args) => {
|
|
7195
7306
|
let scope = null;
|
|
@@ -7211,14 +7322,14 @@ var parseUninstallArgs = (args) => {
|
|
|
7211
7322
|
var SERVER_FILES = ["identity.env", "active", "generations", "backups"];
|
|
7212
7323
|
var countEntries = (directory) => {
|
|
7213
7324
|
try {
|
|
7214
|
-
return
|
|
7325
|
+
return fs15.readdirSync(directory).length;
|
|
7215
7326
|
} catch {
|
|
7216
7327
|
return 0;
|
|
7217
7328
|
}
|
|
7218
7329
|
};
|
|
7219
7330
|
var exists = (file) => {
|
|
7220
7331
|
try {
|
|
7221
|
-
|
|
7332
|
+
fs15.lstatSync(file);
|
|
7222
7333
|
return true;
|
|
7223
7334
|
} catch {
|
|
7224
7335
|
return false;
|
|
@@ -7227,7 +7338,7 @@ var exists = (file) => {
|
|
|
7227
7338
|
var managedBlockCount = (sshConfigFile) => {
|
|
7228
7339
|
let config;
|
|
7229
7340
|
try {
|
|
7230
|
-
config =
|
|
7341
|
+
config = fs15.readFileSync(sshConfigFile, "utf8");
|
|
7231
7342
|
} catch {
|
|
7232
7343
|
return 0;
|
|
7233
7344
|
}
|
|
@@ -7251,27 +7362,27 @@ var describeUninstall = async (runtime, scope) => {
|
|
|
7251
7362
|
if (!read.error.message.startsWith("No Mend server is configured")) throw read.error;
|
|
7252
7363
|
server = "none";
|
|
7253
7364
|
} else if (read.value === null) {
|
|
7254
|
-
server = exists(
|
|
7365
|
+
server = exists(path16.join(configDir, "identity.env")) ? {
|
|
7255
7366
|
version: "(no active generation)",
|
|
7256
7367
|
appUrl: "",
|
|
7257
7368
|
dockerContext: "",
|
|
7258
|
-
generations: countEntries(
|
|
7259
|
-
backups: countEntries(
|
|
7369
|
+
generations: countEntries(path16.join(configDir, "generations")),
|
|
7370
|
+
backups: countEntries(path16.join(configDir, "backups"))
|
|
7260
7371
|
} : "none";
|
|
7261
7372
|
} else {
|
|
7262
7373
|
server = {
|
|
7263
7374
|
version: read.value.config.serverVersion,
|
|
7264
7375
|
appUrl: read.value.config.appUrl,
|
|
7265
7376
|
dockerContext: read.value.config.dockerContext,
|
|
7266
|
-
generations: countEntries(
|
|
7267
|
-
backups: countEntries(
|
|
7377
|
+
generations: countEntries(path16.join(configDir, "generations")),
|
|
7378
|
+
backups: countEntries(path16.join(configDir, "backups"))
|
|
7268
7379
|
};
|
|
7269
7380
|
}
|
|
7270
7381
|
}
|
|
7271
7382
|
let home = null;
|
|
7272
7383
|
if (scope !== "server") {
|
|
7273
|
-
const cliConfig =
|
|
7274
|
-
const sshDirectory =
|
|
7384
|
+
const cliConfig = path16.join(runtime.cliHome, "cli.json");
|
|
7385
|
+
const sshDirectory = path16.join(runtime.cliHome, "ssh");
|
|
7275
7386
|
home = {
|
|
7276
7387
|
cliConfig: exists(cliConfig) ? cliConfig : null,
|
|
7277
7388
|
sshDirectory: exists(sshDirectory) ? sshDirectory : null,
|
|
@@ -7403,7 +7514,7 @@ var removeServer = async (runtime, failures, leftovers) => {
|
|
|
7403
7514
|
}
|
|
7404
7515
|
}
|
|
7405
7516
|
for (const name of SERVER_FILES) {
|
|
7406
|
-
|
|
7517
|
+
fs15.rmSync(path16.join(configDir, name), { recursive: true, force: true });
|
|
7407
7518
|
}
|
|
7408
7519
|
server.writeLine(`removed ${configDir}/{identity.env, active, generations, backups}`);
|
|
7409
7520
|
},
|
|
@@ -7414,20 +7525,20 @@ var removeServer = async (runtime, failures, leftovers) => {
|
|
|
7414
7525
|
var removeHome = async (runtime, plan, failures, leftovers) => {
|
|
7415
7526
|
const { server } = runtime;
|
|
7416
7527
|
if (plan.cliConfig !== null) {
|
|
7417
|
-
|
|
7528
|
+
fs15.rmSync(plan.cliConfig, { force: true });
|
|
7418
7529
|
server.writeLine(`removed ${plan.cliConfig}`);
|
|
7419
7530
|
}
|
|
7420
7531
|
if (plan.sshDirectory !== null) {
|
|
7421
|
-
|
|
7532
|
+
fs15.rmSync(plan.sshDirectory, { recursive: true, force: true });
|
|
7422
7533
|
server.writeLine(`removed ${plan.sshDirectory}`);
|
|
7423
7534
|
}
|
|
7424
7535
|
if (plan.managedSshBlocks > 0) {
|
|
7425
7536
|
try {
|
|
7426
|
-
const before =
|
|
7537
|
+
const before = fs15.readFileSync(runtime.sshConfigFile, "utf8");
|
|
7427
7538
|
const stripped = stripManagedWorkspaceSshBlocks(before);
|
|
7428
7539
|
if (!stripped.ok) throw stripped.error;
|
|
7429
7540
|
if (stripped.value.removed > 0) {
|
|
7430
|
-
|
|
7541
|
+
fs15.writeFileSync(runtime.sshConfigFile, stripped.value.config, { mode: 384 });
|
|
7431
7542
|
server.writeLine(
|
|
7432
7543
|
`removed ${plural(stripped.value.removed, "managed block")} from ${runtime.sshConfigFile}`
|
|
7433
7544
|
);
|
|
@@ -7440,12 +7551,12 @@ var removeHome = async (runtime, plan, failures, leftovers) => {
|
|
|
7440
7551
|
}
|
|
7441
7552
|
let remaining = [];
|
|
7442
7553
|
try {
|
|
7443
|
-
remaining =
|
|
7554
|
+
remaining = fs15.readdirSync(runtime.cliHome);
|
|
7444
7555
|
} catch {
|
|
7445
7556
|
return;
|
|
7446
7557
|
}
|
|
7447
7558
|
if (remaining.length === 0) {
|
|
7448
|
-
|
|
7559
|
+
fs15.rmdirSync(runtime.cliHome);
|
|
7449
7560
|
server.writeLine(`removed ${runtime.cliHome}`);
|
|
7450
7561
|
} else {
|
|
7451
7562
|
leftovers.push(
|
|
@@ -7509,19 +7620,19 @@ var upgradeUrl = async (input) => {
|
|
|
7509
7620
|
// src/main.ts
|
|
7510
7621
|
var mendCliHome = () => {
|
|
7511
7622
|
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
7512
|
-
const preferred =
|
|
7513
|
-
xdg === void 0 || xdg === "" ?
|
|
7623
|
+
const preferred = path17.join(
|
|
7624
|
+
xdg === void 0 || xdg === "" ? path17.join(os11.homedir(), ".config") : xdg,
|
|
7514
7625
|
"mend"
|
|
7515
7626
|
);
|
|
7516
|
-
const legacy =
|
|
7517
|
-
return !
|
|
7627
|
+
const legacy = path17.join(os11.homedir(), ".mend");
|
|
7628
|
+
return !fs16.existsSync(preferred) && fs16.existsSync(legacy) ? legacy : preferred;
|
|
7518
7629
|
};
|
|
7519
|
-
var CONFIG_PATH =
|
|
7630
|
+
var CONFIG_PATH = path17.join(mendCliHome(), "cli.json");
|
|
7520
7631
|
var loadConfig = () => {
|
|
7521
7632
|
let fileConfig = {};
|
|
7522
|
-
if (
|
|
7633
|
+
if (fs16.existsSync(CONFIG_PATH)) {
|
|
7523
7634
|
try {
|
|
7524
|
-
fileConfig = JSON.parse(
|
|
7635
|
+
fileConfig = JSON.parse(fs16.readFileSync(CONFIG_PATH, "utf8"));
|
|
7525
7636
|
} catch {
|
|
7526
7637
|
fail6(`could not parse ${CONFIG_PATH}`);
|
|
7527
7638
|
}
|
|
@@ -7773,7 +7884,7 @@ var adopt = async (config, args) => {
|
|
|
7773
7884
|
}
|
|
7774
7885
|
const sourceIssue = repositoryCloneUrlIssue(source);
|
|
7775
7886
|
if (sourceIssue !== null) return fail6(sourceIssue);
|
|
7776
|
-
const projectName = name ?? normalizeProjectName(
|
|
7887
|
+
const projectName = name ?? normalizeProjectName(path17.basename(source, ".git"));
|
|
7777
7888
|
const auth = authFlagIndex !== -1 && args[authFlagIndex + 1] !== void 0 ? String(args[authFlagIndex + 1]) : null;
|
|
7778
7889
|
if (auth !== null && auth !== "ambient" && auth !== "mend-key" && auth !== "bridge") {
|
|
7779
7890
|
return fail6(`--auth takes "ambient", "mend-key", or "bridge", not "${auth}"`);
|
|
@@ -7822,7 +7933,7 @@ var findProject = async (config, explicit, adoptCwd = false) => {
|
|
|
7822
7933
|
const sourceIssue = repositoryCloneUrlIssue(facts.originUrl);
|
|
7823
7934
|
if (sourceIssue !== null) return fail6(sourceIssue);
|
|
7824
7935
|
const adopted = await api(config, "POST", "/projects", {
|
|
7825
|
-
name: normalizeProjectName(
|
|
7936
|
+
name: normalizeProjectName(path17.basename(facts.repoRoot)),
|
|
7826
7937
|
source: facts.originUrl
|
|
7827
7938
|
});
|
|
7828
7939
|
say6(`${green6("\u2713")} adopted \xB7 ${adopted.name} \xB7 ${dim6(adopted.storePath)}`);
|
|
@@ -8043,9 +8154,12 @@ var attachTty = async (config, sessionId, harness, from, processId, options) =>
|
|
|
8043
8154
|
};
|
|
8044
8155
|
const onWinch = () => sendResize();
|
|
8045
8156
|
let bracketedPaste = false;
|
|
8157
|
+
let starting = null;
|
|
8046
8158
|
const onTtyFrame = (event) => {
|
|
8047
8159
|
if (typeof event.data !== "string") {
|
|
8048
8160
|
const bytes = Buffer.from(event.data);
|
|
8161
|
+
if (bytes.length === 0) return;
|
|
8162
|
+
starting?.pass();
|
|
8049
8163
|
bracketedPaste = trackBracketedPaste(bytes, bracketedPaste);
|
|
8050
8164
|
process.stdout.write(bytes);
|
|
8051
8165
|
return;
|
|
@@ -8156,6 +8270,19 @@ var attachTty = async (config, sessionId, harness, from, processId, options) =>
|
|
|
8156
8270
|
if (connected === "unavailable") return "unavailable";
|
|
8157
8271
|
if (connected === "aborted") return abortReason ?? "interrupted";
|
|
8158
8272
|
ws = connected;
|
|
8273
|
+
if (interactive && processId === void 0 && process.stdout.isTTY === true) {
|
|
8274
|
+
const gate = firstOutputGate({
|
|
8275
|
+
label: startingLabelOf(harness),
|
|
8276
|
+
write: (text) => process.stdout.write(text),
|
|
8277
|
+
now: Date.now,
|
|
8278
|
+
cancelHint: detachKeyEnabled ? "Ctrl+] detaches" : ""
|
|
8279
|
+
});
|
|
8280
|
+
starting = gate;
|
|
8281
|
+
void request(config, "GET", `/sessions/${sessionId}`).then(
|
|
8282
|
+
(detail) => gate.update(agentStartingFacts(detail.currentAgent, detail.processes, Date.now())),
|
|
8283
|
+
() => void 0
|
|
8284
|
+
);
|
|
8285
|
+
}
|
|
8159
8286
|
stopHerdrHint = hintHerdrAttachment(harness);
|
|
8160
8287
|
const onClose = () => finishAttachment?.();
|
|
8161
8288
|
if (ws.readyState === WebSocket.CLOSED) finishAttachment?.();
|
|
@@ -8169,6 +8296,7 @@ var attachTty = async (config, sessionId, harness, from, processId, options) =>
|
|
|
8169
8296
|
return detached ? "detached" : interrupted ? "interrupted" : sawEnd ? "ended" : "dropped";
|
|
8170
8297
|
} finally {
|
|
8171
8298
|
stopConnecting();
|
|
8299
|
+
starting?.stop();
|
|
8172
8300
|
for (const signal of signals) process.off(signal, onSignal);
|
|
8173
8301
|
process.stdin.off("data", onKeys);
|
|
8174
8302
|
process.stdout.off("resize", onWinch);
|
|
@@ -8766,13 +8894,13 @@ var serviceRestart = async (config, args) => {
|
|
|
8766
8894
|
};
|
|
8767
8895
|
var serviceInit = async (args) => {
|
|
8768
8896
|
const root = gitTopLevel(process.cwd()) ?? process.cwd();
|
|
8769
|
-
const target =
|
|
8770
|
-
if (
|
|
8897
|
+
const target = path17.join(root, "mend.toml");
|
|
8898
|
+
if (fs16.existsSync(target)) {
|
|
8771
8899
|
return fail6(`${target} already exists \u2014 edit it directly (init never merges)`);
|
|
8772
8900
|
}
|
|
8773
|
-
const rootFiles =
|
|
8901
|
+
const rootFiles = fs16.readdirSync(root);
|
|
8774
8902
|
const proposals = [];
|
|
8775
|
-
const readRoot = (name) =>
|
|
8903
|
+
const readRoot = (name) => fs16.readFileSync(path17.join(root, name), "utf8");
|
|
8776
8904
|
if (rootFiles.includes("package.json")) {
|
|
8777
8905
|
proposals.push(...proposeFromPackageJson(readRoot("package.json"), rootFiles));
|
|
8778
8906
|
}
|
|
@@ -8782,13 +8910,13 @@ var serviceInit = async (args) => {
|
|
|
8782
8910
|
);
|
|
8783
8911
|
for (const glob of globs) {
|
|
8784
8912
|
const parent = glob.replace(/\/?\*+$/, "");
|
|
8785
|
-
const dirs = glob.endsWith("*") ?
|
|
8913
|
+
const dirs = glob.endsWith("*") ? fs16.existsSync(path17.join(root, parent)) ? fs16.readdirSync(path17.join(root, parent)).map((dir) => path17.join(parent, dir)) : [] : [glob];
|
|
8786
8914
|
for (const dir of dirs) {
|
|
8787
|
-
const manifest =
|
|
8788
|
-
if (!
|
|
8915
|
+
const manifest = path17.join(root, dir, "package.json");
|
|
8916
|
+
if (!fs16.existsSync(manifest)) continue;
|
|
8789
8917
|
for (const proposal of proposeFromWorkspacePackage(
|
|
8790
|
-
|
|
8791
|
-
|
|
8918
|
+
path17.basename(dir),
|
|
8919
|
+
fs16.readFileSync(manifest, "utf8"),
|
|
8792
8920
|
rootFiles
|
|
8793
8921
|
)) {
|
|
8794
8922
|
if (!proposals.some((existing) => existing.name === proposal.name)) {
|
|
@@ -8832,7 +8960,7 @@ var serviceInit = async (args) => {
|
|
|
8832
8960
|
return;
|
|
8833
8961
|
}
|
|
8834
8962
|
}
|
|
8835
|
-
|
|
8963
|
+
fs16.writeFileSync(target, toml);
|
|
8836
8964
|
say6(`${green6("\u2713")} wrote ${target} \u2014 commit it, then: mend service run ${proposals[0]?.name}`);
|
|
8837
8965
|
};
|
|
8838
8966
|
var tunnelServices = async (config, services, portOverride) => {
|
|
@@ -8936,23 +9064,23 @@ var serviceCommand = async (config, args) => {
|
|
|
8936
9064
|
}
|
|
8937
9065
|
};
|
|
8938
9066
|
var isProvider = (value) => value === "claude" || value === "codex" || value === "github";
|
|
8939
|
-
var readIfExists = (file) =>
|
|
9067
|
+
var readIfExists = (file) => fs16.existsSync(file) ? fs16.readFileSync(file, "utf8") : null;
|
|
8940
9068
|
var localCredential = (provider) => {
|
|
8941
|
-
const home =
|
|
9069
|
+
const home = os11.homedir();
|
|
8942
9070
|
switch (provider) {
|
|
8943
9071
|
case "codex":
|
|
8944
9072
|
return readIfExists(
|
|
8945
|
-
|
|
9073
|
+
path17.join(process.env["CODEX_HOME"] ?? path17.join(home, ".codex"), "auth.json")
|
|
8946
9074
|
);
|
|
8947
9075
|
case "claude":
|
|
8948
9076
|
return readIfExists(
|
|
8949
|
-
|
|
8950
|
-
process.env["CLAUDE_CONFIG_DIR"] ??
|
|
9077
|
+
path17.join(
|
|
9078
|
+
process.env["CLAUDE_CONFIG_DIR"] ?? path17.join(home, ".claude"),
|
|
8951
9079
|
".credentials.json"
|
|
8952
9080
|
)
|
|
8953
9081
|
);
|
|
8954
9082
|
case "github": {
|
|
8955
|
-
const result =
|
|
9083
|
+
const result = spawnSync6("gh", ["auth", "token"], { encoding: "utf8" });
|
|
8956
9084
|
const token = result.status === 0 ? result.stdout.trim() : "";
|
|
8957
9085
|
return token === "" ? null : token;
|
|
8958
9086
|
}
|
|
@@ -8964,7 +9092,7 @@ var accountLine = (account) => {
|
|
|
8964
9092
|
const identity = pick("login") ?? pick("email") ?? pick("accountEmail") ?? pick("accountId");
|
|
8965
9093
|
const suffix = pick("tokenSuffix");
|
|
8966
9094
|
const facts = [
|
|
8967
|
-
account.status === "active" ? "connected" : account.status,
|
|
9095
|
+
account.status === "active" ? "connected" : account.status === "invalid" ? "reconnect needed \xB7 the provider refused the login" : account.status,
|
|
8968
9096
|
identity,
|
|
8969
9097
|
suffix === null ? null : `\u2026${suffix}`,
|
|
8970
9098
|
`since ${account.connectedAt.slice(0, 10)}`
|
|
@@ -8997,7 +9125,7 @@ var claudeGrantSecret = () => {
|
|
|
8997
9125
|
return read.kind === "missing" ? null : read.secret;
|
|
8998
9126
|
};
|
|
8999
9127
|
var doctorBundle = (config, args) => doctorBundleCommand(args, {
|
|
9000
|
-
defaultDir:
|
|
9128
|
+
defaultDir: path17.join(mendCliHome(), "bundles"),
|
|
9001
9129
|
now: () => /* @__PURE__ */ new Date(),
|
|
9002
9130
|
say: say6,
|
|
9003
9131
|
warn: (line) => process.stderr.write(`${line}
|
|
@@ -9061,7 +9189,7 @@ var claudeGrant = async () => {
|
|
|
9061
9189
|
return null;
|
|
9062
9190
|
}
|
|
9063
9191
|
}
|
|
9064
|
-
if (status.configDirectory !== null &&
|
|
9192
|
+
if (status.configDirectory !== null && path17.resolve(status.configDirectory) !== path17.resolve(dir)) {
|
|
9065
9193
|
fail6(
|
|
9066
9194
|
`claude: this Claude read ${status.configDirectory} instead of ${dir}, so a separate grant is not possible \u2014 connect the shared one deliberately with --use-my-login`
|
|
9067
9195
|
);
|
|
@@ -9124,8 +9252,16 @@ var connectCommand = async (config, args) => {
|
|
|
9124
9252
|
}
|
|
9125
9253
|
let secret;
|
|
9126
9254
|
if (flags.includes("--from-stdin")) {
|
|
9127
|
-
secret =
|
|
9255
|
+
secret = fs16.readFileSync(0, "utf8").trim();
|
|
9128
9256
|
if (secret === "") return fail6("nothing on stdin");
|
|
9257
|
+
} else if (provider === "codex" && !flags.includes("--use-my-login")) {
|
|
9258
|
+
const grant = codexGrant({
|
|
9259
|
+
cli: codexCli(),
|
|
9260
|
+
personalAuthJson: readIfExists(path17.join(personalCodexHome(), "auth.json")),
|
|
9261
|
+
say: say6
|
|
9262
|
+
});
|
|
9263
|
+
if (grant.kind === "failed") return fail6(grant.reason);
|
|
9264
|
+
secret = grant.secret;
|
|
9129
9265
|
} else if (provider === "claude" && !flags.includes("--use-my-login")) {
|
|
9130
9266
|
secret = await claudeGrant();
|
|
9131
9267
|
if (secret === null) return;
|
|
@@ -9135,10 +9271,10 @@ var connectCommand = async (config, args) => {
|
|
|
9135
9271
|
const where = provider === "github" ? "`gh auth login` first, or pipe a token: gh auth token | mend connect github --from-stdin" : provider === "codex" ? "`codex login` first, or: mend connect codex --from-stdin < auth.json" : "`claude auth login` first, or: mend connect claude --from-stdin";
|
|
9136
9272
|
return fail6(`${provider}: no credential on this machine \u2014 ${where}`);
|
|
9137
9273
|
}
|
|
9138
|
-
if (provider === "claude") {
|
|
9274
|
+
if (provider === "claude" || provider === "codex") {
|
|
9139
9275
|
say6(
|
|
9140
9276
|
dim6(
|
|
9141
|
-
" --use-my-login: Mend and this machine will share one
|
|
9277
|
+
" --use-my-login: Mend and this machine will share one login, and whichever refreshes second is signed out"
|
|
9142
9278
|
)
|
|
9143
9279
|
);
|
|
9144
9280
|
}
|
|
@@ -9170,14 +9306,14 @@ var takeFlagValue4 = (args, flag) => {
|
|
|
9170
9306
|
return at !== -1 && args[at + 1] !== void 0 ? String(args[at + 1]) : null;
|
|
9171
9307
|
};
|
|
9172
9308
|
var saveCliConfig = (next) => {
|
|
9173
|
-
|
|
9174
|
-
|
|
9309
|
+
fs16.mkdirSync(path17.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
|
|
9310
|
+
fs16.writeFileSync(
|
|
9175
9311
|
CONFIG_PATH,
|
|
9176
9312
|
`${JSON.stringify({ url: next.url, token: next.token, deviceId: next.deviceId }, null, 2)}
|
|
9177
9313
|
`,
|
|
9178
9314
|
{ mode: 384 }
|
|
9179
9315
|
);
|
|
9180
|
-
|
|
9316
|
+
fs16.chmodSync(CONFIG_PATH, 384);
|
|
9181
9317
|
};
|
|
9182
9318
|
var login = async (config, args) => {
|
|
9183
9319
|
await loginCommand(args, {
|
|
@@ -9190,7 +9326,7 @@ var login = async (config, args) => {
|
|
|
9190
9326
|
});
|
|
9191
9327
|
};
|
|
9192
9328
|
var logout = async (config) => {
|
|
9193
|
-
if (!
|
|
9329
|
+
if (!fs16.existsSync(CONFIG_PATH) && config.token === null) {
|
|
9194
9330
|
say6(dim6("nothing saved \u2014 already signed out"));
|
|
9195
9331
|
return;
|
|
9196
9332
|
}
|
|
@@ -9243,8 +9379,8 @@ var uninstallCommand = async (config, args) => {
|
|
|
9243
9379
|
const runtime = {
|
|
9244
9380
|
server,
|
|
9245
9381
|
cliHome: mendCliHome(),
|
|
9246
|
-
sshConfigFile:
|
|
9247
|
-
signedIn: config.token === null || !
|
|
9382
|
+
sshConfigFile: path17.join(os11.homedir(), ".ssh", "config"),
|
|
9383
|
+
signedIn: config.token === null || !fs16.existsSync(CONFIG_PATH) ? null : { url: config.url, deviceId: config.deviceId },
|
|
9248
9384
|
revokeDevice: async () => {
|
|
9249
9385
|
if (config.deviceId === null) return "no device id saved";
|
|
9250
9386
|
try {
|
|
@@ -9308,7 +9444,7 @@ var keysShare = async (config) => {
|
|
|
9308
9444
|
return fail6("SSH_AUTH_SOCK is not set \u2014 start (or plug in) your ssh-agent first");
|
|
9309
9445
|
}
|
|
9310
9446
|
await shareAgent({
|
|
9311
|
-
url: () => socketUrl(config, "keys-bridge", { host:
|
|
9447
|
+
url: () => socketUrl(config, "keys-bridge", { host: os11.hostname() }),
|
|
9312
9448
|
agentSock,
|
|
9313
9449
|
signal: new AbortController().signal,
|
|
9314
9450
|
onEvent: (event) => {
|
|
@@ -9438,7 +9574,7 @@ ${usageOf("dotfiles repo")}`);
|
|
|
9438
9574
|
var dotfilesSync = async (config, args) => {
|
|
9439
9575
|
const all = args.includes("--all");
|
|
9440
9576
|
const requestedPaths = args.filter((arg) => !arg.startsWith("--"));
|
|
9441
|
-
const home =
|
|
9577
|
+
const home = os11.homedir();
|
|
9442
9578
|
if (!all && requestedPaths.length === 0) {
|
|
9443
9579
|
const found = scanDotfileCandidates(home);
|
|
9444
9580
|
if (found.length === 0) {
|
|
@@ -9465,13 +9601,13 @@ var dotfilesSync = async (config, args) => {
|
|
|
9465
9601
|
if ("error" in read) return fail6(read.error);
|
|
9466
9602
|
const result = await api(config, "POST", "/dotfiles/snapshot", {
|
|
9467
9603
|
files: read.files,
|
|
9468
|
-
source:
|
|
9604
|
+
source: os11.hostname(),
|
|
9469
9605
|
merge: false
|
|
9470
9606
|
});
|
|
9471
9607
|
const snapshot = result.snapshot;
|
|
9472
9608
|
if (snapshot === null) return fail6("the server accepted the sync but reports no snapshot");
|
|
9473
9609
|
say6(
|
|
9474
|
-
`${green6("synced")} ${snapshot.files.length} file${snapshot.files.length === 1 ? "" : "s"} from ${
|
|
9610
|
+
`${green6("synced")} ${snapshot.files.length} file${snapshot.files.length === 1 ? "" : "s"} from ${os11.hostname()} \xB7 ${dim6(snapshot.sha.slice(0, 7))} ${dim6("\u2014 applies from the next session launch")}`
|
|
9475
9611
|
);
|
|
9476
9612
|
};
|
|
9477
9613
|
var envLoad = async (config, args) => {
|
|
@@ -9485,10 +9621,10 @@ var envLoad = async (config, args) => {
|
|
|
9485
9621
|
const positional = args.filter(
|
|
9486
9622
|
(arg, i) => !arg.startsWith("--") && args[i - 1] !== "--project" && !consumed.has(i)
|
|
9487
9623
|
);
|
|
9488
|
-
const file =
|
|
9624
|
+
const file = path17.resolve(positional[0] ?? ".env");
|
|
9489
9625
|
let contents;
|
|
9490
9626
|
try {
|
|
9491
|
-
contents =
|
|
9627
|
+
contents = fs16.readFileSync(file, "utf8");
|
|
9492
9628
|
} catch {
|
|
9493
9629
|
return fail6(`cannot read ${file} \u2014 pass a path: mend env load path/to/.env`);
|
|
9494
9630
|
}
|
|
@@ -9504,10 +9640,10 @@ var envLoad = async (config, args) => {
|
|
|
9504
9640
|
{ contents, allSecret, secretNames }
|
|
9505
9641
|
);
|
|
9506
9642
|
if (report.loaded.length === 0 && report.rejected.length === 0 && report.malformedLines.length === 0) {
|
|
9507
|
-
say6(dim6(`${
|
|
9643
|
+
say6(dim6(`${path17.basename(file)} has no variables`));
|
|
9508
9644
|
return;
|
|
9509
9645
|
}
|
|
9510
|
-
say6(`${green6("\u2713")} loaded ${
|
|
9646
|
+
say6(`${green6("\u2713")} loaded ${path17.basename(file)} into ${project2.name}`);
|
|
9511
9647
|
for (const line of formatLoadReport(report, { dim: dim6, warn: amber2 })) say6(line);
|
|
9512
9648
|
const plaintextUrls = report.loaded.filter(
|
|
9513
9649
|
(entry) => entry.lane === "configuration" && /_(URL|URI|DSN)$/.test(entry.name)
|
|
@@ -9698,7 +9834,7 @@ var skillsList = async (config, args) => {
|
|
|
9698
9834
|
};
|
|
9699
9835
|
var skillsPush = async (config, args) => {
|
|
9700
9836
|
const dirFlag = takeFlagValue4(args, "--dir");
|
|
9701
|
-
const root = dirFlag === null ?
|
|
9837
|
+
const root = dirFlag === null ? path17.join(os11.homedir(), DEFAULT_SKILLS_DIR) : path17.resolve(dirFlag);
|
|
9702
9838
|
const scanned = scanSkillLibrary(root);
|
|
9703
9839
|
if ("error" in scanned) return fail6(scanned.error);
|
|
9704
9840
|
for (const note of scanned.notes) say6(dim6(` ${note.skill}: ${note.message}`));
|
|
@@ -10373,14 +10509,14 @@ var dashboard = async (config, options = {}) => {
|
|
|
10373
10509
|
`the dashboard needs Node >= 26 (node:ffi) \u2014 this is ${process.version}; every other command still works`
|
|
10374
10510
|
);
|
|
10375
10511
|
}
|
|
10376
|
-
const rerun =
|
|
10512
|
+
const rerun = spawnSync6(
|
|
10377
10513
|
process.execPath,
|
|
10378
10514
|
["--experimental-ffi", "--disable-warning=ExperimentalWarning", ...process.argv.slice(1)],
|
|
10379
10515
|
{ stdio: "inherit" }
|
|
10380
10516
|
);
|
|
10381
10517
|
process.exit(rerun.status ?? 0);
|
|
10382
10518
|
}
|
|
10383
|
-
const { runDashboard } = await import("./dashboard-
|
|
10519
|
+
const { runDashboard } = await import("./dashboard-MGR4YUFD.js");
|
|
10384
10520
|
const agentShare = await startShareIfBridge(config);
|
|
10385
10521
|
const tunnels = options.noTunnel === true || serverIsLocal(config) ? null : createServiceTunnels({
|
|
10386
10522
|
listServices: () => fetchServices(config),
|
|
@@ -10414,7 +10550,7 @@ var startShareIfBridge = async (config) => {
|
|
|
10414
10550
|
}
|
|
10415
10551
|
if (access.mode !== "bridge") return null;
|
|
10416
10552
|
return startAgentShare({
|
|
10417
|
-
url: () => socketUrl(config, "keys-bridge", { host:
|
|
10553
|
+
url: () => socketUrl(config, "keys-bridge", { host: os11.hostname() }),
|
|
10418
10554
|
agentSock
|
|
10419
10555
|
});
|
|
10420
10556
|
};
|
|
@@ -10454,10 +10590,10 @@ var manCommand = (words) => {
|
|
|
10454
10590
|
}
|
|
10455
10591
|
const version = cliVersion();
|
|
10456
10592
|
const page = doc === null ? renderManIndex(version) : renderManPage(doc, version);
|
|
10457
|
-
const file =
|
|
10458
|
-
|
|
10459
|
-
const result =
|
|
10460
|
-
|
|
10593
|
+
const file = path17.join(os11.tmpdir(), `mend-man-${process.pid}-${manFileName(doc)}`);
|
|
10594
|
+
fs16.writeFileSync(file, page);
|
|
10595
|
+
const result = spawnSync6("man", ["-l", file], { stdio: "inherit" });
|
|
10596
|
+
fs16.rmSync(file, { force: true });
|
|
10461
10597
|
if (result.error !== void 0 || result.status !== 0) {
|
|
10462
10598
|
say6(doc === null ? renderIndex() : renderCommand(doc));
|
|
10463
10599
|
}
|