rird-ai 2.3.0 → 2.3.2
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/pty-wrapper.js +285 -0
- package/bin/rird +8 -27
- package/bin/rird-perf.js +37 -0
- package/package.json +16 -23
- package/postinstall.mjs +4 -2
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PTY Wrapper for RIRD
|
|
5
|
+
*
|
|
6
|
+
* Intercepts OpenCode binary output and replaces branding strings.
|
|
7
|
+
* Works on Windows, macOS, and Linux.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node pty-wrapper.js <path-to-binary> [args...]
|
|
11
|
+
*
|
|
12
|
+
* Environment:
|
|
13
|
+
* RIRD_NO_PTY=1 - Disable PTY, use simple pipes (fallback mode)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const childProcess = require("child_process")
|
|
17
|
+
const fs = require("fs")
|
|
18
|
+
const path = require("path")
|
|
19
|
+
const os = require("os")
|
|
20
|
+
|
|
21
|
+
// Branding replacements for output - order matters (specific patterns first)
|
|
22
|
+
const REPLACEMENTS = [
|
|
23
|
+
// URLs and paths
|
|
24
|
+
{ from: /sst\/opencode/gi, to: "rird.ai" },
|
|
25
|
+
{ from: /opencode\.ai/gi, to: "rird.ai" },
|
|
26
|
+
{ from: /rird\.ai\/desktop/gi, to: "rird.ai" },
|
|
27
|
+
{ from: /https?:\/\/[^\s]*opencode[^\s]*/gi, to: (match) => match.replace(/opencode/gi, "rird") },
|
|
28
|
+
// Known fake/test model names
|
|
29
|
+
{ from: /OpenCode Zen/gi, to: "RIRD AI" },
|
|
30
|
+
{ from: /OpenCode\/Zen/gi, to: "RIRD AI" },
|
|
31
|
+
{ from: /opencode-zen/gi, to: "rird-brain" },
|
|
32
|
+
{ from: /Big Pickle/gi, to: "RIRD AI" },
|
|
33
|
+
{ from: /big-pickle/gi, to: "rird-brain" },
|
|
34
|
+
{ from: /gpt-5-nano/gi, to: "RIRD Brain" },
|
|
35
|
+
{ from: /GPT-5 Nano/gi, to: "RIRD Brain" },
|
|
36
|
+
// Provider/model name patterns (catch OpenCode/anything)
|
|
37
|
+
{ from: /OpenCode\s*\/\s*(\w+)/gi, to: "RIRD/$1" },
|
|
38
|
+
// Generic branding
|
|
39
|
+
{ from: /OpenCode/g, to: "RIRD" },
|
|
40
|
+
{ from: /opencode/g, to: "rird" },
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Replace branding strings in a buffer/string
|
|
45
|
+
*/
|
|
46
|
+
function replaceBranding(data) {
|
|
47
|
+
let str = data.toString()
|
|
48
|
+
for (const { from, to } of REPLACEMENTS) {
|
|
49
|
+
str = str.replace(from, to)
|
|
50
|
+
}
|
|
51
|
+
return str
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Try to load node-pty dynamically
|
|
56
|
+
*/
|
|
57
|
+
function tryLoadPty() {
|
|
58
|
+
try {
|
|
59
|
+
// Try bun-pty first (listed in package.json)
|
|
60
|
+
return require("bun-pty")
|
|
61
|
+
} catch (e1) {
|
|
62
|
+
try {
|
|
63
|
+
// Fall back to node-pty
|
|
64
|
+
return require("node-pty")
|
|
65
|
+
} catch (e2) {
|
|
66
|
+
return null
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Run with PTY (interactive TUI support)
|
|
73
|
+
*/
|
|
74
|
+
function runWithPty(pty, binaryPath, args, env) {
|
|
75
|
+
const isWindows = os.platform() === "win32"
|
|
76
|
+
const shell = isWindows ? "cmd.exe" : undefined
|
|
77
|
+
|
|
78
|
+
// Get terminal size
|
|
79
|
+
const cols = process.stdout.columns || 80
|
|
80
|
+
const rows = process.stdout.rows || 24
|
|
81
|
+
|
|
82
|
+
// Spawn PTY
|
|
83
|
+
const ptyProcess = pty.spawn(binaryPath, args, {
|
|
84
|
+
name: "xterm-256color",
|
|
85
|
+
cols: cols,
|
|
86
|
+
rows: rows,
|
|
87
|
+
cwd: process.cwd(),
|
|
88
|
+
env: env,
|
|
89
|
+
// Windows-specific options
|
|
90
|
+
...(isWindows && {
|
|
91
|
+
useConpty: true,
|
|
92
|
+
conptyInheritCursor: true,
|
|
93
|
+
}),
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Handle output with branding replacement
|
|
97
|
+
ptyProcess.onData((data) => {
|
|
98
|
+
const replaced = replaceBranding(data)
|
|
99
|
+
process.stdout.write(replaced)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
// Handle input
|
|
103
|
+
if (process.stdin.isTTY) {
|
|
104
|
+
process.stdin.setRawMode(true)
|
|
105
|
+
}
|
|
106
|
+
process.stdin.resume()
|
|
107
|
+
process.stdin.on("data", (data) => {
|
|
108
|
+
ptyProcess.write(data.toString())
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
// Handle terminal resize
|
|
112
|
+
process.stdout.on("resize", () => {
|
|
113
|
+
ptyProcess.resize(
|
|
114
|
+
process.stdout.columns || 80,
|
|
115
|
+
process.stdout.rows || 24
|
|
116
|
+
)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// Handle exit
|
|
120
|
+
ptyProcess.onExit(({ exitCode }) => {
|
|
121
|
+
if (process.stdin.isTTY) {
|
|
122
|
+
process.stdin.setRawMode(false)
|
|
123
|
+
}
|
|
124
|
+
process.exit(exitCode)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Handle signals
|
|
128
|
+
process.on("SIGINT", () => {
|
|
129
|
+
ptyProcess.kill("SIGINT")
|
|
130
|
+
})
|
|
131
|
+
process.on("SIGTERM", () => {
|
|
132
|
+
ptyProcess.kill("SIGTERM")
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Run with simple pipes (fallback mode, less interactive)
|
|
138
|
+
*/
|
|
139
|
+
function runWithPipes(binaryPath, args, env) {
|
|
140
|
+
const isWindows = os.platform() === "win32"
|
|
141
|
+
|
|
142
|
+
// Use spawn with pipe for stdout/stderr to intercept output
|
|
143
|
+
const child = childProcess.spawn(binaryPath, args, {
|
|
144
|
+
env: env,
|
|
145
|
+
cwd: process.cwd(),
|
|
146
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
147
|
+
// Windows needs shell for some binaries
|
|
148
|
+
...(isWindows && { shell: false }),
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// Buffer for incomplete ANSI sequences
|
|
152
|
+
let stdoutBuffer = ""
|
|
153
|
+
let stderrBuffer = ""
|
|
154
|
+
|
|
155
|
+
// Process stdout
|
|
156
|
+
child.stdout.on("data", (data) => {
|
|
157
|
+
stdoutBuffer += data.toString()
|
|
158
|
+
// Process complete lines or flush on certain patterns
|
|
159
|
+
const replaced = replaceBranding(stdoutBuffer)
|
|
160
|
+
process.stdout.write(replaced)
|
|
161
|
+
stdoutBuffer = ""
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// Process stderr
|
|
165
|
+
child.stderr.on("data", (data) => {
|
|
166
|
+
stderrBuffer += data.toString()
|
|
167
|
+
const replaced = replaceBranding(stderrBuffer)
|
|
168
|
+
process.stderr.write(replaced)
|
|
169
|
+
stderrBuffer = ""
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// Forward stdin
|
|
173
|
+
process.stdin.pipe(child.stdin)
|
|
174
|
+
|
|
175
|
+
// Handle exit
|
|
176
|
+
child.on("exit", (code) => {
|
|
177
|
+
// Flush any remaining buffers
|
|
178
|
+
if (stdoutBuffer) {
|
|
179
|
+
process.stdout.write(replaceBranding(stdoutBuffer))
|
|
180
|
+
}
|
|
181
|
+
if (stderrBuffer) {
|
|
182
|
+
process.stderr.write(replaceBranding(stderrBuffer))
|
|
183
|
+
}
|
|
184
|
+
process.exit(code || 0)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
child.on("error", (err) => {
|
|
188
|
+
console.error("Failed to start process:", err.message)
|
|
189
|
+
process.exit(1)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Handle signals
|
|
193
|
+
process.on("SIGINT", () => {
|
|
194
|
+
child.kill("SIGINT")
|
|
195
|
+
})
|
|
196
|
+
process.on("SIGTERM", () => {
|
|
197
|
+
child.kill("SIGTERM")
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Run with inherited stdio (no branding replacement, but full interactivity)
|
|
203
|
+
* Used when PTY fails and pipes don't work well
|
|
204
|
+
*/
|
|
205
|
+
function runWithInherited(binaryPath, args, env) {
|
|
206
|
+
const result = childProcess.spawnSync(binaryPath, args, {
|
|
207
|
+
stdio: "inherit",
|
|
208
|
+
env: env,
|
|
209
|
+
cwd: process.cwd(),
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
if (result.error) {
|
|
213
|
+
console.error("Failed to start process:", result.error.message)
|
|
214
|
+
process.exit(1)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
process.exit(result.status || 0)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Main entry point
|
|
222
|
+
*/
|
|
223
|
+
function main() {
|
|
224
|
+
const args = process.argv.slice(2)
|
|
225
|
+
|
|
226
|
+
if (args.length === 0) {
|
|
227
|
+
console.error("Usage: pty-wrapper.js <binary> [args...]")
|
|
228
|
+
process.exit(1)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const binaryPath = args[0]
|
|
232
|
+
const binaryArgs = args.slice(1)
|
|
233
|
+
|
|
234
|
+
// Verify binary exists
|
|
235
|
+
if (!fs.existsSync(binaryPath)) {
|
|
236
|
+
console.error("Binary not found:", binaryPath)
|
|
237
|
+
process.exit(1)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Prepare environment
|
|
241
|
+
const env = { ...process.env }
|
|
242
|
+
|
|
243
|
+
// Check for fallback mode
|
|
244
|
+
const usePipes = process.env.RIRD_NO_PTY === "1"
|
|
245
|
+
const useInherited = process.env.RIRD_INHERITED === "1"
|
|
246
|
+
|
|
247
|
+
if (useInherited) {
|
|
248
|
+
// No branding replacement, full interactivity
|
|
249
|
+
runWithInherited(binaryPath, binaryArgs, env)
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (usePipes) {
|
|
254
|
+
// Simple pipes mode
|
|
255
|
+
runWithPipes(binaryPath, binaryArgs, env)
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Try PTY first
|
|
260
|
+
const pty = tryLoadPty()
|
|
261
|
+
|
|
262
|
+
if (pty && process.stdout.isTTY) {
|
|
263
|
+
try {
|
|
264
|
+
runWithPty(pty, binaryPath, binaryArgs, env)
|
|
265
|
+
return
|
|
266
|
+
} catch (err) {
|
|
267
|
+
// PTY failed, fall through to pipes
|
|
268
|
+
console.error("PTY initialization failed, falling back to pipes:", err.message)
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Fall back to pipes
|
|
273
|
+
runWithPipes(binaryPath, binaryArgs, env)
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Export for testing
|
|
277
|
+
module.exports = {
|
|
278
|
+
replaceBranding,
|
|
279
|
+
REPLACEMENTS,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Run if executed directly
|
|
283
|
+
if (require.main === module) {
|
|
284
|
+
main()
|
|
285
|
+
}
|
package/bin/rird
CHANGED
|
@@ -351,6 +351,14 @@ if (command === "activate") {
|
|
|
351
351
|
env.RIRD_ENGINE_DIR = workDir
|
|
352
352
|
// Enable RIRD search tools
|
|
353
353
|
env.RIRD_ENABLE_SEARCH = "true"
|
|
354
|
+
// Ship defaults: prioritize startup/runtime latency.
|
|
355
|
+
// These are opt-out so power users can re-enable by setting env vars.
|
|
356
|
+
if (!("RIRD_DISABLE_SNAPSHOTS" in env)) env.RIRD_DISABLE_SNAPSHOTS = "1"
|
|
357
|
+
if (!("RIRD_DISABLE_SESSION_SUMMARY" in env)) env.RIRD_DISABLE_SESSION_SUMMARY = "1"
|
|
358
|
+
// Keep startup latency low by default (users can still run `rird upgrade`).
|
|
359
|
+
if (!("RIRD_SKIP_AUTO_UPDATE" in env) && !("OPENCODE_SKIP_AUTO_UPDATE" in env)) {
|
|
360
|
+
env.RIRD_SKIP_AUTO_UPDATE = "1"
|
|
361
|
+
}
|
|
354
362
|
|
|
355
363
|
// PRIMARY: Inject license key directly - this is the most reliable auth method
|
|
356
364
|
const licenseKeyPath = path.join(rirdHome, "license.key")
|
|
@@ -366,33 +374,6 @@ if (command === "activate") {
|
|
|
366
374
|
}
|
|
367
375
|
}
|
|
368
376
|
|
|
369
|
-
// FALLBACK: Inject provider API keys from license cache
|
|
370
|
-
const licenseCachePath = path.join(rirdHome, "license_cache.json")
|
|
371
|
-
if (fs.existsSync(licenseCachePath)) {
|
|
372
|
-
try {
|
|
373
|
-
const licenseCache = JSON.parse(fs.readFileSync(licenseCachePath, "utf-8"))
|
|
374
|
-
if (licenseCache.providers) {
|
|
375
|
-
if (licenseCache.providers.deepseek && licenseCache.providers.deepseek.apiKey) {
|
|
376
|
-
env.DEEPSEEK_API_KEY = licenseCache.providers.deepseek.apiKey
|
|
377
|
-
}
|
|
378
|
-
if (licenseCache.providers.siliconflow && licenseCache.providers.siliconflow.apiKey) {
|
|
379
|
-
env.SILICONFLOW_API_KEY = licenseCache.providers.siliconflow.apiKey
|
|
380
|
-
}
|
|
381
|
-
if (licenseCache.providers.openai && licenseCache.providers.openai.apiKey) {
|
|
382
|
-
env.OPENAI_API_KEY = licenseCache.providers.openai.apiKey
|
|
383
|
-
}
|
|
384
|
-
if (licenseCache.providers.anthropic && licenseCache.providers.anthropic.apiKey) {
|
|
385
|
-
env.ANTHROPIC_API_KEY = licenseCache.providers.anthropic.apiKey
|
|
386
|
-
}
|
|
387
|
-
if (licenseCache.providers.google && licenseCache.providers.google.apiKey) {
|
|
388
|
-
env.GOOGLE_GENERATIVE_AI_API_KEY = licenseCache.providers.google.apiKey
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
} catch (e) {
|
|
392
|
-
// Ignore cache read errors
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
|
|
396
377
|
function run(binPath) {
|
|
397
378
|
const result = childProcess.spawnSync(binPath, args, {
|
|
398
379
|
stdio: "inherit",
|
package/bin/rird-perf.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Performance timing wrapper for rird binary
|
|
4
|
+
const startTime = Date.now();
|
|
5
|
+
|
|
6
|
+
// Mark key milestones
|
|
7
|
+
const perf = {
|
|
8
|
+
start: 0,
|
|
9
|
+
importsDone: null,
|
|
10
|
+
commandStart: null,
|
|
11
|
+
commandEnd: null,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
perf.start = Date.now() - startTime;
|
|
15
|
+
|
|
16
|
+
// Track where actual command execution happens
|
|
17
|
+
const originalExit = process.exit;
|
|
18
|
+
let exitCode = 0;
|
|
19
|
+
|
|
20
|
+
process.exit = function(code) {
|
|
21
|
+
perf.commandEnd = Date.now() - startTime;
|
|
22
|
+
|
|
23
|
+
const elapsed = perf.commandEnd - perf.start;
|
|
24
|
+
const debugFlag = process.argv.includes("--perf-debug") || process.env.RIRD_PERF_DEBUG;
|
|
25
|
+
|
|
26
|
+
if (debugFlag || elapsed > 1000) {
|
|
27
|
+
console.error(`[RIRD PERF] Total: ${elapsed}ms`);
|
|
28
|
+
if (elapsed > 1000) {
|
|
29
|
+
console.error("[RIRD WARN] Slow startup detected - consider profiling");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
originalExit.call(process, code);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// Now execute the actual rird binary
|
|
37
|
+
import("./rird.js");
|
package/package.json
CHANGED
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rird-ai",
|
|
3
|
-
"
|
|
4
|
-
"description": "RIRD - AI computer use agent with browser automation",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"homepage": "https://rird.ai",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/rird-ai/rird.git"
|
|
10
|
-
},
|
|
3
|
+
"type": "module",
|
|
11
4
|
"bin": {
|
|
12
5
|
"rird": "./bin/rird"
|
|
13
6
|
},
|
|
14
7
|
"scripts": {
|
|
15
|
-
"postinstall": "node ./postinstall.mjs"
|
|
8
|
+
"postinstall": "bun ./postinstall.mjs || node ./postinstall.mjs"
|
|
16
9
|
},
|
|
10
|
+
"version": "2.3.2",
|
|
17
11
|
"optionalDependencies": {
|
|
18
|
-
"rird-
|
|
19
|
-
"rird-
|
|
20
|
-
"rird-
|
|
21
|
-
"rird-
|
|
22
|
-
"rird-
|
|
23
|
-
"rird-linux-
|
|
24
|
-
"rird-
|
|
25
|
-
"rird-
|
|
26
|
-
"rird-
|
|
27
|
-
"rird-
|
|
28
|
-
"rird-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
}
|
|
12
|
+
"rird-linux-arm64": "2.3.2",
|
|
13
|
+
"rird-linux-x64": "2.3.2",
|
|
14
|
+
"rird-linux-x64-baseline": "2.3.2",
|
|
15
|
+
"rird-linux-arm64-musl": "2.3.2",
|
|
16
|
+
"rird-linux-x64-musl": "2.3.2",
|
|
17
|
+
"rird-linux-x64-baseline-musl": "2.3.2",
|
|
18
|
+
"rird-darwin-arm64": "2.3.2",
|
|
19
|
+
"rird-darwin-x64": "2.3.2",
|
|
20
|
+
"rird-darwin-x64-baseline": "2.3.2",
|
|
21
|
+
"rird-windows-x64": "2.3.2",
|
|
22
|
+
"rird-windows-x64-baseline": "2.3.2"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/postinstall.mjs
CHANGED
|
@@ -350,7 +350,8 @@ function createConfig(pythonCmd) {
|
|
|
350
350
|
|
|
351
351
|
const config = {
|
|
352
352
|
model: "rird/qwen3:8b",
|
|
353
|
-
|
|
353
|
+
// Use the Actor model for tool-call continuation steps (fast + tool-call compatible).
|
|
354
|
+
small_model: "rird/qwen3:8b",
|
|
354
355
|
visionModel: "rird/qwen2.5-vl-7b",
|
|
355
356
|
agent: {
|
|
356
357
|
plan: { model: "rird/deepseek-reasoner" },
|
|
@@ -367,6 +368,7 @@ function createConfig(pythonCmd) {
|
|
|
367
368
|
reasoning: true,
|
|
368
369
|
temperature: true,
|
|
369
370
|
tool_call: true,
|
|
371
|
+
interleaved: { field: "reasoning_content" },
|
|
370
372
|
limit: { context: 32768, output: 8192 },
|
|
371
373
|
options: {}
|
|
372
374
|
},
|
|
@@ -388,7 +390,7 @@ function createConfig(pythonCmd) {
|
|
|
388
390
|
attachment: true,
|
|
389
391
|
reasoning: false,
|
|
390
392
|
temperature: true,
|
|
391
|
-
tool_call:
|
|
393
|
+
tool_call: false,
|
|
392
394
|
limit: { context: 32768, output: 8192 },
|
|
393
395
|
modalities: { input: ["text", "image"], output: ["text"] },
|
|
394
396
|
options: {}
|