@autotask/atools-tool 0.1.3 → 0.1.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 CHANGED
@@ -50,6 +50,12 @@ Recommended selections:
50
50
  npm i -g @autotask/atools-tool
51
51
  ```
52
52
 
53
+ Default full install (recommended):
54
+
55
+ ```bash
56
+ npm i -g @autotask/atools-tool && atools-tool-install && atools-tool openclaw
57
+ ```
58
+
53
59
  Optional installer command:
54
60
 
55
61
  ```bash
package/bin/cli.mjs CHANGED
@@ -8,8 +8,6 @@ function parseArgs(argv) {
8
8
  openclawHome: undefined,
9
9
  port: undefined,
10
10
  upstream: undefined,
11
- fallbackUpstream: undefined,
12
- fallbackApiKey: undefined,
13
11
  maxReqBytes: undefined,
14
12
  logFile: undefined,
15
13
  retryMax: undefined,
@@ -27,8 +25,6 @@ function parseArgs(argv) {
27
25
  if (a === '--port') out.port = Number(next());
28
26
  else if (a === '--openclaw-home') out.openclawHome = next();
29
27
  else if (a === '--upstream') out.upstream = next();
30
- else if (a === '--fallback-upstream') out.fallbackUpstream = next();
31
- else if (a === '--fallback-api-key') out.fallbackApiKey = next();
32
28
  else if (a === '--max-req-bytes') out.maxReqBytes = Number(next());
33
29
  else if (a === '--log-file') out.logFile = next();
34
30
  else if (a === '--retry-max') out.retryMax = Number(next());
@@ -54,8 +50,6 @@ Options:
54
50
  --openclaw-home <path> OpenClaw home dir (default: ~/.openclaw)
55
51
  --port <number> Listen port (default: 18888)
56
52
  --upstream <url> Upstream base URL (default: https://sub2api.atools.live)
57
- --fallback-upstream <url> Fallback base URL on primary 5xx
58
- --fallback-api-key <key> Override Bearer key for fallback upstream
59
53
  --max-req-bytes <number> Compact requests larger than this threshold (default: 68000)
60
54
  --log-file <path> Log file (default: /tmp/openclaw/atools-compat-proxy.log)
61
55
  --retry-max <number> Retry attempts on 5xx for /responses (default: 6)
@@ -81,9 +75,8 @@ async function main() {
81
75
  throw new Error(`unsupported command: ${args.cmd}`);
82
76
  }
83
77
 
84
- const { port, upstream, fallbackUpstream, logFile } = await createProxyServer(args);
85
- const fallbackNote = fallbackUpstream ? `, fallback=${fallbackUpstream}` : '';
86
- process.stdout.write(`atools-tool listening on http://127.0.0.1:${port}, upstream=${upstream}${fallbackNote}, log=${logFile}\n`);
78
+ const { port, upstream, logFile } = await createProxyServer(args);
79
+ process.stdout.write(`atools-tool listening on http://127.0.0.1:${port}, upstream=${upstream}, log=${logFile}\n`);
87
80
  }
88
81
 
89
82
  main().catch((err) => {
@@ -266,8 +266,6 @@ function configureLinuxProxyService() {
266
266
  const original = fs.readFileSync(servicePath, 'utf8');
267
267
  let next = original;
268
268
  next = upsertEnvLine(next, 'SUB2API_UPSTREAM', PROXY_UPSTREAM);
269
- next = upsertEnvLine(next, 'SUB2API_FALLBACK_UPSTREAM', '');
270
- next = upsertEnvLine(next, 'SUB2API_FALLBACK_API_KEY', '');
271
269
  next = upsertEnvLine(next, 'SUB2API_COMPAT_DROP_TOOLS_ON_COMPACT', '0');
272
270
  next = upsertEnvLine(next, 'SUB2API_COMPAT_PORT', String(PROXY_PORT));
273
271
  next = upsertEnvLine(next, 'SUB2API_COMPAT_LOG', PROXY_LOG_FILE);
@@ -317,9 +315,7 @@ function startDetachedProxy() {
317
315
  stdio: 'ignore',
318
316
  env: {
319
317
  ...process.env,
320
- SUB2API_UPSTREAM: PROXY_UPSTREAM,
321
- SUB2API_FALLBACK_UPSTREAM: '',
322
- SUB2API_FALLBACK_API_KEY: ''
318
+ SUB2API_UPSTREAM: PROXY_UPSTREAM
323
319
  }
324
320
  }
325
321
  );
@@ -360,7 +356,7 @@ async function ensureProxyReady() {
360
356
 
361
357
  if (await isPortOpen(PROXY_HOST, PROXY_PORT)) return true;
362
358
 
363
- // Linux fallback: service may exist but not running.
359
+ // Linux retry: service may exist but not running.
364
360
  if (process.platform === 'linux') {
365
361
  const service = configureLinuxProxyService();
366
362
  if (service.exists && isLinuxProxyServiceActive()) {
package/lib/install.mjs CHANGED
@@ -8,7 +8,6 @@ const DEFAULT_PROVIDER = 'sub2api';
8
8
  const DEFAULT_MODEL = 'gpt-5.3-codex';
9
9
  const DEFAULT_PORT = 18888;
10
10
  const DEFAULT_UPSTREAM = 'https://sub2api.atools.live';
11
- const DEFAULT_FALLBACK_UPSTREAM = '';
12
11
  const DEFAULT_LOG_FILE = path.join(os.tmpdir(), 'openclaw', 'atools-compat-proxy.log');
13
12
  const DEFAULT_SERVICE_NAME = 'openclaw-atools-proxy.service';
14
13
  const OPENCLAW_GATEWAY_SERVICE = 'openclaw-gateway.service';
@@ -51,7 +50,7 @@ function defaultServiceId(name = DEFAULT_SERVICE_NAME) {
51
50
  return String(name || DEFAULT_SERVICE_NAME).replace(/\.service$/i, '');
52
51
  }
53
52
 
54
- function buildProxyServeArgs({ port, upstream, fallbackUpstream, fallbackApiKey, maxReqBytes, logFile }) {
53
+ function buildProxyServeArgs({ port, upstream, maxReqBytes, logFile }) {
55
54
  const args = [
56
55
  'serve',
57
56
  '--port',
@@ -63,12 +62,6 @@ function buildProxyServeArgs({ port, upstream, fallbackUpstream, fallbackApiKey,
63
62
  '--max-req-bytes',
64
63
  String(maxReqBytes)
65
64
  ];
66
- if (fallbackUpstream) {
67
- args.push('--fallback-upstream', String(fallbackUpstream));
68
- }
69
- if (fallbackApiKey) {
70
- args.push('--fallback-api-key', String(fallbackApiKey));
71
- }
72
65
  return args;
73
66
  }
74
67
 
@@ -94,12 +87,10 @@ function parseArgs(argv) {
94
87
  model: DEFAULT_MODEL,
95
88
  port: DEFAULT_PORT,
96
89
  upstream: DEFAULT_UPSTREAM,
97
- fallbackUpstream: DEFAULT_FALLBACK_UPSTREAM,
98
90
  maxReqBytes: 68000,
99
91
  logFile: DEFAULT_LOG_FILE,
100
92
  serviceName: DEFAULT_SERVICE_NAME,
101
93
  apiKey: process.env.SUB_OAI_KEY || '',
102
- fallbackApiKey: process.env.FALLBACK_OAI_KEY || '',
103
94
  dryRun: false,
104
95
  noRestart: false
105
96
  };
@@ -113,12 +104,10 @@ function parseArgs(argv) {
113
104
  else if (a === '--model') out.model = next();
114
105
  else if (a === '--port') out.port = Number(next());
115
106
  else if (a === '--upstream') out.upstream = next();
116
- else if (a === '--fallback-upstream') out.fallbackUpstream = next();
117
107
  else if (a === '--max-req-bytes') out.maxReqBytes = Number(next());
118
108
  else if (a === '--log-file') out.logFile = next();
119
109
  else if (a === '--service-name') out.serviceName = next();
120
110
  else if (a === '--api-key') out.apiKey = next();
121
- else if (a === '--fallback-api-key') out.fallbackApiKey = next();
122
111
  else if (a === '--dry-run') out.dryRun = true;
123
112
  else if (a === '--no-restart') out.noRestart = true;
124
113
  else if (a === '--help' || a === '-h') {
@@ -137,13 +126,8 @@ function parseArgs(argv) {
137
126
  function renderSystemdService({
138
127
  nodeBin,
139
128
  cliPath,
140
- serveArgs,
141
- fallbackUpstream,
142
- fallbackApiKey
129
+ serveArgs
143
130
  }) {
144
- const fallbackEnv = fallbackUpstream
145
- ? `Environment=SUB2API_FALLBACK_UPSTREAM=${fallbackUpstream}\nEnvironment=SUB2API_FALLBACK_API_KEY=${fallbackApiKey || ''}\n`
146
- : '';
147
131
  const execArgs = [nodeBin, cliPath, ...serveArgs].join(' ');
148
132
  return `[Unit]
149
133
  Description=OpenClaw ATools Compatibility Proxy
@@ -157,7 +141,6 @@ RestartSec=2
157
141
  Environment=HOME=${os.homedir()}
158
142
  Environment=TMPDIR=/tmp
159
143
  Environment="SUB2API_COMPAT_USER_AGENT=codex_cli_rs/0.101.0 (Ubuntu 24.4.0; x86_64) WindowsTerminal"
160
- ${fallbackEnv}
161
144
 
162
145
  [Install]
163
146
  WantedBy=default.target
@@ -207,13 +190,6 @@ function detectApiKey(openclawHome, explicitKey) {
207
190
  return auth?.profiles?.['sub2api:default']?.key || '';
208
191
  }
209
192
 
210
- function detectFallbackApiKey(openclawHome, explicitKey) {
211
- if (explicitKey) return explicitKey;
212
- const cfgPath = path.join(openclawHome, 'openclaw.json');
213
- const cfg = readJsonIfExists(cfgPath);
214
- return cfg?.models?.providers?.gmn?.apiKey || '';
215
- }
216
-
217
193
  function patchOpenclawConfig(openclawHome, provider, model, port, dryRun) {
218
194
  const cfgPath = path.join(openclawHome, 'openclaw.json');
219
195
  const cfg = readJsonIfExists(cfgPath);
@@ -504,12 +480,10 @@ Options:
504
480
  --model <id> Default: gpt-5.3-codex
505
481
  --port <number> Default: 18888
506
482
  --upstream <url> Default: https://sub2api.atools.live
507
- --fallback-upstream <url> Default: empty (disabled)
508
483
  --max-req-bytes <number> Default: 68000
509
484
  --log-file <path> Default: <tmp>/openclaw/atools-compat-proxy.log
510
485
  --service-name <name> Linux: *.service; macOS/Windows: service id/task name
511
- --api-key <key> Prefer explicit key; fallback env SUB_OAI_KEY then auth-profiles
512
- --fallback-api-key <key> Prefer explicit key; fallback env FALLBACK_OAI_KEY then openclaw gmn key
486
+ --api-key <key> Prefer explicit key; otherwise use SUB_OAI_KEY or auth-profiles
513
487
  --no-restart Do not restart openclaw-gateway.service
514
488
  --dry-run Print actions without changing files
515
489
  -h, --help Show help
@@ -527,12 +501,9 @@ export async function runInstall(rawArgs = process.argv.slice(2)) {
527
501
  const nodeBin = process.execPath;
528
502
  const cliPath = fileURLToPath(new URL('../bin/cli.mjs', import.meta.url));
529
503
  const apiKey = detectApiKey(args.openclawHome, args.apiKey);
530
- const fallbackApiKey = detectFallbackApiKey(args.openclawHome, args.fallbackApiKey);
531
504
  const serveArgs = buildProxyServeArgs({
532
505
  port: args.port,
533
506
  upstream: args.upstream,
534
- fallbackUpstream: args.fallbackUpstream,
535
- fallbackApiKey,
536
507
  maxReqBytes: args.maxReqBytes,
537
508
  logFile: args.logFile
538
509
  });
@@ -541,9 +512,6 @@ export async function runInstall(rawArgs = process.argv.slice(2)) {
541
512
  info(`platform=${process.platform}`);
542
513
  info(`provider/model=${args.provider}/${args.model}`);
543
514
  info(`proxy=http://127.0.0.1:${args.port}/v1 -> ${args.upstream}`);
544
- if (args.fallbackUpstream) {
545
- info(`fallback=${args.fallbackUpstream}`);
546
- }
547
515
 
548
516
  patchOpenclawConfig(args.openclawHome, args.provider, args.model, args.port, args.dryRun);
549
517
  patchAgentModels(args.openclawHome, args.provider, args.model, args.port, apiKey, args.dryRun);
@@ -552,9 +520,7 @@ export async function runInstall(rawArgs = process.argv.slice(2)) {
552
520
  const linuxServiceContent = renderSystemdService({
553
521
  nodeBin,
554
522
  cliPath,
555
- serveArgs,
556
- fallbackUpstream: args.fallbackUpstream,
557
- fallbackApiKey
523
+ serveArgs
558
524
  });
559
525
  const macPlistContent = renderLaunchdPlist({
560
526
  label: defaultServiceId(args.serviceName),
@@ -6,8 +6,6 @@ const DEFAULT_PORT = 18888;
6
6
  const DEFAULT_UPSTREAM = 'https://sub2api.atools.live';
7
7
  const DEFAULT_LOG = '/tmp/openclaw/atools-compat-proxy.log';
8
8
  const DEFAULT_USER_AGENT = 'codex_cli_rs/0.101.0 (Ubuntu 24.4.0; x86_64) WindowsTerminal';
9
- const DEFAULT_FALLBACK_UPSTREAM = '';
10
- const DEFAULT_FALLBACK_API_KEY = '';
11
9
  const DEFAULT_MAX_REQ_BYTES = 68000;
12
10
  const DEFAULT_DROP_TOOLS_ON_COMPACT = false;
13
11
  const DEFAULT_STRIP_PREVIOUS_RESPONSE_ID = false;
@@ -149,13 +147,10 @@ async function forward(url, req, headers, body) {
149
147
  });
150
148
  }
151
149
 
152
- function buildHeaders(baseHeaders, bodyLen, apiKeyOverride = '') {
150
+ function buildHeaders(baseHeaders, bodyLen) {
153
151
  const headers = { ...baseHeaders };
154
152
  headers['content-type'] = 'application/json';
155
153
  headers['content-length'] = String(bodyLen);
156
- if (apiKeyOverride) {
157
- headers.authorization = `Bearer ${apiKeyOverride}`;
158
- }
159
154
  return headers;
160
155
  }
161
156
 
@@ -166,8 +161,6 @@ export async function createProxyServer(options = {}) {
166
161
  const retryMax = Number(options.retryMax ?? process.env.SUB2API_COMPAT_RETRY_MAX ?? 6);
167
162
  const retryBaseMs = Number(options.retryBaseMs ?? process.env.SUB2API_COMPAT_RETRY_BASE_MS ?? 300);
168
163
  const userAgent = options.userAgent ?? process.env.SUB2API_COMPAT_USER_AGENT ?? DEFAULT_USER_AGENT;
169
- const fallbackUpstream = options.fallbackUpstream ?? process.env.SUB2API_FALLBACK_UPSTREAM ?? DEFAULT_FALLBACK_UPSTREAM;
170
- const fallbackApiKey = options.fallbackApiKey ?? process.env.SUB2API_FALLBACK_API_KEY ?? DEFAULT_FALLBACK_API_KEY;
171
164
  const maxReqBytes = Number(options.maxReqBytes ?? process.env.SUB2API_COMPAT_MAX_REQ_BYTES ?? DEFAULT_MAX_REQ_BYTES);
172
165
  const dropToolsOnCompact = options.dropToolsOnCompact
173
166
  ?? ['1', 'true', 'yes', 'on'].includes(String(process.env.SUB2API_COMPAT_DROP_TOOLS_ON_COMPACT || '').toLowerCase());
@@ -178,7 +171,6 @@ export async function createProxyServer(options = {}) {
178
171
  const startAt = Date.now();
179
172
  try {
180
173
  const url = new URL(req.url || '/', upstream);
181
- const fallbackUrl = fallbackUpstream ? new URL(req.url || '/', fallbackUpstream) : null;
182
174
  const chunks = [];
183
175
  for await (const c of req) chunks.push(c);
184
176
  const rawBody = Buffer.concat(chunks);
@@ -245,9 +237,7 @@ export async function createProxyServer(options = {}) {
245
237
 
246
238
  let resp;
247
239
  let attempts = 0;
248
- let via = 'primary';
249
240
  let primaryStatus = null;
250
- let fallbackStatus = null;
251
241
  let compacted = false;
252
242
  let strategy = 'full';
253
243
  const maxAttempts = isResponsesPath ? retryMax : 1;
@@ -260,32 +250,11 @@ export async function createProxyServer(options = {}) {
260
250
  ? 'full'
261
251
  : (strategyIndex === 1 ? 'compact' : (strategyIndex === 2 ? 'aggressive' : 'minimal'));
262
252
  const primaryHeaders = isResponsesPath ? buildHeaders(baseHeaders, requestBody.length) : baseHeaders;
263
- const fallbackHeaders = isResponsesPath
264
- ? buildHeaders(baseHeaders, requestBody.length, fallbackApiKey)
265
- : (fallbackApiKey ? { ...baseHeaders, authorization: `Bearer ${fallbackApiKey}` } : baseHeaders);
266
253
 
267
254
  const primaryResp = await forward(url, req, primaryHeaders, requestBody);
268
255
  primaryStatus = primaryResp.status;
269
- if (primaryResp.status < 500) {
270
- resp = primaryResp;
271
- via = 'primary';
272
- break;
273
- }
274
-
275
- if (fallbackUrl) {
276
- const fbResp = await forward(fallbackUrl, req, fallbackHeaders, requestBody);
277
- fallbackStatus = fbResp.status;
278
- if (fbResp.status < 500) {
279
- resp = fbResp;
280
- via = 'fallback';
281
- break;
282
- }
283
- resp = fbResp;
284
- via = 'fallback';
285
- } else {
286
- resp = primaryResp;
287
- via = 'primary';
288
- }
256
+ resp = primaryResp;
257
+ if (primaryResp.status < 500) break;
289
258
 
290
259
  if (attempts < maxAttempts) {
291
260
  await sleep(retryBaseMs * attempts);
@@ -304,9 +273,7 @@ export async function createProxyServer(options = {}) {
304
273
  path: url.pathname,
305
274
  status: resp.status,
306
275
  attempts,
307
- via,
308
276
  primaryStatus,
309
- fallbackStatus,
310
277
  compacted,
311
278
  strategy,
312
279
  reqBytes: rawBody.length,
@@ -339,7 +306,6 @@ export async function createProxyServer(options = {}) {
339
306
  server,
340
307
  port,
341
308
  upstream,
342
- fallbackUpstream,
343
309
  logFile,
344
310
  close: () => new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())))
345
311
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autotask/atools-tool",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "ATools CLI for OpenClaw proxy compatibility and interactive model configuration",
5
5
  "type": "module",
6
6
  "license": "MIT",