@treeport/treeport 0.5.0 → 0.6.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.
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { A as treeportVersion, C as disableTailscaleRemote, D as resolvePackagePath, E as resolveLocalApiUrl, M as parseDurationMs, N as TERMINAL_CAPTURE_MAX_LINES, O as runDoctor, S as daemonUp, T as readDaemonLogs, _ as serviceStatus, b as daemonHealth, ct as parseEventsSnapshot, d as serviceDisable, f as serviceDoctorCheck, g as serviceStart, h as serviceRun, k as tailscaleRemoteStatus, l as readServiceLogs, lt as parseProductEvent, m as serviceInstalled, p as serviceEnable, s as runLocalUpdate, st as webPanelInputSchema, t as LocalUpdateError, u as serviceApply, ut as SOCKET_IO_PATH, v as serviceStop, w as enableTailscaleRemote, x as daemonStatus, y as daemonDown } from "../../update-BW-a6Bd-.js";
2
+ import { B as parseEventsSnapshot, H as SOCKET_IO_PATH, R as webPanelInputSchema, V as parseProductEvent, t as TERMINAL_CAPTURE_MAX_LINES } from "../../dist-Crk_Xr82.js";
3
+ import { A as treeportVersion, C as disableTailscaleRemote, D as resolvePackagePath, E as resolveLocalApiUrl, M as parseDurationMs, O as runDoctor, S as daemonUp, T as readDaemonLogs, _ as serviceStatus, b as daemonHealth, d as serviceDisable, f as serviceDoctorCheck, g as serviceStart, h as serviceRun, k as tailscaleRemoteStatus, l as readServiceLogs, m as serviceInstalled, p as serviceEnable, s as runLocalUpdate, t as LocalUpdateError, u as serviceApply, v as serviceStop, w as enableTailscaleRemote, x as daemonStatus, y as daemonDown } from "../../update-qVp7yL5D.js";
3
4
  import fs from "node:fs/promises";
4
5
  import path from "node:path";
5
6
  import { Command, CommanderError } from "commander";
@@ -285,7 +286,37 @@ async function webPanelLaunchCwd(worktree) {
285
286
  });
286
287
  return path.relative(worktreeRoot, cwd) || ".";
287
288
  }
