@portel/photon 1.19.0 → 1.20.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/dist/auto-ui/beam/routes/api-browse.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-browse.js +16 -4
- package/dist/auto-ui/beam/routes/api-browse.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.js +4 -4
- package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-marketplace.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-marketplace.js +14 -1
- package/dist/auto-ui/beam/routes/api-marketplace.js.map +1 -1
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +183 -74
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/bridge/index.d.ts.map +1 -1
- package/dist/auto-ui/bridge/index.js +17 -0
- package/dist/auto-ui/bridge/index.js.map +1 -1
- package/dist/auto-ui/streamable-http-transport.d.ts +1 -0
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +64 -16
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/auto-ui/types.d.ts +12 -0
- package/dist/auto-ui/types.d.ts.map +1 -1
- package/dist/auto-ui/types.js.map +1 -1
- package/dist/beam-form.bundle.js +44 -3
- package/dist/beam-form.bundle.js.map +2 -2
- package/dist/beam.bundle.js +1404 -482
- package/dist/beam.bundle.js.map +4 -4
- package/dist/capability-negotiator.d.ts +67 -0
- package/dist/capability-negotiator.d.ts.map +1 -0
- package/dist/capability-negotiator.js +104 -0
- package/dist/capability-negotiator.js.map +1 -0
- package/dist/channel-manager.d.ts +122 -0
- package/dist/channel-manager.d.ts.map +1 -0
- package/dist/channel-manager.js +266 -0
- package/dist/channel-manager.js.map +1 -0
- package/dist/cli/commands/package.d.ts.map +1 -1
- package/dist/cli/commands/package.js +25 -7
- package/dist/cli/commands/package.js.map +1 -1
- package/dist/daemon/client.d.ts.map +1 -1
- package/dist/daemon/client.js +12 -0
- package/dist/daemon/client.js.map +1 -1
- package/dist/daemon/server.js +30 -49
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/worker-manager.d.ts.map +1 -1
- package/dist/daemon/worker-manager.js +21 -7
- package/dist/daemon/worker-manager.js.map +1 -1
- package/dist/loader.d.ts +4 -1
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +73 -11
- package/dist/loader.js.map +1 -1
- package/dist/marketplace-manager.d.ts +6 -0
- package/dist/marketplace-manager.d.ts.map +1 -1
- package/dist/marketplace-manager.js +161 -58
- package/dist/marketplace-manager.js.map +1 -1
- package/dist/namespace-migration.d.ts +1 -0
- package/dist/namespace-migration.d.ts.map +1 -1
- package/dist/namespace-migration.js +86 -0
- package/dist/namespace-migration.js.map +1 -1
- package/dist/resource-server.d.ts +105 -0
- package/dist/resource-server.d.ts.map +1 -0
- package/dist/resource-server.js +723 -0
- package/dist/resource-server.js.map +1 -0
- package/dist/serv/auth/jwt.d.ts +2 -0
- package/dist/serv/auth/jwt.d.ts.map +1 -1
- package/dist/serv/auth/jwt.js +11 -5
- package/dist/serv/auth/jwt.js.map +1 -1
- package/dist/serv/vault/token-vault.d.ts +2 -0
- package/dist/serv/vault/token-vault.d.ts.map +1 -1
- package/dist/serv/vault/token-vault.js +6 -0
- package/dist/serv/vault/token-vault.js.map +1 -1
- package/dist/server.d.ts +20 -149
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +232 -1217
- package/dist/server.js.map +1 -1
- package/dist/shared/audit.d.ts.map +1 -1
- package/dist/shared/audit.js +7 -0
- package/dist/shared/audit.js.map +1 -1
- package/dist/shared/security.d.ts +10 -0
- package/dist/shared/security.d.ts.map +1 -1
- package/dist/shared/security.js +27 -0
- package/dist/shared/security.js.map +1 -1
- package/dist/task-executor.d.ts +69 -0
- package/dist/task-executor.d.ts.map +1 -0
- package/dist/task-executor.js +182 -0
- package/dist/task-executor.js.map +1 -0
- package/dist/types/photon-instance.d.ts +50 -0
- package/dist/types/photon-instance.d.ts.map +1 -0
- package/dist/types/photon-instance.js +9 -0
- package/dist/types/photon-instance.js.map +1 -0
- package/dist/types/server-types.d.ts +61 -0
- package/dist/types/server-types.d.ts.map +1 -0
- package/dist/types/server-types.js +8 -0
- package/dist/types/server-types.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilityNegotiator — client capability detection and negotiation
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates the logic for detecting what an MCP client supports
|
|
5
|
+
* (UI rendering, elicitation, etc.) based on the initialize handshake.
|
|
6
|
+
*
|
|
7
|
+
* The MCP SDK's Zod schema strips unknown fields like `extensions`
|
|
8
|
+
* (protocol 2025-11-25+), so we also capture raw capabilities from
|
|
9
|
+
* the JSON-RPC initialize message before Zod parsing occurs.
|
|
10
|
+
*/
|
|
11
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
12
|
+
export declare class CapabilityNegotiator {
|
|
13
|
+
/**
|
|
14
|
+
* Raw client capabilities captured from the initialize request BEFORE Zod parsing.
|
|
15
|
+
*
|
|
16
|
+
* The MCP SDK uses Zod to validate incoming requests, which strips unknown fields
|
|
17
|
+
* from ClientCapabilities. Notably, `extensions` (protocol 2025-11-25+) is not in
|
|
18
|
+
* the SDK's Zod schema yet, so `getClientCapabilities()` returns an object without
|
|
19
|
+
* it. Real clients like Claude Desktop and ChatGPT send UI capability under
|
|
20
|
+
* `extensions`, not `experimental`. We intercept the raw JSON-RPC message to
|
|
21
|
+
* capture the full capabilities before Zod strips them.
|
|
22
|
+
*
|
|
23
|
+
* Key: Server instance → Value: raw capabilities object from initialize request
|
|
24
|
+
*/
|
|
25
|
+
private rawClientCapabilities;
|
|
26
|
+
/**
|
|
27
|
+
* Store raw capabilities for a server instance.
|
|
28
|
+
* Called from the transport message interceptor.
|
|
29
|
+
*/
|
|
30
|
+
setRawCapabilities(server: Server, capabilities: Record<string, any>): void;
|
|
31
|
+
/**
|
|
32
|
+
* Check if client supports MCP Apps UI (structuredContent + _meta.ui)
|
|
33
|
+
*
|
|
34
|
+
* Looks for the "io.modelcontextprotocol/ui" capability in the client's
|
|
35
|
+
* initialize handshake. Any MCP client that advertises this capability
|
|
36
|
+
* gets rich UI responses — Claude Desktop, ChatGPT, MCPJam, etc.
|
|
37
|
+
*
|
|
38
|
+
* The capability may appear under `experimental` (older SDK types) or
|
|
39
|
+
* `extensions` (protocol version 2025-11-25+). We check both so it
|
|
40
|
+
* just works regardless of which field the client uses.
|
|
41
|
+
*
|
|
42
|
+
* Beam is special-cased because it's our own SSE transport where the
|
|
43
|
+
* capability is implicit.
|
|
44
|
+
*/
|
|
45
|
+
supportsUI(server: Server): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Check if client supports elicitation
|
|
48
|
+
*
|
|
49
|
+
* Elicitation is a client capability declared during initialization.
|
|
50
|
+
* The server can use elicitInput() when the client supports it.
|
|
51
|
+
*/
|
|
52
|
+
supportsElicitation(server: Server): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Intercept a transport to capture raw client capabilities before Zod strips them.
|
|
55
|
+
*
|
|
56
|
+
* The MCP SDK's Zod schema for ClientCapabilities doesn't include `extensions`
|
|
57
|
+
* (protocol 2025-11-25+), so getClientCapabilities() returns an object without it.
|
|
58
|
+
* We intercept the transport's onmessage to capture the raw `initialize` request
|
|
59
|
+
* and store capabilities before Zod parsing occurs.
|
|
60
|
+
*
|
|
61
|
+
* @param onMessage Optional additional message interceptor (e.g. for channel permissions)
|
|
62
|
+
*/
|
|
63
|
+
interceptTransportForRawCapabilities(transport: {
|
|
64
|
+
onmessage?: (...args: any[]) => void;
|
|
65
|
+
}, targetServer: Server, onMessage?: (message: any) => void): void;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=capability-negotiator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-negotiator.d.ts","sourceRoot":"","sources":["../src/capability-negotiator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAIxE,qBAAa,oBAAoB;IAC/B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,qBAAqB,CAA8C;IAE3E;;;OAGG;IACH,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI;IAI3E;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAqBnC;;;;;OAKG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAW5C;;;;;;;;;OASG;IACH,oCAAoC,CAClC,SAAS,EAAE;QAAE,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAA;KAAE,EACnD,YAAY,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,GACjC,IAAI;CAcR"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CapabilityNegotiator — client capability detection and negotiation
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates the logic for detecting what an MCP client supports
|
|
5
|
+
* (UI rendering, elicitation, etc.) based on the initialize handshake.
|
|
6
|
+
*
|
|
7
|
+
* The MCP SDK's Zod schema strips unknown fields like `extensions`
|
|
8
|
+
* (protocol 2025-11-25+), so we also capture raw capabilities from
|
|
9
|
+
* the JSON-RPC initialize message before Zod parsing occurs.
|
|
10
|
+
*/
|
|
11
|
+
const MCP_UI_CAPABILITY = 'io.modelcontextprotocol/ui';
|
|
12
|
+
export class CapabilityNegotiator {
|
|
13
|
+
/**
|
|
14
|
+
* Raw client capabilities captured from the initialize request BEFORE Zod parsing.
|
|
15
|
+
*
|
|
16
|
+
* The MCP SDK uses Zod to validate incoming requests, which strips unknown fields
|
|
17
|
+
* from ClientCapabilities. Notably, `extensions` (protocol 2025-11-25+) is not in
|
|
18
|
+
* the SDK's Zod schema yet, so `getClientCapabilities()` returns an object without
|
|
19
|
+
* it. Real clients like Claude Desktop and ChatGPT send UI capability under
|
|
20
|
+
* `extensions`, not `experimental`. We intercept the raw JSON-RPC message to
|
|
21
|
+
* capture the full capabilities before Zod strips them.
|
|
22
|
+
*
|
|
23
|
+
* Key: Server instance → Value: raw capabilities object from initialize request
|
|
24
|
+
*/
|
|
25
|
+
rawClientCapabilities = new WeakMap();
|
|
26
|
+
/**
|
|
27
|
+
* Store raw capabilities for a server instance.
|
|
28
|
+
* Called from the transport message interceptor.
|
|
29
|
+
*/
|
|
30
|
+
setRawCapabilities(server, capabilities) {
|
|
31
|
+
this.rawClientCapabilities.set(server, capabilities);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if client supports MCP Apps UI (structuredContent + _meta.ui)
|
|
35
|
+
*
|
|
36
|
+
* Looks for the "io.modelcontextprotocol/ui" capability in the client's
|
|
37
|
+
* initialize handshake. Any MCP client that advertises this capability
|
|
38
|
+
* gets rich UI responses — Claude Desktop, ChatGPT, MCPJam, etc.
|
|
39
|
+
*
|
|
40
|
+
* The capability may appear under `experimental` (older SDK types) or
|
|
41
|
+
* `extensions` (protocol version 2025-11-25+). We check both so it
|
|
42
|
+
* just works regardless of which field the client uses.
|
|
43
|
+
*
|
|
44
|
+
* Beam is special-cased because it's our own SSE transport where the
|
|
45
|
+
* capability is implicit.
|
|
46
|
+
*/
|
|
47
|
+
supportsUI(server) {
|
|
48
|
+
// Check SDK-parsed capabilities (works for `experimental` which is in the Zod schema)
|
|
49
|
+
const capabilities = server.getClientCapabilities();
|
|
50
|
+
if (capabilities?.experimental?.[MCP_UI_CAPABILITY]) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
// Check raw capabilities captured before Zod parsing (needed for `extensions`
|
|
54
|
+
// which the SDK's Zod schema strips — Claude Desktop and ChatGPT use this field)
|
|
55
|
+
const raw = this.rawClientCapabilities.get(server);
|
|
56
|
+
if (raw?.extensions?.[MCP_UI_CAPABILITY]) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
// Beam is our own transport — UI support is implicit
|
|
60
|
+
const clientInfo = server.getClientVersion();
|
|
61
|
+
if (clientInfo?.name === 'beam')
|
|
62
|
+
return true;
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if client supports elicitation
|
|
67
|
+
*
|
|
68
|
+
* Elicitation is a client capability declared during initialization.
|
|
69
|
+
* The server can use elicitInput() when the client supports it.
|
|
70
|
+
*/
|
|
71
|
+
supportsElicitation(server) {
|
|
72
|
+
const capabilities = server.getClientCapabilities();
|
|
73
|
+
if (!capabilities) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
// Check for elicitation capability (MCP 2025-06 spec)
|
|
77
|
+
return !!capabilities.elicitation;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Intercept a transport to capture raw client capabilities before Zod strips them.
|
|
81
|
+
*
|
|
82
|
+
* The MCP SDK's Zod schema for ClientCapabilities doesn't include `extensions`
|
|
83
|
+
* (protocol 2025-11-25+), so getClientCapabilities() returns an object without it.
|
|
84
|
+
* We intercept the transport's onmessage to capture the raw `initialize` request
|
|
85
|
+
* and store capabilities before Zod parsing occurs.
|
|
86
|
+
*
|
|
87
|
+
* @param onMessage Optional additional message interceptor (e.g. for channel permissions)
|
|
88
|
+
*/
|
|
89
|
+
interceptTransportForRawCapabilities(transport, targetServer, onMessage) {
|
|
90
|
+
const origOnMessage = transport.onmessage;
|
|
91
|
+
transport.onmessage = (message, extra) => {
|
|
92
|
+
// Capture raw capabilities from initialize request
|
|
93
|
+
if (message?.method === 'initialize' && message?.params) {
|
|
94
|
+
if (message.params.capabilities) {
|
|
95
|
+
this.rawClientCapabilities.set(targetServer, message.params.capabilities);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Call additional interceptor if provided
|
|
99
|
+
onMessage?.(message);
|
|
100
|
+
origOnMessage?.(message, extra);
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=capability-negotiator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-negotiator.js","sourceRoot":"","sources":["../src/capability-negotiator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,iBAAiB,GAAG,4BAA4B,CAAC;AAEvD,MAAM,OAAO,oBAAoB;IAC/B;;;;;;;;;;;OAWG;IACK,qBAAqB,GAAG,IAAI,OAAO,EAA+B,CAAC;IAE3E;;;OAGG;IACH,kBAAkB,CAAC,MAAc,EAAE,YAAiC;QAClE,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvD,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,MAAc;QACvB,sFAAsF;QACtF,MAAM,YAAY,GAAG,MAAM,CAAC,qBAAqB,EAAyB,CAAC;QAC3E,IAAI,YAAY,EAAE,YAAY,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,8EAA8E;QAC9E,iFAAiF;QACjF,MAAM,GAAG,GAAG,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACnD,IAAI,GAAG,EAAE,UAAU,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,qDAAqD;QACrD,MAAM,UAAU,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC7C,IAAI,UAAU,EAAE,IAAI,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QAE7C,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACH,mBAAmB,CAAC,MAAc;QAChC,MAAM,YAAY,GAAG,MAAM,CAAC,qBAAqB,EAAE,CAAC;QAEpD,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,sDAAsD;QACtD,OAAO,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC;IACpC,CAAC;IAED;;;;;;;;;OASG;IACH,oCAAoC,CAClC,SAAmD,EACnD,YAAoB,EACpB,SAAkC;QAElC,MAAM,aAAa,GAAG,SAAS,CAAC,SAAS,CAAC;QAC1C,SAAS,CAAC,SAAS,GAAG,CAAC,OAAY,EAAE,KAAW,EAAE,EAAE;YAClD,mDAAmD;YACnD,IAAI,OAAO,EAAE,MAAM,KAAK,YAAY,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;gBACxD,IAAI,OAAO,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;oBAChC,IAAI,CAAC,qBAAqB,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;gBAC5E,CAAC;YACH,CAAC;YACD,0CAA0C;YAC1C,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC;YACrB,aAAa,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAClC,CAAC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelManager — encapsulates channel/pub-sub logic extracted from PhotonServer.
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Channel capability declaration for MCP server init
|
|
6
|
+
* - Channel notification methods (per-target)
|
|
7
|
+
* - Permission request/response flow
|
|
8
|
+
* - Publishing channel events to daemon
|
|
9
|
+
* - Subscribing to daemon channels and forwarding messages to MCP clients
|
|
10
|
+
* - Cleanup of daemon subscriptions
|
|
11
|
+
*/
|
|
12
|
+
export type ChannelPermissionRequest = {
|
|
13
|
+
request_id: string;
|
|
14
|
+
tool_name: string;
|
|
15
|
+
description: string;
|
|
16
|
+
input_preview: string;
|
|
17
|
+
};
|
|
18
|
+
export type ChannelPermissionResponse = {
|
|
19
|
+
request_id: string;
|
|
20
|
+
behavior: 'allow' | 'deny';
|
|
21
|
+
};
|
|
22
|
+
/** Channel-specific options extracted from PhotonServerOptions */
|
|
23
|
+
export interface ChannelOptions {
|
|
24
|
+
channelMode?: boolean;
|
|
25
|
+
channelName?: string;
|
|
26
|
+
channelTargets?: string[];
|
|
27
|
+
channelInstructions?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Callback interface for sending notifications back to MCP clients.
|
|
31
|
+
* PhotonServer implements this so ChannelManager stays decoupled.
|
|
32
|
+
*/
|
|
33
|
+
export interface ChannelNotificationSink {
|
|
34
|
+
/** Send a notification to the primary (STDIO) server */
|
|
35
|
+
sendNotification(notification: {
|
|
36
|
+
method: string;
|
|
37
|
+
params: any;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
/** Send a notification to all SSE sessions */
|
|
40
|
+
sendNotificationToAllSessions(notification: {
|
|
41
|
+
method: string;
|
|
42
|
+
params: any;
|
|
43
|
+
}): Promise<void>;
|
|
44
|
+
/** Get the photon instance for permission dispatch */
|
|
45
|
+
getPhotonInstance(): any;
|
|
46
|
+
}
|
|
47
|
+
export declare class ChannelManager {
|
|
48
|
+
private channelUnsubscribers;
|
|
49
|
+
private daemonName;
|
|
50
|
+
private options;
|
|
51
|
+
private workingDir?;
|
|
52
|
+
private sink;
|
|
53
|
+
private log;
|
|
54
|
+
constructor(opts: {
|
|
55
|
+
channelOptions: ChannelOptions;
|
|
56
|
+
workingDir?: string;
|
|
57
|
+
sink: ChannelNotificationSink;
|
|
58
|
+
log: (level: string, message: string, data?: Record<string, unknown>) => void;
|
|
59
|
+
});
|
|
60
|
+
/** Whether channel mode is active */
|
|
61
|
+
get isChannelMode(): boolean;
|
|
62
|
+
/** The current daemon name (set after daemon startup) */
|
|
63
|
+
get currentDaemonName(): string | null;
|
|
64
|
+
/** Set the daemon name (called after daemon startup in PhotonServer) */
|
|
65
|
+
setDaemonName(name: string | null): void;
|
|
66
|
+
/**
|
|
67
|
+
* Returns the server name to use.
|
|
68
|
+
* In channel mode, uses the channel name; otherwise defaults to 'photon-mcp'.
|
|
69
|
+
*/
|
|
70
|
+
getServerName(): string;
|
|
71
|
+
/**
|
|
72
|
+
* Returns extra capabilities to merge into the MCP Server capabilities object.
|
|
73
|
+
* Produces the experimental channel entries for each declared target.
|
|
74
|
+
*/
|
|
75
|
+
getExtraCapabilities(): Record<string, any>;
|
|
76
|
+
/**
|
|
77
|
+
* Returns extra server constructor options (e.g. instructions) for channel mode.
|
|
78
|
+
*/
|
|
79
|
+
getExtraServerOptions(): Record<string, any>;
|
|
80
|
+
/**
|
|
81
|
+
* Get the notification methods for all declared channel targets.
|
|
82
|
+
* Each target (e.g. 'claude') maps to `notifications/{target}/channel`.
|
|
83
|
+
*/
|
|
84
|
+
private getChannelNotificationMethods;
|
|
85
|
+
/**
|
|
86
|
+
* Handle a permission request from the client (e.g. Claude Code asking "Allow tool X?").
|
|
87
|
+
* Forwards to the photon instance via channel._dispatchPermission().
|
|
88
|
+
*/
|
|
89
|
+
handlePermissionRequest(params: any): void;
|
|
90
|
+
/**
|
|
91
|
+
* Send a permission response back to the client.
|
|
92
|
+
* Called by the photon instance (via this.channel.respond) when the user approves/denies.
|
|
93
|
+
*/
|
|
94
|
+
respondToPermission(response: ChannelPermissionResponse): void;
|
|
95
|
+
/**
|
|
96
|
+
* Publish a channel event to the daemon for cross-process pub/sub.
|
|
97
|
+
* Called from output handlers whenever an emit has a `channel` field.
|
|
98
|
+
*/
|
|
99
|
+
publishIfChannel(emit: any): void;
|
|
100
|
+
/**
|
|
101
|
+
* Subscribe to daemon channels for cross-process notifications.
|
|
102
|
+
* In channel mode, handleChannelMessage intercepts 'channel-push' events
|
|
103
|
+
* and translates them to notifications/claude/channel for the connected client.
|
|
104
|
+
*/
|
|
105
|
+
subscribeToChannels(): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Handle incoming channel messages and forward as MCP notifications.
|
|
108
|
+
* Routes channel-permission-response and channel-push messages,
|
|
109
|
+
* and forwards everything else as standard MCP notifications with _photon data.
|
|
110
|
+
*/
|
|
111
|
+
private handleChannelMessage;
|
|
112
|
+
/**
|
|
113
|
+
* Check if an incoming message is a channel permission request and handle it.
|
|
114
|
+
* Returns true if it was handled (caller should not process further).
|
|
115
|
+
*/
|
|
116
|
+
interceptPermissionRequest(message: any): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Unsubscribe from all daemon channels. Called during server shutdown.
|
|
119
|
+
*/
|
|
120
|
+
cleanup(): void;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=channel-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-manager.d.ts","sourceRoot":"","sources":["../src/channel-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,GAAG,MAAM,CAAC;CAC5B,CAAC;AAEF,kEAAkE;AAClE,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,wDAAwD;IACxD,gBAAgB,CAAC,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E,8CAA8C;IAC9C,6BAA6B,CAAC,YAAY,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5F,sDAAsD;IACtD,iBAAiB,IAAI,GAAG,CAAC;CAC1B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,oBAAoB,CAAyB;IACrD,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,IAAI,CAA0B;IACtC,OAAO,CAAC,GAAG,CAA2E;gBAE1E,IAAI,EAAE;QAChB,cAAc,EAAE,cAAc,CAAC;QAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,EAAE,uBAAuB,CAAC;QAC9B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAC/E;IAOD,qCAAqC;IACrC,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,yDAAyD;IACzD,IAAI,iBAAiB,IAAI,MAAM,GAAG,IAAI,CAErC;IAED,wEAAwE;IACxE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAQxC;;;OAGG;IACH,aAAa,IAAI,MAAM;IAMvB;;;OAGG;IACH,oBAAoB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAY3C;;OAEG;IACH,qBAAqB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAS5C;;;OAGG;IACH,OAAO,CAAC,6BAA6B;IASrC;;;OAGG;IACH,uBAAuB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI;IAe1C;;;OAGG;IACH,mBAAmB,CAAC,QAAQ,EAAE,yBAAyB,GAAG,IAAI;IAuB9D;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAWjC;;;;OAIG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB1C;;;;OAIG;YACW,oBAAoB;IA0ElC;;;OAGG;IACH,0BAA0B,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO;IAYjD;;OAEG;IACH,OAAO,IAAI,IAAI;CAUhB"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChannelManager — encapsulates channel/pub-sub logic extracted from PhotonServer.
|
|
3
|
+
*
|
|
4
|
+
* Handles:
|
|
5
|
+
* - Channel capability declaration for MCP server init
|
|
6
|
+
* - Channel notification methods (per-target)
|
|
7
|
+
* - Permission request/response flow
|
|
8
|
+
* - Publishing channel events to daemon
|
|
9
|
+
* - Subscribing to daemon channels and forwarding messages to MCP clients
|
|
10
|
+
* - Cleanup of daemon subscriptions
|
|
11
|
+
*/
|
|
12
|
+
import { subscribeChannel, publishToChannel } from './daemon/client.js';
|
|
13
|
+
import { getErrorMessage } from './shared/error-handler.js';
|
|
14
|
+
export class ChannelManager {
|
|
15
|
+
channelUnsubscribers = [];
|
|
16
|
+
daemonName = null;
|
|
17
|
+
options;
|
|
18
|
+
workingDir;
|
|
19
|
+
sink;
|
|
20
|
+
log;
|
|
21
|
+
constructor(opts) {
|
|
22
|
+
this.options = opts.channelOptions;
|
|
23
|
+
this.workingDir = opts.workingDir;
|
|
24
|
+
this.sink = opts.sink;
|
|
25
|
+
this.log = opts.log;
|
|
26
|
+
}
|
|
27
|
+
/** Whether channel mode is active */
|
|
28
|
+
get isChannelMode() {
|
|
29
|
+
return !!this.options.channelMode;
|
|
30
|
+
}
|
|
31
|
+
/** The current daemon name (set after daemon startup) */
|
|
32
|
+
get currentDaemonName() {
|
|
33
|
+
return this.daemonName;
|
|
34
|
+
}
|
|
35
|
+
/** Set the daemon name (called after daemon startup in PhotonServer) */
|
|
36
|
+
setDaemonName(name) {
|
|
37
|
+
this.daemonName = name;
|
|
38
|
+
}
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// MCP Server Init Helpers
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
/**
|
|
43
|
+
* Returns the server name to use.
|
|
44
|
+
* In channel mode, uses the channel name; otherwise defaults to 'photon-mcp'.
|
|
45
|
+
*/
|
|
46
|
+
getServerName() {
|
|
47
|
+
return this.options.channelMode && this.options.channelName
|
|
48
|
+
? this.options.channelName
|
|
49
|
+
: 'photon-mcp';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns extra capabilities to merge into the MCP Server capabilities object.
|
|
53
|
+
* Produces the experimental channel entries for each declared target.
|
|
54
|
+
*/
|
|
55
|
+
getExtraCapabilities() {
|
|
56
|
+
if (!this.options.channelMode || !this.options.channelTargets?.length)
|
|
57
|
+
return {};
|
|
58
|
+
return {
|
|
59
|
+
experimental: Object.fromEntries(this.options.channelTargets.flatMap((t) => [
|
|
60
|
+
[`${t}/channel`, {}],
|
|
61
|
+
[`${t}/channel/permission`, {}],
|
|
62
|
+
])),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Returns extra server constructor options (e.g. instructions) for channel mode.
|
|
67
|
+
*/
|
|
68
|
+
getExtraServerOptions() {
|
|
69
|
+
if (!this.options.channelMode || !this.options.channelInstructions)
|
|
70
|
+
return {};
|
|
71
|
+
return { instructions: this.options.channelInstructions };
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// Notification Methods
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Get the notification methods for all declared channel targets.
|
|
78
|
+
* Each target (e.g. 'claude') maps to `notifications/{target}/channel`.
|
|
79
|
+
*/
|
|
80
|
+
getChannelNotificationMethods() {
|
|
81
|
+
const targets = this.options.channelTargets || [];
|
|
82
|
+
return targets.map((t) => `notifications/${t}/channel`);
|
|
83
|
+
}
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Permission Flow
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Handle a permission request from the client (e.g. Claude Code asking "Allow tool X?").
|
|
89
|
+
* Forwards to the photon instance via channel._dispatchPermission().
|
|
90
|
+
*/
|
|
91
|
+
handlePermissionRequest(params) {
|
|
92
|
+
if (!params?.request_id || !params?.tool_name)
|
|
93
|
+
return;
|
|
94
|
+
const request = {
|
|
95
|
+
request_id: params.request_id,
|
|
96
|
+
tool_name: params.tool_name,
|
|
97
|
+
description: params.description || '',
|
|
98
|
+
input_preview: params.input_preview || '',
|
|
99
|
+
};
|
|
100
|
+
this.log('info', `Permission request: ${request.tool_name} (${request.request_id})`);
|
|
101
|
+
const instance = this.sink.getPhotonInstance();
|
|
102
|
+
if (instance?.channel?._dispatchPermission) {
|
|
103
|
+
instance.channel._dispatchPermission(request);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Send a permission response back to the client.
|
|
108
|
+
* Called by the photon instance (via this.channel.respond) when the user approves/denies.
|
|
109
|
+
*/
|
|
110
|
+
respondToPermission(response) {
|
|
111
|
+
const targets = this.options.channelTargets || [];
|
|
112
|
+
for (const target of targets) {
|
|
113
|
+
const notification = {
|
|
114
|
+
method: `notifications/${target}/channel/permission`,
|
|
115
|
+
params: {
|
|
116
|
+
request_id: response.request_id,
|
|
117
|
+
behavior: response.behavior,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
this.sink.sendNotification(notification).catch((e) => {
|
|
121
|
+
this.log('debug', 'Permission response failed', { error: getErrorMessage(e) });
|
|
122
|
+
});
|
|
123
|
+
this.sink.sendNotificationToAllSessions(notification).catch((e) => {
|
|
124
|
+
this.log('debug', `Failed to send permission to SSE sessions: ${getErrorMessage(e)}`);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
// Publishing
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
/**
|
|
132
|
+
* Publish a channel event to the daemon for cross-process pub/sub.
|
|
133
|
+
* Called from output handlers whenever an emit has a `channel` field.
|
|
134
|
+
*/
|
|
135
|
+
publishIfChannel(emit) {
|
|
136
|
+
if (!this.daemonName || !emit?.channel)
|
|
137
|
+
return;
|
|
138
|
+
publishToChannel(this.daemonName, emit.channel, emit, this.workingDir).catch((e) => {
|
|
139
|
+
this.log('debug', `Failed to publish channel event to daemon: ${e?.message || e}`);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
// Subscribing & Message Handling
|
|
144
|
+
// ---------------------------------------------------------------------------
|
|
145
|
+
/**
|
|
146
|
+
* Subscribe to daemon channels for cross-process notifications.
|
|
147
|
+
* In channel mode, handleChannelMessage intercepts 'channel-push' events
|
|
148
|
+
* and translates them to notifications/claude/channel for the connected client.
|
|
149
|
+
*/
|
|
150
|
+
async subscribeToChannels() {
|
|
151
|
+
if (!this.daemonName)
|
|
152
|
+
return;
|
|
153
|
+
try {
|
|
154
|
+
const unsubscribe = await subscribeChannel(this.daemonName, `${this.daemonName}:*`, (message) => {
|
|
155
|
+
void this.handleChannelMessage(message);
|
|
156
|
+
}, { workingDir: this.workingDir });
|
|
157
|
+
this.channelUnsubscribers.push(unsubscribe);
|
|
158
|
+
this.log('info', `Subscribed to daemon channel: ${this.daemonName}:*`);
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
this.log('warn', `Failed to subscribe to daemon: ${getErrorMessage(error)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Handle incoming channel messages and forward as MCP notifications.
|
|
166
|
+
* Routes channel-permission-response and channel-push messages,
|
|
167
|
+
* and forwards everything else as standard MCP notifications with _photon data.
|
|
168
|
+
*/
|
|
169
|
+
async handleChannelMessage(message) {
|
|
170
|
+
if (!message || typeof message !== 'object')
|
|
171
|
+
return;
|
|
172
|
+
const msg = message;
|
|
173
|
+
if (process.env.PHOTON_DEBUG_EVENTS === '1') {
|
|
174
|
+
console.error(`[PHOTON-SERVER] Received daemon message on ${String(msg.channel)}: event=${String(msg.event)}`);
|
|
175
|
+
}
|
|
176
|
+
// Channel permission responses — photon called this.channel.respond()
|
|
177
|
+
if (this.options.channelMode && String(msg.channel).endsWith(':channel-permission-response')) {
|
|
178
|
+
const data = msg.data;
|
|
179
|
+
if (data?.request_id && data?.behavior) {
|
|
180
|
+
this.respondToPermission({
|
|
181
|
+
request_id: data.request_id,
|
|
182
|
+
behavior: data.behavior,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
// Channel events — translate to client-specific channel notifications.
|
|
188
|
+
if (this.options.channelMode && String(msg.channel).endsWith(':channel-push')) {
|
|
189
|
+
const pushData = msg.data;
|
|
190
|
+
const methods = this.getChannelNotificationMethods();
|
|
191
|
+
if (methods.length === 0)
|
|
192
|
+
return;
|
|
193
|
+
const content = typeof pushData?.content === 'string' ? pushData.content : '';
|
|
194
|
+
const meta = pushData?.meta || {};
|
|
195
|
+
try {
|
|
196
|
+
for (const method of methods) {
|
|
197
|
+
const notification = { method, params: { content, meta } };
|
|
198
|
+
await this.sink.sendNotification(notification);
|
|
199
|
+
await this.sink.sendNotificationToAllSessions(notification);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch (e) {
|
|
203
|
+
this.log('debug', 'Channel notification failed', { error: getErrorMessage(e) });
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// Standard notification with embedded photon data
|
|
208
|
+
const payload = {
|
|
209
|
+
method: 'ui/notifications/host-context-changed',
|
|
210
|
+
params: {
|
|
211
|
+
_photon: {
|
|
212
|
+
photon: this.daemonName,
|
|
213
|
+
channel: msg.channel,
|
|
214
|
+
event: msg.event,
|
|
215
|
+
data: msg.data,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
try {
|
|
220
|
+
if (process.env.PHOTON_DEBUG_EVENTS === '1') {
|
|
221
|
+
console.error(`[PHOTON-SERVER] Sending notification to MCP clients...`);
|
|
222
|
+
}
|
|
223
|
+
await this.sink.sendNotification(payload);
|
|
224
|
+
if (process.env.PHOTON_DEBUG_EVENTS === '1') {
|
|
225
|
+
console.error(`[PHOTON-SERVER] Notification sent successfully`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
console.error(`[PHOTON-SERVER-ERROR] Notification send failed: ${getErrorMessage(e)}`);
|
|
230
|
+
this.log('debug', 'Notification send failed', { error: getErrorMessage(e) });
|
|
231
|
+
}
|
|
232
|
+
// Also send to SSE sessions
|
|
233
|
+
await this.sink.sendNotificationToAllSessions(payload).catch((e) => {
|
|
234
|
+
this.log('debug', 'Session notification failed', { error: getErrorMessage(e) });
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Check if an incoming message is a channel permission request and handle it.
|
|
239
|
+
* Returns true if it was handled (caller should not process further).
|
|
240
|
+
*/
|
|
241
|
+
interceptPermissionRequest(message) {
|
|
242
|
+
if (this.options.channelMode && message?.method?.endsWith('/channel/permission_request')) {
|
|
243
|
+
this.handlePermissionRequest(message.params);
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
// ---------------------------------------------------------------------------
|
|
249
|
+
// Cleanup
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
/**
|
|
252
|
+
* Unsubscribe from all daemon channels. Called during server shutdown.
|
|
253
|
+
*/
|
|
254
|
+
cleanup() {
|
|
255
|
+
for (const unsubscribe of this.channelUnsubscribers) {
|
|
256
|
+
try {
|
|
257
|
+
unsubscribe();
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
/* ignore */
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
this.channelUnsubscribers = [];
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=channel-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-manager.js","sourceRoot":"","sources":["../src/channel-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/package.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+LzC;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"package.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/package.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA+LzC;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8oB9D"}
|
|
@@ -485,7 +485,7 @@ export function registerPackageCommands(program) {
|
|
|
485
485
|
tableData.push({
|
|
486
486
|
name: mcpName,
|
|
487
487
|
local: info.local || '-',
|
|
488
|
-
remote: info.remote || 'local
|
|
488
|
+
remote: info.remote || 'local editable copy',
|
|
489
489
|
status: STATUS.UNKNOWN,
|
|
490
490
|
});
|
|
491
491
|
}
|
|
@@ -645,8 +645,9 @@ export function registerPackageCommands(program) {
|
|
|
645
645
|
program
|
|
646
646
|
.command('fork')
|
|
647
647
|
.argument('<name>', 'Photon name to fork/own')
|
|
648
|
-
.
|
|
649
|
-
.
|
|
648
|
+
.option('--as <newName>', 'New local photon name (required when forking an already-local photon)')
|
|
649
|
+
.description('Create a local editable copy or take ownership of a tracked photon')
|
|
650
|
+
.action(async (name, options, command) => {
|
|
650
651
|
try {
|
|
651
652
|
const { printSuccess, printError } = await import('../../cli-formatter.js');
|
|
652
653
|
const workingDir = getDefaultContext().baseDir;
|
|
@@ -662,7 +663,7 @@ export function registerPackageCommands(program) {
|
|
|
662
663
|
}
|
|
663
664
|
}
|
|
664
665
|
choices.push('Create new GitHub repository');
|
|
665
|
-
choices.push('Local
|
|
666
|
+
choices.push('Local editable copy');
|
|
666
667
|
const rl = createReadline();
|
|
667
668
|
console.error(`\nWhere do you want to fork ${name}?\n`);
|
|
668
669
|
choices.forEach((c, i) => console.error(` [${i + 1}] ${c}`));
|
|
@@ -676,7 +677,7 @@ export function registerPackageCommands(program) {
|
|
|
676
677
|
printError('Invalid choice');
|
|
677
678
|
process.exit(1);
|
|
678
679
|
}
|
|
679
|
-
let forkOptions;
|
|
680
|
+
let forkOptions = {};
|
|
680
681
|
if (choiceIdx < targets.length) {
|
|
681
682
|
// Push to existing marketplace repo
|
|
682
683
|
forkOptions = { targetRepo: targets[choiceIdx].repo };
|
|
@@ -694,8 +695,25 @@ export function registerPackageCommands(program) {
|
|
|
694
695
|
}
|
|
695
696
|
forkOptions = { createRepo: repoName.trim() };
|
|
696
697
|
}
|
|
697
|
-
|
|
698
|
-
|
|
698
|
+
if (options.as?.trim()) {
|
|
699
|
+
forkOptions.newName = options.as.trim();
|
|
700
|
+
}
|
|
701
|
+
let result = await manager.forkPhoton(name, workingDir, forkOptions);
|
|
702
|
+
if (!result.success && result.requiresName && !forkOptions.newName) {
|
|
703
|
+
const rl3 = createReadline();
|
|
704
|
+
const suggested = result.suggestedName ? ` [${result.suggestedName}]` : '';
|
|
705
|
+
const newName = await new Promise((resolve) => {
|
|
706
|
+
rl3.question(`New local photon name${suggested}: `, resolve);
|
|
707
|
+
});
|
|
708
|
+
rl3.close();
|
|
709
|
+
const resolvedName = newName.trim() || result.suggestedName;
|
|
710
|
+
if (!resolvedName) {
|
|
711
|
+
printError('A new local name is required');
|
|
712
|
+
process.exit(1);
|
|
713
|
+
}
|
|
714
|
+
forkOptions.newName = resolvedName;
|
|
715
|
+
result = await manager.forkPhoton(name, workingDir, forkOptions);
|
|
716
|
+
}
|
|
699
717
|
if (result.success) {
|
|
700
718
|
printSuccess(result.message);
|
|
701
719
|
}
|