@synergenius/flow-weaver-pack-weaver 0.8.3 → 0.9.3
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/dist/bot/ai-client.d.ts +22 -2
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +168 -20
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/assistant-core.d.ts +25 -0
- package/dist/bot/assistant-core.d.ts.map +1 -0
- package/dist/bot/assistant-core.js +265 -0
- package/dist/bot/assistant-core.js.map +1 -0
- package/dist/bot/assistant-tools.d.ts +9 -0
- package/dist/bot/assistant-tools.d.ts.map +1 -0
- package/dist/bot/assistant-tools.js +602 -0
- package/dist/bot/assistant-tools.js.map +1 -0
- package/dist/bot/audit-logger.d.ts.map +1 -1
- package/dist/bot/audit-logger.js +9 -5
- package/dist/bot/audit-logger.js.map +1 -1
- package/dist/bot/audit-store.d.ts.map +1 -1
- package/dist/bot/audit-store.js +3 -11
- package/dist/bot/audit-store.js.map +1 -1
- package/dist/bot/bot-manager.d.ts +49 -0
- package/dist/bot/bot-manager.d.ts.map +1 -0
- package/dist/bot/bot-manager.js +279 -0
- package/dist/bot/bot-manager.js.map +1 -0
- package/dist/bot/child-process-tracker.d.ts +6 -0
- package/dist/bot/child-process-tracker.d.ts.map +1 -0
- package/dist/bot/child-process-tracker.js +35 -0
- package/dist/bot/child-process-tracker.js.map +1 -0
- package/dist/bot/cli-provider.d.ts.map +1 -1
- package/dist/bot/cli-provider.js +13 -8
- package/dist/bot/cli-provider.js.map +1 -1
- package/dist/bot/conversation-store.d.ts +40 -0
- package/dist/bot/conversation-store.d.ts.map +1 -0
- package/dist/bot/conversation-store.js +182 -0
- package/dist/bot/conversation-store.js.map +1 -0
- package/dist/bot/cost-store.d.ts.map +1 -1
- package/dist/bot/cost-store.js +10 -14
- package/dist/bot/cost-store.js.map +1 -1
- package/dist/bot/error-guide.d.ts +10 -0
- package/dist/bot/error-guide.d.ts.map +1 -0
- package/dist/bot/error-guide.js +34 -0
- package/dist/bot/error-guide.js.map +1 -0
- package/dist/bot/genesis-store.d.ts.map +1 -1
- package/dist/bot/genesis-store.js +11 -20
- package/dist/bot/genesis-store.js.map +1 -1
- package/dist/bot/index.d.ts +3 -0
- package/dist/bot/index.d.ts.map +1 -1
- package/dist/bot/index.js +3 -0
- package/dist/bot/index.js.map +1 -1
- package/dist/bot/knowledge-store.d.ts +17 -0
- package/dist/bot/knowledge-store.d.ts.map +1 -0
- package/dist/bot/knowledge-store.js +53 -0
- package/dist/bot/knowledge-store.js.map +1 -0
- package/dist/bot/pipeline-runner.d.ts.map +1 -1
- package/dist/bot/pipeline-runner.js +8 -1
- package/dist/bot/pipeline-runner.js.map +1 -1
- package/dist/bot/retry-utils.d.ts +19 -0
- package/dist/bot/retry-utils.d.ts.map +1 -0
- package/dist/bot/retry-utils.js +64 -0
- package/dist/bot/retry-utils.js.map +1 -0
- package/dist/bot/run-store.d.ts.map +1 -1
- package/dist/bot/run-store.js +2 -10
- package/dist/bot/run-store.js.map +1 -1
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +24 -3
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/safe-json.d.ts +32 -0
- package/dist/bot/safe-json.d.ts.map +1 -0
- package/dist/bot/safe-json.js +56 -0
- package/dist/bot/safe-json.js.map +1 -0
- package/dist/bot/safe-path.d.ts +18 -0
- package/dist/bot/safe-path.d.ts.map +1 -0
- package/dist/bot/safe-path.js +40 -0
- package/dist/bot/safe-path.js.map +1 -0
- package/dist/bot/session-state.d.ts.map +1 -1
- package/dist/bot/session-state.js +3 -1
- package/dist/bot/session-state.js.map +1 -1
- package/dist/bot/steering.js +1 -1
- package/dist/bot/steering.js.map +1 -1
- package/dist/bot/step-executor.d.ts +10 -5
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +252 -3
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/system-prompt.d.ts +1 -1
- package/dist/bot/system-prompt.d.ts.map +1 -1
- package/dist/bot/system-prompt.js +69 -43
- package/dist/bot/system-prompt.js.map +1 -1
- package/dist/bot/task-decomposer.d.ts +24 -0
- package/dist/bot/task-decomposer.d.ts.map +1 -0
- package/dist/bot/task-decomposer.js +75 -0
- package/dist/bot/task-decomposer.js.map +1 -0
- package/dist/bot/task-queue.d.ts +17 -4
- package/dist/bot/task-queue.d.ts.map +1 -1
- package/dist/bot/task-queue.js +102 -14
- package/dist/bot/task-queue.js.map +1 -1
- package/dist/bot/terminal-renderer.d.ts +60 -0
- package/dist/bot/terminal-renderer.d.ts.map +1 -0
- package/dist/bot/terminal-renderer.js +205 -0
- package/dist/bot/terminal-renderer.js.map +1 -0
- package/dist/bot/types.d.ts +7 -0
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/bot/weaver-tools.d.ts +18 -0
- package/dist/bot/weaver-tools.d.ts.map +1 -0
- package/dist/bot/weaver-tools.js +215 -0
- package/dist/bot/weaver-tools.js.map +1 -0
- package/dist/cli-bridge.d.ts.map +1 -1
- package/dist/cli-bridge.js +10 -3
- package/dist/cli-bridge.js.map +1 -1
- package/dist/cli-handlers.d.ts +15 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +742 -28
- package/dist/cli-handlers.js.map +1 -1
- package/dist/handlers/on-bot-completed.d.ts +21 -0
- package/dist/handlers/on-bot-completed.d.ts.map +1 -0
- package/dist/handlers/on-bot-completed.js +28 -0
- package/dist/handlers/on-bot-completed.js.map +1 -0
- package/dist/handlers/on-execution-failure.d.ts +23 -0
- package/dist/handlers/on-execution-failure.d.ts.map +1 -0
- package/dist/handlers/on-execution-failure.js +28 -0
- package/dist/handlers/on-execution-failure.js.map +1 -0
- package/dist/handlers/scheduled-run.d.ts +24 -0
- package/dist/handlers/scheduled-run.d.ts.map +1 -0
- package/dist/handlers/scheduled-run.js +25 -0
- package/dist/handlers/scheduled-run.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-tools.js +2 -2
- package/dist/mcp-tools.js.map +1 -1
- package/dist/node-types/abort-task.d.ts.map +1 -1
- package/dist/node-types/abort-task.js +4 -3
- package/dist/node-types/abort-task.js.map +1 -1
- package/dist/node-types/agent-execute.d.ts +38 -0
- package/dist/node-types/agent-execute.d.ts.map +1 -0
- package/dist/node-types/agent-execute.js +256 -0
- package/dist/node-types/agent-execute.js.map +1 -0
- package/dist/node-types/bot-report.d.ts +5 -3
- package/dist/node-types/bot-report.d.ts.map +1 -1
- package/dist/node-types/bot-report.js +39 -7
- package/dist/node-types/bot-report.js.map +1 -1
- package/dist/node-types/build-context.d.ts +3 -3
- package/dist/node-types/build-context.d.ts.map +1 -1
- package/dist/node-types/build-context.js +108 -24
- package/dist/node-types/build-context.js.map +1 -1
- package/dist/node-types/detect-provider.d.ts +2 -2
- package/dist/node-types/detect-provider.d.ts.map +1 -1
- package/dist/node-types/detect-provider.js +3 -1
- package/dist/node-types/detect-provider.js.map +1 -1
- package/dist/node-types/exec-validate-retry.d.ts.map +1 -1
- package/dist/node-types/exec-validate-retry.js +43 -6
- package/dist/node-types/exec-validate-retry.js.map +1 -1
- package/dist/node-types/execute-plan.d.ts.map +1 -1
- package/dist/node-types/execute-plan.js +31 -8
- package/dist/node-types/execute-plan.js.map +1 -1
- package/dist/node-types/execute-target.d.ts.map +1 -1
- package/dist/node-types/execute-target.js +3 -1
- package/dist/node-types/execute-target.js.map +1 -1
- package/dist/node-types/fix-errors.d.ts.map +1 -1
- package/dist/node-types/fix-errors.js +21 -5
- package/dist/node-types/fix-errors.js.map +1 -1
- package/dist/node-types/genesis-observe.d.ts.map +1 -1
- package/dist/node-types/genesis-observe.js +3 -1
- package/dist/node-types/genesis-observe.js.map +1 -1
- package/dist/node-types/genesis-report.js +4 -1
- package/dist/node-types/genesis-report.js.map +1 -1
- package/dist/node-types/git-ops.d.ts.map +1 -1
- package/dist/node-types/git-ops.js +98 -4
- package/dist/node-types/git-ops.js.map +1 -1
- package/dist/node-types/index.d.ts +2 -0
- package/dist/node-types/index.d.ts.map +1 -1
- package/dist/node-types/index.js +2 -0
- package/dist/node-types/index.js.map +1 -1
- package/dist/node-types/load-config.d.ts +2 -2
- package/dist/node-types/load-config.d.ts.map +1 -1
- package/dist/node-types/load-config.js.map +1 -1
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +14 -2
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/node-types/read-workflow.js +8 -2
- package/dist/node-types/read-workflow.js.map +1 -1
- package/dist/node-types/receive-task.d.ts.map +1 -1
- package/dist/node-types/receive-task.js +35 -26
- package/dist/node-types/receive-task.js.map +1 -1
- package/dist/node-types/send-notify.js +2 -1
- package/dist/node-types/send-notify.js.map +1 -1
- package/dist/node-types/validate-gate.d.ts +18 -0
- package/dist/node-types/validate-gate.d.ts.map +1 -0
- package/dist/node-types/validate-gate.js +96 -0
- package/dist/node-types/validate-gate.js.map +1 -0
- package/dist/workflows/genesis-task.d.ts +20 -12
- package/dist/workflows/genesis-task.d.ts.map +1 -1
- package/dist/workflows/genesis-task.js +20 -12
- package/dist/workflows/genesis-task.js.map +1 -1
- package/dist/workflows/weaver-agent.d.ts +35 -0
- package/dist/workflows/weaver-agent.d.ts.map +1 -0
- package/dist/workflows/weaver-agent.js +777 -0
- package/dist/workflows/weaver-agent.js.map +1 -0
- package/dist/workflows/weaver-bot-batch.d.ts +19 -26
- package/dist/workflows/weaver-bot-batch.d.ts.map +1 -1
- package/dist/workflows/weaver-bot-batch.js +1043 -27
- package/dist/workflows/weaver-bot-batch.js.map +1 -1
- package/dist/workflows/weaver-bot.d.ts +21 -35
- package/dist/workflows/weaver-bot.d.ts.map +1 -1
- package/dist/workflows/weaver-bot.js +1119 -36
- package/dist/workflows/weaver-bot.js.map +1 -1
- package/flowweaver.manifest.json +113 -2
- package/package.json +5 -2
- package/src/bot/ai-client.ts +180 -19
- package/src/bot/assistant-core.ts +306 -0
- package/src/bot/assistant-tools.ts +605 -0
- package/src/bot/audit-logger.ts +6 -5
- package/src/bot/audit-store.ts +3 -12
- package/src/bot/bot-manager.ts +293 -0
- package/src/bot/child-process-tracker.ts +40 -0
- package/src/bot/cli-provider.ts +13 -8
- package/src/bot/conversation-store.ts +222 -0
- package/src/bot/cost-store.ts +11 -12
- package/src/bot/error-guide.ts +34 -0
- package/src/bot/genesis-store.ts +11 -17
- package/src/bot/index.ts +5 -0
- package/src/bot/knowledge-store.ts +59 -0
- package/src/bot/pipeline-runner.ts +7 -1
- package/src/bot/retry-utils.ts +76 -0
- package/src/bot/run-store.ts +2 -11
- package/src/bot/runner.ts +26 -3
- package/src/bot/safe-json.ts +76 -0
- package/src/bot/safe-path.ts +44 -0
- package/src/bot/session-state.ts +2 -1
- package/src/bot/steering.ts +1 -1
- package/src/bot/step-executor.ts +313 -5
- package/src/bot/system-prompt.ts +70 -47
- package/src/bot/task-decomposer.ts +100 -0
- package/src/bot/task-queue.ts +119 -15
- package/src/bot/terminal-renderer.ts +241 -0
- package/src/bot/types.ts +8 -0
- package/src/bot/weaver-tools.ts +225 -0
- package/src/cli-bridge.ts +14 -3
- package/src/cli-handlers.ts +760 -29
- package/src/handlers/on-bot-completed.ts +48 -0
- package/src/handlers/on-execution-failure.ts +42 -0
- package/src/handlers/scheduled-run.ts +42 -0
- package/src/index.ts +5 -0
- package/src/mcp-tools.ts +2 -2
- package/src/node-types/abort-task.ts +5 -4
- package/src/node-types/agent-execute.ts +306 -0
- package/src/node-types/bot-report.ts +40 -9
- package/src/node-types/build-context.ts +112 -25
- package/src/node-types/detect-provider.ts +4 -3
- package/src/node-types/exec-validate-retry.ts +47 -8
- package/src/node-types/execute-plan.ts +32 -8
- package/src/node-types/execute-target.ts +2 -1
- package/src/node-types/fix-errors.ts +20 -5
- package/src/node-types/genesis-observe.ts +2 -1
- package/src/node-types/genesis-report.ts +1 -1
- package/src/node-types/git-ops.ts +93 -4
- package/src/node-types/index.ts +2 -0
- package/src/node-types/load-config.ts +3 -3
- package/src/node-types/plan-task.ts +15 -3
- package/src/node-types/read-workflow.ts +2 -2
- package/src/node-types/receive-task.ts +31 -26
- package/src/node-types/send-notify.ts +1 -1
- package/src/node-types/validate-gate.ts +112 -0
- package/src/workflows/genesis-task.ts +20 -12
- package/src/workflows/weaver-agent.ts +799 -0
- package/src/workflows/weaver-bot-batch.ts +1049 -27
- package/src/workflows/weaver-bot.ts +1123 -36
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event handler: bot.completed
|
|
3
|
+
*
|
|
4
|
+
* Triggered by the platform event bus when any bot execution completes.
|
|
5
|
+
* Emits a pack-namespaced event with the completion summary for
|
|
6
|
+
* downstream consumers (dashboard widgets, notification webhooks).
|
|
7
|
+
*
|
|
8
|
+
* Runs inside the platform sandbox with events:emit capability.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface BotCompletedPayload {
|
|
12
|
+
userId?: string;
|
|
13
|
+
botId?: string;
|
|
14
|
+
executionId?: string;
|
|
15
|
+
status?: string;
|
|
16
|
+
executionTimeMs?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface EventBus {
|
|
20
|
+
emit(event: string, payload: Record<string, unknown>): void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
declare const __fw_event_bus__: EventBus | undefined;
|
|
24
|
+
|
|
25
|
+
export async function onBotCompleted(
|
|
26
|
+
_execute: boolean,
|
|
27
|
+
params: BotCompletedPayload,
|
|
28
|
+
): Promise<{ acknowledged: boolean }> {
|
|
29
|
+
const { botId, executionId, status, executionTimeMs } = params;
|
|
30
|
+
|
|
31
|
+
// Only process weaver bot completions
|
|
32
|
+
if (botId !== 'weaver-bot' && botId !== 'weaver-genesis') {
|
|
33
|
+
return { acknowledged: false };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Emit a pack-namespaced event with enriched data
|
|
37
|
+
if (typeof __fw_event_bus__ !== 'undefined') {
|
|
38
|
+
__fw_event_bus__.emit('pack.weaver.run-completed', {
|
|
39
|
+
botId,
|
|
40
|
+
executionId,
|
|
41
|
+
status,
|
|
42
|
+
executionTimeMs,
|
|
43
|
+
completedAt: Date.now(),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { acknowledged: true };
|
|
48
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event handler: execution.failed
|
|
3
|
+
*
|
|
4
|
+
* Triggered by the platform event bus when any workflow execution fails.
|
|
5
|
+
* Sends a notification via the configured webhook (Slack/Discord/generic).
|
|
6
|
+
*
|
|
7
|
+
* Runs inside the platform sandbox — network access is only available
|
|
8
|
+
* through the IPC fetch proxy with domain allowlist enforcement.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
interface ExecutionFailedPayload {
|
|
12
|
+
userId?: string;
|
|
13
|
+
workflowId?: string;
|
|
14
|
+
executionId?: string;
|
|
15
|
+
deploymentSlug?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
executionTimeMs?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function onExecutionFailure(
|
|
21
|
+
_execute: boolean,
|
|
22
|
+
params: ExecutionFailedPayload,
|
|
23
|
+
): Promise<{ notified: boolean; error?: string }> {
|
|
24
|
+
const { workflowId, executionId, deploymentSlug, error } = params;
|
|
25
|
+
|
|
26
|
+
const summary = [
|
|
27
|
+
`Workflow execution failed`,
|
|
28
|
+
deploymentSlug ? `Deployment: ${deploymentSlug}` : null,
|
|
29
|
+
workflowId ? `Workflow: ${workflowId}` : null,
|
|
30
|
+
executionId ? `Execution: ${executionId}` : null,
|
|
31
|
+
error ? `Error: ${error}` : null,
|
|
32
|
+
]
|
|
33
|
+
.filter(Boolean)
|
|
34
|
+
.join('\n');
|
|
35
|
+
|
|
36
|
+
// The platform will route this through webhooks declared in the manifest.
|
|
37
|
+
// This handler emits a structured result that the webhook system can format.
|
|
38
|
+
return {
|
|
39
|
+
notified: true,
|
|
40
|
+
error: undefined,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event handler: schedule.tick
|
|
3
|
+
*
|
|
4
|
+
* Triggered by the platform cron scheduler at the interval declared
|
|
5
|
+
* in the manifest (default: every 30 minutes).
|
|
6
|
+
*
|
|
7
|
+
* Checks the task queue for pending tasks and processes the next one.
|
|
8
|
+
* Emits pack.weaver.task-started and pack.weaver.task-completed events
|
|
9
|
+
* via the sandbox event bus IPC channel.
|
|
10
|
+
*
|
|
11
|
+
* Runs inside the platform sandbox with ai:chat and events:emit capabilities.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface ScheduleTickPayload {
|
|
15
|
+
scheduleId?: string;
|
|
16
|
+
cronExpression?: string;
|
|
17
|
+
timestamp?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface EventBus {
|
|
21
|
+
emit(event: string, payload: Record<string, unknown>): void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const __fw_event_bus__: EventBus | undefined;
|
|
25
|
+
|
|
26
|
+
export async function onScheduledRun(
|
|
27
|
+
_execute: boolean,
|
|
28
|
+
params: ScheduleTickPayload,
|
|
29
|
+
): Promise<{ processed: boolean; taskId?: string; skipped?: string }> {
|
|
30
|
+
// In the sandbox, the task queue is accessible via the workspace filesystem.
|
|
31
|
+
// The handler reads the queue file, picks the next pending task, and processes it.
|
|
32
|
+
|
|
33
|
+
// For now, emit a heartbeat event so the platform knows the scheduler is alive.
|
|
34
|
+
if (typeof __fw_event_bus__ !== 'undefined') {
|
|
35
|
+
__fw_event_bus__.emit('pack.weaver.scheduler-heartbeat', {
|
|
36
|
+
timestamp: Date.now(),
|
|
37
|
+
cronExpression: params.cronExpression,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { processed: false, skipped: 'No pending tasks in queue' };
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -172,6 +172,11 @@ export {
|
|
|
172
172
|
genesisReport,
|
|
173
173
|
} from './node-types/index.js';
|
|
174
174
|
|
|
175
|
+
// Event handlers (for platform extension system)
|
|
176
|
+
export { onExecutionFailure } from './handlers/on-execution-failure.js';
|
|
177
|
+
export { onScheduledRun } from './handlers/scheduled-run.js';
|
|
178
|
+
export { onBotCompleted } from './handlers/on-bot-completed.js';
|
|
179
|
+
|
|
175
180
|
// Eject API (for platform/studio server-side use)
|
|
176
181
|
export { ejectWorkflows } from './cli-handlers.js';
|
|
177
182
|
export type { EjectResult } from './cli-handlers.js';
|
package/src/mcp-tools.ts
CHANGED
|
@@ -206,8 +206,8 @@ export async function registerMcpTools(mcp: McpServer): Promise<void> {
|
|
|
206
206
|
switch (args.action) {
|
|
207
207
|
case 'add': {
|
|
208
208
|
if (!args.task) return { content: [{ type: 'text', text: 'Error: task instruction required' }] };
|
|
209
|
-
const id = await queue.add({ instruction: args.task as string, priority: 0 });
|
|
210
|
-
return { content: [{ type: 'text', text: `Task added: ${id}` }] };
|
|
209
|
+
const { id, duplicate } = await queue.add({ instruction: args.task as string, priority: 0 });
|
|
210
|
+
return { content: [{ type: 'text', text: duplicate ? `Task already queued (${id})` : `Task added: ${id}` }] };
|
|
211
211
|
}
|
|
212
212
|
case 'list':
|
|
213
213
|
return { content: [{ type: 'text', text: JSON.stringify(await queue.list(), null, 2) }] };
|
|
@@ -13,17 +13,18 @@ import type { WeaverContext } from '../bot/types.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export function weaverAbortTask(ctx: string): { ctx: string } {
|
|
15
15
|
const context = JSON.parse(ctx) as WeaverContext;
|
|
16
|
-
const task = JSON.parse(context.taskJson
|
|
16
|
+
const task = context.taskJson ? JSON.parse(context.taskJson) as { instruction?: string } : {};
|
|
17
|
+
const reason = context.rejectionReason ?? 'no reason given';
|
|
17
18
|
const result = {
|
|
18
19
|
success: false,
|
|
19
20
|
outcome: 'aborted',
|
|
20
|
-
summary: `Task aborted: ${
|
|
21
|
-
instruction: task.instruction,
|
|
21
|
+
summary: `Task aborted: ${reason}`,
|
|
22
|
+
instruction: (task as { instruction?: string }).instruction,
|
|
22
23
|
filesModified: [],
|
|
23
24
|
filesCreated: [],
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
console.log(`\x1b[33m→ Task aborted: ${
|
|
27
|
+
console.log(`\x1b[33m→ Task aborted: ${reason}\x1b[0m`);
|
|
27
28
|
|
|
28
29
|
context.resultJson = JSON.stringify(result);
|
|
29
30
|
context.filesModified = '[]';
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import type { WeaverContext } from '../bot/types.js';
|
|
2
|
+
import {
|
|
3
|
+
runAgentLoop,
|
|
4
|
+
createAnthropicProvider,
|
|
5
|
+
getOrCreateCliSession,
|
|
6
|
+
killAllCliSessions,
|
|
7
|
+
type AgentProvider,
|
|
8
|
+
type AgentMessage,
|
|
9
|
+
type ToolDefinition,
|
|
10
|
+
type StreamEvent,
|
|
11
|
+
type StreamOptions,
|
|
12
|
+
type ToolEvent,
|
|
13
|
+
} from '@synergenius/flow-weaver/agent';
|
|
14
|
+
import { WEAVER_TOOLS, createWeaverExecutor } from '../bot/weaver-tools.js';
|
|
15
|
+
import { auditEmit } from '../bot/audit-logger.js';
|
|
16
|
+
import { withRetry } from '../bot/retry-utils.js';
|
|
17
|
+
import { CostTracker } from '../bot/cost-tracker.js';
|
|
18
|
+
|
|
19
|
+
// Clean up persistent sessions on process exit
|
|
20
|
+
let cleanupRegistered = false;
|
|
21
|
+
function registerCleanup(): void {
|
|
22
|
+
if (cleanupRegistered) return;
|
|
23
|
+
cleanupRegistered = true;
|
|
24
|
+
const cleanup = () => { try { killAllCliSessions(); } catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] session cleanup failed:', err); } };
|
|
25
|
+
process.on('exit', cleanup);
|
|
26
|
+
process.on('SIGTERM', cleanup);
|
|
27
|
+
process.on('SIGINT', cleanup);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Adapter: wraps a persistent CliSession as an AgentProvider.
|
|
32
|
+
* The agent loop sends full message history each iteration;
|
|
33
|
+
* we only forward the latest user/tool messages since the session
|
|
34
|
+
* maintains its own conversation state.
|
|
35
|
+
*/
|
|
36
|
+
class CliSessionProvider implements AgentProvider {
|
|
37
|
+
private sentCount = 0;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
private session: { ready: boolean; spawn: () => Promise<void>; send: (msg: string, systemPrompt?: string) => AsyncGenerator<StreamEvent> },
|
|
41
|
+
) {}
|
|
42
|
+
|
|
43
|
+
async *stream(
|
|
44
|
+
messages: AgentMessage[],
|
|
45
|
+
_tools: ToolDefinition[],
|
|
46
|
+
options?: StreamOptions,
|
|
47
|
+
): AsyncGenerator<StreamEvent> {
|
|
48
|
+
if (!this.session.ready) await this.session.spawn();
|
|
49
|
+
|
|
50
|
+
// Only send new messages (session has history of previous ones)
|
|
51
|
+
const newMessages = messages.slice(this.sentCount);
|
|
52
|
+
this.sentCount = messages.length;
|
|
53
|
+
|
|
54
|
+
// Format new messages into a prompt string
|
|
55
|
+
const prompt = newMessages
|
|
56
|
+
.map((m) => {
|
|
57
|
+
if (m.role === 'user') return typeof m.content === 'string' ? m.content : JSON.stringify(m.content);
|
|
58
|
+
if (m.role === 'tool') return `Tool result (${m.toolCallId}): ${typeof m.content === 'string' ? m.content : JSON.stringify(m.content)}`;
|
|
59
|
+
return '';
|
|
60
|
+
})
|
|
61
|
+
.filter(Boolean)
|
|
62
|
+
.join('\n');
|
|
63
|
+
|
|
64
|
+
if (!prompt) return;
|
|
65
|
+
|
|
66
|
+
// Only pass system prompt on the first call
|
|
67
|
+
const systemPrompt = this.sentCount <= messages.length ? options?.systemPrompt : undefined;
|
|
68
|
+
yield* this.session.send(prompt, systemPrompt);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Reset for a new task (new conversation context) */
|
|
72
|
+
resetForNewTask(): void {
|
|
73
|
+
this.sentCount = 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Test-only export (tree-shaken in production bundles)
|
|
78
|
+
export { CliSessionProvider };
|
|
79
|
+
|
|
80
|
+
// Re-use StepLogEntry shape from the bot types
|
|
81
|
+
type LocalStepLogEntry = { step: string; status: string; detail: string };
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Tool-use agent execution. Claude drives the entire task via tool calls
|
|
85
|
+
* (validate, read-file, patch-file, run-shell, etc.) in a single continuous
|
|
86
|
+
* conversation. No separate plan or retry loop needed.
|
|
87
|
+
*
|
|
88
|
+
* @flowWeaver nodeType
|
|
89
|
+
* @label Agent Execute
|
|
90
|
+
* @input ctx [order:0] - Weaver context (JSON)
|
|
91
|
+
* @output ctx [order:0] - Weaver context with results (JSON)
|
|
92
|
+
* @output onSuccess [order:-2] - On Success
|
|
93
|
+
* @output onFailure [order:-1] [hidden] - On Failure
|
|
94
|
+
*/
|
|
95
|
+
export async function weaverAgentExecute(
|
|
96
|
+
execute: boolean,
|
|
97
|
+
ctx: string,
|
|
98
|
+
): Promise<{
|
|
99
|
+
onSuccess: boolean; onFailure: boolean;
|
|
100
|
+
ctx: string;
|
|
101
|
+
}> {
|
|
102
|
+
const context = JSON.parse(ctx) as WeaverContext;
|
|
103
|
+
const { env } = context;
|
|
104
|
+
|
|
105
|
+
if (!execute) {
|
|
106
|
+
context.resultJson = JSON.stringify({ success: true, toolCallCount: 0 });
|
|
107
|
+
context.validationResultJson = '[]';
|
|
108
|
+
context.filesModified = '[]';
|
|
109
|
+
context.stepLogJson = '[]';
|
|
110
|
+
context.allValid = true;
|
|
111
|
+
return { onSuccess: true, onFailure: false, ctx: JSON.stringify(context) };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const { providerInfo: pInfo, projectDir } = env;
|
|
115
|
+
const task = JSON.parse(context.taskJson!);
|
|
116
|
+
|
|
117
|
+
// Build system prompt
|
|
118
|
+
let systemPrompt: string;
|
|
119
|
+
try {
|
|
120
|
+
const mod = await import('../bot/system-prompt.js');
|
|
121
|
+
const basePrompt = await mod.buildSystemPrompt();
|
|
122
|
+
let cliCommands: { name: string; description: string; botCompatible?: boolean; options?: { flags: string; arg?: string; description: string }[] }[] = [];
|
|
123
|
+
try {
|
|
124
|
+
const docMeta = await import('@synergenius/flow-weaver/doc-metadata');
|
|
125
|
+
cliCommands = docMeta.CLI_COMMANDS ?? [];
|
|
126
|
+
} catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] doc-metadata unavailable (older fw):', err); }
|
|
127
|
+
const botPrompt = mod.buildBotSystemPrompt(context.contextBundle, cliCommands, projectDir);
|
|
128
|
+
systemPrompt = basePrompt + '\n\n' + botPrompt;
|
|
129
|
+
} catch (err) {
|
|
130
|
+
if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] system prompt build failed, using fallback:', err);
|
|
131
|
+
systemPrompt = 'You are Weaver, an AI workflow bot. Use the provided tools to complete tasks.';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const taskPrompt = `Task: ${task.instruction}\nProject directory: ${projectDir}\n${task.targets ? 'Target files: ' + task.targets.join(', ') : ''}`;
|
|
135
|
+
|
|
136
|
+
// Create renderer — single source of all terminal output
|
|
137
|
+
const { TerminalRenderer } = await import('../bot/terminal-renderer.js');
|
|
138
|
+
const renderer = new TerminalRenderer({ verbose: !!process.env.WEAVER_VERBOSE });
|
|
139
|
+
|
|
140
|
+
auditEmit('run-start', { task: task.instruction });
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const provider = createProvider(pInfo, projectDir);
|
|
144
|
+
const executor = createWeaverExecutor(projectDir);
|
|
145
|
+
|
|
146
|
+
const filesModified: string[] = [];
|
|
147
|
+
const stepLog: LocalStepLogEntry[] = [];
|
|
148
|
+
const taskStart = Date.now();
|
|
149
|
+
|
|
150
|
+
// Route tool events through renderer + track state
|
|
151
|
+
const onToolEvent = (event: ToolEvent) => {
|
|
152
|
+
renderer.onToolEvent(event);
|
|
153
|
+
|
|
154
|
+
if (event.type === 'tool_call_result') {
|
|
155
|
+
stepLog.push({
|
|
156
|
+
step: event.name,
|
|
157
|
+
status: event.isError ? 'error' : 'ok',
|
|
158
|
+
detail: event.isError ? (event.result ?? '').slice(0, 200) : event.name,
|
|
159
|
+
});
|
|
160
|
+
if ((event.name === 'patch_file' || event.name === 'write_file') && !event.isError && event.args?.file) {
|
|
161
|
+
filesModified.push(event.args.file as string);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const onStreamEvent = (event: StreamEvent) => renderer.onStreamEvent(event);
|
|
167
|
+
|
|
168
|
+
const result = await withRetry(
|
|
169
|
+
() => runAgentLoop(
|
|
170
|
+
provider,
|
|
171
|
+
WEAVER_TOOLS,
|
|
172
|
+
executor,
|
|
173
|
+
[{ role: 'user', content: taskPrompt }],
|
|
174
|
+
{ systemPrompt, maxIterations: 15, onToolEvent, onStreamEvent },
|
|
175
|
+
),
|
|
176
|
+
{
|
|
177
|
+
maxRetries: 3,
|
|
178
|
+
baseDelayMs: 5_000,
|
|
179
|
+
onRetry: (attempt, delay, err) => {
|
|
180
|
+
renderer.warn(`Transient error, retrying in ${delay / 1000}s (attempt ${attempt}/3): ${err.message.slice(0, 80)}`);
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
const usage = result.usage;
|
|
186
|
+
const model = pInfo.model ?? 'claude-sonnet-4-6';
|
|
187
|
+
const estimatedCost = CostTracker.estimateCost(model, {
|
|
188
|
+
inputTokens: usage.promptTokens,
|
|
189
|
+
outputTokens: usage.completionTokens,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Post-agent validation gate: don't trust the AI's self-assessment
|
|
193
|
+
const uniqueFiles = [...new Set(filesModified)];
|
|
194
|
+
let validationPassed = result.success;
|
|
195
|
+
if (uniqueFiles.length > 0) {
|
|
196
|
+
try {
|
|
197
|
+
const { weaverValidateGate } = await import('./validate-gate.js');
|
|
198
|
+
const gateCtx: WeaverContext = { ...context, filesModified: JSON.stringify(uniqueFiles) };
|
|
199
|
+
const gateResult = weaverValidateGate(JSON.stringify(gateCtx));
|
|
200
|
+
const gateData = JSON.parse(gateResult.ctx) as WeaverContext;
|
|
201
|
+
if (!gateData.allValid) {
|
|
202
|
+
validationPassed = false;
|
|
203
|
+
renderer.warn('Post-agent validation found errors — task marked as failed');
|
|
204
|
+
}
|
|
205
|
+
context.validationResultJson = gateData.validationResultJson;
|
|
206
|
+
} catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[agent-execute] validate-gate unavailable, skipping:', err); }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
context.resultJson = JSON.stringify({
|
|
210
|
+
success: validationPassed,
|
|
211
|
+
summary: result.summary,
|
|
212
|
+
toolCallCount: result.toolCallCount,
|
|
213
|
+
usage: { inputTokens: usage.promptTokens, outputTokens: usage.completionTokens, estimatedCost },
|
|
214
|
+
});
|
|
215
|
+
context.filesModified = JSON.stringify(uniqueFiles);
|
|
216
|
+
context.stepLogJson = JSON.stringify(stepLog);
|
|
217
|
+
context.allValid = validationPassed;
|
|
218
|
+
|
|
219
|
+
auditEmit('run-complete', {
|
|
220
|
+
success: validationPassed,
|
|
221
|
+
toolCalls: result.toolCallCount,
|
|
222
|
+
filesModified: uniqueFiles.length,
|
|
223
|
+
tokens: { in: usage.promptTokens, out: usage.completionTokens },
|
|
224
|
+
estimatedCost,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
renderer.taskEnd(validationPassed, {
|
|
228
|
+
toolCalls: result.toolCallCount,
|
|
229
|
+
inputTokens: usage.promptTokens,
|
|
230
|
+
outputTokens: usage.completionTokens,
|
|
231
|
+
estimatedCost,
|
|
232
|
+
filesModified: uniqueFiles.length,
|
|
233
|
+
elapsed: Date.now() - taskStart,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
return { onSuccess: validationPassed, onFailure: !validationPassed, ctx: JSON.stringify(context) };
|
|
237
|
+
} catch (err: unknown) {
|
|
238
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
239
|
+
let errorDetail = msg;
|
|
240
|
+
try {
|
|
241
|
+
const { getErrorGuidance } = await import('../bot/error-guide.js');
|
|
242
|
+
const guidance = getErrorGuidance(msg);
|
|
243
|
+
if (guidance) errorDetail = `${msg}\n Hint: ${guidance}`;
|
|
244
|
+
} catch { /* error guide not available */ }
|
|
245
|
+
renderer.error('Agent error', errorDetail);
|
|
246
|
+
|
|
247
|
+
context.resultJson = JSON.stringify({ success: false, error: msg });
|
|
248
|
+
context.filesModified = '[]';
|
|
249
|
+
context.stepLogJson = '[]';
|
|
250
|
+
context.allValid = false;
|
|
251
|
+
|
|
252
|
+
return { onSuccess: false, onFailure: true, ctx: JSON.stringify(context) };
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Create an AgentProvider from pack-weaver's ProviderInfo.
|
|
258
|
+
*/
|
|
259
|
+
function createProvider(
|
|
260
|
+
pInfo: { type: string; apiKey?: string; model?: string; maxTokens?: number },
|
|
261
|
+
projectDir?: string,
|
|
262
|
+
): AgentProvider {
|
|
263
|
+
const type = pInfo.type ?? 'auto';
|
|
264
|
+
|
|
265
|
+
// Explicit Anthropic API
|
|
266
|
+
if (type === 'anthropic' && pInfo.apiKey) {
|
|
267
|
+
return createAnthropicProvider({
|
|
268
|
+
apiKey: pInfo.apiKey,
|
|
269
|
+
model: pInfo.model,
|
|
270
|
+
maxTokens: pInfo.maxTokens,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Claude CLI — use persistent session for warm start
|
|
275
|
+
if (type === 'claude-cli') {
|
|
276
|
+
return createSessionProvider(pInfo.model, projectDir);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Auto mode: try Anthropic API key from env, then fall back to Claude CLI
|
|
280
|
+
if (type === 'auto') {
|
|
281
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
282
|
+
return createAnthropicProvider({
|
|
283
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
284
|
+
model: pInfo.model,
|
|
285
|
+
maxTokens: pInfo.maxTokens,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
// No API key — use Claude CLI with persistent session
|
|
289
|
+
return createSessionProvider(pInfo.model, projectDir);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
throw new Error(
|
|
293
|
+
`Unsupported provider type: ${type}. Use 'anthropic', 'claude-cli', or 'auto'.`,
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function createSessionProvider(model?: string, projectDir?: string): CliSessionProvider {
|
|
298
|
+
registerCleanup();
|
|
299
|
+
const key = projectDir ?? process.cwd();
|
|
300
|
+
const session = getOrCreateCliSession(key, {
|
|
301
|
+
binPath: 'claude',
|
|
302
|
+
cwd: key,
|
|
303
|
+
model: model ?? 'claude-sonnet-4-6',
|
|
304
|
+
});
|
|
305
|
+
return new CliSessionProvider(session);
|
|
306
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import * as os from 'node:os';
|
|
1
4
|
import type { WeaverContext } from '../bot/types.js';
|
|
5
|
+
import { withFileLock } from '../bot/file-lock.js';
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
8
|
* Generates the final bot session report. Receives context from any
|
|
5
9
|
* of the three paths: read-only, main execution, or abort.
|
|
10
|
+
* Also marks the queue task as completed or failed.
|
|
6
11
|
*
|
|
7
12
|
* @flowWeaver nodeType
|
|
8
|
-
* @expression
|
|
9
13
|
* @label Bot Report
|
|
10
14
|
* @executeWhen DISJUNCTION
|
|
11
15
|
* @input [mainCtx] [order:0] - Context from main path (JSON, optional)
|
|
@@ -15,20 +19,21 @@ import type { WeaverContext } from '../bot/types.js';
|
|
|
15
19
|
* @output reportJson [order:1] [hidden] - Full report (JSON)
|
|
16
20
|
* @output onFailure [hidden]
|
|
17
21
|
*/
|
|
18
|
-
export function weaverBotReport(
|
|
22
|
+
export async function weaverBotReport(
|
|
23
|
+
execute: boolean,
|
|
19
24
|
mainCtx?: string,
|
|
20
25
|
readCtx?: string,
|
|
21
26
|
abortCtx?: string,
|
|
22
|
-
): { summary: string; reportJson: string } {
|
|
27
|
+
): Promise<{ onSuccess: boolean; onFailure: boolean; summary: string; reportJson: string }> {
|
|
23
28
|
const ctxStr = mainCtx ?? readCtx ?? abortCtx;
|
|
24
29
|
|
|
25
|
-
if (!ctxStr) {
|
|
30
|
+
if (!execute || !ctxStr) {
|
|
26
31
|
const report = { task: {}, path: 'unknown', result: null, filesModified: [], gitResult: null, timestamp: Date.now() };
|
|
27
|
-
return { summary: '', reportJson: JSON.stringify(report) };
|
|
32
|
+
return { onSuccess: true, onFailure: false, summary: '', reportJson: JSON.stringify(report) };
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
const context = JSON.parse(ctxStr) as WeaverContext;
|
|
31
|
-
const task = context.taskJson ? JSON.parse(context.taskJson) as { instruction?: string; mode?: string } : {};
|
|
36
|
+
const task = context.taskJson ? JSON.parse(context.taskJson) as { instruction?: string; mode?: string; queueId?: string } : {};
|
|
32
37
|
const files: string[] = context.filesModified ? JSON.parse(context.filesModified) : [];
|
|
33
38
|
const gitResult = context.gitResultJson ? JSON.parse(context.gitResultJson) : null;
|
|
34
39
|
|
|
@@ -52,8 +57,10 @@ export function weaverBotReport(
|
|
|
52
57
|
parts.push(`Task: ${task.instruction}`);
|
|
53
58
|
}
|
|
54
59
|
|
|
60
|
+
const success = result?.success !== false && pathName !== 'abort';
|
|
61
|
+
|
|
55
62
|
if (result) {
|
|
56
|
-
parts.push(`Outcome: ${result.outcome ?? (
|
|
63
|
+
parts.push(`Outcome: ${result.outcome ?? (success ? 'completed' : 'failed')}`);
|
|
57
64
|
if (result.summary) parts.push(`Summary: ${result.summary}`);
|
|
58
65
|
}
|
|
59
66
|
|
|
@@ -67,6 +74,13 @@ export function weaverBotReport(
|
|
|
67
74
|
|
|
68
75
|
const summary = parts.join(' | ');
|
|
69
76
|
|
|
77
|
+
// Mark queue task as completed or failed
|
|
78
|
+
if (task.queueId) {
|
|
79
|
+
try {
|
|
80
|
+
await markQueueTask(task.queueId, success ? 'completed' : 'failed');
|
|
81
|
+
} catch (err) { if (process.env.WEAVER_VERBOSE) console.error('[bot-report] queue update failed:', err); }
|
|
82
|
+
}
|
|
83
|
+
|
|
70
84
|
const report = {
|
|
71
85
|
task,
|
|
72
86
|
path: pathName,
|
|
@@ -76,7 +90,24 @@ export function weaverBotReport(
|
|
|
76
90
|
timestamp: Date.now(),
|
|
77
91
|
};
|
|
78
92
|
|
|
79
|
-
console.log(`\n\x1b[1m${
|
|
93
|
+
console.log(`\n\x1b[1m${success ? '\x1b[32m' : '\x1b[31m'}Bot Report: ${summary}\x1b[0m\n`);
|
|
94
|
+
|
|
95
|
+
return { onSuccess: success, onFailure: !success, summary, reportJson: JSON.stringify(report) };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function markQueueTask(id: string, status: 'completed' | 'failed'): Promise<void> {
|
|
99
|
+
const queuePath = path.join(os.homedir(), '.weaver', 'task-queue.ndjson');
|
|
100
|
+
if (!fs.existsSync(queuePath)) return;
|
|
80
101
|
|
|
81
|
-
|
|
102
|
+
await withFileLock(queuePath, () => {
|
|
103
|
+
const content = fs.readFileSync(queuePath, 'utf-8').trim();
|
|
104
|
+
if (!content) return;
|
|
105
|
+
const tasks = content.split('\n').filter(Boolean).map(l => JSON.parse(l));
|
|
106
|
+
const task = tasks.find((t: { id: string }) => t.id === id);
|
|
107
|
+
if (task) {
|
|
108
|
+
task.status = status;
|
|
109
|
+
fs.writeFileSync(queuePath, tasks.map((t: unknown) => JSON.stringify(t)).join('\n') + '\n', 'utf-8');
|
|
110
|
+
console.log(`\x1b[36m→ Queue task ${id}: ${status}\x1b[0m`);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
82
113
|
}
|