@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.
Files changed (74) hide show
  1. package/dist/bot/audit-store.d.ts.map +1 -1
  2. package/dist/bot/audit-store.js +3 -11
  3. package/dist/bot/audit-store.js.map +1 -1
  4. package/dist/bot/cost-store.d.ts.map +1 -1
  5. package/dist/bot/cost-store.js +10 -14
  6. package/dist/bot/cost-store.js.map +1 -1
  7. package/dist/bot/genesis-store.d.ts.map +1 -1
  8. package/dist/bot/genesis-store.js +11 -20
  9. package/dist/bot/genesis-store.js.map +1 -1
  10. package/dist/bot/index.d.ts +3 -0
  11. package/dist/bot/index.d.ts.map +1 -1
  12. package/dist/bot/index.js +3 -0
  13. package/dist/bot/index.js.map +1 -1
  14. package/dist/bot/pipeline-runner.d.ts.map +1 -1
  15. package/dist/bot/pipeline-runner.js +8 -1
  16. package/dist/bot/pipeline-runner.js.map +1 -1
  17. package/dist/bot/run-store.d.ts.map +1 -1
  18. package/dist/bot/run-store.js +2 -10
  19. package/dist/bot/run-store.js.map +1 -1
  20. package/dist/bot/runner.d.ts.map +1 -1
  21. package/dist/bot/runner.js +12 -2
  22. package/dist/bot/runner.js.map +1 -1
  23. package/dist/bot/safe-json.d.ts +32 -0
  24. package/dist/bot/safe-json.d.ts.map +1 -0
  25. package/dist/bot/safe-json.js +56 -0
  26. package/dist/bot/safe-json.js.map +1 -0
  27. package/dist/bot/safe-path.d.ts +18 -0
  28. package/dist/bot/safe-path.d.ts.map +1 -0
  29. package/dist/bot/safe-path.js +40 -0
  30. package/dist/bot/safe-path.js.map +1 -0
  31. package/dist/bot/task-queue.d.ts.map +1 -1
  32. package/dist/bot/task-queue.js +7 -10
  33. package/dist/bot/task-queue.js.map +1 -1
  34. package/dist/cli-bridge.d.ts.map +1 -1
  35. package/dist/cli-bridge.js +6 -3
  36. package/dist/cli-bridge.js.map +1 -1
  37. package/dist/cli-handlers.d.ts +3 -1
  38. package/dist/cli-handlers.d.ts.map +1 -1
  39. package/dist/cli-handlers.js +146 -0
  40. package/dist/cli-handlers.js.map +1 -1
  41. package/dist/handlers/on-bot-completed.d.ts +21 -0
  42. package/dist/handlers/on-bot-completed.d.ts.map +1 -0
  43. package/dist/handlers/on-bot-completed.js +28 -0
  44. package/dist/handlers/on-bot-completed.js.map +1 -0
  45. package/dist/handlers/on-execution-failure.d.ts +23 -0
  46. package/dist/handlers/on-execution-failure.d.ts.map +1 -0
  47. package/dist/handlers/on-execution-failure.js +28 -0
  48. package/dist/handlers/on-execution-failure.js.map +1 -0
  49. package/dist/handlers/scheduled-run.d.ts +24 -0
  50. package/dist/handlers/scheduled-run.d.ts.map +1 -0
  51. package/dist/handlers/scheduled-run.js +25 -0
  52. package/dist/handlers/scheduled-run.js.map +1 -0
  53. package/dist/index.d.ts +3 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +4 -0
  56. package/dist/index.js.map +1 -1
  57. package/flowweaver.manifest.json +93 -2
  58. package/package.json +1 -1
  59. package/src/bot/audit-store.ts +3 -12
  60. package/src/bot/cost-store.ts +11 -12
  61. package/src/bot/genesis-store.ts +11 -17
  62. package/src/bot/index.ts +5 -0
  63. package/src/bot/pipeline-runner.ts +7 -1
  64. package/src/bot/run-store.ts +2 -11
  65. package/src/bot/runner.ts +14 -2
  66. package/src/bot/safe-json.ts +76 -0
  67. package/src/bot/safe-path.ts +44 -0
  68. package/src/bot/task-queue.ts +6 -8
  69. package/src/cli-bridge.ts +8 -3
  70. package/src/cli-handlers.ts +155 -1
  71. package/src/handlers/on-bot-completed.ts +48 -0
  72. package/src/handlers/on-execution-failure.ts +42 -0
  73. package/src/handlers/scheduled-run.ts +42 -0
  74. 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
+ }
@@ -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
- try {
97
- if (!fs.existsSync(this.filePath)) return [];
98
- const content = fs.readFileSync(this.filePath, 'utf-8').trim();
99
- if (!content) return [];
100
- return content.split('\n').map(line => JSON.parse(line) as QueuedTask);
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
  }
@@ -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';