@powerhousedao/switchboard 6.2.3-dev.33 → 6.2.3-dev.34
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/.tsbuild/test/boot-mcp-enabled.test.d.ts +2 -0
- package/.tsbuild/test/boot-mcp-enabled.test.d.ts.map +1 -0
- package/.tsbuild/test/mcp-flag.test.d.ts +2 -0
- package/.tsbuild/test/mcp-flag.test.d.ts.map +1 -0
- package/.tsbuild/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +10 -0
- package/README.md +1 -0
- package/dist/index.mjs +1 -1
- package/dist/{server-DAgJkmvH.mjs → server-sQoWp21G.mjs} +21 -3
- package/dist/server-sQoWp21G.mjs.map +1 -0
- package/dist/server.d.mts +4 -0
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +3 -3
- package/package.json +15 -15
- package/test/boot-mcp-enabled.test.ts +121 -0
- package/test/mcp-flag.test.ts +56 -0
- package/dist/server-DAgJkmvH.mjs.map +0 -1
package/dist/server.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.mts","names":[],"sources":["../src/workflow-runtime.mts","../src/types.ts","../src/builder-defaults.mts","../src/unsupported-stored-documents.mts","../src/server.mts"],"mappings":";;;;;;;;;;;;;KA2DY;EACN;;EAEA;EACA;;;;;;;;;;YC5CM;;YAKA,wBAAwB;EAClC,eAAe;;YAGL;EACV;EACA;EACA;;YAGU;;EAEV;;;;;EAKA;;EAGA;;YAGU;;;;;;;;;;;;;;;;;;;;;EAqBV,UAAU;;;;;;;;EAQV;;;;;;;EAOA;EACA;EACA;;;;;;EAMA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;IAEM;IACA;;;;;;;EASN,WAAW;;EAEX
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../src/workflow-runtime.mts","../src/types.ts","../src/builder-defaults.mts","../src/unsupported-stored-documents.mts","../src/server.mts"],"mappings":";;;;;;;;;;;;;KA2DY;EACN;;EAEA;EACA;;;;;;;;;;YC5CM;;YAKA,wBAAwB;EAClC,eAAe;;YAGL;EACV;EACA;EACA;;YAGU;;EAEV;;;;;EAKA;;EAGA;;YAGU;;;;;;;;;;;;;;;;;;;;;EAqBV,UAAU;;;;;;;;EAQV;;;;;;;EAOA;EACA;EACA;;;;;;EAMA;EACA;EACA;EACA,QAAQ;EACR;EACA;EACA;IAEM;IACA;;;;;;;EASN,WAAW;;EAEX;;;EAGA;;;EAGA;IACE;;EAEF,kBAAkB;EAClB;EACA;;;;;;EAMA;EACA,SAAS;;;;;;;;;;EAUT;;;;;;;;;;EAUA;;;;;;;;;EASA;IACE;IACA;IACA;;;;;;;;;;;EAWF;IACE;IACA;;;;;;;;;EASF,6BAA6B;;YAGnB;EACV;EACA,SAAS;;EAET,mBAAmB;;EAEnB,+BAA+B;;;EAG/B,mBAAmB;;EAEnB;;EAEA;;EAEA,QAAQ;;;;;EAKR;;;;;;;;;;;;;EAaA,gBAAgB;;;;KCtMN;EAIV,iBAAiB;;EAEjB,mBAAmB;;EAEnB;;;;;;EAMA,gBAAgB;;EAEhB;;EAEA;IACE;IACA,eAAe,QAAQ;;;EAGzB,sBAAsB;EACtB,SAAS;;;;;;EAMT,SAAS;;;;;EAKT,cAAc,KAAK;;EAEnB,wBAAwB;;wBAsBV,gCACd,gBAAgB,gBAChB,eAAe,sBACf,UAAS;;;;wBC3EK,kCACd,OAAO,wCACP,KAAK,OAAO,aACX;;qBAaU,oCAAoC;WACtC;IAAqB;IAAkB;;WACvC;EAET,YAAY,OAAO;SAcZ,QAAQ,iBAAiB,SAAS;;;;;;;;;wBC4G3B,gBAAgB,eAAe;;wBAoI/B,8BACd,SAAS,KAAK,uDACd,oBACA,QAAQ;EACL;EAAmB,YAAY;;;;;;;;;;;;;;;;;;;qBAk7BvB,mBAAgB,UAClB,uBACR,QAAQ"}
|
package/dist/server.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as resolveUnsupportedStoredDocuments, i as StoredDocumentsRefusedError, n as isPortAvailable, o as applySwitchboardReactorDefaults, r as startSwitchboard, t as deriveAttachmentServiceConfig } from "./server-
|
|
2
|
+
import { a as resolveUnsupportedStoredDocuments, i as StoredDocumentsRefusedError, n as isPortAvailable, o as applySwitchboardReactorDefaults, r as startSwitchboard, t as deriveAttachmentServiceConfig } from "./server-sQoWp21G.mjs";
|
|
3
3
|
export { StoredDocumentsRefusedError, applySwitchboardReactorDefaults, deriveAttachmentServiceConfig, isPortAvailable, resolveUnsupportedStoredDocuments, startSwitchboard };
|
|
4
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
5
|
-
//# debugId=
|
|
4
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1396e5c4-5148-5e73-aaff-79e70b5910cc")}catch(e){}}();
|
|
5
|
+
//# debugId=1396e5c4-5148-5e73-aaff-79e70b5910cc
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/switchboard",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "6.2.3-dev.
|
|
4
|
+
"version": "6.2.3-dev.34",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@openfeature/core": "1.9.1",
|
|
42
42
|
"@openfeature/env-var-provider": "0.3.1",
|
|
43
43
|
"@openfeature/server-sdk": "1.19.0",
|
|
44
|
-
"@powerhousedao/config": "6.2.3-dev.
|
|
44
|
+
"@powerhousedao/config": "6.2.3-dev.34",
|
|
45
45
|
"@opentelemetry/api": "^1.9.0",
|
|
46
46
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.218.0",
|
|
47
47
|
"@opentelemetry/exporter-trace-otlp-http": "^0.218.0",
|
|
@@ -55,22 +55,22 @@
|
|
|
55
55
|
"@opentelemetry/sdk-node": "^0.218.0",
|
|
56
56
|
"@opentelemetry/sdk-trace-base": "^2.7.1",
|
|
57
57
|
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
58
|
-
"@powerhousedao/opentelemetry-instrumentation-reactor": "6.2.3-dev.
|
|
59
|
-
"@powerhousedao/reactor": "6.2.3-dev.
|
|
60
|
-
"@powerhousedao/shared": "6.2.3-dev.
|
|
61
|
-
"@powerhousedao/vetra": "6.2.3-dev.
|
|
62
|
-
"@powerhousedao/reactor-api": "6.2.3-dev.
|
|
63
|
-
"@powerhousedao/reactor-attachments": "6.2.3-dev.
|
|
64
|
-
"@powerhousedao/reactor-drive": "6.2.3-dev.
|
|
65
|
-
"@powerhousedao/reactor-group": "6.2.3-dev.
|
|
66
|
-
"@powerhousedao/reactor-workflow": "6.2.3-dev.
|
|
67
|
-
"@powerhousedao/workflow": "6.2.3-dev.
|
|
68
|
-
"@powerhousedao/pglite-fs": "6.2.3-dev.
|
|
58
|
+
"@powerhousedao/opentelemetry-instrumentation-reactor": "6.2.3-dev.34",
|
|
59
|
+
"@powerhousedao/reactor": "6.2.3-dev.34",
|
|
60
|
+
"@powerhousedao/shared": "6.2.3-dev.34",
|
|
61
|
+
"@powerhousedao/vetra": "6.2.3-dev.34",
|
|
62
|
+
"@powerhousedao/reactor-api": "6.2.3-dev.34",
|
|
63
|
+
"@powerhousedao/reactor-attachments": "6.2.3-dev.34",
|
|
64
|
+
"@powerhousedao/reactor-drive": "6.2.3-dev.34",
|
|
65
|
+
"@powerhousedao/reactor-group": "6.2.3-dev.34",
|
|
66
|
+
"@powerhousedao/reactor-workflow": "6.2.3-dev.34",
|
|
67
|
+
"@powerhousedao/workflow": "6.2.3-dev.34",
|
|
68
|
+
"@powerhousedao/pglite-fs": "6.2.3-dev.34",
|
|
69
69
|
"@pyroscope/nodejs": "^0.4.5",
|
|
70
|
-
"@renown/sdk": "6.2.3-dev.
|
|
70
|
+
"@renown/sdk": "6.2.3-dev.34",
|
|
71
71
|
"@sentry/node": "^10.53.1",
|
|
72
72
|
"@sentry/opentelemetry": "^10.53.1",
|
|
73
|
-
"document-model": "6.2.3-dev.
|
|
73
|
+
"document-model": "6.2.3-dev.34",
|
|
74
74
|
"dotenv": "^16.4.7",
|
|
75
75
|
"express": "^4.22.1",
|
|
76
76
|
"graphql": "^16",
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { createServer } from "node:net";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import type { ILogger } from "document-model";
|
|
6
|
+
import { describe, expect, it, vi } from "vitest";
|
|
7
|
+
import { startSwitchboard } from "../src/server.mjs";
|
|
8
|
+
|
|
9
|
+
// `port: 0` would bind a random port the switchboard does not report back, so
|
|
10
|
+
// the test picks a free one and pins it.
|
|
11
|
+
async function freePort(): Promise<number> {
|
|
12
|
+
const server = createServer();
|
|
13
|
+
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
14
|
+
const address = server.address();
|
|
15
|
+
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
16
|
+
if (!address || typeof address === "string") throw new Error("no port");
|
|
17
|
+
return address.port;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function stubLogger(): ILogger {
|
|
21
|
+
const logger = {
|
|
22
|
+
verbose: vi.fn(),
|
|
23
|
+
debug: vi.fn(),
|
|
24
|
+
info: vi.fn(),
|
|
25
|
+
warn: vi.fn(),
|
|
26
|
+
error: vi.fn(),
|
|
27
|
+
child: vi.fn(),
|
|
28
|
+
};
|
|
29
|
+
logger.child.mockReturnValue(logger);
|
|
30
|
+
return logger as unknown as ILogger;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function withSwitchboard(
|
|
34
|
+
env: Record<string, string>,
|
|
35
|
+
mcp: boolean | undefined,
|
|
36
|
+
run: (
|
|
37
|
+
switchboard: Awaited<ReturnType<typeof startSwitchboard>>,
|
|
38
|
+
) => Promise<void>,
|
|
39
|
+
): Promise<void> {
|
|
40
|
+
const tempRoot = await mkdtemp(join(tmpdir(), "switchboard-mcp-enabled-"));
|
|
41
|
+
const names = ["PH_REACTOR_DATABASE_URL", "MCP_ENABLED", ...Object.keys(env)];
|
|
42
|
+
const previous = names.map((name) => [name, process.env[name]] as const);
|
|
43
|
+
process.env.PH_REACTOR_DATABASE_URL = join(tempRoot, "reactor-storage");
|
|
44
|
+
delete process.env.MCP_ENABLED;
|
|
45
|
+
Object.assign(process.env, env);
|
|
46
|
+
let switchboard: Awaited<ReturnType<typeof startSwitchboard>> | undefined;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
switchboard = await startSwitchboard({
|
|
50
|
+
workflows: { enabled: false },
|
|
51
|
+
dbPath: join(tempRoot, "read-model"),
|
|
52
|
+
port: await freePort(),
|
|
53
|
+
strictPort: true,
|
|
54
|
+
mcp,
|
|
55
|
+
disableLocalPackages: true,
|
|
56
|
+
identity: { keypairPath: join(tempRoot, "identity.json") },
|
|
57
|
+
drive: {
|
|
58
|
+
slug: "default-drive",
|
|
59
|
+
global: { name: "Default Drive" },
|
|
60
|
+
},
|
|
61
|
+
logger: stubLogger(),
|
|
62
|
+
});
|
|
63
|
+
await run(switchboard);
|
|
64
|
+
await switchboard.shutdown();
|
|
65
|
+
switchboard = undefined;
|
|
66
|
+
} finally {
|
|
67
|
+
await switchboard?.shutdown();
|
|
68
|
+
for (const [name, value] of previous) {
|
|
69
|
+
if (value === undefined) {
|
|
70
|
+
delete process.env[name];
|
|
71
|
+
} else {
|
|
72
|
+
process.env[name] = value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function postMcp(port: number): Promise<number> {
|
|
80
|
+
const response = await fetch(`http://127.0.0.1:${port}/mcp`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: {
|
|
83
|
+
"content-type": "application/json",
|
|
84
|
+
accept: "application/json, text/event-stream",
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "ping" }),
|
|
87
|
+
});
|
|
88
|
+
await response.body?.cancel();
|
|
89
|
+
return response.status;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
describe("booting Switchboard", () => {
|
|
93
|
+
it("mounts /mcp by default", async () => {
|
|
94
|
+
await withSwitchboard({}, undefined, async ({ mcpEnabled, port }) => {
|
|
95
|
+
expect(mcpEnabled).toBe(true);
|
|
96
|
+
expect(await postMcp(port)).not.toBe(404);
|
|
97
|
+
});
|
|
98
|
+
}, 60_000);
|
|
99
|
+
|
|
100
|
+
it('leaves /mcp unmounted under MCP_ENABLED="false"', async () => {
|
|
101
|
+
await withSwitchboard(
|
|
102
|
+
{ MCP_ENABLED: "false" },
|
|
103
|
+
undefined,
|
|
104
|
+
async ({ mcpEnabled, port }) => {
|
|
105
|
+
expect(mcpEnabled).toBe(false);
|
|
106
|
+
expect(await postMcp(port)).toBe(404);
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
}, 60_000);
|
|
110
|
+
|
|
111
|
+
it("keeps a host's mcp: false over MCP_ENABLED=true", async () => {
|
|
112
|
+
await withSwitchboard(
|
|
113
|
+
{ MCP_ENABLED: "true" },
|
|
114
|
+
false,
|
|
115
|
+
async ({ mcpEnabled, port }) => {
|
|
116
|
+
expect(mcpEnabled).toBe(false);
|
|
117
|
+
expect(await postMcp(port)).toBe(404);
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
}, 60_000);
|
|
121
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { afterEach, beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
import { initFeatureFlags } from "../src/feature-flags.js";
|
|
3
|
+
import { MCP_ENABLED, resolveMcpEnabled } from "../src/mcp-flag.mjs";
|
|
4
|
+
import type { BooleanFlagSource } from "../src/workflow-runtime.mjs";
|
|
5
|
+
|
|
6
|
+
describe("resolveMcpEnabled", () => {
|
|
7
|
+
let featureFlags: BooleanFlagSource;
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
featureFlags = await initFeatureFlags();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
delete process.env[MCP_ENABLED];
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("defaults to on", async () => {
|
|
18
|
+
await expect(resolveMcpEnabled({ featureFlags })).resolves.toBe(true);
|
|
19
|
+
await expect(
|
|
20
|
+
resolveMcpEnabled({ featureFlags, option: true }),
|
|
21
|
+
).resolves.toBe(true);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('is turned off by MCP_ENABLED="false"', async () => {
|
|
25
|
+
process.env[MCP_ENABLED] = "false";
|
|
26
|
+
|
|
27
|
+
await expect(resolveMcpEnabled({ featureFlags })).resolves.toBe(false);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// `ph vetra` passes `mcp: true` explicitly; the env var still wins.
|
|
31
|
+
it("turns off a host that asked for MCP", async () => {
|
|
32
|
+
process.env[MCP_ENABLED] = "false";
|
|
33
|
+
|
|
34
|
+
await expect(
|
|
35
|
+
resolveMcpEnabled({ featureFlags, option: true }),
|
|
36
|
+
).resolves.toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("never turns on a host that switched MCP off", async () => {
|
|
40
|
+
process.env[MCP_ENABLED] = "true";
|
|
41
|
+
|
|
42
|
+
await expect(
|
|
43
|
+
resolveMcpEnabled({ featureFlags, option: false }),
|
|
44
|
+
).resolves.toBe(false);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("leaves MCP on for any value but the string false", async () => {
|
|
48
|
+
for (const raw of ["true", "0", "no", ""]) {
|
|
49
|
+
process.env[MCP_ENABLED] = raw;
|
|
50
|
+
await expect(
|
|
51
|
+
resolveMcpEnabled({ featureFlags }),
|
|
52
|
+
`MCP_ENABLED=${JSON.stringify(raw)}`,
|
|
53
|
+
).resolves.toBe(true);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|