@demicodes/shell 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +18 -18
- package/dist/index.mjs +22 -22
- package/dist/storage.mjs +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -137,7 +137,7 @@ interface ShellAbortInput {
|
|
|
137
137
|
commandId: string;
|
|
138
138
|
maxOutputBytes?: number;
|
|
139
139
|
}
|
|
140
|
-
interface
|
|
140
|
+
interface ShellStreamView {
|
|
141
141
|
path: string;
|
|
142
142
|
offset: number;
|
|
143
143
|
delta: string;
|
|
@@ -153,7 +153,7 @@ interface ShellOutputRecordChunk extends ShellOutputChunk {
|
|
|
153
153
|
offset: number;
|
|
154
154
|
bytes: number;
|
|
155
155
|
}
|
|
156
|
-
interface
|
|
156
|
+
interface ShellOutputView {
|
|
157
157
|
path: string;
|
|
158
158
|
offset: number;
|
|
159
159
|
text: string;
|
|
@@ -194,14 +194,14 @@ interface BinaryStdout {
|
|
|
194
194
|
/** Total byte count of the un-capped stream. */
|
|
195
195
|
totalBytes: number;
|
|
196
196
|
}
|
|
197
|
-
type
|
|
197
|
+
type ShellCommandStatus = {
|
|
198
198
|
status: 'exited';
|
|
199
199
|
shellId: string;
|
|
200
200
|
commandId: string;
|
|
201
201
|
exitCode: number;
|
|
202
|
-
stdout:
|
|
203
|
-
stderr:
|
|
204
|
-
output:
|
|
202
|
+
stdout: ShellStreamView;
|
|
203
|
+
stderr: ShellStreamView;
|
|
204
|
+
output: ShellOutputView;
|
|
205
205
|
runningMs: number;
|
|
206
206
|
idleMs: number;
|
|
207
207
|
audit: BashAuditEvent[];
|
|
@@ -212,18 +212,18 @@ type ShellCommandSnapshot = {
|
|
|
212
212
|
status: 'running';
|
|
213
213
|
shellId: string;
|
|
214
214
|
commandId: string;
|
|
215
|
-
stdout:
|
|
216
|
-
stderr:
|
|
217
|
-
output:
|
|
215
|
+
stdout: ShellStreamView;
|
|
216
|
+
stderr: ShellStreamView;
|
|
217
|
+
output: ShellOutputView;
|
|
218
218
|
runningMs: number;
|
|
219
219
|
idleMs: number;
|
|
220
220
|
} | {
|
|
221
221
|
status: 'aborted';
|
|
222
222
|
shellId: string;
|
|
223
223
|
commandId: string;
|
|
224
|
-
stdout:
|
|
225
|
-
stderr:
|
|
226
|
-
output:
|
|
224
|
+
stdout: ShellStreamView;
|
|
225
|
+
stderr: ShellStreamView;
|
|
226
|
+
output: ShellOutputView;
|
|
227
227
|
runningMs: number;
|
|
228
228
|
idleMs: number;
|
|
229
229
|
};
|
|
@@ -245,10 +245,10 @@ declare class BashEnvironment {
|
|
|
245
245
|
getShell(shellId: string): ShellSession | null;
|
|
246
246
|
registerCommand(command: Command): void;
|
|
247
247
|
registeredCommands(): Command[];
|
|
248
|
-
exec(input: ShellExecInput): Promise<
|
|
249
|
-
status(input: ShellStatusInput): Promise<
|
|
250
|
-
write(input: ShellWriteInput): Promise<
|
|
251
|
-
abort(input: ShellAbortInput): Promise<
|
|
248
|
+
exec(input: ShellExecInput): Promise<ShellCommandStatus>;
|
|
249
|
+
status(input: ShellStatusInput): Promise<ShellCommandStatus>;
|
|
250
|
+
write(input: ShellWriteInput): Promise<ShellCommandStatus>;
|
|
251
|
+
abort(input: ShellAbortInput): Promise<ShellCommandStatus>;
|
|
252
252
|
releaseCommand(commandId: string): Promise<boolean>;
|
|
253
253
|
disposeShell(shellId: string): Promise<boolean>;
|
|
254
254
|
disposeAllShells(): Promise<void>;
|
|
@@ -272,7 +272,7 @@ declare class BashEnvironment {
|
|
|
272
272
|
private finishExited;
|
|
273
273
|
private collectAborted;
|
|
274
274
|
private collectAbortedWithoutForeground;
|
|
275
|
-
private
|
|
275
|
+
private commandStatus;
|
|
276
276
|
private lookupVirtualArtifact;
|
|
277
277
|
private commandArtifactIds;
|
|
278
278
|
private commandArtifact;
|
|
@@ -311,4 +311,4 @@ declare function shellQuote(value: string): string;
|
|
|
311
311
|
/** Picks a heredoc delimiter that does not collide with any line already in `body`. */
|
|
312
312
|
declare function heredocDelimiter(body: string): string;
|
|
313
313
|
//#endregion
|
|
314
|
-
export { AgentSessionCommandStorage, BashAuditEvent, BashEnvironment, BashEnvironmentOptions, BinaryStdout, COMMAND_HELP_DEFAULTS, Command, CommandExecutionContext, CommandIO, CommandInputSpec, CommandMetadataRecord, CommandOutputSpec, CommandRegistry, CommandRunContext, CommandRunResult, CommandStdin, CommandStorage, DEMI_PORTABLE_COMMANDS, Host, HostBackedFileSystem, HostDirent, HostFileStat, HostFileSystem, HostProcess, HostProcessOutputChunk, HostSpawnExit, HostSpawnHandle, HostSpawnParams, HostStore, MAX_TIMEOUT_MS, ParsedCommandInput, RESERVED_COMMAND_NAMES, ShellAbortInput,
|
|
314
|
+
export { AgentSessionCommandStorage, BashAuditEvent, BashEnvironment, BashEnvironmentOptions, BinaryStdout, COMMAND_HELP_DEFAULTS, Command, CommandExecutionContext, CommandIO, CommandInputSpec, CommandMetadataRecord, CommandOutputSpec, CommandRegistry, CommandRunContext, CommandRunResult, CommandStdin, CommandStorage, DEMI_PORTABLE_COMMANDS, Host, HostBackedFileSystem, HostDirent, HostFileStat, HostFileSystem, HostProcess, HostProcessOutputChunk, HostSpawnExit, HostSpawnHandle, HostSpawnParams, HostStore, MAX_TIMEOUT_MS, ParsedCommandInput, RESERVED_COMMAND_NAMES, ShellAbortInput, ShellCommandStatus, ShellExecInput, ShellOutputChunk, ShellOutputRecordChunk, ShellOutputView, ShellStatusInput, ShellStreamView, ShellWriteInput, VirtualFileSystemNode, VirtualFileSystemProvider, emptyStdin, heredocDelimiter, parseCommandInput, renderCommandHelp, runRegisteredCommand, shellQuote, virtualDirectory, virtualFile };
|
package/dist/index.mjs
CHANGED
|
@@ -847,7 +847,7 @@ var BashEnvironment = class {
|
|
|
847
847
|
}
|
|
848
848
|
async status(input) {
|
|
849
849
|
const record = this.requireCommand(input.commandId);
|
|
850
|
-
return this.
|
|
850
|
+
return this.commandStatus(record, input);
|
|
851
851
|
}
|
|
852
852
|
async write(input) {
|
|
853
853
|
const record = this.requireCommand(input.commandId);
|
|
@@ -857,11 +857,11 @@ var BashEnvironment = class {
|
|
|
857
857
|
const data = typeof input.stdin === "string" ? encodeUtf8(input.stdin) : input.stdin;
|
|
858
858
|
if (data.byteLength === 0) throw new Error("shell_write field \"stdin\" must not be empty; use shell_status to poll");
|
|
859
859
|
await foreground.handle.writeStdin(data);
|
|
860
|
-
return this.
|
|
860
|
+
return this.commandStatus(record, input);
|
|
861
861
|
}
|
|
862
862
|
async abort(input) {
|
|
863
863
|
const record = this.requireCommand(input.commandId);
|
|
864
|
-
if (record.status !== "running") return this.
|
|
864
|
+
if (record.status !== "running") return this.commandStatus(record, input);
|
|
865
865
|
const session = this.requireShell(record.shellId);
|
|
866
866
|
const foreground = this.requireForegroundCommand(session, record.id);
|
|
867
867
|
foreground.abortController.abort();
|
|
@@ -1078,7 +1078,7 @@ var BashEnvironment = class {
|
|
|
1078
1078
|
record.exitCode = 2;
|
|
1079
1079
|
session.state.lastExitCode = 2;
|
|
1080
1080
|
session.activeCommandId = void 0;
|
|
1081
|
-
return this.
|
|
1081
|
+
return this.commandStatus(record, input);
|
|
1082
1082
|
}
|
|
1083
1083
|
throw error;
|
|
1084
1084
|
}
|
|
@@ -1243,7 +1243,7 @@ var BashEnvironment = class {
|
|
|
1243
1243
|
foreground = outcome.foreground;
|
|
1244
1244
|
continue;
|
|
1245
1245
|
}
|
|
1246
|
-
if (outcome.kind === "timeout") return this.
|
|
1246
|
+
if (outcome.kind === "timeout") return this.commandStatus(record, input);
|
|
1247
1247
|
if (outcome.kind === "aborted") {
|
|
1248
1248
|
const activeForeground = foreground ?? session.foreground;
|
|
1249
1249
|
if (!activeForeground) return this.collectAbortedWithoutForeground(session, record, input);
|
|
@@ -1389,7 +1389,7 @@ var BashEnvironment = class {
|
|
|
1389
1389
|
};
|
|
1390
1390
|
}
|
|
1391
1391
|
collectExited(session, record, resultOrError, foreground, input = {}) {
|
|
1392
|
-
if (record.status !== "running") return this.
|
|
1392
|
+
if (record.status !== "running") return this.commandStatus(record, input);
|
|
1393
1393
|
if (resultOrError instanceof Error) {
|
|
1394
1394
|
if (resultOrError instanceof ExitError) {
|
|
1395
1395
|
session.exited = true;
|
|
@@ -1476,10 +1476,10 @@ var BashEnvironment = class {
|
|
|
1476
1476
|
record.commandMetadata = [...session.accumulator.commandMetadata];
|
|
1477
1477
|
session.pendingExec = void 0;
|
|
1478
1478
|
if (session.activeCommandId === record.id) session.activeCommandId = void 0;
|
|
1479
|
-
return this.
|
|
1479
|
+
return this.commandStatus(record, input);
|
|
1480
1480
|
}
|
|
1481
1481
|
async collectAborted(session, record, foreground, input = {}) {
|
|
1482
|
-
if (record.status !== "running") return this.
|
|
1482
|
+
if (record.status !== "running") return this.commandStatus(record, input);
|
|
1483
1483
|
foreground.abortController.abort();
|
|
1484
1484
|
foreground.handle.kill("SIGTERM").catch(() => {});
|
|
1485
1485
|
await flushForegroundSinks(session, foreground);
|
|
@@ -1491,10 +1491,10 @@ var BashEnvironment = class {
|
|
|
1491
1491
|
session.foreground = void 0;
|
|
1492
1492
|
session.pendingExec = void 0;
|
|
1493
1493
|
if (session.activeCommandId === record.id) session.activeCommandId = void 0;
|
|
1494
|
-
return this.
|
|
1494
|
+
return this.commandStatus(record, input);
|
|
1495
1495
|
}
|
|
1496
1496
|
collectAbortedWithoutForeground(session, record, input = {}) {
|
|
1497
|
-
if (record.status !== "running") return this.
|
|
1497
|
+
if (record.status !== "running") return this.commandStatus(record, input);
|
|
1498
1498
|
session.abortController?.abort();
|
|
1499
1499
|
session.pendingExec = void 0;
|
|
1500
1500
|
if (session.activeCommandId === record.id) session.activeCommandId = void 0;
|
|
@@ -1506,9 +1506,9 @@ var BashEnvironment = class {
|
|
|
1506
1506
|
}
|
|
1507
1507
|
record.lastOutputAt = Date.now();
|
|
1508
1508
|
record.status = "aborted";
|
|
1509
|
-
return this.
|
|
1509
|
+
return this.commandStatus(record, input);
|
|
1510
1510
|
}
|
|
1511
|
-
|
|
1511
|
+
commandStatus(record, input = {}) {
|
|
1512
1512
|
const foreground = this.shells.get(record.shellId)?.foreground;
|
|
1513
1513
|
if (record.status === "running" && foreground?.commandId === record.id) {
|
|
1514
1514
|
record.stdout = foreground.stdoutBuffer;
|
|
@@ -1517,9 +1517,9 @@ var BashEnvironment = class {
|
|
|
1517
1517
|
record.lastOutputAt = foreground.lastOutputAt;
|
|
1518
1518
|
}
|
|
1519
1519
|
const maxOutputBytes = input.maxOutputBytes ?? this.defaultOutputLimitBytes;
|
|
1520
|
-
const stdout =
|
|
1521
|
-
const stderr =
|
|
1522
|
-
const output =
|
|
1520
|
+
const stdout = streamView(record, "stdout", input.stdoutOffset, maxOutputBytes);
|
|
1521
|
+
const stderr = streamView(record, "stderr", input.stderrOffset, maxOutputBytes);
|
|
1522
|
+
const output = mergedOutputView(record, input.outputOffset, maxOutputBytes);
|
|
1523
1523
|
const base = {
|
|
1524
1524
|
shellId: record.shellId,
|
|
1525
1525
|
commandId: record.id,
|
|
@@ -1590,16 +1590,16 @@ var BashEnvironment = class {
|
|
|
1590
1590
|
const record = this.commandsById.get(commandId);
|
|
1591
1591
|
if (record?.commandScopeId === scopeId) {
|
|
1592
1592
|
this.syncRunningRecord(record);
|
|
1593
|
-
return
|
|
1593
|
+
return commandArtifactFromRecord(record);
|
|
1594
1594
|
}
|
|
1595
1595
|
const value = await this.artifacts.storageFor(scopeId).readJson(`commands/${commandId}/artifact.json`).catch(() => null);
|
|
1596
|
-
return
|
|
1596
|
+
return isCommandArtifact(value) ? value : null;
|
|
1597
1597
|
}
|
|
1598
1598
|
persistCommandArtifact(record) {
|
|
1599
1599
|
const fingerprint = `${record.status}:${record.exitCode ?? ""}:${record.stdout.length}:${record.stderr.length}:${record.binaryStdout?.totalBytes ?? ""}`;
|
|
1600
1600
|
if (record.persistedFingerprint === fingerprint) return;
|
|
1601
1601
|
record.persistedFingerprint = fingerprint;
|
|
1602
|
-
this.artifacts.persist(record.commandScopeId, record.id,
|
|
1602
|
+
this.artifacts.persist(record.commandScopeId, record.id, commandArtifactFromRecord(record));
|
|
1603
1603
|
}
|
|
1604
1604
|
syncRunningRecord(record) {
|
|
1605
1605
|
const foreground = this.shells.get(record.shellId)?.foreground;
|
|
@@ -1636,7 +1636,7 @@ function normalizeTimeoutMs(value) {
|
|
|
1636
1636
|
if (!Number.isFinite(value) || value < 1 || value > 6e5) throw new Error(`timeoutMs must be between 1 and ${MAX_TIMEOUT_MS}`);
|
|
1637
1637
|
return Math.floor(value);
|
|
1638
1638
|
}
|
|
1639
|
-
function
|
|
1639
|
+
function streamView(record, stream, explicitOffset, maxOutputBytes) {
|
|
1640
1640
|
const text = stream === "stdout" ? record.stdout : record.stderr;
|
|
1641
1641
|
const totalBytes = utf8Bytes(text);
|
|
1642
1642
|
const boundedOffset = clampOffset(explicitOffset ?? (stream === "stdout" ? record.stdoutOffset : record.stderrOffset), totalBytes);
|
|
@@ -1656,7 +1656,7 @@ function streamArtifact(record, stream, explicitOffset, maxOutputBytes) {
|
|
|
1656
1656
|
truncated
|
|
1657
1657
|
};
|
|
1658
1658
|
}
|
|
1659
|
-
function
|
|
1659
|
+
function mergedOutputView(record, explicitOffset, maxOutputBytes) {
|
|
1660
1660
|
const totalBytes = record.outputChunks.reduce((total, chunk) => total + chunk.bytes, 0);
|
|
1661
1661
|
const offset = clampOffset(explicitOffset ?? record.outputOffset, totalBytes);
|
|
1662
1662
|
const byteLimit = Math.max(0, Math.floor(maxOutputBytes));
|
|
@@ -1710,7 +1710,7 @@ function ensureRecordOutputCoverage(record) {
|
|
|
1710
1710
|
appendRecordOutput(record, "stdout", record.stdout);
|
|
1711
1711
|
appendRecordOutput(record, "stderr", record.stderr);
|
|
1712
1712
|
}
|
|
1713
|
-
function
|
|
1713
|
+
function commandArtifactFromRecord(record) {
|
|
1714
1714
|
return {
|
|
1715
1715
|
status: record.status,
|
|
1716
1716
|
shellId: record.shellId,
|
|
@@ -1754,7 +1754,7 @@ function commandArtifactMeta(artifact) {
|
|
|
1754
1754
|
} } : {}
|
|
1755
1755
|
};
|
|
1756
1756
|
}
|
|
1757
|
-
function
|
|
1757
|
+
function isCommandArtifact(value) {
|
|
1758
1758
|
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1759
1759
|
const record = value;
|
|
1760
1760
|
return (record.status === "running" || record.status === "exited" || record.status === "aborted") && typeof record.shellId === "string" && typeof record.commandId === "string" && typeof record.startedAt === "number" && typeof record.lastOutputAt === "number" && (typeof record.exitCode === "number" || record.exitCode === null) && typeof record.stdout === "string" && typeof record.stderr === "string";
|
package/dist/storage.mjs
CHANGED
|
@@ -5,7 +5,7 @@ var AgentSessionCommandStorage = class {
|
|
|
5
5
|
constructor(store, agentSessionId) {
|
|
6
6
|
this.store = store;
|
|
7
7
|
validateAgentSessionId(agentSessionId);
|
|
8
|
-
this.agentSessionPrefix =
|
|
8
|
+
this.agentSessionPrefix = `agent-sessions/${agentSessionId}/`;
|
|
9
9
|
}
|
|
10
10
|
readJson(key) {
|
|
11
11
|
return this.store.readJson(this.key(key));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@demicodes/shell",
|
|
3
3
|
"description": "Sandboxable bash engine and Host contract for Demi.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.1",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@demicodes/utils": "^0.
|
|
25
|
+
"@demicodes/utils": "^0.3.1",
|
|
26
26
|
"@demicodes/just-bash": "^3.0.1-demi.5",
|
|
27
27
|
"zod": "^4.0.0"
|
|
28
28
|
},
|