arisa 2.3.28 → 2.3.30

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arisa",
3
- "version": "2.3.28",
3
+ "version": "2.3.30",
4
4
  "description": "Arisa - dynamic agent runtime with daemon/core architecture that evolves through user interaction",
5
5
  "keywords": [
6
6
  "tinyclaw",
@@ -110,7 +110,7 @@ export async function runWithCliFallback(prompt: string, timeoutMs: number): Pro
110
110
  }
111
111
 
112
112
  const reason = result.exitCode === 0
113
- ? "empty output"
113
+ ? `empty output${result.stderr ? ` (stderr: ${summarizeError(result.stderr)})` : ""}`
114
114
  : `exit=${result.exitCode}: ${summarizeError(result.stderr || result.output)}`;
115
115
  failures.push(`${getAgentCliLabel(cli)} ${reason}`);
116
116
  } catch (error) {
@@ -15,6 +15,25 @@ import { getAgentCliLabel, runWithCliFallback } from "./agent-cli";
15
15
 
16
16
  const log = createLogger("daemon");
17
17
 
18
+ /** Detect CLI output that is an error message, not a real response */
19
+ function looksLikeCliError(output: string): boolean {
20
+ const lower = output.toLowerCase();
21
+ const errorPatterns = [
22
+ "authentication_error",
23
+ "invalid bearer token",
24
+ "invalid api key",
25
+ "api error: 401",
26
+ "api error: 403",
27
+ "failed to authenticate",
28
+ "permission_error",
29
+ "rate_limit_error",
30
+ "overloaded_error",
31
+ "api error: 429",
32
+ "api error: 529",
33
+ ];
34
+ return errorPatterns.some(p => lower.includes(p));
35
+ }
36
+
18
37
  export async function fallbackClaude(message: string, coreError?: string): Promise<string> {
19
38
  const systemContext = coreError
20
39
  ? `[System: Core process is down. Error: ${coreError}. You are running in fallback mode from Daemon. The user's project is at ${config.projectDir}. Respond to the user normally. If they ask about the error, explain what you see.]\n\n`
@@ -37,6 +56,11 @@ export async function fallbackClaude(message: string, coreError?: string): Promi
37
56
  const cli = getAgentCliLabel(result.cli);
38
57
  if (result.partial) {
39
58
  log.warn(`Fallback ${cli} returned output but exited with code ${result.exitCode}`);
59
+ // Don't forward CLI error messages (auth failures, API errors) to the user
60
+ if (looksLikeCliError(result.output)) {
61
+ log.error(`Fallback ${cli} output is a CLI error, not forwarding to user: ${result.output.slice(0, 300)}`);
62
+ return `[Fallback mode] ${cli} CLI failed (exit ${result.exitCode}). Please check server logs.`;
63
+ }
40
64
  } else {
41
65
  log.warn(`Using fallback ${cli} CLI`);
42
66
  }
@@ -246,6 +246,32 @@ async function installCli(cli: AgentCliName): Promise<boolean> {
246
246
  }
247
247
  }
248
248
 
249
+ /**
250
+ * Read the OAuth token from Claude CLI's credentials file.
251
+ * Claude CLI stores credentials at ~/.claude/.credentials.json after login.
252
+ */
253
+ function readClaudeCredentialsToken(): string | null {
254
+ const candidateDirs = [
255
+ isRunningAsRoot() ? "/home/arisa/.claude" : null,
256
+ join(process.env.HOME || "~", ".claude"),
257
+ ].filter(Boolean) as string[];
258
+
259
+ for (const dir of candidateDirs) {
260
+ const credsPath = join(dir, ".credentials.json");
261
+ if (!existsSync(credsPath)) continue;
262
+ try {
263
+ const raw = JSON.parse(readFileSync(credsPath, "utf8"));
264
+ const token = raw?.claudeAiOauth?.accessToken;
265
+ if (typeof token === "string" && token.startsWith("sk-ant-") && token.length > 50) {
266
+ return token;
267
+ }
268
+ } catch {
269
+ // Malformed file, skip
270
+ }
271
+ }
272
+ return null;
273
+ }
274
+
249
275
  async function runInteractiveLogin(cli: AgentCliName, vars: Record<string, string>): Promise<boolean> {
250
276
  const args = cli === "claude"
251
277
  ? ["setup-token"]
@@ -317,6 +343,14 @@ async function runInteractiveLogin(cli: AgentCliName, vars: Record<string, strin
317
343
  token = tokenArea.replace(/[^A-Za-z0-9_-]/g, "");
318
344
  }
319
345
 
346
+ // Fallback: if stdout scraping missed the token, read from Claude CLI's credentials file
347
+ if (!token || !token.startsWith("sk-ant-") || token.length <= 50 || token.length >= 150) {
348
+ token = readClaudeCredentialsToken() || "";
349
+ if (token) {
350
+ console.log(` ✓ token read from credentials file`);
351
+ }
352
+ }
353
+
320
354
  if (token && token.startsWith("sk-ant-") && token.length > 50 && token.length < 150) {
321
355
  console.log(` [token] ${token.slice(0, 20)}...${token.slice(-6)} (${token.length} chars)`);
322
356
  vars.CLAUDE_CODE_OAUTH_TOKEN = token;
@@ -324,31 +358,28 @@ async function runInteractiveLogin(cli: AgentCliName, vars: Record<string, strin
324
358
  saveEnv(vars);
325
359
  console.log(" ✓ claude token saved to .env");
326
360
 
327
- // Also write credentials file for arisa user (belt + suspenders)
328
- const claudeDir = isRunningAsRoot() ? "/home/arisa/.claude" : join(process.env.HOME || "~", ".claude");
329
- try {
330
- if (!existsSync(claudeDir)) mkdirSync(claudeDir, { recursive: true });
331
- const credsPath = join(claudeDir, ".credentials.json");
332
- const creds = {
333
- claudeAiOauth: {
334
- accessToken: token,
335
- expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000,
336
- scopes: ["user:inference", "user:profile"],
337
- },
338
- };
339
- writeFileSync(credsPath, JSON.stringify(creds, null, 2) + "\n");
340
- if (isRunningAsRoot()) {
341
- Bun.spawnSync(["chown", "-R", "arisa:arisa", claudeDir]);
361
+ // Also write credentials file for arisa user when running as root
362
+ if (isRunningAsRoot()) {
363
+ const arisaClaudeDir = "/home/arisa/.claude";
364
+ try {
365
+ if (!existsSync(arisaClaudeDir)) mkdirSync(arisaClaudeDir, { recursive: true });
366
+ const credsPath = join(arisaClaudeDir, ".credentials.json");
367
+ const creds = {
368
+ claudeAiOauth: {
369
+ accessToken: token,
370
+ expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000,
371
+ scopes: ["user:inference", "user:profile"],
372
+ },
373
+ };
374
+ writeFileSync(credsPath, JSON.stringify(creds, null, 2) + "\n");
375
+ Bun.spawnSync(["chown", "-R", "arisa:arisa", arisaClaudeDir]);
376
+ console.log(` ✓ credentials written to ${credsPath}`);
377
+ } catch (e) {
378
+ console.log(` ⚠ could not write credentials file: ${e}`);
342
379
  }
343
- console.log(` ✓ credentials written to ${credsPath}`);
344
- } catch (e) {
345
- console.log(` ⚠ could not write credentials file: ${e}`);
346
380
  }
347
381
  } else {
348
- console.log(` ⚠ token extraction failed (indexOf=${startIdx}, len=${token.length})`);
349
- if (startIdx >= 0) {
350
- console.log(` [clean] ${clean.substring(startIdx, startIdx + 150).replace(/\n/g, "\\n")}`);
351
- }
382
+ console.log(` ⚠ token not captured (Claude CLI stored credentials internally)`);
352
383
  }
353
384
  console.log(` ✓ claude login successful`);
354
385
  return true;
@@ -101,7 +101,7 @@ export function buildBunWrappedAgentCliCommand(cli: AgentCliName, args: string[]
101
101
  // Run as arisa user — Claude CLI refuses to run as root.
102
102
  // This path is used by Daemon fallback calls; Core runs as arisa directly.
103
103
  const cliPath = resolveAgentCliPath(cli) || join(ROOT_BUN_BIN, cli);
104
- const inner = ["bun", "--bun", INK_SHIM, cliPath, ...args].map(shellEscape).join(" ");
104
+ const inner = ["bun", "--bun", "--preload", INK_SHIM, cliPath, ...args].map(shellEscape).join(" ");
105
105
  // su without "-" preserves parent env (tokens, keys); explicit HOME/PATH for arisa
106
106
  return ["su", "arisa", "-s", "/bin/bash", "-c", `${ARISA_BUN_ENV} && ${buildEnvExports()}${inner}`];
107
107
  }
@@ -112,5 +112,5 @@ export function buildBunWrappedAgentCliCommand(cli: AgentCliName, args: string[]
112
112
  }
113
113
  // Preload shim that patches process.stdin.setRawMode to prevent Ink crash
114
114
  // when running without a TTY (systemd, su -c, etc.)
115
- return ["bun", "--bun", INK_SHIM, cliPath, ...args];
115
+ return ["bun", "--bun", "--preload", INK_SHIM, cliPath, ...args];
116
116
  }