pi-ca-leash 0.10.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/AGENTS.md +77 -0
- package/ARCHITECTURE.md +290 -0
- package/CHANGELOG.md +158 -0
- package/DEVELOPMENT.md +197 -0
- package/KNOWN_LIMITS.md +80 -0
- package/LICENSE +21 -0
- package/README.md +288 -0
- package/extensions/backend-tool-actions.test.ts +59 -0
- package/extensions/backend-tool-actions.ts +31 -0
- package/extensions/command-drivers.test.ts +37 -0
- package/extensions/command-drivers.ts +126 -0
- package/extensions/command-parity.test.ts +560 -0
- package/extensions/command-visibility.test.ts +21 -0
- package/extensions/command-visibility.ts +10 -0
- package/extensions/index.ts +3218 -0
- package/extensions/llm-tools.test.ts +537 -0
- package/extensions/model-catalog.test.ts +34 -0
- package/extensions/model-catalog.ts +173 -0
- package/extensions/peer-history.test.ts +141 -0
- package/extensions/peer-history.ts +90 -0
- package/extensions/peer-naming.test.ts +25 -0
- package/extensions/peer-naming.ts +129 -0
- package/extensions/peer-relay.test.ts +122 -0
- package/extensions/peer-relay.ts +83 -0
- package/extensions/peer-ux.test.ts +239 -0
- package/extensions/peer-ux.ts +327 -0
- package/extensions/persistence.test.ts +68 -0
- package/extensions/persistence.ts +67 -0
- package/extensions/prompts/extension-log-tool.md +5 -0
- package/extensions/prompts/peer-ask-tool.md +5 -0
- package/extensions/prompts/peer-bridge-system.md +4 -0
- package/extensions/prompts/peer-history-tool.md +3 -0
- package/extensions/prompts/peer-init-user-help.md +11 -0
- package/extensions/prompts/peer-init.md +17 -0
- package/extensions/prompts/peer-interrupt-tool.md +2 -0
- package/extensions/prompts/peer-list-tool.md +3 -0
- package/extensions/prompts/peer-no-babysitting.md +6 -0
- package/extensions/prompts/peer-send-tool.md +5 -0
- package/extensions/prompts/peer-start-tool.md +7 -0
- package/extensions/prompts/peer-stop-tool.md +3 -0
- package/extensions/prompts/runtime-models-tool.md +6 -0
- package/extensions/prompts/subagent-list-tool.md +3 -0
- package/extensions/prompts/subagent-run-tool.md +6 -0
- package/extensions/prompts/subagent-status-tool.md +2 -0
- package/extensions/prompts/team-list-tool.md +2 -0
- package/extensions/prompts/team-message-tool.md +2 -0
- package/extensions/prompts/team-spawn-tool.md +5 -0
- package/extensions/prompts/team-stop-tool.md +2 -0
- package/extensions/prompts/team-task-tool.md +3 -0
- package/extensions/prompts.ts +41 -0
- package/extensions/runtime-driver.test.ts +38 -0
- package/extensions/runtime-driver.ts +33 -0
- package/extensions/runtime-safety.test.ts +21 -0
- package/extensions/runtime-safety.ts +49 -0
- package/extensions/support.test.ts +144 -0
- package/extensions/support.ts +205 -0
- package/extensions/tool-inputs.test.ts +45 -0
- package/extensions/tool-inputs.ts +79 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/bridge.d.ts +48 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/bridge.js +406 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/bridge.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/cli.d.ts +2 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/cli.js +18 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/cli.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/index.d.ts +5 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/index.js +5 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/index.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/persistence.d.ts +12 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/persistence.js +31 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/persistence.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/pi-intercom-transport.d.ts +12 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/pi-intercom-transport.js +347 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/pi-intercom-transport.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/types.d.ts +103 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/types.js +2 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/dist/types.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/intercom-bridge/package.json +32 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/cli.d.ts +2 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/cli.js +26 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/cli.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/driver-config.d.ts +4 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/driver-config.js +12 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/driver-config.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/claude-sdk.d.ts +8 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/claude-sdk.js +320 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/claude-sdk.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/codex-cli.d.ts +24 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/codex-cli.js +266 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/codex-cli.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/messages.d.ts +72 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/messages.js +2 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/drivers/messages.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/index.d.ts +6 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/index.js +5 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/index.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/persistence.d.ts +16 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/persistence.js +94 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/persistence.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/runtime.d.ts +31 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/runtime.js +409 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/runtime.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/types.d.ts +185 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/types.js +2 -0
- package/node_modules/@pi-claude-code-agent/runtime/dist/types.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/runtime/package.json +32 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/backend.d.ts +34 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/backend.js +327 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/backend.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/cli.d.ts +2 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/cli.js +17 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/cli.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/index.d.ts +4 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/index.js +4 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/index.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/persistence.d.ts +12 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/persistence.js +81 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/persistence.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/types.d.ts +72 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/types.js +2 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/dist/types.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/subagents-backend/package.json +32 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/backend.d.ts +27 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/backend.js +194 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/backend.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/cli.d.ts +2 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/cli.js +21 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/cli.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/index.d.ts +4 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/index.js +4 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/index.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/persistence.d.ts +8 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/persistence.js +66 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/persistence.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/types.d.ts +41 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/types.js +2 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/dist/types.js.map +1 -0
- package/node_modules/@pi-claude-code-agent/teams-backend/package.json +33 -0
- package/package.json +98 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export interface NormalizedDriverMessageBlock {
|
|
2
|
+
type: string;
|
|
3
|
+
text?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
id?: string;
|
|
6
|
+
input?: unknown;
|
|
7
|
+
content?: unknown;
|
|
8
|
+
isError?: boolean;
|
|
9
|
+
raw?: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface NormalizedDriverUsage {
|
|
12
|
+
inputTokens?: number;
|
|
13
|
+
outputTokens?: number;
|
|
14
|
+
cacheCreationInputTokens?: number;
|
|
15
|
+
cacheReadInputTokens?: number;
|
|
16
|
+
reasoningOutputTokens?: number;
|
|
17
|
+
totalCostUsd?: number;
|
|
18
|
+
contextTokens?: number;
|
|
19
|
+
contextWindow?: number;
|
|
20
|
+
contextPercentage?: number;
|
|
21
|
+
maxOutputTokens?: number;
|
|
22
|
+
raw?: unknown;
|
|
23
|
+
}
|
|
24
|
+
interface NormalizedDriverMessageBase {
|
|
25
|
+
raw?: unknown;
|
|
26
|
+
metadata?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface SystemDriverMessage extends NormalizedDriverMessageBase {
|
|
29
|
+
type: "system";
|
|
30
|
+
subtype?: string;
|
|
31
|
+
sessionId?: string;
|
|
32
|
+
cwd?: string;
|
|
33
|
+
model?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface AssistantDriverMessage extends NormalizedDriverMessageBase {
|
|
36
|
+
type: "assistant";
|
|
37
|
+
blocks: NormalizedDriverMessageBlock[];
|
|
38
|
+
model?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface ToolUseDriverMessage extends NormalizedDriverMessageBase {
|
|
41
|
+
type: "tool_use";
|
|
42
|
+
toolName: string;
|
|
43
|
+
toolUseId?: string;
|
|
44
|
+
input?: unknown;
|
|
45
|
+
}
|
|
46
|
+
export interface ToolResultDriverMessage extends NormalizedDriverMessageBase {
|
|
47
|
+
type: "tool_result";
|
|
48
|
+
role?: "user" | "tool";
|
|
49
|
+
blocks?: NormalizedDriverMessageBlock[];
|
|
50
|
+
toolName: string;
|
|
51
|
+
toolUseId?: string;
|
|
52
|
+
output?: unknown;
|
|
53
|
+
isError?: boolean;
|
|
54
|
+
}
|
|
55
|
+
export interface ResultDriverMessage extends NormalizedDriverMessageBase {
|
|
56
|
+
type: "result";
|
|
57
|
+
ok: boolean;
|
|
58
|
+
summary: string;
|
|
59
|
+
stopReason?: string;
|
|
60
|
+
usage?: NormalizedDriverUsage;
|
|
61
|
+
}
|
|
62
|
+
export interface ErrorDriverMessage extends NormalizedDriverMessageBase {
|
|
63
|
+
type: "error";
|
|
64
|
+
message: string;
|
|
65
|
+
code?: string;
|
|
66
|
+
}
|
|
67
|
+
export interface StreamEventDriverMessage extends NormalizedDriverMessageBase {
|
|
68
|
+
type: "stream_event";
|
|
69
|
+
summary: string;
|
|
70
|
+
}
|
|
71
|
+
export type NormalizedDriverMessage = SystemDriverMessage | AssistantDriverMessage | ToolUseDriverMessage | ToolResultDriverMessage | ResultDriverMessage | ErrorDriverMessage | StreamEventDriverMessage;
|
|
72
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/drivers/messages.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { ClaudeCodeRuntime } from "./runtime.js";
|
|
2
|
+
export { RUNTIME_DRIVER_ENV, parseRuntimeDriverName, resolveRuntimeDriverFromEnv } from "./driver-config.js";
|
|
3
|
+
export { ClaudeSdkDriver, parseClaudeSdkMessage } from "./drivers/claude-sdk.js";
|
|
4
|
+
export { CodexCliDriver, parseCodexCliEvent, buildCodexCliCommand } from "./drivers/codex-cli.js";
|
|
5
|
+
export type { NormalizedDriverMessage } from "./drivers/messages.js";
|
|
6
|
+
export type { DriverEventEnvelope, InterruptResult, ResultEvent, RuntimeDriver, RuntimeDriverName, RuntimeDriverResolver, RuntimeEvent, RuntimeMessage, RuntimeMessageBlock, RuntimeOptions, RuntimeSessionId, RuntimeSessionState, RuntimeStatus, SendMessageInput, StartSessionInput, ToolEvent, TranscriptChunk, } from "./types.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ClaudeCodeRuntime } from "./runtime.js";
|
|
2
|
+
export { RUNTIME_DRIVER_ENV, parseRuntimeDriverName, resolveRuntimeDriverFromEnv } from "./driver-config.js";
|
|
3
|
+
export { ClaudeSdkDriver, parseClaudeSdkMessage } from "./drivers/claude-sdk.js";
|
|
4
|
+
export { CodexCliDriver, parseCodexCliEvent, buildCodexCliCommand } from "./drivers/codex-cli.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AAC7G,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACjF,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RuntimeEvent, RuntimeSessionId, RuntimeStatus, TranscriptChunk } from "./types.js";
|
|
2
|
+
export declare function defaultStorageDir(): string;
|
|
3
|
+
export declare function sessionDir(storageDir: string, sessionId: RuntimeSessionId): string;
|
|
4
|
+
export declare function statePath(storageDir: string, sessionId: RuntimeSessionId): string;
|
|
5
|
+
export declare function eventsPath(storageDir: string, sessionId: RuntimeSessionId): string;
|
|
6
|
+
export declare function transcriptPath(storageDir: string, sessionId: RuntimeSessionId): string;
|
|
7
|
+
export declare function ensureSessionLayout(storageDir: string, sessionId: RuntimeSessionId): Promise<void>;
|
|
8
|
+
export declare function writeState(storageDir: string, status: RuntimeStatus): Promise<void>;
|
|
9
|
+
export declare function readState(storageDir: string, sessionId: RuntimeSessionId): Promise<RuntimeStatus | undefined>;
|
|
10
|
+
export declare function appendJsonLine(path: string, value: unknown): Promise<void>;
|
|
11
|
+
export declare function appendEvent(storageDir: string, event: RuntimeEvent): Promise<void>;
|
|
12
|
+
export declare function readJsonLines<T>(path: string): Promise<T[]>;
|
|
13
|
+
export declare function readEvents(storageDir: string, sessionId: RuntimeSessionId, cursor?: number): Promise<TranscriptChunk>;
|
|
14
|
+
export declare function readTranscript(storageDir: string, sessionId: RuntimeSessionId, cursor?: number): Promise<TranscriptChunk>;
|
|
15
|
+
export declare function tailTranscript(storageDir: string, sessionId: RuntimeSessionId, limit?: number): Promise<RuntimeEvent[]>;
|
|
16
|
+
export declare function listStates(storageDir: string): Promise<RuntimeStatus[]>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { appendFile, mkdir, readFile, readdir, rename, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export function defaultStorageDir() {
|
|
5
|
+
return join(process.cwd(), ".claude-runtime");
|
|
6
|
+
}
|
|
7
|
+
export function sessionDir(storageDir, sessionId) {
|
|
8
|
+
return join(storageDir, "sessions", sessionId);
|
|
9
|
+
}
|
|
10
|
+
export function statePath(storageDir, sessionId) {
|
|
11
|
+
return join(sessionDir(storageDir, sessionId), "state.json");
|
|
12
|
+
}
|
|
13
|
+
export function eventsPath(storageDir, sessionId) {
|
|
14
|
+
return join(sessionDir(storageDir, sessionId), "events.jsonl");
|
|
15
|
+
}
|
|
16
|
+
export function transcriptPath(storageDir, sessionId) {
|
|
17
|
+
return join(sessionDir(storageDir, sessionId), "transcript.jsonl");
|
|
18
|
+
}
|
|
19
|
+
export async function ensureSessionLayout(storageDir, sessionId) {
|
|
20
|
+
await mkdir(join(sessionDir(storageDir, sessionId), "artifacts"), { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
export async function writeState(storageDir, status) {
|
|
23
|
+
const path = statePath(storageDir, status.sessionId);
|
|
24
|
+
await mkdir(dirname(path), { recursive: true });
|
|
25
|
+
const tempPath = `${path}.${randomUUID()}.tmp`;
|
|
26
|
+
await writeFile(tempPath, `${JSON.stringify(status, null, 2)}\n`, "utf8");
|
|
27
|
+
await rename(tempPath, path);
|
|
28
|
+
}
|
|
29
|
+
export async function readState(storageDir, sessionId) {
|
|
30
|
+
try {
|
|
31
|
+
const raw = await readFile(statePath(storageDir, sessionId), "utf8");
|
|
32
|
+
return JSON.parse(raw);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function appendJsonLine(path, value) {
|
|
39
|
+
await mkdir(dirname(path), { recursive: true });
|
|
40
|
+
const line = `${JSON.stringify(value)}\n`;
|
|
41
|
+
await appendFile(path, line, "utf8");
|
|
42
|
+
}
|
|
43
|
+
async function readFileSafe(path) {
|
|
44
|
+
try {
|
|
45
|
+
return await readFile(path, "utf8");
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return "";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export async function appendEvent(storageDir, event) {
|
|
52
|
+
await appendJsonLine(eventsPath(storageDir, event.sessionId), event);
|
|
53
|
+
if (["message", "tool", "result", "error"].includes(event.type)) {
|
|
54
|
+
await appendJsonLine(transcriptPath(storageDir, event.sessionId), event);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function readJsonLines(path) {
|
|
58
|
+
const raw = await readFileSafe(path);
|
|
59
|
+
return raw
|
|
60
|
+
.split("\n")
|
|
61
|
+
.map((line) => line.trim())
|
|
62
|
+
.filter(Boolean)
|
|
63
|
+
.map((line) => JSON.parse(line));
|
|
64
|
+
}
|
|
65
|
+
export async function readEvents(storageDir, sessionId, cursor = 0) {
|
|
66
|
+
const items = await readJsonLines(eventsPath(storageDir, sessionId));
|
|
67
|
+
return {
|
|
68
|
+
items: items.slice(cursor),
|
|
69
|
+
nextCursor: items.length,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export async function readTranscript(storageDir, sessionId, cursor = 0) {
|
|
73
|
+
const items = await readJsonLines(transcriptPath(storageDir, sessionId));
|
|
74
|
+
return {
|
|
75
|
+
items: items.slice(cursor),
|
|
76
|
+
nextCursor: items.length,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export async function tailTranscript(storageDir, sessionId, limit = 20) {
|
|
80
|
+
const items = await readJsonLines(transcriptPath(storageDir, sessionId));
|
|
81
|
+
return items.slice(-limit);
|
|
82
|
+
}
|
|
83
|
+
export async function listStates(storageDir) {
|
|
84
|
+
const base = join(storageDir, "sessions");
|
|
85
|
+
try {
|
|
86
|
+
const entries = await readdir(base, { withFileTypes: true });
|
|
87
|
+
const states = await Promise.all(entries.filter((entry) => entry.isDirectory()).map((entry) => readState(storageDir, entry.name)));
|
|
88
|
+
return states.filter((state) => Boolean(state));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=persistence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"persistence.js","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { InterruptResult, RuntimeEvent, RuntimeOptions, RuntimeSessionId, RuntimeStatus, SendMessageInput, StartSessionInput, TranscriptChunk } from "./types.js";
|
|
2
|
+
export declare class ClaudeCodeRuntime {
|
|
3
|
+
private readonly storageDir;
|
|
4
|
+
private readonly drivers;
|
|
5
|
+
private readonly defaultDriverName;
|
|
6
|
+
private readonly resolveDriverByName;
|
|
7
|
+
private readonly emitter;
|
|
8
|
+
private readonly activeRuns;
|
|
9
|
+
private readonly sequences;
|
|
10
|
+
constructor(options?: RuntimeOptions);
|
|
11
|
+
start(input: StartSessionInput): Promise<RuntimeStatus>;
|
|
12
|
+
send(input: SendMessageInput): Promise<RuntimeStatus>;
|
|
13
|
+
status(sessionId: RuntimeSessionId): Promise<RuntimeStatus | undefined>;
|
|
14
|
+
list(): Promise<RuntimeStatus[]>;
|
|
15
|
+
interrupt(sessionId: RuntimeSessionId): Promise<InterruptResult>;
|
|
16
|
+
stop(sessionId: RuntimeSessionId): Promise<RuntimeStatus>;
|
|
17
|
+
tail(sessionId: RuntimeSessionId, limit?: number): Promise<RuntimeEvent[]>;
|
|
18
|
+
readTranscript(sessionId: RuntimeSessionId, cursor?: number): Promise<TranscriptChunk>;
|
|
19
|
+
events(sessionId: RuntimeSessionId, cursor?: number): Promise<TranscriptChunk>;
|
|
20
|
+
subscribe(listener: (event: RuntimeEvent) => void, sessionId?: RuntimeSessionId): () => void;
|
|
21
|
+
stream(sessionId?: RuntimeSessionId): AsyncIterable<RuntimeEvent>;
|
|
22
|
+
private runSession;
|
|
23
|
+
private waitForRunCompletion;
|
|
24
|
+
private handleDriverEvent;
|
|
25
|
+
private emitError;
|
|
26
|
+
private resolveDriver;
|
|
27
|
+
private patchStatus;
|
|
28
|
+
private requireSession;
|
|
29
|
+
private emitEvent;
|
|
30
|
+
private nextSequence;
|
|
31
|
+
}
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { EventEmitter, on } from "node:events";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { appendEvent, defaultStorageDir, ensureSessionLayout, listStates, readEvents, readState, readTranscript, tailTranscript, writeState, } from "./persistence.js";
|
|
5
|
+
import { ClaudeSdkDriver, parseClaudeSdkMessage } from "./drivers/claude-sdk.js";
|
|
6
|
+
import { CodexCliDriver } from "./drivers/codex-cli.js";
|
|
7
|
+
export class ClaudeCodeRuntime {
|
|
8
|
+
storageDir;
|
|
9
|
+
drivers = new Map();
|
|
10
|
+
defaultDriverName;
|
|
11
|
+
resolveDriverByName;
|
|
12
|
+
emitter = new EventEmitter();
|
|
13
|
+
activeRuns = new Map();
|
|
14
|
+
sequences = new Map();
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.storageDir = resolve(options.storageDir ?? defaultStorageDir());
|
|
17
|
+
const defaultClaudeDriver = new ClaudeSdkDriver();
|
|
18
|
+
const defaultCodexDriver = new CodexCliDriver();
|
|
19
|
+
this.drivers.set(defaultClaudeDriver.name, defaultClaudeDriver);
|
|
20
|
+
this.drivers.set(defaultCodexDriver.name, defaultCodexDriver);
|
|
21
|
+
if (options.drivers) {
|
|
22
|
+
for (const driver of Object.values(options.drivers)) {
|
|
23
|
+
if (driver) {
|
|
24
|
+
this.drivers.set(driver.name, driver);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (options.driver) {
|
|
29
|
+
this.drivers.set(options.driver.name, options.driver);
|
|
30
|
+
}
|
|
31
|
+
this.defaultDriverName = options.defaultDriver ?? options.driver?.name ?? "claude-sdk";
|
|
32
|
+
this.resolveDriverByName = options.resolveDriver ?? ((name) => {
|
|
33
|
+
const driver = this.drivers.get(name);
|
|
34
|
+
if (!driver) {
|
|
35
|
+
throw new Error(`No runtime driver registered for ${name}`);
|
|
36
|
+
}
|
|
37
|
+
return driver;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async start(input) {
|
|
41
|
+
const sessionId = randomUUID();
|
|
42
|
+
const driverName = input.driver ?? this.defaultDriverName;
|
|
43
|
+
const driver = this.resolveDriver(driverName);
|
|
44
|
+
const cwd = resolve(input.cwd ?? process.cwd());
|
|
45
|
+
const now = new Date().toISOString();
|
|
46
|
+
const status = {
|
|
47
|
+
sessionId,
|
|
48
|
+
driver: driver.name,
|
|
49
|
+
driverSessionId: sessionId,
|
|
50
|
+
state: "starting",
|
|
51
|
+
cwd,
|
|
52
|
+
model: input.model,
|
|
53
|
+
name: input.name,
|
|
54
|
+
createdAt: now,
|
|
55
|
+
updatedAt: now,
|
|
56
|
+
lastActivityAt: now,
|
|
57
|
+
raw: {},
|
|
58
|
+
};
|
|
59
|
+
await ensureSessionLayout(this.storageDir, sessionId);
|
|
60
|
+
await writeState(this.storageDir, status);
|
|
61
|
+
await this.emitEvent({ type: "session.created", sessionId, state: "starting" }, status);
|
|
62
|
+
await this.runSession(status, {
|
|
63
|
+
prompt: input.prompt,
|
|
64
|
+
appendSystemPrompt: input.appendSystemPrompt,
|
|
65
|
+
model: input.model,
|
|
66
|
+
name: input.name,
|
|
67
|
+
permissionMode: input.permissionMode,
|
|
68
|
+
tools: input.tools,
|
|
69
|
+
additionalDirectories: input.additionalDirectories,
|
|
70
|
+
env: input.env,
|
|
71
|
+
});
|
|
72
|
+
return (await this.status(sessionId));
|
|
73
|
+
}
|
|
74
|
+
async send(input) {
|
|
75
|
+
const status = await this.requireSession(input.sessionId);
|
|
76
|
+
if (status.state === "stopped") {
|
|
77
|
+
throw new Error(`Session ${input.sessionId} stopped`);
|
|
78
|
+
}
|
|
79
|
+
if (this.activeRuns.has(input.sessionId)) {
|
|
80
|
+
throw new Error(`Session ${input.sessionId} already active`);
|
|
81
|
+
}
|
|
82
|
+
await this.runSession(status, {
|
|
83
|
+
prompt: input.message,
|
|
84
|
+
appendSystemPrompt: input.appendSystemPrompt,
|
|
85
|
+
model: input.model ?? status.model,
|
|
86
|
+
name: status.name,
|
|
87
|
+
env: input.env,
|
|
88
|
+
resumeSessionId: status.driverSessionId,
|
|
89
|
+
});
|
|
90
|
+
return (await this.status(input.sessionId));
|
|
91
|
+
}
|
|
92
|
+
async status(sessionId) {
|
|
93
|
+
return readState(this.storageDir, sessionId);
|
|
94
|
+
}
|
|
95
|
+
async list() {
|
|
96
|
+
const states = await listStates(this.storageDir);
|
|
97
|
+
return states.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
98
|
+
}
|
|
99
|
+
async interrupt(sessionId) {
|
|
100
|
+
const status = await this.requireSession(sessionId);
|
|
101
|
+
if (status.state === "stopped") {
|
|
102
|
+
return { sessionId, interrupted: false, reason: "already-stopped" };
|
|
103
|
+
}
|
|
104
|
+
const active = this.activeRuns.get(sessionId);
|
|
105
|
+
if (!active) {
|
|
106
|
+
return { sessionId, interrupted: false, reason: "no-active-run" };
|
|
107
|
+
}
|
|
108
|
+
active.handle.kill("SIGINT");
|
|
109
|
+
const next = await this.patchStatus(sessionId, {
|
|
110
|
+
state: "interrupted",
|
|
111
|
+
interruptedAt: new Date().toISOString(),
|
|
112
|
+
});
|
|
113
|
+
await this.emitEvent({ type: "session.updated", sessionId, state: next.state, patch: { interrupted: true } }, next);
|
|
114
|
+
return { sessionId, interrupted: true, signal: "SIGINT", reason: "signalled" };
|
|
115
|
+
}
|
|
116
|
+
async stop(sessionId) {
|
|
117
|
+
await this.requireSession(sessionId);
|
|
118
|
+
const active = this.activeRuns.get(sessionId);
|
|
119
|
+
if (active) {
|
|
120
|
+
active.handle.kill("SIGINT");
|
|
121
|
+
}
|
|
122
|
+
const next = await this.patchStatus(sessionId, {
|
|
123
|
+
state: "stopped",
|
|
124
|
+
stopRequested: true,
|
|
125
|
+
stoppedAt: new Date().toISOString(),
|
|
126
|
+
activeRunId: undefined,
|
|
127
|
+
});
|
|
128
|
+
await this.emitEvent({ type: "session.stopped", sessionId, state: "stopped" }, next);
|
|
129
|
+
return next;
|
|
130
|
+
}
|
|
131
|
+
async tail(sessionId, limit = 20) {
|
|
132
|
+
return tailTranscript(this.storageDir, sessionId, limit);
|
|
133
|
+
}
|
|
134
|
+
async readTranscript(sessionId, cursor = 0) {
|
|
135
|
+
return readTranscript(this.storageDir, sessionId, cursor);
|
|
136
|
+
}
|
|
137
|
+
async events(sessionId, cursor = 0) {
|
|
138
|
+
return readEvents(this.storageDir, sessionId, cursor);
|
|
139
|
+
}
|
|
140
|
+
subscribe(listener, sessionId) {
|
|
141
|
+
const wrapped = (event) => {
|
|
142
|
+
if (!sessionId || event.sessionId === sessionId) {
|
|
143
|
+
listener(event);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
this.emitter.on("event", wrapped);
|
|
147
|
+
return () => this.emitter.off("event", wrapped);
|
|
148
|
+
}
|
|
149
|
+
async *stream(sessionId) {
|
|
150
|
+
for await (const [event] of on(this.emitter, "event")) {
|
|
151
|
+
if (!sessionId || event.sessionId === sessionId) {
|
|
152
|
+
yield event;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async runSession(status, input) {
|
|
157
|
+
const runId = randomUUID();
|
|
158
|
+
const next = await this.patchStatus(status.sessionId, {
|
|
159
|
+
state: input.resumeSessionId ? "running" : "starting",
|
|
160
|
+
activeRunId: runId,
|
|
161
|
+
stopRequested: false,
|
|
162
|
+
model: input.model ?? status.model,
|
|
163
|
+
name: input.name ?? status.name,
|
|
164
|
+
});
|
|
165
|
+
await this.emitEvent({ type: "session.updated", sessionId: status.sessionId, state: next.state, patch: { activeRunId: runId } }, next);
|
|
166
|
+
const driver = this.resolveDriver(status.driver);
|
|
167
|
+
const handle = driver.run({
|
|
168
|
+
sessionId: status.sessionId,
|
|
169
|
+
prompt: input.prompt,
|
|
170
|
+
cwd: next.cwd,
|
|
171
|
+
model: input.model,
|
|
172
|
+
name: input.name,
|
|
173
|
+
appendSystemPrompt: input.appendSystemPrompt,
|
|
174
|
+
permissionMode: input.permissionMode,
|
|
175
|
+
tools: input.tools,
|
|
176
|
+
additionalDirectories: input.additionalDirectories,
|
|
177
|
+
env: input.env,
|
|
178
|
+
resumeSessionId: input.resumeSessionId,
|
|
179
|
+
}, async (event) => {
|
|
180
|
+
await this.handleDriverEvent(status.sessionId, event);
|
|
181
|
+
});
|
|
182
|
+
this.activeRuns.set(status.sessionId, { runId, handle, requestedModel: input.model });
|
|
183
|
+
void this.waitForRunCompletion(status.sessionId, runId, handle);
|
|
184
|
+
}
|
|
185
|
+
async waitForRunCompletion(sessionId, runId, handle) {
|
|
186
|
+
const { code, signal } = await handle.done;
|
|
187
|
+
const current = await this.requireSession(sessionId);
|
|
188
|
+
const stillActive = current.activeRunId === runId;
|
|
189
|
+
if (stillActive) {
|
|
190
|
+
this.activeRuns.delete(sessionId);
|
|
191
|
+
}
|
|
192
|
+
if (!stillActive) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (current.stopRequested || current.state === "stopped") {
|
|
196
|
+
const stopped = await this.patchStatus(sessionId, {
|
|
197
|
+
state: "stopped",
|
|
198
|
+
stoppedAt: current.stoppedAt ?? new Date().toISOString(),
|
|
199
|
+
activeRunId: undefined,
|
|
200
|
+
});
|
|
201
|
+
await this.emitEvent({ type: "session.stopped", sessionId, state: "stopped" }, stopped);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (signal === "SIGINT" || current.state === "interrupted") {
|
|
205
|
+
const interrupted = await this.patchStatus(sessionId, {
|
|
206
|
+
state: "interrupted",
|
|
207
|
+
interruptedAt: current.interruptedAt ?? new Date().toISOString(),
|
|
208
|
+
activeRunId: undefined,
|
|
209
|
+
});
|
|
210
|
+
await this.emitEvent({ type: "session.idle", sessionId, state: "interrupted" }, interrupted);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (code && code !== 0) {
|
|
214
|
+
const failed = await this.patchStatus(sessionId, {
|
|
215
|
+
state: "failed",
|
|
216
|
+
activeRunId: undefined,
|
|
217
|
+
lastError: current.lastError ?? { message: `Driver exited with code ${code}` },
|
|
218
|
+
});
|
|
219
|
+
await this.emitEvent({ type: "session.stopped", sessionId, state: "failed" }, failed);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const idle = await this.patchStatus(sessionId, {
|
|
223
|
+
state: "idle",
|
|
224
|
+
completedAt: new Date().toISOString(),
|
|
225
|
+
activeRunId: undefined,
|
|
226
|
+
});
|
|
227
|
+
await this.emitEvent({ type: "session.idle", sessionId, state: "idle" }, idle);
|
|
228
|
+
}
|
|
229
|
+
async handleDriverEvent(sessionId, envelope) {
|
|
230
|
+
if (envelope.type === "raw") {
|
|
231
|
+
for (const message of parseClaudeSdkMessage(envelope.payload)) {
|
|
232
|
+
await this.handleDriverEvent(sessionId, { type: "message", payload: message });
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (envelope.type === "error") {
|
|
237
|
+
await this.emitError(sessionId, envelope.payload.message, envelope.payload.code, envelope.payload.raw ?? envelope.payload);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
const current = await this.requireSession(sessionId);
|
|
241
|
+
const message = envelope.payload;
|
|
242
|
+
switch (message.type) {
|
|
243
|
+
case "system": {
|
|
244
|
+
if (message.subtype !== "init") {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const active = this.activeRuns.get(sessionId);
|
|
248
|
+
const updated = await this.patchStatus(sessionId, {
|
|
249
|
+
driverSessionId: message.sessionId ?? current.driverSessionId,
|
|
250
|
+
model: active?.requestedModel ?? message.model ?? current.model,
|
|
251
|
+
raw: {
|
|
252
|
+
...(current.raw ?? {}),
|
|
253
|
+
init: message.raw ?? message,
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
await this.emitEvent({
|
|
257
|
+
type: "session.updated",
|
|
258
|
+
sessionId,
|
|
259
|
+
state: updated.state,
|
|
260
|
+
patch: {
|
|
261
|
+
driverSessionId: updated.driverSessionId,
|
|
262
|
+
model: updated.model,
|
|
263
|
+
},
|
|
264
|
+
raw: message.raw ?? message,
|
|
265
|
+
}, updated);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
case "assistant": {
|
|
269
|
+
const updated = await this.patchStatus(sessionId, {});
|
|
270
|
+
await this.emitEvent({
|
|
271
|
+
type: "message",
|
|
272
|
+
sessionId,
|
|
273
|
+
role: "assistant",
|
|
274
|
+
message: { role: "assistant", blocks: toRuntimeBlocks(message.blocks), raw: message.raw ?? message },
|
|
275
|
+
}, updated);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
case "tool_use": {
|
|
279
|
+
const updated = await this.patchStatus(sessionId, {});
|
|
280
|
+
await this.emitEvent({
|
|
281
|
+
type: "tool",
|
|
282
|
+
sessionId,
|
|
283
|
+
phase: "requested",
|
|
284
|
+
toolName: message.toolName,
|
|
285
|
+
toolUseId: message.toolUseId,
|
|
286
|
+
input: message.input,
|
|
287
|
+
raw: message.raw ?? message,
|
|
288
|
+
}, updated);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
case "tool_result": {
|
|
292
|
+
const updated = await this.patchStatus(sessionId, {});
|
|
293
|
+
if (message.blocks?.length) {
|
|
294
|
+
const role = message.role ?? "user";
|
|
295
|
+
await this.emitEvent({
|
|
296
|
+
type: "message",
|
|
297
|
+
sessionId,
|
|
298
|
+
role,
|
|
299
|
+
message: { role, blocks: toRuntimeBlocks(message.blocks), raw: message.raw ?? message },
|
|
300
|
+
}, updated);
|
|
301
|
+
}
|
|
302
|
+
await this.emitEvent({
|
|
303
|
+
type: "tool",
|
|
304
|
+
sessionId,
|
|
305
|
+
phase: "completed",
|
|
306
|
+
toolName: message.toolName,
|
|
307
|
+
toolUseId: message.toolUseId,
|
|
308
|
+
output: message.output,
|
|
309
|
+
isError: message.isError,
|
|
310
|
+
raw: message.raw ?? message,
|
|
311
|
+
}, updated);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
case "result": {
|
|
315
|
+
const updated = await this.patchStatus(sessionId, {});
|
|
316
|
+
await this.emitEvent({
|
|
317
|
+
type: "result",
|
|
318
|
+
sessionId,
|
|
319
|
+
ok: message.ok,
|
|
320
|
+
summary: message.summary,
|
|
321
|
+
stopReason: message.stopReason,
|
|
322
|
+
usage: message.usage,
|
|
323
|
+
raw: message.raw ?? message,
|
|
324
|
+
}, updated);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
case "error": {
|
|
328
|
+
await this.emitError(sessionId, message.message, message.code, message.raw ?? message);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
case "stream_event":
|
|
332
|
+
return;
|
|
333
|
+
default:
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async emitError(sessionId, message, code, raw) {
|
|
338
|
+
const current = await this.patchStatus(sessionId, {
|
|
339
|
+
lastError: {
|
|
340
|
+
message: message ?? "Unknown driver error",
|
|
341
|
+
code,
|
|
342
|
+
},
|
|
343
|
+
});
|
|
344
|
+
await this.emitEvent({
|
|
345
|
+
type: "error",
|
|
346
|
+
sessionId,
|
|
347
|
+
message: message ?? "Unknown driver error",
|
|
348
|
+
code,
|
|
349
|
+
raw,
|
|
350
|
+
}, current);
|
|
351
|
+
}
|
|
352
|
+
resolveDriver(name) {
|
|
353
|
+
return this.resolveDriverByName(name);
|
|
354
|
+
}
|
|
355
|
+
async patchStatus(sessionId, patch) {
|
|
356
|
+
const current = await this.requireSession(sessionId);
|
|
357
|
+
const next = {
|
|
358
|
+
...current,
|
|
359
|
+
...patch,
|
|
360
|
+
updatedAt: new Date().toISOString(),
|
|
361
|
+
lastActivityAt: new Date().toISOString(),
|
|
362
|
+
};
|
|
363
|
+
await writeState(this.storageDir, next);
|
|
364
|
+
return next;
|
|
365
|
+
}
|
|
366
|
+
async requireSession(sessionId) {
|
|
367
|
+
const status = await readState(this.storageDir, sessionId);
|
|
368
|
+
if (!status) {
|
|
369
|
+
throw new Error(`Unknown session ${sessionId}`);
|
|
370
|
+
}
|
|
371
|
+
return status;
|
|
372
|
+
}
|
|
373
|
+
async emitEvent(partial, status) {
|
|
374
|
+
const sequence = (await this.nextSequence(status.sessionId)) + 1;
|
|
375
|
+
this.sequences.set(status.sessionId, sequence);
|
|
376
|
+
const event = {
|
|
377
|
+
id: randomUUID(),
|
|
378
|
+
sequence,
|
|
379
|
+
timestamp: new Date().toISOString(),
|
|
380
|
+
...partial,
|
|
381
|
+
};
|
|
382
|
+
await appendEvent(this.storageDir, event);
|
|
383
|
+
this.emitter.emit("event", event);
|
|
384
|
+
return event;
|
|
385
|
+
}
|
|
386
|
+
async nextSequence(sessionId) {
|
|
387
|
+
const cached = this.sequences.get(sessionId);
|
|
388
|
+
if (typeof cached === "number") {
|
|
389
|
+
return cached;
|
|
390
|
+
}
|
|
391
|
+
const existing = await readEvents(this.storageDir, sessionId, 0);
|
|
392
|
+
const max = existing.items.at(-1)?.sequence ?? 0;
|
|
393
|
+
this.sequences.set(sessionId, max);
|
|
394
|
+
return max;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
function toRuntimeBlocks(blocks) {
|
|
398
|
+
return blocks.map((block) => ({
|
|
399
|
+
type: block.type,
|
|
400
|
+
text: block.text,
|
|
401
|
+
name: block.name,
|
|
402
|
+
id: block.id,
|
|
403
|
+
input: block.input,
|
|
404
|
+
content: block.content,
|
|
405
|
+
isError: block.isError,
|
|
406
|
+
raw: block.raw,
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=runtime.js.map
|