arisa 2.0.4 → 2.0.6
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 +2 -3
- package/src/core/index.ts +25 -5
- package/src/core/intent.ts +11 -4
- package/src/core/onboarding.ts +3 -2
- package/src/core/processor.ts +4 -6
- package/src/daemon/agent-cli.ts +16 -6
- package/src/daemon/codex-login.ts +5 -4
- package/src/shared/ai-cli.ts +22 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arisa",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"description": "Arisa - dynamic agent runtime with daemon/core architecture that evolves through user interaction",
|
|
5
5
|
"preferGlobal": true,
|
|
6
6
|
"bin": {
|
|
@@ -29,8 +29,7 @@
|
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"croner": "^9.0.0",
|
|
31
31
|
"crypto-js": "^4.2.0",
|
|
32
|
-
"deepbase": "^3.4.
|
|
33
|
-
"deepbase-json": "^3.4.6",
|
|
32
|
+
"deepbase": "^3.4.9",
|
|
34
33
|
"elevenlabs": "^1.59.0",
|
|
35
34
|
"grammy": "^1.21.0",
|
|
36
35
|
"openai": "^6.19.0",
|
package/src/core/index.ts
CHANGED
|
@@ -49,16 +49,25 @@ function defaultBackend(): "claude" | "codex" {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function getBackend(chatId: string): "claude" | "codex" {
|
|
52
|
+
const deps = checkDeps();
|
|
53
|
+
|
|
54
|
+
const preferInstalled = (candidate: "claude" | "codex"): "claude" | "codex" => {
|
|
55
|
+
if (candidate === "claude" && !deps.claude && deps.codex) return "codex";
|
|
56
|
+
if (candidate === "codex" && !deps.codex && deps.claude) return "claude";
|
|
57
|
+
return candidate;
|
|
58
|
+
};
|
|
59
|
+
|
|
52
60
|
const current = backendState.get(chatId);
|
|
53
|
-
if (current) return current;
|
|
61
|
+
if (current) return preferInstalled(current);
|
|
54
62
|
|
|
55
63
|
const fromHistory = getLastBackend(chatId);
|
|
56
64
|
if (fromHistory) {
|
|
57
|
-
|
|
58
|
-
|
|
65
|
+
const resolved = preferInstalled(fromHistory);
|
|
66
|
+
backendState.set(chatId, resolved);
|
|
67
|
+
return resolved;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
return defaultBackend();
|
|
70
|
+
return preferInstalled(defaultBackend());
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
// Initialize auth + scheduler + attachments
|
|
@@ -329,9 +338,15 @@ ${messageText}`;
|
|
|
329
338
|
return Response.json(response);
|
|
330
339
|
}
|
|
331
340
|
|
|
341
|
+
const deps = checkDeps();
|
|
342
|
+
if (!deps.claude && !deps.codex) {
|
|
343
|
+
return Response.json({
|
|
344
|
+
text: "No AI CLI is installed. Install at least one:\n<code>bun add -g @anthropic-ai/claude-code</code>\n<code>bun add -g @openai/codex</code>",
|
|
345
|
+
} as CoreResponse);
|
|
346
|
+
}
|
|
347
|
+
|
|
332
348
|
// Route based on current backend state
|
|
333
349
|
const backend = getBackend(msg.chatId);
|
|
334
|
-
const deps = checkDeps();
|
|
335
350
|
const canFallback = backend === "codex" ? deps.claude : deps.codex;
|
|
336
351
|
let agentResponse: string;
|
|
337
352
|
let historyResponse: string | null = null;
|
|
@@ -363,6 +378,11 @@ ${messageText}`;
|
|
|
363
378
|
} else {
|
|
364
379
|
try {
|
|
365
380
|
agentResponse = await processWithClaude(enrichedMessage, msg.chatId);
|
|
381
|
+
if (agentResponse.startsWith("Error:") && canFallback) {
|
|
382
|
+
log.warn("Claude failed, falling back to Codex");
|
|
383
|
+
agentResponse = await processWithCodex(enrichedMessage);
|
|
384
|
+
usedBackend = "codex";
|
|
385
|
+
}
|
|
366
386
|
if (isClaudeRateLimitResponse(agentResponse) && canFallback) {
|
|
367
387
|
log.warn("Claude credits exhausted, falling back to Codex");
|
|
368
388
|
const codexResponse = await processWithCodex(enrichedMessage);
|
package/src/core/intent.ts
CHANGED
|
@@ -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
|
|
55
|
+
return buildBunWrappedAgentCliCommand(
|
|
56
|
+
"claude",
|
|
57
|
+
["--dangerously-skip-permissions", "--model", "haiku", "-p", prompt],
|
|
58
|
+
);
|
|
55
59
|
}
|
|
56
|
-
return
|
|
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 (
|
|
79
|
-
if (
|
|
85
|
+
if (resolveAgentCliPath("claude") !== null) order.push("claude");
|
|
86
|
+
if (resolveAgentCliPath("codex") !== null) order.push("codex");
|
|
80
87
|
return order;
|
|
81
88
|
}
|
|
82
89
|
|
package/src/core/onboarding.ts
CHANGED
|
@@ -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:
|
|
50
|
-
codex:
|
|
50
|
+
claude: isAgentCliInstalled("claude"),
|
|
51
|
+
codex: isAgentCliInstalled("codex"),
|
|
51
52
|
openaiKey: !!config.openaiApiKey,
|
|
52
53
|
os,
|
|
53
54
|
};
|
package/src/core/processor.ts
CHANGED
|
@@ -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(
|
|
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(
|
|
219
|
+
const proc = Bun.spawn(buildBunWrappedAgentCliCommand("codex", args), {
|
|
222
220
|
cwd: config.projectDir,
|
|
223
221
|
stdout: "pipe",
|
|
224
222
|
stderr: "pipe",
|
package/src/daemon/agent-cli.ts
CHANGED
|
@@ -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 =
|
|
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 (
|
|
36
|
-
if (
|
|
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
|
|
51
|
+
return buildBunWrappedAgentCliCommand(
|
|
52
|
+
"claude",
|
|
53
|
+
["--dangerously-skip-permissions", "--model", "sonnet", "-p", prompt],
|
|
54
|
+
);
|
|
47
55
|
}
|
|
48
|
-
return
|
|
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
|
|
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(
|
|
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
|
+
}
|