@jini-ai/platform 0.1.0
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 +201 -0
- package/dist/asset-cache.d.ts +80 -0
- package/dist/asset-cache.d.ts.map +1 -0
- package/dist/asset-cache.js +423 -0
- package/dist/asset-cache.js.map +1 -0
- package/dist/aws-sigv4.d.ts +51 -0
- package/dist/aws-sigv4.d.ts.map +1 -0
- package/dist/aws-sigv4.js +104 -0
- package/dist/aws-sigv4.js.map +1 -0
- package/dist/blob-storage.d.ts +89 -0
- package/dist/blob-storage.d.ts.map +1 -0
- package/dist/blob-storage.js +315 -0
- package/dist/blob-storage.js.map +1 -0
- package/dist/command.d.ts +32 -0
- package/dist/command.d.ts.map +1 -0
- package/dist/command.js +101 -0
- package/dist/command.js.map +1 -0
- package/dist/download.d.ts +92 -0
- package/dist/download.d.ts.map +1 -0
- package/dist/download.js +856 -0
- package/dist/download.js.map +1 -0
- package/dist/fs.d.ts +55 -0
- package/dist/fs.d.ts.map +1 -0
- package/dist/fs.js +124 -0
- package/dist/fs.js.map +1 -0
- package/dist/home-expansion.d.ts +18 -0
- package/dist/home-expansion.d.ts.map +1 -0
- package/dist/home-expansion.js +58 -0
- package/dist/home-expansion.js.map +1 -0
- package/dist/http.d.ts +14 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +39 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +45 -0
- package/dist/index.js.map +1 -0
- package/dist/process.d.ts +159 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +362 -0
- package/dist/process.js.map +1 -0
- package/dist/proxy-env.d.ts +50 -0
- package/dist/proxy-env.d.ts.map +1 -0
- package/dist/proxy-env.js +373 -0
- package/dist/proxy-env.js.map +1 -0
- package/dist/resource-paths.d.ts +68 -0
- package/dist/resource-paths.d.ts.map +1 -0
- package/dist/resource-paths.js +137 -0
- package/dist/resource-paths.js.map +1 -0
- package/dist/sandbox-env.d.ts +86 -0
- package/dist/sandbox-env.d.ts.map +1 -0
- package/dist/sandbox-env.js +215 -0
- package/dist/sandbox-env.js.map +1 -0
- package/dist/shell.d.ts +46 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +78 -0
- package/dist/shell.js.map +1 -0
- package/dist/terminal.d.ts +137 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +398 -0
- package/dist/terminal.js.map +1 -0
- package/dist/toolchain.d.ts +17 -0
- package/dist/toolchain.d.ts.map +1 -0
- package/dist/toolchain.js +251 -0
- package/dist/toolchain.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names the environment variables and on-disk naming this port reads/writes.
|
|
3
|
+
* A consumer supplies its own env-var names and config-directory name — see
|
|
4
|
+
* `source-map.md` for the values a real adapter (e.g. the OD adapter) uses.
|
|
5
|
+
*/
|
|
6
|
+
export interface SandboxEnvConfig {
|
|
7
|
+
/** Env var gating whether sandboxing is on (truthy/falsy string). */
|
|
8
|
+
modeEnvVar: string;
|
|
9
|
+
/** Env var carrying a PATH-delimiter-separated list of allowed import roots. */
|
|
10
|
+
importAllowedRootsEnvVar: string;
|
|
11
|
+
/** Env var carrying the sandbox data directory (required when sandboxing is on). */
|
|
12
|
+
dataDirEnvVar: string;
|
|
13
|
+
/** Env var the spawned process reads back for its agent-home directory. */
|
|
14
|
+
agentHomeEnvVar: string;
|
|
15
|
+
/** Env var the spawned process reads back for its local agent-profiles config path. */
|
|
16
|
+
agentProfilesConfigEnvVar: string;
|
|
17
|
+
/** Sub-directory name (under the agent home) local agent-profile config lives in. */
|
|
18
|
+
agentProfilesDirName: string;
|
|
19
|
+
}
|
|
20
|
+
export interface SandboxRuntimeRoots {
|
|
21
|
+
agentHomeDir: string;
|
|
22
|
+
cacheDir: string;
|
|
23
|
+
configDir: string;
|
|
24
|
+
generatedFilesDir: string;
|
|
25
|
+
logsDir: string;
|
|
26
|
+
mcpConfigDir: string;
|
|
27
|
+
pluginStateDir: string;
|
|
28
|
+
previewStateDir: string;
|
|
29
|
+
skillsCacheDir: string;
|
|
30
|
+
tempDir: string;
|
|
31
|
+
toolConfigDir: string;
|
|
32
|
+
}
|
|
33
|
+
export interface SandboxRuntimeConfig {
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
dataDir: string;
|
|
36
|
+
roots: SandboxRuntimeRoots;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Reads {@link SandboxEnvConfig.modeEnvVar} and interprets it as a boolean
|
|
40
|
+
* flag. Throws if the value is set but neither a recognized truthy nor
|
|
41
|
+
* falsy spelling.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isSandboxModeEnabled(config: SandboxEnvConfig, env?: Record<string, string | undefined>): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* The configured, canonicalized set of directories an imported project root
|
|
46
|
+
* is allowed to live under while sandboxing is on.
|
|
47
|
+
*/
|
|
48
|
+
export declare function sandboxImportAllowedRoots(config: SandboxEnvConfig, env?: Record<string, string | undefined>): string[];
|
|
49
|
+
/**
|
|
50
|
+
* Whether `projectRoot` may be used as an imported-folder project while
|
|
51
|
+
* sandboxing is enabled. Always `true` when sandboxing is off.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isSandboxImportedProjectRootAllowed(config: SandboxEnvConfig, projectRoot: string, env?: Record<string, string | undefined>): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* A human-readable reason `projectRoot` is unavailable under sandboxing, or
|
|
56
|
+
* `null` when it's allowed (including when sandboxing is off).
|
|
57
|
+
*/
|
|
58
|
+
export declare function sandboxImportedProjectRootUnavailableReason(config: SandboxEnvConfig, projectRoot: string, env?: Record<string, string | undefined>): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* Derives the full sandbox directory tree from a data directory. Pure —
|
|
61
|
+
* does not touch the filesystem (see {@link ensureSandboxRuntimeDirs}).
|
|
62
|
+
*/
|
|
63
|
+
export declare function resolveSandboxRuntimeConfig(enabled: boolean, dataDir: string): SandboxRuntimeConfig;
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the sandbox runtime config from environment variables, or `null`
|
|
66
|
+
* when sandboxing is off. Throws when sandboxing is on but the data-dir env
|
|
67
|
+
* var is unset.
|
|
68
|
+
*/
|
|
69
|
+
export declare function resolveSandboxRuntimeConfigFromEnv(config: SandboxEnvConfig, env: Record<string, string | undefined>, projectRoot: string): SandboxRuntimeConfig | null;
|
|
70
|
+
/**
|
|
71
|
+
* The path a spawned agent process's local agent-profiles config lives at,
|
|
72
|
+
* under its sandboxed agent-home directory.
|
|
73
|
+
*/
|
|
74
|
+
export declare function sandboxAgentProfilesConfigPath(config: SandboxEnvConfig, runtime: SandboxRuntimeConfig): string;
|
|
75
|
+
/**
|
|
76
|
+
* Creates every directory in the sandbox tree (idempotent). No-op when
|
|
77
|
+
* sandboxing is disabled.
|
|
78
|
+
*/
|
|
79
|
+
export declare function ensureSandboxRuntimeDirs(runtime: SandboxRuntimeConfig): void;
|
|
80
|
+
/**
|
|
81
|
+
* Overlays sandboxed HOME/XDG/TMPDIR/tool-config env vars onto `baseEnv` for
|
|
82
|
+
* a spawned agent process. Returns `baseEnv` unchanged when sandboxing is
|
|
83
|
+
* disabled.
|
|
84
|
+
*/
|
|
85
|
+
export declare function applySandboxRuntimeEnv(config: SandboxEnvConfig, baseEnv: NodeJS.ProcessEnv, runtime: SandboxRuntimeConfig): NodeJS.ProcessEnv;
|
|
86
|
+
//# sourceMappingURL=sandbox-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-env.d.ts","sourceRoot":"","sources":["../src/sandbox-env.ts"],"names":[],"mappings":"AAmBA;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,wBAAwB,EAAE,MAAM,CAAC;IACjC,oFAAoF;IACpF,aAAa,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;IACxB,uFAAuF;IACvF,yBAAyB,EAAE,MAAM,CAAC;IAClC,qFAAqF;IACrF,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,mBAAmB,CAAC;CAC5B;AAKD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,OAAO,CAUT;AAkED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,gBAAgB,EACxB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,MAAM,EAAE,CAEV;AAED;;;GAGG;AACH,wBAAgB,mCAAmC,CACjD,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,OAAO,CAIT;AAED;;;GAGG;AACH,wBAAgB,2CAA2C,CACzD,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,MAAM,GAAG,IAAI,CAMf;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,oBAAoB,CAmBnG;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,gBAAgB,EACxB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,WAAW,EAAE,MAAM,GAClB,oBAAoB,GAAG,IAAI,CAO7B;AAED;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAER;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAK5E;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,gBAAgB,EACxB,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,OAAO,EAAE,oBAAoB,GAC5B,MAAM,CAAC,UAAU,CA8BnB"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module sandbox-env
|
|
3
|
+
*
|
|
4
|
+
* Sandboxed agent-execution environment: an isolated directory tree (agent
|
|
5
|
+
* home, cache, config, temp, …) plus the environment-variable overrides that
|
|
6
|
+
* redirect a spawned agent process's HOME/XDG/TMPDIR into it, and a policy
|
|
7
|
+
* gate for whether an imported (non-managed) project root may be used while
|
|
8
|
+
* sandboxing is on.
|
|
9
|
+
*
|
|
10
|
+
* Generalized from an upstream flat daemon module: the origin hardcoded its
|
|
11
|
+
* host product's env-var names and a product-branded config sub-directory.
|
|
12
|
+
* Every one of those is now a field on {@link SandboxEnvConfig} so a
|
|
13
|
+
* consumer isn't branded — see `source-map.md` for the exact mapping.
|
|
14
|
+
*/
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { resolveProjectRelativePath } from './home-expansion.js';
|
|
18
|
+
const TRUTHY_VALUES = new Set(['1', 'true', 'yes', 'on']);
|
|
19
|
+
const FALSY_VALUES = new Set(['0', 'false', 'no', 'off', '']);
|
|
20
|
+
/**
|
|
21
|
+
* Reads {@link SandboxEnvConfig.modeEnvVar} and interprets it as a boolean
|
|
22
|
+
* flag. Throws if the value is set but neither a recognized truthy nor
|
|
23
|
+
* falsy spelling.
|
|
24
|
+
*/
|
|
25
|
+
export function isSandboxModeEnabled(config, env = process.env) {
|
|
26
|
+
const raw = env[config.modeEnvVar];
|
|
27
|
+
if (typeof raw !== 'string')
|
|
28
|
+
return false;
|
|
29
|
+
const value = raw.trim().toLowerCase();
|
|
30
|
+
if (TRUTHY_VALUES.has(value))
|
|
31
|
+
return true;
|
|
32
|
+
if (FALSY_VALUES.has(value))
|
|
33
|
+
return false;
|
|
34
|
+
throw new Error(`${config.modeEnvVar} must be one of ${Array.from(TRUTHY_VALUES).join(', ')} ` +
|
|
35
|
+
`or ${Array.from(FALSY_VALUES).join(', ')}`);
|
|
36
|
+
}
|
|
37
|
+
function configuredSandboxImportRoots(config, env) {
|
|
38
|
+
const raw = env[config.importAllowedRootsEnvVar];
|
|
39
|
+
if (typeof raw !== 'string' || !raw.trim())
|
|
40
|
+
return [];
|
|
41
|
+
const roots = raw
|
|
42
|
+
.split(path.delimiter)
|
|
43
|
+
.map((part) => part.trim())
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
const relativeRoot = roots.find((root) => !path.isAbsolute(path.normalize(root)));
|
|
46
|
+
if (relativeRoot) {
|
|
47
|
+
throw new Error(`${config.importAllowedRootsEnvVar} entries must be absolute paths. Got: ${relativeRoot}`);
|
|
48
|
+
}
|
|
49
|
+
return roots;
|
|
50
|
+
}
|
|
51
|
+
function canonicalizePathForContainment(value) {
|
|
52
|
+
const resolved = path.normalize(value);
|
|
53
|
+
try {
|
|
54
|
+
return fs.realpathSync.native(resolved);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return resolved;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// A containment *candidate* (e.g. a project root that hasn't been imported/
|
|
61
|
+
// created yet) may not fully exist on disk, so a bare realpath attempt
|
|
62
|
+
// throws. Unlike `canonicalizePathForContainment` above — used only for the
|
|
63
|
+
// *configured allowed roots*, which are expected to already exist when they
|
|
64
|
+
// matter — this walks up to the nearest existing ancestor, realpaths that,
|
|
65
|
+
// and rejoins the not-yet-existing suffix. Without this, comparing a raw
|
|
66
|
+
// candidate against a realpath'd allowed root misfires whenever the roots
|
|
67
|
+
// sit under a symlinked prefix (macOS's /tmp -> /private/tmp, /var ->
|
|
68
|
+
// /private/var): the candidate would keep the raw prefix while the allowed
|
|
69
|
+
// root was resolved through the symlink, so a legitimately-nested path
|
|
70
|
+
// would look like it climbs above the root.
|
|
71
|
+
function canonicalizeContainmentCandidate(value) {
|
|
72
|
+
const resolved = path.normalize(value);
|
|
73
|
+
let existing = resolved;
|
|
74
|
+
const trailingSegments = [];
|
|
75
|
+
for (;;) {
|
|
76
|
+
try {
|
|
77
|
+
return path.join(fs.realpathSync.native(existing), ...trailingSegments);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// `path.dirname(x) === x` is the fixed point for both an absolute
|
|
81
|
+
// filesystem root ('/') and a relative reference ('.') — walking
|
|
82
|
+
// further would just retry the same path forever, so bail out to the
|
|
83
|
+
// raw normalized value instead of looping.
|
|
84
|
+
const parent = path.dirname(existing);
|
|
85
|
+
if (parent === existing)
|
|
86
|
+
return resolved;
|
|
87
|
+
trailingSegments.unshift(path.basename(existing));
|
|
88
|
+
existing = parent;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function isPathInsideDir(root, candidate) {
|
|
93
|
+
const relative = path.relative(root, candidate);
|
|
94
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The configured, canonicalized set of directories an imported project root
|
|
98
|
+
* is allowed to live under while sandboxing is on.
|
|
99
|
+
*/
|
|
100
|
+
export function sandboxImportAllowedRoots(config, env = process.env) {
|
|
101
|
+
return configuredSandboxImportRoots(config, env).map(canonicalizePathForContainment);
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Whether `projectRoot` may be used as an imported-folder project while
|
|
105
|
+
* sandboxing is enabled. Always `true` when sandboxing is off.
|
|
106
|
+
*/
|
|
107
|
+
export function isSandboxImportedProjectRootAllowed(config, projectRoot, env = process.env) {
|
|
108
|
+
if (!isSandboxModeEnabled(config, env))
|
|
109
|
+
return true;
|
|
110
|
+
const candidate = canonicalizeContainmentCandidate(projectRoot);
|
|
111
|
+
return sandboxImportAllowedRoots(config, env).some((root) => isPathInsideDir(root, candidate));
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* A human-readable reason `projectRoot` is unavailable under sandboxing, or
|
|
115
|
+
* `null` when it's allowed (including when sandboxing is off).
|
|
116
|
+
*/
|
|
117
|
+
export function sandboxImportedProjectRootUnavailableReason(config, projectRoot, env = process.env) {
|
|
118
|
+
if (!isSandboxModeEnabled(config, env))
|
|
119
|
+
return null;
|
|
120
|
+
return isSandboxImportedProjectRootAllowed(config, projectRoot, env)
|
|
121
|
+
? null
|
|
122
|
+
: `Imported-folder projects are not available in ${config.modeEnvVar} unless their root is ` +
|
|
123
|
+
`under ${config.importAllowedRootsEnvVar}.`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Derives the full sandbox directory tree from a data directory. Pure —
|
|
127
|
+
* does not touch the filesystem (see {@link ensureSandboxRuntimeDirs}).
|
|
128
|
+
*/
|
|
129
|
+
export function resolveSandboxRuntimeConfig(enabled, dataDir) {
|
|
130
|
+
const sandboxRoot = path.join(dataDir, 'sandbox');
|
|
131
|
+
return {
|
|
132
|
+
enabled,
|
|
133
|
+
dataDir,
|
|
134
|
+
roots: {
|
|
135
|
+
agentHomeDir: path.join(sandboxRoot, 'agent-home'),
|
|
136
|
+
cacheDir: path.join(sandboxRoot, 'cache'),
|
|
137
|
+
configDir: path.join(sandboxRoot, 'config'),
|
|
138
|
+
generatedFilesDir: path.join(dataDir, 'generated-files'),
|
|
139
|
+
logsDir: path.join(dataDir, 'logs'),
|
|
140
|
+
mcpConfigDir: dataDir,
|
|
141
|
+
pluginStateDir: path.join(dataDir, 'plugins'),
|
|
142
|
+
previewStateDir: path.join(dataDir, 'previews'),
|
|
143
|
+
skillsCacheDir: path.join(dataDir, 'skills'),
|
|
144
|
+
tempDir: path.join(sandboxRoot, 'tmp'),
|
|
145
|
+
toolConfigDir: path.join(sandboxRoot, 'tools'),
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Resolves the sandbox runtime config from environment variables, or `null`
|
|
151
|
+
* when sandboxing is off. Throws when sandboxing is on but the data-dir env
|
|
152
|
+
* var is unset.
|
|
153
|
+
*/
|
|
154
|
+
export function resolveSandboxRuntimeConfigFromEnv(config, env, projectRoot) {
|
|
155
|
+
if (!isSandboxModeEnabled(config, env))
|
|
156
|
+
return null;
|
|
157
|
+
const rawDataDir = env[config.dataDirEnvVar]?.trim();
|
|
158
|
+
if (!rawDataDir) {
|
|
159
|
+
throw new Error(`${config.dataDirEnvVar} is required when ${config.modeEnvVar} is enabled`);
|
|
160
|
+
}
|
|
161
|
+
return resolveSandboxRuntimeConfig(true, resolveProjectRelativePath(rawDataDir, projectRoot));
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The path a spawned agent process's local agent-profiles config lives at,
|
|
165
|
+
* under its sandboxed agent-home directory.
|
|
166
|
+
*/
|
|
167
|
+
export function sandboxAgentProfilesConfigPath(config, runtime) {
|
|
168
|
+
return path.join(runtime.roots.agentHomeDir, config.agentProfilesDirName, 'agents.local.json');
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Creates every directory in the sandbox tree (idempotent). No-op when
|
|
172
|
+
* sandboxing is disabled.
|
|
173
|
+
*/
|
|
174
|
+
export function ensureSandboxRuntimeDirs(runtime) {
|
|
175
|
+
if (!runtime.enabled)
|
|
176
|
+
return;
|
|
177
|
+
for (const dir of new Set(Object.values(runtime.roots))) {
|
|
178
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Overlays sandboxed HOME/XDG/TMPDIR/tool-config env vars onto `baseEnv` for
|
|
183
|
+
* a spawned agent process. Returns `baseEnv` unchanged when sandboxing is
|
|
184
|
+
* disabled.
|
|
185
|
+
*/
|
|
186
|
+
export function applySandboxRuntimeEnv(config, baseEnv, runtime) {
|
|
187
|
+
if (!runtime.enabled)
|
|
188
|
+
return baseEnv;
|
|
189
|
+
const env = { ...baseEnv };
|
|
190
|
+
const { roots } = runtime;
|
|
191
|
+
const codexHome = path.join(roots.agentHomeDir, '.codex');
|
|
192
|
+
const claudeConfigDir = path.join(roots.configDir, 'claude');
|
|
193
|
+
const opencodeHome = path.join(roots.agentHomeDir, '.opencode');
|
|
194
|
+
const npmUserConfig = path.join(roots.toolConfigDir, 'npmrc');
|
|
195
|
+
env[config.modeEnvVar] = '1';
|
|
196
|
+
env[config.dataDirEnvVar] = runtime.dataDir;
|
|
197
|
+
env[config.agentHomeEnvVar] = roots.agentHomeDir;
|
|
198
|
+
env.HOME = roots.agentHomeDir;
|
|
199
|
+
env.USERPROFILE = roots.agentHomeDir;
|
|
200
|
+
env.XDG_CONFIG_HOME = roots.configDir;
|
|
201
|
+
env.XDG_CACHE_HOME = roots.cacheDir;
|
|
202
|
+
env.XDG_DATA_HOME = path.join(roots.configDir, 'data');
|
|
203
|
+
env.XDG_STATE_HOME = path.join(roots.configDir, 'state');
|
|
204
|
+
env.TMPDIR = roots.tempDir;
|
|
205
|
+
env.TEMP = roots.tempDir;
|
|
206
|
+
env.TMP = roots.tempDir;
|
|
207
|
+
env.CODEX_HOME = codexHome;
|
|
208
|
+
env.CLAUDE_CONFIG_DIR = claudeConfigDir;
|
|
209
|
+
env.OPENCODE_TEST_HOME = opencodeHome;
|
|
210
|
+
env[config.agentProfilesConfigEnvVar] = sandboxAgentProfilesConfigPath(config, runtime);
|
|
211
|
+
env.NPM_CONFIG_USERCONFIG = npmUserConfig;
|
|
212
|
+
env.npm_config_userconfig = npmUserConfig;
|
|
213
|
+
return env;
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=sandbox-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox-env.js","sourceRoot":"","sources":["../src/sandbox-env.ts"],"names":[],"mappings":"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"}
|
package/dist/shell.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module shell
|
|
3
|
+
*
|
|
4
|
+
* Buffered command execution that re-enters the user's login shell. A daemon
|
|
5
|
+
* process's own `PATH` frequently lacks entries a shell profile adds (a
|
|
6
|
+
* version manager's shims, a package-manager-installed CLI, a Homebrew
|
|
7
|
+
* prefix), so a plain `execFile` against a bare command name can fail even
|
|
8
|
+
* though the same command works fine in the user's terminal. Routing through
|
|
9
|
+
* `$SHELL -c "..."` re-sources that PATH before running the command.
|
|
10
|
+
*
|
|
11
|
+
* Depends on nothing else in this package; owns no process lifecycle beyond
|
|
12
|
+
* the single buffered child it spawns per call.
|
|
13
|
+
*/
|
|
14
|
+
import { type ExecFileOptions } from "node:child_process";
|
|
15
|
+
export type BufferedCommandResult = {
|
|
16
|
+
code: string | number | null | undefined;
|
|
17
|
+
error: Error | null;
|
|
18
|
+
ok: boolean;
|
|
19
|
+
stderr: string;
|
|
20
|
+
stdout: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Run a command directly via `execFile`, buffering stdout/stderr and never
|
|
24
|
+
* rejecting — failures are reported through the returned `ok`/`error` fields.
|
|
25
|
+
*
|
|
26
|
+
* @param command - The executable to run.
|
|
27
|
+
* @param args - Arguments to pass to the executable.
|
|
28
|
+
* @param opts - `child_process.execFile` options (a 120s timeout and 1MiB
|
|
29
|
+
* buffer apply unless overridden).
|
|
30
|
+
* @returns The buffered result.
|
|
31
|
+
*/
|
|
32
|
+
export declare function execFileBuffered(command: string, args: readonly string[], opts?: ExecFileOptions): Promise<BufferedCommandResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Run an arbitrary command re-entering the user's login shell (via
|
|
35
|
+
* `$SHELL -c`) so shell-profile-only `PATH` entries are visible, then buffer
|
|
36
|
+
* its output. Falls back to a direct `execFile` on Windows, where there is no
|
|
37
|
+
* equivalent login-shell PATH gap.
|
|
38
|
+
*
|
|
39
|
+
* @param command - The executable to run.
|
|
40
|
+
* @param args - Arguments to pass to the executable.
|
|
41
|
+
* @param opts - `child_process.execFile` options; `env` defaults to
|
|
42
|
+
* `process.env` so the re-exported `PATH` above has a base to restore from.
|
|
43
|
+
* @returns The buffered result.
|
|
44
|
+
*/
|
|
45
|
+
export declare function execCommandViaLoginShell(command: string, args: readonly string[], opts?: ExecFileOptions): Promise<BufferedCommandResult>;
|
|
46
|
+
//# sourceMappingURL=shell.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.d.ts","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAY,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAEpE,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,qBAAqB,CAAC,CAYhC;AAqBD;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,qBAAqB,CAAC,CAUhC"}
|
package/dist/shell.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module shell
|
|
3
|
+
*
|
|
4
|
+
* Buffered command execution that re-enters the user's login shell. A daemon
|
|
5
|
+
* process's own `PATH` frequently lacks entries a shell profile adds (a
|
|
6
|
+
* version manager's shims, a package-manager-installed CLI, a Homebrew
|
|
7
|
+
* prefix), so a plain `execFile` against a bare command name can fail even
|
|
8
|
+
* though the same command works fine in the user's terminal. Routing through
|
|
9
|
+
* `$SHELL -c "..."` re-sources that PATH before running the command.
|
|
10
|
+
*
|
|
11
|
+
* Depends on nothing else in this package; owns no process lifecycle beyond
|
|
12
|
+
* the single buffered child it spawns per call.
|
|
13
|
+
*/
|
|
14
|
+
import { execFile } from "node:child_process";
|
|
15
|
+
/**
|
|
16
|
+
* Run a command directly via `execFile`, buffering stdout/stderr and never
|
|
17
|
+
* rejecting — failures are reported through the returned `ok`/`error` fields.
|
|
18
|
+
*
|
|
19
|
+
* @param command - The executable to run.
|
|
20
|
+
* @param args - Arguments to pass to the executable.
|
|
21
|
+
* @param opts - `child_process.execFile` options (a 120s timeout and 1MiB
|
|
22
|
+
* buffer apply unless overridden).
|
|
23
|
+
* @returns The buffered result.
|
|
24
|
+
*/
|
|
25
|
+
export function execFileBuffered(command, args, opts = {}) {
|
|
26
|
+
return new Promise((resolve) => {
|
|
27
|
+
execFile(command, [...args], { timeout: 120_000, maxBuffer: 1024 * 1024, ...opts }, (error, stdout, stderr) => {
|
|
28
|
+
resolve({
|
|
29
|
+
code: error && "code" in error ? error.code : undefined,
|
|
30
|
+
error,
|
|
31
|
+
ok: !error,
|
|
32
|
+
stderr: String(stderr).trim(),
|
|
33
|
+
stdout: String(stdout).trim(),
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** @internal Single-quote a POSIX shell argument, escaping embedded single quotes. */
|
|
39
|
+
function quotePosixShellArg(value) {
|
|
40
|
+
const text = String(value ?? "");
|
|
41
|
+
return `'${text.replace(/'/g, `'\\''`)}'`;
|
|
42
|
+
}
|
|
43
|
+
/** @internal Join a command and its arguments into one quoted POSIX shell command line. */
|
|
44
|
+
function buildShellCommandLine(command, args) {
|
|
45
|
+
return [command, ...args].map(quotePosixShellArg).join(" ");
|
|
46
|
+
}
|
|
47
|
+
/** @internal Wrap a shell command line so a non-login shell still sees the current `PATH`. */
|
|
48
|
+
function buildLoginShellCommand(innerCommand) {
|
|
49
|
+
// Use a non-login shell and re-export PATH so test fakes and wrapped
|
|
50
|
+
// command shims remain visible; login shells often reset PATH from
|
|
51
|
+
// profile scripts.
|
|
52
|
+
return `export PATH=${quotePosixShellArg(process.env.PATH)}; ${innerCommand}`;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Run an arbitrary command re-entering the user's login shell (via
|
|
56
|
+
* `$SHELL -c`) so shell-profile-only `PATH` entries are visible, then buffer
|
|
57
|
+
* its output. Falls back to a direct `execFile` on Windows, where there is no
|
|
58
|
+
* equivalent login-shell PATH gap.
|
|
59
|
+
*
|
|
60
|
+
* @param command - The executable to run.
|
|
61
|
+
* @param args - Arguments to pass to the executable.
|
|
62
|
+
* @param opts - `child_process.execFile` options; `env` defaults to
|
|
63
|
+
* `process.env` so the re-exported `PATH` above has a base to restore from.
|
|
64
|
+
* @returns The buffered result.
|
|
65
|
+
*/
|
|
66
|
+
export function execCommandViaLoginShell(command, args, opts = {}) {
|
|
67
|
+
if (process.platform === "win32")
|
|
68
|
+
return execFileBuffered(command, args, opts);
|
|
69
|
+
// `/bin/sh` is POSIX-guaranteed to exist; unlike a specific shell (zsh,
|
|
70
|
+
// bash), it is never absent on a POSIX host, so it is the only safe
|
|
71
|
+
// default for a product-neutral engine that can't assume its host's shell.
|
|
72
|
+
const shell = process.env.SHELL?.trim() || "/bin/sh";
|
|
73
|
+
return execFileBuffered(shell, ["-c", buildLoginShellCommand(buildShellCommandLine(command, args))], {
|
|
74
|
+
env: process.env,
|
|
75
|
+
...opts,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=shell.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,QAAQ,EAAwB,MAAM,oBAAoB,CAAC;AAUpE;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC9B,OAAe,EACf,IAAuB,EACvB,OAAwB,EAAE;IAE1B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,GAAG,IAAI,EAAE,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YAC5G,OAAO,CAAC;gBACN,IAAI,EAAE,KAAK,IAAI,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;gBACvD,KAAK;gBACL,EAAE,EAAE,CAAC,KAAK;gBACV,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE;gBAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE;aAC9B,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,sFAAsF;AACtF,SAAS,kBAAkB,CAAC,KAAgC;IAC1D,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACjC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC5C,CAAC;AAED,2FAA2F;AAC3F,SAAS,qBAAqB,CAAC,OAAe,EAAE,IAAuB;IACrE,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC9D,CAAC;AAED,8FAA8F;AAC9F,SAAS,sBAAsB,CAAC,YAAoB;IAClD,qEAAqE;IACrE,mEAAmE;IACnE,mBAAmB;IACnB,OAAO,eAAe,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,YAAY,EAAE,CAAC;AAChF,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CACtC,OAAe,EACf,IAAuB,EACvB,OAAwB,EAAE;IAE1B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC/E,wEAAwE;IACxE,oEAAoE;IACpE,2EAA2E;IAC3E,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;IACrD,OAAO,gBAAgB,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,sBAAsB,CAAC,qBAAqB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE;QACnG,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,IAAI;KACR,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** A single running PTY child process, however the caller's backend spawns it. */
|
|
2
|
+
export interface PtyProcess {
|
|
3
|
+
onData(cb: (chunk: string) => void): void;
|
|
4
|
+
onExit(cb: (event: {
|
|
5
|
+
exitCode: number;
|
|
6
|
+
signal?: number;
|
|
7
|
+
}) => void): void;
|
|
8
|
+
write(input: string): void;
|
|
9
|
+
resize(cols: number, rows: number): void;
|
|
10
|
+
kill(signal?: string): void;
|
|
11
|
+
}
|
|
12
|
+
export interface PtySpawnOptions {
|
|
13
|
+
name: string;
|
|
14
|
+
cols: number;
|
|
15
|
+
rows: number;
|
|
16
|
+
cwd: string;
|
|
17
|
+
env: Record<string, string>;
|
|
18
|
+
}
|
|
19
|
+
/** Spawns a PTY. The caller supplies this — e.g. a thin wrapper around `node-pty`'s `spawn`. */
|
|
20
|
+
export type PtySpawn = (shell: string, args: string[], options: PtySpawnOptions) => PtyProcess;
|
|
21
|
+
/**
|
|
22
|
+
* Resolves the candidate paths to node-pty's `spawn-helper` binary. On
|
|
23
|
+
* macOS/Linux, `pty.fork` shells out to this helper via `posix_spawn`;
|
|
24
|
+
* node-pty looks for the native artifacts under `build/Release` first and a
|
|
25
|
+
* platform-tagged `prebuilds/<platform>-<arch>` dir second. Returns both so
|
|
26
|
+
* {@link ensureSpawnHelperExecutable} covers whichever one a given install
|
|
27
|
+
* produced. Empty on win32 (ConPTY has no helper) or when node-pty can't be
|
|
28
|
+
* resolved at all (not installed, or a workspace that doesn't use it).
|
|
29
|
+
*
|
|
30
|
+
* @param options.platform - Defaults to `process.platform`; injectable for tests.
|
|
31
|
+
* @param options.resolve - Defaults to a `require.resolve`-shaped resolver for
|
|
32
|
+
* `'node-pty'`; injectable so this stays testable without the native module.
|
|
33
|
+
*/
|
|
34
|
+
export declare function spawnHelperCandidatePaths(options?: {
|
|
35
|
+
platform?: NodeJS.Platform;
|
|
36
|
+
resolve?: (specifier: string) => string;
|
|
37
|
+
}): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Restores the executable bit on node-pty's `spawn-helper`. pnpm unpacks
|
|
40
|
+
* node-pty's prebuilt binaries with mode 0644 — and node-pty's own
|
|
41
|
+
* post-install hook only chmods `build/Release`, which a prebuild-based
|
|
42
|
+
* install never creates. Re-adding +x before the first fork makes the
|
|
43
|
+
* terminal self-heal across reinstalls without depending on an install hook.
|
|
44
|
+
* Best-effort and idempotent: a missing file or a read-only filesystem is
|
|
45
|
+
* swallowed, and the subsequent spawn surfaces the real error instead.
|
|
46
|
+
*
|
|
47
|
+
* @param candidatePaths - Defaults to {@link spawnHelperCandidatePaths}'s result.
|
|
48
|
+
*/
|
|
49
|
+
export declare function ensureSpawnHelperExecutable(candidatePaths?: string[]): void;
|
|
50
|
+
/**
|
|
51
|
+
* Resolves the shell binary for a new PTY. Honors an explicit request
|
|
52
|
+
* override, then the user's environment (`SHELL` on posix, `ComSpec` on
|
|
53
|
+
* win32), and finally a per-platform default.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveShell(requested?: string | null, options?: {
|
|
56
|
+
platform?: NodeJS.Platform;
|
|
57
|
+
env?: NodeJS.ProcessEnv;
|
|
58
|
+
}): string;
|
|
59
|
+
export type TerminalEventData = {
|
|
60
|
+
data: string;
|
|
61
|
+
} | {
|
|
62
|
+
code: number | null;
|
|
63
|
+
signal: string | null;
|
|
64
|
+
};
|
|
65
|
+
export interface TerminalEventRecord {
|
|
66
|
+
id: number;
|
|
67
|
+
event: 'data' | 'exit';
|
|
68
|
+
data: TerminalEventData;
|
|
69
|
+
timestamp: number;
|
|
70
|
+
byteLength: number;
|
|
71
|
+
}
|
|
72
|
+
/** A minimal push sink a transport adapts its live connection to (e.g. SSE). */
|
|
73
|
+
export interface TerminalSseSink {
|
|
74
|
+
send(event: string, data: TerminalEventData, id: number): void;
|
|
75
|
+
end(): void;
|
|
76
|
+
}
|
|
77
|
+
export interface TerminalSession {
|
|
78
|
+
readonly id: string;
|
|
79
|
+
readonly projectId: string | null;
|
|
80
|
+
readonly cwd: string;
|
|
81
|
+
readonly shell: string;
|
|
82
|
+
readonly cols: number;
|
|
83
|
+
readonly rows: number;
|
|
84
|
+
readonly status: 'running' | 'exited';
|
|
85
|
+
readonly createdAt: number;
|
|
86
|
+
readonly updatedAt: number;
|
|
87
|
+
readonly exitCode: number | null;
|
|
88
|
+
readonly signal: string | null;
|
|
89
|
+
}
|
|
90
|
+
export interface CreateTerminalOptions {
|
|
91
|
+
projectId?: string | null;
|
|
92
|
+
cwd: string;
|
|
93
|
+
cols?: number;
|
|
94
|
+
rows?: number;
|
|
95
|
+
shell?: string | null;
|
|
96
|
+
}
|
|
97
|
+
export interface CreateTerminalServiceOptions {
|
|
98
|
+
/** Lazily loads the PTY spawner. Called at most once, on the first `create()`. */
|
|
99
|
+
loadSpawnPty: () => Promise<PtySpawn>;
|
|
100
|
+
/** Count backstop for the per-session output ring-buffer; `maxBufferBytes` is the real ceiling. */
|
|
101
|
+
maxEvents?: number;
|
|
102
|
+
/** Byte ceiling for retained reattach scrollback per running session (evicted oldest-first). */
|
|
103
|
+
maxBufferBytes?: number;
|
|
104
|
+
/** Trailing scrollback kept once a shell exits, so an unreaped session doesn't pin memory. */
|
|
105
|
+
exitTailBytes?: number;
|
|
106
|
+
/** Coalesces PTY output for roughly one animation frame before fanning out one `data` event. */
|
|
107
|
+
flushIntervalMs?: number;
|
|
108
|
+
/** Flushes immediately once buffered output crosses this size, bypassing the frame timer. */
|
|
109
|
+
flushThresholdBytes?: number;
|
|
110
|
+
/** Drops an exited session from the registry after this idle window. */
|
|
111
|
+
ttlMs?: number;
|
|
112
|
+
/** Grace window given to children to exit during {@link TerminalService.shutdownActive}. */
|
|
113
|
+
shutdownGraceMs?: number;
|
|
114
|
+
}
|
|
115
|
+
export interface TerminalService {
|
|
116
|
+
create(options: CreateTerminalOptions): Promise<TerminalSession>;
|
|
117
|
+
get(id: string): TerminalSession | null;
|
|
118
|
+
list(filter?: {
|
|
119
|
+
projectId?: string | null;
|
|
120
|
+
}): TerminalSession[];
|
|
121
|
+
write(id: string, input: string): boolean;
|
|
122
|
+
resize(id: string, cols: number, rows: number): boolean;
|
|
123
|
+
kill(id: string, signal?: string): boolean;
|
|
124
|
+
/** Replays scrollback after `lastEventId`, then attaches `sink` for live output if still running. */
|
|
125
|
+
attach(id: string, lastEventId: number, sink: TerminalSseSink): 'attached' | 'ended' | 'not-found';
|
|
126
|
+
detach(id: string, sink: TerminalSseSink): void;
|
|
127
|
+
shutdownActive(options?: {
|
|
128
|
+
graceMs?: number;
|
|
129
|
+
}): Promise<void>;
|
|
130
|
+
isTerminal(status: string): boolean;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Builds an in-memory terminal session manager. See the module docblock for
|
|
134
|
+
* the design rationale; see {@link CreateTerminalServiceOptions} for tuning.
|
|
135
|
+
*/
|
|
136
|
+
export declare function createTerminalService(options: CreateTerminalServiceOptions): TerminalService;
|
|
137
|
+
//# sourceMappingURL=terminal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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"}
|