@netnodeag/kraftwerk 0.37.1 → 0.38.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.
Files changed (31) hide show
  1. package/dist/config.d.ts +3 -0
  2. package/dist/config.js +7 -1
  3. package/dist/inspector/instances.d.ts +8 -0
  4. package/dist/inspector/instances.js +23 -3
  5. package/dist/inspector/server.js +2 -0
  6. package/dist/inspector/settings.d.ts +2 -0
  7. package/dist/inspector/settings.js +8 -0
  8. package/inspector/dist/assets/{editor-vM6JSj5h.js → editor-BnDJd3j4.js} +3 -3
  9. package/inspector/dist/assets/{index-MWv5H1oP.js → index-1K-YGe-R.js} +1 -1
  10. package/inspector/dist/assets/{index-C1UXlW32.js → index-5n-x53jb.js} +1 -1
  11. package/inspector/dist/assets/{index-BAAjszPI.js → index-B6zeZ-I9.js} +1 -1
  12. package/inspector/dist/assets/{index-CtMGH62C.js → index-BCwvYdEE.js} +1 -1
  13. package/inspector/dist/assets/{index-DfZT0IoM.js → index-BDHHRQvJ.js} +1 -1
  14. package/inspector/dist/assets/{index-D_11IZBV.js → index-BFmXikOX.js} +1 -1
  15. package/inspector/dist/assets/{index-Bb08wK-l.js → index-C2kxy-K_.js} +1 -1
  16. package/inspector/dist/assets/{index-DykDNrYm.js → index-CTECF5uX.js} +1 -1
  17. package/inspector/dist/assets/index-CtnGrZvu.js +217 -0
  18. package/inspector/dist/assets/{index-BrCwDkJZ.js → index-DBHgQG4M.js} +1 -1
  19. package/inspector/dist/assets/{index-CQ2XKqmD.js → index-DfqhDHp9.js} +1 -1
  20. package/inspector/dist/assets/index-DjMx3a5H.css +1 -0
  21. package/inspector/dist/assets/{index-yGZsomcy.js → index-DmY7gYaR.js} +1 -1
  22. package/inspector/dist/assets/{index-CaKmxcD6.js → index-Dq3QrDLb.js} +1 -1
  23. package/inspector/dist/assets/{index-oIQqfbwm.js → index-Fd3s8yys.js} +1 -1
  24. package/inspector/dist/assets/{index-Bs1U0rWW.js → index-Rmy9pam8.js} +1 -1
  25. package/inspector/dist/assets/{index-CUGfxoYt.js → index-VxlzgVIX.js} +1 -1
  26. package/inspector/dist/assets/{index-CReepirH.js → index-o4xPJnqZ.js} +1 -1
  27. package/inspector/dist/assets/{index-BKcOp35r.js → index-sSEE6ykA.js} +1 -1
  28. package/inspector/dist/index.html +2 -2
  29. package/package.json +1 -1
  30. package/inspector/dist/assets/index-Ba8VU6Wz.js +0 -217
  31. package/inspector/dist/assets/index-D4_mLTSH.css +0 -1
package/dist/config.d.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  *
12
12
  * name: my-project # display name (inspector header, "environment")
13
13
  * icon: "⚡" # emoji shown as inspector favicon
14
+ * color: "#c2410c" # accent for this workspace in the switcher (default: derived from the root)
14
15
  * port: 1981 # port `kraftwerk ui` listens on
15
16
  * workflows: src/workflows # workflows root, relative to the file
16
17
  * output: output # run-artifact directory, relative to the file
