@lowdefy/server-dev 0.0.0-experimental-20260710073520 → 0.0.0-experimental-20260710121634
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/lib/docs/checkpointStore.js +14 -11
- package/lib/docs/createDocsMcpServer.js +12 -8
- package/lib/docs/getOverview.js +2 -2
- package/lib/docs/snapshotState.js +1 -1
- package/manager/processes/readDotEnv.mjs +13 -1
- package/package.json +32 -32
- package/package.original.json +32 -32
- package/src/app.js +15 -9
- package/src/routes/docs/evalOperator.js +13 -1
- package/src/routes/docs/runRequest.js +6 -1
- package/src/routes/docs/snapshotState.js +2 -2
- package/src/routes/docs/stateCheckpointsList.js +1 -1
|
@@ -20,18 +20,21 @@ import { serializer, type } from '@lowdefy/helpers';
|
|
|
20
20
|
|
|
21
21
|
import { getConfigDirectory } from './checkpointPaths.js';
|
|
22
22
|
|
|
23
|
-
// State checkpoints (page state + recorded request/api responses) live
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
23
|
+
// State checkpoints (page state + recorded request/api responses) live under
|
|
24
|
+
// <configDir>/.lowdefy/state-checkpoints/<name>/ — inside .lowdefy because a
|
|
25
|
+
// checkpoint captures the signed-in user's data and live backend responses,
|
|
26
|
+
// which must not sit in a committable spot by default (.lowdefy is gitignored
|
|
27
|
+
// by convention and ignored by the dev file watcher). A sibling of
|
|
28
|
+
// .lowdefy/checkpoints (the config-directory-snapshot checkpoints in
|
|
29
|
+
// checkpointPaths.js/createConfigCheckpoint.js) rather than a subdirectory,
|
|
30
|
+
// because that store prunes its oldest directories beyond a cap. These two
|
|
31
|
+
// "checkpoint" concepts are unrelated: this one is for putting the *running
|
|
32
|
+
// app* into a specific state (agent verification, human testing, e2e test
|
|
33
|
+
// generation), not for reverting config files.
|
|
31
34
|
const NAME_PATTERN = /^[a-z0-9-_]+$/i;
|
|
32
35
|
|
|
33
36
|
function getStateCheckpointsRoot() {
|
|
34
|
-
return path.join(getConfigDirectory(), 'checkpoints');
|
|
37
|
+
return path.join(getConfigDirectory(), '.lowdefy', 'state-checkpoints');
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
function validateName(name) {
|
|
@@ -93,7 +96,7 @@ function readEntries({ dir, subdir }) {
|
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
// Snapshots a running page's state + recorded request/api responses into a
|
|
96
|
-
//
|
|
99
|
+
// checkpoint folder. Refuses to clobber an existing checkpoint unless
|
|
97
100
|
// `overwrite: true` — when overwriting, the old folder is removed first so a
|
|
98
101
|
// checkpoint written with fewer requests than a previous one doesn't leave
|
|
99
102
|
// stale per-request files behind.
|
|
@@ -184,7 +187,7 @@ function checkpointExists({ name }) {
|
|
|
184
187
|
// Lists stored state checkpoints, most recent first. Corrupted/hand-edited
|
|
185
188
|
// checkpoints without a manifest still show up (with null fields) rather
|
|
186
189
|
// than being silently hidden, since these folders are meant to be
|
|
187
|
-
// human-editable
|
|
190
|
+
// human-editable.
|
|
188
191
|
function listStateCheckpoints() {
|
|
189
192
|
const root = getStateCheckpointsRoot();
|
|
190
193
|
if (!fs.existsSync(root)) {
|
|
@@ -52,7 +52,7 @@ Live state: lowdefy_inspect_state reads the ACTUAL state, request results, and e
|
|
|
52
52
|
|
|
53
53
|
Safety: lowdefy_checkpoint snapshots the config files before risky multi-file changes; lowdefy_revert_checkpoint restores them.
|
|
54
54
|
|
|
55
|
-
State checkpoints (testing): lowdefy_snapshot_state captures a page's live state AND its request/api responses into
|
|
55
|
+
State checkpoints (testing): lowdefy_snapshot_state captures a page's live state AND its request/api responses into .lowdefy/state-checkpoints/<name>/ (one file per part; gitignored — checkpoints contain user/session data). lowdefy_load_state puts the app back into that state: headless for your own verification, or registry-only which returns a ?_checkpoint URL the developer can open to manually test the app in that exact state (recorded request data is served automatically). lowdefy_checkpoint_to_mocks converts a checkpoint into e2e mocks.yaml fixtures — use it when asked to write e2e tests.`;
|
|
56
56
|
|
|
57
57
|
function textResult(value) {
|
|
58
58
|
const text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);
|
|
@@ -95,7 +95,7 @@ function createDocsMcpServer({ origin, honoContext } = {}) {
|
|
|
95
95
|
'lowdefy_eval_operator',
|
|
96
96
|
{
|
|
97
97
|
description:
|
|
98
|
-
'Evaluate a Lowdefy operator expression against the live state of a running page — a REPL for config. Pass
|
|
98
|
+
'Evaluate a Lowdefy operator expression against the live state of a running page — a REPL for config. Pass the operator object in the "expression" argument — any JSON value, e.g. {"_state": "customer.name"} or {"_if": {...}}. Evaluates in the real browser runtime (live tab if connected, else headless).',
|
|
99
99
|
inputSchema: {
|
|
100
100
|
pageId: z.string().describe('The page id whose context to evaluate against.'),
|
|
101
101
|
expression: z
|
|
@@ -125,7 +125,7 @@ function createDocsMcpServer({ origin, honoContext } = {}) {
|
|
|
125
125
|
},
|
|
126
126
|
},
|
|
127
127
|
async ({ pageId, requestId, payload }) =>
|
|
128
|
-
textResult(await runRequest({ pageId, requestId, payload, honoContext }))
|
|
128
|
+
textResult(await runRequest({ pageId, requestId, payload, honoContext }))
|
|
129
129
|
);
|
|
130
130
|
|
|
131
131
|
server.registerTool(
|
|
@@ -142,7 +142,7 @@ function createDocsMcpServer({ origin, honoContext } = {}) {
|
|
|
142
142
|
'lowdefy_snapshot_state',
|
|
143
143
|
{
|
|
144
144
|
description:
|
|
145
|
-
"Capture the live state AND recorded request/api responses of a running page into a
|
|
145
|
+
"Capture the live state AND recorded request/api responses of a running page into a checkpoint folder (.lowdefy/state-checkpoints/<name>/, one file per part; gitignored — checkpoints contain user/session data). Snapshot the developer's open tab after they reproduce a scenario, or a headless run. Use for building test fixtures and reproducible app states.",
|
|
146
146
|
inputSchema: {
|
|
147
147
|
pageId: z.string().describe('The page to snapshot.'),
|
|
148
148
|
name: z.string().describe('Checkpoint name (letters, numbers, - and _).'),
|
|
@@ -275,7 +275,9 @@ function createDocsMcpServer({ origin, honoContext } = {}) {
|
|
|
275
275
|
pageId: z
|
|
276
276
|
.string()
|
|
277
277
|
.optional()
|
|
278
|
-
.describe(
|
|
278
|
+
.describe(
|
|
279
|
+
'The page the id belongs to — required for block/request ids on pages not yet built.'
|
|
280
|
+
),
|
|
279
281
|
},
|
|
280
282
|
},
|
|
281
283
|
async ({ id, pageId }) => textResult(await findConfig({ id, pageId }))
|
|
@@ -359,7 +361,7 @@ function createDocsMcpServer({ origin, honoContext } = {}) {
|
|
|
359
361
|
'lowdefy_list_plugins',
|
|
360
362
|
{
|
|
361
363
|
description:
|
|
362
|
-
|
|
364
|
+
"List installed plugin packages (including this project's local custom plugins) and the type names each provides.",
|
|
363
365
|
inputSchema: {},
|
|
364
366
|
},
|
|
365
367
|
() => textResult(listPlugins())
|
|
@@ -426,7 +428,9 @@ function createDocsMcpServer({ origin, honoContext } = {}) {
|
|
|
426
428
|
const doc = getCoreDoc({ slug, kind, type });
|
|
427
429
|
if (doc === null) {
|
|
428
430
|
return notFoundResult(
|
|
429
|
-
`No doc found${slug ? ` for slug "${slug}"` : ''}${
|
|
431
|
+
`No doc found${slug ? ` for slug "${slug}"` : ''}${
|
|
432
|
+
type ? ` for type "${type}"` : ''
|
|
433
|
+
}. Use lowdefy_search_docs to find the right slug.`
|
|
430
434
|
);
|
|
431
435
|
}
|
|
432
436
|
return textResult(doc.markdown);
|
|
@@ -448,7 +452,7 @@ function createDocsMcpServer({ origin, honoContext } = {}) {
|
|
|
448
452
|
'lowdefy_get_plugin_doc',
|
|
449
453
|
{
|
|
450
454
|
description:
|
|
451
|
-
|
|
455
|
+
"Get markdown documentation shipped inside an installed plugin package (README, guides). Useful for this project's local custom plugins.",
|
|
452
456
|
inputSchema: {
|
|
453
457
|
package: z.string().describe('The package name, e.g. "@lowdefy/blocks-antd".'),
|
|
454
458
|
},
|
package/lib/docs/getOverview.js
CHANGED
|
@@ -74,11 +74,11 @@ function getOverview() {
|
|
|
74
74
|
'## Live state and testing',
|
|
75
75
|
'',
|
|
76
76
|
"1. **Inspect live state**: `GET /lowdefy-docs/inspect-state/{pageId}` — or `lowdefy_inspect_state`. The actual state, request results, and event log of a running page. Reads the developer's open browser tab when connected (ask them to interact, then inspect), else runs the page headless.",
|
|
77
|
-
'2. **Evaluate operators**: `POST /lowdefy-docs/eval-operator` `{pageId, expression}` — or `lowdefy_eval_operator`. A REPL for config: run any operator expression against live page state.',
|
|
77
|
+
'2. **Evaluate operators**: `POST /lowdefy-docs/eval-operator` `{pageId, expression}` — or `lowdefy_eval_operator`. A REPL for config: run any operator expression against live page state. The operator goes in the `expression` body key (`operator` is accepted as an alias).',
|
|
78
78
|
'3. **Run a request**: `POST /lowdefy-docs/run-request` `{pageId, requestId, payload}` — or `lowdefy_run_request`. Verify the data shape a request returns. Read-only types always run; writes need `cli.agentTools.allowWriteRequests: true` in lowdefy.yaml.',
|
|
79
79
|
'4. **Understand the app**: `GET /lowdefy-docs/app-map` — or `lowdefy_app_map`. Every page, menu, connection, endpoint, agent, and websocket in one call.',
|
|
80
80
|
'5. **Config checkpoints**: `POST /lowdefy-docs/checkpoints` `{label}` / `POST /lowdefy-docs/checkpoints/revert` `{id}` — or `lowdefy_checkpoint` / `lowdefy_revert_checkpoint`. Snapshot config files before risky changes; revert instantly.',
|
|
81
|
-
"6. **State checkpoints**: `POST /lowdefy-docs/state-checkpoints/snapshot` `{pageId, name}` captures live page state + request responses into
|
|
81
|
+
"6. **State checkpoints**: `POST /lowdefy-docs/state-checkpoints/snapshot` `{pageId, name}` captures live page state + request responses into .lowdefy/state-checkpoints/<name>/ (gitignored — checkpoints contain user/session data); `POST /lowdefy-docs/state-checkpoints/load` `{name, mode}` restores it — 'registry-only' mode returns a ?_checkpoint URL a human can open to test the app in that exact state. `lowdefy_checkpoint_to_mocks` converts one into e2e mocks.yaml fixtures.",
|
|
82
82
|
'',
|
|
83
83
|
'## Routes',
|
|
84
84
|
'',
|
|
@@ -21,7 +21,7 @@ import inspectState from './inspectState.js';
|
|
|
21
21
|
import { writeCheckpoint } from './checkpointStore.js';
|
|
22
22
|
|
|
23
23
|
// Snapshots the running app (page state + recorded request/api responses)
|
|
24
|
-
// into a
|
|
24
|
+
// into a checkpoint folder under .lowdefy — see checkpointStore.js for the
|
|
25
25
|
// on-disk layout. Reuses inspectState.js's tab-first/headless-fallback
|
|
26
26
|
// snapshot rather than driving a browser directly, so a snapshot always
|
|
27
27
|
// reflects whatever a developer is actually looking at when one is
|
|
@@ -18,8 +18,20 @@ import path from 'path';
|
|
|
18
18
|
import dotenv from 'dotenv';
|
|
19
19
|
|
|
20
20
|
function readDotEnv(context) {
|
|
21
|
+
// dotenv.config() never overwrites env vars that are already set, so on
|
|
22
|
+
// the first load the real shell environment wins over .env (standard
|
|
23
|
+
// dotenv behavior). On reloads triggered by the .env watcher the previous
|
|
24
|
+
// load has already populated process.env, so without override the changed
|
|
25
|
+
// values would never take effect and the server would restart with stale
|
|
26
|
+
// env.
|
|
27
|
+
let loadedOnce = false;
|
|
21
28
|
return () => {
|
|
22
|
-
dotenv.config({
|
|
29
|
+
dotenv.config({
|
|
30
|
+
path: path.join(context.directories.config, '.env'),
|
|
31
|
+
silent: true,
|
|
32
|
+
override: loadedOnce,
|
|
33
|
+
});
|
|
34
|
+
loadedOnce = true;
|
|
23
35
|
};
|
|
24
36
|
}
|
|
25
37
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/server-dev",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-20260710121634",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -45,37 +45,37 @@
|
|
|
45
45
|
"@hono/mcp": "0.3.0",
|
|
46
46
|
"@hono/node-server": "2.0.4",
|
|
47
47
|
"@hono/vite-dev-server": "0.26.0",
|
|
48
|
-
"@lowdefy/actions-core": "0.0.0-experimental-
|
|
49
|
-
"@lowdefy/api": "0.0.0-experimental-
|
|
50
|
-
"@lowdefy/block-utils": "0.0.0-experimental-
|
|
51
|
-
"@lowdefy/blocks-aggrid": "0.0.0-experimental-
|
|
52
|
-
"@lowdefy/blocks-antd": "0.0.0-experimental-
|
|
53
|
-
"@lowdefy/blocks-antd-x": "0.0.0-experimental-
|
|
54
|
-
"@lowdefy/blocks-basic": "0.0.0-experimental-
|
|
55
|
-
"@lowdefy/blocks-echarts": "0.0.0-experimental-
|
|
56
|
-
"@lowdefy/blocks-loaders": "0.0.0-experimental-
|
|
57
|
-
"@lowdefy/blocks-markdown": "0.0.0-experimental-
|
|
58
|
-
"@lowdefy/blocks-tiptap": "0.0.0-experimental-
|
|
59
|
-
"@lowdefy/build": "0.0.0-experimental-
|
|
60
|
-
"@lowdefy/client": "0.0.0-experimental-
|
|
61
|
-
"@lowdefy/connection-axios-http": "0.0.0-experimental-
|
|
62
|
-
"@lowdefy/docs-content": "0.0.0-experimental-
|
|
63
|
-
"@lowdefy/engine": "0.0.0-experimental-
|
|
64
|
-
"@lowdefy/errors": "0.0.0-experimental-
|
|
65
|
-
"@lowdefy/helpers": "0.0.0-experimental-
|
|
66
|
-
"@lowdefy/layout": "0.0.0-experimental-
|
|
67
|
-
"@lowdefy/logger": "0.0.0-experimental-
|
|
68
|
-
"@lowdefy/node-utils": "0.0.0-experimental-
|
|
69
|
-
"@lowdefy/operators-change-case": "0.0.0-experimental-
|
|
70
|
-
"@lowdefy/operators-dayjs": "0.0.0-experimental-
|
|
71
|
-
"@lowdefy/operators-diff": "0.0.0-experimental-
|
|
72
|
-
"@lowdefy/operators-js": "0.0.0-experimental-
|
|
73
|
-
"@lowdefy/operators-mql": "0.0.0-experimental-
|
|
74
|
-
"@lowdefy/operators-nunjucks": "0.0.0-experimental-
|
|
75
|
-
"@lowdefy/operators-uuid": "0.0.0-experimental-
|
|
76
|
-
"@lowdefy/operators-yaml": "0.0.0-experimental-
|
|
77
|
-
"@lowdefy/plugin-next-auth": "0.0.0-experimental-
|
|
78
|
-
"@lowdefy/websockets-core": "0.0.0-experimental-
|
|
48
|
+
"@lowdefy/actions-core": "0.0.0-experimental-20260710121634",
|
|
49
|
+
"@lowdefy/api": "0.0.0-experimental-20260710121634",
|
|
50
|
+
"@lowdefy/block-utils": "0.0.0-experimental-20260710121634",
|
|
51
|
+
"@lowdefy/blocks-aggrid": "0.0.0-experimental-20260710121634",
|
|
52
|
+
"@lowdefy/blocks-antd": "0.0.0-experimental-20260710121634",
|
|
53
|
+
"@lowdefy/blocks-antd-x": "0.0.0-experimental-20260710121634",
|
|
54
|
+
"@lowdefy/blocks-basic": "0.0.0-experimental-20260710121634",
|
|
55
|
+
"@lowdefy/blocks-echarts": "0.0.0-experimental-20260710121634",
|
|
56
|
+
"@lowdefy/blocks-loaders": "0.0.0-experimental-20260710121634",
|
|
57
|
+
"@lowdefy/blocks-markdown": "0.0.0-experimental-20260710121634",
|
|
58
|
+
"@lowdefy/blocks-tiptap": "0.0.0-experimental-20260710121634",
|
|
59
|
+
"@lowdefy/build": "0.0.0-experimental-20260710121634",
|
|
60
|
+
"@lowdefy/client": "0.0.0-experimental-20260710121634",
|
|
61
|
+
"@lowdefy/connection-axios-http": "0.0.0-experimental-20260710121634",
|
|
62
|
+
"@lowdefy/docs-content": "0.0.0-experimental-20260710121634",
|
|
63
|
+
"@lowdefy/engine": "0.0.0-experimental-20260710121634",
|
|
64
|
+
"@lowdefy/errors": "0.0.0-experimental-20260710121634",
|
|
65
|
+
"@lowdefy/helpers": "0.0.0-experimental-20260710121634",
|
|
66
|
+
"@lowdefy/layout": "0.0.0-experimental-20260710121634",
|
|
67
|
+
"@lowdefy/logger": "0.0.0-experimental-20260710121634",
|
|
68
|
+
"@lowdefy/node-utils": "0.0.0-experimental-20260710121634",
|
|
69
|
+
"@lowdefy/operators-change-case": "0.0.0-experimental-20260710121634",
|
|
70
|
+
"@lowdefy/operators-dayjs": "0.0.0-experimental-20260710121634",
|
|
71
|
+
"@lowdefy/operators-diff": "0.0.0-experimental-20260710121634",
|
|
72
|
+
"@lowdefy/operators-js": "0.0.0-experimental-20260710121634",
|
|
73
|
+
"@lowdefy/operators-mql": "0.0.0-experimental-20260710121634",
|
|
74
|
+
"@lowdefy/operators-nunjucks": "0.0.0-experimental-20260710121634",
|
|
75
|
+
"@lowdefy/operators-uuid": "0.0.0-experimental-20260710121634",
|
|
76
|
+
"@lowdefy/operators-yaml": "0.0.0-experimental-20260710121634",
|
|
77
|
+
"@lowdefy/plugin-next-auth": "0.0.0-experimental-20260710121634",
|
|
78
|
+
"@lowdefy/websockets-core": "0.0.0-experimental-20260710121634",
|
|
79
79
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
80
80
|
"@tailwindcss/postcss": "4.3.1",
|
|
81
81
|
"@vitejs/plugin-react": "6.0.2",
|
package/package.original.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lowdefy/server-dev",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-20260710121634",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "",
|
|
6
6
|
"homepage": "https://lowdefy.com",
|
|
@@ -51,37 +51,37 @@
|
|
|
51
51
|
"@hono/mcp": "0.3.0",
|
|
52
52
|
"@hono/node-server": "2.0.4",
|
|
53
53
|
"@hono/vite-dev-server": "0.26.0",
|
|
54
|
-
"@lowdefy/actions-core": "0.0.0-experimental-
|
|
55
|
-
"@lowdefy/api": "0.0.0-experimental-
|
|
56
|
-
"@lowdefy/block-utils": "0.0.0-experimental-
|
|
57
|
-
"@lowdefy/blocks-aggrid": "0.0.0-experimental-
|
|
58
|
-
"@lowdefy/blocks-antd": "0.0.0-experimental-
|
|
59
|
-
"@lowdefy/blocks-antd-x": "0.0.0-experimental-
|
|
60
|
-
"@lowdefy/blocks-basic": "0.0.0-experimental-
|
|
61
|
-
"@lowdefy/blocks-echarts": "0.0.0-experimental-
|
|
62
|
-
"@lowdefy/blocks-loaders": "0.0.0-experimental-
|
|
63
|
-
"@lowdefy/blocks-markdown": "0.0.0-experimental-
|
|
64
|
-
"@lowdefy/blocks-tiptap": "0.0.0-experimental-
|
|
65
|
-
"@lowdefy/build": "0.0.0-experimental-
|
|
66
|
-
"@lowdefy/client": "0.0.0-experimental-
|
|
67
|
-
"@lowdefy/connection-axios-http": "0.0.0-experimental-
|
|
68
|
-
"@lowdefy/docs-content": "0.0.0-experimental-
|
|
69
|
-
"@lowdefy/engine": "0.0.0-experimental-
|
|
70
|
-
"@lowdefy/errors": "0.0.0-experimental-
|
|
71
|
-
"@lowdefy/helpers": "0.0.0-experimental-
|
|
72
|
-
"@lowdefy/layout": "0.0.0-experimental-
|
|
73
|
-
"@lowdefy/logger": "0.0.0-experimental-
|
|
74
|
-
"@lowdefy/node-utils": "0.0.0-experimental-
|
|
75
|
-
"@lowdefy/operators-change-case": "0.0.0-experimental-
|
|
76
|
-
"@lowdefy/operators-dayjs": "0.0.0-experimental-
|
|
77
|
-
"@lowdefy/operators-diff": "0.0.0-experimental-
|
|
78
|
-
"@lowdefy/operators-js": "0.0.0-experimental-
|
|
79
|
-
"@lowdefy/operators-mql": "0.0.0-experimental-
|
|
80
|
-
"@lowdefy/operators-nunjucks": "0.0.0-experimental-
|
|
81
|
-
"@lowdefy/operators-uuid": "0.0.0-experimental-
|
|
82
|
-
"@lowdefy/operators-yaml": "0.0.0-experimental-
|
|
83
|
-
"@lowdefy/plugin-next-auth": "0.0.0-experimental-
|
|
84
|
-
"@lowdefy/websockets-core": "0.0.0-experimental-
|
|
54
|
+
"@lowdefy/actions-core": "0.0.0-experimental-20260710121634",
|
|
55
|
+
"@lowdefy/api": "0.0.0-experimental-20260710121634",
|
|
56
|
+
"@lowdefy/block-utils": "0.0.0-experimental-20260710121634",
|
|
57
|
+
"@lowdefy/blocks-aggrid": "0.0.0-experimental-20260710121634",
|
|
58
|
+
"@lowdefy/blocks-antd": "0.0.0-experimental-20260710121634",
|
|
59
|
+
"@lowdefy/blocks-antd-x": "0.0.0-experimental-20260710121634",
|
|
60
|
+
"@lowdefy/blocks-basic": "0.0.0-experimental-20260710121634",
|
|
61
|
+
"@lowdefy/blocks-echarts": "0.0.0-experimental-20260710121634",
|
|
62
|
+
"@lowdefy/blocks-loaders": "0.0.0-experimental-20260710121634",
|
|
63
|
+
"@lowdefy/blocks-markdown": "0.0.0-experimental-20260710121634",
|
|
64
|
+
"@lowdefy/blocks-tiptap": "0.0.0-experimental-20260710121634",
|
|
65
|
+
"@lowdefy/build": "0.0.0-experimental-20260710121634",
|
|
66
|
+
"@lowdefy/client": "0.0.0-experimental-20260710121634",
|
|
67
|
+
"@lowdefy/connection-axios-http": "0.0.0-experimental-20260710121634",
|
|
68
|
+
"@lowdefy/docs-content": "0.0.0-experimental-20260710121634",
|
|
69
|
+
"@lowdefy/engine": "0.0.0-experimental-20260710121634",
|
|
70
|
+
"@lowdefy/errors": "0.0.0-experimental-20260710121634",
|
|
71
|
+
"@lowdefy/helpers": "0.0.0-experimental-20260710121634",
|
|
72
|
+
"@lowdefy/layout": "0.0.0-experimental-20260710121634",
|
|
73
|
+
"@lowdefy/logger": "0.0.0-experimental-20260710121634",
|
|
74
|
+
"@lowdefy/node-utils": "0.0.0-experimental-20260710121634",
|
|
75
|
+
"@lowdefy/operators-change-case": "0.0.0-experimental-20260710121634",
|
|
76
|
+
"@lowdefy/operators-dayjs": "0.0.0-experimental-20260710121634",
|
|
77
|
+
"@lowdefy/operators-diff": "0.0.0-experimental-20260710121634",
|
|
78
|
+
"@lowdefy/operators-js": "0.0.0-experimental-20260710121634",
|
|
79
|
+
"@lowdefy/operators-mql": "0.0.0-experimental-20260710121634",
|
|
80
|
+
"@lowdefy/operators-nunjucks": "0.0.0-experimental-20260710121634",
|
|
81
|
+
"@lowdefy/operators-uuid": "0.0.0-experimental-20260710121634",
|
|
82
|
+
"@lowdefy/operators-yaml": "0.0.0-experimental-20260710121634",
|
|
83
|
+
"@lowdefy/plugin-next-auth": "0.0.0-experimental-20260710121634",
|
|
84
|
+
"@lowdefy/websockets-core": "0.0.0-experimental-20260710121634",
|
|
85
85
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
86
86
|
"@tailwindcss/postcss": "4.3.1",
|
|
87
87
|
"@vitejs/plugin-react": "6.0.2",
|
package/src/app.js
CHANGED
|
@@ -84,12 +84,25 @@ function createApp() {
|
|
|
84
84
|
app.get('/api/icons/dynamic', iconsDynamicHandler);
|
|
85
85
|
app.get('/api/dev-tools', devToolsHandler);
|
|
86
86
|
|
|
87
|
+
// Auth config must be registered before the /lowdefy-docs routes: Hono
|
|
88
|
+
// middleware only applies to routes registered after it, and run-request/
|
|
89
|
+
// eval-operator build a full Lowdefy context whose getSession(c) reads the
|
|
90
|
+
// authConfig this middleware sets. It does not protect any route — it only
|
|
91
|
+
// makes the auth config available on the Hono context.
|
|
92
|
+
if (authJson.configured === true) {
|
|
93
|
+
app.use(
|
|
94
|
+
'*',
|
|
95
|
+
initAuthConfig(() => getAuthConfig({ logger }))
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
// Docs and MCP endpoint for AI coding agents — always on in dev. Serves
|
|
88
100
|
// schemas/examples/docs for every installed plugin (core and local) plus
|
|
89
101
|
// the extracted core docs (@lowdefy/docs-content). Mounted outside /api/*
|
|
90
102
|
// so it can't clash with user API endpoints; /lowdefy-docs is a reserved page
|
|
91
|
-
// prefix in dev.
|
|
92
|
-
// build artifacts and node_modules directly
|
|
103
|
+
// prefix in dev. The handlers need no auth protection or api context —
|
|
104
|
+
// they read build artifacts and node_modules directly — but run-request
|
|
105
|
+
// and eval-operator read the caller's session, hence initAuthConfig above.
|
|
93
106
|
app.all('/lowdefy-docs/mcp', docsMcpHandler);
|
|
94
107
|
app.get('/lowdefy-docs', docsIndexHandler);
|
|
95
108
|
app.get('/lowdefy-docs/build-status', docsBuildStatusHandler);
|
|
@@ -119,13 +132,6 @@ function createApp() {
|
|
|
119
132
|
app.get('/lowdefy-docs/content/:slug{.+}', docsContentHandler);
|
|
120
133
|
app.get('/lowdefy-docs/:kind', docsTypesHandler);
|
|
121
134
|
|
|
122
|
-
if (authJson.configured === true) {
|
|
123
|
-
app.use(
|
|
124
|
-
'*',
|
|
125
|
-
initAuthConfig(() => getAuthConfig({ logger }))
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
135
|
app.use('/api/*', apiContext());
|
|
130
136
|
app.use('/api/auth/*', authMiddleware());
|
|
131
137
|
app.get('/api/root', rootHandler);
|
|
@@ -18,7 +18,19 @@ import evalOperator from '../../../lib/docs/evalOperator.js';
|
|
|
18
18
|
|
|
19
19
|
async function docsEvalOperatorHandler(c) {
|
|
20
20
|
const body = await c.req.json().catch(() => ({}));
|
|
21
|
-
const { pageId,
|
|
21
|
+
const { pageId, source } = body;
|
|
22
|
+
// "operator" is accepted as an alias for "expression" — agents commonly
|
|
23
|
+
// guess it as the body key for this endpoint.
|
|
24
|
+
const expression = body.expression ?? body.operator;
|
|
25
|
+
if (expression === undefined) {
|
|
26
|
+
return c.json(
|
|
27
|
+
{
|
|
28
|
+
error:
|
|
29
|
+
'POST /lowdefy-docs/eval-operator requires an "expression" in the JSON body — the operator expression to evaluate, e.g. {"pageId": "home", "expression": {"_state": "key"}}.',
|
|
30
|
+
},
|
|
31
|
+
400
|
|
32
|
+
);
|
|
33
|
+
}
|
|
22
34
|
// Derived from the incoming request rather than a config value — this is
|
|
23
35
|
// the origin an agent can actually reach the dev server on (host/port it
|
|
24
36
|
// just connected to), regardless of how the server is bound.
|
|
@@ -19,7 +19,12 @@ import runRequest from '../../../lib/docs/runRequest.js';
|
|
|
19
19
|
// Refusals and request errors are returned as 200 data (see runRequest.js) —
|
|
20
20
|
// only malformed input (missing pageId/requestId) is a 400.
|
|
21
21
|
async function docsRunRequestHandler(c) {
|
|
22
|
-
|
|
22
|
+
// Parse the body from a clone: runRequest builds a Lowdefy context whose
|
|
23
|
+
// getSession(c) → getAuthUser(c) reconstructs a Request from c.req.raw,
|
|
24
|
+
// which throws ("body ... disturbed or locked") if the body was already
|
|
25
|
+
// consumed. The normal /api/request path never hits this because its
|
|
26
|
+
// session is read in middleware before the handler touches the body.
|
|
27
|
+
const { pageId, requestId, payload } = await c.req.raw.clone().json();
|
|
23
28
|
try {
|
|
24
29
|
const result = await runRequest({ pageId, requestId, payload, honoContext: c });
|
|
25
30
|
return c.json(result);
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
import snapshotState from '../../../lib/docs/snapshotState.js';
|
|
18
18
|
|
|
19
19
|
// Lets an agent (or a developer) snapshot the running app's page state and
|
|
20
|
-
// recorded request/api responses into a
|
|
21
|
-
// later replay via POST /lowdefy-docs/state/load.
|
|
20
|
+
// recorded request/api responses into a checkpoint folder under .lowdefy,
|
|
21
|
+
// for later replay via POST /lowdefy-docs/state/load.
|
|
22
22
|
async function docsSnapshotStateHandler(c) {
|
|
23
23
|
const { pageId, name, notes, source, overwrite } = await c.req.json();
|
|
24
24
|
// Derived from the incoming request rather than a config value — this is
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { listStateCheckpoints } from '../../../lib/docs/checkpointStore.js';
|
|
18
18
|
|
|
19
|
-
// Lists
|
|
19
|
+
// Lists state checkpoints (page state + recorded request/api
|
|
20
20
|
// responses) — distinct from GET /lowdefy-docs/checkpoints, which lists
|
|
21
21
|
// config-directory-snapshot checkpoints (see checkpointStore.js).
|
|
22
22
|
function docsStateCheckpointsListHandler(c) {
|