xyteeecode 1.0.22 → 1.0.24

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.
Files changed (3) hide show
  1. package/bin/xyteeecode +157 -66
  2. package/package.json +166 -19
  3. package/README.md +0 -127
package/bin/xyteeecode CHANGED
@@ -7,13 +7,126 @@ const os = require("os")
7
7
 
8
8
  const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
9
9
 
10
- // Loader fatal-message patterns. When the bundled musl loader cannot start
11
- // the target it prints one of these and exits non-zero before the program
12
- // does anything; in that case we retry the same binary through its PT_INTERP.
13
- const loaderFatal =
14
- /Not a valid dynamic program|Error loading shared library|Error relocating .*/i
10
+ // The musl/arm64 standalone is a fixed-address (non-PIE) ELF, so it can only
11
+ // start when the kernel execs it with an absolute PT_INTERP loader. On stock
12
+ // Termux the shipped slot ("/lib/ld-musl-aarch64.so.1") does not exist, and
13
+ // spawning the bundled loader by hand cannot map the non-PIE image either
14
+ // ("Could not find a PHDR: broken executable?" / "Not a valid dynamic
15
+ // program"). The working shape is the same as on Alpine: PT_INTERP resolves to
16
+ // the bundled loader's absolute path. The package postinstall performs that
17
+ // rewrite when it runs (npm 11 skips it unless the package is trusted); this
18
+ // helper re-runs it from the launcher at startup so an interrupted install can
19
+ // never leave the CLI unusable.
20
+ function ensureLoaderInterp(binaryPath, loaderPath) {
21
+ const header = Buffer.alloc(0x40)
22
+ let fd = null
23
+ try {
24
+ fd = fs.openSync(binaryPath, "r+")
25
+ } catch {
26
+ return false
27
+ }
28
+ try {
29
+ fs.readSync(fd, header, 0, header.length, 0)
30
+ if (
31
+ header.readUInt8(0) !== 0x7f ||
32
+ header.toString("ascii", 1, 4) !== "ELF" ||
33
+ header.readUInt8(4) !== 2
34
+ ) {
35
+ return false
36
+ }
37
+ const little = header.readUInt8(5) === 1
38
+ const u16 = (o) => (little ? header.readUInt16LE(o) : header.readUInt16BE(o))
39
+ const u32 = (o) => (little ? header.readUInt32LE(o) : header.readUInt32BE(o))
40
+ const u64 = (o) => (little ? header.readBigUInt64LE(o) : header.readBigUInt64BE(o))
41
+
42
+ const readElf = (pos, len) => {
43
+ const buf = Buffer.alloc(len)
44
+ let off = 0
45
+ while (off < len) {
46
+ const read = fs.readSync(fd, buf, off, len - off, pos + off)
47
+ if (read <= 0) throw new Error("short read")
48
+ off += read
49
+ }
50
+ return buf
51
+ }
15
52
 
16
- function run(target, inheritedEnv, bypassLoader) {
53
+ const ePhoff = Number(u64(0x20))
54
+ const ePhentsize = u16(0x36)
55
+ const ePhnum = u16(0x38)
56
+ const eShoff = Number(u64(0x28))
57
+ const eShentsize = u16(0x3a)
58
+ const eShnum = u16(0x3c)
59
+ const size = fs.fstatSync(fd).size
60
+
61
+ let interpPhdr = -1
62
+ let interpOffset = -1
63
+ const phdrs = readElf(ePhoff, ePhnum * ePhentsize)
64
+ for (let i = 0; i < ePhnum; i++) {
65
+ const ph = ePhoff + i * ePhentsize
66
+ if (phdrs.readUInt32LE(i * ePhentsize) === 3) {
67
+ interpPhdr = ph
68
+ interpOffset = Number(
69
+ little
70
+ ? phdrs.readBigUInt64LE(i * ePhentsize + 8)
71
+ : phdrs.readBigUInt64BE(i * ePhentsize + 8),
72
+ )
73
+ }
74
+ }
75
+ if (interpPhdr < 0) {
76
+ return false
77
+ }
78
+
79
+ let existing = ""
80
+ if (interpOffset + 1 <= size) {
81
+ const buf = readElf(interpOffset, Math.min(4096, size - interpOffset))
82
+ const end = buf.indexOf(0)
83
+ existing = buf.toString("utf8", 0, end === -1 ? buf.length : end)
84
+ }
85
+ if (existing === loaderPath) {
86
+ return true
87
+ }
88
+
89
+ const newPath = Buffer.from(loaderPath + "\0", "utf8")
90
+
91
+ // The kernel reads at most PT_INTERP.p_filesz bytes of the interpreter
92
+ // path, so it cannot grow in place (the shipped slot is 24 bytes). Append
93
+ // the full path to the file and retarget the program header at it.
94
+ const newOffset = size
95
+ const buf = Buffer.concat([readElf(0, size), newPath])
96
+ buf.writeBigUInt64LE(BigInt(newOffset), interpPhdr + 8)
97
+ buf.writeBigUInt64LE(BigInt(newPath.length), interpPhdr + 32)
98
+
99
+ // Keep the .interp section header consistent for tools (readelf, etc.).
100
+ for (let i = 0; i < eShnum; i++) {
101
+ const sh = eShoff + i * eShentsize
102
+ const shType = little ? buf.readUInt32LE(sh) : buf.readUInt32BE(sh)
103
+ const shOffset = little
104
+ ? Number(buf.readBigUInt64LE(sh + 24))
105
+ : Number(buf.readBigUInt64BE(sh + 24))
106
+ if (shType === 1 && shOffset === interpOffset) {
107
+ buf.writeBigUInt64LE(BigInt(newOffset), sh + 24)
108
+ buf.writeBigUInt64LE(BigInt(newPath.length), sh + 32)
109
+ break
110
+ }
111
+ }
112
+
113
+ fs.writeFileSync(binaryPath, buf)
114
+ fs.fsyncSync(fd)
115
+ return true
116
+ } catch {
117
+ return false
118
+ } finally {
119
+ if (fd !== null) {
120
+ try {
121
+ fs.closeSync(fd)
122
+ } catch {
123
+ // ignore
124
+ }
125
+ }
126
+ }
127
+ }
128
+
129
+ function run(target, inheritedEnv) {
17
130
  return new Promise((resolve) => {
18
131
  const binDir = path.dirname(target)
19
132
  // The candidate path looks like ".../xyteeecode-linux-arm64-musl/bin/xyteeecode".
@@ -30,11 +143,19 @@ function run(target, inheritedEnv, bypassLoader) {
30
143
  : binDir
31
144
  }
32
145
 
146
+ // process.platform/arch mirror os.platform()/os.arch(); both are checked so
147
+ // the launcher stays scriptable (os is monkeypatchable in node, process
148
+ // fields are monkeypatchable in bundlers like bun).
149
+ const termuxish =
150
+ isTermux() &&
151
+ (os.platform() === "android" || process.platform === "android") &&
152
+ (os.arch() === "arm64" || process.arch === "arm64")
153
+
33
154
  // Bun ≥1.4 on Termux/Android aarch64 segfaults in the HTTP event loop
34
155
  // because epoll_pwait2() trips Android's seccomp policy (faults instead of
35
156
  // returning errno). Disable the syscall there; Bun falls back to
36
157
  // epoll_pwait. See oven-sh/bun#32489/#32632.
37
- if (isTermux()) env.BUN_FEATURE_FLAG_DISABLE_EPOLL_PWAIT2 = "1"
158
+ if (termuxish) env.BUN_FEATURE_FLAG_DISABLE_EPOLL_PWAIT2 = "1"
38
159
 
39
160
  // The bundled musl libc uses a stub resolver that reads /etc/resolv.conf
40
161
  // and speaks raw UDP to its nameservers. Stock Termux/Android has no
@@ -46,69 +167,53 @@ function run(target, inheritedEnv, bypassLoader) {
46
167
  const useDnsShim = !!dnsShim && fs.existsSync(dnsShim)
47
168
 
48
169
  // Stock Termux injects libtermux-exec-ld-preload.so through LD_PRELOAD,
49
- // which the bundled musl loader cannot load. Only on Termux/Android/arm64
50
- // with the musl build, drop the preload variables (this touches the child
51
- // environment only, never the user's shell) and start the binary via the
52
- // bundled loader explicitly. An explicit loader invocation avoids relying
53
- // on bionic's exec for the musl PT_INTERP.
54
- //
55
- // process.platform/arch mirror os.platform()/os.arch(); both are checked so
56
- // the launcher stays scriptable (os is monkeypatchable in node, process
57
- // fields are monkeypatchable in bundlers like bun).
58
- const loader = isMusl ? path.join(binDir, "ld-musl-aarch64.so.1") : null
59
- const useLoader =
60
- !bypassLoader &&
61
- loader !== null &&
62
- isTermux() &&
63
- (os.platform() === "android" || process.platform === "android") &&
64
- (os.arch() === "arm64" || process.arch === "arm64") &&
65
- fs.existsSync(loader)
66
- if (useLoader) {
170
+ // which the bundled musl loader cannot load. Only on Termux/Android/arm64,
171
+ // drop the preload variables (this touches the child environment only,
172
+ // never the user's shell).
173
+ if (termuxish) {
67
174
  delete env.LD_PRELOAD
68
175
  delete env.LD_PRELOAD_64
69
176
  env.LD_LIBRARY_PATH = binDir
70
177
  if (useDnsShim) env.LD_PRELOAD = dnsShim
178
+
179
+ // Wire the bundled loader into PT_INTERP and exec the target directly.
180
+ // Stock Termux's /lib does not exist and spawning the loader by hand
181
+ // cannot start the non-PIE image, so this is the only launch path.
182
+ const loader = isMusl ? path.join(binDir, "ld-musl-aarch64.so.1") : null
183
+ if (loader && fs.existsSync(loader) && !ensureLoaderInterp(target, loader)) {
184
+ console.error(
185
+ "xyteeecode: could not wire the bundled musl loader into " +
186
+ target +
187
+ ". Reinstall with `npm install -g xyteeecode --allow-scripts` " +
188
+ "and make sure the package directory is writable.",
189
+ )
190
+ process.exit(1)
191
+ }
71
192
  } else if (useDnsShim) {
72
193
  env.LD_PRELOAD = env.LD_PRELOAD
73
194
  ? dnsShim + ":" + env.LD_PRELOAD
74
195
  : dnsShim
75
196
  }
76
197
 
77
- const command = useLoader ? loader : target
78
- const args = useLoader ? [target, ...process.argv.slice(2)] : process.argv.slice(2)
79
-
80
- const child = childProcess.spawn(command, args, {
81
- stdio: ["inherit", "inherit", useLoader ? "pipe" : "inherit"],
198
+ const child = childProcess.spawn(target, process.argv.slice(2), {
199
+ stdio: ["inherit", "inherit", "inherit"],
82
200
  env,
83
201
  })
84
202
 
85
- // Keep the loader's diagnostics visible while mirroring them to a buffer:
86
- // a loader that refuses to start the program (e.g. the fixed load address
87
- // of the non-PIE build is already taken by the loader's own mapping on
88
- // some kernels) must trigger a PT_INTERP retry instead of a hard exit.
89
- let loaderStderr = ""
90
- if (useLoader && child.stderr) {
91
- child.stderr.on("data", (chunk) => {
92
- loaderStderr += chunk
93
- process.stderr.write(chunk)
94
- })
95
- }
96
-
97
203
  let spawned = false
98
204
  let exited = false
99
205
 
100
206
  child.on("error", (error) => {
101
- // exec/execvp can't find the interpreter or the executable itself
102
- // (e.g. a glibc binary on Termux's bionic libc). Allow the caller to
103
- // fall back to the next candidate (e.g. the musl build with its bundled
104
- // loader and libraries for Termux).
207
+ // exec/execvp can't find the interpreter or the executable itself.
208
+ // On Termux this usually means the kernel could not resolve PT_INTERP;
209
+ // surface the real reason and let the caller try the next candidate.
105
210
  if (error.code === "ENOENT" && !spawned && !exited) {
106
- if (bypassLoader) {
107
- // The PT_INTERP retry could not even be exec'd (the kernel cannot
108
- // find the rewritten interpreter). Surface the loader-style error:
109
- // nothing else can be tried for this candidate.
110
- console.error(error.message)
111
- process.exit(1)
211
+ if (termuxish) {
212
+ console.error(
213
+ "xyteeecode: could not exec " + target + " (missing interpreter " +
214
+ "after wiring the bundled loader). Reinstall with " +
215
+ "`npm install -g xyteeecode --allow-scripts`.",
216
+ )
112
217
  }
113
218
  resolve(false)
114
219
  return
@@ -145,20 +250,6 @@ function run(target, inheritedEnv, bypassLoader) {
145
250
  return
146
251
  }
147
252
 
148
- if (
149
- !bypassLoader &&
150
- useLoader &&
151
- typeof code === "number" &&
152
- code !== 0 &&
153
- loaderFatal.test(loaderStderr)
154
- ) {
155
- // The bundled loader could not start the binary in this environment.
156
- // Retry the same candidate through its (postinstall-rewritten) PT_INTERP
157
- // with the same sanitized child environment.
158
- run(target, env, true)
159
- return
160
- }
161
-
162
253
  process.exit(typeof code === "number" ? code : 0)
163
254
  })
164
255
  })
package/package.json CHANGED
@@ -1,29 +1,176 @@
1
1
  {
2
+ "$schema": "https://json.schemastore.org/package.json",
3
+ "version": "1.0.24",
2
4
  "name": "xyteeecode",
3
- "version": "1.0.22",
4
- "description": "xyteeecode - AI coding agent CLI",
5
+ "type": "module",
5
6
  "license": "MIT",
6
- "homepage": "https://xyteee.com",
7
- "keywords": [
8
- "ai",
9
- "coding",
10
- "agent",
11
- "cli",
12
- "terminal",
13
- "xyteeecode",
14
- "llm"
15
- ],
16
7
  "files": [
17
- "bin",
18
- "README.md"
8
+ "bin"
19
9
  ],
20
- "bin": {
21
- "xyteeecode": "bin/xyteeecode"
22
- },
23
10
  "engines": {
24
11
  "node": ">=18"
25
12
  },
26
13
  "optionalDependencies": {
27
- "xyteeecode-linux-arm64-musl": "1.0.22"
14
+ "xyteeecode-linux-arm64": "1.0.14",
15
+ "xyteeecode-linux-x64": "1.0.14",
16
+ "xyteeecode-linux-arm64-musl": "1.0.23",
17
+ "xyteeecode-linux-x64-musl": "1.0.14"
18
+ },
19
+ "trustedDependencies": [
20
+ "xyteeecode-linux-arm64",
21
+ "xyteeecode-linux-x64",
22
+ "xyteeecode-linux-arm64-musl",
23
+ "xyteeecode-linux-x64-musl"
24
+ ],
25
+ "scripts": {
26
+ "typecheck": "tsgo --noEmit",
27
+ "test": "bun test --timeout 30000 --only-failures",
28
+ "test:httpapi": "bun run script/httpapi-exercise.ts --mode coverage --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode auth --fail-on-missing --fail-on-skip && bun run script/httpapi-exercise.ts --mode effect --fail-on-missing --fail-on-skip",
29
+ "bench:test": "bun run script/bench-test-suite.ts",
30
+ "profile:test": "bun run script/profile-test-files.ts",
31
+ "build": "bun run script/build.ts",
32
+ "dev": "bun run ./src/index.ts",
33
+ "dev:temporary": "bun run ./src/temporary.ts",
34
+ "test:termux-launcher": "node script/test-termux-launcher.cjs"
35
+ },
36
+ "bin": {
37
+ "xyteeecode": "./bin/xyteeecode"
38
+ },
39
+ "exports": {
40
+ "./*": "./src/*.ts"
41
+ },
42
+ "imports": {
43
+ "#db": {
44
+ "bun": "./src/storage/db.bun.ts",
45
+ "node": "./src/storage/db.node.ts",
46
+ "default": "./src/storage/db.bun.ts"
47
+ }
48
+ },
49
+ "devDependencies": {
50
+ "@babel/core": "7.28.4",
51
+ "@octokit/webhooks-types": "7.6.1",
52
+ "@opencode-ai/core": "workspace:*",
53
+ "@opencode-ai/http-recorder": "workspace:*",
54
+ "@opencode-ai/script": "workspace:*",
55
+ "@standard-schema/spec": "1.0.0",
56
+ "@tsconfig/bun": "catalog:",
57
+ "@types/babel__core": "7.20.5",
58
+ "@types/bun": "catalog:",
59
+ "@types/cross-spawn": "catalog:",
60
+ "@types/mime-types": "3.0.1",
61
+ "@types/npm-package-arg": "6.1.4",
62
+ "@types/semver": "^7.5.8",
63
+ "@types/turndown": "5.0.5",
64
+ "@types/yargs": "17.0.33",
65
+ "@typescript/native-preview": "catalog:",
66
+ "drizzle-orm": "catalog:",
67
+ "prettier": "3.6.2",
68
+ "typescript": "catalog:",
69
+ "vscode-languageserver-types": "3.17.5",
70
+ "why-is-node-running": "3.2.2"
71
+ },
72
+ "dependencies": {
73
+ "@actions/core": "1.11.1",
74
+ "@actions/github": "6.0.1",
75
+ "@agentclientprotocol/sdk": "0.21.0",
76
+ "@ai-sdk/alibaba": "1.0.17",
77
+ "@ai-sdk/amazon-bedrock": "4.0.166",
78
+ "@ai-sdk/anthropic": "3.0.111",
79
+ "@ai-sdk/azure": "3.0.93",
80
+ "@ai-sdk/cerebras": "2.0.60",
81
+ "@ai-sdk/cohere": "3.0.27",
82
+ "@ai-sdk/deepinfra": "2.0.41",
83
+ "@ai-sdk/gateway": "3.0.104",
84
+ "@ai-sdk/google": "3.0.73",
85
+ "@ai-sdk/google-vertex": "4.0.181",
86
+ "@ai-sdk/groq": "3.0.31",
87
+ "@ai-sdk/mistral": "3.0.51",
88
+ "@ai-sdk/openai": "3.0.88",
89
+ "@ai-sdk/openai-compatible": "2.0.41",
90
+ "@ai-sdk/perplexity": "3.0.26",
91
+ "@ai-sdk/provider": "3.0.8",
92
+ "@ai-sdk/togetherai": "2.0.41",
93
+ "@ai-sdk/vercel": "2.0.39",
94
+ "@ai-sdk/xai": "3.0.102",
95
+ "@aws-sdk/credential-providers": "3.1057.0",
96
+ "@clack/prompts": "1.0.0-alpha.1",
97
+ "@effect/opentelemetry": "catalog:",
98
+ "@effect/platform-node": "catalog:",
99
+ "@ff-labs/fff-bun": "0.9.4",
100
+ "@gitlab/opencode-gitlab-auth": "1.3.3",
101
+ "@modelcontextprotocol/sdk": "1.29.0",
102
+ "@octokit/graphql": "9.0.2",
103
+ "@octokit/rest": "catalog:",
104
+ "@openauthjs/openauth": "catalog:",
105
+ "@opencode-ai/codemode": "workspace:*",
106
+ "@opencode-ai/llm": "workspace:*",
107
+ "@opencode-ai/plugin": "workspace:*",
108
+ "@opencode-ai/protocol": "workspace:*",
109
+ "@opencode-ai/schema": "workspace:*",
110
+ "@opencode-ai/script": "workspace:*",
111
+ "@opencode-ai/sdk": "workspace:*",
112
+ "@opencode-ai/server": "workspace:*",
113
+ "@opencode-ai/tui": "workspace:*",
114
+ "@openrouter/ai-sdk-provider": "2.9.0",
115
+ "@opentelemetry/api": "1.9.0",
116
+ "@opentelemetry/context-async-hooks": "2.6.1",
117
+ "@opentelemetry/exporter-trace-otlp-http": "0.214.0",
118
+ "@opentelemetry/sdk-trace-base": "2.6.1",
119
+ "@opentelemetry/sdk-trace-node": "2.6.1",
120
+ "@opentui/core": "catalog:",
121
+ "@opentui/keymap": "catalog:",
122
+ "@opentui/solid": "catalog:",
123
+ "@parcel/watcher": "2.5.1",
124
+ "@pierre/diffs": "catalog:",
125
+ "@silvia-odwyer/photon-node": "0.3.4",
126
+ "@solid-primitives/event-bus": "1.1.2",
127
+ "@solid-primitives/scheduled": "1.5.2",
128
+ "@standard-schema/spec": "1.0.0",
129
+ "@types/ws": "8.18.1",
130
+ "@zip.js/zip.js": "2.7.62",
131
+ "ai": "catalog:",
132
+ "ai-gateway-provider": "3.2.0",
133
+ "bonjour-service": "1.3.0",
134
+ "chokidar": "4.0.3",
135
+ "cross-spawn": "catalog:",
136
+ "decimal.js": "10.5.0",
137
+ "diff": "catalog:",
138
+ "drizzle-orm": "catalog:",
139
+ "effect": "catalog:",
140
+ "fuzzysort": "3.1.0",
141
+ "gitlab-ai-provider": "6.15.0",
142
+ "glob": "13.0.5",
143
+ "google-auth-library": "10.5.0",
144
+ "gray-matter": "4.0.3",
145
+ "htmlparser2": "8.0.2",
146
+ "ignore": "7.0.5",
147
+ "immer": "11.1.4",
148
+ "jsonc-parser": "3.3.1",
149
+ "mime-types": "3.0.2",
150
+ "minimatch": "10.0.3",
151
+ "npm-package-arg": "13.0.2",
152
+ "open": "10.1.2",
153
+ "opencode-gitlab-auth": "2.1.0",
154
+ "opencode-poe-auth": "0.0.1",
155
+ "opentui-spinner": "catalog:",
156
+ "partial-json": "0.1.7",
157
+ "remeda": "catalog:",
158
+ "semver": "^7.6.3",
159
+ "solid-js": "catalog:",
160
+ "strip-ansi": "7.1.2",
161
+ "tree-sitter-bash": "0.25.0",
162
+ "tree-sitter-powershell": "0.25.10",
163
+ "turndown": "7.2.0",
164
+ "ulid": "catalog:",
165
+ "venice-ai-sdk-provider": "2.1.1",
166
+ "vscode-jsonrpc": "8.2.1",
167
+ "web-tree-sitter": "0.25.10",
168
+ "ws": "8.21.0",
169
+ "xdg-basedir": "5.1.0",
170
+ "yargs": "18.0.0",
171
+ "zod": "catalog:"
172
+ },
173
+ "overrides": {
174
+ "drizzle-orm": "catalog:"
28
175
  }
29
- }
176
+ }
package/README.md DELETED
@@ -1,127 +0,0 @@
1
- # xyteeecode
2
-
3
- xyteeecode is an AI coding agent that runs in your terminal — powered by open models, with a clean TUI, agentic tool use, and full session history.
4
-
5
- It is the rebranded fork of [opencode](https://opencode.ai), distributed as a single self-contained binary.
6
-
7
- ## Install
8
-
9
- ```bash
10
- npm install -g xyteeecode
11
- ```
12
-
13
- The package automatically installs the matching binary for your platform (arm64/x64, Linux/macOS/Windows).
14
-
15
- ## Quick start
16
-
17
- ### With Kira free models (no API key setup required after signup)
18
-
19
- 1. Sign up for a free key at [kira](https://kiraai.vn) (the gateway used in this project).
20
- 2. Place your key in your global config:
21
-
22
- `~/.config/opencode/opencode.jsonc`
23
-
24
- ```jsonc
25
- {
26
- "provider": {
27
- "kira": {
28
- "npm": "@ai-sdk/openai-compatible",
29
- "name": "Kira",
30
- "options": {
31
- "baseURL": "https://kiraai.vn/api/v1",
32
- "apiKey": "{env:KIRA_API_KEY}"
33
- },
34
- "models": {
35
- "qwen3.8-flash-free": {
36
- "name": "Qwen 3.8 Flash (free)"
37
- },
38
- "hy3-free": {
39
- "name": "HY3 (free)"
40
- },
41
- "mimo-v2.5-free": {
42
- "name": "Mimo 2.5 (free)"
43
- },
44
- "glm-5.3-free": {
45
- "name": "GLM 5.3 (free)"
46
- }
47
- }
48
- }
49
- },
50
- "model": "kira/qwen3.8-flash-free"
51
- }
52
- ```
53
-
54
- 3. Run:
55
-
56
- ```bash
57
- xyteeecode
58
- ```
59
-
60
- Or run a one-off prompt without opening the TUI:
61
-
62
- ```bash
63
- xyteeecode run "explain this codebase" --model kira/qwen3.8-flash-free
64
- ```
65
-
66
- ### With other providers
67
-
68
- Authenticate with any provider the CLI supports (Anthropic, OpenAI, Groq, and hundreds more via models.dev), either through the built-in `/login` flow or by adding provider config the same way as above.
69
-
70
- ### With Connect (AgentRouter)
71
-
72
- [AgentRouter](https://agentrouter.org) is a free AI coding gateway with OpenAI-compatible and Anthropic-compatible endpoints. Add one or both gateways to your global config:
73
-
74
- `~/.config/opencode/opencode.jsonc`
75
-
76
- ```jsonc
77
- {
78
- "provider": {
79
- "connect": {
80
- "npm": "@ai-sdk/openai-compatible",
81
- "name": "Connect (OpenAI gateway)",
82
- "options": {
83
- "baseURL": "https://agentrouter.org/v1",
84
- "apiKey": "{env:AGENTROUTER_API_KEY}"
85
- },
86
- "models": {
87
- "deepseek-v4-flash": { "name": "DeepSeek V4 Flash" },
88
- "glm-5.3": { "name": "GLM 5.3" },
89
- "gpt-5.6-sol": { "name": "GPT 5.6 SOL" }
90
- }
91
- },
92
- "connect-anthropic": {
93
- "npm": "@ai-sdk/anthropic",
94
- "name": "Connect (Anthropic gateway)",
95
- "options": {
96
- "baseURL": "https://agentrouter.org",
97
- "apiKey": "{env:AGENTROUTER_API_KEY}"
98
- },
99
- "models": {
100
- "claude-opus-5": { "name": "Claude Opus 5" },
101
- "claude-opus-4-8": { "name": "Claude Opus 4-8" }
102
- }
103
- }
104
- },
105
- "model": "connect/deepseek-v4-flash"
106
- }
107
- ```
108
-
109
- Note: for the Anthropic gateway the base URL must **not** include `/v1`. The model list is dynamic — always check the latest models at [https://ps.air-outer.com/console/personal](https://ps.air-outer.com/console/personal).
110
-
111
- ## Commands
112
-
113
- - `xyteeecode` — launch the interactive terminal UI
114
- - `xyteeecode run "<prompt>"` — run a single prompt in the current directory
115
- - `xyteeecode models <provider>` — list available models for a provider
116
- - `xyteeecode agent list` — list agents
117
- - `xyteeecode mcp list` — list MCP servers
118
- - `xyteeecode session list` — list past sessions
119
- - `xyteeecode --help` — all commands and flags
120
-
121
- ## Config
122
-
123
- Global config lives at `~/.config/opencode/opencode.jsonc`. Project config lives in `.opencode/opencode.json` at your project root.
124
-
125
- ## License
126
-
127
- MIT