@algochad/archcoder 2.0.2

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.
Files changed (157) hide show
  1. package/README.md +113 -0
  2. package/bin/cli-entry.js +55 -0
  3. package/bin/cli-output.js +145 -0
  4. package/bin/cli.js +5108 -0
  5. package/bin/cli.test.js +56 -0
  6. package/dist/apple-touch-icon-120x120.png +0 -0
  7. package/dist/apple-touch-icon-152x152.png +0 -0
  8. package/dist/apple-touch-icon-167x167.png +0 -0
  9. package/dist/apple-touch-icon-180x180.png +0 -0
  10. package/dist/apple-touch-icon.png +0 -0
  11. package/dist/apple-touch-icon.svg +67 -0
  12. package/dist/assets/MultiRunWindow-BZp3MjJP.js +1 -0
  13. package/dist/assets/SettingsWindow-DoGYXpX7.js +1 -0
  14. package/dist/assets/TerminalView-BN7BR5Ff.js +3 -0
  15. package/dist/assets/TimelineDialog-ZQ33oVQR.js +1 -0
  16. package/dist/assets/ToolOutputDialog-Blv3pnug.js +16 -0
  17. package/dist/assets/ibm-plex-mono-latin-400-normal-CvHOgSBP.woff +0 -0
  18. package/dist/assets/ibm-plex-mono-latin-400-normal-DMJ8VG8y.woff2 +0 -0
  19. package/dist/assets/ibm-plex-mono-latin-500-normal-CB9ihrfo.woff +0 -0
  20. package/dist/assets/ibm-plex-mono-latin-500-normal-DSY6xOcd.woff2 +0 -0
  21. package/dist/assets/ibm-plex-mono-latin-600-normal-BgSNZQsw.woff2 +0 -0
  22. package/dist/assets/ibm-plex-mono-latin-600-normal-DWFSQ4vo.woff +0 -0
  23. package/dist/assets/ibm-plex-sans-latin-400-normal-CDDApCn2.woff2 +0 -0
  24. package/dist/assets/ibm-plex-sans-latin-400-normal-CYLoc0-x.woff +0 -0
  25. package/dist/assets/ibm-plex-sans-latin-500-normal-6ng42L7E.woff2 +0 -0
  26. package/dist/assets/ibm-plex-sans-latin-500-normal-BgVn5rGT.woff +0 -0
  27. package/dist/assets/ibm-plex-sans-latin-600-normal-Cu4Hd6ag.woff +0 -0
  28. package/dist/assets/ibm-plex-sans-latin-600-normal-CuJfVYMP.woff2 +0 -0
  29. package/dist/assets/index-CtCEGYrr.css +1 -0
  30. package/dist/assets/index-o_d2wtWC.js +48 -0
  31. package/dist/assets/main-5QGBtzdq.css +1 -0
  32. package/dist/assets/main-B6oiMU86.js +8033 -0
  33. package/dist/assets/vendor--DbVqbJpV.css +1 -0
  34. package/dist/assets/vendor-.bun-HTKwyaEM.js +10086 -0
  35. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  36. package/dist/assets/worker-bqd4RMrj.js +155 -0
  37. package/dist/favicon-16.png +0 -0
  38. package/dist/favicon-32.png +0 -0
  39. package/dist/favicon.png +0 -0
  40. package/dist/favicon.svg +67 -0
  41. package/dist/index.html +533 -0
  42. package/dist/logo-dark-192x192.png +0 -0
  43. package/dist/logo-dark-512x512.svg +16 -0
  44. package/dist/logo-light-192x192.png +0 -0
  45. package/dist/logo-light-512x512.svg +16 -0
  46. package/dist/pwa-192.png +0 -0
  47. package/dist/pwa-512.png +0 -0
  48. package/dist/pwa-maskable-192.png +0 -0
  49. package/dist/pwa-maskable-512.png +0 -0
  50. package/dist/site.webmanifest +22 -0
  51. package/dist/sw.js +1 -0
  52. package/package.json +107 -0
  53. package/public/apple-touch-icon-120x120.png +0 -0
  54. package/public/apple-touch-icon-152x152.png +0 -0
  55. package/public/apple-touch-icon-167x167.png +0 -0
  56. package/public/apple-touch-icon-180x180.png +0 -0
  57. package/public/apple-touch-icon.png +0 -0
  58. package/public/apple-touch-icon.svg +67 -0
  59. package/public/favicon-16.png +0 -0
  60. package/public/favicon-32.png +0 -0
  61. package/public/favicon.png +0 -0
  62. package/public/favicon.svg +67 -0
  63. package/public/logo-dark-192x192.png +0 -0
  64. package/public/logo-dark-512x512.svg +16 -0
  65. package/public/logo-light-192x192.png +0 -0
  66. package/public/logo-light-512x512.svg +16 -0
  67. package/public/pwa-192.png +0 -0
  68. package/public/pwa-512.png +0 -0
  69. package/public/pwa-maskable-192.png +0 -0
  70. package/public/pwa-maskable-512.png +0 -0
  71. package/public/site.webmanifest +22 -0
  72. package/server/TERMINAL_INPUT_WS_PROTOCOL.md +44 -0
  73. package/server/index.d.ts +37 -0
  74. package/server/index.js +14694 -0
  75. package/server/lib/cloudflare-tunnel.js +650 -0
  76. package/server/lib/git/DOCUMENTATION.md +146 -0
  77. package/server/lib/git/credentials.js +74 -0
  78. package/server/lib/git/identity-storage.js +110 -0
  79. package/server/lib/git/index.js +6 -0
  80. package/server/lib/git/service.js +3117 -0
  81. package/server/lib/github/DOCUMENTATION.md +170 -0
  82. package/server/lib/github/auth.js +307 -0
  83. package/server/lib/github/device-flow.js +50 -0
  84. package/server/lib/github/index.js +24 -0
  85. package/server/lib/github/octokit.js +10 -0
  86. package/server/lib/github/pr-status.js +478 -0
  87. package/server/lib/github/repo/index.js +55 -0
  88. package/server/lib/installer/desktop.js +289 -0
  89. package/server/lib/installer/download.js +208 -0
  90. package/server/lib/installer/index.js +45 -0
  91. package/server/lib/installer/platform.js +100 -0
  92. package/server/lib/notifications/DOCUMENTATION.md +61 -0
  93. package/server/lib/notifications/index.js +1 -0
  94. package/server/lib/notifications/message.js +49 -0
  95. package/server/lib/notifications/message.test.js +59 -0
  96. package/server/lib/opencode/DOCUMENTATION.md +59 -0
  97. package/server/lib/opencode/agents.js +634 -0
  98. package/server/lib/opencode/auth.js +81 -0
  99. package/server/lib/opencode/commands.js +339 -0
  100. package/server/lib/opencode/index.js +66 -0
  101. package/server/lib/opencode/mcp.js +206 -0
  102. package/server/lib/opencode/providers.js +96 -0
  103. package/server/lib/opencode/shared.js +527 -0
  104. package/server/lib/opencode/skills.js +480 -0
  105. package/server/lib/opencode/tunnel-auth.js +591 -0
  106. package/server/lib/opencode/ui-auth.js +510 -0
  107. package/server/lib/package-manager.js +505 -0
  108. package/server/lib/quota/DOCUMENTATION.md +55 -0
  109. package/server/lib/quota/index.js +24 -0
  110. package/server/lib/quota/providers/claude.js +107 -0
  111. package/server/lib/quota/providers/codex.js +113 -0
  112. package/server/lib/quota/providers/copilot.js +165 -0
  113. package/server/lib/quota/providers/google/api.js +92 -0
  114. package/server/lib/quota/providers/google/auth.js +108 -0
  115. package/server/lib/quota/providers/google/index.js +124 -0
  116. package/server/lib/quota/providers/google/transforms.js +109 -0
  117. package/server/lib/quota/providers/index.js +152 -0
  118. package/server/lib/quota/providers/interface.js +55 -0
  119. package/server/lib/quota/providers/kimi.js +108 -0
  120. package/server/lib/quota/providers/minimax-cn-coding-plan.js +15 -0
  121. package/server/lib/quota/providers/minimax-coding-plan.js +15 -0
  122. package/server/lib/quota/providers/minimax-shared.js +136 -0
  123. package/server/lib/quota/providers/nanogpt.js +124 -0
  124. package/server/lib/quota/providers/ollama-cloud.js +112 -0
  125. package/server/lib/quota/providers/openai.js +91 -0
  126. package/server/lib/quota/providers/openrouter.js +92 -0
  127. package/server/lib/quota/providers/zai.js +91 -0
  128. package/server/lib/quota/utils/auth.js +46 -0
  129. package/server/lib/quota/utils/formatters.js +76 -0
  130. package/server/lib/quota/utils/index.js +10 -0
  131. package/server/lib/quota/utils/transformers.js +55 -0
  132. package/server/lib/skills-catalog/DOCUMENTATION.md +178 -0
  133. package/server/lib/skills-catalog/cache.js +32 -0
  134. package/server/lib/skills-catalog/clawdhub/api.js +158 -0
  135. package/server/lib/skills-catalog/clawdhub/index.js +30 -0
  136. package/server/lib/skills-catalog/clawdhub/install.js +238 -0
  137. package/server/lib/skills-catalog/clawdhub/scan.js +113 -0
  138. package/server/lib/skills-catalog/curated-sources.js +21 -0
  139. package/server/lib/skills-catalog/git.js +77 -0
  140. package/server/lib/skills-catalog/index.js +42 -0
  141. package/server/lib/skills-catalog/install.js +294 -0
  142. package/server/lib/skills-catalog/scan.js +221 -0
  143. package/server/lib/skills-catalog/source.js +85 -0
  144. package/server/lib/terminal/DOCUMENTATION.md +114 -0
  145. package/server/lib/terminal/index.js +12 -0
  146. package/server/lib/terminal/input-ws-protocol.js +66 -0
  147. package/server/lib/terminal/input-ws-protocol.test.js +138 -0
  148. package/server/lib/tts/DOCUMENTATION.md +134 -0
  149. package/server/lib/tts/index.js +16 -0
  150. package/server/lib/tts/service.js +162 -0
  151. package/server/lib/tts/summarization.js +171 -0
  152. package/server/lib/tunnels/index.js +166 -0
  153. package/server/lib/tunnels/providers/cloudflare.js +260 -0
  154. package/server/lib/tunnels/registry.js +51 -0
  155. package/server/lib/tunnels/types.js +219 -0
  156. package/server/lib/utils/lru.js +107 -0
  157. package/server/lib/utils/sse.js +121 -0
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "OpenChamber - AI Coding Companion",
3
+ "short_name": "OpenChamber",
4
+ "description": "OpenChamber desktop companion for the OpenCode AI coding agent",
5
+ "start_url": "/",
6
+ "display": "standalone",
7
+ "background_color": "#151313",
8
+ "theme_color": "#edb449",
9
+ "orientation": "portrait-primary",
10
+ "icons": [
11
+ { "src": "/pwa-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
12
+ { "src": "/pwa-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
13
+ { "src": "/pwa-maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
14
+ { "src": "/pwa-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" },
15
+ { "src": "/apple-touch-icon-180x180.png", "sizes": "180x180", "type": "image/png", "purpose": "any" },
16
+ { "src": "/apple-touch-icon-152x152.png", "sizes": "152x152", "type": "image/png", "purpose": "any" },
17
+ { "src": "/favicon-32.png", "sizes": "32x32", "type": "image/png" },
18
+ { "src": "/favicon-16.png", "sizes": "16x16", "type": "image/png" }
19
+ ],
20
+ "categories": ["developer", "tools", "productivity"],
21
+ "lang": "en"
22
+ }
package/dist/sw.js ADDED
@@ -0,0 +1 @@
1
+ (function(){"use strict";self.addEventListener("install",t=>{t.waitUntil(self.skipWaiting())}),self.addEventListener("activate",t=>{t.waitUntil(self.clients.claim())}),self.addEventListener("push",t=>{t.waitUntil((async()=>{const i=t.data?.json()??null;if(!i||(await self.clients.matchAll({type:"window",includeUncontrolled:!0})).some(a=>a.visibilityState==="visible"||a.focused))return;const s=i.title||"ArchCoder",e=i.body??"",l=i.icon??"/apple-touch-icon-180x180.png",o=i.badge??"/favicon-32.png";await self.registration.showNotification(s,{body:e,icon:l,badge:o,tag:i.tag,data:i.data})})())}),self.addEventListener("notificationclick",t=>{t.notification.close();const n=(t.notification.data??null)?.url??"/";t.waitUntil(self.clients.openWindow(n))})})();
package/package.json ADDED
@@ -0,0 +1,107 @@
1
+ {
2
+ "name": "@algochad/archcoder",
3
+ "version": "2.0.2",
4
+ "private": false,
5
+ "type": "module",
6
+ "main": "./server/index.js",
7
+ "types": "./server/index.d.ts",
8
+ "bin": {
9
+ "archcoder": "./bin/cli.js"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "scripts": {
15
+ "dev": "bun run build:watch",
16
+ "dev:server": "bun server/index.js --port ${ARCHCODER_PORT:-3001}",
17
+ "dev:server:watch": "nodemon --watch server --ext js --exec \"bun server/index.js --port ${ARCHCODER_PORT:-3001}\"",
18
+ "build": "vite build",
19
+ "build:watch": "vite build --watch",
20
+ "type-check": "tsc --noEmit",
21
+ "lint": "eslint \"./src/**/*.{ts,tsx}\" --config ../../eslint.config.js",
22
+ "start": "node bin/cli.js serve"
23
+ },
24
+ "dependencies": {
25
+ "@aws-sdk/client-s3": "^3.700.0",
26
+ "@clack/prompts": "^1.1.0",
27
+ "@codemirror/lang-cpp": "^6.0.3",
28
+ "@codemirror/lang-go": "^6.0.1",
29
+ "@fontsource/ibm-plex-mono": "^5.2.7",
30
+ "@fontsource/ibm-plex-sans": "^5.1.1",
31
+ "@ibm/plex": "^6.4.1",
32
+ "@octokit/rest": "^22.0.1",
33
+ "@opencode-ai/sdk": "^1.2.27",
34
+ "@radix-ui/react-collapsible": "^1.1.12",
35
+ "@radix-ui/react-dialog": "^1.1.15",
36
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
37
+ "@radix-ui/react-scroll-area": "^1.2.10",
38
+ "@radix-ui/react-select": "^2.2.6",
39
+ "@radix-ui/react-separator": "^1.1.7",
40
+ "@radix-ui/react-slot": "^1.2.3",
41
+ "@radix-ui/react-toggle": "^1.1.10",
42
+ "@radix-ui/react-tooltip": "^1.2.8",
43
+ "@remixicon/react": "^4.7.0",
44
+ "@types/react-syntax-highlighter": "^15.5.13",
45
+ "adm-zip": "^0.5.16",
46
+ "bun-pty": "^0.4.5",
47
+ "class-variance-authority": "^0.7.1",
48
+ "clsx": "^2.1.1",
49
+ "cmdk": "^1.1.1",
50
+ "express": "^5.1.0",
51
+ "ghostty-web": "0.3.0",
52
+ "http-proxy-middleware": "^3.0.5",
53
+ "jose": "^6.1.3",
54
+ "jsonc-parser": "^3.3.1",
55
+ "next-themes": "^0.4.6",
56
+ "node-pty": "1.2.0-beta.12",
57
+ "openai": "^4.79.0",
58
+ "qrcode-terminal": "^0.12.0",
59
+ "react": "^19.1.1",
60
+ "react-dom": "^19.1.1",
61
+ "react-markdown": "^10.1.0",
62
+ "react-syntax-highlighter": "^15.6.6",
63
+ "remark-gfm": "^4.0.1",
64
+ "simple-git": "^3.28.0",
65
+ "sonner": "^2.0.7",
66
+ "strip-json-comments": "^5.0.3",
67
+ "tailwind-merge": "^3.3.1",
68
+ "web-push": "^3.6.7",
69
+ "ws": "^8.18.3",
70
+ "yaml": "^2.8.1",
71
+ "zustand": "^5.0.8"
72
+ },
73
+ "devDependencies": {
74
+ "@eslint/js": "^9.33.0",
75
+ "@tailwindcss/postcss": "^4.0.0",
76
+ "@types/adm-zip": "^0.5.7",
77
+ "@types/node": "^24.3.1",
78
+ "@types/react": "^19.1.10",
79
+ "@types/react-dom": "^19.1.7",
80
+ "@vitejs/plugin-react": "^5.0.0",
81
+ "autoprefixer": "^10.4.21",
82
+ "concurrently": "^9.2.1",
83
+ "cors": "^2.8.5",
84
+ "cross-env": "^7.0.3",
85
+ "eslint": "^9.33.0",
86
+ "eslint-plugin-react-hooks": "^5.2.0",
87
+ "eslint-plugin-react-refresh": "^0.4.20",
88
+ "globals": "^16.3.0",
89
+ "nodemon": "^3.1.7",
90
+ "tailwindcss": "^4.0.0",
91
+ "tsx": "^4.20.6",
92
+ "tw-animate-css": "^1.3.8",
93
+ "typescript": "~5.8.3",
94
+ "typescript-eslint": "^8.39.1",
95
+ "vite": "^7.1.2",
96
+ "vite-plugin-pwa": "^1.0.3",
97
+ "workbox-window": "^7.4.0"
98
+ },
99
+ "files": [
100
+ "dist",
101
+ "server",
102
+ "bin",
103
+ "public",
104
+ "package.json",
105
+ "README.md"
106
+ ]
107
+ }
Binary file
@@ -0,0 +1,67 @@
1
+ <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#1a1a1a"/>
5
+ <stop offset="100%" stop-color="#0d0d0d"/>
6
+ </linearGradient>
7
+ <linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
8
+ <stop offset="0%" stop-color="#ff8533" class="shimmer-stop"/>
9
+ <stop offset="50%" stop-color="#ff6b00" class="shimmer-stop"/>
10
+ <stop offset="100%" stop-color="#cc5500" class="shimmer-stop"/>
11
+ </linearGradient>
12
+ <filter id="glow" x="-30%" y="-30%" width="160%" height="160%">
13
+ <feGaussianBlur stdDeviation="8" result="coloredBlur" class="pulse-blur"/>
14
+ <feMerge>
15
+ <feMergeNode in="coloredBlur"/>
16
+ <feMergeNode in="SourceGraphic"/>
17
+ </feMerge>
18
+ </filter>
19
+ <filter id="innerShadow" x="-20%" y="-20%" width="140%" height="140%">
20
+ <feOffset dx="0" dy="4"/>
21
+ <feGaussianBlur stdDeviation="4" result="offset-blur"/>
22
+ <feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"/>
23
+ <feFlood flood-color="#ffffff" flood-opacity="0.1" result="color"/>
24
+ <feComposite operator="in" in="color" in2="inverse" result="shadow"/>
25
+ <feComposite operator="over" in="shadow" in2="SourceGraphic"/>
26
+ </filter>
27
+
28
+ <!-- Animation for pulsing glow effect -->
29
+ <style>
30
+ @keyframes pulse {
31
+ 0%, 100% { stdDeviation: 8; }
32
+ 50% { stdDeviation: 12; }
33
+ }
34
+ @keyframes shimmer {
35
+ 0%, 100% { stop-color: #ff8533; }
36
+ 50% { stop-color: #ffaa66; }
37
+ }
38
+ .pulse-blur {
39
+ animation: pulse 2s ease-in-out infinite;
40
+ }
41
+ .shimmer-stop {
42
+ animation: shimmer 3s ease-in-out infinite;
43
+ }
44
+ </style>
45
+ </defs>
46
+
47
+ <rect width="512" height="512" rx="112" fill="url(#bgGrad)"/>
48
+ <rect x="12" y="12" width="488" height="488" rx="100" fill="none" stroke="#2a2a2a" stroke-width="4"/>
49
+
50
+ <g filter="url(#glow)">
51
+ <path d="M256 64L400 400H336L296 304H216L176 400H112L256 64Z" fill="#ffffff" filter="url(#innerShadow)"/>
52
+ </g>
53
+
54
+ <path d="M256 128L288 208H224L256 128Z" fill="url(#orangeGrad)"/>
55
+ <rect x="232" y="248" width="48" height="16" rx="8" fill="url(#orangeGrad)"/>
56
+
57
+ <circle cx="256" cy="64" r="16" fill="url(#orangeGrad)"/>
58
+
59
+ <circle cx="136" cy="432" r="10" fill="#ff6b00"/>
60
+ <circle cx="184" cy="432" r="8" fill="#ffffff" opacity="0.4"/>
61
+ <circle cx="256" cy="432" r="8" fill="#ffffff" opacity="0.6"/>
62
+ <circle cx="328" cy="432" r="8" fill="#ffffff" opacity="0.4"/>
63
+ <circle cx="376" cy="432" r="10" fill="#ff6b00"/>
64
+
65
+ <path d="M120 400L112 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round"/>
66
+ <path d="M400 400L408 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round"/>
67
+ </svg>
Binary file
Binary file
Binary file
@@ -0,0 +1,67 @@
1
+ <svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <defs>
3
+ <linearGradient id="bgGrad" x1="0%" y1="0%" x2="100%" y2="100%">
4
+ <stop offset="0%" stop-color="#1a1a1a"/>
5
+ <stop offset="100%" stop-color="#0d0d0d"/>
6
+ </linearGradient>
7
+ <linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
8
+ <stop offset="0%" stop-color="#ff8533" class="shimmer-stop"/>
9
+ <stop offset="50%" stop-color="#ff6b00" class="shimmer-stop"/>
10
+ <stop offset="100%" stop-color="#cc5500" class="shimmer-stop"/>
11
+ </linearGradient>
12
+ <filter id="glow" x="-30%" y="-30%" width="160%" height="160%">
13
+ <feGaussianBlur stdDeviation="8" result="coloredBlur" class="pulse-blur"/>
14
+ <feMerge>
15
+ <feMergeNode in="coloredBlur"/>
16
+ <feMergeNode in="SourceGraphic"/>
17
+ </feMerge>
18
+ </filter>
19
+ <filter id="innerShadow" x="-20%" y="-20%" width="140%" height="140%">
20
+ <feOffset dx="0" dy="4"/>
21
+ <feGaussianBlur stdDeviation="4" result="offset-blur"/>
22
+ <feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"/>
23
+ <feFlood flood-color="#ffffff" flood-opacity="0.1" result="color"/>
24
+ <feComposite operator="in" in="color" in2="inverse" result="shadow"/>
25
+ <feComposite operator="over" in="shadow" in2="SourceGraphic"/>
26
+ </filter>
27
+
28
+ <!-- Animation for pulsing glow effect -->
29
+ <style>
30
+ @keyframes pulse {
31
+ 0%, 100% { stdDeviation: 8; }
32
+ 50% { stdDeviation: 12; }
33
+ }
34
+ @keyframes shimmer {
35
+ 0%, 100% { stop-color: #ff8533; }
36
+ 50% { stop-color: #ffaa66; }
37
+ }
38
+ .pulse-blur {
39
+ animation: pulse 2s ease-in-out infinite;
40
+ }
41
+ .shimmer-stop {
42
+ animation: shimmer 3s ease-in-out infinite;
43
+ }
44
+ </style>
45
+ </defs>
46
+
47
+ <rect width="512" height="512" rx="112" fill="url(#bgGrad)"/>
48
+ <rect x="12" y="12" width="488" height="488" rx="100" fill="none" stroke="#2a2a2a" stroke-width="4"/>
49
+
50
+ <g filter="url(#glow)">
51
+ <path d="M256 64L400 400H336L296 304H216L176 400H112L256 64Z" fill="#ffffff" filter="url(#innerShadow)"/>
52
+ </g>
53
+
54
+ <path d="M256 128L288 208H224L256 128Z" fill="url(#orangeGrad)"/>
55
+ <rect x="232" y="248" width="48" height="16" rx="8" fill="url(#orangeGrad)"/>
56
+
57
+ <circle cx="256" cy="64" r="16" fill="url(#orangeGrad)"/>
58
+
59
+ <circle cx="136" cy="432" r="10" fill="#ff6b00"/>
60
+ <circle cx="184" cy="432" r="8" fill="#ffffff" opacity="0.4"/>
61
+ <circle cx="256" cy="432" r="8" fill="#ffffff" opacity="0.6"/>
62
+ <circle cx="328" cy="432" r="8" fill="#ffffff" opacity="0.4"/>
63
+ <circle cx="376" cy="432" r="10" fill="#ff6b00"/>
64
+
65
+ <path d="M120 400L112 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round"/>
66
+ <path d="M400 400L408 400" stroke="#ff6b00" stroke-width="8" stroke-linecap="round"/>
67
+ </svg>
Binary file
@@ -0,0 +1,16 @@
1
+ <svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- OpenChamber logo - simplified, no background (for dark backgrounds) -->
3
+ <g transform="translate(256, 256) scale(4)">
4
+ <!-- Left face - simplified, no grid cells -->
5
+ <path d="M0 0 L-41.568 -24 L-41.568 24 L0 48 Z" fill="white" fill-opacity="0.2" stroke="white" stroke-width="3" stroke-linejoin="round"/>
6
+ <!-- Right face - simplified, no grid cells -->
7
+ <path d="M0 0 L41.568 -24 L41.568 24 L0 48 Z" fill="white" fill-opacity="0.35" stroke="white" stroke-width="3" stroke-linejoin="round"/>
8
+ <!-- Top face - open -->
9
+ <path d="M0 -48 L-41.568 -24 L0 0 L41.568 -24 Z" fill="none" stroke="white" stroke-width="3" stroke-linejoin="round"/>
10
+ <!-- OpenCode logo on top face -->
11
+ <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, -24) scale(0.75)">
12
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M-16 -20 L16 -20 L16 20 L-16 20 Z M-8 -12 L-8 12 L8 12 L8 -12 Z" fill="white"/>
13
+ <path d="M-8 -4 L8 -4 L8 12 L-8 12 Z" fill="white" fill-opacity="0.4"/>
14
+ </g>
15
+ </g>
16
+ </svg>
Binary file
@@ -0,0 +1,16 @@
1
+ <svg width="512" height="512" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- OpenChamber logo - simplified, no background (for light backgrounds) -->
3
+ <g transform="translate(256, 256) scale(4)">
4
+ <!-- Left face - simplified, no grid cells -->
5
+ <path d="M0 0 L-41.568 -24 L-41.568 24 L0 48 Z" fill="black" fill-opacity="0.2" stroke="black" stroke-width="3" stroke-linejoin="round"/>
6
+ <!-- Right face - simplified, no grid cells -->
7
+ <path d="M0 0 L41.568 -24 L41.568 24 L0 48 Z" fill="black" fill-opacity="0.35" stroke="black" stroke-width="3" stroke-linejoin="round"/>
8
+ <!-- Top face - open -->
9
+ <path d="M0 -48 L-41.568 -24 L0 0 L41.568 -24 Z" fill="none" stroke="black" stroke-width="3" stroke-linejoin="round"/>
10
+ <!-- OpenCode logo on top face -->
11
+ <g transform="matrix(0.866, 0.5, -0.866, 0.5, 0, -24) scale(0.75)">
12
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M-16 -20 L16 -20 L16 20 L-16 20 Z M-8 -12 L-8 12 L8 12 L8 -12 Z" fill="black"/>
13
+ <path d="M-8 -4 L8 -4 L8 12 L-8 12 Z" fill="black" fill-opacity="0.4"/>
14
+ </g>
15
+ </g>
16
+ </svg>
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "OpenChamber - AI Coding Companion",
3
+ "short_name": "OpenChamber",
4
+ "description": "OpenChamber desktop companion for the OpenCode AI coding agent",
5
+ "start_url": "/",
6
+ "display": "standalone",
7
+ "background_color": "#151313",
8
+ "theme_color": "#edb449",
9
+ "orientation": "portrait-primary",
10
+ "icons": [
11
+ { "src": "/pwa-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any" },
12
+ { "src": "/pwa-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any" },
13
+ { "src": "/pwa-maskable-192.png", "sizes": "192x192", "type": "image/png", "purpose": "any maskable" },
14
+ { "src": "/pwa-maskable-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" },
15
+ { "src": "/apple-touch-icon-180x180.png", "sizes": "180x180", "type": "image/png", "purpose": "any" },
16
+ { "src": "/apple-touch-icon-152x152.png", "sizes": "152x152", "type": "image/png", "purpose": "any" },
17
+ { "src": "/favicon-32.png", "sizes": "32x32", "type": "image/png" },
18
+ { "src": "/favicon-16.png", "sizes": "16x16", "type": "image/png" }
19
+ ],
20
+ "categories": ["developer", "tools", "productivity"],
21
+ "lang": "en"
22
+ }
@@ -0,0 +1,44 @@
1
+ # Terminal Input WS Protocol
2
+
3
+ ## Goal
4
+ Reduce terminal input latency by replacing per-keystroke HTTP requests with a persistent WebSocket input channel, while keeping SSE output and HTTP endpoints as compatibility fallback.
5
+
6
+ ## Scope
7
+ - Input path: WebSocket (`/api/terminal/input-ws`)
8
+ - Output path: SSE (`/api/terminal/:sessionId/stream`)
9
+ - HTTP input fallback remains: `POST /api/terminal/:sessionId/input`
10
+
11
+ ## Framing
12
+ - Text frame: terminal keystroke payload (hot path)
13
+ - Examples: `"\r"`, `"\u001b[A"`, `"\u0003"`
14
+ - Binary frame: control envelope
15
+ - Byte 0: tag (`0x01` = JSON control)
16
+ - Bytes 1..N: UTF-8 JSON payload
17
+
18
+ ## Control Messages
19
+ - Bind active socket to terminal session:
20
+ - client -> server: `{"t":"b","s":"<sessionId>","v":1}`
21
+ - Keepalive ping:
22
+ - client -> server: `{"t":"p","v":1}`
23
+ - server -> client: `{"t":"po","v":1}`
24
+ - Server control responses:
25
+ - ready: `{"t":"ok","v":1}`
26
+ - bind ok: `{"t":"bok","v":1}`
27
+ - error: `{"t":"e","c":"<code>","f":true|false}`
28
+
29
+ ## Multiplexing Model
30
+ - Single shared socket per client runtime.
31
+ - Socket has one mutable `boundSessionId`.
32
+ - Client sends bind control when active terminal changes.
33
+ - Keystroke frames apply to currently bound session.
34
+ - Client keeps socket open and sends periodic keepalive pings so the channel stays ready for next input.
35
+ - Client primes/opens this socket when the Terminal tab is opened (not per keystroke).
36
+
37
+ ## Security
38
+ - UI auth session required when UI password is enabled.
39
+ - Origin validation enforced for cookie-authenticated browser upgrades.
40
+ - Invalid/malformed frames are rate-limited and may close socket.
41
+
42
+ ## Fallback Behavior
43
+ - On WS unavailable/error/close, client falls back to HTTP input immediately.
44
+ - Existing terminal behavior remains functional during mixed-version rollout.
@@ -0,0 +1,37 @@
1
+ import type { Express } from "express";
2
+ import type { Server } from "http";
3
+
4
+ export interface WebUiServerController {
5
+ expressApp: Express;
6
+ httpServer: Server;
7
+ getPort: () => number | null;
8
+ getOpenCodePort: () => number | null;
9
+ isReady: () => boolean;
10
+ restartOpenCode: () => Promise<void>;
11
+ stop: (options?: { exitProcess?: boolean }) => Promise<void>;
12
+ }
13
+
14
+ export interface StartWebUiServerOptions {
15
+ port?: number;
16
+ attachSignals?: boolean;
17
+ exitOnShutdown?: boolean;
18
+ uiPassword?: string | null;
19
+ }
20
+
21
+ export declare function startWebUiServer(
22
+ options?: StartWebUiServerOptions
23
+ ): Promise<WebUiServerController>;
24
+
25
+ export declare function gracefulShutdown(options?: { exitProcess?: boolean }): Promise<void>;
26
+ export declare function setupProxy(app: Express): void;
27
+ export declare function restartOpenCode(): Promise<void>;
28
+ export declare function parseArgs(argv?: string[]): {
29
+ port: number;
30
+ uiPassword: string | null;
31
+ tryCfTunnel: boolean;
32
+ tunnelProvider?: string;
33
+ tunnelMode?: string;
34
+ tunnelConfigPath?: string | null;
35
+ tunnelToken?: string;
36
+ tunnelHostname?: string;
37
+ };