@sealant/mend 0.35.0 → 0.35.1
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/main.js +306 -282
- 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 +2 -2
- 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 +5 -4
package/dist/main.js
CHANGED
|
@@ -38,11 +38,11 @@ import {
|
|
|
38
38
|
|
|
39
39
|
// src/main.ts
|
|
40
40
|
import { spawn as spawn3, spawnSync as spawnSync6 } from "node:child_process";
|
|
41
|
-
import * as
|
|
41
|
+
import * as fs17 from "node:fs";
|
|
42
42
|
import { createRequire } from "node:module";
|
|
43
43
|
import * as net4 from "node:net";
|
|
44
44
|
import * as os11 from "node:os";
|
|
45
|
-
import * as
|
|
45
|
+
import * as path18 from "node:path";
|
|
46
46
|
|
|
47
47
|
// src/agent-share.ts
|
|
48
48
|
import * as net from "node:net";
|
|
@@ -223,6 +223,15 @@ import * as fs from "node:fs";
|
|
|
223
223
|
import * as os from "node:os";
|
|
224
224
|
import * as path from "node:path";
|
|
225
225
|
var claudeGrantDir = (home) => path.join(home, "claude-grant");
|
|
226
|
+
var forgetGrant = (configDir, platform = process.platform) => {
|
|
227
|
+
fs.rmSync(configDir, { recursive: true, force: true });
|
|
228
|
+
if (platform !== "darwin") return;
|
|
229
|
+
spawnSync(
|
|
230
|
+
"security",
|
|
231
|
+
["delete-generic-password", "-a", os.userInfo().username, "-s", keychainService(configDir)],
|
|
232
|
+
{ stdio: "ignore", timeout: 6e4 }
|
|
233
|
+
);
|
|
234
|
+
};
|
|
226
235
|
var personalClaudeDir = (environment = process.env) => {
|
|
227
236
|
const configured = environment["CLAUDE_CONFIG_DIR"];
|
|
228
237
|
return configured === void 0 || configured === "" ? path.join(os.homedir(), ".claude") : configured;
|
|
@@ -344,16 +353,28 @@ var readClipboardImage = async (platform = process.platform, env = process.env)
|
|
|
344
353
|
|
|
345
354
|
// src/codex-grant.ts
|
|
346
355
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
347
|
-
import * as
|
|
356
|
+
import * as fs3 from "node:fs";
|
|
348
357
|
import * as os2 from "node:os";
|
|
358
|
+
import * as path3 from "node:path";
|
|
359
|
+
|
|
360
|
+
// src/login-dir.ts
|
|
361
|
+
import * as fs2 from "node:fs";
|
|
349
362
|
import * as path2 from "node:path";
|
|
363
|
+
var throwawayLoginDir = (parent, prefix) => {
|
|
364
|
+
fs2.mkdirSync(parent, { recursive: true, mode: 448 });
|
|
365
|
+
const dir = fs2.mkdtempSync(path2.join(parent, prefix));
|
|
366
|
+
fs2.chmodSync(dir, 448);
|
|
367
|
+
return dir;
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// src/codex-grant.ts
|
|
350
371
|
var codexCli = (environment = process.env) => ({
|
|
351
372
|
bin: environment["MEND_CODEX_BIN"] ?? "codex",
|
|
352
373
|
env: environment
|
|
353
374
|
});
|
|
354
375
|
var personalCodexHome = (environment = process.env) => {
|
|
355
376
|
const configured = environment["CODEX_HOME"];
|
|
356
|
-
return configured === void 0 || configured === "" ?
|
|
377
|
+
return configured === void 0 || configured === "" ? path3.join(os2.homedir(), ".codex") : configured;
|
|
357
378
|
};
|
|
358
379
|
var runCodexDeviceLogin = (cli, codexHome) => {
|
|
359
380
|
const result = spawnSync2(cli.bin, ["login", "--device-auth"], {
|
|
@@ -379,19 +400,18 @@ var sameCodexLogin = (a, b) => {
|
|
|
379
400
|
return first !== null && first === refreshTokenOf(b);
|
|
380
401
|
};
|
|
381
402
|
var codexGrant = (input) => {
|
|
382
|
-
const home =
|
|
383
|
-
fs2.chmodSync(home, 448);
|
|
403
|
+
const home = throwawayLoginDir(input.parent, "codex-login-");
|
|
384
404
|
try {
|
|
385
405
|
input.say(" Mend needs its own Codex login; it is sent to your server and not kept here");
|
|
386
406
|
input.say(" your own Codex login stays as it is");
|
|
387
407
|
if (!runCodexDeviceLogin(input.cli, home)) {
|
|
388
408
|
return { kind: "failed", reason: "codex: the login did not complete" };
|
|
389
409
|
}
|
|
390
|
-
const file =
|
|
391
|
-
if (!
|
|
410
|
+
const file = path3.join(home, "auth.json");
|
|
411
|
+
if (!fs3.existsSync(file)) {
|
|
392
412
|
return { kind: "failed", reason: `codex: logged in, but no auth.json appeared in ${home}` };
|
|
393
413
|
}
|
|
394
|
-
const secret =
|
|
414
|
+
const secret = fs3.readFileSync(file, "utf8").trim();
|
|
395
415
|
if (refreshTokenOf(secret) === null) {
|
|
396
416
|
return {
|
|
397
417
|
kind: "failed",
|
|
@@ -406,19 +426,19 @@ var codexGrant = (input) => {
|
|
|
406
426
|
}
|
|
407
427
|
return { kind: "grant", secret };
|
|
408
428
|
} finally {
|
|
409
|
-
|
|
429
|
+
fs3.rmSync(home, { recursive: true, force: true });
|
|
410
430
|
}
|
|
411
431
|
};
|
|
412
432
|
|
|
413
433
|
// src/doctor-bundle-collectors.ts
|
|
414
|
-
import * as
|
|
434
|
+
import * as fs5 from "node:fs";
|
|
415
435
|
import * as os3 from "node:os";
|
|
416
|
-
import * as
|
|
436
|
+
import * as path5 from "node:path";
|
|
417
437
|
|
|
418
438
|
// src/server-runtime.ts
|
|
419
439
|
import { spawn } from "node:child_process";
|
|
420
|
-
import * as
|
|
421
|
-
import * as
|
|
440
|
+
import * as fs4 from "node:fs";
|
|
441
|
+
import * as path4 from "node:path";
|
|
422
442
|
var serverProcessDeadlines = {
|
|
423
443
|
ordinary: 3e4,
|
|
424
444
|
pull: 10 * 6e4,
|
|
@@ -464,7 +484,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
464
484
|
return new Promise((resolve7) => {
|
|
465
485
|
let fd;
|
|
466
486
|
try {
|
|
467
|
-
if (options.stdoutFile !== void 0) fd =
|
|
487
|
+
if (options.stdoutFile !== void 0) fd = fs4.openSync(options.stdoutFile, "wx", 384);
|
|
468
488
|
const child = spawn(command, [...args], {
|
|
469
489
|
env,
|
|
470
490
|
detached: process.platform !== "win32",
|
|
@@ -515,12 +535,12 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
515
535
|
child.once("close", (status) => {
|
|
516
536
|
globalThis.clearTimeout(timer);
|
|
517
537
|
try {
|
|
518
|
-
if (fd !== void 0)
|
|
538
|
+
if (fd !== void 0) fs4.fsyncSync(fd);
|
|
519
539
|
} catch {
|
|
520
540
|
failure2 ??= "Could not durably write process output.";
|
|
521
541
|
} finally {
|
|
522
542
|
try {
|
|
523
|
-
if (fd !== void 0)
|
|
543
|
+
if (fd !== void 0) fs4.closeSync(fd);
|
|
524
544
|
} catch {
|
|
525
545
|
failure2 ??= "Could not close process output file.";
|
|
526
546
|
}
|
|
@@ -534,7 +554,7 @@ var runServerProcess = (command, args, parentEnvironment, options = {}) => {
|
|
|
534
554
|
});
|
|
535
555
|
} catch (cause) {
|
|
536
556
|
try {
|
|
537
|
-
if (fd !== void 0)
|
|
557
|
+
if (fd !== void 0) fs4.closeSync(fd);
|
|
538
558
|
} catch {
|
|
539
559
|
}
|
|
540
560
|
resolve7({
|
|
@@ -555,9 +575,9 @@ var serverComposeArgs = (installation, command) => [
|
|
|
555
575
|
"--project-directory",
|
|
556
576
|
installation.directory,
|
|
557
577
|
"--env-file",
|
|
558
|
-
|
|
578
|
+
path4.join(installation.directory, "server.env"),
|
|
559
579
|
"-f",
|
|
560
|
-
|
|
580
|
+
path4.join(installation.directory, "compose.yaml"),
|
|
561
581
|
...command
|
|
562
582
|
];
|
|
563
583
|
|
|
@@ -603,11 +623,11 @@ var mapLimit = async (items, limit, work) => {
|
|
|
603
623
|
return results;
|
|
604
624
|
};
|
|
605
625
|
var pathOf = (command, env = process.env) => {
|
|
606
|
-
for (const directory of (env["PATH"] ?? "").split(
|
|
626
|
+
for (const directory of (env["PATH"] ?? "").split(path5.delimiter)) {
|
|
607
627
|
if (directory === "") continue;
|
|
608
|
-
const candidate =
|
|
628
|
+
const candidate = path5.join(directory, command);
|
|
609
629
|
try {
|
|
610
|
-
|
|
630
|
+
fs5.accessSync(candidate, fs5.constants.X_OK);
|
|
611
631
|
return candidate;
|
|
612
632
|
} catch {
|
|
613
633
|
}
|
|
@@ -1097,8 +1117,8 @@ var bundleCollectors = (deps) => {
|
|
|
1097
1117
|
};
|
|
1098
1118
|
|
|
1099
1119
|
// src/doctor-bundle.ts
|
|
1100
|
-
import * as
|
|
1101
|
-
import * as
|
|
1120
|
+
import * as fs6 from "node:fs";
|
|
1121
|
+
import * as path6 from "node:path";
|
|
1102
1122
|
import { gzipSync } from "node:zlib";
|
|
1103
1123
|
|
|
1104
1124
|
// src/help.ts
|
|
@@ -1148,7 +1168,7 @@ var COMMANDS = [
|
|
|
1148
1168
|
synopsis: ["<claude|codex|github> [--use-my-login] [--from-stdin] [--remove]"],
|
|
1149
1169
|
description: [
|
|
1150
1170
|
"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
|
|
1171
|
+
"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 in the browser, Codex with a device code, both sent and not kept here. Run it again whenever Mend says a login needs reconnecting. GitHub sends gh's token."
|
|
1152
1172
|
],
|
|
1153
1173
|
options: [
|
|
1154
1174
|
{
|
|
@@ -2539,9 +2559,9 @@ var collectBundle = async (collectors) => {
|
|
|
2539
2559
|
};
|
|
2540
2560
|
var bundleFileName = (now) => `mend-bundle-${now.toISOString().replace(/\.\d{3}Z$/, "Z").replace(/:/g, "-")}.tgz`;
|
|
2541
2561
|
var writeBundle = (files, outPath, now) => {
|
|
2542
|
-
const directory =
|
|
2543
|
-
|
|
2544
|
-
const root =
|
|
2562
|
+
const directory = path6.dirname(outPath);
|
|
2563
|
+
fs6.mkdirSync(directory, { recursive: true, mode: 448 });
|
|
2564
|
+
const root = path6.basename(outPath).replace(/\.(?:tgz|tar\.gz)$/, "");
|
|
2545
2565
|
const entries = files.map((file) => ({
|
|
2546
2566
|
path: file.path,
|
|
2547
2567
|
content: Buffer.from(file.content, "utf8")
|
|
@@ -2550,8 +2570,8 @@ var writeBundle = (files, outPath, now) => {
|
|
|
2550
2570
|
entries.map((entry) => ({ path: `${root}/${entry.path}`, content: entry.content })),
|
|
2551
2571
|
now
|
|
2552
2572
|
);
|
|
2553
|
-
|
|
2554
|
-
|
|
2573
|
+
fs6.writeFileSync(outPath, bytes, { mode: 384 });
|
|
2574
|
+
fs6.chmodSync(outPath, 384);
|
|
2555
2575
|
return {
|
|
2556
2576
|
path: outPath,
|
|
2557
2577
|
bytes: bytes.length,
|
|
@@ -2612,7 +2632,7 @@ var doctorBundleCommand = async (args, deps) => {
|
|
|
2612
2632
|
return;
|
|
2613
2633
|
}
|
|
2614
2634
|
const now = deps.now();
|
|
2615
|
-
const outPath =
|
|
2635
|
+
const outPath = path6.resolve(parsed.args.out ?? path6.join(deps.defaultDir, bundleFileName(now)));
|
|
2616
2636
|
deps.warn("collecting \xB7 this can take a minute");
|
|
2617
2637
|
const files = await collectBundle(deps.collectors(parsed.args.tail));
|
|
2618
2638
|
const written = writeBundle(files, outPath, now);
|
|
@@ -2620,14 +2640,14 @@ var doctorBundleCommand = async (args, deps) => {
|
|
|
2620
2640
|
};
|
|
2621
2641
|
|
|
2622
2642
|
// src/doctor.ts
|
|
2623
|
-
import * as
|
|
2624
|
-
import * as
|
|
2643
|
+
import * as fs8 from "node:fs";
|
|
2644
|
+
import * as path8 from "node:path";
|
|
2625
2645
|
|
|
2626
2646
|
// src/docker-shutdown.ts
|
|
2627
2647
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2628
|
-
import * as
|
|
2648
|
+
import * as fs7 from "node:fs";
|
|
2629
2649
|
import * as os4 from "node:os";
|
|
2630
|
-
import * as
|
|
2650
|
+
import * as path7 from "node:path";
|
|
2631
2651
|
var DOCKER_CAPTURE_STOP_GRACE_SECONDS = 3600;
|
|
2632
2652
|
var DOCKERD_DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 15;
|
|
2633
2653
|
var flagValue = (argv, name) => {
|
|
@@ -2652,12 +2672,12 @@ var daemonKindOf = (info) => {
|
|
|
2652
2672
|
var defaultConfigPath = (facts, daemon) => {
|
|
2653
2673
|
switch (daemon) {
|
|
2654
2674
|
case "desktop":
|
|
2655
|
-
return
|
|
2675
|
+
return path7.join(facts.home, ".docker", "daemon.json");
|
|
2656
2676
|
case "orbstack":
|
|
2657
|
-
return
|
|
2677
|
+
return path7.join(facts.home, ".orbstack", "config", "docker.json");
|
|
2658
2678
|
case "rootless":
|
|
2659
|
-
return
|
|
2660
|
-
facts.xdgConfigHome ??
|
|
2679
|
+
return path7.join(
|
|
2680
|
+
facts.xdgConfigHome ?? path7.join(facts.home, ".config"),
|
|
2661
2681
|
"docker",
|
|
2662
2682
|
"daemon.json"
|
|
2663
2683
|
);
|
|
@@ -2786,7 +2806,7 @@ var parseDockerInfo = (stdout) => {
|
|
|
2786
2806
|
};
|
|
2787
2807
|
var readHostFile = (file) => {
|
|
2788
2808
|
try {
|
|
2789
|
-
return { kind: "read", text:
|
|
2809
|
+
return { kind: "read", text: fs7.readFileSync(file, "utf8") };
|
|
2790
2810
|
} catch (cause) {
|
|
2791
2811
|
return cause instanceof Error && "code" in cause && cause.code === "ENOENT" ? { kind: "absent" } : { kind: "unreadable" };
|
|
2792
2812
|
}
|
|
@@ -2794,21 +2814,21 @@ var readHostFile = (file) => {
|
|
|
2794
2814
|
var hostProcView = {
|
|
2795
2815
|
list: (dir) => {
|
|
2796
2816
|
try {
|
|
2797
|
-
return
|
|
2817
|
+
return fs7.readdirSync(dir);
|
|
2798
2818
|
} catch {
|
|
2799
2819
|
return null;
|
|
2800
2820
|
}
|
|
2801
2821
|
},
|
|
2802
2822
|
read: (file) => {
|
|
2803
2823
|
try {
|
|
2804
|
-
return
|
|
2824
|
+
return fs7.readFileSync(file, "utf8");
|
|
2805
2825
|
} catch {
|
|
2806
2826
|
return null;
|
|
2807
2827
|
}
|
|
2808
2828
|
},
|
|
2809
2829
|
link: (file) => {
|
|
2810
2830
|
try {
|
|
2811
|
-
return
|
|
2831
|
+
return fs7.readlinkSync(file);
|
|
2812
2832
|
} catch {
|
|
2813
2833
|
return null;
|
|
2814
2834
|
}
|
|
@@ -2848,7 +2868,7 @@ var observeDockerd = (proc, host) => {
|
|
|
2848
2868
|
const cmdline = proc.read(`/proc/${entry}/cmdline`);
|
|
2849
2869
|
if (cmdline === null) continue;
|
|
2850
2870
|
const argv = cmdline.split("\0").filter((arg) => arg !== "");
|
|
2851
|
-
if (
|
|
2871
|
+
if (path7.basename(argv[0] ?? "") === "dockerd") daemons.push({ pid: entry, argv });
|
|
2852
2872
|
}
|
|
2853
2873
|
if (daemons.length === 0) return { kind: "none" };
|
|
2854
2874
|
const socket = unixSocketOf(host);
|
|
@@ -3168,10 +3188,10 @@ var runChecks = async (config, probes) => {
|
|
|
3168
3188
|
return checks;
|
|
3169
3189
|
};
|
|
3170
3190
|
var onPath = (command) => {
|
|
3171
|
-
for (const directory of (process.env["PATH"] ?? "").split(
|
|
3191
|
+
for (const directory of (process.env["PATH"] ?? "").split(path8.delimiter)) {
|
|
3172
3192
|
if (directory === "") continue;
|
|
3173
3193
|
try {
|
|
3174
|
-
|
|
3194
|
+
fs8.accessSync(path8.join(directory, command), fs8.constants.X_OK);
|
|
3175
3195
|
return true;
|
|
3176
3196
|
} catch {
|
|
3177
3197
|
}
|
|
@@ -3196,8 +3216,8 @@ var doctorCommand = async (config, localCredential2, claudeGrant2) => {
|
|
|
3196
3216
|
};
|
|
3197
3217
|
|
|
3198
3218
|
// src/dotfiles.ts
|
|
3199
|
-
import * as
|
|
3200
|
-
import * as
|
|
3219
|
+
import * as fs9 from "node:fs";
|
|
3220
|
+
import * as path9 from "node:path";
|
|
3201
3221
|
var DOTFILE_CANDIDATES = [
|
|
3202
3222
|
{
|
|
3203
3223
|
group: "shell",
|
|
@@ -3240,7 +3260,7 @@ var DOTFILE_CANDIDATES = [
|
|
|
3240
3260
|
var MAX_FILE_BYTES = 1024 * 1024;
|
|
3241
3261
|
var statFile = (home, relative4) => {
|
|
3242
3262
|
try {
|
|
3243
|
-
const stat =
|
|
3263
|
+
const stat = fs9.statSync(path9.join(home, relative4));
|
|
3244
3264
|
return stat.isFile() ? stat : null;
|
|
3245
3265
|
} catch {
|
|
3246
3266
|
return null;
|
|
@@ -3253,9 +3273,9 @@ var scanDotfileCandidates = (home) => DOTFILE_CANDIDATES.flatMap(
|
|
|
3253
3273
|
})
|
|
3254
3274
|
);
|
|
3255
3275
|
var homeRelativePath = (home, requested) => {
|
|
3256
|
-
const relative4 =
|
|
3257
|
-
const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${
|
|
3258
|
-
return outside ? null : relative4.split(
|
|
3276
|
+
const relative4 = path9.relative(home, path9.resolve(home, requested));
|
|
3277
|
+
const outside = relative4 === "" || relative4 === ".." || relative4.startsWith(`..${path9.sep}`) || path9.isAbsolute(relative4);
|
|
3278
|
+
return outside ? null : relative4.split(path9.sep).join("/");
|
|
3259
3279
|
};
|
|
3260
3280
|
var readSyncFiles = (home, paths_) => {
|
|
3261
3281
|
const files = [];
|
|
@@ -3275,7 +3295,7 @@ var readSyncFiles = (home, paths_) => {
|
|
|
3275
3295
|
}
|
|
3276
3296
|
files.push({
|
|
3277
3297
|
path: relative4,
|
|
3278
|
-
contentsBase64:
|
|
3298
|
+
contentsBase64: fs9.readFileSync(path9.join(home, relative4)).toString("base64"),
|
|
3279
3299
|
// eslint-disable-next-line no-bitwise -- permission bits are the point
|
|
3280
3300
|
mode: (stat.mode & 511).toString(8)
|
|
3281
3301
|
});
|
|
@@ -3373,11 +3393,18 @@ var parseGitAuthorArgs = (args) => {
|
|
|
3373
3393
|
};
|
|
3374
3394
|
var gitAuthorLine = (author) => `${author.name} <${author.email}> \xB7 ${author.source === "setting" ? "your setting" : "your account's name and email"}`;
|
|
3375
3395
|
|
|
3396
|
+
// src/http-client.ts
|
|
3397
|
+
import { Agent, setGlobalDispatcher } from "undici";
|
|
3398
|
+
var cliDispatcher = (connect2) => new Agent({ allowH2: false, ...connect2 === void 0 ? {} : { connect: connect2 } });
|
|
3399
|
+
var useHttp1 = () => {
|
|
3400
|
+
setGlobalDispatcher(cliDispatcher());
|
|
3401
|
+
};
|
|
3402
|
+
|
|
3376
3403
|
// src/landing.ts
|
|
3377
3404
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
3378
|
-
import * as
|
|
3405
|
+
import * as fs10 from "node:fs";
|
|
3379
3406
|
import * as os5 from "node:os";
|
|
3380
|
-
import * as
|
|
3407
|
+
import * as path10 from "node:path";
|
|
3381
3408
|
var paint = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
|
|
3382
3409
|
var dim = paint("2");
|
|
3383
3410
|
var green = paint("32");
|
|
@@ -3615,9 +3642,9 @@ var gitRemotes = (cwd) => {
|
|
|
3615
3642
|
var remoteForOrigin = (remotes, originUrl, cwd) => {
|
|
3616
3643
|
if (originUrl === null) return null;
|
|
3617
3644
|
const origin = normalizeRemoteUrl(originUrl);
|
|
3618
|
-
const originPath = origin === null ?
|
|
3645
|
+
const originPath = origin === null ? path10.resolve(originUrl.replace(/^file:\/\//, "")) : null;
|
|
3619
3646
|
return remotes.find(
|
|
3620
|
-
(remote) => origin === null ?
|
|
3647
|
+
(remote) => origin === null ? path10.resolve(cwd, remote.url.replace(/^file:\/\//, "")) === originPath : normalizeRemoteUrl(remote.url) === origin
|
|
3621
3648
|
) ?? null;
|
|
3622
3649
|
};
|
|
3623
3650
|
var LOG_LINES = 10;
|
|
@@ -3640,10 +3667,10 @@ var fetchBundle = (cwd, bundle) => {
|
|
|
3640
3667
|
const before = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
|
|
3641
3668
|
const previous = before.status === 0 ? before.stdout.trim() : null;
|
|
3642
3669
|
if (previous !== bundle.tip) {
|
|
3643
|
-
const dir =
|
|
3670
|
+
const dir = fs10.mkdtempSync(path10.join(os5.tmpdir(), "mend-pull-"));
|
|
3644
3671
|
try {
|
|
3645
|
-
const file =
|
|
3646
|
-
|
|
3672
|
+
const file = path10.join(dir, "change.bundle");
|
|
3673
|
+
fs10.writeFileSync(file, bundle.bytes, { mode: 384 });
|
|
3647
3674
|
const fetched = git(cwd, [
|
|
3648
3675
|
"fetch",
|
|
3649
3676
|
"--no-tags",
|
|
@@ -3653,7 +3680,7 @@ var fetchBundle = (cwd, bundle) => {
|
|
|
3653
3680
|
]);
|
|
3654
3681
|
if (fetched.status !== 0) return refusedWith(gitWords(fetched));
|
|
3655
3682
|
} finally {
|
|
3656
|
-
|
|
3683
|
+
fs10.rmSync(dir, { recursive: true, force: true });
|
|
3657
3684
|
}
|
|
3658
3685
|
}
|
|
3659
3686
|
const after = git(cwd, ["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]).stdout.trim();
|
|
@@ -4087,8 +4114,8 @@ var loginCommand = async (args, deps) => {
|
|
|
4087
4114
|
};
|
|
4088
4115
|
|
|
4089
4116
|
// src/organization.ts
|
|
4090
|
-
import * as
|
|
4091
|
-
import * as
|
|
4117
|
+
import * as fs11 from "node:fs";
|
|
4118
|
+
import * as path11 from "node:path";
|
|
4092
4119
|
var paint4 = (code) => (text) => process.stdout.isTTY === true ? `[${code}m${text}[0m` : text;
|
|
4093
4120
|
var dim4 = paint4("2");
|
|
4094
4121
|
var green4 = paint4("32");
|
|
@@ -4152,7 +4179,7 @@ var scanFolder = (dir) => {
|
|
|
4152
4179
|
const files = [];
|
|
4153
4180
|
const skipped = [];
|
|
4154
4181
|
const walk = (relative4) => {
|
|
4155
|
-
const entries =
|
|
4182
|
+
const entries = fs11.readdirSync(path11.join(dir, relative4), { withFileTypes: true }).toSorted((a, b) => a.name.localeCompare(b.name));
|
|
4156
4183
|
for (const entry of entries) {
|
|
4157
4184
|
const child = relative4 === "" ? entry.name : `${relative4}/${entry.name}`;
|
|
4158
4185
|
if (entry.isSymbolicLink()) {
|
|
@@ -4161,7 +4188,7 @@ var scanFolder = (dir) => {
|
|
|
4161
4188
|
if (SKIPPED_DIRECTORIES.has(entry.name)) skipped.push({ path: child, reason: "skipped" });
|
|
4162
4189
|
else walk(child);
|
|
4163
4190
|
} else if (entry.isFile()) {
|
|
4164
|
-
const size =
|
|
4191
|
+
const size = fs11.statSync(path11.join(dir, child)).size;
|
|
4165
4192
|
if (size > FOLDER_FILE_BYTES) skipped.push({ path: child, reason: "over 1 MiB" });
|
|
4166
4193
|
else files.push({ path: child, size });
|
|
4167
4194
|
}
|
|
@@ -4214,7 +4241,7 @@ var folderCommand = async (api2, args) => {
|
|
|
4214
4241
|
}
|
|
4215
4242
|
case "push": {
|
|
4216
4243
|
if (dir === void 0) return fail4("usage: mend folder push <name> <dir> [--replace]");
|
|
4217
|
-
if (!
|
|
4244
|
+
if (!fs11.existsSync(dir) || !fs11.statSync(dir).isDirectory()) {
|
|
4218
4245
|
return fail4(`${dir} is not a directory`);
|
|
4219
4246
|
}
|
|
4220
4247
|
const folder = await folderNamed(api2, name);
|
|
@@ -4229,7 +4256,7 @@ var folderCommand = async (api2, args) => {
|
|
|
4229
4256
|
files: batch.map((file) => ({
|
|
4230
4257
|
path: file.path,
|
|
4231
4258
|
// Read one batch at a time, so a large tree never sits in memory at once.
|
|
4232
|
-
contentsBase64:
|
|
4259
|
+
contentsBase64: fs11.readFileSync(path11.join(dir, file.path)).toString("base64")
|
|
4233
4260
|
})),
|
|
4234
4261
|
// --replace empties the folder with the first batch; the rest add to it.
|
|
4235
4262
|
merge: !(replace && index === 0)
|
|
@@ -4414,7 +4441,7 @@ import { createHmac, randomBytes } from "node:crypto";
|
|
|
4414
4441
|
import { readFile } from "node:fs/promises";
|
|
4415
4442
|
import * as net2 from "node:net";
|
|
4416
4443
|
import * as os8 from "node:os";
|
|
4417
|
-
import * as
|
|
4444
|
+
import * as path13 from "node:path";
|
|
4418
4445
|
|
|
4419
4446
|
// src/server-docker-volumes.ts
|
|
4420
4447
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -4632,9 +4659,9 @@ var verifyServerDockerVolumes = (runtime, input) => ownership(runtime, input, "v
|
|
|
4632
4659
|
|
|
4633
4660
|
// src/server-store.ts
|
|
4634
4661
|
import { randomUUID } from "node:crypto";
|
|
4635
|
-
import * as
|
|
4662
|
+
import * as fs12 from "node:fs";
|
|
4636
4663
|
import * as os7 from "node:os";
|
|
4637
|
-
import * as
|
|
4664
|
+
import * as path12 from "node:path";
|
|
4638
4665
|
var ServerStoreError = class extends Error {
|
|
4639
4666
|
_tag = "ServerStoreError";
|
|
4640
4667
|
};
|
|
@@ -4655,28 +4682,28 @@ var attempt = (operation) => {
|
|
|
4655
4682
|
var hasCode = (cause, code) => cause instanceof Error && "code" in cause && cause.code === code;
|
|
4656
4683
|
var readOptional = (file) => {
|
|
4657
4684
|
try {
|
|
4658
|
-
return
|
|
4685
|
+
return fs12.readFileSync(file, "utf8");
|
|
4659
4686
|
} catch (cause) {
|
|
4660
4687
|
if (hasCode(cause, "ENOENT")) return null;
|
|
4661
4688
|
throw cause;
|
|
4662
4689
|
}
|
|
4663
4690
|
};
|
|
4664
4691
|
var syncDirectory = (directory) => {
|
|
4665
|
-
const fd =
|
|
4692
|
+
const fd = fs12.openSync(directory, "r");
|
|
4666
4693
|
try {
|
|
4667
|
-
|
|
4694
|
+
fs12.fsyncSync(fd);
|
|
4668
4695
|
} finally {
|
|
4669
|
-
|
|
4696
|
+
fs12.closeSync(fd);
|
|
4670
4697
|
}
|
|
4671
4698
|
};
|
|
4672
4699
|
var writeDurable = (file, content, mode = 384) => {
|
|
4673
|
-
const fd =
|
|
4700
|
+
const fd = fs12.openSync(file, "wx", mode);
|
|
4674
4701
|
try {
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4702
|
+
fs12.writeFileSync(fd, content, "utf8");
|
|
4703
|
+
fs12.fchmodSync(fd, mode);
|
|
4704
|
+
fs12.fsyncSync(fd);
|
|
4678
4705
|
} finally {
|
|
4679
|
-
|
|
4706
|
+
fs12.closeSync(fd);
|
|
4680
4707
|
}
|
|
4681
4708
|
};
|
|
4682
4709
|
var fileKeys = ["identity", "config", "env", "compose", "postgresInit"];
|
|
@@ -4696,7 +4723,7 @@ var parseLockOwner = (raw) => {
|
|
|
4696
4723
|
};
|
|
4697
4724
|
var describeOwner = (lockDir) => {
|
|
4698
4725
|
try {
|
|
4699
|
-
const raw = JSON.parse(
|
|
4726
|
+
const raw = JSON.parse(fs12.readFileSync(path12.join(lockDir, "owner.json"), "utf8"));
|
|
4700
4727
|
const owner = parseLockOwner(raw);
|
|
4701
4728
|
if (owner === null) return "owner metadata is invalid";
|
|
4702
4729
|
const label = `PID ${owner.pid} on ${owner.hostname}`;
|
|
@@ -4716,28 +4743,28 @@ var lockGuidance = (lockDir) => new ServerStoreError(
|
|
|
4716
4743
|
);
|
|
4717
4744
|
var acquireLock = (configDir, create) => {
|
|
4718
4745
|
if (create) {
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
} else if (!
|
|
4746
|
+
fs12.mkdirSync(configDir, { recursive: true, mode: 448 });
|
|
4747
|
+
fs12.chmodSync(configDir, 448);
|
|
4748
|
+
} else if (!fs12.existsSync(configDir)) {
|
|
4722
4749
|
throw new ServerStoreError(
|
|
4723
4750
|
"No Mend server is configured. Run mend server setup explicitly to install one."
|
|
4724
4751
|
);
|
|
4725
4752
|
}
|
|
4726
|
-
const lockDir =
|
|
4753
|
+
const lockDir = path12.join(configDir, "server.lock");
|
|
4727
4754
|
try {
|
|
4728
|
-
|
|
4755
|
+
fs12.mkdirSync(lockDir, { mode: 448 });
|
|
4729
4756
|
} catch (cause) {
|
|
4730
4757
|
if (hasCode(cause, "EEXIST")) throw lockGuidance(lockDir);
|
|
4731
4758
|
throw cause;
|
|
4732
4759
|
}
|
|
4733
|
-
const owned =
|
|
4734
|
-
const ownerFile =
|
|
4760
|
+
const owned = fs12.statSync(lockDir);
|
|
4761
|
+
const ownerFile = path12.join(lockDir, "owner.json");
|
|
4735
4762
|
const owner = `${JSON.stringify({ pid: process.pid, hostname: os7.hostname(), token: randomUUID() })}
|
|
4736
4763
|
`;
|
|
4737
4764
|
let open = true;
|
|
4738
4765
|
const assertOwned = () => {
|
|
4739
|
-
const current =
|
|
4740
|
-
if (!open || current.dev !== owned.dev || current.ino !== owned.ino ||
|
|
4766
|
+
const current = fs12.statSync(lockDir);
|
|
4767
|
+
if (!open || current.dev !== owned.dev || current.ino !== owned.ino || fs12.readFileSync(ownerFile, "utf8") !== owner) {
|
|
4741
4768
|
throw new ServerStoreError(
|
|
4742
4769
|
"Server lock ownership was lost. Stop and check for another server command before retrying."
|
|
4743
4770
|
);
|
|
@@ -4753,8 +4780,8 @@ var acquireLock = (configDir, create) => {
|
|
|
4753
4780
|
release: () => {
|
|
4754
4781
|
const result = attempt(() => {
|
|
4755
4782
|
assertOwned();
|
|
4756
|
-
|
|
4757
|
-
|
|
4783
|
+
fs12.unlinkSync(ownerFile);
|
|
4784
|
+
fs12.rmdirSync(lockDir);
|
|
4758
4785
|
});
|
|
4759
4786
|
open = false;
|
|
4760
4787
|
return result;
|
|
@@ -4763,7 +4790,7 @@ var acquireLock = (configDir, create) => {
|
|
|
4763
4790
|
};
|
|
4764
4791
|
var readIdentity = (paths) => {
|
|
4765
4792
|
const identity = readOptional(paths.identity);
|
|
4766
|
-
if (identity === null && (
|
|
4793
|
+
if (identity === null && (fs12.existsSync(paths.active) || fs12.existsSync(paths.generations) && fs12.readdirSync(paths.generations).length > 0)) {
|
|
4767
4794
|
throw new ServerStoreError(
|
|
4768
4795
|
"Server identity.env is missing. Restore it from a retained generation or backup; refusing to generate replacement credentials."
|
|
4769
4796
|
);
|
|
@@ -4773,7 +4800,7 @@ var readIdentity = (paths) => {
|
|
|
4773
4800
|
var readActive = (paths) => {
|
|
4774
4801
|
let target;
|
|
4775
4802
|
try {
|
|
4776
|
-
target =
|
|
4803
|
+
target = fs12.readlinkSync(paths.active);
|
|
4777
4804
|
} catch (cause) {
|
|
4778
4805
|
if (hasCode(cause, "ENOENT")) return null;
|
|
4779
4806
|
throw cause;
|
|
@@ -4783,13 +4810,13 @@ var readActive = (paths) => {
|
|
|
4783
4810
|
"Server active pointer is invalid. Restore the symlink to a retained generation before retrying."
|
|
4784
4811
|
);
|
|
4785
4812
|
}
|
|
4786
|
-
const directory =
|
|
4813
|
+
const directory = path12.join(paths.configDir, target);
|
|
4787
4814
|
const files = {
|
|
4788
|
-
identity:
|
|
4789
|
-
config:
|
|
4790
|
-
env:
|
|
4791
|
-
compose:
|
|
4792
|
-
postgresInit:
|
|
4815
|
+
identity: fs12.readFileSync(path12.join(directory, fileNames.identity), "utf8"),
|
|
4816
|
+
config: fs12.readFileSync(path12.join(directory, fileNames.config), "utf8"),
|
|
4817
|
+
env: fs12.readFileSync(path12.join(directory, fileNames.env), "utf8"),
|
|
4818
|
+
compose: fs12.readFileSync(path12.join(directory, fileNames.compose), "utf8"),
|
|
4819
|
+
postgresInit: fs12.readFileSync(path12.join(directory, fileNames.postgresInit), "utf8")
|
|
4793
4820
|
};
|
|
4794
4821
|
if (readIdentity(paths) !== files.identity) {
|
|
4795
4822
|
throw new ServerStoreError(
|
|
@@ -4799,20 +4826,20 @@ var readActive = (paths) => {
|
|
|
4799
4826
|
return { directory, files };
|
|
4800
4827
|
};
|
|
4801
4828
|
var publishIdentity = (paths, identity) => {
|
|
4802
|
-
const temporary =
|
|
4829
|
+
const temporary = path12.join(paths.configDir, `.identity-${randomUUID()}`);
|
|
4803
4830
|
writeDurable(temporary, identity);
|
|
4804
|
-
|
|
4831
|
+
fs12.linkSync(temporary, paths.identity);
|
|
4805
4832
|
syncDirectory(paths.configDir);
|
|
4806
|
-
|
|
4833
|
+
fs12.unlinkSync(temporary);
|
|
4807
4834
|
};
|
|
4808
4835
|
var prepareGeneration = (paths, files) => {
|
|
4809
|
-
|
|
4836
|
+
fs12.mkdirSync(paths.generations, { recursive: true, mode: 448 });
|
|
4810
4837
|
const generationName = `gen-${randomUUID()}`;
|
|
4811
|
-
const directory =
|
|
4812
|
-
|
|
4838
|
+
const directory = path12.join(paths.generations, generationName);
|
|
4839
|
+
fs12.mkdirSync(directory, { mode: 448 });
|
|
4813
4840
|
for (const key of fileKeys) {
|
|
4814
4841
|
writeDurable(
|
|
4815
|
-
|
|
4842
|
+
path12.join(directory, fileNames[key]),
|
|
4816
4843
|
files[key],
|
|
4817
4844
|
key === "postgresInit" ? 493 : 384
|
|
4818
4845
|
);
|
|
@@ -4823,18 +4850,18 @@ var prepareGeneration = (paths, files) => {
|
|
|
4823
4850
|
return { directory, files };
|
|
4824
4851
|
};
|
|
4825
4852
|
var activateGeneration = (paths, generation) => {
|
|
4826
|
-
const relative4 =
|
|
4827
|
-
if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !==
|
|
4853
|
+
const relative4 = path12.relative(paths.configDir, generation.directory);
|
|
4854
|
+
if (!/^generations\/gen-[0-9a-f-]{36}$/.test(relative4) || readIdentity(paths) !== fs12.readFileSync(path12.join(generation.directory, "identity.env"), "utf8")) {
|
|
4828
4855
|
throw new ServerStoreError("Cannot activate a generation outside this installation identity.");
|
|
4829
4856
|
}
|
|
4830
4857
|
for (const key of fileKeys) {
|
|
4831
|
-
if (
|
|
4858
|
+
if (fs12.readFileSync(path12.join(generation.directory, fileNames[key]), "utf8") !== generation.files[key]) {
|
|
4832
4859
|
throw new ServerStoreError("Cannot activate an incomplete or changed server generation.");
|
|
4833
4860
|
}
|
|
4834
4861
|
}
|
|
4835
|
-
const pointer =
|
|
4836
|
-
|
|
4837
|
-
|
|
4862
|
+
const pointer = path12.join(paths.configDir, `.active-${randomUUID()}`);
|
|
4863
|
+
fs12.symlinkSync(relative4, pointer);
|
|
4864
|
+
fs12.renameSync(pointer, paths.active);
|
|
4838
4865
|
syncDirectory(paths.configDir);
|
|
4839
4866
|
};
|
|
4840
4867
|
var prepareFiles = (paths, files) => {
|
|
@@ -4843,7 +4870,7 @@ var prepareFiles = (paths, files) => {
|
|
|
4843
4870
|
throw new ServerStoreError("Server identity already exists; refusing to replace credentials.");
|
|
4844
4871
|
}
|
|
4845
4872
|
const active = readActive(paths);
|
|
4846
|
-
if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (
|
|
4873
|
+
if (active !== null && fileKeys.every((key) => files[key] === active.files[key]) && (fs12.statSync(path12.join(active.directory, fileNames.postgresInit)).mode & 4095) === 493)
|
|
4847
4874
|
return active;
|
|
4848
4875
|
if (identity === null) publishIdentity(paths, files.identity);
|
|
4849
4876
|
return prepareGeneration(paths, files);
|
|
@@ -4856,9 +4883,9 @@ var commitGeneration = (paths, files) => {
|
|
|
4856
4883
|
var createStore = (configDir, lock) => {
|
|
4857
4884
|
const paths = {
|
|
4858
4885
|
configDir,
|
|
4859
|
-
identity:
|
|
4860
|
-
generations:
|
|
4861
|
-
active:
|
|
4886
|
+
identity: path12.join(configDir, "identity.env"),
|
|
4887
|
+
generations: path12.join(configDir, "generations"),
|
|
4888
|
+
active: path12.join(configDir, "active")
|
|
4862
4889
|
};
|
|
4863
4890
|
const whileOwned = (operation) => attempt(() => {
|
|
4864
4891
|
lock.assertOwned();
|
|
@@ -4871,13 +4898,13 @@ var createStore = (configDir, lock) => {
|
|
|
4871
4898
|
prepare: (files) => whileOwned(() => prepareFiles(paths, files)),
|
|
4872
4899
|
activate: (generation) => whileOwned(() => activateGeneration(paths, generation)),
|
|
4873
4900
|
createBackup: (previous, target) => whileOwned(() => {
|
|
4874
|
-
const backups =
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
const directory =
|
|
4878
|
-
|
|
4901
|
+
const backups = path12.join(configDir, "backups");
|
|
4902
|
+
fs12.mkdirSync(backups, { recursive: true, mode: 448 });
|
|
4903
|
+
fs12.chmodSync(backups, 448);
|
|
4904
|
+
const directory = path12.join(backups, `upgrade-${randomUUID()}`);
|
|
4905
|
+
fs12.mkdirSync(directory, { mode: 448 });
|
|
4879
4906
|
writeDurable(
|
|
4880
|
-
|
|
4907
|
+
path12.join(directory, "recovery.json"),
|
|
4881
4908
|
`${JSON.stringify(
|
|
4882
4909
|
{
|
|
4883
4910
|
previousGeneration: previous.directory,
|
|
@@ -4893,21 +4920,21 @@ var createStore = (configDir, lock) => {
|
|
|
4893
4920
|
syncDirectory(directory);
|
|
4894
4921
|
syncDirectory(backups);
|
|
4895
4922
|
syncDirectory(configDir);
|
|
4896
|
-
const partialFile =
|
|
4923
|
+
const partialFile = path12.join(directory, "database.sql.partial");
|
|
4897
4924
|
return {
|
|
4898
4925
|
directory,
|
|
4899
4926
|
partialFile,
|
|
4900
4927
|
complete: () => whileOwned(() => {
|
|
4901
|
-
if (
|
|
4928
|
+
if (fs12.statSync(partialFile).size === 0)
|
|
4902
4929
|
throw new ServerStoreError("Database backup is empty; refusing target startup.");
|
|
4903
|
-
const fd =
|
|
4930
|
+
const fd = fs12.openSync(partialFile, "r");
|
|
4904
4931
|
try {
|
|
4905
|
-
|
|
4906
|
-
|
|
4932
|
+
fs12.fchmodSync(fd, 384);
|
|
4933
|
+
fs12.fsyncSync(fd);
|
|
4907
4934
|
} finally {
|
|
4908
|
-
|
|
4935
|
+
fs12.closeSync(fd);
|
|
4909
4936
|
}
|
|
4910
|
-
|
|
4937
|
+
fs12.renameSync(partialFile, path12.join(directory, "database.sql"));
|
|
4911
4938
|
syncDirectory(directory);
|
|
4912
4939
|
})
|
|
4913
4940
|
};
|
|
@@ -4916,7 +4943,7 @@ var createStore = (configDir, lock) => {
|
|
|
4916
4943
|
};
|
|
4917
4944
|
var refuseFlatLayout = (configDir) => {
|
|
4918
4945
|
if (["server.json", "server.env", "compose.yaml", "postgres-init.sh"].some(
|
|
4919
|
-
(name) =>
|
|
4946
|
+
(name) => fs12.existsSync(path12.join(configDir, name))
|
|
4920
4947
|
)) {
|
|
4921
4948
|
throw new ServerStoreError(
|
|
4922
4949
|
"Unreleased flat server configuration found. Preserve its credentials and volumes; migrate it to the generation layout before retrying. Setup will not replace this identity."
|
|
@@ -4924,7 +4951,7 @@ var refuseFlatLayout = (configDir) => {
|
|
|
4924
4951
|
}
|
|
4925
4952
|
};
|
|
4926
4953
|
var withServerStore = async (directory, operation, options = {}) => {
|
|
4927
|
-
const configDir =
|
|
4954
|
+
const configDir = path12.resolve(directory);
|
|
4928
4955
|
let lock;
|
|
4929
4956
|
let result;
|
|
4930
4957
|
try {
|
|
@@ -4941,9 +4968,9 @@ var withServerStore = async (directory, operation, options = {}) => {
|
|
|
4941
4968
|
};
|
|
4942
4969
|
|
|
4943
4970
|
// src/version.ts
|
|
4944
|
-
import * as
|
|
4971
|
+
import * as fs13 from "node:fs";
|
|
4945
4972
|
var cliVersion = () => {
|
|
4946
|
-
const raw =
|
|
4973
|
+
const raw = fs13.readFileSync(new URL("../package.json", import.meta.url), "utf8");
|
|
4947
4974
|
const parsed = JSON.parse(raw);
|
|
4948
4975
|
if (typeof parsed === "object" && parsed !== null && "version" in parsed) {
|
|
4949
4976
|
const version = parsed.version;
|
|
@@ -5646,7 +5673,7 @@ var resolveServerVersion = async (runtime, options, existing) => {
|
|
|
5646
5673
|
return version;
|
|
5647
5674
|
};
|
|
5648
5675
|
var parseDockerSocketPath = (socket) => {
|
|
5649
|
-
if (!
|
|
5676
|
+
if (!path13.isAbsolute(socket) || /[\r\n$'"#:\\]/.test(socket)) {
|
|
5650
5677
|
throw setupError(
|
|
5651
5678
|
"--docker-socket must be an absolute path without line breaks or Compose interpolation characters."
|
|
5652
5679
|
);
|
|
@@ -5672,8 +5699,8 @@ var resolveAssets = async (runtime, serverVersion, existing, store, options) =>
|
|
|
5672
5699
|
if (options.assetsDir !== void 0) {
|
|
5673
5700
|
try {
|
|
5674
5701
|
const [compose2, postgresInit2] = await Promise.all([
|
|
5675
|
-
readFile(
|
|
5676
|
-
readFile(
|
|
5702
|
+
readFile(path13.join(options.assetsDir, COMPOSE_ASSET), "utf8"),
|
|
5703
|
+
readFile(path13.join(options.assetsDir, POSTGRES_INIT_ASSET), "utf8")
|
|
5677
5704
|
]);
|
|
5678
5705
|
validateComposeAsset(compose2);
|
|
5679
5706
|
validatePostgresAsset(postgresInit2);
|
|
@@ -6200,9 +6227,9 @@ var manageServer = async (command, args, runtime, store) => {
|
|
|
6200
6227
|
var nodeServerRuntime = () => {
|
|
6201
6228
|
const environment = { ...process.env };
|
|
6202
6229
|
const home = os8.homedir();
|
|
6203
|
-
const configHome = environment["XDG_CONFIG_HOME"] ??
|
|
6230
|
+
const configHome = environment["XDG_CONFIG_HOME"] ?? path13.join(home, ".config");
|
|
6204
6231
|
return {
|
|
6205
|
-
configDir:
|
|
6232
|
+
configDir: path13.join(configHome, "mend"),
|
|
6206
6233
|
platform: process.platform,
|
|
6207
6234
|
cliVersion: cliVersion(),
|
|
6208
6235
|
run: (command, args, options) => runServerProcess(command, args, environment, options),
|
|
@@ -6655,8 +6682,8 @@ var createServiceTunnels = (options) => {
|
|
|
6655
6682
|
};
|
|
6656
6683
|
|
|
6657
6684
|
// src/skills.ts
|
|
6658
|
-
import * as
|
|
6659
|
-
import * as
|
|
6685
|
+
import * as fs14 from "node:fs";
|
|
6686
|
+
import * as path14 from "node:path";
|
|
6660
6687
|
var DEFAULT_SKILLS_DIR = ".agents/skills";
|
|
6661
6688
|
var MAX_SKILL_FILES = 64;
|
|
6662
6689
|
var MAX_SKILL_FILE_BYTES = 512 * 1024;
|
|
@@ -6679,12 +6706,12 @@ var readFrontmatterDescription = (contents) => {
|
|
|
6679
6706
|
var walkFiles = (root) => {
|
|
6680
6707
|
const found = [];
|
|
6681
6708
|
const visit = (dir) => {
|
|
6682
|
-
for (const entry of
|
|
6709
|
+
for (const entry of fs14.readdirSync(dir, { withFileTypes: true })) {
|
|
6683
6710
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
6684
|
-
const absolute =
|
|
6711
|
+
const absolute = path14.join(dir, entry.name);
|
|
6685
6712
|
let stat;
|
|
6686
6713
|
try {
|
|
6687
|
-
stat =
|
|
6714
|
+
stat = fs14.statSync(absolute);
|
|
6688
6715
|
} catch {
|
|
6689
6716
|
continue;
|
|
6690
6717
|
}
|
|
@@ -6698,7 +6725,7 @@ var walkFiles = (root) => {
|
|
|
6698
6725
|
var scanSkillLibrary = (root) => {
|
|
6699
6726
|
let entries;
|
|
6700
6727
|
try {
|
|
6701
|
-
entries =
|
|
6728
|
+
entries = fs14.readdirSync(root, { withFileTypes: true });
|
|
6702
6729
|
} catch {
|
|
6703
6730
|
return { error: `${root} is not a readable directory` };
|
|
6704
6731
|
}
|
|
@@ -6706,23 +6733,23 @@ var scanSkillLibrary = (root) => {
|
|
|
6706
6733
|
const notes = [];
|
|
6707
6734
|
for (const entry of entries.toSorted((a, b) => a.name.localeCompare(b.name))) {
|
|
6708
6735
|
if (entry.name.startsWith(".")) continue;
|
|
6709
|
-
const skillDir =
|
|
6736
|
+
const skillDir = path14.join(root, entry.name);
|
|
6710
6737
|
let stat;
|
|
6711
6738
|
try {
|
|
6712
|
-
stat =
|
|
6739
|
+
stat = fs14.statSync(skillDir);
|
|
6713
6740
|
} catch {
|
|
6714
6741
|
continue;
|
|
6715
6742
|
}
|
|
6716
6743
|
if (!stat.isDirectory()) continue;
|
|
6717
|
-
if (!
|
|
6744
|
+
if (!fs14.existsSync(path14.join(skillDir, "SKILL.md"))) {
|
|
6718
6745
|
notes.push({ skill: entry.name, message: "no SKILL.md \u2014 skipped" });
|
|
6719
6746
|
continue;
|
|
6720
6747
|
}
|
|
6721
6748
|
const files = [];
|
|
6722
6749
|
let bytes = 0;
|
|
6723
6750
|
for (const absolute of walkFiles(skillDir)) {
|
|
6724
|
-
const relative4 =
|
|
6725
|
-
const buffer =
|
|
6751
|
+
const relative4 = path14.relative(skillDir, absolute).split(path14.sep).join("/");
|
|
6752
|
+
const buffer = fs14.readFileSync(absolute);
|
|
6726
6753
|
if (buffer.byteLength > MAX_SKILL_FILE_BYTES) {
|
|
6727
6754
|
notes.push({ skill: entry.name, message: `${relative4} is over 512KB \u2014 skipped` });
|
|
6728
6755
|
continue;
|
|
@@ -6758,15 +6785,15 @@ var scanSkillLibrary = (root) => {
|
|
|
6758
6785
|
|
|
6759
6786
|
// src/ssh-setup.ts
|
|
6760
6787
|
import * as os10 from "node:os";
|
|
6761
|
-
import * as
|
|
6788
|
+
import * as path16 from "node:path";
|
|
6762
6789
|
|
|
6763
6790
|
// ../../packages/workspace-ssh/src/index.ts
|
|
6764
6791
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
6765
6792
|
import { createHash as createHash3 } from "node:crypto";
|
|
6766
|
-
import * as
|
|
6793
|
+
import * as fs15 from "node:fs";
|
|
6767
6794
|
import { isIP as isIP2 } from "node:net";
|
|
6768
6795
|
import * as os9 from "node:os";
|
|
6769
|
-
import * as
|
|
6796
|
+
import * as path15 from "node:path";
|
|
6770
6797
|
var LEGACY_BLOCK_BEGIN = "# >>> mend workspace ssh (managed by `mend ssh setup`) >>>";
|
|
6771
6798
|
var LEGACY_BLOCK_END = "# <<< mend workspace ssh <<<";
|
|
6772
6799
|
var WorkspaceSshError = class extends Error {
|
|
@@ -6853,8 +6880,8 @@ var blockBegin = (alias) => `# >>> mend workspace ssh ${alias} (managed) >>>`;
|
|
|
6853
6880
|
var blockEnd = (alias) => `# <<< mend workspace ssh ${alias} <<<`;
|
|
6854
6881
|
var hasControls = (value) => /[\p{Cc}\p{Zl}\p{Zp}]/u.test(value);
|
|
6855
6882
|
var parseIdentityPath = (value) => {
|
|
6856
|
-
const resolved =
|
|
6857
|
-
value.startsWith("~/") ?
|
|
6883
|
+
const resolved = path15.resolve(
|
|
6884
|
+
value.startsWith("~/") ? path15.join(os9.homedir(), value.slice(2)) : value
|
|
6858
6885
|
);
|
|
6859
6886
|
if (value === "" || hasControls(value) || hasControls(resolved) || value.includes("${") || resolved.includes("${")) {
|
|
6860
6887
|
return failure(
|
|
@@ -7010,7 +7037,7 @@ var agentKeys = () => {
|
|
|
7010
7037
|
};
|
|
7011
7038
|
var readText = (file) => {
|
|
7012
7039
|
try {
|
|
7013
|
-
return success(
|
|
7040
|
+
return success(fs15.readFileSync(file, "utf8"));
|
|
7014
7041
|
} catch (cause) {
|
|
7015
7042
|
const error = cause instanceof Error && "code" in cause ? cause.code : void 0;
|
|
7016
7043
|
return error === "ENOENT" ? success("") : failure(new WorkspaceSshError("key", `Could not read ${file}.`, cause));
|
|
@@ -7068,19 +7095,19 @@ var readRequiredKey = (keyPath, source) => {
|
|
|
7068
7095
|
var readOptionalKey = (keyPath) => {
|
|
7069
7096
|
const parsed = parseIdentityPath(keyPath);
|
|
7070
7097
|
if (!parsed.ok) return parsed;
|
|
7071
|
-
if (!
|
|
7098
|
+
if (!fs15.existsSync(parsed.value) && !fs15.existsSync(publicKeyPath(parsed.value)))
|
|
7072
7099
|
return success(null);
|
|
7073
7100
|
return readRequiredKey(parsed.value, "existing");
|
|
7074
7101
|
};
|
|
7075
7102
|
var pinAgentKey = (configHome, publicKey) => {
|
|
7076
|
-
const identityFile =
|
|
7103
|
+
const identityFile = path15.resolve(
|
|
7077
7104
|
configHome,
|
|
7078
7105
|
"ssh",
|
|
7079
7106
|
`agent-${hashIdentity(publicKeyIdentity(publicKey))}.pub`
|
|
7080
7107
|
);
|
|
7081
7108
|
try {
|
|
7082
|
-
|
|
7083
|
-
|
|
7109
|
+
fs15.mkdirSync(path15.dirname(identityFile), { recursive: true, mode: 448 });
|
|
7110
|
+
fs15.writeFileSync(identityFile, `${publicKey}
|
|
7084
7111
|
`, { mode: 384 });
|
|
7085
7112
|
return success({ publicKey, identityFile, source: "agent" });
|
|
7086
7113
|
} catch (cause) {
|
|
@@ -7091,10 +7118,10 @@ var pinAgentKey = (configHome, publicKey) => {
|
|
|
7091
7118
|
};
|
|
7092
7119
|
var generateDedicatedKey = (privatePath) => {
|
|
7093
7120
|
try {
|
|
7094
|
-
|
|
7121
|
+
fs15.mkdirSync(path15.dirname(privatePath), { recursive: true, mode: 448 });
|
|
7095
7122
|
} catch (cause) {
|
|
7096
7123
|
return failure(
|
|
7097
|
-
new WorkspaceSshError("key", `Could not create ${
|
|
7124
|
+
new WorkspaceSshError("key", `Could not create ${path15.dirname(privatePath)}.`, cause)
|
|
7098
7125
|
);
|
|
7099
7126
|
}
|
|
7100
7127
|
const generated = spawnSync5(
|
|
@@ -7123,7 +7150,7 @@ var generateDedicatedKey = (privatePath) => {
|
|
|
7123
7150
|
return fresh;
|
|
7124
7151
|
};
|
|
7125
7152
|
var dedicatedKey = (configHome, create) => {
|
|
7126
|
-
const privatePath =
|
|
7153
|
+
const privatePath = path15.join(configHome, "ssh", "id_ed25519");
|
|
7127
7154
|
const existing = readOptionalKey(privatePath);
|
|
7128
7155
|
if (existing.ok === false) return failure(existing.error);
|
|
7129
7156
|
if (existing.value !== null || !create) return existing;
|
|
@@ -7171,10 +7198,10 @@ var writeWorkspaceSshConfig = (configFile, target, identityFile) => {
|
|
|
7171
7198
|
const reconciled = reconcileWorkspaceSshConfig(before.value, target, identityFile);
|
|
7172
7199
|
if (reconciled.ok === false) return failure(reconciled.error);
|
|
7173
7200
|
try {
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7201
|
+
fs15.mkdirSync(path15.dirname(configFile), { recursive: true, mode: 448 });
|
|
7202
|
+
fs15.chmodSync(path15.dirname(configFile), 448);
|
|
7203
|
+
fs15.writeFileSync(configFile, reconciled.value, { mode: 384 });
|
|
7204
|
+
fs15.chmodSync(configFile, 384);
|
|
7178
7205
|
return success(void 0);
|
|
7179
7206
|
} catch (cause) {
|
|
7180
7207
|
return failure(new WorkspaceSshError("config", `Could not write ${configFile}.`, cause));
|
|
@@ -7187,7 +7214,7 @@ var dim5 = ansi("2");
|
|
|
7187
7214
|
var green5 = ansi("32");
|
|
7188
7215
|
var warn = ansi("33");
|
|
7189
7216
|
var say5 = (line) => console.log(line);
|
|
7190
|
-
var sshConfigPath = () =>
|
|
7217
|
+
var sshConfigPath = () => path16.join(os10.homedir(), ".ssh", "config");
|
|
7191
7218
|
var flagValue2 = (args, flag) => {
|
|
7192
7219
|
const index = args.indexOf(flag);
|
|
7193
7220
|
return index === -1 || args[index + 1] === void 0 ? null : String(args[index + 1]);
|
|
@@ -7299,8 +7326,8 @@ var sshCommand = async (args, api2, cliHome, serverUrl) => {
|
|
|
7299
7326
|
};
|
|
7300
7327
|
|
|
7301
7328
|
// src/uninstall.ts
|
|
7302
|
-
import * as
|
|
7303
|
-
import * as
|
|
7329
|
+
import * as fs16 from "node:fs";
|
|
7330
|
+
import * as path17 from "node:path";
|
|
7304
7331
|
var UNINSTALL_USAGE = "usage: mend uninstall [--all | --server | --home] [--yes]";
|
|
7305
7332
|
var parseUninstallArgs = (args) => {
|
|
7306
7333
|
let scope = null;
|
|
@@ -7322,14 +7349,14 @@ var parseUninstallArgs = (args) => {
|
|
|
7322
7349
|
var SERVER_FILES = ["identity.env", "active", "generations", "backups"];
|
|
7323
7350
|
var countEntries = (directory) => {
|
|
7324
7351
|
try {
|
|
7325
|
-
return
|
|
7352
|
+
return fs16.readdirSync(directory).length;
|
|
7326
7353
|
} catch {
|
|
7327
7354
|
return 0;
|
|
7328
7355
|
}
|
|
7329
7356
|
};
|
|
7330
7357
|
var exists = (file) => {
|
|
7331
7358
|
try {
|
|
7332
|
-
|
|
7359
|
+
fs16.lstatSync(file);
|
|
7333
7360
|
return true;
|
|
7334
7361
|
} catch {
|
|
7335
7362
|
return false;
|
|
@@ -7338,7 +7365,7 @@ var exists = (file) => {
|
|
|
7338
7365
|
var managedBlockCount = (sshConfigFile) => {
|
|
7339
7366
|
let config;
|
|
7340
7367
|
try {
|
|
7341
|
-
config =
|
|
7368
|
+
config = fs16.readFileSync(sshConfigFile, "utf8");
|
|
7342
7369
|
} catch {
|
|
7343
7370
|
return 0;
|
|
7344
7371
|
}
|
|
@@ -7362,27 +7389,27 @@ var describeUninstall = async (runtime, scope) => {
|
|
|
7362
7389
|
if (!read.error.message.startsWith("No Mend server is configured")) throw read.error;
|
|
7363
7390
|
server = "none";
|
|
7364
7391
|
} else if (read.value === null) {
|
|
7365
|
-
server = exists(
|
|
7392
|
+
server = exists(path17.join(configDir, "identity.env")) ? {
|
|
7366
7393
|
version: "(no active generation)",
|
|
7367
7394
|
appUrl: "",
|
|
7368
7395
|
dockerContext: "",
|
|
7369
|
-
generations: countEntries(
|
|
7370
|
-
backups: countEntries(
|
|
7396
|
+
generations: countEntries(path17.join(configDir, "generations")),
|
|
7397
|
+
backups: countEntries(path17.join(configDir, "backups"))
|
|
7371
7398
|
} : "none";
|
|
7372
7399
|
} else {
|
|
7373
7400
|
server = {
|
|
7374
7401
|
version: read.value.config.serverVersion,
|
|
7375
7402
|
appUrl: read.value.config.appUrl,
|
|
7376
7403
|
dockerContext: read.value.config.dockerContext,
|
|
7377
|
-
generations: countEntries(
|
|
7378
|
-
backups: countEntries(
|
|
7404
|
+
generations: countEntries(path17.join(configDir, "generations")),
|
|
7405
|
+
backups: countEntries(path17.join(configDir, "backups"))
|
|
7379
7406
|
};
|
|
7380
7407
|
}
|
|
7381
7408
|
}
|
|
7382
7409
|
let home = null;
|
|
7383
7410
|
if (scope !== "server") {
|
|
7384
|
-
const cliConfig =
|
|
7385
|
-
const sshDirectory =
|
|
7411
|
+
const cliConfig = path17.join(runtime.cliHome, "cli.json");
|
|
7412
|
+
const sshDirectory = path17.join(runtime.cliHome, "ssh");
|
|
7386
7413
|
home = {
|
|
7387
7414
|
cliConfig: exists(cliConfig) ? cliConfig : null,
|
|
7388
7415
|
sshDirectory: exists(sshDirectory) ? sshDirectory : null,
|
|
@@ -7514,7 +7541,7 @@ var removeServer = async (runtime, failures, leftovers) => {
|
|
|
7514
7541
|
}
|
|
7515
7542
|
}
|
|
7516
7543
|
for (const name of SERVER_FILES) {
|
|
7517
|
-
|
|
7544
|
+
fs16.rmSync(path17.join(configDir, name), { recursive: true, force: true });
|
|
7518
7545
|
}
|
|
7519
7546
|
server.writeLine(`removed ${configDir}/{identity.env, active, generations, backups}`);
|
|
7520
7547
|
},
|
|
@@ -7525,20 +7552,20 @@ var removeServer = async (runtime, failures, leftovers) => {
|
|
|
7525
7552
|
var removeHome = async (runtime, plan, failures, leftovers) => {
|
|
7526
7553
|
const { server } = runtime;
|
|
7527
7554
|
if (plan.cliConfig !== null) {
|
|
7528
|
-
|
|
7555
|
+
fs16.rmSync(plan.cliConfig, { force: true });
|
|
7529
7556
|
server.writeLine(`removed ${plan.cliConfig}`);
|
|
7530
7557
|
}
|
|
7531
7558
|
if (plan.sshDirectory !== null) {
|
|
7532
|
-
|
|
7559
|
+
fs16.rmSync(plan.sshDirectory, { recursive: true, force: true });
|
|
7533
7560
|
server.writeLine(`removed ${plan.sshDirectory}`);
|
|
7534
7561
|
}
|
|
7535
7562
|
if (plan.managedSshBlocks > 0) {
|
|
7536
7563
|
try {
|
|
7537
|
-
const before =
|
|
7564
|
+
const before = fs16.readFileSync(runtime.sshConfigFile, "utf8");
|
|
7538
7565
|
const stripped = stripManagedWorkspaceSshBlocks(before);
|
|
7539
7566
|
if (!stripped.ok) throw stripped.error;
|
|
7540
7567
|
if (stripped.value.removed > 0) {
|
|
7541
|
-
|
|
7568
|
+
fs16.writeFileSync(runtime.sshConfigFile, stripped.value.config, { mode: 384 });
|
|
7542
7569
|
server.writeLine(
|
|
7543
7570
|
`removed ${plural(stripped.value.removed, "managed block")} from ${runtime.sshConfigFile}`
|
|
7544
7571
|
);
|
|
@@ -7551,12 +7578,12 @@ var removeHome = async (runtime, plan, failures, leftovers) => {
|
|
|
7551
7578
|
}
|
|
7552
7579
|
let remaining = [];
|
|
7553
7580
|
try {
|
|
7554
|
-
remaining =
|
|
7581
|
+
remaining = fs16.readdirSync(runtime.cliHome);
|
|
7555
7582
|
} catch {
|
|
7556
7583
|
return;
|
|
7557
7584
|
}
|
|
7558
7585
|
if (remaining.length === 0) {
|
|
7559
|
-
|
|
7586
|
+
fs16.rmdirSync(runtime.cliHome);
|
|
7560
7587
|
server.writeLine(`removed ${runtime.cliHome}`);
|
|
7561
7588
|
} else {
|
|
7562
7589
|
leftovers.push(
|
|
@@ -7620,19 +7647,19 @@ var upgradeUrl = async (input) => {
|
|
|
7620
7647
|
// src/main.ts
|
|
7621
7648
|
var mendCliHome = () => {
|
|
7622
7649
|
const xdg = process.env["XDG_CONFIG_HOME"];
|
|
7623
|
-
const preferred =
|
|
7624
|
-
xdg === void 0 || xdg === "" ?
|
|
7650
|
+
const preferred = path18.join(
|
|
7651
|
+
xdg === void 0 || xdg === "" ? path18.join(os11.homedir(), ".config") : xdg,
|
|
7625
7652
|
"mend"
|
|
7626
7653
|
);
|
|
7627
|
-
const legacy =
|
|
7628
|
-
return !
|
|
7654
|
+
const legacy = path18.join(os11.homedir(), ".mend");
|
|
7655
|
+
return !fs17.existsSync(preferred) && fs17.existsSync(legacy) ? legacy : preferred;
|
|
7629
7656
|
};
|
|
7630
|
-
var CONFIG_PATH =
|
|
7657
|
+
var CONFIG_PATH = path18.join(mendCliHome(), "cli.json");
|
|
7631
7658
|
var loadConfig = () => {
|
|
7632
7659
|
let fileConfig = {};
|
|
7633
|
-
if (
|
|
7660
|
+
if (fs17.existsSync(CONFIG_PATH)) {
|
|
7634
7661
|
try {
|
|
7635
|
-
fileConfig = JSON.parse(
|
|
7662
|
+
fileConfig = JSON.parse(fs17.readFileSync(CONFIG_PATH, "utf8"));
|
|
7636
7663
|
} catch {
|
|
7637
7664
|
fail6(`could not parse ${CONFIG_PATH}`);
|
|
7638
7665
|
}
|
|
@@ -7884,7 +7911,7 @@ var adopt = async (config, args) => {
|
|
|
7884
7911
|
}
|
|
7885
7912
|
const sourceIssue = repositoryCloneUrlIssue(source);
|
|
7886
7913
|
if (sourceIssue !== null) return fail6(sourceIssue);
|
|
7887
|
-
const projectName = name ?? normalizeProjectName(
|
|
7914
|
+
const projectName = name ?? normalizeProjectName(path18.basename(source, ".git"));
|
|
7888
7915
|
const auth = authFlagIndex !== -1 && args[authFlagIndex + 1] !== void 0 ? String(args[authFlagIndex + 1]) : null;
|
|
7889
7916
|
if (auth !== null && auth !== "ambient" && auth !== "mend-key" && auth !== "bridge") {
|
|
7890
7917
|
return fail6(`--auth takes "ambient", "mend-key", or "bridge", not "${auth}"`);
|
|
@@ -7933,7 +7960,7 @@ var findProject = async (config, explicit, adoptCwd = false) => {
|
|
|
7933
7960
|
const sourceIssue = repositoryCloneUrlIssue(facts.originUrl);
|
|
7934
7961
|
if (sourceIssue !== null) return fail6(sourceIssue);
|
|
7935
7962
|
const adopted = await api(config, "POST", "/projects", {
|
|
7936
|
-
name: normalizeProjectName(
|
|
7963
|
+
name: normalizeProjectName(path18.basename(facts.repoRoot)),
|
|
7937
7964
|
source: facts.originUrl
|
|
7938
7965
|
});
|
|
7939
7966
|
say6(`${green6("\u2713")} adopted \xB7 ${adopted.name} \xB7 ${dim6(adopted.storePath)}`);
|
|
@@ -8894,13 +8921,13 @@ var serviceRestart = async (config, args) => {
|
|
|
8894
8921
|
};
|
|
8895
8922
|
var serviceInit = async (args) => {
|
|
8896
8923
|
const root = gitTopLevel(process.cwd()) ?? process.cwd();
|
|
8897
|
-
const target =
|
|
8898
|
-
if (
|
|
8924
|
+
const target = path18.join(root, "mend.toml");
|
|
8925
|
+
if (fs17.existsSync(target)) {
|
|
8899
8926
|
return fail6(`${target} already exists \u2014 edit it directly (init never merges)`);
|
|
8900
8927
|
}
|
|
8901
|
-
const rootFiles =
|
|
8928
|
+
const rootFiles = fs17.readdirSync(root);
|
|
8902
8929
|
const proposals = [];
|
|
8903
|
-
const readRoot = (name) =>
|
|
8930
|
+
const readRoot = (name) => fs17.readFileSync(path18.join(root, name), "utf8");
|
|
8904
8931
|
if (rootFiles.includes("package.json")) {
|
|
8905
8932
|
proposals.push(...proposeFromPackageJson(readRoot("package.json"), rootFiles));
|
|
8906
8933
|
}
|
|
@@ -8910,13 +8937,13 @@ var serviceInit = async (args) => {
|
|
|
8910
8937
|
);
|
|
8911
8938
|
for (const glob of globs) {
|
|
8912
8939
|
const parent = glob.replace(/\/?\*+$/, "");
|
|
8913
|
-
const dirs = glob.endsWith("*") ?
|
|
8940
|
+
const dirs = glob.endsWith("*") ? fs17.existsSync(path18.join(root, parent)) ? fs17.readdirSync(path18.join(root, parent)).map((dir) => path18.join(parent, dir)) : [] : [glob];
|
|
8914
8941
|
for (const dir of dirs) {
|
|
8915
|
-
const manifest =
|
|
8916
|
-
if (!
|
|
8942
|
+
const manifest = path18.join(root, dir, "package.json");
|
|
8943
|
+
if (!fs17.existsSync(manifest)) continue;
|
|
8917
8944
|
for (const proposal of proposeFromWorkspacePackage(
|
|
8918
|
-
|
|
8919
|
-
|
|
8945
|
+
path18.basename(dir),
|
|
8946
|
+
fs17.readFileSync(manifest, "utf8"),
|
|
8920
8947
|
rootFiles
|
|
8921
8948
|
)) {
|
|
8922
8949
|
if (!proposals.some((existing) => existing.name === proposal.name)) {
|
|
@@ -8960,7 +8987,7 @@ var serviceInit = async (args) => {
|
|
|
8960
8987
|
return;
|
|
8961
8988
|
}
|
|
8962
8989
|
}
|
|
8963
|
-
|
|
8990
|
+
fs17.writeFileSync(target, toml);
|
|
8964
8991
|
say6(`${green6("\u2713")} wrote ${target} \u2014 commit it, then: mend service run ${proposals[0]?.name}`);
|
|
8965
8992
|
};
|
|
8966
8993
|
var tunnelServices = async (config, services, portOverride) => {
|
|
@@ -9064,18 +9091,18 @@ var serviceCommand = async (config, args) => {
|
|
|
9064
9091
|
}
|
|
9065
9092
|
};
|
|
9066
9093
|
var isProvider = (value) => value === "claude" || value === "codex" || value === "github";
|
|
9067
|
-
var readIfExists = (file) =>
|
|
9094
|
+
var readIfExists = (file) => fs17.existsSync(file) ? fs17.readFileSync(file, "utf8") : null;
|
|
9068
9095
|
var localCredential = (provider) => {
|
|
9069
9096
|
const home = os11.homedir();
|
|
9070
9097
|
switch (provider) {
|
|
9071
9098
|
case "codex":
|
|
9072
9099
|
return readIfExists(
|
|
9073
|
-
|
|
9100
|
+
path18.join(process.env["CODEX_HOME"] ?? path18.join(home, ".codex"), "auth.json")
|
|
9074
9101
|
);
|
|
9075
9102
|
case "claude":
|
|
9076
9103
|
return readIfExists(
|
|
9077
|
-
|
|
9078
|
-
process.env["CLAUDE_CONFIG_DIR"] ??
|
|
9104
|
+
path18.join(
|
|
9105
|
+
process.env["CLAUDE_CONFIG_DIR"] ?? path18.join(home, ".claude"),
|
|
9079
9106
|
".credentials.json"
|
|
9080
9107
|
)
|
|
9081
9108
|
);
|
|
@@ -9125,7 +9152,7 @@ var claudeGrantSecret = () => {
|
|
|
9125
9152
|
return read.kind === "missing" ? null : read.secret;
|
|
9126
9153
|
};
|
|
9127
9154
|
var doctorBundle = (config, args) => doctorBundleCommand(args, {
|
|
9128
|
-
defaultDir:
|
|
9155
|
+
defaultDir: path18.join(mendCliHome(), "bundles"),
|
|
9129
9156
|
now: () => /* @__PURE__ */ new Date(),
|
|
9130
9157
|
say: say6,
|
|
9131
9158
|
warn: (line) => process.stderr.write(`${line}
|
|
@@ -9166,74 +9193,69 @@ var staleGrantReason = (secret) => {
|
|
|
9166
9193
|
};
|
|
9167
9194
|
var claudeGrant = async () => {
|
|
9168
9195
|
const cli = claudeCli();
|
|
9169
|
-
const
|
|
9196
|
+
const home = mendCliHome();
|
|
9170
9197
|
const personal = personalClaudeDir();
|
|
9171
9198
|
const personalBefore = grantStatus(cli, personal);
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
say6(
|
|
9199
|
+
const dir = throwawayLoginDir(home, "claude-login-");
|
|
9200
|
+
try {
|
|
9201
|
+
if (grantStatus(cli, dir) === null) {
|
|
9202
|
+
fail6(
|
|
9203
|
+
"claude: could not run `claude auth status` \u2014 install Claude Code, set MEND_CLAUDE_BIN, or paste a credential: mend connect claude --from-stdin"
|
|
9204
|
+
);
|
|
9205
|
+
return null;
|
|
9206
|
+
}
|
|
9207
|
+
say6(" Mend needs its own Claude login; it is sent to your server and not kept here");
|
|
9181
9208
|
say6(dim6(" your own Claude login stays as it is"));
|
|
9182
9209
|
if (!runClaudeLogin(cli, dir)) {
|
|
9183
9210
|
fail6("claude: the login did not complete");
|
|
9184
9211
|
return null;
|
|
9185
9212
|
}
|
|
9186
|
-
status = grantStatus(cli, dir)
|
|
9187
|
-
if (!status.loggedIn) {
|
|
9213
|
+
const status = grantStatus(cli, dir);
|
|
9214
|
+
if (status === null || !status.loggedIn) {
|
|
9188
9215
|
fail6("claude: the login completed but Claude still reports no grant in Mend's directory");
|
|
9189
9216
|
return null;
|
|
9190
9217
|
}
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9218
|
+
if (status.configDirectory !== null && path18.resolve(status.configDirectory) !== path18.resolve(dir)) {
|
|
9219
|
+
fail6(
|
|
9220
|
+
`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`
|
|
9221
|
+
);
|
|
9222
|
+
return null;
|
|
9223
|
+
}
|
|
9224
|
+
const read = readGrant(dir);
|
|
9225
|
+
if (read.kind === "missing") {
|
|
9226
|
+
const where = read.triedService === null ? read.triedPath : `${read.triedPath} or the Keychain item ${read.triedService}`;
|
|
9227
|
+
fail6(`claude: logged in, but no credential to read \u2014 looked in ${where}`);
|
|
9228
|
+
return null;
|
|
9229
|
+
}
|
|
9200
9230
|
const why = staleGrantReason(read.secret);
|
|
9201
9231
|
if (why !== null) {
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
fail6("claude: the login did not complete");
|
|
9205
|
-
return null;
|
|
9206
|
-
}
|
|
9207
|
-
read = readGrant(dir);
|
|
9208
|
-
const still = read.kind === "missing" ? "missing" : staleGrantReason(read.secret);
|
|
9209
|
-
if (still !== null) {
|
|
9210
|
-
fail6(`claude: the grant is still ${still} after logging in`);
|
|
9211
|
-
return null;
|
|
9212
|
-
}
|
|
9232
|
+
fail6(`claude: the new login is already ${why}`);
|
|
9233
|
+
return null;
|
|
9213
9234
|
}
|
|
9235
|
+
const mine = localCredential("claude");
|
|
9236
|
+
if (mine !== null && sameGrant(read.secret, mine)) {
|
|
9237
|
+
fail6(
|
|
9238
|
+
"claude: that is the same grant this machine's Claude holds, so both sides would race on refresh \u2014 connect it deliberately with --use-my-login"
|
|
9239
|
+
);
|
|
9240
|
+
return null;
|
|
9241
|
+
}
|
|
9242
|
+
const personalAfter = grantStatus(cli, personal);
|
|
9243
|
+
if (personalBefore?.loggedIn === true && personalAfter?.loggedIn === false) {
|
|
9244
|
+
say6(
|
|
9245
|
+
` your own Claude login was signed out by this one \u2014 this account allows one grant at a time. Run \`claude auth login\` to get it back, and connect the shared grant with --use-my-login instead.`
|
|
9246
|
+
);
|
|
9247
|
+
} else if (personalAfter?.loggedIn === true) {
|
|
9248
|
+
say6(dim6(" your own Claude login still works \xB7 verified"));
|
|
9249
|
+
}
|
|
9250
|
+
const kept = claudeGrantDir(home);
|
|
9251
|
+
if (fs17.existsSync(kept)) {
|
|
9252
|
+
forgetGrant(kept);
|
|
9253
|
+
say6(dim6(` removed the copy an older mend kept in ${kept} \xB7 the server refreshes the login`));
|
|
9254
|
+
}
|
|
9255
|
+
return read.secret;
|
|
9256
|
+
} finally {
|
|
9257
|
+
forgetGrant(dir);
|
|
9214
9258
|
}
|
|
9215
|
-
if (read.kind === "missing") {
|
|
9216
|
-
const where = read.triedService === null ? read.triedPath : `${read.triedPath} or the Keychain item ${read.triedService}`;
|
|
9217
|
-
fail6(`claude: logged in, but no credential to read \u2014 looked in ${where}`);
|
|
9218
|
-
return null;
|
|
9219
|
-
}
|
|
9220
|
-
const mine = localCredential("claude");
|
|
9221
|
-
if (mine !== null && sameGrant(read.secret, mine)) {
|
|
9222
|
-
fail6(
|
|
9223
|
-
"claude: that is the same grant this machine's Claude holds, so both sides would race on refresh \u2014 remove Mend's directory and log in again, or accept the race with --use-my-login"
|
|
9224
|
-
);
|
|
9225
|
-
return null;
|
|
9226
|
-
}
|
|
9227
|
-
const personalAfter = grantStatus(cli, personal);
|
|
9228
|
-
if (personalBefore?.loggedIn === true && personalAfter?.loggedIn === false) {
|
|
9229
|
-
say6(
|
|
9230
|
-
` your own Claude login was signed out by this one \u2014 this account allows one grant at a time. Run \`claude auth login\` to get it back, and connect the shared grant with --use-my-login instead.`
|
|
9231
|
-
);
|
|
9232
|
-
} else if (personalAfter?.loggedIn === true) {
|
|
9233
|
-
say6(dim6(" your own Claude login still works \xB7 verified"));
|
|
9234
|
-
}
|
|
9235
|
-
say6(dim6(` grant ${read.kind === "file" ? read.path : `keychain ${read.service}`}`));
|
|
9236
|
-
return read.secret;
|
|
9237
9259
|
};
|
|
9238
9260
|
var connectCommand = async (config, args) => {
|
|
9239
9261
|
const [providerArg, ...flags] = args;
|
|
@@ -9252,13 +9274,14 @@ var connectCommand = async (config, args) => {
|
|
|
9252
9274
|
}
|
|
9253
9275
|
let secret;
|
|
9254
9276
|
if (flags.includes("--from-stdin")) {
|
|
9255
|
-
secret =
|
|
9277
|
+
secret = fs17.readFileSync(0, "utf8").trim();
|
|
9256
9278
|
if (secret === "") return fail6("nothing on stdin");
|
|
9257
9279
|
} else if (provider === "codex" && !flags.includes("--use-my-login")) {
|
|
9258
9280
|
const grant = codexGrant({
|
|
9259
9281
|
cli: codexCli(),
|
|
9260
|
-
personalAuthJson: readIfExists(
|
|
9261
|
-
say: say6
|
|
9282
|
+
personalAuthJson: readIfExists(path18.join(personalCodexHome(), "auth.json")),
|
|
9283
|
+
say: say6,
|
|
9284
|
+
parent: mendCliHome()
|
|
9262
9285
|
});
|
|
9263
9286
|
if (grant.kind === "failed") return fail6(grant.reason);
|
|
9264
9287
|
secret = grant.secret;
|
|
@@ -9306,14 +9329,14 @@ var takeFlagValue4 = (args, flag) => {
|
|
|
9306
9329
|
return at !== -1 && args[at + 1] !== void 0 ? String(args[at + 1]) : null;
|
|
9307
9330
|
};
|
|
9308
9331
|
var saveCliConfig = (next) => {
|
|
9309
|
-
|
|
9310
|
-
|
|
9332
|
+
fs17.mkdirSync(path18.dirname(CONFIG_PATH), { recursive: true, mode: 448 });
|
|
9333
|
+
fs17.writeFileSync(
|
|
9311
9334
|
CONFIG_PATH,
|
|
9312
9335
|
`${JSON.stringify({ url: next.url, token: next.token, deviceId: next.deviceId }, null, 2)}
|
|
9313
9336
|
`,
|
|
9314
9337
|
{ mode: 384 }
|
|
9315
9338
|
);
|
|
9316
|
-
|
|
9339
|
+
fs17.chmodSync(CONFIG_PATH, 384);
|
|
9317
9340
|
};
|
|
9318
9341
|
var login = async (config, args) => {
|
|
9319
9342
|
await loginCommand(args, {
|
|
@@ -9326,7 +9349,7 @@ var login = async (config, args) => {
|
|
|
9326
9349
|
});
|
|
9327
9350
|
};
|
|
9328
9351
|
var logout = async (config) => {
|
|
9329
|
-
if (!
|
|
9352
|
+
if (!fs17.existsSync(CONFIG_PATH) && config.token === null) {
|
|
9330
9353
|
say6(dim6("nothing saved \u2014 already signed out"));
|
|
9331
9354
|
return;
|
|
9332
9355
|
}
|
|
@@ -9379,8 +9402,8 @@ var uninstallCommand = async (config, args) => {
|
|
|
9379
9402
|
const runtime = {
|
|
9380
9403
|
server,
|
|
9381
9404
|
cliHome: mendCliHome(),
|
|
9382
|
-
sshConfigFile:
|
|
9383
|
-
signedIn: config.token === null || !
|
|
9405
|
+
sshConfigFile: path18.join(os11.homedir(), ".ssh", "config"),
|
|
9406
|
+
signedIn: config.token === null || !fs17.existsSync(CONFIG_PATH) ? null : { url: config.url, deviceId: config.deviceId },
|
|
9384
9407
|
revokeDevice: async () => {
|
|
9385
9408
|
if (config.deviceId === null) return "no device id saved";
|
|
9386
9409
|
try {
|
|
@@ -9621,10 +9644,10 @@ var envLoad = async (config, args) => {
|
|
|
9621
9644
|
const positional = args.filter(
|
|
9622
9645
|
(arg, i) => !arg.startsWith("--") && args[i - 1] !== "--project" && !consumed.has(i)
|
|
9623
9646
|
);
|
|
9624
|
-
const file =
|
|
9647
|
+
const file = path18.resolve(positional[0] ?? ".env");
|
|
9625
9648
|
let contents;
|
|
9626
9649
|
try {
|
|
9627
|
-
contents =
|
|
9650
|
+
contents = fs17.readFileSync(file, "utf8");
|
|
9628
9651
|
} catch {
|
|
9629
9652
|
return fail6(`cannot read ${file} \u2014 pass a path: mend env load path/to/.env`);
|
|
9630
9653
|
}
|
|
@@ -9640,10 +9663,10 @@ var envLoad = async (config, args) => {
|
|
|
9640
9663
|
{ contents, allSecret, secretNames }
|
|
9641
9664
|
);
|
|
9642
9665
|
if (report.loaded.length === 0 && report.rejected.length === 0 && report.malformedLines.length === 0) {
|
|
9643
|
-
say6(dim6(`${
|
|
9666
|
+
say6(dim6(`${path18.basename(file)} has no variables`));
|
|
9644
9667
|
return;
|
|
9645
9668
|
}
|
|
9646
|
-
say6(`${green6("\u2713")} loaded ${
|
|
9669
|
+
say6(`${green6("\u2713")} loaded ${path18.basename(file)} into ${project2.name}`);
|
|
9647
9670
|
for (const line of formatLoadReport(report, { dim: dim6, warn: amber2 })) say6(line);
|
|
9648
9671
|
const plaintextUrls = report.loaded.filter(
|
|
9649
9672
|
(entry) => entry.lane === "configuration" && /_(URL|URI|DSN)$/.test(entry.name)
|
|
@@ -9834,7 +9857,7 @@ var skillsList = async (config, args) => {
|
|
|
9834
9857
|
};
|
|
9835
9858
|
var skillsPush = async (config, args) => {
|
|
9836
9859
|
const dirFlag = takeFlagValue4(args, "--dir");
|
|
9837
|
-
const root = dirFlag === null ?
|
|
9860
|
+
const root = dirFlag === null ? path18.join(os11.homedir(), DEFAULT_SKILLS_DIR) : path18.resolve(dirFlag);
|
|
9838
9861
|
const scanned = scanSkillLibrary(root);
|
|
9839
9862
|
if ("error" in scanned) return fail6(scanned.error);
|
|
9840
9863
|
for (const note of scanned.notes) say6(dim6(` ${note.skill}: ${note.message}`));
|
|
@@ -10590,15 +10613,16 @@ var manCommand = (words) => {
|
|
|
10590
10613
|
}
|
|
10591
10614
|
const version = cliVersion();
|
|
10592
10615
|
const page = doc === null ? renderManIndex(version) : renderManPage(doc, version);
|
|
10593
|
-
const file =
|
|
10594
|
-
|
|
10616
|
+
const file = path18.join(os11.tmpdir(), `mend-man-${process.pid}-${manFileName(doc)}`);
|
|
10617
|
+
fs17.writeFileSync(file, page);
|
|
10595
10618
|
const result = spawnSync6("man", ["-l", file], { stdio: "inherit" });
|
|
10596
|
-
|
|
10619
|
+
fs17.rmSync(file, { force: true });
|
|
10597
10620
|
if (result.error !== void 0 || result.status !== 0) {
|
|
10598
10621
|
say6(doc === null ? renderIndex() : renderCommand(doc));
|
|
10599
10622
|
}
|
|
10600
10623
|
};
|
|
10601
10624
|
var main = async () => {
|
|
10625
|
+
useHttp1();
|
|
10602
10626
|
const [command, ...rest] = process.argv.slice(2);
|
|
10603
10627
|
const ownArgs = rest.slice(0, rest.indexOf("--") === -1 ? rest.length : rest.indexOf("--"));
|
|
10604
10628
|
if (command !== void 0 && command !== "help" && ownArgs.some((a) => a === "--help" || a === "-h")) {
|