@jmfederico/pi-web 1.202609.0 → 1.202609.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/.plugins-ready +1 -0
- package/dist/cli.js +5 -2
- package/dist/cli.js.map +1 -1
- package/dist/client/assets/index-AroYaCLx.js +4130 -0
- package/dist/client/assets/vendor-editor-core-YWog6mAR.js +10 -0
- package/dist/client/assets/vendor-editor-languages-DOCw3vc9.js +28 -0
- package/dist/client/index.html +4 -4
- package/dist/docker/piWebDockerCommandPlan.js +9 -1
- package/dist/docker/piWebDockerCommandPlan.js.map +1 -1
- package/dist/nativeServices/installedServiceDefinitions.js +24 -17
- package/dist/nativeServices/installedServiceDefinitions.js.map +1 -1
- package/dist/nativeServices/serviceAction.js +38 -26
- package/dist/nativeServices/serviceAction.js.map +1 -1
- package/dist/nativeServices/servicePlan.js +3 -3
- package/dist/nativeServices/servicePlan.js.map +1 -1
- package/dist/pi-packages/captains-log/README.md +11 -0
- package/dist/pi-packages/captains-log/dist/browser/channelProtocol.js +29 -0
- package/dist/pi-packages/captains-log/dist/browser/index.js +144 -0
- package/dist/pi-packages/captains-log/dist/browser/markdown.js +67 -0
- package/dist/pi-packages/captains-log/dist/browser/panel.js +105 -0
- package/dist/pi-packages/captains-log/dist/browser/protocol.js +26 -0
- package/dist/pi-packages/captains-log/dist/companion.js +126 -0
- package/dist/pi-packages/captains-log/dist/roundtrip.js +62 -0
- package/dist/pi-packages/captains-log/dist/server.js +210 -0
- package/dist/pi-packages/captains-log/dist/store.js +75 -0
- package/dist/pi-packages/captains-log/docs/usage.md +30 -0
- package/dist/pi-packages/captains-log/package.json +35 -0
- package/dist/pi-packages/captains-log/tsconfig.json +21 -0
- package/dist/pi-packages/captains-log/vite.config.mjs +28 -0
- package/dist/pi-packages/relays/pi-web-plugin.js +1 -1
- package/dist/pi-packages/relays/prompts/relay.md +1 -1
- package/dist/pi-packages/relays/skills/relay-runner/SKILL.md +21 -13
- package/dist/pi-web-plugins/files/browser/assets/files-icon-DZObYhxb.svg +3 -0
- package/dist/pi-web-plugins/files/browser/pi-web-plugin.js +439 -0
- package/dist/pi-web-plugins/files/package.json +15 -0
- package/dist/pi-web-plugins/git/browser/git-panel.js +54 -24
- package/dist/pi-web-plugins/git/browser/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/git/git-backend.js +5 -5
- package/dist/pi-web-plugins/git/server-plugin.js +5 -3
- package/dist/pi-web-plugins/info/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/mermaid/browser/mermaid-engine.js +3590 -0
- package/dist/pi-web-plugins/mermaid/browser/pi-web-plugin.js +6 -0
- package/dist/pi-web-plugins/mermaid/package.json +13 -0
- package/dist/pi-web-plugins/terminal/browser/pi-web-plugin.js +210 -0
- package/dist/pi-web-plugins/terminal/package.json +16 -0
- package/dist/pi-web-plugins/terminal/server-plugin.js +365 -0
- package/dist/{server/terminals → pi-web-plugins/terminal}/terminalService.js +159 -79
- package/dist/pi-web-plugins/updates/pi-web-plugin.js +1 -1
- package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +1 -1
- package/dist/piWebVersionReport.js +12 -4
- package/dist/piWebVersionReport.js.map +1 -1
- package/dist/plugin-api.d.ts +222 -23
- package/dist/plugin-api.js +1 -0
- package/dist/pluginRecoveryCli.js +3 -2
- package/dist/pluginRecoveryCli.js.map +1 -1
- package/dist/server/activity/workspaceActivityService.js.map +1 -1
- package/dist/server/app.js +20 -9
- package/dist/server/app.js.map +1 -1
- package/dist/server/knownPiPackages.js +12 -0
- package/dist/server/knownPiPackages.js.map +1 -0
- package/dist/server/machines/machineClient.js +2 -2
- package/dist/server/machines/machineClient.js.map +1 -1
- package/dist/server/machines/machinePluginProxyRoutes.js +53 -11
- package/dist/server/machines/machinePluginProxyRoutes.js.map +1 -1
- package/dist/server/machines/machineProxyRoutes.js +70 -7
- package/dist/server/machines/machineProxyRoutes.js.map +1 -1
- package/dist/server/notices/serverNoticeStore.js +45 -4
- package/dist/server/notices/serverNoticeStore.js.map +1 -1
- package/dist/server/piPackageService.js +5 -5
- package/dist/server/piPackageService.js.map +1 -1
- package/dist/server/piWebPluginCatalog.js +46 -12
- package/dist/server/piWebPluginCatalog.js.map +1 -1
- package/dist/server/piWebPluginLifecycle.js +26 -3
- package/dist/server/piWebPluginLifecycle.js.map +1 -1
- package/dist/server/piWebPluginService.js +37 -2
- package/dist/server/piWebPluginService.js.map +1 -1
- package/dist/server/pluginCallbackDrain.js +35 -0
- package/dist/server/pluginCallbackDrain.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js +84 -0
- package/dist/server/plugins/pluginBackendChannelProxyAdmission.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js +234 -0
- package/dist/server/plugins/pluginBackendChannelProxyCoordinator.js.map +1 -0
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js +52 -0
- package/dist/server/plugins/pluginBackendChannelProxyRoutes.js.map +1 -0
- package/dist/server/plugins/pluginBackendProxyRoutes.js +38 -31
- package/dist/server/plugins/pluginBackendProxyRoutes.js.map +1 -1
- package/dist/server/plugins/pluginBackendRegistry.js +740 -0
- package/dist/server/plugins/pluginBackendRegistry.js.map +1 -0
- package/dist/server/plugins/serverPluginPiSessionEventsCapability.js +34 -0
- package/dist/server/plugins/serverPluginPiSessionEventsCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginPiSessionsCapability.js +185 -0
- package/dist/server/plugins/serverPluginPiSessionsCapability.js.map +1 -0
- package/dist/server/plugins/serverPluginRuntime.js +1028 -107
- package/dist/server/plugins/serverPluginRuntime.js.map +1 -1
- package/dist/server/plugins/serverPluginWorkspacesCapability.js +114 -0
- package/dist/server/plugins/serverPluginWorkspacesCapability.js.map +1 -0
- package/dist/server/sessiond/pluginBackendChannelRoutes.js +393 -0
- package/dist/server/sessiond/pluginBackendChannelRoutes.js.map +1 -0
- package/dist/server/sessiond/pluginBackendRoutes.js +10 -7
- package/dist/server/sessiond/pluginBackendRoutes.js.map +1 -1
- package/dist/server/sessiond/sessionDaemonShutdown.js +9 -2
- package/dist/server/sessiond/sessionDaemonShutdown.js.map +1 -1
- package/dist/server/sessiond.js +71 -21
- package/dist/server/sessiond.js.map +1 -1
- package/dist/server/sessions/builtinCommands.js +2 -2
- package/dist/server/sessions/builtinCommands.js.map +1 -1
- package/dist/server/sessions/clientSessionPreview.js +14 -0
- package/dist/server/sessions/clientSessionPreview.js.map +1 -0
- package/dist/server/sessions/piSessionEventConnections.js +65 -0
- package/dist/server/sessions/piSessionEventConnections.js.map +1 -0
- package/dist/server/sessions/piSessionService.js +269 -86
- package/dist/server/sessions/piSessionService.js.map +1 -1
- package/dist/server/sessions/sessionActivityMarker.js +105 -0
- package/dist/server/sessions/sessionActivityMarker.js.map +1 -0
- package/dist/server/sessions/sessionEnvironmentFacts.js +5 -4
- package/dist/server/sessions/sessionEnvironmentFacts.js.map +1 -1
- package/dist/server/sessions/sessionNameGenerator.js +3 -2
- package/dist/server/sessions/sessionNameGenerator.js.map +1 -1
- package/dist/server/sessions/sessionRoutes.js +18 -0
- package/dist/server/sessions/sessionRoutes.js.map +1 -1
- package/dist/server/sessions/spawnSessionTool.js +1 -1
- package/dist/server/sessions/spawnSessionTool.js.map +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js +1 -1
- package/dist/server/sessions/spawnSubsessionTool.js.map +1 -1
- package/dist/server/sessions/transcriptMessages.js +54 -0
- package/dist/server/sessions/transcriptMessages.js.map +1 -0
- package/dist/server/terminals/requiredTerminalService.js +103 -0
- package/dist/server/terminals/requiredTerminalService.js.map +1 -0
- package/dist/server/webSocketBridge.js +339 -0
- package/dist/server/webSocketBridge.js.map +1 -1
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js +23 -3
- package/dist/server/workspaces/sessionDaemonWorkspaceCatalog.js.map +1 -1
- package/dist/server/workspaces/workspaceCatalog.js +3 -1
- package/dist/server/workspaces/workspaceCatalog.js.map +1 -1
- package/dist/server/workspaces/workspaceProviderRegistry.js +64 -149
- package/dist/server/workspaces/workspaceProviderRegistry.js.map +1 -1
- package/dist/server/workspaces/workspaceRemovalService.js +23 -1
- package/dist/server/workspaces/workspaceRemovalService.js.map +1 -1
- package/dist/server-plugin-api.d.ts +216 -22
- package/dist/server-plugin-api.js +319 -2
- package/dist/server-plugin-api.js.map +1 -1
- package/dist/serverPluginRecovery.js +4 -0
- package/dist/serverPluginRecovery.js.map +1 -1
- package/dist/sessiond/sessionDaemonClient.js +3 -3
- package/dist/sessiond/sessionDaemonClient.js.map +1 -1
- package/dist/shared/apiTypes.js +1 -1
- package/dist/shared/apiTypes.js.map +1 -1
- package/dist/shared/federatedRoutes.js +45 -17
- package/dist/shared/federatedRoutes.js.map +1 -1
- package/dist/shared/machinePluginIds.js +28 -7
- package/dist/shared/machinePluginIds.js.map +1 -1
- package/dist/shared/pluginApiTypes.d.ts +21 -1
- package/dist/shared/pluginApiTypes.js +0 -1
- package/dist/shared/pluginBackendProtocol.js +163 -2
- package/dist/shared/pluginBackendProtocol.js.map +1 -1
- package/dist/shared/pluginIds.js +8 -1
- package/dist/shared/pluginIds.js.map +1 -1
- package/dist/shared/requiredTerminalPlugin.js +6 -0
- package/dist/shared/requiredTerminalPlugin.js.map +1 -0
- package/dist/shared/serverNoticeContract.js +88 -0
- package/dist/shared/serverNoticeContract.js.map +1 -0
- package/dist/shared/sessionDefaults.js +41 -0
- package/dist/shared/sessionDefaults.js.map +1 -0
- package/docs/config.md +50 -36
- package/docs/plugins.md +146 -1217
- package/examples/session-bridge-plugin/README.md +20 -0
- package/examples/session-bridge-plugin/docs/usage.md +54 -0
- package/examples/session-bridge-plugin/package.json +26 -0
- package/examples/session-bridge-plugin/src/browser/index.ts +84 -0
- package/examples/session-bridge-plugin/src/browser/protocol.ts +24 -0
- package/examples/session-bridge-plugin/src/companion.ts +65 -0
- package/examples/session-bridge-plugin/src/reviewRun.ts +39 -0
- package/examples/session-bridge-plugin/src/server.ts +85 -0
- package/examples/session-bridge-plugin/src/store.ts +57 -0
- package/examples/session-bridge-plugin/tsconfig.json +21 -0
- package/examples/workspace-provider-plugin/README.md +4 -4
- package/examples/workspace-provider-plugin/package.json +1 -1
- package/examples/workspace-provider-plugin/src/browser/index.ts +9 -9
- package/examples/workspace-provider-plugin/src/server.ts +15 -11
- package/package.json +17 -11
- package/dist/client/assets/CodeViewer-CGAlg9S8.js +0 -4
- package/dist/client/assets/TerminalPanel-xhJRhOas.js +0 -187
- package/dist/client/assets/index-DXQKhn1P.js +0 -4326
- package/dist/client/assets/vendor-editor-core-CXO8gGab.js +0 -12
- package/dist/client/assets/vendor-editor-languages-CpW4sJsX.js +0 -46
- package/dist/client/assets/vendor-editor-legacy-CYBnW6ZU.js +0 -1
- package/dist/client/assets/vendor-terminal-BrP-ENHg.css +0 -1
- package/dist/client/assets/vendor-terminal-D8k4UKM2.js +0 -35
- package/dist/server/terminalProxyRoutes.js +0 -141
- package/dist/server/terminalProxyRoutes.js.map +0 -1
- package/dist/server/terminals/terminalRoutes.js +0 -155
- package/dist/server/terminals/terminalRoutes.js.map +0 -1
- package/dist/server/terminals/terminalService.js.map +0 -1
- package/dist/server/terminals/terminalSize.js +0 -17
- package/dist/server/terminals/terminalSize.js.map +0 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pi-web/terminal-plugin",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"piWeb": {
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"id": "pi-web.terminal",
|
|
9
|
+
"browserRoot": "browser",
|
|
10
|
+
"module": "browser/pi-web-plugin.js",
|
|
11
|
+
"serverModule": "server-plugin.js",
|
|
12
|
+
"machineSpecific": true
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
// Generated from pi-web-plugins/terminal/server/server-plugin.ts. Do not edit directly.
|
|
2
|
+
import { TerminalService, } from "./terminalService.js";
|
|
3
|
+
const TERMINAL_CHANNEL_OUTPUT_FRAME_TARGET_BYTES = 60 * 1024;
|
|
4
|
+
export const TERMINAL_SERVICE_CAPABILITY = Object.freeze({
|
|
5
|
+
pluginId: "pi-web.terminal",
|
|
6
|
+
id: "service",
|
|
7
|
+
version: 1,
|
|
8
|
+
parse: snapshotTerminalServiceCapability,
|
|
9
|
+
});
|
|
10
|
+
const plugin = {
|
|
11
|
+
apiVersion: 3,
|
|
12
|
+
name: "Terminal",
|
|
13
|
+
activate(context) {
|
|
14
|
+
return activateTerminalPlugin(context);
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
export default plugin;
|
|
18
|
+
export function activateTerminalPlugin(context) {
|
|
19
|
+
if (context.pluginId !== "pi-web.terminal") {
|
|
20
|
+
throw new Error(`Terminal server entry must activate as plugin id pi-web.terminal, received ${context.pluginId}`);
|
|
21
|
+
}
|
|
22
|
+
const notices = context.notices;
|
|
23
|
+
if (notices?.version !== 1) {
|
|
24
|
+
throw new Error("Terminal server entry requires server notice reporter version 1");
|
|
25
|
+
}
|
|
26
|
+
const service = new TerminalService((input) => { notices.record(input); });
|
|
27
|
+
const requiredTerminalService = Object.freeze({
|
|
28
|
+
closeForCwd: (cwd) => { service.closeForCwd(cwd); },
|
|
29
|
+
runCommand: (options) => service.runCommand(options),
|
|
30
|
+
bindActivitySink: (sink) => { service.bindActivitySink(sink); },
|
|
31
|
+
});
|
|
32
|
+
const serviceProvision = Object.freeze({
|
|
33
|
+
capability: TERMINAL_SERVICE_CAPABILITY,
|
|
34
|
+
value: requiredTerminalService,
|
|
35
|
+
});
|
|
36
|
+
let stopped = false;
|
|
37
|
+
return Object.freeze({
|
|
38
|
+
peer: createTerminalPeer(service, context),
|
|
39
|
+
provides: Object.freeze([serviceProvision]),
|
|
40
|
+
health: () => stopped
|
|
41
|
+
? Object.freeze({ status: "unhealthy", message: "Terminal service is stopped" })
|
|
42
|
+
: Object.freeze({ status: "healthy" }),
|
|
43
|
+
dispose: () => {
|
|
44
|
+
if (stopped)
|
|
45
|
+
return;
|
|
46
|
+
stopped = true;
|
|
47
|
+
service.dispose();
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function snapshotTerminalServiceCapability(value) {
|
|
52
|
+
if (!isRequiredTerminalServiceContribution(value)) {
|
|
53
|
+
throw new Error("Terminal service capability must expose closeForCwd, runCommand, and bindActivitySink");
|
|
54
|
+
}
|
|
55
|
+
return Object.freeze({
|
|
56
|
+
closeForCwd: (cwd) => { value.closeForCwd(cwd); },
|
|
57
|
+
runCommand: (options) => value.runCommand(options),
|
|
58
|
+
bindActivitySink: (sink) => { value.bindActivitySink(sink); },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function isRequiredTerminalServiceContribution(value) {
|
|
62
|
+
if (typeof value !== "object" || value === null)
|
|
63
|
+
return false;
|
|
64
|
+
return typeof Reflect.get(value, "closeForCwd") === "function"
|
|
65
|
+
&& typeof Reflect.get(value, "runCommand") === "function"
|
|
66
|
+
&& typeof Reflect.get(value, "bindActivitySink") === "function";
|
|
67
|
+
}
|
|
68
|
+
export function createTerminalPeer(service, activationContext) {
|
|
69
|
+
return Object.freeze({
|
|
70
|
+
request: (context) => terminalRequest(service, context),
|
|
71
|
+
openChannel: (context) => openTerminalChannel(service, context, activationContext),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function terminalRequest(service, context) {
|
|
75
|
+
throwIfAborted(context.signal);
|
|
76
|
+
const scope = terminalScope(context);
|
|
77
|
+
switch (context.operation) {
|
|
78
|
+
case "terminal.list":
|
|
79
|
+
requireNullishInput(context.input, context.operation);
|
|
80
|
+
return service.list(scope).map(terminalInfoJson);
|
|
81
|
+
case "terminal.create": {
|
|
82
|
+
const input = requireObject(context.input, context.operation);
|
|
83
|
+
return terminalInfoJson(service.create({
|
|
84
|
+
...scope,
|
|
85
|
+
...optionalTerminalName(input),
|
|
86
|
+
...optionalTerminalSize(input),
|
|
87
|
+
}));
|
|
88
|
+
}
|
|
89
|
+
case "terminal.close": {
|
|
90
|
+
service.close(scope, requireIdInput(context.input, context.operation, "terminalId"));
|
|
91
|
+
return { closed: true };
|
|
92
|
+
}
|
|
93
|
+
case "terminal.continue":
|
|
94
|
+
return terminalInfoJson(service.continue(scope, requireIdInput(context.input, context.operation, "terminalId")));
|
|
95
|
+
case "terminal.run": {
|
|
96
|
+
const input = requireObject(context.input, context.operation);
|
|
97
|
+
return commandRunJson(service.runCommand(parseRunCommand(scope, input)));
|
|
98
|
+
}
|
|
99
|
+
case "terminal.list-runs": {
|
|
100
|
+
const input = requireObjectOrEmpty(context.input, context.operation);
|
|
101
|
+
return service.listCommandRunsForScope(scope, parseCommandRunFilter(input)).map(commandRunJson);
|
|
102
|
+
}
|
|
103
|
+
case "terminal.get-run": {
|
|
104
|
+
const run = service.getCommandRunForScope(scope, requireIdInput(context.input, context.operation, "runId"));
|
|
105
|
+
return run === undefined ? null : commandRunJson(run);
|
|
106
|
+
}
|
|
107
|
+
case "terminal.cancel-run":
|
|
108
|
+
return commandRunJson(service.cancelCommandRunForScope(scope, requireIdInput(context.input, context.operation, "runId")));
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Unsupported Terminal operation: ${context.operation}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function openTerminalChannel(service, context, activationContext) {
|
|
114
|
+
if (context.operation !== "terminal.attach") {
|
|
115
|
+
throw new Error(`Unsupported Terminal channel operation: ${context.operation}`);
|
|
116
|
+
}
|
|
117
|
+
throwIfAborted(context.signal);
|
|
118
|
+
const scope = terminalScope(context);
|
|
119
|
+
const input = requireObject(context.input, context.operation);
|
|
120
|
+
const terminalId = requireString(input, "terminalId", context.operation);
|
|
121
|
+
const size = optionalTerminalSize(input);
|
|
122
|
+
if (size.cols !== undefined && size.rows !== undefined) {
|
|
123
|
+
service.resize(scope, terminalId, size.cols, size.rows);
|
|
124
|
+
}
|
|
125
|
+
let resolveCompleted = () => undefined;
|
|
126
|
+
const completed = new Promise((resolve) => { resolveCompleted = resolve; });
|
|
127
|
+
let opening = true;
|
|
128
|
+
let detach = () => undefined;
|
|
129
|
+
const send = (frame) => {
|
|
130
|
+
try {
|
|
131
|
+
context.send(frame);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (opening)
|
|
135
|
+
throw error;
|
|
136
|
+
detach();
|
|
137
|
+
activationContext?.logger.error("Terminal channel output failed", {
|
|
138
|
+
terminalId,
|
|
139
|
+
message: boundedErrorMessage(error),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
detach = service.attach(scope, terminalId, {
|
|
144
|
+
output(data, replay) {
|
|
145
|
+
for (const frame of terminalOutputFrames(data, replay))
|
|
146
|
+
send(frame);
|
|
147
|
+
},
|
|
148
|
+
exit(exitCode) {
|
|
149
|
+
send({ type: "exit", ...(exitCode === undefined ? {} : { exitCode }) });
|
|
150
|
+
},
|
|
151
|
+
closed() {
|
|
152
|
+
resolveCompleted();
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
opening = false;
|
|
156
|
+
let cleanedUp = false;
|
|
157
|
+
const cleanup = () => {
|
|
158
|
+
if (cleanedUp)
|
|
159
|
+
return;
|
|
160
|
+
cleanedUp = true;
|
|
161
|
+
context.signal.removeEventListener("abort", cleanup);
|
|
162
|
+
detach();
|
|
163
|
+
};
|
|
164
|
+
context.signal.addEventListener("abort", cleanup, { once: true });
|
|
165
|
+
return Object.freeze({
|
|
166
|
+
closed: completed,
|
|
167
|
+
receive(data, signal) {
|
|
168
|
+
throwIfAborted(signal);
|
|
169
|
+
throwIfAborted(context.signal);
|
|
170
|
+
const frame = requireObject(data, "terminal.attach frame");
|
|
171
|
+
const type = requireString(frame, "type", "terminal.attach frame");
|
|
172
|
+
if (type === "input") {
|
|
173
|
+
service.write(scope, terminalId, requireTerminalInputData(frame));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (type === "resize") {
|
|
177
|
+
const parsed = requireTerminalSize(frame, "terminal.attach resize frame");
|
|
178
|
+
service.resize(scope, terminalId, parsed.cols, parsed.rows);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
throw new Error(`Unsupported Terminal channel frame: ${type}`);
|
|
182
|
+
},
|
|
183
|
+
close() {
|
|
184
|
+
cleanup();
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function terminalScope(context) {
|
|
189
|
+
if (context.workspace.projectId !== context.project.id) {
|
|
190
|
+
throw new Error("Terminal workspace project scope does not match the host project");
|
|
191
|
+
}
|
|
192
|
+
return Object.freeze({
|
|
193
|
+
projectId: context.project.id,
|
|
194
|
+
workspaceId: context.workspace.id,
|
|
195
|
+
cwd: context.workspace.path,
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function parseRunCommand(scope, input) {
|
|
199
|
+
const metadata = input["metadata"];
|
|
200
|
+
return {
|
|
201
|
+
...scope,
|
|
202
|
+
origin: requireString(input, "origin", "terminal.run"),
|
|
203
|
+
title: requireString(input, "title", "terminal.run"),
|
|
204
|
+
command: requireString(input, "command", "terminal.run"),
|
|
205
|
+
...(metadata === undefined ? {} : { metadata }),
|
|
206
|
+
...optionalTerminalSize(input),
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function parseCommandRunFilter(input) {
|
|
210
|
+
const terminalId = optionalString(input, "terminalId", "terminal.list-runs");
|
|
211
|
+
const metadataValue = input["metadata"];
|
|
212
|
+
const statusesValue = input["statuses"];
|
|
213
|
+
const metadata = metadataValue === undefined ? undefined : stringRecord(metadataValue, "terminal.list-runs metadata");
|
|
214
|
+
const statuses = statusesValue === undefined ? undefined : requireArray(statusesValue, "terminal.list-runs statuses").map((status) => parseCommandRunStatus(status));
|
|
215
|
+
return {
|
|
216
|
+
...(terminalId === undefined ? {} : { terminalId }),
|
|
217
|
+
...(statuses === undefined ? {} : { statuses }),
|
|
218
|
+
...(metadata === undefined ? {} : { metadata }),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function terminalInfoJson(info) {
|
|
222
|
+
return {
|
|
223
|
+
id: info.id,
|
|
224
|
+
cwd: info.cwd,
|
|
225
|
+
name: info.name,
|
|
226
|
+
createdAt: info.createdAt,
|
|
227
|
+
exited: info.exited,
|
|
228
|
+
...(info.exitCode === undefined ? {} : { exitCode: info.exitCode }),
|
|
229
|
+
...(info.commandRunId === undefined ? {} : { commandRunId: info.commandRunId }),
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
function commandRunJson(run) {
|
|
233
|
+
return {
|
|
234
|
+
id: run.id,
|
|
235
|
+
origin: run.origin,
|
|
236
|
+
projectId: run.projectId,
|
|
237
|
+
workspaceId: run.workspaceId,
|
|
238
|
+
terminalId: run.terminalId,
|
|
239
|
+
title: run.title,
|
|
240
|
+
command: run.command,
|
|
241
|
+
status: run.status,
|
|
242
|
+
...(run.exitCode === undefined ? {} : { exitCode: run.exitCode }),
|
|
243
|
+
createdAt: run.createdAt,
|
|
244
|
+
...(run.startedAt === undefined ? {} : { startedAt: run.startedAt }),
|
|
245
|
+
...(run.completedAt === undefined ? {} : { completedAt: run.completedAt }),
|
|
246
|
+
metadata: { ...run.metadata },
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function optionalTerminalName(input) {
|
|
250
|
+
const name = optionalString(input, "name", "terminal.create");
|
|
251
|
+
return name === undefined ? {} : { name };
|
|
252
|
+
}
|
|
253
|
+
function optionalTerminalSize(input) {
|
|
254
|
+
const cols = input["cols"];
|
|
255
|
+
const rows = input["rows"];
|
|
256
|
+
if (cols === undefined && rows === undefined)
|
|
257
|
+
return {};
|
|
258
|
+
return requireTerminalSize(input, "Terminal size");
|
|
259
|
+
}
|
|
260
|
+
function requireTerminalSize(input, label) {
|
|
261
|
+
const cols = input["cols"];
|
|
262
|
+
const rows = input["rows"];
|
|
263
|
+
if (typeof cols !== "number" || !Number.isFinite(cols) || cols <= 0)
|
|
264
|
+
throw new Error(`${label} cols must be a positive number`);
|
|
265
|
+
if (typeof rows !== "number" || !Number.isFinite(rows) || rows <= 0)
|
|
266
|
+
throw new Error(`${label} rows must be a positive number`);
|
|
267
|
+
return { cols: Math.floor(cols), rows: Math.floor(rows) };
|
|
268
|
+
}
|
|
269
|
+
function requireIdInput(input, operation, key) {
|
|
270
|
+
return requireString(requireObject(input, operation), key, operation);
|
|
271
|
+
}
|
|
272
|
+
function requireObject(value, label) {
|
|
273
|
+
if (!isJsonObject(value))
|
|
274
|
+
throw new Error(`${label} input must be an object`);
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
function isJsonObject(value) {
|
|
278
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
279
|
+
}
|
|
280
|
+
function requireObjectOrEmpty(value, label) {
|
|
281
|
+
return value === null ? {} : requireObject(value, label);
|
|
282
|
+
}
|
|
283
|
+
function requireNullishInput(value, label) {
|
|
284
|
+
if (value !== null && (typeof value !== "object" || Array.isArray(value) || Object.keys(value).length > 0)) {
|
|
285
|
+
throw new Error(`${label} input must be null or an empty object`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function requireString(record, key, label) {
|
|
289
|
+
const value = record[key];
|
|
290
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
291
|
+
throw new Error(`${label} ${key} must be a non-empty string`);
|
|
292
|
+
return value;
|
|
293
|
+
}
|
|
294
|
+
function requireTerminalInputData(record) {
|
|
295
|
+
const value = record["data"];
|
|
296
|
+
if (typeof value !== "string" || value === "")
|
|
297
|
+
throw new Error("terminal.attach input frame data must be a non-empty string");
|
|
298
|
+
return value;
|
|
299
|
+
}
|
|
300
|
+
function optionalString(record, key, label) {
|
|
301
|
+
const value = record[key];
|
|
302
|
+
if (value === undefined)
|
|
303
|
+
return undefined;
|
|
304
|
+
if (typeof value !== "string")
|
|
305
|
+
throw new Error(`${label} ${key} must be a string`);
|
|
306
|
+
return value;
|
|
307
|
+
}
|
|
308
|
+
function stringRecord(value, label) {
|
|
309
|
+
const record = requireObject(value, label);
|
|
310
|
+
return Object.fromEntries(Object.entries(record).map(([key, child]) => {
|
|
311
|
+
if (typeof child !== "string")
|
|
312
|
+
throw new Error(`${label} ${key} must be a string`);
|
|
313
|
+
return [key, child];
|
|
314
|
+
}));
|
|
315
|
+
}
|
|
316
|
+
function requireArray(value, label) {
|
|
317
|
+
if (!isJsonArray(value))
|
|
318
|
+
throw new Error(`${label} must be an array`);
|
|
319
|
+
return value;
|
|
320
|
+
}
|
|
321
|
+
function isJsonArray(value) {
|
|
322
|
+
return Array.isArray(value);
|
|
323
|
+
}
|
|
324
|
+
function parseCommandRunStatus(value) {
|
|
325
|
+
if (value === "queued" || value === "running" || value === "succeeded" || value === "failed")
|
|
326
|
+
return value;
|
|
327
|
+
throw new Error(`Invalid Terminal command run status: ${JSON.stringify(value)}`);
|
|
328
|
+
}
|
|
329
|
+
export function terminalOutputFrames(value, replay) {
|
|
330
|
+
const emptyFrame = terminalOutputFrame("", replay, false);
|
|
331
|
+
const emptyFrameBytes = Buffer.byteLength(JSON.stringify(emptyFrame), "utf8");
|
|
332
|
+
const chunks = [];
|
|
333
|
+
let chunk = "";
|
|
334
|
+
let encodedBytes = emptyFrameBytes;
|
|
335
|
+
for (const character of value) {
|
|
336
|
+
// JSON escaping can expand control characters well beyond their raw UTF-8
|
|
337
|
+
// size, so budget the serialized representation rather than the PTY text.
|
|
338
|
+
const characterBytes = Buffer.byteLength(JSON.stringify(character), "utf8") - 2;
|
|
339
|
+
if (encodedBytes + characterBytes > TERMINAL_CHANNEL_OUTPUT_FRAME_TARGET_BYTES && chunk !== "") {
|
|
340
|
+
chunks.push(chunk);
|
|
341
|
+
chunk = "";
|
|
342
|
+
encodedBytes = emptyFrameBytes;
|
|
343
|
+
}
|
|
344
|
+
chunk += character;
|
|
345
|
+
encodedBytes += characterBytes;
|
|
346
|
+
}
|
|
347
|
+
if (chunk !== "" || value === "")
|
|
348
|
+
chunks.push(chunk);
|
|
349
|
+
return chunks.map((data, index) => terminalOutputFrame(data, replay, index === chunks.length - 1));
|
|
350
|
+
}
|
|
351
|
+
function terminalOutputFrame(data, replay, replayComplete) {
|
|
352
|
+
return replay
|
|
353
|
+
? { type: "output", data, replay: true, replayComplete }
|
|
354
|
+
: { type: "output", data, replay: false };
|
|
355
|
+
}
|
|
356
|
+
function throwIfAborted(signal) {
|
|
357
|
+
if (!signal.aborted)
|
|
358
|
+
return;
|
|
359
|
+
const reason = signal.reason;
|
|
360
|
+
throw reason instanceof Error ? reason : new Error("Terminal operation was cancelled", { cause: reason });
|
|
361
|
+
}
|
|
362
|
+
function boundedErrorMessage(error) {
|
|
363
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
364
|
+
return message.length <= 1_024 ? message : `${message.slice(0, 1_021)}...`;
|
|
365
|
+
}
|