@callstack/apex 0.4.2 → 0.4.4
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 +4 -0
- package/dist/cli.js +59 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -234,6 +234,10 @@ Other launchers: `apex run opencode`, `apex run claude`, `apex run pi`, `apex ru
|
|
|
234
234
|
Launchers pass extra arguments through unchanged (without a shell), can be overridden by explicit
|
|
235
235
|
user arguments, and never run `init`. `apex run claude` sets the gateway URL, token, attribution
|
|
236
236
|
flag and model aliases in the child process only, leaving your default Claude provider alone.
|
|
237
|
+
`apex run opencode` checks which OpenCode is installed: OpenCode 1 gets `--model`; OpenCode 2,
|
|
238
|
+
whose terminal interface has no `--model`, starts with `--standalone` and the model in
|
|
239
|
+
`OPENCODE_CONFIG_CONTENT`, so the private server sees the model and, for a config that references
|
|
240
|
+
the key variable, your key too.
|
|
237
241
|
|
|
238
242
|
## Safety and recovery
|
|
239
243
|
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { isDeepStrictEqual, parseArgs, styleText } from "node:util";
|
|
3
|
-
import { spawn } from "node:child_process";
|
|
3
|
+
import { execFile, spawn } from "node:child_process";
|
|
4
4
|
import { access, lstat, mkdir, open, readFile, rename, stat, unlink } from "node:fs/promises";
|
|
5
5
|
import { basename, delimiter, dirname, join, parse, resolve } from "node:path";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -2602,6 +2602,15 @@ async function executable(command, env = process.env, platform = process.platfor
|
|
|
2602
2602
|
}
|
|
2603
2603
|
return null;
|
|
2604
2604
|
}
|
|
2605
|
+
function openCodeMajor(binary) {
|
|
2606
|
+
if (!binary) return Promise.resolve(1);
|
|
2607
|
+
return new Promise((resolve) => {
|
|
2608
|
+
execFile(binary, ["--version"], { timeout: 1e4 }, (error, stdout) => {
|
|
2609
|
+
const match = !error && /^(?:opencode )?v?(\d+)\.\d+\.\d+\s*$/m.exec(stdout);
|
|
2610
|
+
resolve(match ? Number(match[1]) : 1);
|
|
2611
|
+
});
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2605
2614
|
function locations(home = homedir(), env = process.env, platform = process.platform) {
|
|
2606
2615
|
const config = env.XDG_CONFIG_HOME || join(home, ".config");
|
|
2607
2616
|
const userData = platform === "darwin" ? join(home, "Library", "Application Support") : platform === "win32" ? env.APPDATA || join(home, "AppData", "Roaming") : config;
|
|
@@ -2837,16 +2846,34 @@ async function planAssistant(assistant, provider = PROVIDERS.vercel) {
|
|
|
2837
2846
|
}
|
|
2838
2847
|
return changes;
|
|
2839
2848
|
}
|
|
2840
|
-
function
|
|
2841
|
-
const
|
|
2842
|
-
|
|
2843
|
-
|
|
2849
|
+
function launchToken(env, provider) {
|
|
2850
|
+
const token = env[provider.envVar];
|
|
2851
|
+
if (!token?.trim()) throw new Error(`Set ${provider.envVar} in your environment before launching with ${provider.name}.`);
|
|
2852
|
+
return token;
|
|
2853
|
+
}
|
|
2854
|
+
function launchOptions(id, env = process.env, provider = defaultProvider(env), { openCode = 1 } = {}) {
|
|
2855
|
+
const { model } = provider;
|
|
2856
|
+
const token = launchToken(env, provider);
|
|
2844
2857
|
const childEnv = { ...env };
|
|
2845
2858
|
switch (id) {
|
|
2846
|
-
case "opencode":
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2859
|
+
case "opencode": {
|
|
2860
|
+
const selected = `${provider.opencode}/${model}`;
|
|
2861
|
+
if (openCode < 2) return {
|
|
2862
|
+
args: ["--model", selected],
|
|
2863
|
+
env: childEnv
|
|
2864
|
+
};
|
|
2865
|
+
const inline = env.OPENCODE_CONFIG_CONTENT?.trim() ? parseJson(env.OPENCODE_CONFIG_CONTENT, "OPENCODE_CONFIG_CONTENT") : {};
|
|
2866
|
+
childEnv.OPENCODE_CONFIG_CONTENT = JSON.stringify({
|
|
2867
|
+
...inline,
|
|
2868
|
+
model: selected
|
|
2869
|
+
});
|
|
2870
|
+
return {
|
|
2871
|
+
args: ["--standalone"],
|
|
2872
|
+
env: childEnv,
|
|
2873
|
+
afterCommand: true,
|
|
2874
|
+
note: "(OPENCODE_CONFIG_CONTENT model set)"
|
|
2875
|
+
};
|
|
2876
|
+
}
|
|
2850
2877
|
case "codex": return {
|
|
2851
2878
|
args: ["--profile", provider.codex],
|
|
2852
2879
|
env: childEnv
|
|
@@ -2884,15 +2911,15 @@ function launchOptions(id, env = process.env, provider = defaultProvider(env)) {
|
|
|
2884
2911
|
});
|
|
2885
2912
|
return {
|
|
2886
2913
|
args: ["--model", model],
|
|
2887
|
-
env: childEnv
|
|
2914
|
+
env: childEnv,
|
|
2915
|
+
note: "(ANTHROPIC_* gateway environment set)"
|
|
2888
2916
|
};
|
|
2889
2917
|
default: throw new Error(`Cannot launch ${id}. Use the editor's model selector.`);
|
|
2890
2918
|
}
|
|
2891
2919
|
}
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
return [`${id} ${args.join(" ")}`, RUN_NOTE[id]].filter(Boolean).join(" ");
|
|
2920
|
+
function runExpansion(id, provider, installed = {}) {
|
|
2921
|
+
const { args, note } = launchOptions(id, { [provider.envVar]: "placeholder" }, provider, installed);
|
|
2922
|
+
return [`${id} ${args.join(" ")}`, note].filter(Boolean).join(" ");
|
|
2896
2923
|
}
|
|
2897
2924
|
const copilotModel = (provider) => JSON.stringify({
|
|
2898
2925
|
id: provider.model,
|
|
@@ -4796,6 +4823,7 @@ async function planAll(assistants, options, provider) {
|
|
|
4796
4823
|
name: NAMES[assistant.id],
|
|
4797
4824
|
detected: assistant.detected,
|
|
4798
4825
|
evidence: assistant.evidence || null,
|
|
4826
|
+
binary: assistant.binary ?? null,
|
|
4799
4827
|
manual: MANUAL_IDS.includes(assistant.id),
|
|
4800
4828
|
error: null,
|
|
4801
4829
|
files: [],
|
|
@@ -4970,9 +4998,10 @@ async function commandInit(rest) {
|
|
|
4970
4998
|
dim("Apex CLI never reads, stores or logs the key itself.")
|
|
4971
4999
|
]);
|
|
4972
5000
|
const runnable = active.map((entry) => entry.id).filter((id) => RUNNABLE.includes(id));
|
|
5001
|
+
const installed = { openCode: await openCodeMajor(active.find((entry) => entry.id === "opencode")?.binary) };
|
|
4973
5002
|
const rows = runnable.map((id) => ({
|
|
4974
5003
|
command: `${apex} run ${id}`,
|
|
4975
|
-
expansion: runExpansion(id, provider)
|
|
5004
|
+
expansion: runExpansion(id, provider, installed)
|
|
4976
5005
|
}));
|
|
4977
5006
|
const commandWidth = columnWidth(rows.map((row) => row.command));
|
|
4978
5007
|
section(runnable.length ? `Use these commands to run ${provider.model} with your selected harnesses:` : `Use ${provider.model} with your assistant:`, [
|
|
@@ -5139,6 +5168,17 @@ function takeProvider(args) {
|
|
|
5139
5168
|
rest: args
|
|
5140
5169
|
};
|
|
5141
5170
|
}
|
|
5171
|
+
const isDirectory = (path) => stat(path).then((info) => info.isDirectory(), () => false);
|
|
5172
|
+
async function launchArgs(options, args) {
|
|
5173
|
+
const [first, ...rest] = args;
|
|
5174
|
+
if (options.afterCommand && ["run", "mini"].includes(first)) return [
|
|
5175
|
+
first,
|
|
5176
|
+
...options.args,
|
|
5177
|
+
...rest
|
|
5178
|
+
];
|
|
5179
|
+
if (options.afterCommand && first && !first.startsWith("-") && !await isDirectory(first)) return args;
|
|
5180
|
+
return [...options.args, ...args];
|
|
5181
|
+
}
|
|
5142
5182
|
async function commandRun(rest) {
|
|
5143
5183
|
const before = takeProvider(rest);
|
|
5144
5184
|
const [id, ...after] = before.rest;
|
|
@@ -5150,12 +5190,14 @@ async function commandRun(rest) {
|
|
|
5150
5190
|
const chosen = before.value ?? behind.value;
|
|
5151
5191
|
if (chosen !== null && !PROVIDER_IDS.includes(chosen)) throw new Error(`Unknown --provider. Choose from: ${PROVIDER_IDS.join(", ")}`);
|
|
5152
5192
|
const passed = behind.rest;
|
|
5153
|
-
const
|
|
5193
|
+
const provider = chosen === null ? defaultProvider() : PROVIDERS[chosen];
|
|
5194
|
+
launchToken(process.env, provider);
|
|
5154
5195
|
const binary = await executable(id);
|
|
5155
5196
|
if (!binary) throw new Error(`${id} was not found on PATH. Install it first, then run ${APEX} init.`);
|
|
5156
5197
|
if (process.platform === "win32" && /\.(cmd|bat)$/i.test(binary)) throw new Error("Windows .cmd/.bat launchers are not executed through a shell. Use WSL or launch the configured assistant directly.");
|
|
5198
|
+
const options = launchOptions(id, process.env, provider, { openCode: await openCodeMajor(id === "opencode" && binary) });
|
|
5157
5199
|
const args = passed[0] === "--" ? passed.slice(1) : passed;
|
|
5158
|
-
const child = spawn(binary,
|
|
5200
|
+
const child = spawn(binary, await launchArgs(options, args), {
|
|
5159
5201
|
env: options.env,
|
|
5160
5202
|
stdio: "inherit",
|
|
5161
5203
|
shell: false
|