boxdown 1.0.0 → 1.2.0

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 (69) hide show
  1. package/README.md +135 -12
  2. package/assets/devcontainer/README.md +49 -17
  3. package/assets/devcontainer/devcontainer.json +24 -7
  4. package/assets/devcontainer/hooks/initialize.sh +32 -0
  5. package/assets/devcontainer/hooks/post-create.sh +59 -12
  6. package/assets/devcontainer/hooks/post-start.sh +21 -4
  7. package/assets/devcontainer/ssh-config-install.sh +12 -3
  8. package/assets/devcontainer/start.sh +721 -44
  9. package/assets/devcontainer/utils/coding-agent-cli-update.sh +267 -3
  10. package/assets/devcontainer/utils/deps-install.sh +68 -0
  11. package/assets/devcontainer/utils/git-config-bootstrap.sh +128 -0
  12. package/assets/devcontainer/utils/git-signing-bootstrap.sh +56 -0
  13. package/assets/devcontainer/utils/python-bootstrap.sh +69 -0
  14. package/assets/devcontainer/utils/ssh-bootstrap.sh +9 -0
  15. package/dist/bin/cli.cjs +1 -1
  16. package/dist/bin/cli.mjs +1 -1
  17. package/dist/main-Df4E8ARj.cjs +5498 -0
  18. package/dist/main-XMBsKjIK.mjs +5458 -0
  19. package/dist/main-XMBsKjIK.mjs.map +1 -0
  20. package/dist/main.cjs +5 -2
  21. package/dist/main.d.cts +495 -4
  22. package/dist/main.d.cts.map +1 -1
  23. package/dist/main.d.mts +495 -4
  24. package/dist/main.d.mts.map +1 -1
  25. package/dist/main.mjs +2 -2
  26. package/docs/README.md +1 -0
  27. package/docs/architecture.md +32 -0
  28. package/docs/development.md +13 -6
  29. package/docs/features/README.md +2 -0
  30. package/docs/features/commit-signing.md +23 -0
  31. package/docs/features/generated-config-and-state.md +57 -4
  32. package/docs/features/github-auth-refresh.md +12 -0
  33. package/docs/features/lifecycle.md +97 -11
  34. package/docs/features/setup.md +66 -0
  35. package/docs/features/ssh-config-and-proxy.md +228 -7
  36. package/docs/features/start-and-shell.md +40 -5
  37. package/docs/superpowers/plans/2026-07-11-default-commit-signing.md +110 -0
  38. package/docs/superpowers/plans/2026-07-11-progress-ci-regression.md +125 -0
  39. package/docs/superpowers/specs/2026-07-11-default-commit-signing-design.md +396 -0
  40. package/docs/superpowers/specs/2026-07-11-progress-ci-regression-design.md +43 -0
  41. package/docs/testing.md +35 -2
  42. package/docs/todo.md +2 -2
  43. package/package.json +1 -1
  44. package/src/claude-app-config.ts +304 -0
  45. package/src/cli-style.ts +43 -0
  46. package/src/codex-app-config.ts +656 -0
  47. package/src/config.ts +68 -10
  48. package/src/constants.ts +5 -0
  49. package/src/devcontainer.ts +500 -64
  50. package/src/doctor.ts +195 -30
  51. package/src/git-signing.ts +87 -0
  52. package/src/interactive-prompts.ts +692 -0
  53. package/src/list.ts +16 -2
  54. package/src/logging.ts +154 -0
  55. package/src/main.ts +1213 -63
  56. package/src/metadata.ts +52 -3
  57. package/src/package-info.ts +18 -0
  58. package/src/paths.ts +50 -10
  59. package/src/process.ts +80 -2
  60. package/src/progress.ts +593 -0
  61. package/src/purge.ts +208 -0
  62. package/src/shell.ts +19 -0
  63. package/src/ssh-config.ts +134 -16
  64. package/src/ssh-install-targets.ts +111 -0
  65. package/src/ssh-key.ts +53 -13
  66. package/src/status.ts +5 -0
  67. package/dist/main-BuEptwlL.cjs +0 -1707
  68. package/dist/main-ZFTrSVgt.mjs +0 -1685
  69. package/dist/main-ZFTrSVgt.mjs.map +0 -1
package/dist/main.d.mts CHANGED
@@ -4,21 +4,512 @@ type CodingAgentCommandAlias = CodingAgentCli | 'cc';
4
4
  declare function codingAgentFromCommand(command: string): CodingAgentCli | undefined;
5
5
  declare function codingAgentBinary(agent: CodingAgentCli): string;
6
6
  //#endregion
