@geminixiang/mikan 0.3.0-beta.0 → 0.3.1
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.
- package/CHANGELOG.md +31 -0
- package/dist/adapter.d.ts +5 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/context.d.ts +0 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +62 -84
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/shared.d.ts +1 -2
- package/dist/adapters/shared.d.ts.map +1 -1
- package/dist/adapters/shared.js +3 -2
- package/dist/adapters/shared.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +9 -34
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +374 -358
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts +0 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +110 -113
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/slack/session.d.ts +0 -3
- package/dist/adapters/slack/session.d.ts.map +1 -1
- package/dist/adapters/slack/session.js +2 -8
- package/dist/adapters/slack/session.js.map +1 -1
- package/dist/adapters/slack/thread-manager.d.ts +0 -1
- package/dist/adapters/slack/thread-manager.d.ts.map +1 -1
- package/dist/adapters/slack/thread-manager.js +1 -4
- package/dist/adapters/slack/thread-manager.js.map +1 -1
- package/dist/adapters/slack/tools/block-kit.d.ts +16 -0
- package/dist/adapters/slack/tools/block-kit.d.ts.map +1 -0
- package/dist/adapters/slack/tools/block-kit.js +105 -0
- package/dist/adapters/slack/tools/block-kit.js.map +1 -0
- package/dist/adapters/telegram/context.d.ts +0 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +44 -54
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/admin/portal.d.ts.map +1 -1
- package/dist/admin/portal.js +2 -3
- package/dist/admin/portal.js.map +1 -1
- package/dist/agent.d.ts +0 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +47 -80
- package/dist/agent.js.map +1 -1
- package/dist/commands/admin.d.ts +0 -3
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +5 -30
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/session-view.d.ts.map +1 -1
- package/dist/commands/session-view.js +4 -17
- package/dist/commands/session-view.js.map +1 -1
- package/dist/commands/types.d.ts +3 -2
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/utils.d.ts +3 -1
- package/dist/commands/utils.d.ts.map +1 -1
- package/dist/commands/utils.js +15 -5
- package/dist/commands/utils.js.map +1 -1
- package/dist/context.d.ts +0 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +1 -23
- package/dist/context.js.map +1 -1
- package/dist/html.d.ts +2 -0
- package/dist/html.d.ts.map +1 -0
- package/dist/html.js +4 -0
- package/dist/html.js.map +1 -0
- package/dist/login/index.d.ts +2 -1
- package/dist/login/index.d.ts.map +1 -1
- package/dist/login/index.js.map +1 -1
- package/dist/login/portal.d.ts +1 -1
- package/dist/login/portal.d.ts.map +1 -1
- package/dist/login/portal.js +2 -3
- package/dist/login/portal.js.map +1 -1
- package/dist/login/{session.d.ts → store.d.ts} +1 -1
- package/dist/login/store.d.ts.map +1 -0
- package/dist/login/{session.js → store.js} +1 -1
- package/dist/login/store.js.map +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/portal-shell.d.ts +2 -2
- package/dist/portal-shell.d.ts.map +1 -1
- package/dist/portal-shell.js +11 -16
- package/dist/portal-shell.js.map +1 -1
- package/dist/sandbox/cloudflare.d.ts +0 -2
- package/dist/sandbox/cloudflare.d.ts.map +1 -1
- package/dist/sandbox/cloudflare.js +2 -2
- package/dist/sandbox/cloudflare.js.map +1 -1
- package/dist/sandbox/container.d.ts +0 -3
- package/dist/sandbox/container.d.ts.map +1 -1
- package/dist/sandbox/container.js +3 -3
- package/dist/sandbox/container.js.map +1 -1
- package/dist/sandbox/firecracker.d.ts +0 -2
- package/dist/sandbox/firecracker.d.ts.map +1 -1
- package/dist/sandbox/firecracker.js +2 -2
- package/dist/sandbox/firecracker.js.map +1 -1
- package/dist/sandbox/host.d.ts +0 -2
- package/dist/sandbox/host.d.ts.map +1 -1
- package/dist/sandbox/host.js +2 -2
- package/dist/sandbox/host.js.map +1 -1
- package/dist/sandbox/image.d.ts +0 -2
- package/dist/sandbox/image.d.ts.map +1 -1
- package/dist/sandbox/image.js +2 -2
- package/dist/sandbox/image.js.map +1 -1
- package/dist/sandbox/index.d.ts +1 -6
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/index.js +0 -5
- package/dist/sandbox/index.js.map +1 -1
- package/dist/sandbox/path-context.d.ts +0 -1
- package/dist/sandbox/path-context.d.ts.map +1 -1
- package/dist/sandbox/path-context.js +1 -1
- package/dist/sandbox/path-context.js.map +1 -1
- package/dist/sentry.d.ts +2 -2
- package/dist/sentry.d.ts.map +1 -1
- package/dist/sentry.js.map +1 -1
- package/dist/session-view/portal.d.ts.map +1 -1
- package/dist/session-view/portal.js +2 -8
- package/dist/session-view/portal.js.map +1 -1
- package/dist/sessions/chat-session-manager.d.ts.map +1 -1
- package/dist/sessions/chat-session-manager.js +5 -9
- package/dist/sessions/chat-session-manager.js.map +1 -1
- package/dist/tools/index.d.ts +2 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/vault-routing.d.ts +0 -1
- package/dist/vault-routing.d.ts.map +1 -1
- package/dist/vault-routing.js +1 -4
- package/dist/vault-routing.js.map +1 -1
- package/dist/vault.d.ts +2 -1
- package/dist/vault.d.ts.map +1 -1
- package/dist/vault.js.map +1 -1
- package/package.json +3 -1
- package/dist/login/session.d.ts.map +0 -1
- package/dist/login/session.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-session-manager.js","sourceRoot":"","sources":["../../src/sessions/chat-session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { SessionManager, type SessionEntry } from \"@earendil-works/pi-coding-agent\";\nimport { join } from \"path\";\nimport type { ConversationLogMessage } from \"../context.js\";\nimport { isRecord, parseJsonValue, readTextFileIfExists } from \"../file-guards.js\";\nimport { atomicWritePrivateFile } from \"../fs-atomic.js\";\nimport * as log from \"../log.js\";\nimport { isPlatformHistorySession } from \"./metadata.js\";\nimport {\n createManagedSessionFile,\n createManagedSessionFileAtPath,\n extractSessionSuffix,\n getChannelSessionDir,\n getThreadSessionFile,\n openManagedSession,\n resolveChannelSessionFile,\n tryResolveCurrentSession,\n tryResolveThreadSession,\n type ResolvedSessionScope,\n type ThreadRootMessage,\n} from \"./store.js\";\n\nconst DEFAULT_RECENT_DAYS = 14;\nconst DEFAULT_MAX_TOP_LEVEL_MESSAGES = 200;\nconst CHAT_SYNC_CUSTOM_TYPE = \"mikan.chat_sync\";\n\ninterface ChatSyncState {\n source?: string;\n messageCount?: number;\n lastMessageId?: string;\n}\n\ntype SessionAppendMessage = Parameters<SessionManager[\"appendMessage\"]>[0];\n\ninterface LogRecord {\n message: ConversationLogMessage;\n index: number;\n}\n\nexport interface ChatSessionManagerOptions {\n recentDays?: number;\n maxTopLevelMessages?: number;\n now?: () => Date;\n}\n\nexport interface ResolveChatSessionScopeOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n /** The triggering platform message ID. Excluded from bootstrap to avoid duplicate user turns. */\n currentMessageId?: string;\n}\n\nexport interface SyncChatSessionManagerOptions {\n conversationDir: string;\n sessionKey: string;\n sessionManager: SessionManager;\n /** The triggering platform message ID. Excluded from sync to avoid duplicate user turns. */\n currentMessageId?: string;\n}\n\nexport interface ResetChatSessionOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n}\n\nexport interface RegisterThreadSessionOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n}\n\nexport interface HasMaterializedSessionOptions {\n conversationDir: string;\n sessionKey: string;\n}\n\nexport interface ThreadBootstrapWaitOptions {\n parentSessionKey: string;\n sessionKey: string;\n hasThreadSession: () => boolean;\n isParentRunning: () => boolean;\n sleep?: (ms: number) => Promise<void>;\n pollMs?: number;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function isThreadSessionKey(sessionKey: string): boolean {\n return sessionKey.includes(\":\");\n}\n\nexport function extractThreadId(sessionKey: string): string {\n return extractSessionSuffix(sessionKey);\n}\n\nexport function hasMaterializedChatSession(options: HasMaterializedSessionOptions): boolean {\n if (!isThreadSessionKey(options.sessionKey)) {\n return resolveChannelSessionFile(options.conversationDir) !== null;\n }\n return (\n tryResolveThreadSession(getThreadSessionFile(options.conversationDir, options.sessionKey)) !==\n null\n );\n}\n\nexport function registerThreadSession(options: RegisterThreadSessionOptions): string | null {\n if (!isThreadSessionKey(options.sessionKey)) return null;\n\n const threadFile = getThreadSessionFile(options.conversationDir, options.sessionKey);\n return (\n tryResolveThreadSession(threadFile) ??\n createManagedSessionFileAtPath(threadFile, options.cwd ?? options.conversationDir)\n );\n}\n\nexport async function waitForThreadSessionBootstrap(\n options: ThreadBootstrapWaitOptions,\n): Promise<boolean> {\n const {\n parentSessionKey,\n sessionKey,\n hasThreadSession,\n isParentRunning,\n sleep = defaultSleep,\n pollMs = 100,\n } = options;\n\n if (!isThreadSessionKey(sessionKey)) return false;\n if (sessionKey === parentSessionKey) return false;\n if (hasThreadSession()) return false;\n\n let waited = false;\n while (isParentRunning() && !hasThreadSession()) {\n waited = true;\n await sleep(pollMs);\n }\n\n return waited;\n}\n\nexport class ChatSessionManager {\n private readonly recentDays: number;\n private readonly maxTopLevelMessages: number;\n private readonly now: () => Date;\n\n constructor(options: ChatSessionManagerOptions = {}) {\n this.recentDays = options.recentDays ?? DEFAULT_RECENT_DAYS;\n this.maxTopLevelMessages = options.maxTopLevelMessages ?? DEFAULT_MAX_TOP_LEVEL_MESSAGES;\n this.now = options.now ?? (() => new Date());\n }\n\n async resolveSessionScope(\n options: ResolveChatSessionScopeOptions,\n ): Promise<ResolvedSessionScope> {\n const cwd = options.cwd ?? options.conversationDir;\n const sessionDir = getChannelSessionDir(options.conversationDir);\n\n if (!isThreadSessionKey(options.sessionKey)) {\n const contextFile = this.resolveTopLevelSessionFile({\n conversationDir: options.conversationDir,\n sessionDir,\n cwd,\n currentMessageId: options.currentMessageId,\n });\n return { sessionDir, contextFile, threadRootMessage: null };\n }\n\n return this.resolveThreadSessionScope({\n conversationDir: options.conversationDir,\n sessionDir,\n sessionKey: options.sessionKey,\n cwd,\n currentMessageId: options.currentMessageId,\n });\n }\n\n syncSessionManager(options: SyncChatSessionManagerOptions): void {\n const records = readConversationLog(options.conversationDir);\n syncSessionManagerFromLog(\n options.sessionManager,\n selectExistingSessionSyncMessages(records, {\n sessionKey: isThreadSessionKey(options.sessionKey) ? options.sessionKey : null,\n excludeMessageId: options.currentMessageId,\n }),\n );\n }\n\n resetSession(options: ResetChatSessionOptions): string {\n const cwd = options.cwd ?? options.conversationDir;\n if (isThreadSessionKey(options.sessionKey)) {\n return createManagedSessionFileAtPath(\n getThreadSessionFile(options.conversationDir, options.sessionKey),\n cwd,\n );\n }\n\n return createManagedSessionFile(getChannelSessionDir(options.conversationDir), cwd);\n }\n\n registerThreadSession(options: RegisterThreadSessionOptions): string | null {\n return registerThreadSession(options);\n }\n\n hasMaterializedSession(options: HasMaterializedSessionOptions): boolean {\n return hasMaterializedChatSession(options);\n }\n\n private resolveTopLevelSessionFile(options: {\n conversationDir: string;\n sessionDir: string;\n cwd: string;\n currentMessageId?: string;\n }): string {\n const records = readConversationLog(options.conversationDir);\n const existing = tryResolveCurrentSession(options.sessionDir);\n if (existing && !isPlatformHistorySession(existing)) {\n syncSessionFromLog(\n existing,\n options.sessionDir,\n options.cwd,\n selectExistingSessionSyncMessages(records, {\n sessionKey: null,\n excludeMessageId: options.currentMessageId,\n }),\n );\n return existing;\n }\n\n const sessionFile = createManagedSessionFile(options.sessionDir, options.cwd);\n const bootstrapRecords = selectRecentTopLevelMessages(records, {\n recentDays: this.recentDays,\n maxMessages: this.maxTopLevelMessages,\n now: this.now(),\n excludeMessageId: options.currentMessageId,\n });\n bootstrapSessionFromLog(sessionFile, options.sessionDir, options.cwd, bootstrapRecords);\n return sessionFile;\n }\n\n private resolveThreadSessionScope(options: {\n conversationDir: string;\n sessionDir: string;\n sessionKey: string;\n cwd: string;\n currentMessageId?: string;\n }): ResolvedSessionScope {\n const threadFile = getThreadSessionFile(options.conversationDir, options.sessionKey);\n const threadId = extractThreadId(options.sessionKey);\n const records = readConversationLog(options.conversationDir);\n const threadRootMessage = buildThreadRootSeed(findLogRecordById(records, threadId)?.message);\n const existing = tryResolveThreadSession(threadFile);\n if (existing) {\n syncSessionFromLog(\n existing,\n options.sessionDir,\n options.cwd,\n selectExistingSessionSyncMessages(records, {\n sessionKey: options.sessionKey,\n excludeMessageId: options.currentMessageId,\n }),\n );\n return { sessionDir: options.sessionDir, contextFile: existing, threadRootMessage };\n }\n\n createManagedSessionFileAtPath(threadFile, options.cwd);\n const bootstrapRecords = selectThreadBootstrapMessages(records, threadId, {\n recentDays: this.recentDays,\n maxTopLevelMessages: this.maxTopLevelMessages,\n now: this.now(),\n excludeMessageId: options.currentMessageId,\n });\n bootstrapSessionFromLog(threadFile, options.sessionDir, options.cwd, bootstrapRecords);\n\n return { sessionDir: options.sessionDir, contextFile: threadFile, threadRootMessage };\n }\n}\n\nfunction readConversationLog(conversationDir: string): LogRecord[] {\n const logFile = join(conversationDir, \"log.jsonl\");\n const raw = readTextFileIfExists(logFile);\n if (raw === undefined) return [];\n\n const lines = raw.trim().split(\"\\n\").filter(Boolean);\n const records: LogRecord[] = [];\n for (let i = 0; i < lines.length; i++) {\n try {\n const message = parseJsonValue(\n lines[i],\n (value): value is ConversationLogMessage => isRecord(value),\n (detail) => (detail === \"unexpected JSON shape\" ? \"expected a JSON object\" : detail),\n );\n records.push({ message, index: i });\n } catch (err) {\n log.logWarning(\n `Skipping malformed log entry at ${logFile}:${i + 1}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n return records;\n}\n\nfunction findLogRecordById(records: LogRecord[], messageId: string): LogRecord | undefined {\n for (let i = records.length - 1; i >= 0; i--) {\n if (records[i].message.ts === messageId) return records[i];\n }\n return undefined;\n}\n\nfunction selectRecentTopLevelMessages(\n records: LogRecord[],\n options: {\n recentDays: number;\n maxMessages: number;\n now: Date;\n excludeMessageId?: string;\n },\n): LogRecord[] {\n const sinceMs = options.now.getTime() - options.recentDays * 24 * 60 * 60 * 1000;\n return records\n .filter((record) => isTopLevelHistoryMessage(record.message, sinceMs, options.excludeMessageId))\n .slice(-options.maxMessages);\n}\n\nfunction selectThreadBootstrapMessages(\n records: LogRecord[],\n threadId: string,\n options: {\n recentDays: number;\n maxTopLevelMessages: number;\n now: Date;\n excludeMessageId?: string;\n },\n): LogRecord[] {\n const rootRecord = findLogRecordById(records, threadId);\n const topLevelSource = rootRecord\n ? records.filter((record) => record.index <= rootRecord.index)\n : records;\n const topLevelRecords = selectRecentTopLevelMessages(topLevelSource, {\n recentDays: options.recentDays,\n maxMessages: options.maxTopLevelMessages,\n now: options.now,\n excludeMessageId: options.excludeMessageId,\n });\n const threadRecords = records.filter(\n (record) =>\n isRenderableChatMessage(record.message, options.excludeMessageId) &&\n (record.message.ts === threadId || record.message.threadTs === threadId),\n );\n\n return dedupeAndSortRecords([...topLevelRecords, ...threadRecords]);\n}\n\nfunction isTopLevelHistoryMessage(\n message: ConversationLogMessage,\n sinceMs: number,\n excludeMessageId?: string,\n): boolean {\n if (!isRenderableChatMessage(message, excludeMessageId)) return false;\n if (message.threadTs) return false;\n if (!message.date) return true;\n\n const dateMs = new Date(message.date).getTime();\n return !Number.isFinite(dateMs) || dateMs >= sinceMs;\n}\n\nfunction selectExistingSessionSyncMessages(\n records: LogRecord[],\n options: { sessionKey: string | null; excludeMessageId?: string },\n): LogRecord[] {\n const threadId = options.sessionKey ? extractThreadId(options.sessionKey) : null;\n return dedupeAndSortRecords(\n records.filter((record) => {\n if (!isRenderableChatMessage(record.message, options.excludeMessageId)) return false;\n if (!threadId) return !record.message.threadTs;\n return record.message.ts === threadId || record.message.threadTs === threadId;\n }),\n );\n}\n\nfunction isRenderableChatMessage(\n message: ConversationLogMessage,\n excludeMessageId?: string,\n): boolean {\n if (excludeMessageId && message.ts === excludeMessageId) return false;\n if (isChatCommandMessage(message)) return false;\n return !!message.text?.trim();\n}\n\nfunction isChatCommandMessage(message: ConversationLogMessage): boolean {\n const text = message.text?.trim() ?? \"\";\n return (\n !message.isBot &&\n /^\\/(?:pi-[\\w-]+|login|session|new|stop|model|sandbox|admin|auto-reply)(?:@\\w+)?(?:\\s|$)/i.test(\n text,\n )\n );\n}\n\nfunction dedupeAndSortRecords(records: LogRecord[]): LogRecord[] {\n const byKey = new Map<string, LogRecord>();\n for (const record of records) {\n byKey.set(record.message.ts ?? `line:${record.index}`, record);\n }\n\n return Array.from(byKey.values()).toSorted((a, b) => {\n const aTime = sortTime(a);\n const bTime = sortTime(b);\n if (aTime !== bTime) return aTime - bTime;\n return a.index - b.index;\n });\n}\n\nfunction sortTime(record: LogRecord): number {\n if (record.message.date) {\n const dateMs = new Date(record.message.date).getTime();\n if (Number.isFinite(dateMs)) return dateMs;\n }\n\n if (record.message.ts) {\n const tsMs = Number(record.message.ts) * 1000;\n if (Number.isFinite(tsMs)) return tsMs;\n }\n\n return record.index;\n}\n\nfunction bootstrapSessionFromLog(\n sessionFile: string,\n sessionDir: string,\n cwd: string,\n records: LogRecord[],\n): void {\n if (records.length === 0) return;\n\n const sessionManager = openManagedSession(sessionFile, sessionDir, cwd);\n appendLogRecordsToSession(sessionManager, records);\n sessionManager.appendCustomEntry(CHAT_SYNC_CUSTOM_TYPE, {\n source: \"log.jsonl\",\n messageCount: records.length,\n lastMessageId: records.at(-1)?.message.ts,\n });\n forceRewriteSession(sessionManager, sessionFile);\n}\n\nfunction syncSessionFromLog(\n sessionFile: string,\n sessionDir: string,\n cwd: string,\n records: LogRecord[],\n): void {\n if (records.length === 0) return;\n syncSessionManagerFromLog(openManagedSession(sessionFile, sessionDir, cwd), records);\n}\n\nfunction syncSessionManagerFromLog(sessionManager: SessionManager, records: LogRecord[]): void {\n if (records.length === 0) return;\n\n const existingEntries = sessionManager.getEntries();\n const lastSyncedMessageId = getLatestChatSyncState(existingEntries)?.lastMessageId;\n const startIndex = lastSyncedMessageId\n ? records.findIndex((record) => record.message.ts === lastSyncedMessageId) + 1\n : 0;\n const syncCandidates = records.slice(Math.max(startIndex, 0));\n if (syncCandidates.length === 0) return;\n\n const represented = buildRepresentedMessageCounts(existingEntries);\n const newRecords = syncCandidates.filter(\n (record) => !consumeRepresentedLogMessage(record, represented),\n );\n if (newRecords.length === 0) return;\n\n appendLogRecordsToSession(sessionManager, newRecords);\n sessionManager.appendCustomEntry(CHAT_SYNC_CUSTOM_TYPE, {\n source: \"log.jsonl\",\n messageCount: newRecords.length,\n lastMessageId: syncCandidates.at(-1)?.message.ts,\n });\n}\n\nfunction appendLogRecordsToSession(sessionManager: SessionManager, records: LogRecord[]): void {\n for (const record of records) {\n const message = buildHistorySessionMessage(record.message);\n if (message) sessionManager.appendMessage(message);\n }\n}\n\nfunction forceRewriteSession(sessionManager: SessionManager, sessionFile: string): void {\n const header = sessionManager.getHeader();\n if (!header) return;\n\n const content = [header, ...sessionManager.getEntries()]\n .map((entry) => JSON.stringify(entry))\n .join(\"\\n\");\n atomicWritePrivateFile(sessionFile, `${content}\\n`);\n}\n\nfunction getLatestChatSyncState(entries: SessionEntry[]): ChatSyncState | null {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry.type !== \"custom\" || entry.customType !== CHAT_SYNC_CUSTOM_TYPE) continue;\n if (!isRecord(entry.data)) return null;\n return {\n source: typeof entry.data.source === \"string\" ? entry.data.source : undefined,\n messageCount:\n typeof entry.data.messageCount === \"number\" ? entry.data.messageCount : undefined,\n lastMessageId:\n typeof entry.data.lastMessageId === \"string\" ? entry.data.lastMessageId : undefined,\n };\n }\n return null;\n}\n\nfunction buildRepresentedMessageCounts(entries: SessionEntry[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const entry of entries) {\n const comparable = comparableSessionMessage(entry);\n if (!comparable) continue;\n counts.set(comparable, (counts.get(comparable) ?? 0) + 1);\n }\n return counts;\n}\n\nfunction consumeRepresentedLogMessage(record: LogRecord, counts: Map<string, number>): boolean {\n const comparable = comparableLogMessage(record.message);\n if (!comparable) return false;\n\n const count = counts.get(comparable) ?? 0;\n if (count <= 0) return false;\n counts.set(comparable, count - 1);\n return true;\n}\n\nfunction comparableSessionMessage(entry: SessionEntry): string | null {\n if (entry.type !== \"message\") return null;\n const role = entry.message.role;\n if (role !== \"user\" && role !== \"assistant\") return null;\n\n const text = normalizeComparableText(getSessionMessageText(entry));\n if (!text) return null;\n return `${role}:${text}`;\n}\n\nfunction comparableLogMessage(message: ConversationLogMessage): string | null {\n const text = message.text?.trim();\n if (!text) return null;\n return `${message.isBot ? \"assistant\" : \"user\"}:${normalizeComparableText(text)}`;\n}\n\nfunction getSessionMessageText(entry: SessionEntry): string {\n if (entry.type !== \"message\" || !(\"content\" in entry.message)) return \"\";\n const content = entry.message.content;\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n return content\n .map((part) => (part.type === \"text\" && \"text\" in part ? part.text : \"\"))\n .join(\"\\n\");\n}\n\nfunction normalizeComparableText(text: string): string {\n return text\n .replace(\n /^\\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}\\]\\s+\\[[^\\]]+\\](?:\\s+\\[in-thread:[^\\]]+\\])?:\\s*/,\n \"\",\n )\n .trim();\n}\n\nfunction buildHistorySessionMessage(message: ConversationLogMessage): SessionAppendMessage | null {\n const text = message.text?.trim();\n if (!text) return null;\n\n const timestamp = parseMessageTimestamp(message);\n if (!message.isBot) {\n return {\n role: \"user\",\n content: [{ type: \"text\", text: formatHistoryMessage(message) }],\n ...(timestamp !== undefined ? { timestamp } : {}),\n } as SessionAppendMessage;\n }\n\n return {\n role: \"assistant\",\n content: [{ type: \"text\", text }],\n api: \"platform-history\",\n provider: \"platform-history\",\n model: \"platform-history\",\n usage: zeroUsage(),\n stopReason: \"stop\",\n ...(timestamp !== undefined ? { timestamp } : {}),\n } as SessionAppendMessage;\n}\n\nfunction buildThreadRootSeed(\n message: ConversationLogMessage | undefined,\n): ThreadRootMessage | null {\n if (!message) return null;\n return {\n text: message.text,\n userName: message.userName,\n user: message.user,\n loggedAt: message.date ? new Date(message.date).getTime() : undefined,\n isBot: message.isBot,\n };\n}\n\nfunction parseMessageTimestamp(message: ConversationLogMessage): number | undefined {\n if (message.date) {\n const dateMs = new Date(message.date).getTime();\n if (Number.isFinite(dateMs)) return dateMs;\n }\n\n if (message.ts) {\n const tsMs = Number(message.ts) * 1000;\n if (Number.isFinite(tsMs)) return tsMs;\n }\n\n return undefined;\n}\n\nfunction formatHistoryMessage(message: ConversationLogMessage): string {\n const text = message.text?.trim() ?? \"\";\n const userLabel = message.userName || message.user || \"unknown\";\n const timestamp = message.date ? formatLocalTimestamp(new Date(message.date)) : null;\n return timestamp ? `[${timestamp}] [${userLabel}]: ${text}` : `[${userLabel}]: ${text}`;\n}\n\nfunction formatLocalTimestamp(date: Date): string | null {\n const time = date.getTime();\n if (!Number.isFinite(time)) return null;\n\n const offset = -date.getTimezoneOffset();\n const sign = offset >= 0 ? \"+\" : \"-\";\n const abs = Math.abs(offset);\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` +\n `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`\n );\n}\n\nfunction pad(n: number): string {\n return n.toString().padStart(2, \"0\");\n}\n\nfunction zeroUsage(): object {\n return {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chat-session-manager.js","sourceRoot":"","sources":["../../src/sessions/chat-session-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { SessionManager, type SessionEntry } from \"@earendil-works/pi-coding-agent\";\nimport { join } from \"path\";\nimport type { ConversationLogMessage } from \"../context.js\";\nimport { isRecord, parseJsonValue, readTextFileIfExists } from \"../file-guards.js\";\nimport { atomicWritePrivateFile } from \"../fs-atomic.js\";\nimport * as log from \"../log.js\";\nimport { isPlatformHistorySession } from \"./metadata.js\";\nimport {\n createManagedSessionFile,\n createManagedSessionFileAtPath,\n extractSessionSuffix,\n getChannelSessionDir,\n getThreadSessionFile,\n openManagedSession,\n resolveChannelSessionFile,\n tryResolveCurrentSession,\n tryResolveThreadSession,\n type ResolvedSessionScope,\n type ThreadRootMessage,\n} from \"./store.js\";\n\nconst DEFAULT_RECENT_DAYS = 14;\nconst DEFAULT_MAX_TOP_LEVEL_MESSAGES = 200;\nconst CHAT_SYNC_CUSTOM_TYPE = \"mikan.chat_sync\";\n\ntype SessionAppendMessage = Parameters<SessionManager[\"appendMessage\"]>[0];\n\ninterface LogRecord {\n message: ConversationLogMessage;\n index: number;\n}\n\nexport interface ChatSessionManagerOptions {\n recentDays?: number;\n maxTopLevelMessages?: number;\n now?: () => Date;\n}\n\nexport interface ResolveChatSessionScopeOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n /** The triggering platform message ID. Excluded from bootstrap to avoid duplicate user turns. */\n currentMessageId?: string;\n}\n\nexport interface SyncChatSessionManagerOptions {\n conversationDir: string;\n sessionKey: string;\n sessionManager: SessionManager;\n /** The triggering platform message ID. Excluded from sync to avoid duplicate user turns. */\n currentMessageId?: string;\n}\n\nexport interface ResetChatSessionOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n}\n\nexport interface RegisterThreadSessionOptions {\n conversationDir: string;\n sessionKey: string;\n cwd?: string;\n}\n\nexport interface HasMaterializedSessionOptions {\n conversationDir: string;\n sessionKey: string;\n}\n\nexport interface ThreadBootstrapWaitOptions {\n parentSessionKey: string;\n sessionKey: string;\n hasThreadSession: () => boolean;\n isParentRunning: () => boolean;\n sleep?: (ms: number) => Promise<void>;\n pollMs?: number;\n}\n\nfunction defaultSleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function isThreadSessionKey(sessionKey: string): boolean {\n return sessionKey.includes(\":\");\n}\n\nexport function extractThreadId(sessionKey: string): string {\n return extractSessionSuffix(sessionKey);\n}\n\nexport function hasMaterializedChatSession(options: HasMaterializedSessionOptions): boolean {\n if (!isThreadSessionKey(options.sessionKey)) {\n return resolveChannelSessionFile(options.conversationDir) !== null;\n }\n return (\n tryResolveThreadSession(getThreadSessionFile(options.conversationDir, options.sessionKey)) !==\n null\n );\n}\n\nexport function registerThreadSession(options: RegisterThreadSessionOptions): string | null {\n if (!isThreadSessionKey(options.sessionKey)) return null;\n\n const threadFile = getThreadSessionFile(options.conversationDir, options.sessionKey);\n return (\n tryResolveThreadSession(threadFile) ??\n createManagedSessionFileAtPath(threadFile, options.cwd ?? options.conversationDir)\n );\n}\n\nexport async function waitForThreadSessionBootstrap(\n options: ThreadBootstrapWaitOptions,\n): Promise<boolean> {\n const {\n parentSessionKey,\n sessionKey,\n hasThreadSession,\n isParentRunning,\n sleep = defaultSleep,\n pollMs = 100,\n } = options;\n\n if (!isThreadSessionKey(sessionKey)) return false;\n if (sessionKey === parentSessionKey) return false;\n if (hasThreadSession()) return false;\n\n let waited = false;\n while (isParentRunning() && !hasThreadSession()) {\n waited = true;\n await sleep(pollMs);\n }\n\n return waited;\n}\n\nexport class ChatSessionManager {\n private readonly recentDays: number;\n private readonly maxTopLevelMessages: number;\n private readonly now: () => Date;\n\n constructor(options: ChatSessionManagerOptions = {}) {\n this.recentDays = options.recentDays ?? DEFAULT_RECENT_DAYS;\n this.maxTopLevelMessages = options.maxTopLevelMessages ?? DEFAULT_MAX_TOP_LEVEL_MESSAGES;\n this.now = options.now ?? (() => new Date());\n }\n\n async resolveSessionScope(\n options: ResolveChatSessionScopeOptions,\n ): Promise<ResolvedSessionScope> {\n const cwd = options.cwd ?? options.conversationDir;\n const sessionDir = getChannelSessionDir(options.conversationDir);\n\n if (!isThreadSessionKey(options.sessionKey)) {\n const contextFile = this.resolveTopLevelSessionFile({\n conversationDir: options.conversationDir,\n sessionDir,\n cwd,\n currentMessageId: options.currentMessageId,\n });\n return { sessionDir, contextFile, threadRootMessage: null };\n }\n\n return this.resolveThreadSessionScope({\n conversationDir: options.conversationDir,\n sessionDir,\n sessionKey: options.sessionKey,\n cwd,\n currentMessageId: options.currentMessageId,\n });\n }\n\n syncSessionManager(options: SyncChatSessionManagerOptions): void {\n const records = readConversationLog(options.conversationDir);\n syncSessionManagerFromLog(\n options.sessionManager,\n selectExistingSessionSyncMessages(records, {\n sessionKey: isThreadSessionKey(options.sessionKey) ? options.sessionKey : null,\n excludeMessageId: options.currentMessageId,\n }),\n );\n }\n\n resetSession(options: ResetChatSessionOptions): string {\n const cwd = options.cwd ?? options.conversationDir;\n if (isThreadSessionKey(options.sessionKey)) {\n return createManagedSessionFileAtPath(\n getThreadSessionFile(options.conversationDir, options.sessionKey),\n cwd,\n );\n }\n\n return createManagedSessionFile(getChannelSessionDir(options.conversationDir), cwd);\n }\n\n registerThreadSession(options: RegisterThreadSessionOptions): string | null {\n return registerThreadSession(options);\n }\n\n hasMaterializedSession(options: HasMaterializedSessionOptions): boolean {\n return hasMaterializedChatSession(options);\n }\n\n private resolveTopLevelSessionFile(options: {\n conversationDir: string;\n sessionDir: string;\n cwd: string;\n currentMessageId?: string;\n }): string {\n const records = readConversationLog(options.conversationDir);\n const existing = tryResolveCurrentSession(options.sessionDir);\n if (existing && !isPlatformHistorySession(existing)) {\n syncSessionFromLog(\n existing,\n options.sessionDir,\n options.cwd,\n selectExistingSessionSyncMessages(records, {\n sessionKey: null,\n excludeMessageId: options.currentMessageId,\n }),\n );\n return existing;\n }\n\n const sessionFile = createManagedSessionFile(options.sessionDir, options.cwd);\n const bootstrapRecords = selectRecentTopLevelMessages(records, {\n recentDays: this.recentDays,\n maxMessages: this.maxTopLevelMessages,\n now: this.now(),\n excludeMessageId: options.currentMessageId,\n });\n bootstrapSessionFromLog(sessionFile, options.sessionDir, options.cwd, bootstrapRecords);\n return sessionFile;\n }\n\n private resolveThreadSessionScope(options: {\n conversationDir: string;\n sessionDir: string;\n sessionKey: string;\n cwd: string;\n currentMessageId?: string;\n }): ResolvedSessionScope {\n const threadFile = getThreadSessionFile(options.conversationDir, options.sessionKey);\n const threadId = extractThreadId(options.sessionKey);\n const records = readConversationLog(options.conversationDir);\n const threadRootMessage = buildThreadRootSeed(findLogRecordById(records, threadId)?.message);\n const existing = tryResolveThreadSession(threadFile);\n if (existing) {\n syncSessionFromLog(\n existing,\n options.sessionDir,\n options.cwd,\n selectExistingSessionSyncMessages(records, {\n sessionKey: options.sessionKey,\n excludeMessageId: options.currentMessageId,\n }),\n );\n return { sessionDir: options.sessionDir, contextFile: existing, threadRootMessage };\n }\n\n createManagedSessionFileAtPath(threadFile, options.cwd);\n const bootstrapRecords = selectThreadBootstrapMessages(records, threadId, {\n recentDays: this.recentDays,\n maxTopLevelMessages: this.maxTopLevelMessages,\n now: this.now(),\n excludeMessageId: options.currentMessageId,\n });\n bootstrapSessionFromLog(threadFile, options.sessionDir, options.cwd, bootstrapRecords);\n\n return { sessionDir: options.sessionDir, contextFile: threadFile, threadRootMessage };\n }\n}\n\nfunction readConversationLog(conversationDir: string): LogRecord[] {\n const logFile = join(conversationDir, \"log.jsonl\");\n const raw = readTextFileIfExists(logFile);\n if (raw === undefined) return [];\n\n const lines = raw.trim().split(\"\\n\").filter(Boolean);\n const records: LogRecord[] = [];\n for (let i = 0; i < lines.length; i++) {\n try {\n const message = parseJsonValue(\n lines[i],\n (value): value is ConversationLogMessage => isRecord(value),\n (detail) => (detail === \"unexpected JSON shape\" ? \"expected a JSON object\" : detail),\n );\n records.push({ message, index: i });\n } catch (err) {\n log.logWarning(\n `Skipping malformed log entry at ${logFile}:${i + 1}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n return records;\n}\n\nfunction findLogRecordById(records: LogRecord[], messageId: string): LogRecord | undefined {\n for (let i = records.length - 1; i >= 0; i--) {\n if (records[i].message.ts === messageId) return records[i];\n }\n return undefined;\n}\n\nfunction selectRecentTopLevelMessages(\n records: LogRecord[],\n options: {\n recentDays: number;\n maxMessages: number;\n now: Date;\n excludeMessageId?: string;\n },\n): LogRecord[] {\n const sinceMs = options.now.getTime() - options.recentDays * 24 * 60 * 60 * 1000;\n return records\n .filter((record) => isTopLevelHistoryMessage(record.message, sinceMs, options.excludeMessageId))\n .slice(-options.maxMessages);\n}\n\nfunction selectThreadBootstrapMessages(\n records: LogRecord[],\n threadId: string,\n options: {\n recentDays: number;\n maxTopLevelMessages: number;\n now: Date;\n excludeMessageId?: string;\n },\n): LogRecord[] {\n const rootRecord = findLogRecordById(records, threadId);\n const topLevelSource = rootRecord\n ? records.filter((record) => record.index <= rootRecord.index)\n : records;\n const topLevelRecords = selectRecentTopLevelMessages(topLevelSource, {\n recentDays: options.recentDays,\n maxMessages: options.maxTopLevelMessages,\n now: options.now,\n excludeMessageId: options.excludeMessageId,\n });\n const threadRecords = records.filter(\n (record) =>\n isRenderableChatMessage(record.message, options.excludeMessageId) &&\n (record.message.ts === threadId || record.message.threadTs === threadId),\n );\n\n return dedupeAndSortRecords([...topLevelRecords, ...threadRecords]);\n}\n\nfunction isTopLevelHistoryMessage(\n message: ConversationLogMessage,\n sinceMs: number,\n excludeMessageId?: string,\n): boolean {\n if (!isRenderableChatMessage(message, excludeMessageId)) return false;\n if (message.threadTs) return false;\n if (!message.date) return true;\n\n const dateMs = new Date(message.date).getTime();\n return !Number.isFinite(dateMs) || dateMs >= sinceMs;\n}\n\nfunction selectExistingSessionSyncMessages(\n records: LogRecord[],\n options: { sessionKey: string | null; excludeMessageId?: string },\n): LogRecord[] {\n const threadId = options.sessionKey ? extractThreadId(options.sessionKey) : null;\n return dedupeAndSortRecords(\n records.filter((record) => {\n if (!isRenderableChatMessage(record.message, options.excludeMessageId)) return false;\n if (!threadId) return !record.message.threadTs;\n return record.message.ts === threadId || record.message.threadTs === threadId;\n }),\n );\n}\n\nfunction isRenderableChatMessage(\n message: ConversationLogMessage,\n excludeMessageId?: string,\n): boolean {\n if (excludeMessageId && message.ts === excludeMessageId) return false;\n if (isChatCommandMessage(message)) return false;\n return !!message.text?.trim();\n}\n\nfunction isChatCommandMessage(message: ConversationLogMessage): boolean {\n const text = message.text?.trim() ?? \"\";\n return (\n !message.isBot &&\n /^\\/(?:pi-[\\w-]+|login|session|new|stop|model|sandbox|admin|auto-reply)(?:@\\w+)?(?:\\s|$)/i.test(\n text,\n )\n );\n}\n\nfunction dedupeAndSortRecords(records: LogRecord[]): LogRecord[] {\n const byKey = new Map<string, LogRecord>();\n for (const record of records) {\n byKey.set(record.message.ts ?? `line:${record.index}`, record);\n }\n\n return Array.from(byKey.values()).toSorted((a, b) => {\n const aTime = sortTime(a);\n const bTime = sortTime(b);\n if (aTime !== bTime) return aTime - bTime;\n return a.index - b.index;\n });\n}\n\nfunction sortTime(record: LogRecord): number {\n if (record.message.date) {\n const dateMs = new Date(record.message.date).getTime();\n if (Number.isFinite(dateMs)) return dateMs;\n }\n\n if (record.message.ts) {\n const tsMs = Number(record.message.ts) * 1000;\n if (Number.isFinite(tsMs)) return tsMs;\n }\n\n return record.index;\n}\n\nfunction bootstrapSessionFromLog(\n sessionFile: string,\n sessionDir: string,\n cwd: string,\n records: LogRecord[],\n): void {\n if (records.length === 0) return;\n\n const sessionManager = openManagedSession(sessionFile, sessionDir, cwd);\n appendLogRecordsToSession(sessionManager, records);\n sessionManager.appendCustomEntry(CHAT_SYNC_CUSTOM_TYPE, {\n source: \"log.jsonl\",\n messageCount: records.length,\n lastMessageId: records.at(-1)?.message.ts,\n });\n forceRewriteSession(sessionManager, sessionFile);\n}\n\nfunction syncSessionFromLog(\n sessionFile: string,\n sessionDir: string,\n cwd: string,\n records: LogRecord[],\n): void {\n if (records.length === 0) return;\n syncSessionManagerFromLog(openManagedSession(sessionFile, sessionDir, cwd), records);\n}\n\nfunction syncSessionManagerFromLog(sessionManager: SessionManager, records: LogRecord[]): void {\n if (records.length === 0) return;\n\n const existingEntries = sessionManager.getEntries();\n const lastSyncedMessageId = getLatestChatSyncMessageId(existingEntries);\n const startIndex = lastSyncedMessageId\n ? records.findIndex((record) => record.message.ts === lastSyncedMessageId) + 1\n : 0;\n const syncCandidates = records.slice(Math.max(startIndex, 0));\n if (syncCandidates.length === 0) return;\n\n const represented = buildRepresentedMessageCounts(existingEntries);\n const newRecords = syncCandidates.filter(\n (record) => !consumeRepresentedLogMessage(record, represented),\n );\n if (newRecords.length === 0) return;\n\n appendLogRecordsToSession(sessionManager, newRecords);\n sessionManager.appendCustomEntry(CHAT_SYNC_CUSTOM_TYPE, {\n source: \"log.jsonl\",\n messageCount: newRecords.length,\n lastMessageId: syncCandidates.at(-1)?.message.ts,\n });\n}\n\nfunction appendLogRecordsToSession(sessionManager: SessionManager, records: LogRecord[]): void {\n for (const record of records) {\n const message = buildHistorySessionMessage(record.message);\n if (message) sessionManager.appendMessage(message);\n }\n}\n\nfunction forceRewriteSession(sessionManager: SessionManager, sessionFile: string): void {\n const header = sessionManager.getHeader();\n if (!header) return;\n\n const content = [header, ...sessionManager.getEntries()]\n .map((entry) => JSON.stringify(entry))\n .join(\"\\n\");\n atomicWritePrivateFile(sessionFile, `${content}\\n`);\n}\n\nfunction getLatestChatSyncMessageId(entries: SessionEntry[]): string | undefined {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry.type !== \"custom\" || entry.customType !== CHAT_SYNC_CUSTOM_TYPE) continue;\n if (!isRecord(entry.data)) return undefined;\n return typeof entry.data.lastMessageId === \"string\" ? entry.data.lastMessageId : undefined;\n }\n return undefined;\n}\n\nfunction buildRepresentedMessageCounts(entries: SessionEntry[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const entry of entries) {\n const comparable = comparableSessionMessage(entry);\n if (!comparable) continue;\n counts.set(comparable, (counts.get(comparable) ?? 0) + 1);\n }\n return counts;\n}\n\nfunction consumeRepresentedLogMessage(record: LogRecord, counts: Map<string, number>): boolean {\n const comparable = comparableLogMessage(record.message);\n if (!comparable) return false;\n\n const count = counts.get(comparable) ?? 0;\n if (count <= 0) return false;\n counts.set(comparable, count - 1);\n return true;\n}\n\nfunction comparableSessionMessage(entry: SessionEntry): string | null {\n if (entry.type !== \"message\") return null;\n const role = entry.message.role;\n if (role !== \"user\" && role !== \"assistant\") return null;\n\n const text = normalizeComparableText(getSessionMessageText(entry));\n if (!text) return null;\n return `${role}:${text}`;\n}\n\nfunction comparableLogMessage(message: ConversationLogMessage): string | null {\n const text = message.text?.trim();\n if (!text) return null;\n return `${message.isBot ? \"assistant\" : \"user\"}:${normalizeComparableText(text)}`;\n}\n\nfunction getSessionMessageText(entry: SessionEntry): string {\n if (entry.type !== \"message\" || !(\"content\" in entry.message)) return \"\";\n const content = entry.message.content;\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n return content\n .map((part) => (part.type === \"text\" && \"text\" in part ? part.text : \"\"))\n .join(\"\\n\");\n}\n\nfunction normalizeComparableText(text: string): string {\n return text\n .replace(\n /^\\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}\\]\\s+\\[[^\\]]+\\](?:\\s+\\[in-thread:[^\\]]+\\])?:\\s*/,\n \"\",\n )\n .trim();\n}\n\nfunction buildHistorySessionMessage(message: ConversationLogMessage): SessionAppendMessage | null {\n const text = message.text?.trim();\n if (!text) return null;\n\n const timestamp = parseMessageTimestamp(message);\n if (!message.isBot) {\n return {\n role: \"user\",\n content: [{ type: \"text\", text: formatHistoryMessage(message) }],\n ...(timestamp !== undefined ? { timestamp } : {}),\n } as SessionAppendMessage;\n }\n\n return {\n role: \"assistant\",\n content: [{ type: \"text\", text }],\n api: \"platform-history\",\n provider: \"platform-history\",\n model: \"platform-history\",\n usage: zeroUsage(),\n stopReason: \"stop\",\n ...(timestamp !== undefined ? { timestamp } : {}),\n } as SessionAppendMessage;\n}\n\nfunction buildThreadRootSeed(\n message: ConversationLogMessage | undefined,\n): ThreadRootMessage | null {\n if (!message) return null;\n return {\n text: message.text,\n userName: message.userName,\n user: message.user,\n loggedAt: message.date ? new Date(message.date).getTime() : undefined,\n isBot: message.isBot,\n };\n}\n\nfunction parseMessageTimestamp(message: ConversationLogMessage): number | undefined {\n if (message.date) {\n const dateMs = new Date(message.date).getTime();\n if (Number.isFinite(dateMs)) return dateMs;\n }\n\n if (message.ts) {\n const tsMs = Number(message.ts) * 1000;\n if (Number.isFinite(tsMs)) return tsMs;\n }\n\n return undefined;\n}\n\nfunction formatHistoryMessage(message: ConversationLogMessage): string {\n const text = message.text?.trim() ?? \"\";\n const userLabel = message.userName || message.user || \"unknown\";\n const timestamp = message.date ? formatLocalTimestamp(new Date(message.date)) : null;\n return timestamp ? `[${timestamp}] [${userLabel}]: ${text}` : `[${userLabel}]: ${text}`;\n}\n\nfunction formatLocalTimestamp(date: Date): string | null {\n const time = date.getTime();\n if (!Number.isFinite(time)) return null;\n\n const offset = -date.getTimezoneOffset();\n const sign = offset >= 0 ? \"+\" : \"-\";\n const abs = Math.abs(offset);\n return (\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ` +\n `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}` +\n `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`\n );\n}\n\nfunction pad(n: number): string {\n return n.toString().padStart(2, \"0\");\n}\n\nfunction zeroUsage(): object {\n return {\n input: 0,\n output: 0,\n cacheRead: 0,\n cacheWrite: 0,\n totalTokens: 0,\n cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n };\n}\n"]}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AgentTool } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { TSchema } from "@sinclair/typebox";
|
|
3
|
+
import type { ChatResponseBlockKit } from "../adapter.js";
|
|
3
4
|
import type { DockerContainerManager } from "../provisioner.js";
|
|
4
5
|
import type { Executor, SandboxConfig } from "../sandbox/index.js";
|
|
5
6
|
export declare function createMikanTools(executor: Executor, workspaceDir: string, sandboxController?: {
|
|
@@ -8,6 +9,7 @@ export declare function createMikanTools(executor: Executor, workspaceDir: strin
|
|
|
8
9
|
}): {
|
|
9
10
|
tools: AgentTool<TSchema>[];
|
|
10
11
|
setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;
|
|
12
|
+
setBlockKitResponseFunction: (fn: (response: ChatResponseBlockKit) => Promise<void>) => void;
|
|
11
13
|
setEventContext: (context: {
|
|
12
14
|
platform: string;
|
|
13
15
|
conversationId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAG1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAQnE,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,iBAAiB,CAAC,EAAE;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CAC5E,GACA;IACD,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5B,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACrF,2BAA2B,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAC7F,eAAe,EAAE,CAAC,OAAO,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,QAAQ,GAAG,QAAQ,CAAC;QACtC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;IACX,iBAAiB,EAAE,CAAC,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF,CAyBA","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport type { ChatResponseBlockKit } from \"../adapter.js\";\nimport { createAttachTool } from \"../adapters/slack/tools/attach.js\";\nimport { createSlackBlockKitTool } from \"../adapters/slack/tools/block-kit.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { Executor, SandboxConfig } from \"../sandbox/index.js\";\nimport { createBashTool } from \"./bash.js\";\nimport { createEditTool } from \"./edit.js\";\nimport { createEventTool, HostEventStore } from \"./event.js\";\nimport { createReadTool } from \"./read.js\";\nimport { createSandboxTool } from \"./sandbox.js\";\nimport { createWriteTool } from \"./write.js\";\n\nexport function createMikanTools(\n executor: Executor,\n workspaceDir: string,\n sandboxController?: {\n sandbox: SandboxConfig;\n provisioner?: Pick<DockerContainerManager, \"getLimitStatus\" | \"setLimits\">;\n },\n): {\n tools: AgentTool<TSchema>[];\n setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;\n setBlockKitResponseFunction: (fn: (response: ChatResponseBlockKit) => Promise<void>) => void;\n setEventContext: (context: {\n platform: string;\n conversationId: string;\n conversationKind: \"direct\" | \"shared\";\n userId: string;\n }) => void;\n setSandboxContext: (context: { conversationId: string; userId: string }) => void;\n} {\n const { tool: attachTool, setUploadFunction } = createAttachTool();\n const { tool: slackBlockKitTool, setBlockKitResponseFunction } = createSlackBlockKitTool();\n const { tool: eventTool, setEventContext } = createEventTool(\n HostEventStore.fromWorkspaceDir(workspaceDir),\n );\n const { tool: sandboxTool, setSandboxContext } = createSandboxTool(\n sandboxController ?? { sandbox: executor.getSandboxConfig() },\n );\n return {\n tools: [\n createReadTool(executor),\n createBashTool(executor),\n createEditTool(executor),\n createWriteTool(executor),\n eventTool,\n sandboxTool,\n attachTool,\n slackBlockKitTool,\n ],\n setUploadFunction,\n setBlockKitResponseFunction,\n setEventContext,\n setSandboxContext,\n };\n}\n"]}
|
package/dist/tools/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createAttachTool } from "../adapters/slack/tools/attach.js";
|
|
2
|
+
import { createSlackBlockKitTool } from "../adapters/slack/tools/block-kit.js";
|
|
2
3
|
import { createBashTool } from "./bash.js";
|
|
3
4
|
import { createEditTool } from "./edit.js";
|
|
4
5
|
import { createEventTool, HostEventStore } from "./event.js";
|
|
@@ -7,6 +8,7 @@ import { createSandboxTool } from "./sandbox.js";
|
|
|
7
8
|
import { createWriteTool } from "./write.js";
|
|
8
9
|
export function createMikanTools(executor, workspaceDir, sandboxController) {
|
|
9
10
|
const { tool: attachTool, setUploadFunction } = createAttachTool();
|
|
11
|
+
const { tool: slackBlockKitTool, setBlockKitResponseFunction } = createSlackBlockKitTool();
|
|
10
12
|
const { tool: eventTool, setEventContext } = createEventTool(HostEventStore.fromWorkspaceDir(workspaceDir));
|
|
11
13
|
const { tool: sandboxTool, setSandboxContext } = createSandboxTool(sandboxController ?? { sandbox: executor.getSandboxConfig() });
|
|
12
14
|
return {
|
|
@@ -18,8 +20,10 @@ export function createMikanTools(executor, workspaceDir, sandboxController) {
|
|
|
18
20
|
eventTool,
|
|
19
21
|
sandboxTool,
|
|
20
22
|
attachTool,
|
|
23
|
+
slackBlockKitTool,
|
|
21
24
|
],
|
|
22
25
|
setUploadFunction,
|
|
26
|
+
setBlockKitResponseFunction,
|
|
23
27
|
setEventContext,
|
|
24
28
|
setSandboxContext,
|
|
25
29
|
};
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,YAAoB,EACpB,iBAGC;IAaD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,GAAG,uBAAuB,EAAE,CAAC;IAC3F,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,eAAe,CAC1D,cAAc,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAC9C,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,CAChE,iBAAiB,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAC9D,CAAC;IACF,OAAO;QACL,KAAK,EAAE;YACL,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,CAAC;YACxB,eAAe,CAAC,QAAQ,CAAC;YACzB,SAAS;YACT,WAAW;YACX,UAAU;YACV,iBAAiB;SAClB;QACD,iBAAiB;QACjB,2BAA2B;QAC3B,eAAe;QACf,iBAAiB;KAClB,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport type { ChatResponseBlockKit } from \"../adapter.js\";\nimport { createAttachTool } from \"../adapters/slack/tools/attach.js\";\nimport { createSlackBlockKitTool } from \"../adapters/slack/tools/block-kit.js\";\nimport type { DockerContainerManager } from \"../provisioner.js\";\nimport type { Executor, SandboxConfig } from \"../sandbox/index.js\";\nimport { createBashTool } from \"./bash.js\";\nimport { createEditTool } from \"./edit.js\";\nimport { createEventTool, HostEventStore } from \"./event.js\";\nimport { createReadTool } from \"./read.js\";\nimport { createSandboxTool } from \"./sandbox.js\";\nimport { createWriteTool } from \"./write.js\";\n\nexport function createMikanTools(\n executor: Executor,\n workspaceDir: string,\n sandboxController?: {\n sandbox: SandboxConfig;\n provisioner?: Pick<DockerContainerManager, \"getLimitStatus\" | \"setLimits\">;\n },\n): {\n tools: AgentTool<TSchema>[];\n setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;\n setBlockKitResponseFunction: (fn: (response: ChatResponseBlockKit) => Promise<void>) => void;\n setEventContext: (context: {\n platform: string;\n conversationId: string;\n conversationKind: \"direct\" | \"shared\";\n userId: string;\n }) => void;\n setSandboxContext: (context: { conversationId: string; userId: string }) => void;\n} {\n const { tool: attachTool, setUploadFunction } = createAttachTool();\n const { tool: slackBlockKitTool, setBlockKitResponseFunction } = createSlackBlockKitTool();\n const { tool: eventTool, setEventContext } = createEventTool(\n HostEventStore.fromWorkspaceDir(workspaceDir),\n );\n const { tool: sandboxTool, setSandboxContext } = createSandboxTool(\n sandboxController ?? { sandbox: executor.getSandboxConfig() },\n );\n return {\n tools: [\n createReadTool(executor),\n createBashTool(executor),\n createEditTool(executor),\n createWriteTool(executor),\n eventTool,\n sandboxTool,\n attachTool,\n slackBlockKitTool,\n ],\n setUploadFunction,\n setBlockKitResponseFunction,\n setEventContext,\n setSandboxContext,\n };\n}\n"]}
|
package/dist/vault-routing.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { SandboxConfig } from "./sandbox/index.js";
|
|
2
2
|
export declare function resolveActorVaultKey(baseConfig: SandboxConfig, userId: string, conversationId: string): string;
|
|
3
|
-
export declare function containerSharedVaultId(containerName: string): string;
|
|
4
3
|
//# sourceMappingURL=vault-routing.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-routing.d.ts","sourceRoot":"","sources":["../src/vault-routing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,aAAa,EACzB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,MAAM,CAcR
|
|
1
|
+
{"version":3,"file":"vault-routing.d.ts","sourceRoot":"","sources":["../src/vault-routing.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,aAAa,EACzB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,MAAM,CAcR","sourcesContent":["import { DockerContainerManager } from \"./provisioner.js\";\nimport type { SandboxConfig } from \"./sandbox/index.js\";\nexport function resolveActorVaultKey(\n baseConfig: SandboxConfig,\n userId: string,\n conversationId: string,\n): string {\n if (baseConfig.type === \"container\") {\n return `container-${baseConfig.container}`;\n }\n\n if (\n baseConfig.type === \"image\" ||\n baseConfig.type === \"cloudflare\" ||\n baseConfig.type === \"firecracker\"\n ) {\n return DockerContainerManager.sanitizeSegment(conversationId);\n }\n\n return userId;\n}\n"]}
|
package/dist/vault-routing.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DockerContainerManager } from "./provisioner.js";
|
|
2
2
|
export function resolveActorVaultKey(baseConfig, userId, conversationId) {
|
|
3
3
|
if (baseConfig.type === "container") {
|
|
4
|
-
return
|
|
4
|
+
return `container-${baseConfig.container}`;
|
|
5
5
|
}
|
|
6
6
|
if (baseConfig.type === "image" ||
|
|
7
7
|
baseConfig.type === "cloudflare" ||
|
|
@@ -10,7 +10,4 @@ export function resolveActorVaultKey(baseConfig, userId, conversationId) {
|
|
|
10
10
|
}
|
|
11
11
|
return userId;
|
|
12
12
|
}
|
|
13
|
-
export function containerSharedVaultId(containerName) {
|
|
14
|
-
return `container-${containerName}`;
|
|
15
|
-
}
|
|
16
13
|
//# sourceMappingURL=vault-routing.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault-routing.js","sourceRoot":"","sources":["../src/vault-routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,UAAU,oBAAoB,CAClC,UAAyB,EACzB,MAAc,EACd,cAAsB;IAEtB,IAAI,UAAU,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,
|
|
1
|
+
{"version":3,"file":"vault-routing.js","sourceRoot":"","sources":["../src/vault-routing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,MAAM,UAAU,oBAAoB,CAClC,UAAyB,EACzB,MAAc,EACd,cAAsB;IAEtB,IAAI,UAAU,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,aAAa,UAAU,CAAC,SAAS,EAAE,CAAC;IAC7C,CAAC;IAED,IACE,UAAU,CAAC,IAAI,KAAK,OAAO;QAC3B,UAAU,CAAC,IAAI,KAAK,YAAY;QAChC,UAAU,CAAC,IAAI,KAAK,aAAa,EACjC,CAAC;QACD,OAAO,sBAAsB,CAAC,eAAe,CAAC,cAAc,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import { DockerContainerManager } from \"./provisioner.js\";\nimport type { SandboxConfig } from \"./sandbox/index.js\";\nexport function resolveActorVaultKey(\n baseConfig: SandboxConfig,\n userId: string,\n conversationId: string,\n): string {\n if (baseConfig.type === \"container\") {\n return `container-${baseConfig.container}`;\n }\n\n if (\n baseConfig.type === \"image\" ||\n baseConfig.type === \"cloudflare\" ||\n baseConfig.type === \"firecracker\"\n ) {\n return DockerContainerManager.sanitizeSegment(conversationId);\n }\n\n return userId;\n}\n"]}
|
package/dist/vault.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { SandboxConfig } from "./sandbox/index.js";
|
|
2
2
|
export declare function normalizeSharedVaultName(name: string): string | undefined;
|
|
3
3
|
export declare function sharedVaultKey(name: string): string | undefined;
|
|
4
|
-
|
|
4
|
+
interface ResolvedVaultMount {
|
|
5
5
|
source: string;
|
|
6
6
|
target: string;
|
|
7
7
|
}
|
|
@@ -69,4 +69,5 @@ export declare class FileVaultManager implements VaultManager {
|
|
|
69
69
|
private buildResolved;
|
|
70
70
|
}
|
|
71
71
|
export declare function defaultVaultTargetPath(relativePath: string): string;
|
|
72
|
+
export {};
|
|
72
73
|
//# sourceMappingURL=vault.d.ts.map
|
package/dist/vault.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, rmSync } from \"fs\";\nimport { dirname, isAbsolute, join, normalize, sep } from \"path\";\nimport { readTextFileIfExists } from \"./file-guards.js\";\nimport type { SandboxConfig } from \"./sandbox/index.js\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\nimport { reportUserFacingError } from \"./sentry.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst SHARED_VAULT_DIR = \"shared\";\n\nexport function normalizeSharedVaultName(name: string): string | undefined {\n const trimmed = name.trim();\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(trimmed)) return undefined;\n return trimmed;\n}\n\nexport function sharedVaultKey(name: string): string | undefined {\n const normalized = normalizeSharedVaultName(name);\n return normalized ? `${SHARED_VAULT_DIR}/${normalized}` : undefined;\n}\n\nfunction sanitizeCloudflareSandboxId(value: string): string {\n return (\n value\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"unknown\"\n );\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\nexport interface ResolvedVaultMount {\n source: string;\n target: string;\n}\n\n/** Resolved vault ready for use at runtime */\nexport interface ResolvedVault {\n userId: string;\n displayName: string;\n /** Absolute path to vault directory */\n dir: string;\n /** Absolute mount specs */\n mounts: ResolvedVaultMount[];\n /** Parsed from env file */\n env: Record<string, string>;\n}\n\nexport interface VaultManager {\n /** Return true when a vault directory exists for this exact key. */\n hasEntry(key: string): boolean;\n /** Resolve vault for a user; returns undefined when no directory exists. */\n resolve(userId: string): ResolvedVault | undefined;\n /** Get sandbox config with credential injection for a user */\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig;\n /** List all vaults discovered under vaults/. */\n list(): ResolvedVault[];\n /** Check if the vaults directory exists. */\n isEnabled(): boolean;\n /** Merge environment variables into vaults/<key>/env and persist them to disk. */\n upsertEnv(key: string, env: Record<string, string>): void;\n /** Write a private file into vaults/<key>/ and ensure it is mounted into the sandbox. */\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void;\n /** List named shared login profiles under vaults/shared/. */\n listSharedVaults(): string[];\n /** Delete a shared login profile's directory. Returns true when it existed. */\n deleteSharedVault(name: string): boolean;\n /** Copy a shared login profile's files into another vault directory. */\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number };\n}\n\n// ── parseEnvFile ───────────────────────────────────────────────────────────────\n\n/**\n * Parse a KEY=VALUE env file. Supports:\n * - Lines starting with # are comments\n * - Empty lines are skipped\n * - Values can be quoted with single or double quotes (quotes are stripped)\n * - No variable expansion\n * - The value is everything after the first `=` to end of line (no inline comments)\n */\nexport function parseEnvFile(content: string): Record<string, string> {\n const env: Record<string, string> = {};\n const lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n\n const key = trimmed.slice(0, eqIndex).trim();\n if (!key) continue;\n\n let value = trimmed.slice(eqIndex + 1);\n\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n env[key] = value;\n }\n\n return env;\n}\n\n// ── FileVaultManager ───────────────────────────────────────────────────────────\n\nexport class FileVaultManager implements VaultManager {\n private readonly vaultsDir: string;\n\n constructor(stateDir: string) {\n this.vaultsDir = join(stateDir, \"vaults\");\n }\n\n isEnabled(): boolean {\n return existsSync(this.vaultsDir);\n }\n\n hasEntry(key: string): boolean {\n return existsSync(join(this.vaultsDir, key));\n }\n\n listSharedVaults(): string[] {\n const sharedDir = join(this.vaultsDir, SHARED_VAULT_DIR);\n if (!existsSync(sharedDir)) return [];\n return readdirSync(sharedDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && normalizeSharedVaultName(entry.name) === entry.name)\n .map((entry) => entry.name)\n .toSorted((left, right) => left.localeCompare(right));\n }\n\n deleteSharedVault(name: string): boolean {\n const key = sharedVaultKey(name);\n if (!key) throw new Error(`vault: invalid shared login name: ${name}`);\n const dir = join(this.vaultsDir, key);\n const existed = existsSync(dir);\n rmSync(dir, { recursive: true, force: true });\n return existed;\n }\n\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number } {\n const sourceKey = sharedVaultKey(name);\n if (!sourceKey) throw new Error(`vault: invalid shared login name: ${name}`);\n const sourceDir = join(this.vaultsDir, sourceKey);\n if (!existsSync(sourceDir)) throw new Error(`vault: shared login \"${name}\" does not exist`);\n\n const targetDir = join(this.vaultsDir, targetKey);\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(targetDir);\n return copyVaultDir(sourceDir, targetDir);\n }\n\n resolve(userId: string): ResolvedVault | undefined {\n const dir = join(this.vaultsDir, userId);\n if (!existsSync(dir)) return undefined;\n return this.buildResolved(userId);\n }\n\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig {\n if (baseConfig.type === \"cloudflare\") {\n return {\n type: \"cloudflare\",\n sandboxId: `${baseConfig.sandboxId}-${sanitizeCloudflareSandboxId(userId)}`,\n };\n }\n return baseConfig;\n }\n\n list(): ResolvedVault[] {\n if (!existsSync(this.vaultsDir)) return [];\n const keys = new Set<string>();\n for (const entry of readdirSync(this.vaultsDir, { withFileTypes: true })) {\n if (entry.isDirectory()) keys.add(entry.name);\n }\n return Array.from(keys, (key) => this.buildResolved(key));\n }\n\n upsertEnv(key: string, env: Record<string, string>): void {\n const dir = join(this.vaultsDir, key);\n const envPath = join(dir, \"env\");\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const existingContent = readTextFileIfExists(envPath);\n const existing = existingContent ? parseEnvFile(existingContent) : {};\n const merged = { ...existing, ...env };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(envPath, content);\n }\n\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void {\n const normalizedPath = normalizeVaultRelativePath(relativePath);\n if (!normalizedPath || (targetPath !== undefined && !normalizeVaultTargetPath(targetPath))) {\n throw new Error(`vault: invalid relative secret file path for \"${key}\": ${relativePath}`);\n }\n\n const dir = join(this.vaultsDir, key);\n const filePath = join(dir, normalizedPath);\n\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const parentDir = dirname(filePath);\n if (parentDir !== dir) ensurePrivateDir(parentDir);\n atomicWritePrivateFile(filePath, content);\n }\n\n // ── private ────────────────────────────────────────────────────────────────\n\n private buildResolved(key: string): ResolvedVault {\n const dir = join(this.vaultsDir, key);\n const mounts = inferMountsFromDir(dir);\n\n let env: Record<string, string> = {};\n const envContent = readTextFileIfExists(join(dir, \"env\"));\n if (envContent !== undefined) {\n try {\n env = parseEnvFile(envContent);\n } catch (err) {\n console.error(`vault: failed to parse env file for \"${key}\":`, err);\n reportUserFacingError(err, {\n domain: \"sandbox\",\n surface: \"vault_injection\",\n operation: \"parse_env\",\n severity: \"warning\",\n context: { vaultKey: key, fatal: false },\n });\n }\n }\n\n return {\n userId: key,\n displayName: key,\n dir,\n mounts,\n env,\n };\n }\n}\n\nfunction inferMountsFromDir(dir: string): ResolvedVaultMount[] {\n if (!existsSync(dir)) return [];\n\n const mounts: ResolvedVaultMount[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === \"env\") continue;\n const source = join(dir, entry.name);\n const target = inferredVaultTargetPath(entry.name);\n if (!target) continue;\n mounts.push({ source, target });\n }\n return mounts;\n}\n\nfunction ensurePrivateDir(path: string): void {\n mkdirSync(path, { recursive: true, mode: PRIVATE_DIR_MODE });\n chmodSync(path, PRIVATE_DIR_MODE);\n}\n\nfunction copyVaultDir(\n sourceDir: string,\n targetDir: string,\n): {\n filesCopied: number;\n envKeysCopied: number;\n} {\n let filesCopied = 0;\n let envKeysCopied = 0;\n\n for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {\n const sourcePath = join(sourceDir, entry.name);\n const targetPath = join(targetDir, entry.name);\n\n if (entry.name === \"env\" && entry.isFile()) {\n const sourceEnv = parseEnvFile(readTextFileIfExists(sourcePath) ?? \"\");\n const targetEnv = parseEnvFile(readTextFileIfExists(targetPath) ?? \"\");\n const merged = { ...targetEnv, ...sourceEnv };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(targetPath, content);\n envKeysCopied += Object.keys(sourceEnv).length;\n continue;\n }\n\n if (entry.isDirectory()) {\n ensurePrivateDir(targetPath);\n const nested = copyVaultDir(sourcePath, targetPath);\n filesCopied += nested.filesCopied;\n envKeysCopied += nested.envKeysCopied;\n continue;\n }\n\n if (!entry.isFile()) continue;\n copyFileSync(sourcePath, targetPath);\n chmodSync(targetPath, 0o600);\n filesCopied++;\n }\n\n return { filesCopied, envKeysCopied };\n}\n\nfunction normalizeVaultRelativePath(relativePath: string): string | undefined {\n const trimmed = relativePath.trim();\n if (!trimmed || isAbsolute(trimmed)) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n if (!normalized || normalized === \".\" || normalized === \"..\" || normalized.startsWith(\"../\")) {\n return undefined;\n }\n return normalized;\n}\n\nfunction normalizeVaultTargetPath(targetPath?: string): string | undefined {\n if (targetPath === undefined) return undefined;\n\n const trimmed = targetPath.trim();\n if (!trimmed || !trimmed.startsWith(\"/\")) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n return normalized.startsWith(\"/\") ? normalized : undefined;\n}\n\nexport function defaultVaultTargetPath(relativePath: string): string {\n const normalized = normalizeVaultRelativePath(relativePath) ?? relativePath.replace(/^\\/+/, \"\");\n return `/root/${normalized}`;\n}\n\nfunction inferredVaultTargetPath(relativePath: string): string | undefined {\n const normalized = normalizeVaultRelativePath(relativePath);\n if (!normalized) return undefined;\n\n if (normalized === \"gws.json\") {\n return \"/root/.config/gws/credentials.json\";\n }\n if (normalized === \"gcloud-adc.json\") {\n return \"/root/.config/gcloud/application_default_credentials.json\";\n }\n if (normalized === \".ssh\" || normalized.startsWith(\".ssh/\")) {\n return \"/root/.ssh\";\n }\n if (normalized === \".kube\" || normalized.startsWith(\".kube/\")) {\n return \"/root/.kube\";\n }\n if (normalized === \".config/gh\" || normalized.startsWith(\".config/gh/\")) {\n return \"/root/.config/gh\";\n }\n\n return defaultVaultTargetPath(normalized);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, rmSync } from \"fs\";\nimport { dirname, isAbsolute, join, normalize, sep } from \"path\";\nimport { readTextFileIfExists } from \"./file-guards.js\";\nimport type { SandboxConfig } from \"./sandbox/index.js\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\nimport { reportUserFacingError } from \"./sentry.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst SHARED_VAULT_DIR = \"shared\";\n\nexport function normalizeSharedVaultName(name: string): string | undefined {\n const trimmed = name.trim();\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(trimmed)) return undefined;\n return trimmed;\n}\n\nexport function sharedVaultKey(name: string): string | undefined {\n const normalized = normalizeSharedVaultName(name);\n return normalized ? `${SHARED_VAULT_DIR}/${normalized}` : undefined;\n}\n\nfunction sanitizeCloudflareSandboxId(value: string): string {\n return (\n value\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"unknown\"\n );\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\ninterface ResolvedVaultMount {\n source: string;\n target: string;\n}\n\n/** Resolved vault ready for use at runtime */\nexport interface ResolvedVault {\n userId: string;\n displayName: string;\n /** Absolute path to vault directory */\n dir: string;\n /** Absolute mount specs */\n mounts: ResolvedVaultMount[];\n /** Parsed from env file */\n env: Record<string, string>;\n}\n\nexport interface VaultManager {\n /** Return true when a vault directory exists for this exact key. */\n hasEntry(key: string): boolean;\n /** Resolve vault for a user; returns undefined when no directory exists. */\n resolve(userId: string): ResolvedVault | undefined;\n /** Get sandbox config with credential injection for a user */\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig;\n /** List all vaults discovered under vaults/. */\n list(): ResolvedVault[];\n /** Check if the vaults directory exists. */\n isEnabled(): boolean;\n /** Merge environment variables into vaults/<key>/env and persist them to disk. */\n upsertEnv(key: string, env: Record<string, string>): void;\n /** Write a private file into vaults/<key>/ and ensure it is mounted into the sandbox. */\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void;\n /** List named shared login profiles under vaults/shared/. */\n listSharedVaults(): string[];\n /** Delete a shared login profile's directory. Returns true when it existed. */\n deleteSharedVault(name: string): boolean;\n /** Copy a shared login profile's files into another vault directory. */\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number };\n}\n\n// ── parseEnvFile ───────────────────────────────────────────────────────────────\n\n/**\n * Parse a KEY=VALUE env file. Supports:\n * - Lines starting with # are comments\n * - Empty lines are skipped\n * - Values can be quoted with single or double quotes (quotes are stripped)\n * - No variable expansion\n * - The value is everything after the first `=` to end of line (no inline comments)\n */\nexport function parseEnvFile(content: string): Record<string, string> {\n const env: Record<string, string> = {};\n const lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n\n const key = trimmed.slice(0, eqIndex).trim();\n if (!key) continue;\n\n let value = trimmed.slice(eqIndex + 1);\n\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n env[key] = value;\n }\n\n return env;\n}\n\n// ── FileVaultManager ───────────────────────────────────────────────────────────\n\nexport class FileVaultManager implements VaultManager {\n private readonly vaultsDir: string;\n\n constructor(stateDir: string) {\n this.vaultsDir = join(stateDir, \"vaults\");\n }\n\n isEnabled(): boolean {\n return existsSync(this.vaultsDir);\n }\n\n hasEntry(key: string): boolean {\n return existsSync(join(this.vaultsDir, key));\n }\n\n listSharedVaults(): string[] {\n const sharedDir = join(this.vaultsDir, SHARED_VAULT_DIR);\n if (!existsSync(sharedDir)) return [];\n return readdirSync(sharedDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && normalizeSharedVaultName(entry.name) === entry.name)\n .map((entry) => entry.name)\n .toSorted((left, right) => left.localeCompare(right));\n }\n\n deleteSharedVault(name: string): boolean {\n const key = sharedVaultKey(name);\n if (!key) throw new Error(`vault: invalid shared login name: ${name}`);\n const dir = join(this.vaultsDir, key);\n const existed = existsSync(dir);\n rmSync(dir, { recursive: true, force: true });\n return existed;\n }\n\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number } {\n const sourceKey = sharedVaultKey(name);\n if (!sourceKey) throw new Error(`vault: invalid shared login name: ${name}`);\n const sourceDir = join(this.vaultsDir, sourceKey);\n if (!existsSync(sourceDir)) throw new Error(`vault: shared login \"${name}\" does not exist`);\n\n const targetDir = join(this.vaultsDir, targetKey);\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(targetDir);\n return copyVaultDir(sourceDir, targetDir);\n }\n\n resolve(userId: string): ResolvedVault | undefined {\n const dir = join(this.vaultsDir, userId);\n if (!existsSync(dir)) return undefined;\n return this.buildResolved(userId);\n }\n\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig {\n if (baseConfig.type === \"cloudflare\") {\n return {\n type: \"cloudflare\",\n sandboxId: `${baseConfig.sandboxId}-${sanitizeCloudflareSandboxId(userId)}`,\n };\n }\n return baseConfig;\n }\n\n list(): ResolvedVault[] {\n if (!existsSync(this.vaultsDir)) return [];\n const keys = new Set<string>();\n for (const entry of readdirSync(this.vaultsDir, { withFileTypes: true })) {\n if (entry.isDirectory()) keys.add(entry.name);\n }\n return Array.from(keys, (key) => this.buildResolved(key));\n }\n\n upsertEnv(key: string, env: Record<string, string>): void {\n const dir = join(this.vaultsDir, key);\n const envPath = join(dir, \"env\");\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const existingContent = readTextFileIfExists(envPath);\n const existing = existingContent ? parseEnvFile(existingContent) : {};\n const merged = { ...existing, ...env };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(envPath, content);\n }\n\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void {\n const normalizedPath = normalizeVaultRelativePath(relativePath);\n if (!normalizedPath || (targetPath !== undefined && !normalizeVaultTargetPath(targetPath))) {\n throw new Error(`vault: invalid relative secret file path for \"${key}\": ${relativePath}`);\n }\n\n const dir = join(this.vaultsDir, key);\n const filePath = join(dir, normalizedPath);\n\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const parentDir = dirname(filePath);\n if (parentDir !== dir) ensurePrivateDir(parentDir);\n atomicWritePrivateFile(filePath, content);\n }\n\n // ── private ────────────────────────────────────────────────────────────────\n\n private buildResolved(key: string): ResolvedVault {\n const dir = join(this.vaultsDir, key);\n const mounts = inferMountsFromDir(dir);\n\n let env: Record<string, string> = {};\n const envContent = readTextFileIfExists(join(dir, \"env\"));\n if (envContent !== undefined) {\n try {\n env = parseEnvFile(envContent);\n } catch (err) {\n console.error(`vault: failed to parse env file for \"${key}\":`, err);\n reportUserFacingError(err, {\n domain: \"sandbox\",\n surface: \"vault_injection\",\n operation: \"parse_env\",\n severity: \"warning\",\n context: { vaultKey: key, fatal: false },\n });\n }\n }\n\n return {\n userId: key,\n displayName: key,\n dir,\n mounts,\n env,\n };\n }\n}\n\nfunction inferMountsFromDir(dir: string): ResolvedVaultMount[] {\n if (!existsSync(dir)) return [];\n\n const mounts: ResolvedVaultMount[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === \"env\") continue;\n const source = join(dir, entry.name);\n const target = inferredVaultTargetPath(entry.name);\n if (!target) continue;\n mounts.push({ source, target });\n }\n return mounts;\n}\n\nfunction ensurePrivateDir(path: string): void {\n mkdirSync(path, { recursive: true, mode: PRIVATE_DIR_MODE });\n chmodSync(path, PRIVATE_DIR_MODE);\n}\n\nfunction copyVaultDir(\n sourceDir: string,\n targetDir: string,\n): {\n filesCopied: number;\n envKeysCopied: number;\n} {\n let filesCopied = 0;\n let envKeysCopied = 0;\n\n for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {\n const sourcePath = join(sourceDir, entry.name);\n const targetPath = join(targetDir, entry.name);\n\n if (entry.name === \"env\" && entry.isFile()) {\n const sourceEnv = parseEnvFile(readTextFileIfExists(sourcePath) ?? \"\");\n const targetEnv = parseEnvFile(readTextFileIfExists(targetPath) ?? \"\");\n const merged = { ...targetEnv, ...sourceEnv };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(targetPath, content);\n envKeysCopied += Object.keys(sourceEnv).length;\n continue;\n }\n\n if (entry.isDirectory()) {\n ensurePrivateDir(targetPath);\n const nested = copyVaultDir(sourcePath, targetPath);\n filesCopied += nested.filesCopied;\n envKeysCopied += nested.envKeysCopied;\n continue;\n }\n\n if (!entry.isFile()) continue;\n copyFileSync(sourcePath, targetPath);\n chmodSync(targetPath, 0o600);\n filesCopied++;\n }\n\n return { filesCopied, envKeysCopied };\n}\n\nfunction normalizeVaultRelativePath(relativePath: string): string | undefined {\n const trimmed = relativePath.trim();\n if (!trimmed || isAbsolute(trimmed)) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n if (!normalized || normalized === \".\" || normalized === \"..\" || normalized.startsWith(\"../\")) {\n return undefined;\n }\n return normalized;\n}\n\nfunction normalizeVaultTargetPath(targetPath?: string): string | undefined {\n if (targetPath === undefined) return undefined;\n\n const trimmed = targetPath.trim();\n if (!trimmed || !trimmed.startsWith(\"/\")) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n return normalized.startsWith(\"/\") ? normalized : undefined;\n}\n\nexport function defaultVaultTargetPath(relativePath: string): string {\n const normalized = normalizeVaultRelativePath(relativePath) ?? relativePath.replace(/^\\/+/, \"\");\n return `/root/${normalized}`;\n}\n\nfunction inferredVaultTargetPath(relativePath: string): string | undefined {\n const normalized = normalizeVaultRelativePath(relativePath);\n if (!normalized) return undefined;\n\n if (normalized === \"gws.json\") {\n return \"/root/.config/gws/credentials.json\";\n }\n if (normalized === \"gcloud-adc.json\") {\n return \"/root/.config/gcloud/application_default_credentials.json\";\n }\n if (normalized === \".ssh\" || normalized.startsWith(\".ssh/\")) {\n return \"/root/.ssh\";\n }\n if (normalized === \".kube\" || normalized.startsWith(\".kube/\")) {\n return \"/root/.kube\";\n }\n if (normalized === \".config/gh\" || normalized.startsWith(\".config/gh/\")) {\n return \"/root/.config/gh\";\n }\n\n return defaultVaultTargetPath(normalized);\n}\n"]}
|
package/dist/vault.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vault.js","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,IAAI,CAAC;AACzF,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAC/B,MAAM,gBAAgB,GAAG,QAAQ,CAAC;AAElC,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,CAAC,mCAAmC,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IACzE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,UAAU,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,UAAU,CAAC,CAAC,CAAC,GAAG,gBAAgB,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AACtE,CAAC;AAED,SAAS,2BAA2B,CAAC,KAAa;IAChD,OAAO,CACL,KAAK;SACF,WAAW,EAAE;SACb,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,SAAS,CACxC,CAAC;AACJ,CAAC;AA+CD,kFAAkF;AAElF;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE9E,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAElD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,OAAO,KAAK,CAAC,CAAC;YAAE,SAAS;QAE7B,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,IAAI,CAAC,GAAG;YAAE,SAAS;QAEnB,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;QAEvC,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;QAED,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kFAAkF;AAElF,MAAM,OAAO,gBAAgB;IAG3B,YAAY,QAAgB;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAED,SAAS;QACP,OAAO,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpC,CAAC;IAED,QAAQ,CAAC,GAAW;QAClB,OAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/C,CAAC;IAED,gBAAgB;QACd,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,CAAC;QACtC,OAAO,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACnD,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,wBAAwB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC;aAC7F,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAC1B,QAAQ,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,iBAAiB,CAAC,IAAY;QAC5B,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;QACvE,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,iBAAiB,CACf,IAAY,EACZ,SAAiB;QAEjB,MAAM,SAAS,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,IAAI,EAAE,CAAC,CAAC;QAC7E,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAClD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,kBAAkB,CAAC,CAAC;QAE5F,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAClD,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,gBAAgB,CAAC,SAAS,CAAC,CAAC;QAC5B,OAAO,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,CAAC,MAAc;QACpB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QACvC,OAAO,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,gBAAgB,CAAC,MAAc,EAAE,UAAyB;QACxD,IAAI,UAAU,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;YACrC,OAAO;gBACL,IAAI,EAAE,YAAY;gBAClB,SAAS,EAAE,GAAG,UAAU,CAAC,SAAS,IAAI,2BAA2B,CAAC,MAAM,CAAC,EAAE;aAC5E,CAAC;QACJ,CAAC;QACD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,IAAI;QACF,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,OAAO,EAAE,CAAC;QAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACzE,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,SAAS,CAAC,GAAW,EAAE,GAA2B;QAChD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACjC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,eAAe,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACtD,MAAM,QAAQ,GAAG,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,MAAM,MAAM,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,GAAG,EAAE,CAAC;QACvC,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;aACnB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;aACxD,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC;aAC9C,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QACvB,sBAAsB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED,UAAU,CAAC,GAAW,EAAE,YAAoB,EAAE,OAAe,EAAE,UAAmB;QAChF,MAAM,cAAc,GAAG,0BAA0B,CAAC,YAAY,CAAC,CAAC;QAChE,IAAI,CAAC,cAAc,IAAI,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,wBAAwB,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YAC3F,MAAM,IAAI,KAAK,CAAC,iDAAiD,GAAG,MAAM,YAAY,EAAE,CAAC,CAAC;QAC5F,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QAE3C,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjC,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtB,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpC,IAAI,SAAS,KAAK,GAAG;YAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;QACnD,sBAAsB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;IAED,8EAA8E;IAEtE,aAAa,CAAC,GAAW;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QACtC,MAAM,MAAM,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;QAEvC,IAAI,GAAG,GAA2B,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,oBAAoB,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;QAC1D,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;YACjC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,CAAC,KAAK,CAAC,wCAAwC,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;gBACpE,qBAAqB,CAAC,GAAG,EAAE;oBACzB,MAAM,EAAE,SAAS;oBACjB,OAAO,EAAE,iBAAiB;oBAC1B,SAAS,EAAE,WAAW;oBACtB,QAAQ,EAAE,SAAS;oBACnB,OAAO,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE;iBACzC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO;YACL,MAAM,EAAE,GAAG;YACX,WAAW,EAAE,GAAG;YAChB,GAAG;YACH,MAAM;YACN,GAAG;SACJ,CAAC;IACJ,CAAC;CACF;AAED,SAAS,kBAAkB,CAAC,GAAW;IACrC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAEhC,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9D,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK;YAAE,SAAS;QACnC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACpC,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;IAC7D,SAAS,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AACpC,CAAC;AAED,SAAS,YAAY,CACnB,SAAiB,EACjB,SAAiB;IAKjB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACpE,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC3C,MAAM,SAAS,GAAG,YAAY,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;YACvE,MAAM,SAAS,GAAG,YAAY,CAAC,oBAAoB,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;YACvE,MAAM,MAAM,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,SAAS,EAAE,CAAC;YAC9C,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;iBACnB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;iBACxD,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC;iBAC9C,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;YACvB,sBAAsB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAC5C,aAAa,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC;YAC/C,SAAS;QACX,CAAC;QAED,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,gBAAgB,CAAC,UAAU,CAAC,CAAC;YAC7B,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;YACpD,WAAW,IAAI,MAAM,CAAC,WAAW,CAAC;YAClC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC;YACtC,SAAS;QACX,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAAE,SAAS;QAC9B,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QACrC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC7B,WAAW,EAAE,CAAC;IAChB,CAAC;IAED,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,0BAA0B,CAAC,YAAoB;IACtD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IAEtD,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3D,IAAI,CAAC,UAAU,IAAI,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7F,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,wBAAwB,CAAC,UAAmB;IACnD,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAE/C,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IAClC,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAE3D,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3D,OAAO,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,YAAoB;IACzD,MAAM,UAAU,GAAG,0BAA0B,CAAC,YAAY,CAAC,IAAI,YAAY,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChG,OAAO,SAAS,UAAU,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,uBAAuB,CAAC,YAAoB;IACnD,MAAM,UAAU,GAAG,0BAA0B,CAAC,YAAY,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAElC,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;QAC9B,OAAO,oCAAoC,CAAC;IAC9C,CAAC;IACD,IAAI,UAAU,KAAK,iBAAiB,EAAE,CAAC;QACrC,OAAO,2DAA2D,CAAC;IACrE,CAAC;IACD,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5D,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IAAI,UAAU,KAAK,OAAO,IAAI,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,IAAI,UAAU,KAAK,YAAY,IAAI,UAAU,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACxE,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,OAAO,sBAAsB,CAAC,UAAU,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, rmSync } from \"fs\";\nimport { dirname, isAbsolute, join, normalize, sep } from \"path\";\nimport { readTextFileIfExists } from \"./file-guards.js\";\nimport type { SandboxConfig } from \"./sandbox/index.js\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\nimport { reportUserFacingError } from \"./sentry.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst SHARED_VAULT_DIR = \"shared\";\n\nexport function normalizeSharedVaultName(name: string): string | undefined {\n const trimmed = name.trim();\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(trimmed)) return undefined;\n return trimmed;\n}\n\nexport function sharedVaultKey(name: string): string | undefined {\n const normalized = normalizeSharedVaultName(name);\n return normalized ? `${SHARED_VAULT_DIR}/${normalized}` : undefined;\n}\n\nfunction sanitizeCloudflareSandboxId(value: string): string {\n return (\n value\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"unknown\"\n );\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\nexport interface ResolvedVaultMount {\n source: string;\n target: string;\n}\n\n/** Resolved vault ready for use at runtime */\nexport interface ResolvedVault {\n userId: string;\n displayName: string;\n /** Absolute path to vault directory */\n dir: string;\n /** Absolute mount specs */\n mounts: ResolvedVaultMount[];\n /** Parsed from env file */\n env: Record<string, string>;\n}\n\nexport interface VaultManager {\n /** Return true when a vault directory exists for this exact key. */\n hasEntry(key: string): boolean;\n /** Resolve vault for a user; returns undefined when no directory exists. */\n resolve(userId: string): ResolvedVault | undefined;\n /** Get sandbox config with credential injection for a user */\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig;\n /** List all vaults discovered under vaults/. */\n list(): ResolvedVault[];\n /** Check if the vaults directory exists. */\n isEnabled(): boolean;\n /** Merge environment variables into vaults/<key>/env and persist them to disk. */\n upsertEnv(key: string, env: Record<string, string>): void;\n /** Write a private file into vaults/<key>/ and ensure it is mounted into the sandbox. */\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void;\n /** List named shared login profiles under vaults/shared/. */\n listSharedVaults(): string[];\n /** Delete a shared login profile's directory. Returns true when it existed. */\n deleteSharedVault(name: string): boolean;\n /** Copy a shared login profile's files into another vault directory. */\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number };\n}\n\n// ── parseEnvFile ───────────────────────────────────────────────────────────────\n\n/**\n * Parse a KEY=VALUE env file. Supports:\n * - Lines starting with # are comments\n * - Empty lines are skipped\n * - Values can be quoted with single or double quotes (quotes are stripped)\n * - No variable expansion\n * - The value is everything after the first `=` to end of line (no inline comments)\n */\nexport function parseEnvFile(content: string): Record<string, string> {\n const env: Record<string, string> = {};\n const lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n\n const key = trimmed.slice(0, eqIndex).trim();\n if (!key) continue;\n\n let value = trimmed.slice(eqIndex + 1);\n\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n env[key] = value;\n }\n\n return env;\n}\n\n// ── FileVaultManager ───────────────────────────────────────────────────────────\n\nexport class FileVaultManager implements VaultManager {\n private readonly vaultsDir: string;\n\n constructor(stateDir: string) {\n this.vaultsDir = join(stateDir, \"vaults\");\n }\n\n isEnabled(): boolean {\n return existsSync(this.vaultsDir);\n }\n\n hasEntry(key: string): boolean {\n return existsSync(join(this.vaultsDir, key));\n }\n\n listSharedVaults(): string[] {\n const sharedDir = join(this.vaultsDir, SHARED_VAULT_DIR);\n if (!existsSync(sharedDir)) return [];\n return readdirSync(sharedDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && normalizeSharedVaultName(entry.name) === entry.name)\n .map((entry) => entry.name)\n .toSorted((left, right) => left.localeCompare(right));\n }\n\n deleteSharedVault(name: string): boolean {\n const key = sharedVaultKey(name);\n if (!key) throw new Error(`vault: invalid shared login name: ${name}`);\n const dir = join(this.vaultsDir, key);\n const existed = existsSync(dir);\n rmSync(dir, { recursive: true, force: true });\n return existed;\n }\n\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number } {\n const sourceKey = sharedVaultKey(name);\n if (!sourceKey) throw new Error(`vault: invalid shared login name: ${name}`);\n const sourceDir = join(this.vaultsDir, sourceKey);\n if (!existsSync(sourceDir)) throw new Error(`vault: shared login \"${name}\" does not exist`);\n\n const targetDir = join(this.vaultsDir, targetKey);\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(targetDir);\n return copyVaultDir(sourceDir, targetDir);\n }\n\n resolve(userId: string): ResolvedVault | undefined {\n const dir = join(this.vaultsDir, userId);\n if (!existsSync(dir)) return undefined;\n return this.buildResolved(userId);\n }\n\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig {\n if (baseConfig.type === \"cloudflare\") {\n return {\n type: \"cloudflare\",\n sandboxId: `${baseConfig.sandboxId}-${sanitizeCloudflareSandboxId(userId)}`,\n };\n }\n return baseConfig;\n }\n\n list(): ResolvedVault[] {\n if (!existsSync(this.vaultsDir)) return [];\n const keys = new Set<string>();\n for (const entry of readdirSync(this.vaultsDir, { withFileTypes: true })) {\n if (entry.isDirectory()) keys.add(entry.name);\n }\n return Array.from(keys, (key) => this.buildResolved(key));\n }\n\n upsertEnv(key: string, env: Record<string, string>): void {\n const dir = join(this.vaultsDir, key);\n const envPath = join(dir, \"env\");\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const existingContent = readTextFileIfExists(envPath);\n const existing = existingContent ? parseEnvFile(existingContent) : {};\n const merged = { ...existing, ...env };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(envPath, content);\n }\n\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void {\n const normalizedPath = normalizeVaultRelativePath(relativePath);\n if (!normalizedPath || (targetPath !== undefined && !normalizeVaultTargetPath(targetPath))) {\n throw new Error(`vault: invalid relative secret file path for \"${key}\": ${relativePath}`);\n }\n\n const dir = join(this.vaultsDir, key);\n const filePath = join(dir, normalizedPath);\n\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const parentDir = dirname(filePath);\n if (parentDir !== dir) ensurePrivateDir(parentDir);\n atomicWritePrivateFile(filePath, content);\n }\n\n // ── private ────────────────────────────────────────────────────────────────\n\n private buildResolved(key: string): ResolvedVault {\n const dir = join(this.vaultsDir, key);\n const mounts = inferMountsFromDir(dir);\n\n let env: Record<string, string> = {};\n const envContent = readTextFileIfExists(join(dir, \"env\"));\n if (envContent !== undefined) {\n try {\n env = parseEnvFile(envContent);\n } catch (err) {\n console.error(`vault: failed to parse env file for \"${key}\":`, err);\n reportUserFacingError(err, {\n domain: \"sandbox\",\n surface: \"vault_injection\",\n operation: \"parse_env\",\n severity: \"warning\",\n context: { vaultKey: key, fatal: false },\n });\n }\n }\n\n return {\n userId: key,\n displayName: key,\n dir,\n mounts,\n env,\n };\n }\n}\n\nfunction inferMountsFromDir(dir: string): ResolvedVaultMount[] {\n if (!existsSync(dir)) return [];\n\n const mounts: ResolvedVaultMount[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === \"env\") continue;\n const source = join(dir, entry.name);\n const target = inferredVaultTargetPath(entry.name);\n if (!target) continue;\n mounts.push({ source, target });\n }\n return mounts;\n}\n\nfunction ensurePrivateDir(path: string): void {\n mkdirSync(path, { recursive: true, mode: PRIVATE_DIR_MODE });\n chmodSync(path, PRIVATE_DIR_MODE);\n}\n\nfunction copyVaultDir(\n sourceDir: string,\n targetDir: string,\n): {\n filesCopied: number;\n envKeysCopied: number;\n} {\n let filesCopied = 0;\n let envKeysCopied = 0;\n\n for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {\n const sourcePath = join(sourceDir, entry.name);\n const targetPath = join(targetDir, entry.name);\n\n if (entry.name === \"env\" && entry.isFile()) {\n const sourceEnv = parseEnvFile(readTextFileIfExists(sourcePath) ?? \"\");\n const targetEnv = parseEnvFile(readTextFileIfExists(targetPath) ?? \"\");\n const merged = { ...targetEnv, ...sourceEnv };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(targetPath, content);\n envKeysCopied += Object.keys(sourceEnv).length;\n continue;\n }\n\n if (entry.isDirectory()) {\n ensurePrivateDir(targetPath);\n const nested = copyVaultDir(sourcePath, targetPath);\n filesCopied += nested.filesCopied;\n envKeysCopied += nested.envKeysCopied;\n continue;\n }\n\n if (!entry.isFile()) continue;\n copyFileSync(sourcePath, targetPath);\n chmodSync(targetPath, 0o600);\n filesCopied++;\n }\n\n return { filesCopied, envKeysCopied };\n}\n\nfunction normalizeVaultRelativePath(relativePath: string): string | undefined {\n const trimmed = relativePath.trim();\n if (!trimmed || isAbsolute(trimmed)) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n if (!normalized || normalized === \".\" || normalized === \"..\" || normalized.startsWith(\"../\")) {\n return undefined;\n }\n return normalized;\n}\n\nfunction normalizeVaultTargetPath(targetPath?: string): string | undefined {\n if (targetPath === undefined) return undefined;\n\n const trimmed = targetPath.trim();\n if (!trimmed || !trimmed.startsWith(\"/\")) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n return normalized.startsWith(\"/\") ? normalized : undefined;\n}\n\nexport function defaultVaultTargetPath(relativePath: string): string {\n const normalized = normalizeVaultRelativePath(relativePath) ?? relativePath.replace(/^\\/+/, \"\");\n return `/root/${normalized}`;\n}\n\nfunction inferredVaultTargetPath(relativePath: string): string | undefined {\n const normalized = normalizeVaultRelativePath(relativePath);\n if (!normalized) return undefined;\n\n if (normalized === \"gws.json\") {\n return \"/root/.config/gws/credentials.json\";\n }\n if (normalized === \"gcloud-adc.json\") {\n return \"/root/.config/gcloud/application_default_credentials.json\";\n }\n if (normalized === \".ssh\" || normalized.startsWith(\".ssh/\")) {\n return \"/root/.ssh\";\n }\n if (normalized === \".kube\" || normalized.startsWith(\".kube/\")) {\n return \"/root/.kube\";\n }\n if (normalized === \".config/gh\" || normalized.startsWith(\".config/gh/\")) {\n return \"/root/.config/gh\";\n }\n\n return defaultVaultTargetPath(normalized);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"vault.js","sourceRoot":"","sources":["../src/vault.ts"],"names":[],"mappings":"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","sourcesContent":["import { chmodSync, copyFileSync, existsSync, mkdirSync, readdirSync, rmSync } from \"fs\";\nimport { dirname, isAbsolute, join, normalize, sep } from \"path\";\nimport { readTextFileIfExists } from \"./file-guards.js\";\nimport type { SandboxConfig } from \"./sandbox/index.js\";\nimport { atomicWritePrivateFile } from \"./fs-atomic.js\";\nimport { reportUserFacingError } from \"./sentry.js\";\n\nconst PRIVATE_DIR_MODE = 0o700;\nconst SHARED_VAULT_DIR = \"shared\";\n\nexport function normalizeSharedVaultName(name: string): string | undefined {\n const trimmed = name.trim();\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(trimmed)) return undefined;\n return trimmed;\n}\n\nexport function sharedVaultKey(name: string): string | undefined {\n const normalized = normalizeSharedVaultName(name);\n return normalized ? `${SHARED_VAULT_DIR}/${normalized}` : undefined;\n}\n\nfunction sanitizeCloudflareSandboxId(value: string): string {\n return (\n value\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\") || \"unknown\"\n );\n}\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\ninterface ResolvedVaultMount {\n source: string;\n target: string;\n}\n\n/** Resolved vault ready for use at runtime */\nexport interface ResolvedVault {\n userId: string;\n displayName: string;\n /** Absolute path to vault directory */\n dir: string;\n /** Absolute mount specs */\n mounts: ResolvedVaultMount[];\n /** Parsed from env file */\n env: Record<string, string>;\n}\n\nexport interface VaultManager {\n /** Return true when a vault directory exists for this exact key. */\n hasEntry(key: string): boolean;\n /** Resolve vault for a user; returns undefined when no directory exists. */\n resolve(userId: string): ResolvedVault | undefined;\n /** Get sandbox config with credential injection for a user */\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig;\n /** List all vaults discovered under vaults/. */\n list(): ResolvedVault[];\n /** Check if the vaults directory exists. */\n isEnabled(): boolean;\n /** Merge environment variables into vaults/<key>/env and persist them to disk. */\n upsertEnv(key: string, env: Record<string, string>): void;\n /** Write a private file into vaults/<key>/ and ensure it is mounted into the sandbox. */\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void;\n /** List named shared login profiles under vaults/shared/. */\n listSharedVaults(): string[];\n /** Delete a shared login profile's directory. Returns true when it existed. */\n deleteSharedVault(name: string): boolean;\n /** Copy a shared login profile's files into another vault directory. */\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number };\n}\n\n// ── parseEnvFile ───────────────────────────────────────────────────────────────\n\n/**\n * Parse a KEY=VALUE env file. Supports:\n * - Lines starting with # are comments\n * - Empty lines are skipped\n * - Values can be quoted with single or double quotes (quotes are stripped)\n * - No variable expansion\n * - The value is everything after the first `=` to end of line (no inline comments)\n */\nexport function parseEnvFile(content: string): Record<string, string> {\n const env: Record<string, string> = {};\n const lines = content.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\").split(\"\\n\");\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\")) continue;\n\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex === -1) continue;\n\n const key = trimmed.slice(0, eqIndex).trim();\n if (!key) continue;\n\n let value = trimmed.slice(eqIndex + 1);\n\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n\n env[key] = value;\n }\n\n return env;\n}\n\n// ── FileVaultManager ───────────────────────────────────────────────────────────\n\nexport class FileVaultManager implements VaultManager {\n private readonly vaultsDir: string;\n\n constructor(stateDir: string) {\n this.vaultsDir = join(stateDir, \"vaults\");\n }\n\n isEnabled(): boolean {\n return existsSync(this.vaultsDir);\n }\n\n hasEntry(key: string): boolean {\n return existsSync(join(this.vaultsDir, key));\n }\n\n listSharedVaults(): string[] {\n const sharedDir = join(this.vaultsDir, SHARED_VAULT_DIR);\n if (!existsSync(sharedDir)) return [];\n return readdirSync(sharedDir, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && normalizeSharedVaultName(entry.name) === entry.name)\n .map((entry) => entry.name)\n .toSorted((left, right) => left.localeCompare(right));\n }\n\n deleteSharedVault(name: string): boolean {\n const key = sharedVaultKey(name);\n if (!key) throw new Error(`vault: invalid shared login name: ${name}`);\n const dir = join(this.vaultsDir, key);\n const existed = existsSync(dir);\n rmSync(dir, { recursive: true, force: true });\n return existed;\n }\n\n copySharedVaultTo(\n name: string,\n targetKey: string,\n ): { filesCopied: number; envKeysCopied: number } {\n const sourceKey = sharedVaultKey(name);\n if (!sourceKey) throw new Error(`vault: invalid shared login name: ${name}`);\n const sourceDir = join(this.vaultsDir, sourceKey);\n if (!existsSync(sourceDir)) throw new Error(`vault: shared login \"${name}\" does not exist`);\n\n const targetDir = join(this.vaultsDir, targetKey);\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(targetDir);\n return copyVaultDir(sourceDir, targetDir);\n }\n\n resolve(userId: string): ResolvedVault | undefined {\n const dir = join(this.vaultsDir, userId);\n if (!existsSync(dir)) return undefined;\n return this.buildResolved(userId);\n }\n\n getSandboxConfig(userId: string, baseConfig: SandboxConfig): SandboxConfig {\n if (baseConfig.type === \"cloudflare\") {\n return {\n type: \"cloudflare\",\n sandboxId: `${baseConfig.sandboxId}-${sanitizeCloudflareSandboxId(userId)}`,\n };\n }\n return baseConfig;\n }\n\n list(): ResolvedVault[] {\n if (!existsSync(this.vaultsDir)) return [];\n const keys = new Set<string>();\n for (const entry of readdirSync(this.vaultsDir, { withFileTypes: true })) {\n if (entry.isDirectory()) keys.add(entry.name);\n }\n return Array.from(keys, (key) => this.buildResolved(key));\n }\n\n upsertEnv(key: string, env: Record<string, string>): void {\n const dir = join(this.vaultsDir, key);\n const envPath = join(dir, \"env\");\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const existingContent = readTextFileIfExists(envPath);\n const existing = existingContent ? parseEnvFile(existingContent) : {};\n const merged = { ...existing, ...env };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(envPath, content);\n }\n\n upsertFile(key: string, relativePath: string, content: string, targetPath?: string): void {\n const normalizedPath = normalizeVaultRelativePath(relativePath);\n if (!normalizedPath || (targetPath !== undefined && !normalizeVaultTargetPath(targetPath))) {\n throw new Error(`vault: invalid relative secret file path for \"${key}\": ${relativePath}`);\n }\n\n const dir = join(this.vaultsDir, key);\n const filePath = join(dir, normalizedPath);\n\n ensurePrivateDir(this.vaultsDir);\n ensurePrivateDir(dir);\n const parentDir = dirname(filePath);\n if (parentDir !== dir) ensurePrivateDir(parentDir);\n atomicWritePrivateFile(filePath, content);\n }\n\n // ── private ────────────────────────────────────────────────────────────────\n\n private buildResolved(key: string): ResolvedVault {\n const dir = join(this.vaultsDir, key);\n const mounts = inferMountsFromDir(dir);\n\n let env: Record<string, string> = {};\n const envContent = readTextFileIfExists(join(dir, \"env\"));\n if (envContent !== undefined) {\n try {\n env = parseEnvFile(envContent);\n } catch (err) {\n console.error(`vault: failed to parse env file for \"${key}\":`, err);\n reportUserFacingError(err, {\n domain: \"sandbox\",\n surface: \"vault_injection\",\n operation: \"parse_env\",\n severity: \"warning\",\n context: { vaultKey: key, fatal: false },\n });\n }\n }\n\n return {\n userId: key,\n displayName: key,\n dir,\n mounts,\n env,\n };\n }\n}\n\nfunction inferMountsFromDir(dir: string): ResolvedVaultMount[] {\n if (!existsSync(dir)) return [];\n\n const mounts: ResolvedVaultMount[] = [];\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === \"env\") continue;\n const source = join(dir, entry.name);\n const target = inferredVaultTargetPath(entry.name);\n if (!target) continue;\n mounts.push({ source, target });\n }\n return mounts;\n}\n\nfunction ensurePrivateDir(path: string): void {\n mkdirSync(path, { recursive: true, mode: PRIVATE_DIR_MODE });\n chmodSync(path, PRIVATE_DIR_MODE);\n}\n\nfunction copyVaultDir(\n sourceDir: string,\n targetDir: string,\n): {\n filesCopied: number;\n envKeysCopied: number;\n} {\n let filesCopied = 0;\n let envKeysCopied = 0;\n\n for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {\n const sourcePath = join(sourceDir, entry.name);\n const targetPath = join(targetDir, entry.name);\n\n if (entry.name === \"env\" && entry.isFile()) {\n const sourceEnv = parseEnvFile(readTextFileIfExists(sourcePath) ?? \"\");\n const targetEnv = parseEnvFile(readTextFileIfExists(targetPath) ?? \"\");\n const merged = { ...targetEnv, ...sourceEnv };\n const content =\n Object.entries(merged)\n .toSorted(([left], [right]) => left.localeCompare(right))\n .map(([envKey, value]) => `${envKey}=${value}`)\n .join(\"\\n\") + \"\\n\";\n atomicWritePrivateFile(targetPath, content);\n envKeysCopied += Object.keys(sourceEnv).length;\n continue;\n }\n\n if (entry.isDirectory()) {\n ensurePrivateDir(targetPath);\n const nested = copyVaultDir(sourcePath, targetPath);\n filesCopied += nested.filesCopied;\n envKeysCopied += nested.envKeysCopied;\n continue;\n }\n\n if (!entry.isFile()) continue;\n copyFileSync(sourcePath, targetPath);\n chmodSync(targetPath, 0o600);\n filesCopied++;\n }\n\n return { filesCopied, envKeysCopied };\n}\n\nfunction normalizeVaultRelativePath(relativePath: string): string | undefined {\n const trimmed = relativePath.trim();\n if (!trimmed || isAbsolute(trimmed)) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n if (!normalized || normalized === \".\" || normalized === \"..\" || normalized.startsWith(\"../\")) {\n return undefined;\n }\n return normalized;\n}\n\nfunction normalizeVaultTargetPath(targetPath?: string): string | undefined {\n if (targetPath === undefined) return undefined;\n\n const trimmed = targetPath.trim();\n if (!trimmed || !trimmed.startsWith(\"/\")) return undefined;\n\n const normalized = normalize(trimmed).split(sep).join(\"/\");\n return normalized.startsWith(\"/\") ? normalized : undefined;\n}\n\nexport function defaultVaultTargetPath(relativePath: string): string {\n const normalized = normalizeVaultRelativePath(relativePath) ?? relativePath.replace(/^\\/+/, \"\");\n return `/root/${normalized}`;\n}\n\nfunction inferredVaultTargetPath(relativePath: string): string | undefined {\n const normalized = normalizeVaultRelativePath(relativePath);\n if (!normalized) return undefined;\n\n if (normalized === \"gws.json\") {\n return \"/root/.config/gws/credentials.json\";\n }\n if (normalized === \"gcloud-adc.json\") {\n return \"/root/.config/gcloud/application_default_credentials.json\";\n }\n if (normalized === \".ssh\" || normalized.startsWith(\".ssh/\")) {\n return \"/root/.ssh\";\n }\n if (normalized === \".kube\" || normalized.startsWith(\".kube/\")) {\n return \"/root/.kube\";\n }\n if (normalized === \".config/gh\" || normalized.startsWith(\".config/gh/\")) {\n return \"/root/.config/gh\";\n }\n\n return defaultVaultTargetPath(normalized);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geminixiang/mikan",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Multi-platform AI coding agent for Slack, Telegram, and Discord",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"lint:fix": "oxlint --fix",
|
|
36
36
|
"fmt": "oxfmt",
|
|
37
37
|
"fmt:check": "oxfmt --check",
|
|
38
|
+
"knip": "knip",
|
|
38
39
|
"prepublishOnly": "npm run clean && npm run build",
|
|
39
40
|
"prepare": "husky",
|
|
40
41
|
"test:e2e": "vitest --run --config vitest.e2e.config.ts",
|
|
@@ -62,6 +63,7 @@
|
|
|
62
63
|
"@typescript/native-preview": "7.0.0-dev.20260328.1",
|
|
63
64
|
"@vitest/coverage-v8": "^4.1.7",
|
|
64
65
|
"husky": "^9.1.7",
|
|
66
|
+
"knip": "^6.15.0",
|
|
65
67
|
"oxfmt": "^0.44.0",
|
|
66
68
|
"oxlint": "^1.59.0",
|
|
67
69
|
"shx": "^0.4.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/login/session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAgC;IAE9C;;;OAGG;IACH,MAAM,CACJ,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,GACjB,SAAS,CAkBX;IAED,qFAAqF;IACrF,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAI5C;IAED,2EAA2E;IAC3E,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAM/C;IAED,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAOZ;CACF","sourcesContent":["import { randomBytes } from \"crypto\";\nimport type { PlatformName } from \"../adapter.js\";\n\nexport interface LinkToken {\n token: string;\n platform: PlatformName;\n platformUserId: string;\n vaultId: string;\n providerId: string;\n /** Conversation to notify when binding completes */\n conversationId: string;\n expiresAt: number;\n}\n\nconst TTL_MS = 15 * 60 * 1000;\n\nexport class InMemoryLinkTokenStore {\n private tokens = new Map<string, LinkToken>();\n\n /**\n * Create a link token for a platform user.\n * Invalidates any existing token for the same user before creating a new one.\n */\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): LinkToken {\n for (const [key, t] of this.tokens) {\n if (t.platform === platform && t.platformUserId === platformUserId) {\n this.tokens.delete(key);\n }\n }\n\n const token: LinkToken = {\n token: randomBytes(16).toString(\"hex\"),\n platform,\n platformUserId,\n vaultId,\n providerId,\n conversationId,\n expiresAt: Date.now() + TTL_MS,\n };\n this.tokens.set(token.token, token);\n return token;\n }\n\n /** Look up a token without consuming it. Returns undefined if missing or expired. */\n peek(rawToken: string): LinkToken | undefined {\n const entry = this.tokens.get(rawToken);\n if (!entry || Date.now() > entry.expiresAt) return undefined;\n return entry;\n }\n\n /** Consume a token (one-shot). Returns undefined if missing or expired. */\n consume(rawToken: string): LinkToken | undefined {\n const entry = this.tokens.get(rawToken);\n if (!entry) return undefined;\n this.tokens.delete(rawToken);\n if (Date.now() > entry.expiresAt) return undefined;\n return entry;\n }\n\n /** Remove expired tokens. Call periodically to bound memory usage. */\n purge(): void {\n const now = Date.now();\n for (const [key, t] of this.tokens) {\n if (now > t.expiresAt) {\n this.tokens.delete(key);\n }\n }\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/login/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAcrC,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE9B,MAAM,OAAO,sBAAsB;IAAnC;QACU,WAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IAyDhD,CAAC;IAvDC;;;OAGG;IACH,MAAM,CACJ,QAAsB,EACtB,cAAsB,EACtB,cAAsB,EACtB,OAAe,EACf,UAAkB;QAElB,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC,CAAC,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,cAAc,KAAK,cAAc,EAAE,CAAC;gBACnE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAc;YACvB,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;YACtC,QAAQ;YACR,cAAc;YACd,OAAO;YACP,UAAU;YACV,cAAc;YACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM;SAC/B,CAAC;QACF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,qFAAqF;IACrF,IAAI,CAAC,QAAgB;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QAC7D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,2EAA2E;IAC3E,OAAO,CAAC,QAAgB;QACtB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS;YAAE,OAAO,SAAS,CAAC;QACnD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,sEAAsE;IACtE,KAAK;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,IAAI,GAAG,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC;gBACtB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;CACF","sourcesContent":["import { randomBytes } from \"crypto\";\nimport type { PlatformName } from \"../adapter.js\";\n\nexport interface LinkToken {\n token: string;\n platform: PlatformName;\n platformUserId: string;\n vaultId: string;\n providerId: string;\n /** Conversation to notify when binding completes */\n conversationId: string;\n expiresAt: number;\n}\n\nconst TTL_MS = 15 * 60 * 1000;\n\nexport class InMemoryLinkTokenStore {\n private tokens = new Map<string, LinkToken>();\n\n /**\n * Create a link token for a platform user.\n * Invalidates any existing token for the same user before creating a new one.\n */\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): LinkToken {\n for (const [key, t] of this.tokens) {\n if (t.platform === platform && t.platformUserId === platformUserId) {\n this.tokens.delete(key);\n }\n }\n\n const token: LinkToken = {\n token: randomBytes(16).toString(\"hex\"),\n platform,\n platformUserId,\n vaultId,\n providerId,\n conversationId,\n expiresAt: Date.now() + TTL_MS,\n };\n this.tokens.set(token.token, token);\n return token;\n }\n\n /** Look up a token without consuming it. Returns undefined if missing or expired. */\n peek(rawToken: string): LinkToken | undefined {\n const entry = this.tokens.get(rawToken);\n if (!entry || Date.now() > entry.expiresAt) return undefined;\n return entry;\n }\n\n /** Consume a token (one-shot). Returns undefined if missing or expired. */\n consume(rawToken: string): LinkToken | undefined {\n const entry = this.tokens.get(rawToken);\n if (!entry) return undefined;\n this.tokens.delete(rawToken);\n if (Date.now() > entry.expiresAt) return undefined;\n return entry;\n }\n\n /** Remove expired tokens. Call periodically to bound memory usage. */\n purge(): void {\n const now = Date.now();\n for (const [key, t] of this.tokens) {\n if (now > t.expiresAt) {\n this.tokens.delete(key);\n }\n }\n }\n}\n"]}
|