@netnodeag/kraftwerk 0.34.2 → 0.35.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 (36) hide show
  1. package/README.md +14 -0
  2. package/dist/config.d.ts +24 -0
  3. package/dist/config.js +42 -2
  4. package/dist/inspector/git.d.ts +83 -0
  5. package/dist/inspector/git.js +651 -0
  6. package/dist/inspector/instances.js +9 -1
  7. package/dist/inspector/server.js +132 -3
  8. package/dist/inspector/settings.d.ts +11 -1
  9. package/dist/inspector/settings.js +45 -0
  10. package/dist/runner/docker.d.ts +1 -0
  11. package/dist/runner/docker.js +1 -1
  12. package/inspector/dist/assets/{editor-CXrDJOv_.js → editor-BABv2LmA.js} +3 -3
  13. package/inspector/dist/assets/index-BXspF2KR.css +1 -0
  14. package/inspector/dist/assets/{index-DAi1VtJ8.js → index-BbTer62d.js} +1 -1
  15. package/inspector/dist/assets/{index-C3hpUVfp.js → index-Bmm4zJNU.js} +1 -1
  16. package/inspector/dist/assets/{index-x5G6Np7T.js → index-ByWpqzAI.js} +1 -1
  17. package/inspector/dist/assets/{index-Upf-BEsx.js → index-CCACy6uF.js} +1 -1
  18. package/inspector/dist/assets/{index-BNcXQYbv.js → index-Ca9Qs5LZ.js} +1 -1
  19. package/inspector/dist/assets/{index-Bumwg9SM.js → index-CdwTmXTV.js} +1 -1
  20. package/inspector/dist/assets/{index-CMMYSoL3.js → index-CjlJeW9z.js} +1 -1
  21. package/inspector/dist/assets/{index-CszaAR9T.js → index-CzPQygR7.js} +1 -1
  22. package/inspector/dist/assets/{index-DrSWAGft.js → index-DCR7u4Xa.js} +1 -1
  23. package/inspector/dist/assets/{index-BxEG-IdY.js → index-DbWUNbt-.js} +1 -1
  24. package/inspector/dist/assets/{index-BULB2rC_.js → index-DuPp3MUc.js} +1 -1
  25. package/inspector/dist/assets/{index-CNJyAniG.js → index-DuwmzZAr.js} +1 -1
  26. package/inspector/dist/assets/{index-CUCCMUMY.js → index-Gx93brFq.js} +1 -1
  27. package/inspector/dist/assets/{index-BIDsfw8C.js → index-HBdsdJhD.js} +1 -1
  28. package/inspector/dist/assets/{index-7J4zAS6J.js → index-hkVov8T4.js} +1 -1
  29. package/inspector/dist/assets/{index-BQMgADlc.js → index-sLxulPOm.js} +1 -1
  30. package/inspector/dist/assets/index-uEig4--d.js +217 -0
  31. package/inspector/dist/assets/{index-ChuqpBnv.js → index-wkYwz0Tl.js} +1 -1
  32. package/inspector/dist/index.html +2 -2
  33. package/package.json +5 -2
  34. package/schema/kraftwerk.schema.json +35 -1
  35. package/inspector/dist/assets/index-B7rwNdVj.css +0 -1
  36. package/inspector/dist/assets/index-BBGjdXk1.js +0 -213
package/README.md CHANGED
@@ -160,6 +160,20 @@ kraftwerk run --sandbox website-check "https://..." # isolated container per r
160
160
  kraftwerk runner ps / stop <run-id> # see / stop running sandbox containers
