arisa 2.3.35 → 2.3.36

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.35",
3
+ "version": "2.3.36",
4
4
  "description": "Arisa - dynamic agent runtime with daemon/core architecture that evolves through user interaction",
5
5
  "keywords": [
6
6
  "tinyclaw",
@@ -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, isRunningAsRoot, type AgentCliName } from "../shared/ai-cli";
18
+ import { isAgentCliInstalled, buildBunWrappedAgentCliCommand, type AgentCliName } from "../shared/ai-cli";
19
19
 
20
20
  const ENV_PATH = join(dataDir, ".env");
21
21
  const SETUP_DONE_KEY = "ARISA_SETUP_COMPLETE";
@@ -246,31 +246,6 @@ 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
250
  async function runInteractiveLogin(cli: AgentCliName, vars: Record<string, string>): Promise<boolean> {
276
251
  const args = cli === "claude"
@@ -280,116 +255,8 @@ async function runInteractiveLogin(cli: AgentCliName, vars: Record<string, strin
280
255
  console.log(`Starting ${cli} login...`);
281
256
 
282
257
  try {
283
- // For claude: capture stdout to extract OAuth token while still showing output
284
- if (cli === "claude") {
285
- const proc = Bun.spawn(buildBunWrappedAgentCliCommand(cli, args, { skipPreload: true }), {
286
- stdin: "inherit",
287
- stdout: "pipe",
288
- stderr: "inherit",
289
- });
290
-
291
- let output = "";
292
- const reader = (proc.stdout as ReadableStream<Uint8Array>).getReader();
293
- const decoder = new TextDecoder();
294
- while (true) {
295
- const { done, value } = await reader.read();
296
- if (done) break;
297
- const chunk = decoder.decode(value, { stream: true });
298
- process.stdout.write(chunk);
299
- output += chunk;
300
- }
301
-
302
- const exitCode = await proc.exited;
303
- if (exitCode === 0) {
304
- // Strip ANSI with a state machine (regex can't handle all Ink sequences)
305
- function stripAnsi(s: string): string {
306
- let out = "";
307
- for (let i = 0; i < s.length; i++) {
308
- if (s.charCodeAt(i) === 0x1b) {
309
- i++;
310
- if (i >= s.length) break;
311
- if (s[i] === "[") {
312
- // CSI: ESC [ <params 0x20-0x3F>* <final 0x40-0x7E>
313
- i++;
314
- while (i < s.length && s.charCodeAt(i) < 0x40) i++;
315
- // i now on final byte, loop will i++
316
- } else if (s[i] === "]") {
317
- // OSC: ESC ] ... BEL(0x07) or ST(ESC \)
318
- i++;
319
- while (i < s.length && s.charCodeAt(i) !== 0x07 && s[i] !== "\x1b") i++;
320
- } else if (s[i] === "(" || s[i] === ")" || s[i] === "#") {
321
- i++; // skip designator byte
322
- }
323
- // else: 2-byte Fe sequence, already skipped
324
- } else if (s.charCodeAt(i) < 0x20 && s[i] !== "\n" && s[i] !== "\r") {
325
- // skip control chars
326
- } else {
327
- out += s[i];
328
- }
329
- }
330
- return out;
331
- }
332
-
333
- const clean = stripAnsi(output);
334
- const startIdx = clean.indexOf("sk-ant-");
335
- let token = "";
336
-
337
- if (startIdx >= 0) {
338
- let endIdx = clean.indexOf("Store", startIdx);
339
- if (endIdx < 0) endIdx = clean.indexOf("Use this", startIdx);
340
- if (endIdx < 0) endIdx = startIdx + 200;
341
-
342
- const tokenArea = clean.substring(startIdx, endIdx);
343
- token = tokenArea.replace(/[^A-Za-z0-9_-]/g, "");
344
- }
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
-
354
- if (token && token.startsWith("sk-ant-") && token.length > 50 && token.length < 150) {
355
- console.log(` [token] ${token.slice(0, 20)}...${token.slice(-6)} (${token.length} chars)`);
356
- vars.CLAUDE_CODE_OAUTH_TOKEN = token;
357
- process.env.CLAUDE_CODE_OAUTH_TOKEN = token;
358
- saveEnv(vars);
359
- console.log(" ✓ claude token saved to .env");
360
-
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}`);
379
- }
380
- }
381
- } else {
382
- console.log(` ⚠ token not captured (Claude CLI stored credentials internally)`);
383
- }
384
- console.log(` ✓ claude login successful`);
385
- return true;
386
- } else {
387
- console.log(` ✗ claude login failed (exit ${exitCode})`);
388
- return false;
389
- }
390
- }
391
-
392
- // For codex and others: inherit all stdio
258
+ // Let the CLI handle its own credential storage (credentials.json / keychain).
259
+ // Don't intercept stdout or save tokens to .env — the CLI manages refresh internally.
393
260
  const proc = Bun.spawn(buildBunWrappedAgentCliCommand(cli, args, { skipPreload: true }), {
394
261
  stdin: "inherit",
395
262
  stdout: "inherit",
@@ -399,6 +266,16 @@ async function runInteractiveLogin(cli: AgentCliName, vars: Record<string, strin
399
266
 
400
267
  if (exitCode === 0) {
401
268
  console.log(` ✓ ${cli} login successful`);
269
+
270
+ // Clean up stale CLAUDE_CODE_OAUTH_TOKEN from .env if present —
271
+ // it overrides the CLI's own credential store and breaks token refresh.
272
+ if (cli === "claude" && vars.CLAUDE_CODE_OAUTH_TOKEN) {
273
+ delete vars.CLAUDE_CODE_OAUTH_TOKEN;
274
+ delete process.env.CLAUDE_CODE_OAUTH_TOKEN;
275
+ saveEnv(vars);
276
+ console.log(" ✓ removed stale CLAUDE_CODE_OAUTH_TOKEN from .env (CLI manages auth internally)");
277
+ }
278
+
402
279
  return true;
403
280
  } else {
404
281
  console.log(` ✗ ${cli} login failed (exit ${exitCode})`);
@@ -80,9 +80,11 @@ export function isAgentCliInstalled(cli: AgentCliName): boolean {
80
80
 
81
81
  const INK_SHIM = join(dirname(new URL(import.meta.url).pathname), "ink-shim.js");
82
82
 
83
- // Env vars that must survive the su - login shell reset
83
+ // Env vars that must survive the su - login shell reset.
84
+ // Note: CLAUDE_CODE_OAUTH_TOKEN intentionally excluded — the CLI manages
85
+ // its own credentials via ~/.claude/.credentials.json with token refresh.
86
+ // Injecting a stale accessToken via env var breaks refresh.
84
87
  const PASSTHROUGH_VARS = [
85
- "CLAUDE_CODE_OAUTH_TOKEN",
86
88
  "ANTHROPIC_API_KEY",
87
89
  "OPENAI_API_KEY",
88
90
  ];