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.
Files changed (143) hide show
  1. package/backend/app.js +1 -0
  2. package/backend/lib/nodeRegister.js +2 -10
  3. package/backend/mcp/server.js +444 -448
  4. package/backend/middleware/jwtAuth.js +0 -9
  5. package/backend/middleware/runnerOrAdmin.js +26 -0
  6. package/backend/package.json +1 -2
  7. package/backend/routes/mcp.routes.js +46 -0
  8. package/backend/routes/runners.routes.js +11 -6
  9. package/backend/scripts/manage-runners.mjs +7 -6
  10. package/backend/services/runnerService.js +11 -0
  11. package/bin/plum.js +0 -27
  12. package/bin/postinstall.js +5 -5
  13. package/docker-compose.yml +0 -1
  14. package/frontend/.svelte-kit/adapter-node/chunks/internal.js +1 -1
  15. package/frontend/.svelte-kit/adapter-node/manifest-full.js +1 -1
  16. package/frontend/.svelte-kit/adapter-node/manifest.js +1 -1
  17. package/frontend/.svelte-kit/adapter-node/nodes/0.js +1 -1
  18. package/frontend/.svelte-kit/adapter-node/nodes/1.js +1 -1
  19. package/frontend/.svelte-kit/adapter-node/nodes/11.js +1 -1
  20. package/frontend/.svelte-kit/adapter-node/nodes/12.js +1 -1
  21. package/frontend/.svelte-kit/adapter-node/nodes/3.js +1 -1
  22. package/frontend/.svelte-kit/adapter-node/nodes/5.js +1 -1
  23. package/frontend/.svelte-kit/adapter-node/nodes/6.js +1 -1
  24. package/frontend/.svelte-kit/adapter-node/nodes/8.js +1 -1
  25. package/frontend/.svelte-kit/adapter-node/nodes/9.js +1 -1
  26. package/frontend/.svelte-kit/generated/server/internal.js +1 -1
  27. package/frontend/.svelte-kit/output/client/.vite/manifest.json +37 -37
  28. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{nURnIknV.js → DEKAA-Fm.js} +1 -1
  29. package/frontend/.svelte-kit/output/client/_app/immutable/chunks/{DMyTUV-Y.js → Dhf4xQKo.js} +1 -1
  30. package/frontend/{build/client/_app/immutable/entry/app.BkBD0hgA.js → .svelte-kit/output/client/_app/immutable/entry/app.B0GH5ONo.js} +2 -2
  31. package/frontend/{build/client/_app/immutable/entry/start.BxeX1Uxs.js → .svelte-kit/output/client/_app/immutable/entry/start.Bn25CW9X.js} +1 -1
  32. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{0.ChjDHxSJ.js → 0.-imXt0f5.js} +1 -1
  33. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{1.DBSWa36f.js → 1.Dom3CKWF.js} +1 -1
  34. package/frontend/{build/client/_app/immutable/nodes/11.O4cjXCg4.js → .svelte-kit/output/client/_app/immutable/nodes/11.DMlayLCK.js} +1 -1
  35. package/frontend/{build/client/_app/immutable/nodes/12.BWQdeRdX.js → .svelte-kit/output/client/_app/immutable/nodes/12.Bu89A1eX.js} +1 -1
  36. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{3._S1vrLVF.js → 3.B86rlHXU.js} +1 -1
  37. package/frontend/{build/client/_app/immutable/nodes/5.BiFBiWcW.js → .svelte-kit/output/client/_app/immutable/nodes/5.CqTXvadV.js} +1 -1
  38. package/frontend/{build/client/_app/immutable/nodes/6.BNS-W0R_.js → .svelte-kit/output/client/_app/immutable/nodes/6.BJZ62MCd.js} +1 -1
  39. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{8.BEUdazrw.js → 8.B4fj2Xyu.js} +1 -1
  40. package/frontend/.svelte-kit/output/client/_app/immutable/nodes/{9.CrLHYPEJ.js → 9.C57_2xq6.js} +1 -1
  41. package/frontend/.svelte-kit/output/client/_app/version.json +1 -1
  42. package/frontend/.svelte-kit/output/server/chunks/internal.js +1 -1
  43. package/frontend/.svelte-kit/output/server/manifest-full.js +1 -1
  44. package/frontend/.svelte-kit/output/server/manifest.js +1 -1
  45. package/frontend/.svelte-kit/output/server/nodes/0.js +1 -1
  46. package/frontend/.svelte-kit/output/server/nodes/1.js +1 -1
  47. package/frontend/.svelte-kit/output/server/nodes/11.js +1 -1
  48. package/frontend/.svelte-kit/output/server/nodes/12.js +1 -1
  49. package/frontend/.svelte-kit/output/server/nodes/3.js +1 -1
  50. package/frontend/.svelte-kit/output/server/nodes/5.js +1 -1
  51. package/frontend/.svelte-kit/output/server/nodes/6.js +1 -1
  52. package/frontend/.svelte-kit/output/server/nodes/8.js +1 -1
  53. package/frontend/.svelte-kit/output/server/nodes/9.js +1 -1
  54. package/frontend/build/client/_app/immutable/chunks/{nURnIknV.js → DEKAA-Fm.js} +1 -1
  55. package/frontend/build/client/_app/immutable/chunks/DEKAA-Fm.js.br +0 -0
  56. package/frontend/build/client/_app/immutable/chunks/DEKAA-Fm.js.gz +0 -0
  57. package/frontend/build/client/_app/immutable/chunks/{DMyTUV-Y.js → Dhf4xQKo.js} +1 -1
  58. package/frontend/build/client/_app/immutable/chunks/Dhf4xQKo.js.br +0 -0
  59. package/frontend/build/client/_app/immutable/chunks/Dhf4xQKo.js.gz +0 -0
  60. package/frontend/{.svelte-kit/output/client/_app/immutable/entry/app.BkBD0hgA.js → build/client/_app/immutable/entry/app.B0GH5ONo.js} +2 -2
  61. package/frontend/build/client/_app/immutable/entry/app.B0GH5ONo.js.br +0 -0
  62. package/frontend/build/client/_app/immutable/entry/app.B0GH5ONo.js.gz +0 -0
  63. package/frontend/{.svelte-kit/output/client/_app/immutable/entry/start.BxeX1Uxs.js → build/client/_app/immutable/entry/start.Bn25CW9X.js} +1 -1
  64. package/frontend/build/client/_app/immutable/entry/{start.BxeX1Uxs.js.br → start.Bn25CW9X.js.br} +0 -0
  65. package/frontend/build/client/_app/immutable/entry/{start.BxeX1Uxs.js.gz → start.Bn25CW9X.js.gz} +0 -0
  66. package/frontend/build/client/_app/immutable/nodes/{0.ChjDHxSJ.js → 0.-imXt0f5.js} +1 -1
  67. package/frontend/build/client/_app/immutable/nodes/0.-imXt0f5.js.br +0 -0
  68. package/frontend/build/client/_app/immutable/nodes/0.-imXt0f5.js.gz +0 -0
  69. package/frontend/build/client/_app/immutable/nodes/{1.DBSWa36f.js → 1.Dom3CKWF.js} +1 -1
  70. package/frontend/build/client/_app/immutable/nodes/1.Dom3CKWF.js.br +0 -0
  71. package/frontend/build/client/_app/immutable/nodes/1.Dom3CKWF.js.gz +0 -0
  72. package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/11.O4cjXCg4.js → build/client/_app/immutable/nodes/11.DMlayLCK.js} +1 -1
  73. package/frontend/build/client/_app/immutable/nodes/11.DMlayLCK.js.br +0 -0
  74. package/frontend/build/client/_app/immutable/nodes/11.DMlayLCK.js.gz +0 -0
  75. package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/12.BWQdeRdX.js → build/client/_app/immutable/nodes/12.Bu89A1eX.js} +1 -1
  76. package/frontend/build/client/_app/immutable/nodes/12.Bu89A1eX.js.br +0 -0
  77. package/frontend/build/client/_app/immutable/nodes/12.Bu89A1eX.js.gz +0 -0
  78. package/frontend/build/client/_app/immutable/nodes/{3._S1vrLVF.js → 3.B86rlHXU.js} +1 -1
  79. package/frontend/build/client/_app/immutable/nodes/3.B86rlHXU.js.br +0 -0
  80. package/frontend/build/client/_app/immutable/nodes/3.B86rlHXU.js.gz +0 -0
  81. package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/5.BiFBiWcW.js → build/client/_app/immutable/nodes/5.CqTXvadV.js} +1 -1
  82. package/frontend/build/client/_app/immutable/nodes/5.CqTXvadV.js.br +0 -0
  83. package/frontend/build/client/_app/immutable/nodes/5.CqTXvadV.js.gz +0 -0
  84. package/frontend/{.svelte-kit/output/client/_app/immutable/nodes/6.BNS-W0R_.js → build/client/_app/immutable/nodes/6.BJZ62MCd.js} +1 -1
  85. package/frontend/build/client/_app/immutable/nodes/6.BJZ62MCd.js.br +0 -0
  86. package/frontend/build/client/_app/immutable/nodes/6.BJZ62MCd.js.gz +0 -0
  87. package/frontend/build/client/_app/immutable/nodes/{8.BEUdazrw.js → 8.B4fj2Xyu.js} +1 -1
  88. package/frontend/build/client/_app/immutable/nodes/8.B4fj2Xyu.js.br +0 -0
  89. package/frontend/build/client/_app/immutable/nodes/8.B4fj2Xyu.js.gz +0 -0
  90. package/frontend/build/client/_app/immutable/nodes/{9.CrLHYPEJ.js → 9.C57_2xq6.js} +1 -1
  91. package/frontend/build/client/_app/immutable/nodes/9.C57_2xq6.js.br +0 -0
  92. package/frontend/build/client/_app/immutable/nodes/9.C57_2xq6.js.gz +0 -0
  93. package/frontend/build/client/_app/version.json +1 -1
  94. package/frontend/build/client/_app/version.json.br +0 -0
  95. package/frontend/build/client/_app/version.json.gz +0 -0
  96. package/frontend/build/server/chunks/{0-Cn_2hzqk.js → 0-DG1Uv07a.js} +2 -2
  97. package/frontend/build/server/chunks/{0-Cn_2hzqk.js.map → 0-DG1Uv07a.js.map} +1 -1
  98. package/frontend/build/server/chunks/{1-BrRGJFSB.js → 1-iB0ogJU_.js} +2 -2
  99. package/frontend/build/server/chunks/{1-BrRGJFSB.js.map → 1-iB0ogJU_.js.map} +1 -1
  100. package/frontend/build/server/chunks/{11-BLPbHJqB.js → 11-Df9vGZ_v.js} +2 -2
  101. package/frontend/build/server/chunks/{11-BLPbHJqB.js.map → 11-Df9vGZ_v.js.map} +1 -1
  102. package/frontend/build/server/chunks/{12-S1ujUrHy.js → 12-DqvjCln4.js} +2 -2
  103. package/frontend/build/server/chunks/{12-S1ujUrHy.js.map → 12-DqvjCln4.js.map} +1 -1
  104. package/frontend/build/server/chunks/{3-BHkXVoNI.js → 3-BYKCZi4U.js} +2 -2
  105. package/frontend/build/server/chunks/{3-BHkXVoNI.js.map → 3-BYKCZi4U.js.map} +1 -1
  106. package/frontend/build/server/chunks/{5-F1ZsXMt2.js → 5-B6Jc1foX.js} +2 -2
  107. package/frontend/build/server/chunks/{5-F1ZsXMt2.js.map → 5-B6Jc1foX.js.map} +1 -1
  108. package/frontend/build/server/chunks/{6-FlZLnYJ6.js → 6-dXZKtN86.js} +2 -2
  109. package/frontend/build/server/chunks/{6-FlZLnYJ6.js.map → 6-dXZKtN86.js.map} +1 -1
  110. package/frontend/build/server/chunks/{8-DXReUGxi.js → 8-D-Bp7HzS.js} +2 -2
  111. package/frontend/build/server/chunks/{8-DXReUGxi.js.map → 8-D-Bp7HzS.js.map} +1 -1
  112. package/frontend/build/server/chunks/{9-Cy_mGmt4.js → 9-BynjuGUO.js} +2 -2
  113. package/frontend/build/server/chunks/{9-Cy_mGmt4.js.map → 9-BynjuGUO.js.map} +1 -1
  114. package/frontend/build/server/index.js +1 -1
  115. package/frontend/build/server/index.js.map +1 -1
  116. package/frontend/build/server/manifest.js +10 -10
  117. package/frontend/build/server/manifest.js.map +1 -1
  118. package/frontend/src/routes/settings/+page.svelte +4 -5
  119. package/package.json +1 -1
  120. package/frontend/build/client/_app/immutable/chunks/DMyTUV-Y.js.br +0 -0
  121. package/frontend/build/client/_app/immutable/chunks/DMyTUV-Y.js.gz +0 -0
  122. package/frontend/build/client/_app/immutable/chunks/nURnIknV.js.br +0 -0
  123. package/frontend/build/client/_app/immutable/chunks/nURnIknV.js.gz +0 -0
  124. package/frontend/build/client/_app/immutable/entry/app.BkBD0hgA.js.br +0 -0
  125. package/frontend/build/client/_app/immutable/entry/app.BkBD0hgA.js.gz +0 -0
  126. package/frontend/build/client/_app/immutable/nodes/0.ChjDHxSJ.js.br +0 -0
  127. package/frontend/build/client/_app/immutable/nodes/0.ChjDHxSJ.js.gz +0 -0
  128. package/frontend/build/client/_app/immutable/nodes/1.DBSWa36f.js.br +0 -0
  129. package/frontend/build/client/_app/immutable/nodes/1.DBSWa36f.js.gz +0 -0
  130. package/frontend/build/client/_app/immutable/nodes/11.O4cjXCg4.js.br +0 -0
  131. package/frontend/build/client/_app/immutable/nodes/11.O4cjXCg4.js.gz +0 -0
  132. package/frontend/build/client/_app/immutable/nodes/12.BWQdeRdX.js.br +0 -0
  133. package/frontend/build/client/_app/immutable/nodes/12.BWQdeRdX.js.gz +0 -0
  134. package/frontend/build/client/_app/immutable/nodes/3._S1vrLVF.js.br +0 -0
  135. package/frontend/build/client/_app/immutable/nodes/3._S1vrLVF.js.gz +0 -0
  136. package/frontend/build/client/_app/immutable/nodes/5.BiFBiWcW.js.br +0 -0
  137. package/frontend/build/client/_app/immutable/nodes/5.BiFBiWcW.js.gz +0 -0
  138. package/frontend/build/client/_app/immutable/nodes/6.BNS-W0R_.js.br +0 -0
  139. package/frontend/build/client/_app/immutable/nodes/6.BNS-W0R_.js.gz +0 -0
  140. package/frontend/build/client/_app/immutable/nodes/8.BEUdazrw.js.br +0 -0
  141. package/frontend/build/client/_app/immutable/nodes/8.BEUdazrw.js.gz +0 -0
  142. package/frontend/build/client/_app/immutable/nodes/9.CrLHYPEJ.js.br +0 -0
  143. 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 };
