rird-ai 2.3.7 → 2.3.9
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/package.json +23 -16
- package/postinstall.mjs +13 -12
- package/bin/pty-wrapper.js +0 -285
- package/bin/rird-perf.js +0 -37
package/package.json
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rird-ai",
|
|
3
|
-
"
|
|
3
|
+
"version": "2.3.9",
|
|
4
|
+
"description": "RIRD - Computer Use Agent for work automation",
|
|
4
5
|
"bin": {
|
|
5
6
|
"rird": "./bin/rird"
|
|
6
7
|
},
|
|
7
8
|
"scripts": {
|
|
8
|
-
"postinstall": "
|
|
9
|
+
"postinstall": "node ./postinstall.mjs"
|
|
9
10
|
},
|
|
10
|
-
"version": "2.3.7",
|
|
11
11
|
"optionalDependencies": {
|
|
12
|
-
"rird-
|
|
13
|
-
"rird-
|
|
14
|
-
"rird-
|
|
15
|
-
"rird-linux-arm64
|
|
16
|
-
"rird-linux-
|
|
17
|
-
"rird-linux-x64
|
|
18
|
-
"rird-
|
|
19
|
-
"rird-
|
|
20
|
-
"rird-
|
|
21
|
-
"rird-windows-x64": "2.3.
|
|
22
|
-
"rird-windows-x64-baseline": "2.3.
|
|
23
|
-
}
|
|
24
|
-
|
|
12
|
+
"rird-darwin-arm64": "2.3.9",
|
|
13
|
+
"rird-darwin-x64": "2.3.9",
|
|
14
|
+
"rird-darwin-x64-baseline": "2.3.9",
|
|
15
|
+
"rird-linux-arm64": "2.3.9",
|
|
16
|
+
"rird-linux-arm64-musl": "2.3.9",
|
|
17
|
+
"rird-linux-x64": "2.3.9",
|
|
18
|
+
"rird-linux-x64-baseline": "2.3.9",
|
|
19
|
+
"rird-linux-x64-baseline-musl": "2.3.9",
|
|
20
|
+
"rird-linux-x64-musl": "2.3.9",
|
|
21
|
+
"rird-windows-x64": "2.3.9",
|
|
22
|
+
"rird-windows-x64-baseline": "2.3.9"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/rird-ai/rird"
|
|
27
|
+
},
|
|
28
|
+
"keywords": ["ai", "automation", "browser", "agent", "computer-use"],
|
|
29
|
+
"author": "RIRD",
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
}
|
package/postinstall.mjs
CHANGED
|
@@ -19,7 +19,7 @@ const RIRD_HOME = process.env.RIRD_HOME || path.join(os.homedir(), ".rird")
|
|
|
19
19
|
const BIN_DIR = path.join(RIRD_HOME, "bin")
|
|
20
20
|
const ENGINE_DIR = path.join(RIRD_HOME, "engine")
|
|
21
21
|
const VENV_DIR = path.join(RIRD_HOME, "venv")
|
|
22
|
-
const MCP_DIR = path.join(ENGINE_DIR, "
|
|
22
|
+
const MCP_DIR = path.join(ENGINE_DIR, "stealth-browser-mcp")
|
|
23
23
|
|
|
24
24
|
// Installation mode: A = Browser + Files, B = Full Computer Use
|
|
25
25
|
const INSTALL_MODE = process.env.RIRD_MODE || "A"
|
|
@@ -115,14 +115,14 @@ function installPythonDeps(pipCmd) {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
// Clone browser MCP (
|
|
119
|
-
function
|
|
118
|
+
// Clone browser MCP (Stealth)
|
|
119
|
+
function cloneStealthBrowserMcp() {
|
|
120
120
|
if (fs.existsSync(MCP_DIR)) {
|
|
121
|
-
log("
|
|
121
|
+
log("Browser automation already installed")
|
|
122
122
|
return true
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
log("Installing
|
|
125
|
+
log("Installing browser automation...")
|
|
126
126
|
|
|
127
127
|
// Try git clone - use spawnSync for better cross-platform path handling
|
|
128
128
|
if (execSafe("git --version")) {
|
|
@@ -133,7 +133,7 @@ function cloneChromeBrowserMcp() {
|
|
|
133
133
|
shell: isWindows
|
|
134
134
|
})
|
|
135
135
|
if (result.status === 0 || fs.existsSync(MCP_DIR)) {
|
|
136
|
-
log("
|
|
136
|
+
log("Browser automation installed")
|
|
137
137
|
return true
|
|
138
138
|
}
|
|
139
139
|
} catch (e) {
|
|
@@ -146,8 +146,8 @@ function cloneChromeBrowserMcp() {
|
|
|
146
146
|
// Create placeholder for now
|
|
147
147
|
fs.mkdirSync(path.join(MCP_DIR, "src"), { recursive: true })
|
|
148
148
|
fs.writeFileSync(path.join(MCP_DIR, "src", "server.py"), `#!/usr/bin/env python3
|
|
149
|
-
# Placeholder - install the
|
|
150
|
-
print("
|
|
149
|
+
# Placeholder - install the browser automation repo into ~/.rird/engine/stealth-browser-mcp
|
|
150
|
+
print("Browser automation not installed. Install Git and re-run setup.")
|
|
151
151
|
`)
|
|
152
152
|
return false
|
|
153
153
|
}
|
|
@@ -281,7 +281,7 @@ function createConfig(pythonCmd) {
|
|
|
281
281
|
log(`Creating config (Mode ${INSTALL_MODE})...`)
|
|
282
282
|
|
|
283
283
|
const mcpConfig = {
|
|
284
|
-
"
|
|
284
|
+
"stealth-browser": {
|
|
285
285
|
type: "local",
|
|
286
286
|
command: [pythonCmd, path.join(MCP_DIR, "src", "server.py"), "--minimal"],
|
|
287
287
|
environment: {
|
|
@@ -289,7 +289,8 @@ function createConfig(pythonCmd) {
|
|
|
289
289
|
PYTHONPATH: path.join(MCP_DIR, "src"),
|
|
290
290
|
RIRD_PROXIES: "${ROYAL_PROXY}"
|
|
291
291
|
},
|
|
292
|
-
timeout: 30000
|
|
292
|
+
timeout: 30000,
|
|
293
|
+
enabled: true
|
|
293
294
|
}
|
|
294
295
|
}
|
|
295
296
|
|
|
@@ -541,8 +542,8 @@ async function main() {
|
|
|
541
542
|
}
|
|
542
543
|
}
|
|
543
544
|
|
|
544
|
-
// Install browser MCP (
|
|
545
|
-
|
|
545
|
+
// Install browser MCP (Stealth)
|
|
546
|
+
cloneStealthBrowserMcp()
|
|
546
547
|
|
|
547
548
|
// Create license validator
|
|
548
549
|
createLicenseValidator()
|
package/bin/pty-wrapper.js
DELETED
|
@@ -1,285 +0,0 @@
|
|
|
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-perf.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
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");
|