@anhlp/alp-protocol 0.0.0-stage → 0.0.1-beta.4
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 +202 -0
- package/NOTICE +7 -0
- package/README.md +16 -2
- package/conformance/cases.json +951 -0
- package/conformance/run.d.mts +32 -0
- package/conformance/run.mjs +403 -0
- package/dist/connection.d.ts +13 -0
- package/dist/connection.js +0 -0
- package/dist/contract-v1.d.ts +8783 -0
- package/dist/contract-v1.js +5 -0
- package/dist/contract.d.ts +25 -0
- package/dist/contract.js +28 -0
- package/dist/dto.d.ts +104 -0
- package/dist/dto.js +0 -0
- package/dist/events.d.ts +354 -0
- package/dist/events.js +0 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +2 -0
- package/dist/public.d.ts +1 -0
- package/dist/public.js +2 -0
- package/dist/rpc.d.ts +1210 -0
- package/dist/rpc.js +0 -0
- package/dist/schedule.d.ts +43 -0
- package/dist/schedule.js +0 -0
- package/dist/session.d.ts +285 -0
- package/dist/session.js +0 -0
- package/dist/validate.d.ts +12 -0
- package/dist/validate.js +151 -0
- package/dist/wire.d.ts +192 -0
- package/dist/wire.js +137 -0
- package/fixtures/ipc-v1.jsonl +4 -0
- package/fixtures/v1/cases.json +7166 -0
- package/package.json +127 -4
- package/schema/ipc-v1.json +3271 -0
- package/schema/methods-v1.json +8678 -0
- package/src/connection.ts +13 -0
- package/src/contract-v1.js +2 -0
- package/src/contract.js +44 -0
- package/src/dto.ts +78 -0
- package/src/events.ts +207 -0
- package/src/index.ts +7 -0
- package/src/public.js +2 -0
- package/src/rpc.ts +272 -0
- package/src/schedule.ts +43 -0
- package/src/session.ts +213 -0
- package/src/validate.js +115 -0
- package/src/wire.js +213 -0
package/dist/wire.d.ts
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The decoded bytes one attachment.chunk may carry over frames of at most `maxFrame` bytes:
|
|
3
|
+
* `maxChunkBytes`, lowered so its base64 and ATTACHMENT_CHUNK_OVERHEAD fit. At least 1 byte.
|
|
4
|
+
* @param {number} [maxFrame] @param {number} [maxChunkBytes]
|
|
5
|
+
*/
|
|
6
|
+
export function attachmentChunkBytes(maxFrame?: number, maxChunkBytes?: number): number;
|
|
7
|
+
/**
|
|
8
|
+
* Whether a path relative to a workspace names something workspace.files.list and
|
|
9
|
+
* workspace.files.read never show, whatever the grant: a segment that is a blocked directory, or
|
|
10
|
+
* any segment (file or directory) named as a secret is (BLOCKED_* above). Compared without case,
|
|
11
|
+
* as macOS and Windows file systems name files. alpd and the gateway both check it; alpd also
|
|
12
|
+
* refuses a file whose content holds a private key, whatever its name.
|
|
13
|
+
* @param {string} relative a path relative to the workspace, '/'-separated
|
|
14
|
+
*/
|
|
15
|
+
export function blockedFilePath(relative: string): boolean;
|
|
16
|
+
/** UTF-8 socket writers use one newline-terminated JSON value per frame. @param {unknown} value */
|
|
17
|
+
export function encodeFrame(value: unknown): string;
|
|
18
|
+
/** Parse one complete frame. Buffering and failure handling belong to the socket owner. @param {string} line @returns {unknown} */
|
|
19
|
+
export function decodeFrame(line: string): unknown;
|
|
20
|
+
/** One argument for a POSIX shell: as it is when nothing in it is special, else in single quotes. @param {string} arg */
|
|
21
|
+
export function quotePosix(arg: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* One argument for cmd.exe: as it is when nothing in it is special; else in double quotes for the C
|
|
24
|
+
* runtime's split (backslashes before the closing quote doubled), with every cmd.exe metacharacter,
|
|
25
|
+
* the quotes too, escaped with ^ for the interactive parse; the shim's second parse then sees one
|
|
26
|
+
* quoted argument, inside which `&|<>()^` are literal. `"`, `%` and `!` are refused (see above).
|
|
27
|
+
* @param {string} arg
|
|
28
|
+
*/
|
|
29
|
+
export function quoteWindows(arg: string): string;
|
|
30
|
+
/** argv as one command line. @param {string[]} argv @param {'posix' | 'windows'} platform */
|
|
31
|
+
export function commandLine(argv: string[], platform: "posix" | "windows"): string;
|
|
32
|
+
/**
|
|
33
|
+
* session.resumeCommand's answer for a session: `alp attach <sessionId>`, which follows the session in
|
|
34
|
+
* a terminal (a closed one says how to send it a message). ALP keeps the orchestration, so it is never
|
|
35
|
+
* a native runtime's own resume command.
|
|
36
|
+
* @param {string} sessionId @param {'posix' | 'windows'} platform the daemon host's
|
|
37
|
+
*/
|
|
38
|
+
export function resumeCommandOf(sessionId: string, platform: "posix" | "windows"): {
|
|
39
|
+
argv: string[];
|
|
40
|
+
posix: string;
|
|
41
|
+
windows: string;
|
|
42
|
+
display: string;
|
|
43
|
+
platform: "posix" | "windows";
|
|
44
|
+
};
|
|
45
|
+
/** Wire protocol of alpd (plans/reference/ALPD.md §3). Bump only for incompatible changes. */
|
|
46
|
+
export const PROTOCOL_VERSION: 1;
|
|
47
|
+
export const MAX_FRAME: number;
|
|
48
|
+
export class AlpRpcError extends Error {
|
|
49
|
+
constructor(error: any);
|
|
50
|
+
code: any;
|
|
51
|
+
data: any;
|
|
52
|
+
}
|
|
53
|
+
export const ERROR: Readonly<{
|
|
54
|
+
failed: 1000;
|
|
55
|
+
notFound: 1001;
|
|
56
|
+
forbidden: 1003;
|
|
57
|
+
conflict: 1004;
|
|
58
|
+
unavailable: 1005;
|
|
59
|
+
protocol: 1006;
|
|
60
|
+
}>;
|
|
61
|
+
/** The JSON-RPC 2.0 codes alpd answers with (docs/protocol/alp-json-rpc-v1.md §5). */
|
|
62
|
+
export const RPC_ERROR: Readonly<{
|
|
63
|
+
parseError: -32700;
|
|
64
|
+
invalidRequest: -32600;
|
|
65
|
+
methodNotFound: -32601;
|
|
66
|
+
invalidParams: -32602;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* Features a daemon lists in its daemon.hello result. A client uses a feature only when listed;
|
|
70
|
+
* a daemon that lists none (one older than this list) has none of them. Unknown names are ignored.
|
|
71
|
+
*/
|
|
72
|
+
export const CAPABILITY: Readonly<{
|
|
73
|
+
sessionInterruptTurn: "session.interrupt.turn";
|
|
74
|
+
attachAfter: "attach.after";
|
|
75
|
+
sessionHistory: "session.history";
|
|
76
|
+
workspaceRegistry: "workspace.registry";
|
|
77
|
+
workspaceWorktree: "workspace.worktree";
|
|
78
|
+
workspaceGit: "workspace.git";
|
|
79
|
+
promptReceipts: "prompt.receipts";
|
|
80
|
+
extensionMethods: "extension.methods";
|
|
81
|
+
team: "team";
|
|
82
|
+
task: "task";
|
|
83
|
+
library: "library";
|
|
84
|
+
libraryProjectWrite: "library.project.write";
|
|
85
|
+
settingsLanguage: "settings.language";
|
|
86
|
+
sessionListPage: "session.list.page";
|
|
87
|
+
questionWatch: "question.watch";
|
|
88
|
+
questionWatchTurns: "question.watch.turns";
|
|
89
|
+
sessionAttention: "session.attention";
|
|
90
|
+
workspaceFiles: "workspace.files";
|
|
91
|
+
workspaceFilesRead: "workspace.files.read";
|
|
92
|
+
promptImages: "prompt.images";
|
|
93
|
+
schedule: "schedule";
|
|
94
|
+
attachments: "attachments";
|
|
95
|
+
sessionFork: "session.fork";
|
|
96
|
+
sessionRewind: "session.rewind";
|
|
97
|
+
sessionReload: "session.reload";
|
|
98
|
+
sessionResumeCommand: "session.resumeCommand";
|
|
99
|
+
sessionConfigureLive: "session.configure.live";
|
|
100
|
+
terminal: "terminal";
|
|
101
|
+
}>;
|
|
102
|
+
/**
|
|
103
|
+
* What alpd takes as images in one session.prompt (capability `prompt.images`): inline base64 of
|
|
104
|
+
* these types only, never a path or URL; at most `maxCount` of them, each at most `maxBytes`
|
|
105
|
+
* decoded, and `maxTotalBytes` together, so their base64 fits one MAX_FRAME. A gateway may allow
|
|
106
|
+
* less (docs/alp/remote-gateway.md).
|
|
107
|
+
*/
|
|
108
|
+
export const PROMPT_IMAGES: Readonly<{
|
|
109
|
+
mimeTypes: readonly string[];
|
|
110
|
+
maxCount: 8;
|
|
111
|
+
maxBytes: number;
|
|
112
|
+
maxTotalBytes: number;
|
|
113
|
+
}>;
|
|
114
|
+
export function base64Bytes(data: string): number;
|
|
115
|
+
export const RPC_METHODS: readonly string[];
|
|
116
|
+
/**
|
|
117
|
+
* Limits of workspace.files.list and workspace.files.read (capability `workspace.files.read`,
|
|
118
|
+
* docs/protocol/alp-json-rpc-v1.md §12.4): a file of at most `maxReadBytes`, a listing of at most
|
|
119
|
+
* `maxListEntries` entries, a relative path of at most `maxPathLength` characters.
|
|
120
|
+
*/
|
|
121
|
+
export const WORKSPACE_FILES: Readonly<{
|
|
122
|
+
maxReadBytes: number;
|
|
123
|
+
maxListEntries: 1000;
|
|
124
|
+
maxPathLength: 4096;
|
|
125
|
+
}>;
|
|
126
|
+
/**
|
|
127
|
+
* Limits of workspace.git.status and workspace.git.diff (capability `workspace.git`,
|
|
128
|
+
* docs/protocol/alp-json-rpc-v1.md §12.5): one file's patch at most `maxFileBytes`, one page's
|
|
129
|
+
* patches at most `maxTotalBytes` (then `nextCursor`), at most `maxUntracked` untracked files with
|
|
130
|
+
* content, `maxStatusFiles` status rows, a base branch name of at most `maxRefLength` characters.
|
|
131
|
+
* A gateway may lower them; never raise them.
|
|
132
|
+
*/
|
|
133
|
+
export const WORKSPACE_GIT: Readonly<{
|
|
134
|
+
maxFileBytes: number;
|
|
135
|
+
maxTotalBytes: number;
|
|
136
|
+
maxUntracked: 200;
|
|
137
|
+
maxStatusFiles: 1000;
|
|
138
|
+
maxRefLength: 255;
|
|
139
|
+
}>;
|
|
140
|
+
/**
|
|
141
|
+
* Limits of non-image attachments (capability `attachments`, docs/protocol/alp-json-rpc-v1.md
|
|
142
|
+
* §12.6): an object of at most `maxObjectBytes`; a prompt of at most `maxPerPrompt` attachments and
|
|
143
|
+
* `maxPromptBytes` together; chunks of at most `maxChunkBytes` decoded (lowered further by
|
|
144
|
+
* attachmentChunkBytes to fit a frame); `maxActiveUploads` incomplete uploads and
|
|
145
|
+
* `maxPrincipalBytes` stored per principal, `maxDaemonBytes` per daemon, retained references
|
|
146
|
+
* included. An incomplete upload expires after `uploadTimeoutMs`; an unreferenced committed object
|
|
147
|
+
* is purged after `unreferencedRetentionMs`, one whose references were all released after
|
|
148
|
+
* `releasedGraceMs`. A display name is at most `maxNameBytes` UTF-8 bytes. An operator or a gateway
|
|
149
|
+
* may lower them, never raise them; daemon.hello advertises the effective ones (`attachmentLimits`).
|
|
150
|
+
*/
|
|
151
|
+
export const ATTACHMENTS: Readonly<{
|
|
152
|
+
maxObjectBytes: number;
|
|
153
|
+
maxPerPrompt: 8;
|
|
154
|
+
maxPromptBytes: number;
|
|
155
|
+
maxChunkBytes: number;
|
|
156
|
+
maxActiveUploads: 2;
|
|
157
|
+
maxPrincipalBytes: number;
|
|
158
|
+
maxDaemonBytes: number;
|
|
159
|
+
uploadTimeoutMs: number;
|
|
160
|
+
unreferencedRetentionMs: number;
|
|
161
|
+
releasedGraceMs: number;
|
|
162
|
+
maxNameBytes: 255;
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* Limits of terminals (capability `terminal`, docs/protocol/alp-json-rpc-v1.md §12.10): at most
|
|
166
|
+
* `maxPerPrincipal` running terminals per principal and `maxPerDaemon` per daemon; a
|
|
167
|
+
* terminal.input of at most `maxInputBytes` UTF-8 bytes; output events of at most
|
|
168
|
+
* `maxOutputFrameBytes` bytes of data; `replayBytes` of output kept per terminal for attach; a
|
|
169
|
+
* terminal ends after `idleMs` without input or output, after `maxLifetimeMs` in any case, and
|
|
170
|
+
* `reconnectMs` after its stream detached unless attached again. Sizes are `minCols`..`maxCols`
|
|
171
|
+
* columns and `minRows`..`maxRows` rows. An operator or a gateway may lower the first eight, never
|
|
172
|
+
* raise them; daemon.hello advertises the effective ones (`terminalLimits`).
|
|
173
|
+
*/
|
|
174
|
+
export const TERMINAL: Readonly<{
|
|
175
|
+
maxPerPrincipal: 4;
|
|
176
|
+
maxPerDaemon: 16;
|
|
177
|
+
maxInputBytes: number;
|
|
178
|
+
maxOutputFrameBytes: number;
|
|
179
|
+
replayBytes: number;
|
|
180
|
+
idleMs: number;
|
|
181
|
+
maxLifetimeMs: number;
|
|
182
|
+
reconnectMs: number;
|
|
183
|
+
minCols: 2;
|
|
184
|
+
maxCols: 500;
|
|
185
|
+
minRows: 1;
|
|
186
|
+
maxRows: 200;
|
|
187
|
+
}>;
|
|
188
|
+
/** The TERMINAL limits a daemon.hello `terminalLimits` carries (the size bounds are fixed). */
|
|
189
|
+
export const TERMINAL_LIMIT_KEYS: readonly string[];
|
|
190
|
+
export function isTerminalId(id: unknown): boolean;
|
|
191
|
+
/** Room an attachment.chunk request needs besides its base64 data. */
|
|
192
|
+
export const ATTACHMENT_CHUNK_OVERHEAD: number;
|
package/dist/wire.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// packages/protocol/src/wire.js
|
|
2
|
+
var PROTOCOL_VERSION = 1;
|
|
3
|
+
var MAX_FRAME = 16 * 1024 * 1024;
|
|
4
|
+
var AlpRpcError = class extends Error {
|
|
5
|
+
constructor(error) {
|
|
6
|
+
super(error?.message ?? "ALP daemon request failed");
|
|
7
|
+
this.code = error?.code;
|
|
8
|
+
this.data = error?.data;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var ERROR = Object.freeze({ failed: 1e3, notFound: 1001, forbidden: 1003, conflict: 1004, unavailable: 1005, protocol: 1006 });
|
|
12
|
+
var RPC_ERROR = Object.freeze({ parseError: -32700, invalidRequest: -32600, methodNotFound: -32601, invalidParams: -32602 });
|
|
13
|
+
var CAPABILITY = Object.freeze({ sessionInterruptTurn: "session.interrupt.turn", attachAfter: "attach.after", sessionHistory: "session.history", workspaceRegistry: "workspace.registry", workspaceWorktree: "workspace.worktree", workspaceGit: "workspace.git", promptReceipts: "prompt.receipts", extensionMethods: "extension.methods", team: "team", task: "task", library: "library", libraryProjectWrite: "library.project.write", settingsLanguage: "settings.language", sessionListPage: "session.list.page", questionWatch: "question.watch", questionWatchTurns: "question.watch.turns", sessionAttention: "session.attention", workspaceFiles: "workspace.files", workspaceFilesRead: "workspace.files.read", promptImages: "prompt.images", schedule: "schedule", attachments: "attachments", sessionFork: "session.fork", sessionRewind: "session.rewind", sessionReload: "session.reload", sessionResumeCommand: "session.resumeCommand", sessionConfigureLive: "session.configure.live", terminal: "terminal" });
|
|
14
|
+
var PROMPT_IMAGES = Object.freeze({ mimeTypes: Object.freeze(["image/png", "image/jpeg", "image/gif", "image/webp"]), maxCount: 8, maxBytes: 5 * 1024 * 1024, maxTotalBytes: 11 * 1024 * 1024 });
|
|
15
|
+
var base64Bytes = (data) => Math.floor(data.length * 3 / 4) - (data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0);
|
|
16
|
+
var RPC_METHODS = Object.freeze(["assignment.recall", "attachment.abort", "attachment.begin", "attachment.chunk", "attachment.commit", "attachment.delete", "attachment.get", "board.list", "catalog.get", "daemon.hello", "daemon.pause", "daemon.pauses", "daemon.resume", "daemon.shutdown", "daemon.status", "daemon.usage", "extension.register", "extension.unregister", "library.delete", "library.duplicate", "library.get", "library.list", "library.models", "library.rename", "library.save", "library.skills.set", "project.list", "project.register", "project.remove", "question.answer", "question.list", "question.watch", "registry.owner", "registry.worktree", "schedule.cancel", "schedule.create", "schedule.get", "schedule.list", "session.archive", "session.attach", "session.attention.clear", "session.close", "session.configure", "session.configure.preview", "session.create", "session.fork", "session.get", "session.history", "session.interrupt", "session.list", "session.log", "session.message", "session.preview", "session.prompt", "session.release", "session.reload", "session.rename", "session.resumeCommand", "session.rewind", "session.status", "settings.language.get", "settings.language.set", "task.create", "task.gate.resolve", "task.list", "task.update", "team.list", "terminal.attach", "terminal.close", "terminal.create", "terminal.input", "terminal.list", "terminal.resize", "terminal.revoke", "workspace.files.list", "workspace.files.read", "workspace.files.search", "workspace.get", "workspace.git.diff", "workspace.git.status", "workspace.list", "workspace.update", "workspace.worktree.create", "workspace.worktree.options", "workspace.worktree.remove"]);
|
|
17
|
+
var WORKSPACE_FILES = Object.freeze({ maxReadBytes: 1024 * 1024, maxListEntries: 1e3, maxPathLength: 4096 });
|
|
18
|
+
var WORKSPACE_GIT = Object.freeze({ maxFileBytes: 1024 * 1024, maxTotalBytes: 2 * 1024 * 1024, maxUntracked: 200, maxStatusFiles: 1e3, maxRefLength: 255 });
|
|
19
|
+
var ATTACHMENTS = Object.freeze({
|
|
20
|
+
maxObjectBytes: 20 * 1024 * 1024,
|
|
21
|
+
maxPerPrompt: 8,
|
|
22
|
+
maxPromptBytes: 40 * 1024 * 1024,
|
|
23
|
+
maxChunkBytes: 256 * 1024,
|
|
24
|
+
maxActiveUploads: 2,
|
|
25
|
+
maxPrincipalBytes: 200 * 1024 * 1024,
|
|
26
|
+
maxDaemonBytes: 1024 * 1024 * 1024,
|
|
27
|
+
uploadTimeoutMs: 60 * 60 * 1e3,
|
|
28
|
+
unreferencedRetentionMs: 24 * 60 * 60 * 1e3,
|
|
29
|
+
releasedGraceMs: 7 * 24 * 60 * 60 * 1e3,
|
|
30
|
+
maxNameBytes: 255
|
|
31
|
+
});
|
|
32
|
+
var TERMINAL = Object.freeze({
|
|
33
|
+
maxPerPrincipal: 4,
|
|
34
|
+
maxPerDaemon: 16,
|
|
35
|
+
maxInputBytes: 64 * 1024,
|
|
36
|
+
maxOutputFrameBytes: 64 * 1024,
|
|
37
|
+
replayBytes: 1024 * 1024,
|
|
38
|
+
idleMs: 30 * 60 * 1e3,
|
|
39
|
+
maxLifetimeMs: 8 * 60 * 60 * 1e3,
|
|
40
|
+
reconnectMs: 60 * 1e3,
|
|
41
|
+
minCols: 2,
|
|
42
|
+
maxCols: 500,
|
|
43
|
+
minRows: 1,
|
|
44
|
+
maxRows: 200
|
|
45
|
+
});
|
|
46
|
+
var TERMINAL_LIMIT_KEYS = Object.freeze(["maxPerPrincipal", "maxPerDaemon", "maxInputBytes", "maxOutputFrameBytes", "replayBytes", "idleMs", "maxLifetimeMs", "reconnectMs"]);
|
|
47
|
+
var isTerminalId = (id) => typeof id === "string" && /^term_[0-9a-f]{32}$/.test(id);
|
|
48
|
+
var ATTACHMENT_CHUNK_OVERHEAD = 64 * 1024;
|
|
49
|
+
function attachmentChunkBytes(maxFrame = MAX_FRAME, maxChunkBytes = ATTACHMENTS.maxChunkBytes) {
|
|
50
|
+
const fits = Math.floor((maxFrame - ATTACHMENT_CHUNK_OVERHEAD) * 3 / 4);
|
|
51
|
+
return Math.max(1, Math.min(maxChunkBytes, fits));
|
|
52
|
+
}
|
|
53
|
+
var BLOCKED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".alp", ".ssh", ".gnupg", ".aws", ".azure", ".kube", ".docker", "gcloud"]);
|
|
54
|
+
var BLOCKED_NAMES = /* @__PURE__ */ new Set([".netrc", "_netrc", ".npmrc", ".yarnrc.yml", ".pypirc", ".pgpass", ".htpasswd", ".git-credentials", ".dockercfg", ".boto", ".s3cfg", ".mcp.json", "auth.json", "kubeconfig", "settings.local.json", ".vault-token", "application_default_credentials.json", "token.json"]);
|
|
55
|
+
var BLOCKED_PAIRS = /* @__PURE__ */ new Set(["gh/hosts.yml"]);
|
|
56
|
+
var BLOCKED_STEMS = /* @__PURE__ */ new Set(["credential", "credentials", "secret", "secrets"]);
|
|
57
|
+
var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set(["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs", "py", "go", "rs", "java", "kt", "kts", "rb", "php", "cs", "swift", "c", "cc", "cpp", "h", "hpp", "m", "mm", "scala", "dart", "vue", "svelte"]);
|
|
58
|
+
var BLOCKED_EXTENSIONS = /* @__PURE__ */ new Set(["pem", "key", "p12", "pfx", "p8", "jks", "keystore", "ppk", "der", "gpg", "pgp", "asc", "kdbx", "ovpn", "tfstate", "tfvars"]);
|
|
59
|
+
var BLOCKED_PATTERN = /^(?:\.env|id_|service[-_]?account)|_history$|\.tfstate\.backup$/;
|
|
60
|
+
function blockedFilePath(relative) {
|
|
61
|
+
let previous = "";
|
|
62
|
+
for (const segment of String(relative).toLowerCase().split(/[\\/]/)) {
|
|
63
|
+
if (!segment) continue;
|
|
64
|
+
if (BLOCKED_PAIRS.has(`${previous}/${segment}`)) return true;
|
|
65
|
+
previous = segment;
|
|
66
|
+
if (BLOCKED_DIRECTORIES.has(segment) || BLOCKED_NAMES.has(segment) || BLOCKED_PATTERN.test(segment)) return true;
|
|
67
|
+
const dot = segment.lastIndexOf(".");
|
|
68
|
+
const extension = dot >= 0 ? segment.slice(dot + 1) : "";
|
|
69
|
+
if (BLOCKED_EXTENSIONS.has(extension)) return true;
|
|
70
|
+
const parts = segment.replace(/^\.+/, "").split(".");
|
|
71
|
+
if (parts.length > 1) parts.pop();
|
|
72
|
+
if (parts.some((part) => BLOCKED_STEMS.has(part)) && !SOURCE_EXTENSIONS.has(extension)) return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
function encodeFrame(value) {
|
|
77
|
+
return `${JSON.stringify(value)}
|
|
78
|
+
`;
|
|
79
|
+
}
|
|
80
|
+
function decodeFrame(line) {
|
|
81
|
+
return JSON.parse(line);
|
|
82
|
+
}
|
|
83
|
+
var POSIX_SAFE = /^[A-Za-z0-9_@+:,./-]+$/;
|
|
84
|
+
var WINDOWS_SAFE = /^[A-Za-z0-9_@+:./\\-]+$/;
|
|
85
|
+
var assertArgument = (arg) => {
|
|
86
|
+
if (typeof arg !== "string") throw new TypeError("A command argument is a string");
|
|
87
|
+
if (/[\u0000-\u001f\u007f]/.test(arg)) throw new TypeError("A command argument has no control characters");
|
|
88
|
+
return arg;
|
|
89
|
+
};
|
|
90
|
+
function quotePosix(arg) {
|
|
91
|
+
const text = assertArgument(arg);
|
|
92
|
+
return text && POSIX_SAFE.test(text) ? text : `'${text.replace(/'/g, `'\\''`)}'`;
|
|
93
|
+
}
|
|
94
|
+
function quoteWindows(arg) {
|
|
95
|
+
const text = assertArgument(arg);
|
|
96
|
+
if (text && WINDOWS_SAFE.test(text)) return text;
|
|
97
|
+
if (/["%!]/.test(text)) throw new TypeError('A cmd.exe command argument has no ", % or !');
|
|
98
|
+
const quoted = `"${text.replace(/(\\*)$/, "$1$1")}"`;
|
|
99
|
+
return quoted.replace(/([()\][^"`<>&|;, *?])/g, "^$1");
|
|
100
|
+
}
|
|
101
|
+
var SESSION_ID = /^[\w.:-]{1,128}$/;
|
|
102
|
+
function commandLine(argv, platform) {
|
|
103
|
+
return argv.map(platform === "windows" ? quoteWindows : quotePosix).join(" ");
|
|
104
|
+
}
|
|
105
|
+
function resumeCommandOf(sessionId, platform) {
|
|
106
|
+
if (typeof sessionId !== "string" || !SESSION_ID.test(sessionId)) throw new TypeError("A session id is [A-Za-z0-9_.:-], at most 128 characters");
|
|
107
|
+
const argv = ["alp", "attach", sessionId];
|
|
108
|
+
const posix = commandLine(argv, "posix");
|
|
109
|
+
const windows = commandLine(argv, "windows");
|
|
110
|
+
return { argv, posix, windows, display: platform === "windows" ? windows : posix, platform };
|
|
111
|
+
}
|
|
112
|
+
export {
|
|
113
|
+
ATTACHMENTS,
|
|
114
|
+
ATTACHMENT_CHUNK_OVERHEAD,
|
|
115
|
+
AlpRpcError,
|
|
116
|
+
CAPABILITY,
|
|
117
|
+
ERROR,
|
|
118
|
+
MAX_FRAME,
|
|
119
|
+
PROMPT_IMAGES,
|
|
120
|
+
PROTOCOL_VERSION,
|
|
121
|
+
RPC_ERROR,
|
|
122
|
+
RPC_METHODS,
|
|
123
|
+
TERMINAL,
|
|
124
|
+
TERMINAL_LIMIT_KEYS,
|
|
125
|
+
WORKSPACE_FILES,
|
|
126
|
+
WORKSPACE_GIT,
|
|
127
|
+
attachmentChunkBytes,
|
|
128
|
+
base64Bytes,
|
|
129
|
+
blockedFilePath,
|
|
130
|
+
commandLine,
|
|
131
|
+
decodeFrame,
|
|
132
|
+
encodeFrame,
|
|
133
|
+
isTerminalId,
|
|
134
|
+
quotePosix,
|
|
135
|
+
quoteWindows,
|
|
136
|
+
resumeCommandOf
|
|
137
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{"jsonrpc":"2.0","id":1,"method":"daemon.hello","params":{"protocolVersion":1,"client":{"name":"fixture","version":"0.9.5"}}}
|
|
2
|
+
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":1}}
|
|
3
|
+
{"jsonrpc":"2.0","method":"event","params":{"sessionId":"ses_fixture","epoch":"fixture-epoch","seq":1,"ts":"2026-10-14T00:00:00.000Z","event":{"type":"session.ready"}}}
|
|
4
|
+
{"jsonrpc":"2.0","id":2,"error":{"code":1001,"message":"No session ses_fixture"}}
|