@leuria/cli 0.1.2 → 0.1.3

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.
@@ -654,13 +654,13 @@ function compareVersions(a, b) {
654
654
  }
655
655
 
656
656
  // src/version.ts
657
- var VERSION = true ? "0.1.2" : "0.0.0-dev";
657
+ var VERSION = true ? "0.1.3" : "0.0.0-dev";
658
658
 
659
659
  // src/auth.ts
660
660
  import crossSpawn3 from "cross-spawn";
661
- import { mkdtempSync as mkdtempSync2, rmSync as rmSync3 } from "fs";
662
- import { tmpdir as tmpdir2 } from "os";
663
- import { join as join5 } from "path";
661
+ import { mkdtempSync as mkdtempSync3 } from "fs";
662
+ import { tmpdir as tmpdir3 } from "os";
663
+ import { join as join6 } from "path";
664
664
 
665
665
  // src/acp/acp-client.ts
666
666
  import { spawn } from "child_process";
@@ -710,6 +710,9 @@ var AcpLiveSession = class {
710
710
  imagesSupported = false;
711
711
  /** `initialize` result, once connected. */
712
712
  info = null;
713
+ /** The sign-in the agent says it uses, from `_auth/status_update` (Claude): "account", "api_key", "none"… */
714
+ authKind;
715
+ authWaiters = [];
713
716
  /** Models the agent offers, from `session/new` (and later updates). */
714
717
  models = null;
715
718
  get isAlive() {
@@ -778,6 +781,27 @@ var AcpLiveSession = class {
778
781
  return { error: error instanceof Error ? error.message : String(error) };
779
782
  }
780
783
  }
784
+ /**
785
+ * The sign-in the agent reports (see `authKind`), waiting up to `ms` for its first
786
+ * report. Undefined when the agent doesn't report one, or not in time. Some agents
787
+ * (Claude) open a session while signed out and refuse only the first prompt, so a
788
+ * session alone doesn't prove a sign-in.
789
+ */
790
+ async authStatus(ms) {
791
+ if (this.authKind !== void 0) return this.authKind;
792
+ if (!this.info?.agentCapabilities._meta?.authStatus) return void 0;
793
+ return new Promise((resolve) => {
794
+ const timer = setTimeout(() => {
795
+ this.authWaiters = this.authWaiters.filter((w) => w !== waiter);
796
+ resolve(void 0);
797
+ }, ms);
798
+ const waiter = (kind) => {
799
+ clearTimeout(timer);
800
+ resolve(kind);
801
+ };
802
+ this.authWaiters.push(waiter);
803
+ });
804
+ }
781
805
  /** ACP `authenticate` with one of the advertised methods (the agent runs its own flow). */
782
806
  async authenticate(methodId) {
783
807
  if (!this.connection) return { error: "Not connected" };
@@ -919,7 +943,12 @@ ${file.text}
919
943
  });
920
944
  }
921
945
  };
