@treeport/treeport 0.1.0 → 0.2.2

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.
@@ -8,6 +8,55 @@
8
8
  "when": 1785230060971,
9
9
  "tag": "0000_public_baseline",
10
10
  "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "6",
15
+ "when": 1785577623909,
16
+ "tag": "0001_misty_spectrum",
17
+ "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "6",
22
+ "when": 1785589844379,
23
+ "tag": "0002_dazzling_princess_powerful",
24
+ "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "6",
29
+ "when": 1785834323406,
30
+ "tag": "0003_yummy_whirlwind",
31
+ "breakpoints": true
32
+ },
33
+ {
34
+ "idx": 4,
35
+ "version": "6",
36
+ "when": 1785920000000,
37
+ "tag": "0004_persistent_worktree_creation",
38
+ "breakpoints": true
39
+ },
40
+ {
41
+ "idx": 5,
42
+ "version": "6",
43
+ "when": 1786000000000,
44
+ "tag": "0005_git_authoritative_worktrees",
45
+ "breakpoints": true
46
+ },
47
+ {
48
+ "idx": 6,
49
+ "version": "6",
50
+ "when": 1786283200364,
51
+ "tag": "0006_web_panel_launch_input",
52
+ "breakpoints": true
53
+ },
54
+ {
55
+ "idx": 7,
56
+ "version": "6",
57
+ "when": 1786533775050,
58
+ "tag": "0007_dashing_pestilence",
59
+ "breakpoints": true
11
60
  }
12
61
  ]
13
62
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@treeport/treeport",
3
- "version": "0.1.0",
3
+ "version": "0.2.2",
4
4
  "description": "A worktree-first terminal driver for persistent development workspaces.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,7 +36,10 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@hono/node-server": "^2.0.10",
39
+ "@hono/zod-validator": "^0.9.0",
39
40
  "@libsql/client": "^0.17.4",
41
+ "@radix-ui/react-context-menu": "^2.3.7",
42
+ "@vitejs/plugin-react": "5.2.0",
40
43
  "@xterm/headless": "^6.0.0",
41
44
  "commander": "14.0.3",
42
45
  "drizzle-orm": "^0.45.2",
@@ -46,7 +49,9 @@
46
49
  "node-pty": "1.2.0-beta.14",
47
50
  "socket.io": "4.8.3",
48
51
  "socket.io-client": "4.8.3",
49
- "zod": "^4.3.6"
52
+ "vite": "8.1.5",
53
+ "zod": "^4.3.6",
54
+ "@treeport/panel-sdk": "0.2.2"
50
55
  },
51
56
  "devDependencies": {
52
57
  "@fontsource-variable/inter": "^5.2.8",
@@ -54,18 +59,15 @@
54
59
  "@radix-ui/react-alert-dialog": "^1.1.23",
55
60
  "@radix-ui/react-checkbox": "^1.3.11",
56
61
  "@radix-ui/react-dialog": "^1.1.23",
57
- "@radix-ui/react-dropdown-menu": "^2.1.21",
58
62
  "@radix-ui/react-popover": "^1.1.19",
59
63
  "@radix-ui/react-separator": "^1.1.15",
60
64
  "@radix-ui/react-slot": "^1.3.0",
61
- "@radix-ui/react-tabs": "^1.1.17",
62
65
  "@radix-ui/react-tooltip": "^1.2.12",
63
66
  "@tailwindcss/vite": "^4.3.3",
64
67
  "@tanstack/react-query": "^5.101.2",
65
68
  "@tanstack/react-router": "^1.170.18",
66
69
  "@types/react": "^19.2.10",
67
70
  "@types/react-dom": "^19.2.3",
68
- "@vitejs/plugin-react": "^5.1.4",
69
71
  "@xterm/addon-fit": "^0.11.0",
70
72
  "@xterm/addon-web-links": "^0.12.0",
71
73
  "@xterm/xterm": "^6.0.0",
@@ -79,7 +81,6 @@
79
81
  "tailwind-merge": "^3.6.0",
80
82
  "tailwindcss": "^4.3.3",
81
83
  "tsdown": "0.22.14",
82
- "vite": "^8.1.5",
83
84
  "@treeport/shared": "0.1.0"
84
85
  },
