@portel/photon 1.11.0 → 1.12.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 +81 -72
- package/dist/auto-ui/beam/photon-management.d.ts.map +1 -1
- package/dist/auto-ui/beam/photon-management.js +5 -0
- package/dist/auto-ui/beam/photon-management.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-browse.d.ts +1 -2
- package/dist/auto-ui/beam/routes/api-browse.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-browse.js +140 -191
- package/dist/auto-ui/beam/routes/api-browse.js.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.d.ts.map +1 -1
- package/dist/auto-ui/beam/routes/api-config.js +44 -1
- package/dist/auto-ui/beam/routes/api-config.js.map +1 -1
- package/dist/auto-ui/beam.d.ts.map +1 -1
- package/dist/auto-ui/beam.js +874 -20
- package/dist/auto-ui/beam.js.map +1 -1
- package/dist/auto-ui/frontend/index.html +83 -60
- package/dist/auto-ui/streamable-http-transport.d.ts.map +1 -1
- package/dist/auto-ui/streamable-http-transport.js +16 -2
- package/dist/auto-ui/streamable-http-transport.js.map +1 -1
- package/dist/auto-ui/types.d.ts +1 -1
- package/dist/auto-ui/types.d.ts.map +1 -1
- package/dist/auto-ui/types.js.map +1 -1
- package/dist/beam.bundle.js +2794 -322
- package/dist/beam.bundle.js.map +4 -4
- package/dist/cli/commands/package-app.d.ts.map +1 -1
- package/dist/cli/commands/package-app.js +116 -35
- package/dist/cli/commands/package-app.js.map +1 -1
- package/dist/context-store.d.ts +5 -0
- package/dist/context-store.d.ts.map +1 -1
- package/dist/context-store.js +9 -0
- package/dist/context-store.js.map +1 -1
- package/dist/daemon/server.js +303 -6
- package/dist/daemon/server.js.map +1 -1
- package/dist/loader.d.ts +4 -0
- package/dist/loader.d.ts.map +1 -1
- package/dist/loader.js +186 -1
- package/dist/loader.js.map +1 -1
- package/dist/photon-cli-runner.d.ts.map +1 -1
- package/dist/photon-cli-runner.js +21 -1
- package/dist/photon-cli-runner.js.map +1 -1
- package/dist/photon-doc-extractor.d.ts +6 -0
- package/dist/photon-doc-extractor.d.ts.map +1 -1
- package/dist/photon-doc-extractor.js +22 -0
- package/dist/photon-doc-extractor.js.map +1 -1
- package/dist/photons/tunnel.photon.d.ts +5 -9
- package/dist/photons/tunnel.photon.d.ts.map +1 -1
- package/dist/photons/tunnel.photon.js +36 -96
- package/dist/photons/tunnel.photon.js.map +1 -1
- package/dist/photons/tunnel.photon.ts +40 -112
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +27 -2
- package/dist/server.js.map +1 -1
- package/dist/test-runner.d.ts +13 -1
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +529 -122
- package/dist/test-runner.js.map +1 -1
- package/package.json +22 -6
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tunnel - Expose local servers to the internet
|
|
3
3
|
* @description Multi-provider tunneling for remote access to Beam and local services
|
|
4
|
-
* @
|
|
4
|
+
* @icon 🌐
|
|
5
5
|
*/
|
|
6
|
-
import { spawn, spawnSync
|
|
6
|
+
import { spawn, spawnSync } from 'child_process';
|
|
7
7
|
// Track active tunnels
|
|
8
8
|
const activeTunnels = new Map();
|
|
9
9
|
export default class Tunnel {
|
|
@@ -12,21 +12,23 @@ export default class Tunnel {
|
|
|
12
12
|
* @icon 🔍
|
|
13
13
|
*/
|
|
14
14
|
async status() {
|
|
15
|
+
// Check which tunnels are still alive
|
|
16
|
+
for (const [port, tunnel] of activeTunnels) {
|
|
17
|
+
if (tunnel.process.killed || tunnel.process.exitCode !== null) {
|
|
18
|
+
tunnel.info.active = false;
|
|
19
|
+
activeTunnels.delete(port);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
15
22
|
const providers = [
|
|
16
23
|
{
|
|
17
|
-
name: '
|
|
18
|
-
available:
|
|
19
|
-
|
|
24
|
+
name: 'cloudflared',
|
|
25
|
+
available: this._checkCommand('cloudflared'),
|
|
26
|
+
install: 'brew install cloudflared',
|
|
20
27
|
},
|
|
21
28
|
{
|
|
22
29
|
name: 'ngrok',
|
|
23
30
|
available: this._checkCommand('ngrok'),
|
|
24
|
-
install: 'brew install ngrok
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: 'cloudflared',
|
|
28
|
-
available: this._checkCommand('cloudflared'),
|
|
29
|
-
install: 'brew install cloudflared OR https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation',
|
|
31
|
+
install: 'brew install ngrok',
|
|
30
32
|
},
|
|
31
33
|
];
|
|
32
34
|
return {
|
|
@@ -37,67 +39,62 @@ export default class Tunnel {
|
|
|
37
39
|
/**
|
|
38
40
|
* Start a tunnel to expose Beam to the internet
|
|
39
41
|
* @icon 🚀
|
|
42
|
+
* @format qr
|
|
40
43
|
* @param provider Tunnel provider to use
|
|
41
44
|
*/
|
|
42
|
-
async *start({ provider = '
|
|
45
|
+
async *start({ provider = 'cloudflared', } = {}) {
|
|
43
46
|
// Auto-detect Beam port from environment
|
|
44
47
|
const port = parseInt(process.env.BEAM_PORT || '3117', 10);
|
|
45
48
|
// Check if tunnel already exists for this port
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
const existing = activeTunnels.get(port);
|
|
50
|
+
if (existing) {
|
|
51
|
+
// Verify the process is still alive
|
|
52
|
+
if (!existing.process.killed && existing.process.exitCode === null) {
|
|
53
|
+
return {
|
|
54
|
+
message: `Tunnel already running via ${existing.info.provider}`,
|
|
55
|
+
url: existing.info.url,
|
|
56
|
+
link: existing.info.url,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Process died — clean up and start fresh
|
|
60
|
+
activeTunnels.delete(port);
|
|
57
61
|
}
|
|
58
62
|
yield {
|
|
59
63
|
emit: 'status',
|
|
60
64
|
value: { step: 'starting' },
|
|
61
|
-
message: `Starting ${provider} tunnel
|
|
65
|
+
message: `Starting ${provider} tunnel on port ${port}...`,
|
|
62
66
|
};
|
|
63
67
|
try {
|
|
64
68
|
let url;
|
|
65
|
-
let
|
|
69
|
+
let tunnelProcess;
|
|
66
70
|
switch (provider) {
|
|
67
71
|
case 'ngrok':
|
|
68
72
|
if (!this._checkCommand('ngrok')) {
|
|
69
73
|
throw new Error('ngrok not installed. Run: brew install ngrok');
|
|
70
74
|
}
|
|
71
|
-
({ url, process } = await this._startNgrok(port));
|
|
75
|
+
({ url, process: tunnelProcess } = await this._startNgrok(port));
|
|
72
76
|
break;
|
|
73
77
|
case 'cloudflared':
|
|
78
|
+
default:
|
|
74
79
|
if (!this._checkCommand('cloudflared')) {
|
|
75
80
|
throw new Error('cloudflared not installed. Run: brew install cloudflared');
|
|
76
81
|
}
|
|
77
|
-
({ url, process } = await this._startCloudflared(port));
|
|
78
|
-
break;
|
|
79
|
-
case 'localtunnel':
|
|
80
|
-
default:
|
|
81
|
-
({ url, process } = await this._startLocaltunnel(port));
|
|
82
|
+
({ url, process: tunnelProcess } = await this._startCloudflared(port));
|
|
82
83
|
break;
|
|
83
84
|
}
|
|
84
|
-
// Fetch public IP (needed for localtunnel password)
|
|
85
|
-
const publicIp = await this._getPublicIp();
|
|
86
85
|
const info = {
|
|
87
86
|
provider,
|
|
88
87
|
port,
|
|
89
88
|
url,
|
|
90
|
-
pid:
|
|
89
|
+
pid: tunnelProcess.pid,
|
|
91
90
|
startedAt: new Date(),
|
|
91
|
+
active: true,
|
|
92
92
|
};
|
|
93
|
-
activeTunnels.set(port, { process, info });
|
|
93
|
+
activeTunnels.set(port, { process: tunnelProcess, info });
|
|
94
94
|
return {
|
|
95
|
-
message: `Tunnel started
|
|
95
|
+
message: `Tunnel started via ${provider}`,
|
|
96
96
|
url,
|
|
97
97
|
link: url,
|
|
98
|
-
password: provider === 'localtunnel' ? publicIp : undefined,
|
|
99
|
-
provider,
|
|
100
|
-
port,
|
|
101
98
|
};
|
|
102
99
|
}
|
|
103
100
|
catch (error) {
|
|
@@ -153,63 +150,6 @@ export default class Tunnel {
|
|
|
153
150
|
const result = spawnSync(which, [cmd], { stdio: 'ignore' });
|
|
154
151
|
return result.status === 0;
|
|
155
152
|
}
|
|
156
|
-
async _getPublicIp() {
|
|
157
|
-
// Try multiple services in order
|
|
158
|
-
const services = ['https://api.ipify.org', 'https://ifconfig.me/ip', 'https://icanhazip.com'];
|
|
159
|
-
for (const service of services) {
|
|
160
|
-
try {
|
|
161
|
-
const result = execSync(`curl -s --max-time 3 ${service}`, {
|
|
162
|
-
encoding: 'utf-8',
|
|
163
|
-
timeout: 5000,
|
|
164
|
-
});
|
|
165
|
-
const ip = result.trim();
|
|
166
|
-
if (ip && /^\d+\.\d+\.\d+\.\d+$/.test(ip)) {
|
|
167
|
-
return ip;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
return 'unknown';
|
|
175
|
-
}
|
|
176
|
-
async _startLocaltunnel(port) {
|
|
177
|
-
return new Promise((resolve, reject) => {
|
|
178
|
-
const proc = spawn('npx', ['localtunnel', '--port', String(port)], {
|
|
179
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
180
|
-
});
|
|
181
|
-
let output = '';
|
|
182
|
-
const timeout = setTimeout(() => {
|
|
183
|
-
reject(new Error('Timeout waiting for localtunnel URL'));
|
|
184
|
-
}, 30000);
|
|
185
|
-
proc.stdout?.on('data', (data) => {
|
|
186
|
-
output += data.toString();
|
|
187
|
-
// localtunnel outputs: "your url is: https://xxx.loca.lt"
|
|
188
|
-
const match = output.match(/your url is: (https?:\/\/[^\s]+)/i);
|
|
189
|
-
if (match) {
|
|
190
|
-
clearTimeout(timeout);
|
|
191
|
-
resolve({ url: match[1], process: proc });
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
proc.stderr?.on('data', (data) => {
|
|
195
|
-
const err = data.toString();
|
|
196
|
-
if (err.includes('error')) {
|
|
197
|
-
clearTimeout(timeout);
|
|
198
|
-
reject(new Error(err));
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
proc.on('error', (err) => {
|
|
202
|
-
clearTimeout(timeout);
|
|
203
|
-
reject(err);
|
|
204
|
-
});
|
|
205
|
-
proc.on('close', (code) => {
|
|
206
|
-
if (code !== 0 && !output.includes('your url is')) {
|
|
207
|
-
clearTimeout(timeout);
|
|
208
|
-
reject(new Error(`localtunnel exited with code ${code}`));
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
});
|
|
212
|
-
}
|
|
213
153
|
async _startNgrok(port) {
|
|
214
154
|
return new Promise((resolve, reject) => {
|
|
215
155
|
const proc = spawn('ngrok', ['http', String(port), '--log', 'stdout'], {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tunnel.photon.js","sourceRoot":"","sources":["../../src/photons/tunnel.photon.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"tunnel.photon.js","sourceRoot":"","sources":["../../src/photons/tunnel.photon.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tunnel - Expose local servers to the internet
|
|
3
3
|
* @description Multi-provider tunneling for remote access to Beam and local services
|
|
4
|
-
* @
|
|
4
|
+
* @icon 🌐
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { spawn, spawnSync,
|
|
7
|
+
import { spawn, spawnSync, ChildProcess } from 'child_process';
|
|
8
8
|
|
|
9
9
|
interface TunnelInfo {
|
|
10
10
|
provider: string;
|
|
@@ -12,6 +12,7 @@ interface TunnelInfo {
|
|
|
12
12
|
url: string;
|
|
13
13
|
pid: number;
|
|
14
14
|
startedAt: Date;
|
|
15
|
+
active: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
// Track active tunnels
|
|
@@ -27,26 +28,27 @@ export default class Tunnel {
|
|
|
27
28
|
name: string;
|
|
28
29
|
available: boolean;
|
|
29
30
|
install?: string;
|
|
30
|
-
note?: string;
|
|
31
31
|
}>;
|
|
32
32
|
activeTunnels: TunnelInfo[];
|
|
33
33
|
}> {
|
|
34
|
+
// Check which tunnels are still alive
|
|
35
|
+
for (const [port, tunnel] of activeTunnels) {
|
|
36
|
+
if (tunnel.process.killed || tunnel.process.exitCode !== null) {
|
|
37
|
+
tunnel.info.active = false;
|
|
38
|
+
activeTunnels.delete(port);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
34
42
|
const providers = [
|
|
35
43
|
{
|
|
36
|
-
name: '
|
|
37
|
-
available:
|
|
38
|
-
|
|
44
|
+
name: 'cloudflared',
|
|
45
|
+
available: this._checkCommand('cloudflared'),
|
|
46
|
+
install: 'brew install cloudflared',
|
|
39
47
|
},
|
|
40
48
|
{
|
|
41
49
|
name: 'ngrok',
|
|
42
50
|
available: this._checkCommand('ngrok'),
|
|
43
|
-
install: 'brew install ngrok
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'cloudflared',
|
|
47
|
-
available: this._checkCommand('cloudflared'),
|
|
48
|
-
install:
|
|
49
|
-
'brew install cloudflared OR https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation',
|
|
51
|
+
install: 'brew install ngrok',
|
|
50
52
|
},
|
|
51
53
|
];
|
|
52
54
|
|
|
@@ -59,92 +61,82 @@ export default class Tunnel {
|
|
|
59
61
|
/**
|
|
60
62
|
* Start a tunnel to expose Beam to the internet
|
|
61
63
|
* @icon 🚀
|
|
64
|
+
* @format qr
|
|
62
65
|
* @param provider Tunnel provider to use
|
|
63
66
|
*/
|
|
64
67
|
async *start({
|
|
65
|
-
provider = '
|
|
68
|
+
provider = 'cloudflared',
|
|
66
69
|
}: {
|
|
67
|
-
/** Provider:
|
|
68
|
-
provider?: '
|
|
70
|
+
/** Provider: cloudflared or ngrok */
|
|
71
|
+
provider?: 'cloudflared' | 'ngrok';
|
|
69
72
|
} = {}): AsyncGenerator<
|
|
70
73
|
{ emit: string; value?: any; message: string },
|
|
71
74
|
{
|
|
72
75
|
message: string;
|
|
73
76
|
url: string;
|
|
74
77
|
link: string;
|
|
75
|
-
provider: string;
|
|
76
|
-
port: number;
|
|
77
|
-
password?: string;
|
|
78
78
|
}
|
|
79
79
|
> {
|
|
80
80
|
// Auto-detect Beam port from environment
|
|
81
81
|
const port = parseInt(process.env.BEAM_PORT || '3117', 10);
|
|
82
82
|
|
|
83
83
|
// Check if tunnel already exists for this port
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
const existing = activeTunnels.get(port);
|
|
85
|
+
if (existing) {
|
|
86
|
+
// Verify the process is still alive
|
|
87
|
+
if (!existing.process.killed && existing.process.exitCode === null) {
|
|
88
|
+
return {
|
|
89
|
+
message: `Tunnel already running via ${existing.info.provider}`,
|
|
90
|
+
url: existing.info.url,
|
|
91
|
+
link: existing.info.url,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Process died — clean up and start fresh
|
|
95
|
+
activeTunnels.delete(port);
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
yield {
|
|
98
99
|
emit: 'status',
|
|
99
100
|
value: { step: 'starting' },
|
|
100
|
-
message: `Starting ${provider} tunnel
|
|
101
|
+
message: `Starting ${provider} tunnel on port ${port}...`,
|
|
101
102
|
};
|
|
102
103
|
|
|
103
104
|
try {
|
|
104
105
|
let url: string;
|
|
105
|
-
let
|
|
106
|
+
let tunnelProcess: ChildProcess;
|
|
106
107
|
|
|
107
108
|
switch (provider) {
|
|
108
109
|
case 'ngrok':
|
|
109
110
|
if (!this._checkCommand('ngrok')) {
|
|
110
111
|
throw new Error('ngrok not installed. Run: brew install ngrok');
|
|
111
112
|
}
|
|
112
|
-
({ url, process } = await this._startNgrok(port));
|
|
113
|
+
({ url, process: tunnelProcess } = await this._startNgrok(port));
|
|
113
114
|
break;
|
|
114
115
|
|
|
115
116
|
case 'cloudflared':
|
|
117
|
+
default:
|
|
116
118
|
if (!this._checkCommand('cloudflared')) {
|
|
117
119
|
throw new Error('cloudflared not installed. Run: brew install cloudflared');
|
|
118
120
|
}
|
|
119
|
-
({ url, process } = await this._startCloudflared(port));
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case 'localtunnel':
|
|
123
|
-
default:
|
|
124
|
-
({ url, process } = await this._startLocaltunnel(port));
|
|
121
|
+
({ url, process: tunnelProcess } = await this._startCloudflared(port));
|
|
125
122
|
break;
|
|
126
123
|
}
|
|
127
124
|
|
|
128
|
-
// Fetch public IP (needed for localtunnel password)
|
|
129
|
-
const publicIp = await this._getPublicIp();
|
|
130
|
-
|
|
131
125
|
const info: TunnelInfo = {
|
|
132
126
|
provider,
|
|
133
127
|
port,
|
|
134
128
|
url,
|
|
135
|
-
pid:
|
|
129
|
+
pid: tunnelProcess.pid!,
|
|
136
130
|
startedAt: new Date(),
|
|
131
|
+
active: true,
|
|
137
132
|
};
|
|
138
133
|
|
|
139
|
-
activeTunnels.set(port, { process, info });
|
|
134
|
+
activeTunnels.set(port, { process: tunnelProcess, info });
|
|
140
135
|
|
|
141
136
|
return {
|
|
142
|
-
message: `Tunnel started
|
|
137
|
+
message: `Tunnel started via ${provider}`,
|
|
143
138
|
url,
|
|
144
139
|
link: url,
|
|
145
|
-
password: provider === 'localtunnel' ? publicIp : undefined,
|
|
146
|
-
provider,
|
|
147
|
-
port,
|
|
148
140
|
};
|
|
149
141
|
} catch (error: any) {
|
|
150
142
|
throw new Error(error.message);
|
|
@@ -215,70 +207,6 @@ export default class Tunnel {
|
|
|
215
207
|
return result.status === 0;
|
|
216
208
|
}
|
|
217
209
|
|
|
218
|
-
private async _getPublicIp(): Promise<string> {
|
|
219
|
-
// Try multiple services in order
|
|
220
|
-
const services = ['https://api.ipify.org', 'https://ifconfig.me/ip', 'https://icanhazip.com'];
|
|
221
|
-
|
|
222
|
-
for (const service of services) {
|
|
223
|
-
try {
|
|
224
|
-
const result = execSync(`curl -s --max-time 3 ${service}`, {
|
|
225
|
-
encoding: 'utf-8',
|
|
226
|
-
timeout: 5000,
|
|
227
|
-
});
|
|
228
|
-
const ip = result.trim();
|
|
229
|
-
if (ip && /^\d+\.\d+\.\d+\.\d+$/.test(ip)) {
|
|
230
|
-
return ip;
|
|
231
|
-
}
|
|
232
|
-
} catch {
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return 'unknown';
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private async _startLocaltunnel(port: number): Promise<{ url: string; process: ChildProcess }> {
|
|
240
|
-
return new Promise((resolve, reject) => {
|
|
241
|
-
const proc = spawn('npx', ['localtunnel', '--port', String(port)], {
|
|
242
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
let output = '';
|
|
246
|
-
const timeout = setTimeout(() => {
|
|
247
|
-
reject(new Error('Timeout waiting for localtunnel URL'));
|
|
248
|
-
}, 30000);
|
|
249
|
-
|
|
250
|
-
proc.stdout?.on('data', (data) => {
|
|
251
|
-
output += data.toString();
|
|
252
|
-
// localtunnel outputs: "your url is: https://xxx.loca.lt"
|
|
253
|
-
const match = output.match(/your url is: (https?:\/\/[^\s]+)/i);
|
|
254
|
-
if (match) {
|
|
255
|
-
clearTimeout(timeout);
|
|
256
|
-
resolve({ url: match[1], process: proc });
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
proc.stderr?.on('data', (data) => {
|
|
261
|
-
const err = data.toString();
|
|
262
|
-
if (err.includes('error')) {
|
|
263
|
-
clearTimeout(timeout);
|
|
264
|
-
reject(new Error(err));
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
proc.on('error', (err) => {
|
|
269
|
-
clearTimeout(timeout);
|
|
270
|
-
reject(err);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
proc.on('close', (code) => {
|
|
274
|
-
if (code !== 0 && !output.includes('your url is')) {
|
|
275
|
-
clearTimeout(timeout);
|
|
276
|
-
reject(new Error(`localtunnel exited with code ${code}`));
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
|
|
282
210
|
private async _startNgrok(port: number): Promise<{ url: string; process: ChildProcess }> {
|
|
283
211
|
return new Promise((resolve, reject) => {
|
|
284
212
|
const proc = spawn('ngrok', ['http', String(port), '--log', 'stdout'], {
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG5E,OAAO,EAAgB,MAAM,EAAE,aAAa,EAAY,MAAM,oBAAoB,CAAC;AAiBnF,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,KAAK,CAAC;AAE5C;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,KAAK,CAAC;QAAE,WAAW,EAAE,WAAW,CAAC;QAAC,QAAQ,CAAC,EAAE,cAAc,CAAA;KAAE,CAAC,CAAC;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAkBD,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,GAAG,CAAoC;IAC/C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,gBAAgB,CAAoC;IAC5D,OAAO,CAAC,UAAU,CAAgD;IAClE,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,eAAe,CAAC,CAKtB;IACF,OAAO,CAAC,aAAa,CAAkC;IACvD,OAAO,CAAC,oBAAoB,CAAyB;IACrD,OAAO,CAAC,UAAU,CAAuB;IACzC,mEAAmE;IACnE,OAAO,CAAC,kBAAkB,CAAC,CAAS;IACpC,uEAAuE;IACvE,OAAO,CAAC,gBAAgB,CAA6B;IACrD,kFAAkF;IAClF,OAAO,CAAC,wBAAwB,CAAS;IACzC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,qBAAqB,CAA8C;IAC3E,OAAO,CAAC,aAAa,CAQnB;IACF,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,mBAAmB;IAoEjC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIzC,SAAS,IAAI,MAAM;IAI1B,OAAO,CAAC,GAAG;IAIX;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAK1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAKvB;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;;;;OAKG;IACH,OAAO,CAAC,yBAAyB;IAYjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAgC;IAEzE;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,gBAAgB;IAuBxB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IA6C9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA8HzB;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAW1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,eAAe;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/server.js
CHANGED
|
@@ -464,6 +464,16 @@ export class PhotonServer {
|
|
|
464
464
|
description: `List all available instances.`,
|
|
465
465
|
inputSchema: { type: 'object', properties: {} },
|
|
466
466
|
});
|
|
467
|
+
tools.push({
|
|
468
|
+
name: '_undo',
|
|
469
|
+
description: `Undo the last state mutation. Reverts the most recent tool call's changes.`,
|
|
470
|
+
inputSchema: { type: 'object', properties: {} },
|
|
471
|
+
});
|
|
472
|
+
tools.push({
|
|
473
|
+
name: '_redo',
|
|
474
|
+
description: `Redo the last undone mutation. Re-applies a previously undone change.`,
|
|
475
|
+
inputSchema: { type: 'object', properties: {} },
|
|
476
|
+
});
|
|
467
477
|
}
|
|
468
478
|
return { tools };
|
|
469
479
|
}
|
|
@@ -472,8 +482,12 @@ export class PhotonServer {
|
|
|
472
482
|
throw new Error('MCP not loaded');
|
|
473
483
|
}
|
|
474
484
|
const { name: toolName, arguments: args } = request.params;
|
|
475
|
-
// Route _use
|
|
476
|
-
if (this.daemonName &&
|
|
485
|
+
// Route _use, _instances, _undo, _redo through daemon for stateful photons
|
|
486
|
+
if (this.daemonName &&
|
|
487
|
+
(toolName === '_use' ||
|
|
488
|
+
toolName === '_instances' ||
|
|
489
|
+
toolName === '_undo' ||
|
|
490
|
+
toolName === '_redo')) {
|
|
477
491
|
const { sendCommand } = await import('./daemon/client.js');
|
|
478
492
|
const sendOpts = {
|
|
479
493
|
photonPath: this.options.filePath,
|
|
@@ -1290,6 +1304,10 @@ export class PhotonServer {
|
|
|
1290
1304
|
if (!message || typeof message !== 'object')
|
|
1291
1305
|
return;
|
|
1292
1306
|
const msg = message;
|
|
1307
|
+
// Debug logging for cross-client event transmission
|
|
1308
|
+
if (process.env.PHOTON_DEBUG_EVENTS === '1') {
|
|
1309
|
+
console.error(`[PHOTON-SERVER] Received daemon message on ${String(msg.channel)}: event=${String(msg.event)}`);
|
|
1310
|
+
}
|
|
1293
1311
|
// Use STANDARD notification with embedded photon data
|
|
1294
1312
|
// Claude Desktop will forward this (it's a standard notification)
|
|
1295
1313
|
// Our bridge extracts _photon and routes to the appropriate event handler
|
|
@@ -1306,9 +1324,16 @@ export class PhotonServer {
|
|
|
1306
1324
|
},
|
|
1307
1325
|
};
|
|
1308
1326
|
try {
|
|
1327
|
+
if (process.env.PHOTON_DEBUG_EVENTS === '1') {
|
|
1328
|
+
console.error(`[PHOTON-SERVER] Sending notification to MCP clients...`);
|
|
1329
|
+
}
|
|
1309
1330
|
await this.server.notification(payload);
|
|
1331
|
+
if (process.env.PHOTON_DEBUG_EVENTS === '1') {
|
|
1332
|
+
console.error(`[PHOTON-SERVER] Notification sent successfully`);
|
|
1333
|
+
}
|
|
1310
1334
|
}
|
|
1311
1335
|
catch (e) {
|
|
1336
|
+
console.error(`[PHOTON-SERVER-ERROR] Notification send failed: ${getErrorMessage(e)}`);
|
|
1312
1337
|
this.log('debug', 'Notification send failed', { error: getErrorMessage(e) });
|
|
1313
1338
|
}
|
|
1314
1339
|
// Also send to SSE sessions — snapshot to avoid live-iterator + await issues
|