@treeport/treeport 0.7.0 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/node/cli/index.js +79 -55
- package/dist/node/server/index.js +40 -6
- package/dist/node/server/terminal-host-entry.js +18 -1
- package/dist/{update-UYS2lMdD.js → update-BYHlwpAq.js} +488 -97
- package/dist/web/assets/index-BtAAdn1A.js +84 -0
- package/dist/web/index.html +1 -1
- package/package.json +4 -3
- package/dist/web/assets/index-C5cx0N4G.js +0 -84
package/dist/node/cli/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Ct as projectsResponseSchema, Dt as terminalCaptureResponseSchema, G as treeportRpcClientLayer, Gt as webPanelDefinitionsResponseSchema, K as TreeportRpcs, Lt as treeContextResponseSchema, Nt as terminalResponseSchema, Ot as terminalObservationResponseSchema, Pt as terminatedTerminalsResponseSchema, St as projectResponseSchema, Tt as removePreviewResponseSchema, U as decodeUnknownOrNull, V as webPanelInputSchema, _t as packageOperationResponseSchema, bt as packageReloadResponseSchema, dt as openBrowserPanelResponseSchema, et as browserAgentResponseSchema, ft as openWebPanelResponseSchema, gt as packageListingResponseSchema, mt as operationResponseSchema, nt as browserInstallStatusSchema, o as apiErrorBodySchema, t as TERMINAL_CAPTURE_MAX_LINES, tt as browserInstallResponseSchema, ut as okResponseSchema, vt as packageOperationsResponseSchema, yt as packageProjectResponseSchema } from "../../dist-BsLn2Gbc.js";
|
|
3
|
-
import { A as
|
|
3
|
+
import { A as runDoctor, C as daemonStatus, D as readDaemonLogs, E as enableTailscaleRemote, F as formatServiceStatus, I as humanOutput, L as stateName, M as treeportVersion, N as formatLocalUpdateError, O as resolveLocalApiUrl, P as formatLocalUpdateResult, S as daemonHealth, T as disableTailscaleRemote, _ as serviceRun, b as serviceStop, d as readServiceLogs, f as serviceApply, g as serviceInstalled, h as serviceEnable, j as tailscaleRemoteStatus, k as resolvePackagePath, l as runLocalUpdate, m as serviceDoctorCheck, p as serviceDisable, r as confirmLocalUpdate, t as LocalUpdateError, v as serviceStart, w as daemonUp, x as daemonDown, y as serviceStatus, z as parseDurationMs } from "../../update-BYHlwpAq.js";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { Command, CommanderError } from "commander";
|
|
@@ -99,6 +99,9 @@ let writeStderr = (value) => {
|
|
|
99
99
|
};
|
|
100
100
|
let requestedExitCode = 0;
|
|
101
101
|
let cliEnvironment = process.env;
|
|
102
|
+
let output = humanOutput();
|
|
103
|
+
let errorOutput = humanOutput();
|
|
104
|
+
let updateProgressShown = false;
|
|
102
105
|
var CliError = class extends Error {
|
|
103
106
|
exitCode;
|
|
104
107
|
code;
|
|
@@ -119,22 +122,6 @@ async function resolveDaemonLifecycle() {
|
|
|
119
122
|
}
|
|
120
123
|
return await serviceInstalled() ? "service" : "treeport";
|
|
121
124
|
}
|
|
122
|
-
function formatServiceStatus(status) {
|
|
123
|
-
const mode = status.mode === "headless" ? "advanced headless (starts before login)" : status.mode === "user" && status.manager === "launchd" ? "user/login (starts after login)" : status.mode === "user" ? "user service" : "not installed";
|
|
124
|
-
const lines = [
|
|
125
|
-
`Treeport service: ${status.state}`,
|
|
126
|
-
`Mode: ${mode}`,
|
|
127
|
-
`Manager: ${status.manager ?? "unsupported"}`,
|
|
128
|
-
`Starts before login: ${status.enabledAtBoot ? "yes" : "no"}`,
|
|
129
|
-
`Active: ${status.active ? "yes" : "no"}`,
|
|
130
|
-
`Definition: ${status.definitionPath ?? "not installed"}`
|
|
131
|
-
];
|
|
132
|
-
if (status.daemon?.state) lines.push(`PID: ${status.daemon.state.pid}`);
|
|
133
|
-
if (status.issues.length) lines.push(...status.issues.map((issue) => `Issue: ${issue}`));
|
|
134
|
-
if (status.administratorCommand) lines.push("Administrator action required:", status.administratorCommand, "Then run: treeport service status");
|
|
135
|
-
else if (status.recoveryCommands.length) lines.push(`Next: ${status.recoveryCommands[0]}`);
|
|
136
|
-
return lines.join("\n");
|
|
137
|
-
}
|
|
138
125
|
async function ensureServiceDaemon() {
|
|
139
126
|
const result = await serviceStart();
|
|
140
127
|
const state = result.status.daemon?.state;
|
|
@@ -474,12 +461,17 @@ function print(value, human) {
|
|
|
474
461
|
async function main(args) {
|
|
475
462
|
const argv = args[0] === "spawn" || args[0] === "terminal" && args[1] === "create" ? commandArgv(args) : void 0;
|
|
476
463
|
let parserError = "";
|
|
477
|
-
const program = new Command().name("treeport").usage("[options] [folder] [command]").description("Manage Treeport projects, trees, and terminals.").argument("[folder]", "folder or folder inside a Git repository to open").option("--json", "emit machine-readable JSON").configureOutput({
|
|
464
|
+
const program = new Command().name("treeport").usage("[options] [folder] [command]").description("Manage Treeport projects, trees, and terminals.").version(output.heading(await treeportVersion()), "-v, --version", "show installed CLI version").argument("[folder]", "folder or folder inside a Git repository to open").option("--json", "emit machine-readable JSON").configureOutput({
|
|
465
|
+
getOutHasColors: () => output.enabled,
|
|
466
|
+
getErrHasColors: () => errorOutput.enabled,
|
|
478
467
|
writeOut: writeStdout,
|
|
479
468
|
writeErr: (value) => {
|
|
480
469
|
parserError += value;
|
|
481
470
|
}
|
|
482
|
-
}).showHelpAfterError().
|
|
471
|
+
}).showHelpAfterError(jsonOutput).configureHelp({
|
|
472
|
+
styleTitle: output.heading,
|
|
473
|
+
styleCommandText: output.heading
|
|
474
|
+
}).exitOverride();
|
|
483
475
|
program.action(async (folder) => {
|
|
484
476
|
if (folder === void 0) {
|
|
485
477
|
writeStdout(program.helpInformation());
|
|
@@ -535,7 +527,7 @@ async function main(args) {
|
|
|
535
527
|
if (lifecycle === "service") {
|
|
536
528
|
if (options.foreground || options.host || options.port) throw new CliError("An installed service owns the listener and process mode. Run `treeport service enable` to refresh its configuration, or `treeport service disable` to return to local background mode.", 5, "DAEMON_LIFECYCLE_SERVICE");
|
|
537
529
|
const result = await serviceStart();
|
|
538
|
-
print(result, () => formatServiceStatus(result.status));
|
|
530
|
+
print(result, () => formatServiceStatus(result.status, output));
|
|
539
531
|
if (result.administratorCommand || !result.status.healthy) requestedExitCode = 1;
|
|
540
532
|
return;
|
|
541
533
|
}
|
|
@@ -543,10 +535,13 @@ async function main(args) {
|
|
|
543
535
|
const daemonOptions = {};
|
|
544
536
|
if (options.host !== void 0) daemonOptions.host = options.host;
|
|
545
537
|
if (port !== void 0) daemonOptions.port = port;
|
|
546
|
-
if (options.foreground !== void 0)
|
|
538
|
+
if (options.foreground !== void 0) {
|
|
539
|
+
daemonOptions.foreground = options.foreground;
|
|
540
|
+
daemonOptions.output = output;
|
|
541
|
+
}
|
|
547
542
|
const result = await daemonUp(daemonOptions);
|
|
548
543
|
if (options.foreground) return;
|
|
549
|
-
print(result, () =>
|
|
544
|
+
print(result, () => output.blocks(output.summary("Treeport is running", "success"), output.rows([["URL", result.apiUrl]])));
|
|
550
545
|
});
|
|
551
546
|
const stopCommand = program.command("stop").description("Stop the local daemon and preserve terminal sessions").option("--terminate-terminals", "terminate every Treeport-owned terminal process").option("--force", "confirm termination of all terminals").option("--json", "emit machine-readable JSON");
|
|
552
547
|
stopCommand.action(async () => {
|
|
@@ -557,12 +552,12 @@ async function main(args) {
|
|
|
557
552
|
if (options.terminateTerminals) await request("/api/admin/terminate-terminals", terminatedTerminalsResponseSchema, { method: "POST" });
|
|
558
553
|
if (lifecycle === "service") {
|
|
559
554
|
const result = await serviceStop();
|
|
560
|
-
print(result, () => formatServiceStatus(result.status));
|
|
555
|
+
print(result, () => formatServiceStatus(result.status, output));
|
|
561
556
|
if (result.administratorCommand) requestedExitCode = 1;
|
|
562
557
|
return;
|
|
563
558
|
}
|
|
564
559
|
const result = await daemonDown();
|
|
565
|
-
print(result, () => result.wasRunning ? "Treeport is stopped" : "Treeport is already stopped");
|
|
560
|
+
print(result, () => output.summary(result.wasRunning ? "Treeport is stopped" : "Treeport is already stopped", "success"));
|
|
566
561
|
});
|
|
567
562
|
const serviceCommand = program.command("service").description("Manage opt-in OS service supervision");
|
|
568
563
|
serviceCommand.action(() => {
|
|
@@ -571,12 +566,12 @@ async function main(args) {
|
|
|
571
566
|
const serviceEnableCommand = serviceCommand.command("enable").description("Enable automatic startup and unexpected-exit restarts").option("--headless", "use advanced macOS startup before login (requires an administrator)").option("--json", "emit machine-readable JSON");
|
|
572
567
|
serviceEnableCommand.action(async () => {
|
|
573
568
|
const result = await serviceEnable(serviceEnableCommand.opts().headless ? "headless" : "user");
|
|
574
|
-
print(result, () => formatServiceStatus(result.status));
|
|
569
|
+
print(result, () => formatServiceStatus(result.status, output));
|
|
575
570
|
if (result.status.state === "action_required") requestedExitCode = 1;
|
|
576
571
|
});
|
|
577
572
|
serviceCommand.command("status").description("Show OS service supervision status").option("--json", "emit machine-readable JSON").action(async () => {
|
|
578
573
|
const result = await serviceStatus();
|
|
579
|
-
print(result, () => formatServiceStatus(result));
|
|
574
|
+
print(result, () => formatServiceStatus(result, output));
|
|
580
575
|
if (![
|
|
581
576
|
"disabled",
|
|
582
577
|
"healthy",
|
|
@@ -585,7 +580,7 @@ async function main(args) {
|
|
|
585
580
|
});
|
|
586
581
|
serviceCommand.command("disable").description("Stop and unregister OS service supervision").option("--json", "emit machine-readable JSON").action(async () => {
|
|
587
582
|
const result = await serviceDisable();
|
|
588
|
-
print(result, () => formatServiceStatus(result.status));
|
|
583
|
+
print(result, () => formatServiceStatus(result.status, output));
|
|
589
584
|
if (result.administratorCommand || result.status.state !== "disabled") requestedExitCode = 1;
|
|
590
585
|
});
|
|
591
586
|
serviceCommand.command("run", { hidden: true }).action(async () => serviceRun());
|
|
@@ -593,7 +588,7 @@ async function main(args) {
|
|
|
593
588
|
serviceApplyCommand.action(async () => {
|
|
594
589
|
const { request: requestPath } = serviceApplyCommand.opts();
|
|
595
590
|
const result = await serviceApply(requestPath);
|
|
596
|
-
print(result, () => `Applied Treeport service ${result.operation} request
|
|
591
|
+
print(result, () => output.summary(`Applied Treeport service ${result.operation} request.`, "success"));
|
|
597
592
|
});
|
|
598
593
|
const remoteCommand = program.command("remote").description("Expose Treeport privately through Tailscale Serve");
|
|
599
594
|
remoteCommand.action(() => {
|
|
@@ -611,37 +606,48 @@ async function main(args) {
|
|
|
611
606
|
if (port !== void 0) remoteOptions.port = port;
|
|
612
607
|
if (serviceDaemon !== void 0) remoteOptions.daemon = serviceDaemon;
|
|
613
608
|
const result = await enableTailscaleRemote(remoteOptions);
|
|
614
|
-
print(result, () => `Treeport remote access is ${result.alreadyEnabled ? "already enabled" : "enabled"}
|
|
609
|
+
print(result, () => output.blocks(output.summary(`Treeport remote access is ${result.alreadyEnabled ? "already enabled" : "enabled"}`, "success"), output.rows([["URL", result.url]]), "Tailscale authenticates each remote user. Access is limited by your Tailscale policy."));
|
|
615
610
|
});
|
|
616
611
|
remoteCommand.command("status").description("Show Tailscale remote access status").option("--json", "emit machine-readable JSON").action(async () => {
|
|
617
612
|
const result = await tailscaleRemoteStatus();
|
|
618
613
|
print(result, () => {
|
|
619
|
-
if (!result.configured) return "Treeport remote access is disabled";
|
|
620
|
-
return result.active ?
|
|
614
|
+
if (!result.configured) return output.summary("Treeport remote access is disabled");
|
|
615
|
+
return output.blocks(output.summary(result.active ? "Treeport remote access is enabled" : "Treeport remote access is unavailable", result.active ? "success" : "warning"), output.rows([[result.active ? "URL" : "Expected URL", result.url ?? "Unavailable"]]), !result.active && "The Tailscale Serve route no longer points to Treeport.");
|
|
621
616
|
});
|
|
622
617
|
});
|
|
623
618
|
remoteCommand.command("disable").description("Disable Treeport Tailscale remote access").option("--json", "emit machine-readable JSON").action(async () => {
|
|
624
619
|
const result = await disableTailscaleRemote();
|
|
625
620
|
print(result, () => {
|
|
626
|
-
if (result.changedTailscale) return "Treeport remote access is disabled";
|
|
627
|
-
return result.wasEnabled ? "Treeport remote access is disabled" : "Treeport remote access was already disabled; the current Tailscale route was left unchanged.";
|
|
621
|
+
if (result.changedTailscale) return output.summary("Treeport remote access is disabled", "success");
|
|
622
|
+
return output.summary(result.wasEnabled ? "Treeport remote access is disabled" : "Treeport remote access was already disabled; the current Tailscale route was left unchanged.", "success");
|
|
628
623
|
});
|
|
629
624
|
});
|
|
630
625
|
program.command("status").description("Show local daemon status").option("--json", "emit machine-readable JSON").action(async () => {
|
|
631
|
-
const status = await daemonStatus();
|
|
626
|
+
const [cliVersion, status] = await Promise.all([treeportVersion(), daemonStatus()]);
|
|
632
627
|
const supervision = await serviceInstalled() ? await serviceStatus() : null;
|
|
628
|
+
const observed = supervision?.daemon ?? status;
|
|
629
|
+
const daemonVersion = observed.running && observed.verified ? observed.health?.version ?? null : null;
|
|
633
630
|
const projectList = status.verified ? await projects() : [];
|
|
634
631
|
const result = {
|
|
635
632
|
...status,
|
|
633
|
+
cliVersion,
|
|
634
|
+
daemonVersion,
|
|
636
635
|
service: supervision,
|
|
637
636
|
projects: projectList.length,
|
|
638
637
|
worktrees: projectList.reduce((count, project) => count + project.worktrees.length, 0),
|
|
639
638
|
terminals: projectList.reduce((count, project) => count + project.worktrees.reduce((worktreeCount, worktree) => worktreeCount + worktree.terminals.length, 0), 0)
|
|
640
639
|
};
|
|
641
640
|
print(result, () => {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
641
|
+
const healthy = observed.running && observed.verified;
|
|
642
|
+
return output.blocks(output.heading("Treeport status"), output.summary(!observed.state ? "Treeport is stopped" : healthy ? "Treeport is running" : "Treeport is unhealthy", !observed.state ? "neutral" : healthy ? "success" : "failure"), output.rows([
|
|
643
|
+
["CLI version", cliVersion],
|
|
644
|
+
["Daemon version", daemonVersion ?? "Unavailable"],
|
|
645
|
+
observed.state ? ["URL", observed.state.apiUrl] : null
|
|
646
|
+
]), daemonVersion !== null && daemonVersion !== cliVersion && output.summary("Version mismatch: CLI and daemon versions differ", "warning"), healthy && output.detail(output.rows([
|
|
647
|
+
supervision ? null : ["Lifecycle", observed.health?.daemonLifecycle === "external" ? "Externally managed" : observed.health?.daemonLifecycle === "service" ? "OS service" : "Treeport"],
|
|
648
|
+
supervision ? null : ["PID", observed.state.pid],
|
|
649
|
+
status.verified ? ["Workspace", `${result.projects} projects · ${result.worktrees} trees · ${result.terminals} terminals`] : null
|
|
650
|
+
])), !healthy && observed.state !== null && output.rows([["PID", observed.state.pid], ["Logs", path.join(observed.state.dataDir, "logs", "daemon.log")]]), supervision !== null && formatServiceStatus(supervision, output));
|
|
645
651
|
});
|
|
646
652
|
});
|
|
647
653
|
const logsCommand = program.command("logs").description("Show recent daemon logs").option("--lines <count>", "number of lines", "100");
|
|
@@ -652,7 +658,7 @@ async function main(args) {
|
|
|
652
658
|
});
|
|
653
659
|
program.command("doctor").description("Diagnose local requirements and paths").option("--json", "emit machine-readable JSON").action(async () => {
|
|
654
660
|
const checks = [...await runDoctor(), await serviceDoctorCheck()];
|
|
655
|
-
print(checks, () => checks.
|
|
661
|
+
print(checks, () => output.blocks(output.heading("Treeport doctor"), output.summary(checks.every((check) => check.ok) ? "All checks passed" : "Some checks failed", checks.every((check) => check.ok) ? "success" : "failure"), ...checks.map((check) => `${output.summary(check.name, check.ok ? "success" : "failure")}\n${output.indent(check.detail.startsWith("state: ") ? stateName(check.detail.slice(7)) : check.ok ? output.detail(check.detail) : check.detail)}`)));
|
|
656
662
|
if (checks.some((check) => !check.ok)) requestedExitCode = 1;
|
|
657
663
|
});
|
|
658
664
|
program.command("version").description("Show CLI and daemon versions").option("--json", "emit machine-readable JSON").action(async () => {
|
|
@@ -661,7 +667,7 @@ async function main(args) {
|
|
|
661
667
|
cli,
|
|
662
668
|
daemon: status.verified ? status.health?.version ?? null : null
|
|
663
669
|
};
|
|
664
|
-
print(result, () =>
|
|
670
|
+
print(result, () => output.blocks(output.heading("Treeport version"), output.rows([["CLI", result.cli], ["Daemon", result.daemon ?? "Unavailable"]]), result.daemon !== null && result.daemon !== result.cli && output.summary("Version mismatch: CLI and daemon versions differ", "warning")));
|
|
665
671
|
});
|
|
666
672
|
program.command("skills").description("Print the Treeport usage guide for AI agents").action(async () => {
|
|
667
673
|
const skill = await fs.readFile(await resolvePackagePath("skills", "treeport", "SKILL.md"), "utf8");
|
|
@@ -679,7 +685,7 @@ async function main(args) {
|
|
|
679
685
|
print({
|
|
680
686
|
managed: false,
|
|
681
687
|
reason: "outside_treeport"
|
|
682
|
-
}, () => "Not running in a Treeport-managed terminal.");
|
|
688
|
+
}, () => output.blocks(output.heading("Treeport context"), "Not running in a Treeport-managed terminal."));
|
|
683
689
|
return;
|
|
684
690
|
}
|
|
685
691
|
const missing = [
|
|
@@ -743,7 +749,12 @@ async function main(args) {
|
|
|
743
749
|
}).join("").split("\n");
|
|
744
750
|
return ` ${key}: ${first}${rest.length > 0 ? `\n${rest.map((line) => ` ${line}`).join("\n")}` : ""}`;
|
|
745
751
|
}).join("\n");
|
|
746
|
-
return
|
|
752
|
+
return output.blocks(output.heading("Treeport context"), output.rows([
|
|
753
|
+
["Project", `${context.project.name} (${context.project.id})`],
|
|
754
|
+
["Tree", `${context.worktree.name} (${context.worktree.id})`],
|
|
755
|
+
["Path", context.worktree.path],
|
|
756
|
+
["Terminal", `${context.terminal.name} (${context.terminal.id}) — ${stateName(context.terminal.status)}`]
|
|
757
|
+
]), entries.length > 0 && `${output.heading("Tree context")}\n${treeContextText}`, output.detail(output.rows([["API", context.apiUrl], ["Lifecycle", context.daemonLifecycle === "external" ? "Externally managed" : context.daemonLifecycle === "service" ? "OS service" : "Treeport"]])));
|
|
747
758
|
});
|
|
748
759
|
});
|
|
749
760
|
const browserCommand = program.command("browser").description("Manage Browser and its hosted Chromium");
|
|
@@ -868,29 +879,36 @@ async function main(args) {
|
|
|
868
879
|
return lines.join("\n");
|
|
869
880
|
});
|
|
870
881
|
});
|
|
871
|
-
const updatePackagesCommand = program.command("update").description("Update Treeport or explicitly update configured packages").argument("[source]", "one configured npm: source").option("--packages", "update every eligible configured package").option("--json", "emit machine-readable JSON");
|
|
882
|
+
const updatePackagesCommand = program.command("update").description("Update Treeport or explicitly update configured packages").argument("[source]", "one configured npm: source").option("--packages", "update every eligible configured package").option("-y, --yes", "approve the Treeport self-update without prompting").option("--start", "start Treeport after a self-update if it was stopped").option("--json", "emit machine-readable JSON");
|
|
872
883
|
updatePackagesCommand.action(async (source) => {
|
|
873
884
|
const options = updatePackagesCommand.opts();
|
|
874
885
|
if (source && options.packages) throw new CliError("Specify a package source or --packages, not both.", 2);
|
|
886
|
+
if ((source || options.packages) && (options.yes || options.start)) throw new CliError("--yes and --start apply only to Treeport self-updates.", 2);
|
|
875
887
|
if (!source && !options.packages) {
|
|
876
888
|
if (await resolveDaemonLifecycle() === "external") throw new CliError("Cannot update Treeport because this daemon lifecycle is externally managed.", 5, "UPDATE_EXTERNAL_REFUSED");
|
|
877
|
-
const selfUpdateOptions = {
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
if (
|
|
885
|
-
|
|
886
|
-
|
|
889
|
+
const selfUpdateOptions = {
|
|
890
|
+
environment: cliEnvironment,
|
|
891
|
+
yes: options.yes ?? false,
|
|
892
|
+
start: options.start ?? false
|
|
893
|
+
};
|
|
894
|
+
if (!jsonOutput && process.stdin.isTTY && process.stdout.isTTY && process.stderr.isTTY) selfUpdateOptions.confirm = (preview, signal) => confirmLocalUpdate(preview, signal, process.stdin, process.stderr, errorOutput);
|
|
895
|
+
if (!jsonOutput) selfUpdateOptions.progress = (message) => {
|
|
896
|
+
if (!updateProgressShown) {
|
|
897
|
+
writeStderr(`${errorOutput.heading("Treeport update")}\n\n`);
|
|
898
|
+
updateProgressShown = true;
|
|
899
|
+
}
|
|
900
|
+
writeStderr(`${errorOutput.indent(errorOutput.summary(message, "warning"))}\n`);
|
|
901
|
+
};
|
|
902
|
+
const result = await runLocalUpdate(selfUpdateOptions);
|
|
903
|
+
if (updateProgressShown) writeStderr("\n");
|
|
904
|
+
print(result, () => formatLocalUpdateResult(result, output));
|
|
887
905
|
return;
|
|
888
906
|
}
|
|
889
907
|
const results = (await request("/api/packages/update", packageOperationsResponseSchema, {
|
|
890
908
|
method: "POST",
|
|
891
909
|
body: JSON.stringify(source ? { source: await packageSource(source) } : {})
|
|
892
910
|
})).results;
|
|
893
|
-
print(results, () => results.map((result) => `${result.status}
|
|
911
|
+
print(results, () => output.blocks(output.heading("Treeport package update"), ...results.map((result) => output.blocks(output.summary(`${stateName(result.status)} ${result.source ?? "packages"}`, result.status === "skipped" ? "warning" : "success"), output.detail(output.rows([["Scope", stateName(result.scope)]])), result.reason ? output.indent(result.reason) : null))));
|
|
894
912
|
});
|
|
895
913
|
const reloadCommand = program.command("reload").description("Reload package settings and resources without restarting").option("-l, --local", "reload only the registered project containing the current directory").option("--json", "emit machine-readable JSON");
|
|
896
914
|
reloadCommand.action(async () => {
|
|
@@ -1114,10 +1132,13 @@ async function runCliApplication(options) {
|
|
|
1114
1132
|
writeStdout = options.stdout ?? ((value) => process.stdout.write(value));
|
|
1115
1133
|
writeStderr = options.stderr ?? ((value) => process.stderr.write(value));
|
|
1116
1134
|
requestedExitCode = 0;
|
|
1135
|
+
updateProgressShown = false;
|
|
1136
|
+
output = humanOutput(environment, options.stdoutIsTTY ?? (!options.stdout && Boolean(process.stdout.isTTY)), jsonOutput);
|
|
1137
|
+
errorOutput = humanOutput(environment, options.stderrIsTTY ?? (!options.stderr && Boolean(process.stderr.isTTY)), jsonOutput);
|
|
1117
1138
|
try {
|
|
1118
1139
|
await main([...options.args]);
|
|
1119
1140
|
} catch (error) {
|
|
1120
|
-
const cliError = error instanceof CliError ? error : new CliError(error instanceof Error ? error.message : String(error), 1);
|
|
1141
|
+
const cliError = error instanceof CliError || error instanceof LocalUpdateError ? error : new CliError(error instanceof Error ? error.message : String(error), 1);
|
|
1121
1142
|
if (jsonOutput) {
|
|
1122
1143
|
const body = { error: cliError.details === void 0 ? {
|
|
1123
1144
|
code: cliError.code,
|
|
@@ -1128,7 +1149,10 @@ async function runCliApplication(options) {
|
|
|
1128
1149
|
details: cliError.details
|
|
1129
1150
|
} };
|
|
1130
1151
|
writeStderr(`${JSON.stringify(body)}\n`);
|
|
1131
|
-
} else
|
|
1152
|
+
} else {
|
|
1153
|
+
const text = error instanceof LocalUpdateError ? formatLocalUpdateError(error.message, error.details, errorOutput, error.code === "UPDATE_CANCELLED" || error.code === "UPDATE_INTERRUPTED") : errorOutput.blocks(errorOutput.summary(cliError.code === "USAGE_ERROR" ? "Invalid command" : "Command failed", "failure"), errorOutput.indent(cliError.message.replace(/^error: /, "")), cliError.code === "USAGE_ERROR" && errorOutput.next(["treeport --help"]));
|
|
1154
|
+
writeStderr(`${updateProgressShown ? "\n" : ""}${text}\n`);
|
|
1155
|
+
}
|
|
1132
1156
|
requestedExitCode = cliError.exitCode;
|
|
1133
1157
|
}
|
|
1134
1158
|
return requestedExitCode;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $ as applicationUpdateStatusSchema, A as requestWorkspaceOpenSchema, An as parseBrowserOwnerAuth, At as terminalPresetDefinitionsQuerySchema, B as updateWebPanelPermissionGrantSchema, Bt as treeFileSearchResultSchema, C as packageUpdateSchema, Cn as browserTicketRequestSchema, Ct as projectsResponseSchema, D as reorderWorkspaceItemsSchema, Dn as parseBrowserAuth, Dt as terminalCaptureResponseSchema, E as removeWorktreeSchema, En as encodeBrowserFrame, Et as storageValueResponseSchema, F as treeContextValuesSchema, Ft as treeContextFieldListingSchema, Gt as webPanelDefinitionsResponseSchema, H as writeTreeFileSchema, Ht as uploadedFileResponseSchema, I as treeFilePathSchema, It as treeContextFieldsQuerySchema, J as parseSocketHandshake, Jt as worktreesResponseSchema, K as TreeportRpcs, Kt as webPanelResponseSchema, L as updateProjectSchema, Lt as treeContextResponseSchema, M as setWebPanelStorageSchema, Mt as terminalPresetsResponseSchema, N as terminalCaptureQuerySchema, Nt as terminalResponseSchema, O as repositoryTerminalPresetSchema, On as parseBrowserCaptureMessage, Ot as terminalObservationResponseSchema, P as treeContextFieldDefinitionSchema, Pt as terminatedTerminalsResponseSchema, Q as presenceUpdateSchema, Qt as TERMINAL_MAX_CLIENT_MESSAGE_BYTES, R as updateTerminalPresetSchema, Rt as treeFileListingSchema, S as packageRemoveSchema, Sn as browserOwnerTicketResponseSchema, St as projectResponseSchema, T as registerProjectSchema, Tn as browserUrlSchema, Tt as removePreviewResponseSchema, U as decodeUnknownOrNull, Ut as webPanelContextResponseSchema, V as webPanelInputSchema, Vt as treeFileWriteResultSchema, W as isSchemaValue, Wt as webPanelDefinitionResponseSchema, X as PRESENCE_TIMEOUT_MS, Xt as SOCKET_PATH, Y as parseSocketMessage, Yt as BROWSER_VIDEO_CAPTURE_SOURCE, Z as presenceResponseSchema, Zt as TERMINAL_CONTROLLER_GRACE_MS, _ as openBrowserPanelFromTerminalSchema, _n as BROWSER_MAX_MESSAGE_BYTES, _t as packageOperationResponseSchema, at as gitDiffImageSchema, b as packageProjectQuerySchema, bn as browserOwnerIdentitySchema, bt as packageReloadResponseSchema, c as createBrowserPanelSchema, cn as terminalInputSchema, ct as healthResponseSchema, d as createWebPanelSchema, dn as terminalResizeSchema, dt as openBrowserPanelResponseSchema, et as browserAgentResponseSchema, f as createWorktreeSchema, fn as terminalSizeSchema, ft as openWebPanelResponseSchema, g as getWebPanelStorageSchema, gt as packageListingResponseSchema, h as formatCommandLine, hn as webPanelPermissionSchema, ht as operationsResponseSchema, i as TREE_FILE_MAX_BYTES, in as parseTerminalClientEvent, it as directoryBrowseResponseSchema, j as searchTreeFilesSchema, jn as parseBrowserOwnerClientMessage, jt as terminalPresetResponseSchema, k as repositoryTerminalPresetsFileSchema, kn as parseBrowserClientMessage, kt as terminalPresetDefinitionListingSchema, l as createTerminalPresetSchema, ln as terminalOutputAckSchema, lt as listenerDiscoveryResponseSchema, m as deleteWebPanelStorageSchema, mn as gitDiffImageRequestSchema, mt as operationResponseSchema, n as TERMINAL_MAX_UPLOAD_BYTES, nt as browserInstallStatusSchema, o as apiErrorBodySchema, on as terminalBellAcknowledgementSchema, ot as gitDiffResponseSchema, p as deleteTerminalPresetSchema, pn as terminalTakeControlSchema, pt as operationQuerySchema, q as parseProductEvent, qt as worktreeResponseSchema, r as TREE_FILE_LIST_MAX_ENTRIES, rn as parseTerminalAuth, rt as deletePanelQuerySchema, s as browseDirectoryQuerySchema, sn as terminalBinarySchema, st as hasDataResponseSchema, tt as browserInstallResponseSchema, u as createTerminalSchema, un as terminalQueryAuthorityRequestSchema, ut as okResponseSchema, v as openWebPanelSchema, vn as browserAgentCommandSchema, vt as packageOperationsResponseSchema, w as readTreeFileSchema, wn as browserTicketResponseSchema, wt as recentProjectsResponseSchema, x as packageReloadSchema, xn as browserOwnerTicketRequestSchema, xt as prResponseSchema, y as packageInstallSchema, yn as browserOwnerEndpointSchema, yt as packageProjectResponseSchema, z as updateTerminalSchema, zt as treeFileSchema } from "../../dist-BsLn2Gbc.js";
|
|
2
|
-
import {
|
|
2
|
+
import { R as assertLoopbackHost, a as inspectLocalUpdateInstallation, c as resolveLatestTreeportRelease, i as formatLocalUpdateError, n as compareTreeportVersions, o as isCanonicalTreeportVersion, s as readLocalUpdateProgress, u as createUpdateStartupReporter, y as serviceStatus, z as parseDurationMs } from "../../update-BYHlwpAq.js";
|
|
3
3
|
import { a as currentTraceContext, i as terminalHostRecordSchema, n as encodeTerminalHostFrame, s as tracingLayerFromEnvironment, t as TerminalHostFrameDecoder } from "../../terminal-host-protocol-DZkQRAUF.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import fs from "node:fs/promises";
|
|
@@ -3562,6 +3562,7 @@ var dependencies = {
|
|
|
3562
3562
|
"drizzle-orm": "^0.45.2",
|
|
3563
3563
|
"effect": "^3.22.1",
|
|
3564
3564
|
"jsonc-parser": "^3.3.1",
|
|
3565
|
+
"kleur": "^4.1.5",
|
|
3565
3566
|
"node-pty": "1.2.0-beta.14",
|
|
3566
3567
|
"playwright": "1.61.1",
|
|
3567
3568
|
"vite": "8.1.5",
|
|
@@ -3891,6 +3892,7 @@ var WebPanelViteRuntime = class {
|
|
|
3891
3892
|
this.developmentServers.clear();
|
|
3892
3893
|
await Promise.all(servers.map(async ({ server }) => {
|
|
3893
3894
|
await server.waitForRequestsIdle();
|
|
3895
|
+
await Promise.all(Object.values(server.environments).map((environment) => environment.depsOptimizer?.scanProcessing));
|
|
3894
3896
|
await server.close();
|
|
3895
3897
|
}));
|
|
3896
3898
|
}
|
|
@@ -8080,9 +8082,17 @@ var WorktreeRemovalService = class {
|
|
|
8080
8082
|
yield* invalidateProjectsSnapshot();
|
|
8081
8083
|
return {
|
|
8082
8084
|
operationId,
|
|
8083
|
-
preview
|
|
8085
|
+
preview,
|
|
8086
|
+
skipCleanup
|
|
8084
8087
|
};
|
|
8085
8088
|
})).pipe(Effect.onError(() => locks.release({ worktreeIds: [worktreeId] })));
|
|
8089
|
+
yield* Effect.logInfo("Tree removal accepted").pipe(Effect.annotateLogs({
|
|
8090
|
+
operationId: accepted.operationId,
|
|
8091
|
+
worktreeId,
|
|
8092
|
+
name: accepted.preview.name,
|
|
8093
|
+
path: accepted.preview.path,
|
|
8094
|
+
skipCleanup: accepted.skipCleanup
|
|
8095
|
+
}));
|
|
8086
8096
|
const backgroundRemoval = worktreeMutations.enqueue(worktree.projectId, executeRemove(accepted.operationId, worktreeId, accepted.preview.forceRequired)).pipe(Effect.catchAllCause((cause) => Effect.logError(`Background tree removal failed for ${accepted.operationId}: ${Cause.pretty(cause)}`)));
|
|
8087
8097
|
yield* applicationFibers.fork(backgroundRemoval);
|
|
8088
8098
|
yield* Effect.sync(() => events.publish("remove.started", {
|
|
@@ -8119,7 +8129,13 @@ var WorktreeRemovalService = class {
|
|
|
8119
8129
|
UPDATE operations
|
|
8120
8130
|
SET request_json=${serializeOperation(request)},updated_at=${now$1()}
|
|
8121
8131
|
WHERE id=${operationId}
|
|
8122
|
-
`))), Effect.asVoid);
|
|
8132
|
+
`))), Effect.zipRight(Effect.logInfo("Tree removal phase changed").pipe(Effect.annotateLogs({ phase }))), Effect.asVoid);
|
|
8133
|
+
yield* Effect.logInfo("Tree removal executing").pipe(Effect.annotateLogs({
|
|
8134
|
+
name: preview.name,
|
|
8135
|
+
path: preview.path,
|
|
8136
|
+
phase: request.phase,
|
|
8137
|
+
resumed: operation.status === "running"
|
|
8138
|
+
}));
|
|
8123
8139
|
yield* Effect.promise(() => database.db.run(sql`
|
|
8124
8140
|
UPDATE operations SET status='running',error=NULL,updated_at=${now$1()}
|
|
8125
8141
|
WHERE id=${operationId}
|
|
@@ -8337,6 +8353,10 @@ var WorktreeRemovalService = class {
|
|
|
8337
8353
|
updated_at=${timestamp}
|
|
8338
8354
|
WHERE id=${operationId}
|
|
8339
8355
|
`));
|
|
8356
|
+
yield* cleanupWarning ? Effect.logWarning("Tree removal completed with residual files").pipe(Effect.annotateLogs({
|
|
8357
|
+
warning: cleanupWarning,
|
|
8358
|
+
residualPath
|
|
8359
|
+
})) : Effect.logInfo("Tree removal completed");
|
|
8340
8360
|
yield* Effect.sync(() => events.publish("remove.completed", {
|
|
8341
8361
|
operationId,
|
|
8342
8362
|
worktreeId: preview.worktreeId
|
|
@@ -8367,6 +8387,11 @@ var WorktreeRemovalService = class {
|
|
|
8367
8387
|
updated_at=${now$1()}
|
|
8368
8388
|
WHERE id=${operationId}
|
|
8369
8389
|
`));
|
|
8390
|
+
yield* Effect.logWarning("Tree removal cleanup failed after Git removal").pipe(Effect.annotateLogs({
|
|
8391
|
+
warning,
|
|
8392
|
+
phase: request.phase,
|
|
8393
|
+
residualPath: preview.path
|
|
8394
|
+
}));
|
|
8370
8395
|
yield* Effect.sync(() => events.publish("remove.completed", {
|
|
8371
8396
|
operationId,
|
|
8372
8397
|
worktreeId: preview.worktreeId
|
|
@@ -8378,6 +8403,10 @@ var WorktreeRemovalService = class {
|
|
|
8378
8403
|
SET status='failed',result_json=NULL,error=${message},updated_at=${now$1()}
|
|
8379
8404
|
WHERE id=${operationId}
|
|
8380
8405
|
`));
|
|
8406
|
+
yield* Effect.logError("Tree removal failed; Git kept the tree").pipe(Effect.annotateLogs({
|
|
8407
|
+
error: message,
|
|
8408
|
+
phase: request.phase
|
|
8409
|
+
}));
|
|
8381
8410
|
yield* Effect.sync(() => events.publish("remove.failed", {
|
|
8382
8411
|
operationId,
|
|
8383
8412
|
worktreeId: preview.worktreeId,
|
|
@@ -8385,7 +8414,10 @@ var WorktreeRemovalService = class {
|
|
|
8385
8414
|
}));
|
|
8386
8415
|
}
|
|
8387
8416
|
}));
|
|
8388
|
-
}).pipe(Effect.ensuring(Effect.flatMap(MutationLocks, (locks) => locks.release({ worktreeIds: [lockedWorktreeId] })))
|
|
8417
|
+
}).pipe(Effect.ensuring(Effect.flatMap(MutationLocks, (locks) => locks.release({ worktreeIds: [lockedWorktreeId] }))), Effect.annotateLogs({
|
|
8418
|
+
operationId,
|
|
8419
|
+
worktreeId: lockedWorktreeId
|
|
8420
|
+
}));
|
|
8389
8421
|
}
|
|
8390
8422
|
resumeRemove(operationId, worktreeId, force) {
|
|
8391
8423
|
return this.executeRemove(operationId, worktreeId, force);
|
|
@@ -11860,6 +11892,7 @@ var BrowserSessionManager = class {
|
|
|
11860
11892
|
});
|
|
11861
11893
|
const previousController = session.controllerId;
|
|
11862
11894
|
let agentControlled = false;
|
|
11895
|
+
let completed = false;
|
|
11863
11896
|
try {
|
|
11864
11897
|
if (localOwner) {
|
|
11865
11898
|
await this.updateScreencast(session, "agent");
|
|
@@ -11867,7 +11900,7 @@ var BrowserSessionManager = class {
|
|
|
11867
11900
|
}
|
|
11868
11901
|
session.controllerId = "agent";
|
|
11869
11902
|
agentControlled = true;
|
|
11870
|
-
this.broadcastState(session, "controlChanged");
|
|
11903
|
+
if (previousController !== "agent") this.broadcastState(session, "controlChanged");
|
|
11871
11904
|
if (localOwner) result = await this.executeLocalAgentCommand(session, localOwner, input);
|
|
11872
11905
|
else if (this.agentCliRunner) {
|
|
11873
11906
|
const name = session.agentSessionName ?? `treeport-${panelId}-${session.generation}-${crypto.randomBytes(6).toString("hex")}`;
|
|
@@ -11893,11 +11926,12 @@ var BrowserSessionManager = class {
|
|
|
11893
11926
|
this.queuePanelState(session, browser.state);
|
|
11894
11927
|
}
|
|
11895
11928
|
await this.waitForPanelState(session);
|
|
11929
|
+
completed = true;
|
|
11896
11930
|
} catch (cause) {
|
|
11897
11931
|
if (!localOwner) session.agentAttached = false;
|
|
11898
11932
|
throw cause;
|
|
11899
11933
|
} finally {
|
|
11900
|
-
if (agentControlled) {
|
|
11934
|
+
if (agentControlled && !completed) {
|
|
11901
11935
|
if (localOwner && session.localOwner === localOwner) {
|
|
11902
11936
|
const nextController = previousController && previousController !== "agent" ? previousController : LOCAL_BROWSER_OWNER_CONTROLLER;
|
|
11903
11937
|
if (await this.updateScreencast(session, nextController).then(() => true, () => false)) session.controllerId = nextController;
|
|
@@ -66,16 +66,21 @@ var TerminalHostSessionManager = class {
|
|
|
66
66
|
spawnPty;
|
|
67
67
|
terminateProcessTree;
|
|
68
68
|
progressStaleMs;
|
|
69
|
+
sshAgentDiscovery;
|
|
69
70
|
shellIntegrationDir;
|
|
70
71
|
sessions = /* @__PURE__ */ new Map();
|
|
71
72
|
pendingCleanups = /* @__PURE__ */ new Set();
|
|
72
73
|
initialization = null;
|
|
73
|
-
constructor(runtimeDir, launcherPath, spawnPty = pty.spawn, terminateProcessTree = terminatePtyProcessTree, progressStaleMs = TERMINAL_PROGRESS_STALE_MS
|
|
74
|
+
constructor(runtimeDir, launcherPath, spawnPty = pty.spawn, terminateProcessTree = terminatePtyProcessTree, progressStaleMs = TERMINAL_PROGRESS_STALE_MS, sshAgentDiscovery = {
|
|
75
|
+
platform: process.platform,
|
|
76
|
+
launchctlPath: "/bin/launchctl"
|
|
77
|
+
}) {
|
|
74
78
|
this.runtimeDir = runtimeDir;
|
|
75
79
|
this.launcherPath = launcherPath;
|
|
76
80
|
this.spawnPty = spawnPty;
|
|
77
81
|
this.terminateProcessTree = terminateProcessTree;
|
|
78
82
|
this.progressStaleMs = progressStaleMs;
|
|
83
|
+
this.sshAgentDiscovery = sshAgentDiscovery;
|
|
79
84
|
this.shellIntegrationDir = path.join(runtimeDir, "terminal-shell-integration");
|
|
80
85
|
}
|
|
81
86
|
initialize() {
|
|
@@ -110,6 +115,18 @@ var TerminalHostSessionManager = class {
|
|
|
110
115
|
};
|
|
111
116
|
const inheritedEnvironment = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== void 0));
|
|
112
117
|
inheritedEnvironment.TERM = "xterm-256color";
|
|
118
|
+
if (this.sshAgentDiscovery.platform === "darwin" && inheritedEnvironment.SSH_AUTH_SOCK === void 0 && input.env.SSH_AUTH_SOCK === void 0) await execute(this.sshAgentDiscovery.launchctlPath, ["getenv", "SSH_AUTH_SOCK"], {
|
|
119
|
+
timeout: 1e3,
|
|
120
|
+
killSignal: "SIGKILL",
|
|
121
|
+
maxBuffer: 4096
|
|
122
|
+
}).then(async ({ stdout }) => {
|
|
123
|
+
const socket = stdout.trim();
|
|
124
|
+
if (!socket) return;
|
|
125
|
+
if (!path.isAbsolute(socket) || /\p{Cc}/u.test(socket) || !(await fs.stat(socket)).isSocket()) throw new Error("Invalid launchd SSH agent socket");
|
|
126
|
+
inheritedEnvironment.SSH_AUTH_SOCK = socket;
|
|
127
|
+
}).catch(() => {
|
|
128
|
+
console.warn("[Treeport terminal host] Could not discover the macOS SSH agent. Continuing without SSH_AUTH_SOCK; check `launchctl getenv SSH_AUTH_SOCK` or set SSH_AUTH_SOCK explicitly for the terminal.");
|
|
129
|
+
});
|
|
113
130
|
const directLaunch = integrateShellLaunch(input.argv, {
|
|
114
131
|
...inheritedEnvironment,
|
|
115
132
|
...input.env
|