85
86
  "scripts": {
@@ -90,12 +91,11 @@
90
91
  "check:package": "node scripts/check-package.mjs",
91
92
  "test:package": "node scripts/smoke-package.mjs",
92
93
  "dev": "node scripts/development.mjs",
93
- "dev:server": "TREEPORT_DATA_DIR=.treeport-dev/data TREEPORT_RUNTIME_DIR=.treeport-dev/runtime tsdown --watch --on-success \"node --enable-source-maps dist/node/server/index.js\"",
94
- "dev:web": "vite",
94
+ "dev:server": "TREEPORT_DATA_DIR=.treeport-dev/data TREEPORT_RUNTIME_DIR=.treeport-dev/runtime TREEPORT_DAEMON_LIFECYCLE=external tsdown --watch --on-success \"node --enable-source-maps dist/node/server/index.js\"",
95
95
  "start": "TREEPORT_WEB_DIST=dist/web node dist/node/server/index.js",
96
96
  "db:generate": "drizzle-kit generate",
97
97
  "typecheck": "pnpm typecheck:node && pnpm typecheck:web",
98
- "typecheck:node": "tsc --noEmit -p tsconfig.node.json --pretty",
98
+ "typecheck:node": "tsc -b tsconfig.node.json --pretty",
99
99
  "typecheck:web": "tsc --noEmit -p tsconfig.web.json --pretty"
100
100
  }
101
101
  }
@@ -17,6 +17,7 @@ Treeport does not define task sources, planning or approval states, agent tool p
17
17
  - Do not use `eval` or an implicit `sh -lc`. Launch a shell explicitly only when the caller intentionally requests shell semantics.
18
18
  - Do not place untrusted titles, prompts, or other external text into interpolated shell fragments. Pass each value as one argument or use a caller-managed file when the child supports file arguments.
19
19
  - Never delete a terminal or remove a worktree unless the user explicitly asks.
20
+ - Obey the daemon lifecycle reported by `treeport context`. When it is `external`, never run `treeport up`, `treeport down`, or `treeport remote enable`; the parent process owns startup, shutdown, and remote exposure.
20
21
  - Do not restrict a launched agent's normal tools or make it ephemeral unless the caller explicitly asks. The persistent interactive session is intended to remain useful when the user takes over.
21
22
 
22
23
  ## Understand the current context
@@ -27,7 +28,7 @@ Run:
27
28
  treeport context