7
+ //#region src/paths.d.ts
8
+ interface WorkspaceContextOptions {
9
+ workspace?: string;
10
+ cwd?: string;
11
+ env?: NodeJS.ProcessEnv;
12
+ packageRoot?: string;
13
+ assetsDevcontainerDir?: string;
14
+ }
15
+ interface WorkspaceContext {
16
+ workspaceFolder: string;
17
+ workspaceBasename: string;
18
+ workspaceId: string;
19
+ packageRoot: string;
20
+ assetsDevcontainerDir: string;
21
+ cacheRoot: string;
22
+ dataRoot: string;
23
+ workspaceCacheDir: string;
24
+ workspaceDataDir: string;
25
+ generatedConfigPath: string;
26
+ hostAgentsDir: string;
27
+ hostCodexAuthPath: string;
28
+ hostGitconfigPath: string;
29
+ hostGitconfigSnapshotDir: string;
30
+ hostGitconfigSnapshotPath: string;
31
+ gitSigningStateDir: string;
32
+ gitSigningPublicKeyPath: string;
33
+ sshKeyDir: string;
34
+ sshKeyPath: string;
35
+ sshPublicKeyPath: string;
36
+ sshPublicKeyRuntimeDir: string;
37
+ sshPublicKeyRuntimePath: string;
38
+ workspaceLogPath: string;
39
+ }
40
+ interface WorkspaceContextIdentity {
41
+ workspaceFolder: string;
42
+ workspaceBasename: string;
43
+ workspaceId: string;
44
+ }
45
+ declare function packageRootFromImportMeta(importMetaUrl?: string): string;
46
+ declare function workspaceIdFor(workspaceFolder: string): string;
47
+ declare function resolveWorkspaceFolder(workspace: string | undefined, cwd?: string): string;
48
+ declare function defaultCacheRoot(env?: NodeJS.ProcessEnv): string;
49
+ declare function defaultDataRoot(env?: NodeJS.ProcessEnv): string;
50
+ declare function defaultHostAgentsDir(env?: NodeJS.ProcessEnv): string;
51
+ declare function defaultHostCodexAuthPath(env?: NodeJS.ProcessEnv): string;
52
+ declare function defaultHostGitconfigPath(env?: NodeJS.ProcessEnv): string;
53
+ declare function createWorkspaceContextFromIdentity(identity: WorkspaceContextIdentity, options?: Omit<WorkspaceContextOptions, 'workspace' | 'cwd'>): WorkspaceContext;
54
+ declare function createWorkspaceContext(options?: WorkspaceContextOptions): WorkspaceContext;
55
+ //#endregion
56
+ //#region src/doctor.d.ts
57
+ type DoctorLevel = 'ok' | 'fail' | 'warn';
58
+ interface DoctorCheck {
59
+ name: string;
60
+ level: DoctorLevel;
61
+ message: string;
62
+ }
63
+ interface DoctorCommandResult {
64
+ code: number;
65
+ stdout: string;
66
+ stderr: string;
67
+ }
68
+ type DoctorCommandRunner = (command: string, args: string[]) => Promise<DoctorCommandResult>;
69
+ interface RunDoctorChecksOptions {
70
+ includeOptional?: boolean;
71
+ includeDockerMountProbe?: boolean;
72
+ runCommand?: DoctorCommandRunner;
73
+ }
74
+ declare function runDoctorChecks(context: WorkspaceContext, options?: RunDoctorChecksOptions): Promise<DoctorCheck[]>;
75
+ declare function doctorHasFailures(checks: DoctorCheck[]): boolean;
76
+ declare function formatDoctorText(checks: DoctorCheck[]): string;
77
+ //#endregion
78
+ //#region src/logging.d.ts
79
+ type LogStreamName = 'stdout' | 'stderr' | 'boxdown';
80
+ interface WorkspaceCommandLoggerOptions {
81
+ redactions?: string[];
82
+ now?: () => Date;
83
+ }
84
+ interface LoggedCommand {
85
+ stream: (stream: Extract<LogStreamName, 'stdout' | 'stderr'>, chunk: Buffer | string) => void;
86
+ error: (error: unknown) => void;
87
+ finish: (code: number) => void;
88
+ }
89
+ declare class WorkspaceCommandLogger {
90
+ #private;
91
+ readonly logPath: string;
92
+ readonly workspaceFolder: string;
93
+ constructor(context: Pick<WorkspaceContext, 'workspaceFolder' | 'workspaceLogPath'>, options?: WorkspaceCommandLoggerOptions);
94
+ addRedaction(value: string): void;
95
+ disable(): void;
96
+ section(title: string, details?: Record<string, string | number | boolean | undefined>): void;
97
+ boxdown(chunk: Buffer | string): void;
98
+ startCommand(command: string, args: string[], options?: {
99
+ cwd?: string;
100
+ }): LoggedCommand;
101
+ }
102
+ declare function createWorkspaceCommandLogger(context: Pick<WorkspaceContext, 'workspaceFolder' | 'workspaceLogPath'>, options?: WorkspaceCommandLoggerOptions): WorkspaceCommandLogger;
103
+ declare function withLoggedProcessOutput<T>(logger: WorkspaceCommandLogger, action: () => Promise<T>): Promise<T>;
104
+ //#endregion
105
+ //#region src/process.d.ts
106
+ interface BufferedCommandOptions {
107
+ cwd?: string;
108
+ env?: NodeJS.ProcessEnv;
109
+ input?: string;
110
+ mirrorStdout?: 'stdout' | 'stderr' | false;
111
+ mirrorStderr?: 'stdout' | 'stderr' | false;
112
+ logger?: WorkspaceCommandLogger;
113
+ onStdout?: (chunk: Buffer) => void;
114
+ onStderr?: (chunk: Buffer) => void;
115
+ }
116
+ interface CommandResult {
117
+ code: number;
118
+ stdout: string;
119
+ stderr: string;
120
+ }
121
+ declare function buildHostToolPath(env?: NodeJS.ProcessEnv): string;
122
+ declare function runBuffered(command: string, args: string[], options?: BufferedCommandOptions): Promise<CommandResult>;
123
+ declare function runInteractive(command: string, args: string[], options?: Pick<BufferedCommandOptions, 'cwd' | 'env' | 'logger'>): Promise<number>;
124
+ //#endregion
125
+ //#region src/progress.d.ts
126
+ type ProgressOutputTarget = 'stdout' | 'stderr';
127
+ type ProgressMode = 'interactive' | 'verbose' | 'none';
128
+ type ProgressWriter = (target: ProgressOutputTarget, message: string) => void;
129
+ type ProgressRawWriter = (target: ProgressOutputTarget, message: string) => void;
130
+ interface ProgressReporterOptions {
131
+ mode?: ProgressMode;
132
+ verbose?: boolean;
133
+ target?: ProgressOutputTarget;
134
+ write?: ProgressWriter;
135
+ writeRaw?: ProgressRawWriter;
136
+ isTTY?: boolean;
137
+ spinnerFrames?: readonly string[];
138
+ spinnerIntervalMs?: number;
139
+ }
140
+ interface ProgressCommandOptions extends Pick<BufferedCommandOptions, 'cwd' | 'env' | 'input'> {
141
+ logger?: BufferedCommandOptions['logger'];
142
+ progress?: ProgressReporter;
143
+ verboseStdout?: ProgressOutputTarget | false;
144
+ verboseStderr?: ProgressOutputTarget | false;
145
+ spinnerLabel?: string;
146
+ stepId?: string;
147
+ }
148
+ interface ResolveProgressModeOptions {
149
+ verbose?: boolean;
150
+ json?: boolean;
151
+ target?: ProgressOutputTarget;
152
+ env?: NodeJS.ProcessEnv;
153
+ isTTY?: boolean;
154
+ }
155
+ type ProgressStepState = 'pending' | 'running' | 'complete' | 'failed' | 'skipped';
156
+ interface ProgressStepDefinition {
157
+ id: string;
158
+ label: string;
159
+ }
160
+ declare function resolveProgressMode(options?: ResolveProgressModeOptions): ProgressMode;
161
+ declare class ProgressReporter {
162
+ #private;
163
+ readonly mode: ProgressMode;
164
+ readonly verbose: boolean;
165
+ readonly target: ProgressOutputTarget;
166
+ constructor(options?: ProgressReporterOptions);
167
+ section(title: string): void;
168
+ end(): void;
169
+ item(message: string): void;
170
+ detail(message: string): void;
171
+ warn(message: string): void;
172
+ marker(message: string): void;
173
+ setSteps(steps: readonly ProgressStepDefinition[]): void;
174
+ isChecklistActive(): boolean;
175
+ startStep(id: string): void;
176
+ completeStep(id: string): void;
177
+ failStep(id: string): void;
178
+ skipStep(id: string): void;
179
+ hasStep(id: string): boolean;
180
+ startSpinner(message: string): void;
181
+ tickSpinner(): void;
182
+ stopSpinner(status?: 'complete' | 'clear'): void;
183
+ commandEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
184
+ }
185
+ declare function createProgress(options?: ProgressReporterOptions): ProgressReporter;
186
+ declare function progressMarkerLine(message: string): string;
187
+ declare function formatCommandFailure(label: string, result: CommandResult, options?: {
188
+ tailLines?: number;
189
+ }): string;
190
+ declare function runProgressCommand(label: string, command: string, args: string[], options?: ProgressCommandOptions): Promise<CommandResult>;
191
+ declare function assertProgressCommandSucceeded(label: string, result: CommandResult, message: string): void;
192
+ //#endregion
193
+ //#region src/status.d.ts
194
+ type SshAliasSource = 'default' | 'provided';
195
+ type SshManagedBlockState = 'missing' | 'installed' | 'outdated';
196
+ interface ContainerSummary {
197
+ id: string;
198
+ name?: string;
199
+ state?: string;
200
+ status?: string;
201
+ localFolder?: string;
202
+ }
203
+ interface StatusInfo {
204
+ workspace: {
205
+ folder: string;
206
+ basename: string;
207
+ id: string;
208
+ };
209
+ ssh: {
210
+ alias: string;
211
+ aliasSource: SshAliasSource;
212
+ configPath: string;
213
+ configExists: boolean;
214
+ managedBlockState: SshManagedBlockState;
215
+ keyPath: string;
216
+ keyExists: boolean;
217
+ publicKeyPath: string;
218
+ publicKeyExists: boolean;
219
+ publicKeyRuntimePath: string;
220
+ publicKeyRuntimeExists: boolean;
221
+ };
222
+ paths: {
223
+ cacheRoot: string;
224
+ dataRoot: string;
225
+ workspaceCacheDir: string;
226
+ workspaceDataDir: string;
227
+ generatedConfigPath: string;
228
+ generatedConfigExists: boolean;
229
+ logPath: string;
230
+ logExists: boolean;
231
+ assetsDevcontainerDir: string;
232
+ assetsDevcontainerExists: boolean;
233
+ };
234
+ container: {
235
+ found: boolean;
236
+ running: boolean;
237
+ id?: string;
238
+ name?: string;
239
+ state?: string;
240
+ status?: string;
241
+ };
242
+ }
243
+ interface SshConfigStatus {
244
+ configPath: string;
245
+ configExists: boolean;
246
+ managedBlockState: SshManagedBlockState;
247
+ }
248
+ declare function parseDockerPsJsonLines(output: string): ContainerSummary[];
249
+ declare function inspectSshConfigStatus(context: WorkspaceContext, alias: string, configPath: string, exists: (path: string) => boolean, readFile?: (path: string) => string): SshConfigStatus;
250
+ declare function createStatusInfo(context: WorkspaceContext, alias: string, container: ContainerSummary | undefined, exists: (path: string) => boolean, options?: {
251
+ aliasSource?: SshAliasSource;
252
+ sshConfigPath?: string;
253
+ readFile?: (path: string) => string;
254
+ }): StatusInfo;
255
+ declare function statusIsHealthy(status: StatusInfo): boolean;
256
+ declare function formatStatusText(status: StatusInfo, options?: {
257
+ color?: boolean;
258
+ }): string;
259
+ //#endregion
260
+ //#region src/devcontainer.d.ts
261
+ interface StartOptions {
262
+ recreate?: boolean;
263
+ proxyMode?: boolean;
264
+ progress?: ProgressReporter;
265
+ logger?: WorkspaceCommandLogger;
266
+ reuseRunning?: boolean;
267
+ }
268
+ interface ContainerCommandOptions {
269
+ progress?: ProgressReporter;
270
+ logger?: WorkspaceCommandLogger;
271
+ }
272
+ interface TunnelPortForward {
273
+ localPort: number;
274
+ remotePort: number;
275
+ }
276
+ interface SshTunnelOptions {
277
+ bindAddress?: string;
278
+ remoteHost?: string;
279
+ }
280
+ interface DockerImageInfo {
281
+ id: string;
282
+ name?: string;
283
+ }
284
+ declare function parseContainerIdFromUpOutput(output: string): string | undefined;
285
+ declare function findWorkspaceContainer(context: WorkspaceContext, options?: {
286
+ logger?: WorkspaceCommandLogger;
287
+ }): Promise<ContainerSummary | undefined>;
288
+ declare function listWorkspaceContainers(): Promise<ContainerSummary[] | undefined>;
289
+ declare function findRunningContainerId(context: WorkspaceContext, options?: {
290
+ logger?: WorkspaceCommandLogger;
291
+ }): Promise<string | undefined>;
292
+ declare function inspectContainerImage(containerId: string, options?: {
293
+ logger?: WorkspaceCommandLogger;
294
+ }): Promise<DockerImageInfo | undefined>;
295
+ declare function stopWorkspaceContainer(context: WorkspaceContext, options?: {
296
+ logger?: WorkspaceCommandLogger;
297
+ }): Promise<void>;
298
+ declare function removeWorkspaceContainer(context: WorkspaceContext, options?: {
299
+ logger?: WorkspaceCommandLogger;
300
+ }): Promise<void>;
301
+ declare function removeContainerById(containerId: string, options?: {
302
+ volumes?: boolean;
303
+ logger?: WorkspaceCommandLogger;
304
+ }): Promise<void>;
305
+ declare function removeDockerImage(imageId: string, options?: {
306
+ logger?: WorkspaceCommandLogger;
307
+ }): Promise<boolean>;
308
+ declare function startDevcontainer(context: WorkspaceContext, options?: StartOptions): Promise<string>;
309
+ declare function printPortHint(context: WorkspaceContext, containerId: string, options?: {
310
+ logger?: WorkspaceCommandLogger;
311
+ }): Promise<void>;
312
+ declare function openShell(context: WorkspaceContext, options?: {
313
+ logger?: WorkspaceCommandLogger;
314
+ }): Promise<number>;
315
+ declare function codingAgentDevcontainerExecArgs(context: WorkspaceContext, agent: CodingAgentCli, agentArgs?: string[]): string[];
316
+ declare function openCodingAgentCli(context: WorkspaceContext, agent: CodingAgentCli, agentArgs?: string[], options?: {
317
+ logger?: WorkspaceCommandLogger;
318
+ }): Promise<number>;
319
+ declare function ensureContainerSshRuntime(context: WorkspaceContext, options?: ContainerCommandOptions): Promise<void>;
320
+ declare function refreshContainerCodingAgentClis(context: WorkspaceContext, proxyMode?: boolean, agents?: CodingAgentCli[], options?: ContainerCommandOptions): Promise<void>;
321
+ declare function ensureContainerCodingAgentCli(context: WorkspaceContext, agent: CodingAgentCli, options?: ContainerCommandOptions): Promise<void>;
322
+ declare function runSshdProxy(containerId: string, options?: {
323
+ logger?: WorkspaceCommandLogger;
324
+ }): Promise<number>;
325
+ declare function sshTunnelArgs(alias: string, ports: TunnelPortForward[], options?: SshTunnelOptions): string[];
326
+ declare function openSshTunnel(alias: string, ports: TunnelPortForward[], options?: SshTunnelOptions & {
327
+ logger?: WorkspaceCommandLogger;
328
+ }): Promise<number>;
329
+ declare function refreshContainerGhAuth(context: WorkspaceContext, options?: ContainerCommandOptions): Promise<void>;
330
+ //#endregion
331
+ //#region src/cli-style.d.ts
332
+ declare const ansi: {
333
+ readonly bold: "\u001B[1m";
334
+ readonly cyan: "\u001B[36m";
335
+ readonly dim: "\u001B[2m";
336
+ readonly green: "\u001B[32m";
337
+ readonly red: "\u001B[31m";
338
+ readonly yellow: "\u001B[33m";
339
+ readonly reset: "\u001B[0m";
340
+ };
341
+ type CliColor = keyof typeof ansi;
342
+ declare function color(value: string, colorName: CliColor): string;
343
+ declare function selectedMark(): string;
344
+ declare function emptyMark(isFocused: boolean): string;
345
+ declare function promptRail(): string;
346
+ declare function formatPromptTitle(title: string): string;
347
+ declare function formatPromptEnd(): string;
348
+ declare function formatPromptLabel(label: string, isFocused: boolean): string;
349
+ declare function formatPromptDetailLine(detail: string): string;
350
+ //#endregion
351
+ //#region src/interactive-prompts.d.ts
352
+ interface MultiSelectDescriptionSegment {
353
+ text: string;
354
+ color: CliColor;
355
+ }
356
+ interface MultiSelectChoice<T extends string> {
357
+ value: T;
358
+ label: string;
359
+ description: string;
360
+ focusedDescription?: readonly MultiSelectDescriptionSegment[];
361
+ }
362
+ type MultiSelectPromptResult<T extends string> = {
363
+ status: 'selected';
364
+ values: T[];
365
+ } | {
366
+ status: 'skipped';
367
+ values: [];
368
+ } | {
369
+ status: 'cancelled';
370
+ values: [];
371
+ } | {
372
+ status: 'non-interactive';
373
+ values: [];
374
+ };
375
+ type PromptInput = NodeJS.ReadableStream & {
376
+ isTTY?: boolean;
377
+ setRawMode?: (mode: boolean) => void;
378
+ resume: () => PromptInput;
379
+ pause: () => PromptInput;
380
+ };
381
+ type PromptOutput = NodeJS.WritableStream & {
382
+ isTTY?: boolean;
383
+ columns?: number;
384
+ };
385
+ interface MultiSelectPromptOptions<T extends string> {
386
+ title: string;
387
+ choices: readonly MultiSelectChoice<T>[];
388
+ skipLabel: string;
389
+ summaryLabel?: string;
390
+ input?: PromptInput;
391
+ output?: PromptOutput;
392
+ env?: NodeJS.ProcessEnv;
393
+ }
394
+ type TextPromptResult = {
395
+ status: 'submitted';
396
+ value: string;
397
+ } | {
398
+ status: 'cancelled';
399
+ value?: undefined;
400
+ } | {
401
+ status: 'non-interactive';
402
+ value?: undefined;
403
+ };
404
+ interface TextPromptOptions {
405
+ title: string;
406
+ details?: readonly string[];
407
+ defaultValue?: string;
408
+ summaryLabel: string;
409
+ validate?: (value: string) => string | undefined;
410
+ input?: PromptInput;
411
+ output?: PromptOutput;
412
+ env?: NodeJS.ProcessEnv;
413
+ }
414
+ type ConfirmPromptResult = {
415
+ status: 'confirmed';
416
+ } | {
417
+ status: 'denied';
418
+ } | {
419
+ status: 'cancelled';
420
+ } | {
421
+ status: 'non-interactive';
422
+ };
423
+ interface ConfirmPromptOptions {
424
+ title: string;
425
+ details?: readonly string[];
426
+ confirmLabel: string;
427
+ cancelLabel: string;
428
+ summaryLabel: string;
429
+ input?: PromptInput;
430
+ output?: PromptOutput;
431
+ env?: NodeJS.ProcessEnv;
432
+ }
433
+ declare function canPromptInteractively(input: PromptInput, output: PromptOutput, env: NodeJS.ProcessEnv): boolean;
434
+ declare function promptMultiSelect<T extends string>(options: MultiSelectPromptOptions<T>): Promise<MultiSelectPromptResult<T>>;
435
+ declare function promptText(options: TextPromptOptions): Promise<TextPromptResult>;
436
+ declare function promptConfirm(options: ConfirmPromptOptions): Promise<ConfirmPromptResult>;
437
+ //#endregion
438
+ //#region src/ssh-config.d.ts
439
+ declare function defaultSshAlias(workspaceBasename: string): string;
440
+ declare function validateSshAlias(alias: string): void;
441
+ declare function defaultSshConfigPath(env?: NodeJS.ProcessEnv): string;
442
+ declare function buildProxyCommand(context: WorkspaceContext, alias: string): string;
443
+ declare function buildSshConfigBlock(context: WorkspaceContext, alias: string): string;
444
+ declare function replaceSshConfigBlock(existingConfig: string, alias: string, block: string): string;
445
+ declare function removeSshConfigBlock(existingConfig: string, alias: string): string;
446
+ declare function installSshConfig(context: WorkspaceContext, alias: string, options?: {
447
+ quiet?: boolean;
448
+ configPath?: string;
449
+ }): Promise<void>;
450
+ declare function uninstallSshConfig(alias: string, options?: {
451
+ quiet?: boolean;
452
+ configPath?: string;
453
+ }): boolean;
454
+ //#endregion
455
+ //#region src/ssh-install-targets.d.ts
456
+ type SshConfigInstallTarget = 'codex' | 'claude';
457
+ interface SshInstallTargetDefinition {
458
+ value: SshConfigInstallTarget;
459
+ label: string;
460
+ description: string;
461
+ flag: string;
462
+ install: (context: WorkspaceContext, alias: string, options?: SshInstallTargetInstallOptions) => Promise<void> | void;
463
+ }
464
+ interface SshInstallTargetInstallOptions {
465
+ quiet?: boolean;
466
+ }
467
+ declare const SSH_INSTALL_TARGETS: readonly SshInstallTargetDefinition[];
468
+ declare function supportedSshInstallTargetsText(): string;
469
+ declare function sshInstallTargetFlagHintsText(): string;
470
+ declare function isSshConfigInstallTarget(value: string): value is SshConfigInstallTarget;
471
+ declare function dedupeSshInstallTargets(targets: readonly SshConfigInstallTarget[]): SshConfigInstallTarget[];
472
+ declare function installSshInstallTarget(context: WorkspaceContext, alias: string, targetValue: SshConfigInstallTarget, options?: SshInstallTargetInstallOptions): Promise<void>;
473
+ //#endregion
7
474
  //#region src/main.d.ts
