@camstack/core 0.1.34 → 0.1.36

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.
Files changed (30) hide show
  1. package/dist/auth/auth-manager.d.ts +8 -0
  2. package/dist/auth/auth-manager.d.ts.map +1 -1
  3. package/dist/builtins/local-auth/local-auth.addon.d.ts.map +1 -1
  4. package/dist/builtins/local-auth/local-auth.addon.js +26 -5
  5. package/dist/builtins/local-auth/local-auth.addon.js.map +1 -1
  6. package/dist/builtins/local-auth/local-auth.addon.mjs +26 -5
  7. package/dist/builtins/local-auth/local-auth.addon.mjs.map +1 -1
  8. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.d.ts.map +1 -1
  9. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js +32 -1
  10. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.js.map +1 -1
  11. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs +32 -1
  12. package/dist/builtins/mesh-orchestrator/mesh-orchestrator.addon.mjs.map +1 -1
  13. package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts +14 -0
  14. package/dist/builtins/platform-probe/hardware-encoder-probe.d.ts.map +1 -0
  15. package/dist/builtins/platform-probe/index.d.ts +2 -0
  16. package/dist/builtins/platform-probe/index.d.ts.map +1 -1
  17. package/dist/builtins/platform-probe/index.js +198 -5
  18. package/dist/builtins/platform-probe/index.js.map +1 -1
  19. package/dist/builtins/platform-probe/index.mjs +198 -6
  20. package/dist/builtins/platform-probe/index.mjs.map +1 -1
  21. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.d.ts.map +1 -1
  22. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +16 -0
  23. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js.map +1 -1
  24. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +16 -0
  25. package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs.map +1 -1
  26. package/dist/index.js +373 -181
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +373 -181
  29. package/dist/index.mjs.map +1 -1
  30. package/package.json +1 -1
@@ -43,6 +43,12 @@ var RemoteAccessOrchestratorAddon = class extends _camstack_types.BaseAddon {
43
43
  setImmediate(() => {
44
44
  this.autoStartEnabledProviders();
45
45
  });
46
+ this.watchCapability("network-access", { onReady: () => {
47
+ this.autoStartEnabledProviders();
48
+ } });
49
+ this.watchCapability("mesh-network", { onReady: () => {
50
+ this.autoStartEnabledProviders();
51
+ } });
46
52
  return [{
47
53
  capability: _camstack_types.remoteAccessCapability,
48
54
  provider
@@ -62,6 +68,16 @@ var RemoteAccessOrchestratorAddon = class extends _camstack_types.BaseAddon {
62
68
  } });
63
69
  continue;
64
70
  }
71
+ if (impl.getStatus) {
72
+ const status = await impl.getStatus().catch(() => null);
73
+ if (status?.connected) {
74
+ this.ctx.logger.info("autostart: provider already connected — skipping", { meta: {
75
+ addonId,
76
+ url: status.endpoint?.url
77
+ } });
78
+ continue;
79
+ }
80
+ }
65
81
  const endpoint = await impl.start();
