boxdown 1.0.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.
- package/LICENSE +191 -0
- package/README.md +115 -0
- package/assets/devcontainer/README.md +177 -0
- package/assets/devcontainer/devcontainer.json +83 -0
- package/assets/devcontainer/hooks/initialize.sh +55 -0
- package/assets/devcontainer/hooks/post-create.sh +67 -0
- package/assets/devcontainer/hooks/post-start.sh +34 -0
- package/assets/devcontainer/ssh-config-install.sh +172 -0
- package/assets/devcontainer/start.sh +485 -0
- package/assets/devcontainer/utils/codex-cli-update.sh +9 -0
- package/assets/devcontainer/utils/coding-agent-cli-update.sh +360 -0
- package/assets/devcontainer/utils/deps-install.sh +87 -0
- package/assets/devcontainer/utils/ssh-bootstrap.sh +200 -0
- package/dist/bin/cli.cjs +12 -0
- package/dist/bin/cli.d.cts +1 -0
- package/dist/bin/cli.d.mts +1 -0
- package/dist/bin/cli.mjs +14 -0
- package/dist/bin/cli.mjs.map +1 -0
- package/dist/main-BuEptwlL.cjs +1707 -0
- package/dist/main-ZFTrSVgt.mjs +1685 -0
- package/dist/main-ZFTrSVgt.mjs.map +1 -0
- package/dist/main.cjs +7 -0
- package/dist/main.d.cts +24 -0
- package/dist/main.d.cts.map +1 -0
- package/dist/main.d.mts +24 -0
- package/dist/main.d.mts.map +1 -0
- package/dist/main.mjs +3 -0
- package/docs/README.md +34 -0
- package/docs/architecture.md +75 -0
- package/docs/conventions.md +29 -0
- package/docs/development.md +59 -0
- package/docs/features/README.md +14 -0
- package/docs/features/generated-config-and-state.md +64 -0
- package/docs/features/github-auth-refresh.md +64 -0
- package/docs/features/lifecycle.md +64 -0
- package/docs/features/ssh-config-and-proxy.md +60 -0
- package/docs/features/start-and-shell.md +83 -0
- package/docs/testing.md +63 -0
- package/docs/todo.md +170 -0
- package/package.json +128 -0
- package/src/bin/cli.ts +9 -0
- package/src/coding-agents.ts +22 -0
- package/src/config.ts +81 -0
- package/src/constants.ts +9 -0
- package/src/devcontainer-cli.ts +57 -0
- package/src/devcontainer.ts +394 -0
- package/src/doctor.ts +139 -0
- package/src/github-git-auth.ts +143 -0
- package/src/jsonc.ts +123 -0
- package/src/list.ts +102 -0
- package/src/main.ts +362 -0
- package/src/metadata.ts +84 -0
- package/src/paths.ts +117 -0
- package/src/process.ts +94 -0
- package/src/shell.ts +60 -0
- package/src/ssh-config.ts +113 -0
- package/src/ssh-key.ts +52 -0
- package/src/status.ts +327 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
DEVCONTAINER_DIR="$(cd "${HOOKS_DIR}/.." && pwd)"
|
|
6
|
+
|
|
7
|
+
main() {
|
|
8
|
+
configure_local_git
|
|
9
|
+
install_openssh_server
|
|
10
|
+
install_apm
|
|
11
|
+
install_or_update_coding_agent_clis
|
|
12
|
+
install_1password_cli
|
|
13
|
+
install_snyk_cli
|
|
14
|
+
run_deps_install
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
configure_local_git() {
|
|
18
|
+
# Local git prefs only apply inside a repository; skip when there is no .git (avoids postCreate failure).
|
|
19
|
+
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
20
|
+
git config --local commit.gpgsign false
|
|
21
|
+
git config --local core.pager 'less -R'
|
|
22
|
+
git config --local credential.https://github.com.helper '!gh auth git-credential'
|
|
23
|
+
fi
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
install_apm() {
|
|
27
|
+
# Agent Package Manager: https://github.com/microsoft/apm
|
|
28
|
+
curl -sSL https://aka.ms/apm-unix | sh
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
install_or_update_coding_agent_clis() {
|
|
32
|
+
bash "${DEVCONTAINER_DIR}/utils/coding-agent-cli-update.sh" install ||
|
|
33
|
+
echo "post-create: warning: one or more coding-agent CLI refreshes failed." >&2
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
install_openssh_server() {
|
|
37
|
+
bash "${DEVCONTAINER_DIR}/utils/ssh-bootstrap.sh" install
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
install_snyk_cli() {
|
|
41
|
+
# https://docs.snyk.io/snyk-cli/install-the-snyk-cli
|
|
42
|
+
local url
|
|
43
|
+
case "$(uname -m)" in
|
|
44
|
+
aarch64 | arm64) url="https://static.snyk.io/cli/latest/snyk-linux-arm64" ;;
|
|
45
|
+
x86_64 | amd64) url="https://static.snyk.io/cli/latest/snyk-linux" ;;
|
|
46
|
+
*)
|
|
47
|
+
echo "post-create: skipping Snyk CLI (unsupported arch: $(uname -m))" >&2
|
|
48
|
+
return 0
|
|
49
|
+
;;
|
|
50
|
+
esac
|
|
51
|
+
curl --compressed -fsSL "${url}" -o /tmp/snyk
|
|
52
|
+
chmod +x /tmp/snyk
|
|
53
|
+
sudo mv -f /tmp/snyk /usr/local/bin/snyk
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
install_1password_cli() {
|
|
57
|
+
local op_version="2.32.1"
|
|
58
|
+
curl -fsSL "https://cache.agilebits.com/dist/1P/op2/pkg/v${op_version}/op_linux_arm64_v${op_version}.zip" -o /tmp/op.zip
|
|
59
|
+
python3 -c "import zipfile; zipfile.ZipFile('/tmp/op.zip').extract('op', '/tmp')"
|
|
60
|
+
sudo mv /tmp/op /usr/local/bin/op && chmod +x /usr/local/bin/op && rm /tmp/op.zip
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
run_deps_install() {
|
|
64
|
+
bash "${DEVCONTAINER_DIR}/utils/deps-install.sh"
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
main "$@"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# post-start: runs after each container start (postStartCommand in devcontainer.json).
|
|
3
|
+
# Naming matches post-create.sh; extend with more steps as needed (e.g. source scripts from .devcontainer/utils/).
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
HOOKS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
DEVCONTAINER_DIR="$(cd "${HOOKS_DIR}/.." && pwd)"
|
|
9
|
+
|
|
10
|
+
main() {
|
|
11
|
+
configure_sshd_runtime
|
|
12
|
+
refresh_coding_agent_clis
|
|
13
|
+
remove_ephemeral_env_file_if_present
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
configure_sshd_runtime() {
|
|
17
|
+
bash "${DEVCONTAINER_DIR}/utils/ssh-bootstrap.sh" runtime
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
refresh_coding_agent_clis() {
|
|
21
|
+
bash "${DEVCONTAINER_DIR}/utils/coding-agent-cli-update.sh" maybe-update ||
|
|
22
|
+
echo "post-start: warning: one or more coding-agent CLI refreshes failed." >&2
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# When initializeCommand + runArgs inject secrets via .env.development, remove the file
|
|
26
|
+
# after start so it is not left on disk and tooling that assumes absence does not break.
|
|
27
|
+
remove_ephemeral_env_file_if_present() {
|
|
28
|
+
local env_file=".env.development"
|
|
29
|
+
if [[ -f "$env_file" ]]; then
|
|
30
|
+
rm -f "$env_file"
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
main "$@"
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Install/update a host SSH config alias for this repo's portless devcontainer SSH flow.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
|
+
WORKSPACE_FOLDER="$(cd "${SCRIPT_DIR}/.." && pwd -P)"
|
|
9
|
+
REPO_NAME="$(basename "${WORKSPACE_FOLDER}")"
|
|
10
|
+
HOST_ALIAS="${DEVCONTAINER_SSH_HOST_ALIAS:-${REPO_NAME}-devcontainer}"
|
|
11
|
+
SSH_CONFIG="${DEVCONTAINER_SSH_CONFIG:-${HOME}/.ssh/config}"
|
|
12
|
+
SSH_KEY_DIR="${DEVCONTAINER_SSH_KEY_DIR:-${SCRIPT_DIR}/.ssh}"
|
|
13
|
+
SSH_KEY_PATH="${DEVCONTAINER_SSH_KEY_PATH:-${SSH_KEY_DIR}/id_ed25519}"
|
|
14
|
+
START_SCRIPT="${SCRIPT_DIR}/start.sh"
|
|
15
|
+
BEGIN_MARKER="# BEGIN ${HOST_ALIAS} devcontainer ssh"
|
|
16
|
+
END_MARKER="# END ${HOST_ALIAS} devcontainer ssh"
|
|
17
|
+
QUIET=false
|
|
18
|
+
|
|
19
|
+
usage() {
|
|
20
|
+
cat <<EOF
|
|
21
|
+
Usage: $(basename "$0") [options]
|
|
22
|
+
|
|
23
|
+
Install or update a host SSH config alias for this repo's devcontainer.
|
|
24
|
+
|
|
25
|
+
Options:
|
|
26
|
+
--alias <name> SSH host alias to write (default: ${HOST_ALIAS})
|
|
27
|
+
--config <path> SSH config file to update (default: ${SSH_CONFIG})
|
|
28
|
+
--quiet Only print errors.
|
|
29
|
+
--help, -h Show this help and exit.
|
|
30
|
+
|
|
31
|
+
Environment overrides:
|
|
32
|
+
DEVCONTAINER_SSH_HOST_ALIAS
|
|
33
|
+
DEVCONTAINER_SSH_CONFIG
|
|
34
|
+
DEVCONTAINER_SSH_KEY_DIR
|
|
35
|
+
DEVCONTAINER_SSH_KEY_PATH
|
|
36
|
+
EOF
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
die() {
|
|
40
|
+
printf '%s\n' "$*" >&2
|
|
41
|
+
exit 1
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
log() {
|
|
45
|
+
if [ "$QUIET" != true ]; then
|
|
46
|
+
printf '%s\n' "$*"
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
ssh_config_quote() {
|
|
51
|
+
local value="$1"
|
|
52
|
+
value="${value//\\/\\\\}"
|
|
53
|
+
value="${value//\"/\\\"}"
|
|
54
|
+
printf '"%s"' "$value"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
ensure_host_ssh_key() {
|
|
58
|
+
if ! command -v ssh-keygen >/dev/null 2>&1; then
|
|
59
|
+
die "ssh-keygen is required for devcontainer SSH setup."
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
mkdir -p "$SSH_KEY_DIR"
|
|
63
|
+
chmod 0700 "$SSH_KEY_DIR"
|
|
64
|
+
|
|
65
|
+
if [ ! -f "$SSH_KEY_PATH" ]; then
|
|
66
|
+
log "Generating devcontainer SSH identity: $SSH_KEY_PATH"
|
|
67
|
+
ssh-keygen -t ed25519 -f "$SSH_KEY_PATH" -N "" -C "${REPO_NAME}-devcontainer" >/dev/null
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if [ ! -f "${SSH_KEY_PATH}.pub" ]; then
|
|
71
|
+
ssh-keygen -y -f "$SSH_KEY_PATH" > "${SSH_KEY_PATH}.pub"
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
chmod 0600 "$SSH_KEY_PATH"
|
|
75
|
+
chmod 0644 "${SSH_KEY_PATH}.pub"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
write_ssh_config_block() {
|
|
79
|
+
local ssh_dir
|
|
80
|
+
local tmp_file
|
|
81
|
+
local start_script_quoted
|
|
82
|
+
local key_path_quoted
|
|
83
|
+
local host_alias_quoted
|
|
84
|
+
|
|
85
|
+
ssh_dir="$(dirname "$SSH_CONFIG")"
|
|
86
|
+
mkdir -p "$ssh_dir"
|
|
87
|
+
chmod 0700 "$ssh_dir"
|
|
88
|
+
touch "$SSH_CONFIG"
|
|
89
|
+
chmod 0600 "$SSH_CONFIG"
|
|
90
|
+
|
|
91
|
+
tmp_file="$(mktemp)"
|
|
92
|
+
awk -v begin="$BEGIN_MARKER" -v end="$END_MARKER" '
|
|
93
|
+
$0 == begin { skip = 1; next }
|
|
94
|
+
$0 == end { skip = 0; next }
|
|
95
|
+
!skip { print }
|
|
96
|
+
' "$SSH_CONFIG" > "$tmp_file"
|
|
97
|
+
|
|
98
|
+
start_script_quoted="$(ssh_config_quote "$START_SCRIPT")"
|
|
99
|
+
key_path_quoted="$(ssh_config_quote "$SSH_KEY_PATH")"
|
|
100
|
+
host_alias_quoted="$(ssh_config_quote "$HOST_ALIAS")"
|
|
101
|
+
|
|
102
|
+
{
|
|
103
|
+
sed -e '${/^$/d;}' "$tmp_file"
|
|
104
|
+
printf '\n%s\n' "$BEGIN_MARKER"
|
|
105
|
+
printf 'Host %s\n' "$HOST_ALIAS"
|
|
106
|
+
printf ' HostName %s\n' "$HOST_ALIAS"
|
|
107
|
+
printf ' User node\n'
|
|
108
|
+
printf ' IdentityFile none\n'
|
|
109
|
+
printf ' IdentityFile %s\n' "$key_path_quoted"
|
|
110
|
+
printf ' IdentitiesOnly yes\n'
|
|
111
|
+
printf ' ProxyCommand env DEVCONTAINER_SSH_HOST_ALIAS=%s /bin/bash %s --ssh-proxy\n' "$host_alias_quoted" "$start_script_quoted"
|
|
112
|
+
printf ' StrictHostKeyChecking no\n'
|
|
113
|
+
printf ' UserKnownHostsFile /dev/null\n'
|
|
114
|
+
printf ' LogLevel ERROR\n'
|
|
115
|
+
printf '%s\n' "$END_MARKER"
|
|
116
|
+
} > "${tmp_file}.next"
|
|
117
|
+
|
|
118
|
+
if cmp -s "$SSH_CONFIG" "${tmp_file}.next"; then
|
|
119
|
+
log "SSH alias already up to date: ${HOST_ALIAS}"
|
|
120
|
+
rm -f "${tmp_file}.next"
|
|
121
|
+
else
|
|
122
|
+
mv "${tmp_file}.next" "$SSH_CONFIG"
|
|
123
|
+
log "Installed SSH alias: ${HOST_ALIAS}"
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
rm -f "$tmp_file"
|
|
127
|
+
chmod 0600 "$SSH_CONFIG"
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
while [ $# -gt 0 ]; do
|
|
131
|
+
case "$1" in
|
|
132
|
+
--help | -h)
|
|
133
|
+
usage
|
|
134
|
+
exit 0
|
|
135
|
+
;;
|
|
136
|
+
--alias)
|
|
137
|
+
[ $# -ge 2 ] || die "--alias requires a value."
|
|
138
|
+
HOST_ALIAS="$2"
|
|
139
|
+
BEGIN_MARKER="# BEGIN ${HOST_ALIAS} devcontainer ssh"
|
|
140
|
+
END_MARKER="# END ${HOST_ALIAS} devcontainer ssh"
|
|
141
|
+
shift 2
|
|
142
|
+
;;
|
|
143
|
+
--config)
|
|
144
|
+
[ $# -ge 2 ] || die "--config requires a value."
|
|
145
|
+
SSH_CONFIG="$2"
|
|
146
|
+
shift 2
|
|
147
|
+
;;
|
|
148
|
+
--quiet)
|
|
149
|
+
QUIET=true
|
|
150
|
+
shift
|
|
151
|
+
;;
|
|
152
|
+
*)
|
|
153
|
+
die "Unknown option: $1"
|
|
154
|
+
;;
|
|
155
|
+
esac
|
|
156
|
+
done
|
|
157
|
+
|
|
158
|
+
ensure_host_ssh_key
|
|
159
|
+
write_ssh_config_block
|
|
160
|
+
|
|
161
|
+
if [ "$QUIET" != true ]; then
|
|
162
|
+
cat <<EOF
|
|
163
|
+
SSH config: ${SSH_CONFIG}
|
|
164
|
+
Identity file: ${SSH_KEY_PATH}
|
|
165
|
+
|
|
166
|
+
Validate with:
|
|
167
|
+
ssh ${HOST_ALIAS} 'whoami && pwd'
|
|
168
|
+
|
|
169
|
+
Use this host alias in Cursor, Claude, or any other SSH client:
|
|
170
|
+
${HOST_ALIAS}
|
|
171
|
+
EOF
|
|
172
|
+
fi
|