plum-e2e 2.8.0 → 2.8.2
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/backend/app.js +1 -0
- package/backend/lib/nodeRegister.js +2 -10
- package/backend/mcp/server.js +444 -448
- package/backend/middleware/jwtAuth.js +0 -9
- package/backend/middleware/runnerOrAdmin.js +26 -0
- package/backend/package.json +1 -2
- package/backend/routes/mcp.routes.js +46 -0
- package/backend/routes/runners.routes.js +11 -6
- package/backend/scripts/manage-runners.mjs +7 -6
- package/backend/services/runnerService.js +11 -0
- package/bin/plum.js +0 -27
- package/bin/postinstall.js +5 -5
- package/docker-compose.yml +0 -1
- package/frontend/.svelte-kit/adapter-node/chunks/internal.js +1 -1
- package/frontend/.svelte-kit/adapter-node/manifest-full.js +1 -1
- package/frontend/.svelte-kit/adapter-node/manifest.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/0.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/1.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/11.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/12.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/3.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/5.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/6.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/8.js +1 -1
- package/frontend/.svelte-kit/adapter-node/nodes/9.js +1 -1
- package/frontend/.svelte-kit/generated/server/internal.js +1 -1
- package/frontend/.svelte-kit/output/client/.vite/manifest.json +37 -37
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{nURnIknV.js → DEKAA-Fm.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{DMyTUV-Y.js → Dhf4xQKo.js} +1 -1
- package/frontend/{build/client/_app/immutable/entry/app.BkBD0hgA.js → .svelte-kit/output/client/_app/immutable/entry/app.B0GH5ONo.js} +2 -2
- package/frontend/{build/client/_app/immutable/entry/start.BxeX1Uxs.js → .svelte-kit/output/client/_app/immutable/entry/start.Bn25CW9X.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{0.ChjDHxSJ.js → 0.-imXt0f5.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{1.DBSWa36f.js → 1.Dom3CKWF.js} +1 -1
- package/frontend/{build/client/_app/immutable/nodes/11.O4cjXCg4.js → .svelte-kit/output/client/_app/immutable/nodes/11.DMlayLCK.js} +1 -1
- package/frontend/{build/client/_app/immutable/nodes/12.BWQdeRdX.js → .svelte-kit/output/client/_app/immutable/nodes/12.Bu89A1eX.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{3._S1vrLVF.js → 3.B86rlHXU.js} +1 -1
- package/frontend/{build/client/_app/immutable/nodes/5.BiFBiWcW.js → .svelte-kit/output/client/_app/immutable/nodes/5.CqTXvadV.js} +1 -1
- package/frontend/{build/client/_app/immutable/nodes/6.BNS-W0R_.js → .svelte-kit/output/client/_app/immutable/nodes/6.BJZ62MCd.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{8.BEUdazrw.js → 8.B4fj2Xyu.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{9.CrLHYPEJ.js → 9.C57_2xq6.js} +1 -1
- package/frontend/.svelte-kit/output/client/_app/version.json +1 -1
- package/frontend/.svelte-kit/output/server/chunks/internal.js +1 -1
- package/frontend/.svelte-kit/output/server/manifest-full.js +1 -1
- package/frontend/.svelte-kit/output/server/manifest.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/0.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/1.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/11.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/12.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/3.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/5.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/6.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/8.js +1 -1
- package/frontend/.svelte-kit/output/server/nodes/9.js +1 -1
- package/frontend/build/client/_app/immutable/chunks/{nURnIknV.js → DEKAA-Fm.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/DEKAA-Fm.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DEKAA-Fm.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/{DMyTUV-Y.js → Dhf4xQKo.js} +1 -1
- package/frontend/build/client/_app/immutable/chunks/Dhf4xQKo.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/Dhf4xQKo.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/entry/app.BkBD0hgA.js → build/client/_app/immutable/entry/app.B0GH5ONo.js} +2 -2
- package/frontend/build/client/_app/immutable/entry/app.B0GH5ONo.js.br +0 -0
- package/frontend/build/client/_app/immutable/entry/app.B0GH5ONo.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/entry/start.BxeX1Uxs.js → build/client/_app/immutable/entry/start.Bn25CW9X.js} +1 -1
- package/frontend/build/client/_app/immutable/entry/{start.BxeX1Uxs.js.br → start.Bn25CW9X.js.br} +0 -0
- package/frontend/build/client/_app/immutable/entry/{start.BxeX1Uxs.js.gz → start.Bn25CW9X.js.gz} +0 -0
- package/frontend/build/client/_app/immutable/nodes/{0.ChjDHxSJ.js → 0.-imXt0f5.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/0.-imXt0f5.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.-imXt0f5.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{1.DBSWa36f.js → 1.Dom3CKWF.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/1.Dom3CKWF.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/1.Dom3CKWF.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/11.O4cjXCg4.js → build/client/_app/immutable/nodes/11.DMlayLCK.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/11.DMlayLCK.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.DMlayLCK.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/12.BWQdeRdX.js → build/client/_app/immutable/nodes/12.Bu89A1eX.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/12.Bu89A1eX.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.Bu89A1eX.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{3._S1vrLVF.js → 3.B86rlHXU.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/3.B86rlHXU.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/3.B86rlHXU.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/5.BiFBiWcW.js → build/client/_app/immutable/nodes/5.CqTXvadV.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/5.CqTXvadV.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.CqTXvadV.js.gz +0 -0
- package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/6.BNS-W0R_.js → build/client/_app/immutable/nodes/6.BJZ62MCd.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/6.BJZ62MCd.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.BJZ62MCd.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{8.BEUdazrw.js → 8.B4fj2Xyu.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/8.B4fj2Xyu.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.B4fj2Xyu.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/{9.CrLHYPEJ.js → 9.C57_2xq6.js} +1 -1
- package/frontend/build/client/_app/immutable/nodes/9.C57_2xq6.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.C57_2xq6.js.gz +0 -0
- package/frontend/build/client/_app/version.json +1 -1
- package/frontend/build/client/_app/version.json.br +0 -0
- package/frontend/build/client/_app/version.json.gz +0 -0
- package/frontend/build/server/chunks/{0-Cn_2hzqk.js → 0-DG1Uv07a.js} +2 -2
- package/frontend/build/server/chunks/{0-Cn_2hzqk.js.map → 0-DG1Uv07a.js.map} +1 -1
- package/frontend/build/server/chunks/{1-BrRGJFSB.js → 1-iB0ogJU_.js} +2 -2
- package/frontend/build/server/chunks/{1-BrRGJFSB.js.map → 1-iB0ogJU_.js.map} +1 -1
- package/frontend/build/server/chunks/{11-BLPbHJqB.js → 11-Df9vGZ_v.js} +2 -2
- package/frontend/build/server/chunks/{11-BLPbHJqB.js.map → 11-Df9vGZ_v.js.map} +1 -1
- package/frontend/build/server/chunks/{12-S1ujUrHy.js → 12-DqvjCln4.js} +2 -2
- package/frontend/build/server/chunks/{12-S1ujUrHy.js.map → 12-DqvjCln4.js.map} +1 -1
- package/frontend/build/server/chunks/{3-BHkXVoNI.js → 3-BYKCZi4U.js} +2 -2
- package/frontend/build/server/chunks/{3-BHkXVoNI.js.map → 3-BYKCZi4U.js.map} +1 -1
- package/frontend/build/server/chunks/{5-F1ZsXMt2.js → 5-B6Jc1foX.js} +2 -2
- package/frontend/build/server/chunks/{5-F1ZsXMt2.js.map → 5-B6Jc1foX.js.map} +1 -1
- package/frontend/build/server/chunks/{6-FlZLnYJ6.js → 6-dXZKtN86.js} +2 -2
- package/frontend/build/server/chunks/{6-FlZLnYJ6.js.map → 6-dXZKtN86.js.map} +1 -1
- package/frontend/build/server/chunks/{8-DXReUGxi.js → 8-D-Bp7HzS.js} +2 -2
- package/frontend/build/server/chunks/{8-DXReUGxi.js.map → 8-D-Bp7HzS.js.map} +1 -1
- package/frontend/build/server/chunks/{9-Cy_mGmt4.js → 9-BynjuGUO.js} +2 -2
- package/frontend/build/server/chunks/{9-Cy_mGmt4.js.map → 9-BynjuGUO.js.map} +1 -1
- package/frontend/build/server/index.js +1 -1
- package/frontend/build/server/index.js.map +1 -1
- package/frontend/build/server/manifest.js +10 -10
- package/frontend/build/server/manifest.js.map +1 -1
- package/frontend/src/routes/settings/+page.svelte +4 -5
- package/package.json +1 -1
- package/frontend/build/client/_app/immutable/chunks/DMyTUV-Y.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/DMyTUV-Y.js.gz +0 -0
- package/frontend/build/client/_app/immutable/chunks/nURnIknV.js.br +0 -0
- package/frontend/build/client/_app/immutable/chunks/nURnIknV.js.gz +0 -0
- package/frontend/build/client/_app/immutable/entry/app.BkBD0hgA.js.br +0 -0
- package/frontend/build/client/_app/immutable/entry/app.BkBD0hgA.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.ChjDHxSJ.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/0.ChjDHxSJ.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/1.DBSWa36f.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/1.DBSWa36f.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.O4cjXCg4.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/11.O4cjXCg4.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.BWQdeRdX.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/12.BWQdeRdX.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/3._S1vrLVF.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/3._S1vrLVF.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.BiFBiWcW.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/5.BiFBiWcW.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.BNS-W0R_.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/6.BNS-W0R_.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.BEUdazrw.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/8.BEUdazrw.js.gz +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.CrLHYPEJ.js.br +0 -0
- package/frontend/build/client/_app/immutable/nodes/9.CrLHYPEJ.js.gz +0 -0
|
@@ -26,15 +26,6 @@ function jwtAuth(req, res, next) {
|
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// Node/CLI shared secret — grants admin-equivalent access to headless tools
|
|
30
|
-
// (manage-runners.mjs, node self-registration) that have no browser session
|
|
31
|
-
// or JWT to present. Distributed manually to node machines as PLUM_NODE_KEY.
|
|
32
|
-
const nodeKey = process.env.PLUM_NODE_KEY;
|
|
33
|
-
if (nodeKey && auth === `${AUTH_SCHEME.API_KEY} ${nodeKey}`) {
|
|
34
|
-
req.user = { userId: null, role: 'admin' };
|
|
35
|
-
return next();
|
|
36
|
-
}
|
|
37
|
-
|
|
38
29
|
if (!auth || !auth.startsWith(`${AUTH_SCHEME.BEARER} `)) {
|
|
39
30
|
return res.status(401).json({ error: 'Unauthorized' });
|
|
40
31
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { jwtAuth } = require('./jwtAuth');
|
|
7
|
+
const { requireAdmin } = require('./requireAdmin');
|
|
8
|
+
const runnerService = require('../services/runnerService');
|
|
9
|
+
const { AUTH_SCHEME } = require('../lib/authHeader');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Accepts either a logged-in admin (web Settings UI) or any currently-registered
|
|
13
|
+
* runner's own token (manage-runners.mjs and node self-registration have no
|
|
14
|
+
* browser session/JWT to present, but every runner already has a token
|
|
15
|
+
* generated at registration — reuse it instead of a second credential).
|
|
16
|
+
*/
|
|
17
|
+
async function runnerOrAdmin(req, res, next) {
|
|
18
|
+
const auth = req.headers.authorization;
|
|
19
|
+
if (auth && auth.startsWith(`${AUTH_SCHEME.BEARER} `)) {
|
|
20
|
+
const token = auth.slice(AUTH_SCHEME.BEARER.length + 1);
|
|
21
|
+
if (await runnerService.isValidToken(token)) return next();
|
|
22
|
+
}
|
|
23
|
+
jwtAuth(req, res, () => requireAdmin(req, res, next));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { runnerOrAdmin };
|
package/backend/package.json
CHANGED
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
"create-env": "node services/envService.js",
|
|
9
9
|
"test": "node config/scripts/run-tests.js",
|
|
10
10
|
"manage-runners": "node scripts/manage-runners.mjs",
|
|
11
|
-
"create-test": "node config/scripts/create-test.mjs"
|
|
12
|
-
"mcp": "node mcp/server.js"
|
|
11
|
+
"create-test": "node config/scripts/create-test.mjs"
|
|
13
12
|
},
|
|
14
13
|
"keywords": [],
|
|
15
14
|
"author": "silverlunah",
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of Plum.
|
|
3
|
+
* Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const express = require('express');
|
|
8
|
+
const router = express.Router();
|
|
9
|
+
const { jwtAuth } = require('../middleware/jwtAuth');
|
|
10
|
+
const { createMcpServer } = require('../mcp/server');
|
|
11
|
+
|
|
12
|
+
// Use an absolute path to bypass the SDK's wildcard export mapping, same as mcp/server.js.
|
|
13
|
+
const sdkCjs = path.resolve(
|
|
14
|
+
__dirname,
|
|
15
|
+
'..',
|
|
16
|
+
'node_modules',
|
|
17
|
+
'@modelcontextprotocol',
|
|
18
|
+
'sdk',
|
|
19
|
+
'dist',
|
|
20
|
+
'cjs'
|
|
21
|
+
);
|
|
22
|
+
const { StreamableHTTPServerTransport } = require(path.join(sdkCjs, 'server', 'streamableHttp.js'));
|
|
23
|
+
|
|
24
|
+
// Stateless: each request gets its own McpServer + transport, closed once the
|
|
25
|
+
// response finishes. Every Plum tool is a one-shot request/response, so there's
|
|
26
|
+
// no need to keep a session alive between calls.
|
|
27
|
+
async function handleMcp(req, res, next) {
|
|
28
|
+
try {
|
|
29
|
+
const server = createMcpServer({ userId: req.user.userId });
|
|
30
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
31
|
+
res.on('close', () => {
|
|
32
|
+
transport.close();
|
|
33
|
+
server.close();
|
|
34
|
+
});
|
|
35
|
+
await server.connect(transport);
|
|
36
|
+
await transport.handleRequest(req, res, req.body);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
next(e);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
router.post('/', jwtAuth, handleMcp);
|
|
43
|
+
router.get('/', jwtAuth, handleMcp);
|
|
44
|
+
router.delete('/', jwtAuth, handleMcp);
|
|
45
|
+
|
|
46
|
+
module.exports = router;
|
|
@@ -8,8 +8,13 @@ const router = express.Router();
|
|
|
8
8
|
const runnerService = require('../services/runnerService');
|
|
9
9
|
const { jwtAuth } = require('../middleware/jwtAuth');
|
|
10
10
|
const { requireAdmin } = require('../middleware/requireAdmin');
|
|
11
|
+
const { runnerOrAdmin } = require('../middleware/runnerOrAdmin');
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
// List/create are hit by headless CLI tools (manage-runners.mjs, node
|
|
14
|
+
// self-registration) with no session to present — same as before the
|
|
15
|
+
// runner-management routes were locked down. The response never includes
|
|
16
|
+
// runner tokens (toPublicRunner strips them), so listing isn't a secret leak.
|
|
17
|
+
router.get('/', async (req, res) => {
|
|
13
18
|
try {
|
|
14
19
|
const runners = await runnerService.getAll();
|
|
15
20
|
res.json({ runners });
|
|
@@ -30,7 +35,7 @@ router.post('/probe', jwtAuth, requireAdmin, async (req, res) => {
|
|
|
30
35
|
}
|
|
31
36
|
});
|
|
32
37
|
|
|
33
|
-
router.post('/',
|
|
38
|
+
router.post('/', async (req, res) => {
|
|
34
39
|
try {
|
|
35
40
|
const { name, url, token, browser } = req.body;
|
|
36
41
|
if (!name || !url || !token)
|
|
@@ -52,7 +57,7 @@ router.put('/:id', jwtAuth, requireAdmin, async (req, res) => {
|
|
|
52
57
|
}
|
|
53
58
|
});
|
|
54
59
|
|
|
55
|
-
router.delete('/:id',
|
|
60
|
+
router.delete('/:id', runnerOrAdmin, async (req, res) => {
|
|
56
61
|
try {
|
|
57
62
|
await runnerService.stop(req.params.id);
|
|
58
63
|
await runnerService.remove(req.params.id);
|
|
@@ -62,7 +67,7 @@ router.delete('/:id', jwtAuth, requireAdmin, async (req, res) => {
|
|
|
62
67
|
}
|
|
63
68
|
});
|
|
64
69
|
|
|
65
|
-
router.post('/:id/ping',
|
|
70
|
+
router.post('/:id/ping', runnerOrAdmin, async (req, res) => {
|
|
66
71
|
try {
|
|
67
72
|
const result = await runnerService.ping(req.params.id);
|
|
68
73
|
res.json(result);
|
|
@@ -71,7 +76,7 @@ router.post('/:id/ping', jwtAuth, requireAdmin, async (req, res) => {
|
|
|
71
76
|
}
|
|
72
77
|
});
|
|
73
78
|
|
|
74
|
-
router.post('/:id/stop',
|
|
79
|
+
router.post('/:id/stop', runnerOrAdmin, async (req, res) => {
|
|
75
80
|
try {
|
|
76
81
|
const result = await runnerService.stop(req.params.id);
|
|
77
82
|
res.json(result);
|
|
@@ -80,7 +85,7 @@ router.post('/:id/stop', jwtAuth, requireAdmin, async (req, res) => {
|
|
|
80
85
|
}
|
|
81
86
|
});
|
|
82
87
|
|
|
83
|
-
router.post('/:id/restart',
|
|
88
|
+
router.post('/:id/restart', runnerOrAdmin, async (req, res) => {
|
|
84
89
|
try {
|
|
85
90
|
const result = await runnerService.restart(req.params.id);
|
|
86
91
|
res.json(result);
|
|
@@ -31,18 +31,19 @@ const {
|
|
|
31
31
|
findPidOnPort,
|
|
32
32
|
killPort
|
|
33
33
|
} = runnerProcess;
|
|
34
|
-
const { generateToken, registerWithPrimary, detectLanIp } = nodeRegister;
|
|
34
|
+
const { generateToken, registerWithPrimary, detectLanIp, loadNodeConfig } = nodeRegister;
|
|
35
35
|
|
|
36
36
|
const API_URL = process.env.PLUM_API_URL || 'http://localhost:3001';
|
|
37
37
|
|
|
38
38
|
const cancelled = (v) => clack.isCancel(v);
|
|
39
39
|
|
|
40
|
-
// The /runners routes
|
|
41
|
-
//
|
|
42
|
-
//
|
|
40
|
+
// The mutating /runners routes accept a registered runner's own token in place
|
|
41
|
+
// of an admin session — this manager has no browser/JWT to present, but a node
|
|
42
|
+
// already gets a token at registration time (see backend/middleware/jwtAuth.js
|
|
43
|
+
// / runnerOrAdmin.js), so reuse that instead of a separate credential.
|
|
44
|
+
const localToken = loadNodeConfig(process.cwd()).token;
|
|
43
45
|
function authHeaders() {
|
|
44
|
-
|
|
45
|
-
return nodeKey ? { Authorization: `ApiKey ${nodeKey}` } : {};
|
|
46
|
+
return localToken ? { Authorization: `Bearer ${localToken}` } : {};
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
/**
|
|
@@ -75,6 +75,16 @@ const update = async (id, { name, url, token, browser }) => {
|
|
|
75
75
|
// outbound requests to the runner node (ping/stop/restart/dispatch below).
|
|
76
76
|
const getById = (id) => prisma.runner.findUnique({ where: { id } });
|
|
77
77
|
|
|
78
|
+
// A runner's own token (generated once at registration) doubles as its
|
|
79
|
+
// credential for calling back into the primary's control routes — no separate
|
|
80
|
+
// secret to configure. Any currently-registered token is accepted, not just
|
|
81
|
+
// the target runner's own, since one CLI session manages the whole fleet.
|
|
82
|
+
async function isValidToken(token) {
|
|
83
|
+
if (!token) return false;
|
|
84
|
+
const runner = await prisma.runner.findFirst({ where: { token }, select: { id: true } });
|
|
85
|
+
return Boolean(runner);
|
|
86
|
+
}
|
|
87
|
+
|
|
78
88
|
// ---------------------------------------------------------------------------
|
|
79
89
|
// Connectivity
|
|
80
90
|
// ---------------------------------------------------------------------------
|
|
@@ -275,6 +285,7 @@ module.exports = {
|
|
|
275
285
|
remove,
|
|
276
286
|
update,
|
|
277
287
|
getById,
|
|
288
|
+
isValidToken,
|
|
278
289
|
probe,
|
|
279
290
|
ping,
|
|
280
291
|
stop,
|
package/bin/plum.js
CHANGED
|
@@ -1270,32 +1270,6 @@ switch (command) {
|
|
|
1270
1270
|
break;
|
|
1271
1271
|
}
|
|
1272
1272
|
|
|
1273
|
-
case 'mcp': {
|
|
1274
|
-
// Claude launches `plum mcp` directly, skipping every other command
|
|
1275
|
-
// (`server start`, `node start`, `run-test`) that would otherwise have
|
|
1276
|
-
// already called ensureBackendDeps/prepareEnv. Without this guard, a
|
|
1277
|
-
// stale/missing backend/node_modules (e.g. after `npm i -g plum-e2e@latest`
|
|
1278
|
-
// wipes it) makes the MCP server fail outright on its own requires.
|
|
1279
|
-
try {
|
|
1280
|
-
const { ensureBackendDeps } = runnerProcessLib();
|
|
1281
|
-
ensureBackendDeps();
|
|
1282
|
-
} catch (e) {
|
|
1283
|
-
console.error(`Could not verify backend dependencies: ${e.message}`);
|
|
1284
|
-
process.exit(1);
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
const mcpScript = path.join(plumRoot, 'backend', 'mcp', 'server.js');
|
|
1288
|
-
spawn(process.execPath, [mcpScript], {
|
|
1289
|
-
stdio: 'inherit',
|
|
1290
|
-
env: {
|
|
1291
|
-
...process.env,
|
|
1292
|
-
PLUM_API_URL: process.env.PLUM_API_URL || 'http://localhost:3001',
|
|
1293
|
-
PLUM_API_KEY: process.env.PLUM_API_KEY || ''
|
|
1294
|
-
}
|
|
1295
|
-
});
|
|
1296
|
-
break;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
1273
|
default:
|
|
1300
1274
|
console.log('--------------------------------------\n');
|
|
1301
1275
|
console.log('Usage: plum <command>\n');
|
|
@@ -1340,6 +1314,5 @@ switch (command) {
|
|
|
1340
1314
|
console.log(' --browser <name> chromium | firefox (default: chromium)');
|
|
1341
1315
|
console.log(' create-step Interactively scaffold a new step definition');
|
|
1342
1316
|
console.log(' create-test Scaffold a new .feature + Page.ts + Steps.ts');
|
|
1343
|
-
console.log(' mcp Start the Plum MCP server (stdio) for Claude integration');
|
|
1344
1317
|
console.log('\n--------------------------------------\n');
|
|
1345
1318
|
}
|
package/bin/postinstall.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Runs on every `npm install` of this package (including `npm i -g plum-e2e`
|
|
8
|
-
* and re-installs on update), so
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* and re-installs on update), so backend-dependent commands work immediately
|
|
9
|
+
* without the user first having to run `plum server start` or `plum node
|
|
10
|
+
* start` — the only commands that used to trigger a backend `npm install`
|
|
11
|
+
* on their own.
|
|
12
12
|
*
|
|
13
13
|
* Best-effort: a failure here must not fail the overall `npm install`, so
|
|
14
14
|
* errors are logged, not thrown.
|
|
@@ -29,6 +29,6 @@ try {
|
|
|
29
29
|
} catch (e) {
|
|
30
30
|
console.warn(`[plum] Could not install backend dependencies automatically: ${e.message}`);
|
|
31
31
|
console.warn(
|
|
32
|
-
'[plum] If `plum
|
|
32
|
+
'[plum] If `plum server start` or `plum node start` fails, run `npm install` inside its backend/ folder.'
|
|
33
33
|
);
|
|
34
34
|
}
|
package/docker-compose.yml
CHANGED
|
@@ -641,7 +641,7 @@ const options = {
|
|
|
641
641
|
<div class="error">
|
|
642
642
|
<span class="status">` + status + '</span>\n <div class="message">\n <h1>' + message + "</h1>\n </div>\n </div>\n </body>\n</html>\n"
|
|
643
643
|
},
|
|
644
|
-
version_hash: "
|
|
644
|
+
version_hash: "1m6qzpt"
|
|
645
645
|
};
|
|
646
646
|
async function get_hooks() {
|
|
647
647
|
let handle;
|
|
@@ -15,7 +15,7 @@ return {
|
|
|
15
15
|
assets: new Set(["favicon-32x32.png","favicon.ico"]),
|
|
16
16
|
mimeTypes: {".png":"image/png"},
|
|
17
17
|
_: {
|
|
18
|
-
client: {start:"_app/immutable/entry/start.
|
|
18
|
+
client: {start:"_app/immutable/entry/start.Bn25CW9X.js",app:"_app/immutable/entry/app.B0GH5ONo.js",imports:["_app/immutable/entry/start.Bn25CW9X.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/entry/app.B0GH5ONo.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/2zMvi6fR.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
|
19
19
|
nodes: [
|
|
20
20
|
__memo(() => import('./nodes/0.js')),
|
|
21
21
|
__memo(() => import('./nodes/1.js')),
|
|
@@ -15,7 +15,7 @@ return {
|
|
|
15
15
|
assets: new Set(["favicon-32x32.png","favicon.ico"]),
|
|
16
16
|
mimeTypes: {".png":"image/png"},
|
|
17
17
|
_: {
|
|
18
|
-
client: {start:"_app/immutable/entry/start.
|
|
18
|
+
client: {start:"_app/immutable/entry/start.Bn25CW9X.js",app:"_app/immutable/entry/app.B0GH5ONo.js",imports:["_app/immutable/entry/start.Bn25CW9X.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/entry/app.B0GH5ONo.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/2zMvi6fR.js"],stylesheets:[],fonts:[],uses_env_dynamic_public:false},
|
|
19
19
|
nodes: [
|
|
20
20
|
__memo(() => import('./nodes/0.js')),
|
|
21
21
|
__memo(() => import('./nodes/1.js')),
|
|
@@ -10,6 +10,6 @@ let component_cache;
|
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/_layout.svelte.js')).default;
|
|
11
11
|
export { universal };
|
|
12
12
|
export const universal_id = "src/routes/+layout.js";
|
|
13
|
-
export const imports = ["_app/immutable/nodes/0.
|
|
13
|
+
export const imports = ["_app/immutable/nodes/0.-imXt0f5.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/TOCyHP97.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/DEKAA-Fm.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/3InFPLvE.js","_app/immutable/chunks/DHM6H-es.js","_app/immutable/chunks/DbgD7JLa.js","_app/immutable/chunks/BUwxRmHf.js","_app/immutable/chunks/DSLBMEpr.js","_app/immutable/chunks/DMZwI8Cj.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/CMEtqUfQ.js","_app/immutable/chunks/CuqnZUex.js","_app/immutable/chunks/BmZ9upwL.js"];
|
|
14
14
|
export const stylesheets = ["_app/immutable/assets/0.BhSG-jZH.css","_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css","_app/immutable/assets/Modal.BZpm0HP8.css","_app/immutable/assets/Badge.D1SV0Gv7.css"];
|
|
15
15
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 1;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/fallbacks/error.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/1.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/1.Dom3CKWF.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/Dhf4xQKo.js"];
|
|
12
12
|
export const stylesheets = [];
|
|
13
13
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 11;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/test-repository/runs/_id_/_page.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/11.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/11.DMlayLCK.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/TOCyHP97.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/t-La0hgu.js","_app/immutable/chunks/CzlPaHY9.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/CNdcuGR0.js","_app/immutable/chunks/DEKAA-Fm.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/BUwxRmHf.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/DHM6H-es.js","_app/immutable/chunks/CMEtqUfQ.js","_app/immutable/chunks/BKuh57WU.js","_app/immutable/chunks/BAMUGSh7.js"];
|
|
12
12
|
export const stylesheets = ["_app/immutable/assets/11.BWAH0Beh.css","_app/immutable/assets/Button.FOWXxvc7.css","_app/immutable/assets/Modal.BZpm0HP8.css","_app/immutable/assets/EmptyState.DUDM4P19.css"];
|
|
13
13
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 12;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/test-repository/suites/_id_/_page.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/12.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/12.Bu89A1eX.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/TOCyHP97.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/t-La0hgu.js","_app/immutable/chunks/CzlPaHY9.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/CNdcuGR0.js","_app/immutable/chunks/Bfc47y5P.js","_app/immutable/chunks/DEKAA-Fm.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/BUwxRmHf.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/BKuh57WU.js","_app/immutable/chunks/DMZwI8Cj.js","_app/immutable/chunks/CMEtqUfQ.js","_app/immutable/chunks/w0D4Uzlq.js","_app/immutable/chunks/BAMUGSh7.js"];
|
|
12
12
|
export const stylesheets = ["_app/immutable/assets/12.Bj_a_5lv.css","_app/immutable/assets/Button.FOWXxvc7.css","_app/immutable/assets/EmptyState.DUDM4P19.css","_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css","_app/immutable/assets/Modal.BZpm0HP8.css","_app/immutable/assets/Pagination.DZmi2Po-.css"];
|
|
13
13
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 3;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/login/_page.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/3.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/3.B86rlHXU.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/t-La0hgu.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/BmZ9upwL.js","_app/immutable/chunks/DCgHuuvs.js","_app/immutable/chunks/Dlnxgs49.js"];
|
|
12
12
|
export const stylesheets = ["_app/immutable/assets/3.BR3wgXcZ.css"];
|
|
13
13
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 5;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/reports/_id_/_page.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/5.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/5.CqTXvadV.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/TOCyHP97.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/Bfc47y5P.js","_app/immutable/chunks/CNdcuGR0.js","_app/immutable/chunks/DEKAA-Fm.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/DbgD7JLa.js","_app/immutable/chunks/DSLBMEpr.js","_app/immutable/chunks/Co3xKhEp.js","_app/immutable/chunks/CuqnZUex.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/CGeZlCaI.js"];
|
|
12
12
|
export const stylesheets = ["_app/immutable/assets/5.DFV9OgAu.css","_app/immutable/assets/Badge.D1SV0Gv7.css","_app/immutable/assets/BackLink.CvWWuO3h.css"];
|
|
13
13
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 6;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/reports/live/_page.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/6.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/6.BJZ62MCd.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/TOCyHP97.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/2zMvi6fR.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/DEKAA-Fm.js","_app/immutable/chunks/DHM6H-es.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/DbgD7JLa.js","_app/immutable/chunks/Co3xKhEp.js","_app/immutable/chunks/DSLBMEpr.js","_app/immutable/chunks/CGeZlCaI.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/CuqnZUex.js"];
|
|
12
12
|
export const stylesheets = ["_app/immutable/assets/6.CFnbmY-D.css","_app/immutable/assets/BackLink.CvWWuO3h.css","_app/immutable/assets/Badge.D1SV0Gv7.css"];
|
|
13
13
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 8;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/settings/_page.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/8.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/8.B4fj2Xyu.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/TOCyHP97.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/t-La0hgu.js","_app/immutable/chunks/CzlPaHY9.js","_app/immutable/chunks/C8OURRSs.js","_app/immutable/chunks/CNdcuGR0.js","_app/immutable/chunks/2zMvi6fR.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/3InFPLvE.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/BUwxRmHf.js","_app/immutable/chunks/BmZ9upwL.js","_app/immutable/chunks/DHM6H-es.js","_app/immutable/chunks/DCgHuuvs.js","_app/immutable/chunks/DMZwI8Cj.js","_app/immutable/chunks/CMEtqUfQ.js","_app/immutable/chunks/CuqnZUex.js"];
|
|
12
12
|
export const stylesheets = ["_app/immutable/assets/8.BwgOqeMo.css","_app/immutable/assets/Button.FOWXxvc7.css","_app/immutable/assets/ConfirmModal.Dd3uP-Sk.css","_app/immutable/assets/Modal.BZpm0HP8.css","_app/immutable/assets/Badge.D1SV0Gv7.css"];
|
|
13
13
|
export const fonts = [];
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
export const index = 9;
|
|
9
9
|
let component_cache;
|
|
10
10
|
export const component = async () => component_cache ??= (await import('../entries/pages/setup/_page.svelte.js')).default;
|
|
11
|
-
export const imports = ["_app/immutable/nodes/9.
|
|
11
|
+
export const imports = ["_app/immutable/nodes/9.C57_2xq6.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/CgnwuKKO.js","_app/immutable/chunks/CUC98kMZ.js","_app/immutable/chunks/t-La0hgu.js","_app/immutable/chunks/Dhf4xQKo.js","_app/immutable/chunks/BmZ9upwL.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/DCgHuuvs.js","_app/immutable/chunks/Dlnxgs49.js"];
|
|
12
12
|
export const stylesheets = ["_app/immutable/assets/9._h2o67kg.css"];
|
|
13
13
|
export const fonts = [];
|
|
@@ -26,7 +26,7 @@ export const options = {
|
|
|
26
26
|
app: ({ head, body, assets, nonce, env }) => "<!--\nThis file is part of Plum.\nLicensed under the MIT License. See LICENSE file in the project root for details.\n-->\n<!doctype html>\n<html lang=\"en\" data-theme=\"light\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<link rel=\"icon\" href=\"" + assets + "/favicon.ico\" />\n\t\t<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n\t\t<link rel=\"preconnect\" href=\"https://fonts.googleapis.com\" />\n\t\t<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin />\n\t\t<link\n\t\t\thref=\"https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500&family=DM+Sans:wght@300;400;500&display=swap\"\n\t\t\trel=\"stylesheet\"\n\t\t/>\n\t\t<!-- Prevent theme flash before Svelte hydrates -->\n\t\t<script>\n\t\t\ttry {\n\t\t\t\tconst t = localStorage.getItem('plum-theme');\n\t\t\t\tif (t) document.documentElement.setAttribute('data-theme', t);\n\t\t\t} catch (e) {}\n\t\t</script>\n\t\t" + head + "\n\t</head>\n\t<body data-sveltekit-preload-data=\"hover\">\n\t\t<div style=\"display: contents\">" + body + "</div>\n\t</body>\n</html>\n",
|
|
27
27
|
error: ({ status, message }) => "<!doctype html>\n<html lang=\"en\">\n\t<head>\n\t\t<meta charset=\"utf-8\" />\n\t\t<title>" + message + "</title>\n\n\t\t<style>\n\t\t\tbody {\n\t\t\t\t--bg: white;\n\t\t\t\t--fg: #222;\n\t\t\t\t--divider: #ccc;\n\t\t\t\tbackground: var(--bg);\n\t\t\t\tcolor: var(--fg);\n\t\t\t\tfont-family:\n\t\t\t\t\tsystem-ui,\n\t\t\t\t\t-apple-system,\n\t\t\t\t\tBlinkMacSystemFont,\n\t\t\t\t\t'Segoe UI',\n\t\t\t\t\tRoboto,\n\t\t\t\t\tOxygen,\n\t\t\t\t\tUbuntu,\n\t\t\t\t\tCantarell,\n\t\t\t\t\t'Open Sans',\n\t\t\t\t\t'Helvetica Neue',\n\t\t\t\t\tsans-serif;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t.error {\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t\tmax-width: 32rem;\n\t\t\t\tmargin: 0 1rem;\n\t\t\t}\n\n\t\t\t.status {\n\t\t\t\tfont-weight: 200;\n\t\t\t\tfont-size: 3rem;\n\t\t\t\tline-height: 1;\n\t\t\t\tposition: relative;\n\t\t\t\ttop: -0.05rem;\n\t\t\t}\n\n\t\t\t.message {\n\t\t\t\tborder-left: 1px solid var(--divider);\n\t\t\t\tpadding: 0 0 0 1rem;\n\t\t\t\tmargin: 0 0 0 1rem;\n\t\t\t\tmin-height: 2.5rem;\n\t\t\t\tdisplay: flex;\n\t\t\t\talign-items: center;\n\t\t\t}\n\n\t\t\t.message h1 {\n\t\t\t\tfont-weight: 400;\n\t\t\t\tfont-size: 1em;\n\t\t\t\tmargin: 0;\n\t\t\t}\n\n\t\t\t@media (prefers-color-scheme: dark) {\n\t\t\t\tbody {\n\t\t\t\t\t--bg: #222;\n\t\t\t\t\t--fg: #ddd;\n\t\t\t\t\t--divider: #666;\n\t\t\t\t}\n\t\t\t}\n\t\t</style>\n\t</head>\n\t<body>\n\t\t<div class=\"error\">\n\t\t\t<span class=\"status\">" + status + "</span>\n\t\t\t<div class=\"message\">\n\t\t\t\t<h1>" + message + "</h1>\n\t\t\t</div>\n\t\t</div>\n\t</body>\n</html>\n"
|
|
28
28
|
},
|
|
29
|
-
version_hash: "
|
|
29
|
+
version_hash: "1m6qzpt"
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export async function get_hooks() {
|