8
- type BoxdownCommand = 'help' | 'start' | 'list' | 'status' | 'stop' | 'down' | 'doctor' | 'ssh-config-install' | 'ssh-proxy' | 'refresh-gh-token' | 'refresh-gh-token-running' | 'coding-agent';
475
+ type BoxdownCommand = 'help' | 'version' | 'setup' | 'start' | 'list' | 'status' | 'stop' | 'down' | 'purge' | 'doctor' | 'ssh-install' | 'ssh-uninstall' | 'ssh-proxy' | 'tunnel' | 'refresh-gh-token' | 'refresh-gh-token-running' | 'coding-agent';
9
476
  interface ParsedCli {
10
477
  command: BoxdownCommand;
11
478
  agent?: CodingAgentCli;
12
479
  agentArgs?: string[];
13
480
  workspace?: string;
481
+ workspaces?: string[];
14
482
  alias?: string;
483
+ targets?: SshConfigInstallTarget[];
484
+ tunnelPorts?: TunnelPortForward[];
15
485
  recreate: boolean;
16
486
  json: boolean;
487
+ details?: boolean;
488
+ verbose: boolean;
17
489
  }
18
- declare const USAGE = "Usage:\n boxdown start [--workspace <path>] [--recreate]\n boxdown codex [--workspace <path>] [--recreate] [-- <codex args...>]\n boxdown claude [--workspace <path>] [--recreate] [-- <claude args...>]\n boxdown cc [--workspace <path>] [--recreate] [-- <claude args...>]\n boxdown opencode [--workspace <path>] [--recreate] [-- <opencode args...>]\n boxdown antigravity [--workspace <path>] [--recreate] [-- <agy args...>]\n boxdown list [--json]\n boxdown status [--workspace <path>] [--alias <name>] [--json]\n boxdown stop [--workspace <path>]\n boxdown down [--workspace <path>]\n boxdown doctor [--workspace <path>]\n boxdown ssh-config install [--workspace <path>] [--alias <name>]\n boxdown ssh-proxy [--workspace <path>] [--alias <name>]\n boxdown refresh-gh-token [--workspace <path>]\n boxdown refresh-gh-token-running [--workspace <path>]\n\nCommands:\n start Start or reuse the workspace devcontainer, then open\n an interactive shell inside it. Alias: shell.\n codex Start or reuse the devcontainer, then launch Codex.\n claude Start or reuse the devcontainer, then launch Claude\n Code. Alias: cc.\n opencode Start or reuse the devcontainer, then launch\n OpenCode.\n antigravity Start or reuse the devcontainer, then launch\n Antigravity CLI (agy).\n list List Boxdown-known devcontainer workspaces from any\n directory.\n status Show workspace state, generated paths, SSH key paths,\n and the matching devcontainer state.\n stop Stop the workspace devcontainer if it is running.\n down Remove the workspace devcontainer. Keeps Boxdown\n cache, generated config, data, and SSH keys.\n doctor Check required host tools and Boxdown assets.\n ssh-config install Install or update an SSH host alias for the workspace\n devcontainer.\n ssh-proxy Internal command used by the generated SSH\n ProxyCommand. Starts or reuses the devcontainer and\n bridges SSH over docker exec.\n refresh-gh-token Start or reuse the devcontainer, then copy host\n GitHub CLI auth into the container when available.\n refresh-gh-token-running Refresh GitHub CLI auth only if the workspace\n devcontainer is already running.\n\nOptions:\n --workspace <path> Target project directory. Defaults to the current directory.\n --alias <name> SSH host alias. Defaults to <repo-name>-devcontainer.\n --recreate Remove the existing devcontainer before starting.\n --json Print JSON output. Supported by status and list.\n --help, -h Show help.\n";
490
+ interface RunCliOptions {
491
+ promptInput?: PromptInput;
492
+ promptOutput?: PromptOutput;
493
+ env?: NodeJS.ProcessEnv;
494
+ runDoctorChecks?: typeof runDoctorChecks;
495
+ setupWorkspace?: typeof setupWorkspace;
496
+ }
497
+ declare const USAGE = "Usage:\n boxdown setup [--workspace <path>] [--alias <name>] [--recreate] [--target <name>]...\n boxdown start [--workspace <path>] [--recreate]\n boxdown codex [--workspace <path>] [--recreate] [-- <codex args...>]\n boxdown claude [--workspace <path>] [--recreate] [-- <claude args...>]\n boxdown opencode [--workspace <path>] [--recreate] [-- <opencode args...>]\n boxdown antigravity [--workspace <path>] [--recreate] [-- <agy args...>]\n boxdown list [--details] [--json|--format json]\n boxdown status [--workspace <path>] [--alias <name>] [--json|--format json]\n boxdown stop [--workspace <path>]\n boxdown down [--workspace <path>]...\n boxdown purge [--workspace <path|ssh-alias|repo>] [--alias <name>]\n boxdown doctor [--workspace <path>]\n boxdown ssh install [--workspace <path>] [--alias <name>] [--target <name>]...\n boxdown ssh uninstall [--workspace <path>] [--alias <name>]\n boxdown ssh-proxy [--workspace <path>] [--alias <name>]\n boxdown tunnel [--port <port>] [--port <local:remote>] [--workspace <path>] [--alias <name>]\n boxdown refresh-gh-token [--workspace <path>]\n boxdown refresh-gh-token-running [--workspace <path>]\n\nCommands:\n setup Prepare the workspace devcontainer and SSH/app\n integration without opening a shell.\n start, shell Start or reuse the workspace devcontainer, then open\n an interactive shell inside it.\n codex Start or reuse the devcontainer, then launch Codex.\n claude, cc Start or reuse the devcontainer, then launch Claude\n Code.\n opencode Start or reuse the devcontainer, then launch\n OpenCode, installing it first when needed.\n antigravity Start or reuse the devcontainer, then launch\n Antigravity CLI (agy), installing it first when\n needed.\n list List Boxdown-known devcontainer workspaces from any\n directory.\n status Show workspace state, generated paths, SSH key paths,\n and the matching devcontainer state.\n stop Stop the workspace devcontainer if it is running.\n down Remove the workspace devcontainer. Keeps Boxdown\n cache, generated config, data, and SSH keys.\n purge Remove the workspace devcontainer, exact Docker\n image, managed SSH/app config, and Boxdown\n cache/data for this workspace. Prompts for\n tracked workspaces from untracked directories.\n doctor Check required host tools and Boxdown assets.\n ssh install Install or update an SSH host alias for the workspace\n devcontainer.\n ssh uninstall Remove Boxdown's managed SSH host alias block and\n matching Codex/Claude app entries.\n ssh-proxy Internal command used by the generated SSH\n ProxyCommand. Starts or reuses the devcontainer and\n bridges SSH over docker exec.\n tunnel Start or reuse the devcontainer, then keep an SSH\n local port tunnel open for host/browser access.\n refresh-gh-token Start or reuse the devcontainer, then copy host\n GitHub CLI auth into the container when available.\n refresh-gh-token-running Refresh GitHub CLI auth only if the workspace\n devcontainer is already running.\n\nOptions:\n --workspace <path> Target project directory. Defaults to the current directory.\n Repeatable with down. With purge, also accepts PATH,\n SSH ALIAS, or an unambiguous REPO from boxdown list.\n Without --workspace, purge only targets the current\n directory when it is tracked; otherwise interactive\n terminals prompt for tracked workspaces.\n --alias <name> SSH host alias. Defaults to <repo-name>-devcontainer.\n --target <name> Optional SSH install target. Repeatable. Supported by\n setup and ssh install: codex, claude.\n --port <port> Tunnel a local port to the same remote port, or use\n <local:remote>. Repeatable. Supported by tunnel.\n --recreate Remove the existing devcontainer before starting.\n --json Print JSON output. Supported by status and list.\n --format json Print JSON output. Equivalent to --json.\n --details Print detailed human list output. Supported by list.\n --verbose Stream raw Docker, devcontainer, and hook command output.\n Lifecycle commands append the same managed output to the\n per-workspace command log either way.\n --help, -h Show help.\n --version, -v Show version.\n";
19
498
  declare function commandWritesWorkspaceMetadata(command: BoxdownCommand): boolean;
