@sealant/mend 0.34.5 → 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-5DORQXQT.js → chunk-SMH65F6Z.js} +25 -1
- package/dist/{dashboard-KZ4XGZ6W.js → dashboard-MGR4YUFD.js} +1 -1
- package/dist/main.js +343 -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
|
@@ -34,15 +34,15 @@ import {
|
|
|
34
34
|
sessionDisplayName,
|
|
35
35
|
spoken,
|
|
36
36
|
trackBracketedPaste
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-SMH65F6Z.js";
|
|
38
38
|
|
|
39
39
|
// src/main.ts
|
|
40
|
-
import { spawn as spawn3, spawnSync as
|
|
41
|
-
import * as
|
|
40
|
+
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
41
|
+
import * as fs16 from "node:fs";
|
|
42
42
|
import { createRequire } from "node:module";
|
|
43
43
|
import * as net4 from "node:net";
|
|
44
|
-
import * as
|
|
45
|
-
import * as
|
|
44
|
+
import * as os11 from "node:os";
|
|
45
|
+
import * as path17 from "node:path";
|
|
46
46
|
|
|
47
47
|
// src/agent-share.ts
|
|
48
48
|
import * as net from "node:net";
|
|
@@ -342,15 +342,83 @@ var readClipboardImage = async (platform = process.platform, env = process.env)
|
|
|
342
342
|
return null;
|
|
343
343
|
};
|
|
344
344
|
|
|
345
|
-
// src/
|
|
346
|
-
import
|
|
345
|
+
// src/codex-grant.ts
|
|
346
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
347
|
+
import * as fs2 from "node:fs";
|
|
347
348
|
import * as os2 from "node:os";
|
|
348
|
-
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";
|
|
349
417
|
|
|
350
418
|
// src/server-runtime.ts
|
|
351
419
|
import { spawn } from "node:child_process";
|
|
352
|
-
import * as
|
|
353
|
-
import * as
|
|
420
|
+
import * as fs3 from "node:fs";
|
|
421
|
+
import * as path3 from "node:path";
|
|
354
422
|
var serverProcessDeadlines = {
|
|
355
423
|
ordinary: 3e4,
|
|
356
424
|
pull: 10 * 6e4,
|
|
@@ -396,7 +464,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
396
464
|
return new Promise((resolve7) => {
|
|
397
465
|
let fd;
|
|
398
466
|
try {
|
|
399
|
-
if (options.stdoutFile !== void 0) fd =
|
|
467
|
+
if (options.stdoutFile !== void 0) fd = fs3.openSync(options.stdoutFile, "wx", 384);
|
|
400
468
|
const child = spawn(command, [...args], {
|
|
401
469
|
env,
|
|
402
470
|
detached: process.platform !== "win32",
|
|
@@ -447,12 +515,12 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
447
515
|
child.once("close", (status) => {
|
|
448
516
|
globalThis.clearTimeout(timer);
|
|
449
517
|
try {
|
|
450
|
-
if (fd !== void 0)
|
|
518
|
+
if (fd !== void 0) fs3.fsyncSync(fd);
|
|
451
519
|
} catch {
|
|
452
520
|
failure2 ??= "Could not durably write process output.";
|
|
453
521
|
} finally {
|
|
454
522
|
try {
|
|
455
|
-
if (fd !== void 0)
|
|
523
|
+
if (fd !== void 0) fs3.closeSync(fd);
|
|
456
524
|
} catch {
|
|
457
525
|
failure2 ??= "Could not close process output file.";
|
|
458
526
|
}
|
|
@@ -466,7 +534,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
466
534
|
});
|
|
467
535
|
} catch (cause) {
|
|
468
536
|
try {
|
|
469
|
-
if (fd !== void 0)
|
|
537
|
+
if (fd !== void 0) fs3.closeSync(fd);
|
|
470
538
|
} catch {
|
|
471
539
|
}
|
|
472
540
|
resolve7({
|
|
@@ -487,9 +555,9 @@ var serverComposeArgs = (installation, command) => [
|
|
|
487
555
|
"--project-directory",
|
|
488
556
|
installation.directory,
|
|
489
557
|
"--env-file",
|
|
490
|
-
|
|
558
|
+
path3.join(installation.directory, "server.env"),
|
|
491
559
|
"-f",
|
|
492
|
-
|
|
560
|
+
path3.join(installation.directory, "compose.yaml"),
|
|
493
561
|
...command
|
|
494
562
|
];
|
|
495
563
|
|
|
@@ -535,11 +603,11 @@ var mapLimit = async (items, limit, work) => {
|
|
|
535
603
|
return results;
|
|
536
604
|
};
|
|
537
605
|
var pathOf = (command, env = process.env) => {
|
|
538
|
-
for (const directory of (env["PATH"] ?? "").split(
|
|
606
|
+
for (const directory of (env["PATH"] ?? "").split(path4.delimiter)) {
|
|
539
607
|
if (directory === "") continue;
|
|
540
|
-
const candidate =
|
|
608
|
+
const candidate = path4.join(directory, command);
|
|
541
609
|
try {
|
|
542
|
-
|
|
610
|
+
fs4.accessSync(candidate, fs4.constants.X_OK);
|
|
543
611
|
return candidate;
|
|
544
612
|
} catch {
|
|
545
613
|
}
|
|
@@ -557,9 +625,9 @@ var cliCollector = (deps) => ({
|
|
|
557
625
|
node: process.versions.node,
|
|
558
626
|
platform: process.platform,
|
|
559
627
|
arch: process.arch,
|
|
560
|
-
osType:
|
|
561
|
-
osRelease:
|
|
562
|
-
hostname:
|
|
628
|
+
osType: os3.type(),
|
|
629
|
+
osRelease: os3.release(),
|
|
630
|
+
hostname: os3.hostname(),
|
|
563
631
|
term: deps.env["TERM"] ?? null,
|
|
564
632
|
shell: deps.env["SHELL"] ?? null,
|
|
565
633
|
lang: deps.env["LANG"] ?? null,
|
|
@@ -1029,8 +1097,8 @@ var bundleCollectors = (deps) => {
|
|
|
1029
1097
|
};
|
|
1030
1098
|
|
|
1031
1099
|
// src/doctor-bundle.ts
|
|
1032
|
-
import * as
|
|
1033
|
-
import * as
|
|
1100
|
+
import * as fs5 from "node:fs";
|
|
1101
|
+
import * as path5 from "node:path";
|
|
1034
1102
|
import { gzipSync } from "node:zlib";
|
|
1035
1103
|
|
|
1036
1104
|
// src/help.ts
|
|
@@ -1079,19 +1147,20 @@ var COMMANDS = [
|
|
|
1079
1147
|
summary: "send this machine's provider credential to the platform",
|
|
1080
1148
|
synopsis: ["<claude|codex|github> [--use-my-login] [--from-stdin] [--remove]"],
|
|
1081
1149
|
description: [
|
|
1082
|
-
"Sessions run on the platform, so the platform needs your provider login.
|
|
1083
|
-
"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."
|
|
1084
1152
|
],
|
|
1085
1153
|
options: [
|
|
1086
1154
|
{
|
|
1087
1155
|
flag: "--use-my-login",
|
|
1088
|
-
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"
|
|
1089
1157
|
},
|
|
1090
1158
|
{ flag: "--from-stdin", text: "paste a credential instead of reading the provider's file" },
|
|
1091
1159
|
{ flag: "--remove", text: "disconnect the provider" }
|
|
1092
1160
|
],
|
|
1093
1161
|
examples: [
|
|
1094
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" },
|
|
1095
1164
|
{ command: "mend connect github --remove", text: "" }
|
|
1096
1165
|
],
|
|
1097
1166
|
see: ["accounts"]
|
|
@@ -2470,9 +2539,9 @@ var collectBundle = async (collectors) => {
|
|
|
2470
2539
|
};
|
|
2471
2540
|
var bundleFileName = (now) => `mend-bundle-${now.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-")}.tgz`;
|
|
2472
2541
|
var writeBundle = (files, outPath, now) => {
|
|
2473
|
-
const directory =
|
|
2474
|
-
|
|
2475
|
-
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)$/, "");
|
|
2476
2545
|
const entries = files.map((file) => ({
|
|
2477
2546
|
path: file.path,
|
|
2478
2547
|
content: Buffer.from(file.content, "utf8")
|
|
@@ -2481,8 +2550,8 @@ var writeBundle = (files, outPath, now) => {
|
|
|
2481
2550
|
entries.map((entry) => ({ path: `${root}/${entry.path}`, content: entry.content })),
|
|
2482
2551
|
now
|
|
2483
2552
|
);
|
|
2484
|
-
|
|
2485
|
-
|
|
2553
|
+
fs5.writeFileSync(outPath, bytes, { mode: 384 });
|
|
2554
|
+
fs5.chmodSync(outPath, 384);
|
|
2486
2555
|
return {
|
|
2487
2556
|
path: outPath,
|
|
2488
2557
|
bytes: bytes.length,
|
|
@@ -2543,7 +2612,7 @@ var doctorBundleCommand = async (args, deps) => {
|
|
|
2543
2612
|
return;
|
|
2544
2613
|
}
|
|
2545
2614
|
const now = deps.now();
|
|
2546
|
-
const outPath =
|
|
2615
|
+
const outPath = path5.resolve(parsed.args.out ?? path5.join(deps.defaultDir, bundleFileName(now)));
|
|
2547
2616
|
deps.warn("collecting \xB7 this can take a minute");
|
|
2548
2617
|
const files = await collectBundle(deps.collectors(parsed.args.tail));
|
|
2549
2618
|
const written = writeBundle(files, outPath, now);
|
|
@@ -2551,14 +2620,14 @@ var doctorBundleCommand = async (args, deps) => {
|
|
|
2551
2620
|
};
|
|
2552
2621
|
|
|
2553
2622
|
// src/doctor.ts
|
|
2554
|
-
import * as
|
|
2555
|
-
import * as
|
|
2623
|
+
import * as fs7 from "node:fs";
|
|
2624
|
+
import * as path7 from "node:path";
|
|
2556
2625
|
|
|
2557
2626
|
// src/docker-shutdown.ts
|
|
2558
|
-
import { spawnSync as
|
|
2559
|
-
import * as
|
|
2560
|
-
import * as
|
|
2561
|
-
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";
|
|
2562
2631
|
var DOCKER_CAPTURE_STOP_GRACE_SECONDS = 3600;
|
|
2563
2632
|
var DOCKERD_DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 15;
|
|
2564
2633
|
var flagValue = (argv, name) => {
|
|
@@ -2583,12 +2652,12 @@ var daemonKindOf = (info) => {
|
|
|
2583
2652
|
var defaultConfigPath = (facts, daemon) => {
|
|
2584
2653
|
switch (daemon) {
|
|
2585
2654
|
case "desktop":
|
|
2586
|
-
return
|
|
2655
|
+
return path6.join(facts.home, ".docker", "daemon.json");
|
|
2587
2656
|
case "orbstack":
|
|
2588
|
-
return
|
|
2657
|
+
return path6.join(facts.home, ".orbstack", "config", "docker.json");
|
|
2589
2658
|
case "rootless":
|
|
2590
|
-
return
|
|
2591
|
-
facts.xdgConfigHome ??
|
|
2659
|
+
return path6.join(
|
|
2660
|
+
facts.xdgConfigHome ?? path6.join(facts.home, ".config"),
|
|
2592
2661
|
"docker",
|
|
2593
2662
|
"daemon.json"
|
|
2594
2663
|
);
|
|
@@ -2717,7 +2786,7 @@ var parseDockerInfo = (stdout) => {
|
|
|
2717
2786
|
};
|
|
2718
2787
|
var readHostFile = (file) => {
|
|
2719
2788
|
try {
|
|
2720
|
-
return { kind: "read", text:
|
|
2789
|
+
return { kind: "read", text: fs6.readFileSync(file, "utf8") };
|
|
2721
2790
|
} catch (cause) {
|
|
2722
2791
|
return cause instanceof Error && "code" in cause && cause.code === "ENOENT" ? { kind: "absent" } : { kind: "unreadable" };
|
|
2723
2792
|
}
|
|
@@ -2725,21 +2794,21 @@ var readHostFile = (file) => {
|
|
|
2725
2794
|
var hostProcView = {
|
|
2726
2795
|
list: (dir) => {
|
|
2727
2796
|
try {
|
|
2728
|
-
return
|
|
2797
|
+
return fs6.readdirSync(dir);
|
|
2729
2798
|
} catch {
|
|
2730
2799
|
return null;
|
|
2731
2800
|
}
|
|
2732
2801
|
},
|
|
2733
2802
|
read: (file) => {
|
|
2734
2803
|
try {
|
|
2735
|
-
return
|
|
2804
|
+
return fs6.readFileSync(file, "utf8");
|
|
2736
2805
|
} catch {
|
|
2737
2806
|
return null;
|
|
2738
2807
|
}
|
|
2739
2808
|
},
|
|
2740
2809
|
link: (file) => {
|
|
2741
2810
|
try {
|
|
2742
|
-
return
|
|
2811
|
+
return fs6.readlinkSync(file);
|
|
2743
2812
|
} catch {
|
|
2744
2813
|
return null;
|
|
2745
2814
|
}
|
|
@@ -2779,7 +2848,7 @@ var observeDockerd = (proc, host) => {
|
|
|
2779
2848
|
const cmdline = proc.read(`/proc/${entry}/cmdline`);
|
|
2780
2849
|
if (cmdline === null) continue;
|
|
2781
2850
|
const argv = cmdline.split("\0").filter((arg) => arg !== "");
|
|
2782
|
-
if (
|
|
2851
|
+
if (path6.basename(argv[0] ?? "") === "dockerd") daemons.push({ pid: entry, argv });
|
|
2783
2852
|
}
|
|
2784
2853
|
if (daemons.length === 0) return { kind: "none" };
|
|
2785
2854
|
const socket = unixSocketOf(host);
|
|
@@ -2839,7 +2908,7 @@ var observeDockerd = (proc, host) => {
|
|
|
2839
2908
|
var hostDockerEndpoint = () => {
|
|
2840
2909
|
const fromEnv = process.env["DOCKER_HOST"];
|
|
2841
2910
|
if (fromEnv !== void 0 && fromEnv.trim() !== "") return fromEnv.trim();
|
|
2842
|
-
const context =
|
|
2911
|
+
const context = spawnSync3(
|
|
2843
2912
|
"docker",
|
|
2844
2913
|
["context", "inspect", "--format", "{{.Endpoints.docker.Host}}"],
|
|
2845
2914
|
{
|
|
@@ -2865,11 +2934,11 @@ var hostDockerDaemonFacts = (infoStdout) => ({
|
|
|
2865
2934
|
info: infoStdout === null ? null : parseDockerInfo(infoStdout),
|
|
2866
2935
|
...dockerdFactsOf(observeDockerd(hostProcView, hostDockerEndpoint())),
|
|
2867
2936
|
readFile: readHostFile,
|
|
2868
|
-
home:
|
|
2937
|
+
home: os4.homedir(),
|
|
2869
2938
|
xdgConfigHome: process.env["XDG_CONFIG_HOME"] ?? null
|
|
2870
2939
|
});
|
|
2871
2940
|
var observeHostShutdownTimeout = () => {
|
|
2872
|
-
const info =
|
|
2941
|
+
const info = spawnSync3("docker", ["info", "--format", "{{json .}}"], {
|
|
2873
2942
|
encoding: "utf8",
|
|
2874
2943
|
timeout: 3e3,
|
|
2875
2944
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -3028,7 +3097,8 @@ var runChecks = async (config, probes) => {
|
|
|
3028
3097
|
} : {
|
|
3029
3098
|
label: provider,
|
|
3030
3099
|
state: "todo",
|
|
3031
|
-
|
|
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,
|
|
3032
3102
|
fix: `mend connect ${provider}`
|
|
3033
3103
|
}
|
|
3034
3104
|
);
|
|
@@ -3098,10 +3168,10 @@ var runChecks = async (config, probes) => {
|
|
|
3098
3168
|
return checks;
|
|
3099
3169
|
};
|
|
3100
3170
|
var onPath = (command) => {
|
|
3101
|
-
for (const directory of (process.env["PATH"] ?? "").split(
|
|
3171
|
+
for (const directory of (process.env["PATH"] ?? "").split(path7.delimiter)) {
|
|
3102
3172
|
if (directory === "") continue;
|
|
3103
3173
|
try {
|
|
3104
|
-
|
|
3174
|
+
fs7.accessSync(path7.join(directory, command), fs7.constants.X_OK);
|
|
3105
3175
|
return true;
|
|
3106
3176
|
} catch {
|
|
3107
3177
|
}
|
|
@@ -3126,8 +3196,8 @@ var doctorCommand = async (config, localCredential2, claudeGrant2) => {
|
|
|
3126
3196
|
};
|
|
3127
3197
|
|
|
3128
3198
|
// src/dotfiles.ts
|
|
3129
|
-
import * as
|
|
3130
|
-
import * as
|
|
3199
|
+
import * as fs8 from "node:fs";
|
|
3200
|
+
import * as path8 from "node:path";
|
|
3131
3201
|
var DOTFILE_CANDIDATES = [
|
|
3132
3202
|
{
|
|
3133
3203
|
group: "shell",
|
|
@@ -3170,7 +3240,7 @@ var DOTFILE_CANDIDATES = [
|
|
|
3170
3240
|
var MAX_FILE_BYTES = 1024 * 1024;
|
|
3171
3241
|
var statFile = (home, relative4) => {
|
|
3172
3242
|
try {
|
|
3173
|
-
const stat =
|
|
3243
|
+
const stat = fs8.statSync(path8.join(home, relative4));
|
|
3174
3244
|
return stat.isFile() ? stat : null;
|
|
3175
3245
|
} catch {
|
|
3176
3246
|
return null;
|
|
@@ -3183,9 +3253,9 @@ var scanDotfileCandidates = (home) => DOTFILE_CANDIDATES.flatMap(
|
|
|
3183
3253
|
})
|
|
3184
3254
|
);
|
|
3185
3255
|
var homeRelativePath = (home, requested) => {
|
|
3186
|
-
const relative4 =
|
|
3187
|
-
const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${
|
|
3188
|
-
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("/");
|
|
3189
3259
|
};
|
|
3190
3260
|
var readSyncFiles = (home, paths_) => {
|
|
3191
3261
|
const files = [];
|
|
@@ -3205,7 +3275,7 @@ var readSyncFiles = (home, paths_) => {
|
|
|
3205
3275
|
}
|
|
3206
3276
|
files.push({
|
|
3207
3277
|
path: relative4,
|
|
3208
|
-
contentsBase64:
|
|
3278
|
+
contentsBase64: fs8.readFileSync(path8.join(home, relative4)).toString("base64"),
|
|
3209
3279
|
// eslint-disable-next-line no-bitwise -- permission bits are the point
|
|
3210
3280
|
mode: (stat.mode & 511).toString(8)
|
|
3211
3281
|
});
|
|
@@ -3304,10 +3374,10 @@ var parseGitAuthorArgs = (args) => {
|
|
|
3304
3374
|
var gitAuthorLine = (author) => `${author.name} <${author.email}> \xB7 ${author.source === "setting" ? "your setting" : "your account's name and email"}`;
|
|
3305
3375
|
|
|
3306
3376
|
// src/landing.ts
|
|
3307
|
-
import { spawnSync as
|
|
3308
|
-
import * as
|
|
3309
|
-
import * as
|
|
3310
|
-
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";
|
|
3311
3381
|
var paint = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
|
|
3312
3382
|
var dim = paint("2");
|
|
3313
3383
|
var green = paint("32");
|
|
@@ -3522,7 +3592,7 @@ var landCommand = async (api2, args) => {
|
|
|
3522
3592
|
if (!landingSucceeded(report)) process.exitCode = 1;
|
|
3523
3593
|
};
|
|
3524
3594
|
var git = (cwd, args) => {
|
|
3525
|
-
const result =
|
|
3595
|
+
const result = spawnSync4("git", args, { cwd, encoding: "utf8" });
|
|
3526
3596
|
return {
|
|
3527
3597
|
status: result.status ?? 1,
|
|
3528
3598
|
stdout: result.stdout ?? "",
|
|
@@ -3545,9 +3615,9 @@ var gitRemotes = (cwd) => {
|
|
|
3545
3615
|
var remoteForOrigin = (remotes, originUrl, cwd) => {
|
|
3546
3616
|
if (originUrl === null) return null;
|
|
3547
3617
|
const origin = normalizeRemoteUrl(originUrl);
|
|
3548
|
-
const originPath = origin === null ?
|
|
3618
|
+
const originPath = origin === null ? path9.resolve(originUrl.replace(/^file:\/\//, "")) : null;
|
|
3549
3619
|
return remotes.find(
|
|
3550
|
-
(remote) => origin === null ?
|
|
3620
|
+
(remote) => origin === null ? path9.resolve(cwd, remote.url.replace(/^file:\/\//, "")) === originPath : normalizeRemoteUrl(remote.url) === origin
|
|
3551
3621
|
) ?? null;
|
|
3552
3622
|
};
|
|
3553
3623
|
var LOG_LINES = 10;
|
|
@@ -3570,10 +3640,10 @@ var fetchBundle = (cwd, bundle) => {
|
|
|
3570
3640
|
const before = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
|
|
3571
3641
|
const previous = before.status === 0 ? before.stdout.trim() : null;
|
|
3572
3642
|
if (previous !== bundle.tip) {
|
|
3573
|
-
const dir =
|
|
3643
|
+
const dir = fs9.mkdtempSync(path9.join(os5.tmpdir(), "mend-pull-"));
|
|
3574
3644
|
try {
|
|
3575
|
-
const file =
|
|
3576
|
-
|
|
3645
|
+
const file = path9.join(dir, "change.bundle");
|
|
3646
|
+
fs9.writeFileSync(file, bundle.bytes, { mode: 384 });
|
|
3577
3647
|
const fetched = git(cwd, [
|
|
3578
3648
|
"fetch",
|
|
3579
3649
|
"--no-tags",
|
|
@@ -3583,7 +3653,7 @@ var fetchBundle = (cwd, bundle) => {
|
|
|
3583
3653
|
]);
|
|
3584
3654
|
if (fetched.status !== 0) return refusedWith(gitWords(fetched));
|
|
3585
3655
|
} finally {
|
|
3586
|
-
|
|
3656
|
+
fs9.rmSync(dir, { recursive: true, force: true });
|
|
3587
3657
|
}
|
|
3588
3658
|
}
|
|
3589
3659
|
const after = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]).stdout.trim();
|
|
@@ -3774,7 +3844,7 @@ var followStart = async (options) => {
|
|
|
3774
3844
|
|
|
3775
3845
|
// src/login.ts
|
|
3776
3846
|
import { spawn as spawn2 } from "node:child_process";
|
|
3777
|
-
import * as
|
|
3847
|
+
import * as os6 from "node:os";
|
|
3778
3848
|
|
|
3779
3849
|
// src/pair.ts
|
|
3780
3850
|
var paint2 = (code) => (text) => process.stdout.isTTY === true ? `\x1B[${code}m${text}\x1B[0m` : text;
|
|
@@ -3966,7 +4036,7 @@ var resolveServerUrl = async (args, deps) => {
|
|
|
3966
4036
|
};
|
|
3967
4037
|
var loginCommand = async (args, deps) => {
|
|
3968
4038
|
const base = await resolveServerUrl(args, deps);
|
|
3969
|
-
const started = await post(`${base}/api/cli/auth`, { name:
|
|
4039
|
+
const started = await post(`${base}/api/cli/auth`, { name: os6.hostname() });
|
|
3970
4040
|
if (started.status === 429) {
|
|
3971
4041
|
return fail3(
|
|
3972
4042
|
`the server asked for a pause \u2014 try again in ${retryAfterSecondsOf(started.json)}s`
|
|
@@ -4017,8 +4087,8 @@ var loginCommand = async (args, deps) => {
|
|
|
4017
4087
|
};
|
|
4018
4088
|
|
|
4019
4089
|
// src/organization.ts
|
|
4020
|
-
import * as
|
|
4021
|
-
import * as
|
|
4090
|
+
import * as fs10 from "node:fs";
|
|
4091
|
+
import * as path10 from "node:path";
|
|
4022
4092
|
var paint4 = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
|
|
4023
4093
|
var dim4 = paint4("2");
|
|
4024
4094
|
var green4 = paint4("32");
|
|
@@ -4082,7 +4152,7 @@ var scanFolder = (dir) => {
|
|
|
4082
4152
|
const files = [];
|
|
4083
4153
|
const skipped = [];
|
|
4084
4154
|
const walk = (relative4) => {
|
|
4085
|
-
const entries =
|
|
4155
|
+
const entries = fs10.readdirSync(path10.join(dir, relative4), { withFileTypes: true }).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
4086
4156
|
for (const entry of entries) {
|
|
4087
4157
|
const child = relative4 === "" ? entry.name : `${relative4}/${entry.name}`;
|
|
4088
4158
|
if (entry.isSymbolicLink()) {
|
|
@@ -4091,7 +4161,7 @@ var scanFolder = (dir) => {
|
|
|
4091
4161
|
if (SKIPPED_DIRECTORIES.has(entry.name)) skipped.push({ path: child, reason: "skipped" });
|
|
4092
4162
|
else walk(child);
|
|
4093
4163
|
} else if (entry.isFile()) {
|
|
4094
|
-
const size =
|
|
4164
|
+
const size = fs10.statSync(path10.join(dir, child)).size;
|
|
4095
4165
|
if (size > FOLDER_FILE_BYTES) skipped.push({ path: child, reason: "over 1 MiB" });
|
|
4096
4166
|
else files.push({ path: child, size });
|
|
4097
4167
|
}
|
|
@@ -4144,7 +4214,7 @@ var folderCommand = async (api2, args) => {
|
|
|
4144
4214
|
}
|
|
4145
4215
|
case "push": {
|
|
4146
4216
|
if (dir === void 0) return fail4("usage: mend folder push <name> <dir> [--replace]");
|
|
4147
|
-
if (!
|
|
4217
|
+
if (!fs10.existsSync(dir) || !fs10.statSync(dir).isDirectory()) {
|
|
4148
4218
|
return fail4(`${dir} is not a directory`);
|
|
4149
4219
|
}
|
|
4150
4220
|
const folder = await folderNamed(api2, name);
|
|
@@ -4159,7 +4229,7 @@ var folderCommand = async (api2, args) => {
|
|
|
4159
4229
|
files: batch.map((file) => ({
|
|
4160
4230
|
path: file.path,
|
|
4161
4231
|
// Read one batch at a time, so a large tree never sits in memory at once.
|
|
4162
|
-
contentsBase64:
|
|
4232
|
+
contentsBase64: fs10.readFileSync(path10.join(dir, file.path)).toString("base64")
|
|
4163
4233
|
})),
|
|
4164
4234
|
// --replace empties the folder with the first batch; the rest add to it.
|
|
4165
4235
|
merge: !(replace && index === 0)
|
|
@@ -4343,8 +4413,8 @@ var operatorCommand = async (api2, tryApi, baseUrl, args) => {
|
|
|
4343
4413
|
import { createHmac, randomBytes } from "node:crypto";
|
|
4344
4414
|
import { readFile } from "node:fs/promises";
|
|
4345
4415
|
import * as net2 from "node:net";
|
|
4346
|
-
import * as
|
|
4347
|
-
import * as
|
|
4416
|
+
import * as os8 from "node:os";
|
|
4417
|
+
import * as path12 from "node:path";
|
|
4348
4418
|
|
|
4349
4419
|
// src/server-docker-volumes.ts
|
|
4350
4420
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -4562,9 +4632,9 @@ var verifyServerDockerVolumes = (runtime, input) => ownership(runtime, input, "v
|
|
|
4562
4632
|
|
|
4563
4633
|
// src/server-store.ts
|
|
4564
4634
|
import { randomUUID } from "node:crypto";
|
|
4565
|
-
import * as
|
|
4566
|
-
import * as
|
|
4567
|
-
import * as
|
|
4635
|
+
import * as fs11 from "node:fs";
|
|
4636
|
+
import * as os7 from "node:os";
|
|
4637
|
+
import * as path11 from "node:path";
|
|
4568
4638
|
var ServerStoreError = class extends Error {
|
|
4569
4639
|
_tag = "ServerStoreError";
|
|
4570
4640
|
};
|
|
@@ -4585,28 +4655,28 @@ var attempt = (operation) => {
|
|
|
4585
4655
|
var hasCode = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
4586
4656
|
var readOptional = (file) => {
|
|
4587
4657
|
try {
|
|
4588
|
-
return
|
|
4658
|
+
return fs11.readFileSync(file, "utf8");
|
|
4589
4659
|
} catch (cause) {
|
|
4590
4660
|
if (hasCode(cause, "ENOENT")) return null;
|
|
4591
4661
|
throw cause;
|
|
4592
4662
|
}
|
|
4593
4663
|
};
|
|
4594
4664
|
var syncDirectory = (directory) => {
|
|
4595
|
-
const fd =
|
|
4665
|
+
const fd = fs11.openSync(directory, "r");
|
|
4596
4666
|
try {
|
|
4597
|
-
|
|
4667
|
+
fs11.fsyncSync(fd);
|
|
4598
4668
|
} finally {
|
|
4599
|
-
|
|
4669
|
+
fs11.closeSync(fd);
|
|
4600
4670
|
}
|
|
4601
4671
|
};
|
|
4602
4672
|
var writeDurable = (file, content, mode = 384) => {
|
|
4603
|
-
const fd =
|
|
4673
|
+
const fd = fs11.openSync(file, "wx", mode);
|
|
4604
4674
|
try {
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4675
|
+
fs11.writeFileSync(fd, content, "utf8");
|
|
4676
|
+
fs11.fchmodSync(fd, mode);
|
|
4677
|
+
fs11.fsyncSync(fd);
|
|
4608
4678
|
} finally {
|
|
4609
|
-
|
|
4679
|
+
fs11.closeSync(fd);
|
|
4610
4680
|
}
|
|
4611
4681
|
};
|
|
4612
4682
|
var fileKeys = ["identity", "config", "env", "compose", "postgresInit"];
|
|
@@ -4626,11 +4696,11 @@ var parseLockOwner = (raw) => {
|
|
|
4626
4696
|
};
|
|
4627
4697
|
var describeOwner = (lockDir) => {
|
|
4628
4698
|
try {
|
|
4629
|
-
const raw = JSON.parse(
|
|
4699
|
+
const raw = JSON.parse(fs11.readFileSync(path11.join(lockDir, "owner.json"), "utf8"));
|
|
4630
4700
|
const owner = parseLockOwner(raw);
|
|
4631
4701
|
if (owner === null) return "owner metadata is invalid";
|
|
4632
4702
|
const label = `PID ${owner.pid} on ${owner.hostname}`;
|
|
4633
|
-
if (owner.hostname !==
|
|
4703
|
+
if (owner.hostname !== os7.hostname()) return label;
|
|
4634
4704
|
try {
|
|
4635
4705
|
process.kill(owner.pid, 0);
|
|
4636
4706
|
return `${label} is still live`;
|
|
@@ -4646,28 +4716,28 @@ var lockGuidance = (lockDir) => new ServerStoreError(
|
|
|
4646
4716
|
);
|
|
4647
4717
|
var acquireLock = (configDir, create) => {
|
|
4648
4718
|
if (create) {
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
} else if (!
|
|
4719
|
+
fs11.mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
4720
|
+
fs11.chmodSync(configDir, 448);
|
|
4721
|
+
} else if (!fs11.existsSync(configDir)) {
|
|
4652
4722
|
throw new ServerStoreError(
|
|
4653
4723
|
"No Mend server is configured. Run mend server setup explicitly to install one."
|
|
4654
4724
|
);
|
|
4655
4725
|
}
|
|
4656
|
-
const lockDir =
|
|
4726
|
+
const lockDir = path11.join(configDir, "server.lock");
|
|
4657
4727
|
try {
|
|
4658
|
-
|
|
4728
|
+
fs11.mkdirSync(lockDir, { mode: 448 });
|
|
4659
4729
|
} catch (cause) {
|
|
4660
4730
|
if (hasCode(cause, "EEXIST")) throw lockGuidance(lockDir);
|
|
4661
4731
|
throw cause;
|
|
4662
4732
|
}
|
|
4663
|
-
const owned =
|
|
4664
|
-
const ownerFile =
|
|
4665
|
-
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() })}
|
|
4666
4736
|
`;
|
|
4667
4737
|
let open = true;
|
|
4668
4738
|
const assertOwned = () => {
|
|
4669
|
-
const current =
|
|
4670
|
-
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) {
|
|
4671
4741
|
throw new ServerStoreError(
|
|
4672
4742
|
"Server lock ownership was lost. Stop and check for another server command before retrying."
|
|
4673
4743
|
);
|
|
@@ -4683,8 +4753,8 @@ var acquireLock = (configDir, create) => {
|
|
|
4683
4753
|
release: () => {
|
|
4684
4754
|
const result = attempt(() => {
|
|
4685
4755
|
assertOwned();
|
|
4686
|
-
|
|
4687
|
-
|
|
4756
|
+
fs11.unlinkSync(ownerFile);
|
|
4757
|
+
fs11.rmdirSync(lockDir);
|
|
4688
4758
|
});
|
|
4689
4759
|
open = false;
|
|
4690
4760
|
return result;
|
|
@@ -4693,7 +4763,7 @@ var acquireLock = (configDir, create) => {
|
|
|
4693
4763
|
};
|
|
4694
4764
|
var readIdentity = (paths) => {
|
|
4695
4765
|
const identity = readOptional(paths.identity);
|
|
4696
|
-
if (identity === null && (
|
|
4766
|
+
if (identity === null && (fs11.existsSync(paths.active) || fs11.existsSync(paths.generations) && fs11.readdirSync(paths.generations).length > 0)) {
|
|
4697
4767
|
throw new ServerStoreError(
|
|
4698
4768
|
"Server identity.env is missing. Restore it from a retained generation or backup; refusing to generate replacement credentials."
|
|
4699
4769
|
);
|
|
@@ -4703,7 +4773,7 @@ var readIdentity = (paths) => {
|
|
|
4703
4773
|
var readActive = (paths) => {
|
|
4704
4774
|
let target;
|
|
4705
4775
|
try {
|
|
4706
|
-
target =
|
|
4776
|
+
target = fs11.readlinkSync(paths.active);
|
|
4707
4777
|
} catch (cause) {
|
|
4708
4778
|
if (hasCode(cause, "ENOENT")) return null;
|
|
4709
4779
|
throw cause;
|
|
@@ -4713,13 +4783,13 @@ var readActive = (paths) => {
|
|
|
4713
4783
|
"Server active pointer is invalid. Restore the symlink to a retained generation before retrying."
|
|
4714
4784
|
);
|
|
4715
4785
|
}
|
|
4716
|
-
const directory =
|
|
4786
|
+
const directory = path11.join(paths.configDir, target);
|
|
4717
4787
|
const files = {
|
|
4718
|
-
identity:
|
|
4719
|
-
config:
|
|
4720
|
-
env:
|
|
4721
|
-
compose:
|
|
4722
|
-
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")
|
|
4723
4793
|
};
|
|
4724
4794
|
if (readIdentity(paths) !== files.identity) {
|
|
4725
4795
|
throw new ServerStoreError(
|
|
@@ -4729,20 +4799,20 @@ var readActive = (paths) => {
|
|
|
4729
4799
|
return { directory, files };
|
|
4730
4800
|
};
|
|
4731
4801
|
var publishIdentity = (paths, identity) => {
|
|
4732
|
-
const temporary =
|
|
4802
|
+
const temporary = path11.join(paths.configDir, `.identity-${randomUUID()}`);
|
|
4733
4803
|
writeDurable(temporary, identity);
|
|
4734
|
-
|
|
4804
|
+
fs11.linkSync(temporary, paths.identity);
|
|
4735
4805
|
syncDirectory(paths.configDir);
|
|
4736
|
-
|
|
4806
|
+
fs11.unlinkSync(temporary);
|
|
4737
4807
|
};
|
|
4738
4808
|
var prepareGeneration = (paths, files) => {
|
|
4739
|
-
|
|
4809
|
+
fs11.mkdirSync(paths.generations, { recursive: true, mode: 448 });
|
|
4740
4810
|
const generationName = `gen-${randomUUID()}`;
|
|
4741
|
-
const directory =
|
|
4742
|
-
|
|
4811
|
+
const directory = path11.join(paths.generations, generationName);
|
|
4812
|
+
fs11.mkdirSync(directory, { mode: 448 });
|
|
4743
4813
|
for (const key of fileKeys) {
|
|
4744
4814
|
writeDurable(
|
|
4745
|
-
|
|
4815
|
+
path11.join(directory, fileNames[key]),
|
|
4746
4816
|
files[key],
|
|
4747
4817
|
key === "postgresInit" ? 493 : 384
|
|
4748
4818
|
);
|
|
@@ -4753,18 +4823,18 @@ var prepareGeneration = (paths, files) => {
|
|
|
4753
4823
|
return { directory, files };
|
|
4754
4824
|
};
|
|
4755
4825
|
var activateGeneration = (paths, generation) => {
|
|
4756
|
-
const relative4 =
|
|
4757
|
-
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")) {
|
|
4758
4828
|
throw new ServerStoreError("Cannot activate a generation outside this installation identity.");
|
|
4759
4829
|
}
|
|
4760
4830
|
for (const key of fileKeys) {
|
|
4761
|
-
if (
|
|
4831
|
+
if (fs11.readFileSync(path11.join(generation.directory, fileNames[key]), "utf8") !== generation.files[key]) {
|
|
4762
4832
|
throw new ServerStoreError("Cannot activate an incomplete or changed server generation.");
|
|
4763
4833
|
}
|
|
4764
4834
|
}
|
|
4765
|
-
const pointer =
|
|
4766
|
-
|
|
4767
|
-
|
|
4835
|
+
const pointer = path11.join(paths.configDir, `.active-${randomUUID()}`);
|
|
4836
|
+
fs11.symlinkSync(relative4, pointer);
|
|
4837
|
+
fs11.renameSync(pointer, paths.active);
|
|
4768
4838
|
syncDirectory(paths.configDir);
|
|
4769
4839
|
};
|
|
4770
4840
|
var prepareFiles = (paths, files) => {
|
|
@@ -4773,7 +4843,7 @@ var prepareFiles = (paths, files) => {
|
|
|
4773
4843
|
throw new ServerStoreError("Server identity already exists; refusing to replace credentials.");
|
|
4774
4844
|
}
|
|
4775
4845
|
const active = readActive(paths);
|
|
4776
|
-
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)
|
|
4777
4847
|
return active;
|
|
4778
4848
|
if (identity === null) publishIdentity(paths, files.identity);
|
|
4779
4849
|
return prepareGeneration(paths, files);
|
|
@@ -4786,9 +4856,9 @@ var commitGeneration = (paths, files) => {
|
|
|
4786
4856
|
var createStore = (configDir, lock) => {
|
|
4787
4857
|
const paths = {
|
|
4788
4858
|
configDir,
|
|
4789
|
-
identity:
|
|
4790
|
-
generations:
|
|
4791
|
-
active:
|
|
4859
|
+
identity: path11.join(configDir, "identity.env"),
|
|
4860
|
+
generations: path11.join(configDir, "generations"),
|
|
4861
|
+
active: path11.join(configDir, "active")
|
|
4792
4862
|
};
|
|
4793
4863
|
const whileOwned = (operation) => attempt(() => {
|
|
4794
4864
|
lock.assertOwned();
|
|
@@ -4801,13 +4871,13 @@ var createStore = (configDir, lock) => {
|
|
|
4801
4871
|
prepare: (files) => whileOwned(() => prepareFiles(paths, files)),
|
|
4802
4872
|
activate: (generation) => whileOwned(() => activateGeneration(paths, generation)),
|
|
4803
4873
|
createBackup: (previous, target) => whileOwned(() => {
|
|
4804
|
-
const backups =
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
const directory =
|
|
4808
|
-
|
|
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 });
|
|
4809
4879
|
writeDurable(
|
|
4810
|
-
|
|
4880
|
+
path11.join(directory, "recovery.json"),
|
|
4811
4881
|
`${JSON.stringify(
|
|
4812
4882
|
{
|
|
4813
4883
|
previousGeneration: previous.directory,
|
|
@@ -4823,21 +4893,21 @@ var createStore = (configDir, lock) => {
|
|
|
4823
4893
|
syncDirectory(directory);
|
|
4824
4894
|
syncDirectory(backups);
|
|
4825
4895
|
syncDirectory(configDir);
|
|
4826
|
-
const partialFile =
|
|
4896
|
+
const partialFile = path11.join(directory, "database.sql.partial");
|
|
4827
4897
|
return {
|
|
4828
4898
|
directory,
|
|
4829
4899
|
partialFile,
|
|
4830
4900
|
complete: () => whileOwned(() => {
|
|
4831
|
-
if (
|
|
4901
|
+
if (fs11.statSync(partialFile).size === 0)
|
|
4832
4902
|
throw new ServerStoreError("Database backup is empty; refusing target startup.");
|
|
4833
|
-
const fd =
|
|
4903
|
+
const fd = fs11.openSync(partialFile, "r");
|
|
4834
4904
|
try {
|
|
4835
|
-
|
|
4836
|
-
|
|
4905
|
+
fs11.fchmodSync(fd, 384);
|
|
4906
|
+
fs11.fsyncSync(fd);
|
|
4837
4907
|
} finally {
|
|
4838
|
-
|
|
4908
|
+
fs11.closeSync(fd);
|
|
4839
4909
|
}
|
|
4840
|
-
|
|
4910
|
+
fs11.renameSync(partialFile, path11.join(directory, "database.sql"));
|
|
4841
4911
|
syncDirectory(directory);
|
|
4842
4912
|
})
|
|
4843
4913
|
};
|
|
@@ -4846,7 +4916,7 @@ var createStore = (configDir, lock) => {
|
|
|
4846
4916
|
};
|
|
4847
4917
|
var refuseFlatLayout = (configDir) => {
|
|
4848
4918
|
if (["server.json", "server.env", "compose.yaml", "postgres-init.sh"].some(
|
|
4849
|
-
(name) =>
|
|
4919
|
+
(name) => fs11.existsSync(path11.join(configDir, name))
|
|
4850
4920
|
)) {
|
|
4851
4921
|
throw new ServerStoreError(
|
|
4852
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."
|
|
@@ -4854,7 +4924,7 @@ var refuseFlatLayout = (configDir) => {
|
|
|
4854
4924
|
}
|
|
4855
4925
|
};
|
|
4856
4926
|
var withServerStore = async (directory, operation, options = {}) => {
|
|
4857
|
-
const configDir =
|
|
4927
|
+
const configDir = path11.resolve(directory);
|
|
4858
4928
|
let lock;
|
|
4859
4929
|
let result;
|
|
4860
4930
|
try {
|
|
@@ -4871,9 +4941,9 @@ var withServerStore = async (directory, operation, options = {}) => {
|
|
|
4871
4941
|
};
|
|
4872
4942
|
|
|
4873
4943
|
// src/version.ts
|
|
4874
|
-
import * as
|
|
4944
|
+
import * as fs12 from "node:fs";
|
|
4875
4945
|
var cliVersion = () => {
|
|
4876
|
-
const raw =
|
|
4946
|
+
const raw = fs12.readFileSync(new URL("../package.json", import.meta.url), "utf8");
|
|
4877
4947
|
const parsed = JSON.parse(raw);
|
|
4878
4948
|
if (typeof parsed === "object" && parsed !== null && "version" in parsed) {
|
|
4879
4949
|
const version = parsed.version;
|
|
@@ -5576,7 +5646,7 @@ var resolveServerVersion = async (runtime, options, existing) => {
|
|
|
5576
5646
|
return version;
|
|
5577
5647
|
};
|
|
5578
5648
|
var parseDockerSocketPath = (socket) => {
|
|
5579
|
-
if (!
|
|
5649
|
+
if (!path12.isAbsolute(socket) || /[\r\n$'"#:\\]/.test(socket)) {
|
|
5580
5650
|
throw setupError(
|
|
5581
5651
|
"--docker-socket must be an absolute path without line breaks or Compose interpolation characters."
|
|
5582
5652
|
);
|
|
@@ -5602,8 +5672,8 @@ var resolveAssets = async (runtime, serverVersion, existing, store, options) =>
|
|
|
5602
5672
|
if (options.assetsDir !== void 0) {
|
|
5603
5673
|
try {
|
|
5604
5674
|
const [compose2, postgresInit2] = await Promise.all([
|
|
5605
|
-
readFile(
|
|
5606
|
-
readFile(
|
|
5675
|
+
readFile(path12.join(options.assetsDir, COMPOSE_ASSET), "utf8"),
|
|
5676
|
+
readFile(path12.join(options.assetsDir, POSTGRES_INIT_ASSET), "utf8")
|
|
5607
5677
|
]);
|
|
5608
5678
|
validateComposeAsset(compose2);
|
|
5609
5679
|
validatePostgresAsset(postgresInit2);
|
|
@@ -6129,10 +6199,10 @@ var manageServer = async (command, args, runtime, store) => {
|
|
|
6129
6199
|
};
|
|
6130
6200
|
var nodeServerRuntime = () => {
|
|
6131
6201
|
const environment = { ...process.env };
|
|
6132
|
-
const home =
|
|
6133
|
-
const configHome = environment["XDG_CONFIG_HOME"] ??
|
|
6202
|
+
const home = os8.homedir();
|
|
6203
|
+
const configHome = environment["XDG_CONFIG_HOME"] ?? path12.join(home, ".config");
|
|
6134
6204
|
return {
|
|
6135
|
-
configDir:
|
|
6205
|
+
configDir: path12.join(configHome, "mend"),
|
|
6136
6206
|
platform: process.platform,
|
|
6137
6207
|
cliVersion: cliVersion(),
|
|
6138
6208
|
run: (command, args, options) => runServerProcess(command, args, environment, options),
|
|
@@ -6585,8 +6655,8 @@ var createServiceTunnels = (options) => {
|
|
|
6585
6655
|
};
|
|
6586
6656
|
|
|
6587
6657
|
// src/skills.ts
|
|
6588
|
-
import * as
|
|
6589
|
-
import * as
|
|
6658
|
+
import * as fs13 from "node:fs";
|
|
6659
|
+
import * as path13 from "node:path";
|
|
6590
6660
|
var DEFAULT_SKILLS_DIR = ".agents/skills";
|
|
6591
6661
|
var MAX_SKILL_FILES = 64;
|
|
6592
6662
|
var MAX_SKILL_FILE_BYTES = 512 * 1024;
|
|
@@ -6609,12 +6679,12 @@ var readFrontmatterDescription = (contents) => {
|
|
|
6609
6679
|
var walkFiles = (root) => {
|
|
6610
6680
|
const found = [];
|
|
6611
6681
|
const visit = (dir) => {
|
|
6612
|
-
for (const entry of
|
|
6682
|
+
for (const entry of fs13.readdirSync(dir, { withFileTypes: true })) {
|
|
6613
6683
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6614
|
-
const absolute =
|
|
6684
|
+
const absolute = path13.join(dir, entry.name);
|
|
6615
6685
|
let stat;
|
|
6616
6686
|
try {
|
|
6617
|
-
stat =
|
|
6687
|
+
stat = fs13.statSync(absolute);
|
|
6618
6688
|
} catch {
|
|
6619
6689
|
continue;
|
|
6620
6690
|
}
|
|
@@ -6628,7 +6698,7 @@ var walkFiles = (root) => {
|
|
|
6628
6698
|
var scanSkillLibrary = (root) => {
|
|
6629
6699
|
let entries;
|
|
6630
6700
|
try {
|
|
6631
|
-
entries =
|
|
6701
|
+
entries = fs13.readdirSync(root, { withFileTypes: true });
|
|
6632
6702
|
} catch {
|
|
6633
6703
|
return { error: `${root} is not a readable directory` };
|
|
6634
6704
|
}
|
|
@@ -6636,23 +6706,23 @@ var scanSkillLibrary = (root) => {
|
|
|
6636
6706
|
const notes = [];
|
|
6637
6707
|
for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
6638
6708
|
if (entry.name.startsWith(".")) continue;
|
|
6639
|
-
const skillDir =
|
|
6709
|
+
const skillDir = path13.join(root, entry.name);
|
|
6640
6710
|
let stat;
|
|
6641
6711
|
try {
|
|
6642
|
-
stat =
|
|
6712
|
+
stat = fs13.statSync(skillDir);
|
|
6643
6713
|
} catch {
|
|
6644
6714
|
continue;
|
|
6645
6715
|
}
|
|
6646
6716
|
if (!stat.isDirectory()) continue;
|
|
6647
|
-
if (!
|
|
6717
|
+
if (!fs13.existsSync(path13.join(skillDir, "SKILL.md"))) {
|
|
6648
6718
|
notes.push({ skill: entry.name, message: "no SKILL.md \u2014 skipped" });
|
|
6649
6719
|
continue;
|
|
6650
6720
|
}
|
|
6651
6721
|
const files = [];
|
|
6652
6722
|
let bytes = 0;
|
|
6653
6723
|
for (const absolute of walkFiles(skillDir)) {
|
|
6654
|
-
const relative4 =
|
|
6655
|
-
const buffer =
|
|
6724
|
+
const relative4 = path13.relative(skillDir, absolute).split(path13.sep).join("/");
|
|
6725
|
+
const buffer = fs13.readFileSync(absolute);
|
|
6656
6726
|
if (buffer.byteLength > MAX_SKILL_FILE_BYTES) {
|
|
6657
6727
|
notes.push({ skill: entry.name, message: `${relative4} is over 512KB \u2014 skipped` });
|
|
6658
6728
|
continue;
|
|
@@ -6687,16 +6757,16 @@ var scanSkillLibrary = (root) => {
|
|
|
6687
6757
|
};
|
|
6688
6758
|
|
|
6689
6759
|
// src/ssh-setup.ts
|
|
6690
|
-
import * as
|
|
6691
|
-
import * as
|
|
6760
|
+
import * as os10 from "node:os";
|
|
6761
|
+
import * as path15 from "node:path";
|
|
6692
6762
|
|
|
6693
6763
|
// ../../packages/workspace-ssh/src/index.ts
|
|
6694
|
-
import { spawnSync as
|
|
6764
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
6695
6765
|
import { createHash as createHash3 } from "node:crypto";
|
|
6696
|
-
import * as
|
|
6766
|
+
import * as fs14 from "node:fs";
|
|
6697
6767
|
import { isIP as isIP2 } from "node:net";
|
|
6698
|
-
import * as
|
|
6699
|
-
import * as
|
|
6768
|
+
import * as os9 from "node:os";
|
|
6769
|
+
import * as path14 from "node:path";
|
|
6700
6770
|
var LEGACY_BLOCK_BEGIN = "# >>> mend workspace ssh (managed by `mend ssh setup`) >>>";
|
|
6701
6771
|
var LEGACY_BLOCK_END = "# <<< mend workspace ssh <<<";
|
|
6702
6772
|
var WorkspaceSshError = class extends Error {
|
|
@@ -6783,8 +6853,8 @@ var blockBegin = (alias) => `# >>> mend workspace ssh ${alias} (managed) >>>`;
|
|
|
6783
6853
|
var blockEnd = (alias) => `# <<< mend workspace ssh ${alias} <<<`;
|
|
6784
6854
|
var hasControls = (value) => /[\p{Cc}\p{Zl}\p{Zp}]/u.test(value);
|
|
6785
6855
|
var parseIdentityPath = (value) => {
|
|
6786
|
-
const resolved =
|
|
6787
|
-
value.startsWith("~/") ?
|
|
6856
|
+
const resolved = path14.resolve(
|
|
6857
|
+
value.startsWith("~/") ? path14.join(os9.homedir(), value.slice(2)) : value
|
|
6788
6858
|
);
|
|
6789
6859
|
if (value === "" || hasControls(value) || hasControls(resolved) || value.includes("${") || resolved.includes("${")) {
|
|
6790
6860
|
return failure(
|
|
@@ -6930,7 +7000,7 @@ var workspaceSshPublicKeyFingerprint = (publicKey) => {
|
|
|
6930
7000
|
var publicKeyIdentity = (publicKey) => publicKey.trim().split(/\s+/).slice(0, 2).join(" ");
|
|
6931
7001
|
var agentKeys = () => {
|
|
6932
7002
|
if (!process.env["SSH_AUTH_SOCK"]) return [];
|
|
6933
|
-
const listed =
|
|
7003
|
+
const listed = spawnSync5("ssh-add", ["-L"], {
|
|
6934
7004
|
encoding: "utf8",
|
|
6935
7005
|
timeout: 3e3,
|
|
6936
7006
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6940,7 +7010,7 @@ var agentKeys = () => {
|
|
|
6940
7010
|
};
|
|
6941
7011
|
var readText = (file) => {
|
|
6942
7012
|
try {
|
|
6943
|
-
return success(
|
|
7013
|
+
return success(fs14.readFileSync(file, "utf8"));
|
|
6944
7014
|
} catch (cause) {
|
|
6945
7015
|
const error = cause instanceof Error && "code" in cause ? cause.code : void 0;
|
|
6946
7016
|
return error === "ENOENT" ? success("") : failure(new WorkspaceSshError("key", `Could not read ${file}.`, cause));
|
|
@@ -6967,7 +7037,7 @@ var readRequiredKey = (keyPath, source) => {
|
|
|
6967
7037
|
const privatePath = publicPath.slice(0, -".pub".length);
|
|
6968
7038
|
const read = readText(publicPath);
|
|
6969
7039
|
if (!read.ok) return read;
|
|
6970
|
-
const derived =
|
|
7040
|
+
const derived = spawnSync5("ssh-keygen", ["-y", "-P", "", "-f", privatePath], {
|
|
6971
7041
|
encoding: "utf8",
|
|
6972
7042
|
timeout: 3e3,
|
|
6973
7043
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -6998,19 +7068,19 @@ var readRequiredKey = (keyPath, source) => {
|
|
|
6998
7068
|
var readOptionalKey = (keyPath) => {
|
|
6999
7069
|
const parsed = parseIdentityPath(keyPath);
|
|
7000
7070
|
if (!parsed.ok) return parsed;
|
|
7001
|
-
if (!
|
|
7071
|
+
if (!fs14.existsSync(parsed.value) && !fs14.existsSync(publicKeyPath(parsed.value)))
|
|
7002
7072
|
return success(null);
|
|
7003
7073
|
return readRequiredKey(parsed.value, "existing");
|
|
7004
7074
|
};
|
|
7005
7075
|
var pinAgentKey = (configHome, publicKey) => {
|
|
7006
|
-
const identityFile =
|
|
7076
|
+
const identityFile = path14.resolve(
|
|
7007
7077
|
configHome,
|
|
7008
7078
|
"ssh",
|
|
7009
7079
|
`agent-${hashIdentity(publicKeyIdentity(publicKey))}.pub`
|
|
7010
7080
|
);
|
|
7011
7081
|
try {
|
|
7012
|
-
|
|
7013
|
-
|
|
7082
|
+
fs14.mkdirSync(path14.dirname(identityFile), { recursive: true, mode: 448 });
|
|
7083
|
+
fs14.writeFileSync(identityFile, `${publicKey}
|
|
7014
7084
|
`, { mode: 384 });
|
|
7015
7085
|
return success({ publicKey, identityFile, source: "agent" });
|
|
7016
7086
|
} catch (cause) {
|
|
@@ -7021,15 +7091,15 @@ var pinAgentKey = (configHome, publicKey) => {
|
|
|
7021
7091
|
};
|
|
7022
7092
|
var generateDedicatedKey = (privatePath) => {
|
|
7023
7093
|
try {
|
|
7024
|
-
|
|
7094
|
+
fs14.mkdirSync(path14.dirname(privatePath), { recursive: true, mode: 448 });
|
|
7025
7095
|
} catch (cause) {
|
|
7026
7096
|
return failure(
|
|
7027
|
-
new WorkspaceSshError("key", `Could not create ${
|
|
7097
|
+
new WorkspaceSshError("key", `Could not create ${path14.dirname(privatePath)}.`, cause)
|
|
7028
7098
|
);
|
|
7029
7099
|
}
|
|
7030
|
-
const generated =
|
|
7100
|
+
const generated = spawnSync5(
|
|
7031
7101
|
"ssh-keygen",
|
|
7032
|
-
["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${
|
|
7102
|
+
["-t", "ed25519", "-f", privatePath, "-N", "", "-C", `mend-${os9.hostname()}`],
|
|
7033
7103
|
{ encoding: "utf8", timeout: 3e3, stdio: ["ignore", "pipe", "pipe"] }
|
|
7034
7104
|
);
|
|
7035
7105
|
if (generated.status !== 0) {
|
|
@@ -7053,7 +7123,7 @@ var generateDedicatedKey = (privatePath) => {
|
|
|
7053
7123
|
return fresh;
|
|
7054
7124
|
};
|
|
7055
7125
|
var dedicatedKey = (configHome, create) => {
|
|
7056
|
-
const privatePath =
|
|
7126
|
+
const privatePath = path14.join(configHome, "ssh", "id_ed25519");
|
|
7057
7127
|
const existing = readOptionalKey(privatePath);
|
|
7058
7128
|
if (existing.ok === false) return failure(existing.error);
|
|
7059
7129
|
if (existing.value !== null || !create) return existing;
|
|
@@ -7101,10 +7171,10 @@ var writeWorkspaceSshConfig = (configFile, target, identityFile) => {
|
|
|
7101
7171
|
const reconciled = reconcileWorkspaceSshConfig(before.value, target, identityFile);
|
|
7102
7172
|
if (reconciled.ok === false) return failure(reconciled.error);
|
|
7103
7173
|
try {
|
|
7104
|
-
|
|
7105
|
-
|
|
7106
|
-
|
|
7107
|
-
|
|
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);
|
|
7108
7178
|
return success(void 0);
|
|
7109
7179
|
} catch (cause) {
|
|
7110
7180
|
return failure(new WorkspaceSshError("config", `Could not write ${configFile}.`, cause));
|
|
@@ -7117,7 +7187,7 @@ var dim5 = ansi("2");
|
|
|
7117
7187
|
var green5 = ansi("32");
|
|
7118
7188
|
var warn = ansi("33");
|
|
7119
7189
|
var say5 = (line) => console.log(line);
|
|
7120
|
-
var sshConfigPath = () =>
|
|
7190
|
+
var sshConfigPath = () => path15.join(os10.homedir(), ".ssh", "config");
|
|
7121
7191
|
var flagValue2 = (args, flag) => {
|
|
7122
7192
|
const index = args.indexOf(flag);
|
|
7123
7193
|
return index === -1 || args[index + 1] === void 0 ? null : String(args[index + 1]);
|
|
@@ -7187,7 +7257,7 @@ var setup = async (api2, cliHome, serverUrl, args) => {
|
|
|
7187
7257
|
if (picked.value === null) return showFailure("No workspace SSH key is available.");
|
|
7188
7258
|
const registered = await api2("POST", "/workspace-ssh/keys", {
|
|
7189
7259
|
publicKey: picked.value.publicKey,
|
|
7190
|
-
name:
|
|
7260
|
+
name: os10.hostname()
|
|
7191
7261
|
});
|
|
7192
7262
|
const sourceLabel = {
|
|
7193
7263
|
explicit: "from --key",
|
|
@@ -7229,8 +7299,8 @@ var sshCommand = async (args, api2, cliHome, serverUrl) => {
|
|
|
7229
7299
|
};
|
|
7230
7300
|
|
|
7231
7301
|
// src/uninstall.ts
|
|
7232
|
-
import * as
|
|
7233
|
-
import * as
|
|
7302
|
+
import * as fs15 from "node:fs";
|
|
7303
|
+
import * as path16 from "node:path";
|
|
7234
7304
|
var UNINSTALL_USAGE = "usage: mend uninstall [--all | --server | --home] [--yes]";
|
|
7235
7305
|
var parseUninstallArgs = (args) => {
|
|
7236
7306
|
let scope = null;
|
|
@@ -7252,14 +7322,14 @@ var parseUninstallArgs = (args) => {
|
|
|
7252
7322
|
var SERVER_FILES = ["identity.env", "active", "generations", "backups"];
|
|
7253
7323
|
var countEntries = (directory) => {
|
|
7254
7324
|
try {
|
|
7255
|
-
return
|
|
7325
|
+
return fs15.readdirSync(directory).length;
|
|
7256
7326
|
} catch {
|
|
7257
7327
|
return 0;
|
|
7258
7328
|
}
|
|
7259
7329
|
};
|
|
7260
7330
|
var exists = (file) => {
|
|
7261
7331
|
try {
|
|
7262
|
-
|
|
7332
|
+
fs15.lstatSync(file);
|
|
7263
7333
|
return true;
|
|
7264
7334
|
} catch {
|
|
7265
7335
|
return false;
|
|
@@ -7268,7 +7338,7 @@ var exists = (file) => {
|
|
|
7268
7338
|
var managedBlockCount = (sshConfigFile) => {
|
|
7269
7339
|
let config;
|
|
7270
7340
|
try {
|
|
7271
|
-
config =
|
|
7341
|
+
config = fs15.readFileSync(sshConfigFile, "utf8");
|
|
7272
7342
|
} catch {
|
|
7273
7343
|
return 0;
|
|
7274
7344
|
}
|
|
@@ -7292,27 +7362,27 @@ var describeUninstall = async (runtime, scope) => {
|
|
|
7292
7362
|
if (!read.error.message.startsWith("No Mend server is configured")) throw read.error;
|
|
7293
7363
|
server = "none";
|
|
7294
7364
|
} else if (read.value === null) {
|
|
7295
|
-
server = exists(
|
|
7365
|
+
server = exists(path16.join(configDir, "identity.env")) ? {
|
|
7296
7366
|
version: "(no active generation)",
|
|
7297
7367
|
appUrl: "",
|
|
7298
7368
|
dockerContext: "",
|
|
7299
|
-
generations: countEntries(
|
|
7300
|
-
backups: countEntries(
|
|
7369
|
+
generations: countEntries(path16.join(configDir, "generations")),
|
|
7370
|
+
backups: countEntries(path16.join(configDir, "backups"))
|
|
7301
7371
|
} : "none";
|
|
7302
7372
|
} else {
|
|
7303
7373
|
server = {
|
|
7304
7374
|
version: read.value.config.serverVersion,
|
|
7305
7375
|
appUrl: read.value.config.appUrl,
|
|
7306
7376
|
dockerContext: read.value.config.dockerContext,
|
|
7307
|
-
generations: countEntries(
|
|
7308
|
-
backups: countEntries(
|
|
7377
|
+
generations: countEntries(path16.join(configDir, "generations")),
|
|
7378
|
+
backups: countEntries(path16.join(configDir, "backups"))
|
|
7309
7379
|
};
|
|
7310
7380
|
}
|
|
7311
7381
|
}
|
|
7312
7382
|
let home = null;
|
|
7313
7383
|
if (scope !== "server") {
|
|
7314
|
-
const cliConfig =
|
|
7315
|
-
const sshDirectory =
|
|
7384
|
+
const cliConfig = path16.join(runtime.cliHome, "cli.json");
|
|
7385
|
+
const sshDirectory = path16.join(runtime.cliHome, "ssh");
|
|
7316
7386
|
home = {
|
|
7317
7387
|
cliConfig: exists(cliConfig) ? cliConfig : null,
|
|
7318
7388
|
sshDirectory: exists(sshDirectory) ? sshDirectory : null,
|
|
@@ -7444,7 +7514,7 @@ var removeServer = async (runtime, failures, leftovers) => {
|
|
|
7444
7514
|
}
|
|
7445
7515
|
}
|
|
7446
7516
|
for (const name of SERVER_FILES) {
|
|
7447
|
-
|
|
7517
|
+
fs15.rmSync(path16.join(configDir, name), { recursive: true, force: true });
|
|
7448
7518
|
}
|
|
7449
7519
|
server.writeLine(`removed ${configDir}/{identity.env, active, generations, backups}`);
|
|
7450
7520
|
},
|
|
@@ -7455,20 +7525,20 @@ var removeServer = async (runtime, failures, leftovers) => {
|
|
|
7455
7525
|
var removeHome = async (runtime, plan, failures, leftovers) => {
|
|
7456
7526
|
const { server } = runtime;
|
|
7457
7527
|
if (plan.cliConfig !== null) {
|
|
7458
|
-
|
|
7528
|
+
fs15.rmSync(plan.cliConfig, { force: true });
|
|
7459
7529
|
server.writeLine(`removed ${plan.cliConfig}`);
|
|
7460
7530
|
}
|
|
7461
7531
|
if (plan.sshDirectory !== null) {
|
|
7462
|
-
|
|
7532
|
+
fs15.rmSync(plan.sshDirectory, { recursive: true, force: true });
|
|
7463
7533
|
server.writeLine(`removed ${plan.sshDirectory}`);
|
|
7464
7534
|
}
|
|
7465
7535
|
if (plan.managedSshBlocks > 0) {
|
|
7466
7536
|
try {
|
|
7467
|
-
const before =
|
|
7537
|
+
const before = fs15.readFileSync(runtime.sshConfigFile, "utf8");
|
|
7468
7538
|
const stripped = stripManagedWorkspaceSshBlocks(before);
|
|
7469
7539
|
if (!stripped.ok) throw stripped.error;
|
|
7470
7540
|
if (stripped.value.removed > 0) {
|
|
7471
|
-
|
|
7541
|
+
fs15.writeFileSync(runtime.sshConfigFile, stripped.value.config, { mode: 384 });
|
|
7472
7542
|
server.writeLine(
|
|
7473
7543
|
`removed ${plural(stripped.value.removed, "managed block")} from ${runtime.sshConfigFile}`
|
|
7474
7544
|
);
|
|
@@ -7481,12 +7551,12 @@ var removeHome = async (runtime, plan, failures, leftovers) => {
|
|
|
7481
7551
|
}
|
|
7482
7552
|
let remaining = [];
|
|
7483
7553
|
try {
|
|
7484
|
-
remaining =
|
|
7554
|
+
remaining = fs15.readdirSync(runtime.cliHome);
|
|
7485
7555
|
} catch {
|
|
7486
7556
|
return;
|
|
7487
7557
|
}
|
|
7488
7558
|
if (remaining.length === 0) {
|
|
7489
|
-
|
|
7559
|
+
fs15.rmdirSync(runtime.cliHome);
|
|
7490
7560
|
server.writeLine(`removed ${runtime.cliHome}`);
|
|
7491
7561
|
} else {
|
|
7492
7562
|
leftovers.push(
|
|
@@ -7550,19 +7620,19 @@ var upgradeUrl = async (input) => {
|
|
|
7550
7620
|
// src/main.ts
|
|
7551
7621
|
var mendCliHome = () => {
|
|
7552
7622
|
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
7553
|
-
const preferred =
|
|
7554
|
-
xdg === void 0 || xdg === "" ?
|
|
7623
|
+
const preferred = path17.join(
|
|
7624
|
+
xdg === void 0 || xdg === "" ? path17.join(os11.homedir(), ".config") : xdg,
|
|
7555
7625
|
"mend"
|
|
7556
7626
|
);
|
|
7557
|
-
const legacy =
|
|
7558
|
-
return !
|
|
7627
|
+
const legacy = path17.join(os11.homedir(), ".mend");
|
|
7628
|
+
return !fs16.existsSync(preferred) && fs16.existsSync(legacy) ? legacy : preferred;
|
|
7559
7629
|
};
|
|
7560
|
-
var CONFIG_PATH =
|
|
7630
|
+
var CONFIG_PATH = path17.join(mendCliHome(), "cli.json");
|
|
7561
7631
|
var loadConfig = () => {
|
|
7562
7632
|
let fileConfig = {};
|
|
7563
|
-
if (
|
|
7633
|
+
if (fs16.existsSync(CONFIG_PATH)) {
|
|
7564
7634
|
try {
|
|
7565
|
-
fileConfig = JSON.parse(
|
|
7635
|
+
fileConfig = JSON.parse(fs16.readFileSync(CONFIG_PATH, "utf8"));
|
|
7566
7636
|
} catch {
|
|
7567
7637
|
fail6(`could not parse ${CONFIG_PATH}`);
|
|
7568
7638
|
}
|
|
@@ -7814,7 +7884,7 @@ var adopt = async (config, args) => {
|
|
|
7814
7884
|
}
|
|
7815
7885
|
const sourceIssue = repositoryCloneUrlIssue(source);
|
|
7816
7886
|
if (sourceIssue !== null) return fail6(sourceIssue);
|
|
7817
|
-
const projectName = name ?? normalizeProjectName(
|
|
7887
|
+
const projectName = name ?? normalizeProjectName(path17.basename(source, ".git"));
|
|
7818
7888
|
const auth = authFlagIndex !== -1 && args[authFlagIndex + 1] !== void 0 ? String(args[authFlagIndex + 1]) : null;
|
|
7819
7889
|
if (auth !== null && auth !== "ambient" && auth !== "mend-key" && auth !== "bridge") {
|
|
7820
7890
|
return fail6(`--auth takes "ambient", "mend-key", or "bridge", not "${auth}"`);
|
|
@@ -7863,7 +7933,7 @@ var findProject = async (config, explicit, adoptCwd = false) => {
|
|
|
7863
7933
|
const sourceIssue = repositoryCloneUrlIssue(facts.originUrl);
|
|
7864
7934
|
if (sourceIssue !== null) return fail6(sourceIssue);
|
|
7865
7935
|
const adopted = await api(config, "POST", "/projects", {
|
|
7866
|
-
name: normalizeProjectName(
|
|
7936
|
+
name: normalizeProjectName(path17.basename(facts.repoRoot)),
|
|
7867
7937
|
source: facts.originUrl
|
|
7868
7938
|
});
|
|
7869
7939
|
say6(`${green6("\u2713")} adopted \xB7 ${adopted.name} \xB7 ${dim6(adopted.storePath)}`);
|
|
@@ -8824,13 +8894,13 @@ var serviceRestart = async (config, args) => {
|
|
|
8824
8894
|
};
|
|
8825
8895
|
var serviceInit = async (args) => {
|
|
8826
8896
|
const root = gitTopLevel(process.cwd()) ?? process.cwd();
|
|
8827
|
-
const target =
|
|
8828
|
-
if (
|
|
8897
|
+
const target = path17.join(root, "mend.toml");
|
|
8898
|
+
if (fs16.existsSync(target)) {
|
|
8829
8899
|
return fail6(`${target} already exists \u2014 edit it directly (init never merges)`);
|
|
8830
8900
|
}
|
|
8831
|
-
const rootFiles =
|
|
8901
|
+
const rootFiles = fs16.readdirSync(root);
|
|
8832
8902
|
const proposals = [];
|
|
8833
|
-
const readRoot = (name) =>
|
|
8903
|
+
const readRoot = (name) => fs16.readFileSync(path17.join(root, name), "utf8");
|
|
8834
8904
|
if (rootFiles.includes("package.json")) {
|
|
8835
8905
|
proposals.push(...proposeFromPackageJson(readRoot("package.json"), rootFiles));
|
|
8836
8906
|
}
|
|
@@ -8840,13 +8910,13 @@ var serviceInit = async (args) => {
|
|
|
8840
8910
|
);
|
|
8841
8911
|
for (const glob of globs) {
|
|
8842
8912
|
const parent = glob.replace(/\/?\*+$/, "");
|
|
8843
|
-
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];
|
|
8844
8914
|
for (const dir of dirs) {
|
|
8845
|
-
const manifest =
|
|
8846
|
-
if (!
|
|
8915
|
+
const manifest = path17.join(root, dir, "package.json");
|
|
8916
|
+
if (!fs16.existsSync(manifest)) continue;
|
|
8847
8917
|
for (const proposal of proposeFromWorkspacePackage(
|
|
8848
|
-
|
|
8849
|
-
|
|
8918
|
+
path17.basename(dir),
|
|
8919
|
+
fs16.readFileSync(manifest, "utf8"),
|
|
8850
8920
|
rootFiles
|
|
8851
8921
|
)) {
|
|
8852
8922
|
if (!proposals.some((existing) => existing.name === proposal.name)) {
|
|
@@ -8890,7 +8960,7 @@ var serviceInit = async (args) => {
|
|
|
8890
8960
|
return;
|
|
8891
8961
|
}
|
|
8892
8962
|
}
|
|
8893
|
-
|
|
8963
|
+
fs16.writeFileSync(target, toml);
|
|
8894
8964
|
say6(`${green6("\u2713")} wrote ${target} \u2014 commit it, then: mend service run ${proposals[0]?.name}`);
|
|
8895
8965
|
};
|
|
8896
8966
|
var tunnelServices = async (config, services, portOverride) => {
|
|
@@ -8994,23 +9064,23 @@ var serviceCommand = async (config, args) => {
|
|
|
8994
9064
|
}
|
|
8995
9065
|
};
|
|
8996
9066
|
var isProvider = (value) => value === "claude" || value === "codex" || value === "github";
|
|
8997
|
-
var readIfExists = (file) =>
|
|
9067
|
+
var readIfExists = (file) => fs16.existsSync(file) ? fs16.readFileSync(file, "utf8") : null;
|
|
8998
9068
|
var localCredential = (provider) => {
|
|
8999
|
-
const home =
|
|
9069
|
+
const home = os11.homedir();
|
|
9000
9070
|
switch (provider) {
|
|
9001
9071
|
case "codex":
|
|
9002
9072
|
return readIfExists(
|
|
9003
|
-
|
|
9073
|
+
path17.join(process.env["CODEX_HOME"] ?? path17.join(home, ".codex"), "auth.json")
|
|
9004
9074
|
);
|
|
9005
9075
|
case "claude":
|
|
9006
9076
|
return readIfExists(
|
|
9007
|
-
|
|
9008
|
-
process.env["CLAUDE_CONFIG_DIR"] ??
|
|
9077
|
+
path17.join(
|
|
9078
|
+
process.env["CLAUDE_CONFIG_DIR"] ?? path17.join(home, ".claude"),
|
|
9009
9079
|
".credentials.json"
|
|
9010
9080
|
)
|
|
9011
9081
|
);
|
|
9012
9082
|
case "github": {
|
|
9013
|
-
const result =
|
|
9083
|
+
const result = spawnSync6("gh", ["auth", "token"], { encoding: "utf8" });
|
|
9014
9084
|
const token = result.status === 0 ? result.stdout.trim() : "";
|
|
9015
9085
|
return token === "" ? null : token;
|
|
9016
9086
|
}
|
|
@@ -9022,7 +9092,7 @@ var accountLine = (account) => {
|
|
|
9022
9092
|
const identity = pick("login") ?? pick("email") ?? pick("accountEmail") ?? pick("accountId");
|
|
9023
9093
|
const suffix = pick("tokenSuffix");
|
|
9024
9094
|
const facts = [
|
|
9025
|
-
account.status === "active" ? "connected" : account.status,
|
|
9095
|
+
account.status === "active" ? "connected" : account.status === "invalid" ? "reconnect needed \xB7 the provider refused the login" : account.status,
|
|
9026
9096
|
identity,
|
|
9027
9097
|
suffix === null ? null : `\u2026${suffix}`,
|
|
9028
9098
|
`since ${account.connectedAt.slice(0, 10)}`
|
|
@@ -9055,7 +9125,7 @@ var claudeGrantSecret = () => {
|
|
|
9055
9125
|
return read.kind === "missing" ? null : read.secret;
|
|
9056
9126
|
};
|
|
9057
9127
|
var doctorBundle = (config, args) => doctorBundleCommand(args, {
|
|
9058
|
-
defaultDir:
|
|
9128
|
+
defaultDir: path17.join(mendCliHome(), "bundles"),
|
|
9059
9129
|
now: () => /* @__PURE__ */ new Date(),
|
|
9060
9130
|
say: say6,
|
|
9061
9131
|
warn: (line) => process.stderr.write(`${line}
|
|
@@ -9119,7 +9189,7 @@ var claudeGrant = async () => {
|
|
|
9119
9189
|
return null;
|
|
9120
9190
|
}
|
|
9121
9191
|
}
|
|
9122
|
-
if (status.configDirectory !== null &&
|
|
9192
|
+
if (status.configDirectory !== null && path17.resolve(status.configDirectory) !== path17.resolve(dir)) {
|
|
9123
9193
|
fail6(
|
|
9124
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`
|
|
9125
9195
|
);
|
|
@@ -9182,8 +9252,16 @@ var connectCommand = async (config, args) => {
|
|
|
9182
9252
|
}
|
|
9183
9253
|
let secret;
|
|
9184
9254
|
if (flags.includes("--from-stdin")) {
|
|
9185
|
-
secret =
|
|
9255
|
+
secret = fs16.readFileSync(0, "utf8").trim();
|
|
9186
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;
|
|
9187
9265
|
} else if (provider === "claude" && !flags.includes("--use-my-login")) {
|
|
9188
9266
|
secret = await claudeGrant();
|
|
9189
9267
|
if (secret === null) return;
|
|
@@ -9193,10 +9271,10 @@ var connectCommand = async (config, args) => {
|
|
|
9193
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";
|
|
9194
9272
|
return fail6(`${provider}: no credential on this machine \u2014 ${where}`);
|
|
9195
9273
|
}
|
|
9196
|
-
if (provider === "claude") {
|
|
9274
|
+
if (provider === "claude" || provider === "codex") {
|
|
9197
9275
|
say6(
|
|
9198
9276
|
dim6(
|
|
9199
|
-
" --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"
|
|
9200
9278
|
)
|
|
9201
9279
|
);
|
|
9202
9280
|
}
|
|
@@ -9228,14 +9306,14 @@ var takeFlagValue4 = (args, flag) => {
|
|
|
9228
9306
|
return at !== -1 && args[at + 1] !== void 0 ? String(args[at + 1]) : null;
|
|
9229
9307
|
};
|
|
9230
9308
|
var saveCliConfig = (next) => {
|
|
9231
|
-
|
|
9232
|
-
|
|
9309
|
+
fs16.mkdirSync(path17.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
|
|
9310
|
+
fs16.writeFileSync(
|
|
9233
9311
|
CONFIG_PATH,
|
|
9234
9312
|
`${JSON.stringify({ url: next.url, token: next.token, deviceId: next.deviceId }, null, 2)}
|
|
9235
9313
|
`,
|
|
9236
9314
|
{ mode: 384 }
|
|
9237
9315
|
);
|
|
9238
|
-
|
|
9316
|
+
fs16.chmodSync(CONFIG_PATH, 384);
|
|
9239
9317
|
};
|
|
9240
9318
|
var login = async (config, args) => {
|
|
9241
9319
|
await loginCommand(args, {
|
|
@@ -9248,7 +9326,7 @@ var login = async (config, args) => {
|
|
|
9248
9326
|
});
|
|
9249
9327
|
};
|
|
9250
9328
|
var logout = async (config) => {
|
|
9251
|
-
if (!
|
|
9329
|
+
if (!fs16.existsSync(CONFIG_PATH) && config.token === null) {
|
|
9252
9330
|
say6(dim6("nothing saved \u2014 already signed out"));
|
|
9253
9331
|
return;
|
|
9254
9332
|
}
|
|
@@ -9301,8 +9379,8 @@ var uninstallCommand = async (config, args) => {
|
|
|
9301
9379
|
const runtime = {
|
|
9302
9380
|
server,
|
|
9303
9381
|
cliHome: mendCliHome(),
|
|
9304
|
-
sshConfigFile:
|
|
9305
|
-
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 },
|
|
9306
9384
|
revokeDevice: async () => {
|
|
9307
9385
|
if (config.deviceId === null) return "no device id saved";
|
|
9308
9386
|
try {
|
|
@@ -9366,7 +9444,7 @@ var keysShare = async (config) => {
|
|
|
9366
9444
|
return fail6("SSH_AUTH_SOCK is not set \u2014 start (or plug in) your ssh-agent first");
|
|
9367
9445
|
}
|
|
9368
9446
|
await shareAgent({
|
|
9369
|
-
url: () => socketUrl(config, "keys-bridge", { host:
|
|
9447
|
+
url: () => socketUrl(config, "keys-bridge", { host: os11.hostname() }),
|
|
9370
9448
|
agentSock,
|
|
9371
9449
|
signal: new AbortController().signal,
|
|
9372
9450
|
onEvent: (event) => {
|
|
@@ -9496,7 +9574,7 @@ ${usageOf("dotfiles repo")}`);
|
|
|
9496
9574
|
var dotfilesSync = async (config, args) => {
|
|
9497
9575
|
const all = args.includes("--all");
|
|
9498
9576
|
const requestedPaths = args.filter((arg) => !arg.startsWith("--"));
|
|
9499
|
-
const home =
|
|
9577
|
+
const home = os11.homedir();
|
|
9500
9578
|
if (!all && requestedPaths.length === 0) {
|
|
9501
9579
|
const found = scanDotfileCandidates(home);
|
|
9502
9580
|
if (found.length === 0) {
|
|
@@ -9523,13 +9601,13 @@ var dotfilesSync = async (config, args) => {
|
|
|
9523
9601
|
if ("error" in read) return fail6(read.error);
|
|
9524
9602
|
const result = await api(config, "POST", "/dotfiles/snapshot", {
|
|
9525
9603
|
files: read.files,
|
|
9526
|
-
source:
|
|
9604
|
+
source: os11.hostname(),
|
|
9527
9605
|
merge: false
|
|
9528
9606
|
});
|
|
9529
9607
|
const snapshot = result.snapshot;
|
|
9530
9608
|
if (snapshot === null) return fail6("the server accepted the sync but reports no snapshot");
|
|
9531
9609
|
say6(
|
|
9532
|
-
`${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")}`
|
|
9533
9611
|
);
|
|
9534
9612
|
};
|
|
9535
9613
|
var envLoad = async (config, args) => {
|
|
@@ -9543,10 +9621,10 @@ var envLoad = async (config, args) => {
|
|
|
9543
9621
|
const positional = args.filter(
|
|
9544
9622
|
(arg, i) => !arg.startsWith("--") && args[i - 1] !== "--project" && !consumed.has(i)
|
|
9545
9623
|
);
|
|
9546
|
-
const file =
|
|
9624
|
+
const file = path17.resolve(positional[0] ?? ".env");
|
|
9547
9625
|
let contents;
|
|
9548
9626
|
try {
|
|
9549
|
-
contents =
|
|
9627
|
+
contents = fs16.readFileSync(file, "utf8");
|
|
9550
9628
|
} catch {
|
|
9551
9629
|
return fail6(`cannot read ${file} \u2014 pass a path: mend env load path/to/.env`);
|
|
9552
9630
|
}
|
|
@@ -9562,10 +9640,10 @@ var envLoad = async (config, args) => {
|
|
|
9562
9640
|
{ contents, allSecret, secretNames }
|
|
9563
9641
|
);
|
|
9564
9642
|
if (report.loaded.length === 0 && report.rejected.length === 0 && report.malformedLines.length === 0) {
|
|
9565
|
-
say6(dim6(`${
|
|
9643
|
+
say6(dim6(`${path17.basename(file)} has no variables`));
|
|
9566
9644
|
return;
|
|
9567
9645
|
}
|
|
9568
|
-
say6(`${green6("\u2713")} loaded ${
|
|
9646
|
+
say6(`${green6("\u2713")} loaded ${path17.basename(file)} into ${project2.name}`);
|
|
9569
9647
|
for (const line of formatLoadReport(report, { dim: dim6, warn: amber2 })) say6(line);
|
|
9570
9648
|
const plaintextUrls = report.loaded.filter(
|
|
9571
9649
|
(entry) => entry.lane === "configuration" && /_(URL|URI|DSN)$/.test(entry.name)
|
|
@@ -9756,7 +9834,7 @@ var skillsList = async (config, args) => {
|
|
|
9756
9834
|
};
|
|
9757
9835
|
var skillsPush = async (config, args) => {
|
|
9758
9836
|
const dirFlag = takeFlagValue4(args, "--dir");
|
|
9759
|
-
const root = dirFlag === null ?
|
|
9837
|
+
const root = dirFlag === null ? path17.join(os11.homedir(), DEFAULT_SKILLS_DIR) : path17.resolve(dirFlag);
|
|
9760
9838
|
const scanned = scanSkillLibrary(root);
|
|
9761
9839
|
if ("error" in scanned) return fail6(scanned.error);
|
|
9762
9840
|
for (const note of scanned.notes) say6(dim6(` ${note.skill}: ${note.message}`));
|
|
@@ -10431,14 +10509,14 @@ var dashboard = async (config, options = {}) => {
|
|
|
10431
10509
|
`the dashboard needs Node >= 26 (node:ffi) \u2014 this is ${process.version}; every other command still works`
|
|
10432
10510
|
);
|
|
10433
10511
|
}
|
|
10434
|
-
const rerun =
|
|
10512
|
+
const rerun = spawnSync6(
|
|
10435
10513
|
process.execPath,
|
|
10436
10514
|
["--experimental-ffi", "--disable-warning=ExperimentalWarning", ...process.argv.slice(1)],
|
|
10437
10515
|
{ stdio: "inherit" }
|
|
10438
10516
|
);
|
|
10439
10517
|
process.exit(rerun.status ?? 0);
|
|
10440
10518
|
}
|
|
10441
|
-
const { runDashboard } = await import("./dashboard-
|
|
10519
|
+
const { runDashboard } = await import("./dashboard-MGR4YUFD.js");
|
|
10442
10520
|
const agentShare = await startShareIfBridge(config);
|
|
10443
10521
|
const tunnels = options.noTunnel === true || serverIsLocal(config) ? null : createServiceTunnels({
|
|
10444
10522
|
listServices: () => fetchServices(config),
|
|
@@ -10472,7 +10550,7 @@ var startShareIfBridge = async (config) => {
|
|
|
10472
10550
|
}
|
|
10473
10551
|
if (access.mode !== "bridge") return null;
|
|
10474
10552
|
return startAgentShare({
|
|
10475
|
-
url: () => socketUrl(config, "keys-bridge", { host:
|
|
10553
|
+
url: () => socketUrl(config, "keys-bridge", { host: os11.hostname() }),
|
|
10476
10554
|
agentSock
|
|
10477
10555
|
});
|
|
10478
10556
|
};
|
|
@@ -10512,10 +10590,10 @@ var manCommand = (words) => {
|
|
|
10512
10590
|
}
|
|
10513
10591
|
const version = cliVersion();
|
|
10514
10592
|
const page = doc === null ? renderManIndex(version) : renderManPage(doc, version);
|
|
10515
|
-
const file =
|
|
10516
|
-
|
|
10517
|
-
const result =
|
|
10518
|
-
|
|
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 });
|
|
10519
10597
|
if (result.error !== void 0 || result.status !== 0) {
|
|
10520
10598
|
say6(doc === null ? renderIndex() : renderCommand(doc));
|
|
10521
10599
|
}
|