@hivemind-os/collective-daemon 0.2.3 → 0.2.4
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.
|
@@ -62,23 +62,13 @@ var PortalServer = class {
|
|
|
62
62
|
return this.baseUrl;
|
|
63
63
|
}
|
|
64
64
|
getReauthUrl() {
|
|
65
|
+
if (!this.options.authProvider) return null;
|
|
65
66
|
return `${this.baseUrl}/auth/reauth`;
|
|
66
67
|
}
|
|
67
68
|
registerRoutes() {
|
|
68
69
|
this.server.get("/", async (_request, reply) => {
|
|
69
70
|
reply.type("text/html").send(this.renderPage());
|
|
70
71
|
});
|
|
71
|
-
this.server.get("/auth/reauth", async (_request, reply) => {
|
|
72
|
-
reply.type("text/html").send(renderReauthPage(getConfiguredProviders(this.options.config)));
|
|
73
|
-
});
|
|
74
|
-
this.server.get("/auth/google", async (request, reply) => this.startAuthFlow("google", reply, readFlow(request.query)));
|
|
75
|
-
this.server.get("/auth/apple", async (request, reply) => this.startAuthFlow("apple", reply, readFlow(request.query)));
|
|
76
|
-
this.server.get("/auth/callback", async (request, reply) => {
|
|
77
|
-
await this.handleOAuthCallback("google", request.query, reply);
|
|
78
|
-
});
|
|
79
|
-
this.server.post("/auth/apple/callback", async (request, reply) => {
|
|
80
|
-
await this.handleOAuthCallback("apple", request.body ?? {}, reply);
|
|
81
|
-
});
|
|
82
72
|
this.server.get("/api/status", async () => {
|
|
83
73
|
const auth = this.getAuthStatus();
|
|
84
74
|
return {
|
|
@@ -104,9 +94,10 @@ var PortalServer = class {
|
|
|
104
94
|
this.options.logger?.info({ configPath: this.options.configPath }, "Portal settings persisted");
|
|
105
95
|
this.setupComplete = true;
|
|
106
96
|
this.resolveCompletion();
|
|
97
|
+
const address = this.options.authProvider ? await this.options.authProvider.getAddress() : this.options.state?.address ?? "";
|
|
107
98
|
return {
|
|
108
99
|
ok: true,
|
|
109
|
-
address
|
|
100
|
+
address,
|
|
110
101
|
spendingLimitMist: nextLimit.toString()
|
|
111
102
|
};
|
|
112
103
|
} catch (error) {
|
|
@@ -121,6 +112,19 @@ var PortalServer = class {
|
|
|
121
112
|
});
|
|
122
113
|
}
|
|
123
114
|
});
|
|
115
|
+
if (this.options.authProvider) {
|
|
116
|
+
this.server.get("/auth/reauth", async (_request, reply) => {
|
|
117
|
+
reply.type("text/html").send(renderReauthPage(getConfiguredProviders(this.options.config)));
|
|
118
|
+
});
|
|
119
|
+
this.server.get("/auth/google", async (request, reply) => this.startAuthFlow("google", reply, readFlow(request.query)));
|
|
120
|
+
this.server.get("/auth/apple", async (request, reply) => this.startAuthFlow("apple", reply, readFlow(request.query)));
|
|
121
|
+
this.server.get("/auth/callback", async (request, reply) => {
|
|
122
|
+
await this.handleOAuthCallback("google", request.query, reply);
|
|
123
|
+
});
|
|
124
|
+
this.server.post("/auth/apple/callback", async (request, reply) => {
|
|
125
|
+
await this.handleOAuthCallback("apple", request.body ?? {}, reply);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
124
128
|
this.server.get("/network", async (_request, reply) => {
|
|
125
129
|
reply.type("text/html").send(renderNetworkPage(this.options.config.network));
|
|
126
130
|
});
|
|
@@ -341,9 +345,26 @@ var PortalServer = class {
|
|
|
341
345
|
return provider === "apple" ? `${this.baseUrl}/auth/apple/callback` : `${this.baseUrl}/auth/callback`;
|
|
342
346
|
}
|
|
343
347
|
getAuthStatus() {
|
|
348
|
+
if (this.options.getAuthStatus) {
|
|
349
|
+
const status = this.options.getAuthStatus();
|
|
350
|
+
if (status) return status;
|
|
351
|
+
}
|
|
352
|
+
if (!this.options.authProvider) {
|
|
353
|
+
return {
|
|
354
|
+
authMode: "ed25519",
|
|
355
|
+
authenticated: true,
|
|
356
|
+
state: "authenticated",
|
|
357
|
+
address: this.options.state?.address ?? null,
|
|
358
|
+
expiresAt: null,
|
|
359
|
+
expiresInMs: null,
|
|
360
|
+
refreshAvailable: false,
|
|
361
|
+
lastError: null,
|
|
362
|
+
updatedAt: Date.now()
|
|
363
|
+
};
|
|
364
|
+
}
|
|
344
365
|
const fallbackSession = this.options.authProvider.getSession();
|
|
345
366
|
const expiresAt = getJwtExpiryMs(fallbackSession?.jwt);
|
|
346
|
-
return
|
|
367
|
+
return {
|
|
347
368
|
authMode: "zklogin",
|
|
348
369
|
authenticated: this.options.authProvider.isAuthenticated(),
|
|
349
370
|
state: this.options.authProvider.isAuthenticated() ? "authenticated" : "reauth_required",
|
|
@@ -356,6 +377,13 @@ var PortalServer = class {
|
|
|
356
377
|
};
|
|
357
378
|
}
|
|
358
379
|
renderPage() {
|
|
380
|
+
if (!this.options.authProvider) {
|
|
381
|
+
return renderSetupPage({
|
|
382
|
+
address: this.options.state?.address ?? "",
|
|
383
|
+
dailyLimitMist: getCurrentDailyLimitMist(this.options.config),
|
|
384
|
+
setupComplete: true
|
|
385
|
+
});
|
|
386
|
+
}
|
|
359
387
|
const authStatus = this.getAuthStatus();
|
|
360
388
|
if (!this.options.authProvider.isAuthenticated()) {
|
|
361
389
|
return this.options.getAuthStatus && (authStatus.state === "expired" || authStatus.state === "reauth_required") ? renderReauthPage(getConfiguredProviders(this.options.config)) : renderWelcomePage(getConfiguredProviders(this.options.config));
|
|
@@ -1002,4 +1030,4 @@ function isNetworkValidationError(error) {
|
|
|
1002
1030
|
export {
|
|
1003
1031
|
PortalServer
|
|
1004
1032
|
};
|
|
1005
|
-
//# sourceMappingURL=chunk-
|
|
1033
|
+
//# sourceMappingURL=chunk-H5MBULPC.js.map
|
|
@@ -1 +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 & 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 '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\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":[]}
|
|
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 | null {\n if (!this.options.authProvider) return null;\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 // ── Dashboard routes (always available) ──────────────────────\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 const address =\n this.options.authProvider\n ? await this.options.authProvider.getAddress()\n : this.options.state?.address ?? '';\n return {\n ok: true,\n address,\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 // ── Auth routes (zkLogin only) ───────────────────────────────\n if (this.options.authProvider) {\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\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 if (this.options.getAuthStatus) {\n const status = this.options.getAuthStatus();\n if (status) return status;\n }\n\n if (!this.options.authProvider) {\n return {\n authMode: 'ed25519',\n authenticated: true,\n state: 'authenticated',\n address: this.options.state?.address ?? null,\n expiresAt: null,\n expiresInMs: null,\n refreshAvailable: false,\n lastError: null,\n updatedAt: Date.now(),\n };\n }\n\n const fallbackSession = this.options.authProvider.getSession();\n const expiresAt = getJwtExpiryMs(fallbackSession?.jwt);\n return {\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 if (!this.options.authProvider) {\n // ed25519 mode — show the dashboard directly\n return renderSetupPage({\n address: this.options.state?.address ?? '',\n dailyLimitMist: getCurrentDailyLimitMist(this.options.config),\n setupComplete: true,\n });\n }\n\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 & 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 '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\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":[]}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-BHN4GOYD.js";
|
|
5
5
|
import {
|
|
6
6
|
PortalServer
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-H5MBULPC.js";
|
|
8
8
|
import {
|
|
9
9
|
getConfigPath,
|
|
10
10
|
loadConfig
|
|
@@ -271,7 +271,7 @@ import {
|
|
|
271
271
|
ReputationStore,
|
|
272
272
|
StakingClient
|
|
273
273
|
} from "@hivemind-os/collective-core";
|
|
274
|
-
function buildMeshToolContext(state, dataDir) {
|
|
274
|
+
function buildMeshToolContext(state, dataDir, options) {
|
|
275
275
|
const base = {
|
|
276
276
|
did: state.did,
|
|
277
277
|
keypair: state.keypair,
|
|
@@ -285,7 +285,9 @@ function buildMeshToolContext(state, dataDir) {
|
|
|
285
285
|
encryption: state.encryption,
|
|
286
286
|
authProvider: state.authProvider,
|
|
287
287
|
relayAuthProvider: state.relayAuthProvider,
|
|
288
|
-
x402Client: state.x402Client
|
|
288
|
+
x402Client: state.x402Client,
|
|
289
|
+
portalUrl: options?.portalUrl,
|
|
290
|
+
openUrl: options?.openUrl
|
|
289
291
|
};
|
|
290
292
|
defineLazy(base, "stakingClient", () => new StakingClient(state.suiClient, { packageId: state.network.packageId }));
|
|
291
293
|
defineLazy(base, "disputeClient", () => new DisputeClient(state.suiClient, { packageId: state.network.packageId }));
|
|
@@ -352,9 +354,9 @@ async function main() {
|
|
|
352
354
|
authProvider: zkloginProvider,
|
|
353
355
|
logger
|
|
354
356
|
});
|
|
355
|
-
const
|
|
356
|
-
logger.info({ portalUrl }, "Waiting for zkLogin onboarding");
|
|
357
|
-
await openPortalUrl(
|
|
357
|
+
const portalUrl2 = await setupPortal.start();
|
|
358
|
+
logger.info({ portalUrl: portalUrl2 }, "Waiting for zkLogin onboarding");
|
|
359
|
+
await openPortalUrl(portalUrl2, logger, "continue onboarding");
|
|
358
360
|
await setupPortal.waitForAuth();
|
|
359
361
|
await setupPortal.stop();
|
|
360
362
|
setupPortal = void 0;
|
|
@@ -365,8 +367,8 @@ async function main() {
|
|
|
365
367
|
logger.info({ did: daemonState.did }, "Daemon state initialized");
|
|
366
368
|
const getAuthStatus = () => sessionMonitor?.getStatus() ?? createFallbackAuthStatus(daemonState);
|
|
367
369
|
const openReauthPortal = async (force = false) => {
|
|
368
|
-
const
|
|
369
|
-
if (!
|
|
370
|
+
const portalUrl2 = portal?.getReauthUrl() ?? null;
|
|
371
|
+
if (!portalUrl2) {
|
|
370
372
|
return {
|
|
371
373
|
portalUrl: null,
|
|
372
374
|
browserOpened: false,
|
|
@@ -375,36 +377,37 @@ async function main() {
|
|
|
375
377
|
}
|
|
376
378
|
if (!force && reauthPortalOpen) {
|
|
377
379
|
return {
|
|
378
|
-
portalUrl,
|
|
380
|
+
portalUrl: portalUrl2,
|
|
379
381
|
browserOpened: false,
|
|
380
382
|
status: getAuthStatus()
|
|
381
383
|
};
|
|
382
384
|
}
|
|
383
|
-
const browserOpened = await openPortalUrl(
|
|
385
|
+
const browserOpened = await openPortalUrl(portalUrl2, logger, "re-authenticate");
|
|
384
386
|
reauthPortalOpen ||= browserOpened;
|
|
385
387
|
return {
|
|
386
|
-
portalUrl,
|
|
388
|
+
portalUrl: portalUrl2,
|
|
387
389
|
browserOpened,
|
|
388
390
|
status: getAuthStatus()
|
|
389
391
|
};
|
|
390
392
|
};
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
logger.info({ portalUrl }, "Portal server listening");
|
|
402
|
-
}
|
|
393
|
+
portal = new PortalServer({
|
|
394
|
+
config,
|
|
395
|
+
configPath,
|
|
396
|
+
...zkloginProvider ? { authProvider: zkloginProvider } : {},
|
|
397
|
+
state: daemonState,
|
|
398
|
+
logger,
|
|
399
|
+
getAuthStatus
|
|
400
|
+
});
|
|
401
|
+
const portalUrl = await portal.start();
|
|
402
|
+
logger.info({ portalUrl }, "Portal server listening");
|
|
403
403
|
ipcServer = new IpcServer(config.daemon.ipcPath, daemonState, {
|
|
404
404
|
getAuthStatus,
|
|
405
405
|
triggerReauth: () => openReauthPortal(true)
|
|
406
406
|
});
|
|
407
|
-
ipcServer.toolContext = buildMeshToolContext(daemonState, config.daemon.dataDir
|
|
407
|
+
ipcServer.toolContext = buildMeshToolContext(daemonState, config.daemon.dataDir, {
|
|
408
|
+
portalUrl,
|
|
409
|
+
openUrl: (url) => openPortalUrl(url, logger, "open settings")
|
|
410
|
+
});
|
|
408
411
|
await ipcServer.start();
|
|
409
412
|
logger.info({ ipcPath: config.daemon.ipcPath }, "IPC server listening");
|
|
410
413
|
if (sessionMonitor) {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/auth/session-monitor.ts","../src/lifecycle.ts","../src/mcp/tool-context.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { mkdir } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\nimport pino from 'pino';\n\nimport open from 'open';\n\nimport type { DaemonAuthStatus, SessionMonitorAuthProvider } from './auth/session-monitor.js';\nimport { SessionMonitor } from './auth/session-monitor.js';\nimport { getConfigPath, loadConfig } from './config.js';\nimport { IpcServer } from './ipc/server.js';\nimport { DaemonLifecycle } from './lifecycle.js';\nimport { buildMeshToolContext } from './mcp/tool-context.js';\nimport { PortalServer, type PortalAuthProvider } from './portal/server.js';\nimport { loadProviderConfig, ProviderRuntime } from './provider/index.js';\nimport { createDaemonIdentityContext, DaemonState } from './state.js';\n\nexport async function main(): Promise<void> {\n const configPath = getConfigPath();\n const config = loadConfig(configPath);\n\n if (config.daemon.logFile) {\n await mkdir(dirname(config.daemon.logFile), { recursive: true });\n }\n\n const destination = config.daemon.logFile ? pino.destination(config.daemon.logFile) : undefined;\n const logger = pino(\n {\n name: '@hivemind-os/collective-daemon',\n level: config.daemon.logLevel,\n },\n destination,\n );\n const lifecycle = new DaemonLifecycle(config.daemon.pidFile);\n\n if (await lifecycle.isRunning()) {\n logger.info('Daemon is already running.');\n process.exit(0);\n }\n\n let state: DaemonState | undefined;\n let ipcServer: IpcServer | undefined;\n let providerRuntime: ProviderRuntime | undefined;\n let setupPortal: PortalServer | undefined;\n let portal: PortalServer | undefined;\n let sessionMonitor: SessionMonitor | undefined;\n let reauthPortalOpen = false;\n\n try {\n await lifecycle.acquireLock();\n\n const identityContext = await createDaemonIdentityContext(config);\n const zkloginProvider =\n config.auth.mode === 'zklogin' ? (identityContext.authProvider as PortalAuthProvider & SessionMonitorAuthProvider) : undefined;\n sessionMonitor = zkloginProvider\n ? new SessionMonitor({\n authProvider: zkloginProvider,\n logger,\n })\n : undefined;\n\n if (zkloginProvider && !zkloginProvider.isAuthenticated()) {\n setupPortal = new PortalServer({\n config,\n configPath,\n authProvider: zkloginProvider,\n logger,\n });\n const portalUrl = await setupPortal.start();\n logger.info({ portalUrl }, 'Waiting for zkLogin onboarding');\n await openPortalUrl(portalUrl, logger, 'continue onboarding');\n await setupPortal.waitForAuth();\n await setupPortal.stop();\n setupPortal = undefined;\n }\n\n const daemonState = await DaemonState.create(config, identityContext);\n state = daemonState;\n daemonState.setProviderRunning(false);\n logger.info({ did: daemonState.did }, 'Daemon state initialized');\n\n const getAuthStatus = () => sessionMonitor?.getStatus() ?? createFallbackAuthStatus(daemonState);\n\n const openReauthPortal = async (force = false) => {\n const portalUrl = portal?.getReauthUrl() ?? null;\n if (!portalUrl) {\n return {\n portalUrl: null,\n browserOpened: false,\n status: getAuthStatus(),\n };\n }\n\n if (!force && reauthPortalOpen) {\n return {\n portalUrl,\n browserOpened: false,\n status: getAuthStatus(),\n };\n }\n\n const browserOpened = await openPortalUrl(portalUrl, logger, 're-authenticate');\n reauthPortalOpen ||= browserOpened;\n return {\n portalUrl,\n browserOpened,\n status: getAuthStatus(),\n };\n };\n\n if (zkloginProvider) {\n portal = new PortalServer({\n config,\n configPath,\n authProvider: zkloginProvider,\n state: daemonState,\n logger,\n getAuthStatus,\n });\n const portalUrl = await portal.start();\n logger.info({ portalUrl }, 'Portal server listening');\n }\n\n ipcServer = new IpcServer(config.daemon.ipcPath, daemonState, {\n getAuthStatus,\n triggerReauth: () => openReauthPortal(true),\n });\n ipcServer.toolContext = buildMeshToolContext(daemonState, config.daemon.dataDir);\n await ipcServer.start();\n logger.info({ ipcPath: config.daemon.ipcPath }, 'IPC server listening');\n\n if (sessionMonitor) {\n sessionMonitor.on('session:expiring', (status) => {\n ipcServer?.notifyAuthStatusChanged(status);\n });\n sessionMonitor.on('session:expired', async (status) => {\n ipcServer?.notifyAuthStatusChanged(status);\n await openReauthPortal(false);\n });\n sessionMonitor.on('session:refreshed', (status) => {\n reauthPortalOpen = false;\n ipcServer?.notifyAuthStatusChanged(status);\n });\n sessionMonitor.on('session:reauth_required', async (status) => {\n ipcServer?.notifyAuthStatusChanged(status);\n await openReauthPortal(false);\n });\n sessionMonitor.start();\n }\n\n const providerConfig = loadProviderConfig(config);\n if (providerConfig?.enabled) {\n const ipcRef = ipcServer;\n providerRuntime = new ProviderRuntime({\n state: daemonState,\n providerConfig,\n cursorDbPath: join(config.daemon.dataDir, 'provider-cursors.db'),\n relayConfig: config.relay,\n mcpSamplingFn: ipcRef\n ? async (appName, params) => {\n const server = ipcRef.getMcpServerForApp(appName);\n if (!server) {\n throw new Error(`No MCP client connected with appName \"${appName}\"`);\n }\n return server.createMessage(params);\n }\n : undefined,\n broadcastNotification: ipcRef\n ? (method, params) => ipcRef.broadcastNotification(method, params)\n : undefined,\n });\n await providerRuntime.start();\n daemonState.setProviderRunning(true);\n logger.info('Provider runtime started');\n }\n\n lifecycle.setupSignalHandlers(async () => {\n logger.info('Shutting down...');\n sessionMonitor?.stop();\n state?.setProviderRunning(false);\n await providerRuntime?.stop();\n await ipcServer?.stop();\n await portal?.stop();\n await state?.shutdown();\n await lifecycle.releaseLock();\n logger.info('Daemon stopped');\n });\n } catch (error) {\n sessionMonitor?.stop();\n state?.setProviderRunning(false);\n await cleanupWithLogging(logger, 'provider runtime', () => providerRuntime?.stop());\n await cleanupWithLogging(logger, 'IPC server', () => ipcServer?.stop());\n await cleanupWithLogging(logger, 'setup portal server', () => setupPortal?.stop());\n await cleanupWithLogging(logger, 'portal server', () => portal?.stop());\n await cleanupWithLogging(logger, 'daemon state', () => state?.shutdown());\n await cleanupWithLogging(logger, 'daemon lock', () => lifecycle.releaseLock());\n throw error;\n }\n}\n\nasync function cleanupWithLogging(\n logger: { warn: (bindings: { err: unknown }, message: string) => void },\n label: string,\n cleanup: () => Promise<void> | undefined,\n): Promise<void> {\n try {\n await cleanup();\n } catch (error) {\n logger.warn({ err: error }, `Failed to clean up ${label}.`);\n }\n}\n\nfunction createFallbackAuthStatus(state: DaemonState): DaemonAuthStatus {\n const authenticated = state.authProvider.isAuthenticated();\n return {\n authMode: state.authProvider.mode,\n authenticated,\n state: authenticated ? 'authenticated' : 'reauth_required',\n address: authenticated ? state.address : null,\n expiresAt: null,\n expiresInMs: null,\n refreshAvailable: false,\n lastError: null,\n updatedAt: Date.now(),\n };\n}\n\nasync function openPortalUrl(\n portalUrl: string,\n logger: {\n info: (bindings: { portalUrl: string }, message: string) => void;\n warn: (bindings: { portalUrl: string; err?: unknown }, message: string) => void;\n },\n action: string,\n): Promise<boolean> {\n if (isHeadlessEnvironment()) {\n logger.warn({ portalUrl }, `Headless environment detected. Open the portal URL manually to ${action}.`);\n return false;\n }\n\n try {\n await open(portalUrl);\n return true;\n } catch (error) {\n logger.warn({ err: error, portalUrl }, 'Failed to open browser automatically.');\n logger.info({ portalUrl }, `Open the portal URL manually to ${action}.`);\n return false;\n }\n}\n\nfunction isHeadlessEnvironment(): boolean {\n if (process.env.COLLECTIVE_HEADLESS === '1' || process.env.CI === 'true') {\n return true;\n }\n\n if (process.env.SSH_CONNECTION || process.env.SSH_TTY) {\n return true;\n }\n\n return process.platform === 'linux' && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;\n}\n\nmain().catch((error) => {\n console.error('Fatal:', error);\n process.exit(1);\n});\n","import { EventEmitter } from 'node:events';\n\nimport type { AuthMode, StoredZkLoginSession } from '@hivemind-os/collective-core';\n\nexport type DaemonAuthState = 'authenticated' | 'expiring' | 'expired' | 'reauth_required';\n\nexport interface DaemonAuthStatus {\n authMode: AuthMode;\n authenticated: boolean;\n state: DaemonAuthState;\n address: string | null;\n expiresAt: number | null;\n expiresInMs: number | null;\n refreshAvailable: boolean;\n lastError: string | null;\n updatedAt: number;\n}\n\nexport interface SessionMonitorAuthProvider {\n mode: 'zklogin';\n isAuthenticated(): boolean;\n getSession(): StoredZkLoginSession | null;\n getSessionExpiryMs(session?: StoredZkLoginSession | null): number | null;\n refreshSessionIfNeeded(\n currentEpoch?: number,\n options?: { force?: boolean; invalidateOnFailure?: boolean; throwOnFailure?: boolean },\n ): Promise<StoredZkLoginSession | null>;\n clearSession(session?: StoredZkLoginSession | null): Promise<void>;\n}\n\nexport interface SessionMonitorOptions {\n authProvider: SessionMonitorAuthProvider;\n checkIntervalMs?: number;\n warningWindowMs?: number;\n logger?: {\n debug?: (payload: unknown, message?: string) => void;\n info?: (payload: unknown, message?: string) => void;\n warn?: (payload: unknown, message?: string) => void;\n };\n}\n\ntype SessionMonitorEvents = {\n 'session:expiring': [DaemonAuthStatus];\n 'session:expired': [DaemonAuthStatus];\n 'session:refreshed': [DaemonAuthStatus];\n 'session:reauth_required': [DaemonAuthStatus];\n};\n\nconst DEFAULT_CHECK_INTERVAL_MS = 60_000;\nconst DEFAULT_WARNING_WINDOW_MS = 5 * 60 * 1000;\n\nexport class SessionMonitor extends EventEmitter {\n private readonly checkIntervalMs: number;\n private readonly warningWindowMs: number;\n private readonly logger;\n private interval?: ReturnType<typeof setInterval>;\n private checkInFlight?: Promise<DaemonAuthStatus>;\n private status: DaemonAuthStatus;\n\n constructor(private readonly options: SessionMonitorOptions) {\n super();\n this.checkIntervalMs = options.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;\n this.warningWindowMs = options.warningWindowMs ?? DEFAULT_WARNING_WINDOW_MS;\n this.logger = options.logger;\n this.status = this.createStatus(null, 'reauth_required');\n }\n\n override on<EventName extends keyof SessionMonitorEvents>(\n eventName: EventName,\n listener: (...args: SessionMonitorEvents[EventName]) => void,\n ): this {\n return super.on(eventName, listener);\n }\n\n override emit<EventName extends keyof SessionMonitorEvents>(eventName: EventName, ...args: SessionMonitorEvents[EventName]): boolean {\n return super.emit(eventName, ...args);\n }\n\n start(): void {\n if (this.interval) {\n return;\n }\n\n void this.checkNow();\n this.interval = setInterval(() => {\n void this.checkNow();\n }, this.checkIntervalMs);\n }\n\n stop(): void {\n if (!this.interval) {\n return;\n }\n\n clearInterval(this.interval);\n this.interval = undefined;\n }\n\n getStatus(): DaemonAuthStatus {\n return { ...this.status };\n }\n\n async checkNow(): Promise<DaemonAuthStatus> {\n if (this.checkInFlight) {\n return await this.checkInFlight;\n }\n\n this.checkInFlight = this.evaluate().finally(() => {\n this.checkInFlight = undefined;\n });\n return await this.checkInFlight;\n }\n\n private async evaluate(): Promise<DaemonAuthStatus> {\n const previous = this.status;\n const session = this.options.authProvider.getSession();\n if (!session || !this.options.authProvider.isAuthenticated()) {\n const status = this.updateStatus(this.createStatus(session, 'reauth_required'));\n if (previous.state !== status.state || previous.address !== status.address) {\n this.emit('session:reauth_required', { ...status });\n }\n return status;\n }\n\n const expiresAt = this.options.authProvider.getSessionExpiryMs(session);\n const now = Date.now();\n const expiresInMs = expiresAt === null ? null : expiresAt - now;\n\n if (expiresInMs !== null && expiresInMs <= 0) {\n const expiredStatus = this.updateStatus(this.createStatus(session, 'expired', null, expiresAt, expiresInMs));\n if (previous.state !== expiredStatus.state || previous.expiresAt !== expiredStatus.expiresAt) {\n this.emit('session:expired', { ...expiredStatus });\n }\n await this.options.authProvider.clearSession(session);\n const reauthStatus = this.updateStatus(\n this.createStatus(null, 'reauth_required', 'Authentication expired. Please re-authenticate via the daemon portal.'),\n );\n this.emit('session:reauth_required', { ...reauthStatus });\n this.logger?.warn?.({ expiresAt }, 'zkLogin session expired.');\n return reauthStatus;\n }\n\n if (expiresInMs !== null && expiresInMs <= this.warningWindowMs) {\n const expiringStatus = this.updateStatus(this.createStatus(session, 'expiring', null, expiresAt, expiresInMs));\n if (previous.state !== expiringStatus.state || previous.expiresAt !== expiringStatus.expiresAt) {\n this.emit('session:expiring', { ...expiringStatus });\n }\n\n try {\n const refreshed = await this.options.authProvider.refreshSessionIfNeeded(undefined, {\n force: true,\n invalidateOnFailure: true,\n throwOnFailure: true,\n });\n if (!refreshed) {\n const reauthStatus = this.updateStatus(\n this.createStatus(null, 'reauth_required', 'Authentication expired. Please re-authenticate via the daemon portal.'),\n );\n this.emit('session:reauth_required', { ...reauthStatus });\n return reauthStatus;\n }\n\n const refreshedStatus = this.updateStatus(this.createStatus(refreshed, 'authenticated'));\n this.emit('session:refreshed', { ...refreshedStatus });\n this.logger?.info?.({ expiresAt: refreshedStatus.expiresAt }, 'zkLogin session refreshed.');\n return refreshedStatus;\n } catch (error) {\n const detail = error instanceof Error && error.message ? error.message : 'Authentication expired. Please re-authenticate via the daemon portal.';\n const reauthStatus = this.updateStatus(this.createStatus(null, 'reauth_required', detail));\n this.emit('session:reauth_required', { ...reauthStatus });\n this.logger?.warn?.({ err: error }, 'zkLogin session refresh failed.');\n return reauthStatus;\n }\n }\n\n const refreshed = await this.options.authProvider.refreshSessionIfNeeded();\n if (!refreshed) {\n const reauthStatus = this.updateStatus(\n this.createStatus(null, 'reauth_required', 'Authentication expired. Please re-authenticate via the daemon portal.'),\n );\n if (previous.state !== reauthStatus.state || previous.address !== reauthStatus.address) {\n this.emit('session:reauth_required', { ...reauthStatus });\n }\n return reauthStatus;\n }\n\n if (refreshed.jwt !== session.jwt || refreshed.updatedAt !== session.updatedAt) {\n const refreshedStatus = this.updateStatus(this.createStatus(refreshed, 'authenticated'));\n this.emit('session:refreshed', { ...refreshedStatus });\n return refreshedStatus;\n }\n\n return this.updateStatus(this.createStatus(refreshed, 'authenticated'));\n }\n\n private createStatus(\n session: StoredZkLoginSession | null,\n state: DaemonAuthState,\n lastError: string | null = null,\n expiresAt = session ? this.options.authProvider.getSessionExpiryMs(session) : null,\n expiresInMs = expiresAt === null ? null : expiresAt - Date.now(),\n ): DaemonAuthStatus {\n return {\n authMode: this.options.authProvider.mode,\n authenticated: state === 'authenticated' || state === 'expiring',\n state,\n address: session?.address ?? null,\n expiresAt,\n expiresInMs,\n refreshAvailable: Boolean(session?.refreshToken),\n lastError,\n updatedAt: Date.now(),\n };\n }\n\n private updateStatus(nextStatus: DaemonAuthStatus): DaemonAuthStatus {\n this.status = nextStatus;\n return nextStatus;\n }\n}\n","import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nexport class DaemonLifecycle {\n constructor(private readonly pidFilePath: string) {}\n\n async acquireLock(): Promise<void> {\n await mkdir(dirname(this.pidFilePath), { recursive: true, mode: 0o700 });\n\n try {\n await writePrivateFile(this.pidFilePath, `${process.pid}\\n`);\n return;\n } catch (error) {\n if (!isErrnoException(error, 'EEXIST')) {\n throw error;\n }\n }\n\n if (await this.isRunning()) {\n throw new Error(`Daemon is already running (pid file: ${this.pidFilePath}).`);\n }\n\n await rm(this.pidFilePath, { force: true });\n await writePrivateFile(this.pidFilePath, `${process.pid}\\n`);\n }\n\n async isRunning(): Promise<boolean> {\n try {\n const contents = await readFile(this.pidFilePath, 'utf8');\n const pid = Number.parseInt(contents.trim(), 10);\n if (!Number.isInteger(pid) || pid <= 0) {\n return false;\n }\n\n if (isProcessRunning(pid)) {\n return true;\n }\n\n await rm(this.pidFilePath, { force: true });\n return false;\n } catch (error) {\n if (isErrnoException(error, 'ENOENT')) {\n return false;\n }\n\n throw error;\n }\n }\n\n async releaseLock(): Promise<void> {\n await rm(this.pidFilePath, { force: true });\n }\n\n setupSignalHandlers(onShutdown: () => Promise<void>): void {\n let shuttingDown = false;\n\n const handleSignal = (signal: NodeJS.Signals) => {\n if (shuttingDown) {\n return;\n }\n\n shuttingDown = true;\n void (async () => {\n try {\n await onShutdown();\n process.exit(0);\n } catch (error) {\n console.error(`Failed to shut down cleanly after ${signal}:`, error);\n process.exit(1);\n }\n })();\n };\n\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n handleSignal(signal);\n });\n }\n }\n}\n\nasync function writePrivateFile(path: string, contents: string): Promise<void> {\n await writeFile(path, contents, { encoding: 'utf8', flag: 'wx', mode: 0o600 });\n await chmod(path, 0o600);\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return !isErrnoException(error, 'ESRCH');\n }\n}\n\nfunction isErrnoException(error: unknown, code: string): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error && error.code === code;\n}\n","import { join } from 'node:path';\n\nimport {\n DisputeClient,\n MarketplaceClient,\n PaymentRailSelector,\n RelayRegistryClient,\n ReputationEventPublisher,\n ReputationStore,\n StakingClient,\n} from '@hivemind-os/collective-core';\nimport type { MeshToolContext } from '@hivemind-os/collective-mcp-server';\n\nimport type { DaemonState } from '../state.js';\n\n/**\n * Build a {@link MeshToolContext} from daemon state so the\n * `@hivemind-os/collective-mcp-server` tool handlers can run inside the daemon.\n *\n * Optional clients are lazily instantiated on first access to avoid\n * allocating resources (SQLite databases, objects) that may never be used.\n */\nexport function buildMeshToolContext(state: DaemonState, dataDir: string): MeshToolContext {\n const base: MeshToolContext = {\n did: state.did,\n keypair: state.keypair,\n suiClient: state.suiClient,\n registryClient: state.registryClient,\n taskClient: state.taskClient,\n agentCache: state.agentCache,\n blobStore: state.blobStore,\n spendingPolicy: state.spendingPolicy,\n networkConfig: state.network,\n encryption: state.encryption,\n authProvider: state.authProvider,\n relayAuthProvider: state.relayAuthProvider,\n x402Client: state.x402Client,\n };\n\n // Lazy getters for optional clients — instantiated on first access\n defineLazy(base, 'stakingClient', () => new StakingClient(state.suiClient, { packageId: state.network.packageId }));\n defineLazy(base, 'disputeClient', () => new DisputeClient(state.suiClient, { packageId: state.network.packageId }));\n defineLazy(base, 'marketplaceClient', () => new MarketplaceClient(state.suiClient, state.network));\n defineLazy(base, 'relayRegistryClient', () => new RelayRegistryClient(state.suiClient, { packageId: state.network.packageId }));\n defineLazy(base, 'paymentRailSelector', () => new PaymentRailSelector());\n defineLazy(base, 'reputationPublisher', () => new ReputationEventPublisher(state.blobStore, state.authProvider));\n defineLazy(base, 'reputationStore', () => new ReputationStore(join(dataDir, 'reputation.sqlite')));\n\n return base;\n}\n\nfunction defineLazy<T extends object, K extends keyof T>(obj: T, key: K, factory: () => T[K]): void {\n let cached: T[K] | undefined;\n Object.defineProperty(obj, key, {\n configurable: true,\n enumerable: true,\n get() {\n if (cached === undefined) {\n cached = factory();\n }\n return cached;\n },\n });\n}\n"],"mappings":"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","names":["mkdir","dirname","join","refreshed","mkdir","dirname","join"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/auth/session-monitor.ts","../src/lifecycle.ts","../src/mcp/tool-context.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { mkdir } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\nimport pino from 'pino';\n\nimport open from 'open';\n\nimport type { DaemonAuthStatus, SessionMonitorAuthProvider } from './auth/session-monitor.js';\nimport { SessionMonitor } from './auth/session-monitor.js';\nimport { getConfigPath, loadConfig } from './config.js';\nimport { IpcServer } from './ipc/server.js';\nimport { DaemonLifecycle } from './lifecycle.js';\nimport { buildMeshToolContext } from './mcp/tool-context.js';\nimport { PortalServer, type PortalAuthProvider } from './portal/server.js';\nimport { loadProviderConfig, ProviderRuntime } from './provider/index.js';\nimport { createDaemonIdentityContext, DaemonState } from './state.js';\n\nexport async function main(): Promise<void> {\n const configPath = getConfigPath();\n const config = loadConfig(configPath);\n\n if (config.daemon.logFile) {\n await mkdir(dirname(config.daemon.logFile), { recursive: true });\n }\n\n const destination = config.daemon.logFile ? pino.destination(config.daemon.logFile) : undefined;\n const logger = pino(\n {\n name: '@hivemind-os/collective-daemon',\n level: config.daemon.logLevel,\n },\n destination,\n );\n const lifecycle = new DaemonLifecycle(config.daemon.pidFile);\n\n if (await lifecycle.isRunning()) {\n logger.info('Daemon is already running.');\n process.exit(0);\n }\n\n let state: DaemonState | undefined;\n let ipcServer: IpcServer | undefined;\n let providerRuntime: ProviderRuntime | undefined;\n let setupPortal: PortalServer | undefined;\n let portal: PortalServer | undefined;\n let sessionMonitor: SessionMonitor | undefined;\n let reauthPortalOpen = false;\n\n try {\n await lifecycle.acquireLock();\n\n const identityContext = await createDaemonIdentityContext(config);\n const zkloginProvider =\n config.auth.mode === 'zklogin' ? (identityContext.authProvider as PortalAuthProvider & SessionMonitorAuthProvider) : undefined;\n sessionMonitor = zkloginProvider\n ? new SessionMonitor({\n authProvider: zkloginProvider,\n logger,\n })\n : undefined;\n\n if (zkloginProvider && !zkloginProvider.isAuthenticated()) {\n setupPortal = new PortalServer({\n config,\n configPath,\n authProvider: zkloginProvider,\n logger,\n });\n const portalUrl = await setupPortal.start();\n logger.info({ portalUrl }, 'Waiting for zkLogin onboarding');\n await openPortalUrl(portalUrl, logger, 'continue onboarding');\n await setupPortal.waitForAuth();\n await setupPortal.stop();\n setupPortal = undefined;\n }\n\n const daemonState = await DaemonState.create(config, identityContext);\n state = daemonState;\n daemonState.setProviderRunning(false);\n logger.info({ did: daemonState.did }, 'Daemon state initialized');\n\n const getAuthStatus = () => sessionMonitor?.getStatus() ?? createFallbackAuthStatus(daemonState);\n\n const openReauthPortal = async (force = false) => {\n const portalUrl = portal?.getReauthUrl() ?? null;\n if (!portalUrl) {\n return {\n portalUrl: null,\n browserOpened: false,\n status: getAuthStatus(),\n };\n }\n\n if (!force && reauthPortalOpen) {\n return {\n portalUrl,\n browserOpened: false,\n status: getAuthStatus(),\n };\n }\n\n const browserOpened = await openPortalUrl(portalUrl, logger, 're-authenticate');\n reauthPortalOpen ||= browserOpened;\n return {\n portalUrl,\n browserOpened,\n status: getAuthStatus(),\n };\n };\n\n portal = new PortalServer({\n config,\n configPath,\n ...(zkloginProvider ? { authProvider: zkloginProvider } : {}),\n state: daemonState,\n logger,\n getAuthStatus,\n });\n const portalUrl = await portal.start();\n logger.info({ portalUrl }, 'Portal server listening');\n\n ipcServer = new IpcServer(config.daemon.ipcPath, daemonState, {\n getAuthStatus,\n triggerReauth: () => openReauthPortal(true),\n });\n ipcServer.toolContext = buildMeshToolContext(daemonState, config.daemon.dataDir, {\n portalUrl,\n openUrl: (url: string) => openPortalUrl(url, logger, 'open settings'),\n });\n await ipcServer.start();\n logger.info({ ipcPath: config.daemon.ipcPath }, 'IPC server listening');\n\n if (sessionMonitor) {\n sessionMonitor.on('session:expiring', (status) => {\n ipcServer?.notifyAuthStatusChanged(status);\n });\n sessionMonitor.on('session:expired', async (status) => {\n ipcServer?.notifyAuthStatusChanged(status);\n await openReauthPortal(false);\n });\n sessionMonitor.on('session:refreshed', (status) => {\n reauthPortalOpen = false;\n ipcServer?.notifyAuthStatusChanged(status);\n });\n sessionMonitor.on('session:reauth_required', async (status) => {\n ipcServer?.notifyAuthStatusChanged(status);\n await openReauthPortal(false);\n });\n sessionMonitor.start();\n }\n\n const providerConfig = loadProviderConfig(config);\n if (providerConfig?.enabled) {\n const ipcRef = ipcServer;\n providerRuntime = new ProviderRuntime({\n state: daemonState,\n providerConfig,\n cursorDbPath: join(config.daemon.dataDir, 'provider-cursors.db'),\n relayConfig: config.relay,\n mcpSamplingFn: ipcRef\n ? async (appName, params) => {\n const server = ipcRef.getMcpServerForApp(appName);\n if (!server) {\n throw new Error(`No MCP client connected with appName \"${appName}\"`);\n }\n return server.createMessage(params);\n }\n : undefined,\n broadcastNotification: ipcRef\n ? (method, params) => ipcRef.broadcastNotification(method, params)\n : undefined,\n });\n await providerRuntime.start();\n daemonState.setProviderRunning(true);\n logger.info('Provider runtime started');\n }\n\n lifecycle.setupSignalHandlers(async () => {\n logger.info('Shutting down...');\n sessionMonitor?.stop();\n state?.setProviderRunning(false);\n await providerRuntime?.stop();\n await ipcServer?.stop();\n await portal?.stop();\n await state?.shutdown();\n await lifecycle.releaseLock();\n logger.info('Daemon stopped');\n });\n } catch (error) {\n sessionMonitor?.stop();\n state?.setProviderRunning(false);\n await cleanupWithLogging(logger, 'provider runtime', () => providerRuntime?.stop());\n await cleanupWithLogging(logger, 'IPC server', () => ipcServer?.stop());\n await cleanupWithLogging(logger, 'setup portal server', () => setupPortal?.stop());\n await cleanupWithLogging(logger, 'portal server', () => portal?.stop());\n await cleanupWithLogging(logger, 'daemon state', () => state?.shutdown());\n await cleanupWithLogging(logger, 'daemon lock', () => lifecycle.releaseLock());\n throw error;\n }\n}\n\nasync function cleanupWithLogging(\n logger: { warn: (bindings: { err: unknown }, message: string) => void },\n label: string,\n cleanup: () => Promise<void> | undefined,\n): Promise<void> {\n try {\n await cleanup();\n } catch (error) {\n logger.warn({ err: error }, `Failed to clean up ${label}.`);\n }\n}\n\nfunction createFallbackAuthStatus(state: DaemonState): DaemonAuthStatus {\n const authenticated = state.authProvider.isAuthenticated();\n return {\n authMode: state.authProvider.mode,\n authenticated,\n state: authenticated ? 'authenticated' : 'reauth_required',\n address: authenticated ? state.address : null,\n expiresAt: null,\n expiresInMs: null,\n refreshAvailable: false,\n lastError: null,\n updatedAt: Date.now(),\n };\n}\n\nasync function openPortalUrl(\n portalUrl: string,\n logger: {\n info: (bindings: { portalUrl: string }, message: string) => void;\n warn: (bindings: { portalUrl: string; err?: unknown }, message: string) => void;\n },\n action: string,\n): Promise<boolean> {\n if (isHeadlessEnvironment()) {\n logger.warn({ portalUrl }, `Headless environment detected. Open the portal URL manually to ${action}.`);\n return false;\n }\n\n try {\n await open(portalUrl);\n return true;\n } catch (error) {\n logger.warn({ err: error, portalUrl }, 'Failed to open browser automatically.');\n logger.info({ portalUrl }, `Open the portal URL manually to ${action}.`);\n return false;\n }\n}\n\nfunction isHeadlessEnvironment(): boolean {\n if (process.env.COLLECTIVE_HEADLESS === '1' || process.env.CI === 'true') {\n return true;\n }\n\n if (process.env.SSH_CONNECTION || process.env.SSH_TTY) {\n return true;\n }\n\n return process.platform === 'linux' && !process.env.DISPLAY && !process.env.WAYLAND_DISPLAY;\n}\n\nmain().catch((error) => {\n console.error('Fatal:', error);\n process.exit(1);\n});\n","import { EventEmitter } from 'node:events';\n\nimport type { AuthMode, StoredZkLoginSession } from '@hivemind-os/collective-core';\n\nexport type DaemonAuthState = 'authenticated' | 'expiring' | 'expired' | 'reauth_required';\n\nexport interface DaemonAuthStatus {\n authMode: AuthMode;\n authenticated: boolean;\n state: DaemonAuthState;\n address: string | null;\n expiresAt: number | null;\n expiresInMs: number | null;\n refreshAvailable: boolean;\n lastError: string | null;\n updatedAt: number;\n}\n\nexport interface SessionMonitorAuthProvider {\n mode: 'zklogin';\n isAuthenticated(): boolean;\n getSession(): StoredZkLoginSession | null;\n getSessionExpiryMs(session?: StoredZkLoginSession | null): number | null;\n refreshSessionIfNeeded(\n currentEpoch?: number,\n options?: { force?: boolean; invalidateOnFailure?: boolean; throwOnFailure?: boolean },\n ): Promise<StoredZkLoginSession | null>;\n clearSession(session?: StoredZkLoginSession | null): Promise<void>;\n}\n\nexport interface SessionMonitorOptions {\n authProvider: SessionMonitorAuthProvider;\n checkIntervalMs?: number;\n warningWindowMs?: number;\n logger?: {\n debug?: (payload: unknown, message?: string) => void;\n info?: (payload: unknown, message?: string) => void;\n warn?: (payload: unknown, message?: string) => void;\n };\n}\n\ntype SessionMonitorEvents = {\n 'session:expiring': [DaemonAuthStatus];\n 'session:expired': [DaemonAuthStatus];\n 'session:refreshed': [DaemonAuthStatus];\n 'session:reauth_required': [DaemonAuthStatus];\n};\n\nconst DEFAULT_CHECK_INTERVAL_MS = 60_000;\nconst DEFAULT_WARNING_WINDOW_MS = 5 * 60 * 1000;\n\nexport class SessionMonitor extends EventEmitter {\n private readonly checkIntervalMs: number;\n private readonly warningWindowMs: number;\n private readonly logger;\n private interval?: ReturnType<typeof setInterval>;\n private checkInFlight?: Promise<DaemonAuthStatus>;\n private status: DaemonAuthStatus;\n\n constructor(private readonly options: SessionMonitorOptions) {\n super();\n this.checkIntervalMs = options.checkIntervalMs ?? DEFAULT_CHECK_INTERVAL_MS;\n this.warningWindowMs = options.warningWindowMs ?? DEFAULT_WARNING_WINDOW_MS;\n this.logger = options.logger;\n this.status = this.createStatus(null, 'reauth_required');\n }\n\n override on<EventName extends keyof SessionMonitorEvents>(\n eventName: EventName,\n listener: (...args: SessionMonitorEvents[EventName]) => void,\n ): this {\n return super.on(eventName, listener);\n }\n\n override emit<EventName extends keyof SessionMonitorEvents>(eventName: EventName, ...args: SessionMonitorEvents[EventName]): boolean {\n return super.emit(eventName, ...args);\n }\n\n start(): void {\n if (this.interval) {\n return;\n }\n\n void this.checkNow();\n this.interval = setInterval(() => {\n void this.checkNow();\n }, this.checkIntervalMs);\n }\n\n stop(): void {\n if (!this.interval) {\n return;\n }\n\n clearInterval(this.interval);\n this.interval = undefined;\n }\n\n getStatus(): DaemonAuthStatus {\n return { ...this.status };\n }\n\n async checkNow(): Promise<DaemonAuthStatus> {\n if (this.checkInFlight) {\n return await this.checkInFlight;\n }\n\n this.checkInFlight = this.evaluate().finally(() => {\n this.checkInFlight = undefined;\n });\n return await this.checkInFlight;\n }\n\n private async evaluate(): Promise<DaemonAuthStatus> {\n const previous = this.status;\n const session = this.options.authProvider.getSession();\n if (!session || !this.options.authProvider.isAuthenticated()) {\n const status = this.updateStatus(this.createStatus(session, 'reauth_required'));\n if (previous.state !== status.state || previous.address !== status.address) {\n this.emit('session:reauth_required', { ...status });\n }\n return status;\n }\n\n const expiresAt = this.options.authProvider.getSessionExpiryMs(session);\n const now = Date.now();\n const expiresInMs = expiresAt === null ? null : expiresAt - now;\n\n if (expiresInMs !== null && expiresInMs <= 0) {\n const expiredStatus = this.updateStatus(this.createStatus(session, 'expired', null, expiresAt, expiresInMs));\n if (previous.state !== expiredStatus.state || previous.expiresAt !== expiredStatus.expiresAt) {\n this.emit('session:expired', { ...expiredStatus });\n }\n await this.options.authProvider.clearSession(session);\n const reauthStatus = this.updateStatus(\n this.createStatus(null, 'reauth_required', 'Authentication expired. Please re-authenticate via the daemon portal.'),\n );\n this.emit('session:reauth_required', { ...reauthStatus });\n this.logger?.warn?.({ expiresAt }, 'zkLogin session expired.');\n return reauthStatus;\n }\n\n if (expiresInMs !== null && expiresInMs <= this.warningWindowMs) {\n const expiringStatus = this.updateStatus(this.createStatus(session, 'expiring', null, expiresAt, expiresInMs));\n if (previous.state !== expiringStatus.state || previous.expiresAt !== expiringStatus.expiresAt) {\n this.emit('session:expiring', { ...expiringStatus });\n }\n\n try {\n const refreshed = await this.options.authProvider.refreshSessionIfNeeded(undefined, {\n force: true,\n invalidateOnFailure: true,\n throwOnFailure: true,\n });\n if (!refreshed) {\n const reauthStatus = this.updateStatus(\n this.createStatus(null, 'reauth_required', 'Authentication expired. Please re-authenticate via the daemon portal.'),\n );\n this.emit('session:reauth_required', { ...reauthStatus });\n return reauthStatus;\n }\n\n const refreshedStatus = this.updateStatus(this.createStatus(refreshed, 'authenticated'));\n this.emit('session:refreshed', { ...refreshedStatus });\n this.logger?.info?.({ expiresAt: refreshedStatus.expiresAt }, 'zkLogin session refreshed.');\n return refreshedStatus;\n } catch (error) {\n const detail = error instanceof Error && error.message ? error.message : 'Authentication expired. Please re-authenticate via the daemon portal.';\n const reauthStatus = this.updateStatus(this.createStatus(null, 'reauth_required', detail));\n this.emit('session:reauth_required', { ...reauthStatus });\n this.logger?.warn?.({ err: error }, 'zkLogin session refresh failed.');\n return reauthStatus;\n }\n }\n\n const refreshed = await this.options.authProvider.refreshSessionIfNeeded();\n if (!refreshed) {\n const reauthStatus = this.updateStatus(\n this.createStatus(null, 'reauth_required', 'Authentication expired. Please re-authenticate via the daemon portal.'),\n );\n if (previous.state !== reauthStatus.state || previous.address !== reauthStatus.address) {\n this.emit('session:reauth_required', { ...reauthStatus });\n }\n return reauthStatus;\n }\n\n if (refreshed.jwt !== session.jwt || refreshed.updatedAt !== session.updatedAt) {\n const refreshedStatus = this.updateStatus(this.createStatus(refreshed, 'authenticated'));\n this.emit('session:refreshed', { ...refreshedStatus });\n return refreshedStatus;\n }\n\n return this.updateStatus(this.createStatus(refreshed, 'authenticated'));\n }\n\n private createStatus(\n session: StoredZkLoginSession | null,\n state: DaemonAuthState,\n lastError: string | null = null,\n expiresAt = session ? this.options.authProvider.getSessionExpiryMs(session) : null,\n expiresInMs = expiresAt === null ? null : expiresAt - Date.now(),\n ): DaemonAuthStatus {\n return {\n authMode: this.options.authProvider.mode,\n authenticated: state === 'authenticated' || state === 'expiring',\n state,\n address: session?.address ?? null,\n expiresAt,\n expiresInMs,\n refreshAvailable: Boolean(session?.refreshToken),\n lastError,\n updatedAt: Date.now(),\n };\n }\n\n private updateStatus(nextStatus: DaemonAuthStatus): DaemonAuthStatus {\n this.status = nextStatus;\n return nextStatus;\n }\n}\n","import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\n\nexport class DaemonLifecycle {\n constructor(private readonly pidFilePath: string) {}\n\n async acquireLock(): Promise<void> {\n await mkdir(dirname(this.pidFilePath), { recursive: true, mode: 0o700 });\n\n try {\n await writePrivateFile(this.pidFilePath, `${process.pid}\\n`);\n return;\n } catch (error) {\n if (!isErrnoException(error, 'EEXIST')) {\n throw error;\n }\n }\n\n if (await this.isRunning()) {\n throw new Error(`Daemon is already running (pid file: ${this.pidFilePath}).`);\n }\n\n await rm(this.pidFilePath, { force: true });\n await writePrivateFile(this.pidFilePath, `${process.pid}\\n`);\n }\n\n async isRunning(): Promise<boolean> {\n try {\n const contents = await readFile(this.pidFilePath, 'utf8');\n const pid = Number.parseInt(contents.trim(), 10);\n if (!Number.isInteger(pid) || pid <= 0) {\n return false;\n }\n\n if (isProcessRunning(pid)) {\n return true;\n }\n\n await rm(this.pidFilePath, { force: true });\n return false;\n } catch (error) {\n if (isErrnoException(error, 'ENOENT')) {\n return false;\n }\n\n throw error;\n }\n }\n\n async releaseLock(): Promise<void> {\n await rm(this.pidFilePath, { force: true });\n }\n\n setupSignalHandlers(onShutdown: () => Promise<void>): void {\n let shuttingDown = false;\n\n const handleSignal = (signal: NodeJS.Signals) => {\n if (shuttingDown) {\n return;\n }\n\n shuttingDown = true;\n void (async () => {\n try {\n await onShutdown();\n process.exit(0);\n } catch (error) {\n console.error(`Failed to shut down cleanly after ${signal}:`, error);\n process.exit(1);\n }\n })();\n };\n\n for (const signal of ['SIGINT', 'SIGTERM'] as const) {\n process.once(signal, () => {\n handleSignal(signal);\n });\n }\n }\n}\n\nasync function writePrivateFile(path: string, contents: string): Promise<void> {\n await writeFile(path, contents, { encoding: 'utf8', flag: 'wx', mode: 0o600 });\n await chmod(path, 0o600);\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (error) {\n return !isErrnoException(error, 'ESRCH');\n }\n}\n\nfunction isErrnoException(error: unknown, code: string): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error && error.code === code;\n}\n","import { join } from 'node:path';\n\nimport {\n DisputeClient,\n MarketplaceClient,\n PaymentRailSelector,\n RelayRegistryClient,\n ReputationEventPublisher,\n ReputationStore,\n StakingClient,\n} from '@hivemind-os/collective-core';\nimport type { MeshToolContext } from '@hivemind-os/collective-mcp-server';\n\nimport type { DaemonState } from '../state.js';\n\n/**\n * Build a {@link MeshToolContext} from daemon state so the\n * `@hivemind-os/collective-mcp-server` tool handlers can run inside the daemon.\n *\n * Optional clients are lazily instantiated on first access to avoid\n * allocating resources (SQLite databases, objects) that may never be used.\n */\nexport interface ToolContextOptions {\n portalUrl?: string;\n openUrl?: (url: string) => Promise<boolean>;\n}\n\nexport function buildMeshToolContext(state: DaemonState, dataDir: string, options?: ToolContextOptions): MeshToolContext {\n const base: MeshToolContext = {\n did: state.did,\n keypair: state.keypair,\n suiClient: state.suiClient,\n registryClient: state.registryClient,\n taskClient: state.taskClient,\n agentCache: state.agentCache,\n blobStore: state.blobStore,\n spendingPolicy: state.spendingPolicy,\n networkConfig: state.network,\n encryption: state.encryption,\n authProvider: state.authProvider,\n relayAuthProvider: state.relayAuthProvider,\n x402Client: state.x402Client,\n portalUrl: options?.portalUrl,\n openUrl: options?.openUrl,\n };\n\n // Lazy getters for optional clients — instantiated on first access\n defineLazy(base, 'stakingClient', () => new StakingClient(state.suiClient, { packageId: state.network.packageId }));\n defineLazy(base, 'disputeClient', () => new DisputeClient(state.suiClient, { packageId: state.network.packageId }));\n defineLazy(base, 'marketplaceClient', () => new MarketplaceClient(state.suiClient, state.network));\n defineLazy(base, 'relayRegistryClient', () => new RelayRegistryClient(state.suiClient, { packageId: state.network.packageId }));\n defineLazy(base, 'paymentRailSelector', () => new PaymentRailSelector());\n defineLazy(base, 'reputationPublisher', () => new ReputationEventPublisher(state.blobStore, state.authProvider));\n defineLazy(base, 'reputationStore', () => new ReputationStore(join(dataDir, 'reputation.sqlite')));\n\n return base;\n}\n\nfunction defineLazy<T extends object, K extends keyof T>(obj: T, key: K, factory: () => T[K]): void {\n let cached: T[K] | undefined;\n Object.defineProperty(obj, key, {\n configurable: true,\n enumerable: true,\n get() {\n if (cached === undefined) {\n cached = factory();\n }\n return cached;\n },\n });\n}\n"],"mappings":"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","names":["mkdir","dirname","join","refreshed","mkdir","dirname","portalUrl","join"]}
|
package/dist/portal/server.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ interface PortalLogger {
|
|
|
36
36
|
interface PortalServerOptions {
|
|
37
37
|
config: DaemonFullConfig;
|
|
38
38
|
configPath: string;
|
|
39
|
-
authProvider
|
|
39
|
+
authProvider?: PortalAuthProvider;
|
|
40
40
|
state?: DaemonState;
|
|
41
41
|
logger?: PortalLogger;
|
|
42
42
|
getAuthStatus?: () => DaemonAuthStatus | null;
|
|
@@ -56,7 +56,7 @@ declare class PortalServer {
|
|
|
56
56
|
stop(): Promise<void>;
|
|
57
57
|
waitForAuth(): Promise<void>;
|
|
58
58
|
getUrl(): string;
|
|
59
|
-
getReauthUrl(): string;
|
|
59
|
+
getReauthUrl(): string | null;
|
|
60
60
|
private registerRoutes;
|
|
61
61
|
private startAuthFlow;
|
|
62
62
|
private handleOAuthCallback;
|
package/dist/portal/server.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hivemind-os/collective-daemon",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -52,10 +52,10 @@
|
|
|
52
52
|
"pino": "^9.0.0",
|
|
53
53
|
"pino-pretty": "^13.0.0",
|
|
54
54
|
"ws": "^8.0.0",
|
|
55
|
-
"@hivemind-os/collective-core": "0.2.
|
|
56
|
-
"@hivemind-os/collective-
|
|
57
|
-
"@hivemind-os/collective-mcp-server": "0.2.
|
|
58
|
-
"@hivemind-os/collective-
|
|
55
|
+
"@hivemind-os/collective-core": "0.2.4",
|
|
56
|
+
"@hivemind-os/collective-types": "0.2.4",
|
|
57
|
+
"@hivemind-os/collective-mcp-server": "0.2.4",
|
|
58
|
+
"@hivemind-os/collective-relay": "0.2.4"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@types/js-yaml": "^4.0.0",
|