@@ -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
- router.get('/', jwtAuth, requireAdmin, async (req, res) => {
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('/', jwtAuth, requireAdmin, async (req, res) => {
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', jwtAuth, requireAdmin, async (req, res) => {
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', jwtAuth, requireAdmin, async (req, res) => {
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', jwtAuth, requireAdmin, async (req, res) => {
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', jwtAuth, requireAdmin, async (req, res) => {
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 require an authenticated admin session, which this
41
- // headless CLI has no browser/JWT to provide PLUM_NODE_KEY is the shared
42
- // secret that stands in for one (see backend/middleware/jwtAuth.js).
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
- const nodeKey = process.env.PLUM_NODE_KEY;
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
  }
@@ -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 `plum mcp` and other backend-dependent
9
- * commands work immediately without the user first having to run `plum
10
- * server start` or `plum node start` — the only commands that used to trigger
11
- * a backend `npm install` on their own.
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 mcp` fails to start, run `npm install` inside its backend/ folder.'
32
+ '[plum] If `plum server start` or `plum node start` fails, run `npm install` inside its backend/ folder.'
33
33
  );
34
34
  }
@@ -26,7 +26,6 @@ services:
26
26
  - '${BACKEND_PORT:-3001}:3001'
27
27
  environment:
28
28
  DATABASE_URL: 'postgresql://plum:plum@postgres:5432/plum'
29
- PLUM_NODE_KEY: '${PLUM_NODE_KEY:-}'
30
29
  extra_hosts:
31
30
  - 'host.docker.internal:host-gateway'
32
31
  volumes:
@@ -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: "1v80gvr"
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.BxeX1Uxs.js",app:"_app/immutable/entry/app.BkBD0hgA.js",imports:["_app/immutable/entry/start.BxeX1Uxs.js","_app/immutable/chunks/DMyTUV-Y.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/entry/app.BkBD0hgA.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},
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.BxeX1Uxs.js",app:"_app/immutable/entry/app.BkBD0hgA.js",imports:["_app/immutable/entry/start.BxeX1Uxs.js","_app/immutable/chunks/DMyTUV-Y.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/entry/app.BkBD0hgA.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},
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.ChjDHxSJ.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/DMyTUV-Y.js","_app/immutable/chunks/nURnIknV.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"];
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.DBSWa36f.js","_app/immutable/chunks/CMBQQ9RZ.js","_app/immutable/chunks/BAACMRH5.js","_app/immutable/chunks/CKDtmZUQ.js","_app/immutable/chunks/DMyTUV-Y.js"];
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.O4cjXCg4.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/nURnIknV.js","_app/immutable/chunks/DMyTUV-Y.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"];
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.BWQdeRdX.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/nURnIknV.js","_app/immutable/chunks/DMyTUV-Y.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"];
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._S1vrLVF.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/DMyTUV-Y.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/BmZ9upwL.js","_app/immutable/chunks/DCgHuuvs.js","_app/immutable/chunks/Dlnxgs49.js"];
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.BiFBiWcW.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/nURnIknV.js","_app/immutable/chunks/DMyTUV-Y.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"];
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.BNS-W0R_.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/DMyTUV-Y.js","_app/immutable/chunks/nURnIknV.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"];
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.BEUdazrw.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/DMyTUV-Y.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"];
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.CrLHYPEJ.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/DMyTUV-Y.js","_app/immutable/chunks/BmZ9upwL.js","_app/immutable/chunks/C1Vid6wz.js","_app/immutable/chunks/DCgHuuvs.js","_app/immutable/chunks/Dlnxgs49.js"];
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: "1v80gvr"
29
+ version_hash: "1m6qzpt"
30
30
  };
31
31
 
32
32
  export async function get_hooks() {