wave-agent-sdk 1.0.4 → 1.0.6

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.
Files changed (194) hide show
  1. package/dist/agent.d.ts +23 -1
  2. package/dist/agent.js +34 -4
  3. package/dist/managers/aiManager.d.ts +36 -0
  4. package/dist/managers/aiManager.js +240 -8
  5. package/dist/managers/messageManager.d.ts +1 -3
  6. package/dist/managers/messageManager.js +19 -9
  7. package/dist/managers/slashCommandManager.js +28 -0
  8. package/dist/managers/subagentManager.d.ts +0 -2
  9. package/dist/services/session.js +2 -9
  10. package/dist/telemetry/instrumentation.d.ts +1 -1
  11. package/dist/telemetry/instrumentation.js +57 -28
  12. package/dist/tools/bashTool.js +16 -6
  13. package/dist/tools/grepTool.js +11 -3
  14. package/dist/types/index.d.ts +3 -0
  15. package/dist/types/messaging.d.ts +2 -0
  16. package/dist/utils/containerSetup.js +0 -3
  17. package/dist/utils/messageOperations.d.ts +10 -1
  18. package/dist/utils/messageOperations.js +19 -1
  19. package/dist/utils/notificationXml.js +5 -0
  20. package/dist/utils/ripgrep.d.ts +4 -4
  21. package/dist/utils/ripgrep.js +4 -27
  22. package/package.json +20 -8
  23. package/bin/rg +0 -79
  24. package/dist/managers/forkedAgentManager.d.ts +0 -50
  25. package/dist/managers/forkedAgentManager.js +0 -130
  26. package/scripts/install_ripgrep.js +0 -111
  27. package/scripts/postinstall.js +0 -38
  28. package/src/agent.ts +0 -1271
  29. package/src/constants/subagents.ts +0 -4
  30. package/src/constants/toolLimits.ts +0 -15
  31. package/src/constants/tools.ts +0 -24
  32. package/src/core/plugin.ts +0 -237
  33. package/src/core/session.ts +0 -9
  34. package/src/index.ts +0 -38
  35. package/src/managers/MemoryRuleManager.ts +0 -198
  36. package/src/managers/aiManager.ts +0 -2429
  37. package/src/managers/backgroundTaskManager.ts +0 -480
  38. package/src/managers/bangManager.ts +0 -119
  39. package/src/managers/cronManager.ts +0 -401
  40. package/src/managers/foregroundTaskManager.ts +0 -30
  41. package/src/managers/forkedAgentManager.ts +0 -216
  42. package/src/managers/hookManager.ts +0 -1089
  43. package/src/managers/liveConfigManager.ts +0 -445
  44. package/src/managers/lspManager.ts +0 -470
  45. package/src/managers/mcpManager.ts +0 -1010
  46. package/src/managers/messageManager.ts +0 -1180
  47. package/src/managers/messageQueue.ts +0 -157
  48. package/src/managers/permissionManager.ts +0 -1140
  49. package/src/managers/planManager.ts +0 -112
  50. package/src/managers/pluginManager.ts +0 -330
  51. package/src/managers/pluginScopeManager.ts +0 -124
  52. package/src/managers/reversionManager.ts +0 -178
  53. package/src/managers/skillManager.ts +0 -578
  54. package/src/managers/slashCommandManager.ts +0 -564
  55. package/src/managers/subagentManager.ts +0 -915
  56. package/src/managers/toolManager.ts +0 -489
  57. package/src/managers/workflowManager.ts +0 -493
  58. package/src/prompts/autoMemory.ts +0 -33
  59. package/src/prompts/autoMemoryExtraction.ts +0 -146
  60. package/src/prompts/index.ts +0 -506
  61. package/src/prompts/planModeReminders.ts +0 -126
  62. package/src/services/GitService.ts +0 -131
  63. package/src/services/MarketplaceService.ts +0 -1052
  64. package/src/services/MemoryRuleService.ts +0 -71
  65. package/src/services/aiService.ts +0 -919
  66. package/src/services/authService.ts +0 -565
  67. package/src/services/autoMemoryService.ts +0 -280
  68. package/src/services/configurationService.ts +0 -1520
  69. package/src/services/fileWatcher.ts +0 -319
  70. package/src/services/hook.ts +0 -348
  71. package/src/services/initializationService.ts +0 -357
  72. package/src/services/interactionService.ts +0 -229
  73. package/src/services/jsonlHandler.ts +0 -295
  74. package/src/services/memory.ts +0 -234
  75. package/src/services/pluginLoader.ts +0 -252
  76. package/src/services/remoteSettingsService.ts +0 -366
  77. package/src/services/reversionService.ts +0 -122
  78. package/src/services/session.ts +0 -922
  79. package/src/services/taskManager.ts +0 -321
  80. package/src/telemetry/events.ts +0 -65
  81. package/src/telemetry/instrumentation.ts +0 -499
  82. package/src/telemetry/sessionTracing.ts +0 -348
  83. package/src/tools/agentTool.ts +0 -322
  84. package/src/tools/askUserQuestion.ts +0 -160
  85. package/src/tools/bashTool.ts +0 -621
  86. package/src/tools/buildTool.ts +0 -61
  87. package/src/tools/cronCreateTool.ts +0 -161
  88. package/src/tools/cronDeleteTool.ts +0 -51
  89. package/src/tools/cronListTool.ts +0 -47
  90. package/src/tools/editTool.ts +0 -313
  91. package/src/tools/enterPlanMode.ts +0 -124
  92. package/src/tools/enterWorktreeTool.ts +0 -190
  93. package/src/tools/exitPlanMode.ts +0 -139
  94. package/src/tools/exitWorktreeTool.ts +0 -236
  95. package/src/tools/globTool.ts +0 -167
  96. package/src/tools/grepTool.ts +0 -399
  97. package/src/tools/lspTool.ts +0 -883
  98. package/src/tools/readTool.ts +0 -426
  99. package/src/tools/skillTool.ts +0 -248
  100. package/src/tools/taskManagementTools.ts +0 -574
  101. package/src/tools/taskStopTool.ts +0 -78
  102. package/src/tools/types.ts +0 -137
  103. package/src/tools/webFetchTool.ts +0 -373
  104. package/src/tools/workflowTool.ts +0 -205
  105. package/src/tools/writeTool.ts +0 -267
  106. package/src/types/agent.ts +0 -128
  107. package/src/types/auth.ts +0 -19
  108. package/src/types/commands.ts +0 -30
  109. package/src/types/config.ts +0 -43
  110. package/src/types/configuration.ts +0 -168
  111. package/src/types/core.ts +0 -105
  112. package/src/types/cron.ts +0 -13
  113. package/src/types/environment.ts +0 -106
  114. package/src/types/fileSearch.ts +0 -4
  115. package/src/types/history.ts +0 -7
  116. package/src/types/hooks.ts +0 -256
  117. package/src/types/index.ts +0 -41
  118. package/src/types/lsp.ts +0 -98
  119. package/src/types/marketplace.ts +0 -71
  120. package/src/types/mcp.ts +0 -43
  121. package/src/types/memoryRule.ts +0 -31
  122. package/src/types/messaging.ts +0 -114
  123. package/src/types/permissions.ts +0 -80
  124. package/src/types/plugins.ts +0 -39
  125. package/src/types/processes.ts +0 -99
  126. package/src/types/reversion.ts +0 -29
  127. package/src/types/session.ts +0 -12
  128. package/src/types/skills.ts +0 -108
  129. package/src/types/tasks.ts +0 -13
  130. package/src/types/telemetry.ts +0 -98
  131. package/src/types/tools.ts +0 -44
  132. package/src/types/workflow.ts +0 -6
  133. package/src/utils/abortUtils.ts +0 -118
  134. package/src/utils/atomicWrite.ts +0 -61
  135. package/src/utils/bashParser.ts +0 -899
  136. package/src/utils/cacheControlUtils.ts +0 -426
  137. package/src/utils/commandArgumentParser.ts +0 -161
  138. package/src/utils/commandPathResolver.ts +0 -88
  139. package/src/utils/configPaths.ts +0 -220
  140. package/src/utils/configValidator.ts +0 -100
  141. package/src/utils/constants.ts +0 -39
  142. package/src/utils/container.ts +0 -92
  143. package/src/utils/containerSetup.ts +0 -371
  144. package/src/utils/convertMessagesForAPI.ts +0 -326
  145. package/src/utils/cronTasks.ts +0 -128
  146. package/src/utils/cronTasksLock.ts +0 -182
  147. package/src/utils/cronToHuman.ts +0 -99
  148. package/src/utils/customCommands.ts +0 -95
  149. package/src/utils/editUtils.ts +0 -20
  150. package/src/utils/fileFormat.ts +0 -40
  151. package/src/utils/fileSearch.ts +0 -151
  152. package/src/utils/fileUtils.ts +0 -223
  153. package/src/utils/gitUtils.ts +0 -315
  154. package/src/utils/globalLogger.ts +0 -128
  155. package/src/utils/groupMessagesByApiRound.ts +0 -121
  156. package/src/utils/hookMatcher.ts +0 -176
  157. package/src/utils/markdownParser.ts +0 -317
  158. package/src/utils/mcpUtils.ts +0 -138
  159. package/src/utils/messageOperations.ts +0 -641
  160. package/src/utils/modelCapabilities.ts +0 -30
  161. package/src/utils/nameGenerator.ts +0 -99
  162. package/src/utils/notificationXml.ts +0 -57
  163. package/src/utils/openaiClient.ts +0 -251
  164. package/src/utils/parseCronExpression.ts +0 -78
  165. package/src/utils/path.ts +0 -72
  166. package/src/utils/pathEncoder.ts +0 -400
  167. package/src/utils/pathSafety.ts +0 -40
  168. package/src/utils/promptHistory.ts +0 -168
  169. package/src/utils/ripgrep.ts +0 -35
  170. package/src/utils/shellResolver.ts +0 -190
  171. package/src/utils/skillParser.ts +0 -246
  172. package/src/utils/stringUtils.ts +0 -156
  173. package/src/utils/subagentParser.ts +0 -279
  174. package/src/utils/taskReminder.ts +0 -96
  175. package/src/utils/tokenCalculation.ts +0 -43
  176. package/src/utils/tokenEstimate.ts +0 -34
  177. package/src/utils/toolResultStorage.ts +0 -117
  178. package/src/utils/worktreeSession.ts +0 -26
  179. package/src/utils/worktreeUtils.ts +0 -786
  180. package/src/workflow/budgetTracker.ts +0 -34
  181. package/src/workflow/concurrencyLimiter.ts +0 -47
  182. package/src/workflow/journal.ts +0 -95
  183. package/src/workflow/progressReporter.ts +0 -141
  184. package/src/workflow/runState.ts +0 -65
  185. package/src/workflow/scriptRuntime.ts +0 -274
  186. package/src/workflow/structuredOutput.ts +0 -123
  187. package/src/workflow/types.ts +0 -101
  188. package/src/workflow/workflowApis.ts +0 -410
  189. package/vendor/ripgrep/linux-aarch64/rg +0 -0
  190. package/vendor/ripgrep/linux-x86_64/rg +0 -0
  191. package/vendor/ripgrep/macos-aarch64/rg +0 -0
  192. package/vendor/ripgrep/macos-x86_64/rg +0 -0
  193. package/vendor/ripgrep/windows-aarch64/rg.exe +0 -0
  194. package/vendor/ripgrep/windows-x86_64/rg.exe +0 -0
