@hivemind-os/collective-daemon 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/portal/server.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\n\nimport cors from '@fastify/cors';\nimport Fastify, { type FastifyInstance, type FastifyReply } from 'fastify';\n\nimport type { AuthProvider, OAuthConfig, StoredZkLoginSession } from '@hivemind-os/collective-core';\nimport { createPkcePair, type ZkLoginPendingSession } from '@hivemind-os/collective-core';\n\nimport { saveConfig, type DaemonFullConfig } from '../config.js';\nimport type { DaemonAuthStatus } from '../auth/session-monitor.js';\nimport { buildOAuthConfig, type DaemonState } from '../state.js';\n\ntype PortalOAuthProvider = OAuthConfig['provider'];\n\ntype PortalAuthFlow = 'setup' | 'reauth';\n\ninterface PendingAuthState {\n provider: PortalOAuthProvider;\n flow: PortalAuthFlow;\n codeVerifier: string;\n pendingSession: ZkLoginPendingSession;\n createdAt: number;\n}\n\nexport interface PortalAuthProvider extends AuthProvider {\n createAuthorizationRequest(params: {\n redirectUri: string;\n state: string;\n codeChallenge: string;\n scopes?: string[];\n }): Promise<{\n authorizationUrl: string;\n pendingSession: ZkLoginPendingSession;\n }>;\n exchangeAuthorizationCode(code: string, codeVerifier: string, redirectUri: string): Promise<{\n jwt: string;\n refreshToken?: string;\n }>;\n authenticateWithJwt(\n jwt: string,\n params: { pendingSession: ZkLoginPendingSession; refreshToken?: string },\n ): Promise<StoredZkLoginSession>;\n getSession(): StoredZkLoginSession | null;\n getOAuthConfig(): OAuthConfig;\n setOAuthConfig(oauth: OAuthConfig): void;\n}\n\nexport interface PortalLogger {\n info(bindings: Record<string, unknown>, message: string): void;\n warn(bindings: Record<string, unknown>, message: string): void;\n}\n\nexport interface PortalServerOptions {\n config: DaemonFullConfig;\n configPath: string;\n authProvider: PortalAuthProvider;\n state?: DaemonState;\n logger?: PortalLogger;\n getAuthStatus?: () => DaemonAuthStatus | null;\n onAuthenticated?: (session: StoredZkLoginSession) => Promise<void> | void;\n onSettingsSaved?: (config: DaemonFullConfig) => Promise<void> | void;\n}\n\nconst PENDING_AUTH_TTL_MS = 10 * 60 * 1000;\n\ntype OAuthCallbackPayload = {\n code?: string;\n error?: string;\n error_description?: string;\n id_token?: string;\n state?: string;\n user?: string;\n};\n\nexport class PortalServer {\n private readonly server: FastifyInstance;\n private readonly pendingAuth = new Map<string, PendingAuthState>();\n private baseUrl = '';\n private setupComplete = false;\n private completionPromise: Promise<void>;\n private resolveCompletion!: () => void;\n\n constructor(private readonly options: PortalServerOptions) {\n this.server = Fastify({ logger: false });\n this.setupComplete = Boolean(options.state);\n this.completionPromise = new Promise((resolvePromise) => {\n this.resolveCompletion = resolvePromise;\n });\n }\n\n async start(): Promise<string> {\n this.server.addContentTypeParser('application/x-www-form-urlencoded', { parseAs: 'string' }, (_request, body, done) => {\n const payload = typeof body === 'string' ? body : body.toString('utf8');\n done(null, Object.fromEntries(new URLSearchParams(payload)));\n });\n\n await this.server.register(cors, {\n origin: (origin, callback) => {\n if (!origin) {\n callback(null, true);\n return;\n }\n\n callback(null, isLoopbackOrigin(origin));\n },\n });\n this.registerRoutes();\n\n this.baseUrl = await this.server.listen({\n host: '127.0.0.1',\n port: this.options.config.auth.portal?.port ?? 19876,\n });\n return this.baseUrl;\n }\n\n async stop(): Promise<void> {\n this.pendingAuth.clear();\n await this.server.close().catch(() => undefined);\n }\n\n async waitForAuth(): Promise<void> {\n if (this.setupComplete) {\n return;\n }\n\n await this.completionPromise;\n }\n\n getUrl(): string {\n return this.baseUrl;\n }\n\n getReauthUrl(): string {\n return `${this.baseUrl}/auth/reauth`;\n }\n\n private registerRoutes(): void {\n this.server.get('/', async (_request, reply) => {\n reply.type('text/html').send(this.renderPage());\n });\n\n this.server.get('/auth/reauth', async (_request, reply) => {\n reply.type('text/html').send(renderReauthPage(getConfiguredProviders(this.options.config)));\n });\n\n this.server.get('/auth/google', async (request, reply) => this.startAuthFlow('google', reply, readFlow(request.query)));\n this.server.get('/auth/apple', async (request, reply) => this.startAuthFlow('apple', reply, readFlow(request.query)));\n\n this.server.get('/auth/callback', async (request, reply) => {\n await this.handleOAuthCallback('google', request.query as OAuthCallbackPayload, reply);\n });\n\n this.server.post('/auth/apple/callback', async (request, reply) => {\n await this.handleOAuthCallback('apple', (request.body ?? {}) as OAuthCallbackPayload, reply);\n });\n\n this.server.get('/api/status', async () => {\n const auth = this.getAuthStatus();\n return {\n authenticated: auth.authenticated,\n authMode: auth.authMode,\n authState: auth.state,\n address: auth.address,\n auth,\n did: this.options.state?.did ?? null,\n setupComplete: this.setupComplete,\n spendingLimitMist: getCurrentDailyLimitMist(this.options.config).toString(),\n };\n });\n\n this.server.post('/api/settings', async (request, reply) => {\n const previousSettings = snapshotPortalSettings(this.options.config);\n\n try {\n const body = (request.body ?? {}) as {\n dailyLimitMist?: number | string;\n dailyLimitSui?: number | string;\n };\n const nextLimit = normalizeDailyLimit(body);\n updateDailyLimit(this.options.config, nextLimit);\n this.options.state?.spendingPolicy.updatePolicy(this.options.config.spending);\n await this.options.onSettingsSaved?.(this.options.config);\n await saveConfig(this.options.config, this.options.configPath);\n this.options.logger?.info({ configPath: this.options.configPath }, 'Portal settings persisted');\n this.setupComplete = true;\n this.resolveCompletion();\n\n return {\n ok: true,\n address: await this.options.authProvider.getAddress(),\n spendingLimitMist: nextLimit.toString(),\n };\n } catch (error) {\n restorePortalSettings(this.options.config, previousSettings);\n this.options.state?.spendingPolicy.updatePolicy(this.options.config.spending);\n if (!isInputValidationError(error)) {\n this.options.logger?.warn({ err: error, configPath: this.options.configPath }, 'Failed to persist portal settings');\n }\n\n return reply.code(isInputValidationError(error) ? 400 : 500).send({\n ok: false,\n error: getSafeErrorMessage(error, 'Unable to save settings.'),\n });\n }\n });\n\n this.server.get('/network', async (_request, reply) => {\n reply.type('text/html').send(renderNetworkPage(this.options.config.network));\n });\n\n this.server.get('/api/network', async () => {\n return { ...this.options.config.network };\n });\n\n this.server.post('/api/network', async (request, reply) => {\n const previousNetwork = { ...this.options.config.network };\n\n try {\n const body = (request.body ?? {}) as {\n rpcUrl?: string;\n faucetUrl?: string;\n packageId?: string;\n registryId?: string;\n };\n\n const validated = validateNetworkInput(body);\n this.options.config.network = { ...this.options.config.network, ...validated };\n await saveConfig(this.options.config, this.options.configPath);\n await this.options.onSettingsSaved?.(this.options.config);\n this.options.logger?.info({ configPath: this.options.configPath }, 'Portal network config persisted');\n\n return { ok: true, network: { ...this.options.config.network } };\n } catch (error) {\n this.options.config.network = previousNetwork;\n const isValidation = isNetworkValidationError(error);\n if (!isValidation) {\n this.options.logger?.warn({ err: error, configPath: this.options.configPath }, 'Failed to persist network config');\n }\n\n return reply.code(isValidation ? 400 : 500).send({\n ok: false,\n error: getSafeErrorMessage(error, 'Unable to save network settings.'),\n });\n }\n });\n\n // ── Wallet page ──────────────────────────────────────────────\n this.server.get('/wallet', async (_request, reply) => {\n reply.type('text/html').send(renderWalletPage());\n });\n\n this.server.get('/api/wallet', async () => {\n const state = this.options.state;\n if (!state) {\n return { error: 'Daemon state not available.' };\n }\n\n const balanceMist = await state.suiClient.getBalance(state.address);\n return {\n did: state.did,\n address: state.address,\n balanceMist: balanceMist.toString(),\n balanceSui: formatMistToSui(balanceMist),\n spendingToday: formatMistToSui(state.spendingPolicy.getSpent('day')),\n spendingThisHour: formatMistToSui(state.spendingPolicy.getSpent('hour')),\n spendingThisMonth: formatMistToSui(state.spendingPolicy.getSpent('month')),\n dailyLimit: formatDailyLimit(this.options.config),\n };\n });\n\n // ── Discovery page ───────────────────────────────────────────\n this.server.get('/discover', async (_request, reply) => {\n reply.type('text/html').send(renderDiscoverPage());\n });\n\n this.server.get('/api/discover', async (request, reply) => {\n const state = this.options.state;\n if (!state) {\n return { error: 'Daemon state not available.' };\n }\n\n const query = (request.query ?? {}) as { capability?: string; limit?: string };\n const capability = (query.capability ?? '').trim();\n if (!capability) {\n return { capability: '', agents: [] };\n }\n if (capability.length > 200) {\n reply.code(400);\n return { error: 'Capability query must be 200 characters or fewer.' };\n }\n\n const limit = Math.min(Math.max(Number(query.limit) || 10, 1), 50);\n try {\n const agents = await state.registryClient.discoverByCapability(capability, limit, {});\n return {\n capability,\n agents: agents.map((agent) => ({\n name: agent.name,\n did: agent.did,\n active: agent.active,\n capabilities: agent.capabilities.map((cap) => ({\n name: cap.name,\n priceMist: cap.pricing.amount.toString(),\n rail: cap.pricing.rail,\n })),\n endpoint: agent.endpoint,\n })),\n };\n } catch (err) {\n reply.code(502);\n return { error: 'Failed to query the registry. The network may be unavailable.', capability };\n }\n });\n\n // ── Tasks / Spending page ────────────────────────────────────\n this.server.get('/tasks', async (_request, reply) => {\n reply.type('text/html').send(renderTasksPage());\n });\n\n this.server.get('/api/tasks', async () => {\n const state = this.options.state;\n if (!state) {\n return { error: 'Daemon state not available.' };\n }\n\n const recentEntries = state.spendingPolicy.getRecentEntries(50).map((e) => ({\n id: e.id,\n amountSui: formatMistToSui(e.amountBaseUnits),\n rail: e.rail,\n taskId: e.taskId ?? null,\n appId: e.appId ?? null,\n timestamp: e.timestamp,\n }));\n\n return {\n spending: {\n hour: formatMistToSui(state.spendingPolicy.getSpent('hour')),\n day: formatMistToSui(state.spendingPolicy.getSpent('day')),\n month: formatMistToSui(state.spendingPolicy.getSpent('month')),\n },\n dailyLimit: formatDailyLimit(this.options.config),\n providerRunning: state.getStatusBase().providerRunning,\n recentEntries,\n };\n });\n }\n\n private async startAuthFlow(\n provider: PortalOAuthProvider,\n reply: FastifyReply,\n flow: PortalAuthFlow = 'setup',\n ): Promise<unknown> {\n if (!isProviderConfigured(this.options.config, provider)) {\n return reply.code(404).type('text/html').send(renderMessagePage('Authentication unavailable', `${capitalize(provider)} sign-in is not configured.`));\n }\n\n try {\n this.pruneExpiredPendingAuth();\n const state = randomBytes(16).toString('hex');\n const { verifier, challenge } = createPkcePair();\n this.setActiveOAuthConfig(provider);\n const authRequest = await this.options.authProvider.createAuthorizationRequest({\n redirectUri: this.getRedirectUri(provider),\n state,\n codeChallenge: challenge,\n scopes: provider === 'apple' ? ['name', 'email'] : undefined,\n });\n\n this.pendingAuth.set(state, {\n provider,\n flow,\n codeVerifier: verifier,\n pendingSession: authRequest.pendingSession,\n createdAt: Date.now(),\n });\n return reply.redirect(authRequest.authorizationUrl);\n } catch (error) {\n return reply\n .code(500)\n .type('text/html')\n .send(renderMessagePage('Authentication failed', getSafeErrorMessage(error, 'Unable to start sign-in.')));\n }\n }\n\n private async handleOAuthCallback(\n provider: PortalOAuthProvider,\n payload: OAuthCallbackPayload,\n reply: FastifyReply,\n ): Promise<void> {\n const code = readOptionalString(payload.code);\n const error = readOptionalString(payload.error);\n const errorDescription = readOptionalString(payload.error_description);\n const idToken = readOptionalString(payload.id_token);\n const state = readOptionalString(payload.state);\n\n this.pruneExpiredPendingAuth();\n\n if (error) {\n if (state) {\n this.pendingAuth.delete(state);\n }\n reply.code(400).type('text/html').send(renderMessagePage('Authentication failed', getOAuthErrorDetail(error, errorDescription)));\n return;\n }\n\n if (!state) {\n reply.code(400).type('text/html').send(renderMessagePage('Authentication failed', 'Missing callback state.'));\n return;\n }\n\n const pending = this.pendingAuth.get(state);\n if (!pending || pending.provider !== provider) {\n reply.code(400).type('text/html').send(renderMessagePage('Authentication failed', 'Unknown login state.'));\n return;\n }\n\n if (Date.now() - pending.createdAt > PENDING_AUTH_TTL_MS) {\n this.pendingAuth.delete(state);\n reply.code(400).type('text/html').send(renderMessagePage('Authentication failed', 'Login session expired. Please try again.'));\n return;\n }\n\n this.pendingAuth.delete(state);\n\n try {\n this.setActiveOAuthConfig(provider);\n const session = provider === 'apple'\n ? await this.authenticateAppleCallback(idToken, pending)\n : await this.authenticateGoogleCallback(code, pending);\n await this.options.onAuthenticated?.(session);\n\n reply.type('text/html').send(pending.flow === 'reauth' ? renderReauthCompletePage() : this.renderPage());\n } catch (callbackError) {\n reply\n .code(502)\n .type('text/html')\n .send(renderMessagePage('Authentication failed', getSafeErrorMessage(callbackError, 'Unable to complete sign-in.')));\n }\n }\n\n private async authenticateGoogleCallback(\n code: string | undefined,\n pending: PendingAuthState,\n ): Promise<StoredZkLoginSession> {\n if (!code) {\n throw new Error('Missing authorization code.');\n }\n\n const tokens = await this.options.authProvider.exchangeAuthorizationCode(\n code,\n pending.codeVerifier,\n this.getRedirectUri('google'),\n );\n return this.options.authProvider.authenticateWithJwt(tokens.jwt, {\n pendingSession: pending.pendingSession,\n refreshToken: tokens.refreshToken,\n });\n }\n\n private async authenticateAppleCallback(\n idToken: string | undefined,\n pending: PendingAuthState,\n ): Promise<StoredZkLoginSession> {\n if (!idToken) {\n throw new Error('Missing Apple identity token.');\n }\n\n return this.options.authProvider.authenticateWithJwt(idToken, {\n pendingSession: pending.pendingSession,\n });\n }\n\n private pruneExpiredPendingAuth(): void {\n const expiresBefore = Date.now() - PENDING_AUTH_TTL_MS;\n for (const [state, pending] of this.pendingAuth.entries()) {\n if (pending.createdAt <= expiresBefore) {\n this.pendingAuth.delete(state);\n }\n }\n }\n\n private setActiveOAuthConfig(provider: PortalOAuthProvider): void {\n this.options.authProvider.setOAuthConfig({\n ...this.options.authProvider.getOAuthConfig(),\n ...buildOAuthConfig(this.options.config, this.getRedirectUri(provider), provider),\n });\n }\n\n private getRedirectUri(provider: PortalOAuthProvider): string {\n return provider === 'apple' ? `${this.baseUrl}/auth/apple/callback` : `${this.baseUrl}/auth/callback`;\n }\n\n private getAuthStatus(): DaemonAuthStatus {\n const fallbackSession = this.options.authProvider.getSession();\n const expiresAt = getJwtExpiryMs(fallbackSession?.jwt);\n return this.options.getAuthStatus?.() ?? {\n authMode: 'zklogin',\n authenticated: this.options.authProvider.isAuthenticated(),\n state: this.options.authProvider.isAuthenticated() ? 'authenticated' : 'reauth_required',\n address: fallbackSession?.address ?? null,\n expiresAt,\n expiresInMs: expiresAt === null ? null : expiresAt - Date.now(),\n refreshAvailable: Boolean(fallbackSession?.refreshToken),\n lastError: null,\n updatedAt: Date.now(),\n };\n }\n\n private renderPage(): string {\n const authStatus = this.getAuthStatus();\n if (!this.options.authProvider.isAuthenticated()) {\n return this.options.getAuthStatus && (authStatus.state === 'expired' || authStatus.state === 'reauth_required')\n ? renderReauthPage(getConfiguredProviders(this.options.config))\n : renderWelcomePage(getConfiguredProviders(this.options.config));\n }\n\n return renderSetupPage({\n address: this.options.authProvider.getSession()?.address ?? '',\n dailyLimitMist: getCurrentDailyLimitMist(this.options.config),\n setupComplete: this.setupComplete,\n });\n }\n}\n\nfunction renderWelcomePage(providers: PortalOAuthProvider[]): string {\n const detail =\n providers.length === 0\n ? 'Configure Google or Apple sign-in to create a Sui wallet without managing private keys.'\n : providers.length === 1\n ? `Sign in with ${capitalize(providers[0])} to create a Sui wallet without managing private keys.`\n : 'Sign in with Google or Apple to create a Sui wallet without managing private keys.';\n\n return renderAuthPage({\n title: 'Welcome to Agentic Mesh',\n detail,\n providers,\n flow: 'setup',\n });\n}\n\nfunction renderReauthPage(providers: PortalOAuthProvider[]): string {\n return renderAuthPage({\n title: 'Your session has expired',\n detail: 'Re-authenticate to resume wallet-backed operations in Agentic Mesh.',\n providers,\n flow: 'reauth',\n });\n}\n\nfunction renderReauthCompletePage(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Authentication restored</title>\n <style>${BASE_STYLES}</style>\n </head>\n <body>\n <main class=\"card\">\n <h1>Authentication restored</h1>\n <p>Your session is active again. This window will close automatically.</p>\n </main>\n <script>\n setTimeout(() => {\n window.close();\n setTimeout(() => {\n window.location.replace('/');\n }, 250);\n }, 150);\n </script>\n </body>\n</html>`;\n}\n\nfunction renderAuthPage(params: {\n title: string;\n detail: string;\n providers: PortalOAuthProvider[];\n flow: PortalAuthFlow;\n}): string {\n const buttons = params.providers.map((provider) => renderAuthButton(provider, params.flow)).join('');\n\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Agentic Mesh Setup</title>\n <style>${BASE_STYLES}</style>\n </head>\n <body>\n <main class=\"card\">\n <h1>${escapeHtml(params.title)}</h1>\n <p>${escapeHtml(params.detail)}</p>\n ${buttons ? `<div class=\"auth-buttons\">${buttons}</div>` : '<p class=\"error\">No OAuth providers are configured.</p>'}\n </main>\n </body>\n</html>`;\n}\n\nfunction renderAuthButton(provider: PortalOAuthProvider, flow: PortalAuthFlow): string {\n const href = `/auth/${provider}?flow=${flow}`;\n if (provider === 'apple') {\n return `<a class=\"button button--apple\" href=\"${href}\"><span class=\"button__icon\" aria-hidden=\"true\"></span><span>Sign in with Apple</span></a>`;\n }\n\n return `<a class=\"button button--google\" href=\"${href}\"><span>Sign in with Google</span></a>`;\n}\n\nfunction renderSetupPage(params: { address: string; dailyLimitMist: bigint; setupComplete: boolean }): string {\n const currentLimitSui = formatMistToSui(params.dailyLimitMist);\n const successMessage = params.setupComplete ? '<p class=\"success\">Setup complete. You can return to your app.</p>' : '';\n const title = params.setupComplete ? 'Agentic Mesh is ready' : 'Finish setup';\n\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Agentic Mesh Setup</title>\n <style>${BASE_STYLES}${INNER_PAGE_STYLES}</style>\n </head>\n <body>\n <main class=\"card\">\n ${PORTAL_NAV}\n <h1>${escapeHtml(title)}</h1>\n <p>Your Sui address:</p>\n <code>${escapeHtml(params.address)}</code>\n <label for=\"limit\">Daily spending limit (SUI)</label>\n <input id=\"limit\" type=\"range\" min=\"1\" max=\"100\" step=\"1\" value=\"${escapeHtml(currentLimitSui)}\" />\n <div id=\"limit-value\">${escapeHtml(currentLimitSui)} SUI</div>\n <button class=\"button\" id=\"finish\">Finish Setup</button>\n <p class=\"error\" id=\"status\" hidden></p>\n ${successMessage}\n </main>\n <script>\n const slider = document.getElementById('limit');\n const output = document.getElementById('limit-value');\n const button = document.getElementById('finish');\n const status = document.getElementById('status');\n slider.addEventListener('input', () => {\n output.textContent = slider.value + ' SUI';\n });\n button.addEventListener('click', async () => {\n button.disabled = true;\n status.hidden = true;\n try {\n const response = await fetch('/api/settings', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ dailyLimitSui: slider.value }),\n });\n if (!response.ok) {\n const body = await response.json().catch(() => ({}));\n throw new Error(typeof body.error === 'string' ? body.error : 'Unable to save settings.');\n }\n window.location.href = '/';\n } catch (error) {\n status.textContent = error instanceof Error ? error.message : 'Unable to save settings.';\n status.hidden = false;\n button.disabled = false;\n }\n });\n </script>\n </body>\n</html>`;\n}\n\nfunction renderNetworkPage(network: { rpcUrl: string; faucetUrl: string; packageId: string; registryId: string }): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Agentic Mesh — Network</title>\n <style>${BASE_STYLES}${INNER_PAGE_STYLES}${NETWORK_PAGE_STYLES}</style>\n </head>\n <body>\n <main class=\"card\">\n ${PORTAL_NAV}\n <h1>Network Configuration</h1>\n <p>Configure which Sui network the daemon connects to.</p>\n\n <div class=\"presets\">\n <button class=\"preset\" data-rpc=\"https://fullnode.devnet.sui.io:443\" data-faucet=\"https://faucet.devnet.sui.io\">Devnet</button>\n <button class=\"preset\" data-rpc=\"https://fullnode.testnet.sui.io:443\" data-faucet=\"https://faucet.testnet.sui.io\">Testnet</button>\n <button class=\"preset\" data-rpc=\"http://127.0.0.1:9000\" data-faucet=\"http://127.0.0.1:9123\">Local</button>\n </div>\n\n <label for=\"rpcUrl\">RPC URL <span class=\"required\">*</span></label>\n <input id=\"rpcUrl\" type=\"url\" value=\"${escapeAttr(network.rpcUrl)}\" placeholder=\"https://fullnode.devnet.sui.io:443\" required />\n\n <label for=\"faucetUrl\">Faucet URL</label>\n <input id=\"faucetUrl\" type=\"url\" value=\"${escapeAttr(network.faucetUrl)}\" placeholder=\"https://faucet.devnet.sui.io\" />\n\n <label for=\"packageId\">Package ID</label>\n <input id=\"packageId\" type=\"text\" value=\"${escapeAttr(network.packageId)}\" placeholder=\"0x...\" />\n\n <label for=\"registryId\">Registry ID</label>\n <input id=\"registryId\" type=\"text\" value=\"${escapeAttr(network.registryId)}\" placeholder=\"0x...\" />\n\n <p class=\"hint\" id=\"hint\" hidden></p>\n <button class=\"button\" id=\"save\">Save Network Config</button>\n <p class=\"error\" id=\"status\" hidden></p>\n <p class=\"success\" id=\"success\" hidden></p>\n </main>\n <script>\n const rpcUrl = document.getElementById('rpcUrl');\n const faucetUrl = document.getElementById('faucetUrl');\n const packageId = document.getElementById('packageId');\n const registryId = document.getElementById('registryId');\n const saveBtn = document.getElementById('save');\n const status = document.getElementById('status');\n const successEl = document.getElementById('success');\n const hint = document.getElementById('hint');\n\n document.querySelectorAll('.preset').forEach(btn => {\n btn.addEventListener('click', () => {\n rpcUrl.value = btn.dataset.rpc;\n faucetUrl.value = btn.dataset.faucet;\n hint.textContent = 'Preset applied to RPC and Faucet URLs. Package and Registry IDs are unchanged.';\n hint.hidden = false;\n successEl.hidden = true;\n status.hidden = true;\n });\n });\n\n saveBtn.addEventListener('click', async () => {\n saveBtn.disabled = true;\n status.hidden = true;\n successEl.hidden = true;\n hint.hidden = true;\n try {\n const response = await fetch('/api/network', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n rpcUrl: rpcUrl.value.trim(),\n faucetUrl: faucetUrl.value.trim(),\n packageId: packageId.value.trim(),\n registryId: registryId.value.trim(),\n }),\n });\n const body = await response.json().catch(() => ({}));\n if (!response.ok) {\n throw new Error(typeof body.error === 'string' ? body.error : 'Unable to save network settings.');\n }\n successEl.textContent = 'Network configuration saved. The daemon will reconnect to the configured network.';\n successEl.hidden = false;\n } catch (error) {\n status.textContent = error instanceof Error ? error.message : 'Unable to save network settings.';\n status.hidden = false;\n }\n saveBtn.disabled = false;\n });\n </script>\n </body>\n</html>`;\n}\n\nfunction renderMessagePage(title: string, detail: string): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>${escapeHtml(title)}</title>\n <style>${BASE_STYLES}</style>\n </head>\n <body>\n <main class=\"card\">\n <h1>${escapeHtml(title)}</h1>\n <p>${escapeHtml(detail)}</p>\n <a class=\"button\" href=\"/\">Back</a>\n </main>\n </body>\n</html>`;\n}\n\nconst PORTAL_NAV = `\n <nav class=\"nav\">\n <a href=\"/\">Settings</a> · <a href=\"/wallet\">Wallet</a> · <a href=\"/discover\">Discover</a> · <a href=\"/tasks\">Tasks</a> · <a href=\"/network\">Network</a>\n </nav>`;\n\nconst INNER_PAGE_STYLES = `\n .nav { margin-bottom: 16px; font-size: 0.9rem; }\n .nav a { color: #60a5fa; text-decoration: none; }\n .nav a:hover { text-decoration: underline; }\n .stat { display: flex; justify-content: space-between; padding: 10px 0; border-bottom: 1px solid #1e293b; }\n .stat:last-child { border-bottom: 0; }\n .stat-label { color: #94a3b8; font-size: 0.85rem; }\n .stat-value { font-family: monospace; font-size: 0.9rem; }\n .search-row { display: flex; gap: 8px; margin-bottom: 20px; }\n .search-row input { flex: 1; padding: 10px 12px; background: #020617; border: 1px solid #334155; border-radius: 8px; color: #e2e8f0; font-size: 0.9rem; }\n .search-row input:focus { outline: none; border-color: #2563eb; }\n .search-row button { flex-shrink: 0; }\n .agent-card { background: #0f172a; border: 1px solid #1e293b; border-radius: 10px; padding: 16px; margin-bottom: 12px; }\n .agent-name { font-weight: 600; margin-bottom: 4px; }\n .agent-did { font-family: monospace; font-size: 0.75rem; color: #64748b; word-break: break-all; }\n .agent-cap { display: inline-block; background: #1e293b; border-radius: 6px; padding: 4px 10px; margin: 6px 4px 0 0; font-size: 0.8rem; }\n #results-empty { color: #94a3b8; font-style: italic; }\n`;\n\nfunction renderWalletPage(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Agentic Mesh — Wallet</title>\n <style>${BASE_STYLES}${INNER_PAGE_STYLES}</style>\n </head>\n <body>\n <main class=\"card\">\n ${PORTAL_NAV}\n <h1>Wallet</h1>\n <div id=\"loading\">Loading…</div>\n <div id=\"content\" hidden>\n <div class=\"stat\"><span class=\"stat-label\">Address</span><span class=\"stat-value\" id=\"address\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">DID</span><span class=\"stat-value\" id=\"did\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Balance</span><span class=\"stat-value\" id=\"balance\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Spent today</span><span class=\"stat-value\" id=\"spent-day\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Spent this hour</span><span class=\"stat-value\" id=\"spent-hour\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Spent this month</span><span class=\"stat-value\" id=\"spent-month\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Daily limit</span><span class=\"stat-value\" id=\"limit\"></span></div>\n </div>\n </main>\n <script>\n (async () => {\n try {\n const res = await fetch('/api/wallet');\n const data = await res.json();\n document.getElementById('address').textContent = data.address ?? '—';\n document.getElementById('did').textContent = data.did ?? '—';\n document.getElementById('balance').textContent = (data.balanceSui ?? '—') + ' SUI';\n document.getElementById('spent-day').textContent = (data.spendingToday ?? '0') + ' SUI';\n document.getElementById('spent-hour').textContent = (data.spendingThisHour ?? '0') + ' SUI';\n document.getElementById('spent-month').textContent = (data.spendingThisMonth ?? '0') + ' SUI';\n document.getElementById('limit').textContent = data.dailyLimit ?? '—';\n document.getElementById('loading').hidden = true;\n document.getElementById('content').hidden = false;\n } catch (e) {\n document.getElementById('loading').textContent = 'Failed to load wallet data.';\n }\n })();\n </script>\n </body>\n</html>`;\n}\n\nfunction renderDiscoverPage(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Agentic Mesh — Discover</title>\n <style>${BASE_STYLES}${INNER_PAGE_STYLES}</style>\n </head>\n <body>\n <main class=\"card\">\n ${PORTAL_NAV}\n <h1>Discover Agents</h1>\n <div class=\"search-row\">\n <input id=\"capability\" type=\"text\" placeholder=\"Enter capability name…\" />\n <button class=\"button\" id=\"search\">Search</button>\n </div>\n <div id=\"results\"></div>\n </main>\n <script>\n const input = document.getElementById('capability');\n const btn = document.getElementById('search');\n const results = document.getElementById('results');\n\n async function doSearch() {\n const cap = input.value.trim();\n if (!cap) return;\n btn.disabled = true;\n results.innerHTML = '<p style=\"color:#94a3b8\">Searching…</p>';\n try {\n const res = await fetch('/api/discover?capability=' + encodeURIComponent(cap));\n const data = await res.json();\n if (data.error) {\n results.innerHTML = '<p class=\"error\">' + esc(data.error) + '</p>';\n return;\n }\n if (!data.agents || data.agents.length === 0) {\n results.innerHTML = '<p id=\"results-empty\">No agents found for this capability.</p>';\n return;\n }\n results.innerHTML = data.agents.map(a => {\n const caps = (a.capabilities || []).map(c =>\n '<span class=\"agent-cap\">' + esc(c.name) + ' — ' + esc(c.priceMist) + ' MIST</span>'\n ).join('');\n return '<div class=\"agent-card\">' +\n '<div class=\"agent-name\">' + esc(a.name) + (a.active ? '' : ' <span style=\"color:#f87171\">(inactive)</span>') + '</div>' +\n '<div class=\"agent-did\">' + esc(a.did) + '</div>' +\n (caps ? '<div>' + caps + '</div>' : '') +\n (a.endpoint ? '<div style=\"margin-top:6px;font-size:0.8rem;color:#64748b\">' + esc(a.endpoint) + '</div>' : '') +\n '</div>';\n }).join('');\n } catch (e) {\n results.innerHTML = '<p class=\"error\">Search failed.</p>';\n } finally {\n btn.disabled = false;\n }\n }\n\n btn.addEventListener('click', doSearch);\n input.addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(); });\n\n function esc(s) {\n const d = document.createElement('div');\n d.textContent = s ?? '';\n return d.innerHTML;\n }\n </script>\n </body>\n</html>`;\n}\n\nfunction renderTasksPage(): string {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"utf-8\" />\n <title>Agentic Mesh — Tasks</title>\n <style>${BASE_STYLES}${INNER_PAGE_STYLES}\n .tx-table { width: 100%; border-collapse: collapse; margin-top: 16px; font-size: 0.82rem; }\n .tx-table th { text-align: left; color: #94a3b8; padding: 6px 8px; border-bottom: 1px solid #1e293b; }\n .tx-table td { padding: 6px 8px; border-bottom: 1px solid #0f172a; font-family: monospace; }\n .tx-table tr:hover td { background: #0f172a; }\n </style>\n </head>\n <body>\n <main class=\"card\">\n ${PORTAL_NAV}\n <h1>Tasks &amp; Spending</h1>\n <div id=\"loading\">Loading…</div>\n <div id=\"content\" hidden>\n <div class=\"stat\"><span class=\"stat-label\">Spent this hour</span><span class=\"stat-value\" id=\"hour\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Spent today</span><span class=\"stat-value\" id=\"day\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Spent this month</span><span class=\"stat-value\" id=\"month\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Daily limit</span><span class=\"stat-value\" id=\"limit\"></span></div>\n <div class=\"stat\"><span class=\"stat-label\">Provider running</span><span class=\"stat-value\" id=\"provider\"></span></div>\n <h2 style=\"margin-top:24px;font-size:1rem;\">Recent Transactions</h2>\n <div id=\"entries\"></div>\n </div>\n </main>\n <script>\n function esc(s) { const d = document.createElement('div'); d.textContent = s ?? ''; return d.innerHTML; }\n (async () => {\n try {\n const res = await fetch('/api/tasks');\n const data = await res.json();\n document.getElementById('hour').textContent = (data.spending?.hour ?? '0') + ' SUI';\n document.getElementById('day').textContent = (data.spending?.day ?? '0') + ' SUI';\n document.getElementById('month').textContent = (data.spending?.month ?? '0') + ' SUI';\n document.getElementById('limit').textContent = data.dailyLimit ?? '—';\n document.getElementById('provider').textContent = data.providerRunning ? 'Yes' : 'No';\n\n const entries = data.recentEntries ?? [];\n if (entries.length === 0) {\n document.getElementById('entries').innerHTML = '<p style=\"color:#64748b;font-style:italic\">No transactions yet.</p>';\n } else {\n const rows = entries.map(e => '<tr><td>' + esc(new Date(e.timestamp).toLocaleString()) + '</td><td>' + esc(e.amountSui) + ' SUI</td><td>' + esc(e.rail) + '</td><td>' + esc(e.taskId ?? '—') + '</td><td>' + esc(e.appId ?? '—') + '</td></tr>').join('');\n document.getElementById('entries').innerHTML = '<table class=\"tx-table\"><thead><tr><th>Time</th><th>Amount</th><th>Rail</th><th>Task</th><th>App</th></tr></thead><tbody>' + rows + '</tbody></table>';\n }\n\n document.getElementById('loading').hidden = true;\n document.getElementById('content').hidden = false;\n } catch (e) {\n document.getElementById('loading').textContent = 'Failed to load task data.';\n }\n })();\n </script>\n </body>\n</html>`;\n}\n\nfunction snapshotPortalSettings(config: DaemonFullConfig): Pick<DaemonFullConfig, 'auth' | 'payment' | 'spending'> {\n return structuredClone({\n auth: config.auth,\n payment: config.payment,\n spending: config.spending,\n });\n}\n\nfunction restorePortalSettings(\n target: DaemonFullConfig,\n snapshot: Pick<DaemonFullConfig, 'auth' | 'payment' | 'spending'>,\n): void {\n target.auth = snapshot.auth;\n target.payment = snapshot.payment;\n target.spending = snapshot.spending;\n}\n\nfunction normalizeDailyLimit(body: { dailyLimitMist?: number | string; dailyLimitSui?: number | string }): bigint {\n if (body.dailyLimitMist !== undefined) {\n return parsePositiveBigInt(body.dailyLimitMist, 'dailyLimitMist');\n }\n\n if (body.dailyLimitSui !== undefined) {\n return parseSuiToMist(body.dailyLimitSui);\n }\n\n throw new Error('A daily spending limit is required.');\n}\n\nfunction updateDailyLimit(config: DaemonFullConfig, amountMist: bigint): void {\n const nextLimit = { amount: amountMist, interval: 'day' as const };\n const existing = config.spending.limits.findIndex((limit) => limit.interval === 'day' && !limit.scope && !limit.rail);\n if (existing >= 0) {\n config.spending.limits[existing] = nextLimit;\n return;\n }\n\n config.spending.limits.push(nextLimit);\n}\n\nfunction getCurrentDailyLimitMist(config: DaemonFullConfig): bigint {\n return config.spending.limits.find((limit) => limit.interval === 'day' && !limit.scope && !limit.rail)?.amount ?? 0n;\n}\n\nfunction formatDailyLimit(config: DaemonFullConfig): string {\n const limit = getCurrentDailyLimitMist(config);\n return limit === 0n ? 'Unlimited' : formatMistToSui(limit) + ' SUI';\n}\n\nfunction parsePositiveBigInt(value: number | string, field: string): bigint {\n if (typeof value === 'number' && Number.isFinite(value) && value >= 0) {\n return BigInt(Math.floor(value));\n }\n\n if (typeof value === 'string' && /^\\d+$/.test(value.trim())) {\n return BigInt(value.trim());\n }\n\n throw new Error(`${field} must be a non-negative integer.`);\n}\n\nfunction parseSuiToMist(value: number | string): bigint {\n const text = typeof value === 'number' ? value.toString() : value.trim();\n if (!/^\\d+(?:\\.\\d{1,9})?$/.test(text)) {\n throw new Error('dailyLimitSui must be a valid SUI amount.');\n }\n\n const [whole, fraction = ''] = text.split('.');\n return BigInt(whole) * 1_000_000_000n + BigInt(fraction.padEnd(9, '0'));\n}\n\nfunction formatMistToSui(value: bigint): string {\n const whole = value / 1_000_000_000n;\n const fraction = value % 1_000_000_000n;\n if (fraction === 0n) {\n return whole.toString();\n }\n\n return `${whole.toString()}.${fraction.toString().padStart(9, '0').replace(/0+$/, '')}`;\n}\n\nfunction escapeHtml(value: string): string {\n return value.replace(/[&<>\"']/g, (character) => HTML_ESCAPES[character] ?? character);\n}\n\nfunction getSafeErrorMessage(error: unknown, fallback: string): string {\n if (!(error instanceof Error) || !error.message.trim()) {\n return fallback;\n }\n\n return error.message;\n}\n\nfunction getOAuthErrorDetail(error: string, errorDescription?: string): string {\n return errorDescription?.trim() || error.replace(/_/g, ' ');\n}\n\nfunction readFlow(value: unknown): PortalAuthFlow {\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n const flow = (value as Record<string, unknown>).flow;\n if (flow === 'reauth') {\n return 'reauth';\n }\n }\n\n return 'setup';\n}\n\nfunction getJwtExpiryMs(jwt?: string): number | null {\n if (!jwt) {\n return null;\n }\n\n const [, payload] = jwt.split('.');\n if (!payload) {\n return null;\n }\n\n try {\n const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8')) as Record<string, unknown>;\n const exp = parsed.exp;\n const seconds = typeof exp === 'number' ? exp : typeof exp === 'string' && /^\\d+$/.test(exp) ? Number(exp) : null;\n return seconds === null ? null : seconds * 1_000;\n } catch {\n return null;\n }\n}\n\nfunction readOptionalString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;\n}\n\nfunction getConfiguredProviders(config: DaemonFullConfig): PortalOAuthProvider[] {\n return (['google', 'apple'] as const).filter((provider) => isProviderConfigured(config, provider));\n}\n\nfunction isProviderConfigured(config: DaemonFullConfig, provider: PortalOAuthProvider): boolean {\n return provider === 'google' ? Boolean(config.auth.google?.clientId) : Boolean(config.auth.apple?.clientId);\n}\n\nfunction capitalize(value: string): string {\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n\nfunction isInputValidationError(error: unknown): boolean {\n return (\n error instanceof Error &&\n /(dailyLimit|required|valid SUI amount|non-negative integer)/i.test(error.message)\n );\n}\n\nfunction isLoopbackOrigin(origin: string): boolean {\n try {\n const parsed = new URL(origin);\n return parsed.protocol === 'http:' && (parsed.hostname === '127.0.0.1' || parsed.hostname === 'localhost');\n } catch {\n return false;\n }\n}\n\nconst HTML_ESCAPES: Record<string, string> = {\n '&': '&amp;',\n '<': '&lt;',\n '>': '&gt;',\n '\"': '&quot;',\n \"'\": '&#39;',\n};\n\nconst BASE_STYLES = `\n :root { color-scheme: dark; }\n body { font-family: system-ui, sans-serif; background: #0f172a; color: #e2e8f0; display: grid; place-items: center; min-height: 100vh; margin: 0; }\n .card { width: min(480px, 92vw); background: #111827; border: 1px solid #334155; border-radius: 16px; padding: 32px; box-shadow: 0 24px 48px rgba(15, 23, 42, 0.35); }\n h1 { margin-top: 0; }\n p, label { color: #cbd5e1; }\n code { display: block; padding: 12px; border-radius: 10px; background: #020617; overflow-wrap: anywhere; margin-bottom: 20px; }\n .button, button { display: inline-flex; justify-content: center; align-items: center; gap: 10px; border: 0; border-radius: 999px; background: #2563eb; color: white; padding: 12px 18px; text-decoration: none; font-weight: 600; cursor: pointer; }\n .auth-buttons { display: grid; gap: 12px; margin-top: 20px; }\n .button--google { background: #2563eb; }\n .button--apple { background: #000; color: #fff; border: 1px solid #1f2937; }\n .button__icon { font-size: 1.1rem; line-height: 1; }\n input[type='range'] { width: 100%; margin: 16px 0 12px; }\n .success { color: #86efac; }\n .error { color: #fca5a5; }\n`;\n\nconst NETWORK_PAGE_STYLES = `\n .nav { margin-bottom: 16px; }\n .nav a { color: #60a5fa; text-decoration: none; font-size: 0.9rem; }\n .nav a:hover { text-decoration: underline; }\n .presets { display: flex; gap: 8px; margin-bottom: 20px; }\n .preset { background: #1e293b; border: 1px solid #475569; border-radius: 8px; color: #e2e8f0; padding: 8px 14px; font-size: 0.85rem; cursor: pointer; }\n .preset:hover { background: #334155; }\n label { display: block; margin-top: 16px; font-size: 0.85rem; font-weight: 600; }\n .required { color: #f87171; }\n input[type='url'], input[type='text'] { display: block; width: 100%; box-sizing: border-box; margin-top: 6px; padding: 10px 12px; background: #020617; border: 1px solid #334155; border-radius: 8px; color: #e2e8f0; font-family: monospace; font-size: 0.85rem; }\n input[type='url']:focus, input[type='text']:focus { outline: none; border-color: #2563eb; }\n .hint { color: #94a3b8; font-size: 0.85rem; margin-top: 12px; }\n #save { margin-top: 24px; width: 100%; }\n`;\n\nfunction escapeAttr(value: string): string {\n return value.replace(/[&\"'<>]/g, (character) => HTML_ESCAPES[character] ?? character);\n}\n\nfunction validateNetworkInput(body: {\n rpcUrl?: string;\n faucetUrl?: string;\n packageId?: string;\n registryId?: string;\n}): { rpcUrl: string; faucetUrl: string; packageId: string; registryId: string } {\n const rpcUrl = (body.rpcUrl ?? '').trim();\n const faucetUrl = (body.faucetUrl ?? '').trim();\n const packageId = (body.packageId ?? '').trim();\n const registryId = (body.registryId ?? '').trim();\n\n if (!rpcUrl) {\n throw new NetworkValidationError('RPC URL is required.');\n }\n\n validateHttpUrl(rpcUrl, 'RPC URL');\n\n if (faucetUrl) {\n validateHttpUrl(faucetUrl, 'Faucet URL');\n }\n\n if (packageId && !isValidHexId(packageId)) {\n throw new NetworkValidationError('Package ID must be a valid hex address starting with 0x.');\n }\n\n if (registryId && !isValidHexId(registryId)) {\n throw new NetworkValidationError('Registry ID must be a valid hex address starting with 0x.');\n }\n\n return { rpcUrl, faucetUrl, packageId, registryId };\n}\n\nfunction validateHttpUrl(value: string, label: string): void {\n try {\n const parsed = new URL(value);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new NetworkValidationError(`${label} must use http or https protocol.`);\n }\n } catch (error) {\n if (error instanceof NetworkValidationError) {\n throw error;\n }\n throw new NetworkValidationError(`${label} is not a valid URL.`);\n }\n}\n\nfunction isValidHexId(value: string): boolean {\n return /^0x[0-9a-fA-F]{1,64}$/.test(value);\n}\n\nclass NetworkValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'NetworkValidationError';\n }\n}\n\nfunction isNetworkValidationError(error: unknown): boolean {\n return error instanceof NetworkValidationError;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,90 @@
1
+ import * as fs from 'node:fs';
2
+ import { NetworkConfig, AuthConfig, SpendingPolicy, SpendingLimit, PaymentConfig, BlobStoreConfig, EncryptionConfig } from '@hivemind-os/collective-types';
3
+
4
+ interface PipeSecurityRuntime {
5
+ platform?: NodeJS.Platform;
6
+ username?: string;
7
+ runPowerShell?: (script: string) => Promise<string>;
8
+ }
9
+ interface PipeAclSummary {
10
+ owner?: string;
11
+ identities: string[];
12
+ }
13
+ interface PipeSecurityStatus {
14
+ transport: 'windows-pipe' | 'unix-socket';
15
+ userScoped: boolean;
16
+ aclVerified: boolean;
17
+ acl?: PipeAclSummary;
18
+ note: string;
19
+ }
20
+ interface ClientValidationResult {
21
+ allowed: boolean;
22
+ source: 'windows-pid' | 'unix-socket';
23
+ reason?: string;
24
+ expectedUser?: string;
25
+ actualUser?: string;
26
+ }
27
+ declare function getDefaultIpcPath(dataDir: string, runtime?: PipeSecurityRuntime): string;
28
+
29
+ interface DaemonPerAppSpendingConfig {
30
+ limits: SpendingLimit[];
31
+ }
32
+ interface DaemonSpendingPolicy extends SpendingPolicy {
33
+ perApp?: Record<string, DaemonPerAppSpendingConfig>;
34
+ }
35
+ interface DaemonFullConfig {
36
+ network: NetworkConfig;
37
+ identity: {
38
+ dataDir: string;
39
+ };
40
+ auth: AuthConfig;
41
+ spending: DaemonSpendingPolicy;
42
+ payment: PaymentConfig;
43
+ daemon: {
44
+ ipcPath: string;
45
+ dataDir: string;
46
+ pidFile: string;
47
+ logLevel: 'debug' | 'info' | 'warn' | 'error';
48
+ logFile?: string;
49
+ };
50
+ relay: {
51
+ enabled: boolean;
52
+ endpoints: Array<{
53
+ url: string;
54
+ relayDid?: string;
55
+ }>;
56
+ autoConnect: boolean;
57
+ providerMode: boolean;
58
+ reconnectIntervalMs?: number;
59
+ heartbeatIntervalMs?: number;
60
+ };
61
+ blobstore: BlobStoreConfig;
62
+ encryption: EncryptionConfig;
63
+ provider?: {
64
+ enabled: boolean;
65
+ capabilities: Array<{
66
+ name: string;
67
+ description: string;
68
+ version: string;
69
+ priceMist: number;
70
+ currency?: string;
71
+ adapter: string;
72
+ adapterConfig?: Record<string, unknown>;
73
+ }>;
74
+ maxConcurrency?: number;
75
+ autoRegister?: boolean;
76
+ };
77
+ }
78
+ declare const configIo: {
79
+ mkdir: typeof fs.promises.mkdir;
80
+ writeFile: typeof fs.promises.writeFile;
81
+ rename: typeof fs.promises.rename;
82
+ rm: typeof fs.promises.rm;
83
+ };
84
+ declare function getDefaultConfig(): DaemonFullConfig;
85
+ declare function getConfigPath(configPath?: string): string;
86
+
87
+ declare function loadConfig(configPath?: string): DaemonFullConfig;
88
+ declare function saveConfig(config: DaemonFullConfig, configPath?: string): Promise<string>;
89
+
90
+ export { type ClientValidationResult as C, type DaemonFullConfig as D, type PipeSecurityStatus as P, type DaemonPerAppSpendingConfig as a, type DaemonSpendingPolicy as b, configIo as c, getDefaultConfig as d, getDefaultIpcPath as e, getConfigPath as g, loadConfig as l, saveConfig as s };
@@ -0,0 +1,3 @@
1
+ import 'node:fs';
2
+ import '@hivemind-os/collective-types';
3
+ export { D as DaemonFullConfig, a as DaemonPerAppSpendingConfig, b as DaemonSpendingPolicy, c as configIo, g as getConfigPath, d as getDefaultConfig, e as getDefaultIpcPath, l as loadConfig, s as saveConfig } from './config-SuloXL2_.js';
package/dist/config.js ADDED
@@ -0,0 +1,19 @@
1
+ import {
2
+ configIo,
3
+ getConfigPath,
4
+ getDefaultConfig,
5
+ loadConfig,
6
+ saveConfig
7
+ } from "./chunk-CJHYQ7RR.js";
8
+ import {
9
+ getDefaultIpcPath
10
+ } from "./chunk-NXIFS427.js";
11
+ export {
12
+ configIo,
13
+ getConfigPath,
14
+ getDefaultConfig,
15
+ getDefaultIpcPath,
16
+ loadConfig,
17
+ saveConfig
18
+ };
19
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ declare function main(): Promise<void>;
3
+
4
+ export { main };