@loom-framework/core 0.1.0-alpha.151 → 0.1.0-alpha.152

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 (110) hide show
  1. package/builtin-skills/app-skill/SKILL.md +15 -5
  2. package/builtin-skills/app-skill/references/auth.md +23 -19
  3. package/builtin-skills/app-skill/references/evolution.md +90 -0
  4. package/builtin-skills/app-skill/references/process-builder.md +140 -0
  5. package/builtin-skills/app-skill/references/process-metrics.md +93 -0
  6. package/builtin-skills/app-skill/references/process.md +222 -0
  7. package/builtin-skills/loom/SKILL.md +9 -7
  8. package/dist/backend/index.d.ts +4 -0
  9. package/dist/backend/index.d.ts.map +1 -1
  10. package/dist/backend/index.js +52 -2
  11. package/dist/backend/index.js.map +1 -1
  12. package/dist/backend/process/engine.d.ts +84 -0
  13. package/dist/backend/process/engine.d.ts.map +1 -0
  14. package/dist/backend/process/engine.js +511 -0
  15. package/dist/backend/process/engine.js.map +1 -0
  16. package/dist/backend/process/index.d.ts +7 -0
  17. package/dist/backend/process/index.d.ts.map +1 -0
  18. package/dist/backend/process/index.js +6 -0
  19. package/dist/backend/process/index.js.map +1 -0
  20. package/dist/backend/process/logger.d.ts +30 -0
  21. package/dist/backend/process/logger.d.ts.map +1 -0
  22. package/dist/backend/process/logger.js +132 -0
  23. package/dist/backend/process/logger.js.map +1 -0
  24. package/dist/backend/process/queue.d.ts +31 -0
  25. package/dist/backend/process/queue.d.ts.map +1 -0
  26. package/dist/backend/process/queue.js +80 -0
  27. package/dist/backend/process/queue.js.map +1 -0
  28. package/dist/backend/process/registry.d.ts +25 -0
  29. package/dist/backend/process/registry.d.ts.map +1 -0
  30. package/dist/backend/process/registry.js +98 -0
  31. package/dist/backend/process/registry.js.map +1 -0
  32. package/dist/backend/process/trigger.d.ts +29 -0
  33. package/dist/backend/process/trigger.d.ts.map +1 -0
  34. package/dist/backend/process/trigger.js +108 -0
  35. package/dist/backend/process/trigger.js.map +1 -0
  36. package/dist/backend/routes/auth-routes.d.ts +5 -0
  37. package/dist/backend/routes/auth-routes.d.ts.map +1 -1
  38. package/dist/backend/routes/auth-routes.js +221 -1
  39. package/dist/backend/routes/auth-routes.js.map +1 -1
  40. package/dist/backend/routes/index.d.ts +2 -0
  41. package/dist/backend/routes/index.d.ts.map +1 -1
  42. package/dist/backend/routes/index.js +1 -0
  43. package/dist/backend/routes/index.js.map +1 -1
  44. package/dist/backend/routes/process-routes.d.ts +15 -0
  45. package/dist/backend/routes/process-routes.d.ts.map +1 -0
  46. package/dist/backend/routes/process-routes.js +237 -0
  47. package/dist/backend/routes/process-routes.js.map +1 -0
  48. package/dist/cli/commands/auth.d.ts.map +1 -1
  49. package/dist/cli/commands/auth.js +30 -22
  50. package/dist/cli/commands/auth.js.map +1 -1
  51. package/dist/cli/commands/data.d.ts.map +1 -1
  52. package/dist/cli/commands/data.js +36 -47
  53. package/dist/cli/commands/data.js.map +1 -1
  54. package/dist/cli/commands/generate-system-settings.d.ts +3 -2
  55. package/dist/cli/commands/generate-system-settings.d.ts.map +1 -1
  56. package/dist/cli/commands/generate-system-settings.js +50 -7
  57. package/dist/cli/commands/generate-system-settings.js.map +1 -1
  58. package/dist/cli/commands/init.js +1 -1
  59. package/dist/cli/commands/init.js.map +1 -1
  60. package/dist/cli/commands/process.d.ts +8 -0
  61. package/dist/cli/commands/process.d.ts.map +1 -0
  62. package/dist/cli/commands/process.js +444 -0
  63. package/dist/cli/commands/process.js.map +1 -0
  64. package/dist/cli/commands/role.d.ts +5 -2
  65. package/dist/cli/commands/role.d.ts.map +1 -1
  66. package/dist/cli/commands/role.js +145 -18
  67. package/dist/cli/commands/role.js.map +1 -1
  68. package/dist/cli/commands/user-cmd.d.ts.map +1 -1
  69. package/dist/cli/commands/user-cmd.js +41 -50
  70. package/dist/cli/commands/user-cmd.js.map +1 -1
  71. package/dist/cli/generators/capability-generator.d.ts.map +1 -1
  72. package/dist/cli/generators/capability-generator.js +121 -6
  73. package/dist/cli/generators/capability-generator.js.map +1 -1
  74. package/dist/cli/helpers/app-tsx-wiring.d.ts.map +1 -1
  75. package/dist/cli/helpers/app-tsx-wiring.js +21 -14
  76. package/dist/cli/helpers/app-tsx-wiring.js.map +1 -1
  77. package/dist/cli/helpers/auth-client.d.ts +19 -0
  78. package/dist/cli/helpers/auth-client.d.ts.map +1 -0
  79. package/dist/cli/helpers/auth-client.js +42 -0
  80. package/dist/cli/helpers/auth-client.js.map +1 -0
  81. package/dist/cli/index.d.ts.map +1 -1
  82. package/dist/cli/index.js +2 -0
  83. package/dist/cli/index.js.map +1 -1
  84. package/dist/cli/templates/index.d.ts +1 -0
  85. package/dist/cli/templates/index.d.ts.map +1 -1
  86. package/dist/cli/templates/index.js +1 -0
  87. package/dist/cli/templates/index.js.map +1 -1
  88. package/dist/cli/templates/process-management-page.d.ts +8 -0
  89. package/dist/cli/templates/process-management-page.d.ts.map +1 -0
  90. package/dist/cli/templates/process-management-page.js +824 -0
  91. package/dist/cli/templates/process-management-page.js.map +1 -0
  92. package/dist/cli/templates/user-management-page.d.ts +2 -1
  93. package/dist/cli/templates/user-management-page.d.ts.map +1 -1
  94. package/dist/cli/templates/user-management-page.js +321 -62
  95. package/dist/cli/templates/user-management-page.js.map +1 -1
  96. package/dist/config.d.ts +43 -23
  97. package/dist/config.d.ts.map +1 -1
  98. package/dist/config.js +8 -2
  99. package/dist/config.js.map +1 -1
  100. package/dist/types/auth.d.ts +0 -2
  101. package/dist/types/auth.d.ts.map +1 -1
  102. package/dist/types/config.d.ts +2 -0
  103. package/dist/types/config.d.ts.map +1 -1
  104. package/dist/types/index.d.ts +1 -0
  105. package/dist/types/index.d.ts.map +1 -1
  106. package/dist/types/process.d.ts +106 -0
  107. package/dist/types/process.d.ts.map +1 -0
  108. package/dist/types/process.js +5 -0
  109. package/dist/types/process.js.map +1 -0
  110. package/package.json +3 -1
