arisa 5.1.10 → 5.1.11

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": "5.1.10",
3
+ "version": "5.1.11",
4
4
  "description": "Telegram + Pi Agent modular assistant",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1,8 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { access, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
- import readline from "node:readline/promises";
5
- import { stdin, stdout } from "node:process";
6
4
  import { applyConfigDefaults } from "../core/config/config-defaults.js";
7
5
  import { installLockedOfficialTool } from "../core/tools/official-tool-installer.js";
8
6
  import { createHeadlessApp } from "./create-headless-app.js";
@@ -100,15 +98,6 @@ export async function invokeSlaveTool(paths, args, { run = runProcess } = {}) {
100
98
  });
101
99
  }
102
100
 
103
- async function askFromTerminal(prompt) {
104
- const terminal = readline.createInterface({ input: stdin, output: stdout });
105
- try {
106
- return await terminal.question(`${prompt}: `);
107
- } finally {
108
- terminal.close();
109
- }
110
- }
111
-
112
101
  async function listSlaveTools(paths) {
113
102
  const entries = await readdir(paths.toolsDir, { withFileTypes: true }).catch(() => []);
114
103
  const tools = [];
@@ -175,7 +164,6 @@ function explainSlaveBootstrapError(error) {
175
164
 
176
165
  export async function runSlaveBootstrap(url, {
177
166
  paths = getSlavePaths(resolveSlaveHome()),
178
- ask = askFromTerminal,
179
167
  selectAccount = selectSlaveServiceAccount,
180
168
  ensureTool = ensureMasterSlaveTool,
181
169
  installService = installSlaveSystemdService,
@@ -186,7 +174,7 @@ export async function runSlaveBootstrap(url, {
186
174
  } = {}) {
187
175
  parseSlaveBootstrapUrl(url);
188
176
  if (platform !== "linux") throw new Error("Arisa Slave service installation currently requires Linux with systemd");
189
- const account = await selectAccount({ ask });
177
+ const account = await selectAccount();
190
178
  await ensureSlaveConfig(paths);
191
179
  await ensureTool(paths);
192
180
  let result;
@@ -39,29 +39,13 @@ export function getSlavePaths(slaveHome) {
39
39
  export async function selectSlaveServiceAccount({
40
40
  euid = process.geteuid?.(),
41
41
  currentUser = os.userInfo().username,
42
- ask
42
+ environment = process.env
43
43
  } = {}) {
44
44
  if (euid !== 0) {
45
45
  return { scope: "user", user: requireAccountName(currentUser), root: false, dedicated: false };
46
46
  }
47
- if (typeof ask !== "function") throw new Error("Running Arisa Slave as UID 0 requires an explicit account selection");
48
- const choice = String(await ask([
49
- "Run Arisa Slave as:",
50
- "1. dedicated user arisa-slave (recommended)",
51
- "2. another existing user",
52
- "3. root",
53
- "Selection"
54
- ].join("\n"))).trim();
55
- if (choice === "1") return { scope: "system", user: "arisa-slave", root: false, dedicated: true };
56
- if (choice === "2") {
57
- return { scope: "system", user: requireAccountName(await ask("Existing service user")), root: false, dedicated: false };
58
- }
59
- if (choice === "3") {
60
- const confirmation = String(await ask("Type RUN AS ROOT to confirm full root authority")).trim();
61
- if (confirmation !== "RUN AS ROOT") throw new Error("Root execution was not confirmed");
62
- return { scope: "system", user: "root", root: true, dedicated: false };
63
- }
64
- throw new Error("Invalid Arisa Slave service account selection");
47
+ const user = requireAccountName(environment.SUDO_USER || currentUser);
48
+ return { scope: "system", user, root: user === "root", dedicated: false };
65
49
  }
66
50
 
67
51
  function quoteSystemd(value) {
@@ -166,7 +150,8 @@ export async function installSlaveSystemdService({
166
150
  }
167
151
  const systemctlArgs = account.scope === "user" ? ["--user"] : [];
168
152
  await execute("systemctl", [...systemctlArgs, "daemon-reload"], { env: environment });
169
- await execute("systemctl", [...systemctlArgs, "enable", "--now", slaveServiceName], { env: environment });
153
+ await execute("systemctl", [...systemctlArgs, "enable", slaveServiceName], { env: environment });
154
+ await execute("systemctl", [...systemctlArgs, "restart", slaveServiceName], { env: environment });
170
155
  return { unitFile, serviceName: slaveServiceName, account, paths };
171
156
  }
172
157
 
@@ -58,16 +58,21 @@ test("hands one-shot requests through a 0600 file and removes it", async (t) =>
58
58
  await assert.rejects(() => access(handedFile), { code: "ENOENT" });
59
59
  });
60
60
 
61
- test("never selects root without an explicit second confirmation", async () => {
62
- await assert.rejects(() => selectSlaveServiceAccount({ euid: 0 }), /explicit account selection/);
63
- const rejectedAnswers = ["3", "yes"];
64
- await assert.rejects(
65
- () => selectSlaveServiceAccount({ euid: 0, ask: async () => rejectedAnswers.shift() }),
66
- /Root execution was not confirmed/
61
+ test("selects the invoking service account without prompting", async () => {
62
+ assert.deepEqual(
63
+ await selectSlaveServiceAccount({ euid: 1000, currentUser: "storybot", environment: {} }),
64
+ { scope: "user", user: "storybot", root: false, dedicated: false }
67
65
  );
68
- const acceptedAnswers = ["3", "RUN AS ROOT"];
69
66
  assert.deepEqual(
70
- await selectSlaveServiceAccount({ euid: 0, ask: async () => acceptedAnswers.shift() }),
67
+ await selectSlaveServiceAccount({
68
+ euid: 0,
69
+ currentUser: "root",
70
+ environment: { SUDO_USER: "storybot" }
71
+ }),
72
+ { scope: "system", user: "storybot", root: false, dedicated: false }
73
+ );
74
+ assert.deepEqual(
75
+ await selectSlaveServiceAccount({ euid: 0, currentUser: "root", environment: {} }),
71
76
  { scope: "system", user: "root", root: true, dedicated: false }
72
77
  );
73
78
  });
@@ -109,7 +114,7 @@ test("refuses to replace the PID of an active Slave host", async (t) => {
109
114
  await assert.rejects(() => registerSlaveServiceProcess(paths), /already running/);
110
115
  });
111
116
 
112
- test("installs the dedicated Linux systemd target without silently selecting root", async (t) => {
117
+ test("installs and restarts the Linux systemd target after pairing", async (t) => {
113
118
  const root = await mkdtemp(path.join(os.tmpdir(), "arisa-slave-systemd-"));
114
119
  t.after(() => rm(root, { recursive: true, force: true }));
115
120
  const calls = [];
@@ -132,7 +137,11 @@ test("installs the dedicated Linux systemd target without silently selecting roo
132
137
  assert.equal(result.account.root, false);
133
138
  assert.equal(await access(result.unitFile).then(() => true, () => false), true);
134
139
  assert.ok(calls.some(([command]) => command === "useradd"));
135
- assert.ok(calls.some(([command, args]) => command === "systemctl" && args.includes("enable") && args.includes("--now")));
140
+ assert.deepEqual(calls.filter(([command]) => command === "systemctl"), [
141
+ ["systemctl", ["daemon-reload"]],
142
+ ["systemctl", ["enable", "arisa-slave.service"]],
143
+ ["systemctl", ["restart", "arisa-slave.service"]]
144
+ ]);
136
145
  });
137
146
 
138
147
  test("validates before effects and keeps the bootstrap secret out of service metadata", async (t) => {
@@ -144,7 +153,10 @@ test("validates before effects and keeps the bootstrap secret out of service met
144
153
  paths,
145
154
  entryFile: "/opt/arisa/src/index.js",
146
155
  platform: "linux",
147
- selectAccount: async () => ({ scope: "user", user: "tester", root: false, dedicated: false }),
156
+ selectAccount: async (...args) => {
157
+ assert.deepEqual(args, []);
158
+ return { scope: "user", user: "tester", root: false, dedicated: false };
159
+ },
148
160
  ensureTool: async () => { calls.push("tool"); },
149
161
  installService: async () => { calls.push("service"); },
150
162
  invokeTool: async (_paths, args) => {