Skip to content

Commit 792ee02

Browse files
committed
Reuse local Studio expose sessions
1 parent c76b360 commit 792ee02

2 files changed

Lines changed: 80 additions & 32 deletions

File tree

scripts/studio-provider-bridge.mjs

Lines changed: 73 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#!/usr/bin/env node
22

3+
import crypto from "node:crypto";
4+
import os from "node:os";
5+
36
const cloudUrl = (
47
process.env.SIMDECK_CLOUD_URL || "https://simdeck.djdev.me"
58
).replace(/\/$/, "");
@@ -13,52 +16,64 @@ let localToken = process.env.SIMDECK_LOCAL_TOKEN || providerToken;
1316
const registerIntervalMs = Number(
1417
process.env.SIMDECK_PROVIDER_REGISTER_INTERVAL_MS || 15000,
1518
);
19+
const providerId =
20+
process.env.SIMDECK_STUDIO_PROVIDER_ID || stableLocalProviderId();
1621

1722
let stopped = false;
1823
let lastRegisterAt = 0;
24+
let registered = false;
1925
for (const signal of ["SIGINT", "SIGTERM", "SIGHUP"]) {
2026
process.once(signal, () => {
2127
stopped = true;
2228
});
2329
}
2430

25-
if (!previewId || !providerToken) {
26-
const session = await createLocalProviderSession();
27-
previewId = session.sessionId;
28-
providerToken = session.providerToken;
29-
publicUrl = session.url;
30-
}
31+
try {
32+
if (!previewId || !providerToken) {
33+
const session = await createLocalProviderSession();
34+
previewId = session.sessionId;
35+
providerToken = session.providerToken;
36+
publicUrl = session.url;
37+
}
3138

32-
if (!publicUrl) {
33-
publicUrl = `${cloudUrl}/simulator/${encodeURIComponent(previewId)}`;
34-
}
35-
if (!localToken) {
36-
localToken = providerToken;
37-
}
39+
if (!publicUrl) {
40+
publicUrl = `${cloudUrl}/simulator/${encodeURIComponent(previewId)}`;
41+
}
42+
if (!localToken) {
43+
localToken = providerToken;
44+
}
3845

39-
console.log(`[simdeck-provider-bridge] ${publicUrl}`);
46+
console.log(`[simdeck-provider-bridge] ${publicUrl}`);
4047

41-
await registerProvider();
48+
await registerProvider();
4249

43-
while (!stopped) {
44-
try {
45-
if (Date.now() - lastRegisterAt > registerIntervalMs) {
46-
await registerProvider();
47-
}
48-
const next = await fetchJson(`${cloudUrl}/api/actions/providers/rpc/next`, {
49-
previewId,
50-
providerToken,
51-
});
52-
if (!next || !next.request) {
53-
await sleep(250);
54-
continue;
50+
while (!stopped) {
51+
try {
52+
if (Date.now() - lastRegisterAt > registerIntervalMs) {
53+
await registerProvider();
54+
}
55+
const next = await fetchJson(
56+
`${cloudUrl}/api/actions/providers/rpc/next`,
57+
{
58+
previewId,
59+
providerToken,
60+
},
61+
);
62+
if (!next || !next.request) {
63+
await sleep(250);
64+
continue;
65+
}
66+
await handleRequest(next.request);
67+
} catch (error) {
68+
console.error(
69+
`[simdeck-provider-bridge] ${error instanceof Error ? error.message : String(error)}`,
70+
);
71+
await sleep(1000);
5572
}
56-
await handleRequest(next.request);
57-
} catch (error) {
58-
console.error(
59-
`[simdeck-provider-bridge] ${error instanceof Error ? error.message : String(error)}`,
60-
);
61-
await sleep(1000);
73+
}
74+
} finally {
75+
if (registered) {
76+
await markProviderExpired();
6277
}
6378
}
6479

@@ -74,6 +89,7 @@ async function registerProvider() {
7489
simulatorName: metadata.simulator?.name,
7590
runtimeName: metadata.simulator?.runtimeName,
7691
});
92+
registered = true;
7793
lastRegisterAt = Date.now();
7894
} catch (error) {
7995
console.error(
@@ -84,6 +100,7 @@ async function registerProvider() {
84100

85101
async function createLocalProviderSession() {
86102
const response = await fetchJson(`${cloudUrl}/api/local-provider-sessions`, {
103+
providerId,
87104
simulatorName: process.env.SIMDECK_STUDIO_SIMULATOR_NAME,
88105
runtimeName: process.env.SIMDECK_STUDIO_RUNTIME_NAME,
89106
});
@@ -93,6 +110,21 @@ async function createLocalProviderSession() {
93110
return response;
94111
}
95112

113+
async function markProviderExpired() {
114+
try {
115+
await fetchJson(`${cloudUrl}/api/actions/providers/register`, {
116+
previewId,
117+
providerToken,
118+
baseUrl: publicUrl,
119+
status: "expired",
120+
});
121+
} catch (error) {
122+
console.error(
123+
`[simdeck-provider-bridge] provider expiration failed: ${error instanceof Error ? error.message : String(error)}`,
124+
);
125+
}
126+
}
127+
96128
async function localProviderMetadata() {
97129
try {
98130
const simulators = await localJson("/api/simulators");
@@ -200,3 +232,12 @@ async function fetchJson(url, body) {
200232
function sleep(ms) {
201233
return new Promise((resolve) => setTimeout(resolve, ms));
202234
}
235+
236+
function stableLocalProviderId() {
237+
const fingerprint = [
238+
os.hostname(),
239+
localUrl,
240+
process.env.SIMDECK_STUDIO_SIMULATOR_UDID || "",
241+
].join("\n");
242+
return `local-${crypto.createHash("sha256").update(fingerprint).digest("hex").slice(0, 24)}`;
243+
}

server/src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,6 +1321,13 @@ fn expose_to_studio(options: StudioExposeOptions) -> anyhow::Result<()> {
13211321
.map(|simulator| simulator.name.as_str())
13221322
.unwrap_or("Local Mac simulator"),
13231323
)
1324+
.env(
1325+
"SIMDECK_STUDIO_SIMULATOR_UDID",
1326+
selected
1327+
.as_ref()
1328+
.map(|simulator| simulator.udid.as_str())
1329+
.unwrap_or(""),
1330+
)
13241331
.env(
13251332
"SIMDECK_STUDIO_RUNTIME_NAME",
13261333
selected

0 commit comments

Comments
 (0)