@netnodeag/kraftwerk 0.81.0 → 0.82.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/README.md +52 -0
- package/dist/cli/boards.d.ts +2 -0
- package/dist/cli/boards.js +486 -0
- package/dist/cli/init.js +1 -0
- package/dist/cli/kraftwerk.js +3 -0
- package/dist/client/routes.d.ts +14 -0
- package/dist/client/routes.js +14 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +24 -1
- package/dist/core/attention.d.ts +2 -2
- package/dist/core/attention.js +22 -2
- package/dist/core/board-wake.d.ts +21 -0
- package/dist/core/board-wake.js +221 -0
- package/dist/core/boards.d.ts +256 -0
- package/dist/core/boards.js +936 -0
- package/dist/core/chat/acp.d.ts +1 -1
- package/dist/core/chat/acp.js +2 -1
- package/dist/core/chat/backend.d.ts +11 -0
- package/dist/core/chat/backend.js +9 -1
- package/dist/core/chat/context.js +16 -4
- package/dist/core/chat/pi.d.ts +1 -1
- package/dist/core/chat/pi.js +2 -1
- package/dist/core/chat/seats.js +3 -2
- package/dist/core/chat/sessions.d.ts +2 -0
- package/dist/core/chat/sessions.js +1 -0
- package/dist/core/chat/state.js +1 -1
- package/dist/core/chat/types.d.ts +11 -1
- package/dist/core/files.d.ts +1 -1
- package/dist/core/files.js +19 -1
- package/dist/core/git.js +3 -1
- package/dist/core/notifications.d.ts +1 -1
- package/dist/core/refs.d.ts +1 -1
- package/dist/core/refs.js +25 -1
- package/dist/core/search.d.ts +12 -0
- package/dist/core/search.js +19 -0
- package/dist/core/settings.d.ts +2 -0
- package/dist/core/settings.js +13 -1
- package/dist/core/trash.d.ts +1 -1
- package/dist/server/api/boards.d.ts +105 -0
- package/dist/server/api/boards.js +160 -0
- package/dist/server/api/chats.d.ts +2 -0
- package/dist/server/api/chats.js +9 -3
- package/dist/server/api/index.d.ts +105 -2
- package/dist/server/api/index.js +2 -0
- package/dist/server/api/workspace.d.ts +5 -0
- package/dist/server/api/workspace.js +2 -0
- package/dist/server/push.js +1 -1
- package/dist/server/server.js +3 -0
- package/package.json +1 -1
- package/schema/kraftwerk.schema.json +39 -0
- package/web/dist/assets/{dist-Dx3aajX1.js → dist-BBFUIftm.js} +1 -1
- package/web/dist/assets/{dist-ENZYo2jZ.js → dist-Bie1ebP9.js} +1 -1
- package/web/dist/assets/{dist-BiU4F98O.js → dist-BuwdjX6z.js} +1 -1
- package/web/dist/assets/{dist-8IOFzZx-.js → dist-C0ve4Tka.js} +1 -1
- package/web/dist/assets/{dist-FfKYtQxT.js → dist-C16bXf8c.js} +1 -1
- package/web/dist/assets/{dist-cewmjxFi.js → dist-CDUZdAAS.js} +1 -1
- package/web/dist/assets/{dist-BK0VhAPQ.js → dist-CH73jsgO.js} +1 -1
- package/web/dist/assets/{dist-Dy1SvhKC.js → dist-C_rJjRj7.js} +1 -1
- package/web/dist/assets/dist-DYICNp09.js +1 -0
- package/web/dist/assets/{dist-AhnSw4hy.js → dist-DtUB2Uu2.js} +1 -1
- package/web/dist/assets/{dist-Ba6WSVOB.js → dist-DvkrCSGL.js} +1 -1
- package/web/dist/assets/{editor-CaRa1NMC.js → editor-3z92i0tV.js} +3 -3
- package/web/dist/assets/{editor-CZMrhTTL.css → editor-C0_Snnzk.css} +1 -1
- package/web/dist/assets/index-DRhNELK_.js +232 -0
- package/web/dist/assets/index-unGEux3Y.css +2 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/dist-lgUK2Fjt.js +0 -1
- package/web/dist/assets/index-Dpzux3OC.js +0 -231
- package/web/dist/assets/index-Dvt-vE1h.css +0 -2
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { appendTaskLog, assignTask, createBoard, createTask, deleteBoard, deleteTask, ensureDefaultBoard, getBoard, getTask, listAllBoards, listBoards, moveTask, openBoards, pickTask, saveBoard, saveTask, } from "../../core/boards.js";
|
|
3
|
+
import { taskSessions } from "../../core/board-wake.js";
|
|
4
|
+
import { fail, reply, route, statusByMessage } from "./router.js";
|
|
5
|
+
/**
|
|
6
|
+
* Boards and tasks over the API. A board is addressed by its slug and, for
|
|
7
|
+
* a project's board, `?project=<slug>`; a task by its folder name or its
|
|
8
|
+
* bare number. Writes that change a task's column or assignee are logged
|
|
9
|
+
* on the task and may wake the assignee (core/board-wake.ts).
|
|
10
|
+
*/
|
|
11
|
+
export const boardErrors = statusByMessage(/^no (board|task|project)/);
|
|
12
|
+
const Columns = z.array(z.union([z.string(), z.object({ slug: z.string(), label: z.string().optional(), limit: z.number().optional(), done: z.boolean().optional(), review: z.boolean().optional() })]));
|
|
13
|
+
const BoardFields = z.object({
|
|
14
|
+
title: z.string().optional(),
|
|
15
|
+
columns: Columns.optional(),
|
|
16
|
+
agent: z.string().nullable().optional(),
|
|
17
|
+
staleAfter: z.string().nullable().optional(),
|
|
18
|
+
notes: z.string().optional(),
|
|
19
|
+
});
|
|
20
|
+
const TaskFields = z.object({
|
|
21
|
+
title: z.string().optional(),
|
|
22
|
+
status: z.string().optional(),
|
|
23
|
+
order: z.number().optional(),
|
|
24
|
+
assignee: z.string().nullable().optional(),
|
|
25
|
+
due: z.string().nullable().optional(),
|
|
26
|
+
tags: z.array(z.string()).optional(),
|
|
27
|
+
refs: z.array(z.string()).optional(),
|
|
28
|
+
notes: z.string().optional(),
|
|
29
|
+
/** Who writes (human:user by default) and a line for the log. */
|
|
30
|
+
actor: z.string().optional(),
|
|
31
|
+
message: z.string().optional(),
|
|
32
|
+
});
|
|
33
|
+
const refOf = (c) => {
|
|
34
|
+
const project = c.query.get("project");
|
|
35
|
+
return { ...(project ? { project } : {}), board: c.params.board };
|
|
36
|
+
};
|
|
37
|
+
const requireOn = async () => {
|
|
38
|
+
if ((await openBoards()).off)
|
|
39
|
+
fail(409, "boards are off");
|
|
40
|
+
};
|
|
41
|
+
export const boardRoutes = [
|
|
42
|
+
route({ name: "boards.list", method: "GET", path: "/api/boards", summary: "the boards: a project's with ?project=, every board with ?all=1, else the workspace's" }, async (c) => {
|
|
43
|
+
if (c.query.get("all") === "1") {
|
|
44
|
+
const opened = await openBoards();
|
|
45
|
+
return { enabled: !opened.off && !!opened.root, ...(opened.root ? { root: opened.root } : {}), boards: await listAllBoards(), ...(opened.error ? { error: opened.error } : {}) };
|
|
46
|
+
}
|
|
47
|
+
return listBoards(c.query.get("project") ?? undefined);
|
|
48
|
+
}),
|
|
49
|
+
route({
|
|
50
|
+
name: "boards.create",
|
|
51
|
+
method: "POST",
|
|
52
|
+
path: "/api/boards",
|
|
53
|
+
summary: "create a board {title, slug?, project?, columns?, agent?, notes?}",
|
|
54
|
+
errors: boardErrors,
|
|
55
|
+
body: z.object({ title: z.string(), slug: z.string().optional(), project: z.string().optional(), columns: Columns.optional(), agent: z.string().optional(), notes: z.string().optional() }),
|
|
56
|
+
}, async (c) => {
|
|
57
|
+
await requireOn();
|
|
58
|
+
return reply(201, await createBoard(await c.body()));
|
|
59
|
+
}),
|
|
60
|
+
route({ name: "boards.get", method: "GET", path: "/api/boards/:board", summary: "columns, rules, counts and every task (?project=)", errors: boardErrors }, async (c) => {
|
|
61
|
+
await requireOn();
|
|
62
|
+
const ref = refOf(c);
|
|
63
|
+
return (await getBoard(ref)) ?? fail(404, `no board "${ref.board}"`);
|
|
64
|
+
}),
|
|
65
|
+
route({ name: "boards.save", method: "PUT", path: "/api/boards/:board", summary: "save title, columns, default agent, stale_after, rules", errors: boardErrors, body: BoardFields }, async (c) => {
|
|
66
|
+
await requireOn();
|
|
67
|
+
return saveBoard(refOf(c), (await c.body()));
|
|
68
|
+
}),
|
|
69
|
+
route({ name: "boards.delete", method: "DELETE", path: "/api/boards/:board", summary: "move the board, tasks and all, to the trash", errors: boardErrors }, async (c) => {
|
|
70
|
+
await requireOn();
|
|
71
|
+
await deleteBoard(refOf(c));
|
|
72
|
+
return { ok: true };
|
|
73
|
+
}),
|
|
74
|
+
route({
|
|
75
|
+
name: "boards.ensure",
|
|
76
|
+
method: "POST",
|
|
77
|
+
path: "/api/boards/default",
|
|
78
|
+
summary: "the project's (or workspace's) default board `tasks`, created on first use {project?}",
|
|
79
|
+
errors: boardErrors,
|
|
80
|
+
body: z.object({ project: z.string().optional() }),
|
|
81
|
+
}, async (c) => {
|
|
82
|
+
await requireOn();
|
|
83
|
+
const ref = await ensureDefaultBoard((await c.body()).project);
|
|
84
|
+
return (await getBoard(ref));
|
|
85
|
+
}),
|
|
86
|
+
route({
|
|
87
|
+
name: "tasks.create",
|
|
88
|
+
method: "POST",
|
|
89
|
+
path: "/api/boards/:board/tasks",
|
|
90
|
+
summary: "create a task {title, status?, assignee?, due?, tags?, refs?, notes?, actor?, message?} (an assigned agent in a work column is woken)",
|
|
91
|
+
errors: boardErrors,
|
|
92
|
+
body: TaskFields.extend({ title: z.string() }),
|
|
93
|
+
}, async (c) => {
|
|
94
|
+
await requireOn();
|
|
95
|
+
return reply(201, await createTask(refOf(c), (await c.body())));
|
|
96
|
+
}),
|
|
97
|
+
route({ name: "tasks.get", method: "GET", path: "/api/boards/:board/tasks/:task", summary: "the task: fields, markdown, files, log and the sessions that worked on it (:task = folder name or number)", errors: boardErrors }, async (c) => {
|
|
98
|
+
await requireOn();
|
|
99
|
+
const task = (await getTask(refOf(c), c.params.task)) ?? fail(404, `no task "${c.params.task}"`);
|
|
100
|
+
return { ...task, sessions: await taskSessions(task.ref) };
|
|
101
|
+
}),
|
|
102
|
+
route({ name: "tasks.save", method: "PUT", path: "/api/boards/:board/tasks/:task", summary: "save fields and markdown; a status or assignee change is logged (message?)", errors: boardErrors, body: TaskFields }, async (c) => {
|
|
103
|
+
await requireOn();
|
|
104
|
+
const { actor, message, ...fields } = await c.body();
|
|
105
|
+
return saveTask(refOf(c), c.params.task, fields, { actor, message });
|
|
106
|
+
}),
|
|
107
|
+
route({
|
|
108
|
+
name: "tasks.move",
|
|
109
|
+
method: "POST",
|
|
110
|
+
path: "/api/boards/:board/tasks/:task/move",
|
|
111
|
+
summary: "put the task into a column {status, before?, order?, message?, actor?}",
|
|
112
|
+
errors: boardErrors,
|
|
113
|
+
body: z.object({ status: z.string(), before: z.string().optional(), order: z.number().optional(), message: z.string().optional(), actor: z.string().optional() }),
|
|
114
|
+
}, async (c) => {
|
|
115
|
+
await requireOn();
|
|
116
|
+
const { status, ...opts } = await c.body();
|
|
117
|
+
return moveTask(refOf(c), c.params.task, status, opts);
|
|
118
|
+
}),
|
|
119
|
+
route({
|
|
120
|
+
name: "tasks.assign",
|
|
121
|
+
method: "POST",
|
|
122
|
+
path: "/api/boards/:board/tasks/:task/assign",
|
|
123
|
+
summary: 'give the task to an agent {to, message?, actor?}; to = null or "human" hands it back to the review column',
|
|
124
|
+
errors: boardErrors,
|
|
125
|
+
body: z.object({ to: z.string().nullable(), message: z.string().optional(), actor: z.string().optional() }),
|
|
126
|
+
}, async (c) => {
|
|
127
|
+
await requireOn();
|
|
128
|
+
const { to, ...opts } = await c.body();
|
|
129
|
+
return assignTask(refOf(c), c.params.task, to, opts);
|
|
130
|
+
}),
|
|
131
|
+
route({
|
|
132
|
+
name: "tasks.log",
|
|
133
|
+
method: "POST",
|
|
134
|
+
path: "/api/boards/:board/tasks/:task/log",
|
|
135
|
+
summary: "append a stamped line {entry, actor?} to the task's log",
|
|
136
|
+
errors: boardErrors,
|
|
137
|
+
body: z.object({ entry: z.string(), actor: z.string().optional() }),
|
|
138
|
+
}, async (c) => {
|
|
139
|
+
await requireOn();
|
|
140
|
+
const { entry, actor } = await c.body();
|
|
141
|
+
return appendTaskLog(refOf(c), c.params.task, entry, actor);
|
|
142
|
+
}),
|
|
143
|
+
route({
|
|
144
|
+
name: "tasks.pick",
|
|
145
|
+
method: "POST",
|
|
146
|
+
path: "/api/boards/:board/pick",
|
|
147
|
+
summary: "an agent takes the next task of the pick column {agent, column?}; null when there is none",
|
|
148
|
+
errors: boardErrors,
|
|
149
|
+
body: z.object({ agent: z.string(), column: z.string().optional() }),
|
|
150
|
+
}, async (c) => {
|
|
151
|
+
await requireOn();
|
|
152
|
+
const { agent, column } = await c.body();
|
|
153
|
+
return { task: await pickTask(refOf(c), agent, { column }) };
|
|
154
|
+
}),
|
|
155
|
+
route({ name: "tasks.delete", method: "DELETE", path: "/api/boards/:board/tasks/:task", summary: "move the task folder to the trash", errors: boardErrors }, async (c) => {
|
|
156
|
+
await requireOn();
|
|
157
|
+
await deleteTask(refOf(c), c.params.task);
|
|
158
|
+
return { ok: true };
|
|
159
|
+
}),
|
|
160
|
+
];
|
|
@@ -31,9 +31,11 @@ export declare const chatRoutes: (import("./router.js").Route<"agents.sessions",
|
|
|
31
31
|
runId: z.ZodOptional<z.ZodString>;
|
|
32
32
|
bundle: z.ZodOptional<z.ZodString>;
|
|
33
33
|
slug: z.ZodOptional<z.ZodString>;
|
|
34
|
+
task: z.ZodOptional<z.ZodString>;
|
|
34
35
|
}, z.core.$strip>>;
|
|
35
36
|
resume: z.ZodOptional<z.ZodString>;
|
|
36
37
|
project: z.ZodOptional<z.ZodString>;
|
|
38
|
+
task: z.ZodOptional<z.ZodString>;
|
|
37
39
|
}, z.core.$strip>> | import("./router.js").Route<"chats.delete", import("./router.js").Reply<{
|
|
38
40
|
error?: string;
|
|
39
41
|
}>, "/api/chats/:id", undefined> | import("./router.js").Route<"chats.elicitation", import("./router.js").Reply<{
|
package/dist/server/api/chats.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getAgent } from "../../core/agents.js";
|
|
|
5
5
|
import { answerElicitation, cancelChat, createChat, deleteChat, forkChat, getChat, listAgentSessionsFor, listChats, localPerson, postMessage, renameChat, resetSession, resolvePermission, setChatConfig, setChatProject, setChatVibeable, steerChat, stopChatTask, subscribeChat, } from "../../core/chat/sessions.js";
|
|
6
6
|
import { attachmentPath, readMeta, saveAttachment } from "../../core/chat/store.js";
|
|
7
7
|
import { remoteCaller } from "../trust.js";
|
|
8
|
+
import { findTask } from "../../core/boards.js";
|
|
8
9
|
import { getProject } from "../../core/projects.js";
|
|
9
10
|
import { MIME } from "./mime.js";
|
|
10
11
|
import { projectErrors } from "./projects.js";
|
|
@@ -24,10 +25,12 @@ const Said = z.object({
|
|
|
24
25
|
/** Where a new chat runs; unknown kinds fall back to a general chat. */
|
|
25
26
|
const NewChat = z.object({
|
|
26
27
|
agent: z.string().optional(),
|
|
27
|
-
scope: z.object({ kind: z.string(), runId: z.string().optional(), bundle: z.string().optional(), slug: z.string().optional() }).optional(),
|
|
28
|
+
scope: z.object({ kind: z.string(), runId: z.string().optional(), bundle: z.string().optional(), slug: z.string().optional(), task: z.string().optional() }).optional(),
|
|
28
29
|
resume: z.string().optional(),
|
|
29
30
|
/** An agent's chat started in a project: listed with the project (meta.project). */
|
|
30
31
|
project: z.string().optional(),
|
|
32
|
+
/** The board task the chat works on (<project>/<board>/<task> or <board>/<task>): listed on the task's page (meta.task). */
|
|
33
|
+
task: z.string().optional(),
|
|
31
34
|
});
|
|
32
35
|
/** An action on a running chat: `{ok}` when it went through, 409 with the reason when the chat refused. */
|
|
33
36
|
const act = (name, path, summary, body, run) => route({ name, method: "POST", path, summary, errors: 400, body }, async (c) => {
|
|
@@ -47,7 +50,8 @@ async function scopeOf(body) {
|
|
|
47
50
|
const def = await getAgent(s.slug).catch(() => null);
|
|
48
51
|
if (!def)
|
|
49
52
|
fail(404, "agent not found");
|
|
50
|
-
|
|
53
|
+
// `task`: the session is opened for a board task (<project>/<board>/<task>): its context carries the task, and it runs unattended like a routine's.
|
|
54
|
+
return { agent: def.harness, scope: { kind: "agent", slug: def.slug, ...(s.task ? { task: s.task } : {}) } };
|
|
51
55
|
}
|
|
52
56
|
if (s?.kind === "project" && s.slug) {
|
|
53
57
|
// A project chat needs its folder; a missing one is a 404 here, not a chat that apologizes.
|
|
@@ -109,7 +113,7 @@ export const chatRoutes = [
|
|
|
109
113
|
const chats = await listChats();
|
|
110
114
|
return { chats: remoteCaller(c.trust) ? chats.filter((x) => !x.private) : chats };
|
|
111
115
|
}),
|
|
112
|
-
route({ name: "chats.create", method: "POST", path: "/api/chats", summary: "start a chat {agent, scope, resume?, project?}", body: NewChat }, async (c) => {
|
|
116
|
+
route({ name: "chats.create", method: "POST", path: "/api/chats", summary: "start a chat {agent, scope, resume?, project?, task?}", errors: 404, body: NewChat }, async (c) => {
|
|
113
117
|
// `resume` continues one of the agent's own sessions (see agents.sessions).
|
|
114
118
|
const body = await c.body();
|
|
115
119
|
const { agent, scope } = await scopeOf(body);
|
|
@@ -118,12 +122,14 @@ export const chatRoutes = [
|
|
|
118
122
|
if (body.project && scope.kind !== "agent")
|
|
119
123
|
fail(400, "only an agent's chat takes a project");
|
|
120
124
|
const project = body.project ? await projectSlug(body.project) : undefined;
|
|
125
|
+
const task = body.task ? ((await findTask(body.task).catch(() => null)) ?? fail(404, `no task "${body.task}"`)).ref : undefined;
|
|
121
126
|
try {
|
|
122
127
|
return await createChat({
|
|
123
128
|
agent: agent,
|
|
124
129
|
scope,
|
|
125
130
|
author: await personOf(c.trust),
|
|
126
131
|
...(project ? { project } : {}),
|
|
132
|
+
...(task ? { task } : {}),
|
|
127
133
|
...(body.resume ? { resume: body.resume } : {}),
|
|
128
134
|
});
|
|
129
135
|
}
|
|
@@ -104,7 +104,36 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
104
104
|
content: string;
|
|
105
105
|
}, "/api/agents/:slug/skills/:name", undefined> | Route<"agents.skills", {
|
|
106
106
|
skills: import("../../core/skills.js").SkillInfo[];
|
|
107
|
-
}, "/api/agents/:slug/skills", undefined> | Route<"agents.status", Record<string, import("../../core/agent-status.js").AgentStatus>, "/api/agent-status", undefined> | Route<"attention.list", import("../../core/attention.js").AttentionView, "/api/attention", undefined> | Route<"
|
|
107
|
+
}, "/api/agents/:slug/skills", undefined> | Route<"agents.status", Record<string, import("../../core/agent-status.js").AgentStatus>, "/api/agent-status", undefined> | Route<"attention.list", import("../../core/attention.js").AttentionView, "/api/attention", undefined> | Route<"boards.create", import("./router.js").Reply<import("../../core/boards.js").BoardDetail>, "/api/boards", z.ZodObject<{
|
|
108
|
+
title: z.ZodString;
|
|
109
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
110
|
+
project: z.ZodOptional<z.ZodString>;
|
|
111
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
112
|
+
slug: z.ZodString;
|
|
113
|
+
label: z.ZodOptional<z.ZodString>;
|
|
114
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
115
|
+
done: z.ZodOptional<z.ZodBoolean>;
|
|
116
|
+
review: z.ZodOptional<z.ZodBoolean>;
|
|
117
|
+
}, z.core.$strip>]>>>;
|
|
118
|
+
agent: z.ZodOptional<z.ZodString>;
|
|
119
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
120
|
+
}, z.core.$strip>> | Route<"boards.delete", {
|
|
121
|
+
ok: boolean;
|
|
122
|
+
}, "/api/boards/:board", undefined> | Route<"boards.ensure", import("../../core/boards.js").BoardDetail, "/api/boards/default", z.ZodObject<{
|
|
123
|
+
project: z.ZodOptional<z.ZodString>;
|
|
124
|
+
}, z.core.$strip>> | Route<"boards.get", import("../../core/boards.js").BoardDetail, "/api/boards/:board", undefined> | Route<"boards.list", import("../../core/boards.js").BoardsView, "/api/boards", undefined> | Route<"boards.save", import("../../core/boards.js").BoardDetail, "/api/boards/:board", z.ZodObject<{
|
|
125
|
+
title: z.ZodOptional<z.ZodString>;
|
|
126
|
+
columns: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
|
|
127
|
+
slug: z.ZodString;
|
|
128
|
+
label: z.ZodOptional<z.ZodString>;
|
|
129
|
+
limit: z.ZodOptional<z.ZodNumber>;
|
|
130
|
+
done: z.ZodOptional<z.ZodBoolean>;
|
|
131
|
+
review: z.ZodOptional<z.ZodBoolean>;
|
|
132
|
+
}, z.core.$strip>]>>>;
|
|
133
|
+
agent: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
134
|
+
staleAfter: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
135
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
136
|
+
}, z.core.$strip>> | Route<"channels.create", {
|
|
108
137
|
slug: string;
|
|
109
138
|
name: string;
|
|
110
139
|
purpose?: string;
|
|
@@ -220,9 +249,11 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
220
249
|
runId: z.ZodOptional<z.ZodString>;
|
|
221
250
|
bundle: z.ZodOptional<z.ZodString>;
|
|
222
251
|
slug: z.ZodOptional<z.ZodString>;
|
|
252
|
+
task: z.ZodOptional<z.ZodString>;
|
|
223
253
|
}, z.core.$strip>>;
|
|
224
254
|
resume: z.ZodOptional<z.ZodString>;
|
|
225
255
|
project: z.ZodOptional<z.ZodString>;
|
|
256
|
+
task: z.ZodOptional<z.ZodString>;
|
|
226
257
|
}, z.core.$strip>> | Route<"chats.delete", import("./router.js").Reply<{
|
|
227
258
|
error?: string;
|
|
228
259
|
}>, "/api/chats/:id", undefined> | Route<"chats.elicitation", import("./router.js").Reply<{
|
|
@@ -549,6 +580,7 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
549
580
|
contacts: boolean;
|
|
550
581
|
places: boolean;
|
|
551
582
|
events: boolean;
|
|
583
|
+
boards: boolean;
|
|
552
584
|
publicUrl: string;
|
|
553
585
|
cloud: {
|
|
554
586
|
included: boolean;
|
|
@@ -725,10 +757,81 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
725
757
|
enabled: z.ZodBoolean;
|
|
726
758
|
root: z.ZodOptional<z.ZodString>;
|
|
727
759
|
}, z.core.$strip>>;
|
|
760
|
+
boards: z.ZodOptional<z.ZodObject<{
|
|
761
|
+
enabled: z.ZodBoolean;
|
|
762
|
+
root: z.ZodOptional<z.ZodString>;
|
|
763
|
+
}, z.core.$strip>>;
|
|
728
764
|
}, z.core.$loose>> | Route<"skills.get", import("../../core/skills.js").SkillDetail, "/api/skills/:name", undefined> | Route<"skills.list", {
|
|
729
765
|
root: string;
|
|
730
766
|
skills: import("../../core/skills.js").SkillInfo[];
|
|
731
|
-
}, "/api/skills", undefined> | Route<"
|
|
767
|
+
}, "/api/skills", undefined> | Route<"tasks.assign", import("../../core/boards.js").TaskDetail, "/api/boards/:board/tasks/:task/assign", z.ZodObject<{
|
|
768
|
+
to: z.ZodNullable<z.ZodString>;
|
|
769
|
+
message: z.ZodOptional<z.ZodString>;
|
|
770
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
771
|
+
}, z.core.$strip>> | Route<"tasks.create", import("./router.js").Reply<import("../../core/boards.js").TaskDetail>, "/api/boards/:board/tasks", z.ZodObject<{
|
|
772
|
+
status: z.ZodOptional<z.ZodString>;
|
|
773
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
774
|
+
assignee: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
775
|
+
due: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
776
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
777
|
+
refs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
778
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
779
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
780
|
+
message: z.ZodOptional<z.ZodString>;
|
|
781
|
+
title: z.ZodString;
|
|
782
|
+
}, z.core.$strip>> | Route<"tasks.delete", {
|
|
783
|
+
ok: boolean;
|
|
784
|
+
}, "/api/boards/:board/tasks/:task", undefined> | Route<"tasks.get", {
|
|
785
|
+
slug: string;
|
|
786
|
+
number: number;
|
|
787
|
+
title: string;
|
|
788
|
+
status: string;
|
|
789
|
+
order: number;
|
|
790
|
+
assignee?: string;
|
|
791
|
+
due?: string;
|
|
792
|
+
created: string;
|
|
793
|
+
tags: string[];
|
|
794
|
+
refs: string[];
|
|
795
|
+
extra?: Record<string, unknown>;
|
|
796
|
+
ref: string;
|
|
797
|
+
path: string;
|
|
798
|
+
updatedAt?: string;
|
|
799
|
+
configError?: string;
|
|
800
|
+
files: number;
|
|
801
|
+
lastLog?: string;
|
|
802
|
+
stale?: boolean;
|
|
803
|
+
notes: string;
|
|
804
|
+
log: string;
|
|
805
|
+
fileList: string[];
|
|
806
|
+
board: string;
|
|
807
|
+
project?: string;
|
|
808
|
+
sessions: import("../../core/board-wake.js").TaskSession[];
|
|
809
|
+
}, "/api/boards/:board/tasks/:task", undefined> | Route<"tasks.log", import("../../core/boards.js").TaskDetail, "/api/boards/:board/tasks/:task/log", z.ZodObject<{
|
|
810
|
+
entry: z.ZodString;
|
|
811
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
812
|
+
}, z.core.$strip>> | Route<"tasks.move", import("../../core/boards.js").TaskDetail, "/api/boards/:board/tasks/:task/move", z.ZodObject<{
|
|
813
|
+
status: z.ZodString;
|
|
814
|
+
before: z.ZodOptional<z.ZodString>;
|
|
815
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
816
|
+
message: z.ZodOptional<z.ZodString>;
|
|
817
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
818
|
+
}, z.core.$strip>> | Route<"tasks.pick", {
|
|
819
|
+
task: import("../../core/boards.js").TaskDetail | null;
|
|
820
|
+
}, "/api/boards/:board/pick", z.ZodObject<{
|
|
821
|
+
agent: z.ZodString;
|
|
822
|
+
column: z.ZodOptional<z.ZodString>;
|
|
823
|
+
}, z.core.$strip>> | Route<"tasks.save", import("../../core/boards.js").TaskDetail, "/api/boards/:board/tasks/:task", z.ZodObject<{
|
|
824
|
+
title: z.ZodOptional<z.ZodString>;
|
|
825
|
+
status: z.ZodOptional<z.ZodString>;
|
|
826
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
827
|
+
assignee: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
828
|
+
due: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
829
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
830
|
+
refs: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
831
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
832
|
+
actor: z.ZodOptional<z.ZodString>;
|
|
833
|
+
message: z.ZodOptional<z.ZodString>;
|
|
834
|
+
}, z.core.$strip>> | Route<"temp.clean", {
|
|
732
835
|
ok: boolean;
|
|
733
836
|
removed: string[];
|
|
734
837
|
bytes: number;
|
package/dist/server/api/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { agentRoutes } from "./agents.js";
|
|
3
3
|
import { attentionRoutes } from "./attention.js";
|
|
4
|
+
import { boardRoutes } from "./boards.js";
|
|
4
5
|
import { channelRoutes } from "./channels.js";
|
|
5
6
|
import { chatRoutes } from "./chats.js";
|
|
6
7
|
import { cloudRoutes } from "./cloud.js";
|
|
@@ -31,6 +32,7 @@ const domainRoutes = [
|
|
|
31
32
|
...chatRoutes,
|
|
32
33
|
...channelRoutes,
|
|
33
34
|
...projectRoutes,
|
|
35
|
+
...boardRoutes,
|
|
34
36
|
...contactRoutes,
|
|
35
37
|
...placeRoutes,
|
|
36
38
|
...eventRoutes,
|
|
@@ -36,6 +36,7 @@ export declare const workspaceRoutes: (import("./router.js").Route<"identity.get
|
|
|
36
36
|
contacts: boolean;
|
|
37
37
|
places: boolean;
|
|
38
38
|
events: boolean;
|
|
39
|
+
boards: boolean;
|
|
39
40
|
/** @deprecated Always "": the Cloudflare Tunnel that set it was removed in 0.65. Removed with protocol 2. */
|
|
40
41
|
publicUrl: string;
|
|
41
42
|
cloud: {
|
|
@@ -101,6 +102,10 @@ export declare const workspaceRoutes: (import("./router.js").Route<"identity.get
|
|
|
101
102
|
enabled: z.ZodBoolean;
|
|
102
103
|
root: z.ZodOptional<z.ZodString>;
|
|
103
104
|
}, z.core.$strip>>;
|
|
105
|
+
boards: z.ZodOptional<z.ZodObject<{
|
|
106
|
+
enabled: z.ZodBoolean;
|
|
107
|
+
root: z.ZodOptional<z.ZodString>;
|
|
108
|
+
}, z.core.$strip>>;
|
|
104
109
|
}, z.core.$loose>> | import("./router.js").Route<"update.check", import("./router.js").Reply<{
|
|
105
110
|
name: string;
|
|
106
111
|
current: string;
|
|
@@ -36,6 +36,7 @@ const SettingsFields = z.looseObject({
|
|
|
36
36
|
contacts: RootBlock.optional(),
|
|
37
37
|
places: RootBlock.optional(),
|
|
38
38
|
events: RootBlock.optional(),
|
|
39
|
+
boards: RootBlock.optional(),
|
|
39
40
|
});
|
|
40
41
|
/**
|
|
41
42
|
* Mark the workspaces the hub answering this request serves — or, a daemon,
|
|
@@ -111,6 +112,7 @@ export const workspaceRoutes = [
|
|
|
111
112
|
contacts: (ws?.config.contacts && ws.config.contacts.enabled !== false) === true,
|
|
112
113
|
places: (ws?.config.places && ws.config.places.enabled !== false) === true,
|
|
113
114
|
events: (ws?.config.events && ws.config.events.enabled !== false) === true,
|
|
115
|
+
boards: (ws?.config.boards && ws.config.boards.enabled !== false) === true,
|
|
114
116
|
/** @deprecated Always "": the Cloudflare Tunnel that set it was removed in 0.65. Removed with protocol 2. */
|
|
115
117
|
publicUrl: "",
|
|
116
118
|
// The machine's cloud record (only the daemon registers one); `included`: this workspace rides along (no cloud.enabled: false veto).
|
package/dist/server/push.js
CHANGED
|
@@ -11,7 +11,7 @@ export function sealPush(keyBase64, content) {
|
|
|
11
11
|
const ct = Buffer.concat([cipher.update(JSON.stringify(content), "utf8"), cipher.final()]);
|
|
12
12
|
return Buffer.concat([nonce, ct, cipher.getAuthTag()]).toString("base64");
|
|
13
13
|
}
|
|
14
|
-
const LABEL = { approval: "Approval", question: "Question", failed: "Failed", runner: "Routines" };
|
|
14
|
+
const LABEL = { approval: "Approval", question: "Question", failed: "Failed", runner: "Routines", review: "Review" };
|
|
15
15
|
export function contentOf(item, workspace) {
|
|
16
16
|
return {
|
|
17
17
|
title: `${LABEL[item.kind] ?? "Needs you"} · ${workspace.name}`,
|
package/dist/server/server.js
CHANGED
|
@@ -11,6 +11,7 @@ import { startGitSync, stopGitSync } from "../core/git.js";
|
|
|
11
11
|
import { disposeAllDevs, disposeWorkspaceDevs } from "../core/vibeables.js";
|
|
12
12
|
import { proxyUpgrade, serveVibeable } from "./preview.js";
|
|
13
13
|
import { startRoutineScheduler, stopRoutineScheduler } from "../core/routines.js";
|
|
14
|
+
import { startBoardWake, stopBoardWake } from "../core/board-wake.js";
|
|
14
15
|
import { readWorkspaceDotenv } from "../core/env.js";
|
|
15
16
|
import { closeWatches } from "./live.js";
|
|
16
17
|
import { MACHINE_PATHS, runInHub } from "./hub-context.js";
|
|
@@ -415,6 +416,7 @@ export async function startHub(opts) {
|
|
|
415
416
|
fallback ??= entry;
|
|
416
417
|
runInHub(hub, () => ws.run(() => {
|
|
417
418
|
startRoutineScheduler();
|
|
419
|
+
startBoardWake();
|
|
418
420
|
startGitSync();
|
|
419
421
|
// Best-effort, in the background: none of them may hold up (or, failing, break) the open.
|
|
420
422
|
entry.registered = registerInstance(entry.aliasPort ?? port, abs, opts.daemon ? { hub: true, slug, url: entry.url } : {}).catch(() => { });
|
|
@@ -440,6 +442,7 @@ export async function startHub(opts) {
|
|
|
440
442
|
aliasServers.delete(abs);
|
|
441
443
|
await runInHub(hub, () => entry.ws.run(async () => {
|
|
442
444
|
stopRoutineScheduler();
|
|
445
|
+
stopBoardWake();
|
|
443
446
|
stopGitSync();
|
|
444
447
|
closeWatches();
|
|
445
448
|
disposeWorkspaceBackends();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netnodeag/kraftwerk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.82.0",
|
|
4
4
|
"description": "Open-source agentic workspace for teams \u2014 where people and AI agents get work done together: agents (Claude Code, Codex, Pi), skills, shared knowledge, repeatable workflows, and deterministic verification",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -225,6 +225,45 @@
|
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
},
|
|
228
|
+
"boards": {
|
|
229
|
+
"type": [
|
|
230
|
+
"object",
|
|
231
|
+
"null"
|
|
232
|
+
],
|
|
233
|
+
"additionalProperties": false,
|
|
234
|
+
"description": "Boards: tasks as folders (task.md, log.md, files) in columns, per project (projects/<p>/boards/) and workspace-wide under the root; agents are woken when a task is assigned to them. Absent = feature off, a bare `boards:` = on with defaults.",
|
|
235
|
+
"properties": {
|
|
236
|
+
"enabled": {
|
|
237
|
+
"type": "boolean",
|
|
238
|
+
"description": "Turn the feature off while keeping the config. Default: true when this block exists"
|
|
239
|
+
},
|
|
240
|
+
"root": {
|
|
241
|
+
"type": "string",
|
|
242
|
+
"description": "Where the workspace boards live, relative to the project root. Default: kraftwerk-data/boards"
|
|
243
|
+
},
|
|
244
|
+
"columns": {
|
|
245
|
+
"type": "array",
|
|
246
|
+
"description": "Default columns for boards that name none: slugs or {slug, label, limit, done, review}. Default: draft, todo, progress, review (review: true), done (done: true)",
|
|
247
|
+
"items": {
|
|
248
|
+
"anyOf": [
|
|
249
|
+
{ "type": "string" },
|
|
250
|
+
{
|
|
251
|
+
"type": "object",
|
|
252
|
+
"additionalProperties": false,
|
|
253
|
+
"required": ["slug"],
|
|
254
|
+
"properties": {
|
|
255
|
+
"slug": { "type": "string" },
|
|
256
|
+
"label": { "type": "string" },
|
|
257
|
+
"limit": { "type": "integer", "minimum": 1 },
|
|
258
|
+
"done": { "type": "boolean" },
|
|
259
|
+
"review": { "type": "boolean" }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
]
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
228
267
|
"cloud": {
|
|
229
268
|
"type": [
|
|
230
269
|
"object",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{L as e,R as t,Ut as n,i as r,r as i,s as a,u as o}from"./dist-CYzOYk_e.js";import{n as s}from"./dist-
|
|
1
|
+
import{L as e,R as t,Ut as n,i as r,r as i,s as a,u as o}from"./dist-CYzOYk_e.js";import{n as s}from"./dist-C0ve4Tka.js";import{r as c}from"./dist-CDUZdAAS.js";var l=i.deserialize({version:14,states:"%pOVOWOOObQPOOOpOSO'#C_OOOO'#Cp'#CpQVOWOOQxQPOOO!TQQOOQ!YQPOOOOOO,58y,58yO!_OSO,58yOOOO-E6n-E6nO!dQQO'#CqQ{QPOOO!iQPOOQ{QPOOO!qQPOOOOOO1G.e1G.eOOQO,59],59]OOQO-E6o-E6oO!yOpO'#CiO#RO`O'#CiQOQPOOO#ZO#tO'#CmO#fO!bO'#CmOOQO,59T,59TO#qOpO,59TO#vO`O,59TOOOO'#Cr'#CrO#{O#tO,59XOOQO,59X,59XOOOO'#Cs'#CsO$WO!bO,59XOOQO1G.o1G.oOOOO-E6p-E6pOOQO1G.s1G.sOOOO-E6q-E6q",stateData:"$g~OjOS~OQROUROkQO~OWTOXUOZUO`VO~OSXOTWO~OXUO[]OlZO~OY^O~O[_O~OT`O~OYaO~OmcOodO~OmfOogO~O^iOnhO~O_jOphO~ObkOqkOrmO~OcnOsnOtmO~OnpO~OppO~ObkOqkOrrO~OcnOsnOtrO~OWX`~",goto:`!^hPPPiPPPPPPPPPmPPPpPPsy!Q!WTROSRe]Re_QSORYSS[T^Rb[QlfRqlQogRso`,nodeNames:`⚠ Content Text Interpolation InterpolationContent }} Entity Attribute VueAttributeName : Identifier @ Is ScriptAttributeValue AttributeScript AttributeScript AttributeName AttributeValue Entity Entity`,maxTerm:36,nodeProps:[[`isolate`,-3,3,13,17,``]],skippedNodes:[0],repeatNodeCount:4,tokenData:"'y~RdXY!aYZ!a]^!apq!ars!rwx!w}!O!|!O!P#t!Q![#y![!]$s!_!`%g!b!c%l!c!}#y#R#S#y#T#j#y#j#k%q#k#o#y%W;'S#y;'S;:j$m<%lO#y~!fSj~XY!aYZ!a]^!apq!a~!wOm~~!|Oo~!b#RX`!b}!O!|!Q![!|![!]!|!c!}!|#R#S!|#T#o!|%W;'S!|;'S;:j#n<%lO!|!b#qP;=`<%l!|~#yOl~%W$QXY#t`!b}!O!|!Q![#y![!]!|!c!}#y#R#S#y#T#o#y%W;'S#y;'S;:j$m<%lO#y%W$pP;=`<%l#y~$zXX~`!b}!O!|!Q![!|![!]!|!c!}!|#R#S!|#T#o!|%W;'S!|;'S;:j#n<%lO!|~%lO[~~%qOZ~%W%xXY#t`!b}!O&e!Q![#y![!]!|!c!}#y#R#S#y#T#o#y%W;'S#y;'S;:j$m<%lO#y!b&jX`!b}!O!|!Q![!|![!]!|!c!}'V#R#S!|#T#o'V%W;'S!|;'S;:j#n<%lO!|!b'^XW!b`!b}!O!|!Q![!|![!]!|!c!}'V#R#S!|#T#o'V%W;'S!|;'S;:j#n<%lO!|",tokenizers:[6,7,new r(`b~RP#q#rU~XP#q#r[~aOT~~`,17,4),new r("!k~RQvwX#o#p!_~^TU~Opmq!]m!^;'Sm;'S;=`!X<%lOm~pUOpmq!]m!]!^!S!^;'Sm;'S;=`!X<%lOm~!XOU~~![P;=`<%lm~!bP#o#p!e~!jOk~~",72,2),new r(`[~RPwxU~ZOp~~`,11,15),new r(`[~RPrsU~ZOn~~`,11,14),new r("!e~RQvwXwx!_~^Tc~Opmq!]m!^;'Sm;'S;=`!X<%lOm~pUOpmq!]m!]!^!S!^;'Sm;'S;=`!X<%lOm~!XOc~~![P;=`<%lm~!dOt~~",66,35),new r("!e~RQrsXvw^~^Or~~cTb~Oprq!]r!^;'Sr;'S;=`!^<%lOr~uUOprq!]r!]!^!X!^;'Sr;'S;=`!^<%lOr~!^Ob~~!aP;=`<%lr~",66,33)],topRules:{Content:[0,1],Attribute:[1,7]},tokenPrec:157}),u=c.parser.configure({top:`SingleExpression`}),d=l.configure({props:[e({Text:t.content,Is:t.definitionOperator,AttributeName:t.attributeName,VueAttributeName:t.keyword,Identifier:t.variableName,"AttributeValue ScriptAttributeValue":t.attributeValue,Entity:t.character,"{{ }}":t.brace,"@ :":t.punctuation})]}),f={parser:u},p=d.configure({wrap:n((e,t)=>e.name==`InterpolationContent`?f:null)}),m=d.configure({wrap:n((e,t)=>e.name==`AttributeScript`?f:null),top:`Attribute`}),h={parser:p},g={parser:m},_=s();function v(e){return e.configure({dialect:`selfClosing`,wrap:n(b)},`vue`)}var y=v(_.language);function b(e,t){switch(e.name){case`Attribute`:return/^(@|:|v-)/.test(t.read(e.from,e.from+2))?g:null;case`Text`:return h}return null}function x(e={}){let t=_;if(e.base){if(e.base.language.name!=`html`||!(e.base.language instanceof a))throw RangeError(`The base option must be the result of calling html(...)`);t=e.base}return new o(t.language==_.language?y:v(t.language),[t.support,t.language.data.of({closeBrackets:{brackets:[`{`,`"`]}})])}export{x as vue};
|