@@ -88,6 +89,8 @@ export interface ProjectConfig {
88
89
  name?: string;
89
90
  /** Emoji used as the inspector favicon (browser-tab icon). */
90
91
  icon?: string;
92
+ /** Accent colour (#rgb / #rrggbb) for this workspace in the switcher. Default: derived from the root path. */
93
+ color?: string;
91
94
  /** Port `kraftwerk ui` listens on. Default: 1981 (CLI --port wins). */
92
95
  port?: number;
93
96
  /** Workflows root relative to the project root. */
package/dist/config.js CHANGED
@@ -15,6 +15,7 @@ import { parse } from "yaml";
15
15
  *
16
16
  * name: my-project # display name (inspector header, "environment")
17
17
  * icon: "⚡" # emoji shown as inspector favicon
18
+ * color: "#c2410c" # accent for this workspace in the switcher (default: derived from the root)
18
19
  * port: 1981 # port `kraftwerk ui` listens on
19
20
  * workflows: src/workflows # workflows root, relative to the file
20
21
  * output: output # run-artifact directory, relative to the file
@@ -126,7 +127,7 @@ export async function resolveProject(cwd) {
126
127
  const root = gitFallback ?? start;
127
128
  return { root, config: {}, outputDir: path.join(root, "output") };
128
129
  }
129
- const KNOWN_KEYS = ["name", "icon", "port", "workflows", "output", "knowledge", "agents", "skills", "switcher", "git", "repos"];
130
+ const KNOWN_KEYS = ["name", "icon", "color", "port", "workflows", "output", "knowledge", "agents", "skills", "switcher", "git", "repos"];
130
131
  async function loadConfig(configPath) {
131
132
  let raw;
132
133
  try {
@@ -151,6 +152,11 @@ async function loadConfig(configPath) {
151
152
  throw new Error(`${path.basename(configPath)}: port must be an integer`);
152
153
  }
153
154
  }
155
+ else if (key === "color") {
156
+ if (typeof config[key] !== "string" || !/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(config[key])) {
157
+ throw new Error(`${path.basename(configPath)}: color must be a hex colour like "#c2410c"`);
158
+ }
159
+ }
154
160
  else if (key === "switcher") {
155
161
  validateSwitcher(configPath, config[key]);
156
162
  }
@@ -15,6 +15,10 @@ export interface DiscoveredInstance {
15
15
  name: string;
16
16
  url: string;
17
17
  icon?: string;
18
+ /** kraftwerk.yml `color`, if set. */
19
+ color?: string;
20
+ /** True when the name comes from kraftwerk.yml (false = folder name). */
21
+ named?: boolean;
18
22
  live: true;
19
23
  root?: string;
20
24
  /** Server process id (what to signal to stop it). */
@@ -29,6 +33,10 @@ export interface WorkspaceEntry {
29
33
  /** Where the inspector answers (live) or would answer once started. */
30
34
  url: string;
31
35
  icon?: string;
36
+ /** kraftwerk.yml `color`, if set; the UI derives one from the root otherwise. */
37
+ color?: string;
38
+ /** True when the name comes from kraftwerk.yml, false when it is just the folder name. */
39
+ named?: boolean;
32
40
  live: boolean;
33
41
  root?: string;
34
42
  /** Root with ~ for the home dir — what the UI prints. */
@@ -86,6 +86,8 @@ async function probeAt(host, port) {
86
86
  name: meta.projectName || `localhost:${port}`,
87
87
  url: `http://localhost:${port}`,
88
88
  icon: meta.projectIcon || undefined,
89
+ color: meta.projectColor || undefined,
90
+ named: typeof meta.projectNamed === "boolean" ? meta.projectNamed : undefined,
89
91
  live: true,
90
92
  };
91
93
  }
@@ -232,7 +234,7 @@ export async function forgetProject(root) {
232
234
  * so without this check a stale record would wear its parent's identity.
233
235
  */
234
236
  async function describeRoot(root) {
235
- const fallback = { name: path.basename(root), port: 1981, exists: false };
237
+ const fallback = { name: path.basename(root), named: false, port: 1981, exists: false };
236
238
  if (!(await isDir(root)))
237
239
  return fallback;
238
240
  try {
@@ -241,7 +243,9 @@ async function describeRoot(root) {
241
243
  return fallback;
242
244
  return {
243
245
  name: project.config.name ?? path.basename(project.root),
246
+ named: !!project.config.name,
244
247
  icon: project.config.icon || undefined,
248
+ color: project.config.color || undefined,
245
249
  port: project.config.port ?? 1981,
246
250
  exists: true,
247
251
  };
@@ -278,15 +282,29 @@ export async function discoverWorkspaces() {
278
282
  }
279
283
  if (running) {
280
284
  consumed.add(running);
281
- return { ...base, name: running.name, url: running.url, icon: running.icon, live: true, exists: true };
285
+ // An instance from before 0.38 answers without color/named — read them from its root.
286
+ if (running.named === undefined)
287
+ d = await describeRoot(p.root);
288
+ return {
289
+ ...base,
290
+ name: running.name,
291
+ url: running.url,
292
+ icon: running.icon,
293
+ color: running.color ?? d?.color,
294
+ named: running.named ?? d?.named,
295
+ live: true,
296
+ exists: true,
297
+ };
282
298
  }
283
- return { ...base, name: d.name, url: `http://localhost:${d.port}`, icon: d.icon, live: false, exists: d.exists };
299
+ return { ...base, name: d.name, url: `http://localhost:${d.port}`, icon: d.icon, color: d.color, named: d.named, live: false, exists: d.exists };
284
300
  }))).concat(await Promise.all(live
285
301
  .filter((i) => !consumed.has(i))
286
302
  .map(async (i) => ({
287
303
  name: i.name,
288
304
  url: i.url,
289
305
  icon: i.icon,
306
+ color: i.color,
307
+ named: i.named,
290
308
  live: true,
291
309
  root: i.root,
292
310
  rootLabel: i.root ? tildify(i.root) : undefined,
@@ -335,6 +353,8 @@ export async function listWorkspacesDetailed() {
335
353
  all.unshift({
336
354
  name: d.name,
337
355
  icon: d.icon,
356
+ color: d.color,
357
+ named: d.named,
338
358
  url: `http://localhost:${selfPort}`,
339
359
  live: true,
340
360
  root: selfRoot,
@@ -213,6 +213,8 @@ async function handleApi(req, res, url) {
213
213
  restartable: supervised(),
214
214
  projectName,
215
215
  projectIcon: project?.config.icon ?? "",
216
+ projectColor: project?.config.color ?? "",
217
+ projectNamed: !!project?.config.name,
216
218
  projectRoot: project?.root ?? getProjectRoot(),
217
219
  projectRootLabel: tildify(project?.root ?? getProjectRoot()),
218
220
  git: (project?.config.git && project.config.git.enabled !== false) === true,
@@ -20,6 +20,8 @@ export interface SaveSettingsInput {
20
20
  /** Omitted = untouched; empty string = remove the key. */
21
21
  name?: string;
22
22
  icon?: string;
23
+ /** Omitted = untouched; empty string = remove the key. Hex colour otherwise. */
24
+ color?: string;
23
25
  /** Omitted = untouched; empty list = remove the key. */
24
26
  switcher?: SwitcherEntry[];
25
27
  /** Omitted = untouched. See cleanGit for how the block is written. */
@@ -94,6 +94,13 @@ export async function saveSettings(input) {
94
94
  throw new Error("name must be a string");
95
95
  if (input.icon !== undefined && typeof input.icon !== "string")
96
96
  throw new Error("icon must be a string");
97
+ if (input.color !== undefined) {
98
+ if (typeof input.color !== "string")
99
+ throw new Error("color must be a string");
100
+ if (input.color.trim() && !/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(input.color.trim())) {
101
+ throw new Error('color must be a hex colour like "#c2410c"');
102
+ }
103
+ }
97
104
  const switcher = input.switcher === undefined ? undefined : cleanSwitcher(input.switcher);
98
105
  const project = await resolveProject(getProjectRoot());
99
106
  const configPath = project.configPath ?? path.join(project.root, "kraftwerk.yml");
@@ -111,6 +118,7 @@ export async function saveSettings(input) {
111
118
  };
112
119
  setOrDelete("name", input.name?.trim());
113
120
  setOrDelete("icon", input.icon?.trim());
121
+ setOrDelete("color", input.color?.trim());
114
122
  setOrDelete("switcher", switcher);
115
123
  if (input.git !== undefined) {
116
124
  const git = cleanGit(input.git);