20
499
  declare function parseCliArgs(argv: string[]): ParsedCli;
21
- declare function runCli(argv?: string[]): Promise<number>;
500
+ declare function parseTunnelPort(value: string): TunnelPortForward;
501
+ declare function parseTunnelPortList(value: string): TunnelPortForward[];
502
+ interface SetupWorkspaceOptions {
503
+ recreate?: boolean;
504
+ targets?: SshConfigInstallTarget[];
505
+ progress?: ProgressReporter;
506
+ logger?: WorkspaceCommandLogger;
507
+ start?: typeof startDevcontainer;
508
+ installSsh?: typeof installSshConfig;
509
+ installTarget?: typeof installSshInstallTarget;
510
+ }
511
+ declare function setupWorkspace(context: WorkspaceContext, alias: string, options?: SetupWorkspaceOptions): Promise<void>;
512
+ declare function runCli(argv?: string[], options?: RunCliOptions): Promise<number>;
22
513
  //#endregion
23
- export { BoxdownCommand, ParsedCli, USAGE, commandWritesWorkspaceMetadata, parseCliArgs, runCli };
514
+ export { BoxdownCommand, ParsedCli, RunCliOptions, USAGE, commandWritesWorkspaceMetadata, parseCliArgs, parseTunnelPort, parseTunnelPortList, runCli, setupWorkspace };
24
515
  //# sourceMappingURL=main.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.mts","names":[],"sources":["../src/coding-agents.ts","../src/main.ts"],"mappings":";KAAY,cAAA;AAAA,KACA,uBAAA,GAA0B,cAAA;AAAA,iBAUtB,sBAAA,CAAwB,OAAA,WAAkB,cAAA;AAAA,iBAI1C,iBAAA,CAAmB,KAAA,EAAO,cAAA;;;KCJ9B,cAAA;AAAA,UAcK,SAAA;EACf,OAAA,EAAS,cAAA;EACT,KAAA,GAAQ,cAAA;EACR,SAAA;EACA,SAAA;EACA,KAAA;EACA,QAAA;EACA,IAAA;AAAA;AAAA,cAGW,KAAA;AAAA,iBAqDG,8BAAA,CAAgC,OAAA,EAAS,cAAA;AAAA,iBAWzC,YAAA,CAAc,IAAA,aAAiB,SAAA;AAAA,iBAoJzB,MAAA,CAAQ,IAAA,cAAyC,OAAA"}
