@datnguyennnx/localterm-server 0.0.15
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/LICENSE +21 -0
- package/dist/agent-rpc.d.ts +27 -0
- package/dist/agent-rpc.d.ts.map +1 -0
- package/dist/agent-rpc.js +213 -0
- package/dist/agent-rpc.js.map +1 -0
- package/dist/agent-token.d.ts +21 -0
- package/dist/agent-token.d.ts.map +1 -0
- package/dist/agent-token.js +53 -0
- package/dist/agent-token.js.map +1 -0
- package/dist/constants.d.ts +54 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +106 -0
- package/dist/constants.js.map +1 -0
- package/dist/default-shell.d.ts +2 -0
- package/dist/default-shell.d.ts.map +1 -0
- package/dist/default-shell.js +44 -0
- package/dist/default-shell.js.map +1 -0
- package/dist/ensure-spawn-helper-executable.d.ts +2 -0
- package/dist/ensure-spawn-helper-executable.d.ts.map +1 -0
- package/dist/ensure-spawn-helper-executable.js +34 -0
- package/dist/ensure-spawn-helper-executable.js.map +1 -0
- package/dist/errors.d.ts +64 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +84 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +424 -0
- package/dist/index.js.map +1 -0
- package/dist/osc133-chunk-parser.d.ts +8 -0
- package/dist/osc133-chunk-parser.d.ts.map +1 -0
- package/dist/osc133-chunk-parser.js +29 -0
- package/dist/osc133-chunk-parser.js.map +1 -0
- package/dist/osc7-chunk-parser.d.ts +7 -0
- package/dist/osc7-chunk-parser.d.ts.map +1 -0
- package/dist/osc7-chunk-parser.js +29 -0
- package/dist/osc7-chunk-parser.js.map +1 -0
- package/dist/output-batcher.d.ts +11 -0
- package/dist/output-batcher.d.ts.map +1 -0
- package/dist/output-batcher.js +47 -0
- package/dist/output-batcher.js.map +1 -0
- package/dist/protocol.d.ts +5 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +3 -0
- package/dist/protocol.js.map +1 -0
- package/dist/schemas.d.ts +66 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +118 -0
- package/dist/schemas.js.map +1 -0
- package/dist/security.d.ts +5 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +63 -0
- package/dist/security.js.map +1 -0
- package/dist/session-registry.d.ts +9 -0
- package/dist/session-registry.d.ts.map +1 -0
- package/dist/session-registry.js +19 -0
- package/dist/session-registry.js.map +1 -0
- package/dist/session.d.ts +62 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +238 -0
- package/dist/session.js.map +1 -0
- package/dist/shell-integration/index.d.ts +7 -0
- package/dist/shell-integration/index.d.ts.map +1 -0
- package/dist/shell-integration/index.js +29 -0
- package/dist/shell-integration/index.js.map +1 -0
- package/dist/static-resolver.d.ts +7 -0
- package/dist/static-resolver.d.ts.map +1 -0
- package/dist/static-resolver.js +65 -0
- package/dist/static-resolver.js.map +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/format-working-directory-title.d.ts +2 -0
- package/dist/utils/format-working-directory-title.d.ts.map +1 -0
- package/dist/utils/format-working-directory-title.js +28 -0
- package/dist/utils/format-working-directory-title.js.map +1 -0
- package/dist/utils/get-heartbeat-action.d.ts +4 -0
- package/dist/utils/get-heartbeat-action.d.ts.map +1 -0
- package/dist/utils/get-heartbeat-action.js +11 -0
- package/dist/utils/get-heartbeat-action.js.map +1 -0
- package/dist/utils/parse-osc133-from-chunk.d.ts +10 -0
- package/dist/utils/parse-osc133-from-chunk.d.ts.map +1 -0
- package/dist/utils/parse-osc133-from-chunk.js +44 -0
- package/dist/utils/parse-osc133-from-chunk.js.map +1 -0
- package/dist/utils/parse-osc7-from-chunk.d.ts +2 -0
- package/dist/utils/parse-osc7-from-chunk.d.ts.map +1 -0
- package/dist/utils/parse-osc7-from-chunk.js +32 -0
- package/dist/utils/parse-osc7-from-chunk.js.map +1 -0
- package/dist/utils/resolve-cwd-for-pid.d.ts +2 -0
- package/dist/utils/resolve-cwd-for-pid.d.ts.map +1 -0
- package/dist/utils/resolve-cwd-for-pid.js +29 -0
- package/dist/utils/resolve-cwd-for-pid.js.map +1 -0
- package/dist/utils/strip-ansi.d.ts +13 -0
- package/dist/utils/strip-ansi.d.ts.map +1 -0
- package/dist/utils/strip-ansi.js +28 -0
- package/dist/utils/strip-ansi.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Aiden Bai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type CommandBoundary } from "./utils/parse-osc133-from-chunk.js";
|
|
2
|
+
import { Session } from "./session.js";
|
|
3
|
+
import { SessionRegistry } from "./session-registry.js";
|
|
4
|
+
export interface ManagedSession {
|
|
5
|
+
id: string;
|
|
6
|
+
session: Session;
|
|
7
|
+
outputBuffer: string[];
|
|
8
|
+
outputOffset: number;
|
|
9
|
+
lastBoundary: CommandBoundary | null;
|
|
10
|
+
}
|
|
11
|
+
export interface RpcContext {
|
|
12
|
+
registry: SessionRegistry;
|
|
13
|
+
allowDestructiveCommands: boolean;
|
|
14
|
+
sendResponse: (id: string, result?: unknown, error?: string) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface RpcRequest {
|
|
17
|
+
id: string;
|
|
18
|
+
method: string;
|
|
19
|
+
params?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
export declare const handleRpcRequest: (ctx: RpcContext, request: RpcRequest) => void;
|
|
22
|
+
/**
|
|
23
|
+
* Map of session ID → ManagedSession for all sessions created via RPC.
|
|
24
|
+
* These are separate from the human-connection sessions managed by the registry.
|
|
25
|
+
*/
|
|
26
|
+
export declare const activeSessions: Map<string, ManagedSession>;
|
|
27
|
+
//# sourceMappingURL=agent-rpc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-rpc.d.ts","sourceRoot":"","sources":["../src/agent-rpc.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAE1E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAUxD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,eAAe,GAAG,IAAI,CAAC;CACtC;AAkBD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,eAAe,CAAC;IAC1B,wBAAwB,EAAE,OAAO,CAAC;IAClC,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACtE;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,eAAO,MAAM,gBAAgB,GAC3B,KAAK,UAAU,EACf,SAAS,UAAU,KAClB,IAuBF,CAAC;AA2PF;;;GAGG;AACH,eAAO,MAAM,cAAc,6BAAoC,CAAC"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { AGENT_COMMAND_DENYLIST } from "./constants.js";
|
|
2
|
+
import { stripAnsi } from "./utils/strip-ansi.js";
|
|
3
|
+
import {} from "./utils/parse-osc133-from-chunk.js";
|
|
4
|
+
import {} from "./types.js";
|
|
5
|
+
import { Session } from "./session.js";
|
|
6
|
+
import { SessionRegistry } from "./session-registry.js";
|
|
7
|
+
let nextSessionId = 1;
|
|
8
|
+
const generateSessionId = () => `s${nextSessionId++}`;
|
|
9
|
+
const DENYLIST_PATTERNS = AGENT_COMMAND_DENYLIST.map((pattern) => ({
|
|
10
|
+
pattern,
|
|
11
|
+
regex: new RegExp(`\\b${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`),
|
|
12
|
+
}));
|
|
13
|
+
const isCommandDenied = (command) => {
|
|
14
|
+
for (const { pattern } of DENYLIST_PATTERNS) {
|
|
15
|
+
if (command.includes(pattern))
|
|
16
|
+
return pattern;
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
|
20
|
+
export const handleRpcRequest = (ctx, request) => {
|
|
21
|
+
const { id, method, params } = request;
|
|
22
|
+
try {
|
|
23
|
+
switch (method) {
|
|
24
|
+
case "spawn_session":
|
|
25
|
+
return handleSpawnSession(ctx, id, params ?? {});
|
|
26
|
+
case "list_sessions":
|
|
27
|
+
return handleListSessions(ctx, id);
|
|
28
|
+
case "write_input":
|
|
29
|
+
return handleWriteInput(ctx, id, params ?? {});
|
|
30
|
+
case "read_output":
|
|
31
|
+
return handleReadOutput(ctx, id, params ?? {});
|
|
32
|
+
case "wait_for_boundary":
|
|
33
|
+
return handleWaitForBoundary(ctx, id, params ?? {});
|
|
34
|
+
case "exec":
|
|
35
|
+
return handleExec(ctx, id, params ?? {});
|
|
36
|
+
default:
|
|
37
|
+
ctx.sendResponse(id, undefined, `unknown method: ${method}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
42
|
+
ctx.sendResponse(id, undefined, `internal error: ${message}`);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const handleSpawnSession = (ctx, id, params) => {
|
|
46
|
+
const { cwd, shell } = params;
|
|
47
|
+
const sessionId = generateSessionId();
|
|
48
|
+
const input = {
|
|
49
|
+
cwd,
|
|
50
|
+
shell,
|
|
51
|
+
mode: "agent",
|
|
52
|
+
env: { LOCALTERM_SHELL_INTEGRATION: "1" },
|
|
53
|
+
};
|
|
54
|
+
const session = new Session(input);
|
|
55
|
+
ctx.registry.register(session);
|
|
56
|
+
const managed = {
|
|
57
|
+
id: sessionId,
|
|
58
|
+
session,
|
|
59
|
+
outputBuffer: [],
|
|
60
|
+
outputOffset: 0,
|
|
61
|
+
lastBoundary: null,
|
|
62
|
+
};
|
|
63
|
+
activeSessions.set(sessionId, managed);
|
|
64
|
+
session.on("output", (data) => {
|
|
65
|
+
const text = stripAnsi(data);
|
|
66
|
+
if (text)
|
|
67
|
+
managed.outputBuffer.push(text);
|
|
68
|
+
});
|
|
69
|
+
session.on("commandBoundary", (boundary) => {
|
|
70
|
+
managed.lastBoundary = boundary;
|
|
71
|
+
});
|
|
72
|
+
session.on("exit", () => {
|
|
73
|
+
ctx.registry.unregister(session);
|
|
74
|
+
activeSessions.delete(sessionId);
|
|
75
|
+
});
|
|
76
|
+
ctx.sendResponse(id, {
|
|
77
|
+
sessionId,
|
|
78
|
+
shell: session.shell,
|
|
79
|
+
pid: session.pid,
|
|
80
|
+
cwd: session.cwd,
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
const handleListSessions = (ctx, id) => {
|
|
84
|
+
const sessions = Array.from(activeSessions.values()).map((m) => ({
|
|
85
|
+
id: m.id,
|
|
86
|
+
cwd: m.session.cwd,
|
|
87
|
+
pid: m.session.pid,
|
|
88
|
+
mode: "agent",
|
|
89
|
+
running: !m.session.isExited,
|
|
90
|
+
}));
|
|
91
|
+
ctx.sendResponse(id, { sessions });
|
|
92
|
+
};
|
|
93
|
+
const handleWriteInput = (ctx, id, params) => {
|
|
94
|
+
const { sessionId, data } = params;
|
|
95
|
+
const managed = activeSessions.get(sessionId);
|
|
96
|
+
if (!managed) {
|
|
97
|
+
ctx.sendResponse(id, undefined, `session not found: ${sessionId}`);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (managed.session.isExited) {
|
|
101
|
+
ctx.sendResponse(id, undefined, "session has exited");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
managed.session.write(data);
|
|
105
|
+
ctx.sendResponse(id, { ok: true });
|
|
106
|
+
};
|
|
107
|
+
const handleReadOutput = (ctx, id, params) => {
|
|
108
|
+
const { sessionId, sinceOffset } = params;
|
|
109
|
+
const managed = activeSessions.get(sessionId);
|
|
110
|
+
if (!managed) {
|
|
111
|
+
ctx.sendResponse(id, undefined, `session not found: ${sessionId}`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const offset = typeof sinceOffset === "number" ? sinceOffset : managed.outputOffset;
|
|
115
|
+
const buffer = managed.outputBuffer;
|
|
116
|
+
let charCount = 0;
|
|
117
|
+
let startIndex = -1;
|
|
118
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
119
|
+
const entryLen = buffer[i].length;
|
|
120
|
+
if (charCount + entryLen > offset && startIndex === -1)
|
|
121
|
+
startIndex = i;
|
|
122
|
+
charCount += entryLen;
|
|
123
|
+
}
|
|
124
|
+
if (startIndex === -1) {
|
|
125
|
+
ctx.sendResponse(id, { text: "", offset: charCount });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const text = buffer.slice(startIndex).join("");
|
|
129
|
+
const newOffset = charCount;
|
|
130
|
+
managed.outputOffset = newOffset;
|
|
131
|
+
ctx.sendResponse(id, { text, offset: newOffset });
|
|
132
|
+
};
|
|
133
|
+
const handleWaitForBoundary = (ctx, id, params) => {
|
|
134
|
+
const { sessionId, timeoutMs } = params;
|
|
135
|
+
const managed = activeSessions.get(sessionId);
|
|
136
|
+
if (!managed) {
|
|
137
|
+
ctx.sendResponse(id, undefined, `session not found: ${sessionId}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const timeout = typeof timeoutMs === "number" ? timeoutMs : 30000;
|
|
141
|
+
if (managed.lastBoundary) {
|
|
142
|
+
const boundary = managed.lastBoundary;
|
|
143
|
+
managed.lastBoundary = null;
|
|
144
|
+
ctx.sendResponse(id, boundary);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
const handler = (boundary) => {
|
|
148
|
+
managed.session.off("commandBoundary", handler);
|
|
149
|
+
clearTimeout(timer);
|
|
150
|
+
ctx.sendResponse(id, boundary);
|
|
151
|
+
};
|
|
152
|
+
const timer = setTimeout(() => {
|
|
153
|
+
managed.session.off("commandBoundary", handler);
|
|
154
|
+
ctx.sendResponse(id, undefined, "timeout");
|
|
155
|
+
}, timeout);
|
|
156
|
+
timer.unref();
|
|
157
|
+
managed.session.on("commandBoundary", handler);
|
|
158
|
+
};
|
|
159
|
+
const handleExec = (ctx, id, params) => {
|
|
160
|
+
const { sessionId, command, timeoutMs } = params;
|
|
161
|
+
const managed = activeSessions.get(sessionId);
|
|
162
|
+
if (!managed) {
|
|
163
|
+
ctx.sendResponse(id, undefined, `session not found: ${sessionId}`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!command || typeof command !== "string") {
|
|
167
|
+
ctx.sendResponse(id, undefined, "command must be a non-empty string");
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (!ctx.allowDestructiveCommands) {
|
|
171
|
+
const denied = isCommandDenied(command);
|
|
172
|
+
if (denied) {
|
|
173
|
+
ctx.sendResponse(id, undefined, `command denied by allowlist (matched pattern: "${denied}"). pass --yolo to localterm start to disable this check.`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (managed.session.isExited) {
|
|
178
|
+
ctx.sendResponse(id, undefined, "session has exited");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const timeout = typeof timeoutMs === "number" ? timeoutMs : 60000;
|
|
182
|
+
managed.session.write(command + "\n");
|
|
183
|
+
const handler = (boundary) => {
|
|
184
|
+
if (boundary.phase !== "command-end")
|
|
185
|
+
return;
|
|
186
|
+
managed.session.off("commandBoundary", handler);
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
const text = managed.outputBuffer.join("");
|
|
189
|
+
managed.outputBuffer = [];
|
|
190
|
+
ctx.sendResponse(id, {
|
|
191
|
+
stdoutText: text,
|
|
192
|
+
exitCode: boundary.exitCode ?? 0,
|
|
193
|
+
});
|
|
194
|
+
};
|
|
195
|
+
const timer = setTimeout(() => {
|
|
196
|
+
managed.session.off("commandBoundary", handler);
|
|
197
|
+
const text = managed.outputBuffer.join("");
|
|
198
|
+
managed.outputBuffer = [];
|
|
199
|
+
ctx.sendResponse(id, {
|
|
200
|
+
stdoutText: text,
|
|
201
|
+
exitCode: null,
|
|
202
|
+
partial: true,
|
|
203
|
+
});
|
|
204
|
+
}, timeout);
|
|
205
|
+
timer.unref();
|
|
206
|
+
managed.session.on("commandBoundary", handler);
|
|
207
|
+
};
|
|
208
|
+
/**
|
|
209
|
+
* Map of session ID → ManagedSession for all sessions created via RPC.
|
|
210
|
+
* These are separate from the human-connection sessions managed by the registry.
|
|
211
|
+
*/
|
|
212
|
+
export const activeSessions = new Map();
|
|
213
|
+
//# sourceMappingURL=agent-rpc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-rpc.js","sourceRoot":"","sources":["../src/agent-rpc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load the agent token from ~/.localterm/agent-token.
|
|
3
|
+
* If the file doesn't exist, generate a new token, write it with 0600 perms,
|
|
4
|
+
* and return it. This ensures every `localterm start` gets a fresh token
|
|
5
|
+
* and the file persists across the daemon's lifetime.
|
|
6
|
+
*
|
|
7
|
+
* Token is used for authenticating agent-mode WebSocket connections.
|
|
8
|
+
* Human-mode connections (default) do not require a token.
|
|
9
|
+
*/
|
|
10
|
+
export declare const loadOrCreateAgentToken: () => string;
|
|
11
|
+
/**
|
|
12
|
+
* Validate a candidate token against the stored agent token.
|
|
13
|
+
* Returns true if the token matches (constant-time comparison not required —
|
|
14
|
+
* this is a local-only security boundary; timing attacks assume network access).
|
|
15
|
+
*/
|
|
16
|
+
export declare const validateAgentToken: (candidate: string) => boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get the path to the agent token file (for CLI display / docs).
|
|
19
|
+
*/
|
|
20
|
+
export declare const getAgentTokenPath: () => string;
|
|
21
|
+
//# sourceMappingURL=agent-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-token.d.ts","sourceRoot":"","sources":["../src/agent-token.ts"],"names":[],"mappings":"AAgBA;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,QAAO,MAWzC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,GAAI,WAAW,MAAM,KAAG,OAKtD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,QAAO,MAAoB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
const LOCALTERM_DIR = join(homedir(), ".localterm");
|
|
6
|
+
const TOKEN_FILE = join(LOCALTERM_DIR, "agent-token");
|
|
7
|
+
const TOKEN_BYTES = 32; // 256-bit token
|
|
8
|
+
/**
|
|
9
|
+
* Generate a cryptographically random hex token.
|
|
10
|
+
*/
|
|
11
|
+
const generateToken = () => {
|
|
12
|
+
return randomBytes(TOKEN_BYTES).toString("hex");
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Load the agent token from ~/.localterm/agent-token.
|
|
16
|
+
* If the file doesn't exist, generate a new token, write it with 0600 perms,
|
|
17
|
+
* and return it. This ensures every `localterm start` gets a fresh token
|
|
18
|
+
* and the file persists across the daemon's lifetime.
|
|
19
|
+
*
|
|
20
|
+
* Token is used for authenticating agent-mode WebSocket connections.
|
|
21
|
+
* Human-mode connections (default) do not require a token.
|
|
22
|
+
*/
|
|
23
|
+
export const loadOrCreateAgentToken = () => {
|
|
24
|
+
if (existsSync(TOKEN_FILE)) {
|
|
25
|
+
const raw = readFileSync(TOKEN_FILE, "utf-8").trim();
|
|
26
|
+
if (raw.length > 0)
|
|
27
|
+
return raw;
|
|
28
|
+
}
|
|
29
|
+
const token = generateToken();
|
|
30
|
+
if (!existsSync(LOCALTERM_DIR)) {
|
|
31
|
+
mkdirSync(LOCALTERM_DIR, { recursive: true, mode: 0o755 });
|
|
32
|
+
}
|
|
33
|
+
writeFileSync(TOKEN_FILE, token + "\n", { mode: 0o600, encoding: "utf-8" });
|
|
34
|
+
return token;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Validate a candidate token against the stored agent token.
|
|
38
|
+
* Returns true if the token matches (constant-time comparison not required —
|
|
39
|
+
* this is a local-only security boundary; timing attacks assume network access).
|
|
40
|
+
*/
|
|
41
|
+
export const validateAgentToken = (candidate) => {
|
|
42
|
+
if (!existsSync(TOKEN_FILE))
|
|
43
|
+
return false;
|
|
44
|
+
const stored = readFileSync(TOKEN_FILE, "utf-8").trim();
|
|
45
|
+
if (!stored)
|
|
46
|
+
return false;
|
|
47
|
+
return candidate === stored;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Get the path to the agent token file (for CLI display / docs).
|
|
51
|
+
*/
|
|
52
|
+
export const getAgentTokenPath = () => TOKEN_FILE;
|
|
53
|
+
//# sourceMappingURL=agent-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-token.js","sourceRoot":"","sources":["../src/agent-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;AACpD,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;AACtD,MAAM,WAAW,GAAG,EAAE,CAAC,CAAC,gBAAgB;AAExC;;GAEG;AACH,MAAM,aAAa,GAAG,GAAW,EAAE;IACjC,OAAO,WAAW,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAW,EAAE;IACjD,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACrD,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;IACjC,CAAC;IACD,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAC9B,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,SAAS,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,aAAa,CAAC,UAAU,EAAE,KAAK,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5E,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,SAAiB,EAAW,EAAE;IAC/D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;IACxD,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,OAAO,SAAS,KAAK,MAAM,CAAC;AAC9B,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAW,EAAE,CAAC,UAAU,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export declare const DEFAULT_PORT = 3417;
|
|
2
|
+
export declare const DEFAULT_HOST = "127.0.0.1";
|
|
3
|
+
export declare const DEFAULT_COLS = 120;
|
|
4
|
+
export declare const DEFAULT_ROWS = 32;
|
|
5
|
+
export declare const DEFAULT_SHELL_FALLBACK = "/bin/sh";
|
|
6
|
+
export declare const TERM_TYPE = "xterm-256color";
|
|
7
|
+
export declare const COLORTERM_VALUE = "truecolor";
|
|
8
|
+
export declare const TITLE_POLL_INTERVAL_MS = 500;
|
|
9
|
+
export declare const CWD_RESOLVE_TIMEOUT_MS = 250;
|
|
10
|
+
export declare const CWD_RESOLVE_BACKOFF_MS = 30000;
|
|
11
|
+
export declare const CWD_RESOLVE_COOLDOWN_MS = 5000;
|
|
12
|
+
export declare const MAX_PENDING_PARSE_BYTES: number;
|
|
13
|
+
export declare const TITLE_MAX_PATH_SEGMENTS = 3;
|
|
14
|
+
export declare const TITLE_TRUNCATION_PREFIX = "\u2026";
|
|
15
|
+
/**
|
|
16
|
+
* Strip terminal-emulator identity env vars inherited from the daemon's parent.
|
|
17
|
+
* If we leak e.g. TERM_PROGRAM=ghostty, modern Ink-based TUIs (Cursor Agent,
|
|
18
|
+
* Claude Code) will probe for that terminal's protocol (kitty keyboard,
|
|
19
|
+
* XTQVERSION, XTGETTCAP, OSC 1337, etc.) and — when xterm.js doesn't answer —
|
|
20
|
+
* fall back to a degraded inline-plain rendering instead of the full boxed
|
|
21
|
+
* TUI. Removing these lets the TUI treat us as a generic xterm-256color and
|
|
22
|
+
* render normally.
|
|
23
|
+
*/
|
|
24
|
+
export declare const PTY_ENV_DENYLIST: string[];
|
|
25
|
+
export declare const MAX_INPUT_BYTES: number;
|
|
26
|
+
export declare const MAX_OUTPUT_BYTES: number;
|
|
27
|
+
export declare const MAX_TITLE_LENGTH: number;
|
|
28
|
+
export declare const MAX_COLS = 1000;
|
|
29
|
+
export declare const MAX_ROWS = 1000;
|
|
30
|
+
export declare const MAX_CONCURRENT_SESSIONS = 64;
|
|
31
|
+
export declare const OUTPUT_BATCH_WINDOW_MS = 2;
|
|
32
|
+
export declare const OUTPUT_BATCH_MAX_BYTES: number;
|
|
33
|
+
export declare const WS_OUTBOUND_PAUSE_HIGH_WATER_BYTES: number;
|
|
34
|
+
export declare const WS_OUTBOUND_RESUME_LOW_WATER_BYTES: number;
|
|
35
|
+
export declare const WS_OUTBOUND_DRAIN_POLL_MS = 50;
|
|
36
|
+
export declare const WS_BACKPRESSURE_THRESHOLD_BYTES: number;
|
|
37
|
+
export declare const WS_HEARTBEAT_INTERVAL_MS = 20000;
|
|
38
|
+
export declare const WS_HEARTBEAT_TIMEOUT_MS = 60000;
|
|
39
|
+
export declare const WS_HEARTBEAT_GRACE_MS = 15000;
|
|
40
|
+
export declare const SERVER_STOP_GRACE_MS = 1500;
|
|
41
|
+
export declare const LOOPBACK_HOSTS: Set<string>;
|
|
42
|
+
export declare const HTTP_STATUS_NOT_FOUND = 404;
|
|
43
|
+
export declare const WS_READY_STATE_OPEN = 1;
|
|
44
|
+
export declare const WS_CLOSE_POLICY_VIOLATION = 1008;
|
|
45
|
+
export declare const WS_CLOSE_BACKPRESSURE = 4429;
|
|
46
|
+
export declare const WS_CLOSE_CAPACITY_REACHED = 4503;
|
|
47
|
+
/**
|
|
48
|
+
* Commands that are denied by default in agent-mode exec() unless the
|
|
49
|
+
* --yolo flag is passed at server start. Matching is substring-based so
|
|
50
|
+
* "rm -rf /" is caught by "rm". This is a safety net, not a security
|
|
51
|
+
* boundary — the loopback-only binding is the real security model.
|
|
52
|
+
*/
|
|
53
|
+
export declare const AGENT_COMMAND_DENYLIST: string[];
|
|
54
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,OAAO,CAAC;AACjC,eAAO,MAAM,YAAY,cAAc,CAAC;AACxC,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,sBAAsB,YAAY,CAAC;AAEhD,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAC1C,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAC7C,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAC7C,eAAO,MAAM,uBAAuB,QAAW,CAAC;AAChD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,uBAAuB,WAAM,CAAC;AAE3C;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,UAe5B,CAAC;AAEF,eAAO,MAAM,eAAe,QAAY,CAAC;AACzC,eAAO,MAAM,gBAAgB,QAAkB,CAAC;AAChD,eAAO,MAAM,gBAAgB,QAAW,CAAC;AACzC,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,QAAQ,OAAO,CAAC;AAC7B,eAAO,MAAM,uBAAuB,KAAK,CAAC;AAC1C,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,sBAAsB,QAAY,CAAC;AAOhD,eAAO,MAAM,kCAAkC,QAAkB,CAAC;AAClE,eAAO,MAAM,kCAAkC,QAAkB,CAAC;AAClE,eAAO,MAAM,yBAAyB,KAAK,CAAC;AAC5C,eAAO,MAAM,+BAA+B,QAAmB,CAAC;AAOhE,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAC/C,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAC9C,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAK5C,eAAO,MAAM,oBAAoB,OAAQ,CAAC;AAE1C,eAAO,MAAM,cAAc,aAOzB,CAAC;AAEH,eAAO,MAAM,qBAAqB,MAAM,CAAC;AAEzC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,UAelC,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
export const DEFAULT_PORT = 3417;
|
|
2
|
+
export const DEFAULT_HOST = "127.0.0.1";
|
|
3
|
+
export const DEFAULT_COLS = 120;
|
|
4
|
+
export const DEFAULT_ROWS = 32;
|
|
5
|
+
export const DEFAULT_SHELL_FALLBACK = "/bin/sh";
|
|
6
|
+
export const TERM_TYPE = "xterm-256color";
|
|
7
|
+
export const COLORTERM_VALUE = "truecolor";
|
|
8
|
+
export const TITLE_POLL_INTERVAL_MS = 500;
|
|
9
|
+
export const CWD_RESOLVE_TIMEOUT_MS = 250;
|
|
10
|
+
export const CWD_RESOLVE_BACKOFF_MS = 30_000;
|
|
11
|
+
export const CWD_RESOLVE_COOLDOWN_MS = 5_000;
|
|
12
|
+
export const MAX_PENDING_PARSE_BYTES = 4 * 1024;
|
|
13
|
+
export const TITLE_MAX_PATH_SEGMENTS = 3;
|
|
14
|
+
export const TITLE_TRUNCATION_PREFIX = "…";
|
|
15
|
+
/**
|
|
16
|
+
* Strip terminal-emulator identity env vars inherited from the daemon's parent.
|
|
17
|
+
* If we leak e.g. TERM_PROGRAM=ghostty, modern Ink-based TUIs (Cursor Agent,
|
|
18
|
+
* Claude Code) will probe for that terminal's protocol (kitty keyboard,
|
|
19
|
+
* XTQVERSION, XTGETTCAP, OSC 1337, etc.) and — when xterm.js doesn't answer —
|
|
20
|
+
* fall back to a degraded inline-plain rendering instead of the full boxed
|
|
21
|
+
* TUI. Removing these lets the TUI treat us as a generic xterm-256color and
|
|
22
|
+
* render normally.
|
|
23
|
+
*/
|
|
24
|
+
export const PTY_ENV_DENYLIST = [
|
|
25
|
+
"LOCALTERM_DAEMON_CHILD",
|
|
26
|
+
"TERM_PROGRAM",
|
|
27
|
+
"TERM_PROGRAM_VERSION",
|
|
28
|
+
"TERM_SESSION_ID",
|
|
29
|
+
"ITERM_SESSION_ID",
|
|
30
|
+
"ITERM_PROFILE",
|
|
31
|
+
"KITTY_WINDOW_ID",
|
|
32
|
+
"KITTY_PID",
|
|
33
|
+
"WT_SESSION",
|
|
34
|
+
"WT_PROFILE_ID",
|
|
35
|
+
"GHOSTTY_RESOURCES_DIR",
|
|
36
|
+
"GHOSTTY_BIN_DIR",
|
|
37
|
+
"VSCODE_INJECTION",
|
|
38
|
+
"VSCODE_GIT_IPC_HANDLE",
|
|
39
|
+
];
|
|
40
|
+
export const MAX_INPUT_BYTES = 64 * 1024;
|
|
41
|
+
export const MAX_OUTPUT_BYTES = 1 * 1024 * 1024;
|
|
42
|
+
export const MAX_TITLE_LENGTH = 4 * 1024;
|
|
43
|
+
export const MAX_COLS = 1000;
|
|
44
|
+
export const MAX_ROWS = 1000;
|
|
45
|
+
export const MAX_CONCURRENT_SESSIONS = 64;
|
|
46
|
+
export const OUTPUT_BATCH_WINDOW_MS = 2;
|
|
47
|
+
export const OUTPUT_BATCH_MAX_BYTES = 64 * 1024;
|
|
48
|
+
// High/low water marks gate the PTY -> WS pipe instead of killing the socket.
|
|
49
|
+
// Crossing the high water mark pauses the PTY so the OS pipe absorbs further
|
|
50
|
+
// child output until the WS drains below the low water mark, at which point
|
|
51
|
+
// we resume. Anything past WS_BACKPRESSURE_THRESHOLD_BYTES is treated as a
|
|
52
|
+
// runaway: the WS *did* drain below low water at some point, the receiver is
|
|
53
|
+
// genuinely stuck, and we'd rather drop the connection than balloon memory.
|
|
54
|
+
export const WS_OUTBOUND_PAUSE_HIGH_WATER_BYTES = 4 * 1024 * 1024;
|
|
55
|
+
export const WS_OUTBOUND_RESUME_LOW_WATER_BYTES = 1 * 1024 * 1024;
|
|
56
|
+
export const WS_OUTBOUND_DRAIN_POLL_MS = 50;
|
|
57
|
+
export const WS_BACKPRESSURE_THRESHOLD_BYTES = 64 * 1024 * 1024;
|
|
58
|
+
// Heartbeat: send a WS ping every N ms; if no pong arrives within the timeout
|
|
59
|
+
// we tear down the socket. Without this, half-open connections (laptop sleep,
|
|
60
|
+
// VPN dropout, kernel‑side TCP keepalives at 2h+) wedge the session — the
|
|
61
|
+
// server keeps a dead PTY tied to a phantom client and the browser eventually
|
|
62
|
+
// trips its own write error and shows "Shell ended" with no idea why.
|
|
63
|
+
export const WS_HEARTBEAT_INTERVAL_MS = 20_000;
|
|
64
|
+
export const WS_HEARTBEAT_TIMEOUT_MS = 60_000;
|
|
65
|
+
export const WS_HEARTBEAT_GRACE_MS = 15_000;
|
|
66
|
+
// Hard ceiling for server.stop() — clients get terminated, then the http
|
|
67
|
+
// server is given this long to actually close before we resolve anyway. Keeps
|
|
68
|
+
// the daemon's SIGTERM path bounded so the CLI's force-exit fallback never
|
|
69
|
+
// fires for normal shutdowns.
|
|
70
|
+
export const SERVER_STOP_GRACE_MS = 1_500;
|
|
71
|
+
export const LOOPBACK_HOSTS = new Set([
|
|
72
|
+
"127.0.0.1",
|
|
73
|
+
"localhost",
|
|
74
|
+
"::1",
|
|
75
|
+
"[::1]",
|
|
76
|
+
"0:0:0:0:0:0:0:1",
|
|
77
|
+
"[0:0:0:0:0:0:0:1]",
|
|
78
|
+
]);
|
|
79
|
+
export const HTTP_STATUS_NOT_FOUND = 404;
|
|
80
|
+
export const WS_READY_STATE_OPEN = 1;
|
|
81
|
+
export const WS_CLOSE_POLICY_VIOLATION = 1008;
|
|
82
|
+
export const WS_CLOSE_BACKPRESSURE = 4429;
|
|
83
|
+
export const WS_CLOSE_CAPACITY_REACHED = 4503;
|
|
84
|
+
/**
|
|
85
|
+
* Commands that are denied by default in agent-mode exec() unless the
|
|
86
|
+
* --yolo flag is passed at server start. Matching is substring-based so
|
|
87
|
+
* "rm -rf /" is caught by "rm". This is a safety net, not a security
|
|
88
|
+
* boundary — the loopback-only binding is the real security model.
|
|
89
|
+
*/
|
|
90
|
+
export const AGENT_COMMAND_DENYLIST = [
|
|
91
|
+
"rm",
|
|
92
|
+
"dd",
|
|
93
|
+
"mkfs",
|
|
94
|
+
"mkfs.",
|
|
95
|
+
"fdisk",
|
|
96
|
+
"parted",
|
|
97
|
+
"format",
|
|
98
|
+
"shred",
|
|
99
|
+
"wipefs",
|
|
100
|
+
"blockdev",
|
|
101
|
+
"> /dev/",
|
|
102
|
+
"> /dev/sd",
|
|
103
|
+
"> /dev/nvme",
|
|
104
|
+
":(){ :|:& };:", // fork bomb
|
|
105
|
+
];
|
|
106
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-shell.d.ts","sourceRoot":"","sources":["../src/default-shell.ts"],"names":[],"mappings":"AAaA,eAAO,MAAM,eAAe,QAAO,MAyBlC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { accessSync, constants as fsConstants } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import { DEFAULT_SHELL_FALLBACK } from "./constants.js";
|
|
4
|
+
const isExecutable = (binaryPath) => {
|
|
5
|
+
try {
|
|
6
|
+
accessSync(binaryPath, fsConstants.X_OK);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
export const getDefaultShell = () => {
|
|
14
|
+
if (process.platform === "win32") {
|
|
15
|
+
return process.env.COMSPEC ?? "cmd.exe";
|
|
16
|
+
}
|
|
17
|
+
// Priority order (matches VS Code, Hyper, and Warp):
|
|
18
|
+
// 1. LOCALTERM_SHELL — explicit user override
|
|
19
|
+
// 2. os.userInfo().shell — the user's persistent login shell from passwd / DSCL
|
|
20
|
+
// 3. process.env.SHELL — only as a fallback; this leaks the parent process's
|
|
21
|
+
// shell (often zsh from launchd / a Cursor terminal) and is wrong when the
|
|
22
|
+
// user has changed their default with `chsh`
|
|
23
|
+
// 4. DEFAULT_SHELL_FALLBACK
|
|
24
|
+
const candidates = [];
|
|
25
|
+
if (process.env.LOCALTERM_SHELL)
|
|
26
|
+
candidates.push(process.env.LOCALTERM_SHELL);
|
|
27
|
+
try {
|
|
28
|
+
const userInfo = os.userInfo();
|
|
29
|
+
if (userInfo.shell)
|
|
30
|
+
candidates.push(userInfo.shell);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
/* os.userInfo throws on systems without /etc/passwd entry for the uid */
|
|
34
|
+
}
|
|
35
|
+
if (process.env.SHELL)
|
|
36
|
+
candidates.push(process.env.SHELL);
|
|
37
|
+
candidates.push(DEFAULT_SHELL_FALLBACK);
|
|
38
|
+
for (const candidate of candidates) {
|
|
39
|
+
if (isExecutable(candidate))
|
|
40
|
+
return candidate;
|
|
41
|
+
}
|
|
42
|
+
return DEFAULT_SHELL_FALLBACK;
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=default-shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-shell.js","sourceRoot":"","sources":["../src/default-shell.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,IAAI,WAAW,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAExD,MAAM,YAAY,GAAG,CAAC,UAAkB,EAAW,EAAE;IACnD,IAAI,CAAC;QACH,UAAU,CAAC,UAAU,EAAE,WAAW,CAAC,IAAI,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,eAAe,GAAG,GAAW,EAAE;IAC1C,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,SAAS,CAAC;IAC1C,CAAC;IACD,qDAAqD;IACrD,gDAAgD;IAChD,kFAAkF;IAClF,+EAA+E;IAC/E,gFAAgF;IAChF,kDAAkD;IAClD,8BAA8B;IAC9B,MAAM,UAAU,GAAa,EAAE,CAAC;IAChC,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC9E,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,QAAQ,CAAC,KAAK;YAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK;QAAE,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1D,UAAU,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;IACxC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,YAAY,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;IAChD,CAAC;IACD,OAAO,sBAAsB,CAAC;AAChC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure-spawn-helper-executable.d.ts","sourceRoot":"","sources":["../src/ensure-spawn-helper-executable.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,2BAA2B,QAAO,IAkB9C,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { chmodSync, existsSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const requireCjs = createRequire(import.meta.url);
|
|
5
|
+
const SPAWN_HELPER_MODE = 0o755;
|
|
6
|
+
let alreadyEnsured = false;
|
|
7
|
+
const candidateSpawnHelperPaths = (nodePtyDir) => [
|
|
8
|
+
path.join(nodePtyDir, "build", "Release", "spawn-helper"),
|
|
9
|
+
path.join(nodePtyDir, "prebuilds", `${process.platform}-${process.arch}`, "spawn-helper"),
|
|
10
|
+
];
|
|
11
|
+
export const ensureSpawnHelperExecutable = () => {
|
|
12
|
+
if (alreadyEnsured)
|
|
13
|
+
return;
|
|
14
|
+
alreadyEnsured = true;
|
|
15
|
+
let nodePtyDir;
|
|
16
|
+
try {
|
|
17
|
+
const ptyEntry = requireCjs.resolve("node-pty");
|
|
18
|
+
nodePtyDir = path.dirname(path.dirname(ptyEntry));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
for (const candidate of candidateSpawnHelperPaths(nodePtyDir)) {
|
|
24
|
+
if (!existsSync(candidate))
|
|
25
|
+
continue;
|
|
26
|
+
try {
|
|
27
|
+
chmodSync(candidate, SPAWN_HELPER_MODE);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
/* helper already executable, or filesystem refused chmod (e.g. read-only mount) */
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=ensure-spawn-helper-executable.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensure-spawn-helper-executable.js","sourceRoot":"","sources":["../src/ensure-spawn-helper-executable.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAEhC,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,MAAM,yBAAyB,GAAG,CAAC,UAAkB,EAAY,EAAE,CAAC;IAClE,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,CAAC;IACzD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,cAAc,CAAC;CAC1F,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAS,EAAE;IACpD,IAAI,cAAc;QAAE,OAAO;IAC3B,cAAc,GAAG,IAAI,CAAC;IACtB,IAAI,UAAkB,CAAC;IACvB,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QAChD,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,KAAK,MAAM,SAAS,IAAI,yBAAyB,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9D,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,SAAS;QACrC,IAAI,CAAC;YACH,SAAS,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,mFAAmF;QACrF,CAAC;IACH,CAAC;AACH,CAAC,CAAC"}
|