161
161
  ```
162
162
 
163
+ The inspector binds `127.0.0.1` — it has no authentication of its own and
164
+ its chat runs coding agents against the repo, so it stays off the LAN, and
165
+ while bound there it only answers requests whose `Host` is a loopback name
166
+ (so a page on another site cannot reach it by re-pointing its own DNS at
167
+ 127.0.0.1). Inside a container it binds all interfaces instead, because the
168
+ port mapping is the boundary there (`deploy-starter/` publishes to
169
+ localhost, and its traefik override adds basic-auth). `KRAFTWERK_UI_HOST`
170
+ overrides the default either way. A reverse proxy in front of it must set
171
+ `X-Forwarded-Host` to the host the browser addressed (Caddy and traefik do
172
+ by default; nginx needs `proxy_set_header X-Forwarded-Host $host;`): the
173
+ loopback bind answers a non-loopback `Host` only when that header is
174
+ present, and state-changing requests are refused when `Origin` names a
175
+ different host.
176
+
163
177
  ### The kraftwerk.yml project config
164
178
 
165
179
  Optional, at the project root, and also the root marker for the walk-up. All
package/dist/config.d.ts CHANGED
@@ -21,12 +21,34 @@
21
21
  * - name: other space
22
22
  * url: https://localhost:1985
23
23
  * icon: "🛰" # optional emoji shown next to the entry
24
+ * git: # workspace git sync (absent = off, bare key = on with defaults)
25
+ * remote: origin
26
+ * interval: 300 # seconds between background fetches
27
+ * autosync: pull # off | pull
24
28
  */
25
29
  /** Stable, versionless URL of the workflow JSON schema (editor validation). */
26
30
  export declare const SCHEMA_URL = "https://raw.githubusercontent.com/NETNODEAG/kraftwerk/main/kraftwerk/schema/workflow.schema.json";
27
31
  /** Stable, versionless URL of the kraftwerk.yml JSON schema (editor validation). */
28
32
  export declare const CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/NETNODEAG/kraftwerk/main/kraftwerk/schema/kraftwerk.schema.json";
29
33
  export declare const CONFIG_FILENAMES: string[];
34
+ /**
35
+ * Git sync for the workspace. The synced paths are not configurable: they
36
+ * are the roots this file already declares (workflows, knowledge, agents,
37
+ * skills) plus kraftwerk.yml itself. Commit and push stay manual; the
38
+ * interval only fetches, and pulls when `autosync` allows it.
39
+ */
40
+ export interface GitConfig {
41
+ /** false keeps the block but turns the feature off. Default: true. */
42
+ enabled?: boolean;
43
+ /** Remote to fetch, pull and push. Default: origin. */
44
+ remote?: string;
45
+ /** Branch to sync. Default: whatever is checked out. */
46
+ branch?: string;
47
+ /** Seconds between background fetches. 0 disables the timer. Default: 300. */
48
+ interval?: number;
49
+ /** off = fetch only. pull = also fast-forward when behind and clean. Default: pull. */
50
+ autosync?: "off" | "pull";
51
+ }
30
52
  /** One entry of the workspace switcher: another kraftwerk instance to link to. */
31
53
  export interface SwitcherEntry {
32
54
  /** Display name of the other workspace. */
@@ -55,6 +77,8 @@ export interface ProjectConfig {
55
77
  skills?: string;
56
78
  /** Other kraftwerk workspaces, shown as a switcher dropdown in the inspector header. */
57
79
  switcher?: SwitcherEntry[];
80
+ /** Workspace git sync. Absent = off. */
81
+ git?: GitConfig;
58
82
  }
59
83
  export interface Project {
60
84
  /** Absolute project root the CLI operates on. */
package/dist/config.js CHANGED
@@ -24,6 +24,10 @@ import { parse } from "yaml";
24
24
  * - name: other space
25
25
  * url: https://localhost:1985
26
26
  * icon: "🛰" # optional emoji shown next to the entry
27
+ * git: # workspace git sync (absent = off, bare key = on with defaults)
28
+ * remote: origin
29
+ * interval: 300 # seconds between background fetches
30
+ * autosync: pull # off | pull
27
31
  */
28
32
  /** Stable, versionless URL of the workflow JSON schema (editor validation). */
29
33
  export const SCHEMA_URL = "https://raw.githubusercontent.com/NETNODEAG/kraftwerk/main/kraftwerk/schema/workflow.schema.json";
@@ -83,6 +87,7 @@ export async function resolveProject(cwd) {
83
87
  const root = gitFallback ?? start;
84
88
  return { root, config: {}, outputDir: path.join(root, "output") };
85
89
  }
90
+ const KNOWN_KEYS = ["name", "icon", "port", "workflows", "output", "knowledge", "agents", "skills", "switcher", "git"];
86
91
  async function loadConfig(configPath) {
87
92
  let raw;
88
93
  try {
@@ -94,10 +99,10 @@ async function loadConfig(configPath) {
94
99
  if (raw === null || raw === undefined)
95
100
  return {};
96
101
  if (typeof raw !== "object" || Array.isArray(raw)) {
97
- throw new Error(`${path.basename(configPath)}: expected a mapping (name, icon, port, workflows, output, knowledge, agents, skills, switcher)`);
102
+ throw new Error(`${path.basename(configPath)}: expected a mapping (${KNOWN_KEYS.join(", ")})`);
98
103
  }
99
104
  const config = raw;
100
- const known = ["name", "icon", "port", "workflows", "output", "knowledge", "agents", "skills", "switcher"];
105
+ const known = KNOWN_KEYS;
101
106
  for (const key of Object.keys(config)) {
102
107
  if (!known.includes(key)) {
103
108
  throw new Error(`${path.basename(configPath)}: unknown key "${key}" (allowed: ${known.join(", ")})`);
@@ -110,6 +115,14 @@ async function loadConfig(configPath) {
110
115
  else if (key === "switcher") {
111
116
  validateSwitcher(configPath, config[key]);
112
117
  }
118
+ else if (key === "git") {
119
+ // A bare `git:` line parses as null. Every field has a default, so read
120
+ // it as "on, with defaults" instead of failing the whole config load
121
+ // and taking the rest of the inspector down with it.
122
+ if (config[key] === null)
123
+ config[key] = {};
124
+ validateGit(configPath, config[key]);
125
+ }
113
126
  else if (typeof config[key] !== "string") {
114
127
  throw new Error(`${path.basename(configPath)}: ${key} must be a string`);
115
128
  }
@@ -142,3 +155,30 @@ function validateSwitcher(configPath, value) {
142
155
  }
143
156
  });
144
157
  }
158
+ function validateGit(configPath, value) {
159
+ const file = path.basename(configPath);
160
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
161
+ throw new Error(`${file}: git must be a mapping (enabled, remote, branch, interval, autosync)`);
162
+ }
163
+ const g = value;
164
+ const known = ["enabled", "remote", "branch", "interval", "autosync"];
165
+ for (const key of Object.keys(g)) {
166
+ if (!known.includes(key)) {
167
+ throw new Error(`${file}: git.${key} is unknown (allowed: ${known.join(", ")})`);
168
+ }
169
+ }
170
+ if (g.enabled !== undefined && typeof g.enabled !== "boolean") {
171
+ throw new Error(`${file}: git.enabled must be true or false`);
172
+ }
173
+ for (const key of ["remote", "branch"]) {
174
+ if (g[key] !== undefined && (typeof g[key] !== "string" || !g[key].trim())) {
175
+ throw new Error(`${file}: git.${key} must be a non-empty string`);
176
+ }
177
+ }
178
+ if (g.interval !== undefined && (typeof g.interval !== "number" || !Number.isInteger(g.interval) || g.interval < 0)) {
179
+ throw new Error(`${file}: git.interval must be a whole number of seconds (0 disables the timer)`);
180
+ }
181
+ if (g.autosync !== undefined && g.autosync !== "off" && g.autosync !== "pull") {
182
+ throw new Error(`${file}: git.autosync must be "off" or "pull"`);
183
+ }
184
+ }
@@ -0,0 +1,83 @@
1
+ export interface GitFile {
2
+ /** Path relative to the repo root, which is what git speaks. */
3
+ path: string;
4
+ /** Two-letter porcelain code, e.g. " M", "??", "A ". */
5
+ code: string;
6
+ /** Human label for the code: modified, added, deleted, renamed, untracked. */
7
+ status: string;
8
+ /** Inside a synced root and not denied, so it can be selected. */
9
+ syncable: boolean;
10
+ /** Why it cannot be selected, when it cannot. */
11
+ reason?: string;
12
+ }
13
+ export interface GitStatus {
14
+ enabled: boolean;
15
+ /** Set when the feature is on but the repo cannot be used. */
16
+ error?: string;
17
+ repoRoot?: string;
18
+ branch?: string;
19
+ /** Configured or inferred upstream, absent when the branch tracks nothing. */
20
+ upstream?: string;
21
+ remote?: string;
22
+ ahead?: number;
23
+ behind?: number;
24
+ /** True when the branches have diverged, which needs a terminal. */
25
+ diverged?: boolean;
26
+ files?: GitFile[];
27
+ /** Blocked entries left out of `files` because the list was capped. */
28
+ blockedHidden?: number;
29
+ /** Repo-relative roots that may be committed. */
30
+ scope?: string[];
31
+ autosync?: "off" | "pull";
32
+ interval?: number;
33
+ lastFetch?: string;
34
+ lastPull?: string;
35
+ lastError?: string;
36
+ }
37
+ export interface GitDiff {
38
+ diff: string;
39
+ error?: string;
40
+ /** The diff was cut at MAX_DIFF; the rest is available in a terminal. */
41
+ truncated?: boolean;
42
+ }
43
+ /**
44
+ * Everything the git screen renders. Each call shells out to git a handful
45
+ * of times, and the nav badge polls it from every open tab, so results are
46
+ * cached for a beat.
47
+ */
48
+ export declare function gitStatus(fresh?: boolean): Promise<GitStatus>;
49
+ /**
50
+ * Unified diff for one changed file. Reading is as sensitive as writing
51
+ * here: this endpoint has no auth of its own, and a diff of an untracked
52
+ * .env is the file itself. So the status list is the one source of readable
53
+ * paths: a file not listed there, or listed as blocked, is not shown.
54
+ * Status never lists ignored files, which covers .gitignore too.
55
+ */
56
+ export declare function gitDiff(file: string): Promise<GitDiff>;
57
+ /** Stage the given repo-relative paths and commit just those. */
58
+ export declare function gitCommit(paths: string[], message: string): Promise<{
59
+ ok: boolean;
60
+ error?: string;
61
+ committed?: number;
62
+ }>;
63
+ export declare function gitFetch(): Promise<{
64
+ ok: boolean;
65
+ error?: string;
66
+ }>;
67
+ /** Fast-forward only. A diverged branch is the user's problem, not ours. */
68
+ export declare function gitPull(): Promise<{
69
+ ok: boolean;
70
+ error?: string;
71
+ }>;
72
+ export declare function gitPush(): Promise<{
73
+ ok: boolean;
74
+ error?: string;
75
+ }>;
76
+ /**
77
+ * Fetch on an interval, and fast-forward when `autosync: pull` is set. Never
78
+ * commits. A pull is skipped while the tree is dirty, so nobody's unsaved
79
+ * work is disturbed by a background merge. Ticks are chained, not scheduled
80
+ * on a fixed interval: a fetch against a slow remote must finish (or time
81
+ * out) before the next one starts, or a short interval piles up processes.
82
+ */
83
+ export declare function startGitSync(): void;