11#!/usr/bin/env node
22
3+ import crypto from "node:crypto" ;
4+ import os from "node:os" ;
5+
36const 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;
1316const registerIntervalMs = Number (
1417 process . env . SIMDECK_PROVIDER_REGISTER_INTERVAL_MS || 15000 ,
1518) ;
19+ const providerId =
20+ process . env . SIMDECK_STUDIO_PROVIDER_ID || stableLocalProviderId ( ) ;
1621
1722let stopped = false ;
1823let lastRegisterAt = 0 ;
24+ let registered = false ;
1925for ( 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
85101async 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+
96128async function localProviderMetadata ( ) {
97129 try {
98130 const simulators = await localJson ( "/api/simulators" ) ;
@@ -200,3 +232,12 @@ async function fetchJson(url, body) {
200232function 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+ }
0 commit comments