@netnodeag/kraftwerk 0.79.0 → 0.81.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 +88 -2
- package/dist/cli/daemon.js +10 -9
- package/dist/cli/doctor.js +1 -1
- package/dist/cli/entities.d.ts +8 -0
- package/dist/cli/entities.js +222 -0
- package/dist/cli/init.js +6 -2
- package/dist/cli/kraftwerk.js +10 -0
- package/dist/cli/temp.d.ts +2 -0
- package/dist/cli/temp.js +62 -0
- package/dist/cli/ui.js +2 -1
- package/dist/client/routes.d.ts +18 -0
- package/dist/client/routes.js +18 -0
- package/dist/config.d.ts +25 -0
- package/dist/config.js +17 -6
- package/dist/core/chat/acp.js +2 -1
- package/dist/core/chat/backend.d.ts +2 -0
- package/dist/core/chat/context.js +15 -7
- package/dist/core/chat/pi.js +2 -1
- package/dist/core/chat/seats.js +3 -0
- package/dist/core/chat/sessions.js +3 -0
- package/dist/core/entities.d.ts +171 -0
- package/dist/core/entities.js +441 -0
- package/dist/core/files.js +1 -0
- package/dist/core/git.js +3 -1
- package/dist/core/journal.d.ts +12 -2
- package/dist/core/journal.js +50 -5
- package/dist/core/log.d.ts +18 -0
- package/dist/core/log.js +23 -0
- package/dist/core/projects.d.ts +22 -33
- package/dist/core/projects.js +41 -135
- package/dist/core/records.d.ts +28 -0
- package/dist/core/records.js +71 -0
- package/dist/core/refs.d.ts +74 -0
- package/dist/core/refs.js +143 -0
- package/dist/core/search.d.ts +10 -0
- package/dist/core/search.js +12 -0
- package/dist/core/settings.d.ts +4 -0
- package/dist/core/settings.js +24 -3
- package/dist/core/slug.d.ts +9 -0
- package/dist/core/slug.js +18 -0
- package/dist/core/temp.d.ts +73 -0
- package/dist/core/temp.js +154 -0
- package/dist/core/trash.d.ts +1 -1
- package/dist/harness.d.ts +2 -0
- package/dist/harnesses/acp.js +1 -1
- package/dist/harnesses/claude.js +1 -0
- package/dist/harnesses/codex.js +1 -0
- package/dist/harnesses/pi.js +1 -0
- package/dist/run.d.ts +7 -0
- package/dist/run.js +13 -1
- package/dist/server/api/devices.js +2 -1
- package/dist/server/api/entities.d.ts +74 -0
- package/dist/server/api/entities.js +60 -0
- package/dist/server/api/files.d.ts +9 -1
- package/dist/server/api/files.js +13 -0
- package/dist/server/api/index.d.ts +75 -5
- package/dist/server/api/index.js +5 -0
- package/dist/server/api/projects.d.ts +3 -0
- package/dist/server/api/projects.js +3 -0
- package/dist/server/api/workspace.d.ts +15 -0
- package/dist/server/api/workspace.js +6 -0
- package/dist/server/cloud.js +3 -2
- package/dist/server/relay.d.ts +3 -2
- package/dist/server/relay.js +7 -7
- package/dist/server/server.js +3 -2
- package/dist/yaml.js +1 -0
- package/package.json +1 -1
- package/schema/kraftwerk.schema.json +54 -0
- package/web/dist/assets/{dist-cNijUfNl.js → dist-8IOFzZx-.js} +1 -1
- package/web/dist/assets/{dist-E48sO9Gc.js → dist-AhnSw4hy.js} +1 -1
- package/web/dist/assets/{dist-BTD8k55t.js → dist-BK0VhAPQ.js} +1 -1
- package/web/dist/assets/{dist-A0LN_JdM.js → dist-Ba6WSVOB.js} +1 -1
- package/web/dist/assets/{dist-DgP31lcC.js → dist-BiU4F98O.js} +1 -1
- package/web/dist/assets/{dist-wtIUmbnn.js → dist-Dx3aajX1.js} +1 -1
- package/web/dist/assets/{dist-DdOsafun.js → dist-Dy1SvhKC.js} +1 -1
- package/web/dist/assets/{dist-D93lSM0a.js → dist-ENZYo2jZ.js} +1 -1
- package/web/dist/assets/{dist-BasQkRXA.js → dist-FfKYtQxT.js} +1 -1
- package/web/dist/assets/{dist-xLZN2eGO.js → dist-cewmjxFi.js} +1 -1
- package/web/dist/assets/dist-lgUK2Fjt.js +1 -0
- package/web/dist/assets/{editor-BQlHN7qT.js → editor-CaRa1NMC.js} +3 -3
- package/web/dist/assets/index-Dpzux3OC.js +231 -0
- package/web/dist/assets/index-Dvt-vE1h.css +2 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/dist-B04mMuvH.js +0 -1
- package/web/dist/assets/index-BKDJocDd.css +0 -2
- package/web/dist/assets/index-C7ddpWNC.js +0 -231
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import type { EntityKind } from "../../config.js";
|
|
3
|
+
import { type EntityBase, type EntityKindDef } from "../../core/entities.js";
|
|
4
|
+
/**
|
|
5
|
+
* Contacts, places and events over the API. One factory, three kinds: the
|
|
6
|
+
* routes are named per kind (`contacts.list`, `places.get`, `events.save`)
|
|
7
|
+
* with the kind's own result type, so the typed client and the UI see
|
|
8
|
+
* three ordinary route families — not one generic entities endpoint whose
|
|
9
|
+
* results would be a union. Adding a kind is one definition in
|
|
10
|
+
* core/entities.ts and one factory call below.
|
|
11
|
+
*/
|
|
12
|
+
export declare function entityRoutes<D extends EntityBase, K extends EntityKind>(def: EntityKindDef<D, K>): readonly [import("./router.js").Route<`${K}.list`, import("../../core/entities.js").EntitiesView<D>, `/api/${K}`, undefined>, import("./router.js").Route<`${K}.create`, import("./router.js").Reply<import("../../core/entities.js").EntityDetail<D>>, `/api/${K}`, z.ZodObject<{
|
|
13
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
14
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
15
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
16
|
+
title: z.ZodString;
|
|
17
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
18
|
+
}, z.core.$loose>>, import("./router.js").Route<`${K}.get`, import("../../core/entities.js").EntityDetail<D>, `/api/${K}/:slug`, undefined>, import("./router.js").Route<`${K}.save`, import("../../core/entities.js").EntityDetail<D>, `/api/${K}/:slug`, z.ZodObject<{
|
|
19
|
+
title: z.ZodOptional<z.ZodString>;
|
|
20
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
21
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
22
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
23
|
+
}, z.core.$loose>>, import("./router.js").Route<`${K}.delete`, {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
}, `/api/${K}/:slug`, undefined>];
|
|
26
|
+
export declare const contactRoutes: readonly [import("./router.js").Route<"contacts.list", import("../../core/entities.js").EntitiesView<import("../../core/entities.js").ContactDef>, "/api/contacts", undefined>, import("./router.js").Route<"contacts.create", import("./router.js").Reply<import("../../core/entities.js").EntityDetail<import("../../core/entities.js").ContactDef>>, "/api/contacts", z.ZodObject<{
|
|
27
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
28
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
29
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
30
|
+
title: z.ZodString;
|
|
31
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
32
|
+
}, z.core.$loose>>, import("./router.js").Route<"contacts.get", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").ContactDef>, "/api/contacts/:slug", undefined>, import("./router.js").Route<"contacts.save", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").ContactDef>, "/api/contacts/:slug", z.ZodObject<{
|
|
33
|
+
title: z.ZodOptional<z.ZodString>;
|
|
34
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
35
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
36
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
37
|
+
}, z.core.$loose>>, import("./router.js").Route<"contacts.delete", {
|
|
38
|
+
ok: boolean;
|
|
39
|
+
}, "/api/contacts/:slug", undefined>];
|
|
40
|
+
export declare const placeRoutes: readonly [import("./router.js").Route<"places.list", import("../../core/entities.js").EntitiesView<import("../../core/entities.js").PlaceDef>, "/api/places", undefined>, import("./router.js").Route<"places.create", import("./router.js").Reply<import("../../core/entities.js").EntityDetail<import("../../core/entities.js").PlaceDef>>, "/api/places", z.ZodObject<{
|
|
41
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
42
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
43
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
44
|
+
title: z.ZodString;
|
|
45
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
46
|
+
}, z.core.$loose>>, import("./router.js").Route<"places.get", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").PlaceDef>, "/api/places/:slug", undefined>, import("./router.js").Route<"places.save", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").PlaceDef>, "/api/places/:slug", z.ZodObject<{
|
|
47
|
+
title: z.ZodOptional<z.ZodString>;
|
|
48
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
49
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
50
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
51
|
+
}, z.core.$loose>>, import("./router.js").Route<"places.delete", {
|
|
52
|
+
ok: boolean;
|
|
53
|
+
}, "/api/places/:slug", undefined>];
|
|
54
|
+
export declare const eventRoutes: readonly [import("./router.js").Route<"events.list", import("../../core/entities.js").EntitiesView<import("../../core/entities.js").EventDef>, "/api/events", undefined>, import("./router.js").Route<"events.create", import("./router.js").Reply<import("../../core/entities.js").EntityDetail<import("../../core/entities.js").EventDef>>, "/api/events", z.ZodObject<{
|
|
55
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
56
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
57
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
58
|
+
title: z.ZodString;
|
|
59
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
60
|
+
}, z.core.$loose>>, import("./router.js").Route<"events.get", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").EventDef>, "/api/events/:slug", undefined>, import("./router.js").Route<"events.save", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").EventDef>, "/api/events/:slug", z.ZodObject<{
|
|
61
|
+
title: z.ZodOptional<z.ZodString>;
|
|
62
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
63
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
64
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
65
|
+
}, z.core.$loose>>, import("./router.js").Route<"events.delete", {
|
|
66
|
+
ok: boolean;
|
|
67
|
+
}, "/api/events/:slug", undefined>];
|
|
68
|
+
/** References: resolve `kind:slug` tokens — given as a list, or found in a text — against the registry. */
|
|
69
|
+
export declare const refRoutes: import("./router.js").Route<"refs.resolve", {
|
|
70
|
+
refs: import("../../core/refs.js").ResolvedRef[];
|
|
71
|
+
}, "/api/refs/resolve", z.ZodObject<{
|
|
72
|
+
tokens: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
73
|
+
text: z.ZodDefault<z.ZodString>;
|
|
74
|
+
}, z.core.$strip>>[];
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { CONTACTS, createEntity, deleteEntity, EVENTS, getEntity, listEntities, openEntities, PLACES, saveEntity } from "../../core/entities.js";
|
|
3
|
+
import { findRefs, parseRef, resolveTokens } from "../../core/refs.js";
|
|
4
|
+
import { fail, reply, route, statusByMessage } from "./router.js";
|
|
5
|
+
/**
|
|
6
|
+
* Contacts, places and events over the API. One factory, three kinds: the
|
|
7
|
+
* routes are named per kind (`contacts.list`, `places.get`, `events.save`)
|
|
8
|
+
* with the kind's own result type, so the typed client and the UI see
|
|
9
|
+
* three ordinary route families — not one generic entities endpoint whose
|
|
10
|
+
* results would be a union. Adding a kind is one definition in
|
|
11
|
+
* core/entities.ts and one factory call below.
|
|
12
|
+
*/
|
|
13
|
+
export function entityRoutes(def) {
|
|
14
|
+
const errors = statusByMessage(new RegExp(`^no ${def.singular}`));
|
|
15
|
+
const kind = def.kind;
|
|
16
|
+
/** The editable fields. Loose: the domain checks values and keeps what it knows (unknown keys stay in the frontmatter). */
|
|
17
|
+
const Fields = z.looseObject({
|
|
18
|
+
title: z.string().optional(),
|
|
19
|
+
notes: z.string().optional(),
|
|
20
|
+
tags: z.unknown().optional(),
|
|
21
|
+
records: z.unknown().optional(),
|
|
22
|
+
...Object.fromEntries(def.fields.map((f) => [f.name, z.unknown().optional()])),
|
|
23
|
+
});
|
|
24
|
+
const Create = Fields.extend({ title: z.string(), slug: z.string().optional() });
|
|
25
|
+
const fieldNames = def.fields.map((f) => f.name).join(", ");
|
|
26
|
+
return [
|
|
27
|
+
route({ name: `${kind}.list`, method: "GET", path: `/api/${kind}`, summary: `every ${def.singular} file under the root${def.span ? " (?from=&to= narrow by date; an entry counts while it runs)" : ""}` }, async (c) => listEntities(def, { from: c.query.get("from") ?? undefined, to: c.query.get("to") ?? undefined })),
|
|
28
|
+
route({ name: `${kind}.create`, method: "POST", path: `/api/${kind}`, summary: `create one {title, slug?, notes?, tags?, records?, ${fieldNames}}`, errors: 400, body: Create }, async (c) => {
|
|
29
|
+
if ((await openEntities(kind)).off)
|
|
30
|
+
fail(409, `${kind} are off`);
|
|
31
|
+
return reply(201, await createEntity(def, (await c.body())));
|
|
32
|
+
}),
|
|
33
|
+
route({ name: `${kind}.get`, method: "GET", path: `/api/${kind}/:slug`, summary: "fields, notes and link states", errors }, async (c) => (await getEntity(def, c.params.slug)) ?? fail(404, `no ${def.singular} "${c.params.slug}"`)),
|
|
34
|
+
route({ name: `${kind}.save`, method: "PUT", path: `/api/${kind}/:slug`, summary: 'save fields and notes ("" clears a field)', errors, body: Fields }, async (c) => saveEntity(def, c.params.slug, (await c.body()))),
|
|
35
|
+
route({ name: `${kind}.delete`, method: "DELETE", path: `/api/${kind}/:slug`, summary: "move the file to the trash", errors }, async (c) => {
|
|
36
|
+
await deleteEntity(def, c.params.slug);
|
|
37
|
+
return { ok: true };
|
|
38
|
+
}),
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
export const contactRoutes = entityRoutes(CONTACTS);
|
|
42
|
+
export const placeRoutes = entityRoutes(PLACES);
|
|
43
|
+
export const eventRoutes = entityRoutes(EVENTS);
|
|
44
|
+
/** References: resolve `kind:slug` tokens — given as a list, or found in a text — against the registry. */
|
|
45
|
+
export const refRoutes = [
|
|
46
|
+
route({
|
|
47
|
+
name: "refs.resolve",
|
|
48
|
+
method: "POST",
|
|
49
|
+
path: "/api/refs/resolve",
|
|
50
|
+
summary: "resolve references {tokens?: [kind:slug], text?} to whether each target exists and where it opens",
|
|
51
|
+
errors: 400,
|
|
52
|
+
body: z.object({ tokens: z.array(z.string()).default([]), text: z.string().default("") }),
|
|
53
|
+
}, async (c) => {
|
|
54
|
+
const { tokens, text } = await c.body();
|
|
55
|
+
const given = tokens.map((t) => parseRef(t) ?? fail(400, `not a reference: "${t}" (expected <kind>:<slug>, e.g. contact:anna-meier)`));
|
|
56
|
+
const found = findRefs(text);
|
|
57
|
+
const all = [...given, ...found].filter((r, i, arr) => arr.findIndex((x) => x.kind === r.kind && x.slug === r.slug) === i);
|
|
58
|
+
return { refs: await resolveTokens(all) };
|
|
59
|
+
}),
|
|
60
|
+
];
|
|
@@ -20,4 +20,12 @@ export declare const fileRoutes: (import("./router.js").Route<"files.delete", {
|
|
|
20
20
|
}[];
|
|
21
21
|
}, "/api/files/recent", undefined> | import("./router.js").Route<"files.scopes", {
|
|
22
22
|
scopes: import("../../core/files.js").FilesScopeInfo[];
|
|
23
|
-
}, "/api/files", undefined> | import("./router.js").Route<"files.upload", import("./router.js").Reply<import("../../core/files.js").FileEntry>, "/api/files/upload", undefined>)
|
|
23
|
+
}, "/api/files", undefined> | import("./router.js").Route<"files.upload", import("./router.js").Reply<import("../../core/files.js").FileEntry>, "/api/files/upload", undefined> | import("./router.js").Route<"temp.clean", {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
removed: string[];
|
|
26
|
+
bytes: number;
|
|
27
|
+
}, "/api/temp", undefined> | import("./router.js").Route<"temp.list", {
|
|
28
|
+
entries: import("../../core/temp.js").TempEntry[];
|
|
29
|
+
bytes: number;
|
|
30
|
+
root: string;
|
|
31
|
+
}, "/api/temp", undefined>)[];
|
package/dist/server/api/files.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { deleteFile, filesSummary, listFiles, listScopes, makeFolder, moveFile, openFile, saveUpload } from "../../core/files.js";
|
|
3
|
+
import { cleanTemp, listTemp } from "../../core/temp.js";
|
|
4
|
+
import { tildify } from "../../core/instances.js";
|
|
3
5
|
import { reply, route } from "./router.js";
|
|
4
6
|
const errors = (message) => (/^no (file|folder|project)/.test(message) ? 404 : 400);
|
|
5
7
|
/** The scope (workspace, or a project's folder) and the path inside it, from the query. */
|
|
@@ -45,4 +47,15 @@ export const fileRoutes = [
|
|
|
45
47
|
await deleteFile(...where(c));
|
|
46
48
|
return { ok: true };
|
|
47
49
|
}),
|
|
50
|
+
// The temp root: one scratch folder per session (core/temp.ts). Nothing in it is a deliverable, so a clean deletes for good.
|
|
51
|
+
route({ name: "temp.list", method: "GET", path: "/api/temp", summary: "the sessions' scratch folders, newest first", errors }, async () => {
|
|
52
|
+
const view = await listTemp();
|
|
53
|
+
return { ...view, root: tildify(view.root) };
|
|
54
|
+
}),
|
|
55
|
+
route({ name: "temp.clean", method: "DELETE", path: "/api/temp", summary: "remove scratch folders idle a week ?olderThanDays, or every one ?all=1", errors }, async (c) => {
|
|
56
|
+
const raw = c.query.get("olderThanDays");
|
|
57
|
+
const days = raw === null ? NaN : Number(raw);
|
|
58
|
+
const gone = await cleanTemp({ all: c.query.get("all") === "1", ...(Number.isFinite(days) && days >= 0 ? { olderThanMs: days * 24 * 60 * 60 * 1000 } : {}) });
|
|
59
|
+
return { ok: true, removed: gone.map((e) => e.id), bytes: gone.reduce((n, e) => n + e.bytes, 0) };
|
|
60
|
+
}),
|
|
48
61
|
];
|
|
@@ -367,7 +367,20 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
367
367
|
claimCode?: string | null;
|
|
368
368
|
claimUrl?: string | null;
|
|
369
369
|
workspaces?: number;
|
|
370
|
-
}, "/api/cloud/unlink", undefined> | Route<"
|
|
370
|
+
}, "/api/cloud/unlink", undefined> | Route<"contacts.create", import("./router.js").Reply<import("../../core/entities.js").EntityDetail<import("../../core/entities.js").ContactDef>>, "/api/contacts", z.ZodObject<{
|
|
371
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
372
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
373
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
374
|
+
title: z.ZodString;
|
|
375
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
376
|
+
}, z.core.$loose>> | Route<"contacts.delete", {
|
|
377
|
+
ok: boolean;
|
|
378
|
+
}, "/api/contacts/:slug", undefined> | Route<"contacts.get", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").ContactDef>, "/api/contacts/:slug", undefined> | Route<"contacts.list", import("../../core/entities.js").EntitiesView<import("../../core/entities.js").ContactDef>, "/api/contacts", undefined> | Route<"contacts.save", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").ContactDef>, "/api/contacts/:slug", z.ZodObject<{
|
|
379
|
+
title: z.ZodOptional<z.ZodString>;
|
|
380
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
381
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
382
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
383
|
+
}, z.core.$loose>> | Route<"devices.code", {
|
|
371
384
|
code: string;
|
|
372
385
|
expiresAt: string;
|
|
373
386
|
urls: string[];
|
|
@@ -427,7 +440,20 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
427
440
|
}, "/api/devices/relay", z.ZodObject<{
|
|
428
441
|
enabled: z.ZodBoolean;
|
|
429
442
|
url: z.ZodOptional<z.ZodString>;
|
|
430
|
-
}, z.core.$strip>> | Route<"
|
|
443
|
+
}, z.core.$strip>> | Route<"events.create", import("./router.js").Reply<import("../../core/entities.js").EntityDetail<import("../../core/entities.js").EventDef>>, "/api/events", z.ZodObject<{
|
|
444
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
445
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
446
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
447
|
+
title: z.ZodString;
|
|
448
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
449
|
+
}, z.core.$loose>> | Route<"events.delete", {
|
|
450
|
+
ok: boolean;
|
|
451
|
+
}, "/api/events/:slug", undefined> | Route<"events.get", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").EventDef>, "/api/events/:slug", undefined> | Route<"events.list", import("../../core/entities.js").EntitiesView<import("../../core/entities.js").EventDef>, "/api/events", undefined> | Route<"events.save", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").EventDef>, "/api/events/:slug", z.ZodObject<{
|
|
452
|
+
title: z.ZodOptional<z.ZodString>;
|
|
453
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
454
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
455
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
456
|
+
}, z.core.$loose>> | Route<"files.delete", {
|
|
431
457
|
ok: boolean;
|
|
432
458
|
}, "/api/files", undefined> | Route<"files.list", import("../../core/files.js").FilesListing, "/api/files/list", undefined> | Route<"files.mkdir", import("./router.js").Reply<import("../../core/files.js").FileEntry>, "/api/files/folder", z.ZodObject<{
|
|
433
459
|
scope: z.ZodOptional<z.ZodString>;
|
|
@@ -520,6 +546,9 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
520
546
|
repos: boolean;
|
|
521
547
|
vibeables: boolean;
|
|
522
548
|
projects: boolean;
|
|
549
|
+
contacts: boolean;
|
|
550
|
+
places: boolean;
|
|
551
|
+
events: boolean;
|
|
523
552
|
publicUrl: string;
|
|
524
553
|
cloud: {
|
|
525
554
|
included: boolean;
|
|
@@ -545,7 +574,20 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
545
574
|
href: string;
|
|
546
575
|
}, "/api/notifications/:id/diagnose", undefined> | Route<"notifications.list", import("../../core/notifications.js").NotificationsView, "/api/notifications", undefined> | Route<"notifications.read", import("../../core/notifications.js").NotificationsView, "/api/notifications/read", z.ZodObject<{
|
|
547
576
|
ids: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
548
|
-
}, z.core.$strip>> | Route<"
|
|
577
|
+
}, z.core.$strip>> | Route<"places.create", import("./router.js").Reply<import("../../core/entities.js").EntityDetail<import("../../core/entities.js").PlaceDef>>, "/api/places", z.ZodObject<{
|
|
578
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
579
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
580
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
581
|
+
title: z.ZodString;
|
|
582
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
583
|
+
}, z.core.$loose>> | Route<"places.delete", {
|
|
584
|
+
ok: boolean;
|
|
585
|
+
}, "/api/places/:slug", undefined> | Route<"places.get", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").PlaceDef>, "/api/places/:slug", undefined> | Route<"places.list", import("../../core/entities.js").EntitiesView<import("../../core/entities.js").PlaceDef>, "/api/places", undefined> | Route<"places.save", import("../../core/entities.js").EntityDetail<import("../../core/entities.js").PlaceDef>, "/api/places/:slug", z.ZodObject<{
|
|
586
|
+
title: z.ZodOptional<z.ZodString>;
|
|
587
|
+
notes: z.ZodOptional<z.ZodString>;
|
|
588
|
+
tags: z.ZodOptional<z.ZodUnknown>;
|
|
589
|
+
records: z.ZodOptional<z.ZodUnknown>;
|
|
590
|
+
}, z.core.$loose>> | Route<"projects.create", import("./router.js").Reply<import("../../core/projects.js").ProjectDetail>, "/api/projects", z.ZodObject<{
|
|
549
591
|
title: z.ZodString;
|
|
550
592
|
goal: z.ZodOptional<z.ZodString>;
|
|
551
593
|
slug: z.ZodOptional<z.ZodString>;
|
|
@@ -576,6 +618,9 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
576
618
|
repos: z.ZodOptional<z.ZodUnknown>;
|
|
577
619
|
workflows: z.ZodOptional<z.ZodUnknown>;
|
|
578
620
|
agents: z.ZodOptional<z.ZodUnknown>;
|
|
621
|
+
contacts: z.ZodOptional<z.ZodUnknown>;
|
|
622
|
+
places: z.ZodOptional<z.ZodUnknown>;
|
|
623
|
+
events: z.ZodOptional<z.ZodUnknown>;
|
|
579
624
|
}, z.core.$loose>> | Route<"projects.saveLayout", {
|
|
580
625
|
ok: boolean;
|
|
581
626
|
layout: import("../../core/projects.js").ProjectLayout;
|
|
@@ -588,7 +633,12 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
588
633
|
}, z.core.$strip>> | Route<"protocol.get", {
|
|
589
634
|
version: number;
|
|
590
635
|
routes: RouteInfo[];
|
|
591
|
-
}, "/api/protocol", undefined> | Route<"
|
|
636
|
+
}, "/api/protocol", undefined> | Route<"refs.resolve", {
|
|
637
|
+
refs: import("../../core/refs.js").ResolvedRef[];
|
|
638
|
+
}, "/api/refs/resolve", z.ZodObject<{
|
|
639
|
+
tokens: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
640
|
+
text: z.ZodDefault<z.ZodString>;
|
|
641
|
+
}, z.core.$strip>> | Route<"repos.add", import("./router.js").Reply<import("../../core/repos.js").RepoInfo>, "/api/repos", z.ZodObject<{
|
|
592
642
|
url: z.ZodDefault<z.ZodString>;
|
|
593
643
|
name: z.ZodOptional<z.ZodString>;
|
|
594
644
|
branch: z.ZodOptional<z.ZodString>;
|
|
@@ -663,10 +713,30 @@ export declare const routes: (Route<"agents.addJournal", {
|
|
|
663
713
|
enabled: z.ZodBoolean;
|
|
664
714
|
root: z.ZodOptional<z.ZodString>;
|
|
665
715
|
}, z.core.$strip>>;
|
|
716
|
+
contacts: z.ZodOptional<z.ZodObject<{
|
|
717
|
+
enabled: z.ZodBoolean;
|
|
718
|
+
root: z.ZodOptional<z.ZodString>;
|
|
719
|
+
}, z.core.$strip>>;
|
|
720
|
+
places: z.ZodOptional<z.ZodObject<{
|
|
721
|
+
enabled: z.ZodBoolean;
|
|
722
|
+
root: z.ZodOptional<z.ZodString>;
|
|
723
|
+
}, z.core.$strip>>;
|
|
724
|
+
events: z.ZodOptional<z.ZodObject<{
|
|
725
|
+
enabled: z.ZodBoolean;
|
|
726
|
+
root: z.ZodOptional<z.ZodString>;
|
|
727
|
+
}, z.core.$strip>>;
|
|
666
728
|
}, z.core.$loose>> | Route<"skills.get", import("../../core/skills.js").SkillDetail, "/api/skills/:name", undefined> | Route<"skills.list", {
|
|
667
729
|
root: string;
|
|
668
730
|
skills: import("../../core/skills.js").SkillInfo[];
|
|
669
|
-
}, "/api/skills", undefined> | Route<"
|
|
731
|
+
}, "/api/skills", undefined> | Route<"temp.clean", {
|
|
732
|
+
ok: boolean;
|
|
733
|
+
removed: string[];
|
|
734
|
+
bytes: number;
|
|
735
|
+
}, "/api/temp", undefined> | Route<"temp.list", {
|
|
736
|
+
entries: import("../../core/temp.js").TempEntry[];
|
|
737
|
+
bytes: number;
|
|
738
|
+
root: string;
|
|
739
|
+
}, "/api/temp", undefined> | Route<"trash.empty", {
|
|
670
740
|
ok: boolean;
|
|
671
741
|
purged: number;
|
|
672
742
|
}, "/api/trash", undefined> | Route<"trash.list", {
|
package/dist/server/api/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { channelRoutes } from "./channels.js";
|
|
|
5
5
|
import { chatRoutes } from "./chats.js";
|
|
6
6
|
import { cloudRoutes } from "./cloud.js";
|
|
7
7
|
import { deviceRoutes } from "./devices.js";
|
|
8
|
+
import { contactRoutes, eventRoutes, placeRoutes, refRoutes } from "./entities.js";
|
|
8
9
|
import { hubRoutes } from "./hub.js";
|
|
9
10
|
import { fileRoutes } from "./files.js";
|
|
10
11
|
import { gitRoutes } from "./git.js";
|
|
@@ -30,6 +31,10 @@ const domainRoutes = [
|
|
|
30
31
|
...chatRoutes,
|
|
31
32
|
...channelRoutes,
|
|
32
33
|
...projectRoutes,
|
|
34
|
+
...contactRoutes,
|
|
35
|
+
...placeRoutes,
|
|
36
|
+
...eventRoutes,
|
|
37
|
+
...refRoutes,
|
|
33
38
|
...runRoutes,
|
|
34
39
|
...knowledgeRoutes,
|
|
35
40
|
...fileRoutes,
|
|
@@ -32,6 +32,9 @@ export declare const projectRoutes: (import("./router.js").Route<"projects.creat
|
|
|
32
32
|
repos: z.ZodOptional<z.ZodUnknown>;
|
|
33
33
|
workflows: z.ZodOptional<z.ZodUnknown>;
|
|
34
34
|
agents: z.ZodOptional<z.ZodUnknown>;
|
|
35
|
+
contacts: z.ZodOptional<z.ZodUnknown>;
|
|
36
|
+
places: z.ZodOptional<z.ZodUnknown>;
|
|
37
|
+
events: z.ZodOptional<z.ZodUnknown>;
|
|
35
38
|
}, z.core.$loose>> | import("./router.js").Route<"projects.saveLayout", {
|
|
36
39
|
ok: boolean;
|
|
37
40
|
layout: import("../../core/projects.js").ProjectLayout;
|
|
@@ -23,6 +23,9 @@ const ProjectFields = z.looseObject({
|
|
|
23
23
|
repos: z.unknown().optional(),
|
|
24
24
|
workflows: z.unknown().optional(),
|
|
25
25
|
agents: z.unknown().optional(),
|
|
26
|
+
contacts: z.unknown().optional(),
|
|
27
|
+
places: z.unknown().optional(),
|
|
28
|
+
events: z.unknown().optional(),
|
|
26
29
|
});
|
|
27
30
|
/** Projects: folders with a goal, a brief, a log and links, plus the rail's order. */
|
|
28
31
|
export const projectRoutes = [
|
|
@@ -33,6 +33,9 @@ export declare const workspaceRoutes: (import("./router.js").Route<"identity.get
|
|
|
33
33
|
repos: boolean;
|
|
34
34
|
vibeables: boolean;
|
|
35
35
|
projects: boolean;
|
|
36
|
+
contacts: boolean;
|
|
37
|
+
places: boolean;
|
|
38
|
+
events: boolean;
|
|
36
39
|
/** @deprecated Always "": the Cloudflare Tunnel that set it was removed in 0.65. Removed with protocol 2. */
|
|
37
40
|
publicUrl: string;
|
|
38
41
|
cloud: {
|
|
@@ -86,6 +89,18 @@ export declare const workspaceRoutes: (import("./router.js").Route<"identity.get
|
|
|
86
89
|
enabled: z.ZodBoolean;
|
|
87
90
|
root: z.ZodOptional<z.ZodString>;
|
|
88
91
|
}, z.core.$strip>>;
|
|
92
|
+
contacts: z.ZodOptional<z.ZodObject<{
|
|
93
|
+
enabled: z.ZodBoolean;
|
|
94
|
+
root: z.ZodOptional<z.ZodString>;
|
|
95
|
+
}, z.core.$strip>>;
|
|
96
|
+
places: z.ZodOptional<z.ZodObject<{
|
|
97
|
+
enabled: z.ZodBoolean;
|
|
98
|
+
root: z.ZodOptional<z.ZodString>;
|
|
99
|
+
}, z.core.$strip>>;
|
|
100
|
+
events: z.ZodOptional<z.ZodObject<{
|
|
101
|
+
enabled: z.ZodBoolean;
|
|
102
|
+
root: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, z.core.$strip>>;
|
|
89
104
|
}, z.core.$loose>> | import("./router.js").Route<"update.check", import("./router.js").Reply<{
|
|
90
105
|
name: string;
|
|
91
106
|
current: string;
|
|
@@ -33,6 +33,9 @@ const SettingsFields = z.looseObject({
|
|
|
33
33
|
repos: RootBlock.optional(),
|
|
34
34
|
vibeables: RootBlock.optional(),
|
|
35
35
|
projects: RootBlock.optional(),
|
|
36
|
+
contacts: RootBlock.optional(),
|
|
37
|
+
places: RootBlock.optional(),
|
|
38
|
+
events: RootBlock.optional(),
|
|
36
39
|
});
|
|
37
40
|
/**
|
|
38
41
|
* Mark the workspaces the hub answering this request serves — or, a daemon,
|
|
@@ -105,6 +108,9 @@ export const workspaceRoutes = [
|
|
|
105
108
|
repos: (ws?.config.repos && ws.config.repos.enabled !== false) === true,
|
|
106
109
|
vibeables: (ws?.config.vibeables && ws.config.vibeables.enabled !== false) === true,
|
|
107
110
|
projects: (ws?.config.projects && ws.config.projects.enabled !== false) === true,
|
|
111
|
+
contacts: (ws?.config.contacts && ws.config.contacts.enabled !== false) === true,
|
|
112
|
+
places: (ws?.config.places && ws.config.places.enabled !== false) === true,
|
|
113
|
+
events: (ws?.config.events && ws.config.events.enabled !== false) === true,
|
|
108
114
|
/** @deprecated Always "": the Cloudflare Tunnel that set it was removed in 0.65. Removed with protocol 2. */
|
|
109
115
|
publicUrl: "",
|
|
110
116
|
// The machine's cloud record (only the daemon registers one); `included`: this workspace rides along (no cloud.enabled: false veto).
|
package/dist/server/cloud.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
|
+
import { log } from "../core/log.js";
|
|
2
3
|
import os from "node:os";
|
|
3
4
|
import { resolveWorkspace } from "../config.js";
|
|
4
5
|
import { claimUrlFor, machineCloud, machineGoodbye, machineHeartbeat, machineRegister, machineUnlink, MAX_REPORTED_ROUTINES, retiredCloudDir, unknownMachine, workspaceInvite, } from "../core/cloud.js";
|
|
@@ -74,10 +75,10 @@ function warnYml(cloud, ymlUrls) {
|
|
|
74
75
|
return;
|
|
75
76
|
m.warned.add(key);
|
|
76
77
|
if (cloud.source === "kraftwerk.yml") {
|
|
77
|
-
|
|
78
|
+
log("warn", "cloud", `kraftwerk.yml cloud.url is deprecated — the cloud is the machine's now: \`kraftwerk cloud on --url ${cloud.url}\` (using ${cloud.url} for now)`);
|
|
78
79
|
}
|
|
79
80
|
else {
|
|
80
|
-
|
|
81
|
+
log("warn", "cloud", `kraftwerk.yml cloud.url (${ymlUrls[0]}) is ignored: the machine's cloud is ${cloud.url} (${cloud.source}); remove it from kraftwerk.yml`);
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
async function register() {
|
package/dist/server/relay.d.ts
CHANGED
|
@@ -93,14 +93,15 @@ export interface RelayOptions {
|
|
|
93
93
|
connect?: (url: string) => WebSocket;
|
|
94
94
|
/** Give up on a connection the relay has not accepted (`ok`) within this long, and try again (default 15 s). */
|
|
95
95
|
handshakeTimeoutMs?: number;
|
|
96
|
-
log
|
|
96
|
+
/** One relay event, leveled — the daemon writes it as a standard log line. */
|
|
97
|
+
log?: (level: "info" | "warn", line: string) => void;
|
|
97
98
|
}
|
|
98
99
|
/** Hold the daemon's socket to the relay, reconnecting with backoff, and answer every client behind it. */
|
|
99
100
|
export declare function startRelay(opts: RelayOptions): Promise<RelayLink>;
|
|
100
101
|
/** A short fingerprint of the machine key, to compare what a phone shows with what this machine shows. */
|
|
101
102
|
export declare const keyFingerprint: (key: string) => string;
|
|
102
103
|
/** Start, restart or stop this daemon's relay link to match ~/.kraftwerk/relay.json. */
|
|
103
|
-
export declare function applyRelay(port: number, version: string, log?: (line: string) => void): Promise<RelayStatus>;
|
|
104
|
+
export declare function applyRelay(port: number, version: string, log?: (level: "info" | "warn", line: string) => void): Promise<RelayStatus>;
|
|
104
105
|
/** Hand push notifications to this daemon's relay link; false without one. */
|
|
105
106
|
export declare const relayPush: (pushes: PushMessage[]) => boolean;
|
|
106
107
|
/** The link's state; "off" when this process holds none (not the daemon, or the relay is off). */
|
package/dist/server/relay.js
CHANGED
|
@@ -188,7 +188,7 @@ export async function startRelay(opts) {
|
|
|
188
188
|
send({ t: "msg", c, d: JSON.stringify(ready) });
|
|
189
189
|
}
|
|
190
190
|
catch (err) {
|
|
191
|
-
log(`
|
|
191
|
+
log("warn", `handshake refused — ${err.message}`);
|
|
192
192
|
send({ t: "close", c, reason: "handshake failed" });
|
|
193
193
|
dropConn(c);
|
|
194
194
|
}
|
|
@@ -216,7 +216,7 @@ export async function startRelay(opts) {
|
|
|
216
216
|
return;
|
|
217
217
|
}
|
|
218
218
|
if (!r.ok) {
|
|
219
|
-
log(`
|
|
219
|
+
log("warn", `member refused — ${r.error}`);
|
|
220
220
|
await reply(c, conn, { t: "auth", ok: false, error: r.error });
|
|
221
221
|
send({ t: "close", c, reason: r.error.slice(0, 100) });
|
|
222
222
|
dropConn(c);
|
|
@@ -234,7 +234,7 @@ export async function startRelay(opts) {
|
|
|
234
234
|
if (!check.ok) {
|
|
235
235
|
await reply(c, conn, { t: "fail", id: msg.id, error: check.error });
|
|
236
236
|
if (check.ended) {
|
|
237
|
-
log(`
|
|
237
|
+
log("warn", `member session dropped — ${check.error}`);
|
|
238
238
|
send({ t: "close", c, reason: check.error.slice(0, 100) });
|
|
239
239
|
dropConn(c);
|
|
240
240
|
}
|
|
@@ -282,7 +282,7 @@ export async function startRelay(opts) {
|
|
|
282
282
|
catch { }
|
|
283
283
|
status.state = "error";
|
|
284
284
|
status.error = "the relay did not answer";
|
|
285
|
-
log("
|
|
285
|
+
log("warn", "no answer from the relay — trying again");
|
|
286
286
|
schedule();
|
|
287
287
|
}, opts.handshakeTimeoutMs ?? 15_000);
|
|
288
288
|
handshake.unref?.();
|
|
@@ -305,7 +305,7 @@ export async function startRelay(opts) {
|
|
|
305
305
|
status.state = "connected";
|
|
306
306
|
status.error = undefined;
|
|
307
307
|
status.since = new Date().toISOString();
|
|
308
|
-
log(`
|
|
308
|
+
log("info", `connected to ${opts.settings.url} as ${id}`);
|
|
309
309
|
}
|
|
310
310
|
else if (msg.t === "open" && msg.c) {
|
|
311
311
|
dropConn(msg.c);
|
|
@@ -337,11 +337,11 @@ export async function startRelay(opts) {
|
|
|
337
337
|
if (stopped)
|
|
338
338
|
return;
|
|
339
339
|
if (status.state === "connected")
|
|
340
|
-
log("
|
|
340
|
+
log("warn", "connection lost — reconnecting");
|
|
341
341
|
status.state = "error";
|
|
342
342
|
status.error = ev.code === 4401 ? `the relay refused this machine: ${ev.reason || "wrong secret"}` : ev.reason || "the relay connection closed";
|
|
343
343
|
if (ev.code === 4401)
|
|
344
|
-
log(
|
|
344
|
+
log("warn", `${status.error}`);
|
|
345
345
|
schedule();
|
|
346
346
|
});
|
|
347
347
|
socket.addEventListener("error", () => { });
|
package/dist/server/server.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
|
+
import { log } from "../core/log.js";
|
|
2
3
|
import http from "node:http";
|
|
3
4
|
import net from "node:net";
|
|
4
5
|
import path from "node:path";
|
|
@@ -145,7 +146,7 @@ function installExitHandlers() {
|
|
|
145
146
|
process.on("uncaughtException", (err) => {
|
|
146
147
|
if (closedOnly(err))
|
|
147
148
|
return;
|
|
148
|
-
|
|
149
|
+
log("error", "server", `crashed: ${err instanceof Error ? (err.stack ?? err.message) : String(err)}`);
|
|
149
150
|
process.exit(1);
|
|
150
151
|
});
|
|
151
152
|
// Chat agent subprocesses must die with the server — signals bypass "exit" handlers, so hook the signals themselves.
|
|
@@ -377,7 +378,7 @@ export async function startHub(opts) {
|
|
|
377
378
|
await opts.onChange?.(hub);
|
|
378
379
|
}
|
|
379
380
|
catch (err) {
|
|
380
|
-
|
|
381
|
+
log("warn", "daemon", `could not note the open workspaces: ${err.message}`);
|
|
381
382
|
}
|
|
382
383
|
}
|
|
383
384
|
async function openNow(root, o) {
|
package/dist/yaml.js
CHANGED
|
@@ -293,6 +293,7 @@ export async function loadWorkflow(givenPath) {
|
|
|
293
293
|
verbose,
|
|
294
294
|
workspaceContext: [
|
|
295
295
|
`Working directory (read and create all files here): ${runDir}`,
|
|
296
|
+
`Temporary files (scratch, downloads, intermediate output, one-off scripts) go to ${runDir}/temp — TMPDIR and KRAFTWERK_TEMP point there; nothing in it counts as a result of the run.`,
|
|
296
297
|
workspace,
|
|
297
298
|
]
|
|
298
299
|
.filter(Boolean)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netnodeag/kraftwerk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.81.0",
|
|
4
4
|
"description": "Open-source agentic workspace for teams \u2014 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",
|
|
@@ -171,6 +171,60 @@
|
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
173
|
},
|
|
174
|
+
"contacts": {
|
|
175
|
+
"type": [
|
|
176
|
+
"object",
|
|
177
|
+
"null"
|
|
178
|
+
],
|
|
179
|
+
"additionalProperties": false,
|
|
180
|
+
"description": "Contacts: people and organisations the workspace works with, one Markdown file (YAML frontmatter + notes) each under the root, part of the workspace. Referenced as contact:<slug> from projects, events and text. Absent = feature off, a bare `contacts:` = on with defaults.",
|
|
181
|
+
"properties": {
|
|
182
|
+
"enabled": {
|
|
183
|
+
"type": "boolean",
|
|
184
|
+
"description": "Turn the feature off while keeping the config. Default: true when this block exists"
|
|
185
|
+
},
|
|
186
|
+
"root": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"description": "Where the contact files live, relative to the project root. Default: kraftwerk-data/contacts"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
},
|
|
192
|
+
"places": {
|
|
193
|
+
"type": [
|
|
194
|
+
"object",
|
|
195
|
+
"null"
|
|
196
|
+
],
|
|
197
|
+
"additionalProperties": false,
|
|
198
|
+
"description": "Places: addresses and points of interest, one Markdown file (YAML frontmatter + notes) each under the root, part of the workspace. Referenced as place:<slug>; a Google Maps link is derived from coordinates or the address. Absent = feature off, a bare `places:` = on with defaults.",
|
|
199
|
+
"properties": {
|
|
200
|
+
"enabled": {
|
|
201
|
+
"type": "boolean",
|
|
202
|
+
"description": "Turn the feature off while keeping the config. Default: true when this block exists"
|
|
203
|
+
},
|
|
204
|
+
"root": {
|
|
205
|
+
"type": "string",
|
|
206
|
+
"description": "Where the place files live, relative to the project root. Default: kraftwerk-data/places"
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
"events": {
|
|
211
|
+
"type": [
|
|
212
|
+
"object",
|
|
213
|
+
"null"
|
|
214
|
+
],
|
|
215
|
+
"additionalProperties": false,
|
|
216
|
+
"description": "Events: dated, one-off entries (a meeting, a launch, a deadline) with a place, contacts and a project, one Markdown file (YAML frontmatter + notes) each under the root, part of the workspace. Referenced as event:<slug>. Absent = feature off, a bare `events:` = on with defaults.",
|
|
217
|
+
"properties": {
|
|
218
|
+
"enabled": {
|
|
219
|
+
"type": "boolean",
|
|
220
|
+
"description": "Turn the feature off while keeping the config. Default: true when this block exists"
|
|
221
|
+
},
|
|
222
|
+
"root": {
|
|
223
|
+
"type": "string",
|
|
224
|
+
"description": "Where the event files live, relative to the project root. Default: kraftwerk-data/events"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
174
228
|
"cloud": {
|
|
175
229
|
"type": [
|
|
176
230
|
"object",
|