@undefineds.co/xpod 0.3.67 → 0.3.69

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 (47) hide show
  1. package/config/components-ignore.json +1 -0
  2. package/dist/api/ApiServer.d.ts +9 -1
  3. package/dist/api/ApiServer.js +22 -1
  4. package/dist/api/ApiServer.js.map +1 -1
  5. package/dist/api/container/common.js +1 -1
  6. package/dist/api/container/common.js.map +1 -1
  7. package/dist/api/container/routes.js +13 -0
  8. package/dist/api/container/routes.js.map +1 -1
  9. package/dist/api/container/types.d.ts +1 -1
  10. package/dist/api/container/types.js.map +1 -1
  11. package/dist/api/handlers/DeviceNotificationRuntime.d.ts +26 -0
  12. package/dist/api/handlers/DeviceNotificationRuntime.js +77 -0
  13. package/dist/api/handlers/DeviceNotificationRuntime.js.map +1 -0
  14. package/dist/api/handlers/DeviceNotificationTicketHandler.d.ts +50 -0
  15. package/dist/api/handlers/DeviceNotificationTicketHandler.js +130 -0
  16. package/dist/api/handlers/DeviceNotificationTicketHandler.js.map +1 -0
  17. package/dist/api/handlers/DeviceNotificationTicketHandler.jsonld +117 -0
  18. package/dist/api/runs/InngestRunExecutionBackend.d.ts +2 -2
  19. package/dist/api/tasks/InngestTaskScheduler.d.ts +4 -4
  20. package/dist/components/components.jsonld +6 -1
  21. package/dist/components/context.jsonld +122 -0
  22. package/dist/http/DeviceNotificationWebSocketServer.d.ts +29 -0
  23. package/dist/http/DeviceNotificationWebSocketServer.js +138 -0
  24. package/dist/http/DeviceNotificationWebSocketServer.js.map +1 -0
  25. package/dist/http/DeviceNotificationWebSocketServer.jsonld +148 -0
  26. package/dist/index.d.ts +7 -1
  27. package/dist/index.js +13 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/notifications/DeviceNotificationHub.d.ts +74 -0
  30. package/dist/notifications/DeviceNotificationHub.js +313 -0
  31. package/dist/notifications/DeviceNotificationHub.js.map +1 -0
  32. package/dist/notifications/DeviceNotificationHub.jsonld +258 -0
  33. package/dist/notifications/DeviceNotificationResourceListener.d.ts +21 -0
  34. package/dist/notifications/DeviceNotificationResourceListener.js +38 -0
  35. package/dist/notifications/DeviceNotificationResourceListener.js.map +1 -0
  36. package/dist/notifications/DeviceNotificationResourceListener.jsonld +98 -0
  37. package/dist/notifications/device-notification-protocol.d.ts +56 -0
  38. package/dist/notifications/device-notification-protocol.js +127 -0
  39. package/dist/notifications/device-notification-protocol.js.map +1 -0
  40. package/dist/notifications/device-notification-protocol.jsonld +29 -0
  41. package/dist/notifications/index.d.ts +3 -0
  42. package/dist/notifications/index.js +20 -0
  43. package/dist/notifications/index.js.map +1 -0
  44. package/dist/storage/ObservableResourceStore.d.ts +3 -0
  45. package/dist/storage/ObservableResourceStore.js +17 -1
  46. package/dist/storage/ObservableResourceStore.js.map +1 -1
  47. package/package.json +1 -1
