@netnodeag/kraftwerk 0.61.0 → 0.63.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/cli/routines.js +2 -3
- package/dist/client/index.d.ts +59 -0
- package/dist/client/index.js +63 -0
- package/dist/client/live.d.ts +34 -0
- package/dist/client/live.js +169 -0
- package/dist/client/routes.d.ts +125 -0
- package/dist/client/routes.js +126 -0
- package/dist/inspector/api/agents.d.ts +31 -0
- package/dist/inspector/api/agents.js +50 -0
- package/dist/inspector/api/attention.d.ts +10 -0
- package/dist/inspector/api/attention.js +23 -0
- package/dist/inspector/api/channels.d.ts +67 -0
- package/dist/inspector/api/channels.js +58 -0
- package/dist/inspector/api/chats.d.ts +55 -0
- package/dist/inspector/api/chats.js +154 -0
- package/dist/inspector/api/files.d.ts +15 -0
- package/dist/inspector/api/files.js +43 -0
- package/dist/inspector/api/git.d.ts +25 -0
- package/dist/inspector/api/git.js +56 -0
- package/dist/inspector/api/index.d.ts +334 -0
- package/dist/inspector/api/index.js +43 -0
- package/dist/inspector/api/knowledge.d.ts +13 -0
- package/dist/inspector/api/knowledge.js +56 -0
- package/dist/inspector/api/mime.d.ts +2 -0
- package/dist/inspector/api/mime.js +18 -0
- package/dist/inspector/api/projects.d.ts +11 -0
- package/dist/inspector/api/projects.js +32 -0
- package/dist/inspector/api/router.d.ts +75 -0
- package/dist/inspector/api/router.js +186 -0
- package/dist/inspector/api/runs.d.ts +33 -0
- package/dist/inspector/api/runs.js +105 -0
- package/dist/inspector/api/socket.d.ts +3 -0
- package/dist/inspector/api/socket.js +115 -0
- package/dist/inspector/api/vibeables.d.ts +4 -0
- package/dist/inspector/api/vibeables.js +31 -0
- package/dist/inspector/api/workspace.d.ts +55 -0
- package/dist/inspector/api/workspace.js +117 -0
- package/dist/inspector/chat/sessions.js +31 -19
- package/dist/inspector/context.d.ts +5 -7
- package/dist/inspector/context.js +6 -20
- package/dist/inspector/git.js +52 -43
- package/dist/inspector/live.d.ts +42 -0
- package/dist/inspector/live.js +91 -0
- package/dist/inspector/notifications.js +19 -13
- package/dist/inspector/routines.d.ts +22 -1
- package/dist/inspector/routines.js +32 -5
- package/dist/inspector/runner.js +5 -4
- package/dist/inspector/server.d.ts +2 -2
- package/dist/inspector/server.js +59 -1389
- package/dist/inspector/version.d.ts +9 -0
- package/dist/inspector/version.js +27 -0
- package/dist/inspector/vibeables.js +25 -23
- package/dist/inspector/workspace.d.ts +42 -0
- package/dist/inspector/workspace.js +63 -0
- package/dist/inspector/ws.d.ts +11 -0
- package/dist/inspector/ws.js +143 -0
- package/inspector/dist/assets/{dist-B3L4wDm3.js → dist-9RESajfo.js} +1 -1
- package/inspector/dist/assets/{dist-Cwukoo61.js → dist-Bm5S1BoZ.js} +1 -1
- package/inspector/dist/assets/dist-C8BofvHx.js +1 -0
- package/inspector/dist/assets/{dist-wYE0FpHk.js → dist-CIs4wnTu.js} +1 -1
- package/inspector/dist/assets/{dist-De7-pK5i.js → dist-CjuETd-_.js} +1 -1
- package/inspector/dist/assets/{dist-B58fOC3R.js → dist-CmVK6rgN.js} +1 -1
- package/inspector/dist/assets/{dist-zk56dqRQ.js → dist-CryXvMV_.js} +1 -1
- package/inspector/dist/assets/{dist-BiWJ3XY1.js → dist-D1hk0iC5.js} +1 -1
- package/inspector/dist/assets/{dist-CH9pz3rR.js → dist-DvW9QE1d.js} +1 -1
- package/inspector/dist/assets/{dist-Dno2CmWo.js → dist-VD9jMxBv.js} +1 -1
- package/inspector/dist/assets/{dist-BKBuUSVQ.js → dist-VlAqWD8T.js} +1 -1
- package/inspector/dist/assets/{editor-CMQMyCOv.js → editor-DkWqwl89.js} +3 -3
- package/inspector/dist/assets/index-Bra74Nxe.css +2 -0
- package/inspector/dist/assets/index-DRJS2pGj.js +229 -0
- package/inspector/dist/index.html +2 -2
- package/package.json +7 -2
- package/inspector/dist/assets/dist-Bj93ipvc.js +0 -1
- package/inspector/dist/assets/index-1wfYRgHE.css +0 -2
- package/inspector/dist/assets/index-CtygNkZJ.js +0 -229
|
@@ -2,32 +2,38 @@ import { promises as fs } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import { getOutputDir } from "./context.js";
|
|
5
|
+
import { perWorkspace } from "./workspace.js";
|
|
6
|
+
import { touch } from "./live.js";
|
|
5
7
|
const MAX_ITEMS = 200;
|
|
6
8
|
const BODY_MAX = 280;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
/** The in-memory copy and the write queue, one per workspace. */
|
|
10
|
+
const store = perWorkspace(() => ({ cache: null, writeChain: Promise.resolve() }));
|
|
9
11
|
const file = () => path.join(getOutputDir(), "notifications.json");
|
|
10
12
|
async function load() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
const st = store();
|
|
14
|
+
if (st.cache)
|
|
15
|
+
return st.cache;
|
|
13
16
|
try {
|
|
14
17
|
const parsed = JSON.parse(await fs.readFile(file(), "utf8"));
|
|
15
|
-
cache = Array.isArray(parsed) ? parsed : [];
|
|
18
|
+
st.cache = Array.isArray(parsed) ? parsed : [];
|
|
16
19
|
}
|
|
17
20
|
catch {
|
|
18
|
-
cache = [];
|
|
21
|
+
st.cache = [];
|
|
19
22
|
}
|
|
20
|
-
return cache;
|
|
23
|
+
return st.cache;
|
|
21
24
|
}
|
|
22
25
|
function persist(items) {
|
|
23
|
-
|
|
24
|
-
|
|
26
|
+
const st = store();
|
|
27
|
+
const [dir, target] = [getOutputDir(), file()];
|
|
28
|
+
st.cache = items;
|
|
29
|
+
touch();
|
|
30
|
+
st.writeChain = st.writeChain
|
|
25
31
|
.then(async () => {
|
|
26
|
-
await fs.mkdir(
|
|
32
|
+
await fs.mkdir(dir, { recursive: true });
|
|
27
33
|
// Write-then-rename: a reader (the UI, a test) never sees a half-written file.
|
|
28
|
-
const tmp = `${
|
|
34
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
29
35
|
await fs.writeFile(tmp, JSON.stringify(items, null, 2));
|
|
30
|
-
await fs.rename(tmp,
|
|
36
|
+
await fs.rename(tmp, target);
|
|
31
37
|
})
|
|
32
38
|
.catch(() => { });
|
|
33
39
|
}
|
|
@@ -92,5 +98,5 @@ export async function clearNotifications() {
|
|
|
92
98
|
}
|
|
93
99
|
/** Tests / project switches: forget the in-memory copy so the next read hits disk. */
|
|
94
100
|
export function resetNotificationsCache() {
|
|
95
|
-
cache = null;
|
|
101
|
+
store().cache = null;
|
|
96
102
|
}
|
|
@@ -49,10 +49,31 @@ export interface SaveRoutineInput {
|
|
|
49
49
|
}
|
|
50
50
|
export declare function saveRoutine(slug: string, input: SaveRoutineInput): Promise<Routine>;
|
|
51
51
|
export declare function deleteRoutine(slug: string, id: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Whether a routine's prompt names a workflow: its slug or its name as a whole word
|
|
54
|
+
* ("Run the daily-ceo-report workflow", "Run helpdesk workflow"). Routines are an
|
|
55
|
+
* agent's prompts, so this is how a workflow learns it runs on a schedule.
|
|
56
|
+
*/
|
|
57
|
+
export declare function mentionsWorkflow(prompt: string, w: {
|
|
58
|
+
slug: string;
|
|
59
|
+
name?: string;
|
|
60
|
+
}): boolean;
|
|
61
|
+
/** Every agent's routines with their run state, each with the workflows its prompt names (GET /api/routines). */
|
|
62
|
+
export declare function allRoutines(workflows: {
|
|
63
|
+
slug: string;
|
|
64
|
+
name?: string;
|
|
65
|
+
}[]): Promise<(RoutineStatus & {
|
|
66
|
+
agent: {
|
|
67
|
+
slug: string;
|
|
68
|
+
name: string;
|
|
69
|
+
emoji: string;
|
|
70
|
+
};
|
|
71
|
+
workflows: string[];
|
|
72
|
+
})[]>;
|
|
52
73
|
export declare function routineStatuses(slug: string): Promise<RoutineStatus[]>;
|
|
53
74
|
export declare function runRoutineNow(slug: string, id: string): Promise<{
|
|
54
75
|
chatId: string;
|
|
55
76
|
}>;
|
|
56
|
-
/** Start the
|
|
77
|
+
/** Start the current workspace's scheduler; safe to call more than once. Ticks run in that workspace. */
|
|
57
78
|
export declare function startRoutineScheduler(): void;
|
|
58
79
|
export {};
|
|
@@ -2,6 +2,7 @@ import { promises as fs } from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { parse, stringify } from "yaml";
|
|
4
4
|
import { getOutputDir } from "./context.js";
|
|
5
|
+
import { perWorkspace } from "./workspace.js";
|
|
5
6
|
import { getAgent, listAgents, safeAgentSlug, agentsRoot } from "./agents.js";
|
|
6
7
|
import { chatAwaitingApproval, createChat, postMessage } from "./chat/sessions.js";
|
|
7
8
|
import { pushNotification } from "./notifications.js";
|
|
@@ -166,6 +167,30 @@ async function writeState(state) {
|
|
|
166
167
|
await fs.mkdir(getOutputDir(), { recursive: true });
|
|
167
168
|
await fs.writeFile(stateFile(), JSON.stringify(state, null, 2));
|
|
168
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Whether a routine's prompt names a workflow: its slug or its name as a whole word
|
|
172
|
+
* ("Run the daily-ceo-report workflow", "Run helpdesk workflow"). Routines are an
|
|
173
|
+
* agent's prompts, so this is how a workflow learns it runs on a schedule.
|
|
174
|
+
*/
|
|
175
|
+
export function mentionsWorkflow(prompt, w) {
|
|
176
|
+
const text = prompt.toLowerCase();
|
|
177
|
+
return [w.slug, w.name].some((n) => {
|
|
178
|
+
if (!n?.trim())
|
|
179
|
+
return false;
|
|
180
|
+
const esc = n.trim().toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
181
|
+
return new RegExp(`(^|[^a-z0-9_-])${esc}($|[^a-z0-9_-])`).test(text);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
/** Every agent's routines with their run state, each with the workflows its prompt names (GET /api/routines). */
|
|
185
|
+
export async function allRoutines(workflows) {
|
|
186
|
+
const agents = (await listAgents()).filter((a) => !a.archived);
|
|
187
|
+
const per = await Promise.all(agents.map(async (a) => (await routineStatuses(a.slug).catch(() => [])).map((r) => ({
|
|
188
|
+
...r,
|
|
189
|
+
agent: { slug: a.slug, name: a.name, emoji: a.emoji },
|
|
190
|
+
workflows: workflows.filter((w) => mentionsWorkflow(r.prompt, w)).map((w) => w.slug),
|
|
191
|
+
}))));
|
|
192
|
+
return per.flat();
|
|
193
|
+
}
|
|
169
194
|
export async function routineStatuses(slug) {
|
|
170
195
|
const [routines, state] = await Promise.all([listRoutines(slug), readState()]);
|
|
171
196
|
return routines.map((r) => {
|
|
@@ -227,7 +252,8 @@ export async function runRoutineNow(slug, id) {
|
|
|
227
252
|
}
|
|
228
253
|
}
|
|
229
254
|
/* ---------- scheduler ---------- */
|
|
230
|
-
|
|
255
|
+
/** The scheduler's timer, one per workspace. */
|
|
256
|
+
const scheduler = perWorkspace(() => ({ timer: null }));
|
|
231
257
|
async function tick() {
|
|
232
258
|
const now = new Date();
|
|
233
259
|
const p = (n) => String(n).padStart(2, "0");
|
|
@@ -271,10 +297,11 @@ async function tick() {
|
|
|
271
297
|
if (dirty)
|
|
272
298
|
await writeState(state);
|
|
273
299
|
}
|
|
274
|
-
/** Start the
|
|
300
|
+
/** Start the current workspace's scheduler; safe to call more than once. Ticks run in that workspace. */
|
|
275
301
|
export function startRoutineScheduler() {
|
|
276
|
-
|
|
302
|
+
const s = scheduler();
|
|
303
|
+
if (s.timer)
|
|
277
304
|
return;
|
|
278
|
-
timer = setInterval(() => void tick().catch(() => { }), 20_000);
|
|
279
|
-
timer.unref?.();
|
|
305
|
+
s.timer = setInterval(() => void tick().catch(() => { }), 20_000);
|
|
306
|
+
s.timer.unref?.();
|
|
280
307
|
}
|
package/dist/inspector/runner.js
CHANGED
|
@@ -2,6 +2,7 @@ import { spawn, spawnSync } from "node:child_process";
|
|
|
2
2
|
import { mkdirSync, openSync, closeSync, writeFileSync } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { getProjectRoot } from "./context.js";
|
|
5
|
+
import { perWorkspace } from "./workspace.js";
|
|
5
6
|
import { RUN_ID_RE, getRun, safeRunDir } from "./runs.js";
|
|
6
7
|
import { pushNotification } from "./notifications.js";
|
|
7
8
|
import { newRunId } from "../workflow.js";
|
|
@@ -29,7 +30,7 @@ export function dockerStatus() {
|
|
|
29
30
|
return { available: true, image: image.status === 0 };
|
|
30
31
|
}
|
|
31
32
|
/** Launchers this inspector started and that have not exited yet, by run id. */
|
|
32
|
-
const
|
|
33
|
+
const launchers = perWorkspace(() => new Map());
|
|
33
34
|
export function triggerRun(opts) {
|
|
34
35
|
if (opts.sandbox) {
|
|
35
36
|
const docker = dockerStatus();
|
|
@@ -57,7 +58,7 @@ export function triggerRun(opts) {
|
|
|
57
58
|
});
|
|
58
59
|
child.unref();
|
|
59
60
|
closeSync(log);
|
|
60
|
-
|
|
61
|
+
launchers().set(runId, child);
|
|
61
62
|
// Detached, but as long as the inspector lives it still hears the exit:
|
|
62
63
|
// that is when the run's outcome goes to the bell (read from the trace,
|
|
63
64
|
// the exit code alone does not say whether a gate blocked). The exit
|
|
@@ -65,7 +66,7 @@ export function triggerRun(opts) {
|
|
|
65
66
|
// framework wrote a trace (missing env var, npx failure) shows as failed
|
|
66
67
|
// instead of running until the stale timeout.
|
|
67
68
|
child.on("exit", (code, signal) => {
|
|
68
|
-
|
|
69
|
+
launchers().delete(runId);
|
|
69
70
|
try {
|
|
70
71
|
writeFileSync(path.join(runDir, "trigger.json"), JSON.stringify({ exitCode: code ?? 1, signal, finishedAt: new Date().toISOString() }, null, 2) + "\n");
|
|
71
72
|
}
|
|
@@ -93,7 +94,7 @@ export function stopRun(runId) {
|
|
|
93
94
|
return false;
|
|
94
95
|
// A local run this inspector launched: the launcher is detached into its
|
|
95
96
|
// own process group, so the negative pid ends npx and the framework under it.
|
|
96
|
-
const child =
|
|
97
|
+
const child = launchers().get(runId);
|
|
97
98
|
if (child?.pid && child.exitCode == null) {
|
|
98
99
|
try {
|
|
99
100
|
process.kill(-child.pid, "SIGTERM");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
-
|
|
3
|
-
export
|
|
2
|
+
import { RESTART_EXIT_CODE } from "./version.js";
|
|
3
|
+
export { RESTART_EXIT_CODE };
|
|
4
4
|
export interface InspectorOptions {
|
|
5
5
|
outputDir: string;
|
|
6
6
|
staticDir: string;
|