1
+ {"version":3,"file":"main.d.mts","names":[],"sources":["../src/coding-agents.ts","../src/paths.ts","../src/doctor.ts","../src/logging.ts","../src/process.ts","../src/progress.ts","../src/status.ts","../src/devcontainer.ts","../src/cli-style.ts","../src/interactive-prompts.ts","../src/ssh-config.ts","../src/ssh-install-targets.ts","../src/main.ts"],"mappings":"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"}
package/dist/main.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { i as runCli, n as commandWritesWorkspaceMetadata, r as parseCliArgs, t as USAGE } from "./main-ZFTrSVgt.mjs";
1
+ import { a as parseTunnelPortList, i as parseTunnelPort, n as commandWritesWorkspaceMetadata, o as runCli, r as parseCliArgs, s as setupWorkspace, t as USAGE } from "./main-XMBsKjIK.mjs";
2
2
 
3
- export { USAGE, commandWritesWorkspaceMetadata, parseCliArgs, runCli };
3
+ export { USAGE, commandWritesWorkspaceMetadata, parseCliArgs, parseTunnelPort, parseTunnelPortList, runCli, setupWorkspace };
package/docs/README.md CHANGED
@@ -19,6 +19,7 @@ repository.
19
19
  ## Feature Docs
20
20
 