66
82
  this.ctx.logger.info("autostart: provider started", { meta: {
67
83
  addonId,
@@ -1 +1 @@
1
- {"version":3,"file":"remote-access-orchestrator.addon.js","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log + skip; the operator can\n // retry from the UI when the addon is ready.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA2CA,IAAa,gCAAb,cAAmD,gBAAA,UAA0C;CAC3F,cAAc;EACZ,MAAM,EAAE,kBAAkB,EAAE,EAAE,CAAC;;CAGjC,MAAgB,eAAgD;EAC9D,MAAM,WAAsC;GAC1C,eAAe,YAAY,KAAK,eAAe;GAC/C,eAAe,OAAO,EAAE,cAAc;IACpC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,CAAC,MAAM,OAAO,MAAM,IAAI,MAAM,2BAA2B,QAAQ,0BAA0B;IAC/F,MAAM,WAAW,MAAM,KAAK,OAAO;IAEnC,MAAM,KAAK,YAAY,SAAS,KAAK;IACrC,OAAO;;GAET,cAAc,OAAO,EAAE,cAAc;IACnC,MAAM,OAAO,KAAK,YAAY,QAAQ;IACtC,IAAI,MAAM,MAAM,MAAM,KAAK,MAAM;IAEjC,MAAM,KAAK,YAAY,SAAS,MAAM;IACtC,OAAO,EAAE,SAAS,MAAe;;GAEpC;EACD,KAAK,IAAI,OAAO,KAAK,0CAA0C,EAC7D,MAAM,EAAE,cAAc,KAAK,OAAO,iBAAiB,QAAQ,EAC5D,CAAC;EAOF,mBAAmB;GAAE,KAAK,2BAA2B;IAAG;EACxD,OAAO,CAAC;GAAE,YAAY,gBAAA;GAAwB;GAAU,CAAC;;CAG3D,MAAc,4BAA2C;EACvD,MAAM,MAAM,KAAK,OAAO;EACxB,IAAI,IAAI,WAAW,GAAG;EACtB,KAAK,IAAI,OAAO,KAAK,iDAAiD,EACpE,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,EAAE,EAC7B,CAAC;EACF,KAAK,MAAM,WAAW,KACpB,IAAI;GACF,MAAM,OAAO,KAAK,YAAY,QAAQ;GACtC,IAAI,CAAC,MAAM,OAAO;IAIhB,KAAK,IAAI,OAAO,KAAK,gDAAgD,EACnE,MAAM;KAAE;KAAS,SAAS,CAAC,CAAC;KAAM,UAAU,CAAC,CAAC,MAAM;KAAO,EAC5D,CAAC;IACF;;GAEF,MAAM,WAAW,MAAM,KAAK,OAAO;GACnC,KAAK,IAAI,OAAO,KAAK,+BAA+B,EAClD,MAAM;IAAE;IAAS,KAAK,SAAS;IAAK,EACrC,CAAC;WACK,KAAK;GACZ,KAAK,IAAI,OAAO,MAAM,oCAAoC,EACxD,MAAM;IACJ;IACA,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,EACF,CAAC;;;CAKR,MAAc,YAAY,SAAiB,SAAiC;EAC1E,MAAM,UAAU,IAAI,IAAI,KAAK,OAAO,iBAAiB;EACrD,MAAM,aAAa,QAAQ,IAAI,QAAQ;EACvC,IAAI,SAAS,QAAQ,IAAI,QAAQ;OAAO,QAAQ,OAAO,QAAQ;EAC/D,IAAI,eAAe,SAAS;EAC5B,MAAM,KAAK,qBAAqB,EAAE,kBAAkB,CAAC,GAAG,QAAQ,EAAE,CAAC;;CAGrE,YAAoB,SAA6E;EAK/F,QAJgB,KAAK,cAAc,qBACjC,iBACD,IAAI,EAAE,EACe,MAAM,CAAC,QAAQ,OAAO,QACrC,GAAQ,MAAM;;CAGvB,MAAc,gBAA8D;EAC1E,MAAM,UAAU,KAAK,cAAc,qBACjC,iBACD,IAAI,EAAE;EACP,MAAM,UAAU,IAAI,IAAI,KAAK,OAAO,iBAAiB;EACrD,MAAM,MAAkC,EAAE;EAC1C,KAAK,MAAM,CAAC,SAAS,SAAS,SAAS;GACrC,IAAI,YAAY;GAChB,IAAI,WAAiD;GACrD,IAAI;GACJ,IAAI,KAAK,WACP,IAAI;IACF,MAAM,IAAI,MAAM,KAAK,WAAW;IAChC,YAAY,EAAE;IACd,WAAW,EAAE;IACb,QAAQ,EAAE;YACH,KAAK;IACZ,QAAQ,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;;GAG5D,IAAI,KAAK;IACP;IACA,aAAa,KAAK,eAAe;IAIjC,SAAS,QAAQ,IAAI,QAAQ;IAC7B;IACA;IACA,GAAI,UAAU,KAAA,IAAY,EAAE,OAAO,GAAG,EAAE;IACzC,CAAC;;EAEJ,OAAO"}
1
+ {"version":3,"file":"remote-access-orchestrator.addon.js","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n\n // Lazy retry — forked providers (cloudflare-tunnel etc) typically\n // register 15-20 s after this orchestrator boots, well past the\n // `setImmediate` above. Hook BaseAddon's `system.ready-state`\n // subscription so we re-run autoStart every time the\n // `network-access` cap transitions to ready (whichever node holds\n // it). The inner logic is idempotent + skips already-connected\n // providers.\n this.watchCapability('network-access', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n // Same watch for `mesh-network`. The tailscale-ingress provider\n // registers `network-access` synchronously at boot, but `start()`\n // throws when the tailnet isn't joined yet — so the boot-time\n // autoStart call fails for tailscale ingresses if the tailscale\n // daemon hadn't logged in by then. Watching `mesh-network` here\n // re-triggers autoStart the moment the client transitions to\n // joined (manual operator login or auto-rejoin), without needing\n // a server restart. Same idempotency rules: providers already\n // connected are skipped.\n this.watchCapability('mesh-network', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log at debug level — the\n // provider-registered subscription below will retry as soon\n // as the addon appears.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n // Idempotent: skip when the provider is already connected.\n // Avoids spamming start() on every provider-registered event\n // and prevents respawning a child process that's already alive.\n if (impl.getStatus) {\n const status = await impl.getStatus().catch(() => null)\n if (status?.connected) {\n this.ctx.logger.info('autostart: provider already connected — skipping', {\n meta: { addonId, url: status.endpoint?.url },\n })\n continue\n }\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":"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"}
@@ -38,6 +38,12 @@ var RemoteAccessOrchestratorAddon = class extends BaseAddon {
38
38
  setImmediate(() => {
39
39
  this.autoStartEnabledProviders();
40
40
  });
41
+ this.watchCapability("network-access", { onReady: () => {
42
+ this.autoStartEnabledProviders();
43
+ } });
44
+ this.watchCapability("mesh-network", { onReady: () => {
45
+ this.autoStartEnabledProviders();
46
+ } });
41
47
  return [{
42
48
  capability: remoteAccessCapability,
43
49
  provider
@@ -57,6 +63,16 @@ var RemoteAccessOrchestratorAddon = class extends BaseAddon {
57
63
  } });
58
64
  continue;
59
65
  }
66
+ if (impl.getStatus) {
67
+ const status = await impl.getStatus().catch(() => null);
68
+ if (status?.connected) {
69
+ this.ctx.logger.info("autostart: provider already connected — skipping", { meta: {
70
+ addonId,
71
+ url: status.endpoint?.url
72
+ } });
73
+ continue;
74
+ }
75
+ }
60
76
  const endpoint = await impl.start();
61
77
  this.ctx.logger.info("autostart: provider started", { meta: {
62
78
  addonId,
@@ -1 +1 @@
1
- {"version":3,"file":"remote-access-orchestrator.addon.mjs","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log + skip; the operator can\n // retry from the UI when the addon is ready.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":"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"}
1
+ {"version":3,"file":"remote-access-orchestrator.addon.mjs","names":[],"sources":["../../../src/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.ts"],"sourcesContent":["/**\n * Remote-access orchestrator — singleton facade over the\n * `network-access` collection (Cloudflare Tunnel, ngrok, Tailscale, …).\n * Mirrors the auth-orchestrator and backup-orchestrator patterns.\n *\n * Persistence + autostart contract:\n * The orchestrator owns the \"operator wants this provider running\"\n * intent — a `enabledProviders: string[]` slice in its addon-store\n * blob (BaseAddon.config). On boot we iterate the list and call\n * `provider.start()` for each enabled entry. `startProvider` /\n * `stopProvider` mutate this list so a Start press persists across\n * restarts. Same shape as turn-orchestrator's setProviderEnabled.\n */\nimport {\n BaseAddon,\n remoteAccessCapability,\n type IRemoteAccessOrchestrator,\n type RemoteAccessProviderInfo,\n type ProviderRegistration,\n} from '@camstack/types'\n\ninterface NetworkAccessLike {\n start?: () => Promise<{ url: string; hostname: string; port: number; protocol: 'http' | 'https' }>\n stop?: () => Promise<void>\n getStatus?: () => Promise<{\n connected: boolean\n endpoint: { url: string; hostname: string; port: number; protocol: 'http' | 'https' } | null\n error?: string\n }>\n}\n\ninterface NetworkAccessRegistrationMeta {\n readonly displayName?: string\n}\n\ninterface RemoteAccessOrchestratorConfig {\n /**\n * addonIds the operator has explicitly Started. Auto-respawned on\n * boot so a tunnel set up once stays up across hub restarts.\n */\n readonly enabledProviders: readonly string[]\n}\n\nexport class RemoteAccessOrchestratorAddon extends BaseAddon<RemoteAccessOrchestratorConfig> {\n constructor() {\n super({ enabledProviders: [] })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const provider: IRemoteAccessOrchestrator = {\n listProviders: async () => this.listProviders(),\n startProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) throw new Error(`Remote-access provider \"${addonId}\" does not support start`)\n const endpoint = await impl.start()\n // Persist intent — next boot will auto-respawn this provider.\n await this.markEnabled(addonId, true)\n return endpoint\n },\n stopProvider: async ({ addonId }) => {\n const impl = this.resolveImpl(addonId)\n if (impl?.stop) await impl.stop()\n // Clear intent — boot must NOT respawn this on next start.\n await this.markEnabled(addonId, false)\n return { success: true as const }\n },\n } satisfies IRemoteAccessOrchestrator\n this.ctx.logger.info('Remote-access orchestrator initialized', {\n meta: { enabledCount: this.config.enabledProviders.length },\n })\n // Defer autostart to next tick so the orchestrator's own provider\n // registration completes first. `resolveImpl` reads from the\n // capabilities registry which only sees in-process / cluster-mirrored\n // providers once they've ALSO registered — small delay gives the\n // cluster bridge time to discover them on cold boot. Errors are\n // logged but never block init.\n setImmediate(() => { this.autoStartEnabledProviders() })\n\n // Lazy retry — forked providers (cloudflare-tunnel etc) typically\n // register 15-20 s after this orchestrator boots, well past the\n // `setImmediate` above. Hook BaseAddon's `system.ready-state`\n // subscription so we re-run autoStart every time the\n // `network-access` cap transitions to ready (whichever node holds\n // it). The inner logic is idempotent + skips already-connected\n // providers.\n this.watchCapability('network-access', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n // Same watch for `mesh-network`. The tailscale-ingress provider\n // registers `network-access` synchronously at boot, but `start()`\n // throws when the tailnet isn't joined yet — so the boot-time\n // autoStart call fails for tailscale ingresses if the tailscale\n // daemon hadn't logged in by then. Watching `mesh-network` here\n // re-triggers autoStart the moment the client transitions to\n // joined (manual operator login or auto-rejoin), without needing\n // a server restart. Same idempotency rules: providers already\n // connected are skipped.\n this.watchCapability('mesh-network', {\n onReady: () => { void this.autoStartEnabledProviders() },\n })\n\n return [{ capability: remoteAccessCapability, provider }]\n }\n\n private async autoStartEnabledProviders(): Promise<void> {\n const ids = this.config.enabledProviders\n if (ids.length === 0) return\n this.ctx.logger.info('Auto-starting enabled remote-access providers', {\n meta: { addonIds: [...ids] },\n })\n for (const addonId of ids) {\n try {\n const impl = this.resolveImpl(addonId)\n if (!impl?.start) {\n // Provider isn't loaded yet (worker bridge still hydrating)\n // OR it doesn't implement start. Log at debug level — the\n // provider-registered subscription below will retry as soon\n // as the addon appears.\n this.ctx.logger.warn('autostart: provider not ready or unsupported', {\n meta: { addonId, hasImpl: !!impl, hasStart: !!impl?.start },\n })\n continue\n }\n // Idempotent: skip when the provider is already connected.\n // Avoids spamming start() on every provider-registered event\n // and prevents respawning a child process that's already alive.\n if (impl.getStatus) {\n const status = await impl.getStatus().catch(() => null)\n if (status?.connected) {\n this.ctx.logger.info('autostart: provider already connected — skipping', {\n meta: { addonId, url: status.endpoint?.url },\n })\n continue\n }\n }\n const endpoint = await impl.start()\n this.ctx.logger.info('autostart: provider started', {\n meta: { addonId, url: endpoint.url },\n })\n } catch (err) {\n this.ctx.logger.error('autostart: provider start failed', {\n meta: {\n addonId,\n error: err instanceof Error ? err.message : String(err),\n },\n })\n }\n }\n }\n\n private async markEnabled(addonId: string, enabled: boolean): Promise<void> {\n const current = new Set(this.config.enabledProviders)\n const wasEnabled = current.has(addonId)\n if (enabled) current.add(addonId); else current.delete(addonId)\n if (wasEnabled === enabled) return\n await this.updateGlobalSettings({ enabledProviders: [...current] })\n }\n\n private resolveImpl(addonId: string): (NetworkAccessLike & NetworkAccessRegistrationMeta) | null {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const found = entries.find(([id]) => id === addonId)\n return found?.[1] ?? null\n }\n\n private async listProviders(): Promise<readonly RemoteAccessProviderInfo[]> {\n const entries = this.capabilities?.getCollectionEntries<NetworkAccessLike & NetworkAccessRegistrationMeta>(\n 'network-access',\n ) ?? []\n const enabled = new Set(this.config.enabledProviders)\n const out: RemoteAccessProviderInfo[] = []\n for (const [addonId, impl] of entries) {\n let connected = false\n let endpoint: RemoteAccessProviderInfo['endpoint'] = null\n let error: string | undefined\n if (impl.getStatus) {\n try {\n const s = await impl.getStatus()\n connected = s.connected\n endpoint = s.endpoint\n error = s.error\n } catch (err) {\n error = err instanceof Error ? err.message : String(err)\n }\n }\n out.push({\n addonId,\n displayName: impl.displayName ?? addonId,\n // `enabled` is now the operator's persisted intent — orthogonal\n // to `connected` (which reflects the live tunnel state). Boot\n // tries to bring enabled→connected automatically.\n enabled: enabled.has(addonId),\n connected,\n endpoint,\n ...(error !== undefined ? { error } : {}),\n })\n }\n return out\n }\n}\n\nexport default RemoteAccessOrchestratorAddon\n"],"mappings":"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"}