@hivemind-os/collective-daemon 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\nimport * as fs from 'node:fs';\nimport * as fsPromises from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { dirname, extname, join, resolve } from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\n\nimport {\n PaymentRail,\n NETWORK_PRESETS,\n getNetworkPreset,\n type AuthConfig,\n type BlobStoreConfig,\n type EncryptionConfig,\n type NetworkConfig,\n type NetworkName,\n type PaymentConfig,\n type SpendingLimit,\n type SpendingPolicy,\n} from '@hivemind-os/collective-types';\nimport yaml from 'js-yaml';\n\nimport { getDefaultIpcPath } from './ipc/pipe-security.js';\n\nexport interface DaemonPerAppSpendingConfig {\n limits: SpendingLimit[];\n}\n\nexport interface DaemonSpendingPolicy extends SpendingPolicy {\n perApp?: Record<string, DaemonPerAppSpendingConfig>;\n}\n\nexport interface DaemonFullConfig {\n network: NetworkConfig;\n identity: {\n dataDir: string;\n };\n auth: AuthConfig;\n spending: DaemonSpendingPolicy;\n payment: PaymentConfig;\n daemon: {\n ipcPath: string;\n dataDir: string;\n pidFile: string;\n logLevel: 'debug' | 'info' | 'warn' | 'error';\n logFile?: string;\n };\n relay: {\n enabled: boolean;\n endpoints: Array<{\n url: string;\n relayDid?: string;\n }>;\n autoConnect: boolean;\n providerMode: boolean;\n reconnectIntervalMs?: number;\n heartbeatIntervalMs?: number;\n };\n blobstore: BlobStoreConfig;\n encryption: EncryptionConfig;\n provider?: {\n enabled: boolean;\n capabilities: Array<{\n name: string;\n description: string;\n version: string;\n priceMist: number;\n currency?: string;\n adapter: string;\n adapterConfig?: Record<string, unknown>;\n }>;\n maxConcurrency?: number;\n autoRegister?: boolean;\n };\n}\n\ntype LooseRecord = Record<string, unknown>;\ninterface ConfigDiff {\n path: string[];\n value?: unknown;\n delete?: true;\n}\n\nconst LOG_LEVELS = new Set<DaemonFullConfig['daemon']['logLevel']>(['debug', 'info', 'warn', 'error']);\nconst configSaveLocks = new Map<string, Promise<void>>();\nexport const configIo = {\n mkdir: fsPromises.mkdir,\n writeFile: fsPromises.writeFile,\n rename: fsPromises.rename,\n rm: fsPromises.rm,\n};\n\nexport function getDefaultConfig(): DaemonFullConfig {\n return buildDefaultConfig(resolve(homedir(), '.hivemind-os/collective'));\n}\n\nexport function getConfigPath(configPath?: string): string {\n return resolve(expandHome(configPath ?? join(getEnvDataDir() ?? resolve(homedir(), '.hivemind-os/collective'), 'config.yaml')));\n}\n\nexport { getDefaultIpcPath };\n\nexport function loadConfig(configPath?: string): DaemonFullConfig {\n const resolvedConfigPath = getConfigPath(configPath);\n\n const parsed = loadConfigFile(resolvedConfigPath);\n const config = buildResolvedConfig(parsed);\n validateConfig(config);\n\n if (!fs.existsSync(resolvedConfigPath)) {\n fs.mkdirSync(dirname(resolvedConfigPath), { recursive: true, mode: 0o700 });\n writePrivateConfigFile(resolvedConfigPath, formatConfigContents(serializeConfig(config), resolvedConfigPath));\n } else {\n enforcePrivateFilePermissions(resolvedConfigPath);\n }\n\n return config;\n}\n\nexport async function saveConfig(config: DaemonFullConfig, configPath = getConfigPath()): Promise<string> {\n const resolvedConfigPath = getConfigPath(configPath);\n validateConfig(config);\n\n await withConfigSaveLock(resolvedConfigPath, async () => {\n const parsed = loadConfigFile(resolvedConfigPath);\n const currentConfig = buildResolvedConfig(parsed);\n const updates = collectConfigDiffs(serializeConfig(currentConfig), serializeConfig(config));\n const nextParsed = applyConfigDiffs(parsed, updates);\n await writePrivateConfigFileAtomic(\n resolvedConfigPath,\n formatConfigContents(nextParsed, resolvedConfigPath),\n );\n });\n\n return resolvedConfigPath;\n}\n\nfunction writePrivateConfigFile(configPath: string, contents: string): void {\n fs.writeFileSync(configPath, contents, { encoding: 'utf8', mode: 0o600 });\n enforcePrivateFilePermissions(configPath);\n}\n\nasync function writePrivateConfigFileAtomic(configPath: string, contents: string): Promise<void> {\n const tempPath = `${configPath}.${process.pid}.${randomUUID()}.tmp`;\n await configIo.mkdir(dirname(configPath), { recursive: true, mode: 0o700 });\n\n try {\n await configIo.writeFile(tempPath, contents, { encoding: 'utf8', mode: 0o600 });\n await configIo.rename(tempPath, configPath);\n enforcePrivateFilePermissions(configPath);\n } finally {\n await configIo.rm(tempPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction enforcePrivateFilePermissions(path: string): void {\n fs.chmodSync(path, 0o600);\n}\n\nfunction loadConfigFile(configPath: string): LooseRecord {\n if (!fs.existsSync(configPath)) {\n return {};\n }\n\n const loaded = yaml.load(fs.readFileSync(configPath, 'utf8'));\n return isRecord(loaded) ? loaded : {};\n}\n\nfunction buildResolvedConfig(parsed: LooseRecord): DaemonFullConfig {\n const baseDataDir = normalizePath(\n getEnvDataDir() ?? readString(getNestedValue(parsed, 'daemon', 'dataDir')) ?? resolve(homedir(), '.hivemind-os/collective'),\n );\n const hasExplicitIpcPath = readString(getNestedValue(parsed, 'daemon', 'ipcPath')) !== undefined;\n\n return applyEnvironmentOverrides(mergeConfig(buildDefaultConfig(baseDataDir), parsed), { hasExplicitIpcPath });\n}\n\nasync function withConfigSaveLock<T>(configPath: string, operation: () => Promise<T>): Promise<T> {\n const previous = configSaveLocks.get(configPath) ?? Promise.resolve();\n let release!: () => void;\n const gate = new Promise<void>((resolve) => {\n release = resolve;\n });\n const chain = previous.catch(() => undefined).then(() => gate);\n configSaveLocks.set(configPath, chain);\n\n await previous.catch(() => undefined);\n\n try {\n return await operation();\n } finally {\n release();\n if (configSaveLocks.get(configPath) === chain) {\n configSaveLocks.delete(configPath);\n }\n }\n}\n\nfunction collectConfigDiffs(current: unknown, next: unknown, path: string[] = []): ConfigDiff[] {\n if (isRecord(current) && isRecord(next)) {\n const diffs: ConfigDiff[] = [];\n const keys = new Set([...Object.keys(current), ...Object.keys(next)]);\n\n for (const key of keys) {\n const hasCurrent = Object.hasOwn(current, key);\n const hasNext = Object.hasOwn(next, key);\n if (!hasNext) {\n diffs.push({ path: [...path, key], delete: true });\n continue;\n }\n\n if (!hasCurrent) {\n diffs.push({ path: [...path, key], value: structuredClone(next[key]) });\n continue;\n }\n\n diffs.push(...collectConfigDiffs(current[key], next[key], [...path, key]));\n }\n\n return diffs;\n }\n\n return isDeepStrictEqual(current, next) ? [] : [{ path, value: structuredClone(next) }];\n}\n\nfunction applyConfigDiffs(parsed: LooseRecord, diffs: ConfigDiff[]): LooseRecord {\n const next = structuredClone(parsed);\n\n for (const diff of diffs) {\n if (diff.path.length === 0) {\n return isRecord(diff.value) ? structuredClone(diff.value) : {};\n }\n\n if (diff.delete) {\n deleteNestedValue(next, diff.path);\n continue;\n }\n\n setNestedValue(next, diff.path, structuredClone(diff.value));\n }\n\n return next;\n}\n\nfunction setNestedValue(record: LooseRecord, path: string[], value: unknown): void {\n let current = record;\n for (const segment of path.slice(0, -1)) {\n const next = current[segment];\n if (!isRecord(next)) {\n current[segment] = {};\n }\n current = current[segment] as LooseRecord;\n }\n\n current[path[path.length - 1] as string] = value;\n}\n\nfunction deleteNestedValue(record: LooseRecord, path: string[]): void {\n const parents: Array<{ record: LooseRecord; key: string }> = [];\n let current: LooseRecord | undefined = record;\n\n for (const segment of path.slice(0, -1)) {\n if (!current || !isRecord(current[segment])) {\n return;\n }\n\n parents.push({ record: current, key: segment });\n current = current[segment] as LooseRecord;\n }\n\n if (!current) {\n return;\n }\n\n delete current[path[path.length - 1] as string];\n\n for (let index = parents.length - 1; index >= 0; index -= 1) {\n const parent = parents[index];\n const child = parent.record[parent.key];\n if (!isRecord(child) || Object.keys(child).length > 0) {\n break;\n }\n\n delete parent.record[parent.key];\n }\n}\n\nfunction formatConfigContents(config: LooseRecord, configPath: string): string {\n if (extname(configPath).toLowerCase() === '.json') {\n return `${JSON.stringify(config, null, 2)}\\n`;\n }\n\n return yaml.dump(config, { lineWidth: 120 });\n}\n\nfunction buildDefaultConfig(dataDir: string): DaemonFullConfig {\n const resolvedDataDir = normalizePath(dataDir);\n const defaultNetwork = NETWORK_PRESETS.testnet;\n\n return {\n network: {\n rpcUrl: defaultNetwork.rpcUrl,\n faucetUrl: defaultNetwork.faucetUrl,\n packageId: defaultNetwork.packageId,\n registryId: defaultNetwork.registryId,\n },\n identity: {\n dataDir: join(resolvedDataDir, 'identity'),\n },\n auth: {\n mode: 'ed25519',\n portal: {\n port: 19876,\n },\n },\n spending: {\n defaultRail: PaymentRail.SUI_ESCROW,\n limits: [{ amount: 1_000_000_000n, interval: 'day', currency: 'MIST' }],\n },\n payment: {\n preferredRail: 'auto',\n evm: {\n enabled: false,\n network: 'base',\n },\n },\n daemon: {\n ipcPath: getDefaultIpcPath(resolvedDataDir),\n dataDir: resolvedDataDir,\n pidFile: join(resolvedDataDir, 'daemon.pid'),\n logLevel: 'info',\n },\n relay: {\n enabled: false,\n endpoints: [],\n autoConnect: true,\n providerMode: false,\n reconnectIntervalMs: 5_000,\n heartbeatIntervalMs: 30_000,\n },\n blobstore: {\n mode: 'filesystem',\n filesystem: {\n dataDir: join(resolvedDataDir, 'blobs'),\n },\n },\n encryption: {\n enabled: true,\n requireEncryption: false,\n },\n };\n}\n\nfunction mergeConfig(defaults: DaemonFullConfig, parsed: LooseRecord): DaemonFullConfig {\n const network = isRecord(parsed.network) ? parsed.network : {};\n const identity = isRecord(parsed.identity) ? parsed.identity : {};\n const auth = isRecord(parsed.auth) ? parsed.auth : {};\n const payment = isRecord(parsed.payment) ? parsed.payment : {};\n const daemon = isRecord(parsed.daemon) ? parsed.daemon : {};\n const relay = isRecord(parsed.relay) ? parsed.relay : {};\n const blobstore = isRecord(parsed.blobstore) ? parsed.blobstore : {};\n const encryption = isRecord(parsed.encryption) ? parsed.encryption : {};\n const provider = isRecord(parsed.provider) ? parsed.provider : undefined;\n\n return {\n network: {\n ...resolveNetworkFromConfig(network, defaults.network),\n },\n identity: {\n dataDir: normalizePath(readString(identity.dataDir) ?? defaults.identity.dataDir),\n },\n auth: normalizeAuthConfig(auth, defaults.auth),\n spending: normalizeSpendingPolicy(parsed.spending, defaults.spending),\n payment: normalizePaymentConfig(payment, defaults.payment),\n daemon: {\n ipcPath: readString(daemon.ipcPath) ?? defaults.daemon.ipcPath,\n dataDir: normalizePath(readString(daemon.dataDir) ?? defaults.daemon.dataDir),\n pidFile: normalizePath(readString(daemon.pidFile) ?? defaults.daemon.pidFile),\n logLevel: normalizeLogLevel(daemon.logLevel, defaults.daemon.logLevel),\n logFile: readString(daemon.logFile) ? normalizePath(readString(daemon.logFile) as string) : undefined,\n },\n relay: normalizeRelayConfig(relay, defaults.relay),\n blobstore: normalizeBlobStoreConfig(blobstore, defaults.blobstore),\n encryption: normalizeEncryptionConfig(encryption, defaults.encryption),\n provider: normalizeProviderConfig(provider),\n };\n}\n\nfunction applyEnvironmentOverrides(\n config: DaemonFullConfig,\n options: { hasExplicitIpcPath: boolean } = { hasExplicitIpcPath: false },\n): DaemonFullConfig {\n const envDataDir = getEnvDataDir();\n const withDataDir = envDataDir\n ? {\n ...config,\n identity: { dataDir: join(envDataDir, 'identity') },\n daemon: {\n ...config.daemon,\n dataDir: envDataDir,\n pidFile: join(envDataDir, 'daemon.pid'),\n ipcPath: options.hasExplicitIpcPath ? config.daemon.ipcPath : getDefaultIpcPath(envDataDir),\n },\n blobstore: applyBlobStoreDataDirOverride(config.blobstore, join(envDataDir, 'blobs')),\n }\n : config;\n\n return {\n ...withDataDir,\n network: {\n ...withDataDir.network,\n ...resolveNetworkEnvOverrides(withDataDir.network),\n },\n daemon: {\n ...withDataDir.daemon,\n logLevel: normalizeLogLevel(process.env.COLLECTIVE_LOG_LEVEL, withDataDir.daemon.logLevel),\n },\n };\n}\n\nfunction resolveNetworkEnvOverrides(base: NetworkConfig): Partial<NetworkConfig> {\n // COLLECTIVE_NETWORK=testnet|mainnet|devnet|local applies a full preset\n const networkName = process.env.COLLECTIVE_NETWORK as NetworkName | undefined;\n const preset = networkName ? getNetworkPreset(networkName) : undefined;\n const merged = preset ? { ...base, ...preset } : base;\n\n // Individual env vars override the preset\n return {\n rpcUrl: process.env.COLLECTIVE_RPC_URL ?? merged.rpcUrl,\n faucetUrl: merged.faucetUrl,\n packageId: process.env.COLLECTIVE_PACKAGE_ID ?? merged.packageId,\n registryId: process.env.COLLECTIVE_REGISTRY_ID ?? merged.registryId,\n };\n}\n\nfunction normalizeAuthConfig(value: LooseRecord, defaults: AuthConfig): AuthConfig {\n const google = isRecord(value.google) ? value.google : {};\n const apple = isRecord(value.apple) ? value.apple : {};\n const portal = isRecord(value.portal) ? value.portal : {};\n\n return {\n mode: value.mode === 'zklogin' ? 'zklogin' : defaults.mode,\n google: readString(google.clientId)\n ? {\n clientId: readString(google.clientId) as string,\n }\n : defaults.google,\n apple: readString(apple.clientId)\n ? {\n clientId: readString(apple.clientId) as string,\n }\n : defaults.apple,\n portal: {\n port: readPositiveInteger(portal.port, 'auth.portal.port') ?? defaults.portal?.port ?? 19876,\n },\n };\n}\n\nfunction normalizePaymentConfig(value: LooseRecord, defaults: PaymentConfig): PaymentConfig {\n const evm = isRecord(value.evm) ? value.evm : {};\n const preferredRail = readString(value.preferredRail);\n const network = readString(evm.network);\n\n return {\n preferredRail:\n preferredRail === 'sui' || preferredRail === 'x402' || preferredRail === 'auto'\n ? preferredRail\n : defaults.preferredRail,\n evm: {\n enabled: readBoolean(evm.enabled) ?? defaults.evm?.enabled ?? false,\n network:\n network === 'base' || network === 'base-sepolia' || network === 'localhost'\n ? network\n : (defaults.evm?.network ?? 'base'),\n rpcUrl: readString(evm.rpcUrl) ?? defaults.evm?.rpcUrl,\n },\n };\n}\n\nfunction normalizeRelayConfig(\n value: LooseRecord,\n defaults: DaemonFullConfig['relay'],\n): DaemonFullConfig['relay'] {\n const endpoints = Array.isArray(value.endpoints)\n ? value.endpoints\n .map((entry) => {\n if (!isRecord(entry)) {\n return null;\n }\n\n const url = readString(entry.url);\n if (!url) {\n return null;\n }\n\n const relayDid = readString(entry.relayDid);\n return relayDid ? { url, relayDid } : { url };\n })\n .filter((entry): entry is NonNullable<typeof entry> => entry !== null)\n : defaults.endpoints;\n\n return {\n enabled: readBoolean(value.enabled) ?? defaults.enabled,\n endpoints,\n autoConnect: readBoolean(value.autoConnect) ?? defaults.autoConnect,\n providerMode: readBoolean(value.providerMode) ?? defaults.providerMode,\n reconnectIntervalMs: readPositiveInteger(value.reconnectIntervalMs, 'relay.reconnectIntervalMs') ?? defaults.reconnectIntervalMs,\n heartbeatIntervalMs: readPositiveInteger(value.heartbeatIntervalMs, 'relay.heartbeatIntervalMs') ?? defaults.heartbeatIntervalMs,\n };\n}\n\nfunction normalizeEncryptionConfig(\n value: LooseRecord,\n defaults: DaemonFullConfig['encryption'],\n): DaemonFullConfig['encryption'] {\n return {\n enabled: readBoolean(value.enabled) ?? defaults.enabled,\n requireEncryption: readBoolean(value.requireEncryption) ?? defaults.requireEncryption,\n };\n}\n\nfunction normalizeBlobStoreConfig(value: unknown, defaults: BlobStoreConfig): BlobStoreConfig {\n const blobstore = isRecord(value) ? value : {};\n const filesystem = isRecord(blobstore.filesystem) ? blobstore.filesystem : {};\n const walrus = isRecord(blobstore.walrus) ? blobstore.walrus : {};\n const hybrid = isRecord(blobstore.hybrid) ? blobstore.hybrid : {};\n const mode = normalizeBlobStoreMode(readString(blobstore.mode) ?? readString(blobstore.type) ?? defaults.mode);\n const filesystemDataDir = readString(filesystem.dataDir) ?? readString(blobstore.baseDir) ?? defaults.filesystem?.dataDir;\n const publisherUrl = readString(walrus.publisherUrl) ?? readString(blobstore.publisherUrl) ?? defaults.walrus?.publisherUrl;\n const aggregatorUrl = readString(walrus.aggregatorUrl) ?? readString(blobstore.aggregatorUrl) ?? defaults.walrus?.aggregatorUrl;\n\n return {\n mode,\n filesystem: filesystemDataDir\n ? {\n dataDir: normalizePath(filesystemDataDir),\n }\n : defaults.filesystem,\n walrus:\n publisherUrl || aggregatorUrl || defaults.walrus\n ? {\n publisherUrl: publisherUrl ?? '',\n aggregatorUrl: aggregatorUrl ?? '',\n epochs: readPositiveInteger(walrus.epochs, 'blobstore.walrus.epochs') ?? defaults.walrus?.epochs,\n maxBlobSize:\n readPositiveInteger(walrus.maxBlobSize, 'blobstore.walrus.maxBlobSize') ?? defaults.walrus?.maxBlobSize,\n retryAttempts:\n readPositiveInteger(walrus.retryAttempts, 'blobstore.walrus.retryAttempts') ?? defaults.walrus?.retryAttempts,\n retryDelayMs:\n readPositiveInteger(walrus.retryDelayMs, 'blobstore.walrus.retryDelayMs') ?? defaults.walrus?.retryDelayMs,\n timeoutMs: readPositiveInteger(walrus.timeoutMs, 'blobstore.walrus.timeoutMs') ?? defaults.walrus?.timeoutMs,\n }\n : defaults.walrus,\n hybrid: {\n cacheLocally: readBoolean(hybrid.cacheLocally) ?? defaults.hybrid?.cacheLocally ?? true,\n preferWalrus: readBoolean(hybrid.preferWalrus) ?? defaults.hybrid?.preferWalrus ?? true,\n },\n };\n}\n\nfunction applyBlobStoreDataDirOverride(config: BlobStoreConfig, dataDir: string): BlobStoreConfig {\n if (config.mode !== 'filesystem' && config.mode !== 'hybrid') {\n return config;\n }\n\n return {\n ...config,\n filesystem: {\n dataDir,\n },\n };\n}\n\nfunction normalizeBlobStoreMode(value: string): BlobStoreConfig['mode'] {\n if (value === 'filesystem' || value === 'walrus' || value === 'hybrid') {\n return value;\n }\n\n throw new Error(`Unsupported blobstore mode: ${value}`);\n}\n\nfunction normalizeSpendingPolicy(value: unknown, defaults: DaemonSpendingPolicy): DaemonSpendingPolicy {\n if (!isRecord(value)) {\n return defaults;\n }\n\n return {\n defaultRail: normalizeRail(value.defaultRail) ?? defaults.defaultRail,\n requireConfirmationAbove:\n value.requireConfirmationAbove === undefined\n ? defaults.requireConfirmationAbove\n : parseBigInt(value.requireConfirmationAbove, 'spending.requireConfirmationAbove'),\n allowlist: Array.isArray(value.allowlist)\n ? value.allowlist.filter((entry): entry is string => typeof entry === 'string')\n : defaults.allowlist,\n denylist: Array.isArray(value.denylist)\n ? value.denylist.filter((entry): entry is string => typeof entry === 'string')\n : defaults.denylist,\n limits:\n Array.isArray(value.limits) && value.limits.length > 0\n ? value.limits.map((limit, index) => normalizeSpendingLimit(limit, index, 'spending.limits'))\n : defaults.limits,\n perApp: normalizePerAppSpendingConfig(value.perApp, defaults.perApp),\n };\n}\n\nfunction normalizePerAppSpendingConfig(\n value: unknown,\n defaults: DaemonSpendingPolicy['perApp'],\n): DaemonSpendingPolicy['perApp'] {\n if (!isRecord(value)) {\n return defaults;\n }\n\n const perApp = Object.entries(value).map(([appName, config]) => {\n if (!isRecord(config)) {\n throw new Error(`spending.perApp.${appName} must be an object.`);\n }\n\n if (!Array.isArray(config.limits) || config.limits.length === 0) {\n throw new Error(`spending.perApp.${appName}.limits must contain at least one limit.`);\n }\n\n return [\n appName,\n {\n limits: config.limits.map((limit, index) =>\n normalizeSpendingLimit(limit, index, `spending.perApp.${appName}.limits`),\n ),\n },\n ] as const;\n });\n\n return Object.fromEntries(perApp);\n}\n\nfunction normalizeSpendingLimit(\n value: unknown,\n index: number,\n path: string,\n): DaemonSpendingPolicy['limits'][number] {\n if (!isRecord(value)) {\n throw new Error(`${path}[${index}] must be an object.`);\n }\n\n const interval = readString(value.interval);\n if (!interval || !['transaction', 'hour', 'day', 'month', 'lifetime'].includes(interval)) {\n throw new Error(`${path}[${index}].interval is invalid.`);\n }\n\n const normalizedInterval = interval as DaemonSpendingPolicy['limits'][number]['interval'];\n\n return {\n amount: parseBigInt(value.amount, `${path}[${index}].amount`),\n interval: normalizedInterval,\n rail: normalizeRail(value.rail),\n currency: readString(value.currency)?.toUpperCase(),\n scope: readString(value.scope) ?? undefined,\n };\n}\n\nfunction normalizeProviderConfig(value: LooseRecord | undefined): DaemonFullConfig['provider'] {\n if (!value) {\n return undefined;\n }\n\n const capabilities = Array.isArray(value.capabilities)\n ? value.capabilities.map((entry, index) => normalizeProviderCapability(entry, index))\n : [];\n\n return {\n enabled: readBoolean(value.enabled) ?? false,\n capabilities,\n maxConcurrency: readPositiveInteger(value.maxConcurrency, 'provider.maxConcurrency'),\n autoRegister: readBoolean(value.autoRegister),\n };\n}\n\nfunction normalizeProviderCapability(\n value: unknown,\n index: number,\n): NonNullable<DaemonFullConfig['provider']>['capabilities'][number] {\n if (!isRecord(value)) {\n throw new Error(`provider.capabilities[${index}] must be an object.`);\n }\n\n const name = readString(value.name);\n const description = readString(value.description);\n const version = readString(value.version);\n const adapter = readString(value.adapter);\n const priceMist = readPositiveInteger(value.priceMist, `provider.capabilities[${index}].priceMist`);\n\n if (!name || !description || !version || !adapter || priceMist === undefined) {\n throw new Error(`provider.capabilities[${index}] is incomplete.`);\n }\n\n return {\n name,\n description,\n version,\n priceMist,\n currency: readString(value.currency),\n adapter,\n adapterConfig: isRecord(value.adapterConfig) ? value.adapterConfig : undefined,\n };\n}\n\nfunction validateConfig(config: DaemonFullConfig): void {\n if (!config.network.rpcUrl) {\n throw new Error('network.rpcUrl is required.');\n }\n\n if (!config.identity.dataDir) {\n throw new Error('identity.dataDir is required.');\n }\n\n if (!config.auth.mode) {\n throw new Error('auth.mode is required.');\n }\n\n if (config.auth.mode === 'zklogin' && !config.auth.google?.clientId) {\n throw new Error('auth.google.clientId is required when auth.mode is zklogin.');\n }\n\n if (!config.payment.evm) {\n throw new Error('payment.evm configuration is required.');\n }\n\n if (!config.daemon.ipcPath || !config.daemon.dataDir || !config.daemon.pidFile) {\n throw new Error('daemon configuration is incomplete.');\n }\n\n if (config.relay.enabled && config.relay.endpoints.length === 0) {\n throw new Error('relay.endpoints must contain at least one entry when relay is enabled.');\n }\n\n for (const endpoint of config.relay.endpoints) {\n if (!/^wss?:\\/\\//i.test(endpoint.url)) {\n throw new Error(`Invalid relay endpoint URL: ${endpoint.url}`);\n }\n }\n\n if (config.blobstore.mode === 'filesystem' || config.blobstore.mode === 'hybrid') {\n if (!config.blobstore.filesystem?.dataDir) {\n throw new Error('blobstore.filesystem.dataDir is required.');\n }\n }\n\n if (config.blobstore.mode === 'walrus' || config.blobstore.mode === 'hybrid') {\n if (!config.blobstore.walrus?.publisherUrl || !config.blobstore.walrus.aggregatorUrl) {\n throw new Error('blobstore.walrus.publisherUrl and blobstore.walrus.aggregatorUrl are required.');\n }\n }\n\n if (!LOG_LEVELS.has(config.daemon.logLevel)) {\n throw new Error(`Invalid log level: ${config.daemon.logLevel}`);\n }\n\n if (config.encryption.requireEncryption && !config.encryption.enabled) {\n throw new Error('encryption.requireEncryption cannot be true when encryption.enabled is false.');\n }\n}\n\nfunction serializeConfig(config: DaemonFullConfig): LooseRecord {\n return serializeValue(config) as LooseRecord;\n}\n\nfunction serializeValue(value: unknown): unknown {\n if (typeof value === 'bigint') {\n return value.toString();\n }\n\n if (Array.isArray(value)) {\n return value.map((entry) => serializeValue(entry));\n }\n\n if (isRecord(value)) {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, entry]) => entry !== undefined)\n .map(([key, entry]) => [key, serializeValue(entry)]),\n );\n }\n\n return value;\n}\n\nfunction normalizeLogLevel(value: unknown, fallback: DaemonFullConfig['daemon']['logLevel']) {\n return typeof value === 'string' && LOG_LEVELS.has(value as DaemonFullConfig['daemon']['logLevel'])\n ? (value as DaemonFullConfig['daemon']['logLevel'])\n : fallback;\n}\n\nfunction normalizeRail(value: unknown): PaymentRail | undefined {\n if (\n value === PaymentRail.SUI_ESCROW ||\n value === PaymentRail.SUI_TRANSFER ||\n value === PaymentRail.X402_BASE\n ) {\n return value;\n }\n\n return undefined;\n}\n\nfunction parseBigInt(value: unknown, field: string): bigint {\n if (typeof value === 'bigint') {\n return value;\n }\n\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) {\n return BigInt(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 getEnvDataDir(): string | undefined {\n return process.env.COLLECTIVE_DATA_DIR ? normalizePath(process.env.COLLECTIVE_DATA_DIR) : undefined;\n}\n\nfunction getNestedValue(record: LooseRecord, ...keys: string[]): unknown {\n let current: unknown = record;\n for (const key of keys) {\n if (!isRecord(current)) {\n return undefined;\n }\n\n current = current[key];\n }\n\n return current;\n}\n\nfunction normalizePath(value: string): string {\n return resolve(expandHome(value));\n}\n\nfunction expandHome(value: string): string {\n if (value === '~') {\n return homedir();\n }\n\n if (value.startsWith('~/') || value.startsWith('~\\\\')) {\n return join(homedir(), value.slice(2));\n }\n\n return value;\n}\n\n/**\n * Resolve the network config from YAML. If a `name` field is present (e.g. \"testnet\"),\n * use the corresponding preset as the base, then overlay any explicit fields.\n */\nfunction resolveNetworkFromConfig(network: LooseRecord, defaults: NetworkConfig): NetworkConfig {\n const nameField = readString(network.name);\n const preset = nameField ? getNetworkPreset(nameField) : undefined;\n const base = preset ?? defaults;\n\n return {\n rpcUrl: readString(network.rpcUrl) ?? base.rpcUrl,\n faucetUrl: readString(network.faucetUrl) ?? base.faucetUrl,\n packageId: readHexString(network.packageId) ?? base.packageId,\n registryId: readHexString(network.registryId) ?? base.registryId,\n };\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim() : undefined;\n}\n\nfunction readBoolean(value: unknown): boolean | undefined {\n return typeof value === 'boolean' ? value : undefined;\n}\n\nfunction readPositiveInteger(value: unknown, field: string): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) {\n return value;\n }\n\n if (typeof value === 'string' && /^\\d+$/.test(value.trim())) {\n return Number(value.trim());\n }\n\n throw new Error(`${field} must be a non-negative integer.`);\n}\n\nfunction readHexString(value: unknown): string | undefined {\n if (typeof value === 'string' && value.trim()) {\n return value.trim();\n }\n\n if (typeof value === 'number' && Number.isSafeInteger(value) && value >= 0) {\n return `0x${value.toString(16)}`;\n }\n\n return undefined;\n}\n\nfunction isRecord(value: unknown): value is LooseRecord {\n return Boolean(value) && typeof value === 'object' && !Array.isArray(value);\n}\n"],"mappings":"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","names":["resolve"]}
@@ -0,0 +1,183 @@
1
+ // src/ipc/pipe-security.ts
2
+ import { execFile } from "child_process";
3
+ import { userInfo } from "os";
4
+ import { join } from "path";
5
+ import { promisify } from "util";
6
+ var execFileAsync = promisify(execFile);
7
+ var LEGACY_WINDOWS_PIPE_PATH = "\\\\.\\pipe\\hivemind-collective";
8
+ function getDefaultIpcPath(dataDir, runtime = {}) {
9
+ return getPlatform(runtime) === "win32" ? buildWindowsUserPipePath(runtime.username) : join(dataDir, "mesh.sock");
10
+ }
11
+ function buildWindowsUserPipePath(username = getCurrentUsername()) {
12
+ return `${LEGACY_WINDOWS_PIPE_PATH}-${sanitizePipeSegment(username)}`;
13
+ }
14
+ function isLegacyWindowsPipePath(ipcPath) {
15
+ return ipcPath.toLowerCase() === LEGACY_WINDOWS_PIPE_PATH.toLowerCase();
16
+ }
17
+ async function verifyPipeSecurity(ipcPath, runtime = {}) {
18
+ if (getPlatform(runtime) !== "win32") {
19
+ return {
20
+ transport: "unix-socket",
21
+ userScoped: true,
22
+ aclVerified: false,
23
+ note: "Unix IPC isolation relies on socket permissions (chmod 0o600)."
24
+ };
25
+ }
26
+ if (isLegacyWindowsPipePath(ipcPath)) {
27
+ return {
28
+ transport: "windows-pipe",
29
+ userScoped: false,
30
+ aclVerified: false,
31
+ note: "Legacy shared Windows pipe name configured; prefer the per-user default pipe name."
32
+ };
33
+ }
34
+ try {
35
+ const acl = await inspectWindowsPipeAcl(ipcPath, runtime);
36
+ return {
37
+ transport: "windows-pipe",
38
+ userScoped: true,
39
+ aclVerified: true,
40
+ acl,
41
+ note: "Using a user-scoped Windows named pipe."
42
+ };
43
+ } catch (error) {
44
+ return {
45
+ transport: "windows-pipe",
46
+ userScoped: true,
47
+ aclVerified: false,
48
+ note: `Using a user-scoped Windows named pipe. ACL inspection was unavailable: ${getErrorMessage(error)}`
49
+ };
50
+ }
51
+ }
52
+ async function validateClientProcessOwnership(pid, runtime = {}) {
53
+ if (!Number.isInteger(pid) || pid < 0) {
54
+ return {
55
+ allowed: false,
56
+ source: "windows-pid",
57
+ reason: `Invalid client PID: ${pid}`
58
+ };
59
+ }
60
+ if (getPlatform(runtime) !== "win32") {
61
+ return {
62
+ allowed: true,
63
+ source: "unix-socket"
64
+ };
65
+ }
66
+ const expectedUser = runtime.username ?? getCurrentUsername();
67
+ try {
68
+ const owner = await readWindowsProcessOwner(pid, runtime);
69
+ const actualUser = owner.domain ? `${owner.domain}\\${owner.user}` : owner.user;
70
+ if (!owner.user) {
71
+ return {
72
+ allowed: false,
73
+ source: "windows-pid",
74
+ expectedUser,
75
+ reason: `Unable to determine the owner of process ${pid}.`
76
+ };
77
+ }
78
+ if (normalizeUserName(owner.user) !== normalizeUserName(expectedUser)) {
79
+ return {
80
+ allowed: false,
81
+ source: "windows-pid",
82
+ expectedUser,
83
+ actualUser,
84
+ reason: `Process ${pid} is owned by ${actualUser}, expected ${expectedUser}.`
85
+ };
86
+ }
87
+ return {
88
+ allowed: true,
89
+ source: "windows-pid",
90
+ expectedUser,
91
+ actualUser
92
+ };
93
+ } catch (error) {
94
+ return {
95
+ allowed: false,
96
+ source: "windows-pid",
97
+ expectedUser,
98
+ reason: `Unable to validate client process ${pid}: ${getErrorMessage(error)}`
99
+ };
100
+ }
101
+ }
102
+ function sanitizePipeSegment(value) {
103
+ const leaf = value.split(/[\\/]+/).filter(Boolean).at(-1) ?? value;
104
+ const sanitized = leaf.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
105
+ return sanitized || "unknown-user";
106
+ }
107
+ function normalizeUserName(value) {
108
+ const leaf = value.split("\\").filter(Boolean).at(-1) ?? value;
109
+ return leaf.trim().toLowerCase();
110
+ }
111
+ function getCurrentUsername() {
112
+ try {
113
+ return userInfo().username;
114
+ } catch {
115
+ return process.env.USERNAME ?? process.env.USER ?? "unknown-user";
116
+ }
117
+ }
118
+ function getPlatform(runtime) {
119
+ return runtime.platform ?? process.platform;
120
+ }
121
+ async function inspectWindowsPipeAcl(ipcPath, runtime) {
122
+ const stdout = await runPowerShell(
123
+ [
124
+ `$path = '${escapePowerShellString(ipcPath)}'`,
125
+ "$acl = Get-Acl -LiteralPath $path -ErrorAction Stop",
126
+ "$identities = @($acl.Access | ForEach-Object { $_.IdentityReference.Value } | Sort-Object -Unique)",
127
+ "[pscustomobject]@{ owner = $acl.Owner; identities = $identities } | ConvertTo-Json -Compress -Depth 4"
128
+ ].join("; "),
129
+ runtime
130
+ );
131
+ const parsed = parseJson(stdout);
132
+ return {
133
+ owner: typeof parsed.owner === "string" ? parsed.owner : void 0,
134
+ identities: Array.isArray(parsed.identities) ? parsed.identities.filter((identity) => typeof identity === "string") : []
135
+ };
136
+ }
137
+ async function readWindowsProcessOwner(pid, runtime) {
138
+ const stdout = await runPowerShell(
139
+ [
140
+ `$process = Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}" -ErrorAction Stop`,
141
+ 'if ($null -eq $process) { throw "Process not found." }',
142
+ "$owner = Invoke-CimMethod -InputObject $process -MethodName GetOwner -ErrorAction Stop",
143
+ 'if ($null -eq $owner -or [string]::IsNullOrWhiteSpace($owner.User)) { throw "Process owner is unavailable." }',
144
+ "[pscustomobject]@{ user = $owner.User; domain = $owner.Domain } | ConvertTo-Json -Compress"
145
+ ].join("; "),
146
+ runtime
147
+ );
148
+ const parsed = parseJson(stdout);
149
+ return {
150
+ user: typeof parsed.user === "string" ? parsed.user : void 0,
151
+ domain: typeof parsed.domain === "string" ? parsed.domain : void 0
152
+ };
153
+ }
154
+ async function runPowerShell(script, runtime) {
155
+ if (runtime.runPowerShell) {
156
+ return runtime.runPowerShell(script);
157
+ }
158
+ const { stdout } = await execFileAsync(
159
+ "powershell.exe",
160
+ ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script],
161
+ { windowsHide: true }
162
+ );
163
+ return stdout.trim();
164
+ }
165
+ function parseJson(value) {
166
+ if (!value.trim()) {
167
+ throw new Error("PowerShell command returned no output.");
168
+ }
169
+ return JSON.parse(value);
170
+ }
171
+ function escapePowerShellString(value) {
172
+ return value.replace(/'/g, "''");
173
+ }
174
+ function getErrorMessage(error) {
175
+ return error instanceof Error ? error.message : String(error);
176
+ }
177
+
178
+ export {
179
+ getDefaultIpcPath,
180
+ verifyPipeSecurity,
181
+ validateClientProcessOwnership
182
+ };
183
+ //# sourceMappingURL=chunk-NXIFS427.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ipc/pipe-security.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { userInfo } from 'node:os';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\n\nconst execFileAsync = promisify(execFile);\n\nexport const LEGACY_WINDOWS_PIPE_PATH = '\\\\\\\\.\\\\pipe\\\\hivemind-collective';\n\nexport interface PipeSecurityRuntime {\n platform?: NodeJS.Platform;\n username?: string;\n runPowerShell?: (script: string) => Promise<string>;\n}\n\nexport interface PipeAclSummary {\n owner?: string;\n identities: string[];\n}\n\nexport interface PipeSecurityStatus {\n transport: 'windows-pipe' | 'unix-socket';\n userScoped: boolean;\n aclVerified: boolean;\n acl?: PipeAclSummary;\n note: string;\n}\n\nexport interface ClientValidationResult {\n allowed: boolean;\n source: 'windows-pid' | 'unix-socket';\n reason?: string;\n expectedUser?: string;\n actualUser?: string;\n}\n\nexport function getDefaultIpcPath(dataDir: string, runtime: PipeSecurityRuntime = {}): string {\n return getPlatform(runtime) === 'win32' ? buildWindowsUserPipePath(runtime.username) : join(dataDir, 'mesh.sock');\n}\n\nexport function buildWindowsUserPipePath(username = getCurrentUsername()): string {\n return `${LEGACY_WINDOWS_PIPE_PATH}-${sanitizePipeSegment(username)}`;\n}\n\nexport function isLegacyWindowsPipePath(ipcPath: string): boolean {\n return ipcPath.toLowerCase() === LEGACY_WINDOWS_PIPE_PATH.toLowerCase();\n}\n\nexport async function verifyPipeSecurity(\n ipcPath: string,\n runtime: PipeSecurityRuntime = {},\n): Promise<PipeSecurityStatus> {\n if (getPlatform(runtime) !== 'win32') {\n return {\n transport: 'unix-socket',\n userScoped: true,\n aclVerified: false,\n note: 'Unix IPC isolation relies on socket permissions (chmod 0o600).',\n };\n }\n\n if (isLegacyWindowsPipePath(ipcPath)) {\n return {\n transport: 'windows-pipe',\n userScoped: false,\n aclVerified: false,\n note: 'Legacy shared Windows pipe name configured; prefer the per-user default pipe name.',\n };\n }\n\n try {\n const acl = await inspectWindowsPipeAcl(ipcPath, runtime);\n return {\n transport: 'windows-pipe',\n userScoped: true,\n aclVerified: true,\n acl,\n note: 'Using a user-scoped Windows named pipe.',\n };\n } catch (error) {\n return {\n transport: 'windows-pipe',\n userScoped: true,\n aclVerified: false,\n note: `Using a user-scoped Windows named pipe. ACL inspection was unavailable: ${getErrorMessage(error)}`,\n };\n }\n}\n\nexport async function validateClientProcessOwnership(\n pid: number,\n runtime: PipeSecurityRuntime = {},\n): Promise<ClientValidationResult> {\n if (!Number.isInteger(pid) || pid < 0) {\n return {\n allowed: false,\n source: 'windows-pid',\n reason: `Invalid client PID: ${pid}`,\n };\n }\n\n if (getPlatform(runtime) !== 'win32') {\n return {\n allowed: true,\n source: 'unix-socket',\n };\n }\n\n const expectedUser = runtime.username ?? getCurrentUsername();\n\n try {\n const owner = await readWindowsProcessOwner(pid, runtime);\n const actualUser = owner.domain ? `${owner.domain}\\\\${owner.user}` : owner.user;\n if (!owner.user) {\n return {\n allowed: false,\n source: 'windows-pid',\n expectedUser,\n reason: `Unable to determine the owner of process ${pid}.`,\n };\n }\n\n if (normalizeUserName(owner.user) !== normalizeUserName(expectedUser)) {\n return {\n allowed: false,\n source: 'windows-pid',\n expectedUser,\n actualUser,\n reason: `Process ${pid} is owned by ${actualUser}, expected ${expectedUser}.`,\n };\n }\n\n return {\n allowed: true,\n source: 'windows-pid',\n expectedUser,\n actualUser,\n };\n } catch (error) {\n return {\n allowed: false,\n source: 'windows-pid',\n expectedUser,\n reason: `Unable to validate client process ${pid}: ${getErrorMessage(error)}`,\n };\n }\n}\n\nfunction sanitizePipeSegment(value: string): string {\n const leaf = value.split(/[\\\\/]+/).filter(Boolean).at(-1) ?? value;\n const sanitized = leaf.trim().toLowerCase().replace(/[^a-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '');\n return sanitized || 'unknown-user';\n}\n\nfunction normalizeUserName(value: string): string {\n const leaf = value.split('\\\\').filter(Boolean).at(-1) ?? value;\n return leaf.trim().toLowerCase();\n}\n\nfunction getCurrentUsername(): string {\n try {\n return userInfo().username;\n } catch {\n return process.env.USERNAME ?? process.env.USER ?? 'unknown-user';\n }\n}\n\nfunction getPlatform(runtime: PipeSecurityRuntime): NodeJS.Platform {\n return runtime.platform ?? process.platform;\n}\n\nasync function inspectWindowsPipeAcl(ipcPath: string, runtime: PipeSecurityRuntime): Promise<PipeAclSummary> {\n const stdout = await runPowerShell(\n [\n `$path = '${escapePowerShellString(ipcPath)}'`,\n '$acl = Get-Acl -LiteralPath $path -ErrorAction Stop',\n '$identities = @($acl.Access | ForEach-Object { $_.IdentityReference.Value } | Sort-Object -Unique)',\n '[pscustomobject]@{ owner = $acl.Owner; identities = $identities } | ConvertTo-Json -Compress -Depth 4',\n ].join('; '),\n runtime,\n );\n const parsed = parseJson(stdout) as { owner?: unknown; identities?: unknown };\n\n return {\n owner: typeof parsed.owner === 'string' ? parsed.owner : undefined,\n identities: Array.isArray(parsed.identities)\n ? parsed.identities.filter((identity): identity is string => typeof identity === 'string')\n : [],\n };\n}\n\nasync function readWindowsProcessOwner(\n pid: number,\n runtime: PipeSecurityRuntime,\n): Promise<{ user?: string; domain?: string }> {\n const stdout = await runPowerShell(\n [\n `$process = Get-CimInstance Win32_Process -Filter \\\"ProcessId = ${pid}\\\" -ErrorAction Stop`,\n 'if ($null -eq $process) { throw \\\"Process not found.\\\" }',\n '$owner = Invoke-CimMethod -InputObject $process -MethodName GetOwner -ErrorAction Stop',\n 'if ($null -eq $owner -or [string]::IsNullOrWhiteSpace($owner.User)) { throw \\\"Process owner is unavailable.\\\" }',\n '[pscustomobject]@{ user = $owner.User; domain = $owner.Domain } | ConvertTo-Json -Compress',\n ].join('; '),\n runtime,\n );\n const parsed = parseJson(stdout) as { user?: unknown; domain?: unknown };\n return {\n user: typeof parsed.user === 'string' ? parsed.user : undefined,\n domain: typeof parsed.domain === 'string' ? parsed.domain : undefined,\n };\n}\n\nasync function runPowerShell(script: string, runtime: PipeSecurityRuntime): Promise<string> {\n if (runtime.runPowerShell) {\n return runtime.runPowerShell(script);\n }\n\n const { stdout } = await execFileAsync(\n 'powershell.exe',\n ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', script],\n { windowsHide: true },\n );\n return stdout.trim();\n}\n\nfunction parseJson(value: string): unknown {\n if (!value.trim()) {\n throw new Error('PowerShell command returned no output.');\n }\n\n return JSON.parse(value) as unknown;\n}\n\nfunction escapePowerShellString(value: string): string {\n return value.replace(/'/g, \"''\");\n}\n\nfunction getErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,305 @@
1
+ // src/state.ts
2
+ import { createHash } from "crypto";
3
+ import { mkdir } from "fs/promises";
4
+ import { join } from "path";
5
+ import pino from "pino";
6
+ import {
7
+ AgentCache,
8
+ createDID,
9
+ deriveEvmKey,
10
+ Ed25519AuthProvider,
11
+ ed25519ToX25519,
12
+ EncryptedBlobStore,
13
+ EvmWallet,
14
+ FilesystemBlobStore,
15
+ HybridBlobStore,
16
+ loadOrCreateKeypair,
17
+ MeshSuiClient,
18
+ RegistryClient,
19
+ SqliteCursorStore,
20
+ SpendingPolicyEngine,
21
+ SessionExpiredError,
22
+ TaskClient,
23
+ WalrusBlobStore,
24
+ X402Client,
25
+ ZkLoginProvider,
26
+ ZkLoginSessionStore
27
+ } from "@hivemind-os/collective-core";
28
+ import { PaymentRail } from "@hivemind-os/collective-types";
29
+ import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519";
30
+ var logger = pino({ name: "@hivemind-os/collective-daemon:state" });
31
+ var DaemonState = class _DaemonState {
32
+ keypair;
33
+ identityKeypair;
34
+ authProvider;
35
+ relayAuthProvider;
36
+ did;
37
+ suiClient;
38
+ registryClient;
39
+ taskClient;
40
+ agentCache;
41
+ blobStore;
42
+ encryptionKeyPair;
43
+ encryption;
44
+ spendingPolicy;
45
+ evmWallet;
46
+ x402Client;
47
+ network;
48
+ startedAt;
49
+ address;
50
+ cursorStore;
51
+ subscriptions;
52
+ providerRunning = false;
53
+ constructor(params) {
54
+ this.keypair = params.keypair;
55
+ this.identityKeypair = params.identityKeypair;
56
+ this.authProvider = params.authProvider;
57
+ this.relayAuthProvider = params.relayAuthProvider;
58
+ this.did = params.did;
59
+ this.suiClient = params.suiClient;
60
+ this.registryClient = params.registryClient;
61
+ this.taskClient = params.taskClient;
62
+ this.agentCache = params.agentCache;
63
+ this.blobStore = params.blobStore;
64
+ this.encryptionKeyPair = params.encryptionKeyPair;
65
+ this.encryption = params.encryption;
66
+ this.spendingPolicy = params.spendingPolicy;
67
+ this.evmWallet = params.evmWallet;
68
+ this.x402Client = params.x402Client;
69
+ this.cursorStore = params.cursorStore;
70
+ this.network = params.network;
71
+ this.subscriptions = [];
72
+ this.startedAt = Date.now();
73
+ this.address = params.address;
74
+ }
75
+ static async create(config, identityContext) {
76
+ await mkdir(config.daemon.dataDir, { recursive: true });
77
+ await mkdir(config.identity.dataDir, { recursive: true });
78
+ const context = identityContext ?? await createDaemonIdentityContext(config);
79
+ if (!context.authProvider.isAuthenticated()) {
80
+ throw new Error("A valid auth session is required before the daemon can start.");
81
+ }
82
+ const suiClient = new MeshSuiClient(config.network);
83
+ const registryClient = new RegistryClient(suiClient, config.network);
84
+ const taskClient = new TaskClient(suiClient, config.network);
85
+ const agentCache = new AgentCache(join(config.daemon.dataDir, "agent-cache.sqlite"));
86
+ const encryptionKeyPair = ed25519ToX25519(context.identitySecretKey);
87
+ const baseBlobStore = await createBlobStore(config);
88
+ const blobStore = config.encryption.enabled ? new EncryptedBlobStore(baseBlobStore, encryptionKeyPair) : baseBlobStore;
89
+ const spendingPolicy = new SpendingPolicyEngine({
90
+ policy: config.spending,
91
+ dbPath: join(config.daemon.dataDir, "spending.sqlite")
92
+ });
93
+ const cursorStore = new SqliteCursorStore(join(config.daemon.dataDir, "event-cursors.sqlite"));
94
+ const address = await context.authProvider.getAddress();
95
+ const paymentContext = createPaymentContext(config, context);
96
+ return new _DaemonState({
97
+ keypair: context.authProvider.toSuiSigner(),
98
+ identityKeypair: context.identityKeypair,
99
+ authProvider: context.authProvider,
100
+ relayAuthProvider: new Ed25519AuthProvider(context.identityKeypair),
101
+ did: context.did,
102
+ suiClient,
103
+ registryClient,
104
+ taskClient,
105
+ agentCache,
106
+ blobStore,
107
+ encryptionKeyPair,
108
+ encryption: {
109
+ enabled: config.encryption.enabled,
110
+ requireEncryption: config.encryption.requireEncryption,
111
+ publicKey: config.encryption.enabled ? toHex(encryptionKeyPair.publicKey) : void 0
112
+ },
113
+ spendingPolicy,
114
+ evmWallet: paymentContext.evmWallet,
115
+ x402Client: paymentContext.x402Client,
116
+ cursorStore,
117
+ network: config.network,
118
+ address
119
+ });
120
+ }
121
+ setProviderRunning(providerRunning) {
122
+ this.providerRunning = providerRunning;
123
+ }
124
+ getStatusBase() {
125
+ return {
126
+ did: this.did,
127
+ address: this.address,
128
+ evmAddress: this.evmWallet?.address,
129
+ uptime: Date.now() - this.startedAt,
130
+ spendingToday: formatMistAsSui(this.spendingPolicy.getSpent("day", PaymentRail.SUI_ESCROW)),
131
+ providerRunning: this.providerRunning,
132
+ authMode: this.authProvider.mode,
133
+ authenticated: this.authProvider.isAuthenticated()
134
+ };
135
+ }
136
+ async shutdown() {
137
+ this.providerRunning = false;
138
+ for (const subscription of this.subscriptions) {
139
+ subscription.stop();
140
+ }
141
+ this.subscriptions.length = 0;
142
+ this.cursorStore.close();
143
+ this.agentCache.close();
144
+ this.spendingPolicy.close();
145
+ }
146
+ };
147
+ async function createDaemonIdentityContext(config) {
148
+ await mkdir(config.daemon.dataDir, { recursive: true });
149
+ await mkdir(config.identity.dataDir, { recursive: true });
150
+ const identity = await loadOrCreateKeypair(config.identity.dataDir);
151
+ const identityKeypair = Ed25519Keypair.fromSecretKey(identity.secretKey);
152
+ const did = createDID(identity.publicKey);
153
+ if (config.auth.mode !== "zklogin") {
154
+ return {
155
+ authProvider: new Ed25519AuthProvider(identityKeypair),
156
+ did,
157
+ identityKeypair,
158
+ identitySecretKey: identity.secretKey
159
+ };
160
+ }
161
+ const suiClient = new MeshSuiClient(config.network);
162
+ const sessionStore = new ZkLoginSessionStore(
163
+ join(config.daemon.dataDir, "sessions"),
164
+ deriveSessionEncryptionKey(identity.secretKey)
165
+ );
166
+ const currentEpoch = Number.parseInt((await suiClient.client.getCurrentEpoch()).epoch, 10);
167
+ const persistedSession = await sessionStore.loadLatestValid(currentEpoch);
168
+ const authProvider = new ZkLoginProvider({
169
+ client: suiClient.client,
170
+ oauth: buildOAuthConfig(config, "", getStoredOAuthProvider(persistedSession)),
171
+ sessionStore
172
+ });
173
+ try {
174
+ await authProvider.restoreSession();
175
+ } catch (error) {
176
+ if (!(error instanceof SessionExpiredError)) {
177
+ throw error;
178
+ }
179
+ logger.warn({ err: error, sessionState: authProvider.getSessionState() }, "Stored zkLogin session requires re-authentication.");
180
+ }
181
+ return {
182
+ authProvider,
183
+ did,
184
+ identityKeypair,
185
+ identitySecretKey: identity.secretKey
186
+ };
187
+ }
188
+ function buildOAuthConfig(config, redirectUri = "", preferredProvider) {
189
+ const provider = resolveOAuthProvider(config, preferredProvider);
190
+ if (provider === "google") {
191
+ return {
192
+ provider,
193
+ clientId: config.auth.google?.clientId ?? "",
194
+ redirectUri
195
+ };
196
+ }
197
+ return {
198
+ provider,
199
+ clientId: config.auth.apple?.clientId ?? "",
200
+ redirectUri
201
+ };
202
+ }
203
+ function resolveOAuthProvider(config, preferredProvider) {
204
+ if (preferredProvider === "google" && config.auth.google?.clientId) {
205
+ return "google";
206
+ }
207
+ if (preferredProvider === "apple" && config.auth.apple?.clientId) {
208
+ return "apple";
209
+ }
210
+ if (config.auth.google?.clientId) {
211
+ return "google";
212
+ }
213
+ if (config.auth.apple?.clientId) {
214
+ return "apple";
215
+ }
216
+ throw new Error("OAuth provider configuration is incomplete.");
217
+ }
218
+ function getStoredOAuthProvider(session) {
219
+ if (!session) {
220
+ return void 0;
221
+ }
222
+ if (session.provider === "google" || session.iss === "https://accounts.google.com") {
223
+ return "google";
224
+ }
225
+ if (session.provider === "apple" || session.iss === "https://appleid.apple.com") {
226
+ return "apple";
227
+ }
228
+ return void 0;
229
+ }
230
+ function createPaymentContext(config, identityContext) {
231
+ if (!config.payment.evm?.enabled || !(identityContext.authProvider instanceof ZkLoginProvider)) {
232
+ return {};
233
+ }
234
+ const session = identityContext.authProvider.getSession();
235
+ if (!session) {
236
+ return {};
237
+ }
238
+ const privateKey = deriveEvmKey(identityContext.identitySecretKey, session.salt, session.sub);
239
+ const evmWallet = new EvmWallet(privateKey, {
240
+ network: config.payment.evm.network,
241
+ rpcUrl: config.payment.evm.rpcUrl
242
+ });
243
+ return {
244
+ evmWallet,
245
+ x402Client: new X402Client(evmWallet)
246
+ };
247
+ }
248
+ async function createBlobStore(config) {
249
+ switch (config.blobstore.mode) {
250
+ case "filesystem": {
251
+ const dataDir = config.blobstore.filesystem?.dataDir;
252
+ if (!dataDir) {
253
+ throw new Error("blobstore.filesystem.dataDir is required for filesystem mode.");
254
+ }
255
+ await mkdir(dataDir, { recursive: true });
256
+ return new FilesystemBlobStore(dataDir);
257
+ }
258
+ case "walrus": {
259
+ const walrus = config.blobstore.walrus;
260
+ if (!walrus?.publisherUrl || !walrus.aggregatorUrl) {
261
+ throw new Error("blobstore.walrus.publisherUrl and blobstore.walrus.aggregatorUrl are required.");
262
+ }
263
+ return new WalrusBlobStore(walrus);
264
+ }
265
+ case "hybrid": {
266
+ const dataDir = config.blobstore.filesystem?.dataDir;
267
+ const walrus = config.blobstore.walrus;
268
+ if (!dataDir) {
269
+ throw new Error("blobstore.filesystem.dataDir is required for hybrid mode.");
270
+ }
271
+ if (!walrus?.publisherUrl || !walrus.aggregatorUrl) {
272
+ throw new Error("blobstore.walrus.publisherUrl and blobstore.walrus.aggregatorUrl are required for hybrid mode.");
273
+ }
274
+ await mkdir(dataDir, { recursive: true });
275
+ return new HybridBlobStore(
276
+ new WalrusBlobStore(walrus),
277
+ new FilesystemBlobStore(dataDir),
278
+ config.blobstore.hybrid
279
+ );
280
+ }
281
+ default:
282
+ throw new Error(`Unsupported blobstore mode: ${String(config.blobstore.mode)}`);
283
+ }
284
+ }
285
+ function deriveSessionEncryptionKey(secretKey) {
286
+ return createHash("sha256").update(Buffer.from(secretKey)).update("hivemind-collective:zklogin-session:v1").digest();
287
+ }
288
+ function formatMistAsSui(amountMist) {
289
+ const whole = amountMist / 1000000000n;
290
+ const fractional = amountMist % 1000000000n;
291
+ if (fractional === 0n) {
292
+ return `${whole.toString()} SUI`;
293
+ }
294
+ return `${whole.toString()}.${fractional.toString().padStart(9, "0").replace(/0+$/, "")} SUI`;
295
+ }
296
+ function toHex(value) {
297
+ return Buffer.from(value).toString("hex");
298
+ }
299
+
300
+ export {
301
+ DaemonState,
302
+ createDaemonIdentityContext,
303
+ buildOAuthConfig
304
+ };
305
+ //# sourceMappingURL=chunk-Q3V4V7UR.js.map