@amistio/cli 0.1.36 → 0.1.38
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 +21 -1
- package/dist/host-helper.js +315 -0
- package/dist/index.js +1921 -359
- package/package.json +4 -2
- package/dist/index.js.map +0 -7
package/README.md
CHANGED
|
@@ -9,7 +9,19 @@ npm install -g @amistio/cli
|
|
|
9
9
|
amistio --help
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
The package install
|
|
12
|
+
The package install provides the `amistio` command and the optional `amistio-host-helper` executable. Repository cloning, project pairing, credential storage, sync watching, helper activation, and runner execution happen only when the user explicitly runs commands such as `amistio bootstrap`, `amistio import`, `amistio pair`, `amistio sync watch`, `amistio host-helper status`, or `amistio run --watch`. When the app copies a personal project into an organization, the CLI command syntax stays the same; create the org-scoped code and run `amistio import <code>` from the intended local checkout. Import scans repo-local project-brain docs plus recognized repo-local IDE and local AI tool memory or instruction files such as `AGENTS.md`, `.github/copilot-instructions.md`, `.cursor/rules/*.mdc`, `.windsurfrules`, and `memories/*.md`; durable Amistio memory belongs in `docs/memory/`, and global plugin memory outside the repository is not scanned by default.
|
|
13
|
+
|
|
14
|
+
For enterprise setup, use the web Runner panel as the primary guide. It shows repository, pairing code, GitHub access, AI provider, local runner, and verification readiness with plain next actions; advanced CLI logs remain secondary diagnostics. The panel also shows trust/privacy boundaries, cost forecast and budget posture, runner health, blockers, verification health, and runner-version distribution from safe runner metadata.
|
|
15
|
+
|
|
16
|
+
Optional host helpers are configured outside the SaaS with `AMISTIO_HOST_HELPER_PATH`. The official npm package ships `amistio-host-helper` for Level 1 supervised process execution diagnostics; enable it with `AMISTIO_HOST_HELPER_PATH=$(command -v amistio-host-helper)` on reviewed runner machines and confirm `amistio host-helper status` plus `amistio host-helper conformance`. PTY and sandbox requests use a helper only when its versioned handshake advertises support; otherwise the CLI returns deterministic unsupported results instead of silently downgrading. The npm helper does not advertise PTY or sandbox support. Helper request envelopes include allowlisted environment values, explicit sandbox policy metadata, and bounded normalized output, and must never include provider tokens, OAuth material, runner API tokens, local auth files, or arbitrary SaaS-originated shell text.
|
|
17
|
+
|
|
18
|
+
Before publishing the CLI, run the package release gate:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
corepack pnpm --config.verify-deps-before-run=false --filter @amistio/cli release:check
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The check packs and installs the package, rejects source maps/tests/source files in the tarball, verifies both installed bins, and runs helper handshake, status, and conformance from the installed artifact.
|
|
13
25
|
|
|
14
26
|
Runner lifecycle controls in the web app, such as update, restart, and remove, apply only to the runner paired by that user unless the active organization role is an admin role. The runner API binds command polling, command status, logs, activity, and tool sessions to the local runner credential that produced them.
|
|
15
27
|
|
|
@@ -27,6 +39,8 @@ After pairing, confirm that at least one local AI tool is available:
|
|
|
27
39
|
|
|
28
40
|
```sh
|
|
29
41
|
amistio tools
|
|
42
|
+
amistio host-helper status
|
|
43
|
+
amistio host-helper conformance
|
|
30
44
|
```
|
|
31
45
|
|
|
32
46
|
If no supported tool is available, install and authenticate one of the supported local tools, such as opencode, then start the runner:
|
|
@@ -37,12 +51,16 @@ amistio run --watch --tool opencode
|
|
|
37
51
|
amistio run --watch --tool opencode --provider anthropic --model-id claude-opus-4.6 --reasoning-effort xhigh
|
|
38
52
|
amistio run --watch --max-concurrent-work 2 --tool opencode
|
|
39
53
|
amistio run --watch --background --tool opencode
|
|
54
|
+
AMISTIO_HOST_HELPER_PATH=$(command -v amistio-host-helper) amistio host-helper status
|
|
55
|
+
AMISTIO_HOST_HELPER_PATH=$(command -v amistio-host-helper) amistio host-helper conformance
|
|
40
56
|
amistio runner status
|
|
41
57
|
amistio runner smoke-session-lifecycle
|
|
42
58
|
```
|
|
43
59
|
|
|
44
60
|
Provider-backed model preferences use sanitized catalog fields: `--provider`, `--model-id`, optional `--model-variant`, and `--reasoning-effort` (`auto`, `low`, `medium`, `high`, or `xhigh`). Opencode catalog metadata is synthesized by Amistio or derived from readable local OpenCode JSON config until opencode exposes a native catalog. Provider credentials, API keys, and local secret paths stay in the local tool configuration; they are not stored in Amistio preferences or runner heartbeats.
|
|
45
61
|
|
|
62
|
+
Opencode remains an optional compatibility route. When a provider/model is named for opencode, Amistio validates it against the safe provider catalog before launching the external tool instead of silently falling back. Command-mode local tool runs are bounded by `--tool-timeout-seconds`, wait for process cleanup after timeout, and return redacted/capped stdout and stderr so large external output, local execution-root paths, and secret-looking assignments are not persisted unbounded.
|
|
63
|
+
|
|
46
64
|
When `--tool copilot` uses the GitHub Copilot SDK, Amistio approves read-only permission requests by default and denies mutating, network, MCP, hook, memory, and shell requests. Set `AMISTIO_COPILOT_APPROVE_ALL=1` only on a local machine where broad Copilot SDK approval is intentional.
|
|
47
65
|
|
|
48
66
|
When `--tool codex` uses the Codex SDK, intermediate progress can be quiet until the final response. For live Codex CLI logs, run `amistio run --watch --tool codex --invocation-channel command`.
|
|
@@ -65,6 +83,8 @@ Watch mode prints a completed-work success once per work item, keeps fresh compl
|
|
|
65
83
|
|
|
66
84
|
Known validation failures such as `unsafe_context_path` are printed with attention-needed next steps. For project-context refresh path-safety failures, deploy the latest web/API fix, update and restart the runner when applicable, retry the refresh, and capture only bounded non-secret output if it repeats.
|
|
67
85
|
|
|
86
|
+
If watch mode reports that the runner was forgotten by the server, stop the foreground process and start or pair a new runner from the Runner panel. Forgotten runner IDs are intentionally blocked from heartbeats and work claims, so the CLI exits instead of retrying with the tombstoned identity.
|
|
87
|
+
|
|
68
88
|
App-evaluation result finalization rejections print safe validation paths and preserve the local finalization evidence without exposing raw source or secrets. If a structured app-evaluation result is rejected, update and restart the runner, confirm the web/API deployment is current, and retry the evaluation before acting on cleanup or implementation recommendations.
|
|
69
89
|
|
|
70
90
|
When brain generation or plan revision output is parsed but the Amistio API is temporarily unavailable during finalization, the runner keeps a safe pending result envelope in user-level Amistio config and replays it before claiming more work. The envelope uses a stable idempotency key and does not store raw tool stdout, provider sessions, credentials, or arbitrary local paths.
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/host-helper.ts
|
|
4
|
+
import { realpathSync } from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
// src/host-execution.ts
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
var hostExecutionProtocolVersion = "amistio.hostExecution.v1";
|
|
10
|
+
var nativeHostHelperSecretBoundary = {
|
|
11
|
+
credentialPolicy: "never-send-credentials",
|
|
12
|
+
environmentPolicy: "allowlist-only",
|
|
13
|
+
forbiddenInputs: [
|
|
14
|
+
"runner API tokens",
|
|
15
|
+
"provider API keys",
|
|
16
|
+
"OAuth tokens",
|
|
17
|
+
"refresh tokens",
|
|
18
|
+
"SaaS credentials",
|
|
19
|
+
"local auth files",
|
|
20
|
+
"unbounded environment variables",
|
|
21
|
+
"arbitrary SaaS-sourced shell text"
|
|
22
|
+
]
|
|
23
|
+
};
|
|
24
|
+
var defaultNativeHostOutputBudgetBytes = 64 * 1024;
|
|
25
|
+
var nodeHostExecutionCapabilities = {
|
|
26
|
+
protocolVersion: hostExecutionProtocolVersion,
|
|
27
|
+
implementation: "node",
|
|
28
|
+
processGroups: process.platform === "win32" ? { supported: false, reason: "Windows process-group cleanup needs a native helper or platform-specific implementation." } : { supported: true },
|
|
29
|
+
signalEscalation: { supported: true },
|
|
30
|
+
streamCapture: { supported: true },
|
|
31
|
+
pty: { supported: false, reason: "PTY bridging is reserved for a future native helper." },
|
|
32
|
+
sandbox: { supported: false, reason: "Sandboxed command execution is reserved for a future native helper." },
|
|
33
|
+
outputEncoding: "utf8"
|
|
34
|
+
};
|
|
35
|
+
var defaultHostExecution = {
|
|
36
|
+
capabilities: nodeHostExecutionCapabilities,
|
|
37
|
+
executeCommand: executeNodeCommand,
|
|
38
|
+
commandExists
|
|
39
|
+
};
|
|
40
|
+
async function executeNodeCommand(request) {
|
|
41
|
+
if (request.requirePty) {
|
|
42
|
+
return unsupportedResult("pty_unsupported", "PTY command execution is not supported by the default Node host execution port.");
|
|
43
|
+
}
|
|
44
|
+
if (request.sandbox && request.sandbox !== "none") {
|
|
45
|
+
return unsupportedResult("sandbox_unsupported", `${request.sandbox} sandbox execution is not supported by the default Node host execution port.`);
|
|
46
|
+
}
|
|
47
|
+
return new Promise((resolve) => {
|
|
48
|
+
let child;
|
|
49
|
+
try {
|
|
50
|
+
child = spawn(request.command, [...request.args], {
|
|
51
|
+
cwd: request.cwd,
|
|
52
|
+
env: request.env ?? process.env,
|
|
53
|
+
detached: process.platform !== "win32",
|
|
54
|
+
shell: request.shell ?? false,
|
|
55
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
56
|
+
});
|
|
57
|
+
} catch (error) {
|
|
58
|
+
resolve(spawnFailedResult(error));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let stdout = "";
|
|
62
|
+
let stderr = "";
|
|
63
|
+
let settled = false;
|
|
64
|
+
let forceKillTimer;
|
|
65
|
+
let timedOutError;
|
|
66
|
+
const timeout = request.timeoutMs && request.timeoutMs > 0 ? setTimeout(() => {
|
|
67
|
+
if (settled) return;
|
|
68
|
+
const message = request.timeoutMessage ?? `Command timed out after ${request.timeoutMs}ms: ${request.command}`;
|
|
69
|
+
stderr += `${message}
|
|
70
|
+
`;
|
|
71
|
+
timedOutError = { code: "timeout", message };
|
|
72
|
+
killProcessTree(child, "SIGTERM");
|
|
73
|
+
forceKillTimer = setTimeout(() => killProcessTree(child, "SIGKILL"), request.killGraceMs ?? 5e3);
|
|
74
|
+
forceKillTimer.unref?.();
|
|
75
|
+
}, request.timeoutMs) : void 0;
|
|
76
|
+
timeout?.unref?.();
|
|
77
|
+
const resolveOnce = (result) => {
|
|
78
|
+
if (settled) return;
|
|
79
|
+
settled = true;
|
|
80
|
+
if (timeout) clearTimeout(timeout);
|
|
81
|
+
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
82
|
+
resolve(result);
|
|
83
|
+
};
|
|
84
|
+
child.on("error", (error) => {
|
|
85
|
+
if (timedOutError) return;
|
|
86
|
+
resolveOnce(spawnFailedResult(error, stdout, stderr));
|
|
87
|
+
});
|
|
88
|
+
child.stdout.setEncoding("utf8");
|
|
89
|
+
child.stderr.setEncoding("utf8");
|
|
90
|
+
child.stdout.on("data", (chunk) => {
|
|
91
|
+
stdout += chunk;
|
|
92
|
+
if (request.streamOutput) process.stdout.write(chunk);
|
|
93
|
+
});
|
|
94
|
+
child.stderr.on("data", (chunk) => {
|
|
95
|
+
stderr += chunk;
|
|
96
|
+
if (request.streamOutput) process.stderr.write(chunk);
|
|
97
|
+
});
|
|
98
|
+
child.stdin.on("error", () => void 0);
|
|
99
|
+
if (request.stdin) {
|
|
100
|
+
child.stdin.write(request.stdin);
|
|
101
|
+
}
|
|
102
|
+
child.stdin.end();
|
|
103
|
+
child.on("close", (exitCode) => {
|
|
104
|
+
if (forceKillTimer) clearTimeout(forceKillTimer);
|
|
105
|
+
if (timedOutError) {
|
|
106
|
+
resolveOnce({ status: "timedOut", exitCode: 1, stdout, stderr, error: timedOutError });
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
resolveOnce({ status: "completed", exitCode: exitCode ?? 1, stdout, stderr });
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
function unsupportedResult(code, message) {
|
|
114
|
+
return { status: "unsupported", exitCode: 1, stdout: "", stderr: "", error: { code, message } };
|
|
115
|
+
}
|
|
116
|
+
function spawnFailedResult(error, stdout = "", stderr = "") {
|
|
117
|
+
return { status: "spawnFailed", exitCode: 1, stdout, stderr, error: { code: "spawn_failed", message: errorMessage(error) } };
|
|
118
|
+
}
|
|
119
|
+
function killProcessTree(child, signal) {
|
|
120
|
+
if (child.pid && process.platform !== "win32") {
|
|
121
|
+
try {
|
|
122
|
+
process.kill(-child.pid, signal);
|
|
123
|
+
return;
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
child.kill(signal);
|
|
128
|
+
}
|
|
129
|
+
async function commandExists(command) {
|
|
130
|
+
const lookupCommand = process.platform === "win32" ? "where" : "which";
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
const lookup = spawn(lookupCommand, [command], { stdio: "ignore" });
|
|
133
|
+
lookup.on("error", () => resolve(false));
|
|
134
|
+
lookup.on("close", (exitCode) => resolve(exitCode === 0));
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function errorMessage(error) {
|
|
138
|
+
return error instanceof Error ? error.message : String(error);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/host-helper.ts
|
|
142
|
+
var maxRequestEnvelopeBytes = 512 * 1024;
|
|
143
|
+
var defaultOutputBudgetBytes = 64 * 1024;
|
|
144
|
+
function createOfficialHostHelperHandshake() {
|
|
145
|
+
const capabilities = {
|
|
146
|
+
...nodeHostExecutionCapabilities,
|
|
147
|
+
implementation: "nativeHelper",
|
|
148
|
+
pty: { supported: false, reason: "The official npm helper does not allocate PTYs yet. Use a future native helper build for PTY support." },
|
|
149
|
+
sandbox: { supported: false, reason: "The official npm helper does not enforce OS sandboxes yet. Use a future native helper build for sandbox support." }
|
|
150
|
+
};
|
|
151
|
+
return { protocolVersion: hostExecutionProtocolVersion, capabilities, secretBoundary: nativeHostHelperSecretBoundary };
|
|
152
|
+
}
|
|
153
|
+
async function executeOfficialHostHelperRequest(input) {
|
|
154
|
+
const parsed = parseJson(input);
|
|
155
|
+
if (!isNativeHostCommandRequestEnvelope(parsed)) {
|
|
156
|
+
const requestId = requestIdFromUnknown(parsed) ?? "invalid-request";
|
|
157
|
+
return resultEnvelope(requestId, protocolMismatch("Host helper request envelope did not match the Amistio host execution protocol."));
|
|
158
|
+
}
|
|
159
|
+
if (parsed.stdin === "provided") {
|
|
160
|
+
return resultEnvelope(parsed.requestId, unsupportedResult2("helper_failed", "The official helper does not accept stdin payload delegation."));
|
|
161
|
+
}
|
|
162
|
+
if (parsed.shell) {
|
|
163
|
+
return resultEnvelope(parsed.requestId, unsupportedResult2("helper_failed", "The official helper does not execute shell-wrapped requests."));
|
|
164
|
+
}
|
|
165
|
+
if (parsed.requirePty) {
|
|
166
|
+
return resultEnvelope(parsed.requestId, unsupportedResult2("pty_unsupported", "PTY execution is not supported by the official npm helper."));
|
|
167
|
+
}
|
|
168
|
+
if (parsed.sandbox !== "none") {
|
|
169
|
+
return resultEnvelope(parsed.requestId, unsupportedResult2("sandbox_unsupported", `${parsed.sandbox} sandbox execution is not supported by the official npm helper.`));
|
|
170
|
+
}
|
|
171
|
+
const commandRequest = {
|
|
172
|
+
command: parsed.command,
|
|
173
|
+
args: parsed.args,
|
|
174
|
+
cwd: parsed.cwd,
|
|
175
|
+
env: sanitizeRequestEnvironment(parsed),
|
|
176
|
+
shell: false,
|
|
177
|
+
streamOutput: false,
|
|
178
|
+
outputBudgetBytes: positiveInteger(parsed.outputBudgetBytes) ?? defaultOutputBudgetBytes
|
|
179
|
+
};
|
|
180
|
+
const timeoutMs = positiveInteger(parsed.timeoutMs);
|
|
181
|
+
if (timeoutMs !== void 0) {
|
|
182
|
+
commandRequest.timeoutMs = timeoutMs;
|
|
183
|
+
}
|
|
184
|
+
const result = await defaultHostExecution.executeCommand(commandRequest);
|
|
185
|
+
return resultEnvelope(parsed.requestId, result, positiveInteger(parsed.outputBudgetBytes) ?? defaultOutputBudgetBytes);
|
|
186
|
+
}
|
|
187
|
+
async function runOfficialHostHelperCli(argv = process.argv.slice(2), io = process) {
|
|
188
|
+
const mode = argv[0];
|
|
189
|
+
if (mode === "--amistio-host-helper-handshake") {
|
|
190
|
+
io.stdout.write(`${JSON.stringify(createOfficialHostHelperHandshake())}
|
|
191
|
+
`);
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
if (mode === "--amistio-host-helper-execute") {
|
|
195
|
+
try {
|
|
196
|
+
const input = await readStdinLimited(io.stdin, maxRequestEnvelopeBytes);
|
|
197
|
+
const result = await executeOfficialHostHelperRequest(input);
|
|
198
|
+
io.stdout.write(`${JSON.stringify(result)}
|
|
199
|
+
`);
|
|
200
|
+
return 0;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
io.stderr.write(`${errorMessage2(error)}
|
|
203
|
+
`);
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (mode === "--help" || mode === "-h") {
|
|
208
|
+
io.stdout.write("Usage: amistio-host-helper --amistio-host-helper-handshake | --amistio-host-helper-execute\n");
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
io.stderr.write("Unknown Amistio host helper mode. Use --help for usage.\n");
|
|
212
|
+
return 1;
|
|
213
|
+
}
|
|
214
|
+
function resultEnvelope(requestId, result, outputBudgetBytes = defaultOutputBudgetBytes) {
|
|
215
|
+
return {
|
|
216
|
+
protocolVersion: hostExecutionProtocolVersion,
|
|
217
|
+
requestId,
|
|
218
|
+
status: result.status,
|
|
219
|
+
exitCode: result.exitCode,
|
|
220
|
+
stdout: capUtf8Text(result.stdout, outputBudgetBytes),
|
|
221
|
+
stderr: capUtf8Text(result.stderr, outputBudgetBytes),
|
|
222
|
+
...result.error ? { error: result.error } : {}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function unsupportedResult2(code, message) {
|
|
226
|
+
return { status: "unsupported", exitCode: 1, stdout: "", stderr: "", error: { code, message } };
|
|
227
|
+
}
|
|
228
|
+
function protocolMismatch(message) {
|
|
229
|
+
return { status: "protocolMismatch", exitCode: 1, stdout: "", stderr: "", error: { code: "protocol_mismatch", message } };
|
|
230
|
+
}
|
|
231
|
+
function sanitizeRequestEnvironment(request) {
|
|
232
|
+
const allowedNames = new Set(request.envAllowlist);
|
|
233
|
+
const env = {};
|
|
234
|
+
for (const [name, value] of Object.entries(request.env)) {
|
|
235
|
+
if (!allowedNames.has(name) || isSensitiveEnvironmentName(name)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
env[name] = value;
|
|
239
|
+
}
|
|
240
|
+
return env;
|
|
241
|
+
}
|
|
242
|
+
function isNativeHostCommandRequestEnvelope(value) {
|
|
243
|
+
if (!isRecord(value) || value.protocolVersion !== hostExecutionProtocolVersion) return false;
|
|
244
|
+
return typeof value.requestId === "string" && value.requestId.trim().length > 0 && typeof value.command === "string" && value.command.trim().length > 0 && Array.isArray(value.args) && value.args.every((item) => typeof item === "string") && typeof value.cwd === "string" && value.cwd.trim().length > 0 && typeof value.shell === "boolean" && typeof value.timeoutMs === "number" && Number.isFinite(value.timeoutMs) && value.timeoutMs >= 0 && (value.stdin === "none" || value.stdin === "provided") && isStringRecord(value.env) && Array.isArray(value.envAllowlist) && value.envAllowlist.every((item) => typeof item === "string") && typeof value.streamOutput === "boolean" && typeof value.requirePty === "boolean" && isSandboxMode(value.sandbox) && isRecord(value.sandboxPolicy) && typeof value.outputBudgetBytes === "number" && Number.isFinite(value.outputBudgetBytes) && value.outputBudgetBytes >= 0;
|
|
245
|
+
}
|
|
246
|
+
function isSandboxMode(value) {
|
|
247
|
+
return value === "none" || value === "filesystemReadOnly" || value === "networkDisabled";
|
|
248
|
+
}
|
|
249
|
+
function isStringRecord(value) {
|
|
250
|
+
return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
|
|
251
|
+
}
|
|
252
|
+
function requestIdFromUnknown(value) {
|
|
253
|
+
return isRecord(value) && typeof value.requestId === "string" && value.requestId.trim() ? value.requestId : void 0;
|
|
254
|
+
}
|
|
255
|
+
function readStdinLimited(stdin, limitBytes) {
|
|
256
|
+
return new Promise((resolve, reject) => {
|
|
257
|
+
let input = "";
|
|
258
|
+
let bytes = 0;
|
|
259
|
+
stdin.setEncoding("utf8");
|
|
260
|
+
stdin.on("data", (chunk) => {
|
|
261
|
+
bytes += Buffer.byteLength(chunk, "utf8");
|
|
262
|
+
if (bytes > limitBytes) {
|
|
263
|
+
reject(new Error(`Host helper request exceeded ${limitBytes} bytes.`));
|
|
264
|
+
stdin.pause();
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
input += chunk;
|
|
268
|
+
});
|
|
269
|
+
stdin.on("error", reject);
|
|
270
|
+
stdin.on("end", () => resolve(input));
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
function capUtf8Text(value, budgetBytes) {
|
|
274
|
+
if (Buffer.byteLength(value, "utf8") <= budgetBytes) return value;
|
|
275
|
+
let capped = Buffer.from(value, "utf8").subarray(0, budgetBytes).toString("utf8");
|
|
276
|
+
while (Buffer.byteLength(capped, "utf8") > budgetBytes && capped.length > 0) {
|
|
277
|
+
capped = capped.slice(0, -1);
|
|
278
|
+
}
|
|
279
|
+
return capped;
|
|
280
|
+
}
|
|
281
|
+
function isSensitiveEnvironmentName(name) {
|
|
282
|
+
return /(?:TOKEN|SECRET|PASSWORD|PASS|KEY|CREDENTIAL|AUTH|OAUTH|COOKIE|SESSION|PRIVATE|CERT|SSH)/i.test(name);
|
|
283
|
+
}
|
|
284
|
+
function isRecord(value) {
|
|
285
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
286
|
+
}
|
|
287
|
+
function parseJson(value) {
|
|
288
|
+
try {
|
|
289
|
+
return JSON.parse(value);
|
|
290
|
+
} catch {
|
|
291
|
+
return void 0;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
function positiveInteger(value) {
|
|
295
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
296
|
+
}
|
|
297
|
+
function errorMessage2(error) {
|
|
298
|
+
return error instanceof Error ? error.message : String(error);
|
|
299
|
+
}
|
|
300
|
+
function isDirectExecution(metaUrl) {
|
|
301
|
+
if (!process.argv[1]) return false;
|
|
302
|
+
try {
|
|
303
|
+
return realpathSync(fileURLToPath(metaUrl)) === realpathSync(process.argv[1]);
|
|
304
|
+
} catch {
|
|
305
|
+
return fileURLToPath(metaUrl) === fileURLToPath(new URL(`file://${process.argv[1]}`).href);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (isDirectExecution(import.meta.url)) {
|
|
309
|
+
process.exitCode = await runOfficialHostHelperCli();
|
|
310
|
+
}
|
|
311
|
+
export {
|
|
312
|
+
createOfficialHostHelperHandshake,
|
|
313
|
+
executeOfficialHostHelperRequest,
|
|
314
|
+
runOfficialHostHelperCli
|
|
315
|
+
};
|