@@ -0,0 +1,132 @@
1
+ /**
2
+ * ProcessLogger - Execution log I/O with crash recovery and aggregate metrics
3
+ *
4
+ * Logs stored in .loom/process-logs/{processName}/{runId}.json
5
+ * Uses atomic write (temp file + rename) for safety.
6
+ */
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ export class ProcessLogger {
10
+ logsDir;
11
+ constructor(projectRoot) {
12
+ this.logsDir = path.join(projectRoot, '.loom', 'process-logs');
13
+ }
14
+ async initialize() {
15
+ await fs.mkdir(this.logsDir, { recursive: true });
16
+ }
17
+ async writeLog(entry) {
18
+ const dir = path.join(this.logsDir, entry.processName);
19
+ await fs.mkdir(dir, { recursive: true });
20
+ const filePath = path.join(dir, `${entry.runId}.json`);
21
+ const tmpPath = filePath + '.tmp';
22
+ await fs.writeFile(tmpPath, JSON.stringify(entry, null, 2), 'utf-8');
23
+ await fs.rename(tmpPath, filePath);
24
+ }
25
+ async readLog(processName, runId) {
26
+ try {
27
+ const filePath = path.join(this.logsDir, processName, `${runId}.json`);
28
+ const data = await fs.readFile(filePath, 'utf-8');
29
+ return JSON.parse(data);
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ async listLogs(processName, options) {
36
+ const dir = path.join(this.logsDir, processName);
37
+ try {
38
+ const files = await fs.readdir(dir);
39
+ const jsonFiles = files.filter(f => f.endsWith('.json')).sort().reverse();
40
+ const offset = options?.offset ?? 0;
41
+ const limit = options?.limit ?? 50;
42
+ const selected = jsonFiles.slice(offset, offset + limit);
43
+ const entries = [];
44
+ for (const file of selected) {
45
+ try {
46
+ const data = await fs.readFile(path.join(dir, file), 'utf-8');
47
+ entries.push(JSON.parse(data));
48
+ }
49
+ catch {
50
+ // Skip corrupt entries
51
+ }
52
+ }
53
+ // Sort by startedAt desc
54
+ entries.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
55
+ return entries;
56
+ }
57
+ catch {
58
+ return [];
59
+ }
60
+ }
61
+ async listProcessNames() {
62
+ try {
63
+ const dirs = await fs.readdir(this.logsDir, { withFileTypes: true });
64
+ return dirs.filter(d => d.isDirectory()).map(d => d.name);
65
+ }
66
+ catch {
67
+ return [];
68
+ }
69
+ }
70
+ /**
71
+ * Crash recovery: find log entries with status='running' and mark them as failed.
72
+ * Called during ProcessEngine.initialize().
73
+ */
74
+ async recoverCrashedEntries() {
75
+ let recovered = 0;
76
+ const names = await this.listProcessNames();
77
+ for (const name of names) {
78
+ const logs = await this.listLogs(name, { limit: 100 });
79
+ for (const entry of logs) {
80
+ if (entry.status === 'running') {
81
+ await this.writeLog({
82
+ ...entry,
83
+ status: 'failed',
84
+ endedAt: new Date().toISOString(),
85
+ durationMs: 0,
86
+ error: 'Server crashed during execution',
87
+ });
88
+ recovered++;
89
+ }
90
+ }
91
+ }
92
+ return recovered;
93
+ }
94
+ /**
95
+ * Aggregate metrics from log entries for a process.
96
+ * Groups by date, computes totals and averages.
97
+ */
98
+ async aggregateMetrics(processName) {
99
+ const logs = await this.listLogs(processName, { limit: 1000 });
100
+ if (logs.length === 0)
101
+ return [];
102
+ const byDate = new Map();
103
+ for (const log of logs) {
104
+ const date = new Date(log.startedAt).toISOString().split('T')[0];
105
+ const entries = byDate.get(date) ?? [];
106
+ entries.push(log);
107
+ byDate.set(date, entries);
108
+ }
109
+ const result = [];
110
+ for (const [date, entries] of byDate) {
111
+ const completed = entries.filter(e => e.status === 'completed');
112
+ const failed = entries.filter(e => e.status === 'failed');
113
+ const timeout = entries.filter(e => e.status === 'timeout');
114
+ const totalDuration = entries.reduce((sum, e) => sum + e.durationMs, 0);
115
+ result.push({
116
+ processName,
117
+ date,
118
+ totalRuns: entries.length,
119
+ completedRuns: completed.length,
120
+ failedRuns: failed.length,
121
+ timeoutRuns: timeout.length,
122
+ avgDurationMs: entries.length > 0 ? Math.round(totalDuration / entries.length) : 0,
123
+ lastRunAt: entries[0]?.startedAt ?? null,
124
+ lastStatus: entries[0]?.status ?? null,
125
+ });
126
+ }
127
+ // Sort by date desc
128
+ result.sort((a, b) => b.date.localeCompare(a.date));
129
+ return result;
130
+ }
131
+ }
132
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/backend/process/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * ExecutionQueue - FIFO queue with maxConcurrent limit
3
+ *
4
+ * In-memory with crash recovery awareness.
5
+ * On server crash, queued entries are lost (acceptable).
6
+ * Running entries are recovered by ProcessEngine.initialize() scanning logs.
7
+ */
8
+ import type { ProcessQueueEntry } from '../../types/process.js';
9
+ export declare class ExecutionQueue {
10
+ private queue;
11
+ private running;
12
+ private maxConcurrent;
13
+ constructor(maxConcurrent: number);
14
+ enqueue(entry: ProcessQueueEntry): void;
15
+ dequeue(): ProcessQueueEntry | undefined;
16
+ markRunning(runId: string, sessionId: string): void;
17
+ markCompleted(runId: string): void;
18
+ markFailed(runId: string, error: string): void;
19
+ markTimeout(runId: string): void;
20
+ cancelForProcess(processName: string): number;
21
+ getQueueStatus(): {
22
+ queued: number;
23
+ running: number;
24
+ maxConcurrent: number;
25
+ entries: ProcessQueueEntry[];
26
+ };
27
+ getRunning(): ProcessQueueEntry[];
28
+ getQueued(): ProcessQueueEntry[];
29
+ hasSlot(): boolean;
30
+ }
31
+ //# sourceMappingURL=queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../../src/backend/process/queue.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,OAAO,CAA6C;IAC5D,OAAO,CAAC,aAAa,CAAS;gBAElB,aAAa,EAAE,MAAM;IAIjC,OAAO,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAIvC,OAAO,IAAI,iBAAiB,GAAG,SAAS;IAIxC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAWnD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IASlC,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAU9C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAUhC,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM;IAM7C,cAAc,IAAI;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,iBAAiB,EAAE,CAAA;KAAE;IAS1G,UAAU,IAAI,iBAAiB,EAAE;IAIjC,SAAS,IAAI,iBAAiB,EAAE;IAIhC,OAAO,IAAI,OAAO;CAGnB"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * ExecutionQueue - FIFO queue with maxConcurrent limit
3
+ *
4
+ * In-memory with crash recovery awareness.
5
+ * On server crash, queued entries are lost (acceptable).
6
+ * Running entries are recovered by ProcessEngine.initialize() scanning logs.
7
+ */
8
+ export class ExecutionQueue {
9
+ queue = [];
10
+ running = new Map();
11
+ maxConcurrent;
12
+ constructor(maxConcurrent) {
13
+ this.maxConcurrent = maxConcurrent;
14
+ }
15
+ enqueue(entry) {
16
+ this.queue.push(entry);
17
+ }
18
+ dequeue() {
19
+ return this.queue.shift();
20
+ }
21
+ markRunning(runId, sessionId) {
22
+ const entry = this.queue.find(e => e.runId === runId);
23
+ if (entry) {
24
+ entry.state = 'running';
25
+ entry.sessionId = sessionId;
26
+ entry.startedAt = new Date().toISOString();
27
+ this.queue = this.queue.filter(e => e.runId !== runId);
28
+ this.running.set(runId, entry);
29
+ }
30
+ }
31
+ markCompleted(runId) {
32
+ const entry = this.running.get(runId);
33
+ if (entry) {
34
+ entry.state = 'completed';
35
+ entry.endedAt = new Date().toISOString();
36
+ this.running.delete(runId);
37
+ }
38
+ }
39
+ markFailed(runId, error) {
40
+ const entry = this.running.get(runId);
41
+ if (entry) {
42
+ entry.state = 'failed';
43
+ entry.endedAt = new Date().toISOString();
44
+ entry.error = error;
45
+ this.running.delete(runId);
46
+ }
47
+ }
48
+ markTimeout(runId) {
49
+ const entry = this.running.get(runId);
50
+ if (entry) {
51
+ entry.state = 'timeout';
52
+ entry.endedAt = new Date().toISOString();
53
+ entry.error = 'Execution timed out';
54
+ this.running.delete(runId);
55
+ }
56
+ }
57
+ cancelForProcess(processName) {
58
+ const before = this.queue.length;
59
+ this.queue = this.queue.filter(e => e.processName !== processName);
60
+ return before - this.queue.length;
61
+ }
62
+ getQueueStatus() {
63
+ return {
64
+ queued: this.queue.length,
65
+ running: this.running.size,
66
+ maxConcurrent: this.maxConcurrent,
67
+ entries: [...this.running.values(), ...this.queue],
68
+ };
69
+ }
70
+ getRunning() {
71
+ return Array.from(this.running.values());
72
+ }
73
+ getQueued() {
74
+ return [...this.queue];
75
+ }
76
+ hasSlot() {
77
+ return this.running.size < this.maxConcurrent;
78
+ }
79
+ }
80
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue.js","sourceRoot":"","sources":["../../../src/backend/process/queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * ProcessRegistry - Manages process definitions in .loom/processes.json
3
+ *
4
+ * Uses atomic file write (temp file + rename) to prevent corruption.
5
+ */
6
+ import type { ProcessDefinition } from '../../types/process.js';
7
+ export declare class ProcessRegistry {
8
+ private processes;
9
+ private filePath;
10
+ constructor(projectRoot: string);
11
+ initialize(): Promise<void>;
12
+ addProcess(def: Omit<ProcessDefinition, 'createdAt' | 'updatedAt' | 'paused'>): Promise<ProcessDefinition>;
13
+ removeProcess(name: string): Promise<boolean>;
14
+ getProcess(name: string): Promise<ProcessDefinition | undefined>;
15
+ /** Synchronous version for status checks */
16
+ getProcessSync(name: string): ProcessDefinition | undefined;
17
+ listProcesses(options?: {
18
+ status?: 'active' | 'paused';
19
+ }): Promise<ProcessDefinition[]>;
20
+ updateProcess(name: string, updates: Partial<Pick<ProcessDefinition, 'description' | 'cron' | 'cronTimezone' | 'onEvent' | 'eventFilter' | 'eventDebounceMs' | 'prompt' | 'paused' | 'metricsConfig'>>): Promise<ProcessDefinition | undefined>;
21
+ /** Validate that a full process has a PROCESS.md file */
22
+ validateProcessFile(name: string): Promise<boolean>;
23
+ private save;
24
+ }
25
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/backend/process/registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAA6C;IAC9D,OAAO,CAAC,QAAQ,CAAS;gBAEb,WAAW,EAAE,MAAM;IAIzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAc3B,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkB1G,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO7C,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAItE,4CAA4C;IAC5C,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIrD,aAAa,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAOvF,aAAa,CACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,iBAAiB,EAAE,aAAa,GAAG,MAAM,GAAG,cAAc,GAAG,SAAS,GAAG,aAAa,GAAG,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,GAAG,eAAe,CAAC,CAAC,GACzK,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAezC,yDAAyD;IACnD,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;YAU3C,IAAI;CAUnB"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * ProcessRegistry - Manages process definitions in .loom/processes.json
3
+ *
4
+ * Uses atomic file write (temp file + rename) to prevent corruption.
5
+ */
6
+ import fs from 'fs/promises';
7
+ import path from 'path';
8
+ export class ProcessRegistry {
9
+ processes = new Map();
10
+ filePath;
11
+ constructor(projectRoot) {
12
+ this.filePath = path.join(projectRoot, '.loom', 'processes.json');
13
+ }
14
+ async initialize() {
15
+ try {
16
+ const data = await fs.readFile(this.filePath, 'utf-8');
17
+ const parsed = JSON.parse(data);
18
+ if (Array.isArray(parsed)) {
19
+ for (const def of parsed) {
20
+ this.processes.set(def.name, def);
21
+ }
22
+ }
23
+ }
24
+ catch {
25
+ // File doesn't exist yet — start with empty registry
26
+ }
27
+ }
28
+ async addProcess(def) {
29
+ if (this.processes.has(def.name)) {
30
+ throw new Error(`Process "${def.name}" already exists`);
31
+ }
32
+ const now = new Date().toISOString();
33
+ const full = {
34
+ ...def,
35
+ paused: false,
36
+ createdAt: now,
37
+ updatedAt: now,
38
+ };
39
+ this.processes.set(def.name, full);
40
+ await this.save();
41
+ return full;
42
+ }
43
+ async removeProcess(name) {
44
+ if (!this.processes.has(name))
45
+ return false;
46
+ this.processes.delete(name);
47
+ await this.save();
48
+ return true;
49
+ }
50
+ async getProcess(name) {
51
+ return this.processes.get(name);
52
+ }
53
+ /** Synchronous version for status checks */
54
+ getProcessSync(name) {
55
+ return this.processes.get(name);
56
+ }
57
+ async listProcesses(options) {
58
+ const all = Array.from(this.processes.values());
59
+ if (options?.status === 'active')
60
+ return all.filter(p => !p.paused);
61
+ if (options?.status === 'paused')
62
+ return all.filter(p => p.paused);
63
+ return all;
64
+ }
65
+ async updateProcess(name, updates) {
66
+ const existing = this.processes.get(name);
67
+ if (!existing)
68
+ return undefined;
69
+ const updated = {
70
+ ...existing,
71
+ ...updates,
72
+ updatedAt: new Date().toISOString(),
73
+ };
74
+ this.processes.set(name, updated);
75
+ await this.save();
76
+ return updated;
77
+ }
78
+ /** Validate that a full process has a PROCESS.md file */
79
+ async validateProcessFile(name) {
80
+ try {
81
+ const processDir = path.join(path.dirname(this.filePath), '..', '.claude', 'processes', name);
82
+ await fs.access(path.join(processDir, 'PROCESS.md'));
83
+ return true;
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ }
89
+ async save() {
90
+ const dir = path.dirname(this.filePath);
91
+ await fs.mkdir(dir, { recursive: true });
92
+ const data = JSON.stringify(Array.from(this.processes.values()), null, 2);
93
+ const tmpPath = this.filePath + '.tmp';
94
+ await fs.writeFile(tmpPath, data, 'utf-8');
95
+ await fs.rename(tmpPath, this.filePath);
96
+ }
97
+ }
98
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/backend/process/registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * TriggerManager - Manages cron jobs and EventBus subscriptions with debounce
3
+ */
4
+ import type { EventBus } from '../events/event-bus.js';
5
+ import type { ProcessTriggerType } from '../../types/process.js';
6
+ import type { ProcessRegistry } from './registry.js';
7
+ type TriggerCallback = (processName: string, triggerType: ProcessTriggerType, eventPayload?: Record<string, unknown>) => void;
8
+ export declare class TriggerManager {
9
+ private cronJobs;
10
+ private eventUnsubscribers;
11
+ private debounceTimers;
12
+ private onTrigger;
13
+ constructor(onTrigger: TriggerCallback);
14
+ /** Start a cron trigger. Validates expression via node-cron. */
15
+ startCron(processName: string, expression: string, timezone?: string): void;
16
+ stopCron(processName: string): void;
17
+ /**
18
+ * Start an event trigger with debounce.
19
+ * When events arrive rapidly, waits debounceMs after the last event
20
+ * before firing, merging payloads (last event wins).
21
+ */
22
+ startEventSubscription(processName: string, eventBus: EventBus, pattern: string, filter?: Record<string, unknown>, debounceMs?: number): void;
23
+ stopEventSubscription(processName: string): void;
24
+ /** Restore all triggers from registry (for server restart) */
25
+ restoreFromRegistry(registry: ProcessRegistry, eventBus?: EventBus): Promise<void>;
26
+ stopAll(): void;
27
+ }
28
+ export {};
29
+ //# sourceMappingURL=trigger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger.d.ts","sourceRoot":"","sources":["../../../src/backend/process/trigger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,KAAK,eAAe,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,kBAAkB,EAAE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAQ9H,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,kBAAkB,CAAsC;IAChE,OAAO,CAAC,cAAc,CAA0C;IAChE,OAAO,CAAC,SAAS,CAAkB;gBAEvB,SAAS,EAAE,eAAe;IAItC,gEAAgE;IAChE,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IAiB3E,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAQnC;;;;OAIG;IACH,sBAAsB,CACpB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI;IAiCP,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAchD,8DAA8D;IACxD,mBAAmB,CAAC,QAAQ,EAAE,eAAe,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBxF,OAAO,IAAI,IAAI;CAgBhB"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * TriggerManager - Manages cron jobs and EventBus subscriptions with debounce
3
+ */
4
+ import cron from 'node-cron';
5
+ export class TriggerManager {
6
+ cronJobs = new Map();
7
+ eventUnsubscribers = new Map();
8
+ debounceTimers = new Map();
9
+ onTrigger;
10
+ constructor(onTrigger) {
11
+ this.onTrigger = onTrigger;
12
+ }
13
+ /** Start a cron trigger. Validates expression via node-cron. */
14
+ startCron(processName, expression, timezone) {
15
+ this.stopCron(processName);
16
+ if (!cron.validate(expression)) {
17
+ throw new Error(`Invalid cron expression for process "${processName}": ${expression}`);
18
+ }
19
+ const options = { scheduled: true };
20
+ if (timezone)
21
+ options.timezone = timezone;
22
+ const task = cron.schedule(expression, () => {
23
+ this.onTrigger(processName, 'cron');
24
+ }, options);
25
+ this.cronJobs.set(processName, task);
26
+ }
27
+ stopCron(processName) {
28
+ const task = this.cronJobs.get(processName);
29
+ if (task) {
30
+ task.stop();
31
+ this.cronJobs.delete(processName);
32
+ }
33
+ }
34
+ /**
35
+ * Start an event trigger with debounce.
36
+ * When events arrive rapidly, waits debounceMs after the last event
37
+ * before firing, merging payloads (last event wins).
38
+ */
39
+ startEventSubscription(processName, eventBus, pattern, filter, debounceMs) {
40
+ this.stopEventSubscription(processName);
41
+ const debounce = debounceMs ?? 1000;
42
+ let lastPayload;
43
+ const unsubscribe = eventBus.on(pattern, (event) => {
44
+ // Apply filter if specified
45
+ if (filter) {
46
+ const matches = Object.entries(filter).every(([key, value]) => event.payload[key] === value);
47
+ if (!matches)
48
+ return;
49
+ }
50
+ lastPayload = event.payload;
51
+ // Clear existing timer
52
+ const existing = this.debounceTimers.get(processName);
53
+ if (existing)
54
+ clearTimeout(existing);
55
+ // Set new timer
56
+ const timer = setTimeout(() => {
57
+ this.debounceTimers.delete(processName);
58
+ this.onTrigger(processName, 'event', lastPayload);
59
+ }, debounce);
60
+ this.debounceTimers.set(processName, timer);
61
+ });
62
+ this.eventUnsubscribers.set(processName, unsubscribe);
63
+ }
64
+ stopEventSubscription(processName) {
65
+ const unsubscribe = this.eventUnsubscribers.get(processName);
66
+ if (unsubscribe) {
67
+ unsubscribe();
68
+ this.eventUnsubscribers.delete(processName);
69
+ }
70
+ const timer = this.debounceTimers.get(processName);
71
+ if (timer) {
72
+ clearTimeout(timer);
73
+ this.debounceTimers.delete(processName);
74
+ }
75
+ }
76
+ /** Restore all triggers from registry (for server restart) */
77
+ async restoreFromRegistry(registry, eventBus) {
78
+ const processes = await registry.listProcesses({ status: 'active' });
79
+ for (const proc of processes) {
80
+ if (proc.cron) {
81
+ try {
82
+ this.startCron(proc.name, proc.cron, proc.cronTimezone);
83
+ }
84
+ catch (err) {
85
+ console.error(`[TriggerManager] Failed to restore cron for "${proc.name}":`, err);
86
+ }
87
+ }
88
+ if (proc.onEvent && eventBus) {
89
+ this.startEventSubscription(proc.name, eventBus, proc.onEvent, proc.eventFilter, proc.eventDebounceMs);
90
+ }
91
+ }
92
+ }
93
+ stopAll() {
94
+ for (const task of this.cronJobs.values()) {
95
+ task.stop();
96
+ }
97
+ this.cronJobs.clear();
98
+ for (const unsubscribe of this.eventUnsubscribers.values()) {
99
+ unsubscribe();
100
+ }
101
+ this.eventUnsubscribers.clear();
102
+ for (const timer of this.debounceTimers.values()) {
103
+ clearTimeout(timer);
104
+ }
105
+ this.debounceTimers.clear();
106
+ }
107
+ }
108
+ //# sourceMappingURL=trigger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trigger.js","sourceRoot":"","sources":["../../../src/backend/process/trigger.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,9 @@
7
7
  * POST /api/v1/auth/refresh - Refresh access token
8
8
  * GET /api/v1/auth/whoami - Current user info
9
9
  * GET /api/v1/auth/roles - List roles + permissions
10
+ * POST /api/v1/auth/roles - Create role (admin only)
11
+ * PUT /api/v1/auth/roles/:role - Update role (admin only)
12
+ * DELETE /api/v1/auth/roles/:role - Delete role (admin only)
10
13
  * GET /api/v1/auth/users - List users (admin only)
11
14
  * POST /api/v1/auth/users - Create user (admin only)
12
15
  * GET /api/v1/auth/users/:id - Get user
@@ -23,6 +26,8 @@ export interface AuthRouteOptions {
23
26
  userStore: UserStore;
24
27
  tokenStore: TokenStore;
25
28
  eventBus?: EventBus;
29
+ projectRoot: string;
30
+ modelNames: string[];
26
31
  }
27
32
  export declare function registerAuthRoutes(fastify: FastifyInstance, options: AuthRouteOptions): void;
28
33
  //# sourceMappingURL=auth-routes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-routes.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/auth-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAMvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAgV5F"}
1
+ {"version":3,"file":"auth-routes.d.ts","sourceRoot":"","sources":["../../../src/backend/routes/auth-routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG/C,OAAO,KAAK,EAAE,UAAU,EAAiC,MAAM,qBAAqB,CAAC;AACrF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAMvD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,GAAG,IAAI,CA4kB5F"}