@netnodeag/kraftwerk 0.24.0 → 0.26.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/inspector/server.js +26 -1
- package/dist/inspector/settings.d.ts +27 -0
- package/dist/inspector/settings.js +59 -0
- package/dist/inspector/team.d.ts +4 -0
- package/dist/inspector/team.js +25 -0
- package/inspector/dist/assets/index-Bn2oy_08.js +87 -0
- package/inspector/dist/assets/index-C2_lRFXK.css +1 -0
- package/inspector/dist/index.html +2 -2
- package/package.json +1 -1
- package/inspector/dist/assets/index--RZKXr4_.css +0 -1
- package/inspector/dist/assets/index-BR7MXPTF.js +0 -87
package/dist/inspector/server.js
CHANGED
|
@@ -8,9 +8,10 @@ import { listWorkflows, getWorkflow } from "./workflows.js";
|
|
|
8
8
|
import { dockerStatus, triggerRun, stopRun } from "./runner.js";
|
|
9
9
|
import { cancelChat, createChat, deleteChat, disposeAllBackends, getChat, listChats, postMessage, resolvePermission, subscribeChat, } from "./chat/sessions.js";
|
|
10
10
|
import { bundleDetail, conceptDetail, createBundle, knowledgeIndex, putConcept, verifyFromUi, } from "./knowledge.js";
|
|
11
|
-
import { deleteMember, getMember, listMembers, saveMember, teamRoot, } from "./team.js";
|
|
11
|
+
import { deleteMember, getMember, listMembers, saveMember, setMemberArchived, teamRoot, } from "./team.js";
|
|
12
12
|
import { deleteAgentSkill, getSkill, listAgentSkills, listSkills, readSkill, saveAgentSkill, skillsRoot, } from "./skills.js";
|
|
13
13
|
import { discoverInstances, registerInstance, unregisterInstance } from "./instances.js";
|
|
14
|
+
import { getSettings, saveSettings } from "./settings.js";
|
|
14
15
|
import { deleteRoutine, routineStatuses, runRoutineNow, saveRoutine, startRoutineScheduler, } from "./routines.js";
|
|
15
16
|
/**
|
|
16
17
|
* The inspector server: a plain node:http server with no dependencies.
|
|
@@ -118,6 +119,20 @@ async function handleApi(req, res, url) {
|
|
|
118
119
|
switcher,
|
|
119
120
|
});
|
|
120
121
|
}
|
|
122
|
+
// GET /api/settings — kraftwerk.yml (parsed + resolved paths) for the settings page
|
|
123
|
+
if (seg.length === 2 && seg[1] === "settings" && method === "GET") {
|
|
124
|
+
return json(res, await getSettings());
|
|
125
|
+
}
|
|
126
|
+
// PUT /api/settings — write the UI-editable subset (name, icon, switcher) back
|
|
127
|
+
if (seg.length === 2 && seg[1] === "settings" && method === "PUT") {
|
|
128
|
+
try {
|
|
129
|
+
const input = JSON.parse(await readBody(req));
|
|
130
|
+
return json(res, await saveSettings(input));
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
return json(res, { error: err.message }, 400);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
121
136
|
// GET /api/update-check — ask the npm registry for the latest published version
|
|
122
137
|
if (seg.length === 2 && seg[1] === "update-check" && method === "GET") {
|
|
123
138
|
const name = await getPkgName();
|
|
@@ -343,6 +358,16 @@ async function handleApi(req, res, url) {
|
|
|
343
358
|
}
|
|
344
359
|
}
|
|
345
360
|
}
|
|
361
|
+
// POST /api/team/:slug/archive — archive/unarchive a member ({ archived: boolean })
|
|
362
|
+
if (seg.length === 4 && seg[1] === "team" && seg[3] === "archive" && method === "POST") {
|
|
363
|
+
try {
|
|
364
|
+
const body = JSON.parse(await readBody(req));
|
|
365
|
+
return json(res, await setMemberArchived(decodeURIComponent(seg[2]), body.archived === true));
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
return json(res, { error: err.message }, 400);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
346
371
|
// GET/POST /api/team/:slug/skills — the agent's own skills / create or update one
|
|
347
372
|
if (seg.length === 4 && seg[1] === "team" && seg[3] === "skills") {
|
|
348
373
|
const slug = decodeURIComponent(seg[2]);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ProjectConfig, type SwitcherEntry } from "../config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Workspace settings: read kraftwerk.yml for the UI and write the
|
|
4
|
+
* UI-editable subset (name, icon, switcher) back. Edits go through the
|
|
5
|
+
* yaml Document API so comments and every other key survive untouched;
|
|
6
|
+
* a missing kraftwerk.yml is created at the project root on first save.
|
|
7
|
+
*/
|
|
8
|
+
export interface SettingsView {
|
|
9
|
+
root: string;
|
|
10
|
+
configPath: string;
|
|
11
|
+
exists: boolean;
|
|
12
|
+
config: ProjectConfig;
|
|
13
|
+
resolved: {
|
|
14
|
+
workflowsRoot: string | null;
|
|
15
|
+
outputDir: string;
|
|
16
|
+
port: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export interface SaveSettingsInput {
|
|
20
|
+
/** Omitted = untouched; empty string = remove the key. */
|
|
21
|
+
name?: string;
|
|
22
|
+
icon?: string;
|
|
23
|
+
/** Omitted = untouched; empty list = remove the key. */
|
|
24
|
+
switcher?: SwitcherEntry[];
|
|
25
|
+
}
|
|
26
|
+
export declare function getSettings(): Promise<SettingsView>;
|
|
27
|
+
export declare function saveSettings(input: SaveSettingsInput): Promise<SettingsView>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseDocument } from "yaml";
|
|
4
|
+
import { resolveProject } from "../config.js";
|
|
5
|
+
import { getProjectRoot } from "./context.js";
|
|
6
|
+
export async function getSettings() {
|
|
7
|
+
const project = await resolveProject(getProjectRoot());
|
|
8
|
+
return {
|
|
9
|
+
root: project.root,
|
|
10
|
+
configPath: project.configPath ?? path.join(project.root, "kraftwerk.yml"),
|
|
11
|
+
exists: !!project.configPath,
|
|
12
|
+
config: project.config,
|
|
13
|
+
resolved: {
|
|
14
|
+
workflowsRoot: project.workflowsRoot ?? null,
|
|
15
|
+
outputDir: project.outputDir,
|
|
16
|
+
port: project.config.port ?? 1981,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function cleanSwitcher(value) {
|
|
21
|
+
if (!Array.isArray(value))
|
|
22
|
+
throw new Error("switcher must be a list");
|
|
23
|
+
return value.map((entry, i) => {
|
|
24
|
+
const name = typeof entry?.name === "string" ? entry.name.trim() : "";
|
|
25
|
+
const url = typeof entry?.url === "string" ? entry.url.trim() : "";
|
|
26
|
+
if (!name)
|
|
27
|
+
throw new Error(`switcher entry ${i + 1}: name is required`);
|
|
28
|
+
if (!/^https?:\/\//.test(url))
|
|
29
|
+
throw new Error(`switcher entry ${i + 1}: url must start with http:// or https://`);
|
|
30
|
+
const icon = typeof entry.icon === "string" ? entry.icon.trim() : "";
|
|
31
|
+
return icon ? { name, url, icon } : { name, url };
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export async function saveSettings(input) {
|
|
35
|
+
if (input.name !== undefined && typeof input.name !== "string")
|
|
36
|
+
throw new Error("name must be a string");
|
|
37
|
+
if (input.icon !== undefined && typeof input.icon !== "string")
|
|
38
|
+
throw new Error("icon must be a string");
|
|
39
|
+
const switcher = input.switcher === undefined ? undefined : cleanSwitcher(input.switcher);
|
|
40
|
+
const project = await resolveProject(getProjectRoot());
|
|
41
|
+
const configPath = project.configPath ?? path.join(project.root, "kraftwerk.yml");
|
|
42
|
+
const raw = project.configPath ? await fs.readFile(configPath, "utf8") : "";
|
|
43
|
+
const doc = parseDocument(raw);
|
|
44
|
+
if (doc.errors.length > 0)
|
|
45
|
+
throw new Error(`${path.basename(configPath)}: ${doc.errors[0].message}`);
|
|
46
|
+
const setOrDelete = (key, value) => {
|
|
47
|
+
if (value === undefined)
|
|
48
|
+
return; // untouched
|
|
49
|
+
if (value === "" || (Array.isArray(value) && value.length === 0))
|
|
50
|
+
doc.delete(key);
|
|
51
|
+
else
|
|
52
|
+
doc.set(key, value);
|
|
53
|
+
};
|
|
54
|
+
setOrDelete("name", input.name?.trim());
|
|
55
|
+
setOrDelete("icon", input.icon?.trim());
|
|
56
|
+
setOrDelete("switcher", switcher);
|
|
57
|
+
await fs.writeFile(configPath, doc.toString());
|
|
58
|
+
return getSettings();
|
|
59
|
+
}
|
package/dist/inspector/team.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface TeamMember {
|
|
|
30
30
|
* Absent = all discovered skills; empty list = no skills.
|
|
31
31
|
*/
|
|
32
32
|
skills?: string[];
|
|
33
|
+
/** Archived: hidden from the active roster, restorable any time. */
|
|
34
|
+
archived?: boolean;
|
|
33
35
|
}
|
|
34
36
|
export interface TeamMemberDetail extends TeamMember {
|
|
35
37
|
/** Contents of system.md — the member's system prompt. */
|
|
@@ -58,4 +60,6 @@ export interface SaveMemberInput {
|
|
|
58
60
|
system?: string;
|
|
59
61
|
}
|
|
60
62
|
export declare function saveMember(input: SaveMemberInput): Promise<TeamMemberDetail>;
|
|
63
|
+
/** Archive/unarchive a member: toggles `archived:` in agent.yml, nothing else. */
|
|
64
|
+
export declare function setMemberArchived(slug: string, archived: boolean): Promise<TeamMemberDetail>;
|
|
61
65
|
export declare function deleteMember(slug: string): Promise<void>;
|
package/dist/inspector/team.js
CHANGED
|
@@ -52,6 +52,7 @@ function normalize(slug, raw) {
|
|
|
52
52
|
workflows: Array.isArray(raw.workflows) ? raw.workflows.map(String) : [],
|
|
53
53
|
knowledge: Array.isArray(raw.knowledge) ? raw.knowledge.map(String) : [],
|
|
54
54
|
...(Array.isArray(raw.skills) ? { skills: raw.skills.map(String) } : {}),
|
|
55
|
+
...(raw.archived === true ? { archived: true } : {}),
|
|
55
56
|
};
|
|
56
57
|
}
|
|
57
58
|
export async function listMembers() {
|
|
@@ -101,6 +102,11 @@ export async function saveMember(input) {
|
|
|
101
102
|
const slug = input.slug ? safeMemberSlug(input.slug) : slugFromName(name);
|
|
102
103
|
const dir = path.join(await teamRoot(), slug);
|
|
103
104
|
await fs.mkdir(dir, { recursive: true });
|
|
105
|
+
// Profile edits must not silently unarchive: carry the flag over.
|
|
106
|
+
const existing = await fs
|
|
107
|
+
.readFile(path.join(dir, "agent.yml"), "utf8")
|
|
108
|
+
.then((raw) => (parse(raw) ?? {}))
|
|
109
|
+
.catch(() => null);
|
|
104
110
|
const yml = {
|
|
105
111
|
name,
|
|
106
112
|
emoji: input.emoji?.trim() || "🤖",
|
|
@@ -112,11 +118,30 @@ export async function saveMember(input) {
|
|
|
112
118
|
workflows: (input.workflows ?? []).map(String),
|
|
113
119
|
knowledge: (input.knowledge ?? []).map(String),
|
|
114
120
|
...(input.skills ? { skills: input.skills.map(String) } : {}),
|
|
121
|
+
...(existing?.archived === true ? { archived: true } : {}),
|
|
115
122
|
};
|
|
116
123
|
await fs.writeFile(path.join(dir, "agent.yml"), stringify(yml));
|
|
117
124
|
await fs.writeFile(path.join(dir, "system.md"), (input.system ?? "").trim() + "\n");
|
|
118
125
|
return (await getMember(slug));
|
|
119
126
|
}
|
|
127
|
+
/** Archive/unarchive a member: toggles `archived:` in agent.yml, nothing else. */
|
|
128
|
+
export async function setMemberArchived(slug, archived) {
|
|
129
|
+
const dir = path.join(await teamRoot(), safeMemberSlug(slug));
|
|
130
|
+
let raw;
|
|
131
|
+
try {
|
|
132
|
+
raw = (parse(await fs.readFile(path.join(dir, "agent.yml"), "utf8")) ?? {});
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
throw new Error("agent not found");
|
|
136
|
+
}
|
|
137
|
+
const yml = { ...raw };
|
|
138
|
+
if (archived)
|
|
139
|
+
yml.archived = true;
|
|
140
|
+
else
|
|
141
|
+
delete yml.archived;
|
|
142
|
+
await fs.writeFile(path.join(dir, "agent.yml"), stringify(yml));
|
|
143
|
+
return (await getMember(slug));
|
|
144
|
+
}
|
|
120
145
|
export async function deleteMember(slug) {
|
|
121
146
|
const dir = path.join(await teamRoot(), safeMemberSlug(slug));
|
|
122
147
|
await fs.rm(dir, { recursive: true, force: true });
|