forge-openclaw-plugin 0.2.26 → 0.2.28
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 +60 -3
- package/dist/assets/{board-ta0rUHOf.js → board-DPFvZf-D.js} +2 -2
- package/dist/assets/{board-ta0rUHOf.js.map → board-DPFvZf-D.js.map} +1 -1
- package/dist/assets/index-Auw3JrdE.css +1 -0
- package/dist/assets/index-D1H7myQH.js +85 -0
- package/dist/assets/index-D1H7myQH.js.map +1 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js +2 -0
- package/dist/assets/knowledge-graph-layout.worker-DRvzPxhP.js.map +1 -0
- package/dist/assets/{motion-fBKPB6yw.js → motion-Bvwc85ch.js} +2 -2
- package/dist/assets/{motion-fBKPB6yw.js.map → motion-Bvwc85ch.js.map} +1 -1
- package/dist/assets/{table-C-IGTQni.js → table-FJQTJvUR.js} +2 -2
- package/dist/assets/{table-C-IGTQni.js.map → table-FJQTJvUR.js.map} +1 -1
- package/dist/assets/{ui-DInOpaYF.js → ui-GXFcgvSw.js} +2 -2
- package/dist/assets/{ui-DInOpaYF.js.map → ui-GXFcgvSw.js.map} +1 -1
- package/dist/assets/vendor-Cwf49UMz.js +1247 -0
- package/dist/assets/vendor-Cwf49UMz.js.map +1 -0
- package/dist/index.html +7 -7
- package/dist/openclaw/local-runtime.js +16 -0
- package/dist/openclaw/routes.d.ts +27 -0
- package/dist/openclaw/routes.js +16 -12
- package/dist/server/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/dist/server/server/migrations/038_data_management_settings.sql +11 -0
- package/dist/server/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/dist/server/server/migrations/040_screen_time_domain.sql +89 -0
- package/dist/server/server/migrations/041_companion_source_states.sql +21 -0
- package/dist/server/server/migrations/042_movement_boxes.sql +47 -0
- package/dist/server/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/dist/server/server/src/app.js +1900 -91
- package/dist/server/server/src/connectors/box-registry.js +44 -9
- package/dist/server/server/src/data-management-types.js +107 -0
- package/dist/server/server/src/db.js +68 -4
- package/dist/server/server/src/demo-data.js +2 -2
- package/dist/server/server/src/health.js +702 -18
- package/dist/server/server/src/managers/platform/llm-manager.js +7 -4
- package/dist/server/server/src/managers/platform/mock-workbench-provider.js +149 -0
- package/dist/server/server/src/managers/platform/secrets-manager.js +18 -1
- package/dist/server/server/src/managers/runtime.js +9 -0
- package/dist/server/server/src/movement.js +1971 -112
- package/dist/server/server/src/openapi.js +1390 -105
- package/dist/server/server/src/psyche-types.js +9 -1
- package/dist/server/server/src/repositories/activity-events.js +8 -0
- package/dist/server/server/src/repositories/ai-connectors.js +522 -74
- package/dist/server/server/src/repositories/calendar.js +151 -0
- package/dist/server/server/src/repositories/habits.js +37 -1
- package/dist/server/server/src/repositories/model-settings.js +13 -3
- package/dist/server/server/src/repositories/notes.js +3 -0
- package/dist/server/server/src/repositories/settings.js +380 -18
- package/dist/server/server/src/repositories/tasks.js +170 -10
- package/dist/server/server/src/runtime-data-root.js +82 -0
- package/dist/server/server/src/screen-time.js +802 -0
- package/dist/server/server/src/services/data-management.js +788 -0
- package/dist/server/server/src/services/entity-crud.js +205 -2
- package/dist/server/server/src/services/knowledge-graph.js +1455 -0
- package/dist/server/server/src/services/life-force-model.js +217 -0
- package/dist/server/server/src/services/life-force.js +2506 -0
- package/dist/server/server/src/services/psyche-observation-calendar.js +383 -16
- package/dist/server/server/src/types.js +307 -14
- package/dist/server/server/src/web.js +228 -13
- package/dist/server/src/components/customization/utility-widgets.js +136 -27
- package/dist/server/src/components/ui/info-tooltip.js +25 -0
- package/dist/server/src/components/workbench-boxes/calendar/calendar-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/goals/goals-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/habits/habits-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/health/health-boxes.js +63 -8
- package/dist/server/src/components/workbench-boxes/insights/insights-boxes.js +50 -0
- package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +62 -54
- package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +18 -8
- package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +56 -38
- package/dist/server/src/components/workbench-boxes/overview/overview-boxes.js +65 -0
- package/dist/server/src/components/workbench-boxes/preferences/preferences-boxes.js +78 -0
- package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +35 -30
- package/dist/server/src/components/workbench-boxes/psyche/psyche-boxes.js +88 -0
- package/dist/server/src/components/workbench-boxes/questionnaires/questionnaires-boxes.js +61 -0
- package/dist/server/src/components/workbench-boxes/review/review-boxes.js +53 -0
- package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +3 -1
- package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +39 -3
- package/dist/server/src/components/workbench-boxes/strategies/strategies-boxes.js +62 -0
- package/dist/server/src/components/workbench-boxes/tasks/tasks-boxes.js +76 -0
- package/dist/server/src/components/workbench-boxes/today/today-boxes.js +47 -32
- package/dist/server/src/components/workbench-boxes/wiki/wiki-boxes.js +60 -0
- package/dist/server/src/lib/api.js +280 -21
- package/dist/server/src/lib/data-management-types.js +1 -0
- package/dist/server/src/lib/entity-visuals.js +279 -0
- package/dist/server/src/lib/knowledge-graph-types.js +276 -0
- package/dist/server/src/lib/knowledge-graph.js +470 -0
- package/dist/server/src/lib/schemas.js +4 -0
- package/dist/server/src/lib/snapshot-normalizer.js +45 -1
- package/dist/server/src/lib/workbench/contracts.js +229 -0
- package/dist/server/src/lib/workbench/nodes.js +200 -0
- package/dist/server/src/lib/workbench/registry.js +52 -5
- package/dist/server/src/lib/workbench/runtime.js +254 -38
- package/dist/server/src/lib/workbench/tool-catalog.js +68 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/037_workbench_public_inputs_and_run_inputs.sql +5 -0
- package/server/migrations/038_data_management_settings.sql +11 -0
- package/server/migrations/039_life_force_and_action_points.sql +114 -0
- package/server/migrations/040_screen_time_domain.sql +89 -0
- package/server/migrations/041_companion_source_states.sql +21 -0
- package/server/migrations/042_movement_boxes.sql +47 -0
- package/server/migrations/043_movement_box_overlap_overrides.sql +26 -0
- package/skills/forge-openclaw/SKILL.md +41 -11
- package/skills/forge-openclaw/entity_conversation_playbooks.md +448 -34
- package/skills/forge-openclaw/psyche_entity_playbooks.md +170 -17
- package/dist/assets/index-Ro0ZF_az.css +0 -1
- package/dist/assets/index-ytlpSj23.js +0 -79
- package/dist/assets/index-ytlpSj23.js.map +0 -1
- package/dist/assets/vendor-lE3tZJcC.js +0 -876
- package/dist/assets/vendor-lE3tZJcC.js.map +0 -1
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
1
3
|
import { access, readFile } from "node:fs/promises";
|
|
2
4
|
import path from "node:path";
|
|
5
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
3
6
|
const distDir = path.join(process.cwd(), "dist");
|
|
4
7
|
const packagedRuntimeDistDir = path.join(process.cwd(), "plugins", "forge-codex", "runtime", "dist");
|
|
5
8
|
const contentTypes = {
|
|
@@ -17,7 +20,9 @@ function normalizeBasePath(value) {
|
|
|
17
20
|
return "/";
|
|
18
21
|
}
|
|
19
22
|
const withLeadingSlash = value.startsWith("/") ? value : `/${value}`;
|
|
20
|
-
return withLeadingSlash.endsWith("/")
|
|
23
|
+
return withLeadingSlash.endsWith("/")
|
|
24
|
+
? withLeadingSlash
|
|
25
|
+
: `${withLeadingSlash}/`;
|
|
21
26
|
}
|
|
22
27
|
function normalizeAbsoluteUrl(value) {
|
|
23
28
|
const url = new URL(value);
|
|
@@ -31,6 +36,56 @@ function getDevWebOrigin() {
|
|
|
31
36
|
const value = process.env.FORGE_DEV_WEB_ORIGIN?.trim();
|
|
32
37
|
return value && value.length > 0 ? value : null;
|
|
33
38
|
}
|
|
39
|
+
function shouldAutostartDevWeb(env) {
|
|
40
|
+
const value = env.FORGE_DEV_WEB_AUTOSTART?.trim().toLowerCase();
|
|
41
|
+
return value !== "0" && value !== "false" && value !== "no";
|
|
42
|
+
}
|
|
43
|
+
function getDevWebCommand(env) {
|
|
44
|
+
const value = env.FORGE_DEV_WEB_COMMAND?.trim();
|
|
45
|
+
return value && value.length > 0 ? value : "npm run dev:web";
|
|
46
|
+
}
|
|
47
|
+
function getDefaultDevWebOriginPort(origin) {
|
|
48
|
+
if (origin?.port && origin.port.trim().length > 0) {
|
|
49
|
+
return origin.port;
|
|
50
|
+
}
|
|
51
|
+
if (origin?.protocol === "https:") {
|
|
52
|
+
return "443";
|
|
53
|
+
}
|
|
54
|
+
return "3027";
|
|
55
|
+
}
|
|
56
|
+
function getDefaultViteCliPath(cwd) {
|
|
57
|
+
const candidate = path.join(cwd, "node_modules", "vite", "bin", "vite.js");
|
|
58
|
+
return existsSync(candidate) ? candidate : null;
|
|
59
|
+
}
|
|
60
|
+
function buildManagedDevWebLaunch(input) {
|
|
61
|
+
const explicitCommand = input.env.FORGE_DEV_WEB_COMMAND?.trim();
|
|
62
|
+
if (explicitCommand && explicitCommand.length > 0) {
|
|
63
|
+
return {
|
|
64
|
+
command: explicitCommand,
|
|
65
|
+
env: input.env,
|
|
66
|
+
shell: true
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
const viteCliPath = getDefaultViteCliPath(input.cwd);
|
|
70
|
+
if (!viteCliPath) {
|
|
71
|
+
return {
|
|
72
|
+
command: getDevWebCommand(input.env),
|
|
73
|
+
env: input.env,
|
|
74
|
+
shell: true
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
const host = input.env.FORGE_DEV_WEB_HOST?.trim() || "127.0.0.1";
|
|
78
|
+
const port = input.env.FORGE_DEV_WEB_PORT?.trim() || getDefaultDevWebOriginPort(input.origin);
|
|
79
|
+
return {
|
|
80
|
+
command: process.execPath,
|
|
81
|
+
args: [viteCliPath, "--host", host, "--port", port],
|
|
82
|
+
env: {
|
|
83
|
+
...input.env,
|
|
84
|
+
FORGE_BASE_PATH: getDefaultBasePath()
|
|
85
|
+
},
|
|
86
|
+
shell: false
|
|
87
|
+
};
|
|
88
|
+
}
|
|
34
89
|
function stripBasePath(requestPath, basePath) {
|
|
35
90
|
const normalizedBasePath = normalizeBasePath(basePath);
|
|
36
91
|
if (normalizedBasePath === "/") {
|
|
@@ -63,19 +118,174 @@ async function getClientDir() {
|
|
|
63
118
|
return packagedRuntimeDistDir;
|
|
64
119
|
}
|
|
65
120
|
}
|
|
66
|
-
|
|
67
|
-
|
|
121
|
+
function parseRequestTarget(requestPath) {
|
|
122
|
+
return new URL(requestPath, "http://forge.local");
|
|
123
|
+
}
|
|
124
|
+
function copyProxyHeaders(response, reply) {
|
|
125
|
+
for (const [name, value] of response.headers) {
|
|
126
|
+
const lowerName = name.toLowerCase();
|
|
127
|
+
if (lowerName === "connection" ||
|
|
128
|
+
lowerName === "content-length" ||
|
|
129
|
+
lowerName === "keep-alive" ||
|
|
130
|
+
lowerName === "transfer-encoding") {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
reply.header(name, value);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
async function proxyDevAsset(input) {
|
|
137
|
+
const target = new URL(input.pathname.startsWith("/") ? input.pathname.slice(1) : input.pathname, input.origin);
|
|
138
|
+
target.search = input.search;
|
|
139
|
+
const response = await input.fetchImpl(target, { redirect: "manual" });
|
|
140
|
+
input.reply.code(response.status);
|
|
141
|
+
copyProxyHeaders(response, input.reply);
|
|
142
|
+
if (!response.headers.has("cache-control")) {
|
|
143
|
+
input.reply.header("Cache-Control", "no-store, max-age=0, must-revalidate");
|
|
144
|
+
}
|
|
145
|
+
if (!response.body) {
|
|
146
|
+
return "";
|
|
147
|
+
}
|
|
148
|
+
return Buffer.from(await response.arrayBuffer());
|
|
149
|
+
}
|
|
150
|
+
async function waitForProcessExit(child, timeoutMs = 5_000) {
|
|
151
|
+
if (child.exitCode !== null) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
await Promise.race([
|
|
155
|
+
new Promise((resolve) => {
|
|
156
|
+
child.once("exit", () => resolve());
|
|
157
|
+
child.once("close", () => resolve());
|
|
158
|
+
}),
|
|
159
|
+
delay(timeoutMs).then(() => { })
|
|
160
|
+
]);
|
|
161
|
+
}
|
|
162
|
+
export function createManagedDevWebRuntime(options = {}) {
|
|
163
|
+
const env = options.env ?? process.env;
|
|
164
|
+
const originValue = env.FORGE_DEV_WEB_ORIGIN?.trim();
|
|
165
|
+
const origin = originValue ? normalizeAbsoluteUrl(originValue) : null;
|
|
166
|
+
const cwd = options.cwd ?? process.cwd();
|
|
167
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
168
|
+
const spawnImpl = options.spawnImpl ?? spawn;
|
|
169
|
+
const autostart = shouldAutostartDevWeb(env);
|
|
170
|
+
const waitTimeoutMs = Number(env.FORGE_DEV_WEB_START_TIMEOUT_MS ?? 30_000);
|
|
171
|
+
const pollIntervalMs = 500;
|
|
172
|
+
let child = null;
|
|
173
|
+
let startupPromise = null;
|
|
174
|
+
async function probe() {
|
|
175
|
+
if (!origin) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const controller = new AbortController();
|
|
179
|
+
const timeout = setTimeout(() => controller.abort(), 1_500);
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetchImpl(origin, {
|
|
182
|
+
method: "GET",
|
|
183
|
+
redirect: "manual",
|
|
184
|
+
signal: controller.signal
|
|
185
|
+
});
|
|
186
|
+
return response.status < 500 ? origin : null;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
clearTimeout(timeout);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function waitUntilReady(processRef) {
|
|
196
|
+
const startedAt = Date.now();
|
|
197
|
+
while (Date.now() - startedAt < waitTimeoutMs) {
|
|
198
|
+
const readyOrigin = await probe();
|
|
199
|
+
if (readyOrigin) {
|
|
200
|
+
return readyOrigin;
|
|
201
|
+
}
|
|
202
|
+
if (processRef.exitCode !== null) {
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
await delay(pollIntervalMs);
|
|
206
|
+
}
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
async function ensureReady() {
|
|
210
|
+
if (!origin) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const readyOrigin = await probe();
|
|
214
|
+
if (readyOrigin || !autostart) {
|
|
215
|
+
return readyOrigin;
|
|
216
|
+
}
|
|
217
|
+
if (!startupPromise) {
|
|
218
|
+
startupPromise = (async () => {
|
|
219
|
+
if (!child || child.exitCode !== null) {
|
|
220
|
+
const launch = buildManagedDevWebLaunch({ cwd, env, origin });
|
|
221
|
+
const nextChild = launch.shell
|
|
222
|
+
? spawnImpl(launch.command, {
|
|
223
|
+
cwd,
|
|
224
|
+
env: launch.env,
|
|
225
|
+
shell: true,
|
|
226
|
+
stdio: "inherit"
|
|
227
|
+
})
|
|
228
|
+
: spawnImpl(launch.command, launch.args ?? [], {
|
|
229
|
+
cwd,
|
|
230
|
+
env: launch.env,
|
|
231
|
+
stdio: "inherit"
|
|
232
|
+
});
|
|
233
|
+
child = nextChild;
|
|
234
|
+
nextChild.once("exit", () => {
|
|
235
|
+
if (child === nextChild) {
|
|
236
|
+
child = null;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const startedOrigin = await waitUntilReady(child);
|
|
241
|
+
startupPromise = null;
|
|
242
|
+
return startedOrigin;
|
|
243
|
+
})().catch((error) => {
|
|
244
|
+
startupPromise = null;
|
|
245
|
+
throw error;
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
return startupPromise;
|
|
249
|
+
}
|
|
250
|
+
async function stop() {
|
|
251
|
+
if (!child || child.exitCode !== null) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
const childRef = child;
|
|
255
|
+
childRef.kill("SIGTERM");
|
|
256
|
+
await waitForProcessExit(childRef);
|
|
257
|
+
if (childRef.exitCode === null) {
|
|
258
|
+
childRef.kill("SIGKILL");
|
|
259
|
+
await waitForProcessExit(childRef, 1_000);
|
|
260
|
+
}
|
|
261
|
+
child = null;
|
|
262
|
+
}
|
|
263
|
+
return {
|
|
264
|
+
ensureReady,
|
|
265
|
+
stop
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
async function serveAsset(requestPath, reply, options) {
|
|
269
|
+
const requestTarget = parseRequestTarget(requestPath);
|
|
270
|
+
if (requestTarget.pathname.startsWith("/api")) {
|
|
68
271
|
reply.code(404);
|
|
69
272
|
return { error: "Not found" };
|
|
70
273
|
}
|
|
71
|
-
const normalizedRequestPath = stripBasePath(
|
|
72
|
-
const devWebOrigin =
|
|
274
|
+
const normalizedRequestPath = stripBasePath(requestTarget.pathname, getDefaultBasePath());
|
|
275
|
+
const devWebOrigin = await options.devWebRuntime.ensureReady();
|
|
73
276
|
if (devWebOrigin) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
277
|
+
try {
|
|
278
|
+
return await proxyDevAsset({
|
|
279
|
+
origin: devWebOrigin,
|
|
280
|
+
pathname: normalizedRequestPath,
|
|
281
|
+
search: requestTarget.search,
|
|
282
|
+
reply,
|
|
283
|
+
fetchImpl: options.fetchImpl
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
reply.header("X-Forge-Web-Fallback", "built");
|
|
288
|
+
}
|
|
79
289
|
}
|
|
80
290
|
const clientDir = await getClientDir();
|
|
81
291
|
const assetPath = resolveAsset(clientDir, normalizedRequestPath);
|
|
@@ -111,7 +321,12 @@ async function serveAsset(requestPath, reply) {
|
|
|
111
321
|
return { error: "Asset not found" };
|
|
112
322
|
}
|
|
113
323
|
}
|
|
114
|
-
export async function registerWebRoutes(app) {
|
|
115
|
-
|
|
116
|
-
|
|
324
|
+
export async function registerWebRoutes(app, options = {}) {
|
|
325
|
+
const devWebRuntime = options.devWebRuntime ?? createManagedDevWebRuntime();
|
|
326
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
327
|
+
app.addHook("onClose", async () => {
|
|
328
|
+
await devWebRuntime.stop();
|
|
329
|
+
});
|
|
330
|
+
app.get("/", async (_request, reply) => serveAsset("/", reply, { devWebRuntime, fetchImpl }));
|
|
331
|
+
app.get("/*", async (request, reply) => serveAsset(request.url, reply, { devWebRuntime, fetchImpl }));
|
|
117
332
|
}
|
|
@@ -4,6 +4,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
4
4
|
import { CalendarDays, CloudSun, ExternalLink, Music4, NotebookPen, Save, TimerReset } from "lucide-react";
|
|
5
5
|
import { createNote, createWikiPage } from "../../lib/api.js";
|
|
6
6
|
import { buildStaticWorkbenchExecution } from "../../lib/workbench/runtime.js";
|
|
7
|
+
import { createContextOutput, createNoteTool, createSummaryOutput } from "../../lib/workbench/contracts.js";
|
|
7
8
|
import { cn } from "../../lib/utils.js";
|
|
8
9
|
import { createGenericWorkbenchNodeView } from "../workbench-boxes/shared/generic-node-view.js";
|
|
9
10
|
function formatMonthGrid(baseDate) {
|
|
@@ -175,19 +176,43 @@ const timeWidgetDefinition = {
|
|
|
175
176
|
tags: ["utility", "clock"],
|
|
176
177
|
inputs: [],
|
|
177
178
|
params: [],
|
|
178
|
-
output: [
|
|
179
|
+
output: [
|
|
180
|
+
createSummaryOutput({
|
|
181
|
+
label: "Current time",
|
|
182
|
+
description: "Formatted local time string published by the clock widget."
|
|
183
|
+
}),
|
|
184
|
+
createContextOutput({
|
|
185
|
+
key: "clock",
|
|
186
|
+
label: "Clock state",
|
|
187
|
+
description: "Structured clock state including the current ISO timestamp.",
|
|
188
|
+
modelName: "ForgeClockState"
|
|
189
|
+
})
|
|
190
|
+
],
|
|
179
191
|
tools: [],
|
|
180
192
|
NodeView: createGenericWorkbenchNodeView({
|
|
181
193
|
title: "Clock",
|
|
182
194
|
description: "Live local time widget.",
|
|
183
195
|
inputs: [],
|
|
184
196
|
params: [],
|
|
185
|
-
output: [
|
|
197
|
+
output: [
|
|
198
|
+
createSummaryOutput({
|
|
199
|
+
label: "Current time",
|
|
200
|
+
description: "Formatted local time string published by the clock widget."
|
|
201
|
+
}),
|
|
202
|
+
createContextOutput({
|
|
203
|
+
key: "clock",
|
|
204
|
+
label: "Clock state",
|
|
205
|
+
description: "Structured clock state including the current ISO timestamp.",
|
|
206
|
+
modelName: "ForgeClockState"
|
|
207
|
+
})
|
|
208
|
+
],
|
|
186
209
|
tools: []
|
|
187
210
|
}),
|
|
188
211
|
WebView: TimeWidget,
|
|
189
212
|
execute: (input) => buildStaticWorkbenchExecution(input, {
|
|
190
|
-
|
|
213
|
+
clock: {
|
|
214
|
+
now: input.context.now
|
|
215
|
+
}
|
|
191
216
|
}, new Intl.DateTimeFormat(undefined, {
|
|
192
217
|
hour: "2-digit",
|
|
193
218
|
minute: "2-digit"
|
|
@@ -205,19 +230,43 @@ const calendarWidgetDefinition = {
|
|
|
205
230
|
tags: ["utility", "calendar"],
|
|
206
231
|
inputs: [],
|
|
207
232
|
params: [],
|
|
208
|
-
output: [
|
|
233
|
+
output: [
|
|
234
|
+
createSummaryOutput({
|
|
235
|
+
label: "Month view",
|
|
236
|
+
description: "Summary of the compact month calendar widget."
|
|
237
|
+
}),
|
|
238
|
+
createContextOutput({
|
|
239
|
+
key: "calendarView",
|
|
240
|
+
label: "Calendar view",
|
|
241
|
+
description: "Structured mini-calendar state including the current month anchor.",
|
|
242
|
+
modelName: "ForgeMiniCalendarView"
|
|
243
|
+
})
|
|
244
|
+
],
|
|
209
245
|
tools: [],
|
|
210
246
|
NodeView: createGenericWorkbenchNodeView({
|
|
211
247
|
title: "Mini calendar",
|
|
212
248
|
description: "Compact month calendar widget.",
|
|
213
249
|
inputs: [],
|
|
214
250
|
params: [],
|
|
215
|
-
output: [
|
|
251
|
+
output: [
|
|
252
|
+
createSummaryOutput({
|
|
253
|
+
label: "Month view",
|
|
254
|
+
description: "Summary of the compact month calendar widget."
|
|
255
|
+
}),
|
|
256
|
+
createContextOutput({
|
|
257
|
+
key: "calendarView",
|
|
258
|
+
label: "Calendar view",
|
|
259
|
+
description: "Structured mini-calendar state including the current month anchor.",
|
|
260
|
+
modelName: "ForgeMiniCalendarView"
|
|
261
|
+
})
|
|
262
|
+
],
|
|
216
263
|
tools: []
|
|
217
264
|
}),
|
|
218
265
|
WebView: MiniCalendarWidget,
|
|
219
266
|
execute: (input) => buildStaticWorkbenchExecution(input, {
|
|
220
|
-
|
|
267
|
+
calendarView: {
|
|
268
|
+
now: input.context.now
|
|
269
|
+
}
|
|
221
270
|
}, "Compact month calendar")
|
|
222
271
|
};
|
|
223
272
|
MiniCalendarWidget.workbench = calendarWidgetDefinition;
|
|
@@ -239,19 +288,43 @@ const spotifyWidgetDefinition = {
|
|
|
239
288
|
}
|
|
240
289
|
],
|
|
241
290
|
params: [],
|
|
242
|
-
output: [
|
|
291
|
+
output: [
|
|
292
|
+
createSummaryOutput({
|
|
293
|
+
label: "Spotify link",
|
|
294
|
+
description: "Summary of the pinned music link widget."
|
|
295
|
+
}),
|
|
296
|
+
createContextOutput({
|
|
297
|
+
key: "spotifyLink",
|
|
298
|
+
label: "Spotify state",
|
|
299
|
+
description: "Structured Spotify widget state and pinned surface context.",
|
|
300
|
+
modelName: "ForgeSpotifyWidgetState"
|
|
301
|
+
})
|
|
302
|
+
],
|
|
243
303
|
tools: [],
|
|
244
304
|
NodeView: createGenericWorkbenchNodeView({
|
|
245
305
|
title: "Spotify",
|
|
246
306
|
description: "Pinned music link widget.",
|
|
247
307
|
inputs: [{ key: "surfaceId", label: "Surface id", kind: "text" }],
|
|
248
308
|
params: [],
|
|
249
|
-
output: [
|
|
309
|
+
output: [
|
|
310
|
+
createSummaryOutput({
|
|
311
|
+
label: "Spotify link",
|
|
312
|
+
description: "Summary of the pinned music link widget."
|
|
313
|
+
}),
|
|
314
|
+
createContextOutput({
|
|
315
|
+
key: "spotifyLink",
|
|
316
|
+
label: "Spotify state",
|
|
317
|
+
description: "Structured Spotify widget state and pinned surface context.",
|
|
318
|
+
modelName: "ForgeSpotifyWidgetState"
|
|
319
|
+
})
|
|
320
|
+
],
|
|
250
321
|
tools: []
|
|
251
322
|
}),
|
|
252
323
|
WebView: SpotifyWidget,
|
|
253
324
|
execute: (input) => buildStaticWorkbenchExecution(input, {
|
|
254
|
-
|
|
325
|
+
spotifyLink: {
|
|
326
|
+
surfaceId: input.inputs.surfaceId ?? null
|
|
327
|
+
}
|
|
255
328
|
}, "Pinned Spotify link")
|
|
256
329
|
};
|
|
257
330
|
SpotifyWidget.workbench = spotifyWidgetDefinition;
|
|
@@ -266,18 +339,40 @@ const weatherWidgetDefinition = {
|
|
|
266
339
|
tags: ["utility", "weather"],
|
|
267
340
|
inputs: [],
|
|
268
341
|
params: [],
|
|
269
|
-
output: [
|
|
342
|
+
output: [
|
|
343
|
+
createSummaryOutput({
|
|
344
|
+
label: "Weather summary",
|
|
345
|
+
description: "Summary of the weather widget state."
|
|
346
|
+
}),
|
|
347
|
+
createContextOutput({
|
|
348
|
+
key: "weather",
|
|
349
|
+
label: "Weather payload",
|
|
350
|
+
description: "Structured weather widget payload.",
|
|
351
|
+
modelName: "ForgeWeatherWidgetState"
|
|
352
|
+
})
|
|
353
|
+
],
|
|
270
354
|
tools: [],
|
|
271
355
|
NodeView: createGenericWorkbenchNodeView({
|
|
272
356
|
title: "Weather",
|
|
273
357
|
description: "Location-aware weather widget.",
|
|
274
358
|
inputs: [],
|
|
275
359
|
params: [],
|
|
276
|
-
output: [
|
|
360
|
+
output: [
|
|
361
|
+
createSummaryOutput({
|
|
362
|
+
label: "Weather summary",
|
|
363
|
+
description: "Summary of the weather widget state."
|
|
364
|
+
}),
|
|
365
|
+
createContextOutput({
|
|
366
|
+
key: "weather",
|
|
367
|
+
label: "Weather payload",
|
|
368
|
+
description: "Structured weather widget payload.",
|
|
369
|
+
modelName: "ForgeWeatherWidgetState"
|
|
370
|
+
})
|
|
371
|
+
],
|
|
277
372
|
tools: []
|
|
278
373
|
}),
|
|
279
374
|
WebView: WeatherWidget,
|
|
280
|
-
execute: (input) => buildStaticWorkbenchExecution(input, null, "Weather widget")
|
|
375
|
+
execute: (input) => buildStaticWorkbenchExecution(input, { weather: null }, "Weather widget")
|
|
281
376
|
};
|
|
282
377
|
WeatherWidget.workbench = weatherWidgetDefinition;
|
|
283
378
|
const quickCaptureWidgetDefinition = {
|
|
@@ -298,33 +393,47 @@ const quickCaptureWidgetDefinition = {
|
|
|
298
393
|
}
|
|
299
394
|
],
|
|
300
395
|
params: [],
|
|
301
|
-
output: [
|
|
396
|
+
output: [
|
|
397
|
+
createSummaryOutput({
|
|
398
|
+
label: "Draft summary",
|
|
399
|
+
description: "Summary of the quick-capture draft state."
|
|
400
|
+
}),
|
|
401
|
+
createContextOutput({
|
|
402
|
+
key: "draft",
|
|
403
|
+
label: "Draft context",
|
|
404
|
+
description: "Structured quick-capture draft context.",
|
|
405
|
+
modelName: "ForgeQuickCaptureDraft"
|
|
406
|
+
})
|
|
407
|
+
],
|
|
302
408
|
tools: [
|
|
303
|
-
|
|
304
|
-
key: "forge.create_note",
|
|
305
|
-
label: "Create note",
|
|
306
|
-
description: "Create a Forge evidence note from captured markdown.",
|
|
307
|
-
accessMode: "write"
|
|
308
|
-
}
|
|
409
|
+
createNoteTool("Create a Forge evidence note from captured markdown.")
|
|
309
410
|
],
|
|
310
411
|
NodeView: createGenericWorkbenchNodeView({
|
|
311
412
|
title: "Quick capture",
|
|
312
413
|
description: "Draft a quick note or wiki page.",
|
|
313
414
|
inputs: [{ key: "defaultUserId", label: "Default user id", kind: "text" }],
|
|
314
415
|
params: [],
|
|
315
|
-
output: [
|
|
416
|
+
output: [
|
|
417
|
+
createSummaryOutput({
|
|
418
|
+
label: "Draft summary",
|
|
419
|
+
description: "Summary of the quick-capture draft state."
|
|
420
|
+
}),
|
|
421
|
+
createContextOutput({
|
|
422
|
+
key: "draft",
|
|
423
|
+
label: "Draft context",
|
|
424
|
+
description: "Structured quick-capture draft context.",
|
|
425
|
+
modelName: "ForgeQuickCaptureDraft"
|
|
426
|
+
})
|
|
427
|
+
],
|
|
316
428
|
tools: [
|
|
317
|
-
|
|
318
|
-
key: "forge.create_note",
|
|
319
|
-
label: "Create note",
|
|
320
|
-
description: "Create a Forge evidence note from captured markdown.",
|
|
321
|
-
accessMode: "write"
|
|
322
|
-
}
|
|
429
|
+
createNoteTool("Create a Forge evidence note from captured markdown.")
|
|
323
430
|
]
|
|
324
431
|
}),
|
|
325
432
|
WebView: QuickCaptureWidget,
|
|
326
433
|
execute: (input) => buildStaticWorkbenchExecution(input, {
|
|
327
|
-
|
|
434
|
+
draft: {
|
|
435
|
+
defaultUserId: input.inputs.defaultUserId ?? null
|
|
436
|
+
}
|
|
328
437
|
}, "Quick capture can draft notes and wiki pages.")
|
|
329
438
|
};
|
|
330
439
|
QuickCaptureWidget.workbench = quickCaptureWidgetDefinition;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
3
|
+
import { CircleHelp } from "lucide-react";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
export function FieldHint({ children, className }) {
|
|
6
|
+
return _jsx("div", { className: cn("text-sm leading-6 text-white/50", className), children: children });
|
|
7
|
+
}
|
|
8
|
+
export function InfoTooltip({ content, label = "Explain this field", className }) {
|
|
9
|
+
const [open, setOpen] = useState(false);
|
|
10
|
+
const containerRef = useRef(null);
|
|
11
|
+
const tooltipId = useId();
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (!open) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const handlePointerDown = (event) => {
|
|
17
|
+
if (!containerRef.current?.contains(event.target)) {
|
|
18
|
+
setOpen(false);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
22
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
23
|
+
}, [open]);
|
|
24
|
+
return (_jsxs("span", { ref: containerRef, className: cn("relative inline-flex items-center", className), onMouseEnter: () => setOpen(true), onMouseLeave: () => setOpen(false), children: [_jsx("button", { type: "button", "aria-label": label, "aria-describedby": open ? tooltipId : undefined, "aria-expanded": open, className: "inline-flex size-5 items-center justify-center rounded-full text-white/42 transition hover:bg-white/[0.06] hover:text-white/78 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[rgba(192,193,255,0.35)]", onFocus: () => setOpen(true), onBlur: () => setOpen(false), onClick: () => setOpen((current) => !current), children: _jsx(CircleHelp, { className: "size-3.5" }) }), _jsx("span", { id: tooltipId, role: "tooltip", className: cn("pointer-events-none absolute right-0 top-[calc(100%+0.55rem)] z-40 w-[min(16rem,calc(100vw-2.5rem))] max-w-[calc(100vw-2.5rem)] rounded-[18px] border border-white/8 bg-[rgba(12,17,30,0.96)] px-3 py-2.5 text-sm leading-6 text-white/74 shadow-[0_18px_48px_rgba(3,8,18,0.42)] transition", open ? "translate-y-0 opacity-100" : "translate-y-1 opacity-0"), children: content })] }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { buildSearchWorkbenchExecution, buildStaticWorkbenchExecution } from "../../../lib/workbench/runtime.js";
|
|
3
|
+
import { createSearchEntitiesTool, createSearchInputs, createSearchOutputs, createSearchParams, createSummaryOutput } from "../../../lib/workbench/contracts.js";
|
|
4
|
+
import { createGenericWorkbenchNodeView } from "../shared/generic-node-view.js";
|
|
5
|
+
import { defineWorkbenchBox } from "../shared/define-workbench-box.js";
|
|
6
|
+
function Slot({ children }) {
|
|
7
|
+
return _jsx(_Fragment, { children: children });
|
|
8
|
+
}
|
|
9
|
+
function defineCalendarBox(id, title, description, tags, execute, output, tools, options) {
|
|
10
|
+
const inputs = options?.inputs ?? [];
|
|
11
|
+
const params = options?.params ?? [];
|
|
12
|
+
return defineWorkbenchBox(Slot, {
|
|
13
|
+
id,
|
|
14
|
+
surfaceId: "calendar",
|
|
15
|
+
routePath: "/calendar",
|
|
16
|
+
title,
|
|
17
|
+
icon: "calendar",
|
|
18
|
+
description,
|
|
19
|
+
category: "Calendar",
|
|
20
|
+
tags,
|
|
21
|
+
inputs,
|
|
22
|
+
params,
|
|
23
|
+
output,
|
|
24
|
+
tools,
|
|
25
|
+
NodeView: createGenericWorkbenchNodeView({
|
|
26
|
+
title,
|
|
27
|
+
description,
|
|
28
|
+
inputs,
|
|
29
|
+
params,
|
|
30
|
+
output,
|
|
31
|
+
tools
|
|
32
|
+
}),
|
|
33
|
+
execute
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export const CalendarOverviewBox = defineCalendarBox("surface:calendar:overview", "Calendar overview", "Main calendar surface for mirrored events, work blocks, and planned timeboxes.", ["calendar", "overview"], (input) => buildStaticWorkbenchExecution(input, {
|
|
37
|
+
surfaces: ["events", "work_blocks", "timeboxes"]
|
|
38
|
+
}, "Calendar overview spanning mirrored events, work blocks, and timeboxes."), [createSummaryOutput({ label: "Calendar summary", description: "Summary of mirrored events, work blocks, and timeboxes." })], []);
|
|
39
|
+
export const CalendarEventsBox = defineCalendarBox("surface:calendar:events", "Calendar events", "Mirrored calendar events and Forge-managed calendar records.", ["calendar", "events", "mirrored"], (input) => buildSearchWorkbenchExecution(input, {
|
|
40
|
+
query: "",
|
|
41
|
+
entityTypes: ["calendar_event"],
|
|
42
|
+
limit: 20
|
|
43
|
+
}), createSearchOutputs({
|
|
44
|
+
itemKind: "calendar_event",
|
|
45
|
+
itemLabel: "Calendar event"
|
|
46
|
+
}), [createSearchEntitiesTool("Search calendar-backed Forge entities and planning records.")], {
|
|
47
|
+
inputs: createSearchInputs({
|
|
48
|
+
itemKind: "calendar_event",
|
|
49
|
+
itemLabel: "Calendar event",
|
|
50
|
+
defaultEntityTypes: ["calendar_event"],
|
|
51
|
+
defaultLimit: 20
|
|
52
|
+
}),
|
|
53
|
+
params: createSearchParams({
|
|
54
|
+
itemKind: "calendar_event",
|
|
55
|
+
defaultEntityTypes: ["calendar_event"],
|
|
56
|
+
defaultLimit: 20
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
export const CalendarPlanningBox = defineCalendarBox("surface:calendar:planning", "Planning blocks", "Planned task timeboxes and reusable work block templates.", ["calendar", "planning", "timeboxes"], (input) => buildSearchWorkbenchExecution(input, {
|
|
60
|
+
query: "",
|
|
61
|
+
entityTypes: ["task_timebox", "work_block_template"],
|
|
62
|
+
limit: 20
|
|
63
|
+
}), createSearchOutputs({
|
|
64
|
+
itemKind: "calendar_plan",
|
|
65
|
+
itemLabel: "Planning record"
|
|
66
|
+
}), [createSearchEntitiesTool("Search calendar-backed Forge entities and planning records.")], {
|
|
67
|
+
inputs: createSearchInputs({
|
|
68
|
+
itemKind: "calendar_plan",
|
|
69
|
+
itemLabel: "Planning record",
|
|
70
|
+
defaultEntityTypes: ["task_timebox", "work_block_template"],
|
|
71
|
+
defaultLimit: 20
|
|
72
|
+
}),
|
|
73
|
+
params: createSearchParams({
|
|
74
|
+
itemKind: "calendar_plan",
|
|
75
|
+
defaultEntityTypes: ["task_timebox", "work_block_template"],
|
|
76
|
+
defaultLimit: 20
|
|
77
|
+
})
|
|
78
|
+
});
|