@openacp/cli 0.2.18 → 0.2.22
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/README.md +49 -213
- package/dist/{chunk-3UGSZYSQ.js → chunk-XOVJLTEC.js} +27 -3
- package/dist/chunk-XOVJLTEC.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +8 -8
- package/dist/index.js +1 -1
- package/dist/{main-4H43GCXO.js → main-GC6JY7DS.js} +4 -4
- package/dist/{setup-FB4DGR6R.js → setup-XQBEZZQB.js} +2 -2
- package/dist/{setup-FB4DGR6R.js.map → setup-XQBEZZQB.js.map} +1 -1
- package/dist/{tunnel-service-FPRPBPQ5.js → tunnel-service-I6NUMBT4.js} +263 -15
- package/dist/tunnel-service-I6NUMBT4.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-3UGSZYSQ.js.map +0 -1
- package/dist/tunnel-service-FPRPBPQ5.js.map +0 -1
- /package/dist/{main-4H43GCXO.js.map → main-GC6JY7DS.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/config.ts","../../src/core/plugin-manager.ts"],"sourcesContent":["import { z } from \"zod\";\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\nimport * as os from \"node:os\";\nimport { createChildLogger } from \"./log.js\";\nconst log = createChildLogger({ module: \"config\" });\n\nconst BaseChannelSchema = z\n .object({\n enabled: z.boolean().default(false),\n adapter: z.string().optional(), // package name for plugin adapters\n })\n .passthrough();\n\nexport const PLUGINS_DIR = path.join(os.homedir(), \".openacp\", \"plugins\");\n\nconst AgentSchema = z.object({\n command: z.string(),\n args: z.array(z.string()).default([]),\n workingDirectory: z.string().optional(),\n env: z.record(z.string(), z.string()).default({}),\n});\n\nconst LoggingSchema = z\n .object({\n level: z\n .enum([\"silent\", \"debug\", \"info\", \"warn\", \"error\", \"fatal\"])\n .default(\"info\"),\n logDir: z.string().default(\"~/.openacp/logs\"),\n maxFileSize: z.union([z.string(), z.number()]).default(\"10m\"),\n maxFiles: z.number().default(7),\n sessionLogRetentionDays: z.number().default(30),\n })\n .default({});\n\nexport type LoggingConfig = z.infer<typeof LoggingSchema>;\n\nconst TunnelAuthSchema = z\n .object({\n enabled: z.boolean().default(false),\n token: z.string().optional(),\n })\n .default({});\n\nconst TunnelSchema = z\n .object({\n enabled: z.boolean().default(false),\n port: z.number().default(3100),\n provider: z.enum([\"cloudflare\", \"ngrok\", \"bore\"]).default(\"cloudflare\"),\n options: z.record(z.string(), z.unknown()).default({}),\n storeTtlMinutes: z.number().default(60),\n auth: TunnelAuthSchema,\n })\n .default({});\n\nexport type TunnelConfig = z.infer<typeof TunnelSchema>;\n\nexport const ConfigSchema = z.object({\n channels: z.record(z.string(), BaseChannelSchema),\n agents: z.record(z.string(), AgentSchema),\n defaultAgent: z.string(),\n workspace: z\n .object({\n baseDir: z.string().default(\"~/openacp-workspace\"),\n })\n .default({}),\n security: z\n .object({\n allowedUserIds: z.array(z.string()).default([]),\n maxConcurrentSessions: z.number().default(5),\n sessionTimeoutMinutes: z.number().default(60),\n })\n .default({}),\n logging: LoggingSchema,\n sessionStore: z\n .object({\n ttlDays: z.number().default(30),\n })\n .default({}),\n tunnel: TunnelSchema,\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\n\nexport function expandHome(p: string): string {\n if (p.startsWith(\"~\")) {\n return path.join(os.homedir(), p.slice(1));\n }\n return p;\n}\n\nconst DEFAULT_CONFIG = {\n channels: {\n telegram: {\n enabled: false,\n botToken: \"YOUR_BOT_TOKEN_HERE\",\n chatId: 0,\n notificationTopicId: null,\n assistantTopicId: null,\n },\n },\n agents: {\n claude: { command: \"claude-agent-acp\", args: [], env: {} },\n codex: { command: \"codex\", args: [\"--acp\"], env: {} },\n },\n defaultAgent: \"claude\",\n workspace: { baseDir: \"~/openacp-workspace\" },\n security: {\n allowedUserIds: [],\n maxConcurrentSessions: 5,\n sessionTimeoutMinutes: 60,\n },\n sessionStore: { ttlDays: 30 },\n};\n\nexport class ConfigManager {\n private config!: Config;\n private configPath: string;\n\n constructor() {\n this.configPath =\n process.env.OPENACP_CONFIG_PATH || expandHome(\"~/.openacp/config.json\");\n }\n\n async load(): Promise<void> {\n // 1. Ensure directory exists\n const dir = path.dirname(this.configPath);\n fs.mkdirSync(dir, { recursive: true });\n\n // 2. If config file doesn't exist, create default\n if (!fs.existsSync(this.configPath)) {\n fs.writeFileSync(\n this.configPath,\n JSON.stringify(DEFAULT_CONFIG, null, 2),\n );\n log.info({ configPath: this.configPath }, \"Config created\");\n log.info(\n \"Please edit it with your Telegram bot token and chat ID, then restart.\",\n );\n process.exit(1);\n }\n\n // 3. Read and parse\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n\n // 4. Apply env var overrides\n this.applyEnvOverrides(raw);\n\n // 5. Validate with Zod\n const result = ConfigSchema.safeParse(raw);\n if (!result.success) {\n log.error(\"Config validation failed\");\n for (const issue of result.error.issues) {\n log.error(\n { path: issue.path.join(\".\"), message: issue.message },\n \"Validation error\",\n );\n }\n process.exit(1);\n }\n this.config = result.data;\n }\n\n get(): Config {\n return this.config;\n }\n\n async save(updates: Record<string, unknown>): Promise<void> {\n // Read current file, merge updates, write back\n const raw = JSON.parse(fs.readFileSync(this.configPath, \"utf-8\"));\n this.deepMerge(raw, updates);\n fs.writeFileSync(this.configPath, JSON.stringify(raw, null, 2));\n // Re-validate and update in-memory config\n const result = ConfigSchema.safeParse(raw);\n if (result.success) {\n this.config = result.data;\n }\n }\n\n resolveWorkspace(input?: string): string {\n if (!input) {\n const resolved = expandHome(this.config.workspace.baseDir);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n if (input.startsWith(\"/\") || input.startsWith(\"~\")) {\n const resolved = expandHome(input);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n // Named workspace → lowercase, under baseDir\n const name = input.toLowerCase();\n const resolved = path.join(expandHome(this.config.workspace.baseDir), name);\n fs.mkdirSync(resolved, { recursive: true });\n return resolved;\n }\n\n async exists(): Promise<boolean> {\n return fs.existsSync(this.configPath);\n }\n\n getConfigPath(): string {\n return this.configPath;\n }\n\n async writeNew(config: Config): Promise<void> {\n const dir = path.dirname(this.configPath);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(this.configPath, JSON.stringify(config, null, 2));\n }\n\n private applyEnvOverrides(raw: Record<string, unknown>): void {\n const overrides: [string, string[]][] = [\n [\"OPENACP_TELEGRAM_BOT_TOKEN\", [\"channels\", \"telegram\", \"botToken\"]],\n [\"OPENACP_TELEGRAM_CHAT_ID\", [\"channels\", \"telegram\", \"chatId\"]],\n [\"OPENACP_DEFAULT_AGENT\", [\"defaultAgent\"]],\n ];\n for (const [envVar, configPath] of overrides) {\n const value = process.env[envVar];\n if (value !== undefined) {\n let target = raw as Record<string, any>;\n for (let i = 0; i < configPath.length - 1; i++) {\n if (!target[configPath[i]]) target[configPath[i]] = {};\n target = target[configPath[i]];\n }\n const key = configPath[configPath.length - 1];\n // Convert chatId to number\n target[key] = key === \"chatId\" ? Number(value) : value;\n }\n }\n\n // Logging env var overrides\n if (process.env.OPENACP_LOG_LEVEL) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).level =\n process.env.OPENACP_LOG_LEVEL;\n }\n if (process.env.OPENACP_LOG_DIR) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).logDir =\n process.env.OPENACP_LOG_DIR;\n }\n if (process.env.OPENACP_DEBUG && !process.env.OPENACP_LOG_LEVEL) {\n raw.logging = raw.logging || {};\n (raw.logging as Record<string, unknown>).level = \"debug\";\n }\n\n // Tunnel env var overrides\n if (process.env.OPENACP_TUNNEL_ENABLED) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).enabled =\n process.env.OPENACP_TUNNEL_ENABLED === \"true\";\n }\n if (process.env.OPENACP_TUNNEL_PORT) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).port = Number(\n process.env.OPENACP_TUNNEL_PORT,\n );\n }\n if (process.env.OPENACP_TUNNEL_PROVIDER) {\n raw.tunnel = raw.tunnel || {};\n (raw.tunnel as Record<string, unknown>).provider =\n process.env.OPENACP_TUNNEL_PROVIDER;\n }\n }\n\n private deepMerge(\n target: Record<string, any>,\n source: Record<string, any>,\n ): void {\n for (const key of Object.keys(source)) {\n if (\n source[key] &&\n typeof source[key] === \"object\" &&\n !Array.isArray(source[key])\n ) {\n if (!target[key]) target[key] = {};\n this.deepMerge(target[key], source[key]);\n } else {\n target[key] = source[key];\n }\n }\n }\n}\n","import { execSync } from 'node:child_process'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { createRequire } from 'node:module'\nimport { PLUGINS_DIR } from './config.js'\nimport { createChildLogger } from './log.js'\nconst log = createChildLogger({ module: 'plugin-manager' })\nimport type { ChannelAdapter } from './channel.js'\n\nexport interface AdapterFactory {\n name: string\n createAdapter(core: any, config: any): ChannelAdapter\n}\n\nfunction ensurePluginsDir(): void {\n fs.mkdirSync(PLUGINS_DIR, { recursive: true })\n const pkgPath = path.join(PLUGINS_DIR, 'package.json')\n if (!fs.existsSync(pkgPath)) {\n fs.writeFileSync(pkgPath, JSON.stringify({ name: 'openacp-plugins', private: true, dependencies: {} }, null, 2))\n }\n}\n\nexport function installPlugin(packageName: string): void {\n ensurePluginsDir()\n log.info({ packageName }, 'Installing plugin')\n execSync(`npm install ${packageName} --prefix \"${PLUGINS_DIR}\"`, { stdio: 'inherit' })\n log.info({ packageName }, 'Plugin installed successfully')\n}\n\nexport function uninstallPlugin(packageName: string): void {\n ensurePluginsDir()\n log.info({ packageName }, 'Uninstalling plugin')\n execSync(`npm uninstall ${packageName} --prefix \"${PLUGINS_DIR}\"`, { stdio: 'inherit' })\n log.info({ packageName }, 'Plugin uninstalled')\n}\n\nexport function listPlugins(): Record<string, string> {\n const pkgPath = path.join(PLUGINS_DIR, 'package.json')\n if (!fs.existsSync(pkgPath)) return {}\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))\n return pkg.dependencies || {}\n}\n\nexport async function loadAdapterFactory(packageName: string): Promise<AdapterFactory | null> {\n try {\n const require = createRequire(path.join(PLUGINS_DIR, 'package.json'))\n const resolved = require.resolve(packageName)\n const mod = await import(resolved)\n\n // Plugin must export `adapterFactory` or default export conforming to AdapterFactory\n const factory: AdapterFactory | undefined = mod.adapterFactory || mod.default\n if (!factory || typeof factory.createAdapter !== 'function') {\n log.error({ packageName }, 'Plugin does not export a valid AdapterFactory (needs .createAdapter())')\n return null\n }\n return factory\n } catch (err) {\n log.error({ packageName, err }, 'Failed to load plugin')\n log.error({ packageName }, 'Run: npx openacp install <packageName>')\n return null\n }\n}\n"],"mappings":"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","names":["resolved","fs","path","log","require"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/tunnel/tunnel-service.ts","../../src/tunnel/providers/cloudflare.ts","../../src/tunnel/viewer-store.ts","../../src/tunnel/server.ts","../../src/tunnel/templates/file-viewer.ts","../../src/tunnel/templates/diff-viewer.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport type { TunnelConfig } from '../core/config.js'\nimport { createChildLogger } from '../core/log.js'\nimport type { TunnelProvider } from './provider.js'\nimport { CloudflareTunnelProvider } from './providers/cloudflare.js'\nimport { ViewerStore } from './viewer-store.js'\nimport { createTunnelServer } from './server.js'\n\nconst log = createChildLogger({ module: 'tunnel' })\n\nexport class TunnelService {\n private provider: TunnelProvider\n private store: ViewerStore\n private server: ReturnType<typeof serve> | null = null\n private publicUrl = ''\n private config: TunnelConfig\n\n constructor(config: TunnelConfig) {\n this.config = config\n this.store = new ViewerStore(config.storeTtlMinutes)\n this.provider = this.createProvider(config.provider, config.options)\n }\n\n async start(): Promise<string> {\n // 1. Start HTTP server\n const authToken = this.config.auth.enabled ? this.config.auth.token : undefined\n const app = createTunnelServer(this.store, authToken)\n\n this.server = serve({ fetch: app.fetch, port: this.config.port })\n log.info({ port: this.config.port }, 'Tunnel HTTP server started')\n\n // 2. Start tunnel provider\n try {\n this.publicUrl = await this.provider.start(this.config.port)\n log.info({ url: this.publicUrl }, 'Tunnel public URL ready')\n } catch (err) {\n log.warn({ err }, 'Tunnel provider failed to start, running without public URL')\n this.publicUrl = `http://localhost:${this.config.port}`\n }\n\n return this.publicUrl\n }\n\n async stop(): Promise<void> {\n await this.provider.stop()\n if (this.server) {\n this.server.close()\n this.server = null\n }\n this.store.destroy()\n log.info('Tunnel service stopped')\n }\n\n getPublicUrl(): string {\n return this.publicUrl\n }\n\n getStore(): ViewerStore {\n return this.store\n }\n\n fileUrl(entryId: string): string {\n return `${this.publicUrl}/view/${entryId}`\n }\n\n diffUrl(entryId: string): string {\n return `${this.publicUrl}/diff/${entryId}`\n }\n\n private createProvider(name: string, options: Record<string, unknown>): TunnelProvider {\n switch (name) {\n case 'cloudflare':\n return new CloudflareTunnelProvider(options)\n default:\n log.warn({ provider: name }, 'Unknown tunnel provider, falling back to cloudflare')\n return new CloudflareTunnelProvider(options)\n }\n }\n}\n","import { spawn, type ChildProcess } from 'node:child_process'\nimport { createChildLogger } from '../../core/log.js'\nimport type { TunnelProvider } from '../provider.js'\n\nconst log = createChildLogger({ module: 'cloudflare-tunnel' })\n\nexport class CloudflareTunnelProvider implements TunnelProvider {\n private child: ChildProcess | null = null\n private publicUrl = ''\n private options: Record<string, unknown>\n\n constructor(options: Record<string, unknown> = {}) {\n this.options = options\n }\n\n async start(localPort: number): Promise<string> {\n const args = ['tunnel', '--url', `http://localhost:${localPort}`]\n if (this.options.domain) {\n args.push('--hostname', String(this.options.domain))\n }\n\n return new Promise<string>((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.stop()\n reject(new Error('Cloudflare tunnel timed out after 30s. Is cloudflared installed?'))\n }, 30_000)\n\n try {\n this.child = spawn('cloudflared', args, { stdio: ['ignore', 'pipe', 'pipe'] })\n } catch {\n clearTimeout(timeout)\n reject(new Error(\n 'Failed to start cloudflared. Install it from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/'\n ))\n return\n }\n\n const urlPattern = /https:\\/\\/[a-zA-Z0-9-]+\\.trycloudflare\\.com/\n\n const onData = (data: Buffer) => {\n const line = data.toString()\n log.debug(line.trim())\n const match = line.match(urlPattern)\n if (match) {\n clearTimeout(timeout)\n this.publicUrl = match[0]\n log.info({ url: this.publicUrl }, 'Cloudflare tunnel ready')\n resolve(this.publicUrl)\n }\n }\n\n this.child.stdout?.on('data', onData)\n this.child.stderr?.on('data', onData)\n\n this.child.on('error', (err) => {\n clearTimeout(timeout)\n reject(new Error(\n `cloudflared failed to start: ${err.message}. Install it from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/`\n ))\n })\n\n this.child.on('exit', (code) => {\n if (!this.publicUrl) {\n clearTimeout(timeout)\n reject(new Error(`cloudflared exited with code ${code} before establishing tunnel`))\n }\n })\n })\n }\n\n async stop(): Promise<void> {\n if (this.child) {\n this.child.kill('SIGTERM')\n this.child = null\n log.info('Cloudflare tunnel stopped')\n }\n }\n\n getPublicUrl(): string {\n return this.publicUrl\n }\n}\n","import * as path from 'node:path'\nimport { nanoid } from 'nanoid'\nimport { createChildLogger } from '../core/log.js'\n\nconst log = createChildLogger({ module: 'viewer-store' })\n\nconst MAX_CONTENT_SIZE = 1_000_000 // 1MB\n\nconst EXTENSION_LANGUAGE: Record<string, string> = {\n '.ts': 'typescript', '.tsx': 'typescript', '.js': 'javascript', '.jsx': 'javascript',\n '.py': 'python', '.rs': 'rust', '.go': 'go', '.java': 'java', '.kt': 'kotlin',\n '.rb': 'ruby', '.php': 'php', '.c': 'c', '.cpp': 'cpp', '.h': 'c', '.hpp': 'cpp',\n '.cs': 'csharp', '.swift': 'swift', '.sh': 'bash', '.zsh': 'bash', '.bash': 'bash',\n '.json': 'json', '.yaml': 'yaml', '.yml': 'yaml', '.toml': 'toml',\n '.xml': 'xml', '.html': 'html', '.css': 'css', '.scss': 'scss',\n '.sql': 'sql', '.md': 'markdown', '.dockerfile': 'dockerfile',\n '.tf': 'hcl', '.vue': 'xml', '.svelte': 'xml',\n}\n\nexport interface ViewerEntry {\n id: string\n type: 'file' | 'diff'\n filePath?: string\n content: string\n oldContent?: string\n language?: string\n sessionId: string\n workingDirectory: string\n createdAt: number\n expiresAt: number\n}\n\nexport class ViewerStore {\n private entries = new Map<string, ViewerEntry>()\n private cleanupTimer: ReturnType<typeof setInterval>\n private ttlMs: number\n\n constructor(ttlMinutes: number = 60) {\n this.ttlMs = ttlMinutes * 60 * 1000\n this.cleanupTimer = setInterval(() => this.cleanup(), 5 * 60 * 1000)\n }\n\n storeFile(sessionId: string, filePath: string, content: string, workingDirectory: string): string | null {\n if (!this.isPathAllowed(filePath, workingDirectory)) {\n log.warn({ filePath, workingDirectory }, 'Path outside workspace, rejecting')\n return null\n }\n if (content.length > MAX_CONTENT_SIZE) {\n log.debug({ filePath, size: content.length }, 'File too large for viewer')\n return null\n }\n\n const id = nanoid(12)\n const now = Date.now()\n this.entries.set(id, {\n id,\n type: 'file',\n filePath,\n content,\n language: this.detectLanguage(filePath),\n sessionId,\n workingDirectory,\n createdAt: now,\n expiresAt: now + this.ttlMs,\n })\n log.debug({ id, filePath }, 'Stored file for viewing')\n return id\n }\n\n storeDiff(sessionId: string, filePath: string, oldContent: string, newContent: string, workingDirectory: string): string | null {\n if (!this.isPathAllowed(filePath, workingDirectory)) {\n log.warn({ filePath, workingDirectory }, 'Path outside workspace, rejecting')\n return null\n }\n const combined = oldContent.length + newContent.length\n if (combined > MAX_CONTENT_SIZE) {\n log.debug({ filePath, size: combined }, 'Diff content too large for viewer')\n return null\n }\n\n const id = nanoid(12)\n const now = Date.now()\n this.entries.set(id, {\n id,\n type: 'diff',\n filePath,\n content: newContent,\n oldContent,\n language: this.detectLanguage(filePath),\n sessionId,\n workingDirectory,\n createdAt: now,\n expiresAt: now + this.ttlMs,\n })\n log.debug({ id, filePath }, 'Stored diff for viewing')\n return id\n }\n\n get(id: string): ViewerEntry | undefined {\n const entry = this.entries.get(id)\n if (!entry) return undefined\n if (Date.now() > entry.expiresAt) {\n this.entries.delete(id)\n return undefined\n }\n return entry\n }\n\n private cleanup(): void {\n const now = Date.now()\n let removed = 0\n for (const [id, entry] of this.entries) {\n if (now > entry.expiresAt) {\n this.entries.delete(id)\n removed++\n }\n }\n if (removed > 0) {\n log.debug({ removed, remaining: this.entries.size }, 'Cleaned up expired viewer entries')\n }\n }\n\n private isPathAllowed(filePath: string, workingDirectory: string): boolean {\n const resolved = path.resolve(workingDirectory, filePath)\n return resolved.startsWith(path.resolve(workingDirectory))\n }\n\n private detectLanguage(filePath: string): string | undefined {\n const ext = path.extname(filePath).toLowerCase()\n return EXTENSION_LANGUAGE[ext]\n }\n\n destroy(): void {\n clearInterval(this.cleanupTimer)\n this.entries.clear()\n }\n}\n","import { Hono } from 'hono'\nimport type { ViewerStore } from './viewer-store.js'\nimport { renderFileViewer } from './templates/file-viewer.js'\nimport { renderDiffViewer } from './templates/diff-viewer.js'\n\nfunction notFoundPage(): string {\n return `<!DOCTYPE html>\n<html><head><meta charset=\"UTF-8\"><title>Not Found - OpenACP</title>\n<style>body{background:#0d1117;color:#c9d1d9;font-family:sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0}\n.box{text-align:center;padding:40px}.code{font-size:72px;font-weight:bold;color:#484f58}p{margin-top:16px;color:#8b949e}</style>\n</head><body><div class=\"box\"><div class=\"code\">404</div><p>This viewer link has expired or does not exist.</p></div></body></html>`\n}\n\nexport function createTunnelServer(store: ViewerStore, authToken?: string): Hono {\n const app = new Hono()\n\n // Auth middleware\n if (authToken) {\n app.use('*', async (c, next) => {\n if (c.req.path === '/health') return next()\n const bearer = c.req.header('Authorization')?.replace('Bearer ', '')\n const query = c.req.query('token')\n if (bearer !== authToken && query !== authToken) {\n return c.text('Unauthorized', 401)\n }\n return next()\n })\n }\n\n app.get('/health', (c) => c.json({ status: 'ok' }))\n\n app.get('/view/:id', (c) => {\n const entry = store.get(c.req.param('id'))\n if (!entry || entry.type !== 'file') {\n return c.html(notFoundPage(), 404)\n }\n return c.html(renderFileViewer(entry))\n })\n\n app.get('/diff/:id', (c) => {\n const entry = store.get(c.req.param('id'))\n if (!entry || entry.type !== 'diff') {\n return c.html(notFoundPage(), 404)\n }\n return c.html(renderDiffViewer(entry))\n })\n\n app.get('/api/file/:id', (c) => {\n const entry = store.get(c.req.param('id'))\n if (!entry || entry.type !== 'file') {\n return c.json({ error: 'not found' }, 404)\n }\n return c.json({ filePath: entry.filePath, content: entry.content, language: entry.language })\n })\n\n app.get('/api/diff/:id', (c) => {\n const entry = store.get(c.req.param('id'))\n if (!entry || entry.type !== 'diff') {\n return c.json({ error: 'not found' }, 404)\n }\n return c.json({ filePath: entry.filePath, oldContent: entry.oldContent, newContent: entry.content, language: entry.language })\n })\n\n return app\n}\n","import type { ViewerEntry } from '../viewer-store.js'\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\n// Map our language IDs to Monaco language IDs\nconst MONACO_LANGUAGE: Record<string, string> = {\n typescript: 'typescript', javascript: 'javascript', python: 'python',\n rust: 'rust', go: 'go', java: 'java', kotlin: 'kotlin', ruby: 'ruby',\n php: 'php', c: 'c', cpp: 'cpp', csharp: 'csharp', swift: 'swift',\n bash: 'shell', json: 'json', yaml: 'yaml', toml: 'ini', xml: 'xml',\n html: 'html', css: 'css', scss: 'scss', sql: 'sql', markdown: 'markdown',\n dockerfile: 'dockerfile', hcl: 'hcl', plaintext: 'plaintext',\n}\n\nfunction getMonacoLang(lang?: string): string {\n if (!lang) return 'plaintext'\n return MONACO_LANGUAGE[lang] || 'plaintext'\n}\n\nexport function renderFileViewer(entry: ViewerEntry): string {\n const fileName = entry.filePath || 'untitled'\n const lang = getMonacoLang(entry.language)\n // Escape </script> inside content to prevent premature tag closure\n const safeContent = JSON.stringify(entry.content).replace(/<\\//g, '<\\\\/')\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(fileName)} - OpenACP</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { background: #1e1e1e; color: #d4d4d4; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; display: flex; flex-direction: column; height: 100vh; }\n .header { background: #252526; border-bottom: 1px solid #3c3c3c; padding: 8px 16px; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; z-index: 10; }\n .file-info { display: flex; align-items: center; gap: 8px; font-size: 13px; min-width: 0; }\n .file-icon { font-size: 14px; flex-shrink: 0; }\n .file-path { color: #969696; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .file-name { color: #e0e0e0; font-weight: 500; flex-shrink: 0; }\n .actions { display: flex; gap: 6px; flex-shrink: 0; }\n .btn { background: #3c3c3c; color: #d4d4d4; border: 1px solid #505050; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; transition: background 0.15s; }\n .btn:hover { background: #505050; }\n .btn.active { background: #0e639c; border-color: #1177bb; }\n #editor-container { flex: 1; overflow: hidden; }\n .status-bar { background: #007acc; color: #fff; padding: 2px 16px; font-size: 12px; display: flex; justify-content: space-between; flex-shrink: 0; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div class=\"file-info\">\n <span class=\"file-icon\">📄</span>\n ${formatBreadcrumb(fileName)}\n </div>\n <div class=\"actions\">\n <button class=\"btn\" onclick=\"toggleWordWrap()\" id=\"btn-wrap\">Wrap</button>\n <button class=\"btn\" onclick=\"toggleMinimap()\" id=\"btn-minimap\">Minimap</button>\n <button class=\"btn\" onclick=\"toggleTheme()\" id=\"btn-theme\">Light</button>\n <button class=\"btn\" onclick=\"copyCode()\">Copy</button>\n </div>\n </div>\n <div id=\"editor-container\"></div>\n <div class=\"status-bar\">\n <span>${escapeHtml(entry.language || 'plaintext')} | ${entry.content.split('\\n').length} lines</span>\n <span>OpenACP Viewer (read-only)</span>\n </div>\n\n <script src=\"https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/loader.js\"></script>\n <script>\n const content = ${safeContent};\n const lang = ${JSON.stringify(lang)};\n let editor;\n let isDark = true;\n let wordWrap = false;\n let minimap = true;\n\n require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs' } });\n require(['vs/editor/editor.main'], function () {\n editor = monaco.editor.create(document.getElementById('editor-container'), {\n value: content,\n language: lang,\n theme: 'vs-dark',\n readOnly: true,\n automaticLayout: true,\n minimap: { enabled: true },\n scrollBeyondLastLine: false,\n fontSize: 13,\n fontFamily: \"'SF Mono', SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace\",\n lineNumbers: 'on',\n renderLineHighlight: 'all',\n wordWrap: 'off',\n padding: { top: 8 },\n });\n\n // Handle line range from URL hash: #L42 or #L42-L55\n function highlightFromHash() {\n const hash = location.hash.slice(1);\n const match = hash.match(/^L(\\\\d+)(?:-L?(\\\\d+))?$/);\n if (!match) return;\n const startLine = parseInt(match[1], 10);\n const endLine = match[2] ? parseInt(match[2], 10) : startLine;\n editor.revealLineInCenter(startLine);\n editor.setSelection(new monaco.Selection(startLine, 1, endLine + 1, 1));\n }\n highlightFromHash();\n window.addEventListener('hashchange', highlightFromHash);\n });\n\n function toggleTheme() {\n isDark = !isDark;\n monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs');\n document.body.style.background = isDark ? '#1e1e1e' : '#ffffff';\n document.querySelector('.header').style.background = isDark ? '#252526' : '#f3f3f3';\n document.querySelector('.header').style.borderColor = isDark ? '#3c3c3c' : '#e0e0e0';\n document.getElementById('btn-theme').textContent = isDark ? 'Light' : 'Dark';\n }\n\n function toggleWordWrap() {\n wordWrap = !wordWrap;\n editor.updateOptions({ wordWrap: wordWrap ? 'on' : 'off' });\n document.getElementById('btn-wrap').classList.toggle('active', wordWrap);\n }\n\n function toggleMinimap() {\n minimap = !minimap;\n editor.updateOptions({ minimap: { enabled: minimap } });\n document.getElementById('btn-minimap').classList.toggle('active', !minimap);\n }\n\n function copyCode() {\n navigator.clipboard.writeText(content).then(() => {\n const btn = event.target;\n btn.textContent = 'Copied!';\n setTimeout(() => btn.textContent = 'Copy', 2000);\n });\n }\n </script>\n</body>\n</html>`\n}\n\nfunction formatBreadcrumb(filePath: string): string {\n const parts = filePath.split('/')\n if (parts.length <= 1) return `<span class=\"file-name\">${escapeHtml(filePath)}</span>`\n const dir = parts.slice(0, -1).join(' / ')\n const name = parts[parts.length - 1]\n return `<span class=\"file-path\">${escapeHtml(dir)} /</span> <span class=\"file-name\">${escapeHtml(name)}</span>`\n}\n","import { createPatch } from 'diff'\nimport type { ViewerEntry } from '../viewer-store.js'\n\nfunction escapeHtml(text: string): string {\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nconst MONACO_LANGUAGE: Record<string, string> = {\n typescript: 'typescript', javascript: 'javascript', python: 'python',\n rust: 'rust', go: 'go', java: 'java', kotlin: 'kotlin', ruby: 'ruby',\n php: 'php', c: 'c', cpp: 'cpp', csharp: 'csharp', swift: 'swift',\n bash: 'shell', json: 'json', yaml: 'yaml', toml: 'ini', xml: 'xml',\n html: 'html', css: 'css', scss: 'scss', sql: 'sql', markdown: 'markdown',\n dockerfile: 'dockerfile', hcl: 'hcl', plaintext: 'plaintext',\n}\n\nfunction getMonacoLang(lang?: string): string {\n if (!lang) return 'plaintext'\n return MONACO_LANGUAGE[lang] || 'plaintext'\n}\n\nexport function renderDiffViewer(entry: ViewerEntry): string {\n const fileName = entry.filePath || 'untitled'\n const lang = getMonacoLang(entry.language)\n const oldContent = entry.oldContent || ''\n const newContent = entry.content\n\n // Count changes for stats\n const patch = createPatch(fileName, oldContent, newContent, 'before', 'after')\n const adds = (patch.match(/^\\+[^+]/gm) || []).length\n const dels = (patch.match(/^-[^-]/gm) || []).length\n\n // Escape </script> in content\n const safeOld = JSON.stringify(oldContent).replace(/<\\//g, '<\\\\/')\n const safeNew = JSON.stringify(newContent).replace(/<\\//g, '<\\\\/')\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>${escapeHtml(fileName)} (diff) - OpenACP</title>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { background: #1e1e1e; color: #d4d4d4; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; display: flex; flex-direction: column; height: 100vh; }\n .header { background: #252526; border-bottom: 1px solid #3c3c3c; padding: 8px 16px; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; z-index: 10; }\n .file-info { display: flex; align-items: center; gap: 8px; font-size: 13px; }\n .file-icon { font-size: 14px; }\n .file-name { color: #e0e0e0; font-weight: 500; }\n .stats { font-size: 12px; margin-left: 12px; }\n .stats .add { color: #4ec9b0; }\n .stats .del { color: #f14c4c; }\n .actions { display: flex; gap: 6px; }\n .btn { background: #3c3c3c; color: #d4d4d4; border: 1px solid #505050; padding: 4px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; transition: background 0.15s; }\n .btn:hover { background: #505050; }\n .btn.active { background: #0e639c; border-color: #1177bb; }\n #editor-container { flex: 1; overflow: hidden; }\n .status-bar { background: #007acc; color: #fff; padding: 2px 16px; font-size: 12px; display: flex; justify-content: space-between; flex-shrink: 0; }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <div class=\"file-info\">\n <span class=\"file-icon\">📝</span>\n <span class=\"file-name\">${escapeHtml(fileName)}</span>\n <span class=\"stats\"><span class=\"add\">+${adds}</span> / <span class=\"del\">-${dels}</span></span>\n </div>\n <div class=\"actions\">\n <button class=\"btn active\" id=\"btn-side\" onclick=\"setView('side')\">Side by Side</button>\n <button class=\"btn\" id=\"btn-inline\" onclick=\"setView('inline')\">Inline</button>\n <button class=\"btn\" onclick=\"toggleTheme()\" id=\"btn-theme\">Light</button>\n </div>\n </div>\n <div id=\"editor-container\"></div>\n <div class=\"status-bar\">\n <span>${escapeHtml(entry.language || 'plaintext')} | <span class=\"add\">+${adds}</span> <span class=\"del\">-${dels}</span></span>\n <span>OpenACP Diff Viewer</span>\n </div>\n\n <script src=\"https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs/loader.js\"></script>\n <script>\n const oldContent = ${safeOld};\n const newContent = ${safeNew};\n const lang = ${JSON.stringify(lang)};\n let diffEditor;\n let isDark = true;\n let renderSideBySide = true;\n\n require.config({ paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.52.2/min/vs' } });\n require(['vs/editor/editor.main'], function () {\n const originalModel = monaco.editor.createModel(oldContent, lang);\n const modifiedModel = monaco.editor.createModel(newContent, lang);\n\n diffEditor = monaco.editor.createDiffEditor(document.getElementById('editor-container'), {\n theme: 'vs-dark',\n readOnly: true,\n automaticLayout: true,\n renderSideBySide: true,\n scrollBeyondLastLine: false,\n fontSize: 13,\n fontFamily: \"'SF Mono', SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace\",\n padding: { top: 8 },\n enableSplitViewResizing: true,\n renderOverviewRuler: true,\n });\n\n diffEditor.setModel({ original: originalModel, modified: modifiedModel });\n });\n\n function setView(mode) {\n renderSideBySide = mode === 'side';\n diffEditor.updateOptions({ renderSideBySide });\n document.getElementById('btn-side').classList.toggle('active', renderSideBySide);\n document.getElementById('btn-inline').classList.toggle('active', !renderSideBySide);\n }\n\n function toggleTheme() {\n isDark = !isDark;\n monaco.editor.setTheme(isDark ? 'vs-dark' : 'vs');\n document.body.style.background = isDark ? '#1e1e1e' : '#ffffff';\n document.querySelector('.header').style.background = isDark ? '#252526' : '#f3f3f3';\n document.querySelector('.header').style.borderColor = isDark ? '#3c3c3c' : '#e0e0e0';\n document.getElementById('btn-theme').textContent = isDark ? 'Light' : 'Dark';\n }\n </script>\n</body>\n</html>`\n}\n"],"mappings":"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","names":["resolve","log","escapeHtml","MONACO_LANGUAGE","getMonacoLang","log"]}
|
|
File without changes
|