arisa 2.3.25 → 2.3.27
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 +30 -17
- package/package.json +2 -2
- package/src/daemon/auto-install.ts +2 -5
- package/src/daemon/index.ts +13 -15
- package/src/daemon/lifecycle.ts +2 -1
- package/src/daemon/setup.ts +2 -3
- package/src/shared/ai-cli.ts +6 -4
package/bin/arisa.js
CHANGED
|
@@ -416,14 +416,15 @@ function arisaUserExists() {
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
function isArisaUserProvisioned() {
|
|
419
|
-
return arisaUserExists()
|
|
419
|
+
return arisaUserExists();
|
|
420
420
|
}
|
|
421
421
|
|
|
422
422
|
function step(ok, msg) {
|
|
423
423
|
process.stdout.write(` ${ok ? "\u2713" : "\u2717"} ${msg}\n`);
|
|
424
424
|
}
|
|
425
425
|
|
|
426
|
-
const
|
|
426
|
+
const ROOT_BUN_INSTALL = process.env.BUN_INSTALL || join(homeDir, ".bun");
|
|
427
|
+
const ARISA_BUN_ENV = `export BUN_INSTALL=${ROOT_BUN_INSTALL} && export PATH=${ROOT_BUN_INSTALL}/bin:$PATH`;
|
|
427
428
|
|
|
428
429
|
function provisionArisaUser() {
|
|
429
430
|
process.stdout.write("Creating user 'arisa' for Claude/Codex CLI execution...\n");
|
|
@@ -445,21 +446,20 @@ function provisionArisaUser() {
|
|
|
445
446
|
step(false, `Sudo setup skipped: ${e.message || e}`);
|
|
446
447
|
}
|
|
447
448
|
|
|
448
|
-
// 3.
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
stdio: "inherit",
|
|
452
|
-
timeout: 180_000,
|
|
453
|
-
});
|
|
454
|
-
if (bunInstall.status !== 0) {
|
|
455
|
-
step(false, "Failed to install bun");
|
|
456
|
-
process.exit(1);
|
|
457
|
-
}
|
|
458
|
-
step(true, "Bun installed for arisa");
|
|
449
|
+
// 3. Give arisa access to root's bun (no separate install needed)
|
|
450
|
+
grantBunAccess();
|
|
451
|
+
step(true, "Access to root's bun granted");
|
|
459
452
|
|
|
460
453
|
process.stdout.write(" Done. Core will run as arisa; Claude/Codex calls are direct.\n\n");
|
|
461
454
|
}
|
|
462
455
|
|
|
456
|
+
function grantBunAccess() {
|
|
457
|
+
// Allow arisa to traverse into /root (execute only, not read)
|
|
458
|
+
spawnSync("chmod", ["o+x", homeDir], { stdio: "ignore" });
|
|
459
|
+
// Allow arisa to read+execute root's bun and globally installed CLIs
|
|
460
|
+
spawnSync("chmod", ["-R", "o+rX", ROOT_BUN_INSTALL], { stdio: "ignore" });
|
|
461
|
+
}
|
|
462
|
+
|
|
463
463
|
// Provision arisa user if running as root and not yet done
|
|
464
464
|
if (isRoot() && !isArisaUserProvisioned()) {
|
|
465
465
|
provisionArisaUser();
|
|
@@ -468,6 +468,8 @@ if (isRoot() && !isArisaUserProvisioned()) {
|
|
|
468
468
|
// When root + arisa exists: route all runtime data through arisa's home
|
|
469
469
|
// so Core (running as arisa) and Daemon (root) share the same data dir.
|
|
470
470
|
if (isRoot() && arisaUserExists()) {
|
|
471
|
+
// Ensure arisa can access root's bun on every startup
|
|
472
|
+
grantBunAccess();
|
|
471
473
|
const arisaDataDir = "/home/arisa/.arisa";
|
|
472
474
|
const rootDataDir = join("/root", ".arisa");
|
|
473
475
|
|
|
@@ -533,12 +535,23 @@ switch (command) {
|
|
|
533
535
|
if (isDefaultInvocation) {
|
|
534
536
|
printForegroundNotice();
|
|
535
537
|
}
|
|
536
|
-
//
|
|
537
|
-
|
|
538
|
-
|
|
538
|
+
// Single bun process: daemon + core in-process with --watch.
|
|
539
|
+
// When root, run as arisa (Claude CLI refuses root). su without "-"
|
|
540
|
+
// preserves parent env (ARISA_DATA_DIR, tokens, API keys).
|
|
541
|
+
let child;
|
|
542
|
+
if (isRoot() && arisaUserExists()) {
|
|
543
|
+
const bunEnv = `export HOME=/home/arisa && ${ARISA_BUN_ENV}`;
|
|
544
|
+
const cmd = `${bunEnv} && cd ${pkgRoot} && exec bun --watch ${daemonEntry}`;
|
|
545
|
+
child = spawnSync("su", ["arisa", "-s", "/bin/bash", "-c", cmd], {
|
|
546
|
+
stdio: "inherit",
|
|
547
|
+
env: process.env,
|
|
548
|
+
});
|
|
549
|
+
} else {
|
|
550
|
+
child = runWithBun(["--watch", daemonEntry, ...rest]);
|
|
551
|
+
}
|
|
552
|
+
process.exit(child.status === null ? 1 : child.status);
|
|
539
553
|
}
|
|
540
554
|
case "core": {
|
|
541
|
-
// Run core in-process
|
|
542
555
|
await import(coreEntry);
|
|
543
556
|
break;
|
|
544
557
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arisa",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.27",
|
|
4
4
|
"description": "Arisa - dynamic agent runtime with daemon/core architecture that evolves through user interaction",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tinyclaw",
|
|
@@ -52,4 +52,4 @@
|
|
|
52
52
|
"@types/bun": "latest",
|
|
53
53
|
"@types/crypto-js": "^4.2.2"
|
|
54
54
|
}
|
|
55
|
-
}
|
|
55
|
+
}
|
|
@@ -19,7 +19,6 @@ const CLI_PACKAGES: Record<AgentCliName, string> = {
|
|
|
19
19
|
codex: "@openai/codex",
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
const ARISA_BUN_ENV = 'export BUN_INSTALL=/home/arisa/.bun && export PATH=/home/arisa/.bun/bin:$PATH';
|
|
23
22
|
const INSTALL_TIMEOUT = 120_000; // 2min
|
|
24
23
|
|
|
25
24
|
type NotifyFn = (text: string) => Promise<void>;
|
|
@@ -34,10 +33,8 @@ async function installCli(cli: AgentCliName): Promise<boolean> {
|
|
|
34
33
|
log.info(`Auto-install: installing ${cli} (${pkg})...`);
|
|
35
34
|
|
|
36
35
|
try {
|
|
37
|
-
//
|
|
38
|
-
const cmd =
|
|
39
|
-
? ["su", "-", "arisa", "-c", `${ARISA_BUN_ENV} && bun add -g ${pkg}`]
|
|
40
|
-
: ["bun", "add", "-g", pkg];
|
|
36
|
+
// Install into root's bun (arisa has read+execute access)
|
|
37
|
+
const cmd = ["bun", "add", "-g", pkg];
|
|
41
38
|
|
|
42
39
|
const proc = Bun.spawn(cmd, {
|
|
43
40
|
stdout: "pipe",
|
package/src/daemon/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module daemon/index
|
|
3
|
-
* @role
|
|
3
|
+
* @role Single-process entry point: Daemon + Core in one bun runtime.
|
|
4
4
|
* @responsibilities
|
|
5
5
|
* - Run interactive setup if config is missing
|
|
6
6
|
* - Start the Telegram channel adapter
|
|
7
|
-
* -
|
|
8
|
-
* - Run HTTP server
|
|
7
|
+
* - Load Core in-process (HTTP server, Claude CLI, scheduler)
|
|
8
|
+
* - Run HTTP server for Core → Daemon pushes (scheduler)
|
|
9
9
|
* - Route incoming messages to Core via bridge
|
|
10
10
|
* - Route Core responses back to channel
|
|
11
|
-
* @dependencies All daemon/* modules, shared/*
|
|
12
|
-
* @effects Network (Telegram, HTTP servers), spawns
|
|
11
|
+
* @dependencies All daemon/* modules, core/*, shared/*
|
|
12
|
+
* @effects Network (Telegram, HTTP servers), spawns Claude CLI
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
// Log version at startup
|
|
@@ -32,8 +32,7 @@ const { createLogger } = await import("../shared/logger");
|
|
|
32
32
|
const { serveWithRetry, claimProcess, releaseProcess, cleanupSocket } = await import("../shared/ports");
|
|
33
33
|
const { TelegramChannel } = await import("./channels/telegram");
|
|
34
34
|
const { sendToCore } = await import("./bridge");
|
|
35
|
-
|
|
36
|
-
const { setAutoFixNotify } = await import("./autofix");
|
|
35
|
+
// lifecycle/autofix removed — Core runs in-process, --watch handles restarts
|
|
37
36
|
const { maybeStartCodexDeviceAuth, setCodexLoginNotify } = await import("./codex-login");
|
|
38
37
|
const { maybeStartClaudeSetupToken, maybeFeedClaudeCode, setClaudeLoginNotify, isClaudeLoginPending } = await import("./claude-login");
|
|
39
38
|
const { autoInstallMissingClis, setAutoInstallNotify, setAuthProbeCallback } = await import("./auto-install");
|
|
@@ -95,8 +94,6 @@ const sendToAllChats = async (text: string) => {
|
|
|
95
94
|
}
|
|
96
95
|
};
|
|
97
96
|
|
|
98
|
-
setLifecycleNotify(sendToAllChats);
|
|
99
|
-
setAutoFixNotify(sendToAllChats);
|
|
100
97
|
setAutoInstallNotify(sendToAllChats);
|
|
101
98
|
setAuthProbeCallback((cli, errorText) => {
|
|
102
99
|
if (cli === "claude") {
|
|
@@ -256,11 +253,13 @@ const pushServer = await serveWithRetry({
|
|
|
256
253
|
|
|
257
254
|
log.info(`Daemon push server listening on ${config.daemonSocket}`);
|
|
258
255
|
|
|
259
|
-
// ---
|
|
260
|
-
|
|
256
|
+
// --- Load Core in-process (single bun process, no child spawn) ---
|
|
257
|
+
log.info("Loading Core...");
|
|
258
|
+
await import("../core/index.ts");
|
|
259
|
+
log.info("Core loaded");
|
|
261
260
|
|
|
262
|
-
// --- Auto-install missing CLIs (
|
|
263
|
-
|
|
261
|
+
// --- Auto-install missing CLIs (delayed to avoid peak memory) ---
|
|
262
|
+
setTimeout(() => void autoInstallMissingClis(), 5000);
|
|
264
263
|
|
|
265
264
|
// --- Connect Telegram (with retry for 409 conflict from stale polling sessions) ---
|
|
266
265
|
(async function connectTelegram(maxRetries = 5) {
|
|
@@ -284,8 +283,7 @@ waitForCoreReady(30_000).then(() => void autoInstallMissingClis());
|
|
|
284
283
|
|
|
285
284
|
// --- Graceful shutdown ---
|
|
286
285
|
function shutdown() {
|
|
287
|
-
log.info("Shutting down
|
|
288
|
-
stopCore();
|
|
286
|
+
log.info("Shutting down...");
|
|
289
287
|
cleanupSocket(config.daemonSocket);
|
|
290
288
|
cleanupSocket(config.coreSocket);
|
|
291
289
|
releaseProcess("daemon");
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -131,7 +131,8 @@ export function startCore() {
|
|
|
131
131
|
// (encryption keys, DB, PID files, etc.) before Core reads them.
|
|
132
132
|
spawnSync("chown", ["-R", "arisa:arisa", config.arisaDir], { stdio: "ignore" });
|
|
133
133
|
|
|
134
|
-
const
|
|
134
|
+
const bunInstall = process.env.BUN_INSTALL || "/root/.bun";
|
|
135
|
+
const bunEnv = `export HOME=/home/arisa && export BUN_INSTALL=${bunInstall} && export PATH=${bunInstall}/bin:$PATH`;
|
|
135
136
|
const inner = `${bunEnv} && cd ${config.projectDir} && exec bun --watch ${coreEntry}`;
|
|
136
137
|
cmd = ["su", "arisa", "-s", "/bin/bash", "-c", inner];
|
|
137
138
|
log.info(`Starting Core as arisa: bun --watch ${coreEntry}`);
|
package/src/daemon/setup.ts
CHANGED
|
@@ -230,9 +230,8 @@ async function setupClis(inq: typeof import("@inquirer/prompts") | null, vars: R
|
|
|
230
230
|
|
|
231
231
|
async function installCli(cli: AgentCliName): Promise<boolean> {
|
|
232
232
|
try {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
: ["bun", "add", "-g", CLI_PACKAGES[cli]];
|
|
233
|
+
// Install into root's bun (arisa has read+execute access)
|
|
234
|
+
const cmd = ["bun", "add", "-g", CLI_PACKAGES[cli]];
|
|
236
235
|
const proc = Bun.spawn(cmd, {
|
|
237
236
|
stdout: "inherit",
|
|
238
237
|
stderr: "inherit",
|
package/src/shared/ai-cli.ts
CHANGED
|
@@ -10,9 +10,11 @@ import { delimiter, dirname, join } from "path";
|
|
|
10
10
|
|
|
11
11
|
export type AgentCliName = "claude" | "codex";
|
|
12
12
|
|
|
13
|
-
const ARISA_USER_BUN = "/home/arisa/.bun/bin";
|
|
14
13
|
const ARISA_HOME = "/home/arisa";
|
|
15
|
-
|
|
14
|
+
// Use root's bun — arisa user has traverse+read access via chmod o+x /root, o+rX /root/.bun
|
|
15
|
+
const ROOT_BUN_INSTALL = process.env.BUN_INSTALL || "/root/.bun";
|
|
16
|
+
const ROOT_BUN_BIN = `${ROOT_BUN_INSTALL}/bin`;
|
|
17
|
+
const ARISA_BUN_ENV = `export HOME=${ARISA_HOME} && export BUN_INSTALL=${ROOT_BUN_INSTALL} && export PATH=${ROOT_BUN_BIN}:$PATH`;
|
|
16
18
|
|
|
17
19
|
export function isRunningAsRoot(): boolean {
|
|
18
20
|
return process.getuid?.() === 0;
|
|
@@ -43,7 +45,7 @@ function candidatePaths(cli: AgentCliName): string[] {
|
|
|
43
45
|
// When root, CLIs are installed under arisa user's bun
|
|
44
46
|
return unique([
|
|
45
47
|
cliOverrideEnvVar(cli),
|
|
46
|
-
join(
|
|
48
|
+
join(ROOT_BUN_BIN, cli),
|
|
47
49
|
]);
|
|
48
50
|
}
|
|
49
51
|
|
|
@@ -98,7 +100,7 @@ export function buildBunWrappedAgentCliCommand(cli: AgentCliName, args: string[]
|
|
|
98
100
|
if (isRunningAsRoot()) {
|
|
99
101
|
// Run as arisa user — Claude CLI refuses to run as root.
|
|
100
102
|
// This path is used by Daemon fallback calls; Core runs as arisa directly.
|
|
101
|
-
const cliPath = resolveAgentCliPath(cli) || join(
|
|
103
|
+
const cliPath = resolveAgentCliPath(cli) || join(ROOT_BUN_BIN, cli);
|
|
102
104
|
const inner = ["bun", "--bun", INK_SHIM, cliPath, ...args].map(shellEscape).join(" ");
|
|
103
105
|
// su without "-" preserves parent env (tokens, keys); explicit HOME/PATH for arisa
|
|
104
106
|
return ["su", "arisa", "-s", "/bin/bash", "-c", `${ARISA_BUN_ENV} && ${buildEnvExports()}${inner}`];
|