arisa 2.0.3 → 2.0.5

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/README.md CHANGED
@@ -10,15 +10,19 @@ Arisa is intentionally dynamic: the project grows as the user builds a relations
10
10
 
11
11
  Arisa can execute actions with operational control over the system where it runs. Before deploying it, make sure you understand and accept the associated security risks. It is strongly recommended to run Arisa in an isolated environment (for example, a Docker container or a dedicated VPS) that does not store sensitive information or critical assets.
12
12
 
13
- ## Commands
14
-
15
- Requires [Bun](https://bun.sh).
16
- For Bun global installs, use your user environment.
13
+ ## Requirements and Installation
17
14
 
18
15
  ```bash
19
- bun add -g arisa # Global install from package registry (recommended)
16
+ curl -fsSL https://bun.sh/install | bash # Install Bun https://bun.sh
17
+
18
+ bun add -g @anthropic-ai/claude-code # Install Claude CLI (both or one is required)
19
+ bun add -g @openai/codex # Install Codex CLI (both or one is required)
20
+
21
+ bun add -g arisa # Install Arisa CLI
20
22
  ```
21
23
 
24
+ ## Commands
25
+
22
26
  ```bash
23
27
  arisa # Foreground daemon mode (Ctrl+C to stop)
24
28
  arisa start # Start as service (enables autostart with systemd --user)
package/bin/arisa.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
 
3
3
  const { spawn, spawnSync } = require("node:child_process");
4
4
  const {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arisa",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "Arisa - dynamic agent runtime with daemon/core architecture that evolves through user interaction",
5
5
  "preferGlobal": true,
6
6
  "bin": {
@@ -20,7 +20,7 @@
20
20
  "bun": ">=1.0.0"
21
21
  },
22
22
  "scripts": {
23
- "arisa": "node ./bin/arisa.js",
23
+ "arisa": "bun ./bin/arisa.js",
24
24
  "daemon": "bun src/daemon/index.ts",
25
25
  "dev": "bun --watch src/core/index.ts",
26
26
  "start": "bun src/daemon/index.ts",
package/src/core/index.ts CHANGED
@@ -178,9 +178,7 @@ ${messageText}`;
178
178
  if (msg.command === "/claude") {
179
179
  const deps = checkDeps();
180
180
  if (!deps.claude) {
181
- const hint = deps.os === "macOS"
182
- ? "<code>brew install claude-code</code> or <code>bun add -g @anthropic-ai/claude-code</code>"
183
- : "<code>bun add -g @anthropic-ai/claude-code</code>";
181
+ const hint = "<code>bun add -g @anthropic-ai/claude-code</code>";
184
182
  return Response.json({ text: `Claude CLI is not installed.\n${hint}` } as CoreResponse);
185
183
  }
186
184
  backendState.set(msg.chatId, "claude");
@@ -11,6 +11,7 @@
11
11
 
12
12
  import { config } from "../shared/config";
13
13
  import { createLogger } from "../shared/logger";
14
+ import { buildBunWrappedAgentCliCommand, resolveAgentCliPath } from "../shared/ai-cli";
14
15
 
15
16
  const log = createLogger("core");
16
17
 
@@ -51,9 +52,15 @@ Rules:
51
52
 
52
53
  function buildCmd(cli: "claude" | "codex", prompt: string): string[] {
53
54
  if (cli === "claude") {
54
- return ["claude", "--dangerously-skip-permissions", "--model", "haiku", "-p", prompt];
55
+ return buildBunWrappedAgentCliCommand(
56
+ "claude",
57
+ ["--dangerously-skip-permissions", "--model", "haiku", "-p", prompt],
58
+ );
55
59
  }
56
- return ["codex", "exec", "--dangerously-bypass-approvals-and-sandbox", "-C", config.projectDir, prompt];
60
+ return buildBunWrappedAgentCliCommand(
61
+ "codex",
62
+ ["exec", "--dangerously-bypass-approvals-and-sandbox", "-C", config.projectDir, prompt],
63
+ );
57
64
  }
58
65
 
59
66
  // Track which CLI actually works (not just Bun.which, which can find broken shims)
@@ -75,8 +82,8 @@ async function trySpawn(prompt: string, cli: "claude" | "codex"): Promise<string
75
82
  function getCliOrder(): Array<"claude" | "codex"> {
76
83
  if (verifiedCli) return [verifiedCli];
77
84
  const order: Array<"claude" | "codex"> = [];
78
- if (Bun.which("claude") !== null) order.push("claude");
79
- if (Bun.which("codex") !== null) order.push("codex");
85
+ if (resolveAgentCliPath("claude") !== null) order.push("claude");
86
+ if (resolveAgentCliPath("codex") !== null) order.push("codex");
80
87
  return order;
81
88
  }
82
89
 
@@ -13,6 +13,7 @@
13
13
  import { config } from "../shared/config";
14
14
  import { createLogger } from "../shared/logger";
15
15
  import { getOnboardedUsers, addOnboarded as dbAddOnboarded, isOnboarded as dbIsOnboarded } from "../shared/db";
16
+ import { isAgentCliInstalled } from "../shared/ai-cli";
16
17
 
17
18
  const log = createLogger("core");
18
19
 
@@ -46,8 +47,8 @@ export function checkDeps(): DepsStatus {
46
47
  : "Linux";
47
48
 
48
49
  return {
49
- claude: Bun.which("claude") !== null,
50
- codex: Bun.which("codex") !== null,
50
+ claude: isAgentCliInstalled("claude"),
51
+ codex: isAgentCliInstalled("codex"),
51
52
  openaiKey: !!config.openaiApiKey,
52
53
  os,
53
54
  };
@@ -16,6 +16,7 @@ import { getRecentHistory } from "./history";
16
16
  import { shouldContinue } from "./context";
17
17
  import { config } from "../shared/config";
18
18
  import { createLogger } from "../shared/logger";
19
+ import { buildBunWrappedAgentCliCommand } from "../shared/ai-cli";
19
20
  import { existsSync, mkdirSync, readFileSync, appendFileSync } from "fs";
20
21
  import { join } from "path";
21
22
 
@@ -25,10 +26,7 @@ const PROMPT_PREVIEW_MAX = 220;
25
26
  export const CLAUDE_RATE_LIMIT_MESSAGE = "Claude is out of credits right now. Please try again in a few minutes.";
26
27
  export const CODEX_AUTH_REQUIRED_MESSAGE = [
27
28
  "Codex login is required.",
28
- "Check the Arisa daemon logs now and complete the device-auth steps shown there.",
29
- "If the login flow is not running, execute:",
30
- "<code>codex login --device-auth</code>",
31
- "Then send your message again.",
29
+ "Check the Arisa daemon logs now and complete the device-auth steps shown there."
32
30
  ].join("\n");
33
31
 
34
32
  function logActivity(backend: string, model: string | null, durationMs: number, status: string) {
@@ -147,7 +145,7 @@ async function runClaude(message: string, chatId: string): Promise<string> {
147
145
  log.info(`Claude spawn | cmd: claude --dangerously-skip-permissions --model ${model.model} -p <prompt>`);
148
146
  log.debug(`Claude prompt >>>>\n${prompt}\n<<<<`);
149
147
 
150
- const proc = Bun.spawn(["claude", ...args], {
148
+ const proc = Bun.spawn(buildBunWrappedAgentCliCommand("claude", args), {
151
149
  cwd: config.projectDir,
152
150
  stdout: "pipe",
153
151
  stderr: "pipe",
@@ -218,7 +216,7 @@ export async function processWithCodex(message: string): Promise<string> {
218
216
  );
219
217
  log.debug(`Codex prompt >>>>\n${message}\n<<<<`);
220
218
 
221
- const proc = Bun.spawn(["codex", ...args], {
219
+ const proc = Bun.spawn(buildBunWrappedAgentCliCommand("codex", args), {
222
220
  cwd: config.projectDir,
223
221
  stdout: "pipe",
224
222
  stderr: "pipe",
@@ -11,10 +11,15 @@
11
11
 
12
12
  import { config } from "../shared/config";
13
13
  import { createLogger } from "../shared/logger";
14
+ import {
15
+ buildBunWrappedAgentCliCommand,
16
+ resolveAgentCliPath,
17
+ type AgentCliName,
18
+ } from "../shared/ai-cli";
14
19
 
15
20
  const log = createLogger("daemon");
16
21
 
17
- export type AgentCli = "claude" | "codex";
22
+ export type AgentCli = AgentCliName;
18
23
 
19
24
  export interface CliExecutionResult {
20
25
  cli: AgentCli;
@@ -32,8 +37,8 @@ export interface CliFallbackOutcome {
32
37
 
33
38
  export function getAvailableAgentCli(): AgentCli[] {
34
39
  const order: AgentCli[] = [];
35
- if (Bun.which("claude") !== null) order.push("claude");
36
- if (Bun.which("codex") !== null) order.push("codex");
40
+ if (resolveAgentCliPath("claude") !== null) order.push("claude");
41
+ if (resolveAgentCliPath("codex") !== null) order.push("codex");
37
42
  return order;
38
43
  }
39
44
 
@@ -43,9 +48,15 @@ export function getAgentCliLabel(cli: AgentCli): string {
43
48
 
44
49
  function buildCommand(cli: AgentCli, prompt: string): string[] {
45
50
  if (cli === "claude") {
46
- return ["claude", "--dangerously-skip-permissions", "--model", "sonnet", "-p", prompt];
51
+ return buildBunWrappedAgentCliCommand(
52
+ "claude",
53
+ ["--dangerously-skip-permissions", "--model", "sonnet", "-p", prompt],
54
+ );
47
55
  }
48
- return ["codex", "exec", "--dangerously-bypass-approvals-and-sandbox", "-C", config.projectDir, prompt];
56
+ return buildBunWrappedAgentCliCommand(
57
+ "codex",
58
+ ["exec", "--dangerously-bypass-approvals-and-sandbox", "-C", config.projectDir, prompt],
59
+ );
49
60
  }
50
61
 
51
62
  async function runSingleCli(
@@ -116,4 +127,3 @@ function summarizeError(raw: string): string {
116
127
  if (!clean) return "no details";
117
128
  return clean.length > 200 ? `${clean.slice(0, 200)}...` : clean;
118
129
  }
119
-
@@ -3,18 +3,19 @@
3
3
  * @role Trigger Codex device auth flow from Daemon when auth errors are detected.
4
4
  * @responsibilities
5
5
  * - Detect codex auth-required signals in Core responses
6
- * - Run `codex login --device-auth` in background from daemon process
6
+ * - Run `codex login --device-auth` (wrapped via Bun) in background from daemon process
7
7
  * - Avoid duplicate runs with in-progress lock + cooldown
8
8
  * @effects Spawns codex CLI process, writes to daemon logs/terminal
9
9
  */
10
10
 
11
11
  import { config } from "../shared/config";
12
12
  import { createLogger } from "../shared/logger";
13
+ import { buildBunWrappedAgentCliCommand } from "../shared/ai-cli";
13
14
 
14
15
  const log = createLogger("daemon");
15
16
 
16
17
  const AUTH_HINT_PATTERNS = [
17
- /codex login --device-auth/i,
18
+ /codex.*login --device-auth/i,
18
19
  /codex is not authenticated on this server/i,
19
20
  /missing bearer authentication in header/i,
20
21
  ];
@@ -59,12 +60,12 @@ export function maybeStartCodexDeviceAuth(rawCoreText: string, chatId?: string):
59
60
  }
60
61
 
61
62
  async function runCodexDeviceAuth(): Promise<void> {
62
- log.warn("Codex auth required. Starting `codex login --device-auth` now.");
63
+ log.warn("Codex auth required. Starting `bun --bun <path-to-codex> login --device-auth` now.");
63
64
  log.warn("Complete device auth using the URL/code printed below in this Arisa terminal.");
64
65
 
65
66
  let proc: ReturnType<typeof Bun.spawn>;
66
67
  try {
67
- proc = Bun.spawn(["codex", "login", "--device-auth"], {
68
+ proc = Bun.spawn(buildBunWrappedAgentCliCommand("codex", ["login", "--device-auth"]), {
68
69
  cwd: config.projectDir,
69
70
  stdin: "inherit",
70
71
  stdout: "inherit",
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @module shared/ai-cli
3
+ * @role Resolve agent CLI binaries and execute them via Bun runtime.
4
+ */
5
+
6
+ export type AgentCliName = "claude" | "codex";
7
+
8
+ export function resolveAgentCliPath(cli: AgentCliName): string | null {
9
+ return Bun.which(cli);
10
+ }
11
+
12
+ export function isAgentCliInstalled(cli: AgentCliName): boolean {
13
+ return resolveAgentCliPath(cli) !== null;
14
+ }
15
+
16
+ export function buildBunWrappedAgentCliCommand(cli: AgentCliName, args: string[]): string[] {
17
+ const cliPath = resolveAgentCliPath(cli);
18
+ if (!cliPath) {
19
+ throw new Error(`${cli} CLI not found in PATH`);
20
+ }
21
+ return ["bun", "--bun", cliPath, ...args];
22
+ }