21
21
  - [Feature index](./features/README.md)
22
+ - [Setup command](./features/setup.md)
22
23
  - [Start command](./features/start-and-shell.md)
23
24
  - [Lifecycle commands](./features/lifecycle.md)
24
25
  - [SSH config and proxy workflow](./features/ssh-config-and-proxy.md)
@@ -38,6 +38,28 @@ Per-workspace state is keyed by a hash of the resolved workspace path. This
38
38
  prevents collisions between repositories with the same basename in different
39
39
  folders.
40
40
 
41
+ ## External App Integrations
42
+
43
+ External app configuration is optional. `boxdown setup` and `boxdown ssh
44
+ install` ask about optional targets in interactive terminals, while
45
+ non-interactive runs skip them unless `--target` is provided. For example,
46
+ `boxdown setup --target codex` or `boxdown ssh install --target codex` writes
47
+ Codex app remote project configuration under `~/.codex/codex-app/config.json`,
48
+ and `--target claude` writes Claude app SSH remote configuration under
49
+ `~/Library/Application Support/Claude/ssh_configs.json`, but neither file
50
+ becomes Boxdown workspace state.
51
+
52
+ Boxdown writes the Codex app config entry needed to point Codex at the
53
+ Boxdown-managed SSH alias and canonical container-side project path,
54
+ `/workspaces/<repo-name>`. On install, it migrates matching older
55
+ `/home/node/<repo-name>` project entries for the same alias. On uninstall, it
56
+ removes matching Codex app/sidebar entries for both the canonical and legacy
57
+ paths. Other Codex global state remains Codex-owned.
58
+
59
+ Boxdown writes the Claude SSH remote entry needed to point Claude at the same
60
+ Boxdown-managed SSH alias. On uninstall, it removes that matching Claude SSH
61
+ remote and trusted-host entry.
62
+
41
63
  ## Container Asset Mounts
