arisa 2.3.52 → 2.3.53
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/arisa.js +39 -0
- package/package.json +1 -1
- package/src/daemon/auto-install.ts +9 -2
- package/src/daemon/setup.ts +11 -2
- package/src/shared/ai-cli.ts +4 -1
package/bin/arisa.js
CHANGED
|
@@ -499,6 +499,45 @@ if (isRoot() && arisaUserExists()) {
|
|
|
499
499
|
process.umask(0o000);
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
+
// Pre-flight: install missing CLIs while still root (before su arisa).
|
|
503
|
+
// arisa user has read+execute but NOT write access to root's bun dir.
|
|
504
|
+
function preflightInstallClis() {
|
|
505
|
+
const clis = {
|
|
506
|
+
claude: "@anthropic-ai/claude-code",
|
|
507
|
+
codex: "@openai/codex",
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
const bunBinDir = join(ROOT_BUN_INSTALL, "bin");
|
|
511
|
+
const missing = [];
|
|
512
|
+
|
|
513
|
+
for (const [name, pkg] of Object.entries(clis)) {
|
|
514
|
+
if (existsSync(join(bunBinDir, name))) continue;
|
|
515
|
+
missing.push({ name, pkg });
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (missing.length === 0) return;
|
|
519
|
+
|
|
520
|
+
process.stdout.write(`[preflight] Installing missing CLIs as root: ${missing.map((m) => m.name).join(", ")}\n`);
|
|
521
|
+
for (const { name, pkg } of missing) {
|
|
522
|
+
const result = spawnSync("bun", ["add", "-g", pkg], {
|
|
523
|
+
stdio: "inherit",
|
|
524
|
+
timeout: 180000,
|
|
525
|
+
});
|
|
526
|
+
if (result.status === 0) {
|
|
527
|
+
process.stdout.write(`[preflight] ✓ ${name} installed\n`);
|
|
528
|
+
} else {
|
|
529
|
+
process.stdout.write(`[preflight] ✗ ${name} install failed\n`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// Re-grant read+execute access after installing new binaries
|
|
534
|
+
grantBunAccess();
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
if (isRoot() && arisaUserExists()) {
|
|
538
|
+
preflightInstallClis();
|
|
539
|
+
}
|
|
540
|
+
|
|
502
541
|
// Then fall through to normal daemon startup
|
|
503
542
|
|
|
504
543
|
// ── Non-root flow (unchanged) ───────────────────────────────────────
|
package/package.json
CHANGED
|
@@ -33,13 +33,20 @@ async function installCli(cli: AgentCliName): Promise<boolean> {
|
|
|
33
33
|
log.info(`Auto-install: installing ${cli} (${pkg})...`);
|
|
34
34
|
|
|
35
35
|
try {
|
|
36
|
-
// Install into root's bun (arisa has read+execute access)
|
|
37
36
|
const cmd = ["bun", "add", "-g", pkg];
|
|
37
|
+
const env = { ...process.env };
|
|
38
|
+
|
|
39
|
+
// When not root, BUN_INSTALL may point to root's dir (read-only for us).
|
|
40
|
+
// Install to user's own bun dir instead.
|
|
41
|
+
if (!isRunningAsRoot()) {
|
|
42
|
+
const home = process.env.HOME || "/home/arisa";
|
|
43
|
+
env.BUN_INSTALL = `${home}/.bun`;
|
|
44
|
+
}
|
|
38
45
|
|
|
39
46
|
const proc = Bun.spawn(cmd, {
|
|
40
47
|
stdout: "pipe",
|
|
41
48
|
stderr: "pipe",
|
|
42
|
-
env
|
|
49
|
+
env,
|
|
43
50
|
});
|
|
44
51
|
|
|
45
52
|
const timeout = setTimeout(() => proc.kill(), INSTALL_TIMEOUT);
|
package/src/daemon/setup.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
|
|
|
15
15
|
import { dirname, join } from "path";
|
|
16
16
|
import { dataDir } from "../shared/paths";
|
|
17
17
|
import { secrets, setSecret } from "../shared/secrets";
|
|
18
|
-
import { isAgentCliInstalled, buildBunWrappedAgentCliCommand, type AgentCliName } from "../shared/ai-cli";
|
|
18
|
+
import { isAgentCliInstalled, isRunningAsRoot, buildBunWrappedAgentCliCommand, type AgentCliName } from "../shared/ai-cli";
|
|
19
19
|
|
|
20
20
|
const ENV_PATH = join(dataDir, ".env");
|
|
21
21
|
|
|
@@ -249,11 +249,20 @@ async function isCliAuthenticated(cli: AgentCliName): Promise<boolean> {
|
|
|
249
249
|
|
|
250
250
|
async function installCli(cli: AgentCliName): Promise<boolean> {
|
|
251
251
|
try {
|
|
252
|
-
// Install into root's bun (arisa has read+execute access)
|
|
253
252
|
const cmd = ["bun", "add", "-g", CLI_PACKAGES[cli]];
|
|
253
|
+
const env = { ...process.env };
|
|
254
|
+
|
|
255
|
+
// When not root, BUN_INSTALL may point to root's dir (read-only for us).
|
|
256
|
+
// Install to user's own bun dir instead.
|
|
257
|
+
if (!isRunningAsRoot()) {
|
|
258
|
+
const home = process.env.HOME || "/home/arisa";
|
|
259
|
+
env.BUN_INSTALL = `${home}/.bun`;
|
|
260
|
+
}
|
|
261
|
+
|
|
254
262
|
const proc = Bun.spawn(cmd, {
|
|
255
263
|
stdout: "inherit",
|
|
256
264
|
stderr: "inherit",
|
|
265
|
+
env,
|
|
257
266
|
});
|
|
258
267
|
const timeout = setTimeout(() => proc.kill(), 180_000);
|
|
259
268
|
const exitCode = await proc.exited;
|
package/src/shared/ai-cli.ts
CHANGED
|
@@ -41,11 +41,13 @@ function cliOverrideEnvVar(cli: AgentCliName): string | undefined {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function candidatePaths(cli: AgentCliName): string[] {
|
|
44
|
+
const arisaBunBin = `${ARISA_HOME}/.bun/bin`;
|
|
45
|
+
|
|
44
46
|
if (isRunningAsRoot()) {
|
|
45
|
-
// When root, CLIs are installed under arisa user's bun
|
|
46
47
|
return unique([
|
|
47
48
|
cliOverrideEnvVar(cli),
|
|
48
49
|
join(ROOT_BUN_BIN, cli),
|
|
50
|
+
join(arisaBunBin, cli),
|
|
49
51
|
]);
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -61,6 +63,7 @@ function candidatePaths(cli: AgentCliName): string[] {
|
|
|
61
63
|
return unique([
|
|
62
64
|
cliOverrideEnvVar(cli),
|
|
63
65
|
bunInstall ? join(bunInstall, "bin", cli) : null,
|
|
66
|
+
join(arisaBunBin, cli),
|
|
64
67
|
join(bunDir, cli),
|
|
65
68
|
fromPath,
|
|
66
69
|
...fromEnvPath,
|