@portel/photon 1.23.1 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +262 -18
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/beam.bundle.js +58287 -56177
- package/dist/beam.bundle.js.map +4 -4
- package/dist/capability-negotiator.d.ts +9 -0
- package/dist/capability-negotiator.d.ts.map +1 -1
- package/dist/capability-negotiator.js +14 -0
- package/dist/capability-negotiator.js.map +1 -1
- package/dist/cli/commands/claim.d.ts +17 -0
- package/dist/cli/commands/claim.d.ts.map +1 -0
- package/dist/cli/commands/claim.js +124 -0
- package/dist/cli/commands/claim.js.map +1 -0
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +2 -0
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/claims.d.ts +108 -0
- package/dist/daemon/claims.d.ts.map +1 -0
- package/dist/daemon/claims.js +245 -0
- package/dist/daemon/claims.js.map +1 -0
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +15 -29
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/cron.d.ts +36 -0
- package/dist/daemon/cron.d.ts.map +1 -0
- package/dist/daemon/cron.js +216 -0
- package/dist/daemon/cron.js.map +1 -0
- package/dist/daemon/schedule-loader.d.ts +76 -0
- package/dist/daemon/schedule-loader.d.ts.map +1 -0
- package/dist/daemon/schedule-loader.js +124 -0
- package/dist/daemon/schedule-loader.js.map +1 -0
- package/dist/daemon/server.js +76 -226
- package/dist/daemon/server.js.map +1 -1
- package/dist/deploy/cloudflare.d.ts.map +1 -1
- package/dist/deploy/cloudflare.js +154 -4
- package/dist/deploy/cloudflare.js.map +1 -1
- package/dist/loader.d.ts +22 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +246 -30
- package/dist/loader.js.map +1 -1
- package/dist/photon-cli-runner.d.ts.map +1 -1
- package/dist/photon-cli-runner.js +32 -2
- package/dist/photon-cli-runner.js.map +1 -1
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +50 -1
- package/dist/server.js.map +1 -1
- package/dist/shared/memory-sqlite.d.ts +37 -0
- package/dist/shared/memory-sqlite.d.ts.map +1 -0
- package/dist/shared/memory-sqlite.js +143 -0
- package/dist/shared/memory-sqlite.js.map +1 -0
- package/dist/telemetry/context.d.ts +9 -0
- package/dist/telemetry/context.d.ts.map +1 -1
- package/dist/telemetry/context.js.map +1 -1
- package/package.json +4 -4
- package/templates/cloudflare/worker.ts.template +53 -74
- package/templates/cloudflare/wrangler.toml.template +6 -1
|
@@ -16,6 +16,15 @@ export interface RequestContext {
|
|
|
16
16
|
parentTraceparent?: string;
|
|
17
17
|
/** Authenticated caller when available. */
|
|
18
18
|
caller?: CallerInfo;
|
|
19
|
+
/**
|
|
20
|
+
* Originating CLI invocation directory, propagated end-to-end across
|
|
21
|
+
* worker thread and cross-photon-call boundaries. Lets photons resolve
|
|
22
|
+
* defaults relative to where the user ran the command, not the daemon's
|
|
23
|
+
* cwd. `process.cwd()` inside a worker is the daemon process's cwd, which
|
|
24
|
+
* is rarely what the photon author wants. Photons read this back via
|
|
25
|
+
* `this.callerCwd`.
|
|
26
|
+
*/
|
|
27
|
+
cwd?: string;
|
|
19
28
|
/** Wall-clock start of the tool call. */
|
|
20
29
|
startedAt: number;
|
|
21
30
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/telemetry/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5E;AAED,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D"}
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/telemetry/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAE5E;AAED,wBAAgB,iBAAiB,IAAI,cAAc,GAAG,SAAS,CAE9D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/telemetry/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/telemetry/context.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAyBrD,MAAM,OAAO,GAAG,IAAI,iBAAiB,EAAkB,CAAC;AAExD,MAAM,UAAU,qBAAqB,CAAI,GAAmB,EAAE,EAAW;IACvE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,OAAO,CAAC,QAAQ,EAAE,CAAC;AAC5B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portel/photon",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.25.0",
|
|
4
4
|
"description": "You focus on the business logic. We'll enable the rest. Build MCP servers and CLI tools in a single TypeScript file.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"@modelcontextprotocol/ext-apps": "^1.0.1",
|
|
114
114
|
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
115
115
|
"@portel/cli": "^1.1.0",
|
|
116
|
-
"@portel/photon-core": "^2.
|
|
116
|
+
"@portel/photon-core": "^2.25.0",
|
|
117
117
|
"boxen": "^8.0.1",
|
|
118
118
|
"chalk": "^5.4.1",
|
|
119
119
|
"chart.js": "^4.5.1",
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
"@codemirror/theme-one-dark": "^6.1.3",
|
|
142
142
|
"@codemirror/view": "^6.39.12",
|
|
143
143
|
"@playwright/test": "^1.58.0",
|
|
144
|
-
"@release-it/conventional-changelog": "^
|
|
144
|
+
"@release-it/conventional-changelog": "^11.0.0",
|
|
145
145
|
"@types/inquirer": "^9.0.9",
|
|
146
146
|
"@types/node": "^22.10.2",
|
|
147
147
|
"@types/qrcode": "^1.5.6",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"openapi-types": "^12.1.3",
|
|
158
158
|
"playwright": "^1.57.0",
|
|
159
159
|
"prettier": "^3.8.0",
|
|
160
|
-
"release-it": "^
|
|
160
|
+
"release-it": "^20.0.0",
|
|
161
161
|
"tsx": "^4.20.6",
|
|
162
162
|
"typescript": "^5.7.3"
|
|
163
163
|
},
|
|
@@ -11,13 +11,6 @@ interface Env {
|
|
|
11
11
|
[key: string]: string | undefined;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
type SSESession = {
|
|
15
|
-
writer: WritableStreamDefaultWriter<Uint8Array>;
|
|
16
|
-
encoder: TextEncoder;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const sessions = new Map<string, SSESession>();
|
|
20
|
-
|
|
21
14
|
// Tool definitions extracted at build time
|
|
22
15
|
// @ts-ignore - Will be replaced during build
|
|
23
16
|
const TOOL_DEFINITIONS: any[] = __TOOL_DEFINITIONS__;
|
|
@@ -67,11 +60,19 @@ async function handleMCPRequest(request: any, env: Env): Promise<any> {
|
|
|
67
60
|
throw new Error(`Unknown tool: ${name}`);
|
|
68
61
|
}
|
|
69
62
|
const result = await method.call(photon, args || {});
|
|
63
|
+
// String returns (e.g. markdown from `read`) flow through as-is so
|
|
64
|
+
// clients receive renderable text. Other shapes are JSON-stringified
|
|
65
|
+
// for legibility. Structured shapes are also surfaced via
|
|
66
|
+
// `structuredContent` per MCP — clients that prefer JSON pick it up.
|
|
67
|
+
const isString = typeof result === 'string';
|
|
68
|
+
const text = isString ? result : JSON.stringify(result, null, 2);
|
|
69
|
+
const isObject = result !== null && typeof result === 'object';
|
|
70
70
|
return {
|
|
71
71
|
jsonrpc: '2.0',
|
|
72
72
|
id,
|
|
73
73
|
result: {
|
|
74
|
-
content: [{ type: 'text', text
|
|
74
|
+
content: [{ type: 'text', text }],
|
|
75
|
+
...(isObject && { structuredContent: result }),
|
|
75
76
|
},
|
|
76
77
|
};
|
|
77
78
|
} catch (error: any) {
|
|
@@ -100,66 +101,34 @@ async function handleMCPRequest(request: any, env: Env): Promise<any> {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
// SSE stream handler
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
clearInterval(interval);
|
|
121
|
-
}
|
|
122
|
-
}, 30000);
|
|
123
|
-
|
|
124
|
-
// Cleanup on close
|
|
125
|
-
request.signal.addEventListener('abort', () => {
|
|
126
|
-
clearInterval(interval);
|
|
127
|
-
sessions.delete(sessionId);
|
|
128
|
-
writer.close().catch(() => {});
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
return new Response(readable, {
|
|
132
|
-
headers: {
|
|
133
|
-
'Content-Type': 'text/event-stream',
|
|
134
|
-
'Cache-Control': 'no-cache',
|
|
135
|
-
'Connection': 'keep-alive',
|
|
136
|
-
'Access-Control-Allow-Origin': '*',
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Message handler for SSE
|
|
142
|
-
async function handleMessage(request: Request, env: Env): Promise<Response> {
|
|
143
|
-
const url = new URL(request.url);
|
|
144
|
-
const sessionId = url.searchParams.get('sessionId');
|
|
145
|
-
|
|
146
|
-
if (!sessionId || !sessions.has(sessionId)) {
|
|
147
|
-
return new Response('Invalid session', { status: 400 });
|
|
104
|
+
/**
|
|
105
|
+
* Stateless MCP Streamable HTTP handler.
|
|
106
|
+
*
|
|
107
|
+
* Each POST /mcp is a complete JSON-RPC exchange: body in, result out, no
|
|
108
|
+
* session state carried across requests. This matches the MCP Streamable
|
|
109
|
+
* HTTP spec's single-endpoint pattern and stays within the Cloudflare
|
|
110
|
+
* Workers free tier (no Durable Objects needed for session storage).
|
|
111
|
+
*/
|
|
112
|
+
async function handleStreamableMCP(request: Request, env: Env): Promise<Response> {
|
|
113
|
+
let body: unknown;
|
|
114
|
+
try {
|
|
115
|
+
body = await request.json();
|
|
116
|
+
} catch (error: any) {
|
|
117
|
+
return Response.json(
|
|
118
|
+
{ jsonrpc: '2.0', id: null, error: { code: -32700, message: `Parse error: ${error?.message ?? String(error)}` } },
|
|
119
|
+
{ status: 400, headers: CORS_HEADERS }
|
|
120
|
+
);
|
|
148
121
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const body = await request.json();
|
|
152
|
-
const response = await handleMCPRequest(body, env);
|
|
153
|
-
|
|
154
|
-
// Send response via SSE
|
|
155
|
-
const data = `event: message\ndata: ${JSON.stringify(response)}\n\n`;
|
|
156
|
-
await session.writer.write(session.encoder.encode(data));
|
|
157
|
-
|
|
158
|
-
return new Response('OK', {
|
|
159
|
-
headers: { 'Access-Control-Allow-Origin': '*' },
|
|
160
|
-
});
|
|
122
|
+
const result = await handleMCPRequest(body, env);
|
|
123
|
+
return Response.json(result, { headers: CORS_HEADERS });
|
|
161
124
|
}
|
|
162
125
|
|
|
126
|
+
const CORS_HEADERS = {
|
|
127
|
+
'Access-Control-Allow-Origin': '*',
|
|
128
|
+
'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
|
|
129
|
+
'Access-Control-Allow-Headers': 'Content-Type, Mcp-Session-Id',
|
|
130
|
+
};
|
|
131
|
+
|
|
163
132
|
// Playground HTML
|
|
164
133
|
function getPlaygroundHTML(): string {
|
|
165
134
|
return `<!DOCTYPE html>
|
|
@@ -324,25 +293,35 @@ export default {
|
|
|
324
293
|
if (url.pathname === '/' && request.method === 'GET') {
|
|
325
294
|
return Response.json({
|
|
326
295
|
name: PHOTON_NAME,
|
|
327
|
-
transport: '
|
|
296
|
+
transport: 'streamable-http',
|
|
328
297
|
runtime: 'cloudflare-workers',
|
|
329
298
|
endpoints: {
|
|
330
|
-
|
|
331
|
-
messages: '/mcp/messages',
|
|
299
|
+
mcp: '/mcp',
|
|
332
300
|
...(DEV_MODE ? { playground: '/playground' } : {}),
|
|
333
301
|
},
|
|
334
302
|
tools: TOOL_DEFINITIONS.length,
|
|
335
303
|
});
|
|
336
304
|
}
|
|
337
305
|
|
|
338
|
-
//
|
|
306
|
+
// MCP Streamable HTTP: one endpoint, stateless per request.
|
|
307
|
+
if (url.pathname === '/mcp' && request.method === 'POST') {
|
|
308
|
+
return handleStreamableMCP(request, env);
|
|
309
|
+
}
|
|
339
310
|
if (url.pathname === '/mcp' && request.method === 'GET') {
|
|
340
|
-
|
|
311
|
+
// Spec-compliant no-op: clients that open an SSE stream for server
|
|
312
|
+
// notifications get a valid empty stream. We do not push anything
|
|
313
|
+
// server-initiated from a stateless Worker.
|
|
314
|
+
return new Response(': streamable-http\n\n', {
|
|
315
|
+
headers: {
|
|
316
|
+
'Content-Type': 'text/event-stream',
|
|
317
|
+
'Cache-Control': 'no-cache',
|
|
318
|
+
...CORS_HEADERS,
|
|
319
|
+
},
|
|
320
|
+
});
|
|
341
321
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
return handleMessage(request, env);
|
|
322
|
+
if (url.pathname === '/mcp' && request.method === 'DELETE') {
|
|
323
|
+
// Session termination: stateless, nothing to clean up.
|
|
324
|
+
return new Response(null, { status: 204, headers: CORS_HEADERS });
|
|
346
325
|
}
|
|
347
326
|
|
|
348
327
|
// Dev-only endpoints
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
name = "__PHOTON_NAME__"
|
|
2
2
|
main = "src/worker.ts"
|
|
3
|
-
|
|
3
|
+
# 2024-09-23+ enables the Node.js-style module resolution path so packages
|
|
4
|
+
# that ship distinct browser/node entries (e.g. linkedom inside
|
|
5
|
+
# @extractus/article-extractor) resolve to the Node build instead of a
|
|
6
|
+
# browser build that references `window`.
|
|
7
|
+
compatibility_date = "2024-09-23"
|
|
8
|
+
compatibility_flags = ["nodejs_compat"]
|
|
4
9
|
|
|
5
10
|
# Uncomment to add environment variables
|
|
6
11
|
# [vars]
|