@hyperdrive.bot/paseo-protocol 0.2.5
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 +12 -0
- package/dist/agent-attention-notification.d.ts +41 -0
- package/dist/agent-attention-notification.js +140 -0
- package/dist/agent-labels.d.ts +9 -0
- package/dist/agent-labels.js +23 -0
- package/dist/agent-lifecycle.d.ts +3 -0
- package/dist/agent-lifecycle.js +8 -0
- package/dist/agent-state-bucket.d.ts +24 -0
- package/dist/agent-state-bucket.js +45 -0
- package/dist/agent-title-limits.d.ts +2 -0
- package/dist/agent-title-limits.js +2 -0
- package/dist/agent-types.d.ts +461 -0
- package/dist/agent-types.js +22 -0
- package/dist/binary-frames/chunk-size.d.ts +2 -0
- package/dist/binary-frames/chunk-size.js +7 -0
- package/dist/binary-frames/demux.d.ts +11 -0
- package/dist/binary-frames/demux.js +23 -0
- package/dist/binary-frames/file-transfer.d.ts +50 -0
- package/dist/binary-frames/file-transfer.js +114 -0
- package/dist/binary-frames/index.d.ts +4 -0
- package/dist/binary-frames/index.js +4 -0
- package/dist/binary-frames/terminal.d.ts +31 -0
- package/dist/binary-frames/terminal.js +99 -0
- package/dist/branch-slug.d.ts +14 -0
- package/dist/branch-slug.js +49 -0
- package/dist/browser-automation/capabilities.d.ts +7 -0
- package/dist/browser-automation/capabilities.js +31 -0
- package/dist/browser-automation/rpc-schemas.d.ts +1112 -0
- package/dist/browser-automation/rpc-schemas.js +462 -0
- package/dist/chat/rpc-schemas.d.ts +158 -0
- package/dist/chat/rpc-schemas.js +103 -0
- package/dist/chat/types.d.ts +30 -0
- package/dist/chat/types.js +22 -0
- package/dist/client-capabilities.d.ts +8 -0
- package/dist/client-capabilities.js +16 -0
- package/dist/connection-offer.d.ts +36 -0
- package/dist/connection-offer.js +53 -0
- package/dist/daemon-endpoints.d.ts +47 -0
- package/dist/daemon-endpoints.js +201 -0
- package/dist/error-utils.d.ts +11 -0
- package/dist/error-utils.js +27 -0
- package/dist/generated/validation/ws-outbound.aot.d.ts +2 -0
- package/dist/generated/validation/ws-outbound.aot.js +46936 -0
- package/dist/git-remote.d.ts +16 -0
- package/dist/git-remote.js +72 -0
- package/dist/host-connection-schema.d.ts +11 -0
- package/dist/host-connection-schema.js +9 -0
- package/dist/literal-union.d.ts +2 -0
- package/dist/literal-union.js +2 -0
- package/dist/loop/rpc-schemas.d.ts +636 -0
- package/dist/loop/rpc-schemas.js +163 -0
- package/dist/messages.d.ts +36565 -0
- package/dist/messages.js +4703 -0
- package/dist/paseo-config-schema.d.ts +117 -0
- package/dist/paseo-config-schema.js +79 -0
- package/dist/path-utils.d.ts +2 -0
- package/dist/path-utils.js +16 -0
- package/dist/provider-config.d.ts +136 -0
- package/dist/provider-config.js +119 -0
- package/dist/provider-icon-names.d.ts +5 -0
- package/dist/provider-icon-names.js +52 -0
- package/dist/provider-manifest.d.ts +114 -0
- package/dist/provider-manifest.js +344 -0
- package/dist/schedule/cron-expression.d.ts +13 -0
- package/dist/schedule/cron-expression.js +97 -0
- package/dist/schedule/rpc-schemas.d.ts +574 -0
- package/dist/schedule/rpc-schemas.js +151 -0
- package/dist/schedule/types.d.ts +202 -0
- package/dist/schedule/types.js +75 -0
- package/dist/terminal-activity.d.ts +21 -0
- package/dist/terminal-activity.js +25 -0
- package/dist/terminal-input-mode.d.ts +26 -0
- package/dist/terminal-input-mode.js +151 -0
- package/dist/terminal-key-input.d.ts +13 -0
- package/dist/terminal-key-input.js +201 -0
- package/dist/terminal-profiles.d.ts +6 -0
- package/dist/terminal-profiles.js +28 -0
- package/dist/terminal-snapshot.d.ts +3 -0
- package/dist/terminal-snapshot.js +178 -0
- package/dist/terminal-stream-protocol.d.ts +2 -0
- package/dist/terminal-stream-protocol.js +2 -0
- package/dist/terminal-subscription-key.d.ts +2 -0
- package/dist/terminal-subscription-key.js +9 -0
- package/dist/tool-call-display.d.ts +11 -0
- package/dist/tool-call-display.js +135 -0
- package/dist/tool-name-normalization.d.ts +9 -0
- package/dist/tool-name-normalization.js +82 -0
- package/dist/validation/ws-outbound-schema-metadata.d.ts +6828 -0
- package/dist/validation/ws-outbound-schema-metadata.js +3 -0
- package/dist/validation/ws-outbound.d.ts +12 -0
- package/dist/validation/ws-outbound.js +8 -0
- package/package.json +42 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const CLAUDE_MODES = [
|
|
3
|
+
{
|
|
4
|
+
id: "default",
|
|
5
|
+
label: "Always Ask",
|
|
6
|
+
description: "Prompts for permission the first time a tool is used",
|
|
7
|
+
icon: "ShieldCheck",
|
|
8
|
+
colorTier: "safe",
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: "auto",
|
|
12
|
+
label: "Auto mode",
|
|
13
|
+
description: "Uses a model classifier to review permission prompts automatically",
|
|
14
|
+
icon: "ShieldQuestionMark",
|
|
15
|
+
colorTier: "moderate",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "acceptEdits",
|
|
19
|
+
label: "Accept File Edits",
|
|
20
|
+
description: "Automatically approves edit-focused tools without prompting",
|
|
21
|
+
icon: "ShieldAlert",
|
|
22
|
+
colorTier: "moderate",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "plan",
|
|
26
|
+
label: "Plan Mode",
|
|
27
|
+
description: "Analyze the codebase without executing tools or edits",
|
|
28
|
+
icon: "ShieldCheck",
|
|
29
|
+
colorTier: "planning",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "bypassPermissions",
|
|
33
|
+
label: "Bypass",
|
|
34
|
+
description: "Skip all permission prompts (use with caution)",
|
|
35
|
+
icon: "ShieldAlert",
|
|
36
|
+
colorTier: "dangerous",
|
|
37
|
+
isUnattended: true,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
const CODEX_MODES = [
|
|
41
|
+
{
|
|
42
|
+
id: "auto",
|
|
43
|
+
label: "Default Permissions",
|
|
44
|
+
description: "Edit files and run commands with Codex's default approval flow.",
|
|
45
|
+
icon: "ShieldAlert",
|
|
46
|
+
colorTier: "moderate",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "auto-review",
|
|
50
|
+
label: "Auto-review",
|
|
51
|
+
description: "Same workspace-write permissions as Default, but eligible `on-request` approvals are routed through the auto-reviewer subagent.",
|
|
52
|
+
icon: "ShieldQuestionMark",
|
|
53
|
+
colorTier: "moderate",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "full-access",
|
|
57
|
+
label: "Full Access",
|
|
58
|
+
description: "Edit files, run commands, and access the network without additional prompts.",
|
|
59
|
+
icon: "ShieldAlert",
|
|
60
|
+
colorTier: "dangerous",
|
|
61
|
+
isUnattended: true,
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
const COPILOT_MODES = [
|
|
65
|
+
{
|
|
66
|
+
id: "https://agentclientprotocol.com/protocol/session-modes#agent",
|
|
67
|
+
label: "Agent",
|
|
68
|
+
description: "Default agent mode for conversational interactions",
|
|
69
|
+
icon: "ShieldAlert",
|
|
70
|
+
colorTier: "moderate",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: "https://agentclientprotocol.com/protocol/session-modes#plan",
|
|
74
|
+
label: "Plan",
|
|
75
|
+
description: "Plan mode for creating and executing multi-step plans",
|
|
76
|
+
icon: "ShieldCheck",
|
|
77
|
+
colorTier: "planning",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "allow-all",
|
|
81
|
+
label: "Allow All",
|
|
82
|
+
description: "Automatically approves all Copilot tool, path, and URL requests.",
|
|
83
|
+
icon: "ShieldOff",
|
|
84
|
+
colorTier: "dangerous",
|
|
85
|
+
isUnattended: true,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
const OPENCODE_MODES = [
|
|
89
|
+
{
|
|
90
|
+
id: "build",
|
|
91
|
+
label: "Build",
|
|
92
|
+
description: "Allows edits and tool execution for implementation work",
|
|
93
|
+
icon: "Bot",
|
|
94
|
+
colorTier: "moderate",
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: "plan",
|
|
98
|
+
label: "Plan",
|
|
99
|
+
description: "Read-only planning mode that avoids file edits",
|
|
100
|
+
icon: "Bot",
|
|
101
|
+
colorTier: "planning",
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
const MOCK_LOAD_TEST_MODES = [
|
|
105
|
+
{
|
|
106
|
+
id: "load-test",
|
|
107
|
+
label: "Load Test",
|
|
108
|
+
description: "Streams repeated markdown, reasoning, and tool calls for app stress testing",
|
|
109
|
+
icon: "ShieldOff",
|
|
110
|
+
colorTier: "dangerous",
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
const MOCK_SLOW_MODES = [
|
|
114
|
+
{
|
|
115
|
+
id: "default",
|
|
116
|
+
label: "Default",
|
|
117
|
+
description: "Dev-only mode for the mock slow provider",
|
|
118
|
+
icon: "ShieldOff",
|
|
119
|
+
colorTier: "dangerous",
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
export const AGENT_PROVIDER_DEFINITIONS = [
|
|
123
|
+
{
|
|
124
|
+
id: "claude",
|
|
125
|
+
label: "Claude",
|
|
126
|
+
description: "Anthropic's multi-tool assistant with MCP support, streaming, and deep reasoning",
|
|
127
|
+
defaultModeId: "default",
|
|
128
|
+
modes: CLAUDE_MODES,
|
|
129
|
+
voice: {
|
|
130
|
+
enabled: true,
|
|
131
|
+
defaultModeId: "default",
|
|
132
|
+
defaultModel: "haiku",
|
|
133
|
+
},
|
|
134
|
+
wireFamily: "anthropic-messages",
|
|
135
|
+
compatibleProviders: [],
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: "codex",
|
|
139
|
+
label: "Codex",
|
|
140
|
+
description: "OpenAI's Codex workspace agent with sandbox controls and optional network access",
|
|
141
|
+
defaultModeId: "auto",
|
|
142
|
+
modes: CODEX_MODES,
|
|
143
|
+
voice: {
|
|
144
|
+
enabled: true,
|
|
145
|
+
defaultModeId: "auto",
|
|
146
|
+
defaultModel: "gpt-5.4-mini",
|
|
147
|
+
},
|
|
148
|
+
wireFamily: "openai-compatible",
|
|
149
|
+
compatibleProviders: [],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: "copilot",
|
|
153
|
+
label: "Copilot",
|
|
154
|
+
description: "GitHub Copilot via Agent Client Protocol with dynamic modes and session support",
|
|
155
|
+
defaultModeId: "https://agentclientprotocol.com/protocol/session-modes#agent",
|
|
156
|
+
modes: COPILOT_MODES,
|
|
157
|
+
wireFamily: "generic-acp",
|
|
158
|
+
compatibleProviders: [],
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: "opencode",
|
|
162
|
+
label: "OpenCode",
|
|
163
|
+
description: "Open-source coding assistant with multi-provider model support",
|
|
164
|
+
defaultModeId: "build",
|
|
165
|
+
modes: OPENCODE_MODES,
|
|
166
|
+
voice: {
|
|
167
|
+
enabled: true,
|
|
168
|
+
defaultModeId: "build",
|
|
169
|
+
},
|
|
170
|
+
wireFamily: "generic-acp",
|
|
171
|
+
compatibleProviders: [],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
id: "pi",
|
|
175
|
+
label: "Pi",
|
|
176
|
+
description: "Minimal terminal-based coding agent with multi-provider LLM support",
|
|
177
|
+
defaultModeId: null,
|
|
178
|
+
modes: [],
|
|
179
|
+
wireFamily: "anthropic-messages",
|
|
180
|
+
compatibleProviders: [],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "omp",
|
|
184
|
+
label: "OMP",
|
|
185
|
+
description: "Pi-compatible coding agent distributed as Oh My Pi",
|
|
186
|
+
enabledByDefault: false,
|
|
187
|
+
defaultModeId: null,
|
|
188
|
+
modes: [],
|
|
189
|
+
wireFamily: "anthropic-messages",
|
|
190
|
+
compatibleProviders: [],
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
export const DEV_AGENT_PROVIDER_DEFINITIONS = [
|
|
194
|
+
{
|
|
195
|
+
id: "mock",
|
|
196
|
+
label: "Mock Load Test",
|
|
197
|
+
description: "Development-only provider that emits synthetic agent traffic for performance tests",
|
|
198
|
+
defaultModeId: "load-test",
|
|
199
|
+
modes: MOCK_LOAD_TEST_MODES,
|
|
200
|
+
wireFamily: "anthropic-messages",
|
|
201
|
+
compatibleProviders: [],
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: "mock-slow",
|
|
205
|
+
label: "Mock Slow Provider",
|
|
206
|
+
description: "Dev-only: hangs during model discovery to test loading and timeout UI",
|
|
207
|
+
defaultModeId: "default",
|
|
208
|
+
modes: MOCK_SLOW_MODES,
|
|
209
|
+
wireFamily: "anthropic-messages",
|
|
210
|
+
},
|
|
211
|
+
];
|
|
212
|
+
export function getAgentProviderDefinition(provider, definitions = [
|
|
213
|
+
...AGENT_PROVIDER_DEFINITIONS,
|
|
214
|
+
...DEV_AGENT_PROVIDER_DEFINITIONS,
|
|
215
|
+
]) {
|
|
216
|
+
const definition = definitions.find((entry) => entry.id === provider);
|
|
217
|
+
if (!definition) {
|
|
218
|
+
throw new Error(`Unknown agent provider: ${provider}`);
|
|
219
|
+
}
|
|
220
|
+
return definition;
|
|
221
|
+
}
|
|
222
|
+
export const BUILTIN_PROVIDER_IDS = AGENT_PROVIDER_DEFINITIONS.map((d) => d.id);
|
|
223
|
+
export const AGENT_PROVIDER_IDS = BUILTIN_PROVIDER_IDS;
|
|
224
|
+
export const AgentProviderSchema = z.string();
|
|
225
|
+
export function isValidAgentProvider(value, validIds = BUILTIN_PROVIDER_IDS) {
|
|
226
|
+
return Array.isArray(validIds) ? validIds.includes(value) : new Set(validIds).has(value);
|
|
227
|
+
}
|
|
228
|
+
export function getUnattendedModeId(provider, definitions = [
|
|
229
|
+
...AGENT_PROVIDER_DEFINITIONS,
|
|
230
|
+
...DEV_AGENT_PROVIDER_DEFINITIONS,
|
|
231
|
+
]) {
|
|
232
|
+
const definition = definitions.find((entry) => entry.id === provider);
|
|
233
|
+
return definition?.modes.find((mode) => mode.isUnattended)?.id;
|
|
234
|
+
}
|
|
235
|
+
export function getModeVisuals(provider, modeId, definitions) {
|
|
236
|
+
const definition = definitions.find((entry) => entry.id === provider);
|
|
237
|
+
const mode = definition?.modes.find((m) => m.id === modeId);
|
|
238
|
+
if (!mode)
|
|
239
|
+
return undefined;
|
|
240
|
+
return { icon: mode.icon, colorTier: mode.colorTier };
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Validates that `compatibleProviders` declarations are symmetric across all
|
|
244
|
+
* provided definitions: if A declares B compatible, B MUST declare A as well.
|
|
245
|
+
* One-sided declarations are dropped from the effective set and emitted as
|
|
246
|
+
* warnings so they fail loudly at boot rather than producing a half-broken
|
|
247
|
+
* swap experience at runtime.
|
|
248
|
+
*
|
|
249
|
+
* Wire-family mismatches are ALSO dropped + warned: even if both sides
|
|
250
|
+
* declare each other, a swap between two providers in different families
|
|
251
|
+
* cannot work (different wire formats / resume tokens).
|
|
252
|
+
*/
|
|
253
|
+
export function validateCompatibleProvidersSymmetry(definitions) {
|
|
254
|
+
const byId = new Map();
|
|
255
|
+
for (const definition of definitions) {
|
|
256
|
+
byId.set(definition.id, definition);
|
|
257
|
+
}
|
|
258
|
+
const effective = new Map();
|
|
259
|
+
for (const definition of definitions) {
|
|
260
|
+
effective.set(definition.id, new Set());
|
|
261
|
+
}
|
|
262
|
+
const warnings = [];
|
|
263
|
+
for (const definition of definitions) {
|
|
264
|
+
const declared = definition.compatibleProviders ?? [];
|
|
265
|
+
for (const other of declared) {
|
|
266
|
+
if (other === definition.id) {
|
|
267
|
+
warnings.push(`Provider '${definition.id}' lists itself in compatibleProviders — ignored.`);
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const counterpart = byId.get(other);
|
|
271
|
+
if (!counterpart) {
|
|
272
|
+
warnings.push(`Provider '${definition.id}' lists unknown provider '${other}' as compatible — ignored.`);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
const counterpartDeclares = (counterpart.compatibleProviders ?? []).includes(definition.id);
|
|
276
|
+
if (!counterpartDeclares) {
|
|
277
|
+
warnings.push(`Asymmetric compatibility: '${definition.id}' lists '${other}' but '${other}' does not list '${definition.id}' — dropped.`);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (counterpart.wireFamily !== definition.wireFamily) {
|
|
281
|
+
warnings.push(`Wire-family mismatch between '${definition.id}' (${definition.wireFamily}) and '${other}' (${counterpart.wireFamily}) — swap dropped.`);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
effective.get(definition.id)?.add(other);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
effective: new Map([...effective.entries()].map(([k, v]) => [k, v])),
|
|
289
|
+
warnings,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Compute the effective set of compatibleProviders for a runtime registry
|
|
294
|
+
* that may include custom providers extending builtins (e.g. "Claude (Pool)"
|
|
295
|
+
* with `extends: "claude"`). Strategy:
|
|
296
|
+
*
|
|
297
|
+
* 1. Start from each provider's explicit `compatibleProviders` list.
|
|
298
|
+
* 2. Auto-link providers that share the same `extends` base (siblings) AND
|
|
299
|
+
* auto-link each derived provider with its base — but ONLY when both have
|
|
300
|
+
* the same wireFamily AND the base did not explicitly opt out.
|
|
301
|
+
* 3. Run symmetry validation on the resulting graph; drop asymmetric edges.
|
|
302
|
+
*
|
|
303
|
+
* The result is a Map keyed by provider id, listing the providers each one
|
|
304
|
+
* may swap TO.
|
|
305
|
+
*/
|
|
306
|
+
export function computeEffectiveCompatibleProviders(definitions,
|
|
307
|
+
/** Optional: providerId -> base provider id (the `extends` target). */
|
|
308
|
+
extensionMap) {
|
|
309
|
+
if (!extensionMap || extensionMap.size === 0) {
|
|
310
|
+
return validateCompatibleProvidersSymmetry(definitions);
|
|
311
|
+
}
|
|
312
|
+
// Build an augmented set of definitions where derived providers inherit
|
|
313
|
+
// bidirectional compatibility with siblings + the base.
|
|
314
|
+
const familyByBase = new Map();
|
|
315
|
+
for (const [derivedId, baseId] of extensionMap.entries()) {
|
|
316
|
+
const siblings = familyByBase.get(baseId) ?? [];
|
|
317
|
+
siblings.push(derivedId);
|
|
318
|
+
familyByBase.set(baseId, siblings);
|
|
319
|
+
}
|
|
320
|
+
const augmented = definitions.map((definition) => {
|
|
321
|
+
const baseId = extensionMap.get(definition.id);
|
|
322
|
+
const siblings = baseId ? (familyByBase.get(baseId) ?? []) : [];
|
|
323
|
+
const baseSiblings = familyByBase.get(definition.id) ?? [];
|
|
324
|
+
const inherited = new Set(definition.compatibleProviders ?? []);
|
|
325
|
+
// Derived → base + sibling derivatives
|
|
326
|
+
if (baseId) {
|
|
327
|
+
inherited.add(baseId);
|
|
328
|
+
for (const sibling of siblings) {
|
|
329
|
+
if (sibling !== definition.id)
|
|
330
|
+
inherited.add(sibling);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Base → derivatives extending it
|
|
334
|
+
for (const derived of baseSiblings) {
|
|
335
|
+
inherited.add(derived);
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
...definition,
|
|
339
|
+
compatibleProviders: [...inherited],
|
|
340
|
+
};
|
|
341
|
+
});
|
|
342
|
+
return validateCompatibleProvidersSymmetry(augmented);
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=provider-manifest.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface CronFieldMatcher {
|
|
2
|
+
matches(value: number): boolean;
|
|
3
|
+
}
|
|
4
|
+
export interface ParsedCronExpression {
|
|
5
|
+
minute: CronFieldMatcher;
|
|
6
|
+
hour: CronFieldMatcher;
|
|
7
|
+
dayOfMonth: CronFieldMatcher;
|
|
8
|
+
month: CronFieldMatcher;
|
|
9
|
+
dayOfWeek: CronFieldMatcher;
|
|
10
|
+
}
|
|
11
|
+
export declare function parseCronExpression(expression: string): ParsedCronExpression;
|
|
12
|
+
export declare function validateCronExpression(expression: string): string | null;
|
|
13
|
+
//# sourceMappingURL=cron-expression.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const CRON_FIELD_BOUNDS = [
|
|
2
|
+
{ min: 0, max: 59, name: "minute" },
|
|
3
|
+
{ min: 0, max: 23, name: "hour" },
|
|
4
|
+
{ min: 1, max: 31, name: "day-of-month" },
|
|
5
|
+
{ min: 1, max: 12, name: "month" },
|
|
6
|
+
{ min: 0, max: 6, name: "day-of-week" },
|
|
7
|
+
];
|
|
8
|
+
function createRange(start, end, step) {
|
|
9
|
+
const values = [];
|
|
10
|
+
for (let value = start; value <= end; value += step) {
|
|
11
|
+
values.push(value);
|
|
12
|
+
}
|
|
13
|
+
return values;
|
|
14
|
+
}
|
|
15
|
+
function parseStepPart(part, bounds) {
|
|
16
|
+
const stepParts = part.split("/");
|
|
17
|
+
if (stepParts.length > 2) {
|
|
18
|
+
throw new Error(`Invalid cron ${bounds.name} step`);
|
|
19
|
+
}
|
|
20
|
+
const [base, stepSource] = stepParts;
|
|
21
|
+
const normalizedStep = stepSource?.trim();
|
|
22
|
+
const step = normalizedStep === undefined ? 1 : Number.parseInt(normalizedStep, 10);
|
|
23
|
+
if (!Number.isInteger(step) ||
|
|
24
|
+
step <= 0 ||
|
|
25
|
+
(normalizedStep !== undefined && String(step) !== normalizedStep)) {
|
|
26
|
+
throw new Error(`Invalid cron ${bounds.name} step`);
|
|
27
|
+
}
|
|
28
|
+
return { base, step };
|
|
29
|
+
}
|
|
30
|
+
function parseField(source, bounds) {
|
|
31
|
+
const trimmed = source.trim();
|
|
32
|
+
if (!trimmed) {
|
|
33
|
+
throw new Error(`Invalid cron ${bounds.name} field`);
|
|
34
|
+
}
|
|
35
|
+
const allowed = new Set();
|
|
36
|
+
for (const rawPart of trimmed.split(",")) {
|
|
37
|
+
const part = rawPart.trim();
|
|
38
|
+
if (!part) {
|
|
39
|
+
throw new Error(`Invalid cron ${bounds.name} field`);
|
|
40
|
+
}
|
|
41
|
+
const { base, step } = parseStepPart(part, bounds);
|
|
42
|
+
if (base === "*") {
|
|
43
|
+
for (const value of createRange(bounds.min, bounds.max, step)) {
|
|
44
|
+
allowed.add(value);
|
|
45
|
+
}
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const rangeMatch = base.match(/^(\d+)-(\d+)$/);
|
|
49
|
+
if (rangeMatch) {
|
|
50
|
+
const start = Number.parseInt(rangeMatch[1], 10);
|
|
51
|
+
const end = Number.parseInt(rangeMatch[2], 10);
|
|
52
|
+
if (start > end || start < bounds.min || end > bounds.max) {
|
|
53
|
+
throw new Error(`Invalid cron ${bounds.name} range`);
|
|
54
|
+
}
|
|
55
|
+
for (const value of createRange(start, end, step)) {
|
|
56
|
+
allowed.add(value);
|
|
57
|
+
}
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!/^\d+$/.test(base)) {
|
|
61
|
+
throw new Error(`Invalid cron ${bounds.name} value`);
|
|
62
|
+
}
|
|
63
|
+
const value = Number.parseInt(base, 10);
|
|
64
|
+
if (!Number.isInteger(value) || value < bounds.min || value > bounds.max) {
|
|
65
|
+
throw new Error(`Invalid cron ${bounds.name} value`);
|
|
66
|
+
}
|
|
67
|
+
allowed.add(value);
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
matches(value) {
|
|
71
|
+
return allowed.has(value);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function parseCronExpression(expression) {
|
|
76
|
+
const parts = expression.trim().split(/\s+/);
|
|
77
|
+
if (parts.length !== 5) {
|
|
78
|
+
throw new Error("Cron expressions must have 5 fields");
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
minute: parseField(parts[0], CRON_FIELD_BOUNDS[0]),
|
|
82
|
+
hour: parseField(parts[1], CRON_FIELD_BOUNDS[1]),
|
|
83
|
+
dayOfMonth: parseField(parts[2], CRON_FIELD_BOUNDS[2]),
|
|
84
|
+
month: parseField(parts[3], CRON_FIELD_BOUNDS[3]),
|
|
85
|
+
dayOfWeek: parseField(parts[4], CRON_FIELD_BOUNDS[4]),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function validateCronExpression(expression) {
|
|
89
|
+
try {
|
|
90
|
+
parseCronExpression(expression);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
return error instanceof Error ? error.message : "Invalid cron expression";
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=cron-expression.js.map
|