arisa 2.0.1 → 2.0.2

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
@@ -23,10 +23,10 @@ export PATH="$BUN_INSTALL/bin:$PATH"
23
23
 
24
24
  ```bash
25
25
  bun install -g arisa # Global install from package registry (recommended)
26
- npm install -g arisa # Alternative global install via npm (may require sudo)
27
26
  ```
28
27
 
29
28
  ```bash
29
+ arisa # Foreground daemon mode (Ctrl+C to stop)
30
30
  arisa start # Start as service (enables autostart with systemd --user)
31
31
  arisa stop # Stop service
32
32
  arisa status # Service status
@@ -36,13 +36,6 @@ arisa core # Foreground core-only mode
36
36
  arisa dev # Foreground core watch mode
37
37
  ```
38
38
 
39
- ```bash
40
- bun install # Install dependencies
41
- bun run daemon # Start everything (Daemon spawns Core with --watch)
42
- bun run dev # Start Core only with hot-reload (for development)
43
- npm install -g . # Global install via Node/npm
44
- bun add -g . # Global install via Bun
45
- ```
46
39
 
47
40
  On Linux with `systemd --user`, `arisa start` enables auto-start on reboot. To keep it running even without an active login session:
48
41
 
@@ -95,12 +88,12 @@ src/
95
88
  │ ├── media.ts # Voice transcription (Whisper), image analysis (Vision), speech synthesis (ElevenLabs)
96
89
  │ ├── scheduler.ts # Cron + one-time tasks with croner, persists via deepbase
97
90
  │ ├── format.ts # Telegram chunking (4096 char limit)
98
- │ ├── file-detector.ts # Detect file paths in responses for auto-sending
91
+ │ ├── file-detector.ts # Detect file paths in responses for auto-sending
99
92
  │ └── context.ts # Manage -c flag and reset_flag
100
93
 
101
94
  └── shared/
102
95
  ├── types.ts # All shared interfaces
103
- ├── config.ts # Env vars, ports, paths
96
+ ├── config.ts # Env vars, ports, paths
104
97
  ├── logger.ts # Logger → .arisa/logs/
105
98
  └── db.ts # Unified persistence layer (deepbase)
106
99
  ```
package/bin/arisa.js CHANGED
@@ -27,15 +27,17 @@ const systemdUserDir = join(homeDir, ".config", "systemd", "user");
27
27
  const systemdUserUnitPath = join(systemdUserDir, systemdServiceName);
28
28
 
29
29
  const args = process.argv.slice(2);
30
- const command = (args[0] || "start").toLowerCase();
31
- const rest = args.slice(1);
30
+ const inputCommand = (args[0] || "").toLowerCase();
31
+ const command = inputCommand || "daemon";
32
+ const rest = inputCommand ? args.slice(1) : args;
33
+ const isDefaultInvocation = inputCommand === "";
32
34
 
33
35
  function printHelp() {
34
36
  process.stdout.write(
35
37
  `Arisa CLI
36
38
 
37
39
  Usage:
38
- arisa Start service (default)
40
+ arisa Start daemon in foreground (default)
39
41
  arisa start Start service and enable restart-on-boot
40
42
  arisa stop Stop service
41
43
  arisa status Show service status
@@ -396,6 +398,11 @@ function restartService() {
396
398
  return restartDetachedFallback();
397
399
  }
398
400
 
