@portel/photon 1.33.1 → 1.33.3
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 +74 -19
- package/dist/auto-ui/frontend/pure-view.html +5 -2
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +62 -11
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/beam.bundle.js +19 -17
- package/dist/beam.bundle.js.map +2 -2
- package/dist/capability-negotiator.d.ts +11 -0
- package/dist/capability-negotiator.d.ts.map +1 -1
- package/dist/capability-negotiator.js +20 -0
- package/dist/capability-negotiator.js.map +1 -1
- package/dist/cli/commands/maker.d.ts.map +1 -1
- package/dist/cli/commands/maker.js +159 -27
- package/dist/cli/commands/maker.js.map +1 -1
- package/dist/daemon/worker-dep-proxy.d.ts +17 -0
- package/dist/daemon/worker-dep-proxy.d.ts.map +1 -0
- package/dist/daemon/worker-dep-proxy.js +92 -0
- package/dist/daemon/worker-dep-proxy.js.map +1 -0
- package/dist/daemon/worker-host.js +8 -28
- package/dist/daemon/worker-host.js.map +1 -1
- package/dist/deploy/cloudflare.d.ts.map +1 -1
- package/dist/deploy/cloudflare.js +15 -0
- package/dist/deploy/cloudflare.js.map +1 -1
- package/dist/photon-doc-extractor.d.ts +10 -0
- package/dist/photon-doc-extractor.d.ts.map +1 -1
- package/dist/photon-doc-extractor.js +26 -2
- package/dist/photon-doc-extractor.js.map +1 -1
- package/dist/resource-server.d.ts +15 -0
- package/dist/resource-server.d.ts.map +1 -1
- package/dist/resource-server.js +86 -5
- package/dist/resource-server.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +20 -10
- package/dist/server.js.map +1 -1
- package/dist/template-manager.js +14 -14
- package/dist/version.d.ts +7 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +8 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/templates/cloudflare/worker.ts.template +26 -8
package/README.md
CHANGED
|
@@ -12,16 +12,54 @@
|
|
|
12
12
|
|
|
13
13
|
### Define intent once. Deliver everywhere.
|
|
14
14
|
|
|
15
|
-
Photon turns
|
|
15
|
+
Define the capability once. Photon turns it into the interfaces people now
|
|
16
|
+
expect around AI tools:
|
|
16
17
|
|
|
17
|
-
- **MCP server** for
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
18
|
+
- **MCP server** for Claude, ChatGPT, Cursor, and agents
|
|
19
|
+
- **Embedded app UI** for chat clients that support MCP app resources
|
|
20
|
+
- **CLI tool** for scripts, demos, and automation
|
|
21
|
+
- **Beam web interface** for humans
|
|
20
22
|
|
|
21
23
|
Photon is free and open source software released under the [MIT license](./LICENSE).
|
|
22
24
|
|
|
23
25
|
*Interfaces are optional. Intent is mandatory.*
|
|
24
26
|
|
|
27
|
+
```bash
|
|
28
|
+
gh repo star portel-dev/photon
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## From one method to every surface
|
|
34
|
+
|
|
35
|
+
The weather example is intentionally small: one TypeScript method, a few
|
|
36
|
+
docblock tags, and one `@ui` HTML asset. Photon turns that into a CLI command,
|
|
37
|
+
Beam UI, MCP tool, and embedded app surface for MCP app-capable chat clients.
|
|
38
|
+
Claude Desktop can run it from a local stdio MCP command; ChatGPT developer
|
|
39
|
+
mode can connect to the same Photon over a public HTTPS `/mcp` endpoint.
|
|
40
|
+
|
|
41
|
+
<div align="center">
|
|
42
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/photon-surface-map.svg" alt="Infographic showing one Photon method becoming CLI, Beam, MCP, Claude Desktop, ChatGPT, and other agent surfaces" width="100%">
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
Real clients, same Photon:
|
|
46
|
+
|
|
47
|
+
<div align="center">
|
|
48
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/chatgpt-photon-weather-local-dot.png" alt="Real ChatGPT developer-mode session rendering the Photon weather app from a public HTTPS MCP endpoint" width="100%">
|
|
49
|
+
<p><sub>ChatGPT developer mode rendering the Photon weather UI from a public HTTPS <code>/mcp</code> endpoint.</sub></p>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
<div align="center">
|
|
53
|
+
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/showcase/weather/claude-weather-real.png" alt="Real Claude Desktop session rendering the Photon weather app through local MCP" width="100%">
|
|
54
|
+
<p><sub>Claude Desktop rendering the same Photon through local MCP.</sub></p>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
[Follow the step-by-step tutorial](./docs/tutorials/from-method-to-chat-app.md)
|
|
58
|
+
or open the runnable example in
|
|
59
|
+
[`examples/weather-showcase`](./examples/weather-showcase).
|
|
60
|
+
The tutorial also includes Beam, CLI, and a concept animation that explains the
|
|
61
|
+
transformation without pretending to be proof.
|
|
62
|
+
|
|
25
63
|
---
|
|
26
64
|
|
|
27
65
|
## One definition. Multiple interfaces.
|
|
@@ -32,6 +70,26 @@ Photon is free and open source software released under the [MIT license](./LICEN
|
|
|
32
70
|
|
|
33
71
|
---
|
|
34
72
|
|
|
73
|
+
## The Promise
|
|
74
|
+
|
|
75
|
+
Photon is the modern dev stack for the agentic age: each photon is a small,
|
|
76
|
+
auditable brick that can be used by humans, agents, schedulers, webhooks, and
|
|
77
|
+
apps without rewriting the same capability for every interface.
|
|
78
|
+
|
|
79
|
+
| Define once | Photon derives |
|
|
80
|
+
|---|---|
|
|
81
|
+
| TypeScript method | MCP tool, CLI command, Beam action |
|
|
82
|
+
| JSDoc intent | AI descriptions, human docs, form labels |
|
|
83
|
+
| Types and tags | Validation, output formats, safety hints |
|
|
84
|
+
| `@ui`, `@get`, `@post` | Embedded apps and web routes |
|
|
85
|
+
| `@scheduled`, `@webhook`, `@locked` | Automation, integration, coordination |
|
|
86
|
+
| `@auth`, scopes, JWT | Agent-safe deployed access |
|
|
87
|
+
|
|
88
|
+
That is the core idea: **tiny trusted capabilities compose into larger
|
|
89
|
+
systems**.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
35
93
|
### Example
|
|
36
94
|
|
|
37
95
|
```typescript
|
|
@@ -98,16 +156,6 @@ pnpm dlx @portel/photon mcp install my-tool
|
|
|
98
156
|
>
|
|
99
157
|
> **Where do photon files live?** `./` (a project directory you cd into) or `~/.photon/` (global, auto-discovered). User settings persist under `~/.photon/state/<photon>/`. See [Where things live](docs/getting-started.md#where-things-live).
|
|
100
158
|
|
|
101
|
-
<div align="center">
|
|
102
|
-
|
|
103
|
-
<a href="https://www.youtube.com/watch?v=FI0M8s6ZKv4">
|
|
104
|
-
<img src="https://img.youtube.com/vi/FI0M8s6ZKv4/maxresdefault.jpg" alt="Watch: Why Photon? (2 min)" width="100%">
|
|
105
|
-
</a>
|
|
106
|
-
|
|
107
|
-
</div>
|
|
108
|
-
|
|
109
|
-
---
|
|
110
|
-
|
|
111
159
|
### How It Works
|
|
112
160
|
|
|
113
161
|
You write a TypeScript class. Methods are your capabilities. Types describe what's valid. Comments explain the intent. Photon reads all of it and generates three interfaces from one file. Same logic. Same validation. Same data.
|
|
@@ -423,15 +471,17 @@ Full reference: [`docs/reference/CLAIM-CODES.md`](docs/reference/CLAIM-CODES.md)
|
|
|
423
471
|
|
|
424
472
|
## Marketplace
|
|
425
473
|
|
|
426
|
-
|
|
474
|
+
A curated set of photons is ready to install. The public gallery is now kept
|
|
475
|
+
small on purpose: polished apps and tools in one place, teaching examples in
|
|
476
|
+
another.
|
|
427
477
|
|
|
428
478
|
<div align="center">
|
|
429
479
|
<img src="https://raw.githubusercontent.com/portel-dev/photon/main/assets/beam-marketplace.png" alt="Marketplace" width="100%">
|
|
430
480
|
</div>
|
|
431
481
|
|
|
432
482
|
```bash
|
|
433
|
-
photon search
|
|
434
|
-
photon add
|
|
483
|
+
photon search boards
|
|
484
|
+
photon add boards
|
|
435
485
|
```
|
|
436
486
|
|
|
437
487
|
You can also install directly from any GitHub repository using qualified refs:
|
|
@@ -440,7 +490,11 @@ You can also install directly from any GitHub repository using qualified refs:
|
|
|
440
490
|
photon add owner/repo/photon-name
|
|
441
491
|
```
|
|
442
492
|
|
|
443
|
-
Browse the
|
|
493
|
+
Browse the [Photon Apps marketplace](https://github.com/portel-dev/photons)
|
|
494
|
+
for ready-to-use photons, or the
|
|
495
|
+
[Photon Examples marketplace](https://github.com/portel-dev/photon-examples)
|
|
496
|
+
for focused learning examples. You can also host a private marketplace for your
|
|
497
|
+
team: internal tools that stay off the public internet.
|
|
444
498
|
|
|
445
499
|
---
|
|
446
500
|
|
|
@@ -575,6 +629,7 @@ Uses Bun's compiler under the hood. The binary bundles the photon, its `@depende
|
|
|
575
629
|
| Guide | |
|
|
576
630
|
|---|---|
|
|
577
631
|
| [Getting Started](./docs/getting-started.md) | Install, build, and run your first photon in 5 minutes |
|
|
632
|
+
| [From Method to Chat App](./docs/tutorials/from-method-to-chat-app.md) | Weather showcase: CLI, Beam, MCP, and embedded app UI from one method |
|
|
578
633
|
| [Core Concepts](./docs/concepts.md) | The 6 ideas behind Photon |
|
|
579
634
|
| [Tag Reference](./docs/reference/DOCBLOCK-TAGS.md) | Public reference for every docblock tag Photon understands |
|
|
580
635
|
| [Output Formats](./docs/formats.md) | Visual gallery of every `@format` type |
|
|
@@ -617,6 +672,6 @@ Photon is free and open source under the [MIT license](./LICENSE).
|
|
|
617
672
|
|
|
618
673
|
The project is still evolving and contributions are welcome.
|
|
619
674
|
|
|
620
|
-
- Star the repository if the idea resonates
|
|
675
|
+
- Star the repository if the idea resonates: `gh repo star portel-dev/photon`
|
|
621
676
|
- [Report issues](https://github.com/portel-dev/photon/issues)
|
|
622
677
|
- [Contribute improvements or examples](./CONTRIBUTING.md)
|
|
@@ -236,9 +236,12 @@
|
|
|
236
236
|
// Handle JSON-RPC tools/call from bridge and form components
|
|
237
237
|
if (msg.jsonrpc === '2.0' && msg.method === 'tools/call' && msg.id != null) {
|
|
238
238
|
var rawName = (msg.params && msg.params.name) || '';
|
|
239
|
-
// Don't double-prefix: invoke-form sends fully-qualified names (photon
|
|
239
|
+
// Don't double-prefix: invoke-form sends fully-qualified names (photon.method),
|
|
240
240
|
// bridge sends unqualified names (just method). Check for prefix before adding.
|
|
241
|
-
var toolName =
|
|
241
|
+
var toolName =
|
|
242
|
+
rawName.indexOf('.') !== -1 || rawName.indexOf('/') !== -1
|
|
243
|
+
? rawName
|
|
244
|
+
: photonName + '.' + rawName;
|
|
242
245
|
var args = (msg.params && msg.params.arguments) || {};
|
|
243
246
|
|
|
244
247
|
var doCall = function() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streamable-http-transport.d.ts","sourceRoot":"","sources":["../../src/auto-ui/streamable-http-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAuB5D,OAAO,KAAK,EAOV,aAAa,EACb,cAAc,EACd,eAAe,EAChB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"streamable-http-transport.d.ts","sourceRoot":"","sources":["../../src/auto-ui/streamable-http-transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -148,6 +148,33 @@ function decodeJWTCaller(authHeader) {
|
|
|
148
148
|
return undefined;
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
|
+
function isOpenAIAppSession(session) {
|
|
152
|
+
const name = session.clientInfo?.name?.toLowerCase();
|
|
153
|
+
return name === 'chatgpt' || !!name?.includes('openai');
|
|
154
|
+
}
|
|
155
|
+
function namespacedToolName(serverName, methodName) {
|
|
156
|
+
return `${serverName}.${methodName}`;
|
|
157
|
+
}
|
|
158
|
+
function toolNameForSession(session, photonName, methodName) {
|
|
159
|
+
return isOpenAIAppSession(session) ? methodName : namespacedToolName(photonName, methodName);
|
|
160
|
+
}
|
|
161
|
+
function splitNamespacedToolName(name) {
|
|
162
|
+
const dotIndex = name.indexOf('.');
|
|
163
|
+
if (dotIndex !== -1) {
|
|
164
|
+
return {
|
|
165
|
+
serverName: name.slice(0, dotIndex),
|
|
166
|
+
methodName: name.slice(dotIndex + 1),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const slashIndex = name.indexOf('/');
|
|
170
|
+
if (slashIndex !== -1) {
|
|
171
|
+
return {
|
|
172
|
+
serverName: name.slice(0, slashIndex),
|
|
173
|
+
methodName: name.slice(slashIndex + 1),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
151
178
|
// ════════════════════════════════════════════════════════════════════════════════
|
|
152
179
|
// SESSION MANAGEMENT
|
|
153
180
|
// ════════════════════════════════════════════════════════════════════════════════
|
|
@@ -1150,7 +1177,7 @@ const handlers = {
|
|
|
1150
1177
|
: undefined;
|
|
1151
1178
|
const meta = buildToolMCPMeta(method, { uiResourceUri });
|
|
1152
1179
|
tools.push({
|
|
1153
|
-
name:
|
|
1180
|
+
name: toolNameForSession(session, photon.name, method.name),
|
|
1154
1181
|
description: method.description || `Execute ${method.name}`,
|
|
1155
1182
|
inputSchema: method.params || { type: 'object', properties: {} },
|
|
1156
1183
|
'x-photon-id': photon.id, // Unique ID (hash of path) for subscriptions
|
|
@@ -1186,7 +1213,7 @@ const handlers = {
|
|
|
1186
1213
|
if (!photon.configured || !photon.stateful)
|
|
1187
1214
|
continue;
|
|
1188
1215
|
tools.push({
|
|
1189
|
-
name:
|
|
1216
|
+
name: namespacedToolName(photon.name, '_use'),
|
|
1190
1217
|
description: `Switch to a named instance of ${photon.name}. Omit name to select interactively.`,
|
|
1191
1218
|
inputSchema: {
|
|
1192
1219
|
type: 'object',
|
|
@@ -1201,21 +1228,21 @@ const handlers = {
|
|
|
1201
1228
|
'x-photon-internal': true,
|
|
1202
1229
|
});
|
|
1203
1230
|
tools.push({
|
|
1204
|
-
name:
|
|
1231
|
+
name: namespacedToolName(photon.name, '_instances'),
|
|
1205
1232
|
description: `List all available instances of ${photon.name}.`,
|
|
1206
1233
|
inputSchema: { type: 'object', properties: {} },
|
|
1207
1234
|
'x-photon-id': photon.id,
|
|
1208
1235
|
'x-photon-internal': true,
|
|
1209
1236
|
});
|
|
1210
1237
|
tools.push({
|
|
1211
|
-
name:
|
|
1238
|
+
name: namespacedToolName(photon.name, '_undo'),
|
|
1212
1239
|
description: `Undo the last state mutation on ${photon.name}. Reverts the most recent change.`,
|
|
1213
1240
|
inputSchema: { type: 'object', properties: {} },
|
|
1214
1241
|
'x-photon-id': photon.id,
|
|
1215
1242
|
'x-photon-internal': true,
|
|
1216
1243
|
});
|
|
1217
1244
|
tools.push({
|
|
1218
|
-
name:
|
|
1245
|
+
name: namespacedToolName(photon.name, '_redo'),
|
|
1219
1246
|
description: `Redo the last undone mutation on ${photon.name}. Re-applies a previously undone change.`,
|
|
1220
1247
|
inputSchema: { type: 'object', properties: {} },
|
|
1221
1248
|
'x-photon-id': photon.id,
|
|
@@ -1230,7 +1257,7 @@ const handlers = {
|
|
|
1230
1257
|
for (const method of mcp.methods) {
|
|
1231
1258
|
const meta = buildToolMCPMeta(method, { uiResourceUri: method.linkedUi });
|
|
1232
1259
|
tools.push({
|
|
1233
|
-
name:
|
|
1260
|
+
name: namespacedToolName(mcp.name, method.name),
|
|
1234
1261
|
description: method.description || `Execute ${method.name}`,
|
|
1235
1262
|
inputSchema: method.params || { type: 'object', properties: {} },
|
|
1236
1263
|
'x-external-mcp': true, // Marker for frontend to identify external MCPs
|
|
@@ -1542,9 +1569,31 @@ const handlers = {
|
|
|
1542
1569
|
if (name === 'beam/studio-parse') {
|
|
1543
1570
|
return handleBeamStudioParse(req, args || {});
|
|
1544
1571
|
}
|
|
1545
|
-
// Parse tool name: server-name
|
|
1546
|
-
|
|
1547
|
-
|
|
1572
|
+
// Parse tool name: server-name.method-name.
|
|
1573
|
+
// ChatGPT/OpenAI app sessions receive slashless names in tools/list because
|
|
1574
|
+
// their connector layer treats slash-qualified names as app resource paths.
|
|
1575
|
+
let serverName;
|
|
1576
|
+
let methodName;
|
|
1577
|
+
const namespacedName = splitNamespacedToolName(name);
|
|
1578
|
+
if (!namespacedName && isOpenAIAppSession(session)) {
|
|
1579
|
+
const matches = ctx.photons.filter((photon) => photon.configured && photon.methods?.some((method) => method.name === name));
|
|
1580
|
+
if (matches.length === 1) {
|
|
1581
|
+
const photon = matches[0];
|
|
1582
|
+
serverName = photon.name;
|
|
1583
|
+
methodName = name;
|
|
1584
|
+
}
|
|
1585
|
+
else {
|
|
1586
|
+
return {
|
|
1587
|
+
jsonrpc: '2.0',
|
|
1588
|
+
id: req.id,
|
|
1589
|
+
result: {
|
|
1590
|
+
content: [{ type: 'text', text: `Invalid tool name: ${name}` }],
|
|
1591
|
+
isError: true,
|
|
1592
|
+
},
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
else if (!namespacedName) {
|
|
1548
1597
|
return {
|
|
1549
1598
|
jsonrpc: '2.0',
|
|
1550
1599
|
id: req.id,
|
|
@@ -1554,8 +1603,10 @@ const handlers = {
|
|
|
1554
1603
|
},
|
|
1555
1604
|
};
|
|
1556
1605
|
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1606
|
+
else {
|
|
1607
|
+
serverName = namespacedName.serverName;
|
|
1608
|
+
methodName = namespacedName.methodName;
|
|
1609
|
+
}
|
|
1559
1610
|
// Per-photon auth check: if this photon requires auth but caller is anonymous, reject
|
|
1560
1611
|
const targetPhoton = ctx.photons.find((p) => p.name === serverName);
|
|
1561
1612
|
// Claim-code scope enforcement: filtering tools/list alone is not a
|