28
29
  ```
29
30
 
30
- Inside a managed terminal, this reports the current project, worktree, terminal, paths, statuses, IDs, and daemon URL. It resolves the injected IDs strictly; it does not guess identity from the current path.
31
+ Inside a managed terminal, this reports the current project, worktree, terminal, paths, statuses, IDs, daemon URL, and whether the daemon lifecycle is managed by Treeport or by an external process. It resolves the injected IDs strictly; it does not guess identity from the current path.
31
32
 
32
33
  Outside Treeport it reports that the terminal is not managed and exits successfully. `TREEPORT_API_URL` may be configured outside a managed terminal; if any context ID is present, however, all injected values are required. Partial IDs or IDs that no longer belong together fail instead of falling back to path inference.
33
34
 
@@ -150,4 +151,4 @@ JSON success output is written to stdout. JSON errors use `{ "error": { "code",
150
151
  - `5`: API, domain, or invalid-context refusal.
151
152
  - `130`: a terminal wait was interrupted with Ctrl+C.
152
153
 
153
- Treeport currently has no authentication. Use it only through a trusted local or private-network listener; do not invent credentials or put secrets in command arguments or URLs.
154
+ Treeport trusts the local OS boundary for loopback access and delegates supported remote authentication to Tailscale Serve. Do not expose the daemon through a direct network listener, invent credentials, or put secrets in command arguments or URLs.
@@ -1,254 +0,0 @@
1
- import { z } from "zod";
2
- //#region ../../packages/shared/dist/terminal-protocol.js
3
- const SOCKET_IO_PATH = "/api/socket.io/";
4
- const TERMINAL_CONTROLLER_GRACE_MS = 1e4;
5
- const TERMINAL_OUTPUT_HIGH_WATERMARK = 256 * 1024;
6
- const TERMINAL_OUTPUT_LOW_WATERMARK = 64 * 1024;
7
- const TERMINAL_OUTPUT_STALL_TIMEOUT_MS = 3e4;
8
- const TERMINAL_MAX_CLIENT_MESSAGE_BYTES = 128 * 1024;
9
- const TERMINAL_MAX_INPUT_BYTES = 64 * 1024;
10
- const terminalId = z.string().min(1).max(128);
11
- const clientId = z.string().min(1).max(128);
12
- const streamId = z.string().min(1).max(128);
13
- const generation = z.number().int().nonnegative();
14
- const dimensions = {
15
- cols: z.number().int().min(2).max(1e3),
16
- rows: z.number().int().min(2).max(500)
17
- };
18
- const terminalSizeSchema = z.strictObject(dimensions);
19
- const terminalProgressSchema = z.strictObject({
20
- state: z.enum([
21
- "normal",
22
- "error",
23
- "indeterminate",
24
- "paused"
25
- ]),
26
- value: z.number().int().min(0).max(100).nullable()
27
- });
28
- const terminalRuntimeMetadataSchema = z.strictObject({
29
- terminalId: z.string().min(1),
30
- title: z.string().max(256).nullable(),
31
- hasForegroundProcess: z.boolean().nullable().optional(),
32
- progress: terminalProgressSchema.nullable(),
33
- progressStartedAt: z.string().datetime().nullable().default(null),
34
- progressClearedAt: z.string().datetime().nullable().default(null),
35
- bell: z.strictObject({
36
- sequence: z.number().int().positive(),
37
- at: z.string().datetime(),
38
- unread: z.boolean()
39
- }).nullable().default(null)
40
- });
41
- const terminalBellAcknowledgementSchema = z.strictObject({ sequence: z.number().int().positive() });
42
- function parseTerminalRuntimeMetadata(value) {
43
- const parsed = terminalRuntimeMetadataSchema.safeParse(value);
44
- return parsed.success ? parsed.data : null;
45
- }
46
- function parseTerminalProgress(data) {
47
- const [command, rawState, rawValue, ...extra] = data.split(";");
48
- if (command !== "4" || extra.length > 0 || !/^[0-4]$/.test(rawState ?? "")) return;
49
- const state = Number(rawState);
50
- if (state === 0) return null;
51
- if (rawValue !== void 0 && rawValue !== "" && !/^\d{1,3}$/.test(rawValue)) return;
52
- const value = rawValue === void 0 || rawValue === "" ? null : Number(rawValue);
53
- if (value !== null && value > 100) return;
54
- return {
55
- state: [
56
- void 0,
57
- "normal",
58
- "error",
59
- "indeterminate",
60
- "paused"
61
- ][state],
62
- value
63
- };
64
- }
65
- const terminalAuthSchema = z.strictObject({
66
- terminalId,
67
- clientId,
68
- ...dimensions
69
- });
70
- const terminalInputSchema = z.strictObject({
71
- generation,
72
- data: z.string().max(TERMINAL_MAX_INPUT_BYTES)
73
- });
74
- const terminalBinarySchema = z.strictObject({
75
- generation,
76
- data: z.string().max(TERMINAL_MAX_INPUT_BYTES)
77
- });
78
- const terminalResizeSchema = z.strictObject({
79
- generation,
80
- ...dimensions
81
- });
82
- const terminalTakeControlSchema = z.strictObject({
83
- generation,
84
- ...dimensions
85
- });
86
- const terminalLegacyTakeControlSchema = z.strictObject({ generation });
87
- const terminalOutputAckSchema = z.strictObject({
88
- streamId,
89
- sequence: z.number().int().nonnegative()
90
- });
91
- const terminalReadyBase = {
92
- connectionId: z.string().min(1).max(128),
93
- streamId,
94
- generation,
95
- controller: z.boolean(),
96
- reset: z.literal("full")
97
- };
98
- const terminalLegacyReadySchema = z.strictObject(terminalReadyBase);
99
- const terminalReadyV2Schema = z.strictObject({
100
- ...terminalReadyBase,
101
- ...dimensions,
102
- revision: z.number().int().positive()
103
- });
104
- z.union([terminalLegacyReadySchema, terminalReadyV2Schema]);
105
- z.strictObject({
106
- ...dimensions,
107
- revision: z.number().int().positive()
108
- });
109
- z.strictObject({
110
- streamId,
111
- sequence: z.number().int().positive(),
112
- data: z.string()
113
- });
114
- z.strictObject({ title: z.string().max(256) });
115
- z.strictObject({ progress: terminalProgressSchema.nullable() });
116
- z.strictObject({
117
- generation,
118
- controller: z.boolean()
119
- });
120
- z.strictObject({ exitCode: z.number().int().nullable() });
121
- z.strictObject({
122
- code: z.string().min(1).max(80),
123
- message: z.string().min(1).max(1e3),
124
- retryable: z.boolean()
125
- });
126
- function parseTerminalAuth(value) {
127
- const parsed = terminalAuthSchema.safeParse(value);
128
- return parsed.success ? parsed.data : null;
129
- }
130
- //#endregion
131
- //#region ../../packages/shared/dist/socket-protocol.js
132
- const productEventTypeSchema = z.enum([
133
- "project.created",
134
- "project.updated",
135
- "project.removed",
136
- "worktree.created",
137
- "worktree.updated",
138
- "worktree.removed",
139
- "terminal.created",
140
- "terminal.updated",
141
- "terminal.removed",
142
- "terminal.metadata",
143
- "terminal.controller_changed",
144
- "remove.started",
145
- "remove.completed",
146
- "remove.failed"
147
- ]);
148
- const productEventSchema = z.strictObject({
149
- id: z.string().min(1).max(128),
150
- type: productEventTypeSchema,
151
- at: z.string().datetime(),
152
- data: z.record(z.string(), z.unknown())
153
- });
154
- const eventsSnapshotSchema = z.strictObject({
155
- at: z.string().datetime(),
156
- terminalMetadata: z.array(terminalRuntimeMetadataSchema)
157
- });
158
- function parseEventsSnapshot(value) {
159
- const parsed = eventsSnapshotSchema.safeParse(value);
160
- return parsed.success ? parsed.data : null;
161
- }
162
- function parseProductEvent(value) {
163
- const parsed = productEventSchema.safeParse(value);
164
- return parsed.success ? parsed.data : null;
165
- }
166
- //#endregion
167
- //#region ../../packages/shared/dist/index.js
168
- const TERMINAL_MAX_UPLOAD_BYTES = 50 * 1024 * 1024;
169
- const TERMINAL_EXECUTABLE_MAX_LENGTH = 4096;
170
- const TERMINAL_ARGUMENT_MAX_LENGTH = 4096;
171
- const TERMINAL_CAPTURE_MAX_LINES = 5e3;
172
- const PROJECT_COLORS = [
173
- "rose",
174
- "orange",
175
- "amber",
176
- "emerald",
177
- "cyan",
178
- "blue",
179
- "violet",
180
- "pink"
181
- ];
182
- const browseDirectoryQuerySchema = z.object({
183
- input: z.string().trim().min(1).max(4096),
184
- hidden: z.enum(["true", "false"]).optional().default("false").transform((value) => value === "true")
185
- });
186
- const terminalCaptureQuerySchema = z.object({ lines: z.coerce.number().int().min(1).max(TERMINAL_CAPTURE_MAX_LINES).optional().default(200) });
187
- const registerProjectSchema = z.object({
188
- path: z.string().trim().min(1),
189
- name: z.string().trim().min(1).max(120).optional()
190
- });
191
- const updateProjectSchema = z.object({ color: z.enum(PROJECT_COLORS).nullable() });
192
- const terminalNameSchema = z.string().trim().min(1).max(120);
193
- const terminalArgvSchema = z.array(z.string()).min(1).max(128);
194
- const terminalPresetArgumentSchema = z.string().max(TERMINAL_ARGUMENT_MAX_LENGTH);
195
- const terminalPresetFields = {
196
- name: terminalNameSchema,
197
- executable: z.string().min(1).max(TERMINAL_EXECUTABLE_MAX_LENGTH).refine((value) => value.trim().length > 0, { message: "Executable cannot be blank" }),
198
- args: z.array(terminalPresetArgumentSchema).max(127),
199
- closeOnSuccess: z.boolean().default(false)
200
- };
201
- const terminalPresetRevisionSchema = z.string().min(1).max(64);
202
- const initialTerminalSchema = z.object({
203
- name: terminalNameSchema,
204
- argv: terminalArgvSchema.optional(),
205
- returnToShell: z.boolean().optional(),
206
- initialSize: terminalSizeSchema.optional()
207
- });
208
- const createWorktreeSchema = z.object({
209
- name: z.string().trim().min(1).max(120),
210
- base: z.enum(["default", "current"]).default("default"),
211
- sourceWorktreeId: z.string().min(1).optional(),
212
- initialTerminal: initialTerminalSchema.optional()
213
- }).superRefine((value, context) => {
214
- if (value.base === "current" && !value.sourceWorktreeId) context.addIssue({
215
- code: "custom",
216
- path: ["sourceWorktreeId"],
217
- message: "A source worktree is required when starting from current"
218
- });
219
- });
220
- const createTerminalSchema = z.object({
221
- name: terminalNameSchema,
222
- argv: terminalArgvSchema.optional(),
223
- returnToShell: z.boolean().optional(),
224
- closeOnSuccess: z.boolean().optional(),
225
- initialSize: terminalSizeSchema.optional()
226
- }).refine((value) => !(value.returnToShell && value.closeOnSuccess), { message: "A terminal cannot return to a shell and close on success" });
227
- const updateTerminalSchema = z.object({ name: terminalNameSchema });
228
- const createTerminalPresetSchema = z.object(terminalPresetFields);
229
- const updateTerminalPresetSchema = z.object({
230
- ...terminalPresetFields,
231
- closeOnSuccess: z.boolean().optional(),
232
- expectedUpdatedAt: terminalPresetRevisionSchema
233
- });
234
- const deleteTerminalPresetSchema = z.object({ expectedUpdatedAt: terminalPresetRevisionSchema });
235
- const removeWorktreeSchema = z.object({
236
- confirmationToken: z.string().length(64),
237
- confirmDestructive: z.boolean()
238
- });
239
- const spawnSchema = z.object({
240
- project: z.string().min(1),
241
- worktreeName: z.string().trim().min(1).max(120),
242
- name: terminalNameSchema,
243
- argv: terminalArgvSchema.optional(),
244
- base: z.enum(["default", "current"]).default("default"),
245
- sourceWorktreeId: z.string().min(1).optional()
246
- }).superRefine((value, context) => {
247
- if (value.base === "current" && !value.sourceWorktreeId) context.addIssue({
248
- code: "custom",
249
- path: ["sourceWorktreeId"],
250
- message: "A source worktree is required when starting from current"
251
- });
252
- });
253
- //#endregion
254
- export { terminalLegacyTakeControlSchema as A, TERMINAL_OUTPUT_STALL_TIMEOUT_MS as C, terminalBellAcknowledgementSchema as D, parseTerminalRuntimeMetadata as E, terminalResizeSchema as M, terminalSizeSchema as N, terminalBinarySchema as O, terminalTakeControlSchema as P, TERMINAL_OUTPUT_LOW_WATERMARK as S, parseTerminalProgress as T, SOCKET_IO_PATH as _, createTerminalSchema as a, TERMINAL_MAX_INPUT_BYTES as b, registerProjectSchema as c, terminalCaptureQuerySchema as d, updateProjectSchema as f, parseProductEvent as g, parseEventsSnapshot as h, createTerminalPresetSchema as i, terminalOutputAckSchema as j, terminalInputSchema as k, removeWorktreeSchema as l, updateTerminalSchema as m, TERMINAL_MAX_UPLOAD_BYTES as n, createWorktreeSchema as o, updateTerminalPresetSchema as p, browseDirectoryQuerySchema as r, deleteTerminalPresetSchema as s, TERMINAL_CAPTURE_MAX_LINES as t, spawnSchema as u, TERMINAL_CONTROLLER_GRACE_MS as v, parseTerminalAuth as w, TERMINAL_OUTPUT_HIGH_WATERMARK as x, TERMINAL_MAX_CLIENT_MESSAGE_BYTES as y };