42
64
 
43
65
  The generated config mounts `assets/devcontainer/` read-only into the container
@@ -47,6 +69,10 @@ When the host has `~/.agents`, the generated config also mounts it read-only at
47
69
  `/home/node/.agents` so host-global agent configuration is available inside the
48
70
  container without being copied into target repositories.
49
71
 
72
+ When the host has file-backed Codex auth at `~/.codex/auth.json`, the generated
73
+ config mounts that single file read-only at `/home/node/.codex/auth.json`. It
74
+ does not mount the full host `~/.codex` directory.
75
+
50
76
  The container receives only a public SSH key mount. The private host key stays
51
77
  on the host and is referenced from the user's SSH config.
52
78
 
@@ -58,6 +84,12 @@ shared mounted utility: post-create runs an immediate install/update, while
58
84
  post-start and SSH proxy setup run throttled best-effort refreshes for
59
85
  already-running containers.
60
86
 
87
+ Baseline Python is also lifecycle-owned. The devcontainer installs Debian
88
+ `python3`, `python3-venv`, `python3-pip`, and `pipx` during post-create instead
89
+ of using the Dev Containers Python feature, which keeps the image layer smaller
90
+ and avoids shipping Python dev-tool virtualenvs in every container. uv remains a
91
+ separate feature and does not provide the system Python runtime by default.
92
+
61
93
  Tool refreshes are container-side behavior, not generated config schema.
