subconscious-cli 6.1.0 → 6.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 +52 -47
- package/bin/agent-data.js +37 -1
- package/bin/agent-help.js +12 -4
- package/bin/agent-install.js +19 -135
- package/bin/agents.js +6 -5
- package/bin/cli.js +2 -1
- package/bin/command-output.js +75 -0
- package/bin/models.js +12 -0
- package/bin/native/subc-tui-darwin-amd64 +0 -0
- package/bin/native/subc-tui-darwin-arm64 +0 -0
- package/bin/native/subc-tui-linux-amd64 +0 -0
- package/bin/native/subc-tui-linux-arm64 +0 -0
- package/bin/native/subc-tui-windows-amd64.exe +0 -0
- package/bin/native/subc-tui-windows-arm64.exe +0 -0
- package/bin/runbook/README.md +13 -2
- package/bin/runbook/claude-code/agent.json +6 -6
- package/bin/runbook/codex/agent.json +1 -0
- package/bin/runbook/copilot/agent.json +1 -0
- package/bin/runbook/cursor/agent.json +3 -2
- package/bin/runbook/deepseek-harness/agent.json +1 -0
- package/bin/runbook/opencode/agent.json +1 -0
- package/bin/runbook/pi/agent.json +1 -0
- package/bin/runbook/shared.json +1 -2
- package/bin/sessions.js +20 -50
- package/bin/tui.js +18 -8
- package/bin/update-check.js +30 -0
- package/bin/upgrade.js +3 -3
- package/bin/windows/agents.js +14 -47
- package/bin/windows/launch.js +1 -1
- package/bin/windows/process.js +1 -18
- package/package.json +2 -3
- package/bin/runbook/subconscious-code/agent.json +0 -156
- package/bin/runbook/subconscious-code/install.sh +0 -154
- package/bin/windows/install.js +0 -182
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"id": "subconscious-code",
|
|
3
|
-
"command": "marathon",
|
|
4
|
-
"aliases": ["sc", "subconscious-code"],
|
|
5
|
-
"name": "Marathon",
|
|
6
|
-
"description": "Launch the native Subconscious coding agent with the active profile, gateway, and model.",
|
|
7
|
-
"protocol": "openai",
|
|
8
|
-
"homepage": "https://github.com/subconscious-systems/subconscious-code",
|
|
9
|
-
"install": {
|
|
10
|
-
"commands": {
|
|
11
|
-
"darwin": "subc marathon install",
|
|
12
|
-
"linux": "subc marathon install",
|
|
13
|
-
"win32": "subc marathon install"
|
|
14
|
-
},
|
|
15
|
-
"method": "github-release",
|
|
16
|
-
"repository": "subconscious-systems/subconscious-code",
|
|
17
|
-
"version": "latest",
|
|
18
|
-
"pin_env": "SC_CODE_VERSION",
|
|
19
|
-
"asset": "marathon-{target}.tar.gz",
|
|
20
|
-
"checksum": "marathon-{target}.tar.gz.sha256",
|
|
21
|
-
"legacy_asset": "sc-{target}.tar.gz",
|
|
22
|
-
"targets": [
|
|
23
|
-
"aarch64-apple-darwin",
|
|
24
|
-
"x86_64-apple-darwin",
|
|
25
|
-
"aarch64-unknown-linux-musl",
|
|
26
|
-
"x86_64-unknown-linux-musl"
|
|
27
|
-
],
|
|
28
|
-
"install_dir": "~/.local/bin",
|
|
29
|
-
"install_dir_env": "SC_INSTALL_DIR",
|
|
30
|
-
"minimum_version": null,
|
|
31
|
-
"verified": true,
|
|
32
|
-
"windows_targets": ["x86_64-pc-windows-msvc", "aarch64-pc-windows-msvc"],
|
|
33
|
-
"windows_assets": [
|
|
34
|
-
"marathon-{target}.zip",
|
|
35
|
-
"marathon-{target}.exe",
|
|
36
|
-
"sc-{target}.zip",
|
|
37
|
-
"sc-{target}.exe"
|
|
38
|
-
],
|
|
39
|
-
"note": "Windows installs come from the native launcher, which takes the first asset in windows_assets that the release has, plus its .sha256."
|
|
40
|
-
},
|
|
41
|
-
"runbook": {
|
|
42
|
-
"mode": "launch",
|
|
43
|
-
"setup_script": "install.sh",
|
|
44
|
-
"setup_actions": ["install"],
|
|
45
|
-
"setup_needs_auth": false,
|
|
46
|
-
"binary_install_script": "install.sh"
|
|
47
|
-
},
|
|
48
|
-
"help": {
|
|
49
|
-
"usage": "subc [-p NAME] marathon [help|install] [Marathon arguments...]",
|
|
50
|
-
"behavior": "Launches the native Subconscious coding agent with the selected profile gateway, credential, and model.",
|
|
51
|
-
"options": [
|
|
52
|
-
["help", "Show this help"],
|
|
53
|
-
[
|
|
54
|
-
"install",
|
|
55
|
-
{
|
|
56
|
-
"win32": "Install a verified Windows release, when published upstream",
|
|
57
|
-
"default": "Install the latest precompiled Linux or macOS release"
|
|
58
|
-
}
|
|
59
|
-
],
|
|
60
|
-
["--model MODEL", "Override the profile model for this launch"]
|
|
61
|
-
],
|
|
62
|
-
"args": ["ARGS...", "Pass arguments directly to Marathon"]
|
|
63
|
-
},
|
|
64
|
-
"sessions": { "key": "sc", "portable": false },
|
|
65
|
-
"prerequisites": [
|
|
66
|
-
{ "command": "gh|curl", "needed_for": "install", "required": true },
|
|
67
|
-
{
|
|
68
|
-
"command": "sha256sum|shasum",
|
|
69
|
-
"needed_for": "install",
|
|
70
|
-
"required": true
|
|
71
|
-
},
|
|
72
|
-
{ "command": "tar", "needed_for": "install", "required": true }
|
|
73
|
-
],
|
|
74
|
-
"launch": {
|
|
75
|
-
"argv": ["marathon", "{args}"],
|
|
76
|
-
"headless_argv": ["marathon", "--print={prompt}", "{args}"],
|
|
77
|
-
"headless_platforms": ["darwin", "linux"],
|
|
78
|
-
"resume": ["--resume", "{sessionFile}"],
|
|
79
|
-
"persistent_setup": false
|
|
80
|
-
},
|
|
81
|
-
"inputs": [
|
|
82
|
-
{
|
|
83
|
-
"name": "SC_API_KEY",
|
|
84
|
-
"label": "Subconscious Code API key",
|
|
85
|
-
"description": "Subconscious Code credential override; blank uses API_KEY.",
|
|
86
|
-
"type": "secret"
|
|
87
|
-
}
|
|
88
|
-
],
|
|
89
|
-
"env": [
|
|
90
|
-
{
|
|
91
|
-
"name": "SC_API_KEY",
|
|
92
|
-
"value": "{apiKey}",
|
|
93
|
-
"description": "Gateway credential."
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"name": "SC_BASE_URL",
|
|
97
|
-
"value": "{baseUrlV1}",
|
|
98
|
-
"description": "OpenAI-compatible base URL for model requests."
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"name": "SC_DLR_URL",
|
|
102
|
-
"value": "{baseUrl}",
|
|
103
|
-
"description": "Gateway origin for Marathon's DLR feature.",
|
|
104
|
-
"note": "What DLR does is defined in the Marathon binary, not in this repo."
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
"name": "SC_DLR_ENABLED",
|
|
108
|
-
"value": "true",
|
|
109
|
-
"description": "Turns Marathon's DLR feature on.",
|
|
110
|
-
"note": "What DLR does is defined in the Marathon binary, not in this repo."
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
"name": "SC_MODEL",
|
|
114
|
-
"value": "{model}",
|
|
115
|
-
"description": "Model for the session."
|
|
116
|
-
}
|
|
117
|
-
],
|
|
118
|
-
"config": [],
|
|
119
|
-
"capabilities": {
|
|
120
|
-
"compaction": {
|
|
121
|
-
"default": "unknown",
|
|
122
|
-
"on": {
|
|
123
|
-
"set_by_subc": false,
|
|
124
|
-
"kind": null,
|
|
125
|
-
"name": null,
|
|
126
|
-
"value": null,
|
|
127
|
-
"verified": false,
|
|
128
|
-
"note": "subc sets no compaction knob; Marathon's built-in behaviour applies."
|
|
129
|
-
},
|
|
130
|
-
"off": {
|
|
131
|
-
"set_by_subc": false,
|
|
132
|
-
"kind": null,
|
|
133
|
-
"name": null,
|
|
134
|
-
"value": null,
|
|
135
|
-
"verified": false,
|
|
136
|
-
"note": "No off knob is set or documented by subc."
|
|
137
|
-
},
|
|
138
|
-
"threshold": {
|
|
139
|
-
"set_by_subc": false,
|
|
140
|
-
"kind": null,
|
|
141
|
-
"name": null,
|
|
142
|
-
"value": null,
|
|
143
|
-
"verified": false,
|
|
144
|
-
"note": "No threshold knob is set or documented by subc."
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
"mcp": {
|
|
148
|
-
"configured_by_subc": false,
|
|
149
|
-
"transports": null,
|
|
150
|
-
"verified": false,
|
|
151
|
-
"note": "subc configures no MCP servers. Transports Marathon supports are not described in this repo."
|
|
152
|
-
},
|
|
153
|
-
"headers": [],
|
|
154
|
-
"hooks": []
|
|
155
|
-
}
|
|
156
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Install a precompiled Marathon release for the current platform.
|
|
3
|
-
|
|
4
|
-
set -euo pipefail
|
|
5
|
-
|
|
6
|
-
REPOSITORY="subconscious-systems/subconscious-code"
|
|
7
|
-
REPOSITORY_URL="https://github.com/${REPOSITORY}"
|
|
8
|
-
INSTALL_DIR="${SC_INSTALL_DIR:-${HOME}/.local/bin}"
|
|
9
|
-
|
|
10
|
-
usage() {
|
|
11
|
-
cat <<'EOF'
|
|
12
|
-
Usage: subc marathon install
|
|
13
|
-
|
|
14
|
-
Install the latest Marathon release. Linux downloads and verifies the
|
|
15
|
-
published static binary. macOS downloads and verifies the matching native binary.
|
|
16
|
-
EOF
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
case "${1:-install}" in
|
|
20
|
-
install) ;;
|
|
21
|
-
help|-h|--help)
|
|
22
|
-
usage
|
|
23
|
-
exit 0
|
|
24
|
-
;;
|
|
25
|
-
*)
|
|
26
|
-
echo "error: unsupported Marathon setup action: $1" >&2
|
|
27
|
-
usage >&2
|
|
28
|
-
exit 2
|
|
29
|
-
;;
|
|
30
|
-
esac
|
|
31
|
-
|
|
32
|
-
release_tag() {
|
|
33
|
-
if [[ -n "${SC_CODE_VERSION:-}" ]]; then
|
|
34
|
-
printf '%s\n' "${SC_CODE_VERSION#v}"
|
|
35
|
-
return
|
|
36
|
-
fi
|
|
37
|
-
if command -v gh >/dev/null 2>&1; then
|
|
38
|
-
local tag
|
|
39
|
-
tag="$(gh release view --repo "$REPOSITORY" --json tagName --jq '.tagName' 2>/dev/null || true)"
|
|
40
|
-
if [[ -n "$tag" ]]; then
|
|
41
|
-
printf '%s\n' "${tag#v}"
|
|
42
|
-
return
|
|
43
|
-
fi
|
|
44
|
-
fi
|
|
45
|
-
if command -v curl >/dev/null 2>&1; then
|
|
46
|
-
local tag
|
|
47
|
-
tag="$(curl -fsSL "https://api.github.com/repos/${REPOSITORY}/releases/latest" 2>/dev/null \
|
|
48
|
-
| sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' \
|
|
49
|
-
| head -n 1 || true)"
|
|
50
|
-
if [[ -n "$tag" ]]; then
|
|
51
|
-
printf '%s\n' "${tag#v}"
|
|
52
|
-
return
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
return 1
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
platform="$(uname -s)"
|
|
59
|
-
architecture="$(uname -m)"
|
|
60
|
-
version="$(release_tag || true)"
|
|
61
|
-
|
|
62
|
-
if [[ -z "$version" ]]; then
|
|
63
|
-
echo "error: no published Marathon release is available yet" >&2
|
|
64
|
-
echo "The release may still be building. Retry subc marathon install after it finishes." >&2
|
|
65
|
-
exit 1
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
case "${platform}:${architecture}" in
|
|
69
|
-
Darwin:arm64) target="aarch64-apple-darwin" ;;
|
|
70
|
-
Darwin:x86_64) target="x86_64-apple-darwin" ;;
|
|
71
|
-
Linux:aarch64|Linux:arm64) target="aarch64-unknown-linux-musl" ;;
|
|
72
|
-
Linux:x86_64|Linux:amd64) target="x86_64-unknown-linux-musl" ;;
|
|
73
|
-
*)
|
|
74
|
-
echo "error: unsupported platform or architecture: ${platform} ${architecture}" >&2
|
|
75
|
-
exit 1
|
|
76
|
-
;;
|
|
77
|
-
esac
|
|
78
|
-
|
|
79
|
-
executable="marathon"
|
|
80
|
-
asset="${executable}-${target}.tar.gz"
|
|
81
|
-
checksum="${asset}.sha256"
|
|
82
|
-
work_dir="$(mktemp -d "${TMPDIR:-/tmp}/subc-marathon-install.XXXXXX")"
|
|
83
|
-
staging=''
|
|
84
|
-
cleanup() {
|
|
85
|
-
rm -f "$work_dir/$asset" "$work_dir/$checksum" "$work_dir/sc" "$work_dir/marathon"
|
|
86
|
-
if [[ -n "$staging" ]]; then rm -f "$staging"; fi
|
|
87
|
-
rmdir "$work_dir" 2>/dev/null || true
|
|
88
|
-
}
|
|
89
|
-
trap cleanup EXIT HUP INT TERM
|
|
90
|
-
|
|
91
|
-
echo "Downloading Marathon v${version} for ${target}..."
|
|
92
|
-
if command -v gh >/dev/null 2>&1 \
|
|
93
|
-
&& assets="$(gh release view "v${version}" --repo "$REPOSITORY" --json assets --jq '.assets[].name' 2>/dev/null)"; then
|
|
94
|
-
if ! printf '%s\n' "$assets" | grep -Fxq "$asset"; then
|
|
95
|
-
# Pinned legacy releases are still installed as marathon, never as sc.
|
|
96
|
-
executable="sc"
|
|
97
|
-
asset="${executable}-${target}.tar.gz"
|
|
98
|
-
checksum="${asset}.sha256"
|
|
99
|
-
fi
|
|
100
|
-
if ! printf '%s\n' "$assets" | grep -Fxq "$asset" \
|
|
101
|
-
|| ! printf '%s\n' "$assets" | grep -Fxq "$checksum"; then
|
|
102
|
-
echo "error: release is missing $asset or $checksum" >&2
|
|
103
|
-
exit 1
|
|
104
|
-
fi
|
|
105
|
-
gh release download "v${version}" --repo "$REPOSITORY" \
|
|
106
|
-
--pattern "$asset" --pattern "$checksum" --dir "$work_dir"
|
|
107
|
-
else
|
|
108
|
-
command -v curl >/dev/null 2>&1 || {
|
|
109
|
-
echo "error: curl is required to download Marathon" >&2
|
|
110
|
-
exit 1
|
|
111
|
-
}
|
|
112
|
-
release_url="${REPOSITORY_URL}/releases/download/v${version}"
|
|
113
|
-
status="$(curl -sSL -w '%{http_code}' "${release_url}/${asset}" -o "$work_dir/$asset")"
|
|
114
|
-
if [[ "$status" == 404 ]]; then
|
|
115
|
-
rm -f "$work_dir/$asset"
|
|
116
|
-
executable="sc"
|
|
117
|
-
asset="${executable}-${target}.tar.gz"
|
|
118
|
-
checksum="${asset}.sha256"
|
|
119
|
-
curl -fL "${release_url}/${asset}" -o "$work_dir/$asset"
|
|
120
|
-
elif [[ "$status" != 200 ]]; then
|
|
121
|
-
echo "error: Marathon download failed (HTTP $status)" >&2
|
|
122
|
-
exit 1
|
|
123
|
-
fi
|
|
124
|
-
curl -fL "${release_url}/${checksum}" -o "$work_dir/$checksum"
|
|
125
|
-
fi
|
|
126
|
-
|
|
127
|
-
if command -v sha256sum >/dev/null 2>&1; then
|
|
128
|
-
(cd "$work_dir" && sha256sum --check "$checksum")
|
|
129
|
-
elif command -v shasum >/dev/null 2>&1; then
|
|
130
|
-
expected="$(awk '{print $1}' "$work_dir/$checksum")"
|
|
131
|
-
actual="$(shasum -a 256 "$work_dir/$asset" | awk '{print $1}')"
|
|
132
|
-
[[ "$actual" == "$expected" ]] || {
|
|
133
|
-
echo "error: Marathon checksum verification failed" >&2
|
|
134
|
-
exit 1
|
|
135
|
-
}
|
|
136
|
-
else
|
|
137
|
-
echo "error: sha256sum or shasum is required to verify the download" >&2
|
|
138
|
-
exit 1
|
|
139
|
-
fi
|
|
140
|
-
|
|
141
|
-
tar -xzf "$work_dir/$asset" -C "$work_dir" "$executable"
|
|
142
|
-
if [[ ! -f "$work_dir/$executable" || -L "$work_dir/$executable" ]]; then
|
|
143
|
-
echo "error: release has no regular root $executable executable" >&2
|
|
144
|
-
exit 1
|
|
145
|
-
fi
|
|
146
|
-
mkdir -p "$INSTALL_DIR"
|
|
147
|
-
if [[ -d "$INSTALL_DIR/marathon" ]]; then
|
|
148
|
-
echo "error: $INSTALL_DIR/marathon is a directory; refusing to replace it" >&2
|
|
149
|
-
exit 1
|
|
150
|
-
fi
|
|
151
|
-
staging="$(mktemp "$INSTALL_DIR/.marathon.XXXXXX")"
|
|
152
|
-
install -m 0755 "$work_dir/$executable" "$staging"
|
|
153
|
-
mv -f "$staging" "$INSTALL_DIR/marathon"
|
|
154
|
-
echo "Installed Marathon v${version} to $INSTALL_DIR/marathon"
|
package/bin/windows/install.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import os from 'node:os';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { agentById, installCommands } from '../agent-data.js';
|
|
6
|
-
import { powershellCommand, runWindows } from './process.js';
|
|
7
|
-
|
|
8
|
-
const marathon = agentById('subconscious-code').install;
|
|
9
|
-
const repository = marathon.repository;
|
|
10
|
-
|
|
11
|
-
function npmInstall(command) {
|
|
12
|
-
const match = /^npm (?:i|install) -g ([@a-zA-Z0-9/._-]+)$/.exec(
|
|
13
|
-
command || '',
|
|
14
|
-
);
|
|
15
|
-
return match ? { command: 'npm', args: ['install', '-g', match[1]] } : null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function windowsInstallSpec(id, env = process.env) {
|
|
19
|
-
const install = installCommands(agentById(id)) || {};
|
|
20
|
-
const command = install.win32;
|
|
21
|
-
if (id === 'subconscious-code')
|
|
22
|
-
return { nativeRelease: true, display: 'subc marathon install' };
|
|
23
|
-
if (!command) return null; // Never fall back to a Linux installer.
|
|
24
|
-
if (id === 'claude-code') {
|
|
25
|
-
const primary = powershellCommand(
|
|
26
|
-
"$ErrorActionPreference = 'Stop'; Invoke-RestMethod 'https://claude.ai/install.ps1' | Invoke-Expression; if (-not $?) { exit 1 }",
|
|
27
|
-
env,
|
|
28
|
-
);
|
|
29
|
-
return {
|
|
30
|
-
...primary,
|
|
31
|
-
display: command,
|
|
32
|
-
fallback: npmInstall(install.fallback),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const spec = npmInstall(command);
|
|
36
|
-
if (!spec) throw new Error(`No safe Windows installer is defined for ${id}`);
|
|
37
|
-
return { ...spec, display: command };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function windowsReleaseAsset(release, arch = process.arch) {
|
|
41
|
-
const target = marathon.windows_targets.find((triple) =>
|
|
42
|
-
triple.startsWith({ x64: 'x86_64-', arm64: 'aarch64-' }[arch] ?? '-'),
|
|
43
|
-
);
|
|
44
|
-
if (!target)
|
|
45
|
-
throw new Error(
|
|
46
|
-
`Subconscious Code does not support Windows architecture ${arch}`,
|
|
47
|
-
);
|
|
48
|
-
// Pinned older releases are installed under the new executable name too.
|
|
49
|
-
// Never resolve or overwrite Windows' own sc.exe.
|
|
50
|
-
const names = marathon.windows_assets.map((name) =>
|
|
51
|
-
name.replaceAll('{target}', target),
|
|
52
|
-
);
|
|
53
|
-
const asset = names
|
|
54
|
-
.map((name) => release.assets?.find((asset) => asset.name === name))
|
|
55
|
-
.find(Boolean);
|
|
56
|
-
if (!asset)
|
|
57
|
-
throw new Error(
|
|
58
|
-
`Marathon ${release.tag_name || 'latest'} has no published native Windows ${arch} binary for subc marathon install.`,
|
|
59
|
-
);
|
|
60
|
-
const checksum = release.assets.find(
|
|
61
|
-
(candidate) => candidate.name === `${asset.name}.sha256`,
|
|
62
|
-
);
|
|
63
|
-
if (!checksum)
|
|
64
|
-
throw new Error(
|
|
65
|
-
`The Windows release is missing ${asset.name}.sha256; refusing an unverified installation.`,
|
|
66
|
-
);
|
|
67
|
-
return { asset, checksum };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export async function installWindowsSC(
|
|
71
|
-
env,
|
|
72
|
-
{
|
|
73
|
-
fetchImpl = fetch,
|
|
74
|
-
run = runWindows,
|
|
75
|
-
home = os.homedir(),
|
|
76
|
-
arch = process.arch,
|
|
77
|
-
log = console.log,
|
|
78
|
-
} = {},
|
|
79
|
-
) {
|
|
80
|
-
const version = env.SC_CODE_VERSION?.replace(/^v/, '');
|
|
81
|
-
const endpoint = version
|
|
82
|
-
? `tags/${encodeURIComponent(`v${version}`)}`
|
|
83
|
-
: 'latest';
|
|
84
|
-
const response = await fetchImpl(
|
|
85
|
-
`https://api.github.com/repos/${repository}/releases/${endpoint}`,
|
|
86
|
-
{
|
|
87
|
-
headers: { Accept: 'application/vnd.github+json' },
|
|
88
|
-
signal: AbortSignal.timeout(15000),
|
|
89
|
-
},
|
|
90
|
-
);
|
|
91
|
-
if (!response.ok)
|
|
92
|
-
throw new Error(
|
|
93
|
-
`Could not find a Subconscious Code release (HTTP ${response.status}).`,
|
|
94
|
-
);
|
|
95
|
-
const release = await response.json();
|
|
96
|
-
const { asset, checksum } = windowsReleaseAsset(release, arch);
|
|
97
|
-
const download = async (asset) => {
|
|
98
|
-
const url = new URL(asset.browser_download_url);
|
|
99
|
-
if (
|
|
100
|
-
url.origin !== 'https://github.com' ||
|
|
101
|
-
!url.pathname.startsWith(`/${repository}/releases/download/`)
|
|
102
|
-
)
|
|
103
|
-
throw new Error('Unexpected release download URL');
|
|
104
|
-
const response = await fetchImpl(url.href, {
|
|
105
|
-
signal: AbortSignal.timeout(120000),
|
|
106
|
-
});
|
|
107
|
-
if (!response.ok)
|
|
108
|
-
throw new Error(
|
|
109
|
-
`Download failed (HTTP ${response.status}): ${asset.name}`,
|
|
110
|
-
);
|
|
111
|
-
return Buffer.from(await response.arrayBuffer());
|
|
112
|
-
};
|
|
113
|
-
log(`Downloading Marathon ${release.tag_name} for Windows ${arch}...`);
|
|
114
|
-
const [binary, checksumText] = await Promise.all([
|
|
115
|
-
download(asset),
|
|
116
|
-
download(checksum),
|
|
117
|
-
]);
|
|
118
|
-
const expected = checksumText.toString('utf8').trim().split(/\s+/)[0];
|
|
119
|
-
if (
|
|
120
|
-
!/^[a-f0-9]{64}$/i.test(expected) ||
|
|
121
|
-
createHash('sha256').update(binary).digest('hex') !== expected.toLowerCase()
|
|
122
|
-
)
|
|
123
|
-
throw new Error('Subconscious Code checksum verification failed');
|
|
124
|
-
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'subc-sc-install-'));
|
|
125
|
-
const installDir = path.resolve(
|
|
126
|
-
env.SC_INSTALL_DIR || path.join(home, '.local', 'bin'),
|
|
127
|
-
);
|
|
128
|
-
const destination = path.join(installDir, 'marathon.exe');
|
|
129
|
-
const staging = path.join(installDir, `marathon-${randomUUID()}.tmp`);
|
|
130
|
-
try {
|
|
131
|
-
const extracted = path.join(dir, 'marathon.exe');
|
|
132
|
-
if (asset.name.endsWith('.zip')) {
|
|
133
|
-
const archive = path.join(dir, 'release.zip');
|
|
134
|
-
await fs.writeFile(archive, binary, { flag: 'wx' });
|
|
135
|
-
// Extract only the exact root executable. No archive-controlled paths.
|
|
136
|
-
const executable = asset.name.startsWith('marathon-')
|
|
137
|
-
? 'marathon.exe'
|
|
138
|
-
: 'sc.exe';
|
|
139
|
-
const spec = powershellCommand(
|
|
140
|
-
"$ErrorActionPreference = 'Stop'; Add-Type -AssemblyName System.IO.Compression.FileSystem; $z = [IO.Compression.ZipFile]::OpenRead($env:SUBC_ARCHIVE); try { $e = $z.GetEntry($env:SUBC_EXECUTABLE); if ($null -eq $e) { throw 'Archive has no expected root executable' }; [IO.Compression.ZipFileExtensions]::ExtractToFile($e, $env:SUBC_EXTRACTED, $false) } finally { $z.Dispose() }",
|
|
141
|
-
{
|
|
142
|
-
...env,
|
|
143
|
-
SUBC_ARCHIVE: archive,
|
|
144
|
-
SUBC_EXTRACTED: extracted,
|
|
145
|
-
SUBC_EXECUTABLE: executable,
|
|
146
|
-
},
|
|
147
|
-
);
|
|
148
|
-
if (await run(spec.command, spec.args, { env: spec.env }))
|
|
149
|
-
throw new Error('Could not extract the Windows release');
|
|
150
|
-
} else await fs.writeFile(extracted, binary, { flag: 'wx' });
|
|
151
|
-
const magic = (await fs.readFile(extracted))
|
|
152
|
-
.subarray(0, 2)
|
|
153
|
-
.toString('ascii');
|
|
154
|
-
if (magic !== 'MZ') throw new Error('Release is not a Windows executable');
|
|
155
|
-
await fs.mkdir(installDir, { recursive: true });
|
|
156
|
-
await fs.copyFile(extracted, staging);
|
|
157
|
-
await fs.rename(staging, destination);
|
|
158
|
-
log(`Installed ${destination}. Run subc marathon to launch it.`);
|
|
159
|
-
return 0;
|
|
160
|
-
} finally {
|
|
161
|
-
await fs.rm(staging, { force: true });
|
|
162
|
-
await fs.rm(dir, { recursive: true, force: true });
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export async function installWindowsAgent(id, env, run = runWindows) {
|
|
167
|
-
const spec = windowsInstallSpec(id, env);
|
|
168
|
-
if (!spec) throw new Error('Install this agent separately, then rerun subc.');
|
|
169
|
-
if (spec.nativeRelease) return installWindowsSC(env, { run });
|
|
170
|
-
let code;
|
|
171
|
-
try {
|
|
172
|
-
code = await run(spec.command, spec.args, { env: spec.env || env });
|
|
173
|
-
} catch (error) {
|
|
174
|
-
if (!spec.fallback) throw error;
|
|
175
|
-
code = 1;
|
|
176
|
-
}
|
|
177
|
-
if (code && spec.fallback) {
|
|
178
|
-
console.error('Native installer failed; trying the npm package.');
|
|
179
|
-
code = await run(spec.fallback.command, spec.fallback.args, { env });
|
|
180
|
-
}
|
|
181
|
-
return code;
|
|
182
|
-
}
|