@synergenius/flow-weaver-pack-weaver 0.8.3 → 0.9.0
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/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/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/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/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/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 +12 -2
- 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/task-queue.d.ts.map +1 -1
- package/dist/bot/task-queue.js +7 -10
- package/dist/bot/task-queue.js.map +1 -1
- package/dist/cli-bridge.d.ts.map +1 -1
- package/dist/cli-bridge.js +6 -3
- package/dist/cli-bridge.js.map +1 -1
- package/dist/cli-handlers.d.ts +3 -1
- package/dist/cli-handlers.d.ts.map +1 -1
- package/dist/cli-handlers.js +146 -0
- 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/flowweaver.manifest.json +93 -2
- package/package.json +1 -1
- package/src/bot/audit-store.ts +3 -12
- package/src/bot/cost-store.ts +11 -12
- package/src/bot/genesis-store.ts +11 -17
- package/src/bot/index.ts +5 -0
- package/src/bot/pipeline-runner.ts +7 -1
- package/src/bot/run-store.ts +2 -11
- package/src/bot/runner.ts +14 -2
- package/src/bot/safe-json.ts +76 -0
- package/src/bot/safe-path.ts +44 -0
- package/src/bot/task-queue.ts +6 -8
- package/src/cli-bridge.ts +8 -3
- package/src/cli-handlers.ts +155 -1
- 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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path safety utilities.
|
|
3
|
+
*
|
|
4
|
+
* Prevents path traversal attacks and ensures file operations stay within
|
|
5
|
+
* expected boundaries. Critical for any code that constructs paths from
|
|
6
|
+
* user input, AI output, or external configuration.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as path from 'node:path';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validate that a relative path does not escape the base directory.
|
|
13
|
+
* Returns the resolved absolute path if safe, or null if the path
|
|
14
|
+
* attempts traversal.
|
|
15
|
+
*/
|
|
16
|
+
export function safePath(baseDir: string, relativePath: string): string | null {
|
|
17
|
+
const normalized = path.normalize(relativePath);
|
|
18
|
+
|
|
19
|
+
// Reject absolute paths and explicit traversal
|
|
20
|
+
if (path.isAbsolute(normalized)) return null;
|
|
21
|
+
if (normalized.startsWith('..')) return null;
|
|
22
|
+
|
|
23
|
+
const resolved = path.resolve(baseDir, normalized);
|
|
24
|
+
const resolvedBase = path.resolve(baseDir);
|
|
25
|
+
|
|
26
|
+
// Ensure resolved path is within baseDir
|
|
27
|
+
if (!resolved.startsWith(resolvedBase + path.sep) && resolved !== resolvedBase) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return resolved;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Validate and resolve a path, throwing a descriptive error on traversal.
|
|
36
|
+
*/
|
|
37
|
+
export function safePathOrThrow(baseDir: string, relativePath: string, context?: string): string {
|
|
38
|
+
const resolved = safePath(baseDir, relativePath);
|
|
39
|
+
if (resolved === null) {
|
|
40
|
+
const prefix = context ? `${context}: ` : '';
|
|
41
|
+
throw new Error(`${prefix}Unsafe file path rejected: "${relativePath}"`);
|
|
42
|
+
}
|
|
43
|
+
return resolved;
|
|
44
|
+
}
|
package/src/bot/task-queue.ts
CHANGED
|
@@ -3,6 +3,7 @@ import * as path from 'node:path';
|
|
|
3
3
|
import * as os from 'node:os';
|
|
4
4
|
import * as crypto from 'node:crypto';
|
|
5
5
|
import { withFileLock } from './file-lock.js';
|
|
6
|
+
import { parseNdjson } from './safe-json.js';
|
|
6
7
|
|
|
7
8
|
export interface QueuedTask {
|
|
8
9
|
id: string;
|
|
@@ -93,14 +94,11 @@ export class TaskQueue {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
private readAll(): QueuedTask[] {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
} catch {
|
|
102
|
-
return [];
|
|
103
|
-
}
|
|
97
|
+
if (!fs.existsSync(this.filePath)) return [];
|
|
98
|
+
const content = fs.readFileSync(this.filePath, 'utf-8').trim();
|
|
99
|
+
if (!content) return [];
|
|
100
|
+
const { records } = parseNdjson<QueuedTask>(content, 'task-queue');
|
|
101
|
+
return records;
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
private writeAll(tasks: QueuedTask[]): void {
|
package/src/cli-bridge.ts
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
handleRun, handleHistory, handleCosts, handleWatch,
|
|
4
4
|
handleCron, handlePipeline, handleDashboard, handleProviders,
|
|
5
5
|
handleEject, handleBot, handleSession, handleSteer, handleQueue,
|
|
6
|
-
handleGenesis, handleAudit,
|
|
6
|
+
handleGenesis, handleAudit, handleInit, printHelp,
|
|
7
7
|
} from './cli-handlers.js';
|
|
8
8
|
|
|
9
9
|
const handlers: Record<string, (opts: ParsedArgs) => Promise<void>> = {
|
|
@@ -22,6 +22,7 @@ const handlers: Record<string, (opts: ParsedArgs) => Promise<void>> = {
|
|
|
22
22
|
queue: handleQueue,
|
|
23
23
|
genesis: handleGenesis,
|
|
24
24
|
audit: handleAudit,
|
|
25
|
+
init: handleInit,
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export async function handleCommand(
|
|
@@ -33,9 +34,13 @@ export async function handleCommand(
|
|
|
33
34
|
throw new Error(`Unknown weaver command: ${name}`);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
// Import parseArgs to handle the remaining flags
|
|
37
37
|
const { parseArgs } = await import('./cli-handlers.js');
|
|
38
|
-
// Prepend dummy entries so parseArgs skips argv[0] and argv[1]
|
|
39
38
|
const opts = parseArgs(['node', 'weaver', name, ...args]);
|
|
39
|
+
|
|
40
|
+
if (opts.showHelp) {
|
|
41
|
+
printHelp(name);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
40
45
|
await handler(opts);
|
|
41
46
|
}
|
package/src/cli-handlers.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type { ExecutionEvent, WeaverConfig, RunRecord, RunOutcome, RunCostSummar
|
|
|
13
13
|
import { AuditStore } from './bot/audit-store.js';
|
|
14
14
|
|
|
15
15
|
export interface ParsedArgs {
|
|
16
|
-
command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | 'session' | 'steer' | 'queue' | 'genesis' | 'audit';
|
|
16
|
+
command: 'run' | 'history' | 'costs' | 'providers' | 'watch' | 'cron' | 'pipeline' | 'dashboard' | 'eject' | 'bot' | 'session' | 'steer' | 'queue' | 'genesis' | 'audit' | 'init';
|
|
17
17
|
file?: string;
|
|
18
18
|
verbose: boolean;
|
|
19
19
|
dryRun: boolean;
|
|
@@ -236,6 +236,8 @@ export function parseArgs(argv: string[]): ParsedArgs {
|
|
|
236
236
|
}
|
|
237
237
|
} else if (arg === 'genesis') {
|
|
238
238
|
result.command = 'genesis';
|
|
239
|
+
} else if (arg === 'init') {
|
|
240
|
+
result.command = 'init';
|
|
239
241
|
} else if (arg === '--init') {
|
|
240
242
|
result.genesisInit = true;
|
|
241
243
|
} else if (arg === '--watch') {
|
|
@@ -1443,3 +1445,155 @@ function printAuditEvents(events: AuditEvent[], json: boolean): void {
|
|
|
1443
1445
|
console.log(`${time} [${e.type}]${dataStr}`);
|
|
1444
1446
|
}
|
|
1445
1447
|
}
|
|
1448
|
+
|
|
1449
|
+
// --- Help ---
|
|
1450
|
+
|
|
1451
|
+
const COMMAND_HELP: Record<string, string> = {
|
|
1452
|
+
run: 'run <workflow.ts> Execute a workflow with AI agent channel',
|
|
1453
|
+
bot: 'bot "task description" Create or modify a workflow from a task',
|
|
1454
|
+
init: 'init Create a .weaver.json config file',
|
|
1455
|
+
history: 'history [id] [--limit N] Show execution history',
|
|
1456
|
+
costs: 'costs [--since 7d] Show AI token usage and costs',
|
|
1457
|
+
providers: 'providers List available AI providers',
|
|
1458
|
+
watch: 'watch <workflow.ts> Re-run on file changes',
|
|
1459
|
+
cron: 'cron "*/5 * * * *" <file> Schedule workflow execution',
|
|
1460
|
+
pipeline: 'pipeline <config.json> Run multi-stage pipeline',
|
|
1461
|
+
dashboard: 'dashboard <file> [--port N] Start live execution dashboard',
|
|
1462
|
+
session: 'session Start continuous task queue processing',
|
|
1463
|
+
queue: 'queue <add|list|clear> [task] Manage task queue',
|
|
1464
|
+
steer: 'steer <pause|resume|cancel> Control a running session',
|
|
1465
|
+
genesis: 'genesis [--init] [--watch] Run self-evolution cycle',
|
|
1466
|
+
eject: 'eject [--workflow bot|genesis] Export managed workflows',
|
|
1467
|
+
audit: 'audit [runId] [--limit N] View audit log',
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1470
|
+
export function printHelp(command?: string): void {
|
|
1471
|
+
if (command && command !== 'help' && COMMAND_HELP[command]) {
|
|
1472
|
+
console.log(`\nUsage: flow-weaver weaver ${COMMAND_HELP[command]}\n`);
|
|
1473
|
+
printCommandHelp(command);
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
console.log(`
|
|
1478
|
+
Weaver — AI-powered workflow automation for Flow Weaver
|
|
1479
|
+
|
|
1480
|
+
Usage: flow-weaver weaver <command> [options]
|
|
1481
|
+
|
|
1482
|
+
Commands:
|
|
1483
|
+
${Object.values(COMMAND_HELP).join('\n ')}
|
|
1484
|
+
|
|
1485
|
+
Global options:
|
|
1486
|
+
-h, --help Show help
|
|
1487
|
+
-v, --verbose Show detailed output
|
|
1488
|
+
-n, --dry-run Preview without executing
|
|
1489
|
+
--quiet Suppress output
|
|
1490
|
+
-p, --params <json> Input parameters as JSON
|
|
1491
|
+
-c, --config <path> Path to .weaver.json config
|
|
1492
|
+
--approval <mode> Override approval mode (auto|prompt|web|timeout-auto)
|
|
1493
|
+
|
|
1494
|
+
Quick start:
|
|
1495
|
+
flow-weaver weaver init # create .weaver.json
|
|
1496
|
+
flow-weaver weaver providers # check available AI providers
|
|
1497
|
+
flow-weaver weaver bot "Create a hello world workflow" # create your first workflow
|
|
1498
|
+
`);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
function printCommandHelp(command: string): void {
|
|
1502
|
+
const help: Record<string, string> = {
|
|
1503
|
+
run: `Options:
|
|
1504
|
+
--dashboard Enable live dashboard
|
|
1505
|
+
--port <n> Dashboard port (default: 4242)`,
|
|
1506
|
+
bot: `Options:
|
|
1507
|
+
--file <path> Target file for modification (switches to modify mode)
|
|
1508
|
+
--template <name> Use a template for scaffolding
|
|
1509
|
+
--batch <n> Process multiple tasks
|
|
1510
|
+
--auto-approve Skip approval gate
|
|
1511
|
+
--dashboard Enable live dashboard`,
|
|
1512
|
+
history: `Options:
|
|
1513
|
+
--limit <n> Number of records (default: 20)
|
|
1514
|
+
--outcome <type> Filter: completed, failed, error, skipped
|
|
1515
|
+
--workflow <path> Filter by workflow file
|
|
1516
|
+
--since <range> Time range: 7d, 2h, or ISO date
|
|
1517
|
+
--json Output as JSON
|
|
1518
|
+
--prune Remove old records
|
|
1519
|
+
--clear Delete all history`,
|
|
1520
|
+
costs: `Options:
|
|
1521
|
+
--since <range> Time range: 7d, 30d, or ISO date
|
|
1522
|
+
--model <name> Filter by model`,
|
|
1523
|
+
genesis: `Options:
|
|
1524
|
+
--init Initialize .genesis/config.json
|
|
1525
|
+
--watch Run multiple evolution cycles
|
|
1526
|
+
--project-dir <p> Project directory`,
|
|
1527
|
+
watch: `Options:
|
|
1528
|
+
--debounce <ms> Debounce interval (default: 500)
|
|
1529
|
+
--log <path> Log file for daemon output`,
|
|
1530
|
+
pipeline: `Options:
|
|
1531
|
+
--stage <id> Run a specific stage only`,
|
|
1532
|
+
queue: `Actions:
|
|
1533
|
+
add "task" Add task to queue
|
|
1534
|
+
list Show queue contents
|
|
1535
|
+
clear Remove all tasks
|
|
1536
|
+
remove <id> Remove a specific task`,
|
|
1537
|
+
steer: `Commands:
|
|
1538
|
+
pause Pause current execution
|
|
1539
|
+
resume Resume paused execution
|
|
1540
|
+
cancel Cancel current execution
|
|
1541
|
+
redirect "task" Switch to a different task
|
|
1542
|
+
queue "task" Add task to queue from steer`,
|
|
1543
|
+
};
|
|
1544
|
+
|
|
1545
|
+
if (help[command]) {
|
|
1546
|
+
console.log(help[command]);
|
|
1547
|
+
console.log('');
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// --- Init ---
|
|
1552
|
+
|
|
1553
|
+
export async function handleInit(opts: ParsedArgs): Promise<void> {
|
|
1554
|
+
const dir = opts.file ? path.resolve(opts.file) : process.cwd();
|
|
1555
|
+
const configPath = path.join(dir, '.weaver.json');
|
|
1556
|
+
|
|
1557
|
+
if (fs.existsSync(configPath)) {
|
|
1558
|
+
console.log(`[weaver] Config already exists: ${configPath}`);
|
|
1559
|
+
console.log(' Edit it directly or delete it to regenerate.');
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
// Detect best available provider
|
|
1564
|
+
let provider: string = 'auto';
|
|
1565
|
+
try {
|
|
1566
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
1567
|
+
provider = 'anthropic';
|
|
1568
|
+
} else {
|
|
1569
|
+
const { execFileSync } = await import('node:child_process');
|
|
1570
|
+
try {
|
|
1571
|
+
execFileSync('claude', ['--version'], { stdio: 'pipe' });
|
|
1572
|
+
provider = 'claude-cli';
|
|
1573
|
+
} catch {
|
|
1574
|
+
try {
|
|
1575
|
+
execFileSync('copilot', ['--version'], { stdio: 'pipe' });
|
|
1576
|
+
provider = 'copilot-cli';
|
|
1577
|
+
} catch {
|
|
1578
|
+
// Stay with auto
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
} catch {
|
|
1583
|
+
// Stay with auto
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
const config = {
|
|
1587
|
+
provider: provider === 'auto' ? 'auto' : { name: provider },
|
|
1588
|
+
approval: 'auto',
|
|
1589
|
+
};
|
|
1590
|
+
|
|
1591
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
1592
|
+
|
|
1593
|
+
console.log(`\x1b[32m✓\x1b[0m Created ${configPath}`);
|
|
1594
|
+
console.log(` Provider: ${provider}`);
|
|
1595
|
+
console.log('');
|
|
1596
|
+
console.log('Next steps:');
|
|
1597
|
+
console.log(' flow-weaver weaver providers # verify provider detection');
|
|
1598
|
+
console.log(' flow-weaver weaver bot "Create a ..." # create your first workflow');
|
|
1599
|
+
}
|
|
@@ -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';
|