prepia 1.0.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/LICENSE +21 -0
- package/README.md +312 -0
- package/bin/prepia.mjs +119 -0
- package/package.json +53 -0
- package/skill/SKILL.md +148 -0
- package/skill/config.json +29 -0
- package/src/analytics/dashboard.mjs +84 -0
- package/src/analytics/tracker.mjs +131 -0
- package/src/api/middleware.mjs +219 -0
- package/src/api/routes.mjs +142 -0
- package/src/api/server.mjs +150 -0
- package/src/cache/disk-store.mjs +199 -0
- package/src/cache/manager.mjs +142 -0
- package/src/cache/memory-store.mjs +205 -0
- package/src/chain/dag.mjs +209 -0
- package/src/chain/executor.mjs +103 -0
- package/src/chain/scheduler.mjs +89 -0
- package/src/client/adapters.mjs +483 -0
- package/src/client/connector.mjs +391 -0
- package/src/client/index.mjs +483 -0
- package/src/client/websocket.mjs +353 -0
- package/src/core/context-packager.mjs +169 -0
- package/src/core/engine.mjs +338 -0
- package/src/core/event-bus.mjs +84 -0
- package/src/core/prepimshot.mjs +120 -0
- package/src/core/task-decomposer.mjs +158 -0
- package/src/edge/lite.mjs +90 -0
- package/src/guard/checker.mjs +123 -0
- package/src/guard/fact-checker.mjs +105 -0
- package/src/guard/hallucination.mjs +108 -0
- package/src/index.mjs +67 -0
- package/src/models/local-model.mjs +171 -0
- package/src/models/provider.mjs +192 -0
- package/src/models/router.mjs +156 -0
- package/src/morph/optimizer.mjs +142 -0
- package/src/network/p2p.mjs +146 -0
- package/src/persona/detector.mjs +118 -0
- package/src/plugins/loader.mjs +120 -0
- package/src/plugins/registry.mjs +164 -0
- package/src/plugins/sandbox.mjs +79 -0
- package/src/rate/limiter.mjs +145 -0
- package/src/rate/shield.mjs +150 -0
- package/src/script/executor.mjs +164 -0
- package/src/script/parser.mjs +134 -0
- package/src/security/privacy.mjs +108 -0
- package/src/security/sanitizer.mjs +133 -0
- package/src/shadow/daemon.mjs +128 -0
- package/src/stream/handler.mjs +204 -0
- package/src/tools/calculator.mjs +312 -0
- package/src/tools/file-ops.mjs +138 -0
- package/src/tools/http-client.mjs +127 -0
- package/src/tools/orchestrator.mjs +205 -0
- package/src/tools/web-scraper.mjs +159 -0
- package/src/tools/web-search.mjs +129 -0
- package/src/vault/knowledge-base.mjs +207 -0
- package/src/vault/pattern-learner.mjs +192 -0
- package/workflows/analyze.json +32 -0
- package/workflows/automate.json +32 -0
- package/workflows/research.json +37 -0
- package/workflows/summarize.json +32 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview DAG (Directed Acyclic Graph) for task dependencies.
|
|
3
|
+
* @module chain/dag
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export class DAG {
|
|
7
|
+
constructor() {
|
|
8
|
+
/** @type {Map<string, Set<string>>} Adjacency list: node -> dependencies */
|
|
9
|
+
this._nodes = new Map();
|
|
10
|
+
/** @type {Map<string, *>} Node metadata */
|
|
11
|
+
this._metadata = new Map();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Add a node to the DAG.
|
|
16
|
+
* @param {string} id - Node ID
|
|
17
|
+
* @param {*} [metadata] - Optional metadata
|
|
18
|
+
*/
|
|
19
|
+
addNode(id, metadata) {
|
|
20
|
+
if (!this._nodes.has(id)) {
|
|
21
|
+
this._nodes.set(id, new Set());
|
|
22
|
+
}
|
|
23
|
+
if (metadata !== undefined) {
|
|
24
|
+
this._metadata.set(id, metadata);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Add a dependency edge (from depends on to).
|
|
30
|
+
* @param {string} from - Dependent node
|
|
31
|
+
* @param {string} to - Dependency node
|
|
32
|
+
*/
|
|
33
|
+
addEdge(from, to) {
|
|
34
|
+
this.addNode(from);
|
|
35
|
+
this.addNode(to);
|
|
36
|
+
// Check for cycle before adding
|
|
37
|
+
if (this._wouldCycle(from, to)) {
|
|
38
|
+
throw new Error(`Adding edge ${from} -> ${to} would create a cycle`);
|
|
39
|
+
}
|
|
40
|
+
this._nodes.get(from).add(to);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check if adding an edge would create a cycle.
|
|
45
|
+
* @param {string} from
|
|
46
|
+
* @param {string} to
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
* @private
|
|
49
|
+
*/
|
|
50
|
+
_wouldCycle(from, to) {
|
|
51
|
+
if (from === to) return true;
|
|
52
|
+
// BFS from to to see if we can reach from
|
|
53
|
+
const visited = new Set();
|
|
54
|
+
const queue = [to];
|
|
55
|
+
while (queue.length > 0) {
|
|
56
|
+
const current = queue.shift();
|
|
57
|
+
if (current === from) return true;
|
|
58
|
+
if (visited.has(current)) continue;
|
|
59
|
+
visited.add(current);
|
|
60
|
+
const deps = this._nodes.get(current);
|
|
61
|
+
if (deps) {
|
|
62
|
+
for (const dep of deps) {
|
|
63
|
+
queue.push(dep);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if the DAG has a cycle.
|
|
72
|
+
* @returns {boolean}
|
|
73
|
+
*/
|
|
74
|
+
hasCycle() {
|
|
75
|
+
const visited = new Set();
|
|
76
|
+
const inStack = new Set();
|
|
77
|
+
|
|
78
|
+
const dfs = (node) => {
|
|
79
|
+
if (inStack.has(node)) return true;
|
|
80
|
+
if (visited.has(node)) return false;
|
|
81
|
+
visited.add(node);
|
|
82
|
+
inStack.add(node);
|
|
83
|
+
const deps = this._nodes.get(node) || new Set();
|
|
84
|
+
for (const dep of deps) {
|
|
85
|
+
if (dfs(dep)) return true;
|
|
86
|
+
}
|
|
87
|
+
inStack.delete(node);
|
|
88
|
+
return false;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
for (const node of this._nodes.keys()) {
|
|
92
|
+
if (dfs(node)) return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Get topological order of nodes.
|
|
99
|
+
* @returns {string[]} Nodes in topological order
|
|
100
|
+
* @throws {Error} If cycle detected
|
|
101
|
+
*/
|
|
102
|
+
topologicalSort() {
|
|
103
|
+
const visited = new Set();
|
|
104
|
+
const result = [];
|
|
105
|
+
const inStack = new Set();
|
|
106
|
+
|
|
107
|
+
const dfs = (node) => {
|
|
108
|
+
if (inStack.has(node)) throw new Error('Cycle detected in DAG');
|
|
109
|
+
if (visited.has(node)) return;
|
|
110
|
+
visited.add(node);
|
|
111
|
+
inStack.add(node);
|
|
112
|
+
const deps = this._nodes.get(node) || new Set();
|
|
113
|
+
for (const dep of deps) {
|
|
114
|
+
dfs(dep);
|
|
115
|
+
}
|
|
116
|
+
inStack.delete(node);
|
|
117
|
+
result.push(node);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
for (const node of this._nodes.keys()) {
|
|
121
|
+
dfs(node);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get nodes with no dependencies (roots).
|
|
129
|
+
* @returns {string[]}
|
|
130
|
+
*/
|
|
131
|
+
getRoots() {
|
|
132
|
+
const hasDeps = new Set();
|
|
133
|
+
for (const deps of this._nodes.values()) {
|
|
134
|
+
for (const dep of deps) {
|
|
135
|
+
hasDeps.add(dep);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return Array.from(this._nodes.keys()).filter(n => !hasDeps.has(n));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get dependencies of a node.
|
|
143
|
+
* @param {string} id
|
|
144
|
+
* @returns {string[]}
|
|
145
|
+
*/
|
|
146
|
+
getDependencies(id) {
|
|
147
|
+
return Array.from(this._nodes.get(id) || []);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Get nodes that depend on a given node.
|
|
152
|
+
* @param {string} id
|
|
153
|
+
* @returns {string[]}
|
|
154
|
+
*/
|
|
155
|
+
getDependents(id) {
|
|
156
|
+
const result = [];
|
|
157
|
+
for (const [node, deps] of this._nodes) {
|
|
158
|
+
if (deps.has(id)) result.push(node);
|
|
159
|
+
}
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get metadata for a node.
|
|
165
|
+
* @param {string} id
|
|
166
|
+
* @returns {*}
|
|
167
|
+
*/
|
|
168
|
+
getMetadata(id) {
|
|
169
|
+
return this._metadata.get(id);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get all node IDs.
|
|
174
|
+
* @returns {string[]}
|
|
175
|
+
*/
|
|
176
|
+
getNodes() {
|
|
177
|
+
return Array.from(this._nodes.keys());
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Get the number of nodes.
|
|
182
|
+
* @returns {number}
|
|
183
|
+
*/
|
|
184
|
+
get size() {
|
|
185
|
+
return this._nodes.size;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Remove a node and all its edges.
|
|
190
|
+
* @param {string} id
|
|
191
|
+
*/
|
|
192
|
+
removeNode(id) {
|
|
193
|
+
this._nodes.delete(id);
|
|
194
|
+
this._metadata.delete(id);
|
|
195
|
+
for (const deps of this._nodes.values()) {
|
|
196
|
+
deps.delete(id);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Clear the entire DAG.
|
|
202
|
+
*/
|
|
203
|
+
clear() {
|
|
204
|
+
this._nodes.clear();
|
|
205
|
+
this._metadata.clear();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export default DAG;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Task execution engine with retry support.
|
|
3
|
+
* @module chain/executor
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
|
|
8
|
+
export class Executor extends EventEmitter {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object} [options]
|
|
11
|
+
* @param {number} [options.maxRetries=3] - Max retry attempts
|
|
12
|
+
* @param {number} [options.retryDelay=1000] - Base retry delay in ms
|
|
13
|
+
* @param {number} [options.timeout=30000] - Task timeout in ms
|
|
14
|
+
*/
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
super();
|
|
17
|
+
this._maxRetries = options.maxRetries ?? 3;
|
|
18
|
+
this._retryDelay = options.retryDelay ?? 1000;
|
|
19
|
+
this._timeout = options.timeout ?? 30000;
|
|
20
|
+
this._taskHandlers = new Map();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Register a task handler.
|
|
25
|
+
* @param {string} type - Task type
|
|
26
|
+
* @param {Function} handler - Async handler function(params) => result
|
|
27
|
+
*/
|
|
28
|
+
register(type, handler) {
|
|
29
|
+
this._taskHandlers.set(type, handler);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Execute a single task with retries.
|
|
34
|
+
* @param {Object} task
|
|
35
|
+
* @param {string} task.id - Task ID
|
|
36
|
+
* @param {string} task.type - Task type
|
|
37
|
+
* @param {Object} [task.params] - Task parameters
|
|
38
|
+
* @returns {Promise<*>}
|
|
39
|
+
*/
|
|
40
|
+
async execute(task) {
|
|
41
|
+
const handler = this._taskHandlers.get(task.type);
|
|
42
|
+
if (!handler) {
|
|
43
|
+
throw new Error(`No handler registered for task type: ${task.type}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let lastError;
|
|
47
|
+
for (let attempt = 0; attempt <= this._maxRetries; attempt++) {
|
|
48
|
+
try {
|
|
49
|
+
this.emit('task:attempt', { id: task.id, attempt });
|
|
50
|
+
const result = await Promise.race([
|
|
51
|
+
handler(task.params || {}),
|
|
52
|
+
new Promise((_, reject) =>
|
|
53
|
+
setTimeout(() => reject(new Error('Task timeout')), this._timeout)
|
|
54
|
+
),
|
|
55
|
+
]);
|
|
56
|
+
this.emit('task:success', { id: task.id, attempt });
|
|
57
|
+
return result;
|
|
58
|
+
} catch (err) {
|
|
59
|
+
lastError = err;
|
|
60
|
+
this.emit('task:retry', { id: task.id, attempt, error: err.message });
|
|
61
|
+
if (attempt < this._maxRetries) {
|
|
62
|
+
await new Promise(r => setTimeout(r, this._retryDelay * Math.pow(2, attempt)));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.emit('task:failed', { id: task.id, error: lastError?.message });
|
|
68
|
+
throw new Error(`Task ${task.id} failed after ${this._maxRetries + 1} attempts: ${lastError?.message}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Execute multiple tasks in parallel.
|
|
73
|
+
* @param {Object[]} tasks
|
|
74
|
+
* @param {number} [concurrency=5]
|
|
75
|
+
* @returns {Promise<Map<string, *>>}
|
|
76
|
+
*/
|
|
77
|
+
async executeAll(tasks, concurrency = 5) {
|
|
78
|
+
const results = new Map();
|
|
79
|
+
const executing = new Set();
|
|
80
|
+
|
|
81
|
+
for (const task of tasks) {
|
|
82
|
+
const promise = this.execute(task)
|
|
83
|
+
.then(result => {
|
|
84
|
+
executing.delete(promise);
|
|
85
|
+
results.set(task.id, result);
|
|
86
|
+
})
|
|
87
|
+
.catch(err => {
|
|
88
|
+
executing.delete(promise);
|
|
89
|
+
results.set(task.id, { error: err.message });
|
|
90
|
+
});
|
|
91
|
+
executing.add(promise);
|
|
92
|
+
|
|
93
|
+
if (executing.size >= concurrency) {
|
|
94
|
+
await Promise.race(executing);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
await Promise.all(executing);
|
|
99
|
+
return results;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export default Executor;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Parallel task scheduler respecting DAG dependencies.
|
|
3
|
+
* @module chain/scheduler
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { EventEmitter } from 'node:events';
|
|
7
|
+
|
|
8
|
+
export class Scheduler extends EventEmitter {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Object} [options]
|
|
11
|
+
* @param {number} [options.maxConcurrency=5] - Max parallel tasks
|
|
12
|
+
*/
|
|
13
|
+
constructor(options = {}) {
|
|
14
|
+
super();
|
|
15
|
+
this._maxConcurrency = options.maxConcurrency ?? 5;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Schedule and execute tasks respecting dependencies.
|
|
20
|
+
* @param {import('./dag.mjs').DAG} dag - Task dependency graph
|
|
21
|
+
* @param {Function} executor - Async function(taskId) => result
|
|
22
|
+
* @returns {Promise<Map<string, *>>} Results map
|
|
23
|
+
*/
|
|
24
|
+
async schedule(dag, executor) {
|
|
25
|
+
const results = new Map();
|
|
26
|
+
const completed = new Set();
|
|
27
|
+
const failed = new Set();
|
|
28
|
+
const running = new Set();
|
|
29
|
+
const pending = new Set(dag.getNodes());
|
|
30
|
+
|
|
31
|
+
const getReady = () => {
|
|
32
|
+
const ready = [];
|
|
33
|
+
for (const nodeId of pending) {
|
|
34
|
+
if (running.has(nodeId) || failed.has(nodeId)) continue;
|
|
35
|
+
const deps = dag.getDependencies(nodeId);
|
|
36
|
+
const allDepsResolved = deps.every(d => completed.has(d) || failed.has(d));
|
|
37
|
+
const noDepsFailed = deps.every(d => !failed.has(d));
|
|
38
|
+
if (allDepsResolved && noDepsFailed) {
|
|
39
|
+
ready.push(nodeId);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return ready;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const trySchedule = () => {
|
|
47
|
+
if (completed.size + failed.size === dag.size) {
|
|
48
|
+
resolve(results);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const ready = getReady();
|
|
53
|
+
for (const nodeId of ready) {
|
|
54
|
+
if (running.size >= this._maxConcurrency) break;
|
|
55
|
+
if (running.has(nodeId)) continue;
|
|
56
|
+
|
|
57
|
+
running.add(nodeId);
|
|
58
|
+
pending.delete(nodeId);
|
|
59
|
+
this.emit('task:start', { nodeId });
|
|
60
|
+
|
|
61
|
+
executor(nodeId, dag.getMetadata(nodeId))
|
|
62
|
+
.then(result => {
|
|
63
|
+
results.set(nodeId, result);
|
|
64
|
+
completed.add(nodeId);
|
|
65
|
+
running.delete(nodeId);
|
|
66
|
+
this.emit('task:complete', { nodeId, result });
|
|
67
|
+
trySchedule();
|
|
68
|
+
})
|
|
69
|
+
.catch(err => {
|
|
70
|
+
results.set(nodeId, { error: err.message });
|
|
71
|
+
failed.add(nodeId);
|
|
72
|
+
running.delete(nodeId);
|
|
73
|
+
this.emit('task:error', { nodeId, error: err.message });
|
|
74
|
+
trySchedule();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// If nothing is running and nothing is ready, we're stuck
|
|
79
|
+
if (running.size === 0 && getReady().length === 0) {
|
|
80
|
+
resolve(results);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
trySchedule();
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export default Scheduler;
|