@johpaz/hive-sdk 0.0.14 → 0.0.15
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/.github/CODEOWNERS +9 -0
- package/.github/workflows/publish.yml +89 -0
- package/.github/workflows/version-bump.yml +102 -0
- package/CHANGELOG.md +38 -0
- package/README.md +158 -0
- package/bun.lock +543 -0
- package/bunfig.toml +7 -0
- package/docs/API-AGENTS.md +316 -0
- package/docs/API-CONTEXT-COMPILER.md +252 -0
- package/docs/API-DAG-SCHEDULER.md +273 -0
- package/docs/API-TOOLS-SKILLS-CHANNELS.md +293 -0
- package/docs/API-WORKERS-EVENTS.md +152 -0
- package/docs/INDEX.md +141 -0
- package/docs/README.md +68 -0
- package/package.json +54 -105
- package/packages/cli/package.json +17 -0
- package/packages/cli/src/commands/init.ts +56 -0
- package/packages/cli/src/commands/run.ts +45 -0
- package/packages/cli/src/commands/test.ts +42 -0
- package/packages/cli/src/commands/trace.ts +55 -0
- package/packages/cli/src/index.ts +43 -0
- package/packages/core/package.json +58 -0
- package/packages/core/src/ace/Curator.ts +158 -0
- package/packages/core/src/ace/Reflector.ts +200 -0
- package/packages/core/src/ace/Tracer.ts +100 -0
- package/packages/core/src/ace/index.ts +4 -0
- package/packages/core/src/agent/AgentRunner.ts +699 -0
- package/packages/core/src/agent/Compaction.ts +221 -0
- package/packages/core/src/agent/ContextCompiler.ts +567 -0
- package/packages/core/src/agent/ContextGuard.ts +91 -0
- package/packages/core/src/agent/ConversationStore.ts +244 -0
- package/packages/core/src/agent/Hooks.ts +166 -0
- package/packages/core/src/agent/NativeTools.ts +31 -0
- package/packages/core/src/agent/PromptBuilder.ts +169 -0
- package/packages/core/src/agent/Service.ts +267 -0
- package/packages/core/src/agent/StuckLoop.ts +133 -0
- package/packages/core/src/agent/index.ts +12 -0
- package/packages/core/src/agent/providers/LLMClient.ts +149 -0
- package/packages/core/src/agent/providers/anthropic.ts +212 -0
- package/packages/core/src/agent/providers/gemini.ts +215 -0
- package/packages/core/src/agent/providers/index.ts +199 -0
- package/packages/core/src/agent/providers/interface.ts +195 -0
- package/packages/core/src/agent/providers/ollama.ts +175 -0
- package/packages/core/src/agent/providers/openai-compat.ts +231 -0
- package/packages/core/src/agent/providers.ts +1 -0
- package/packages/core/src/agent/selectors/PlaybookSelector.ts +147 -0
- package/packages/core/src/agent/selectors/SkillSelector.ts +478 -0
- package/packages/core/src/agent/selectors/ToolSelector.ts +577 -0
- package/packages/core/src/agent/selectors/index.ts +6 -0
- package/packages/core/src/api/createAgent.test.ts +48 -0
- package/packages/core/src/api/createAgent.ts +122 -0
- package/packages/core/src/api/index.ts +2 -0
- package/packages/core/src/canvas/CanvasManager.ts +390 -0
- package/packages/core/src/canvas/a2ui-tools.ts +255 -0
- package/packages/core/src/canvas/canvas-tools.ts +448 -0
- package/packages/core/src/canvas/emitter.ts +149 -0
- package/packages/core/src/canvas/index.ts +6 -0
- package/packages/core/src/config/index.ts +2 -0
- package/packages/core/src/config/loader.ts +554 -0
- package/packages/core/src/ethics/EthicsGuard.test.ts +54 -0
- package/packages/core/src/ethics/EthicsGuard.ts +66 -0
- package/packages/core/src/ethics/index.ts +2 -0
- package/packages/core/src/gateway/channel-notify.test.ts +14 -0
- package/packages/core/src/gateway/channel-notify.ts +12 -0
- package/packages/core/src/gateway/index.ts +1 -0
- package/packages/core/src/index.ts +37 -0
- package/packages/core/src/mcp/MCPClient.ts +439 -0
- package/packages/core/src/mcp/MCPToolAdapter.ts +176 -0
- package/packages/core/src/mcp/config.ts +13 -0
- package/packages/core/src/mcp/hot-reload.ts +147 -0
- package/packages/core/src/mcp/index.ts +11 -0
- package/packages/core/src/mcp/logger.ts +42 -0
- package/packages/core/src/mcp/singleton.ts +21 -0
- package/packages/core/src/mcp/transports/index.ts +67 -0
- package/packages/core/src/mcp/transports/sse.ts +241 -0
- package/packages/core/src/mcp/transports/websocket.ts +159 -0
- package/packages/core/src/memory/Scratchpad.test.ts +47 -0
- package/packages/core/src/memory/Scratchpad.ts +37 -0
- package/packages/core/src/memory/Storage.ts +6 -0
- package/packages/core/src/memory/index.ts +2 -0
- package/packages/core/src/multimodal/VisionService.ts +293 -0
- package/packages/core/src/multimodal/index.ts +2 -0
- package/packages/core/src/multimodal/types.ts +28 -0
- package/packages/core/src/security/Pairing.ts +250 -0
- package/packages/core/src/security/RateLimit.ts +270 -0
- package/packages/core/src/security/index.ts +4 -0
- package/packages/core/src/skills/SkillLoader.ts +388 -0
- package/packages/core/src/skills/bundled-data.generated.ts +3332 -0
- package/packages/core/src/skills/defineSkill.ts +18 -0
- package/packages/core/src/skills/index.ts +4 -0
- package/packages/core/src/state/index.ts +2 -0
- package/packages/core/src/state/store.ts +312 -0
- package/packages/core/src/storage/SQLiteStorage.ts +407 -0
- package/packages/core/src/storage/crypto.ts +101 -0
- package/packages/core/src/storage/index.ts +10 -0
- package/packages/core/src/storage/onboarding.ts +1603 -0
- package/packages/core/src/storage/schema.ts +689 -0
- package/packages/core/src/storage/seed.ts +740 -0
- package/packages/core/src/storage/usage.ts +374 -0
- package/packages/core/src/swarm/AgentBus.ts +460 -0
- package/packages/core/src/swarm/AgentExecutor.ts +53 -0
- package/packages/core/src/swarm/Coordinator.ts +251 -0
- package/packages/core/src/swarm/EventBridge.ts +122 -0
- package/packages/core/src/swarm/EventBus.ts +169 -0
- package/packages/core/src/swarm/TaskGraph.ts +192 -0
- package/packages/core/src/swarm/TaskNode.ts +97 -0
- package/packages/core/src/swarm/TaskResult.ts +22 -0
- package/packages/core/src/swarm/WorkerPool.ts +236 -0
- package/packages/core/src/swarm/errors.ts +37 -0
- package/packages/core/src/swarm/index.ts +30 -0
- package/packages/core/src/swarm/presets/HiveLearnPreset.ts +99 -0
- package/packages/core/src/swarm/presets/ResearchPreset.ts +97 -0
- package/packages/core/src/swarm/presets/index.ts +4 -0
- package/packages/core/src/swarm/strategies/ParallelStrategy.ts +21 -0
- package/packages/core/src/swarm/strategies/PriorityStrategy.ts +46 -0
- package/packages/core/src/swarm/strategies/index.ts +3 -0
- package/packages/core/src/swarm/types.ts +164 -0
- package/packages/core/src/tools/ToolExecutor.ts +58 -0
- package/packages/core/src/tools/ToolRegistry.test.ts +98 -0
- package/packages/core/src/tools/ToolRegistry.ts +61 -0
- package/packages/core/src/tools/agents/get-available-models.ts +118 -0
- package/packages/core/src/tools/agents/index.ts +715 -0
- package/packages/core/src/tools/bridge-events.ts +26 -0
- package/packages/core/src/tools/canvas/index.ts +375 -0
- package/packages/core/src/tools/cli/index.ts +142 -0
- package/packages/core/src/tools/codebridge/index.ts +342 -0
- package/packages/core/src/tools/core/index.ts +476 -0
- package/packages/core/src/tools/cron/index.ts +626 -0
- package/packages/core/src/tools/filesystem/fs-delete.ts +78 -0
- package/packages/core/src/tools/filesystem/fs-edit.ts +106 -0
- package/packages/core/src/tools/filesystem/fs-exists.ts +63 -0
- package/packages/core/src/tools/filesystem/fs-glob.ts +108 -0
- package/packages/core/src/tools/filesystem/fs-list.ts +129 -0
- package/packages/core/src/tools/filesystem/fs-read.ts +72 -0
- package/packages/core/src/tools/filesystem/fs-write.ts +67 -0
- package/packages/core/src/tools/filesystem/index.ts +34 -0
- package/packages/core/src/tools/filesystem/workspace-guard.ts +62 -0
- package/packages/core/src/tools/index.ts +231 -0
- package/packages/core/src/tools/meeting/index.ts +363 -0
- package/packages/core/src/tools/office/index.ts +47 -0
- package/packages/core/src/tools/office/office-escribir-docx.ts +192 -0
- package/packages/core/src/tools/office/office-escribir-pdf.ts +172 -0
- package/packages/core/src/tools/office/office-escribir-pptx.ts +174 -0
- package/packages/core/src/tools/office/office-escribir-xlsx.ts +116 -0
- package/packages/core/src/tools/office/office-leer-docx.ts +93 -0
- package/packages/core/src/tools/office/office-leer-pdf.ts +114 -0
- package/packages/core/src/tools/office/office-leer-pptx.ts +136 -0
- package/packages/core/src/tools/office/office-leer-xlsx.ts +124 -0
- package/packages/core/src/tools/projects/index.ts +37 -0
- package/packages/core/src/tools/projects/project-create.ts +94 -0
- package/packages/core/src/tools/projects/project-done.ts +66 -0
- package/packages/core/src/tools/projects/project-fail.ts +66 -0
- package/packages/core/src/tools/projects/project-list.ts +96 -0
- package/packages/core/src/tools/projects/project-update.ts +72 -0
- package/packages/core/src/tools/projects/task-create.ts +68 -0
- package/packages/core/src/tools/projects/task-evaluate.ts +93 -0
- package/packages/core/src/tools/projects/task-update.ts +93 -0
- package/packages/core/src/tools/types.ts +39 -0
- package/packages/core/src/tools/voice/index.ts +104 -0
- package/packages/core/src/tools/web/browser-click.ts +78 -0
- package/packages/core/src/tools/web/browser-extract.ts +139 -0
- package/packages/core/src/tools/web/browser-navigate.ts +106 -0
- package/packages/core/src/tools/web/browser-screenshot.ts +87 -0
- package/packages/core/src/tools/web/browser-script.ts +88 -0
- package/packages/core/src/tools/web/browser-service.ts +554 -0
- package/packages/core/src/tools/web/browser-type.ts +101 -0
- package/packages/core/src/tools/web/browser-wait.ts +136 -0
- package/packages/core/src/tools/web/index.ts +41 -0
- package/packages/core/src/tools/web/web-fetch.ts +78 -0
- package/packages/core/src/tools/web/web-search.ts +123 -0
- package/packages/core/src/utils/benchmark.ts +80 -0
- package/packages/core/src/utils/crypto.ts +73 -0
- package/packages/core/src/utils/date.ts +42 -0
- package/packages/core/src/utils/index.ts +10 -0
- package/packages/core/src/utils/logger.ts +389 -0
- package/packages/core/src/utils/retry.ts +70 -0
- package/packages/core/src/utils/toon.ts +253 -0
- package/packages/core/src/voice/index.ts +656 -0
- package/test/setup-db.ts +216 -0
- package/tsconfig.json +39 -0
- package/src/agents.ts +0 -1
- package/src/canvas.ts +0 -1
- package/src/channels.ts +0 -1
- package/src/config.ts +0 -1
- package/src/events.ts +0 -1
- package/src/gateway.ts +0 -1
- package/src/index.ts +0 -304
- package/src/mcp.ts +0 -1
- package/src/multimodal.ts +0 -1
- package/src/scheduler.ts +0 -1
- package/src/security.ts +0 -1
- package/src/skills.ts +0 -1
- package/src/state.ts +0 -1
- package/src/storage.ts +0 -1
- package/src/tools.ts +0 -1
- package/src/tts.ts +0 -1
- package/src/types.ts +0 -82
- package/src/utils.ts +0 -1
- package/src/voice.ts +0 -1
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron Tools for Coordinator Agent
|
|
3
|
+
*
|
|
4
|
+
* Tools for managing cron jobs via natural language chat.
|
|
5
|
+
* These tools are registered with the Coordinator agent and allow
|
|
6
|
+
* users to create, list, update, pause, resume, delete, and trigger cron jobs.
|
|
7
|
+
*
|
|
8
|
+
* @category cron
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Tool } from "../types";
|
|
12
|
+
import { getDb } from "../../storage/SQLiteStorage.ts";
|
|
13
|
+
import { logger } from "../../utils/logger.ts";
|
|
14
|
+
import { Cron } from "croner";
|
|
15
|
+
|
|
16
|
+
const log = logger.child("CronTools");
|
|
17
|
+
|
|
18
|
+
let _scheduler: any = null;
|
|
19
|
+
|
|
20
|
+
export function setSchedulerInstance(scheduler: any): void {
|
|
21
|
+
_scheduler = scheduler;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getSchedulerInstance(): any {
|
|
25
|
+
return _scheduler;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getUserTimezone(): string {
|
|
29
|
+
const db = getDb();
|
|
30
|
+
const user = db.query("SELECT timezone FROM users LIMIT 1").get() as { timezone: string } | undefined;
|
|
31
|
+
return user?.timezone || "UTC";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function resolveBestChannel(userId: string, explicitChannel?: string): string {
|
|
35
|
+
const db = getDb();
|
|
36
|
+
|
|
37
|
+
const user = db.query("SELECT preferred_cron_channel FROM users WHERE id = ? LIMIT 1").get(userId) as {
|
|
38
|
+
preferred_cron_channel: string;
|
|
39
|
+
} | undefined;
|
|
40
|
+
|
|
41
|
+
const activeChannels = db.query(`
|
|
42
|
+
SELECT ui.channel FROM user_identities ui
|
|
43
|
+
JOIN channels c ON c.id = ui.channel
|
|
44
|
+
WHERE ui.user_id = ? AND c.active = 1 AND c.status = 'connected'
|
|
45
|
+
`).all(userId) as { channel: string }[];
|
|
46
|
+
|
|
47
|
+
const identities = activeChannels.length > 0
|
|
48
|
+
? activeChannels
|
|
49
|
+
: db.query("SELECT channel FROM user_identities WHERE user_id = ?").all(userId) as { channel: string }[];
|
|
50
|
+
|
|
51
|
+
if (identities.length === 0) {
|
|
52
|
+
return "webchat";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let bestChannel = "";
|
|
56
|
+
|
|
57
|
+
if (explicitChannel && explicitChannel !== "system") {
|
|
58
|
+
if (identities.some((i) => i.channel === explicitChannel)) {
|
|
59
|
+
bestChannel = explicitChannel;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!bestChannel && user?.preferred_cron_channel && user.preferred_cron_channel !== "auto") {
|
|
64
|
+
if (identities.some((i) => i.channel === user.preferred_cron_channel)) {
|
|
65
|
+
bestChannel = user.preferred_cron_channel;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!bestChannel) {
|
|
70
|
+
const preferred = ["telegram", "discord", "slack", "whatsapp", "webchat"];
|
|
71
|
+
for (const p of preferred) {
|
|
72
|
+
if (identities.some((i) => i.channel === p)) {
|
|
73
|
+
bestChannel = p;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (!bestChannel) {
|
|
80
|
+
bestChannel = identities[0].channel;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return bestChannel;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ─── cron.create ─────────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
export const cronCreateTool: Tool = {
|
|
89
|
+
name: "cron.create",
|
|
90
|
+
description: "Create a new cron job. Use for recurring reminders, daily reports, automated checks. Spanish: crear tarea programada, agendar recordatorio, programar reporte",
|
|
91
|
+
parameters: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
name: { type: "string", description: "Short name for the job (e.g., 'daily-report', 'morning-reminder')" },
|
|
95
|
+
task: { type: "string", description: "REQUIRED: Natural language instruction the agent reads when the job triggers (e.g., 'Generate daily sales report and send summary via Telegram')" },
|
|
96
|
+
task_type: { type: "string", enum: ["recurring", "one_shot"], description: "Type: 'recurring' for cron-based, 'one_shot' for single execution" },
|
|
97
|
+
cron_expression: { type: "string", description: "Cron expression (5-7 fields) for recurring tasks. Example: '0 9 * * *' (daily at 9 AM)" },
|
|
98
|
+
fire_at: { type: "string", description: "ISO 8601 datetime for one_shot tasks. Example: '2026-04-01T09:00:00'" },
|
|
99
|
+
payload: { type: "object", description: "Payload with 'prompt' or 'message' field. Defaults to using 'task' field if omitted" },
|
|
100
|
+
agent_id: { type: "string", description: "Target agent ID (optional, defaults to Coordinator)" },
|
|
101
|
+
tool_name: { type: "string", description: "Specific tool to execute (optional)" },
|
|
102
|
+
max_runs: { type: "number", description: "Maximum executions (optional, null = unlimited)" },
|
|
103
|
+
channel: { type: "string", description: "Notification channel (system, telegram, discord, whatsapp, cli)" },
|
|
104
|
+
start_at: { type: "string", description: "ISO 8601 datetime: start of execution window (Croner startAt). Optional." },
|
|
105
|
+
stop_at: { type: "string", description: "ISO 8601 datetime: end of execution window (Croner stopAt). Optional." },
|
|
106
|
+
dom_and_dow: { type: "boolean", description: "If true, both day-of-month AND day-of-week must match (Croner domAndDow). Default: false (OR logic)" },
|
|
107
|
+
},
|
|
108
|
+
required: ["name", "task", "task_type"],
|
|
109
|
+
},
|
|
110
|
+
execute: async (params: Record<string, unknown>) => {
|
|
111
|
+
const timezone = getUserTimezone();
|
|
112
|
+
|
|
113
|
+
const name = params.name as string | undefined;
|
|
114
|
+
const task = params.task as string | undefined;
|
|
115
|
+
const task_type = params.task_type as "recurring" | "one_shot" | undefined;
|
|
116
|
+
const cron_expression = params.cron_expression as string | undefined;
|
|
117
|
+
const fire_at = params.fire_at as string | undefined;
|
|
118
|
+
const payload = params.payload as Record<string, unknown> | undefined;
|
|
119
|
+
const agent_id = params.agent_id as string | undefined;
|
|
120
|
+
const tool_name = params.tool_name as string | undefined;
|
|
121
|
+
const max_runs = params.max_runs as number | undefined;
|
|
122
|
+
const channel = (params.channel as string) || "system";
|
|
123
|
+
const start_at = params.start_at as string | undefined;
|
|
124
|
+
const stop_at = params.stop_at as string | undefined;
|
|
125
|
+
const dom_and_dow = params.dom_and_dow as boolean | undefined;
|
|
126
|
+
|
|
127
|
+
if (!name) {
|
|
128
|
+
return { ok: false, error: "Missing required field: name" };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!task) {
|
|
132
|
+
return { ok: false, error: "Missing required field: task — provide the instruction the agent should execute" };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!task_type) {
|
|
136
|
+
return { ok: false, error: "Missing required field: task_type (recurring or one_shot)" };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (task_type === "recurring" && !cron_expression) {
|
|
140
|
+
return { ok: false, error: "recurring task requires cron_expression" };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (task_type === "one_shot" && !fire_at) {
|
|
144
|
+
return { ok: false, error: "one_shot task requires fire_at" };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (cron_expression) {
|
|
148
|
+
try {
|
|
149
|
+
new Cron(cron_expression);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
return { ok: false, error: `Invalid cron expression: ${(err as Error).message}` };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (fire_at) {
|
|
156
|
+
const fireAtDate = new Date(fire_at);
|
|
157
|
+
if (fireAtDate.getTime() <= Date.now()) {
|
|
158
|
+
return { ok: false, error: "fire_at must be in the future" };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const payloadObj = payload && !payload._internal
|
|
163
|
+
? payload
|
|
164
|
+
: { prompt: task, ...payload };
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
if (_scheduler) {
|
|
168
|
+
const result = _scheduler.create({
|
|
169
|
+
name,
|
|
170
|
+
task,
|
|
171
|
+
task_type,
|
|
172
|
+
cron_expression,
|
|
173
|
+
fire_at,
|
|
174
|
+
timezone,
|
|
175
|
+
payload: payloadObj,
|
|
176
|
+
agent_id: agent_id || null,
|
|
177
|
+
tool_name: tool_name || null,
|
|
178
|
+
max_runs: max_runs || null,
|
|
179
|
+
channel,
|
|
180
|
+
start_at: start_at || undefined,
|
|
181
|
+
stop_at: stop_at || undefined,
|
|
182
|
+
dom_and_dow: dom_and_dow || false,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
log.info(`[create] Job "${name}" created via scheduler: ${result.id}`);
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
ok: true,
|
|
189
|
+
task_id: result.id,
|
|
190
|
+
next_run: result.nextRun,
|
|
191
|
+
message: `Job "${name}" scheduled. Next run: ${result.nextRun ? new Date(result.nextRun).toLocaleString() : "unknown"}`,
|
|
192
|
+
};
|
|
193
|
+
} else {
|
|
194
|
+
const db = getDb();
|
|
195
|
+
const id = crypto.randomUUID().replace(/-/g, "").slice(0, 16);
|
|
196
|
+
const now = new Date().toISOString();
|
|
197
|
+
const payloadJson = JSON.stringify(payloadObj || { prompt: task });
|
|
198
|
+
|
|
199
|
+
db.query(`
|
|
200
|
+
INSERT INTO cron_jobs (
|
|
201
|
+
id, name, task, task_type, cron_expression, fire_at, timezone,
|
|
202
|
+
start_at, stop_at, dom_and_dow,
|
|
203
|
+
payload, agent_id, tool_name, max_runs, channel,
|
|
204
|
+
status, created_at, updated_at
|
|
205
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?, ?)
|
|
206
|
+
`).run(
|
|
207
|
+
id, name, task, task_type, cron_expression || null, fire_at || null, timezone,
|
|
208
|
+
start_at || null, stop_at || null, dom_and_dow ? 1 : 0,
|
|
209
|
+
payloadJson, agent_id || null, tool_name || null, max_runs || null, channel,
|
|
210
|
+
now, now
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
ok: true,
|
|
215
|
+
task_id: id,
|
|
216
|
+
message: `Job "${name}" saved (scheduler not active)`,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
log.error(`[create] Failed: ${(err as Error).message}`);
|
|
221
|
+
return { ok: false, error: `Failed to create job: ${(err as Error).message}` };
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
// ─── cron.list ────────────────────────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
export const cronListTool: Tool = {
|
|
229
|
+
name: "cron.list",
|
|
230
|
+
description: "List all cron jobs with their next execution times and status. Spanish: ver tareas programadas, listar cronograma",
|
|
231
|
+
parameters: {
|
|
232
|
+
type: "object",
|
|
233
|
+
properties: {
|
|
234
|
+
status: { type: "string", enum: ["active", "paused", "completed", "failed", "cancelled"], description: "Filter by status" },
|
|
235
|
+
task_type: { type: "string", enum: ["recurring", "one_shot"], description: "Filter by task type" },
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
execute: async (params: Record<string, unknown>) => {
|
|
239
|
+
const db = getDb();
|
|
240
|
+
|
|
241
|
+
const status = params.status as string | undefined;
|
|
242
|
+
const task_type = params.task_type as string | undefined;
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
let query = "SELECT * FROM cron_jobs WHERE 1=1";
|
|
246
|
+
const args: any[] = [];
|
|
247
|
+
|
|
248
|
+
if (status) {
|
|
249
|
+
query += " AND status = ?";
|
|
250
|
+
args.push(status);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (task_type) {
|
|
254
|
+
query += " AND task_type = ?";
|
|
255
|
+
args.push(task_type);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
query += " ORDER BY next_run_at ASC";
|
|
259
|
+
|
|
260
|
+
const tasks = db.query(query).all(...args) as any[];
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
ok: true,
|
|
264
|
+
tasks: tasks.map((t) => ({
|
|
265
|
+
id: t.id,
|
|
266
|
+
name: t.name,
|
|
267
|
+
task: t.task,
|
|
268
|
+
type: t.task_type,
|
|
269
|
+
status: t.status,
|
|
270
|
+
cron_expression: t.cron_expression,
|
|
271
|
+
fire_at: t.fire_at,
|
|
272
|
+
start_at: t.start_at,
|
|
273
|
+
stop_at: t.stop_at,
|
|
274
|
+
next_run: t.next_run_at,
|
|
275
|
+
last_run: t.last_run_at,
|
|
276
|
+
run_count: t.run_count,
|
|
277
|
+
channel: t.channel,
|
|
278
|
+
})),
|
|
279
|
+
count: tasks.length,
|
|
280
|
+
};
|
|
281
|
+
} catch (err) {
|
|
282
|
+
log.error(`[list] Failed: ${(err as Error).message}`);
|
|
283
|
+
return { ok: false, error: `Failed to list jobs: ${(err as Error).message}` };
|
|
284
|
+
}
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// ─── cron.update ───────────────────────────────────────────────────────────────
|
|
289
|
+
|
|
290
|
+
export const cronUpdateTool: Tool = {
|
|
291
|
+
name: "cron.update",
|
|
292
|
+
description: "Update an existing cron job: change expression, task instruction, channel, time window, etc. Spanish: actualizar tarea programada, modificar cron, editar recordatorio",
|
|
293
|
+
parameters: {
|
|
294
|
+
type: "object",
|
|
295
|
+
properties: {
|
|
296
|
+
task_id: { type: "string", description: "ID of the job to update" },
|
|
297
|
+
name: { type: "string", description: "New name for the job" },
|
|
298
|
+
task: { type: "string", description: "New instruction the agent reads when the job triggers" },
|
|
299
|
+
cron_expression: { type: "string", description: "New cron expression (for recurring tasks)" },
|
|
300
|
+
fire_at: { type: "string", description: "New fire_at datetime (for one_shot tasks)" },
|
|
301
|
+
payload: { type: "object", description: "New payload object" },
|
|
302
|
+
channel: { type: "string", description: "New notification channel" },
|
|
303
|
+
max_runs: { type: "number", description: "New max executions limit" },
|
|
304
|
+
start_at: { type: "string", description: "New start of execution window (ISO 8601)" },
|
|
305
|
+
stop_at: { type: "string", description: "New end of execution window (ISO 8601)" },
|
|
306
|
+
dom_and_dow: { type: "boolean", description: "Toggle AND logic for day-of-month + day-of-week" },
|
|
307
|
+
agent_id: { type: "string", description: "New target agent ID" },
|
|
308
|
+
tool_name: { type: "string", description: "New tool to execute" },
|
|
309
|
+
},
|
|
310
|
+
required: ["task_id"],
|
|
311
|
+
},
|
|
312
|
+
execute: async (params: Record<string, unknown>) => {
|
|
313
|
+
const task_id = params.task_id as string | undefined;
|
|
314
|
+
|
|
315
|
+
if (!task_id) {
|
|
316
|
+
return { ok: false, error: "Missing required field: task_id" };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const changes: Record<string, unknown> = {};
|
|
320
|
+
if (params.name !== undefined) changes.name = params.name;
|
|
321
|
+
if (params.task !== undefined) changes.task = params.task;
|
|
322
|
+
if (params.cron_expression !== undefined) changes.cron_expression = params.cron_expression;
|
|
323
|
+
if (params.fire_at !== undefined) changes.fire_at = params.fire_at;
|
|
324
|
+
if (params.payload !== undefined) changes.payload = params.payload;
|
|
325
|
+
if (params.channel !== undefined) changes.channel = params.channel;
|
|
326
|
+
if (params.max_runs !== undefined) changes.max_runs = params.max_runs;
|
|
327
|
+
if (params.start_at !== undefined) changes.start_at = params.start_at;
|
|
328
|
+
if (params.stop_at !== undefined) changes.stop_at = params.stop_at;
|
|
329
|
+
if (params.dom_and_dow !== undefined) changes.dom_and_dow = params.dom_and_dow;
|
|
330
|
+
if (params.agent_id !== undefined) changes.agent_id = params.agent_id;
|
|
331
|
+
if (params.tool_name !== undefined) changes.tool_name = params.tool_name;
|
|
332
|
+
|
|
333
|
+
if (Object.keys(changes).length === 0) {
|
|
334
|
+
return { ok: false, error: "No fields to update. Provide at least one field besides task_id." };
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
if (_scheduler) {
|
|
339
|
+
const success = _scheduler.update(task_id, changes);
|
|
340
|
+
if (success) {
|
|
341
|
+
return { ok: true, message: `Job "${task_id}" updated` };
|
|
342
|
+
} else {
|
|
343
|
+
return { ok: false, error: `Job "${task_id}" not found` };
|
|
344
|
+
}
|
|
345
|
+
} else {
|
|
346
|
+
const db = getDb();
|
|
347
|
+
const fields: string[] = [];
|
|
348
|
+
const values: any[] = [];
|
|
349
|
+
|
|
350
|
+
if (changes.name !== undefined) { fields.push("name = ?"); values.push(changes.name); }
|
|
351
|
+
if (changes.task !== undefined) { fields.push("task = ?"); values.push(changes.task); }
|
|
352
|
+
if (changes.cron_expression !== undefined) { fields.push("cron_expression = ?"); values.push(changes.cron_expression); }
|
|
353
|
+
if (changes.fire_at !== undefined) { fields.push("fire_at = ?"); values.push(changes.fire_at); }
|
|
354
|
+
if (changes.payload !== undefined) { fields.push("payload = ?"); values.push(JSON.stringify(changes.payload)); }
|
|
355
|
+
if (changes.channel !== undefined) { fields.push("channel = ?"); values.push(changes.channel); }
|
|
356
|
+
if (changes.max_runs !== undefined) { fields.push("max_runs = ?"); values.push(changes.max_runs); }
|
|
357
|
+
if (changes.start_at !== undefined) { fields.push("start_at = ?"); values.push(changes.start_at); }
|
|
358
|
+
if (changes.stop_at !== undefined) { fields.push("stop_at = ?"); values.push(changes.stop_at); }
|
|
359
|
+
if (changes.dom_and_dow !== undefined) { fields.push("dom_and_dow = ?"); values.push(changes.dom_and_dow ? 1 : 0); }
|
|
360
|
+
if (changes.agent_id !== undefined) { fields.push("agent_id = ?"); values.push(changes.agent_id); }
|
|
361
|
+
if (changes.tool_name !== undefined) { fields.push("tool_name = ?"); values.push(changes.tool_name); }
|
|
362
|
+
|
|
363
|
+
if (fields.length === 0) {
|
|
364
|
+
return { ok: true, message: "No changes to apply" };
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
values.push(task_id);
|
|
368
|
+
const result = db.query(`UPDATE cron_jobs SET ${fields.join(", ")} WHERE id = ?`).run(...values);
|
|
369
|
+
|
|
370
|
+
if (result.changes > 0) {
|
|
371
|
+
return { ok: true, message: `Job "${task_id}" updated (scheduler not active)` };
|
|
372
|
+
} else {
|
|
373
|
+
return { ok: false, error: `Job "${task_id}" not found` };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
} catch (err) {
|
|
377
|
+
log.error(`[update] Failed: ${(err as Error).message}`);
|
|
378
|
+
return { ok: false, error: `Failed to update job: ${(err as Error).message}` };
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
// ─── cron.pause ───────────────────────────────────────────────────────────────
|
|
384
|
+
|
|
385
|
+
export const cronPauseTool: Tool = {
|
|
386
|
+
name: "cron.pause",
|
|
387
|
+
description: "Pause a cron job temporarily without deleting it. Spanish: pausar tarea programada, detener temporalmente",
|
|
388
|
+
parameters: {
|
|
389
|
+
type: "object",
|
|
390
|
+
properties: {
|
|
391
|
+
task_id: { type: "string", description: "ID of the job to pause" },
|
|
392
|
+
},
|
|
393
|
+
required: ["task_id"],
|
|
394
|
+
},
|
|
395
|
+
execute: async (params: Record<string, unknown>) => {
|
|
396
|
+
const task_id = params.task_id as string | undefined;
|
|
397
|
+
|
|
398
|
+
if (!task_id) {
|
|
399
|
+
return { ok: false, error: "Missing required field: task_id" };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
if (_scheduler) {
|
|
404
|
+
const success = _scheduler.pause(task_id);
|
|
405
|
+
if (success) {
|
|
406
|
+
return { ok: true, message: `Job "${task_id}" paused` };
|
|
407
|
+
} else {
|
|
408
|
+
return { ok: false, error: `Job "${task_id}" not found or already paused` };
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
const db = getDb();
|
|
412
|
+
const result = db.query(
|
|
413
|
+
"UPDATE cron_jobs SET status = 'paused' WHERE id = ?"
|
|
414
|
+
).run(task_id);
|
|
415
|
+
|
|
416
|
+
if (result.changes > 0) {
|
|
417
|
+
return { ok: true, message: `Job "${task_id}" paused (scheduler not active)` };
|
|
418
|
+
} else {
|
|
419
|
+
return { ok: false, error: `Job "${task_id}" not found` };
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
} catch (err) {
|
|
423
|
+
log.error(`[pause] Failed: ${(err as Error).message}`);
|
|
424
|
+
return { ok: false, error: `Failed to pause job: ${(err as Error).message}` };
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// ─── cron.resume ──────────────────────────────────────────────────────────────
|
|
430
|
+
|
|
431
|
+
export const cronResumeTool: Tool = {
|
|
432
|
+
name: "cron.resume",
|
|
433
|
+
description: "Resume a paused cron job. Spanish: reanudar tarea programada, continuar",
|
|
434
|
+
parameters: {
|
|
435
|
+
type: "object",
|
|
436
|
+
properties: {
|
|
437
|
+
task_id: { type: "string", description: "ID of the job to resume" },
|
|
438
|
+
},
|
|
439
|
+
required: ["task_id"],
|
|
440
|
+
},
|
|
441
|
+
execute: async (params: Record<string, unknown>) => {
|
|
442
|
+
const task_id = params.task_id as string | undefined;
|
|
443
|
+
|
|
444
|
+
if (!task_id) {
|
|
445
|
+
return { ok: false, error: "Missing required field: task_id" };
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
if (_scheduler) {
|
|
450
|
+
const success = _scheduler.resume(task_id);
|
|
451
|
+
if (success) {
|
|
452
|
+
return { ok: true, message: `Job "${task_id}" resumed` };
|
|
453
|
+
} else {
|
|
454
|
+
return { ok: false, error: `Job "${task_id}" not found or already active` };
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
const db = getDb();
|
|
458
|
+
const result = db.query(
|
|
459
|
+
"UPDATE cron_jobs SET status = 'active' WHERE id = ?"
|
|
460
|
+
).run(task_id);
|
|
461
|
+
|
|
462
|
+
if (result.changes > 0) {
|
|
463
|
+
return { ok: true, message: `Job "${task_id}" resumed (scheduler not active)` };
|
|
464
|
+
} else {
|
|
465
|
+
return { ok: false, error: `Job "${task_id}" not found` };
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
} catch (err) {
|
|
469
|
+
log.error(`[resume] Failed: ${(err as Error).message}`);
|
|
470
|
+
return { ok: false, error: `Failed to resume job: ${(err as Error).message}` };
|
|
471
|
+
}
|
|
472
|
+
},
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
// ─── cron.delete ──────────────────────────────────────────────────────────────
|
|
476
|
+
|
|
477
|
+
export const cronDeleteTool: Tool = {
|
|
478
|
+
name: "cron.delete",
|
|
479
|
+
description: "Delete a cron job permanently. Spanish: eliminar tarea programada, cancelar recordatorio",
|
|
480
|
+
parameters: {
|
|
481
|
+
type: "object",
|
|
482
|
+
properties: {
|
|
483
|
+
task_id: { type: "string", description: "ID of the job to delete" },
|
|
484
|
+
},
|
|
485
|
+
required: ["task_id"],
|
|
486
|
+
},
|
|
487
|
+
execute: async (params: Record<string, unknown>) => {
|
|
488
|
+
const task_id = params.task_id as string | undefined;
|
|
489
|
+
|
|
490
|
+
if (!task_id) {
|
|
491
|
+
return { ok: false, error: "Missing required field: task_id" };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
if (_scheduler) {
|
|
496
|
+
const success = _scheduler.delete(task_id);
|
|
497
|
+
if (success) {
|
|
498
|
+
return { ok: true, message: `Job "${task_id}" deleted` };
|
|
499
|
+
} else {
|
|
500
|
+
return { ok: false, error: `Job "${task_id}" not found` };
|
|
501
|
+
}
|
|
502
|
+
} else {
|
|
503
|
+
const db = getDb();
|
|
504
|
+
const result = db.query(
|
|
505
|
+
"DELETE FROM cron_jobs WHERE id = ?"
|
|
506
|
+
).run(task_id);
|
|
507
|
+
|
|
508
|
+
if (result.changes > 0) {
|
|
509
|
+
return { ok: true, message: `Job "${task_id}" deleted (scheduler not active)` };
|
|
510
|
+
} else {
|
|
511
|
+
return { ok: false, error: `Job "${task_id}" not found` };
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
} catch (err) {
|
|
515
|
+
log.error(`[delete] Failed: ${(err as Error).message}`);
|
|
516
|
+
return { ok: false, error: `Failed to delete job: ${(err as Error).message}` };
|
|
517
|
+
}
|
|
518
|
+
},
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// ─── cron.trigger ─────────────────────────────────────────────────────────────
|
|
522
|
+
|
|
523
|
+
export const cronTriggerTool: Tool = {
|
|
524
|
+
name: "cron.trigger",
|
|
525
|
+
description: "Manually trigger a cron job execution immediately. Spanish: ejecutar tarea ahora, forzar ejecución",
|
|
526
|
+
parameters: {
|
|
527
|
+
type: "object",
|
|
528
|
+
properties: {
|
|
529
|
+
task_id: { type: "string", description: "ID of the job to trigger" },
|
|
530
|
+
},
|
|
531
|
+
required: ["task_id"],
|
|
532
|
+
},
|
|
533
|
+
execute: async (params: Record<string, unknown>) => {
|
|
534
|
+
const task_id = params.task_id as string | undefined;
|
|
535
|
+
|
|
536
|
+
if (!task_id) {
|
|
537
|
+
return { ok: false, error: "Missing required field: task_id" };
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
try {
|
|
541
|
+
if (_scheduler) {
|
|
542
|
+
const success = _scheduler.trigger(task_id);
|
|
543
|
+
if (success) {
|
|
544
|
+
return { ok: true, message: `Job "${task_id}" triggered` };
|
|
545
|
+
} else {
|
|
546
|
+
return { ok: false, error: `Job "${task_id}" not found or not active` };
|
|
547
|
+
}
|
|
548
|
+
} else {
|
|
549
|
+
return { ok: false, error: "Scheduler not active - cannot trigger jobs" };
|
|
550
|
+
}
|
|
551
|
+
} catch (err) {
|
|
552
|
+
log.error(`[trigger] Failed: ${(err as Error).message}`);
|
|
553
|
+
return { ok: false, error: `Failed to trigger job: ${(err as Error).message}` };
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
// ─── cron.history ─────────────────────────────────────────────────────────────
|
|
559
|
+
|
|
560
|
+
export const cronHistoryTool: Tool = {
|
|
561
|
+
name: "cron.history",
|
|
562
|
+
description: "Get execution history for a cron job. Spanish: historial de ejecuciones, logs de tarea",
|
|
563
|
+
parameters: {
|
|
564
|
+
type: "object",
|
|
565
|
+
properties: {
|
|
566
|
+
task_id: { type: "string", description: "ID of the job" },
|
|
567
|
+
limit: { type: "number", description: "Maximum number of records (default: 10)" },
|
|
568
|
+
},
|
|
569
|
+
required: ["task_id"],
|
|
570
|
+
},
|
|
571
|
+
execute: async (params: Record<string, unknown>) => {
|
|
572
|
+
const task_id = params.task_id as string | undefined;
|
|
573
|
+
const limit = (params.limit as number) || 10;
|
|
574
|
+
|
|
575
|
+
if (!task_id) {
|
|
576
|
+
return { ok: false, error: "Missing required field: task_id" };
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
try {
|
|
580
|
+
const db = getDb();
|
|
581
|
+
const runs = db.query(`
|
|
582
|
+
SELECT * FROM task_runs
|
|
583
|
+
WHERE task_id = ?
|
|
584
|
+
ORDER BY started_at DESC
|
|
585
|
+
LIMIT ?
|
|
586
|
+
`).all(task_id, limit) as any[];
|
|
587
|
+
|
|
588
|
+
return {
|
|
589
|
+
ok: true,
|
|
590
|
+
history: runs.map((r) => ({
|
|
591
|
+
id: r.id,
|
|
592
|
+
status: r.status,
|
|
593
|
+
started_at: r.started_at,
|
|
594
|
+
finished_at: r.finished_at,
|
|
595
|
+
duration_ms: r.duration_ms,
|
|
596
|
+
error_message: r.error_message,
|
|
597
|
+
})),
|
|
598
|
+
count: runs.length,
|
|
599
|
+
};
|
|
600
|
+
} catch (err) {
|
|
601
|
+
log.error(`[history] Failed: ${(err as Error).message}`);
|
|
602
|
+
return { ok: false, error: `Failed to get history: ${(err as Error).message}` };
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Create all cron tools
|
|
609
|
+
*/
|
|
610
|
+
export function createTools(): Tool[] {
|
|
611
|
+
return [
|
|
612
|
+
cronCreateTool,
|
|
613
|
+
cronListTool,
|
|
614
|
+
cronUpdateTool,
|
|
615
|
+
cronPauseTool,
|
|
616
|
+
cronResumeTool,
|
|
617
|
+
cronDeleteTool,
|
|
618
|
+
cronTriggerTool,
|
|
619
|
+
cronHistoryTool,
|
|
620
|
+
];
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Alias for backward compatibility
|
|
625
|
+
*/
|
|
626
|
+
export const createCronTools = createTools;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fs_delete - Delete file or directory from workspace
|
|
3
|
+
*
|
|
4
|
+
* @category filesystem
|
|
5
|
+
* @seedId fs_delete
|
|
6
|
+
* @spanish eliminar archivo, borrar archivo, borrar carpeta
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Tool } from "../types.ts";
|
|
10
|
+
import { logger } from "../../utils/logger.ts";
|
|
11
|
+
import { resolveInWorkspace, getWorkspace } from "./workspace-guard.ts";
|
|
12
|
+
import * as fs from "node:fs";
|
|
13
|
+
|
|
14
|
+
const log = logger.child("fs-delete");
|
|
15
|
+
|
|
16
|
+
export const fsDeleteTool: Tool = {
|
|
17
|
+
name: "fs_delete",
|
|
18
|
+
description: "Delete file or directory from workspace. Spanish: eliminar archivo, borrar archivo, borrar carpeta",
|
|
19
|
+
parameters: {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
path: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: "Path to the file or directory to delete",
|
|
25
|
+
},
|
|
26
|
+
recursive: {
|
|
27
|
+
type: "boolean",
|
|
28
|
+
description: "Delete recursively for directories (default: false)",
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
required: ["path"],
|
|
32
|
+
},
|
|
33
|
+
execute: async (params: Record<string, unknown>, config?: any) => {
|
|
34
|
+
const workspace = getWorkspace(config);
|
|
35
|
+
let targetPath: string;
|
|
36
|
+
try {
|
|
37
|
+
targetPath = resolveInWorkspace(params.path as string, workspace);
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return { ok: false, error: (e as Error).message };
|
|
40
|
+
}
|
|
41
|
+
const recursive = (params.recursive as boolean) ?? false;
|
|
42
|
+
|
|
43
|
+
log.debug(`Deleting: ${targetPath}`);
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
if (!fs.existsSync(targetPath)) {
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: `Path not found: ${targetPath}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const stats = fs.statSync(targetPath);
|
|
54
|
+
|
|
55
|
+
if (stats.isDirectory()) {
|
|
56
|
+
if (recursive) {
|
|
57
|
+
fs.rmSync(targetPath, { recursive: true });
|
|
58
|
+
} else {
|
|
59
|
+
fs.rmdirSync(targetPath);
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
fs.unlinkSync(targetPath);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
path: targetPath,
|
|
68
|
+
deleted: true,
|
|
69
|
+
};
|
|
70
|
+
} catch (error) {
|
|
71
|
+
log.error(`Error deleting: ${(error as Error).message}`);
|
|
72
|
+
return {
|
|
73
|
+
ok: false,
|
|
74
|
+
error: `Failed to delete: ${(error as Error).message}`,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
};
|