@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
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # @algochad/archcoder
2
+
3
+ Run [OpenCode](https://opencode.ai) in your browser. Install the CLI, open `localhost:3000`, done. Works on desktop browsers, tablets, and phones as a PWA.
4
+
5
+ [![Discord](https://img.shields.io/badge/Discord-join.svg?style=flat&labelColor=100F0F&color=8B7EC8&logo=discord&logoColor=FFFCF0)](https://discord.gg/uPECDcCQN)
6
+
7
+ ArchCoder is a private project. For support, contact [@algochad](https://x.com/algochad).
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ # Quick install
13
+ curl -fsSL https://archcoder.archlast.com/cli | bash
14
+
15
+ # Or using your preferred package manager
16
+ npm install -g @algochad/archcoder
17
+ pnpm add -g @algochad/archcoder
18
+ bun add -g @algochad/archcoder
19
+ yarn global add @algochad/archcoder
20
+ ```
21
+
22
+ > **Prerequisites:** [OpenCode CLI](https://opencode.ai) installed, Node.js 20+.
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ archcoder # Start on port 3000
28
+ archcoder --port 8080 # Custom port
29
+ archcoder --ui-password secret # Password-protect UI
30
+ archcoder tunnel help # Tunnel lifecycle commands
31
+ archcoder tunnel providers # Show provider capabilities
32
+ archcoder tunnel profile add --provider cloudflare --mode managed-remote --name prod-main --hostname app.example.com --token <token>
33
+ archcoder tunnel start --profile prod-main
34
+ archcoder tunnel start --provider cloudflare --mode quick --qr
35
+ archcoder tunnel start --provider cloudflare --mode managed-local --config ~/.cloudflared/config.yml
36
+ archcoder tunnel status --all # Show tunnel state across instances
37
+ archcoder tunnel stop --port 3000 # Stop tunnel only (server stays running)
38
+ archcoder logs # Follow latest instance logs
39
+ OPENCODE_PORT=4096 OPENCODE_SKIP_START=true archcoder # Connect to external OpenCode server
40
+ OPENCODE_HOST=https://myhost:4096 OPENCODE_SKIP_START=true archcoder # Connect via custom host/HTTPS
41
+ archcoder stop # Stop server
42
+ archcoder update # Update to latest version
43
+ ```
44
+
45
+ ### Tunnel behavior notes
46
+
47
+ - One active tunnel per running ArchCoder instance (port).
48
+ - Starting a different tunnel mode/provider on the same instance replaces the active tunnel.
49
+ - Replacing or stopping a tunnel revokes existing connect links and invalidates remote tunnel sessions.
50
+ - Connect links are one-time tokens; generating a new link revokes the previous unused link.
51
+
52
+ **Optional env vars:**
53
+ ```yaml
54
+ environment:
55
+ UI_PASSWORD: your_secure_password
56
+ ARCHCODER_TUNNEL_MODE: quick # quick | managed-remote | managed-local
57
+ ARCHCODER_TUNNEL_PROVIDER: cloudflare
58
+ ```
59
+
60
+ For `managed-remote` mode, also set:
61
+
62
+ ```yaml
63
+ environment:
64
+ ARCHCODER_TUNNEL_MODE: managed-remote
65
+ ARCHCODER_TUNNEL_HOSTNAME: app.example.com
66
+ ARCHCODER_TUNNEL_TOKEN: <token>
67
+ ```
68
+
69
+ For `managed-local` mode, you can set:
70
+
71
+ ```yaml
72
+ environment:
73
+ ARCHCODER_TUNNEL_MODE: managed-local
74
+ ARCHCODER_TUNNEL_CONFIG: /home/archcoder/.cloudflared/config.yml
75
+ ```
76
+
77
+ Managed-local path note: `ARCHCODER_TUNNEL_CONFIG` must use a container path under `/home/archcoder/...`. If the config file references `credentials-file`, ensure that JSON path is also mounted and reachable inside the container.
78
+
79
+ **Data directory:** mount `data/` for persistent storage. Ensure permissions:
80
+ ```bash
81
+ mkdir -p data/archcoder data/opencode/share data/opencode/config data/ssh
82
+ chown -R 1000:1000 data/
83
+ ```
84
+
85
+ </details>
86
+
87
+ <details>
88
+ <summary>Background & daemon mode</summary>
89
+
90
+ ```bash
91
+ archcoder # Runs in background by default
92
+ archcoder stop # Stop background server
93
+ ```
94
+
95
+ </details>
96
+
97
+ ## What makes the web version special
98
+
99
+ - **Remote access** - Cloudflare tunnel with QR onboarding. Scan from your phone, start coding.
100
+ - **Mobile-first PWA** - optimized chat controls, keyboard-safe layouts, drag-to-reorder projects
101
+ - **Background notifications** - know when your agent finishes, even from another tab
102
+ - **Self-update** - update and restart from the UI, server settings stay intact
103
+ - **Cross-tab tracking** - session activity stays in sync across browser tabs
104
+
105
+ - Cloudflare tunnel access with quick, managed-remote, and managed-local modes
106
+ - One-scan onboarding with tunnel QR + password URL helpers
107
+ - Mobile-first experience: optimized chat controls, keyboard-safe layouts, and attachment-friendly UI
108
+ - Background notifications plus reliable cross-tab session activity tracking
109
+ - Built-in self-update + restart flow that keeps your server settings intact
110
+
111
+ ## License
112
+
113
+ MIT
@@ -0,0 +1,55 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath, pathToFileURL } from 'url';
4
+
5
+ function normalizeCliEntryPath(filePath, realpath = fs.realpathSync) {
6
+ if (typeof filePath !== 'string' || filePath.trim().length === 0) {
7
+ return null;
8
+ }
9
+
10
+ const resolvedPath = path.resolve(filePath);
11
+ try {
12
+ return realpath(resolvedPath);
13
+ } catch {
14
+ return resolvedPath;
15
+ }
16
+ }
17
+
18
+ function isModuleCliExecution(
19
+ entryPath = process.argv[1],
20
+ moduleUrl,
21
+ realpath = fs.realpathSync,
22
+ expectedBinName,
23
+ ) {
24
+ if (typeof entryPath !== 'string' || entryPath.trim().length === 0) {
25
+ return false;
26
+ }
27
+ if (typeof moduleUrl !== 'string' || moduleUrl.trim().length === 0) {
28
+ return false;
29
+ }
30
+
31
+ try {
32
+ const normalizedEntryPath = normalizeCliEntryPath(entryPath, realpath);
33
+ const normalizedModulePath = normalizeCliEntryPath(fileURLToPath(moduleUrl), realpath);
34
+ if (!normalizedEntryPath || !normalizedModulePath) {
35
+ return false;
36
+ }
37
+ if (pathToFileURL(normalizedEntryPath).href === pathToFileURL(normalizedModulePath).href) {
38
+ return true;
39
+ }
40
+
41
+ if (typeof expectedBinName === 'string' && expectedBinName.trim().length > 0) {
42
+ const parsedEntryName = path.parse(normalizedEntryPath).name.toLowerCase();
43
+ return parsedEntryName === expectedBinName.trim().toLowerCase();
44
+ }
45
+
46
+ return false;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ export {
53
+ normalizeCliEntryPath,
54
+ isModuleCliExecution,
55
+ };
@@ -0,0 +1,145 @@
1
+ /**
2
+ * CLI output formatting adapter.
3
+ *
4
+ * Wraps @clack/prompts for structured, beautiful terminal output.
5
+ * Custom formatters (icons, redaction) live here to isolate the
6
+ * formatting dependency from the rest of the CLI.
7
+ */
8
+
9
+ import {
10
+ intro,
11
+ outro,
12
+ log,
13
+ note,
14
+ box,
15
+ progress,
16
+ spinner,
17
+ confirm,
18
+ select,
19
+ text,
20
+ password,
21
+ cancel,
22
+ isCancel,
23
+ } from '@clack/prompts';
24
+
25
+ // ── Provider icons ──────────────────────────────────────────────
26
+
27
+ const TUNNEL_PROVIDER_ICON = {
28
+ cloudflare: '☁',
29
+ };
30
+
31
+ function formatProviderWithIcon(provider) {
32
+ if (typeof provider !== 'string' || provider.trim().length === 0) {
33
+ return 'unknown';
34
+ }
35
+ const normalized = provider.trim().toLowerCase();
36
+ const icon = TUNNEL_PROVIDER_ICON[normalized];
37
+ return icon ? `${icon} ${normalized}` : normalized;
38
+ }
39
+
40
+ // ── Status-aware log dispatch ───────────────────────────────────
41
+
42
+ /**
43
+ * Print a status-tagged message using clack log primitives.
44
+ *
45
+ * @param {'success'|'warning'|'error'|'info'|'neutral'} status
46
+ * @param {string} message Primary line
47
+ * @param {string} [detail] Optional dim secondary line appended after newline
48
+ */
49
+ function logStatus(status, message, detail) {
50
+ const full = detail ? `${message}\n${detail}` : message;
51
+ switch (status) {
52
+ case 'success':
53
+ log.success(full);
54
+ break;
55
+ case 'warning':
56
+ log.warn(full);
57
+ break;
58
+ case 'error':
59
+ log.error(full);
60
+ break;
61
+ case 'info':
62
+ case 'neutral':
63
+ default:
64
+ log.info(full);
65
+ break;
66
+ }
67
+ }
68
+
69
+ // ── TTY detection ───────────────────────────────────────────────
70
+
71
+ /**
72
+ * Whether both stdout and stdin are interactive TTYs.
73
+ * Prompts must be disabled when stdin is piped (e.g. --token-stdin).
74
+ */
75
+ const isTTY = Boolean(process.stdout?.isTTY) && Boolean(process.stdin?.isTTY);
76
+
77
+ function isJsonMode(options) {
78
+ return Boolean(options?.json);
79
+ }
80
+
81
+ function isQuietMode(options) {
82
+ return Boolean(options?.quiet);
83
+ }
84
+
85
+ function shouldRenderHumanOutput(options) {
86
+ return !isJsonMode(options) && !isQuietMode(options);
87
+ }
88
+
89
+ function canPrompt(options) {
90
+ return shouldRenderHumanOutput(options) && isTTY;
91
+ }
92
+
93
+ function createSpinner(options) {
94
+ return canPrompt(options) ? spinner() : null;
95
+ }
96
+
97
+ async function createProgress(options, config) {
98
+ return canPrompt(options) ? progress(config) : null;
99
+ }
100
+
101
+ function printJson(payload) {
102
+ const base = payload && typeof payload === 'object' && !Array.isArray(payload)
103
+ ? { ...payload }
104
+ : { data: payload };
105
+
106
+ const messages = Array.isArray(base.messages) ? base.messages : undefined;
107
+ const hasWarning = Boolean(messages?.some((entry) => entry?.level === 'warning'));
108
+ const hasError = Boolean(messages?.some((entry) => entry?.level === 'error'));
109
+ const normalizedStatus = base.status === 'ok' || base.status === 'warning' || base.status === 'error'
110
+ ? base.status
111
+ : (hasError ? 'error' : (hasWarning ? 'warning' : 'ok'));
112
+
113
+ const output = {
114
+ status: normalizedStatus,
115
+ ...base,
116
+ };
117
+
118
+ process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
119
+ }
120
+
121
+ export {
122
+ intro,
123
+ outro,
124
+ log,
125
+ note,
126
+ box,
127
+ progress,
128
+ spinner,
129
+ confirm,
130
+ select,
131
+ text,
132
+ password,
133
+ cancel,
134
+ isCancel,
135
+ isTTY,
136
+ isJsonMode,
137
+ isQuietMode,
138
+ shouldRenderHumanOutput,
139
+ canPrompt,
140
+ createSpinner,
141
+ createProgress,
142
+ printJson,
143
+ formatProviderWithIcon,
144
+ logStatus,
145
+ };