62
94
  Failures should warn without making the devcontainer unusable. SSH proxy
63
95
  refresh output must stay off stdout because stdout carries SSH traffic. Locking
@@ -3,7 +3,7 @@
3
3
  ## Requirements
4
4
 
5
5
  - Node.js 24 or newer.
6
- - pnpm 10.26 or newer.
6
+ - pnpm 11 or newer.
7
7
  - Docker for manual container workflow testing.
8
8
  - OpenSSH tools for SSH config and proxy testing.
9
9
  - GitHub CLI for `refresh-gh-token` workflow testing.
@@ -20,8 +20,9 @@ Run the CLI from source:
20
20
 
21
21
  ```sh
22
22
  pnpm run start -- --help
23
+ pnpm run start -- setup --workspace /path/to/project
23
24
  pnpm run start -- start --workspace /path/to/project
24
- pnpm run start -- ssh-config install --workspace /path/to/project
25
+ pnpm run start -- ssh install --workspace /path/to/project
25
26
  ```
26
27
 
27
28
  The published binary is `boxdown`, but local development goes through
@@ -51,9 +52,15 @@ Use `--workspace` while developing so you do not accidentally target the
51
52
  Boxdown repo itself:
52
53
 
53
54
  ```sh
54
- pnpm run start -- start --workspace ~/projects/repos/example
55
- pnpm run start -- ssh-config install --workspace ~/projects/repos/example
55
+ pnpm run start -- setup --workspace ~/projects/repos/example
56
+ pnpm run start -- setup --workspace ~/projects/repos/example --target codex
57
+ pnpm run start -- setup --workspace ~/projects/repos/example --target claude
56
58
  ```
57
59
 
58
- Starting a real container writes Boxdown state under user cache/data directories
59
- and may pull images or install the Dev Containers CLI.
60
+ In an interactive terminal, `ssh install` asks whether to install optional SSH
61
+ targets. Add `--target codex` or `--target claude` when testing an app target
62
+ explicitly, or set `CI=1` when you want to exercise the non-interactive skip
63
+ path.
64
+
65
+ Setting up a real container writes Boxdown state under user cache/data
66
+ directories and may pull images or install the Dev Containers CLI.