@@ -190,7 +190,7 @@ export interface ApiContainerCradle {
190
190
  authenticator: Authenticator;
191
191
  nodeRepo: EdgeNodeRepository;
192
192
  serviceTokenRepo: ServiceTokenRepositoryPort;
193
- gatewayInternalPodAccess: ClientCredentialsInternalPodAccessTokenProvider;
193
+ gatewayInternalPodAccess?: ClientCredentialsInternalPodAccessTokenProvider;
194
194
  gatewayAccessKeyRepository: GatewayAccessKeyRepository;
195
195
  invocationTokenCodec: InvocationTokenCodec;
196
196
  aiConnectionInvocationKeyIssuer: AiConnectionInvocationKeyIssuer;
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/api/container/types.ts"],"names":[],"mappings":";AAAA;;;;GAIG","sourcesContent":["/**\n * API Container 依赖类型定义\n *\n * 定义容器中注册的所有服务接口\n */\n\nimport type { ApiServer } from '../ApiServer';\nimport type { AuthMiddleware } from '../middleware/AuthMiddleware';\nimport type { Authenticator } from '../auth/Authenticator';\nimport type { GatewayAccessKeyRepository } from '../ai-gateway/auth/GatewayApiKeyAuthenticator';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { ServiceTokenRepositoryPort } from '../../identity/drizzle/ServiceTokenRepository';\nimport type { VercelChatService } from '../service/VercelChatService';\nimport type { SubdomainService } from '../../subdomain/SubdomainService';\nimport type { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport type { DnsProvider } from '../../dns/DnsProvider';\nimport type { TunnelProvider } from '../../tunnel/TunnelProvider';\nimport type { ActiveTunnelProvider, TunnelProfile } from '../../tunnel/TunnelProfiles';\nimport type { AcmeCertificateManager } from '../../edge/acme/AcmeCertificateManager';\nimport type { ClusterCertificateManager } from '../../edge/acme/ClusterCertificateManager';\nimport type { EdgeNodeCertificateCapabilityBridge } from '../../edge/EdgeNodeCertificateCapabilityBridge';\nimport type { IdentityDatabase } from '../../identity/drizzle/db';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { PodLookupRepository } from '../../identity/drizzle/PodLookupRepository';\nimport type { AccountRoleRepository } from '../../identity/drizzle/AccountRoleRepository';\nimport type { ChatKitService, AiProvider } from '../chatkit';\nimport type { StoreContext } from '../chatkit/store';\nimport type { PodChatKitStore } from '../chatkit/pod-store';\nimport type { RuntimeHost } from '../../runtime/host/types';\nimport type { ProviderRegistry, EmbeddingService } from '../../ai/service';\nimport type { VectorService } from '../service/VectorService';\nimport type { RdfSearchIndexingService } from '../service/RdfSearchIndexingService';\nimport type { RdfStorageStatsService } from '../service/RdfStorageStatsService';\nimport type { InngestRunExecutionBackend } from '../runs/InngestRunExecutionBackend';\nimport type { RunContextRetriever } from '../runs/RunExecutionBackend';\nimport type { EmbeddedInngestRuntimeConfig } from '../runs/EmbeddedInngestService';\nimport type { RunAuthContextRegistry } from '../runs/RunAuthContextRegistry';\nimport type { TaskAuthBindingService, TaskService, InngestTaskScheduler } from '../tasks';\nimport type { PodMatrixStore } from '../matrix';\nimport type { ClientReconcilerCoordinator, ServerGroupReconcilerService } from '../reconciler';\nimport type { AuthMode } from '../../authorization/AuthMode';\nimport type { RdfEngineLike } from '../../storage/rdf';\nimport type { CredentialVault } from '../ai-gateway/credentials/CredentialVault';\nimport type { ProviderConnectService } from '../ai-gateway/connect';\nimport type { ProviderQuotaService } from '../ai-gateway/quota';\nimport type { AiGatewayService, GatewayCredentialStore } from '../ai-gateway/AiGatewayService';\nimport type { ProviderRuntimeRegistry } from '../ai-gateway/providers/ProviderRuntimeRegistry';\nimport type { ProviderRegistry as GatewayProviderRegistry } from '../ai-gateway/providers/ProviderRegistry';\nimport type { SessionAffinityStore } from '../ai-gateway/routing/SessionAffinityStore';\nimport type { AiConnectionInvocationKeyIssuer } from '../ai-gateway/auth/AiConnectionInvocationKeyIssuer';\nimport type { InvocationTokenCodec } from '../ai-gateway/auth/InvocationTokenCodec';\nimport type { ClientCredentialsInternalPodAccessTokenProvider } from '../ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider';\nimport type { AiClientConfigurationService } from '../service/AiClientConfigurationService';\n\n/**\n * 容器配置\n */\nexport interface ApiContainerConfig {\n /** 运行模式: cloud 持有密钥, local 调用远程 */\n edition: 'cloud' | 'local';\n\n /** API Server 端口 */\n port: number;\n\n /** API Server 主机 */\n host: string;\n\n /** API Server Unix socket 路径 */\n socketPath?: string;\n\n /** Runtime host implementation */\n runtimeHost?: RuntimeHost;\n\n /** Solid authorization mode used by CSS and SP-local Pod provisioning. */\n authMode: AuthMode;\n\n /** RDF term-id index used by CSS LDP structured reads. */\n rdfIndexPath?: string;\n\n /** 数据库连接 URL */\n databaseUrl: string;\n\n /** RDF/SPARQL facts database connection URL. */\n sparqlEndpoint?: string;\n\n /** Route SPARQL reads through an installed native SPARQL provider. */\n rdfNativeSparqlEnabled?: boolean;\n\n /** Redis connection URL, used by embedded infrastructure such as Inngest in cloud mode. */\n redisUrl?: string;\n\n /** Embedded Inngest runtime configuration. */\n inngest?: {\n enabled: boolean;\n mode?: 'managed' | 'spawn';\n port?: number;\n host?: string;\n baseUrl?: string;\n eventKey?: string;\n signingKey?: string;\n binaryPath?: string;\n sqliteDir?: string;\n };\n\n /** Resolved runtime config passed from API bootstrap after starting/locating Inngest. */\n inngestRuntimeConfig?: EmbeddedInngestRuntimeConfig;\n\n /** CORS 允许的源 */\n corsOrigins: string[];\n\n /** CSS Token 端点 */\n cssTokenEndpoint: string;\n solidBaseUrl?: string;\n\n /** Gateway locator encryption secret. Internal platform secret; not a user/provider AI credential. */\n gatewayLocatorSecret?: string;\n gatewayLocatorKeyId?: string;\n gatewayPreviousLocatorSecrets?: Array<{ kid: string; secret: string }>;\n\n /** Internal service client used to read user-owned private Pod gateway-key hashes. */\n gatewayInternalClientId?: string;\n gatewayInternalClientSecret?: string;\n\n /** Runtime-generated secret used only between GatewayProxy and the internal API server for admin ingress evidence. */\n gatewayAdminProxyAuthSecret?: string;\n\n /** AI Provider Connect is disabled by default for backwards-compatible startup. */\n aiGatewayConnectEnabled?: boolean;\n /** Local filesystem host capability for AI coding-client configuration. Disabled unless explicitly injected. */\n aiClientConfiguration?: {\n enabled: boolean;\n homeDir: string;\n backupRoot?: string;\n authority: 'local-filesystem';\n };\n /** Platform signing secret for short-lived provider Connect attempts. */\n aiGatewayConnectSigningSecret?: string;\n /** Xpod/Moonshot-issued Kimi device-code OAuth client id. Never reuse official CLI ids. */\n aiGatewayKimiClientId?: string;\n /** Generic Pod SecretCell credential vault factory, configured by Xpod operations. */\n secretCellCredentialVaultFactory?: () => CredentialVault;\n /** Explicit provider endpoint overrides for controlled deployments and local E2E fixtures. */\n aiGatewayProviderBaseUrls?: Partial<Record<'openai', string>>;\n\n /** 子域名功能配置 (cloud 模式) */\n subdomain?: {\n /** 节点域名根域名 (如 undefineds.site),有值即启用子域名功能 */\n baseStorageDomain?: string;\n cloudflareAccountId?: string;\n cloudflareApiToken?: string;\n tencentDnsSecretId?: string;\n tencentDnsSecretKey?: string;\n };\n\n /** Cloud API 端点 (local 托管式,调用 cloud 的子域名 API) */\n cloudApiEndpoint?: string;\n\n /** 节点 ID (local 托管式) */\n nodeId?: string;\n\n /** 节点 Token (local 托管式,调用 Cloud API 的认证) */\n nodeToken?: string;\n\n /** Local SP service token(Cloud 回调 Local、Local 服务级 API 的唯一服务凭据) */\n serviceToken?: string;\n\n /** 已签发的 provisionCode(Local 首次注册/刷新后持久化恢复) */\n provisionCode?: string;\n\n /** Local canonical public URL(通常来自 CSS_BASE_URL,也可从 setup 恢复) */\n publicUrl?: string;\n\n /** Cloud 分配或用户指定的 SP 域名 */\n spDomain?: string;\n\n /** Local setup/provision 状态文件路径 */\n localSetupPath?: string;\n\n /** Local setup/provision 状态文件中的 provider key */\n localSetupProviderId?: string;\n\n /** OIDC Issuer URL (local 托管式,使用 Cloud IdP) */\n oidcIssuer?: string;\n\n /** Active tunnel provider after resolving profile selection. */\n tunnelProvider?: ActiveTunnelProvider;\n\n /** Recorded tunnel profiles. Only tunnelActiveProfileId/activeTunnelProfile takes effect at runtime. */\n tunnelProfiles?: TunnelProfile[];\n\n /** Selected tunnel profile id. */\n tunnelActiveProfileId?: string;\n\n /** Selected tunnel profile. */\n activeTunnelProfile?: TunnelProfile;\n\n /** Cloudflare Tunnel Token (local 托管式/自管式,启动 cloudflared) */\n cloudflareTunnelToken?: string;\n\n /** SakuraFRP Tunnel Token (SAKURA_TUNNEL_TOKEN;local 托管式/自管式,启动 frpc) */\n sakuraTunnelToken?: string;\n\n /** ngrok authtoken (local only; not sent to Cloud). */\n ngrokAuthToken?: string;\n\n /** Fixed ngrok endpoint/custom domain, e.g. https://example.ngrok-free.dev. */\n ngrokUrl?: string;\n\n /** ngrok executable path. */\n ngrokPath?: string;\n\n /** 是否接受 Edge 节点注册 (cloud 模式) */\n edgeNodesEnabled?: boolean;\n}\n\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\n\n/**\n * 容器中注册的所有服务\n */\nexport interface ApiContainerCradle {\n // 配置\n config: ApiContainerConfig;\n\n // 核心服务\n db: IdentityDatabase;\n apiServer: ApiServer;\n authMiddleware: AuthMiddleware;\n authenticator: Authenticator;\n\n // 仓库\n nodeRepo: EdgeNodeRepository;\n serviceTokenRepo: ServiceTokenRepositoryPort;\n gatewayInternalPodAccess: ClientCredentialsInternalPodAccessTokenProvider;\n gatewayAccessKeyRepository: GatewayAccessKeyRepository;\n invocationTokenCodec: InvocationTokenCodec;\n aiConnectionInvocationKeyIssuer: AiConnectionInvocationKeyIssuer;\n aiClientConfigurationService?: AiClientConfigurationService;\n providerConnectService: ProviderConnectService;\n providerQuotaService?: ProviderQuotaService;\n gatewayProviderRegistry: GatewayProviderRegistry;\n gatewayCredentialStore: GatewayCredentialStore;\n gatewayRuntimeRegistry: ProviderRuntimeRegistry;\n gatewaySessionAffinityStore: SessionAffinityStore;\n aiGatewayService: AiGatewayService;\n\n // 业务服务\n chatService: VercelChatService;\n\n // ChatKit 服务 (OpenAI ChatKit 协议)\n chatKitStore: PodChatKitStore;\n chatKitAiProvider: AiProvider;\n inngestRuntimeConfig: EmbeddedInngestRuntimeConfig | undefined;\n runAuthContextRegistry: RunAuthContextRegistry;\n rdfEngine: RdfEngineLike | undefined;\n runContextRetriever: RunContextRetriever<StoreContext> | undefined;\n rdfSearchIndexingService: RdfSearchIndexingService | undefined;\n runExecutionBackend: InngestRunExecutionBackend;\n taskAuthBindingService: TaskAuthBindingService<StoreContext>;\n taskService: TaskService<StoreContext>;\n inngestTaskScheduler: InngestTaskScheduler<StoreContext>;\n chatKitService: ChatKitService<StoreContext>;\n matrixStore: PodMatrixStore;\n clientReconcilerCoordinator: ClientReconcilerCoordinator;\n serverGroupReconcilerService: ServerGroupReconcilerService;\n providerRegistry: ProviderRegistry;\n embeddingService: EmbeddingService;\n vectorService: VectorService;\n rdfStorageStatsService: RdfStorageStatsService;\n\n // Cloud 模式: 身份服务\n ddnsRepo?: DdnsRepository;\n podLookupRepo?: PodLookupRepository;\n accountRoleRepo?: AccountRoleRepository;\n\n // 子域名相关 (可选,按 edition 注册)\n // Cloud 模式 或 Local 自管模式\n dnsProvider?: DnsProvider;\n dnsCoordinator?: EdgeNodeDnsCoordinator;\n healthProbeService?: EdgeNodeHealthProbeService;\n capabilityDetector?: EdgeNodeCapabilityDetector;\n localNetworkManager?: LocalNetworkManager;\n\n tunnelProvider?: TunnelProvider;\n subdomainService?: SubdomainService;\n // Local 托管式\n subdomainClient?: SubdomainClient;\n // Local 托管式 DDNS 管理\n ddnsManager?: DdnsManager;\n // Local 托管式/自管式 (启动 cloudflared)\n localTunnelProvider?: TunnelProvider;\n\n // Optional runtime certificate managers. Routes only enable TLS status/renewal\n // when one of these services is explicitly registered by the active profile.\n edgeNodeCertificateCapabilityBridge?: EdgeNodeCertificateCapabilityBridge;\n certificateManager?: unknown;\n acmeCertificateManager?: AcmeCertificateManager;\n clusterCertificateManager?: ClusterCertificateManager;\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/api/container/types.ts"],"names":[],"mappings":";AAAA;;;;GAIG","sourcesContent":["/**\n * API Container 依赖类型定义\n *\n * 定义容器中注册的所有服务接口\n */\n\nimport type { ApiServer } from '../ApiServer';\nimport type { AuthMiddleware } from '../middleware/AuthMiddleware';\nimport type { Authenticator } from '../auth/Authenticator';\nimport type { GatewayAccessKeyRepository } from '../ai-gateway/auth/GatewayApiKeyAuthenticator';\nimport type { EdgeNodeRepository } from '../../identity/drizzle/EdgeNodeRepository';\nimport type { ServiceTokenRepositoryPort } from '../../identity/drizzle/ServiceTokenRepository';\nimport type { VercelChatService } from '../service/VercelChatService';\nimport type { SubdomainService } from '../../subdomain/SubdomainService';\nimport type { SubdomainClient } from '../../subdomain/SubdomainClient';\nimport type { DnsProvider } from '../../dns/DnsProvider';\nimport type { TunnelProvider } from '../../tunnel/TunnelProvider';\nimport type { ActiveTunnelProvider, TunnelProfile } from '../../tunnel/TunnelProfiles';\nimport type { AcmeCertificateManager } from '../../edge/acme/AcmeCertificateManager';\nimport type { ClusterCertificateManager } from '../../edge/acme/ClusterCertificateManager';\nimport type { EdgeNodeCertificateCapabilityBridge } from '../../edge/EdgeNodeCertificateCapabilityBridge';\nimport type { IdentityDatabase } from '../../identity/drizzle/db';\nimport type { DdnsRepository } from '../../identity/drizzle/DdnsRepository';\nimport type { PodLookupRepository } from '../../identity/drizzle/PodLookupRepository';\nimport type { AccountRoleRepository } from '../../identity/drizzle/AccountRoleRepository';\nimport type { ChatKitService, AiProvider } from '../chatkit';\nimport type { StoreContext } from '../chatkit/store';\nimport type { PodChatKitStore } from '../chatkit/pod-store';\nimport type { RuntimeHost } from '../../runtime/host/types';\nimport type { ProviderRegistry, EmbeddingService } from '../../ai/service';\nimport type { VectorService } from '../service/VectorService';\nimport type { RdfSearchIndexingService } from '../service/RdfSearchIndexingService';\nimport type { RdfStorageStatsService } from '../service/RdfStorageStatsService';\nimport type { InngestRunExecutionBackend } from '../runs/InngestRunExecutionBackend';\nimport type { RunContextRetriever } from '../runs/RunExecutionBackend';\nimport type { EmbeddedInngestRuntimeConfig } from '../runs/EmbeddedInngestService';\nimport type { RunAuthContextRegistry } from '../runs/RunAuthContextRegistry';\nimport type { TaskAuthBindingService, TaskService, InngestTaskScheduler } from '../tasks';\nimport type { PodMatrixStore } from '../matrix';\nimport type { ClientReconcilerCoordinator, ServerGroupReconcilerService } from '../reconciler';\nimport type { AuthMode } from '../../authorization/AuthMode';\nimport type { RdfEngineLike } from '../../storage/rdf';\nimport type { CredentialVault } from '../ai-gateway/credentials/CredentialVault';\nimport type { ProviderConnectService } from '../ai-gateway/connect';\nimport type { ProviderQuotaService } from '../ai-gateway/quota';\nimport type { AiGatewayService, GatewayCredentialStore } from '../ai-gateway/AiGatewayService';\nimport type { ProviderRuntimeRegistry } from '../ai-gateway/providers/ProviderRuntimeRegistry';\nimport type { ProviderRegistry as GatewayProviderRegistry } from '../ai-gateway/providers/ProviderRegistry';\nimport type { SessionAffinityStore } from '../ai-gateway/routing/SessionAffinityStore';\nimport type { AiConnectionInvocationKeyIssuer } from '../ai-gateway/auth/AiConnectionInvocationKeyIssuer';\nimport type { InvocationTokenCodec } from '../ai-gateway/auth/InvocationTokenCodec';\nimport type { ClientCredentialsInternalPodAccessTokenProvider } from '../ai-gateway/auth/ClientCredentialsInternalPodAccessTokenProvider';\nimport type { AiClientConfigurationService } from '../service/AiClientConfigurationService';\n\n/**\n * 容器配置\n */\nexport interface ApiContainerConfig {\n /** 运行模式: cloud 持有密钥, local 调用远程 */\n edition: 'cloud' | 'local';\n\n /** API Server 端口 */\n port: number;\n\n /** API Server 主机 */\n host: string;\n\n /** API Server Unix socket 路径 */\n socketPath?: string;\n\n /** Runtime host implementation */\n runtimeHost?: RuntimeHost;\n\n /** Solid authorization mode used by CSS and SP-local Pod provisioning. */\n authMode: AuthMode;\n\n /** RDF term-id index used by CSS LDP structured reads. */\n rdfIndexPath?: string;\n\n /** 数据库连接 URL */\n databaseUrl: string;\n\n /** RDF/SPARQL facts database connection URL. */\n sparqlEndpoint?: string;\n\n /** Route SPARQL reads through an installed native SPARQL provider. */\n rdfNativeSparqlEnabled?: boolean;\n\n /** Redis connection URL, used by embedded infrastructure such as Inngest in cloud mode. */\n redisUrl?: string;\n\n /** Embedded Inngest runtime configuration. */\n inngest?: {\n enabled: boolean;\n mode?: 'managed' | 'spawn';\n port?: number;\n host?: string;\n baseUrl?: string;\n eventKey?: string;\n signingKey?: string;\n binaryPath?: string;\n sqliteDir?: string;\n };\n\n /** Resolved runtime config passed from API bootstrap after starting/locating Inngest. */\n inngestRuntimeConfig?: EmbeddedInngestRuntimeConfig;\n\n /** CORS 允许的源 */\n corsOrigins: string[];\n\n /** CSS Token 端点 */\n cssTokenEndpoint: string;\n solidBaseUrl?: string;\n\n /** Gateway locator encryption secret. Internal platform secret; not a user/provider AI credential. */\n gatewayLocatorSecret?: string;\n gatewayLocatorKeyId?: string;\n gatewayPreviousLocatorSecrets?: Array<{ kid: string; secret: string }>;\n\n /** Internal service client used to read user-owned private Pod gateway-key hashes. */\n gatewayInternalClientId?: string;\n gatewayInternalClientSecret?: string;\n\n /** Runtime-generated secret used only between GatewayProxy and the internal API server for admin ingress evidence. */\n gatewayAdminProxyAuthSecret?: string;\n\n /** AI Provider Connect is disabled by default for backwards-compatible startup. */\n aiGatewayConnectEnabled?: boolean;\n /** Local filesystem host capability for AI coding-client configuration. Disabled unless explicitly injected. */\n aiClientConfiguration?: {\n enabled: boolean;\n homeDir: string;\n backupRoot?: string;\n authority: 'local-filesystem';\n };\n /** Platform signing secret for short-lived provider Connect attempts. */\n aiGatewayConnectSigningSecret?: string;\n /** Xpod/Moonshot-issued Kimi device-code OAuth client id. Never reuse official CLI ids. */\n aiGatewayKimiClientId?: string;\n /** Generic Pod SecretCell credential vault factory, configured by Xpod operations. */\n secretCellCredentialVaultFactory?: () => CredentialVault;\n /** Explicit provider endpoint overrides for controlled deployments and local E2E fixtures. */\n aiGatewayProviderBaseUrls?: Partial<Record<'openai', string>>;\n\n /** 子域名功能配置 (cloud 模式) */\n subdomain?: {\n /** 节点域名根域名 (如 undefineds.site),有值即启用子域名功能 */\n baseStorageDomain?: string;\n cloudflareAccountId?: string;\n cloudflareApiToken?: string;\n tencentDnsSecretId?: string;\n tencentDnsSecretKey?: string;\n };\n\n /** Cloud API 端点 (local 托管式,调用 cloud 的子域名 API) */\n cloudApiEndpoint?: string;\n\n /** 节点 ID (local 托管式) */\n nodeId?: string;\n\n /** 节点 Token (local 托管式,调用 Cloud API 的认证) */\n nodeToken?: string;\n\n /** Local SP service token(Cloud 回调 Local、Local 服务级 API 的唯一服务凭据) */\n serviceToken?: string;\n\n /** 已签发的 provisionCode(Local 首次注册/刷新后持久化恢复) */\n provisionCode?: string;\n\n /** Local canonical public URL(通常来自 CSS_BASE_URL,也可从 setup 恢复) */\n publicUrl?: string;\n\n /** Cloud 分配或用户指定的 SP 域名 */\n spDomain?: string;\n\n /** Local setup/provision 状态文件路径 */\n localSetupPath?: string;\n\n /** Local setup/provision 状态文件中的 provider key */\n localSetupProviderId?: string;\n\n /** OIDC Issuer URL (local 托管式,使用 Cloud IdP) */\n oidcIssuer?: string;\n\n /** Active tunnel provider after resolving profile selection. */\n tunnelProvider?: ActiveTunnelProvider;\n\n /** Recorded tunnel profiles. Only tunnelActiveProfileId/activeTunnelProfile takes effect at runtime. */\n tunnelProfiles?: TunnelProfile[];\n\n /** Selected tunnel profile id. */\n tunnelActiveProfileId?: string;\n\n /** Selected tunnel profile. */\n activeTunnelProfile?: TunnelProfile;\n\n /** Cloudflare Tunnel Token (local 托管式/自管式,启动 cloudflared) */\n cloudflareTunnelToken?: string;\n\n /** SakuraFRP Tunnel Token (SAKURA_TUNNEL_TOKEN;local 托管式/自管式,启动 frpc) */\n sakuraTunnelToken?: string;\n\n /** ngrok authtoken (local only; not sent to Cloud). */\n ngrokAuthToken?: string;\n\n /** Fixed ngrok endpoint/custom domain, e.g. https://example.ngrok-free.dev. */\n ngrokUrl?: string;\n\n /** ngrok executable path. */\n ngrokPath?: string;\n\n /** 是否接受 Edge 节点注册 (cloud 模式) */\n edgeNodesEnabled?: boolean;\n}\n\nimport { EdgeNodeDnsCoordinator } from '../../edge/EdgeNodeDnsCoordinator';\nimport { EdgeNodeHealthProbeService } from '../../edge/EdgeNodeHealthProbeService';\nimport { EdgeNodeCapabilityDetector } from '../../edge/EdgeNodeCapabilityDetector';\nimport { LocalNetworkManager } from '../../edge/LocalNetworkManager';\nimport { DdnsManager } from '../../edge/DdnsManager';\n\n/**\n * 容器中注册的所有服务\n */\nexport interface ApiContainerCradle {\n // 配置\n config: ApiContainerConfig;\n\n // 核心服务\n db: IdentityDatabase;\n apiServer: ApiServer;\n authMiddleware: AuthMiddleware;\n authenticator: Authenticator;\n\n // 仓库\n nodeRepo: EdgeNodeRepository;\n serviceTokenRepo: ServiceTokenRepositoryPort;\n gatewayInternalPodAccess?: ClientCredentialsInternalPodAccessTokenProvider;\n gatewayAccessKeyRepository: GatewayAccessKeyRepository;\n invocationTokenCodec: InvocationTokenCodec;\n aiConnectionInvocationKeyIssuer: AiConnectionInvocationKeyIssuer;\n aiClientConfigurationService?: AiClientConfigurationService;\n providerConnectService: ProviderConnectService;\n providerQuotaService?: ProviderQuotaService;\n gatewayProviderRegistry: GatewayProviderRegistry;\n gatewayCredentialStore: GatewayCredentialStore;\n gatewayRuntimeRegistry: ProviderRuntimeRegistry;\n gatewaySessionAffinityStore: SessionAffinityStore;\n aiGatewayService: AiGatewayService;\n\n // 业务服务\n chatService: VercelChatService;\n\n // ChatKit 服务 (OpenAI ChatKit 协议)\n chatKitStore: PodChatKitStore;\n chatKitAiProvider: AiProvider;\n inngestRuntimeConfig: EmbeddedInngestRuntimeConfig | undefined;\n runAuthContextRegistry: RunAuthContextRegistry;\n rdfEngine: RdfEngineLike | undefined;\n runContextRetriever: RunContextRetriever<StoreContext> | undefined;\n rdfSearchIndexingService: RdfSearchIndexingService | undefined;\n runExecutionBackend: InngestRunExecutionBackend;\n taskAuthBindingService: TaskAuthBindingService<StoreContext>;\n taskService: TaskService<StoreContext>;\n inngestTaskScheduler: InngestTaskScheduler<StoreContext>;\n chatKitService: ChatKitService<StoreContext>;\n matrixStore: PodMatrixStore;\n clientReconcilerCoordinator: ClientReconcilerCoordinator;\n serverGroupReconcilerService: ServerGroupReconcilerService;\n providerRegistry: ProviderRegistry;\n embeddingService: EmbeddingService;\n vectorService: VectorService;\n rdfStorageStatsService: RdfStorageStatsService;\n\n // Cloud 模式: 身份服务\n ddnsRepo?: DdnsRepository;\n podLookupRepo?: PodLookupRepository;\n accountRoleRepo?: AccountRoleRepository;\n\n // 子域名相关 (可选,按 edition 注册)\n // Cloud 模式 或 Local 自管模式\n dnsProvider?: DnsProvider;\n dnsCoordinator?: EdgeNodeDnsCoordinator;\n healthProbeService?: EdgeNodeHealthProbeService;\n capabilityDetector?: EdgeNodeCapabilityDetector;\n localNetworkManager?: LocalNetworkManager;\n\n tunnelProvider?: TunnelProvider;\n subdomainService?: SubdomainService;\n // Local 托管式\n subdomainClient?: SubdomainClient;\n // Local 托管式 DDNS 管理\n ddnsManager?: DdnsManager;\n // Local 托管式/自管式 (启动 cloudflared)\n localTunnelProvider?: TunnelProvider;\n\n // Optional runtime certificate managers. Routes only enable TLS status/renewal\n // when one of these services is explicitly registered by the active profile.\n edgeNodeCertificateCapabilityBridge?: EdgeNodeCertificateCapabilityBridge;\n certificateManager?: unknown;\n acmeCertificateManager?: AcmeCertificateManager;\n clusterCertificateManager?: ClusterCertificateManager;\n}\n"]}
@@ -0,0 +1,26 @@
1
+ import type { ApiServer } from '../ApiServer';
2
+ import { DeviceNotificationTicketHandler } from './DeviceNotificationTicketHandler';
3
+ import { DeviceNotificationWebSocketServer } from '../../http/DeviceNotificationWebSocketServer';
4
+ import { DeviceNotificationHub } from '../../notifications/DeviceNotificationHub';
5
+ import { DeviceNotificationResourceListener } from '../../notifications/DeviceNotificationResourceListener';
6
+ import { XPOD_NOTIFICATIONS_PROTOCOL } from '../../notifications/device-notification-protocol';
7
+ export interface DeviceNotificationRuntimeOptions {
8
+ origin: string;
9
+ ticketEndpoint?: string;
10
+ webSocketEndpoint?: string;
11
+ ticketTtlMs?: number;
12
+ authorizeTopic?: ConstructorParameters<typeof DeviceNotificationHub>[0]['authorizeTopic'];
13
+ }
14
+ export interface DeviceNotificationRuntimeHandle {
15
+ hub: DeviceNotificationHub;
16
+ ticketHandler: DeviceNotificationTicketHandler;
17
+ webSocketServer: DeviceNotificationWebSocketServer;
18
+ resourceListener: DeviceNotificationResourceListener;
19
+ descriptor: {
20
+ protocol: typeof XPOD_NOTIFICATIONS_PROTOCOL;
21
+ ticketEndpoint: string;
22
+ webSocketEndpoint: string;
23
+ };
24
+ stop(): void;
25
+ }
26
+ export declare function registerDeviceNotificationRuntime(server: ApiServer, options: DeviceNotificationRuntimeOptions): DeviceNotificationRuntimeHandle;
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerDeviceNotificationRuntime = registerDeviceNotificationRuntime;
4
+ const DeviceNotificationTicketHandler_1 = require("./DeviceNotificationTicketHandler");
5
+ const DeviceNotificationWebSocketServer_1 = require("../../http/DeviceNotificationWebSocketServer");
6
+ const DeviceNotificationHub_1 = require("../../notifications/DeviceNotificationHub");
7
+ const DeviceNotificationResourceListener_1 = require("../../notifications/DeviceNotificationResourceListener");
8
+ const ObservableResourceStore_1 = require("../../storage/ObservableResourceStore");
9
+ const device_notification_protocol_1 = require("../../notifications/device-notification-protocol");
10
+ function registerDeviceNotificationRuntime(server, options) {
11
+ const ticketEndpoint = options.ticketEndpoint ?? '/v1/notifications/tickets';
12
+ const webSocketEndpoint = options.webSocketEndpoint ?? '/v1/notifications/ws';
13
+ const descriptor = {
14
+ protocol: device_notification_protocol_1.XPOD_NOTIFICATIONS_PROTOCOL,
15
+ ticketEndpoint,
16
+ webSocketEndpoint,
17
+ };
18
+ const hub = new DeviceNotificationHub_1.DeviceNotificationHub({
19
+ origin: options.origin,
20
+ authorizeTopic: options.authorizeTopic,
21
+ });
22
+ const ticketStore = new DeviceNotificationTicketHandler_1.DeviceNotificationTicketStore();
23
+ const ticketHandler = new DeviceNotificationTicketHandler_1.DeviceNotificationTicketHandler({
24
+ origin: options.origin,
25
+ webSocketEndpoint,
26
+ ticketTtlMs: options.ticketTtlMs,
27
+ ticketStore,
28
+ });
29
+ const webSocketServer = new DeviceNotificationWebSocketServer_1.DeviceNotificationWebSocketServer({
30
+ hub,
31
+ ticketStore,
32
+ path: webSocketEndpoint,
33
+ });
34
+ const resourceListener = new DeviceNotificationResourceListener_1.DeviceNotificationResourceListener({
35
+ origin: options.origin,
36
+ hub,
37
+ });
38
+ server.addResponseHeaders({
39
+ 'X-Xpod-Notifications': JSON.stringify(descriptor),
40
+ });
41
+ server.post(ticketEndpoint, async (request, response) => {
42
+ await ticketHandler.handle(request, response);
43
+ });
44
+ server.get('/.well-known/xpod-notifications', async (_request, response) => {
45
+ sendJson(response, 200, descriptor);
46
+ }, { public: true });
47
+ server.addUpgradeHandler((request, socket, head) => {
48
+ webSocketServer.handleUpgrade(request, socket, head);
49
+ });
50
+ ObservableResourceStore_1.ObservableResourceStore.addGlobalListener(resourceListener);
51
+ server.addShutdownHandler(() => {
52
+ webSocketServer.stop();
53
+ ticketStore.clear();
54
+ ObservableResourceStore_1.ObservableResourceStore.removeGlobalListener(resourceListener);
55
+ });
56
+ return {
57
+ hub,
58
+ ticketHandler,
59
+ webSocketServer,
60
+ resourceListener,
61
+ descriptor,
62
+ stop: () => {
63
+ webSocketServer.stop();
64
+ ticketStore.clear();
65
+ ObservableResourceStore_1.ObservableResourceStore.removeGlobalListener(resourceListener);
66
+ },
67
+ };
68
+ }
69
+ function sendJson(response, status, body) {
70
+ const encoded = JSON.stringify(body);
71
+ response.writeHead(status, {
72
+ 'Content-Type': 'application/json',
73
+ 'Content-Length': Buffer.byteLength(encoded),
74
+ });
75
+ response.end(encoded);
76
+ }
77
+ //# sourceMappingURL=DeviceNotificationRuntime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceNotificationRuntime.js","sourceRoot":"","sources":["../../../src/api/handlers/DeviceNotificationRuntime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ServerResponse } from 'node:http';\nimport type { ApiServer } from '../ApiServer';\nimport { DeviceNotificationTicketHandler, DeviceNotificationTicketStore } from './DeviceNotificationTicketHandler';\nimport { DeviceNotificationWebSocketServer } from '../../http/DeviceNotificationWebSocketServer';\nimport { DeviceNotificationHub } from '../../notifications/DeviceNotificationHub';\nimport { DeviceNotificationResourceListener } from '../../notifications/DeviceNotificationResourceListener';\nimport { ObservableResourceStore } from '../../storage/ObservableResourceStore';\nimport { XPOD_NOTIFICATIONS_PROTOCOL } from '../../notifications/device-notification-protocol';\nimport type { AuthenticatedRequest } from '../middleware/AuthMiddleware';\n\nexport interface DeviceNotificationRuntimeOptions {\n origin: string;\n ticketEndpoint?: string;\n webSocketEndpoint?: string;\n ticketTtlMs?: number;\n authorizeTopic?: ConstructorParameters<typeof DeviceNotificationHub>[0]['authorizeTopic'];\n}\n\nexport interface DeviceNotificationRuntimeHandle {\n hub: DeviceNotificationHub;\n ticketHandler: DeviceNotificationTicketHandler;\n webSocketServer: DeviceNotificationWebSocketServer;\n resourceListener: DeviceNotificationResourceListener;\n descriptor: {\n protocol: typeof XPOD_NOTIFICATIONS_PROTOCOL;\n ticketEndpoint: string;\n webSocketEndpoint: string;\n };\n stop(): void;\n}\n\nexport function registerDeviceNotificationRuntime(\n server: ApiServer,\n options: DeviceNotificationRuntimeOptions,\n): DeviceNotificationRuntimeHandle {\n const ticketEndpoint = options.ticketEndpoint ?? '/v1/notifications/tickets';\n const webSocketEndpoint = options.webSocketEndpoint ?? '/v1/notifications/ws';\n const descriptor: DeviceNotificationRuntimeHandle['descriptor'] = {\n protocol: XPOD_NOTIFICATIONS_PROTOCOL,\n ticketEndpoint,\n webSocketEndpoint,\n };\n const hub = new DeviceNotificationHub({\n origin: options.origin,\n authorizeTopic: options.authorizeTopic,\n });\n const ticketStore = new DeviceNotificationTicketStore();\n const ticketHandler = new DeviceNotificationTicketHandler({\n origin: options.origin,\n webSocketEndpoint,\n ticketTtlMs: options.ticketTtlMs,\n ticketStore,\n });\n const webSocketServer = new DeviceNotificationWebSocketServer({\n hub,\n ticketStore,\n path: webSocketEndpoint,\n });\n const resourceListener = new DeviceNotificationResourceListener({\n origin: options.origin,\n hub,\n });\n\n server.addResponseHeaders({\n 'X-Xpod-Notifications': JSON.stringify(descriptor),\n });\n server.post(ticketEndpoint, async (request, response) => {\n await ticketHandler.handle(request as AuthenticatedRequest, response);\n });\n server.get('/.well-known/xpod-notifications', async (_request, response) => {\n sendJson(response, 200, descriptor);\n }, { public: true });\n server.addUpgradeHandler((request, socket, head) => {\n webSocketServer.handleUpgrade(request, socket, head);\n });\n ObservableResourceStore.addGlobalListener(resourceListener);\n server.addShutdownHandler(() => {\n webSocketServer.stop();\n ticketStore.clear();\n ObservableResourceStore.removeGlobalListener(resourceListener);\n });\n\n return {\n hub,\n ticketHandler,\n webSocketServer,\n resourceListener,\n descriptor,\n stop: () => {\n webSocketServer.stop();\n ticketStore.clear();\n ObservableResourceStore.removeGlobalListener(resourceListener);\n },\n };\n}\n\nfunction sendJson(response: ServerResponse, status: number, body: unknown): void {\n const encoded = JSON.stringify(body);\n response.writeHead(status, {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(encoded),\n });\n response.end(encoded);\n}\n"]}
@@ -0,0 +1,50 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { AuthContext } from '../auth/AuthContext';
3
+ export interface DeviceNotificationIdentity {
4
+ webId: string;
5
+ localPart: string;
6
+ }
7
+ export interface DeviceNotificationTicketRecord {
8
+ digest: string;
9
+ identity: DeviceNotificationIdentity;
10
+ deviceSessionId: string;
11
+ origin: string;
12
+ expiresAt: number;
13
+ }
14
+ export declare class DeviceNotificationTicketStore {
15
+ private readonly records;
16
+ mint(input: {
17
+ identity: DeviceNotificationIdentity;
18
+ deviceSessionId: string;
19
+ origin: string;
20
+ ttlMs: number;
21
+ }): string;
22
+ consume(ticket: string): DeviceNotificationTicketRecord | undefined;
23
+ size(): number;
24
+ clear(): void;
25
+ private digest;
26
+ }
27
+ export interface DeviceNotificationTicketHandlerOptions {
28
+ webSocketEndpoint: string;
29
+ origin: string;
30
+ ticketTtlMs?: number;
31
+ ticketStore?: DeviceNotificationTicketStore;
32
+ }
33
+ type TicketRequest = IncomingMessage & {
34
+ auth?: AuthContext | {
35
+ webId?: string;
36
+ localPart?: string;
37
+ };
38
+ };
39
+ export declare class DeviceNotificationTicketHandler {
40
+ readonly ticketStore: DeviceNotificationTicketStore;
41
+ private readonly webSocketEndpoint;
42
+ private readonly origin;
43
+ private readonly ticketTtlMs;
44
+ constructor(options: DeviceNotificationTicketHandlerOptions);
45
+ handle(request: TicketRequest, response: ServerResponse): Promise<void>;
46
+ private extractIdentity;
47
+ private readJson;
48
+ private sendJson;
49
+ }
50
+ export {};
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DeviceNotificationTicketHandler = exports.DeviceNotificationTicketStore = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ class DeviceNotificationTicketStore {
6
+ constructor() {
7
+ this.records = new Map();
8
+ }
9
+ mint(input) {
10
+ const ticket = (0, node_crypto_1.randomBytes)(32).toString('base64url');
11
+ this.records.set(this.digest(ticket), {
12
+ digest: this.digest(ticket),
13
+ identity: input.identity,
14
+ deviceSessionId: input.deviceSessionId,
15
+ origin: new URL(input.origin).origin,
16
+ expiresAt: Date.now() + input.ttlMs,
17
+ });
18
+ return ticket;
19
+ }
20
+ consume(ticket) {
21
+ const digest = this.digest(ticket);
22
+ const record = this.records.get(digest);
23
+ this.records.delete(digest);
24
+ if (!record || record.expiresAt < Date.now()) {
25
+ return undefined;
26
+ }
27
+ return record;
28
+ }
29
+ size() {
30
+ return this.records.size;
31
+ }
32
+ clear() {
33
+ this.records.clear();
34
+ }
35
+ digest(ticket) {
36
+ return (0, node_crypto_1.createHash)('sha256').update(ticket).digest('hex');
37
+ }
38
+ }
39
+ exports.DeviceNotificationTicketStore = DeviceNotificationTicketStore;
40
+ class DeviceNotificationTicketHandler {
41
+ constructor(options) {
42
+ this.webSocketEndpoint = options.webSocketEndpoint;
43
+ this.origin = new URL(options.origin).origin;
44
+ this.ticketTtlMs = options.ticketTtlMs ?? 60_000;
45
+ this.ticketStore = options.ticketStore ?? new DeviceNotificationTicketStore();
46
+ }
47
+ async handle(request, response) {
48
+ const identity = this.extractIdentity(request);
49
+ if (!identity) {
50
+ this.sendJson(response, 401, { error: 'Unauthorized' });
51
+ return;
52
+ }
53
+ const body = await this.readJson(request);
54
+ const deviceSessionId = typeof body.deviceSessionId === 'string'
55
+ ? body.deviceSessionId
56
+ : typeof body.sessionId === 'string'
57
+ ? body.sessionId
58
+ : undefined;
59
+ const origin = typeof body.origin === 'string' ? body.origin : undefined;
60
+ if (!deviceSessionId || !origin) {
61
+ this.sendJson(response, 400, { error: 'deviceSessionId and origin are required' });
62
+ return;
63
+ }
64
+ try {
65
+ if (new URL(origin).origin !== this.origin) {
66
+ this.sendJson(response, 400, { error: 'origin must match the notification service origin' });
67
+ return;
68
+ }
69
+ }
70
+ catch {
71
+ this.sendJson(response, 400, { error: 'origin must be a valid URL' });
72
+ return;
73
+ }
74
+ const ticket = this.ticketStore.mint({
75
+ identity,
76
+ deviceSessionId,
77
+ origin: this.origin,
78
+ ttlMs: this.ticketTtlMs,
79
+ });
80
+ this.sendJson(response, 201, {
81
+ protocol: 'xpod.notifications.v1',
82
+ ticket,
83
+ webSocketEndpoint: this.webSocketEndpoint,
84
+ expiresInMs: this.ticketTtlMs,
85
+ });
86
+ }
87
+ extractIdentity(request) {
88
+ const auth = request.auth;
89
+ const webId = auth && 'webId' in auth && typeof auth.webId === 'string' ? auth.webId : undefined;
90
+ if (!webId) {
91
+ return undefined;
92
+ }
93
+ return {
94
+ webId,
95
+ localPart: auth && 'localPart' in auth && typeof auth.localPart === 'string'
96
+ ? auth.localPart
97
+ : deriveLocalPart(webId),
98
+ };
99
+ }
100
+ async readJson(request) {
101
+ const chunks = [];
102
+ await new Promise((resolve, reject) => {
103
+ request.on('data', (chunk) => chunks.push(chunk));
104
+ request.on('end', () => resolve());
105
+ request.on('error', reject);
106
+ });
107
+ const body = Buffer.concat(chunks).toString('utf8');
108
+ return body.length > 0 ? JSON.parse(body) : {};
109
+ }
110
+ sendJson(response, status, body) {
111
+ const encoded = JSON.stringify(body);
112
+ response.writeHead(status, {
113
+ 'Content-Type': 'application/json',
114
+ 'Content-Length': Buffer.byteLength(encoded),
115
+ });
116
+ response.end(encoded);
117
+ }
118
+ }
119
+ exports.DeviceNotificationTicketHandler = DeviceNotificationTicketHandler;
120
+ function deriveLocalPart(webId) {
121
+ try {
122
+ const url = new URL(webId);
123
+ const parts = url.pathname.split('/').filter(Boolean);
124
+ return parts[0] ?? 'unknown';
125
+ }
126
+ catch {
127
+ return 'unknown';
128
+ }
129
+ }
130
+ //# sourceMappingURL=DeviceNotificationTicketHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DeviceNotificationTicketHandler.js","sourceRoot":"","sources":["../../../src/api/handlers/DeviceNotificationTicketHandler.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash, randomBytes } from 'node:crypto';\nimport type { IncomingMessage, ServerResponse } from 'node:http';\nimport type { AuthContext } from '../auth/AuthContext';\n\nexport interface DeviceNotificationIdentity {\n webId: string;\n localPart: string;\n}\n\nexport interface DeviceNotificationTicketRecord {\n digest: string;\n identity: DeviceNotificationIdentity;\n deviceSessionId: string;\n origin: string;\n expiresAt: number;\n}\n\nexport class DeviceNotificationTicketStore {\n private readonly records = new Map<string, DeviceNotificationTicketRecord>();\n\n public mint(input: {\n identity: DeviceNotificationIdentity;\n deviceSessionId: string;\n origin: string;\n ttlMs: number;\n }): string {\n const ticket = randomBytes(32).toString('base64url');\n this.records.set(this.digest(ticket), {\n digest: this.digest(ticket),\n identity: input.identity,\n deviceSessionId: input.deviceSessionId,\n origin: new URL(input.origin).origin,\n expiresAt: Date.now() + input.ttlMs,\n });\n return ticket;\n }\n\n public consume(ticket: string): DeviceNotificationTicketRecord | undefined {\n const digest = this.digest(ticket);\n const record = this.records.get(digest);\n this.records.delete(digest);\n if (!record || record.expiresAt < Date.now()) {\n return undefined;\n }\n return record;\n }\n\n public size(): number {\n return this.records.size;\n }\n\n public clear(): void {\n this.records.clear();\n }\n\n private digest(ticket: string): string {\n return createHash('sha256').update(ticket).digest('hex');\n }\n}\n\nexport interface DeviceNotificationTicketHandlerOptions {\n webSocketEndpoint: string;\n origin: string;\n ticketTtlMs?: number;\n ticketStore?: DeviceNotificationTicketStore;\n}\n\ntype TicketRequest = IncomingMessage & {\n auth?: AuthContext | { webId?: string; localPart?: string };\n};\n\nexport class DeviceNotificationTicketHandler {\n public readonly ticketStore: DeviceNotificationTicketStore;\n private readonly webSocketEndpoint: string;\n private readonly origin: string;\n private readonly ticketTtlMs: number;\n\n public constructor(options: DeviceNotificationTicketHandlerOptions) {\n this.webSocketEndpoint = options.webSocketEndpoint;\n this.origin = new URL(options.origin).origin;\n this.ticketTtlMs = options.ticketTtlMs ?? 60_000;\n this.ticketStore = options.ticketStore ?? new DeviceNotificationTicketStore();\n }\n\n public async handle(request: TicketRequest, response: ServerResponse): Promise<void> {\n const identity = this.extractIdentity(request);\n if (!identity) {\n this.sendJson(response, 401, { error: 'Unauthorized' });\n return;\n }\n const body = await this.readJson(request);\n const deviceSessionId = typeof body.deviceSessionId === 'string'\n ? body.deviceSessionId\n : typeof body.sessionId === 'string'\n ? body.sessionId\n : undefined;\n const origin = typeof body.origin === 'string' ? body.origin : undefined;\n if (!deviceSessionId || !origin) {\n this.sendJson(response, 400, { error: 'deviceSessionId and origin are required' });\n return;\n }\n try {\n if (new URL(origin).origin !== this.origin) {\n this.sendJson(response, 400, { error: 'origin must match the notification service origin' });\n return;\n }\n } catch {\n this.sendJson(response, 400, { error: 'origin must be a valid URL' });\n return;\n }\n const ticket = this.ticketStore.mint({\n identity,\n deviceSessionId,\n origin: this.origin,\n ttlMs: this.ticketTtlMs,\n });\n this.sendJson(response, 201, {\n protocol: 'xpod.notifications.v1',\n ticket,\n webSocketEndpoint: this.webSocketEndpoint,\n expiresInMs: this.ticketTtlMs,\n });\n }\n\n private extractIdentity(request: TicketRequest): DeviceNotificationIdentity | undefined {\n const auth = request.auth;\n const webId = auth && 'webId' in auth && typeof auth.webId === 'string' ? auth.webId : undefined;\n if (!webId) {\n return undefined;\n }\n return {\n webId,\n localPart: auth && 'localPart' in auth && typeof auth.localPart === 'string'\n ? auth.localPart\n : deriveLocalPart(webId),\n };\n }\n\n private async readJson(request: IncomingMessage): Promise<Record<string, unknown>> {\n const chunks: Buffer[] = [];\n await new Promise<void>((resolve, reject) => {\n request.on('data', (chunk: Buffer) => chunks.push(chunk));\n request.on('end', () => resolve());\n request.on('error', reject);\n });\n const body = Buffer.concat(chunks).toString('utf8');\n return body.length > 0 ? JSON.parse(body) : {};\n }\n\n private sendJson(response: ServerResponse, status: number, body: unknown): void {\n const encoded = JSON.stringify(body);\n response.writeHead(status, {\n 'Content-Type': 'application/json',\n 'Content-Length': Buffer.byteLength(encoded),\n });\n response.end(encoded);\n }\n}\n\nfunction deriveLocalPart(webId: string): string {\n try {\n const url = new URL(webId);\n const parts = url.pathname.split('/').filter(Boolean);\n return parts[0] ?? 'unknown';\n } catch {\n return 'unknown';\n }\n}\n"]}
@@ -0,0 +1,117 @@
1
+ {
2
+ "@context": [
3
+ "https://linkedsoftwaredependencies.org/bundles/npm/@undefineds.co/xpod/^0.0.0/components/context.jsonld"
4
+ ],
5
+ "@id": "npmd:@undefineds.co/xpod",
6
+ "components": [
7
+ {
8
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler",
9
+ "@type": "Class",
10
+ "requireElement": "DeviceNotificationTicketHandler",
11
+ "parameters": [
12
+ {
13
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_webSocketEndpoint",
14
+ "range": "xsd:string"
15
+ },
16
+ {
17
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_origin",
18
+ "range": "xsd:string"
19
+ },
20
+ {
21
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_ticketTtlMs",
22
+ "range": {
23
+ "@type": "ParameterRangeUnion",
24
+ "parameterRangeElements": [
25
+ "xsd:number",
26
+ {
27
+ "@type": "ParameterRangeUndefined"
28
+ }
29
+ ]
30
+ }
31
+ },
32
+ {
33
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_ticketStore",
34
+ "range": {
35
+ "@type": "ParameterRangeUnion",
36
+ "parameterRangeElements": [
37
+ "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketStore",
38
+ {
39
+ "@type": "ParameterRangeUndefined"
40
+ }
41
+ ]
42
+ }
43
+ }
44
+ ],
45
+ "memberFields": [
46
+ {
47
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_ticketStore",
48
+ "memberFieldName": "ticketStore",
49
+ "range": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketStore"
50
+ },
51
+ {
52
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_webSocketEndpoint",
53
+ "memberFieldName": "webSocketEndpoint"
54
+ },
55
+ {
56
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_origin",
57
+ "memberFieldName": "origin"
58
+ },
59
+ {
60
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_ticketTtlMs",
61
+ "memberFieldName": "ticketTtlMs"
62
+ },
63
+ {
64
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_constructor",
65
+ "memberFieldName": "constructor"
66
+ },
67
+ {
68
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_handle",
69
+ "memberFieldName": "handle"
70
+ },
71
+ {
72
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_extractIdentity",
73
+ "memberFieldName": "extractIdentity"
74
+ },
75
+ {
76
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_readJson",
77
+ "memberFieldName": "readJson"
78
+ },
79
+ {
80
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler__member_sendJson",
81
+ "memberFieldName": "sendJson"
82
+ }
83
+ ],
84
+ "constructorArguments": [
85
+ {
86
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options__constructorArgument",
87
+ "fields": [
88
+ {
89
+ "keyRaw": "webSocketEndpoint",
90
+ "value": {
91
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_webSocketEndpoint"
92
+ }
93
+ },
94
+ {
95
+ "keyRaw": "origin",
96
+ "value": {
97
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_origin"
98
+ }
99
+ },
100
+ {
101
+ "keyRaw": "ticketTtlMs",
102
+ "value": {
103
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_ticketTtlMs"
104
+ }
105
+ },
106
+ {
107
+ "keyRaw": "ticketStore",
108
+ "value": {
109
+ "@id": "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld#DeviceNotificationTicketHandler_options_ticketStore"
110
+ }
111
+ }
112
+ ]
113
+ }
114
+ ]
115
+ }
116
+ ]
117
+ }
@@ -156,7 +156,7 @@ export declare class InngestRunExecutionBackend implements RunExecutionBackend {
156
156
  readonly event: "xpod/run.continue_requested";
157
157
  }], Omit<Omit<import("inngest").BaseContext<Inngest.Any>, never> & Record<never, never> & {
158
158
  logger: import("inngest").Logger;
159
- }, "group" | "requestId" | "attempt" | "event" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
159
+ }, "group" | "requestId" | "event" | "attempt" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
160
160
  step: {
161
161
  sendEvent: (idOrOptions: import("inngest").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<Inngest<import("inngest").ClientOptions>>>>;
162
162
  waitForSignal: <TData>(idOrOptions: import("inngest").StepOptionsOrId, opts: {
@@ -337,7 +337,7 @@ export declare class InngestRunExecutionBackend implements RunExecutionBackend {
337
337
  readonly event: "xpod/run.continue_requested";
338
338
  }], Omit<Omit<import("inngest").BaseContext<Inngest.Any>, never> & Record<never, never> & {
339
339
  logger: import("inngest").Logger;
340
- }, "group" | "requestId" | "attempt" | "event" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
340
+ }, "group" | "requestId" | "event" | "attempt" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest").FailureEventArgs<EventPayload<any>> & {
341
341
  step: {
342
342
  sendEvent: (idOrOptions: import("inngest").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<Inngest<import("inngest").ClientOptions>>>>;
343
343
  waitForSignal: <TData>(idOrOptions: import("inngest").StepOptionsOrId, opts: {
@@ -133,7 +133,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
133
133
  readonly event: "xpod/task.materialize_due";
134
134
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
135
135
  logger: import("inngest").Logger;
136
- }, "group" | "requestId" | "attempt" | "event" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
136
+ }, "group" | "requestId" | "event" | "attempt" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
137
137
  step: {
138
138
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
139
139
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -311,7 +311,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
311
311
  readonly event: "xpod/task.materialize_due";
312
312
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
313
313
  logger: import("inngest").Logger;
314
- }, "group" | "requestId" | "attempt" | "event" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
314
+ }, "group" | "requestId" | "event" | "attempt" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
315
315
  step: {
316
316
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
317
317
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -494,7 +494,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
494
494
  readonly event: "xpod/task.event";
495
495
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
496
496
  logger: import("inngest").Logger;
497
- }, "group" | "requestId" | "attempt" | "event" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
497
+ }, "group" | "requestId" | "event" | "attempt" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
498
498
  step: {
499
499
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
500
500
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -670,7 +670,7 @@ export declare class InngestTaskScheduler<TContext = StoreContext> {
670
670
  readonly event: "xpod/task.event";
671
671
  }], Omit<Omit<import("inngest/types").BaseContext<import("inngest").Inngest.Any>, never> & Record<never, never> & {
672
672
  logger: import("inngest").Logger;
673
- }, "group" | "requestId" | "attempt" | "event" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
673
+ }, "group" | "requestId" | "event" | "attempt" | "events" | "runId" | "step" | "defer" | "jobId" | "maxAttempts"> & import("inngest/types").FailureEventArgs<EventPayload<any>> & {
674
674
  step: {
675
675
  sendEvent: (idOrOptions: import("inngest/types").StepOptionsOrId, payload: import("inngest").SendEventPayload) => Promise<import("inngest/types").SendEventOutput<import("inngest").ClientOptionsFromInngest<import("inngest").Inngest<import("inngest/types").ClientOptions>>>>;
676
676
  waitForSignal: <TData>(idOrOptions: import("inngest/types").StepOptionsOrId, opts: {
@@ -44,6 +44,10 @@
44
44
  "undefineds:dist/http/TracingHandler.jsonld",
45
45
  "undefineds:dist/http/admin/EdgeNodeCertificateHttpHandler.jsonld",
46
46
  "undefineds:dist/http/terminal/TerminalHttpHandler.jsonld",
47
+ "undefineds:dist/notifications/DeviceNotificationHub.jsonld",
48
+ "undefineds:dist/notifications/DeviceNotificationResourceListener.jsonld",
49
+ "undefineds:dist/http/DeviceNotificationWebSocketServer.jsonld",
50
+ "undefineds:dist/api/handlers/DeviceNotificationTicketHandler.jsonld",
47
51
  "undefineds:dist/pods/ReservedSuffixIdentifierGenerator.jsonld",
48
52
  "undefineds:dist/identity/drizzle/DrizzleIndexedStorage.jsonld",
49
53
  "undefineds:dist/identity/ValidatingIdentityProviderHttpHandler.jsonld",
@@ -128,6 +132,7 @@
128
132
  "undefineds:dist/edge/reachability/TcpP2PSignalingSession.jsonld",
129
133
  "undefineds:dist/edge/reachability/ManagedClientFetch.jsonld",
130
134
  "undefineds:dist/edge/reachability/ManagedClientP2PSmoke.jsonld",
131
- "undefineds:dist/edge/reachability/P2PRealnetAcceptance.jsonld"
135
+ "undefineds:dist/edge/reachability/P2PRealnetAcceptance.jsonld",
136
+ "undefineds:dist/notifications/device-notification-protocol.jsonld"
132
137
  ]
133
138
  }