boxdown 1.0.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -12
- package/assets/devcontainer/README.md +65 -21
- package/assets/devcontainer/devcontainer.json +27 -15
- package/assets/devcontainer/hooks/initialize.sh +76 -22
- package/assets/devcontainer/hooks/post-create.sh +70 -12
- package/assets/devcontainer/hooks/post-start.sh +20 -13
- package/assets/devcontainer/ssh-config-install.sh +12 -3
- package/assets/devcontainer/start.sh +721 -44
- package/assets/devcontainer/utils/coding-agent-cli-update.sh +267 -3
- package/assets/devcontainer/utils/deps-install.sh +68 -0
- package/assets/devcontainer/utils/git-config-bootstrap.sh +128 -0
- package/assets/devcontainer/utils/git-signing-bootstrap.sh +109 -0
- package/assets/devcontainer/utils/python-bootstrap.sh +69 -0
- package/assets/devcontainer/utils/secret-env-bootstrap.sh +22 -0
- package/assets/devcontainer/utils/ssh-agent-proxy-bootstrap.sh +27 -0
- package/assets/devcontainer/utils/ssh-agent-proxy.mjs +39 -0
- package/assets/devcontainer/utils/ssh-bootstrap.sh +9 -0
- package/dist/bin/cli.cjs +1 -1
- package/dist/bin/cli.mjs +1 -1
- package/dist/main-BDgyf2t5.cjs +5758 -0
- package/dist/main-J4_2Up3o.mjs +5718 -0
- package/dist/main-J4_2Up3o.mjs.map +1 -0
- package/dist/main.cjs +5 -2
- package/dist/main.d.cts +501 -4
- package/dist/main.d.cts.map +1 -1
- package/dist/main.d.mts +501 -4
- package/dist/main.d.mts.map +1 -1
- package/dist/main.mjs +2 -2
- package/docs/README.md +1 -0
- package/docs/architecture.md +32 -0
- package/docs/development.md +13 -6
- package/docs/features/README.md +2 -0
- package/docs/features/commit-signing.md +94 -0
- package/docs/features/generated-config-and-state.md +73 -5
- package/docs/features/github-auth-refresh.md +15 -2
- package/docs/features/lifecycle.md +103 -11
- package/docs/features/setup.md +66 -0
- package/docs/features/ssh-config-and-proxy.md +228 -7
- package/docs/features/start-and-shell.md +45 -5
- package/docs/superpowers/plans/2026-07-11-default-commit-signing.md +110 -0
- package/docs/superpowers/plans/2026-07-11-progress-ci-regression.md +125 -0
- package/docs/superpowers/plans/2026-07-17-node-ssh-agent-proxy-signing.md +132 -0
- package/docs/superpowers/plans/2026-07-17-runtime-secret-environment.md +174 -0
- package/docs/superpowers/specs/2026-07-11-default-commit-signing-design.md +416 -0
- package/docs/superpowers/specs/2026-07-11-progress-ci-regression-design.md +43 -0
- package/docs/superpowers/specs/2026-07-17-node-ssh-agent-proxy-design.md +63 -0
- package/docs/superpowers/specs/2026-07-17-runtime-secret-environment-design.md +194 -0
- package/docs/testing.md +35 -2
- package/docs/todo.md +2 -2
- package/package.json +1 -1
- package/src/claude-app-config.ts +304 -0
- package/src/cli-style.ts +43 -0
- package/src/codex-app-config.ts +656 -0
- package/src/config.ts +80 -10
- package/src/constants.ts +12 -0
- package/src/devcontainer.ts +511 -64
- package/src/doctor.ts +292 -30
- package/src/git-signing.ts +267 -0
- package/src/interactive-prompts.ts +692 -0
- package/src/list.ts +16 -2
- package/src/logging.ts +164 -0
- package/src/main.ts +1214 -63
- package/src/metadata.ts +52 -3
- package/src/package-info.ts +18 -0
- package/src/paths.ts +71 -11
- package/src/process.ts +80 -2
- package/src/progress.ts +593 -0
- package/src/purge.ts +216 -0
- package/src/shell.ts +25 -0
- package/src/ssh-config.ts +134 -16
- package/src/ssh-install-targets.ts +111 -0
- package/src/ssh-key.ts +53 -13
- package/src/status.ts +5 -0
- package/dist/main-BuEptwlL.cjs +0 -1707
- package/dist/main-ZFTrSVgt.mjs +0 -1685
- package/dist/main-ZFTrSVgt.mjs.map +0 -1
package/src/config.ts
CHANGED
|
@@ -3,12 +3,19 @@ import { join } from 'node:path'
|
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
BOXDOWN_CONTAINER_AGENTS_DIR,
|
|
6
|
+
BOXDOWN_CONTAINER_CODEX_AUTH_PATH,
|
|
7
|
+
BOXDOWN_CONTAINER_CODEX_DIR,
|
|
6
8
|
BOXDOWN_CONTAINER_DEVCONTAINER_DIR,
|
|
9
|
+
BOXDOWN_CONTAINER_GITCONFIG_PATH,
|
|
10
|
+
BOXDOWN_CONTAINER_HOST_GITCONFIG_DIR,
|
|
11
|
+
BOXDOWN_CONTAINER_SECRET_ENV_BOOTSTRAP,
|
|
12
|
+
BOXDOWN_CONTAINER_SECRET_ENV_DIR,
|
|
7
13
|
BOXDOWN_CONTAINER_SSH_DIR,
|
|
8
14
|
BOXDOWN_CONTAINER_SSH_PUBLIC_KEY_PATH
|
|
9
15
|
} from './constants.ts'
|
|
10
16
|
import { parseJsonc } from './jsonc.ts'
|
|
11
17
|
import type { WorkspaceContext } from './paths.ts'
|
|
18
|
+
import type { GitSigningPlan } from './git-signing.ts'
|
|
12
19
|
import { shellQuote } from './shell.ts'
|
|
13
20
|
|
|
14
21
|
export interface DevcontainerConfig {
|
|
@@ -35,42 +42,105 @@ function directoryExists (path: string): boolean {
|
|
|
35
42
|
}
|
|
36
43
|
}
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
function fileExists (path: string): boolean {
|
|
46
|
+
try {
|
|
47
|
+
return statSync(path).isFile()
|
|
48
|
+
} catch {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function mountHasTarget (mount: string, target: string): boolean {
|
|
54
|
+
return mount.split(',').some((part) => part.trim() === `target=${target}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function hasMountTarget (mounts: string[], target: string): boolean {
|
|
58
|
+
return mounts.some((mount) => mountHasTarget(mount, target))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function buildGeneratedDevcontainerConfig (context: WorkspaceContext, signing?: GitSigningPlan): DevcontainerConfig {
|
|
39
62
|
const baseConfig = readBaseDevcontainerConfig(context.assetsDevcontainerDir)
|
|
40
63
|
const mounts = Array.isArray(baseConfig.mounts)
|
|
41
|
-
? baseConfig.mounts
|
|
64
|
+
? baseConfig.mounts
|
|
65
|
+
.filter((mount): mount is string => typeof mount === 'string')
|
|
66
|
+
.filter((mount) => !mountHasTarget(mount, BOXDOWN_CONTAINER_GITCONFIG_PATH))
|
|
42
67
|
: []
|
|
43
68
|
|
|
44
69
|
const boxdownMounts = [
|
|
45
70
|
`type=bind,source=${context.assetsDevcontainerDir},target=${BOXDOWN_CONTAINER_DEVCONTAINER_DIR},readonly`,
|
|
46
|
-
`type=bind,source=${context.sshPublicKeyRuntimeDir},target=${BOXDOWN_CONTAINER_SSH_DIR},readonly
|
|
71
|
+
`type=bind,source=${context.sshPublicKeyRuntimeDir},target=${BOXDOWN_CONTAINER_SSH_DIR},readonly`,
|
|
72
|
+
`type=bind,source=${context.hostGitconfigSnapshotDir},target=${BOXDOWN_CONTAINER_HOST_GITCONFIG_DIR},readonly`,
|
|
73
|
+
`type=bind,source=${context.workspaceSecretEnvDir},target=${BOXDOWN_CONTAINER_SECRET_ENV_DIR},readonly`
|
|
47
74
|
]
|
|
48
75
|
|
|
49
76
|
if (
|
|
50
77
|
directoryExists(context.hostAgentsDir) &&
|
|
51
|
-
!mounts
|
|
78
|
+
!hasMountTarget(mounts, BOXDOWN_CONTAINER_AGENTS_DIR)
|
|
52
79
|
) {
|
|
53
80
|
boxdownMounts.push(`type=bind,source=${context.hostAgentsDir},target=${BOXDOWN_CONTAINER_AGENTS_DIR},readonly`)
|
|
54
81
|
}
|
|
55
82
|
|
|
83
|
+
if (signing?.enabled === true && signing.agentSocketSource !== undefined) {
|
|
84
|
+
boxdownMounts.push(`type=bind,source=${signing.agentSocketSource},target=/run/boxdown/ssh-agent.sock`)
|
|
85
|
+
boxdownMounts.push(`type=bind,source=${context.gitSigningStateDir},target=/opt/boxdown/state/git-signing,readonly`)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
fileExists(context.hostCodexAuthPath) &&
|
|
90
|
+
!hasMountTarget(mounts, BOXDOWN_CONTAINER_CODEX_DIR) &&
|
|
91
|
+
!hasMountTarget(mounts, BOXDOWN_CONTAINER_CODEX_AUTH_PATH)
|
|
92
|
+
) {
|
|
93
|
+
boxdownMounts.push(`type=bind,source=${context.hostCodexAuthPath},target=${BOXDOWN_CONTAINER_CODEX_AUTH_PATH},readonly`)
|
|
94
|
+
}
|
|
95
|
+
|
|
56
96
|
return {
|
|
57
97
|
...baseConfig,
|
|
58
98
|
name: `Boxdown: ${context.workspaceBasename}`,
|
|
59
99
|
mounts: [...mounts, ...boxdownMounts],
|
|
60
|
-
initializeCommand:
|
|
61
|
-
|
|
62
|
-
|
|
100
|
+
initializeCommand: [
|
|
101
|
+
`BOXDOWN_WORKSPACE_FOLDER=${shellQuote(context.workspaceFolder)}`,
|
|
102
|
+
`BOXDOWN_HOST_GITCONFIG_PATH=${shellQuote(context.hostGitconfigPath)}`,
|
|
103
|
+
`BOXDOWN_HOST_GITCONFIG_SNAPSHOT_PATH=${shellQuote(context.hostGitconfigSnapshotPath)}`,
|
|
104
|
+
`BOXDOWN_SECRET_ENV_DIR=${shellQuote(context.workspaceSecretEnvDir)}`,
|
|
105
|
+
`BOXDOWN_PROGRESS=${shellQuote('${localEnv:BOXDOWN_PROGRESS}')}`,
|
|
106
|
+
`BOXDOWN_VERBOSE=${shellQuote('${localEnv:BOXDOWN_VERBOSE}')}`,
|
|
107
|
+
'bash',
|
|
108
|
+
shellQuote(join(context.assetsDevcontainerDir, 'hooks', 'initialize.sh'))
|
|
109
|
+
].join(' '),
|
|
110
|
+
postCreateCommand: [
|
|
111
|
+
`BOXDOWN_PROGRESS=${shellQuote('${localEnv:BOXDOWN_PROGRESS}')}`,
|
|
112
|
+
`BOXDOWN_VERBOSE=${shellQuote('${localEnv:BOXDOWN_VERBOSE}')}`,
|
|
113
|
+
'bash',
|
|
114
|
+
shellQuote(`${BOXDOWN_CONTAINER_DEVCONTAINER_DIR}/hooks/post-create.sh`)
|
|
115
|
+
].join(' '),
|
|
116
|
+
postStartCommand: [
|
|
117
|
+
`BOXDOWN_PROGRESS=${shellQuote('${localEnv:BOXDOWN_PROGRESS}')}`,
|
|
118
|
+
`BOXDOWN_VERBOSE=${shellQuote('${localEnv:BOXDOWN_VERBOSE}')}`,
|
|
119
|
+
'bash',
|
|
120
|
+
shellQuote(`${BOXDOWN_CONTAINER_DEVCONTAINER_DIR}/hooks/post-start.sh`)
|
|
121
|
+
].join(' '),
|
|
63
122
|
containerEnv: {
|
|
64
123
|
...(baseConfig.containerEnv ?? {}),
|
|
65
124
|
BOXDOWN_CONTAINER_WORKSPACE_FOLDER: '/workspaces/${localWorkspaceFolderBasename}',
|
|
66
125
|
BOXDOWN_WORKSPACE_BASENAME: '${localWorkspaceFolderBasename}',
|
|
67
|
-
|
|
126
|
+
BOXDOWN_SECRET_ENV_DIR: BOXDOWN_CONTAINER_SECRET_ENV_DIR,
|
|
127
|
+
BASH_ENV: BOXDOWN_CONTAINER_SECRET_ENV_BOOTSTRAP,
|
|
128
|
+
DEVCONTAINER_SSH_PUBLIC_KEY_FILE: BOXDOWN_CONTAINER_SSH_PUBLIC_KEY_PATH,
|
|
129
|
+
BOXDOWN_GIT_SIGNING_ENABLED: signing?.enabled === true ? '1' : '0',
|
|
130
|
+
BOXDOWN_GIT_SIGNING_KEY_PATH: '/opt/boxdown/state/git-signing/signing-key.pub',
|
|
131
|
+
...(signing?.enabled === false && signing.reason !== undefined ? { BOXDOWN_GIT_SIGNING_REASON: signing.reason } : {}),
|
|
132
|
+
...(signing?.enabled === true
|
|
133
|
+
? {
|
|
134
|
+
BOXDOWN_GIT_SIGNING_SOURCE_SOCKET: '/run/boxdown/ssh-agent.sock',
|
|
135
|
+
SSH_AUTH_SOCK: '/run/boxdown/ssh-agent-node.sock'
|
|
136
|
+
}
|
|
137
|
+
: {})
|
|
68
138
|
}
|
|
69
139
|
}
|
|
70
140
|
}
|
|
71
141
|
|
|
72
|
-
export function writeGeneratedDevcontainerConfig (context: WorkspaceContext): DevcontainerConfig {
|
|
73
|
-
const config = buildGeneratedDevcontainerConfig(context)
|
|
142
|
+
export function writeGeneratedDevcontainerConfig (context: WorkspaceContext, signing?: GitSigningPlan): DevcontainerConfig {
|
|
143
|
+
const config = buildGeneratedDevcontainerConfig(context, signing)
|
|
74
144
|
mkdirSync(context.workspaceCacheDir, { recursive: true })
|
|
75
145
|
writeFileSync(context.generatedConfigPath, `${JSON.stringify(config, null, 2)}\n`)
|
|
76
146
|
return config
|
package/src/constants.ts
CHANGED
|
@@ -2,8 +2,20 @@ export const DEVCONTAINER_CLI_VERSION = '0.84.1'
|
|
|
2
2
|
|
|
3
3
|
export const BOXDOWN_CONTAINER_DEVCONTAINER_DIR = '/opt/boxdown/devcontainer'
|
|
4
4
|
export const BOXDOWN_CONTAINER_STATE_DIR = '/opt/boxdown/state'
|
|
5
|
+
export const BOXDOWN_CONTAINER_HOST_GITCONFIG_DIR = `${BOXDOWN_CONTAINER_STATE_DIR}/host-gitconfig`
|
|
6
|
+
export const BOXDOWN_CONTAINER_HOST_GITCONFIG_PATH = `${BOXDOWN_CONTAINER_HOST_GITCONFIG_DIR}/.gitconfig`
|
|
5
7
|
export const BOXDOWN_CONTAINER_SSH_DIR = `${BOXDOWN_CONTAINER_STATE_DIR}/ssh`
|
|
6
8
|
export const BOXDOWN_CONTAINER_SSH_PUBLIC_KEY_PATH = `${BOXDOWN_CONTAINER_SSH_DIR}/id_ed25519.pub`
|
|
7
9
|
export const BOXDOWN_CONTAINER_AGENTS_DIR = '/home/node/.agents'
|
|
10
|
+
export const BOXDOWN_CONTAINER_GITCONFIG_PATH = '/home/node/.gitconfig'
|
|
11
|
+
export const BOXDOWN_CONTAINER_CODEX_DIR = '/home/node/.codex'
|
|
12
|
+
export const BOXDOWN_CONTAINER_CODEX_AUTH_PATH = `${BOXDOWN_CONTAINER_CODEX_DIR}/auth.json`
|
|
13
|
+
export const BOXDOWN_CONTAINER_SECRET_ENV_DIR = '/run/boxdown/secrets'
|
|
14
|
+
export const BOXDOWN_CONTAINER_SECRET_ENV_BOOTSTRAP = `${BOXDOWN_CONTAINER_DEVCONTAINER_DIR}/utils/secret-env-bootstrap.sh`
|
|
15
|
+
export const BOXDOWN_SECRET_ENV_NAMES = [
|
|
16
|
+
'ANTHROPIC_API_KEY',
|
|
17
|
+
'SNYK_TOKEN',
|
|
18
|
+
'OP_SERVICE_ACCOUNT_TOKEN'
|
|
19
|
+
] as const
|
|
8
20
|
|
|
9
21
|
export const PACKAGE_NAME = 'boxdown'
|