dockai 0.2.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 (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -0
  3. package/dist/api.js +17 -0
  4. package/dist/api.js.map +1 -0
  5. package/dist/commands/claude.js +301 -0
  6. package/dist/commands/claude.js.map +1 -0
  7. package/dist/commands/connect.js +55 -0
  8. package/dist/commands/connect.js.map +1 -0
  9. package/dist/commands/deploy.js +98 -0
  10. package/dist/commands/deploy.js.map +1 -0
  11. package/dist/commands/login.js +103 -0
  12. package/dist/commands/login.js.map +1 -0
  13. package/dist/commands/logout.js +10 -0
  14. package/dist/commands/logout.js.map +1 -0
  15. package/dist/commands/logs.js +26 -0
  16. package/dist/commands/logs.js.map +1 -0
  17. package/dist/commands/open.js +26 -0
  18. package/dist/commands/open.js.map +1 -0
  19. package/dist/commands/paste.js +63 -0
  20. package/dist/commands/paste.js.map +1 -0
  21. package/dist/commands/projects.js +31 -0
  22. package/dist/commands/projects.js.map +1 -0
  23. package/dist/commands/proxy.js +55 -0
  24. package/dist/commands/proxy.js.map +1 -0
  25. package/dist/commands/push.js +53 -0
  26. package/dist/commands/push.js.map +1 -0
  27. package/dist/commands/sessions.js +330 -0
  28. package/dist/commands/sessions.js.map +1 -0
  29. package/dist/commands/ssh-config.js +138 -0
  30. package/dist/commands/ssh-config.js.map +1 -0
  31. package/dist/commands/start-stop.js +37 -0
  32. package/dist/commands/start-stop.js.map +1 -0
  33. package/dist/commands/status.js +38 -0
  34. package/dist/commands/status.js.map +1 -0
  35. package/dist/commands/whoami.js +27 -0
  36. package/dist/commands/whoami.js.map +1 -0
  37. package/dist/config.js +34 -0
  38. package/dist/config.js.map +1 -0
  39. package/dist/index.js +154 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/lib/client-tools.js +175 -0
  42. package/dist/lib/client-tools.js.map +1 -0
  43. package/dist/lib/pick-project.js +176 -0
  44. package/dist/lib/pick-project.js.map +1 -0
  45. package/dist/lib/remote.js +161 -0
  46. package/dist/lib/remote.js.map +1 -0
  47. package/dist/terminal.js +80 -0
  48. package/dist/terminal.js.map +1 -0
  49. package/dist/version.js +17 -0
  50. package/dist/version.js.map +1 -0
  51. package/package.json +52 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pol Amoros
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # dockai
2
+
3
+ Command-line client for [DockAI](https://github.com/polamoros/DockAI) — a self-hosted manager for Claude Code sessions running in Docker.
4
+
5
+ The CLI talks to your DockAI server over its HTTPS/WebSocket API. It is generic: anyone running a DockAI instance can use the same `dockai` binary, configured per-machine with the server URL.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g dockai
11
+ # or run ad-hoc without installing:
12
+ npx dockai <command>
13
+ ```
14
+
15
+ Requires Node.js 20+.
16
+
17
+ ## Setup
18
+
19
+ 1. Sign in to your DockAI server in a browser.
20
+ 2. Open **Settings → API Tokens → Create token**, copy the generated token.
21
+ 3. From your terminal:
22
+
23
+ ```bash
24
+ dockai login --server https://your-dockai.example.com
25
+ # paste your token when prompted
26
+ ```
27
+
28
+ The CLI stores `{ serverUrl, token }` in `~/.config/dockai/config.json`.
29
+
30
+ ## Commands
31
+
32
+ ```
33
+ dockai login [--server <url>] Authenticate against a DockAI server
34
+ dockai logout Clear the saved API token
35
+ dockai whoami Show the currently logged-in account
36
+ dockai projects List your projects
37
+ dockai start <slug> Start a project's worker container
38
+ dockai stop <slug> Stop a project's worker container
39
+ dockai connect <slug> Open an interactive terminal in the container
40
+ dockai connect <slug> --claude Drop directly into a Claude session
41
+ dockai connect <slug> --ssh Connect via raw SSH (LAN/VPN only — see note)
42
+ dockai logs <slug> [--tail N] Tail project logs
43
+ dockai open <slug> Open the Claude.ai session URL in your browser
44
+ dockai status Server status + project overview
45
+ ```
46
+
47
+ ## A note on `connect --ssh`
48
+
49
+ The default `connect` flow uses WebSockets over the same HTTPS the dashboard
50
+ serves on, so it works from anywhere your DockAI server is reachable on the
51
+ internet.
52
+
53
+ The `--ssh` variant connects directly to the worker container's SSH daemon,
54
+ which is bound to a per-project port on the DockAI host (usually 2200–2299).
55
+ Those ports are not typically exposed to the internet, so `--ssh` only works
56
+ when you're on the same LAN, a VPN, or otherwise have direct network access
57
+ to the host. For remote use, stick with the default WebSocket transport.
58
+
59
+ ## License
60
+
61
+ MIT — see the [LICENSE](https://github.com/polamoros/DockAI/blob/main/LICENSE) in the main repo.
package/dist/api.js ADDED
@@ -0,0 +1,17 @@
1
+ import { createTRPCClient, httpBatchLink } from "@trpc/client";
2
+ import superjson from "superjson";
3
+ import { getServerUrl, getToken } from "./config.js";
4
+ export function createApiClient() {
5
+ const serverUrl = getServerUrl();
6
+ const token = getToken();
7
+ return createTRPCClient({
8
+ links: [
9
+ httpBatchLink({
10
+ url: `${serverUrl}/api/trpc`,
11
+ transformer: superjson,
12
+ headers: () => (token ? { Authorization: `Bearer ${token}` } : {}),
13
+ }),
14
+ ],
15
+ });
16
+ }
17
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAerD,MAAM,UAAU,eAAe;IAC7B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IAEzB,OAAO,gBAAgB,CAAC;QACtB,KAAK,EAAE;YACL,aAAa,CAAC;gBACZ,GAAG,EAAE,GAAG,SAAS,WAAW;gBAC5B,WAAW,EAAE,SAAS;gBACtB,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACnE,CAAC;SACH;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,301 @@
1
+ import { spawn } from "node:child_process";
2
+ import { basename, resolve } from "node:path";
3
+ import { CLIENT_PORT_FILE, CLIENT_REMOTE_PORT, startClientTools } from "../lib/client-tools.js";
4
+ import { BROWSER_BRIDGE_REMOTE_DIR, apiReachable, assertRunning, dq, explainSshFailure, localBrowserBridgeSockets, resetTerminal, runRemote, sq, sshHost, workspaceDir } from "../lib/remote.js";
5
+ /**
6
+ * Open Claude Code inside a project's worker over SSH.
7
+ *
8
+ * The Claude CLI has no "run against remote host" flag — its Local/Cloud/SSH
9
+ * backends are an app-side concept — so from a terminal this is the two-step
10
+ * `ssh` + `claude`. This command is that, in one step, with the workspace
11
+ * directory already correct.
12
+ *
13
+ * It runs inside tmux by default. An SSH connection dies with the laptop lid,
14
+ * a network switch, or a train tunnel; the point of DockAI is that the work
15
+ * doesn't. Reconnecting re-attaches to the same session rather than starting
16
+ * a new conversation, and the session stays visible to the dashboard.
17
+ */
18
+ export async function claudeCommand(slug, options, extraArgs = []) {
19
+ const host = sshHost(slug);
20
+ const workspace = workspaceDir(slug);
21
+ // tmux session names can't contain dots or colons, and we mangle the rest
22
+ // to keep the remote command free of anything needing quoting.
23
+ let session = (options.session || "claude-cli").replace(/[^a-zA-Z0-9_-]/g, "-");
24
+ await assertRunning(slug);
25
+ // The host block may be missing (fresh machine) or stale (the install
26
+ // moved); either way the failure would surface as a bare ssh error.
27
+ try {
28
+ const { ensureSshConfig } = await import("./ssh-config.js");
29
+ const did = ensureSshConfig();
30
+ if (did !== "unchanged")
31
+ console.log(`SSH config ${did}.`);
32
+ }
33
+ catch { /* ssh will say what is wrong */ }
34
+ // Join the conversation the phone is on, rather than starting a second
35
+ // process beside it. A conversation registered for Remote Control lives in
36
+ // tmux `claude-<id8>`; `claude -r` in a fresh tmux session resumed the same
37
+ // transcript in a second process, so the laptop's turns never reached the
38
+ // app and both processes wrote one file (2026-09-20). With no session named
39
+ // and no flags, one live conversation is attached to outright; several, or
40
+ // `-r`/`--resume`, go through DockAI's own picker, which attaches to a live
41
+ // one and registers an idle one before attaching. Bound conversations (the
42
+ // server's own children) have no terminal to join and are said so.
43
+ if (options.tmux !== false && !options.session) {
44
+ const joined = await joinRemoteConversation(slug, extraArgs);
45
+ if (joined.session)
46
+ session = joined.session;
47
+ extraArgs = joined.extraArgs;
48
+ }
49
+ // Attaching to a running session cannot change its flags, so flags with an
50
+ // existing session would be silently ignored — the trap that reads as
51
+ // "--permission-mode didn't work". Ask, when there is someone to ask.
52
+ if (extraArgs.length && options.tmux !== false) {
53
+ const exists = (await runRemote(slug, `tmux has-session -t ${sq(session)} 2>/dev/null`)).code === 0;
54
+ if (exists && process.stdin.isTTY && process.stdout.isTTY) {
55
+ const { selectFromList } = await import("../lib/pick-project.js");
56
+ const fresh = `${session}-${Date.now().toString(36).slice(-4)}`;
57
+ const choice = await selectFromList(`Session "${session}" is already running; its flags can't be changed.`, [
58
+ { label: `Start a new session "${fresh}" with these flags`, value: "new" },
59
+ { label: `Attach to "${session}" (flags ignored)`, value: "attach" },
60
+ ]);
61
+ if (choice === null)
62
+ process.exit(0);
63
+ if (choice === "new")
64
+ return claudeCommand(slug, { ...options, session: fresh }, extraArgs);
65
+ }
66
+ }
67
+ // Claude in Chrome: forward the laptop's browser-bridge sockets into the
68
+ // worker so the session there can drive this machine's Chrome. The remote
69
+ // side must have the directory, and no stale socket from a dropped tunnel.
70
+ // The project may have decided this. A project set to use its own browser
71
+ // should not also get the laptop's Chrome forwarded — given both, the agent
72
+ // picks whichever it likes, and "use my Chrome" stops meaning anything.
73
+ // An explicit --chrome / --no-chrome on the command line still wins.
74
+ const explicit = options.chrome !== undefined
75
+ || extraArgs.some((a) => a === "--chrome" || a === "--no-chrome");
76
+ const pref = await projectPreferences(slug);
77
+ let wantsLaptopChrome = options.chrome !== false;
78
+ if (!explicit && pref.agentBrowser === "project")
79
+ wantsLaptopChrome = false;
80
+ // Commands on this machine: the flag decides when given, the project's own
81
+ // setting otherwise. Said out loud below either way.
82
+ const allowRun = options.allowRun ?? pref.clientAllowRun;
83
+ const sockets = wantsLaptopChrome ? localBrowserBridgeSockets() : [];
84
+ const forwards = sockets.flatMap((s) => ["-R", `${BROWSER_BRIDGE_REMOTE_DIR}/${basename(s)}:${s}`]);
85
+ if (sockets.length) {
86
+ await runRemote(slug, `mkdir -p ${BROWSER_BRIDGE_REMOTE_DIR} && chmod 700 ${BROWSER_BRIDGE_REMOTE_DIR} && rm -f ${BROWSER_BRIDGE_REMOTE_DIR}/*.sock`);
87
+ const saysChrome = extraArgs.some((a) => a === "--chrome" || a === "--no-chrome");
88
+ if (!saysChrome)
89
+ extraArgs = [...extraArgs, "--chrome"];
90
+ }
91
+ // Client tools: this machine's files, clipboard and (opt-in) shell, served
92
+ // on a loopback port and forwarded into the worker for its `dockai` MCP
93
+ // server. Roots default to where this command was run.
94
+ if (options.client !== false) {
95
+ const roots = (options.root?.length ? options.root : [process.cwd()]).map((r) => resolve(r));
96
+ const { port } = await startClientTools({ roots, allowRun });
97
+ // A fresh remote port per attach, named in a file the worker's MCP server
98
+ // reads on every call. A fixed port is held by whatever ssh session last
99
+ // bound it — a detached-and-reattached laptop found its forward refused
100
+ // and every client tool answering "No client attached" (2026-09-20).
101
+ const remotePort = CLIENT_REMOTE_PORT + Math.floor(Math.random() * 200);
102
+ await runRemote(slug, `echo ${remotePort} > "$HOME/${CLIENT_PORT_FILE}"`);
103
+ forwards.push("-R", `127.0.0.1:${remotePort}:127.0.0.1:${port}`);
104
+ console.log(`Client tools: ${roots.join(", ")}${allowRun ? ` (commands allowed${options.allowRun === undefined ? " by the project's setting" : ""})` : ""}`);
105
+ }
106
+ const claudeCmd = ["claude", ...extraArgs.map(sq)].join(" ");
107
+ // `-c` sets the start directory, so no `cd` is needed and the shell-command
108
+ // stays a single token for tmux to hand to /bin/sh.
109
+ const remote = options.tmux === false
110
+ ? `cd ${sq(workspace)} && exec ${claudeCmd}`
111
+ : `tmux attach-session -t ${session} 2>/dev/null || ` +
112
+ `tmux new-session -s ${session} -c ${sq(workspace)} ${dq(claudeCmd)}`;
113
+ if (options.tmux === false) {
114
+ console.log(`Connecting to ${host} (no tmux — session ends when you disconnect)...`);
115
+ }
116
+ else {
117
+ console.log(`Connecting to ${host} [${session}]...`);
118
+ // Said every time because the cost of not knowing is high: Ctrl-c twice
119
+ // exits the Claude process in the pane — the conversation's own process,
120
+ // and with it any background agents it was running (2026-09-20).
121
+ console.log("Leave with Ctrl-b d (detach) or by closing this window. Ctrl-c twice exits Claude there and stops its background work.");
122
+ }
123
+ if (sockets.length)
124
+ console.log(`Chrome: forwarding ${sockets.length} browser bridge socket(s) — Claude there can use this Chrome.`);
125
+ // -t forces a PTY: Claude is a full-screen TUI and ssh won't allocate one
126
+ // for a remote command otherwise.
127
+ const attach = () => new Promise((resolve) => {
128
+ const startedAt = Date.now();
129
+ const child = spawn("ssh", ["-t", ...forwards, host, remote], { stdio: "inherit" });
130
+ child.on("error", () => {
131
+ console.error("\nCould not run `ssh`.");
132
+ process.exit(1);
133
+ });
134
+ child.on("close", (code) => {
135
+ resetTerminal();
136
+ // ssh's own 255 after a session that was up is a dropped tunnel, not a
137
+ // config problem; -1 marks it so the caller can reconnect.
138
+ resolve(code === 255 && Date.now() - startedAt > 5000 ? -1 : code);
139
+ });
140
+ });
141
+ for (;;) {
142
+ const code = await attach();
143
+ if (code === 255) {
144
+ explainSshFailure(slug);
145
+ process.exit(1);
146
+ }
147
+ if (code !== -1 || options.tmux === false)
148
+ return;
149
+ // The tmux session is still there; only the tunnel went. Typical cause is
150
+ // a DockAI deploy, which takes half a minute — wait it out and reattach
151
+ // rather than dumping the user at a shell mid-conversation.
152
+ console.log("\nConnection to DockAI lost — reconnecting (Ctrl-C to stop)...");
153
+ const deadline = Date.now() + 3 * 60 * 1000;
154
+ let back = false;
155
+ while (Date.now() < deadline) {
156
+ await new Promise((r) => setTimeout(r, 3000));
157
+ if (await apiReachable()) {
158
+ back = true;
159
+ break;
160
+ }
161
+ }
162
+ if (!back) {
163
+ console.error("DockAI did not come back within 3 minutes. Re-run this command to reattach.");
164
+ process.exit(1);
165
+ }
166
+ await assertRunning(slug);
167
+ console.log(`Reattaching to ${host} [${session}]...`);
168
+ }
169
+ }
170
+ const wantsResume = (args) => args.findIndex((a) => a === "-r" || a === "--resume" || a.startsWith("--resume="));
171
+ /** `-r`, `--resume`, `--resume <ref>` and `--resume=<ref>` out of the args, with the ref they named. */
172
+ function takeResume(args) {
173
+ const i = wantsResume(args);
174
+ if (i === -1)
175
+ return { rest: args, ref: null };
176
+ const flag = args[i];
177
+ if (flag.startsWith("--resume="))
178
+ return { rest: [...args.slice(0, i), ...args.slice(i + 1)], ref: flag.slice(9) || null };
179
+ const next = args[i + 1];
180
+ const hasRef = flag === "--resume" && next !== undefined && !next.startsWith("-");
181
+ return { rest: [...args.slice(0, i), ...args.slice(i + (hasRef ? 2 : 1))], ref: hasRef ? next : null };
182
+ }
183
+ /**
184
+ * Decide which tmux session to attach to, from the project's conversations.
185
+ *
186
+ * Returns the session to attach to (null: start a new one) and the claude
187
+ * args left after DockAI has consumed `-r`/`--resume`.
188
+ */
189
+ async function joinRemoteConversation(slug, extraArgs) {
190
+ const { rest, ref } = takeResume(extraArgs);
191
+ const picking = rest.length !== extraArgs.length;
192
+ let sessions;
193
+ try {
194
+ const { createApiClient } = await import("../api.js");
195
+ ({ sessions } = (await createApiClient().project.claudeSessions.query({ slug })));
196
+ }
197
+ catch {
198
+ // An older orchestrator or no network: behave as before, flags intact.
199
+ return { session: null, extraArgs };
200
+ }
201
+ const listed = sessions.filter((s) => s.messageCount > 0 || s.remoteState !== null);
202
+ const live = listed.filter((s) => s.remoteState === "interactive");
203
+ const paneOf = (s) => `claude-${s.id.slice(0, 8)}`;
204
+ const tty = process.stdin.isTTY && process.stdout.isTTY;
205
+ // A new conversation is made by the worker, as an interactive Remote
206
+ // Control session in its own pane, so it is on the phone from birth and the
207
+ // dashboard's card lists it the same way. Flags after the project name mean
208
+ // the person wants a plain `claude` with those flags instead.
209
+ const startNew = async () => {
210
+ try {
211
+ const { createApiClient } = await import("../api.js");
212
+ const started = (await createApiClient().project.remoteSessionNew.mutate({ slug }));
213
+ if (started.tmux) {
214
+ console.log("Started a new conversation, registered for Remote Control — it will appear on your phone in a few seconds.");
215
+ return { session: started.tmux, extraArgs: [] };
216
+ }
217
+ }
218
+ catch (err) {
219
+ console.log(`Could not start it through DockAI (${err instanceof Error ? err.message : String(err)}); starting claude directly.`);
220
+ }
221
+ return { session: null, extraArgs: rest };
222
+ };
223
+ if (!picking) {
224
+ if (extraArgs.length)
225
+ return { session: null, extraArgs };
226
+ if (live.length === 1) {
227
+ console.log(`Joining "${live[0].title || live[0].firstPrompt}" — the conversation on your phone.`);
228
+ return { session: paneOf(live[0]), extraArgs };
229
+ }
230
+ if (live.length === 0)
231
+ return startNew();
232
+ if (!tty)
233
+ return { session: null, extraArgs };
234
+ }
235
+ if (!tty) {
236
+ console.error("`-r` needs a terminal to pick from; name the conversation with --resume <id|title>.");
237
+ process.exit(1);
238
+ }
239
+ const { selectFromList } = await import("../lib/pick-project.js");
240
+ let chosen;
241
+ if (ref) {
242
+ const wanted = ref.toLowerCase();
243
+ const matches = listed.filter((s) => s.id.startsWith(wanted) || (s.title || "").toLowerCase() === wanted);
244
+ if (matches.length !== 1) {
245
+ console.error(matches.length === 0 ? `No conversation matches "${ref}".` : `"${ref}" matches ${matches.length} conversations; use the id.`);
246
+ process.exit(1);
247
+ }
248
+ chosen = matches[0];
249
+ }
250
+ else {
251
+ const state = (s) => s.remoteState === "interactive" ? "live · on your phone"
252
+ : s.remoteState === "server" ? "live · served by the server (phone only)"
253
+ : s.origin === "phone" ? "from phone, idle"
254
+ : s.remoteId ? "idle" : "local only";
255
+ const sorted = [...listed].sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
256
+ chosen = await selectFromList("Which conversation?", [
257
+ ...sorted.map((s) => ({ label: `${(s.title || s.firstPrompt || s.id.slice(0, 8)).slice(0, 60)} — ${state(s)}`, value: s })),
258
+ { label: "Start a new conversation", value: "new" },
259
+ ]);
260
+ if (chosen === null)
261
+ process.exit(0);
262
+ }
263
+ if (chosen === "new")
264
+ return startNew();
265
+ if (chosen.remoteState === "interactive")
266
+ return { session: paneOf(chosen), extraArgs: rest };
267
+ if (chosen.remoteState === "server") {
268
+ console.error("That conversation is served by the project's Remote Control server: it has no terminal to join. Use it from the Claude app, or hand it off to a terminal one from the dashboard.");
269
+ process.exit(1);
270
+ }
271
+ // Idle: register it for Remote Control first, so the phone sees the same
272
+ // thread, then attach to the pane that registration creates.
273
+ console.log(`Resuming "${chosen.title || chosen.firstPrompt}" with Remote Control…`);
274
+ const { createApiClient } = await import("../api.js");
275
+ const started = (await createApiClient().project.remoteSessionStart.mutate({ slug, sessionId: chosen.id }));
276
+ if (!started.tmux) {
277
+ console.error("It was re-queued onto the Remote Control server instead of a terminal; use it from the Claude app.");
278
+ process.exit(1);
279
+ }
280
+ if (rest.length)
281
+ console.log(`Note: ${rest.join(" ")} cannot apply to a conversation that is already running.`);
282
+ return { session: started.tmux, extraArgs: [] };
283
+ }
284
+ /**
285
+ * Which browser this project wants a session to use, and whether commands on
286
+ * the attached laptop are allowed by default.
287
+ *
288
+ * Best-effort: an older orchestrator, or no network, simply means the
289
+ * command behaves as it always did rather than failing.
290
+ */
291
+ async function projectPreferences(slug) {
292
+ try {
293
+ const { createApiClient } = await import("../api.js");
294
+ const project = await createApiClient().project.getBySlug.query({ slug });
295
+ return { agentBrowser: project.agentBrowser ?? null, clientAllowRun: Boolean(project.clientAllowRun) };
296
+ }
297
+ catch {
298
+ return { agentBrowser: null, clientAllowRun: false };
299
+ }
300
+ }
301
+ //# sourceMappingURL=claude.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/commands/claude.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { spawn } from "node:child_process";
2
+ import { connectTerminal } from "../terminal.js";
3
+ import { createApiClient } from "../api.js";
4
+ import { getServerUrl } from "../config.js";
5
+ export async function connectCommand(slug, options) {
6
+ if (options.ssh) {
7
+ console.log(`Connecting to ${slug} via SSH...`);
8
+ try {
9
+ await connectSSH(slug);
10
+ }
11
+ catch (err) {
12
+ const msg = err instanceof Error ? err.message : String(err);
13
+ console.error(`SSH connection failed: ${msg}`);
14
+ process.exit(1);
15
+ }
16
+ }
17
+ else {
18
+ console.log(`Connecting to ${slug}...`);
19
+ try {
20
+ await connectTerminal(slug, { claude: options.claude });
21
+ }
22
+ catch (err) {
23
+ const msg = err instanceof Error ? err.message : String(err);
24
+ console.error(`Connection failed: ${msg}`);
25
+ process.exit(1);
26
+ }
27
+ }
28
+ }
29
+ async function connectSSH(slug) {
30
+ const api = createApiClient();
31
+ const info = (await api.project.sshInfo.query({ slug }));
32
+ if (!info) {
33
+ console.error("No SSH info available. Is the project running?");
34
+ process.exit(1);
35
+ }
36
+ // Resolve host from server URL if SSH_HOST not set
37
+ const serverUrl = getServerUrl();
38
+ const host = info.host === "localhost" ? new URL(serverUrl).hostname : info.host;
39
+ console.log(`ssh -p ${info.port} ${info.user}@${host}`);
40
+ const child = spawn("ssh", ["-p", String(info.port), `${info.user}@${host}`], {
41
+ stdio: "inherit",
42
+ });
43
+ return new Promise((resolve, reject) => {
44
+ child.on("close", (code) => {
45
+ if (code !== 0 && code !== null) {
46
+ reject(new Error(`SSH exited with code ${code}`));
47
+ }
48
+ else {
49
+ resolve();
50
+ }
51
+ });
52
+ child.on("error", reject);
53
+ });
54
+ }
55
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import { createApiClient } from "../api.js";
2
+ import { getToken } from "../config.js";
3
+ /**
4
+ * Redeploy DockAI on its host, through DockAI itself.
5
+ *
6
+ * The orchestrator runs the deploy in a helper container with the Docker
7
+ * socket, so nothing here needs ssh or host credentials — which is what
8
+ * makes it safe to run from inside a DockAI project too. The dashboard and
9
+ * this command lose the server for about a minute while it restarts; the
10
+ * wait loop tolerates that.
11
+ */
12
+ export async function deployCommand(name, options) {
13
+ if (name)
14
+ return deployTarget(name, options);
15
+ if (!getToken()) {
16
+ console.error("Not logged in. Run `dockai login` first.");
17
+ process.exit(1);
18
+ }
19
+ const api = createApiClient();
20
+ try {
21
+ await api.admin.system.deploy.mutate();
22
+ }
23
+ catch (err) {
24
+ console.error(err instanceof Error ? err.message : String(err));
25
+ process.exit(1);
26
+ }
27
+ console.log("Deploy started.");
28
+ if (options.wait === false)
29
+ return;
30
+ let printed = 0;
31
+ let outages = 0;
32
+ for (let i = 0; i < 120; i++) {
33
+ await new Promise((r) => setTimeout(r, 3000));
34
+ let s;
35
+ try {
36
+ s = await api.admin.system.deployStatus.query();
37
+ outages = 0;
38
+ }
39
+ catch {
40
+ // The orchestrator is being recreated. Expected once per deploy.
41
+ if (outages++ === 0)
42
+ console.log(" (orchestrator restarting…)");
43
+ continue;
44
+ }
45
+ if (s.log.length > printed) {
46
+ process.stdout.write(s.log.slice(printed));
47
+ printed = s.log.length;
48
+ }
49
+ if (s.state === "done") {
50
+ console.log("\nDeploy complete.");
51
+ return;
52
+ }
53
+ if (s.state === "failed") {
54
+ console.error(`\nDeploy failed (exit ${s.exitCode}).`);
55
+ process.exit(1);
56
+ }
57
+ }
58
+ console.error("Timed out waiting for the deploy to finish.");
59
+ process.exit(1);
60
+ }
61
+ /** Redeploy a named target (Admin → System → Deploy targets). */
62
+ async function deployTarget(name, options) {
63
+ if (!getToken()) {
64
+ console.error("Not logged in. Run `dockai login` first.");
65
+ process.exit(1);
66
+ }
67
+ const api = createApiClient();
68
+ try {
69
+ await api.admin.deployTargets.run.mutate({ name });
70
+ }
71
+ catch (err) {
72
+ console.error(err instanceof Error ? err.message : String(err));
73
+ process.exit(1);
74
+ }
75
+ console.log(`Deploy of ${name} started.`);
76
+ if (options.wait === false)
77
+ return;
78
+ let printed = 0;
79
+ for (let i = 0; i < 200; i++) {
80
+ await new Promise((r) => setTimeout(r, 3000));
81
+ const s = await api.admin.deployTargets.status.query({ name });
82
+ if (s.log.length > printed) {
83
+ process.stdout.write(s.log.slice(printed));
84
+ printed = s.log.length;
85
+ }
86
+ if (s.state === "done") {
87
+ console.log(`\n${name} deployed.`);
88
+ return;
89
+ }
90
+ if (s.state === "failed") {
91
+ console.error(`\nDeploy of ${name} failed (exit ${s.exitCode}).`);
92
+ process.exit(1);
93
+ }
94
+ }
95
+ console.error("Timed out waiting for the deploy to finish.");
96
+ process.exit(1);
97
+ }
98
+ //# sourceMappingURL=deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}