@autotask/atools-tool 0.1.5 → 0.1.6

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
@@ -68,10 +68,14 @@ atools-tool-install --help
68
68
  - macOS: launchd user agent (`~/Library/LaunchAgents`)
69
69
  - Windows: Task Scheduler user task (ONLOGON + start now)
70
70
 
71
+ By default, installer enables proxy model rewrite:
72
+
73
+ - force outbound model to `gpt-5.3-codex` (for codex-only upstream compatibility)
74
+
71
75
  ## Proxy Command
72
76
 
73
77
  ```bash
74
- atools-tool serve --port 18888 --upstream https://sub2api.atools.live
78
+ atools-tool serve --port 18888 --upstream https://sub2api.atools.live --force-model gpt-5.3-codex
75
79
  ```
76
80
 
77
81
  ## Repo Install
package/bin/cli.mjs CHANGED
@@ -8,6 +8,7 @@ function parseArgs(argv) {
8
8
  openclawHome: undefined,
9
9
  port: undefined,
10
10
  upstream: undefined,
11
+ forceModel: undefined,
11
12
  maxReqBytes: undefined,
12
13
  logFile: undefined,
13
14
  retryMax: undefined,
@@ -25,6 +26,7 @@ function parseArgs(argv) {
25
26
  if (a === '--port') out.port = Number(next());
26
27
  else if (a === '--openclaw-home') out.openclawHome = next();
27
28
  else if (a === '--upstream') out.upstream = next();
29
+ else if (a === '--force-model') out.forceModel = next();
28
30
  else if (a === '--max-req-bytes') out.maxReqBytes = Number(next());
29
31
  else if (a === '--log-file') out.logFile = next();
30
32
  else if (a === '--retry-max') out.retryMax = Number(next());
@@ -50,6 +52,7 @@ Options:
50
52
  --openclaw-home <path> OpenClaw home dir (default: ~/.openclaw)
51
53
  --port <number> Listen port (default: 18888)
52
54
  --upstream <url> Upstream base URL (default: https://sub2api.atools.live)
55
+ --force-model <id> Force outbound model id (e.g. gpt-5.3-codex)
53
56
  --max-req-bytes <number> Compact requests larger than this threshold (default: 68000)
54
57
  --log-file <path> Log file (default: /tmp/openclaw/atools-compat-proxy.log)
55
58
  --retry-max <number> Retry attempts on 5xx for /responses (default: 6)
@@ -16,6 +16,7 @@ const PROXY_HOST = '127.0.0.1';
16
16
  const PROXY_PORT = 18888;
17
17
  const PROXY_BASE_URL = `http://${PROXY_HOST}:${PROXY_PORT}/v1`;
18
18
  const PROXY_UPSTREAM = 'https://sub2api.atools.live';
19
+ const PROXY_FORCE_MODEL = 'gpt-5.3-codex';
19
20
  const PROXY_LOG_FILE = path.join(os.tmpdir(), 'openclaw', 'atools-compat-proxy.log');
20
21
  const PROXY_SERVICE = 'openclaw-atools-proxy.service';
21
22
 
@@ -266,6 +267,7 @@ function configureLinuxProxyService() {
266
267
  const original = fs.readFileSync(servicePath, 'utf8');
267
268
  let next = original;
268
269
  next = upsertEnvLine(next, 'SUB2API_UPSTREAM', PROXY_UPSTREAM);
270
+ next = upsertEnvLine(next, 'SUB2API_COMPAT_FORCE_MODEL', PROXY_FORCE_MODEL);
269
271
  next = upsertEnvLine(next, 'SUB2API_COMPAT_DROP_TOOLS_ON_COMPACT', '0');
270
272
  next = upsertEnvLine(next, 'SUB2API_COMPAT_PORT', String(PROXY_PORT));
271
273
  next = upsertEnvLine(next, 'SUB2API_COMPAT_LOG', PROXY_LOG_FILE);
@@ -315,7 +317,8 @@ function startDetachedProxy() {
315
317
  stdio: 'ignore',
316
318
  env: {
317
319
  ...process.env,
318
- SUB2API_UPSTREAM: PROXY_UPSTREAM
320
+ SUB2API_UPSTREAM: PROXY_UPSTREAM,
321
+ SUB2API_COMPAT_FORCE_MODEL: PROXY_FORCE_MODEL
319
322
  }
320
323
  }
321
324
  );
package/lib/install.mjs CHANGED
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
6
6
 
7
7
  const DEFAULT_PROVIDER = 'sub2api';
8
8
  const DEFAULT_MODEL = 'gpt-5.3-codex';
9
+ const DEFAULT_FORCE_MODEL = 'gpt-5.3-codex';
9
10
  const DEFAULT_PORT = 18888;
10
11
  const DEFAULT_UPSTREAM = 'https://sub2api.atools.live';
11
12
  const DEFAULT_LOG_FILE = path.join(os.tmpdir(), 'openclaw', 'atools-compat-proxy.log');
@@ -50,7 +51,7 @@ function defaultServiceId(name = DEFAULT_SERVICE_NAME) {
50
51
  return String(name || DEFAULT_SERVICE_NAME).replace(/\.service$/i, '');
51
52
  }
52
53
 
53
- function buildProxyServeArgs({ port, upstream, maxReqBytes, logFile }) {
54
+ function buildProxyServeArgs({ port, upstream, forceModel, maxReqBytes, logFile }) {
54
55
  const args = [
55
56
  'serve',
56
57
  '--port',
@@ -62,6 +63,9 @@ function buildProxyServeArgs({ port, upstream, maxReqBytes, logFile }) {
62
63
  '--max-req-bytes',
63
64
  String(maxReqBytes)
64
65
  ];
66
+ if (forceModel) {
67
+ args.push('--force-model', String(forceModel));
68
+ }
65
69
  return args;
66
70
  }
67
71
 
@@ -85,6 +89,7 @@ function parseArgs(argv) {
85
89
  openclawHome: process.env.OPENCLAW_HOME || path.join(os.homedir(), '.openclaw'),
86
90
  provider: DEFAULT_PROVIDER,
87
91
  model: DEFAULT_MODEL,
92
+ forceModel: DEFAULT_FORCE_MODEL,
88
93
  port: DEFAULT_PORT,
89
94
  upstream: DEFAULT_UPSTREAM,
90
95
  maxReqBytes: 68000,
@@ -102,6 +107,7 @@ function parseArgs(argv) {
102
107
  if (a === '--openclaw-home') out.openclawHome = next();
103
108
  else if (a === '--provider') out.provider = next();
104
109
  else if (a === '--model') out.model = next();
110
+ else if (a === '--force-model') out.forceModel = next();
105
111
  else if (a === '--port') out.port = Number(next());
106
112
  else if (a === '--upstream') out.upstream = next();
107
113
  else if (a === '--max-req-bytes') out.maxReqBytes = Number(next());
@@ -480,6 +486,7 @@ Options:
480
486
  --openclaw-home <path> Default: ~/.openclaw
481
487
  --provider <name> Default: sub2api
482
488
  --model <id> Default: gpt-5.3-codex
489
+ --force-model <id> Default: gpt-5.3-codex (proxy rewrites outbound model)
483
490
  --port <number> Default: 18888
484
491
  --upstream <url> Default: https://sub2api.atools.live
485
492
  --max-req-bytes <number> Default: 68000
@@ -506,6 +513,7 @@ export async function runInstall(rawArgs = process.argv.slice(2)) {
506
513
  const serveArgs = buildProxyServeArgs({
507
514
  port: args.port,
508
515
  upstream: args.upstream,
516
+ forceModel: args.forceModel,
509
517
  maxReqBytes: args.maxReqBytes,
510
518
  logFile: args.logFile
511
519
  });
@@ -513,6 +521,7 @@ export async function runInstall(rawArgs = process.argv.slice(2)) {
513
521
  info(`openclawHome=${args.openclawHome}`);
514
522
  info(`platform=${process.platform}`);
515
523
  info(`provider/model=${args.provider}/${args.model}`);
524
+ info(`proxy forceModel=${args.forceModel || '(disabled)'}`);
516
525
  info(`proxy=http://127.0.0.1:${args.port}/v1 -> ${args.upstream}`);
517
526
 
518
527
  patchOpenclawConfig(args.openclawHome, args.provider, args.model, args.port, args.dryRun);
@@ -9,6 +9,7 @@ const DEFAULT_USER_AGENT = 'codex_cli_rs/0.101.0 (Ubuntu 24.4.0; x86_64) Windows
9
9
  const DEFAULT_MAX_REQ_BYTES = 68000;
10
10
  const DEFAULT_DROP_TOOLS_ON_COMPACT = false;
11
11
  const DEFAULT_STRIP_PREVIOUS_RESPONSE_ID = false;
12
+ const DEFAULT_FORCE_MODEL = '';
12
13
 
13
14
  function ensureParentDir(filePath) {
14
15
  const dir = path.dirname(filePath);
@@ -54,13 +55,19 @@ function normalizeContent(content) {
54
55
  return [toInputText('')];
55
56
  }
56
57
 
57
- function normalizeResponsesPayload(payload, { stripPreviousResponseId = DEFAULT_STRIP_PREVIOUS_RESPONSE_ID } = {}) {
58
+ function normalizeResponsesPayload(payload, {
59
+ stripPreviousResponseId = DEFAULT_STRIP_PREVIOUS_RESPONSE_ID,
60
+ forceModel = DEFAULT_FORCE_MODEL
61
+ } = {}) {
58
62
  if (!payload || typeof payload !== 'object') return payload;
59
63
  const out = { ...payload };
60
64
 
61
65
  if (stripPreviousResponseId && 'previous_response_id' in out) {
62
66
  delete out.previous_response_id;
63
67
  }
68
+ if (forceModel) {
69
+ out.model = forceModel;
70
+ }
64
71
 
65
72
  if (typeof out.input === 'string') {
66
73
  out.input = [{ role: 'user', content: normalizeContent(out.input) }];
@@ -166,6 +173,7 @@ export async function createProxyServer(options = {}) {
166
173
  ?? ['1', 'true', 'yes', 'on'].includes(String(process.env.SUB2API_COMPAT_DROP_TOOLS_ON_COMPACT || '').toLowerCase());
167
174
  const stripPreviousResponseId = options.stripPreviousResponseId
168
175
  ?? ['1', 'true', 'yes', 'on'].includes(String(process.env.SUB2API_COMPAT_STRIP_PREVIOUS_RESPONSE_ID || '').toLowerCase());
176
+ const forceModel = String(options.forceModel ?? process.env.SUB2API_COMPAT_FORCE_MODEL ?? '').trim();
169
177
 
170
178
  const server = http.createServer(async (req, res) => {
171
179
  const startAt = Date.now();
@@ -189,7 +197,12 @@ export async function createProxyServer(options = {}) {
189
197
  if (isResponsesPath && rawBody.length > 0) {
190
198
  try {
191
199
  const parsed = JSON.parse(rawBody.toString('utf8'));
192
- const normalized = normalizeResponsesPayload(parsed, { stripPreviousResponseId });
200
+ const inputModel = typeof parsed.model === 'string' ? parsed.model : '';
201
+ const normalized = normalizeResponsesPayload(parsed, {
202
+ stripPreviousResponseId,
203
+ forceModel
204
+ });
205
+ const outputModel = typeof normalized.model === 'string' ? normalized.model : '';
193
206
  body = Buffer.from(JSON.stringify(normalized));
194
207
  if (body.length > maxReqBytes) {
195
208
  const compacted = compactResponsesPayload(normalized, { dropToolsOnCompact });
@@ -214,6 +227,13 @@ export async function createProxyServer(options = {}) {
214
227
  minimalBody = minimalBuf;
215
228
  }
216
229
  }
230
+ if (forceModel && inputModel && inputModel !== outputModel) {
231
+ appendLog(logFile, {
232
+ method: req.method,
233
+ path: url.pathname,
234
+ modelRewrite: { from: inputModel, to: outputModel }
235
+ });
236
+ }
217
237
  } catch {
218
238
  // Keep original body if parse fails.
219
239
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autotask/atools-tool",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "ATools CLI for OpenClaw proxy compatibility and interactive model configuration",
5
5
  "type": "module",
6
6
  "license": "MIT",