922
- return sdk.client({ name: "leuria" }).onNotification(
946
+ return sdk.client({ name: "leuria" }).onNotification("_auth/status_update", rawParams, (ctx) => {
947
+ const kind = ctx.params?.authStatus?.kind;
948
+ if (typeof kind !== "string") return;
949
+ this.authKind = kind;
950
+ for (const waiter of this.authWaiters.splice(0)) waiter(kind);
951
+ }).onNotification(
923
952
  "session/update",
924
953
  rawParams,
925
954
  (ctx) => handleSessionUpdate(ctx.params)
@@ -949,13 +978,105 @@ function killTree(child) {
949
978
  }
950
979
  }
951
980
 
981
+ // src/scratch.ts
982
+ import { rm } from "fs/promises";
983
+ function removeScratch(dir, onFailure) {
984
+ rm(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 500 }).catch((error) => onFailure?.(error));
985
+ }
986
+
987
+ // src/terminal-window.ts
988
+ import { spawn as spawn2 } from "child_process";
989
+ import { chmodSync as chmodSync3, existsSync as existsSync4, mkdtempSync, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
990
+ import { tmpdir } from "os";
991
+ import { join as join4 } from "path";
992
+ function canOpenTerminalWindow() {
993
+ return process.platform === "win32" || process.platform === "darwin";
994
+ }
995
+ async function runInTerminalWindow(options) {
996
+ if (!canOpenTerminalWindow()) throw new Error("Leuria can't open a terminal window on this system");
997
+ const dir = mkdtempSync(join4(tmpdir(), "leuria-window-"));
998
+ const done = join4(dir, "exit-code");
999
+ try {
1000
+ if (process.platform === "win32") {
1001
+ const script = join4(dir, "sign-in.cmd");
1002
+ writeFileSync4(script, windowsScript(options, done), "utf-8");
1003
+ spawn2("cmd.exe", ["/d", "/c", `start "${options.title.replaceAll('"', "")}" "${script}"`], {
1004
+ detached: true,
1005
+ stdio: "ignore",
1006
+ windowsHide: false,
1007
+ windowsVerbatimArguments: true
1008
+ }).unref();
1009
+ } else {
1010
+ const script = join4(dir, "sign-in.command");
1011
+ writeFileSync4(script, macScript(options, done), "utf-8");
1012
+ chmodSync3(script, 448);
1013
+ spawn2("open", ["-a", "Terminal", script], { detached: true, stdio: "ignore" }).unref();
1014
+ }
1015
+ return await waitFor(done, options.signal, options.timeoutMs ?? 15 * 6e4);
1016
+ } finally {
1017
+ removeScratch(dir);
1018
+ }
1019
+ }
1020
+ function waitFor(file, signal, timeoutMs) {
1021
+ return new Promise((resolve) => {
1022
+ const finish = (code) => {
1023
+ clearInterval(poll);
1024
+ clearTimeout(timer);
1025
+ signal?.removeEventListener("abort", onAbort);
1026
+ resolve(code);
1027
+ };
1028
+ const onAbort = () => finish(null);
1029
+ const poll = setInterval(() => {
1030
+ if (!existsSync4(file)) return;
1031
+ const code = Number.parseInt(readFileSync4(file, "utf-8").trim(), 10);
1032
+ finish(Number.isNaN(code) ? null : code);
1033
+ }, 1e3);
1034
+ const timer = setTimeout(() => finish(null), timeoutMs);
1035
+ if (signal?.aborted) onAbort();
1036
+ else signal?.addEventListener("abort", onAbort, { once: true });
1037
+ });
1038
+ }
1039
+ function windowsScript(options, done) {
1040
+ const text = (value) => value.replaceAll("%", "%%");
1041
+ const quote = (value) => `"${text(value).replaceAll('"', '""')}"`;
1042
+ return [
1043
+ "@echo off",
1044
+ "chcp 65001 >nul",
1045
+ `title ${text(options.title).replace(/[&|<>^]/g, "")}`,
1046
+ `echo ${text(options.title).replace(/[&|<>^]/g, "")}`,
1047
+ "echo Leuria opened this window. Follow the steps below: it closes by itself when you're done.",
1048
+ "echo.",
1049
+ `cd /d ${quote(options.cwd)}`,
1050
+ ...Object.entries(options.env).map(([key, value]) => `set "${key}=${text(value)}"`),
1051
+ [options.command, ...options.args].map(quote).join(" "),
1052
+ `echo %errorlevel%> ${quote(done)}`,
1053
+ ""
1054
+ ].join("\r\n");
1055
+ }
1056
+ function macScript(options, done) {
1057
+ const quote = (value) => `'${value.replaceAll("'", "'\\''")}'`;
1058
+ return [
1059
+ "#!/bin/bash",
1060
+ "clear",
1061
+ `echo ${quote(options.title)}`,
1062
+ `echo ${quote("Leuria opened this window. Follow the steps below, then close it.")}`,
1063
+ "echo",
1064
+ `cd ${quote(options.cwd)}`,
1065
+ ...Object.entries(options.env).map(([key, value]) => `export ${key}=${quote(value)}`),
1066
+ [options.command, ...options.args].map(quote).join(" "),
1067
+ `echo $? > ${quote(done)}`,
1068
+ `echo; echo ${quote("Done. You can close this window.")}`,
1069
+ ""
1070
+ ].join("\n");
1071
+ }
1072
+
952
1073
  // src/browser-link.ts
953
1074
  import { execFileSync } from "child_process";
954
- import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync5, mkdtempSync, readFileSync as readFileSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
955
- import { tmpdir } from "os";
956
- import { delimiter, join as join4 } from "path";
1075
+ import { chmodSync as chmodSync4, existsSync as existsSync5, mkdirSync as mkdirSync5, mkdtempSync as mkdtempSync2, readFileSync as readFileSync5, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
1076
+ import { tmpdir as tmpdir2 } from "os";
1077
+ import { delimiter, join as join5 } from "path";
957
1078
  function realCommand(name) {
958
- if (name === "open" && existsSync4("/usr/bin/open")) return "/usr/bin/open";
1079
+ if (name === "open" && existsSync5("/usr/bin/open")) return "/usr/bin/open";
959
1080
  try {
960
1081
  return execFileSync("/bin/sh", ["-c", `command -v ${name}`], { encoding: "utf-8" }).trim() || null;
961
1082
  } catch {
@@ -966,11 +1087,11 @@ function catchBrowserLinks(onUrl, options = {}) {
966
1087
  if (process.platform === "win32") return null;
967
1088
  const basePath = options.basePath ?? process.env.PATH ?? "";
968
1089
  const findReal = options.real ?? realCommand;
969
- const dir = mkdtempSync(join4(tmpdir(), "leuria-open-"));
970
- const bin = join4(dir, "bin");
971
- const log = join4(dir, "opened.txt");
1090
+ const dir = mkdtempSync2(join5(tmpdir2(), "leuria-open-"));
1091
+ const bin = join5(dir, "bin");
1092
+ const log = join5(dir, "opened.txt");
972
1093
  mkdirSync5(bin);
973
- writeFileSync4(log, "");
1094
+ writeFileSync5(log, "");
974
1095
  for (const name of ["open", "xdg-open"]) {
975
1096
  const real = findReal(name);
976
1097
  if (!real) continue;
@@ -978,14 +1099,14 @@ function catchBrowserLinks(onUrl, options = {}) {
978
1099
  for arg in "$@"; do printf '%s\\n' "$arg" >> '${log}'; done
979
1100
  exec '${real}' "$@"
980
1101
  `;
981
- writeFileSync4(join4(bin, name), script);
982
- chmodSync3(join4(bin, name), 493);
1102
+ writeFileSync5(join5(bin, name), script);
1103
+ chmodSync4(join5(bin, name), 493);
983
1104
  }
984
1105
  const seen = /* @__PURE__ */ new Set();
985
1106
  const timer = setInterval(() => {
986
1107
  let lines;
987
1108
  try {
988
- lines = readFileSync4(log, "utf-8").split("\n");
1109
+ lines = readFileSync5(log, "utf-8").split("\n");
989
1110
  } catch {
990
1111
  return;
991
1112
  }
@@ -1071,7 +1192,7 @@ function preferredMethod(methods, options = {}) {
1071
1192
  }
1072
1193
  async function open(id, onProgress, terminalAuth = false, extraEnv = {}) {
1073
1194
  const agent = await ensureAgent(id, { onProgress });
1074
- const sandbox = mkdtempSync2(join5(tmpdir2(), "leuria-auth-"));
1195
+ const sandbox = mkdtempSync3(join6(tmpdir3(), "leuria-auth-"));
1075
1196
  const custom = profileFor(id)?.configure?.({ sandbox, mcpUrl: "http://127.0.0.1:9/unused", mcpToken: "unused" });
1076
1197
  const env = { ...agent.env, ...custom?.env, ...extraEnv };
1077
1198
  const session = new AcpLiveSession({
@@ -1085,7 +1206,7 @@ async function open(id, onProgress, terminalAuth = false, extraEnv = {}) {
1085
1206
  });
1086
1207
  const close = () => {
1087
1208
  session.close();
1088
- rmSync3(sandbox, { recursive: true, force: true });
1209
+ removeScratch(sandbox);
1089
1210
  };
1090
1211
  return { agent, session, close, env, sandbox };
1091
1212
  }
@@ -1114,7 +1235,7 @@ function signInStallMs() {
1114
1235
  }
1115
1236
  async function checkSignIn(id, onProgress, options = {}) {
1116
1237
  if (isLlmId(id)) return checkLlm(id);
1117
- const { session, close } = await open(id, onProgress);
1238
+ const { session, close } = await open(id, onProgress, true);
1118
1239
  let timer;
1119
1240
  const timedOut = new Promise((resolve) => {
1120
1241
  timer = setTimeout(() => {
@@ -1132,22 +1253,25 @@ async function checkSignIn(id, onProgress, options = {}) {
1132
1253
  async function check() {
1133
1254
  const connected = await session.connect();
1134
1255
  if (connected.error) return { ok: false, detail: `the agent did not start: ${connected.error}`, methods: [] };
1135
- const methods = agentMethods(session.info?.authMethods ?? []);
1256
+ const all = session.info?.authMethods ?? [];
1257
+ const methods = agentMethods(all);
1258
+ const window = canOpenTerminalWindow() && methods.length === 0 && all.some(isTerminal) ? { window: true } : {};
1136
1259
  const created = await session.newSession();
1137
- if (!created.error) return { ok: true, detail: "ready", methods, ...withChoice(id, session.models) };
1138
- return { ok: false, detail: created.authRequired ? "not signed in" : created.error, methods };
1260
+ if (created.error) return { ok: false, detail: created.authRequired ? "not signed in" : created.error, methods, ...window };
1261
+ if (await session.authStatus(5e3) === "none") return { ok: false, detail: "not signed in", methods, ...window };
1262
+ return { ok: true, detail: "ready", methods, ...withChoice(id, session.models) };
1139
1263
  }
1140
1264
  }
1141
1265
  async function signIn(id, options = {}) {
1142
1266
  if (isLlmId(id)) return checkLlm(id);
1143
1267
  let pageOpened = false;
1144
- const link = options.onUrl && !options.terminal ? catchBrowserLinks((url) => {
1268
+ const link = options.onUrl && !options.terminal && !options.window ? catchBrowserLinks((url) => {
1145
1269
  pageOpened = true;
1146
1270
  options.onUrl?.(url);
1147
1271
  }) : null;
1148
1272
  let opened;
1149
1273
  try {
1150
- opened = await open(id, options.onProgress, options.terminal, link?.env);
1274
+ opened = await open(id, options.onProgress, options.terminal || options.window, link?.env);
1151
1275
  } catch (error) {
1152
1276
  link?.stop();
1153
1277
  throw error;
@@ -1190,13 +1314,30 @@ async function signIn(id, options = {}) {
1190
1314
  const connected = await session.connect();
1191
1315
  if (connected.error) return { ok: false, detail: `the agent did not start: ${connected.error}`, methods: [] };
1192
1316
  const all = session.info?.authMethods ?? [];
1193
- const methods = options.terminal ? all : agentMethods(all);
1317
+ const terminal = options.terminal || options.window;
1318
+ const methods = terminal ? all : agentMethods(all);
1194
1319
  if (methods.length === 0) {
1195
1320
  return { ok: false, detail: `${agent.name} offers no sign-in method Leuria can run; sign in with its own tool.`, methods };
1196
1321
  }
1197
- const method = options.methodId ? methods.find((m) => m.id === options.methodId) : preferredMethod(methods, { terminal: options.terminal });
1322
+ const method = options.methodId ? methods.find((m) => m.id === options.methodId) : preferredMethod(methods, { terminal });
1198
1323
  if (!method) return { ok: false, detail: `Unknown method. Available: ${methods.map((m) => m.id).join(", ")}`, methods };
1199
1324
  options.onProgress?.(`Signing in to ${agent.name} with ${method.name}${method.description ? ` (${method.description})` : ""}\u2026`);
1325
+ if (isTerminal(method) && options.window) {
1326
+ session.close();
1327
+ const code = await runInTerminalWindow({
1328
+ title: `Sign in to ${agentName(id)}`,
1329
+ command: agent.command,
1330
+ args: [...agent.launchArgs, ...method.args ?? []],
1331
+ // A compiled engine runs JavaScript agents as plain Bun, as sessions do.
1332
+ env: { ...env, ...method.env ?? {}, ...process.versions.bun ? { BUN_BE_BUN: "1" } : {} },
1333
+ cwd: sandbox,
1334
+ signal: options.signal
1335
+ });
1336
+ if (options.signal?.aborted) return { ok: false, detail: "cancelled", methods };
1337
+ if (code === null) return { ok: false, detail: "the sign-in window didn't finish", methods, window: true };
1338
+ if (code !== 0) return { ok: false, detail: `the sign-in ended with code ${code}`, methods, window: true };
1339
+ return checkSignIn(id, options.onProgress);
1340
+ }
1200
1341
  if (isTerminal(method)) {
1201
1342
  session.close();
1202
1343
  const code = await new Promise((resolve, reject) => {
@@ -1582,9 +1723,9 @@ import cacache from "cacache";
1582
1723
  // src/skill-sources.ts
1583
1724
  import { createHash as createHash3 } from "crypto";
1584
1725
  import { lookup } from "dns/promises";
1585
- import { mkdtemp, readdir, readFile, rm, stat } from "fs/promises";
1586
- import { tmpdir as tmpdir3 } from "os";
1587
- import { extname, join as join6, relative, sep } from "path";
1726
+ import { mkdtemp, readdir, readFile, rm as rm2, stat } from "fs/promises";
1727
+ import { tmpdir as tmpdir4 } from "os";
1728
+ import { extname, join as join7, relative, sep } from "path";
1588
1729
  import { downloadTemplate } from "giget";
1589
1730
  import ipaddr from "ipaddr.js";
1590
1731
  import { parse as parseYaml } from "yaml";
@@ -1658,7 +1799,7 @@ function parseSkillMd(text, files = {}) {
1658
1799
  return { name, description: description.slice(0, MAX_DESCRIPTION), body: match[2].trim(), files };
1659
1800
  }
1660
1801
  async function fromGithub(source) {
1661
- const dir = await mkdtemp(join6(tmpdir3(), "leuria-skills-"));
1802
+ const dir = await mkdtemp(join7(tmpdir4(), "leuria-skills-"));
1662
1803
  try {
1663
1804
  const path2 = source.subpath ? `/${source.subpath}` : "";
1664
1805
  await downloadTemplate(`gh:${source.repo}${path2}#${source.ref ?? "HEAD"}`, {
@@ -1678,21 +1819,21 @@ async function fromGithub(source) {
1678
1819
  continue;
1679
1820
  }
1680
1821
  if (source.skill && skill.name !== source.skill) continue;
1681
- skill.files = await readTextFiles(join6(skillFile, ".."));
1822
+ skill.files = await readTextFiles(join7(skillFile, ".."));
1682
1823
  found.push(skill);
1683
1824
  }
1684
1825
  return found;
1685
1826
  } finally {
1686
- await rm(dir, { recursive: true, force: true });
1827
+ await rm2(dir, { recursive: true, force: true });
1687
1828
  }
1688
1829
  }
1689
1830
  async function findSkillFiles(dir, depth) {
1690
1831
  const entries = await readdir(dir, { withFileTypes: true });
1691
1832
  const own = entries.find((e) => e.isFile() && e.name.toLowerCase() === "skill.md");
1692
- if (own) return [join6(dir, own.name)];
1833
+ if (own) return [join7(dir, own.name)];
1693
1834
  if (depth >= MAX_DEPTH) return [];
1694
1835
  const nested = await Promise.all(
1695
- entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => findSkillFiles(join6(dir, e.name), depth + 1))
1836
+ entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules").map((e) => findSkillFiles(join7(dir, e.name), depth + 1))
1696
1837
  );
1697
1838
  return nested.flat();
1698
1839
  }
@@ -1701,7 +1842,7 @@ async function readTextFiles(dir) {
1701
1842
  let total = 0;
1702
1843
  const walk = async (at, depth) => {
1703
1844
  for (const entry of await readdir(at, { withFileTypes: true })) {
1704
- const full = join6(at, entry.name);
1845
+ const full = join7(at, entry.name);
1705
1846
  if (entry.isDirectory() && depth < MAX_DEPTH) await walk(full, depth + 1);
1706
1847
  if (!entry.isFile() || !TEXT_FILES.has(extname(entry.name).toLowerCase())) continue;
1707
1848
  const path2 = relative(dir, full).split(sep).join("/");
@@ -2432,9 +2573,9 @@ function handleStream(req, res, sm, sessionId) {
2432
2573
 
2433
2574
  // src/session-manager.ts
2434
2575
  import { randomUUID } from "crypto";
2435
- import { mkdirSync as mkdirSync6, rmSync as rmSync4 } from "fs";
2436
- import { tmpdir as tmpdir4 } from "os";
2437
- import { join as join7 } from "path";
2576
+ import { mkdirSync as mkdirSync6 } from "fs";
2577
+ import { tmpdir as tmpdir5 } from "os";
2578
+ import { join as join8 } from "path";
2438
2579
 
2439
2580
  // src/llm/llm-session.ts
2440
2581
  var LlmSession = class {
@@ -2919,7 +3060,7 @@ var SessionManager = class {
2919
3060
  * files (CLAUDE.md, repo) to act on.
2920
3061
  */
2921
3062
  allocateSandbox(sessionId) {
2922
- const dir = join7(tmpdir4(), `leuria-${sessionId}`);
3063
+ const dir = join8(tmpdir5(), `leuria-${sessionId}`);
2923
3064
  mkdirSync6(dir, { recursive: true });
2924
3065
  return dir;
2925
3066
  }
@@ -2931,14 +3072,13 @@ var SessionManager = class {
2931
3072
  session.live = void 0;
2932
3073
  this.options.webMcpServer.removeChannel(session.id);
2933
3074
  setTimeout(() => this.sessions.delete(session.id), FORGET_AFTER_MS).unref();
2934
- try {
2935
- rmSync4(session.cwd, { recursive: true, force: true });
2936
- } catch (err) {
2937
- this.options.logger.warn("failed to remove sandbox", {
3075
+ removeScratch(
3076
+ session.cwd,
3077
+ (err) => this.options.logger.warn("failed to remove sandbox", {
2938
3078
  cwd: session.cwd,
2939
3079
  err: err instanceof Error ? err.message : String(err)
2940
- });
2941
- }
3080
+ })
3081
+ );
2942
3082
  }
2943
3083
  notify(session, event, data) {
2944
3084
  for (const listener of session.listeners) {
package/dist/cli.js CHANGED
@@ -40,7 +40,7 @@ import {
40
40
  signIn,
41
41
  startEngine,
42
42
  updateAgents
43
- } from "./chunk-JOCM56MT.js";
43
+ } from "./chunk-JBVFUXX5.js";
44
44
 
45
45
  // src/cli.ts
46
46
  import { spawn } from "child_process";
@@ -551,8 +551,10 @@ function createAdminHandler(ctx) {
551
551
  const result = await signIn(signingInAgent, {
552
552
  signal: controller.signal,
553
553
  methodId: typeof body.methodId === "string" ? body.methodId : void 0,
554
- // The app has no terminal: only agent methods (the agent runs its own flow).
554
+ // The app has no terminal: agent methods (the agent runs its own flow), or
555
+ // a terminal-only sign-in (Claude) in a window Leuria opens.
555
556
  terminal: false,
557
+ window: body.window === true,
556
558
  // Offered in the app too, in case the browser did not come up.
557
559
  onUrl: (url) => ctx.emit({ event: "signin_url", url }),
558
560
  onProgress: (message) => ctx.emit({ event: "progress", stage: "signin", message })
@@ -717,6 +719,7 @@ function createAdminHandler(ctx) {
717
719
  // `message` is for the CLI; the app words each stage itself (no jargon for visitors).
718
720
  onStep: (message) => ctx.emit({ event: "progress", stage: "check", step: ++checkStep, message })
719
721
  });
722
+ if (!result.ok) ctx.logger.warn("check failed", { agent, steps: result.steps.filter((step) => !step.ok) });
720
723
  sendJson(res, 200, result);
721
724
  return true;
722
725
  }
package/dist/index.d.ts CHANGED
@@ -921,6 +921,11 @@ interface SignInStatus {
921
921
  methods: AgentInfo["authMethods"];
922
922
  /** Models the agent offers once signed in (from `session/new`), with the visitor's choice as `current`. */
923
923
  models?: AgentModels;
924
+ /**
925
+ * The agent signs in only on the command line (Claude): Leuria can open a
926
+ * terminal window for it (`signIn(id, { window: true })`).
927
+ */
928
+ window?: boolean;
924
929
  }
925
930
  /**
926
931
  * Is the visitor signed in to this agent? Opens (and closes) a real ACP session, within a time limit.
@@ -934,6 +939,8 @@ declare function signIn(id: string, options?: {
934
939
  methodId?: string;
935
940
  /** The caller can run an interactive terminal method (a TTY). */
936
941
  terminal?: boolean;
942
+ /** Run the agent's terminal sign-in in a new terminal window (the desktop app has no terminal). */
943
+ window?: boolean;
937
944
  onProgress?: (message: string) => void;
938
945
  /** Cancel a sign-in in progress: the agent is stopped (which frees its login callback). */
939
946
  signal?: AbortSignal;
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  resolveAgentCommand,
19
19
  signIn,
20
20
  startEngine
21
- } from "./chunk-JOCM56MT.js";
21
+ } from "./chunk-JBVFUXX5.js";
22
22
  export {
23
23
  DEFAULT_PORT,
24
24
  GrantStore,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leuria/cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Bring your own AI to the web: the Leuria engine. Lets websites you approve use the AI on your machine, through tools the page provides.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",