401
+ function printForegroundNotice() {
402
+ process.stdout.write("Starting Arisa in foreground. Press Ctrl+C to stop.\n");
403
+ process.stdout.write("Use `arisa start` to run it as a background service.\n");
404
+ }
405
+
399
406
  if (command === "help" || command === "--help" || command === "-h") {
400
407
  printHelp();
401
408
  process.exit(0);
@@ -421,6 +428,9 @@ switch (command) {
421
428
  break;
422
429
  case "daemon":
423
430
  case "run": {
431
+ if (isDefaultInvocation) {
432
+ printForegroundNotice();
433
+ }
424
434
  const child = runWithBun([daemonEntry, ...rest]);
425
435
  process.exit(child.status === null ? 1 : child.status);
426
436
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arisa",
3
- "version": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Arisa - dynamic agent runtime with daemon/core architecture that evolves through user interaction",
5
5
  "preferGlobal": true,
6
6
  "bin": {
@@ -24,10 +24,11 @@ const ACTIVITY_LOG = join(config.logsDir, "activity.log");
24
24
  const PROMPT_PREVIEW_MAX = 220;
25
25
  export const CLAUDE_RATE_LIMIT_MESSAGE = "Claude is out of credits right now. Please try again in a few minutes.";
26
26
  export const CODEX_AUTH_REQUIRED_MESSAGE = [
27
- "Codex is not authenticated on this server.",
28
- "Check the server terminal and run:",
27
+ "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:",
29
30
  "<code>codex login --device-auth</code>",
30
- "Then try again.",
31
+ "Then send your message again.",
31
32
  ].join("\n");
32
33
 
33
34
  function logActivity(backend: string, model: string | null, durationMs: number, status: string) {
@@ -23,13 +23,22 @@ const RETRY_COOLDOWN_MS = 30_000;
23
23
 
24
24
  let loginInProgress = false;
25
25
  let lastLoginAttemptAt = 0;
26
+ const pendingChatIds = new Set<string>();
27
+
28
+ type NotifyFn = (chatId: string, text: string) => Promise<void>;
29
+ let notifyFn: NotifyFn | null = null;
30
+
31
+ export function setCodexLoginNotify(fn: NotifyFn) {
32
+ notifyFn = fn;
33
+ }
26
34
 
27
35
  function needsCodexLogin(text: string): boolean {
28
36
  return AUTH_HINT_PATTERNS.some((pattern) => pattern.test(text));
29
37
  }
30
38
 
31
- export function maybeStartCodexDeviceAuth(rawCoreText: string): void {
39
+ export function maybeStartCodexDeviceAuth(rawCoreText: string, chatId?: string): void {
32
40
  if (!rawCoreText || !needsCodexLogin(rawCoreText)) return;
41
+ if (chatId) pendingChatIds.add(chatId);
33
42
 
34
43
  if (loginInProgress) {
35
44
  log.info("Codex device auth already in progress; skipping duplicate trigger");
@@ -50,7 +59,8 @@ export function maybeStartCodexDeviceAuth(rawCoreText: string): void {
50
59
  }
51
60
 
52
61
  async function runCodexDeviceAuth(): Promise<void> {
53
- log.warn("Codex auth error detected. Running: codex login --device-auth");
62
+ log.warn("Codex auth required. Starting `codex login --device-auth` now.");
63
+ log.warn("Complete device auth using the URL/code printed below in this Arisa terminal.");
54
64
 
55
65
  let proc: ReturnType<typeof Bun.spawn>;
56
66
  try {
@@ -68,8 +78,32 @@ async function runCodexDeviceAuth(): Promise<void> {
68
78
 
69
79
  const exitCode = await proc.exited;
70
80
  if (exitCode === 0) {
71
- log.info("Codex device auth finished successfully");
81
+ log.info("Codex device auth finished successfully. You can retry your message.");
82
+ await notifySuccess();
72
83
  } else {
73
84
  log.error(`Codex device auth finished with exit code ${exitCode}`);
85
+ pendingChatIds.clear();
74
86
  }
75
87
  }
88
+
89
+ async function notifySuccess(): Promise<void> {
90
+ if (!notifyFn || pendingChatIds.size === 0) return;
91
+
92
+ const text = [
93
+ "<b>Codex login completed successfully.</b>",
94
+ "Then try again.",
95
+ ].join("\n");
96
+
97
+ const chats = Array.from(pendingChatIds);
98
+ pendingChatIds.clear();
99
+
100
+ await Promise.all(
101
+ chats.map(async (chatId) => {
102
+ try {
103
+ await notifyFn?.(chatId, text);
104
+ } catch (error) {
105
+ log.error(`Failed to send Codex login success notice to ${chatId}: ${error}`);
106
+ }
107
+ }),
108
+ );
109
+ }
@@ -28,7 +28,7 @@ const { TelegramChannel } = await import("./channels/telegram");
28
28
  const { sendToCore } = await import("./bridge");
29
29
  const { startCore, stopCore, setLifecycleNotify } = await import("./lifecycle");
30
30
  const { setAutoFixNotify } = await import("./autofix");
31
- const { maybeStartCodexDeviceAuth } = await import("./codex-login");
31
+ const { maybeStartCodexDeviceAuth, setCodexLoginNotify } = await import("./codex-login");
32
32
  const { chunkMessage, markdownToTelegramHtml } = await import("../core/format");
33
33
  const { saveMessageRecord } = await import("../shared/db");
34
34
 
@@ -61,6 +61,9 @@ const sendToAllChats = async (text: string) => {
61
61
 
62
62
  setLifecycleNotify(sendToAllChats);
63
63
  setAutoFixNotify(sendToAllChats);
64
+ setCodexLoginNotify(async (chatId, text) => {
65
+ await telegram.send(chatId, text);
66
+ });
64
67
 
65
68
  telegram.onMessage(async (msg) => {
66
69
  knownChatIds.add(msg.chatId);
@@ -78,7 +81,7 @@ telegram.onMessage(async (msg) => {
78
81
  clearInterval(typingInterval);
79
82
 
80
83
  const raw = response.text || "";
81
- maybeStartCodexDeviceAuth(raw);
84
+ maybeStartCodexDeviceAuth(raw, msg.chatId);
82
85
  const messageParts = raw.split(/\n---CHUNK---\n/g);
83
86
  let sentText = false;
84
87