@netnodeag/kraftwerk 0.76.0 → 0.77.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 +33 -27
- package/dist/cli/cloud.d.ts +2 -0
- package/dist/cli/cloud.js +262 -0
- package/dist/cli/daemon.js +9 -0
- package/dist/cli/doctor.js +16 -8
- package/dist/cli/init.js +2 -2
- package/dist/cli/kraftwerk.js +4 -0
- package/dist/client/routes.d.ts +4 -0
- package/dist/client/routes.js +4 -0
- package/dist/config.d.ts +15 -28
- package/dist/config.js +5 -19
- package/dist/core/cloud.d.ts +115 -0
- package/dist/core/cloud.js +94 -0
- package/dist/server/api/cloud.d.ts +72 -0
- package/dist/server/api/cloud.js +38 -0
- package/dist/server/api/devices.d.ts +8 -8
- package/dist/server/api/index.d.ts +88 -10
- package/dist/server/api/index.js +2 -0
- package/dist/server/api/workspace.d.ts +16 -1
- package/dist/server/api/workspace.js +2 -1
- package/dist/server/cloud.d.ts +65 -33
- package/dist/server/cloud.js +207 -188
- package/dist/server/hub-context.d.ts +1 -1
- package/dist/server/hub-context.js +3 -3
- package/dist/server/relay.d.ts +13 -0
- package/dist/server/relay.js +29 -1
- package/dist/server/server.d.ts +3 -2
- package/dist/server/server.js +6 -6
- package/package.json +1 -1
- package/schema/kraftwerk.schema.json +4 -4
- package/web/dist/assets/{dist-T1sNLC62.js → dist-BGHweZKi.js} +1 -1
- package/web/dist/assets/{dist-DFXmaDc5.js → dist-BVPxOqcx.js} +1 -1
- package/web/dist/assets/{dist-BYkcY-Vl.js → dist-CCNyRSAA.js} +1 -1
- package/web/dist/assets/{dist-flnLXEzU.js → dist-CXoAMU1b.js} +1 -1
- package/web/dist/assets/{dist-l7foWlFx.js → dist-DCFREMXk.js} +1 -1
- package/web/dist/assets/{dist-BQpWYkxm.js → dist-DT2Tjvwx.js} +1 -1
- package/web/dist/assets/{dist-BwIca_Uj.js → dist-DdA5PoVJ.js} +1 -1
- package/web/dist/assets/{dist-fgB7OEJy.js → dist-DoD3qX1P.js} +1 -1
- package/web/dist/assets/{dist-vuciRCSz.js → dist-FFA4-S6d.js} +1 -1
- package/web/dist/assets/{dist-BTVP-6On.js → dist-yGx0qYbw.js} +1 -1
- package/web/dist/assets/dist-zjUUP5a9.js +1 -0
- package/web/dist/assets/{editor-C6Ok8NPo.js → editor-CyWapLQr.js} +3 -3
- package/web/dist/assets/{index-z4tpCCzk.js → index-CNXcNoKJ.js} +4 -4
- package/web/dist/index.html +1 -1
- package/web/dist/assets/dist-BURf1l-A.js +0 -1
package/dist/server/relay.js
CHANGED
|
@@ -3,7 +3,7 @@ import { promises as fs } from "node:fs";
|
|
|
3
3
|
import http from "node:http";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
-
import { daemonHandshake, exportKeyPair, generateKeyPair, importKeyPair, relayId, toBase64, fromBase64, } from "../client/relay.js";
|
|
6
|
+
import { daemonHandshake, exportKeyPair, generateKeyPair, importKeyPair, relayId, toBase64, fromBase64, fromBase64Url, } from "../client/relay.js";
|
|
7
7
|
import { CLOUD_DEFAULT_URL } from "../config.js";
|
|
8
8
|
import { dropPushToken } from "../core/devices.js";
|
|
9
9
|
const settingsFile = () => path.join(os.homedir(), ".kraftwerk", "relay.json");
|
|
@@ -36,6 +36,34 @@ export async function setRelay(enabled, url) {
|
|
|
36
36
|
await writeRelaySettings(s);
|
|
37
37
|
return s;
|
|
38
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* The machine's key pair and secret, created (with the relay staying off)
|
|
41
|
+
* when relay.json is missing: the daemon's first start, `kraftwerk login`.
|
|
42
|
+
* The cloud knows the machine by the same id and secret the relay does; a
|
|
43
|
+
* damaged file is refused, never overwritten (it holds the id).
|
|
44
|
+
*/
|
|
45
|
+
export async function ensureRelayKeys() {
|
|
46
|
+
const s = await readRelaySettings();
|
|
47
|
+
if (s)
|
|
48
|
+
return s;
|
|
49
|
+
try {
|
|
50
|
+
await fs.access(settingsFile());
|
|
51
|
+
throw new Error(`${settingsFile()} is damaged — fix it or remove it (removing gives this machine a new id)`);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err.code !== "ENOENT")
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
const pair = await exportKeyPair(await generateKeyPair());
|
|
58
|
+
const fresh = { enabled: false, url: CLOUD_DEFAULT_URL, ...pair, secret: randomBytes(32).toString("base64url") };
|
|
59
|
+
await writeRelaySettings(fresh);
|
|
60
|
+
return fresh;
|
|
61
|
+
}
|
|
62
|
+
/** The machine's id (its relay id), public key and secret: what the cloud knows it by. */
|
|
63
|
+
export async function machineCredentials() {
|
|
64
|
+
const s = await ensureRelayKeys();
|
|
65
|
+
return { id: await relayId(fromBase64Url(s.publicKey)), key: s.publicKey, secret: s.secret };
|
|
66
|
+
}
|
|
39
67
|
/** wss://host/api/relay/<role> for a cloud URL. */
|
|
40
68
|
export const relayEndpoint = (cloudUrl, role) => `${cloudUrl.replace(/\/+$/, "").replace(/^http/, "ws")}/api/relay/${role}`;
|
|
41
69
|
/**
|
package/dist/server/server.d.ts
CHANGED
|
@@ -34,8 +34,9 @@ export interface OpenWorkspace {
|
|
|
34
34
|
* a standalone `kraftwerk ui` exactly one — its default, also reached
|
|
35
35
|
* without a slug, as before. Each open workspace runs in its own Workspace
|
|
36
36
|
* (core/workspace.ts) with its own .env, background jobs (routines, git
|
|
37
|
-
* sync
|
|
38
|
-
* and leaves the others running.
|
|
37
|
+
* sync), registry entry and live state; closing one ends all of that
|
|
38
|
+
* and leaves the others running. The daemon's hub also carries the
|
|
39
|
+
* machine's cloud record (server/cloud.ts), which lists the open workspaces.
|
|
39
40
|
*/
|
|
40
41
|
export interface Hub {
|
|
41
42
|
readonly server: http.Server;
|
package/dist/server/server.js
CHANGED
|
@@ -3,7 +3,7 @@ import http from "node:http";
|
|
|
3
3
|
import net from "node:net";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { setDefaultWorkspace, Workspace } from "../core/workspace.js";
|
|
6
|
-
import {
|
|
6
|
+
import { stopMachineCloud } from "./cloud.js";
|
|
7
7
|
import { disposeAllBackends, disposeWorkspaceBackends } from "../core/chat/sessions.js";
|
|
8
8
|
import { markWorkspaceStopped, registerInstance, registerWorkspace, unregisterInstance } from "../core/instances.js";
|
|
9
9
|
import { startGitSync, stopGitSync } from "../core/git.js";
|
|
@@ -156,8 +156,8 @@ function installExitHandlers() {
|
|
|
156
156
|
// Stamp first: unregistering forgets which workspaces this process served.
|
|
157
157
|
markWorkspaceStopped();
|
|
158
158
|
unregisterInstance();
|
|
159
|
-
// The cloud goodbye is bounded and a no-op
|
|
160
|
-
void
|
|
159
|
+
// The machine's cloud goodbye is bounded and a no-op outside the daemon, so the exit stays prompt.
|
|
160
|
+
void stopMachineCloud().finally(() => process.exit(sig === "SIGINT" ? 130 : 143));
|
|
161
161
|
});
|
|
162
162
|
}
|
|
163
163
|
process.once("exit", (code) => {
|
|
@@ -345,6 +345,9 @@ export async function startHub(opts) {
|
|
|
345
345
|
await hub.close(o.root);
|
|
346
346
|
opts.onChange = notify;
|
|
347
347
|
hubs.delete(hub);
|
|
348
|
+
// The daemon's machine client (started by `kraftwerk daemon`) says goodbye with the hub it reports.
|
|
349
|
+
if (opts.daemon)
|
|
350
|
+
await stopMachineCloud(hub);
|
|
348
351
|
// A connected browser tab (its socket, a keep-alive connection) must not hold the stop open.
|
|
349
352
|
for (const s of [...sockets])
|
|
350
353
|
s.close();
|
|
@@ -408,7 +411,6 @@ export async function startHub(opts) {
|
|
|
408
411
|
// Best-effort, in the background: none of them may hold up (or, failing, break) the open.
|
|
409
412
|
entry.registered = registerInstance(entry.aliasPort ?? port, abs, opts.daemon ? { hub: true, slug, url: entry.url } : {}).catch(() => { });
|
|
410
413
|
void registerWorkspace(abs).catch(() => { });
|
|
411
|
-
void startCloudSync(entry.url, version).catch(() => { });
|
|
412
414
|
}));
|
|
413
415
|
await noteChange();
|
|
414
416
|
return entry;
|
|
@@ -434,8 +436,6 @@ export async function startHub(opts) {
|
|
|
434
436
|
closeWatches();
|
|
435
437
|
disposeWorkspaceBackends();
|
|
436
438
|
disposeWorkspaceDevs();
|
|
437
|
-
await cloudGoodbye();
|
|
438
|
-
stopCloudSync();
|
|
439
439
|
}));
|
|
440
440
|
await entry.registered;
|
|
441
441
|
unregisterInstance(abs);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netnodeag/kraftwerk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.77.0",
|
|
4
4
|
"description": "Open-source agentic workspace for teams — 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",
|
|
@@ -177,21 +177,21 @@
|
|
|
177
177
|
"null"
|
|
178
178
|
],
|
|
179
179
|
"additionalProperties": false,
|
|
180
|
-
"description": "kraftwerk cloud
|
|
180
|
+
"description": "kraftwerk cloud: the daemon registers this MACHINE (its relay id) once, and the workspaces it serves ride along. This block only vetoes: `enabled: false` leaves this workspace out of the machine's record. The machine's switch and the cloud's origin are ~/.kraftwerk/cloud.json (`kraftwerk cloud on|off`) and KRAFTWERK_CLOUD_URL; `kraftwerk login` claims the machine.",
|
|
181
181
|
"properties": {
|
|
182
182
|
"enabled": {
|
|
183
183
|
"type": "boolean",
|
|
184
|
-
"description": "false
|
|
184
|
+
"description": "false leaves this workspace out of the machine's cloud record. Default: true"
|
|
185
185
|
},
|
|
186
186
|
"url": {
|
|
187
187
|
"type": "string",
|
|
188
188
|
"pattern": "^https?://",
|
|
189
|
-
"description": "
|
|
189
|
+
"description": "Deprecated: the cloud's origin is the machine's (~/.kraftwerk/cloud.json, KRAFTWERK_CLOUD_URL). Still used, with a warning, when the machine names none."
|
|
190
190
|
},
|
|
191
191
|
"interval": {
|
|
192
192
|
"type": "integer",
|
|
193
193
|
"minimum": 1,
|
|
194
|
-
"description": "
|
|
194
|
+
"description": "Deprecated and ignored: the cloud sets the heartbeat interval."
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
}
|