@netnodeag/kraftwerk 0.14.1 → 0.15.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 CHANGED
@@ -96,6 +96,16 @@ directory, default `output/`), `knowledge:` (OKF knowledge-bundle root,
96
96
  default `knowledge/`), and `agents:` (team agent-definition root, default
97
97
  `agents/`).
98
98
 
99
+ `switcher:` links other kraftwerk workspaces from the inspector header —
100
+ the workspace name becomes a dropdown listing them:
101
+
102
+ ```yaml
103
+ switcher:
104
+ - name: other space
105
+ url: https://localhost:1985
106
+ icon: "🛰" # optional
107
+ ```
108
+
99
109
  ### Triggering from CI / cron / webhooks
100
110
 
101
111
  `run --json` is the machine mode: non-interactive, one JSON result object
package/dist/config.d.ts CHANGED
@@ -17,12 +17,25 @@
17
17
  * knowledge: knowledge # OKF knowledge-bundle root, relative to the file
18
18
  * agents: agents # team agent-definition root, relative to the file
19
19
  * skills: skills # workspace skill root, relative to the file
20
+ * switcher: # other kraftwerk workspaces, linked from the header dropdown
21
+ * - name: other space
22
+ * url: https://localhost:1985
23
+ * icon: "🛰" # optional emoji shown next to the entry
20
24
  */
21
25
  /** Stable, versionless URL of the workflow JSON schema (editor validation). */
22
26
  export declare const SCHEMA_URL = "https://raw.githubusercontent.com/NETNODEAG/kraftwerk/main/kraftwerk/schema/workflow.schema.json";
23
27
  /** Stable, versionless URL of the kraftwerk.yml JSON schema (editor validation). */
24
28
  export declare const CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/NETNODEAG/kraftwerk/main/kraftwerk/schema/kraftwerk.schema.json";
25
29
  export declare const CONFIG_FILENAMES: string[];
30
+ /** One entry of the workspace switcher: another kraftwerk instance to link to. */
31
+ export interface SwitcherEntry {
32
+ /** Display name of the other workspace. */
33
+ name: string;
34
+ /** URL the other workspace's inspector runs on, e.g. https://localhost:1985 */
35
+ url: string;
36
+ /** Optional emoji shown next to the entry. */
37
+ icon?: string;
38
+ }
26
39
  export interface ProjectConfig {
27
40
  /** Display name of the project ("environment"), shown in the inspector header. */
28
41
  name?: string;
@@ -40,6 +53,8 @@ export interface ProjectConfig {
40
53
  agents?: string;
41
54
  /** Workspace skill root relative to the project root. Default: skills */
42
55
  skills?: string;
56
+ /** Other kraftwerk workspaces, shown as a switcher dropdown in the inspector header. */
57
+ switcher?: SwitcherEntry[];
43
58
  }
44
59
  export interface Project {
45
60
  /** Absolute project root the CLI operates on. */
package/dist/config.js CHANGED
@@ -20,6 +20,10 @@ import { parse } from "yaml";
20
20
  * knowledge: knowledge # OKF knowledge-bundle root, relative to the file
21
21
  * agents: agents # team agent-definition root, relative to the file
22
22
  * skills: skills # workspace skill root, relative to the file
23
+ * switcher: # other kraftwerk workspaces, linked from the header dropdown
24
+ * - name: other space
25
+ * url: https://localhost:1985
26
+ * icon: "🛰" # optional emoji shown next to the entry
23
27
  */
24
28
  /** Stable, versionless URL of the workflow JSON schema (editor validation). */
25
29
  export const SCHEMA_URL = "https://raw.githubusercontent.com/NETNODEAG/kraftwerk/main/kraftwerk/schema/workflow.schema.json";
@@ -89,10 +93,10 @@ async function loadConfig(configPath) {
89
93
  if (raw === null || raw === undefined)
90
94
  return {};
91
95
  if (typeof raw !== "object" || Array.isArray(raw)) {
92
- throw new Error(`${path.basename(configPath)}: expected a mapping (name, icon, port, workflows, output, knowledge, agents, skills)`);
96
+ throw new Error(`${path.basename(configPath)}: expected a mapping (name, icon, port, workflows, output, knowledge, agents, skills, switcher)`);
93
97
  }
94
98
  const config = raw;
95
- const known = ["name", "icon", "port", "workflows", "output", "knowledge", "agents", "skills"];
99
+ const known = ["name", "icon", "port", "workflows", "output", "knowledge", "agents", "skills", "switcher"];
96
100
  for (const key of Object.keys(config)) {
97
101
  if (!known.includes(key)) {
98
102
  throw new Error(`${path.basename(configPath)}: unknown key "${key}" (allowed: ${known.join(", ")})`);
@@ -102,9 +106,38 @@ async function loadConfig(configPath) {
102
106
  throw new Error(`${path.basename(configPath)}: port must be an integer`);
103
107
  }
104
108
  }
109
+ else if (key === "switcher") {
110
+ validateSwitcher(configPath, config[key]);
111
+ }
105
112
  else if (typeof config[key] !== "string") {
106
113
  throw new Error(`${path.basename(configPath)}: ${key} must be a string`);
107
114
  }
108
115
  }
109
116
  return config;
110
117
  }
118
+ function validateSwitcher(configPath, value) {
119
+ const file = path.basename(configPath);
120
+ if (!Array.isArray(value)) {
121
+ throw new Error(`${file}: switcher must be a list of { name, url, icon? } entries`);
122
+ }
123
+ value.forEach((entry, i) => {
124
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
125
+ throw new Error(`${file}: switcher[${i}] must be a mapping with name and url`);
126
+ }
127
+ const e = entry;
128
+ for (const key of Object.keys(e)) {
129
+ if (!["name", "url", "icon"].includes(key)) {
130
+ throw new Error(`${file}: switcher[${i}]: unknown key "${key}" (allowed: name, url, icon)`);
131
+ }
132
+ }
133
+ if (typeof e.name !== "string" || !e.name.trim()) {
134
+ throw new Error(`${file}: switcher[${i}]: name must be a non-empty string`);
135
+ }
136
+ if (typeof e.url !== "string" || !/^https?:\/\//.test(e.url)) {
137
+ throw new Error(`${file}: switcher[${i}]: url must be an http(s) URL`);
138
+ }
139
+ if (e.icon !== undefined && typeof e.icon !== "string") {
140
+ throw new Error(`${file}: switcher[${i}]: icon must be a string`);
141
+ }
142
+ });
143
+ }
@@ -76,7 +76,12 @@ async function handleApi(req, res, url) {
76
76
  if (seg.length === 2 && seg[1] === "meta" && method === "GET") {
77
77
  const project = await resolveProject(getProjectRoot()).catch(() => null);
78
78
  const projectName = project?.config.name ?? (project ? path.basename(project.root) : "");
79
- return json(res, { version: await getPkgVersion(), projectName, projectIcon: project?.config.icon ?? "" });
79
+ return json(res, {
80
+ version: await getPkgVersion(),
81
+ projectName,
82
+ projectIcon: project?.config.icon ?? "",
83
+ switcher: project?.config.switcher ?? [],
84
+ });
80
85
  }
81
86
  // GET /api/runs
82
87
  if (seg.length === 2 && seg[1] === "runs" && method === "GET") {