288
- function webPanelUrl(worktree, panelId) {
289
+ async function resolveBrowserPanel(panelId) {
290
+ const candidates = (await projects()).flatMap((project) => project.worktrees.flatMap((worktree) => worktree.panels.filter((panel) => panel.kind === "browser").map((panel) => ({
291
+ panel,
292
+ worktree
293
+ }))));
294
+ if (panelId) {
295
+ const match = candidates.find((candidate) => candidate.panel.id === panelId);
296
+ if (!match) throw new CliError(`Browser ${panelId} was not found`, 5);
297
+ return match;
298
+ }
299
+ const worktree = await resolveWorktree(".");
300
+ const matches = candidates.filter((candidate) => candidate.worktree.id === worktree.id);
301
+ if (matches.length === 1) return matches[0];
302
+ if (matches.length === 0) throw new CliError(`No Browser is open in worktree ${worktree.name}`, 5, "BROWSER_PANEL_NOT_FOUND");
303
+ throw new CliError(`More than one Browser is open in worktree ${worktree.name}; specify --panel`, 5, "BROWSER_PANEL_AMBIGUOUS", { panelIds: matches.map((candidate) => candidate.panel.id) });
304
+ }
305
+ async function runBrowserAgentCommand(command, args, panelId) {
306
+ const { panel } = await resolveBrowserPanel(panelId);
307
+ const result = await request(`/api/panels/${encodeURIComponent(panel.id)}/browser-agent`, {
308
+ method: "POST",
309
+ body: JSON.stringify({
310
+ command,
311
+ args
312
+ })
313
+ });
314
+ return {
315
+ panelId: panel.id,
316
+ output: result.output
317
+ };
318
+ }
319
+ function panelUrl(worktree, panelId) {
289
320
  const target = new URL(apiUrl);
290
321
  target.pathname = `/projects/${encodeURIComponent(worktree.projectId)}/worktrees/${encodeURIComponent(worktree.id)}/panels/${encodeURIComponent(panelId)}`;
291
322
  target.search = "";
@@ -441,13 +472,10 @@ async function waitForTerminal(terminalId, condition, timeoutMs) {
441
472
  function print(value, human) {
442
473
  writeStdout(`${jsonOutput ? JSON.stringify(value) : human ? human() : JSON.stringify(value, null, 2)}\n`);
443
474
  }
444
- const agentGuidance = `AI agents:
445
- If you're an AI agent, use \`treeport skills\` to see the usage guide.
446
- `;
447
475
  async function main(args) {
448
476
  const argv = args[0] === "spawn" || args[0] === "terminal" && args[1] === "create" ? commandArgv(args) : void 0;
449
477
  let parserError = "";
450
- 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").addHelpText("beforeAll", agentGuidance).configureOutput({
478
+ 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({
451
479
  writeOut: writeStdout,
452
480
  writeErr: (value) => {
453
481
  parserError += value;
@@ -673,6 +701,7 @@ async function main(args) {
673
701
  worktreeId,
674
702
  terminalId
675
703
  });
704
+ const treeContext = (await request(`/api/worktrees/${encodeURIComponent(worktree.id)}/context`)).context;
676
705
  const context = {
677
706
  managed: true,
678
707
  apiUrl,
@@ -695,7 +724,8 @@ async function main(args) {
695
724
  head: worktree.head,
696
725
  branch: worktree.branch,
697
726
  detached: worktree.detached,
698
- kind: worktree.kind
727
+ kind: worktree.kind,
728
+ context: treeContext
699
729
  },
700
730
  terminal: {
701
731
  id: terminal.id,
@@ -705,8 +735,108 @@ async function main(args) {
705
735
  exitCode: terminal.exitCode
706
736
  }
707
737
  };
708
- print(context, () => `Treeport context\n\nProject: ${context.project.name} (${context.project.id})\nTree: ${context.worktree.name} (${context.worktree.id})\nPath: ${context.worktree.path}\nTerminal: ${context.terminal.name} (${context.terminal.id}) — ${context.terminal.status}\nAPI: ${context.apiUrl}\nLifecycle: ${context.daemonLifecycle === "external" ? "externally managed" : context.daemonLifecycle === "service" ? "managed by the OS service" : "managed by Treeport"}`);
738
+ print(context, () => {
739
+ const entries = Object.entries(context.worktree.context);
740
+ const treeContextText = entries.length === 0 ? "—" : entries.map(([key, value]) => {
741
+ const [first = "", ...rest] = [...value].map((character) => {
742
+ const code = character.charCodeAt(0);
743
+ return code !== 10 && (code <= 31 || code >= 127 && code <= 159) ? `\\u${code.toString(16).padStart(4, "0")}` : character;
744
+ }).join("").split("\n");
745
+ return ` ${key}: ${first}${rest.length > 0 ? `\n${rest.map((line) => ` ${line}`).join("\n")}` : ""}`;
746
+ }).join("\n");
747
+ return `Treeport context\n\nProject: ${context.project.name} (${context.project.id})\nTree: ${context.worktree.name} (${context.worktree.id})\nPath: ${context.worktree.path}\nContext:\n${treeContextText}\nTerminal: ${context.terminal.name} (${context.terminal.id}) — ${context.terminal.status}\nAPI: ${context.apiUrl}\nLifecycle: ${context.daemonLifecycle === "external" ? "externally managed" : context.daemonLifecycle === "service" ? "managed by the OS service" : "managed by Treeport"}`;
748
+ });
749
+ });
750
+ const browserCommand = program.command("browser").description("Manage Browser and its hosted Chromium");
751
+ browserCommand.action(() => {
752
+ writeStdout(browserCommand.helpInformation());
753
+ });
754
+ const browserOpenCommand = browserCommand.command("open").description("Open Browser and request client navigation").argument("[url]", "absolute HTTP or HTTPS URL").requiredOption("--worktree <id-or-path-or-dot>", "owning tree").option("--json", "emit machine-readable JSON");
755
+ browserOpenCommand.action(async (url) => {
756
+ const worktree = await resolveWorktree(browserOpenCommand.opts().worktree);
757
+ const body = {
758
+ url,
759
+ sourceTerminalId: contextWorktreeId === worktree.id ? contextTerminalId ?? null : null
760
+ };
761
+ const result = await request(`/api/worktrees/${encodeURIComponent(worktree.id)}/browser-panels`, {
762
+ method: "POST",
763
+ body: JSON.stringify(body)
764
+ });
765
+ const output = {
766
+ ...result,
767
+ url: panelUrl(worktree, result.panel.id)
768
+ };
769
+ print(output, () => `Opened ${result.panel.title} (${result.panel.id})\n${output.url}`);
709
770
  });
771
+ browserCommand.command("install").description("Install the Chromium build used by Browser").option("--json", "emit machine-readable JSON").action(async () => {
772
+ const result = await request("/api/browser/install", { method: "POST" });
773
+ print(result, () => result.message);
774
+ });
775
+ browserCommand.command("status").description("Show hosted browser installation status").option("--json", "emit machine-readable JSON").action(async () => {
776
+ const result = await request("/api/browser/status");
777
+ print(result, () => `${result.installed ? "Chromium is installed" : "Chromium is not installed"}\nLaunch ready: ${result.launchReady ? "yes" : "no"}\nPlaywright: ${result.playwrightVersion}\nBrowser: ${result.channel} ${result.browserRevision}\nExecutable: ${result.executablePath}${result.launchError ? `\nLaunch error: ${result.launchError}` : ""}`);
778
+ });
779
+ browserCommand.command("remove").description("Remove Treeport's hosted Chromium build").option("--json", "emit machine-readable JSON").action(async () => {
780
+ await request("/api/browser/install", { method: "DELETE" });
781
+ print({ removed: true }, () => "Removed Treeport hosted Chromium");
782
+ });
783
+ browserCommand.command("list").description("List open Browser sessions").option("--json", "emit machine-readable JSON").action(async () => {
784
+ const panels = (await projects()).flatMap((project) => project.worktrees.flatMap((worktree) => worktree.panels.filter((panel) => panel.kind === "browser").map((panel) => ({
785
+ panelId: panel.id,
786
+ title: panel.title,
787
+ worktreeId: worktree.id,
788
+ worktree: worktree.name,
789
+ projectId: project.id,
790
+ project: project.name
791
+ }))));
792
+ print(panels, () => panels.length ? panels.map((panel) => `${panel.panelId}\t${panel.project} / ${panel.worktree}\t${panel.title}`).join("\n") : "Browser is not open.");
793
+ });
794
+ const printAgentResult = async (command, args, panelId) => {
795
+ const result = await runBrowserAgentCommand(command, args, panelId);
796
+ print(result, () => result.output);
797
+ };
798
+ const browserSnapshotCommand = browserCommand.command("snapshot").description("Capture an accessibility snapshot of the hosted page").option("--panel <panel-id>", "Browser ID").option("--json", "emit machine-readable JSON");
799
+ browserSnapshotCommand.action(async () => printAgentResult("snapshot", [], browserSnapshotCommand.opts().panel));
800
+ const browserClickCommand = browserCommand.command("click").description("Click an element from the latest browser snapshot").argument("<target>", "Playwright element reference or selector").option("--panel <panel-id>", "Browser ID").option("--json", "emit machine-readable JSON");
801
+ browserClickCommand.action(async (target) => printAgentResult("click", [target], browserClickCommand.opts().panel));
802
+ const browserFillCommand = browserCommand.command("fill").description("Fill an editable element from the latest browser snapshot").argument("<target>", "Playwright element reference or selector").argument("<text>", "text to enter").option("--panel <panel-id>", "Browser ID").option("--json", "emit machine-readable JSON");
803
+ browserFillCommand.action(async (target, text) => printAgentResult("fill", [target, text], browserFillCommand.opts().panel));
804
+ const browserPressCommand = browserCommand.command("press").description("Press a key in the hosted page").argument("<key>", "Playwright key name").option("--panel <panel-id>", "Browser ID").option("--json", "emit machine-readable JSON");
805
+ browserPressCommand.action(async (key) => printAgentResult("press", [key], browserPressCommand.opts().panel));
806
+ const browserGotoCommand = browserCommand.command("goto").description("Navigate the hosted page").argument("<url>", "absolute HTTP or HTTPS URL").option("--panel <panel-id>", "Browser ID").option("--json", "emit machine-readable JSON");
807
+ browserGotoCommand.action(async (url) => printAgentResult("goto", [url], browserGotoCommand.opts().panel));
808
+ const browserConsoleCommand = browserCommand.command("console").description("List page console messages").argument("[level]", "minimum console level").option("--panel <panel-id>", "Browser ID").option("--json", "emit machine-readable JSON");
809
+ browserConsoleCommand.action(async (level) => printAgentResult("console", level ? [level] : [], browserConsoleCommand.opts().panel));
810
+ for (const [name, description, agentName] of [
811
+ [
812
+ "back",
813
+ "Go back in the hosted page",
814
+ "go-back"
815
+ ],
816
+ [
817
+ "forward",
818
+ "Go forward in the hosted page",
819
+ "go-forward"
820
+ ],
821
+ [
822
+ "reload",
823
+ "Reload the hosted page",
824
+ "reload"
825
+ ],
826
+ [
827
+ "network",
828
+ "List page network requests",
829
+ "requests"
830
+ ],
831
+ [
832
+ "screenshot",
833
+ "Capture a screenshot of the hosted page",
834
+ "screenshot"
835
+ ]
836
+ ]) {
837
+ const command = browserCommand.command(name).description(description).option("--panel <panel-id>", "Browser ID").option("--json", "emit machine-readable JSON");
838
+ command.action(async () => printAgentResult(agentName, [], command.opts().panel));
839
+ }
710
840
  const installCommand = program.command("install").description("Install and configure a Treeport package").argument("<source>", "npm: source or local directory").option("-l, --local", "configure the registered project containing the current directory").option("--json", "emit machine-readable JSON");
711
841
  installCommand.action(async (source) => {
712
842
  const options = installCommand.opts();
@@ -858,7 +988,7 @@ async function main(args) {
858
988
  });
859
989
  const output = {
860
990
  ...result,
861
- url: webPanelUrl(worktree, result.panel.id)
991
+ url: panelUrl(worktree, result.panel.id)
862
992
  };
863
993
  print(output, () => `${result.reused ? "Reused" : "Opened"} ${result.panel.title} (${result.panel.id})\n${output.url}`);
864
994
  });
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import "../../../dist-Crk_Xr82.js";
2
3
  import { t as integrateShellLaunch } from "../../../shell-integration-Be_c91lw.js";
3
4
  import fs from "node:fs/promises";
4
5
  import path from "node:path";
@@ -13,6 +14,7 @@ const FORWARDED_SIGNALS = [
13
14
  ];
14
15
  const launchSpecSchema = z.object({
15
16
  argv: z.array(z.string()),
17
+ initialTitle: z.string().trim().min(1).max(120).optional(),
16
18
  fallbackArgv: z.array(z.string()).optional(),
17
19
  cwd: z.string(),
18
20
  env: z.record(z.string(), z.string()),
@@ -159,30 +161,57 @@ async function runLaunchSpec(spec, dependencies = {}) {
159
161
  ...spec.env
160
162
  };
161
163
  const command = integrateShellLaunch(spec.argv, commandEnvironment, spec.shellIntegrationDir, spec.tmuxExecutable);
162
- const result = await runChild(command.argv, {
163
- cwd: spec.cwd,
164
- env: command.env,
165
- spawnProcess,
166
- signalSource
167
- });
168
- if (result.spawnError) stderr.write(`Treeport launcher: ${safeDiagnostic(result.spawnError.message) || "spawn error"}\n`);
169
- if (result.forwardedSignal && (!spec.fallbackArgv || result.forwardedSignal !== "SIGINT")) return 1;
170
- if (spec.fallbackArgv) {
171
- if (spec.tmuxExecutable && tmuxPane && spec.fallbackArgv[0]) await runChild([
164
+ const initialTitle = spec.initialTitle ? safeDiagnostic(spec.initialTitle) : "";
165
+ if (initialTitle && spec.tmuxExecutable && tmuxPane) {
166
+ if ((await runChild([
172
167
  spec.tmuxExecutable,
173
168
  "set-option",
174
169
  "-p",
175
170
  "-t",
176
171
  tmuxPane,
177
172
  "--",
178
- "@treeport-fallback-shell",
179
- Buffer.from(JSON.stringify(spec.fallbackArgv[0]), "utf8").toString("base64url")
173
+ "@treeport-command",
174
+ initialTitle
180
175
  ], {
181
176
  cwd: spec.cwd,
182
177
  env: process.env,
183
178
  spawnProcess,
184
179
  signalSource
185
- });
180
+ })).forwardedSignal) return 1;
181
+ }
182
+ const result = await runChild(command.argv, {
183
+ cwd: spec.cwd,
184
+ env: command.env,
185
+ spawnProcess,
186
+ signalSource
187
+ });
188
+ if (result.spawnError) stderr.write(`Treeport launcher: ${safeDiagnostic(result.spawnError.message) || "spawn error"}\n`);
189
+ if (result.forwardedSignal && (!spec.fallbackArgv || result.forwardedSignal !== "SIGINT")) return 1;
190
+ if (spec.fallbackArgv) {
191
+ if (spec.tmuxExecutable && tmuxPane && spec.fallbackArgv[0]) {
192
+ if ((await runChild([
193
+ spec.tmuxExecutable,
194
+ "set-option",
195
+ "-p",
196
+ "-u",
197
+ "-t",
198
+ tmuxPane,
199
+ "@treeport-command",
200
+ ";",
201
+ "set-option",
202
+ "-p",
203
+ "-t",
204
+ tmuxPane,
205
+ "--",
206
+ "@treeport-fallback-shell",
207
+ Buffer.from(JSON.stringify(spec.fallbackArgv[0]), "utf8").toString("base64url")
208
+ ], {
209
+ cwd: spec.cwd,
210
+ env: process.env,
211
+ spawnProcess,
212
+ signalSource
213
+ })).forwardedSignal) return 1;
214
+ }
186
215
  const fallback = integrateShellLaunch(spec.fallbackArgv, commandEnvironment, spec.shellIntegrationDir, spec.tmuxExecutable);
187
216
  const fallbackResult = await runChild(fallback.argv, {
188
217
  cwd: spec.cwd,