@@ -1,99 +0,0 @@
1
- /**
2
- * Random name generator for plan files
3
- */
4
-
5
- const adjectives = [
6
- "gentle",
7
- "swift",
8
- "calm",
9
- "bold",
10
- "bright",
11
- "cool",
12
- "warm",
13
- "quiet",
14
- "loud",
15
- "fast",
16
- "slow",
17
- "high",
18
- "low",
19
- "deep",
20
- "shallow",
21
- "broad",
22
- "narrow",
23
- "long",
24
- "short",
25
- "brave",
26
- "wild",
27
- "tame",
28
- "smart",
29
- "clever",
30
- "kind",
31
- "fair",
32
- "just",
33
- "wise",
34
- "strong",
35
- "weak",
36
- ];
37
-
38
- const nouns = [
39
- "breeze",
40
- "river",
41
- "mountain",
42
- "forest",
43
- "ocean",
44
- "valley",
45
- "peak",
46
- "stream",
47
- "cloud",
48
- "storm",
49
- "sun",
50
- "moon",
51
- "star",
52
- "field",
53
- "meadow",
54
- "desert",
55
- "island",
56
- "coast",
57
- "plain",
58
- "glacier",
59
- "eagle",
60
- "wolf",
61
- "bear",
62
- "lion",
63
- "tiger",
64
- "hawk",
65
- "owl",
66
- "fox",
67
- "deer",
68
- "elk",
69
- ];
70
-
71
- /**
72
- * Generates a random English name (adjective-adjective-noun)
73
- */
74
- export function generateRandomName(seed?: string): string {
75
- let adj1Index: number;
76
- let adj2Index: number;
77
- let nounIndex: number;
78
-
79
- if (seed) {
80
- // Simple hash function to derive indices from seed
81
- let hash = 0;
82
- for (let i = 0; i < seed.length; i++) {
83
- hash = (hash << 5) - hash + seed.charCodeAt(i);
84
- hash |= 0; // Convert to 32bit integer
85
- }
86
- adj1Index = Math.abs(hash) % adjectives.length;
87
- adj2Index = Math.abs(hash >> 4) % adjectives.length;
88
- nounIndex = Math.abs(hash >> 8) % nouns.length;
89
- } else {
90
- adj1Index = Math.floor(Math.random() * adjectives.length);
91
- adj2Index = Math.floor(Math.random() * adjectives.length);
92
- nounIndex = Math.floor(Math.random() * nouns.length);
93
- }
94
-
95
- const adj1 = adjectives[adj1Index];
96
- const adj2 = adjectives[adj2Index];
97
- const noun = nouns[nounIndex];
98
- return `${adj1}-${adj2}-${noun}`;
99
- }
@@ -1,57 +0,0 @@
1
- import type { TaskNotificationBlock } from "../types/messaging.js";
2
-
3
- export function taskNotificationToXml(block: TaskNotificationBlock): string {
4
- let xml = `<task-notification>\n`;
5
- xml += `<task-id>${block.taskId}</task-id>\n`;
6
- xml += `<task-type>${block.taskType}</task-type>\n`;
7
- if (block.outputFile) {
8
- xml += `<output-file>${block.outputFile}</output-file>\n`;
9
- }
10
- xml += `<status>${block.status}</status>\n`;
11
- xml += `<summary>${block.summary}</summary>\n`;
12
- xml += `</task-notification>`;
13
- return xml;
14
- }
15
-
16
- function extractTag(xml: string, tag: string): string | null {
17
- const regex = new RegExp(`<${tag}>(.*?)</${tag}>`, "s");
18
- const match = xml.match(regex);
19
- return match ? match[1] : null;
20
- }
21
-
22
- export function parseTaskNotificationXml(
23
- xml: string,
24
- ): TaskNotificationBlock | null {
25
- try {
26
- const taskId = extractTag(xml, "task-id");
27
- const taskType = extractTag(xml, "task-type") as
28
- | "shell"
29
- | "agent"
30
- | "workflow"
31
- | null;
32
- const status = extractTag(xml, "status") as
33
- | "completed"
34
- | "failed"
35
- | "killed"
36
- | "aborted"
37
- | null;
38
- const summary = extractTag(xml, "summary");
39
-
40
- if (!taskId || !taskType || !status || !summary) {
41
- return null;
42
- }
43
-
44
- const outputFile = extractTag(xml, "output-file") || undefined;
45
-
46
- return {
47
- type: "task_notification",
48
- taskId,
49
- taskType,
50
- status,
51
- summary,
52
- ...(outputFile && { outputFile }),
53
- };
54
- } catch {
55
- return null;
56
- }
57
- }
@@ -1,251 +0,0 @@
1
- import {
2
- ChatCompletionCreateParamsNonStreaming,
3
- ChatCompletionCreateParamsStreaming,
4
- ChatCompletionChunk,
5
- ChatCompletion,
6
- } from "openai/resources.js";
7
- import { GatewayConfig } from "../types/config.js";
8
- import { logger } from "./globalLogger.js";
9
-
10
- const MAX_RETRIES = 10;
11
- const BASE_DELAY_MS = 500;
12
- const MAX_DELAY_MS = 32000;
13
-
14
- function getRetryDelay(
15
- attempt: number,
16
- retryAfterHeader: string | null,
17
- ): number {
18
- if (retryAfterHeader) {
19
- const seconds = parseInt(retryAfterHeader, 10);
20
- if (!isNaN(seconds)) {
21
- return seconds * 1000;
22
- }
23
- }
24
- const baseDelay = Math.min(
25
- BASE_DELAY_MS * Math.pow(2, attempt - 1),
26
- MAX_DELAY_MS,
27
- );
28
- const jitter = Math.random() * 0.25 * baseDelay;
29
- return baseDelay + jitter;
30
- }
31
-
32
- type CreateParams =
33
- | ChatCompletionCreateParamsNonStreaming
34
- | ChatCompletionCreateParamsStreaming;
35
-
36
- interface APIResponse<T> {
37
- data: T;
38
- response: Response;
39
- }
40
-
41
- interface APIPromise<T> extends Promise<T> {
42
- withResponse(): Promise<APIResponse<T>>;
43
- }
44
-
45
- export class OpenAIClient {
46
- constructor(private config: GatewayConfig) {}
47
-
48
- get chat() {
49
- return {
50
- completions: {
51
- create: <P extends CreateParams>(
52
- params: P,
53
- options?: { signal?: AbortSignal },
54
- ): APIPromise<
55
- P extends ChatCompletionCreateParamsStreaming
56
- ? AsyncIterable<ChatCompletionChunk>
57
- : ChatCompletion
58
- > => {
59
- const responsePromise = this._create(params, options);
60
- const promise = responsePromise.then(
61
- (res) => res.data,
62
- ) as unknown as APIPromise<
63
- P extends ChatCompletionCreateParamsStreaming
64
- ? AsyncIterable<ChatCompletionChunk>
65
- : ChatCompletion
66
- >;
67
- promise.withResponse = () =>
68
- responsePromise as Promise<
69
- APIResponse<
70
- P extends ChatCompletionCreateParamsStreaming
71
- ? AsyncIterable<ChatCompletionChunk>
72
- : ChatCompletion
73
- >
74
- >;
75
- // Prevent unhandled rejection if only withResponse() is used
76
- promise.catch((e) => {
77
- if (!(e instanceof Error && e.name === "AbortError")) {
78
- logger.error("Unhandled OpenAI promise rejection:", e);
79
- }
80
- });
81
- return promise;
82
- },
83
- },
84
- };
85
- }
86
-
87
- private async _create<P extends CreateParams>(
88
- params: P,
89
- options?: { signal?: AbortSignal },
90
- ): Promise<
91
- APIResponse<
92
- P extends ChatCompletionCreateParamsStreaming
93
- ? AsyncIterable<ChatCompletionChunk>
94
- : ChatCompletion
95
- >
96
- > {
97
- const {
98
- baseURL,
99
- apiKey,
100
- sessionId,
101
- defaultHeaders,
102
- fetchOptions,
103
- fetch: customFetch,
104
- } = this.config;
105
- const url = `${baseURL}/chat/completions`;
106
- const headers = {
107
- "Content-Type": "application/json",
108
- ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
109
- ...(sessionId ? { "x-session-id": sessionId } : {}),
110
- ...defaultHeaders,
111
- };
112
-
113
- const fetchFn = (customFetch as typeof fetch) || fetch;
114
- let lastError: (Error & { status?: number; body?: unknown }) | undefined;
115
- let lastRetryAfter: string | null = null;
116
-
117
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
118
- if (attempt > 0) {
119
- const delay = getRetryDelay(attempt, lastRetryAfter);
120
- await new Promise((resolve) => setTimeout(resolve, delay));
121
- }
122
-
123
- let response: Response;
124
- try {
125
- response = await fetchFn(url, {
126
- method: "POST",
127
- headers,
128
- body: JSON.stringify(params),
129
- signal: options?.signal,
130
- ...(fetchOptions as RequestInit),
131
- });
132
- } catch (e) {
133
- if (e instanceof Error && e.name === "AbortError") {
134
- throw e;
135
- }
136
- if (attempt < MAX_RETRIES) {
137
- logger.warn("OpenAI API network error, retrying...", {
138
- model: params.model,
139
- attempt: attempt + 1,
140
- error: e,
141
- });
142
- lastError = e as Error;
143
- lastRetryAfter = null;
144
- continue;
145
- }
146
- throw e;
147
- }
148
-
149
- if (response.ok) {
150
- if (params.stream) {
151
- return {
152
- data: this.streamChatCompletion(response),
153
- response,
154
- } as unknown as APIResponse<
155
- P extends ChatCompletionCreateParamsStreaming
156
- ? AsyncIterable<ChatCompletionChunk>
157
- : ChatCompletion
158
- >;
159
- } else {
160
- const data = await response.json();
161
- return {
162
- data,
163
- response,
164
- } as unknown as APIResponse<
165
- P extends ChatCompletionCreateParamsStreaming
166
- ? AsyncIterable<ChatCompletionChunk>
167
- : ChatCompletion
168
- >;
169
- }
170
- }
171
-
172
- let responseText = "";
173
- try {
174
- responseText = await response.text();
175
- } catch (e) {
176
- logger.error("Failed to read error response text", { error: e });
177
- }
178
-
179
- const error = new Error(
180
- `HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}: ${responseText}`,
181
- ) as Error & { status?: number; body?: unknown };
182
- error.status = response.status;
183
- error.body = responseText;
184
-
185
- const retryableStatus =
186
- response.status === 429 ||
187
- (response.status >= 500 && response.status !== 501);
188
- if (retryableStatus && attempt < MAX_RETRIES) {
189
- lastRetryAfter = response.headers.get("retry-after");
190
- logger.warn("OpenAI API error, retrying...", {
191
- model: params.model,
192
- attempt: attempt + 1,
193
- status: response.status,
194
- retryAfter: lastRetryAfter,
195
- });
196
- lastError = error;
197
- continue;
198
- }
199
-
200
- logger.error("OpenAI API Error:", {
201
- status: response.status,
202
- statusText: response.statusText,
203
- body: responseText,
204
- });
205
- throw error;
206
- }
207
- throw lastError;
208
- }
209
-
210
- private async *streamChatCompletion(
211
- response: Response,
212
- ): AsyncIterable<ChatCompletionChunk> {
213
- const reader = response.body?.getReader();
214
- if (!reader) return;
215
-
216
- const decoder = new TextDecoder();
217
- let buffer = "";
218
-
219
- try {
220
- while (true) {
221
- const { done, value } = await reader.read();
222
- if (done) break;
223
-
224
- buffer += decoder.decode(value, { stream: true });
225
- const lines = buffer.split("\n");
226
- buffer = lines.pop() || "";
227
-
228
- for (const line of lines) {
229
- const trimmedLine = line.trim();
230
- if (!trimmedLine || !trimmedLine.startsWith("data:")) continue;
231
-
232
- const data = trimmedLine.slice(5).trim();
233
- if (data === "[DONE]") return;
234
- if (!data) continue; // Skip empty data lines (keepalive heartbeats)
235
-
236
- try {
237
- const json = JSON.parse(data);
238
- yield json as ChatCompletionChunk;
239
- } catch (e) {
240
- logger.error("Failed to parse stream chunk", {
241
- error: e instanceof Error ? e.message : String(e),
242
- data,
243
- });
244
- }
245
- }
246
- }
247
- } finally {
248
- reader.releaseLock();
249
- }
250
- }
251
- }
@@ -1,78 +0,0 @@
1
- // Minimal cron expression validation.
2
- // Supports the standard 5-field cron subset:
3
- // minute hour day-of-month month day-of-week
4
- //
5
- // Field syntax: wildcard, N, step (star-slash-N), range (N-M), list (N,M,...).
6
- // No L, W, ?, or name aliases. Based on Claude Code's parseCronExpression.
7
-
8
- type FieldRange = { min: number; max: number };
9
-
10
- const FIELD_RANGES: FieldRange[] = [
11
- { min: 0, max: 59 }, // minute
12
- { min: 0, max: 23 }, // hour
13
- { min: 1, max: 31 }, // dayOfMonth
14
- { min: 1, max: 12 }, // month
15
- { min: 0, max: 6 }, // dayOfWeek (0=Sunday; 7 accepted as Sunday alias)
16
- ];
17
-
18
- function expandField(field: string, range: FieldRange): number[] | null {
19
- const { min, max } = range;
20
- const out = new Set<number>();
21
-
22
- for (const part of field.split(",")) {
23
- // wildcard or star-slash-N
24
- const stepMatch = part.match(/^\*(?:\/(\d+))?$/);
25
- if (stepMatch) {
26
- const step = stepMatch[1] ? parseInt(stepMatch[1], 10) : 1;
27
- if (step < 1) return null;
28
- for (let i = min; i <= max; i += step) out.add(i);
29
- continue;
30
- }
31
-
32
- // N-M or N-M/S
33
- const rangeMatch = part.match(/^(\d+)-(\d+)(?:\/(\d+))?$/);
34
- if (rangeMatch) {
35
- const lo = parseInt(rangeMatch[1]!, 10);
36
- const hi = parseInt(rangeMatch[2]!, 10);
37
- const step = rangeMatch[3] ? parseInt(rangeMatch[3], 10) : 1;
38
- const isDow = min === 0 && max === 6;
39
- const effMax = isDow ? 7 : max;
40
- if (lo > hi || step < 1 || lo < min || hi > effMax) return null;
41
- for (let i = lo; i <= hi; i += step) {
42
- out.add(isDow && i === 7 ? 0 : i);
43
- }
44
- continue;
45
- }
46
-
47
- // plain N
48
- const singleMatch = part.match(/^\d+$/);
49
- if (singleMatch) {
50
- let n = parseInt(part, 10);
51
- if (min === 0 && max === 6 && n === 7) n = 0;
52
- if (n < min || n > max) return null;
53
- out.add(n);
54
- continue;
55
- }
56
-
57
- return null;
58
- }
59
-
60
- if (out.size === 0) return null;
61
- return Array.from(out).sort((a, b) => a - b);
62
- }
63
-
64
- /**
65
- * Validate a 5-field cron expression.
66
- * Returns true if valid, false otherwise.
67
- */
68
- export function parseCronExpression(expr: string): boolean {
69
- const parts = expr.trim().split(/\s+/);
70
- if (parts.length !== 5) return false;
71
-
72
- for (let i = 0; i < 5; i++) {
73
- const result = expandField(parts[i]!, FIELD_RANGES[i]!);
74
- if (!result) return false;
75
- }
76
-
77
- return true;
78
- }
package/src/utils/path.ts DELETED
@@ -1,72 +0,0 @@
1
- import { resolve } from "path";
2
- import { homedir } from "os";
3
- import { relative } from "path";
4
-
5
- /**
6
- * Handle paths, supporting ~ expansion
7
- * @param filePath File path
8
- * @param workdir Working directory
9
- * @returns Resolved absolute path
10
- */
11
- export function resolvePath(filePath: string, workdir: string): string {
12
- // If the path starts with ~, replace it with the user's home directory
13
- if (filePath.startsWith("~/")) {
14
- return resolve(homedir(), filePath.slice(2));
15
- }
16
-
17
- // If the path starts with ~ but has no slash, it means it's the home directory
18
- if (filePath === "~") {
19
- return homedir();
20
- }
21
-
22
- // For other paths, resolve using the specified working directory
23
- return resolve(workdir, filePath);
24
- }
25
-
26
- /**
27
- * Get relative path for display, use relative path if shorter and not in parent directory.
28
- *
29
- * The returned path is always normalized to forward slashes (posix) regardless of
30
- * platform. This keeps display output consistent across Windows/Unix so that logs,
31
- * LLM context, and tests don't need platform-specific branches — `path.relative`
32
- * and `path.join` produce backslashes on Windows, which we collapse here.
33
- *
34
- * @param filePath Absolute path
35
- * @param workdir Working directory
36
- * @returns Path for display (relative or absolute), always forward-slash
37
- */
38
- export function getDisplayPath(filePath: string, workdir: string): string {
39
- if (!filePath) {
40
- return filePath;
41
- }
42
-
43
- try {
44
- const relativePath = relative(workdir, filePath);
45
-
46
- // If the relative path is empty (i.e., the paths are the same), return "."
47
- if (relativePath === "") {
48
- return ".";
49
- }
50
-
51
- // If the relative path is shorter than the absolute path and does not start with .. (not in parent directory), use the relative path
52
- if (
53
- relativePath.length < filePath.length &&
54
- !relativePath.startsWith("..")
55
- ) {
56
- return toPosixPath(relativePath);
57
- }
58
- } catch {
59
- // If calculating the relative path fails, keep the original path
60
- }
61
- return toPosixPath(filePath);
62
- }
63
-
64
- /**
65
- * Convert backslashes to forward slashes for shell compatibility on Windows.
66
- * On Windows, os.tmpdir() returns paths with backslashes (e.g., C:\Users\foo\Temp),
67
- * which are treated as escape characters when interpolated into shell command strings.
68
- * Returns the path as-is on non-Windows platforms.
69
- */
70
- export function toPosixPath(p: string): string {
71
- return process.platform === "win32" ? p.replace(/\\/g, "/") : p;
72
- }