@treeport/treeport 0.6.1 → 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/README.md +1 -1
- package/dist/dist-BsLn2Gbc.js +1630 -0
- package/dist/node/cli/index.js +172 -125
- package/dist/node/server/core/launcher.js +11 -49
- package/dist/node/server/index.js +9125 -7162
- package/dist/node/server/terminal-host-entry.js +979 -0
- package/dist/{shell-integration-Be_c91lw.js → shell-integration-CPmrVa3B.js} +46 -46
- package/dist/terminal-host-protocol-DZkQRAUF.js +378 -0
- package/dist/{update-qVp7yL5D.js → update-BYHlwpAq.js} +568 -147
- package/dist/web/assets/index-BWYDUD7N.css +2 -0
- package/dist/web/assets/index-BtAAdn1A.js +84 -0
- package/dist/web/index.html +2 -2
- package/drizzle/0012_terminal_host_cutover.sql +41 -0
- package/drizzle/0013_workspace_item_order.sql +13 -0
- package/drizzle/meta/0012_snapshot.json +919 -0
- package/drizzle/meta/0013_snapshot.json +987 -0
- package/drizzle/meta/_journal.json +14 -0
- package/package.json +19 -13
- package/skills/treeport/SKILL.md +4 -4
- package/dist/dist-Crk_Xr82.js +0 -735
- package/dist/web/assets/index-2LLiNn3-.js +0 -146
- package/dist/web/assets/index-DmDs47YU.css +0 -2
|
@@ -11,19 +11,18 @@ const ZSH_DELEGATE = (file) => `if [[ -r "$TREEPORT_USER_ZDOTDIR/${file}" ]]; th
|
|
|
11
11
|
fi
|
|
12
12
|
`;
|
|
13
13
|
const ZSH_INTEGRATION = `${ZSH_DELEGATE(".zshrc")}
|
|
14
|
-
if [[ -n "$
|
|
15
|
-
|
|
16
|
-
unset TREEPORT_TMUX_EXECUTABLE
|
|
14
|
+
if [[ -n "$TREEPORT_SHELL_INTEGRATION" ]]; then
|
|
15
|
+
unset TREEPORT_SHELL_INTEGRATION
|
|
17
16
|
autoload -Uz add-zsh-hook 2>/dev/null
|
|
18
17
|
_treeport_command_title_preexec() {
|
|
19
18
|
emulate -L zsh
|
|
20
19
|
local title=\${1//[[:cntrl:]]/}
|
|
21
20
|
title=\${title[1,256]}
|
|
22
|
-
|
|
21
|
+
printf '\\033]777;command;%s\\033\\\\' "$title"
|
|
23
22
|
}
|
|
24
23
|
_treeport_command_title_precmd() {
|
|
25
24
|
emulate -L zsh
|
|
26
|
-
|
|
25
|
+
printf '\\033]777;command;\\033\\\\'
|
|
27
26
|
}
|
|
28
27
|
add-zsh-hook -d preexec _treeport_command_title_preexec 2>/dev/null
|
|
29
28
|
add-zsh-hook -d precmd _treeport_command_title_precmd 2>/dev/null
|
|
@@ -36,21 +35,8 @@ unset TREEPORT_USER_ZDOTDIR
|
|
|
36
35
|
`;
|
|
37
36
|
const BASH_INTEGRATION = `[[ $- == *i* ]] || return
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
_treeport_bash_remaining_prompt=\${PROMPT_COMMAND//"$_treeport_bash_injected_prompt"/}
|
|
41
|
-
_treeport_bash_remaining_prompt=\${_treeport_bash_remaining_prompt#;}
|
|
42
|
-
_treeport_bash_remaining_prompt=\${_treeport_bash_remaining_prompt%;}
|
|
43
|
-
if [[ -n "\${TREEPORT_BASH_PROMPT_COMMAND_SET-}" ]]; then
|
|
44
|
-
PROMPT_COMMAND=$TREEPORT_BASH_PROMPT_COMMAND
|
|
45
|
-
[[ -n "$_treeport_bash_remaining_prompt" ]] && PROMPT_COMMAND="\${PROMPT_COMMAND:+$PROMPT_COMMAND;}$_treeport_bash_remaining_prompt"
|
|
46
|
-
else
|
|
47
|
-
PROMPT_COMMAND=$_treeport_bash_remaining_prompt
|
|
48
|
-
fi
|
|
49
|
-
unset TREEPORT_BASH_INTEGRATION_FILE TREEPORT_BASH_PROMPT_COMMAND TREEPORT_BASH_PROMPT_COMMAND_SET
|
|
50
|
-
unset _treeport_bash_injected_prompt _treeport_bash_remaining_prompt
|
|
38
|
+
unset TREEPORT_SHELL_INTEGRATION
|
|
51
39
|
|
|
52
|
-
_treeport_tmux_executable=$TREEPORT_TMUX_EXECUTABLE
|
|
53
|
-
unset TREEPORT_TMUX_EXECUTABLE
|
|
54
40
|
_treeport_command_title_preexec() {
|
|
55
41
|
local fallback=\${1-} line title
|
|
56
42
|
line=$(HISTTIMEFORMAT= builtin history 1)
|
|
@@ -60,13 +46,10 @@ _treeport_command_title_preexec() {
|
|
|
60
46
|
[[ -n "$title" ]] || title=$fallback
|
|
61
47
|
title=$(LC_ALL=C printf '%s' "$title" | tr -d '\\000-\\037\\177')
|
|
62
48
|
title="\${title:0:256}"
|
|
63
|
-
|
|
64
|
-
"$_treeport_tmux_executable" set-option -p -t "$TMUX_PANE" -- @treeport-command "$title" >/dev/null 2>&1
|
|
49
|
+
printf '\\033]777;command;%s\\033\\\\' "$title"
|
|
65
50
|
}
|
|
66
51
|
_treeport_command_title_prompt() {
|
|
67
|
-
|
|
68
|
-
"$_treeport_tmux_executable" set-option -p -u -t "$TMUX_PANE" @treeport-command >/dev/null 2>&1
|
|
69
|
-
fi
|
|
52
|
+
printf '\\033]777;command;\\033\\\\'
|
|
70
53
|
[[ -n "\${_treeport_uses_debug_trap-}" ]] && _treeport_command_ready=1
|
|
71
54
|
}
|
|
72
55
|
|
|
@@ -94,6 +77,26 @@ if [[ -n "\${_treeport_uses_debug_trap-}" ]]; then
|
|
|
94
77
|
_treeport_command_ready=1
|
|
95
78
|
fi
|
|
96
79
|
`;
|
|
80
|
+
const BASH_PROFILE_DELEGATE = `_treeport_integration_home=$HOME
|
|
81
|
+
HOME=$TREEPORT_USER_HOME
|
|
82
|
+
unset TREEPORT_USER_HOME
|
|
83
|
+
if [[ -r "$HOME/.bash_profile" ]]; then
|
|
84
|
+
source "$HOME/.bash_profile"
|
|
85
|
+
elif [[ -r "$HOME/.bash_login" ]]; then
|
|
86
|
+
source "$HOME/.bash_login"
|
|
87
|
+
elif [[ -r "$HOME/.profile" ]]; then
|
|
88
|
+
source "$HOME/.profile"
|
|
89
|
+
fi
|
|
90
|
+
source "$_treeport_integration_home/../treeport.bash"
|
|
91
|
+
unset _treeport_integration_home
|
|
92
|
+
`;
|
|
93
|
+
const BASH_RC_DELEGATE = `_treeport_integration_home=$HOME
|
|
94
|
+
HOME=$TREEPORT_USER_HOME
|
|
95
|
+
unset TREEPORT_USER_HOME
|
|
96
|
+
[[ -r "$HOME/.bashrc" ]] && source "$HOME/.bashrc"
|
|
97
|
+
source "$_treeport_integration_home/../treeport.bash"
|
|
98
|
+
unset _treeport_integration_home
|
|
99
|
+
`;
|
|
97
100
|
const FISH_INTEGRATION = `if set -q TREEPORT_FISH_XDG_DATA_DIR
|
|
98
101
|
if set -q TREEPORT_FISH_XDG_DATA_DIRS_SET
|
|
99
102
|
set -l treeport_xdg_dirs
|
|
@@ -108,32 +111,28 @@ const FISH_INTEGRATION = `if set -q TREEPORT_FISH_XDG_DATA_DIR
|
|
|
108
111
|
end
|
|
109
112
|
set -e TREEPORT_FISH_XDG_DATA_DIR TREEPORT_FISH_XDG_DATA_DIRS_SET
|
|
110
113
|
end
|
|
111
|
-
set -
|
|
112
|
-
set -e TREEPORT_TMUX_EXECUTABLE
|
|
114
|
+
set -e TREEPORT_SHELL_INTEGRATION
|
|
113
115
|
|
|
114
116
|
function _treeport_command_title_preexec --on-event fish_preexec
|
|
115
117
|
set -l title (string replace -ar '[[:cntrl:]]' '' -- "$argv[1]" | string sub -l 256)
|
|
116
|
-
|
|
117
|
-
command "$_treeport_tmux_executable" set-option -p -t "$TMUX_PANE" -- @treeport-command "$title" >/dev/null 2>&1
|
|
118
|
-
end
|
|
118
|
+
printf '\\e]777;command;%s\\e\\\\' "$title"
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
function _treeport_command_title_prompt --on-event fish_prompt
|
|
122
|
-
|
|
123
|
-
command "$_treeport_tmux_executable" set-option -p -u -t "$TMUX_PANE" @treeport-command >/dev/null 2>&1
|
|
124
|
-
end
|
|
122
|
+
printf '\\e]777;command;\\e\\\\'
|
|
125
123
|
end
|
|
126
124
|
`;
|
|
127
125
|
async function prepareShellIntegration(root) {
|
|
128
126
|
const zshDir = path.join(root, "zsh");
|
|
129
127
|
const bashDir = path.join(root, "bash");
|
|
128
|
+
const bashHome = path.join(bashDir, "home");
|
|
130
129
|
const fishDir = path.join(root, "fish", "fish", "vendor_conf.d");
|
|
131
130
|
await Promise.all([
|
|
132
131
|
fs.mkdir(zshDir, {
|
|
133
132
|
recursive: true,
|
|
134
133
|
mode: 448
|
|
135
134
|
}),
|
|
136
|
-
fs.mkdir(
|
|
135
|
+
fs.mkdir(bashHome, {
|
|
137
136
|
recursive: true,
|
|
138
137
|
mode: 448
|
|
139
138
|
}),
|
|
@@ -151,12 +150,14 @@ async function prepareShellIntegration(root) {
|
|
|
151
150
|
].map((file) => fs.writeFile(path.join(zshDir, file), ZSH_DELEGATE(file), { mode: 384 })),
|
|
152
151
|
fs.writeFile(path.join(zshDir, ".zshrc"), ZSH_INTEGRATION, { mode: 384 }),
|
|
153
152
|
fs.writeFile(path.join(bashDir, "treeport.bash"), BASH_INTEGRATION, { mode: 384 }),
|
|
153
|
+
fs.writeFile(path.join(bashHome, ".bash_profile"), BASH_PROFILE_DELEGATE, { mode: 384 }),
|
|
154
|
+
fs.writeFile(path.join(bashHome, ".bashrc"), BASH_RC_DELEGATE, { mode: 384 }),
|
|
154
155
|
fs.writeFile(path.join(fishDir, "treeport.fish"), FISH_INTEGRATION, { mode: 384 })
|
|
155
156
|
]);
|
|
156
157
|
}
|
|
157
|
-
function integrateShellLaunch(argv, env, root,
|
|
158
|
+
function integrateShellLaunch(argv, env, root, enabled) {
|
|
158
159
|
const shell = path.basename(argv[0] ?? "").replace(/^-/, "");
|
|
159
|
-
if (!root || !
|
|
160
|
+
if (!root || !enabled || ![
|
|
160
161
|
"zsh",
|
|
161
162
|
"bash",
|
|
162
163
|
"fish"
|
|
@@ -188,7 +189,7 @@ function integrateShellLaunch(argv, env, root, tmuxExecutable) {
|
|
|
188
189
|
}
|
|
189
190
|
const integratedEnv = {
|
|
190
191
|
...env,
|
|
191
|
-
|
|
192
|
+
TREEPORT_SHELL_INTEGRATION: "1"
|
|
192
193
|
};
|
|
193
194
|
if (shell === "zsh") {
|
|
194
195
|
const integrationZdotDir = path.join(root, "zsh");
|
|
@@ -239,19 +240,18 @@ function integrateShellLaunch(argv, env, root, tmuxExecutable) {
|
|
|
239
240
|
env
|
|
240
241
|
};
|
|
241
242
|
}
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
PROMPT_COMMAND: "source \"${TREEPORT_BASH_INTEGRATION_FILE}\""
|
|
243
|
+
const userHome = env.HOME?.trim();
|
|
244
|
+
if (!userHome) return {
|
|
245
|
+
argv,
|
|
246
|
+
env
|
|
247
247
|
};
|
|
248
|
-
if (env.PROMPT_COMMAND !== void 0) {
|
|
249
|
-
bashEnvironment.TREEPORT_BASH_PROMPT_COMMAND_SET = "1";
|
|
250
|
-
bashEnvironment.TREEPORT_BASH_PROMPT_COMMAND = env.PROMPT_COMMAND;
|
|
251
|
-
}
|
|
252
248
|
return {
|
|
253
249
|
argv,
|
|
254
|
-
env:
|
|
250
|
+
env: {
|
|
251
|
+
...integratedEnv,
|
|
252
|
+
HOME: path.join(root, "bash", "home"),
|
|
253
|
+
TREEPORT_USER_HOME: userHome
|
|
254
|
+
}
|
|
255
255
|
};
|
|
256
256
|
}
|
|
257
257
|
//#endregion
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import fsSync from "node:fs";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import * as Exit from "effect/Exit";
|
|
7
|
+
import * as Cause from "effect/Cause";
|
|
8
|
+
import * as Option from "effect/Option";
|
|
9
|
+
import * as Tracer from "effect/Tracer";
|
|
10
|
+
import * as ManagedRuntime from "effect/ManagedRuntime";
|
|
11
|
+
import { NodeSdk } from "@effect/opentelemetry";
|
|
12
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
13
|
+
//#region src/server/tracing.ts
|
|
14
|
+
const SAFE_ATTRIBUTE_NAMES = /* @__PURE__ */ new Set([
|
|
15
|
+
"http.request.method",
|
|
16
|
+
"http.response.status_code",
|
|
17
|
+
"network.protocol",
|
|
18
|
+
"url.path",
|
|
19
|
+
"treeport.channel",
|
|
20
|
+
"treeport.client.id",
|
|
21
|
+
"treeport.connection.id",
|
|
22
|
+
"treeport.mutation.coordinator",
|
|
23
|
+
"treeport.mutation.queue_wait_ms",
|
|
24
|
+
"treeport.mutation.queued_ahead",
|
|
25
|
+
"treeport.panel.id",
|
|
26
|
+
"treeport.request.id",
|
|
27
|
+
"treeport.terminal.id",
|
|
28
|
+
"treeport.terminal.launch_kind",
|
|
29
|
+
"treeport.terminal.pending_output_bytes",
|
|
30
|
+
"treeport.terminal.snapshot_bytes",
|
|
31
|
+
"treeport.terminal_host.method",
|
|
32
|
+
"treeport.terminal_host.queue_wait_ms",
|
|
33
|
+
"treeport.worktree.id"
|
|
34
|
+
]);
|
|
35
|
+
function tracingDestination(environment = process.env) {
|
|
36
|
+
if (environment.TREEPORT_TRACE !== "jsonl") return null;
|
|
37
|
+
const configuredPath = environment.TREEPORT_TRACE_FILE?.trim();
|
|
38
|
+
if (configuredPath && !path.isAbsolute(configuredPath)) throw new Error("TREEPORT_TRACE_FILE must be an absolute path");
|
|
39
|
+
return { filePath: configuredPath || null };
|
|
40
|
+
}
|
|
41
|
+
function safeAttributes(attributes) {
|
|
42
|
+
const result = {};
|
|
43
|
+
for (const [name, value] of Object.entries(attributes)) {
|
|
44
|
+
if (!SAFE_ATTRIBUTE_NAMES.has(name)) continue;
|
|
45
|
+
if (typeof value === "string") result[name] = value.slice(0, 512);
|
|
46
|
+
else if (typeof value === "number" || typeof value === "boolean") result[name] = value;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function hrTimeMilliseconds(value) {
|
|
51
|
+
return value[0] * 1e3 + value[1] / 1e6;
|
|
52
|
+
}
|
|
53
|
+
var JsonLinesSpanExporter = class {
|
|
54
|
+
serviceName;
|
|
55
|
+
descriptor;
|
|
56
|
+
closed = false;
|
|
57
|
+
constructor(serviceName, destination) {
|
|
58
|
+
this.serviceName = serviceName;
|
|
59
|
+
if (destination.filePath) {
|
|
60
|
+
fsSync.mkdirSync(path.dirname(destination.filePath), {
|
|
61
|
+
recursive: true,
|
|
62
|
+
mode: 448
|
|
63
|
+
});
|
|
64
|
+
this.descriptor = fsSync.openSync(destination.filePath, "a", 384);
|
|
65
|
+
fsSync.chmodSync(destination.filePath, 384);
|
|
66
|
+
} else this.descriptor = null;
|
|
67
|
+
}
|
|
68
|
+
export(spans, resultCallback) {
|
|
69
|
+
if (this.closed) {
|
|
70
|
+
resultCallback({ code: 1 });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
for (const span of spans) {
|
|
75
|
+
const context = span.spanContext();
|
|
76
|
+
const record = {
|
|
77
|
+
type: "treeport.trace.span",
|
|
78
|
+
timestamp: new Date(hrTimeMilliseconds(span.startTime)).toISOString(),
|
|
79
|
+
service: this.serviceName,
|
|
80
|
+
traceId: context.traceId,
|
|
81
|
+
spanId: context.spanId,
|
|
82
|
+
parentSpanId: span.parentSpanContext?.spanId ?? null,
|
|
83
|
+
name: span.name,
|
|
84
|
+
kind: span.kind,
|
|
85
|
+
durationMs: Number(hrTimeMilliseconds(span.duration).toFixed(3)),
|
|
86
|
+
status: span.status.code,
|
|
87
|
+
attributes: safeAttributes(span.attributes)
|
|
88
|
+
};
|
|
89
|
+
const line = `${JSON.stringify(record)}\n`;
|
|
90
|
+
if (this.descriptor === null) process.stderr.write(line);
|
|
91
|
+
else fsSync.writeSync(this.descriptor, line);
|
|
92
|
+
}
|
|
93
|
+
resultCallback({ code: 0 });
|
|
94
|
+
} catch (error) {
|
|
95
|
+
resultCallback({
|
|
96
|
+
code: 1,
|
|
97
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async forceFlush() {
|
|
102
|
+
if (this.descriptor !== null && !this.closed) fsSync.fsyncSync(this.descriptor);
|
|
103
|
+
}
|
|
104
|
+
async shutdown() {
|
|
105
|
+
if (this.closed) return;
|
|
106
|
+
await this.forceFlush();
|
|
107
|
+
this.closed = true;
|
|
108
|
+
if (this.descriptor !== null) fsSync.closeSync(this.descriptor);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
function makeTracingLayer(options) {
|
|
112
|
+
if (!options.destination) return Layer.empty;
|
|
113
|
+
const exporter = new JsonLinesSpanExporter(options.serviceName, options.destination);
|
|
114
|
+
return NodeSdk.layer(() => ({
|
|
115
|
+
resource: {
|
|
116
|
+
serviceName: options.serviceName,
|
|
117
|
+
serviceVersion: options.serviceVersion
|
|
118
|
+
},
|
|
119
|
+
spanProcessor: new BatchSpanProcessor(exporter, {
|
|
120
|
+
maxQueueSize: 2048,
|
|
121
|
+
maxExportBatchSize: 256,
|
|
122
|
+
scheduledDelayMillis: 250
|
|
123
|
+
}),
|
|
124
|
+
shutdownTimeout: "5 seconds"
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
function tracingLayerFromEnvironment(serviceName, serviceVersion, environment = process.env) {
|
|
128
|
+
return makeTracingLayer({
|
|
129
|
+
serviceName,
|
|
130
|
+
serviceVersion,
|
|
131
|
+
destination: tracingDestination(environment)
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
function tracingEnabled(environment = process.env) {
|
|
135
|
+
return tracingDestination(environment) !== null;
|
|
136
|
+
}
|
|
137
|
+
const currentTraceContext = tracingEnabled() ? Effect.option(Effect.currentSpan).pipe(Effect.map(Option.match({
|
|
138
|
+
onNone: () => null,
|
|
139
|
+
onSome: (span) => ({
|
|
140
|
+
traceId: span.traceId,
|
|
141
|
+
spanId: span.spanId,
|
|
142
|
+
sampled: span.sampled
|
|
143
|
+
})
|
|
144
|
+
}))) : Effect.succeed(null);
|
|
145
|
+
function makeHostTraceRuntime(serviceVersion) {
|
|
146
|
+
if (!tracingEnabled()) return null;
|
|
147
|
+
const runtime = ManagedRuntime.make(tracingLayerFromEnvironment("treeport-terminal-host", serviceVersion));
|
|
148
|
+
return {
|
|
149
|
+
async run(name, parent, attributes, evaluate) {
|
|
150
|
+
const exit = await runtime.runPromiseExit(Effect.tryPromise({
|
|
151
|
+
try: evaluate,
|
|
152
|
+
catch: (cause) => cause
|
|
153
|
+
}).pipe(Effect.withSpan(name, {
|
|
154
|
+
parent: Tracer.externalSpan({
|
|
155
|
+
traceId: parent.traceId,
|
|
156
|
+
spanId: parent.spanId,
|
|
157
|
+
sampled: parent.sampled
|
|
158
|
+
}),
|
|
159
|
+
attributes
|
|
160
|
+
})));
|
|
161
|
+
if (Exit.isSuccess(exit)) return exit.value;
|
|
162
|
+
const failure = Cause.failureOption(exit.cause);
|
|
163
|
+
if (Option.isSome(failure)) throw failure.value;
|
|
164
|
+
throw Cause.squash(exit.cause);
|
|
165
|
+
},
|
|
166
|
+
dispose() {
|
|
167
|
+
return runtime.dispose();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/server/terminal-host-protocol.ts
|
|
173
|
+
const TERMINAL_HOST_MAX_FRAME_BYTES = 64 * 1024 * 1024;
|
|
174
|
+
const terminalHostRecordSchema = z.object({
|
|
175
|
+
protocolVersion: z.number().int().positive(),
|
|
176
|
+
hostId: z.string().min(1),
|
|
177
|
+
hostKey: z.string().min(1),
|
|
178
|
+
pid: z.number().int().positive(),
|
|
179
|
+
socketPath: z.string().min(1),
|
|
180
|
+
startedAt: z.string().min(1)
|
|
181
|
+
}).strict();
|
|
182
|
+
const terminalSizeSchema = z.object({
|
|
183
|
+
cols: z.number().int().positive(),
|
|
184
|
+
rows: z.number().int().positive()
|
|
185
|
+
}).strict();
|
|
186
|
+
const terminalIdSchema = z.object({ terminalId: z.string().min(1) }).strict();
|
|
187
|
+
const worktreeIdSchema = z.object({ worktreeId: z.string().min(1) }).strict();
|
|
188
|
+
const setupTaskSchema = z.object({
|
|
189
|
+
label: z.string(),
|
|
190
|
+
argv: z.array(z.string()),
|
|
191
|
+
cwd: z.string(),
|
|
192
|
+
env: z.record(z.string(), z.string()),
|
|
193
|
+
timeoutMs: z.number().positive()
|
|
194
|
+
}).strict();
|
|
195
|
+
const createSchema = z.object({
|
|
196
|
+
terminalId: z.string().min(1),
|
|
197
|
+
worktreeId: z.string().min(1),
|
|
198
|
+
name: z.string(),
|
|
199
|
+
createdAt: z.string(),
|
|
200
|
+
cwd: z.string(),
|
|
201
|
+
argv: z.array(z.string()),
|
|
202
|
+
initialTitle: z.string().optional(),
|
|
203
|
+
shellCommand: z.string().nullable(),
|
|
204
|
+
interactiveShell: z.boolean(),
|
|
205
|
+
fallbackArgv: z.array(z.string()).optional(),
|
|
206
|
+
closeOnSuccess: z.boolean().optional(),
|
|
207
|
+
initialSize: terminalSizeSchema.optional(),
|
|
208
|
+
env: z.record(z.string(), z.string()),
|
|
209
|
+
setupTasks: z.array(setupTaskSchema).optional(),
|
|
210
|
+
setupError: z.string().optional()
|
|
211
|
+
}).strict();
|
|
212
|
+
const terminalHostInputSchemas = {
|
|
213
|
+
handshake: z.object({
|
|
214
|
+
token: z.string(),
|
|
215
|
+
hostKey: z.string(),
|
|
216
|
+
protocolVersion: z.number().int()
|
|
217
|
+
}).strict(),
|
|
218
|
+
create: createSchema,
|
|
219
|
+
inventory: worktreeIdSchema,
|
|
220
|
+
state: terminalIdSchema,
|
|
221
|
+
attach: terminalIdSchema,
|
|
222
|
+
unsubscribeOutput: terminalIdSchema,
|
|
223
|
+
subscribeRuntime: terminalIdSchema,
|
|
224
|
+
unsubscribeRuntime: terminalIdSchema,
|
|
225
|
+
runtimeState: terminalIdSchema,
|
|
226
|
+
write: z.object({
|
|
227
|
+
terminalId: z.string(),
|
|
228
|
+
data: z.string(),
|
|
229
|
+
encoding: z.enum(["utf8", "base64"]),
|
|
230
|
+
authority: z.object({
|
|
231
|
+
attachmentId: z.string(),
|
|
232
|
+
generation: z.number().int().positive()
|
|
233
|
+
}).strict()
|
|
234
|
+
}).strict(),
|
|
235
|
+
prepareQueryAuthority: terminalIdSchema,
|
|
236
|
+
activateQueryAuthority: terminalIdSchema.extend({
|
|
237
|
+
transitionId: z.string(),
|
|
238
|
+
attachmentId: z.string(),
|
|
239
|
+
generation: z.number().int().positive()
|
|
240
|
+
}).strict(),
|
|
241
|
+
hostQueryAuthority: terminalIdSchema,
|
|
242
|
+
resize: terminalIdSchema.extend({
|
|
243
|
+
cols: z.number().int().positive(),
|
|
244
|
+
rows: z.number().int().positive()
|
|
245
|
+
}).strict(),
|
|
246
|
+
capture: terminalIdSchema.extend({ lines: z.number().int().positive() }).strict(),
|
|
247
|
+
rename: terminalIdSchema.extend({
|
|
248
|
+
name: z.string(),
|
|
249
|
+
updatedAt: z.string()
|
|
250
|
+
}).strict(),
|
|
251
|
+
processes: worktreeIdSchema,
|
|
252
|
+
titleState: terminalIdSchema,
|
|
253
|
+
signal: terminalIdSchema.extend({ signal: z.enum([
|
|
254
|
+
"SIGINT",
|
|
255
|
+
"SIGTERM",
|
|
256
|
+
"SIGKILL",
|
|
257
|
+
"SIGHUP"
|
|
258
|
+
]) }).strict(),
|
|
259
|
+
kill: terminalIdSchema,
|
|
260
|
+
killWorktree: worktreeIdSchema,
|
|
261
|
+
shutdown: z.object({ ifEmpty: z.literal(true) }).strict()
|
|
262
|
+
};
|
|
263
|
+
const TERMINAL_HOST_REQUEST_METHODS = [
|
|
264
|
+
"handshake",
|
|
265
|
+
"create",
|
|
266
|
+
"inventory",
|
|
267
|
+
"state",
|
|
268
|
+
"attach",
|
|
269
|
+
"unsubscribeOutput",
|
|
270
|
+
"subscribeRuntime",
|
|
271
|
+
"unsubscribeRuntime",
|
|
272
|
+
"runtimeState",
|
|
273
|
+
"write",
|
|
274
|
+
"prepareQueryAuthority",
|
|
275
|
+
"activateQueryAuthority",
|
|
276
|
+
"hostQueryAuthority",
|
|
277
|
+
"resize",
|
|
278
|
+
"capture",
|
|
279
|
+
"rename",
|
|
280
|
+
"processes",
|
|
281
|
+
"titleState",
|
|
282
|
+
"signal",
|
|
283
|
+
"kill",
|
|
284
|
+
"killWorktree",
|
|
285
|
+
"shutdown"
|
|
286
|
+
];
|
|
287
|
+
const runtimeEventSchema = z.object({
|
|
288
|
+
title: z.string().optional(),
|
|
289
|
+
progress: z.strictObject({
|
|
290
|
+
state: z.enum([
|
|
291
|
+
"normal",
|
|
292
|
+
"error",
|
|
293
|
+
"indeterminate",
|
|
294
|
+
"paused"
|
|
295
|
+
]),
|
|
296
|
+
value: z.number().int().min(0).max(100).nullable()
|
|
297
|
+
}).nullable().optional(),
|
|
298
|
+
bell: z.object({
|
|
299
|
+
sequence: z.number().int().positive(),
|
|
300
|
+
at: z.string().datetime()
|
|
301
|
+
}).strict().optional(),
|
|
302
|
+
exitCode: z.number().int().nullable().optional(),
|
|
303
|
+
titleState: z.object({
|
|
304
|
+
terminalTitle: z.string().nullable(),
|
|
305
|
+
currentCommand: z.string().nullable(),
|
|
306
|
+
commandLine: z.string().nullable()
|
|
307
|
+
}).strict().optional()
|
|
308
|
+
}).strict();
|
|
309
|
+
const terminalHostFrameSchema = z.union([
|
|
310
|
+
z.object({
|
|
311
|
+
protocolVersion: z.number().int(),
|
|
312
|
+
type: z.literal("request"),
|
|
313
|
+
id: z.string(),
|
|
314
|
+
method: z.enum(TERMINAL_HOST_REQUEST_METHODS),
|
|
315
|
+
input: z.record(z.string(), z.unknown()),
|
|
316
|
+
trace: z.object({
|
|
317
|
+
traceId: z.string().regex(/^[0-9a-f]{32}$/),
|
|
318
|
+
spanId: z.string().regex(/^[0-9a-f]{16}$/),
|
|
319
|
+
sampled: z.boolean()
|
|
320
|
+
}).strict().optional()
|
|
321
|
+
}).strict(),
|
|
322
|
+
z.object({
|
|
323
|
+
protocolVersion: z.number().int(),
|
|
324
|
+
type: z.literal("response"),
|
|
325
|
+
id: z.string(),
|
|
326
|
+
result: z.any().optional(),
|
|
327
|
+
error: z.object({
|
|
328
|
+
code: z.string(),
|
|
329
|
+
message: z.string(),
|
|
330
|
+
hostProtocolVersion: z.number().int().optional(),
|
|
331
|
+
liveSessionCount: z.number().int().nonnegative().optional()
|
|
332
|
+
}).strict().nullable()
|
|
333
|
+
}).strict(),
|
|
334
|
+
z.object({
|
|
335
|
+
protocolVersion: z.number().int(),
|
|
336
|
+
type: z.literal("event"),
|
|
337
|
+
event: z.literal("output"),
|
|
338
|
+
data: z.object({
|
|
339
|
+
terminalId: z.string(),
|
|
340
|
+
output: z.string(),
|
|
341
|
+
sequence: z.number().int().positive()
|
|
342
|
+
}).strict()
|
|
343
|
+
}).strict(),
|
|
344
|
+
z.object({
|
|
345
|
+
protocolVersion: z.number().int(),
|
|
346
|
+
type: z.literal("event"),
|
|
347
|
+
event: z.literal("runtime"),
|
|
348
|
+
data: z.object({
|
|
349
|
+
terminalId: z.string(),
|
|
350
|
+
value: runtimeEventSchema
|
|
351
|
+
}).strict()
|
|
352
|
+
}).strict()
|
|
353
|
+
]);
|
|
354
|
+
function encodeTerminalHostFrame(frame) {
|
|
355
|
+
const payload = Buffer.from(JSON.stringify(frame), "utf8");
|
|
356
|
+
if (payload.byteLength > TERMINAL_HOST_MAX_FRAME_BYTES) throw new Error("Terminal host frame exceeds the byte limit");
|
|
357
|
+
const header = Buffer.allocUnsafe(4);
|
|
358
|
+
header.writeUInt32BE(payload.byteLength);
|
|
359
|
+
return Buffer.concat([header, payload]);
|
|
360
|
+
}
|
|
361
|
+
var TerminalHostFrameDecoder = class {
|
|
362
|
+
buffer = Buffer.alloc(0);
|
|
363
|
+
push(chunk) {
|
|
364
|
+
this.buffer = this.buffer.byteLength ? Buffer.concat([this.buffer, chunk]) : chunk;
|
|
365
|
+
const frames = [];
|
|
366
|
+
while (this.buffer.byteLength >= 4) {
|
|
367
|
+
const length = this.buffer.readUInt32BE(0);
|
|
368
|
+
if (length <= 0 || length > TERMINAL_HOST_MAX_FRAME_BYTES) throw new Error("Invalid terminal host frame length");
|
|
369
|
+
if (this.buffer.byteLength < length + 4) break;
|
|
370
|
+
const payload = this.buffer.subarray(4, length + 4);
|
|
371
|
+
this.buffer = this.buffer.subarray(length + 4);
|
|
372
|
+
frames.push(terminalHostFrameSchema.parse(JSON.parse(payload.toString("utf8"))));
|
|
373
|
+
}
|
|
374
|
+
return frames;
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
//#endregion
|
|
378
|
+
export { currentTraceContext as a, terminalHostRecordSchema as i, encodeTerminalHostFrame as n, makeHostTraceRuntime as o, terminalHostInputSchemas as r, tracingLayerFromEnvironment as s, TerminalHostFrameDecoder as t };
|