traderclaw-cli 1.0.73 → 1.0.74
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/bin/installer-step-engine.mjs +178 -3
- package/bin/openclaw-trader.mjs +152 -25
- package/package.json +2 -2
|
@@ -1562,6 +1562,115 @@ function configureOpenClawLlmProvider({ provider, model, credential }, configPat
|
|
|
1562
1562
|
return { configPath, provider, model };
|
|
1563
1563
|
}
|
|
1564
1564
|
|
|
1565
|
+
/**
|
|
1566
|
+
* Sets only `agents.defaults.model.primary` (OAuth / subscription paths where credentials live in OpenClaw auth profiles).
|
|
1567
|
+
* Does not write API keys into config.env.
|
|
1568
|
+
*/
|
|
1569
|
+
function configureOpenClawLlmModelPrimaryOnly({ provider, model }, configPath = CONFIG_FILE) {
|
|
1570
|
+
if (!provider || !model) {
|
|
1571
|
+
throw new Error("LLM provider and model are required.");
|
|
1572
|
+
}
|
|
1573
|
+
if (!model.startsWith(`${provider}/`)) {
|
|
1574
|
+
throw new Error(`Selected model '${model}' does not match provider '${provider}'.`);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
let config = {};
|
|
1578
|
+
try {
|
|
1579
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
1580
|
+
} catch {
|
|
1581
|
+
config = {};
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
if (!config.agents) config.agents = {};
|
|
1585
|
+
if (!config.agents.defaults) config.agents.defaults = {};
|
|
1586
|
+
ensureAgentsDefaultsSchemaCompat(config);
|
|
1587
|
+
if (!config.agents.defaults.model || typeof config.agents.defaults.model !== "object") {
|
|
1588
|
+
config.agents.defaults.model = {};
|
|
1589
|
+
}
|
|
1590
|
+
config.agents.defaults.model.primary = model;
|
|
1591
|
+
|
|
1592
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
1593
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1594
|
+
return { configPath, provider, model };
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
/**
|
|
1598
|
+
* Runs `openclaw models auth login --provider openai-codex` and feeds the pasted redirect URL or code on stdin
|
|
1599
|
+
* when the CLI prompts (with a timed fallback for non-interactive / SSH).
|
|
1600
|
+
*/
|
|
1601
|
+
function runOpenClawCodexOAuthLogin(paste, emitLog) {
|
|
1602
|
+
return new Promise((resolve, reject) => {
|
|
1603
|
+
const child = spawn("openclaw", ["models", "auth", "login", "--provider", "openai-codex"], {
|
|
1604
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1605
|
+
shell: false,
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
let stdout = "";
|
|
1609
|
+
let stderr = "";
|
|
1610
|
+
let pasteSent = false;
|
|
1611
|
+
|
|
1612
|
+
const sendPaste = () => {
|
|
1613
|
+
if (pasteSent) return;
|
|
1614
|
+
const p = String(paste || "").trim();
|
|
1615
|
+
if (!p) return;
|
|
1616
|
+
pasteSent = true;
|
|
1617
|
+
try {
|
|
1618
|
+
child.stdin.write(`${p}\n`);
|
|
1619
|
+
} catch {
|
|
1620
|
+
// ignore
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
|
|
1624
|
+
let fallbackTimer = setTimeout(() => sendPaste(), 9000);
|
|
1625
|
+
|
|
1626
|
+
const onChunk = (chunk) => {
|
|
1627
|
+
const combined = (stdout + stderr).toLowerCase();
|
|
1628
|
+
const c = typeof chunk === "string" ? chunk : chunk.toString();
|
|
1629
|
+
if (!pasteSent && /paste|authorization|redirect|callback/i.test(combined) && c.length > 0) {
|
|
1630
|
+
clearTimeout(fallbackTimer);
|
|
1631
|
+
fallbackTimer = setTimeout(() => sendPaste(), 400);
|
|
1632
|
+
}
|
|
1633
|
+
};
|
|
1634
|
+
|
|
1635
|
+
child.stdout?.on("data", (d) => {
|
|
1636
|
+
const t = d.toString();
|
|
1637
|
+
stdout += t;
|
|
1638
|
+
const urls = extractUrls(t);
|
|
1639
|
+
emitLog("info", t, urls);
|
|
1640
|
+
onChunk(t);
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1643
|
+
child.stderr?.on("data", (d) => {
|
|
1644
|
+
const t = d.toString();
|
|
1645
|
+
stderr += t;
|
|
1646
|
+
const urls = extractUrls(t);
|
|
1647
|
+
emitLog("warn", t, urls);
|
|
1648
|
+
onChunk(t);
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
child.on("close", (code) => {
|
|
1652
|
+
clearTimeout(fallbackTimer);
|
|
1653
|
+
if (code === 0) {
|
|
1654
|
+
resolve({ stdout, stderr });
|
|
1655
|
+
return;
|
|
1656
|
+
}
|
|
1657
|
+
const detail = `${stderr}\n${stdout}`.trim();
|
|
1658
|
+
const err = new Error(
|
|
1659
|
+
detail || `openclaw models auth login failed with exit code ${code}. Try running the same command in a normal shell, then re-run the wizard with "already logged in" checked.`,
|
|
1660
|
+
);
|
|
1661
|
+
err.code = code;
|
|
1662
|
+
err.stdout = stdout;
|
|
1663
|
+
err.stderr = stderr;
|
|
1664
|
+
reject(err);
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
child.on("error", (e) => {
|
|
1668
|
+
clearTimeout(fallbackTimer);
|
|
1669
|
+
reject(e);
|
|
1670
|
+
});
|
|
1671
|
+
});
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1565
1674
|
function verifyInstallation(modeConfig, apiKey) {
|
|
1566
1675
|
const gatewayFile = join(CONFIG_DIR, "gateway", modeConfig.gatewayConfig);
|
|
1567
1676
|
let llmConfigured = false;
|
|
@@ -1645,9 +1754,12 @@ export class InstallerStepEngine {
|
|
|
1645
1754
|
this.modeConfig = modeConfig;
|
|
1646
1755
|
this.options = {
|
|
1647
1756
|
lane: normalizeLane(options.lane),
|
|
1757
|
+
llmAuthMode: options.llmAuthMode === "oauth" ? "oauth" : "api_key",
|
|
1648
1758
|
llmProvider: options.llmProvider || "",
|
|
1649
1759
|
llmModel: options.llmModel || "",
|
|
1650
1760
|
llmCredential: options.llmCredential || "",
|
|
1761
|
+
llmOAuthPaste: typeof options.llmOAuthPaste === "string" ? options.llmOAuthPaste.trim() : "",
|
|
1762
|
+
llmOAuthSkipLogin: options.llmOAuthSkipLogin === true,
|
|
1651
1763
|
apiKey: options.apiKey || "",
|
|
1652
1764
|
orchestratorUrl: options.orchestratorUrl || "https://api.traderclaw.ai",
|
|
1653
1765
|
gatewayBaseUrl: options.gatewayBaseUrl || "",
|
|
@@ -1924,15 +2036,78 @@ export class InstallerStepEngine {
|
|
|
1924
2036
|
const provider = String(this.options.llmProvider || "").trim();
|
|
1925
2037
|
const requestedModel = String(this.options.llmModel || "").trim();
|
|
1926
2038
|
const credential = String(this.options.llmCredential || "").trim();
|
|
1927
|
-
|
|
2039
|
+
const authMode = this.options.llmAuthMode === "oauth" ? "oauth" : "api_key";
|
|
2040
|
+
|
|
2041
|
+
if (!provider) {
|
|
1928
2042
|
throw new Error(
|
|
1929
|
-
"Missing required LLM settings. Select provider
|
|
2043
|
+
"Missing required LLM settings. Select provider in the wizard before starting installation.",
|
|
1930
2044
|
);
|
|
1931
2045
|
}
|
|
1932
2046
|
if (!commandExists("openclaw")) {
|
|
1933
2047
|
throw new Error("OpenClaw is not available yet. Install step must complete before LLM configuration.");
|
|
1934
2048
|
}
|
|
1935
2049
|
|
|
2050
|
+
if (authMode === "oauth") {
|
|
2051
|
+
if (provider !== "openai-codex") {
|
|
2052
|
+
throw new Error("OAuth mode requires LLM provider openai-codex (ChatGPT / Codex subscription).");
|
|
2053
|
+
}
|
|
2054
|
+
const skipLogin = this.options.llmOAuthSkipLogin === true;
|
|
2055
|
+
const oauthPaste = String(this.options.llmOAuthPaste || "").trim();
|
|
2056
|
+
if (!skipLogin && !oauthPaste) {
|
|
2057
|
+
throw new Error(
|
|
2058
|
+
"Codex OAuth requires a pasted authorization code or redirect URL, or enable skip if you already ran openclaw models auth login on this host.",
|
|
2059
|
+
);
|
|
2060
|
+
}
|
|
2061
|
+
if (!skipLogin) {
|
|
2062
|
+
try {
|
|
2063
|
+
await runOpenClawCodexOAuthLogin(oauthPaste, (level, text, urls) =>
|
|
2064
|
+
this.emitLog("configure_llm", level, text, urls || []),
|
|
2065
|
+
);
|
|
2066
|
+
} catch (err) {
|
|
2067
|
+
const tail = `${err?.stderr || ""}\n${err?.stdout || ""}\n${err?.message || ""}`.trim();
|
|
2068
|
+
throw new Error(
|
|
2069
|
+
`${tail}\n\nIf OAuth cannot complete from the wizard, run in a shell: openclaw models auth login --provider openai-codex — then re-run the wizard with "already logged in" checked.`,
|
|
2070
|
+
);
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
const selection = resolveLlmModelSelection(provider, requestedModel);
|
|
2075
|
+
for (const msg of selection.warnings) {
|
|
2076
|
+
this.emitLog("configure_llm", "warn", msg);
|
|
2077
|
+
}
|
|
2078
|
+
const model = selection.model;
|
|
2079
|
+
|
|
2080
|
+
const saved = configureOpenClawLlmModelPrimaryOnly({ provider, model });
|
|
2081
|
+
this.emitLog(
|
|
2082
|
+
"configure_llm",
|
|
2083
|
+
"info",
|
|
2084
|
+
`Configured OpenClaw model primary=${model} (Codex OAuth; credentials in OpenClaw auth profiles, not OPENAI_API_KEY).`,
|
|
2085
|
+
);
|
|
2086
|
+
|
|
2087
|
+
await runCommandWithEvents("openclaw", ["config", "validate"], {
|
|
2088
|
+
onEvent: (evt) => this.emitLog("configure_llm", evt.type === "stderr" ? "warn" : "info", evt.text, evt.urls || []),
|
|
2089
|
+
});
|
|
2090
|
+
|
|
2091
|
+
try {
|
|
2092
|
+
await runCommandWithEvents("openclaw", ["models", "status", "--check", "--probe-provider", provider], {
|
|
2093
|
+
onEvent: (evt) => this.emitLog("configure_llm", evt.type === "stderr" ? "warn" : "info", evt.text, evt.urls || []),
|
|
2094
|
+
});
|
|
2095
|
+
} catch (err) {
|
|
2096
|
+
const details = `${err?.stderr || ""}\n${err?.stdout || ""}\n${err?.message || ""}`.trim();
|
|
2097
|
+
throw new Error(
|
|
2098
|
+
`LLM provider validation failed for '${provider}'. Check OAuth login and model, then retry.\n${details}`,
|
|
2099
|
+
);
|
|
2100
|
+
}
|
|
2101
|
+
|
|
2102
|
+
return { configured: true, provider, model, configPath: saved.configPath, authMode: "oauth" };
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
if (!credential) {
|
|
2106
|
+
throw new Error(
|
|
2107
|
+
"Missing required LLM settings. Paste your API key or token in the wizard before starting installation.",
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
|
|
1936
2111
|
const selection = resolveLlmModelSelection(provider, requestedModel);
|
|
1937
2112
|
for (const msg of selection.warnings) {
|
|
1938
2113
|
this.emitLog("configure_llm", "warn", msg);
|
|
@@ -1957,7 +2132,7 @@ export class InstallerStepEngine {
|
|
|
1957
2132
|
);
|
|
1958
2133
|
}
|
|
1959
2134
|
|
|
1960
|
-
return { configured: true, provider, model, configPath: saved.configPath };
|
|
2135
|
+
return { configured: true, provider, model, configPath: saved.configPath, authMode: "api_key" };
|
|
1961
2136
|
}
|
|
1962
2137
|
|
|
1963
2138
|
buildSetupHandoff() {
|
package/bin/openclaw-trader.mjs
CHANGED
|
@@ -1689,6 +1689,9 @@ function parseInstallWizardArgs(args) {
|
|
|
1689
1689
|
llmProvider: "",
|
|
1690
1690
|
llmModel: "",
|
|
1691
1691
|
llmCredential: "",
|
|
1692
|
+
llmAuthMode: "api_key",
|
|
1693
|
+
llmOAuthPaste: "",
|
|
1694
|
+
llmOAuthSkipLogin: false,
|
|
1692
1695
|
orchestratorUrl: "https://api.traderclaw.ai",
|
|
1693
1696
|
gatewayBaseUrl: "",
|
|
1694
1697
|
gatewayToken: "",
|
|
@@ -1709,6 +1712,12 @@ function parseInstallWizardArgs(args) {
|
|
|
1709
1712
|
if (key === "--llm-provider" && next) out.llmProvider = args[++i];
|
|
1710
1713
|
if (key === "--llm-model" && next) out.llmModel = args[++i];
|
|
1711
1714
|
if ((key === "--llm-api-key" || key === "--llm-token") && next) out.llmCredential = args[++i];
|
|
1715
|
+
if (key === "--llm-auth" && next) {
|
|
1716
|
+
const v = args[++i];
|
|
1717
|
+
out.llmAuthMode = v === "oauth" ? "oauth" : "api_key";
|
|
1718
|
+
}
|
|
1719
|
+
if (key === "--llm-oauth-paste" && next) out.llmOAuthPaste = args[++i];
|
|
1720
|
+
if (key === "--llm-oauth-skip-login") out.llmOAuthSkipLogin = true;
|
|
1712
1721
|
if ((key === "--url" || key === "-u") && next) out.orchestratorUrl = args[++i];
|
|
1713
1722
|
if ((key === "--gateway-base-url" || key === "-g") && next) out.gatewayBaseUrl = args[++i];
|
|
1714
1723
|
if ((key === "--gateway-token" || key === "-t") && next) out.gatewayToken = args[++i];
|
|
@@ -1867,7 +1876,7 @@ async function cmdPrecheck(args) {
|
|
|
1867
1876
|
|
|
1868
1877
|
log.info("Manual staging run commands:");
|
|
1869
1878
|
log.info(" 1) traderclaw install --wizard");
|
|
1870
|
-
log.info(" 2) In wizard, set LLM
|
|
1879
|
+
log.info(" 2) In wizard, set LLM (API key or Codex OAuth) and Telegram token");
|
|
1871
1880
|
log.info(" 3) Approve tailscale login in provided URL");
|
|
1872
1881
|
log.info(" 4) Confirm /v1/responses returns non-404 on funnel host");
|
|
1873
1882
|
log.info(" 5) Verify Telegram channel setup + probe");
|
|
@@ -1944,7 +1953,13 @@ async function loadWizardLlmCatalogAsync() {
|
|
|
1944
1953
|
},
|
|
1945
1954
|
{
|
|
1946
1955
|
id: "openai-codex",
|
|
1947
|
-
models: [
|
|
1956
|
+
models: [
|
|
1957
|
+
{ id: "openai-codex/gpt-5.4", name: "GPT-5.4 Codex (recommended, ChatGPT OAuth)" },
|
|
1958
|
+
{
|
|
1959
|
+
id: "openai-codex/gpt-5.3-codex-spark",
|
|
1960
|
+
name: "GPT-5.3 Codex Spark (subscription entitlement; experimental)",
|
|
1961
|
+
},
|
|
1962
|
+
],
|
|
1948
1963
|
},
|
|
1949
1964
|
{
|
|
1950
1965
|
id: "google",
|
|
@@ -2027,7 +2042,8 @@ function wizardHtml(defaults) {
|
|
|
2027
2042
|
.card { background:#121a31; border:1px solid #22315a; border-radius: 12px; padding: 16px; margin-bottom: 16px; }
|
|
2028
2043
|
.grid { display:grid; grid-template-columns:1fr 1fr; gap: 12px; }
|
|
2029
2044
|
label { display:block; font-size: 12px; color:#9cb0de; margin-bottom: 4px; }
|
|
2030
|
-
input, select { width:100%; padding:10px; border-radius:8px; border:1px solid #334a87; background:#0d1530; color:#e8eef9; }
|
|
2045
|
+
input, select, textarea { width:100%; padding:10px; border-radius:8px; border:1px solid #334a87; background:#0d1530; color:#e8eef9; font-family: inherit; font-size: 14px; box-sizing: border-box; }
|
|
2046
|
+
textarea { min-height: 88px; resize: vertical; }
|
|
2031
2047
|
button { border:0; border-radius:8px; padding:10px 14px; background:#4d7cff; color:#fff; cursor:pointer; font-weight:600; }
|
|
2032
2048
|
button:disabled { opacity:0.6; cursor:not-allowed; }
|
|
2033
2049
|
.muted { color:#9cb0de; font-size:13px; }
|
|
@@ -2069,9 +2085,20 @@ function wizardHtml(defaults) {
|
|
|
2069
2085
|
</div>
|
|
2070
2086
|
<div class="card" id="llmCard">
|
|
2071
2087
|
<h3>Required: OpenClaw LLM Provider</h3>
|
|
2072
|
-
<p class="muted">
|
|
2073
|
-
<div
|
|
2074
|
-
<
|
|
2088
|
+
<p class="muted">Use an API key for OpenAI Platform and other providers, or ChatGPT/Codex OAuth for subscription access (no separate API billing). OAuth tokens are stored by OpenClaw — not as <code>OPENAI_API_KEY</code>.</p>
|
|
2089
|
+
<div style="margin-bottom:12px;">
|
|
2090
|
+
<label style="margin-bottom:8px;">LLM authentication</label>
|
|
2091
|
+
<label style="display:flex; align-items:flex-start; gap:8px; font-size:14px; color:#e8eef9; margin-bottom:6px; cursor:pointer;">
|
|
2092
|
+
<input id="llmAuthModeApiKey" name="llmAuthMode" type="radio" value="api_key" style="width:auto; margin-top:3px;" checked />
|
|
2093
|
+
<span><strong>API key</strong> — OpenAI Platform (<code>openai</code>), Anthropic, OpenRouter, etc.</span>
|
|
2094
|
+
</label>
|
|
2095
|
+
<label style="display:flex; align-items:flex-start; gap:8px; font-size:14px; color:#e8eef9; cursor:pointer;">
|
|
2096
|
+
<input id="llmAuthModeOauth" name="llmAuthMode" type="radio" value="oauth" style="width:auto; margin-top:3px;" />
|
|
2097
|
+
<span><strong>ChatGPT / Codex (OAuth)</strong> — provider <code>openai-codex</code> (Plus/Pro subscription via OpenClaw login)</span>
|
|
2098
|
+
</label>
|
|
2099
|
+
</div>
|
|
2100
|
+
<div class="grid" id="llmProviderModelGrid">
|
|
2101
|
+
<div id="llmProviderWrap">
|
|
2075
2102
|
<label>LLM provider (required)</label>
|
|
2076
2103
|
<select id="llmProvider"></select>
|
|
2077
2104
|
</div>
|
|
@@ -2080,21 +2107,31 @@ function wizardHtml(defaults) {
|
|
|
2080
2107
|
<select id="llmModel"></select>
|
|
2081
2108
|
</div>
|
|
2082
2109
|
</div>
|
|
2110
|
+
<p class="muted hidden" id="llmOauthProviderNote">Provider is fixed to <code>openai-codex</code>. Pick a Codex model below (or enable manual selection).</p>
|
|
2083
2111
|
<div style="margin-top:8px;">
|
|
2084
2112
|
<label style="display:flex; align-items:center; gap:8px; font-size:13px; color:#9cb0de;">
|
|
2085
2113
|
<input id="llmModelManual" type="checkbox" style="width:auto; padding:0; margin:0;" />
|
|
2086
2114
|
Choose model manually (advanced)
|
|
2087
2115
|
</label>
|
|
2088
2116
|
</div>
|
|
2089
|
-
<div style="margin-top:12px;">
|
|
2117
|
+
<div style="margin-top:12px;" id="llmApiKeyBlock">
|
|
2090
2118
|
<label>LLM API key or token (required)</label>
|
|
2091
2119
|
<input id="llmCredential" type="password" placeholder="Paste the credential for the selected provider/model" />
|
|
2092
|
-
<p class="muted">
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2120
|
+
<p class="muted">Written to OpenClaw <code>config.env</code> for the selected provider. If you do not choose a model manually, the installer picks a safe default.</p>
|
|
2121
|
+
</div>
|
|
2122
|
+
<div style="margin-top:12px;" id="llmOauthBlock" class="hidden">
|
|
2123
|
+
<label>Paste authorization code or full redirect URL</label>
|
|
2124
|
+
<textarea id="llmOAuthPaste" autocomplete="off" placeholder="After the installer prints an OAuth URL in the log, sign in locally and paste the code or full callback URL here. Leave empty if you use the option below."></textarea>
|
|
2125
|
+
<label style="display:flex; align-items:flex-start; gap:8px; font-size:13px; color:#9cb0de; margin-top:8px; cursor:pointer;">
|
|
2126
|
+
<input id="llmOAuthSkipLogin" type="checkbox" style="width:auto; margin-top:3px;" />
|
|
2127
|
+
<span>I already ran <code>openclaw models auth login --provider openai-codex</code> on this machine</span>
|
|
2128
|
+
</label>
|
|
2129
|
+
<p class="muted">If login hangs over SSH, complete OAuth in a normal shell first, then enable the checkbox above and start again. Live install logs will show the authorize URL when the CLI prints it.</p>
|
|
2130
|
+
</div>
|
|
2131
|
+
<p class="muted" id="llmLoadState" aria-live="polite">Loading LLM provider catalog...</p>
|
|
2132
|
+
<div id="llmLoadingHint" class="loading-hint" role="status" aria-live="polite">
|
|
2133
|
+
<span class="spinner" aria-hidden="true"></span>
|
|
2134
|
+
<span id="llmLoadingHintText">Fetching provider list...</span>
|
|
2098
2135
|
</div>
|
|
2099
2136
|
</div>
|
|
2100
2137
|
<div class="card" id="xCard">
|
|
@@ -2228,6 +2265,14 @@ function wizardHtml(defaults) {
|
|
|
2228
2265
|
const llmModelEl = document.getElementById("llmModel");
|
|
2229
2266
|
const llmModelManualEl = document.getElementById("llmModelManual");
|
|
2230
2267
|
const llmCredentialEl = document.getElementById("llmCredential");
|
|
2268
|
+
const llmAuthModeApiKey = document.getElementById("llmAuthModeApiKey");
|
|
2269
|
+
const llmAuthModeOauth = document.getElementById("llmAuthModeOauth");
|
|
2270
|
+
const llmProviderWrap = document.getElementById("llmProviderWrap");
|
|
2271
|
+
const llmOauthProviderNote = document.getElementById("llmOauthProviderNote");
|
|
2272
|
+
const llmApiKeyBlock = document.getElementById("llmApiKeyBlock");
|
|
2273
|
+
const llmOauthBlock = document.getElementById("llmOauthBlock");
|
|
2274
|
+
const llmOAuthPasteEl = document.getElementById("llmOAuthPaste");
|
|
2275
|
+
const llmOAuthSkipLoginEl = document.getElementById("llmOAuthSkipLogin");
|
|
2231
2276
|
const telegramTokenEl = document.getElementById("telegramToken");
|
|
2232
2277
|
const llmLoadStateEl = document.getElementById("llmLoadState");
|
|
2233
2278
|
const llmLoadingHintEl = document.getElementById("llmLoadingHint");
|
|
@@ -2254,14 +2299,61 @@ function wizardHtml(defaults) {
|
|
|
2254
2299
|
let pollTimer = null;
|
|
2255
2300
|
let pollIntervalMs = 1200;
|
|
2256
2301
|
let installLocked = false;
|
|
2302
|
+
let savedApiKeyProvider = "";
|
|
2303
|
+
|
|
2304
|
+
(function initLlmAuthDefaults() {
|
|
2305
|
+
const mode = ${JSON.stringify(defaults.llmAuthMode || "api_key")};
|
|
2306
|
+
if (mode === "oauth") {
|
|
2307
|
+
llmAuthModeOauth.checked = true;
|
|
2308
|
+
llmAuthModeApiKey.checked = false;
|
|
2309
|
+
}
|
|
2310
|
+
llmOAuthPasteEl.value = ${JSON.stringify(defaults.llmOAuthPaste || "")};
|
|
2311
|
+
if (${defaults.llmOAuthSkipLogin === true ? "true" : "false"}) {
|
|
2312
|
+
llmOAuthSkipLoginEl.checked = true;
|
|
2313
|
+
}
|
|
2314
|
+
applyLlmAuthModeUi();
|
|
2315
|
+
})();
|
|
2316
|
+
|
|
2317
|
+
function isOauthMode() {
|
|
2318
|
+
return llmAuthModeOauth && llmAuthModeOauth.checked;
|
|
2319
|
+
}
|
|
2320
|
+
|
|
2321
|
+
function effectiveLlmProvider() {
|
|
2322
|
+
return isOauthMode() ? "openai-codex" : llmProviderEl.value.trim();
|
|
2323
|
+
}
|
|
2324
|
+
|
|
2325
|
+
function applyLlmAuthModeUi() {
|
|
2326
|
+
if (!llmProviderWrap || !llmApiKeyBlock || !llmOauthBlock) return;
|
|
2327
|
+
if (isOauthMode()) {
|
|
2328
|
+
savedApiKeyProvider = llmProviderEl.value || savedApiKeyProvider;
|
|
2329
|
+
llmProviderEl.value = "openai-codex";
|
|
2330
|
+
llmProviderWrap.classList.add("hidden");
|
|
2331
|
+
llmOauthProviderNote.classList.remove("hidden");
|
|
2332
|
+
llmApiKeyBlock.classList.add("hidden");
|
|
2333
|
+
llmOauthBlock.classList.remove("hidden");
|
|
2334
|
+
} else {
|
|
2335
|
+
llmProviderWrap.classList.remove("hidden");
|
|
2336
|
+
llmOauthProviderNote.classList.add("hidden");
|
|
2337
|
+
llmApiKeyBlock.classList.remove("hidden");
|
|
2338
|
+
llmOauthBlock.classList.add("hidden");
|
|
2339
|
+
if (savedApiKeyProvider) {
|
|
2340
|
+
llmProviderEl.value = savedApiKeyProvider;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
function onLlmAuthModeChange() {
|
|
2346
|
+
applyLlmAuthModeUi();
|
|
2347
|
+
refreshModelOptions("");
|
|
2348
|
+
}
|
|
2257
2349
|
|
|
2258
2350
|
function hasRequiredInputs() {
|
|
2259
|
-
return
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
);
|
|
2351
|
+
if (!llmCatalogReady || !Boolean(telegramTokenEl.value.trim())) return false;
|
|
2352
|
+
if (isOauthMode()) {
|
|
2353
|
+
if (llmOAuthSkipLoginEl.checked) return true;
|
|
2354
|
+
return Boolean(llmOAuthPasteEl.value.trim());
|
|
2355
|
+
}
|
|
2356
|
+
return Boolean(llmProviderEl.value.trim()) && Boolean(llmCredentialEl.value.trim());
|
|
2265
2357
|
}
|
|
2266
2358
|
|
|
2267
2359
|
/** All-or-nothing: 0 or 4 non-empty X fields; partial is invalid. */
|
|
@@ -2348,7 +2440,7 @@ function wizardHtml(defaults) {
|
|
|
2348
2440
|
}
|
|
2349
2441
|
|
|
2350
2442
|
function refreshModelOptions(preferredModel) {
|
|
2351
|
-
const provider =
|
|
2443
|
+
const provider = effectiveLlmProvider();
|
|
2352
2444
|
const providerEntry = (llmCatalog.providers || []).find((entry) => entry.id === provider);
|
|
2353
2445
|
const modelItems = (providerEntry ? providerEntry.models : []).map((item) => ({ value: item.id, label: item.name + " (" + item.id + ")" }));
|
|
2354
2446
|
if (modelItems.length === 0) {
|
|
@@ -2378,8 +2470,9 @@ function wizardHtml(defaults) {
|
|
|
2378
2470
|
return;
|
|
2379
2471
|
}
|
|
2380
2472
|
setSelectOptions(llmProviderEl, providers, "${defaults.llmProvider}");
|
|
2473
|
+
applyLlmAuthModeUi();
|
|
2381
2474
|
refreshModelOptions("${defaults.llmModel}");
|
|
2382
|
-
const catalogMsg = "
|
|
2475
|
+
const catalogMsg = "Choose API key or Codex OAuth, then start installation. After setup, run \`openclaw models list\` for your live catalog.";
|
|
2383
2476
|
setLlmCatalogReady(true, catalogMsg, false);
|
|
2384
2477
|
} catch (err) {
|
|
2385
2478
|
setLlmCatalogReady(false, "Failed to load LLM providers. Reload the page and try again.", true);
|
|
@@ -2433,10 +2526,14 @@ function wizardHtml(defaults) {
|
|
|
2433
2526
|
manualEl.textContent = "";
|
|
2434
2527
|
readyEl.textContent = "Starting installation...";
|
|
2435
2528
|
|
|
2529
|
+
const oauth = isOauthMode();
|
|
2436
2530
|
const payload = {
|
|
2437
|
-
|
|
2531
|
+
llmAuthMode: oauth ? "oauth" : "api_key",
|
|
2532
|
+
llmProvider: oauth ? "openai-codex" : llmProviderEl.value.trim(),
|
|
2438
2533
|
llmModel: llmModelEl.value.trim(),
|
|
2439
|
-
llmCredential: llmCredentialEl.value.trim(),
|
|
2534
|
+
llmCredential: oauth ? "" : llmCredentialEl.value.trim(),
|
|
2535
|
+
llmOAuthPaste: llmOAuthPasteEl.value.trim(),
|
|
2536
|
+
llmOAuthSkipLogin: llmOAuthSkipLoginEl.checked,
|
|
2440
2537
|
apiKey: document.getElementById("apiKey").value.trim(),
|
|
2441
2538
|
telegramToken: document.getElementById("telegramToken").value.trim(),
|
|
2442
2539
|
referralCode: document.getElementById("referralCode").value.trim(),
|
|
@@ -2445,10 +2542,18 @@ function wizardHtml(defaults) {
|
|
|
2445
2542
|
xAccessTokenMain: xAccessTokenMainEl.value.trim(),
|
|
2446
2543
|
xAccessTokenMainSecret: xAccessTokenMainSecretEl.value.trim(),
|
|
2447
2544
|
};
|
|
2448
|
-
if (
|
|
2545
|
+
if (oauth) {
|
|
2546
|
+
if (!payload.llmOAuthSkipLogin && !payload.llmOAuthPaste) {
|
|
2547
|
+
stateEl.textContent = "blocked";
|
|
2548
|
+
readyEl.textContent = "";
|
|
2549
|
+
manualEl.textContent =
|
|
2550
|
+
"Codex OAuth: paste the authorization code or full redirect URL, or check the box if you already ran openclaw models auth login on this machine.";
|
|
2551
|
+
return;
|
|
2552
|
+
}
|
|
2553
|
+
} else if (!payload.llmProvider || !payload.llmCredential) {
|
|
2449
2554
|
stateEl.textContent = "blocked";
|
|
2450
2555
|
readyEl.textContent = "";
|
|
2451
|
-
manualEl.textContent = "LLM provider and
|
|
2556
|
+
manualEl.textContent = "LLM provider and API key are required before starting installation.";
|
|
2452
2557
|
return;
|
|
2453
2558
|
}
|
|
2454
2559
|
if (!payload.telegramToken) {
|
|
@@ -2690,7 +2795,11 @@ function wizardHtml(defaults) {
|
|
|
2690
2795
|
llmModelEl.disabled = !llmModelManualEl.checked;
|
|
2691
2796
|
updateStartButtonState();
|
|
2692
2797
|
});
|
|
2798
|
+
llmAuthModeApiKey.addEventListener("change", onLlmAuthModeChange);
|
|
2799
|
+
llmAuthModeOauth.addEventListener("change", onLlmAuthModeChange);
|
|
2693
2800
|
llmCredentialEl.addEventListener("input", updateStartButtonState);
|
|
2801
|
+
llmOAuthPasteEl.addEventListener("input", updateStartButtonState);
|
|
2802
|
+
llmOAuthSkipLoginEl.addEventListener("change", updateStartButtonState);
|
|
2694
2803
|
telegramTokenEl.addEventListener("input", updateStartButtonState);
|
|
2695
2804
|
xConsumerKeyEl.addEventListener("input", updateStartButtonState);
|
|
2696
2805
|
xConsumerSecretEl.addEventListener("input", updateStartButtonState);
|
|
@@ -2831,12 +2940,17 @@ async function cmdInstall(args) {
|
|
|
2831
2940
|
}
|
|
2832
2941
|
|
|
2833
2942
|
const body = await parseJsonBody(req).catch(() => ({}));
|
|
2943
|
+
const rawLlmAuth = body.llmAuthMode != null ? body.llmAuthMode : defaults.llmAuthMode;
|
|
2834
2944
|
const wizardOpts = {
|
|
2835
2945
|
mode: "light",
|
|
2836
2946
|
lane: defaults.lane,
|
|
2947
|
+
llmAuthMode: rawLlmAuth === "oauth" ? "oauth" : "api_key",
|
|
2837
2948
|
llmProvider: body.llmProvider || defaults.llmProvider,
|
|
2838
2949
|
llmModel: body.llmModel || defaults.llmModel,
|
|
2839
2950
|
llmCredential: body.llmCredential || defaults.llmCredential,
|
|
2951
|
+
llmOAuthPaste: typeof body.llmOAuthPaste === "string" ? body.llmOAuthPaste.trim() : defaults.llmOAuthPaste,
|
|
2952
|
+
llmOAuthSkipLogin:
|
|
2953
|
+
typeof body.llmOAuthSkipLogin === "boolean" ? body.llmOAuthSkipLogin : defaults.llmOAuthSkipLogin === true,
|
|
2840
2954
|
apiKey: body.apiKey || defaults.apiKey,
|
|
2841
2955
|
orchestratorUrl: defaults.orchestratorUrl,
|
|
2842
2956
|
gatewayBaseUrl: defaults.gatewayBaseUrl,
|
|
@@ -2870,9 +2984,12 @@ async function cmdInstall(args) {
|
|
|
2870
2984
|
{
|
|
2871
2985
|
mode: "light",
|
|
2872
2986
|
lane: defaults.lane,
|
|
2987
|
+
llmAuthMode: wizardOpts.llmAuthMode,
|
|
2873
2988
|
llmProvider: body.llmProvider || defaults.llmProvider,
|
|
2874
2989
|
llmModel: body.llmModel || defaults.llmModel,
|
|
2875
2990
|
llmCredential: body.llmCredential || defaults.llmCredential,
|
|
2991
|
+
llmOAuthPaste: wizardOpts.llmOAuthPaste,
|
|
2992
|
+
llmOAuthSkipLogin: wizardOpts.llmOAuthSkipLogin,
|
|
2876
2993
|
apiKey: body.apiKey || defaults.apiKey,
|
|
2877
2994
|
orchestratorUrl: defaults.orchestratorUrl,
|
|
2878
2995
|
gatewayBaseUrl: defaults.gatewayBaseUrl,
|
|
@@ -3240,6 +3357,15 @@ Config subcommands:
|
|
|
3240
3357
|
config set <k> <v> Update a configuration value
|
|
3241
3358
|
config reset Remove plugin configuration
|
|
3242
3359
|
|
|
3360
|
+
Install wizard (traderclaw install --wizard):
|
|
3361
|
+
--port Local port for the wizard (default 17890)
|
|
3362
|
+
--llm-provider e.g. openai, openai-codex, anthropic
|
|
3363
|
+
--llm-model e.g. openai/gpt-5.4 or openai-codex/gpt-5.4
|
|
3364
|
+
--llm-api-key, --llm-token API key for LLM (api_key mode)
|
|
3365
|
+
--llm-auth api_key|oauth OpenAI Platform key vs ChatGPT/Codex OAuth (openai-codex)
|
|
3366
|
+
--llm-oauth-paste Paste redirect URL or code for Codex OAuth (non-skip)
|
|
3367
|
+
--llm-oauth-skip-login Skip login when you already ran openclaw models auth login
|
|
3368
|
+
|
|
3243
3369
|
Examples:
|
|
3244
3370
|
traderclaw signup
|
|
3245
3371
|
traderclaw setup
|
|
@@ -3249,6 +3375,7 @@ Examples:
|
|
|
3249
3375
|
traderclaw precheck --allow-install
|
|
3250
3376
|
traderclaw install --wizard
|
|
3251
3377
|
traderclaw install --wizard --lane quick-local
|
|
3378
|
+
traderclaw install --wizard --llm-auth oauth --llm-provider openai-codex --llm-oauth-skip-login
|
|
3252
3379
|
traderclaw repair-openclaw
|
|
3253
3380
|
traderclaw gateway ensure-persistent
|
|
3254
3381
|
traderclaw setup --signup --user-id my_agent_001 --referral-code ABCD1234
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "traderclaw-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.74",
|
|
4
4
|
"description": "Global TraderClaw CLI (install --wizard, setup, precheck). Installs solana-traderclaw as a dependency for OpenClaw plugin files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"node": ">=22"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"solana-traderclaw": "^1.0.
|
|
20
|
+
"solana-traderclaw": "^1.0.74"
|
|
21
21
|
},
|
|
22
22
|
"keywords": [
|
|
23
23
|
"traderclaw",
|