@brendangraham/steer 0.1.21
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/.gitignore +2 -0
- package/CHANGELOG.md +49 -0
- package/LICENSE +661 -0
- package/README.md +285 -0
- package/binary-install.js +192 -0
- package/binary.js +126 -0
- package/install.js +4 -0
- package/npm-shrinkwrap.json +718 -0
- package/package.json +76 -0
- package/run-schema-generator.js +4 -0
- package/run-steer.js +4 -0
package/README.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Steer
|
|
2
|
+
|
|
3
|
+
Steer is an AI coding agent written in Rust.
|
|
4
|
+
|
|
5
|
+
It includes a TUI, supports headless execution, and exposes a gRPC interface for programmatic usage.
|
|
6
|
+
|
|
7
|
+
https://github.com/user-attachments/assets/5a31ccc9-6a96-4005-ab5a-ae3aa7ae34c4
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
### Shell Script
|
|
14
|
+
```
|
|
15
|
+
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/BrendanGraham14/steer/releases/latest/download/steer-installer.sh | sh
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### Cargo
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cargo install steer
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
Simply run `steer` to start the TUI in a local session.
|
|
27
|
+
|
|
28
|
+
More options:
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
❯ steer --help
|
|
32
|
+
Command-line interface for Steer coding agent.
|
|
33
|
+
|
|
34
|
+
Usage: steer [OPTIONS] [COMMAND]
|
|
35
|
+
|
|
36
|
+
Commands:
|
|
37
|
+
tui Launch the interactive terminal UI (default)
|
|
38
|
+
preferences Manage user preferences
|
|
39
|
+
headless Run in headless mode
|
|
40
|
+
server Start the gRPC server
|
|
41
|
+
session Session management commands
|
|
42
|
+
help Print this message or the help of the given subcommand(s)
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--session <SESSION>
|
|
46
|
+
Resume an existing session instead of starting a new one (local or remote modes)
|
|
47
|
+
-d, --directory <DIRECTORY>
|
|
48
|
+
Optional directory to work in
|
|
49
|
+
-m, --model <MODEL>
|
|
50
|
+
Model to use [default: claude-opus-4-20250514] [possible values: claude-3-5-sonnet-20240620, claude-3-5-sonnet-20241022, claude-3-7-sonnet-20250219, claude-3-5-haiku-20241022, claude-sonnet-4-20250514, claude-opus-4-20250514, gpt-4.1-2025-04-14, gpt-4.1-mini-2025-04-14, gpt-4.1-nano-2025-04-14, o3-2025-04-16, o3-pro-2025-06-10, o4-mini-2025-04-16, gemini-2.5-flash-preview-04-17, gemini-2.5-pro-preview-05-06, gemini-2.5-pro-preview-06-05, grok-3, grok-3-mini, grok-4-0709]
|
|
51
|
+
--remote <REMOTE>
|
|
52
|
+
Connect to a remote gRPC server instead of running locally
|
|
53
|
+
--system-prompt <SYSTEM_PROMPT>
|
|
54
|
+
Custom system prompt to use instead of the default
|
|
55
|
+
--session-config <SESSION_CONFIG>
|
|
56
|
+
Path to session configuration file (TOML format) for new sessions
|
|
57
|
+
--theme <THEME>
|
|
58
|
+
Theme to use for the TUI (defaults to "default")
|
|
59
|
+
-h, --help
|
|
60
|
+
Print help
|
|
61
|
+
-V, --version
|
|
62
|
+
Print version
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Headless mode
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Read prompt from stdin and return a single JSON response
|
|
69
|
+
echo "What is 2+2?" | steer headless
|
|
70
|
+
|
|
71
|
+
# Provide a JSON file containing `Vec<Message>` in the Steer message format
|
|
72
|
+
steer headless --messages-json /tmp/messages.json
|
|
73
|
+
|
|
74
|
+
# Run inside an existing session (keeps history / tool approvals), pipe the prompt from a file
|
|
75
|
+
steer headless --session b4e1a7de-2e83-45ad-977c-2c4efdb3d9c6 < prompt.txt
|
|
76
|
+
|
|
77
|
+
# Supply a custom session configuration (tool approvals, MCP backends, etc.)
|
|
78
|
+
steer headless --session-config config.toml < prompt.txt
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Authentication
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# Launch Steer and follow the first-run setup wizard
|
|
85
|
+
steer
|
|
86
|
+
|
|
87
|
+
# Re-run the wizard any time inside the chat
|
|
88
|
+
/auth
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### gRPC server / remote mode
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
# Start a standalone server (default 127.0.0.1:50051)
|
|
95
|
+
steer server --port 50051
|
|
96
|
+
|
|
97
|
+
# Connect to an already running server
|
|
98
|
+
steer tui --remote 192.168.1.10:50051
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Sessions
|
|
102
|
+
|
|
103
|
+
Steer persists data to a session. You may create, list, delete, and resume sessions.
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# List saved sessions
|
|
107
|
+
steer session list --limit 20
|
|
108
|
+
|
|
109
|
+
# Delete a session
|
|
110
|
+
steer session delete <SESSION_ID> --force
|
|
111
|
+
|
|
112
|
+
# Create a new session with a config file
|
|
113
|
+
steer session create --session-config config.toml
|
|
114
|
+
|
|
115
|
+
# Create with overrides
|
|
116
|
+
steer session create --session-config config.toml --system-prompt "Custom prompt"
|
|
117
|
+
|
|
118
|
+
# Resume a session
|
|
119
|
+
steer --session <SESSION_ID>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Session Configuration Files
|
|
123
|
+
|
|
124
|
+
Sessions can be configured using TOML configuration files. This is useful for:
|
|
125
|
+
- Consistent project-specific configurations
|
|
126
|
+
- Setting up MCP (Model Context Protocol) backends
|
|
127
|
+
- Pre-approving tools or bash commands
|
|
128
|
+
- Sharing configurations with your team
|
|
129
|
+
|
|
130
|
+
#### Example: Minimal Configuration
|
|
131
|
+
```toml
|
|
132
|
+
# session-minimal.toml
|
|
133
|
+
[tool_config]
|
|
134
|
+
backends = [
|
|
135
|
+
{ type = "mcp", server_name = "calculator", transport = { type = "stdio", command = "python", args = ["-m", "mcp_calculator"] }, tool_filter = "all" }
|
|
136
|
+
]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Example: Pre-approved Tools
|
|
140
|
+
```toml
|
|
141
|
+
# session-preapproved.toml
|
|
142
|
+
system_prompt = "You are a helpful coding assistant."
|
|
143
|
+
|
|
144
|
+
[tool_config]
|
|
145
|
+
visibility = "all"
|
|
146
|
+
approval_policy = { type = "pre_approved", tools = ["grep", "ls", "view", "glob", "todo_read"] }
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
#### Example: Full Configuration
|
|
150
|
+
```toml
|
|
151
|
+
# session-full.toml
|
|
152
|
+
system_prompt = "You are a helpful assistant with access to calculator and web tools."
|
|
153
|
+
|
|
154
|
+
[workspace]
|
|
155
|
+
type = "local"
|
|
156
|
+
|
|
157
|
+
[tool_config]
|
|
158
|
+
backends = [
|
|
159
|
+
{ type = "mcp", server_name = "calculator", transport = { type = "stdio", command = "python", args = ["-m", "mcp_calculator"] }, tool_filter = "all" },
|
|
160
|
+
{ type = "mcp", server_name = "web-tools", transport = { type = "tcp", host = "127.0.0.1", port = 3000 }, tool_filter = "all" }
|
|
161
|
+
]
|
|
162
|
+
visibility = "all"
|
|
163
|
+
approval_policy = "always_ask"
|
|
164
|
+
|
|
165
|
+
[metadata]
|
|
166
|
+
project = "my-project"
|
|
167
|
+
environment = "development"
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
See the `examples/` directory for more configuration examples.
|
|
171
|
+
|
|
172
|
+
### MCP Transport Options
|
|
173
|
+
|
|
174
|
+
Steer supports multiple transport types for connecting to MCP servers:
|
|
175
|
+
|
|
176
|
+
#### Stdio
|
|
177
|
+
```toml
|
|
178
|
+
transport = { type = "stdio", command = "python", args = ["-m", "mcp_server"] }
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
#### TCP
|
|
182
|
+
```toml
|
|
183
|
+
transport = { type = "tcp", host = "127.0.0.1", port = 3000 }
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### Unix Socket
|
|
187
|
+
```toml
|
|
188
|
+
transport = { type = "unix", path = "/tmp/mcp.sock" }
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### SSE
|
|
192
|
+
```toml
|
|
193
|
+
transport = { type = "sse", url = "http://localhost:3000/events", headers = { "Authorization" = "Bearer token" } }
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### HTTP
|
|
197
|
+
```toml
|
|
198
|
+
transport = { type = "http", url = "http://localhost:3000", headers = { "X-API-Key" = "secret" } }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Slash commands (inside the chat UI)
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
/help Show help
|
|
207
|
+
/auth Set up authentication for AI providers
|
|
208
|
+
/model Show or change the current model
|
|
209
|
+
/clear Clear conversation history and tool approvals
|
|
210
|
+
/compact Summarize the current conversation
|
|
211
|
+
/theme Change or list available themes
|
|
212
|
+
/mcp Show MCP server connection status
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Notifications
|
|
218
|
+
|
|
219
|
+
Steer supports desktop and audio notifications when certain events occur.
|
|
220
|
+
|
|
221
|
+
### Notification Types
|
|
222
|
+
|
|
223
|
+
| Notification Type | When |
|
|
224
|
+
|---|---|
|
|
225
|
+
| **Processing Complete** | Assistant finishes processing your request |
|
|
226
|
+
| **Tool Approval** | A tool needs your approval (e.g., `bash`, `edit_file`) |
|
|
227
|
+
| **Error** | An error occurs during processing |
|
|
228
|
+
|
|
229
|
+
### Configuration
|
|
230
|
+
|
|
231
|
+
Both sound and desktop notifications are enabled by default. To disable either of them, edit the configuration via `steer preferences edit`:
|
|
232
|
+
|
|
233
|
+
```toml
|
|
234
|
+
[ui.notifications]
|
|
235
|
+
sound = true
|
|
236
|
+
desktop = true
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
## Authentication
|
|
242
|
+
|
|
243
|
+
Steer supports multiple methods for providing credentials.
|
|
244
|
+
|
|
245
|
+
### Interactive Setup
|
|
246
|
+
|
|
247
|
+
The first time you start Steer it should launch a setup wizard. The auth flow can also be triggered via the `/auth` command.
|
|
248
|
+
|
|
249
|
+
All providers (Anthropic, OpenAI, Gemini, xAI) support API key authentication.
|
|
250
|
+
|
|
251
|
+
For Claude Pro/Max users, Steer also supports authenticating via OAuth. **Note**: If OAuth tokens and an API key are saved for Anthropic, the OAuth token takes precedence.
|
|
252
|
+
|
|
253
|
+
Credentials are stored securely using the OS-native keyring.
|
|
254
|
+
|
|
255
|
+
### Environment Variables
|
|
256
|
+
|
|
257
|
+
Steer can also load credentials via environment variables. It will detect the following environment variables:
|
|
258
|
+
|
|
259
|
+
* `ANTHROPIC_API_KEY` or `CLAUDE_API_KEY`
|
|
260
|
+
* `OPENAI_API_KEY`
|
|
261
|
+
* `GOOGLE_API_KEY` or `GEMINI_API_KEY`
|
|
262
|
+
* `XAI_API_KEY` or `GROK_API_KEY`
|
|
263
|
+
|
|
264
|
+
Environment variables take precedence over stored credentials.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Tool approval system
|
|
269
|
+
|
|
270
|
+
Read-only tools run automatically ( `view`, `grep`, `ls`, `glob`, `fetch`, `todo.read` ). Mutating tools ( `edit`, `replace`, `bash`, etc.) require approval on first use, with the option to remember the decision for the rest of the session.
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
### Bash Command Approval
|
|
274
|
+
|
|
275
|
+
You can pre-approve specific bash commands using glob patterns in your session configuration:
|
|
276
|
+
|
|
277
|
+
```toml
|
|
278
|
+
[tool_config.tools.bash]
|
|
279
|
+
approved_patterns = [
|
|
280
|
+
"git status",
|
|
281
|
+
"git log*",
|
|
282
|
+
"npm run*",
|
|
283
|
+
"cargo build*"
|
|
284
|
+
]
|
|
285
|
+
```
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
const { createWriteStream, existsSync, mkdirSync, mkdtemp } = require("fs");
|
|
2
|
+
const { join, sep } = require("path");
|
|
3
|
+
const { spawnSync } = require("child_process");
|
|
4
|
+
const { tmpdir } = require("os");
|
|
5
|
+
|
|
6
|
+
const axios = require("axios");
|
|
7
|
+
const rimraf = require("rimraf");
|
|
8
|
+
const tmpDir = tmpdir();
|
|
9
|
+
|
|
10
|
+
const error = (msg) => {
|
|
11
|
+
console.error(msg);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class Package {
|
|
16
|
+
constructor(name, url, filename, zipExt, binaries) {
|
|
17
|
+
let errors = [];
|
|
18
|
+
if (typeof url !== "string") {
|
|
19
|
+
errors.push("url must be a string");
|
|
20
|
+
} else {
|
|
21
|
+
try {
|
|
22
|
+
new URL(url);
|
|
23
|
+
} catch (e) {
|
|
24
|
+
errors.push(e);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (name && typeof name !== "string") {
|
|
28
|
+
errors.push("package name must be a string");
|
|
29
|
+
}
|
|
30
|
+
if (!name) {
|
|
31
|
+
errors.push("You must specify the name of your package");
|
|
32
|
+
}
|
|
33
|
+
if (binaries && typeof binaries !== "object") {
|
|
34
|
+
errors.push("binaries must be a string => string map");
|
|
35
|
+
}
|
|
36
|
+
if (!binaries) {
|
|
37
|
+
errors.push("You must specify the binaries in the package");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (errors.length > 0) {
|
|
41
|
+
let errorMsg =
|
|
42
|
+
"One or more of the parameters you passed to the Binary constructor are invalid:\n";
|
|
43
|
+
errors.forEach((error) => {
|
|
44
|
+
errorMsg += error;
|
|
45
|
+
});
|
|
46
|
+
errorMsg +=
|
|
47
|
+
'\n\nCorrect usage: new Package("my-binary", "https://example.com/binary/download.tar.gz", {"my-binary": "my-binary"})';
|
|
48
|
+
error(errorMsg);
|
|
49
|
+
}
|
|
50
|
+
this.url = url;
|
|
51
|
+
this.name = name;
|
|
52
|
+
this.filename = filename;
|
|
53
|
+
this.zipExt = zipExt;
|
|
54
|
+
this.installDirectory = join(__dirname, "node_modules", ".bin_real");
|
|
55
|
+
this.binaries = binaries;
|
|
56
|
+
|
|
57
|
+
if (!existsSync(this.installDirectory)) {
|
|
58
|
+
mkdirSync(this.installDirectory, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
exists() {
|
|
63
|
+
for (const binaryName in this.binaries) {
|
|
64
|
+
const binRelPath = this.binaries[binaryName];
|
|
65
|
+
const binPath = join(this.installDirectory, binRelPath);
|
|
66
|
+
if (!existsSync(binPath)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
install(fetchOptions, suppressLogs = false) {
|
|
74
|
+
if (this.exists()) {
|
|
75
|
+
if (!suppressLogs) {
|
|
76
|
+
console.error(
|
|
77
|
+
`${this.name} is already installed, skipping installation.`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return Promise.resolve();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (existsSync(this.installDirectory)) {
|
|
84
|
+
rimraf.sync(this.installDirectory);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
mkdirSync(this.installDirectory, { recursive: true });
|
|
88
|
+
|
|
89
|
+
if (!suppressLogs) {
|
|
90
|
+
console.error(`Downloading release from ${this.url}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return axios({ ...fetchOptions, url: this.url, responseType: "stream" })
|
|
94
|
+
.then((res) => {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
mkdtemp(`${tmpDir}${sep}`, (err, directory) => {
|
|
97
|
+
let tempFile = join(directory, this.filename);
|
|
98
|
+
const sink = res.data.pipe(createWriteStream(tempFile));
|
|
99
|
+
sink.on("error", (err) => reject(err));
|
|
100
|
+
sink.on("close", () => {
|
|
101
|
+
if (/\.tar\.*/.test(this.zipExt)) {
|
|
102
|
+
const result = spawnSync("tar", [
|
|
103
|
+
"xf",
|
|
104
|
+
tempFile,
|
|
105
|
+
// The tarballs are stored with a leading directory
|
|
106
|
+
// component; we strip one component in the
|
|
107
|
+
// shell installers too.
|
|
108
|
+
"--strip-components",
|
|
109
|
+
"1",
|
|
110
|
+
"-C",
|
|
111
|
+
this.installDirectory,
|
|
112
|
+
]);
|
|
113
|
+
if (result.status == 0) {
|
|
114
|
+
resolve();
|
|
115
|
+
} else if (result.error) {
|
|
116
|
+
reject(result.error);
|
|
117
|
+
} else {
|
|
118
|
+
reject(
|
|
119
|
+
new Error(
|
|
120
|
+
`An error occurred untarring the artifact: stdout: ${result.stdout}; stderr: ${result.stderr}`,
|
|
121
|
+
),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
} else if (this.zipExt == ".zip") {
|
|
125
|
+
const result = spawnSync("unzip", [
|
|
126
|
+
"-q",
|
|
127
|
+
tempFile,
|
|
128
|
+
"-d",
|
|
129
|
+
this.installDirectory,
|
|
130
|
+
]);
|
|
131
|
+
if (result.status == 0) {
|
|
132
|
+
resolve();
|
|
133
|
+
} else if (result.error) {
|
|
134
|
+
reject(result.error);
|
|
135
|
+
} else {
|
|
136
|
+
reject(
|
|
137
|
+
new Error(
|
|
138
|
+
`An error occurred unzipping the artifact: stdout: ${result.stdout}; stderr: ${result.stderr}`,
|
|
139
|
+
),
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
reject(
|
|
144
|
+
new Error(`Unrecognized file extension: ${this.zipExt}`),
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
})
|
|
151
|
+
.then(() => {
|
|
152
|
+
if (!suppressLogs) {
|
|
153
|
+
console.error(`${this.name} has been installed!`);
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
.catch((e) => {
|
|
157
|
+
error(`Error fetching release: ${e.message}`);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
run(binaryName, fetchOptions) {
|
|
162
|
+
const promise = !this.exists()
|
|
163
|
+
? this.install(fetchOptions, true)
|
|
164
|
+
: Promise.resolve();
|
|
165
|
+
|
|
166
|
+
promise
|
|
167
|
+
.then(() => {
|
|
168
|
+
const [, , ...args] = process.argv;
|
|
169
|
+
|
|
170
|
+
const options = { cwd: process.cwd(), stdio: "inherit" };
|
|
171
|
+
|
|
172
|
+
const binRelPath = this.binaries[binaryName];
|
|
173
|
+
if (!binRelPath) {
|
|
174
|
+
error(`${binaryName} is not a known binary in ${this.name}`);
|
|
175
|
+
}
|
|
176
|
+
const binPath = join(this.installDirectory, binRelPath);
|
|
177
|
+
const result = spawnSync(binPath, args, options);
|
|
178
|
+
|
|
179
|
+
if (result.error) {
|
|
180
|
+
error(result.error);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
process.exit(result.status);
|
|
184
|
+
})
|
|
185
|
+
.catch((e) => {
|
|
186
|
+
error(e.message);
|
|
187
|
+
process.exit(1);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
module.exports.Package = Package;
|
package/binary.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
const { Package } = require("./binary-install");
|
|
2
|
+
const os = require("os");
|
|
3
|
+
const cTable = require("console.table");
|
|
4
|
+
const libc = require("detect-libc");
|
|
5
|
+
const { configureProxy } = require("axios-proxy-builder");
|
|
6
|
+
|
|
7
|
+
const error = (msg) => {
|
|
8
|
+
console.error(msg);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
name,
|
|
14
|
+
artifactDownloadUrl,
|
|
15
|
+
supportedPlatforms,
|
|
16
|
+
glibcMinimum,
|
|
17
|
+
} = require("./package.json");
|
|
18
|
+
|
|
19
|
+
const builderGlibcMajorVersion = glibcMinimum.major;
|
|
20
|
+
const builderGlibcMInorVersion = glibcMinimum.series;
|
|
21
|
+
|
|
22
|
+
const getPlatform = () => {
|
|
23
|
+
const rawOsType = os.type();
|
|
24
|
+
const rawArchitecture = os.arch();
|
|
25
|
+
|
|
26
|
+
// We want to use rust-style target triples as the canonical key
|
|
27
|
+
// for a platform, so translate the "os" library's concepts into rust ones
|
|
28
|
+
let osType = "";
|
|
29
|
+
switch (rawOsType) {
|
|
30
|
+
case "Windows_NT":
|
|
31
|
+
osType = "pc-windows-msvc";
|
|
32
|
+
break;
|
|
33
|
+
case "Darwin":
|
|
34
|
+
osType = "apple-darwin";
|
|
35
|
+
break;
|
|
36
|
+
case "Linux":
|
|
37
|
+
osType = "unknown-linux-gnu";
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let arch = "";
|
|
42
|
+
switch (rawArchitecture) {
|
|
43
|
+
case "x64":
|
|
44
|
+
arch = "x86_64";
|
|
45
|
+
break;
|
|
46
|
+
case "arm64":
|
|
47
|
+
arch = "aarch64";
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (rawOsType === "Linux") {
|
|
52
|
+
if (libc.familySync() == "musl") {
|
|
53
|
+
osType = "unknown-linux-musl-dynamic";
|
|
54
|
+
} else if (libc.isNonGlibcLinuxSync()) {
|
|
55
|
+
console.warn(
|
|
56
|
+
"Your libc is neither glibc nor musl; trying static musl binary instead",
|
|
57
|
+
);
|
|
58
|
+
osType = "unknown-linux-musl-static";
|
|
59
|
+
} else {
|
|
60
|
+
let libcVersion = libc.versionSync();
|
|
61
|
+
let splitLibcVersion = libcVersion.split(".");
|
|
62
|
+
let libcMajorVersion = splitLibcVersion[0];
|
|
63
|
+
let libcMinorVersion = splitLibcVersion[1];
|
|
64
|
+
if (
|
|
65
|
+
libcMajorVersion != builderGlibcMajorVersion ||
|
|
66
|
+
libcMinorVersion < builderGlibcMInorVersion
|
|
67
|
+
) {
|
|
68
|
+
// We can't run the glibc binaries, but we can run the static musl ones
|
|
69
|
+
// if they exist
|
|
70
|
+
console.warn(
|
|
71
|
+
"Your glibc isn't compatible; trying static musl binary instead",
|
|
72
|
+
);
|
|
73
|
+
osType = "unknown-linux-musl-static";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Assume the above succeeded and build a target triple to look things up with.
|
|
79
|
+
// If any of it failed, this lookup will fail and we'll handle it like normal.
|
|
80
|
+
let targetTriple = `${arch}-${osType}`;
|
|
81
|
+
let platform = supportedPlatforms[targetTriple];
|
|
82
|
+
|
|
83
|
+
if (!platform) {
|
|
84
|
+
error(
|
|
85
|
+
`Platform with type "${rawOsType}" and architecture "${rawArchitecture}" is not supported by ${name}.\nYour system must be one of the following:\n\n${Object.keys(
|
|
86
|
+
supportedPlatforms,
|
|
87
|
+
).join(",")}`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return platform;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const getPackage = () => {
|
|
95
|
+
const platform = getPlatform();
|
|
96
|
+
const url = `${artifactDownloadUrl}/${platform.artifactName}`;
|
|
97
|
+
let filename = platform.artifactName;
|
|
98
|
+
let ext = platform.zipExt;
|
|
99
|
+
let binary = new Package(name, url, filename, ext, platform.bins);
|
|
100
|
+
|
|
101
|
+
return binary;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const install = (suppressLogs) => {
|
|
105
|
+
if (!artifactDownloadUrl || artifactDownloadUrl.length === 0) {
|
|
106
|
+
console.warn("in demo mode, not installing binaries");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const package = getPackage();
|
|
110
|
+
const proxy = configureProxy(package.url);
|
|
111
|
+
|
|
112
|
+
return package.install(proxy, suppressLogs);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const run = (binaryName) => {
|
|
116
|
+
const package = getPackage();
|
|
117
|
+
const proxy = configureProxy(package.url);
|
|
118
|
+
|
|
119
|
+
package.run(binaryName, proxy);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
module.exports = {
|
|
123
|
+
install,
|
|
124
|
+
run,
|
|
125
|
+
getPackage,
|
|
126
|
+
};
|
package/install.js
ADDED