@corenel/cli 0.4.2 → 0.4.4

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.
Files changed (2) hide show
  1. package/dist/cli.js +241 -75
  2. package/package.json +5 -5
package/dist/cli.js CHANGED
@@ -18931,42 +18931,58 @@ function credentialFilePath(stateDir) {
18931
18931
  }
18932
18932
  var TOKEN_ENV = "CORENEL_TOKEN";
18933
18933
  var API_BASE_ENV = "CORENEL_API_BASE";
18934
- async function writeAuthToken(token, stateDir) {
18935
- const dir = credentialDir(stateDir);
18936
- const path = tokenFilePath(stateDir);
18937
- await fs2.mkdir(dir, { recursive: true, mode: 448 });
18938
- await fs2.writeFile(path, token.trim() + "\n", { mode: 384 });
18939
- try {
18940
- await fs2.chmod(path, 384);
18941
- } catch {
18942
- }
18943
- try {
18944
- await fs2.chmod(dir, 448);
18945
- } catch {
18946
- }
18947
- }
18948
18934
  async function writeLoginCredential(token, opts = {}) {
18949
18935
  const cred = {
18950
18936
  accessToken: token.trim(),
18951
18937
  ...opts.account ? { account: opts.account } : {},
18952
18938
  ...opts.apiBase ? { apiBase: opts.apiBase } : {}
18953
18939
  };
18954
- const path = credentialFilePath(opts.stateDir);
18955
- await fs2.mkdir(credentialDir(opts.stateDir), { recursive: true, mode: 448 });
18956
- await fs2.writeFile(path, `${JSON.stringify(cred, null, 2)}
18940
+ const dir = credentialDir(opts.stateDir);
18941
+ const credPath = credentialFilePath(opts.stateDir);
18942
+ const tokPath = tokenFilePath(opts.stateDir);
18943
+ await fs2.mkdir(dir, { recursive: true, mode: 448 });
18944
+ const credTmp = `${credPath}.tmp-${process.pid.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
18945
+ const tokTmp = `${tokPath}.tmp-${process.pid.toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
18946
+ await fs2.writeFile(credTmp, `${JSON.stringify(cred, null, 2)}
18947
+ `, { mode: 384 });
18948
+ await fs2.writeFile(tokTmp, `${cred.accessToken}
18957
18949
  `, { mode: 384 });
18958
18950
  try {
18959
- await fs2.chmod(path, 384);
18951
+ await fs2.chmod(credTmp, 384);
18960
18952
  } catch {
18961
18953
  }
18962
- await writeAuthToken(cred.accessToken, opts.stateDir);
18954
+ try {
18955
+ await fs2.chmod(tokTmp, 384);
18956
+ } catch {
18957
+ }
18958
+ try {
18959
+ await fs2.rename(credTmp, credPath);
18960
+ await fs2.rename(tokTmp, tokPath);
18961
+ } catch (e) {
18962
+ try {
18963
+ await fs2.rm(credTmp, { force: true });
18964
+ } catch {
18965
+ }
18966
+ try {
18967
+ await fs2.rm(tokTmp, { force: true });
18968
+ } catch {
18969
+ }
18970
+ throw e;
18971
+ }
18972
+ try {
18973
+ await fs2.chmod(dir, 448);
18974
+ } catch {
18975
+ }
18976
+ }
18977
+ function isMissing(err) {
18978
+ return typeof err === "object" && err !== null && err.code === "ENOENT";
18963
18979
  }
18964
- async function readAuthToken(stateDir) {
18980
+ async function readAuthTokenRead(stateDir) {
18965
18981
  try {
18966
18982
  const t = (await fs2.readFile(tokenFilePath(stateDir), "utf8")).trim();
18967
- return t || null;
18968
- } catch {
18969
- return null;
18983
+ return { value: t || null, unreadable: false };
18984
+ } catch (err) {
18985
+ return { value: null, unreadable: !isMissing(err) };
18970
18986
  }
18971
18987
  }
18972
18988
  async function clearAuthToken(stateDir) {
@@ -18975,27 +18991,30 @@ async function clearAuthToken(stateDir) {
18975
18991
  } catch {
18976
18992
  }
18977
18993
  }
18978
- async function readCredentialFile(stateDir) {
18994
+ async function readCredentialFileRead(stateDir) {
18979
18995
  let raw;
18980
18996
  try {
18981
18997
  raw = await fs2.readFile(credentialFilePath(stateDir), "utf8");
18982
- } catch {
18983
- return null;
18998
+ } catch (err) {
18999
+ return { value: null, unreadable: !isMissing(err) };
18984
19000
  }
18985
19001
  try {
18986
19002
  const parsed = JSON.parse(raw);
18987
- if (typeof parsed !== "object" || parsed === null) return null;
19003
+ if (typeof parsed !== "object" || parsed === null) return { value: null, unreadable: false };
18988
19004
  const c = parsed;
18989
- if (typeof c.accessToken !== "string" || !c.accessToken) return null;
19005
+ if (typeof c.accessToken !== "string" || !c.accessToken) return { value: null, unreadable: false };
18990
19006
  return {
18991
- accessToken: c.accessToken,
18992
- ...typeof c.refreshToken === "string" ? { refreshToken: c.refreshToken } : {},
18993
- ...typeof c.expiresAt === "number" ? { expiresAt: c.expiresAt } : {},
18994
- ...typeof c.account === "string" ? { account: c.account } : {},
18995
- ...typeof c.apiBase === "string" && c.apiBase ? { apiBase: c.apiBase } : {}
19007
+ value: {
19008
+ accessToken: c.accessToken,
19009
+ ...typeof c.refreshToken === "string" ? { refreshToken: c.refreshToken } : {},
19010
+ ...typeof c.expiresAt === "number" ? { expiresAt: c.expiresAt } : {},
19011
+ ...typeof c.account === "string" ? { account: c.account } : {},
19012
+ ...typeof c.apiBase === "string" && c.apiBase ? { apiBase: c.apiBase } : {}
19013
+ },
19014
+ unreadable: false
18996
19015
  };
18997
19016
  } catch {
18998
- return null;
19017
+ return { value: null, unreadable: false };
18999
19018
  }
19000
19019
  }
19001
19020
  async function clearAllCredentials(stateDir) {
@@ -19011,20 +19030,29 @@ async function resolveNodeCredential(opts = {}) {
19011
19030
  const stateDir = opts.stateDir;
19012
19031
  const fromEnv = env[TOKEN_ENV]?.trim();
19013
19032
  if (fromEnv) return { token: fromEnv, source: "env" };
19014
- const cred = await readCredentialFile(stateDir);
19015
- const fileToken = await readAuthToken(stateDir);
19033
+ const credRead = await readCredentialFileRead(stateDir);
19034
+ const tokenRead = await readAuthTokenRead(stateDir);
19035
+ const cred = credRead.value;
19036
+ const fileToken = tokenRead.value;
19037
+ const unreadable = credRead.unreadable || tokenRead.unreadable ? { unreadable: true } : {};
19016
19038
  if (fileToken) {
19017
19039
  const same = cred && cred.accessToken === fileToken ? cred : null;
19018
- return withExpiry({ token: fileToken, source: "token-file", path: tokenFilePath(stateDir) }, same, now);
19040
+ return {
19041
+ ...withExpiry({ token: fileToken, source: "token-file", path: tokenFilePath(stateDir) }, same, now),
19042
+ ...unreadable
19043
+ };
19019
19044
  }
19020
19045
  if (cred) {
19021
- return withExpiry(
19022
- { token: cred.accessToken, source: "credential-file", path: credentialFilePath(stateDir) },
19023
- cred,
19024
- now
19025
- );
19046
+ return {
19047
+ ...withExpiry(
19048
+ { token: cred.accessToken, source: "credential-file", path: credentialFilePath(stateDir) },
19049
+ cred,
19050
+ now
19051
+ ),
19052
+ ...unreadable
19053
+ };
19026
19054
  }
19027
- return { token: null, source: "none" };
19055
+ return { token: null, source: "none", ...unreadable };
19028
19056
  }
19029
19057
  function withExpiry(base, cred, now) {
19030
19058
  if (!cred) return base;
@@ -22162,24 +22190,35 @@ import { PermissionService as PermissionService4 } from "@corenel/harness/guardr
22162
22190
  import { ROOT_NODE_ID } from "@corenel/protocol";
22163
22191
  import { makeSpawn } from "@corenel/harness/core/spawn";
22164
22192
  function inMemoryMemory() {
22165
- const items = [];
22166
- let n = 0;
22193
+ const items = /* @__PURE__ */ new Map();
22167
22194
  return {
22168
- async save(text, tags = []) {
22169
- const it = { id: `m${++n}`, text, tags, createdAt: n };
22170
- items.push(it);
22195
+ async save(input) {
22196
+ const it = {
22197
+ id: input.name,
22198
+ name: input.name,
22199
+ type: input.type,
22200
+ description: input.description,
22201
+ text: input.body,
22202
+ tags: [],
22203
+ createdAt: Date.now()
22204
+ };
22205
+ items.set(input.name, it);
22171
22206
  return it;
22172
22207
  },
22208
+ async read(name) {
22209
+ return items.get(name) ?? null;
22210
+ },
22173
22211
  async recall(q) {
22174
- return items.filter((i) => i.text.includes(q));
22212
+ return [...items.values()].filter((i) => i.text.includes(q) || i.description.includes(q));
22175
22213
  },
22176
22214
  async list() {
22177
- return [...items];
22215
+ return [...items.values()];
22178
22216
  },
22179
- async forget(id) {
22180
- const i = items.findIndex((x) => x.id === id);
22181
- if (i >= 0) items.splice(i, 1);
22217
+ async forget(name) {
22218
+ items.delete(name);
22182
22219
  }
22220
+ // No core(): this stub has no tiers, and Task 9 injects nothing rather than
22221
+ // pretending it has a memory block.
22183
22222
  };
22184
22223
  }
22185
22224
  function inMemoryKv() {
@@ -22211,7 +22250,7 @@ function previewWrite(files) {
22211
22250
  }
22212
22251
  function nodeToolCtx(signal, opts = {}) {
22213
22252
  const base = {
22214
- memory: inMemoryMemory(),
22253
+ memory: opts.memory ?? inMemoryMemory(),
22215
22254
  kv: inMemoryKv(),
22216
22255
  search: nullSearch,
22217
22256
  confirm: async () => true,
@@ -22252,6 +22291,42 @@ function tokenGuard(getToken) {
22252
22291
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
22253
22292
  var MIN_POLL_MS = 1e3;
22254
22293
  var DEFAULT_LIFETIME_S = 900;
22294
+ var ACCOUNT_LOOKUP_MS = 5e3;
22295
+ function deadlineSignal(ms, outer) {
22296
+ const ac = new AbortController();
22297
+ const onOuter = () => ac.abort();
22298
+ const timer = setTimeout(() => ac.abort(), ms);
22299
+ if (outer) {
22300
+ if (outer.aborted) ac.abort();
22301
+ else outer.addEventListener("abort", onOuter, { once: true });
22302
+ }
22303
+ return {
22304
+ signal: ac.signal,
22305
+ dispose: () => {
22306
+ clearTimeout(timer);
22307
+ outer?.removeEventListener("abort", onOuter);
22308
+ }
22309
+ };
22310
+ }
22311
+ async function whoIsThis(base, token, f, outer, deadlineMs = ACCOUNT_LOOKUP_MS) {
22312
+ const { signal, dispose } = deadlineSignal(deadlineMs, outer);
22313
+ try {
22314
+ const res = await f(`${base}/auth/me`, {
22315
+ headers: { Authorization: `Bearer ${token}` },
22316
+ signal
22317
+ });
22318
+ if (!res.ok) return void 0;
22319
+ const body = JSON.parse(await res.text());
22320
+ const email = body?.user?.email;
22321
+ if (typeof email !== "string") return void 0;
22322
+ const trimmed = email.trim();
22323
+ return trimmed ? trimmed : void 0;
22324
+ } catch {
22325
+ return void 0;
22326
+ } finally {
22327
+ dispose();
22328
+ }
22329
+ }
22255
22330
  function defaultOpen(uri, code2) {
22256
22331
  process.stdout.write(`
22257
22332
  To authorize, open: ${uri}
@@ -22300,7 +22375,9 @@ async function deviceLogin(opts) {
22300
22375
  client_id: "corenel-cli"
22301
22376
  });
22302
22377
  if (tok.access_token) {
22303
- await opts.store(tok.access_token);
22378
+ const claimed = typeof tok.account === "string" ? tok.account.trim() : "";
22379
+ const account = claimed || await whoIsThis(base, tok.access_token, f, opts.signal, opts.accountLookupMs);
22380
+ await opts.store(tok.access_token, account || void 0);
22304
22381
  return tok.access_token;
22305
22382
  }
22306
22383
  if (tok.error === "authorization_pending") continue;
@@ -22450,6 +22527,41 @@ function isValidAgentName(name) {
22450
22527
  // ../crew/store.ts
22451
22528
  var import_yaml3 = __toESM(require_dist(), 1);
22452
22529
  import { flattenFiles as flattenFiles3 } from "@corenel/protocol";
22530
+
22531
+ // ../crew/sessionBinding.ts
22532
+ var SESSION_NAME_RE = /^[a-z0-9_-]{1,32}$/;
22533
+ function isValidSessionName(name) {
22534
+ return SESSION_NAME_RE.test(name);
22535
+ }
22536
+ function parseSessionBinding(meta) {
22537
+ const raw = meta.session;
22538
+ if (raw === void 0) return { kind: "absent" };
22539
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
22540
+ return { kind: "malformed", reason: "session: must be a block with `name:` and `workdir:`" };
22541
+ }
22542
+ const o = raw;
22543
+ const name = typeof o.name === "string" ? o.name : "";
22544
+ const workdir = typeof o.workdir === "string" ? o.workdir.trim() : "";
22545
+ if (!isValidSessionName(name)) {
22546
+ return {
22547
+ kind: "malformed",
22548
+ reason: `session.name ${JSON.stringify(name)} must be 1-32 characters of a-z, 0-9, "-" or "_"`
22549
+ };
22550
+ }
22551
+ if (!workdir) return { kind: "malformed", reason: "session.workdir is required" };
22552
+ return {
22553
+ kind: "binding",
22554
+ binding: {
22555
+ name,
22556
+ workdir,
22557
+ // Anything but an explicit `false` is true, matching how `enabled` is read
22558
+ // in store.ts — absent means the default, and the default is on.
22559
+ createIfMissing: o.createIfMissing !== false
22560
+ }
22561
+ };
22562
+ }
22563
+
22564
+ // ../crew/store.ts
22453
22565
  var TRIGGER_TYPES = [
22454
22566
  "manual",
22455
22567
  "file-change",
@@ -22535,6 +22647,7 @@ async function loadCrewAgentAt(files, root, name, scope = "workspace") {
22535
22647
  if (!isValidAgentName(name)) throw new Error(`invalid crew agent name: ${name}`);
22536
22648
  const dir = `${root}/${name}`;
22537
22649
  const { meta, body } = parseFrontmatter(await readOr(files, `${dir}/AGENT.md`));
22650
+ const session = parseSessionBinding(meta);
22538
22651
  return {
22539
22652
  name,
22540
22653
  jobTitle: typeof meta.jobTitle === "string" ? meta.jobTitle : "",
@@ -22549,7 +22662,9 @@ async function loadCrewAgentAt(files, root, name, scope = "workspace") {
22549
22662
  policy: parsePolicyYaml(await readOr(files, `${dir}/POLICY.yaml`), name),
22550
22663
  settings: parseSettings(await readOr(files, `${dir}/settings.json`, "{}")),
22551
22664
  budget: parseBudget(meta),
22552
- canCall: parseCanCall(meta)
22665
+ canCall: parseCanCall(meta),
22666
+ ...session.kind === "binding" ? { session: session.binding } : {},
22667
+ ...session.kind === "malformed" ? { sessionError: session.reason } : {}
22553
22668
  };
22554
22669
  }
22555
22670
  async function loadCrewAgentAtIfExists(files, root, name, scope = "workspace") {
@@ -22566,7 +22681,10 @@ async function loadCrewAgentAtIfExists(files, root, name, scope = "workspace") {
22566
22681
  // ../crew/runtime.ts
22567
22682
  import { saveCustomPolicy } from "@corenel/harness/guardrail";
22568
22683
  import { allTools } from "@corenel/harness/tools/builtins";
22684
+ import { registerSyncConfig, readConfigSyncRendered } from "@corenel/harness/prompts/syncConfig";
22569
22685
  import { makeCallAgentTool } from "@corenel/harness/tools/callAgent";
22686
+ var MEMORY_BLOCK = registerSyncConfig("crew/memory.md");
22687
+ var SELF_DIRECTION_BLOCK = registerSyncConfig("crew/self-direction.md");
22570
22688
  function buildSystemExtra(def, memoryLocation) {
22571
22689
  const parts = [];
22572
22690
  if (def.jobTitle?.trim()) parts.push(`--- Role ---
@@ -22580,11 +22698,7 @@ ${def.instructions.trim()}`);
22580
22698
  const location = memoryLocation ?? "memory/";
22581
22699
  const dir = location.replace(/\/+$/, "");
22582
22700
  const index = `${dir}/MEMORY.md`;
22583
- parts.push(
22584
- `--- Memory ---
22585
- You keep durable notes in your own memory folder, \`${dir}/\`, one Markdown file per note, alongside \`${index}\`, an index you maintain for yourself.
22586
- Read \`${index}\` at the start of a run to recall what you already know, and call recall_memory to look up one note by name. If you can call save_memory, use it for what is worth knowing on a later run, and keep \`${index}\` current as you do. If a file tool you have takes a \`root\` argument, you can pass \`${dir}/\` as \`root\` to read and edit those files directly.`
22587
- );
22701
+ parts.push(readConfigSyncRendered(MEMORY_BLOCK, { dir, index }));
22588
22702
  const selfDirection = selfDirectionBlock(def);
22589
22703
  if (selfDirection) parts.push(selfDirection);
22590
22704
  return parts.join("\n\n");
@@ -22595,23 +22709,58 @@ function selfDirectionBlock(def) {
22595
22709
  const raw = trigger.config?.fallbackMs;
22596
22710
  const fallbackMs = typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : null;
22597
22711
  const consequence = fallbackMs == null ? "If this run ends without that call, nothing will wake it again and your work stops here until someone starts you by hand." : `If this run ends without that call you will be woken anyway, about ${Math.round(fallbackMs / 6e4)} minutes from now -- but that is a backstop for a run that could not schedule itself, not a substitute for choosing when you should next run.`;
22598
- return `--- Self-direction ---
22599
- You run on your own schedule. You will not run again unless you call \`schedule_self\` with the time you should next wake, before this run ends.
22600
- ${consequence}
22601
- So decide, before you finish, whether there is more to do and when it should happen.`;
22712
+ return readConfigSyncRendered(SELF_DIRECTION_BLOCK, { consequence });
22602
22713
  }
22603
22714
 
22604
22715
  // ../crew/memory.ts
22605
- import { flattenFiles as flattenFiles4 } from "@corenel/protocol";
22606
- import { parseFact, renderFact, slugify, uniqueSlug, FACT_INDEX } from "@corenel/harness/core/factFile";
22716
+ import { makeFactStore } from "@corenel/harness/memory/store";
22717
+ function agentMemoryRoot(crewRoot, agentName) {
22718
+ return `${crewRoot}/${agentName}/memory`;
22719
+ }
22720
+ function makeAgentMemoryProvider(getFiles, memoryRoot) {
22721
+ const store = makeFactStore(getFiles, memoryRoot);
22722
+ const toItem = (f) => ({
22723
+ id: f.name,
22724
+ name: f.name,
22725
+ type: f.type,
22726
+ description: f.description,
22727
+ text: f.body,
22728
+ tags: [],
22729
+ createdAt: f.created
22730
+ });
22731
+ return {
22732
+ async save(input) {
22733
+ return toItem(await store.save(input));
22734
+ },
22735
+ async read(name) {
22736
+ const hit = await store.read(name);
22737
+ return hit ? toItem(hit) : null;
22738
+ },
22739
+ async recall(query, limit) {
22740
+ return (await store.recall(query, limit)).map(toItem);
22741
+ },
22742
+ async list(limit) {
22743
+ return (await store.list(limit)).map(toItem);
22744
+ },
22745
+ async forget(name) {
22746
+ return store.forget(name);
22747
+ },
22748
+ async core() {
22749
+ const block = await store.core();
22750
+ return { items: block.facts.map(toItem), archivedCount: block.archivedCount };
22751
+ }
22752
+ };
22753
+ }
22607
22754
 
22608
22755
  // ../crew/room/store.ts
22609
- import { flattenFiles as flattenFiles5 } from "@corenel/protocol";
22756
+ import { flattenFiles as flattenFiles4 } from "@corenel/protocol";
22610
22757
  import { encodeLine, foldSession } from "@corenel/harness/sessions/jsonl";
22611
22758
  import { FINDINGS_FILE, encodeFindingLine, foldFindings } from "@corenel/harness/rooms/findings";
22612
22759
 
22613
22760
  // ../crew/room/prompt.ts
22761
+ import { registerSyncConfig as registerSyncConfig2, readConfigSyncRendered as readConfigSyncRendered2 } from "@corenel/harness/prompts/syncConfig";
22614
22762
  import { MAX_ROOM_FINDINGS, renderFindings } from "@corenel/harness/rooms/findings";
22763
+ var ROOM_BLOCK = registerSyncConfig2("crew/room.md");
22615
22764
 
22616
22765
  // ../crew/room/turn.ts
22617
22766
  import { readRoomOutcome } from "@corenel/harness/tools/roomTools";
@@ -22641,6 +22790,12 @@ function workspaceStateDir() {
22641
22790
  const fromEnv = process.env.CORENEL_STATE_DIR;
22642
22791
  return fromEnv && /^\.[A-Za-z0-9][A-Za-z0-9._-]*$/.test(fromEnv) ? fromEnv : stateDirName3();
22643
22792
  }
22793
+ function guestCrewRoot() {
22794
+ return `${workspaceStateDir()}/${CREW_SEG}`;
22795
+ }
22796
+ function guestAgentMemoryRoot(agent) {
22797
+ return agentMemoryRoot(guestCrewRoot(), agent);
22798
+ }
22644
22799
  function mutatingCeiling(tools, defaultLevel = "deny") {
22645
22800
  const overrides = {};
22646
22801
  for (const t of tools) if (!t.mutates) overrides[toolAddress(t)] = "allow";
@@ -22656,7 +22811,9 @@ function eventLine(e) {
22656
22811
  }
22657
22812
  async function runCrewAgentCli(opts) {
22658
22813
  const files = new NodeFileService(opts.cwd);
22659
- const root = `${workspaceStateDir()}/${CREW_SEG}`;
22814
+ const root = guestCrewRoot();
22815
+ const memoryRoot = guestAgentMemoryRoot(opts.agent);
22816
+ const memory = makeAgentMemoryProvider(() => files, memoryRoot);
22660
22817
  const def = await loadCrewAgentAtIfExists(files, root, opts.agent, "workspace");
22661
22818
  if (!def) {
22662
22819
  opts.warn(`no such agent "${opts.agent}" under ${opts.cwd}/${root}`);
@@ -22674,7 +22831,7 @@ async function runCrewAgentCli(opts) {
22674
22831
  }
22675
22832
  registerGuard(tokenGuard(() => token));
22676
22833
  const tools = allTools2();
22677
- const extra = buildSystemExtra(def);
22834
+ const extra = buildSystemExtra(def, memoryRoot);
22678
22835
  let system = extra;
22679
22836
  try {
22680
22837
  const base = await composeAgentSystem({
@@ -22717,7 +22874,7 @@ ${extra}` : base;
22717
22874
  // Cause-aware refusal text -- see cli.ts's `run()` for why the kernel's
22718
22875
  // own "denied by the user" default would be a lie on this path.
22719
22876
  describePermissionDenial: nonInteractiveDenialMessage,
22720
- toolCtx: nodeToolCtx(ac.signal, { files }),
22877
+ toolCtx: nodeToolCtx(ac.signal, { files, memory }),
22721
22878
  policy: effectivePolicy,
22722
22879
  signal: ac.signal,
22723
22880
  onEvent: (e) => opts.emit(eventLine(e))
@@ -23729,9 +23886,14 @@ function isOnValue(value) {
23729
23886
  return value === void 0 || !FALSY_FLAG_VALUES.has(value.toLowerCase());
23730
23887
  }
23731
23888
  function hasBooleanFlag(argv, name) {
23732
- for (const tok of argv) {
23733
- const parsed = classifyToken(tok);
23734
- if (!parsed || parsed.name !== name) continue;
23889
+ for (let i = 0; i < argv.length; i++) {
23890
+ const parsed = classifyToken(argv[i]);
23891
+ if (!parsed) continue;
23892
+ if (parsed.value === void 0 && !isBooleanFlag(parsed.name)) {
23893
+ i++;
23894
+ continue;
23895
+ }
23896
+ if (parsed.name !== name) continue;
23735
23897
  if (isOnValue(parsed.value)) return true;
23736
23898
  }
23737
23899
  return false;
@@ -24245,10 +24407,14 @@ async function main() {
24245
24407
  }
24246
24408
  case "login": {
24247
24409
  const base = await apiBase(flag(rest, "base", ""));
24248
- await deviceLogin({ base, store: (token) => writeLoginCredential(token, { apiBase: base }) });
24410
+ await deviceLogin({
24411
+ base,
24412
+ store: (token, account) => writeLoginCredential(token, { apiBase: base, ...account ? { account } : {} })
24413
+ });
24249
24414
  process.stdout.write(`
24250
24415
  Logged in. Token saved to ${authTokenPath()}
24251
24416
  `);
24417
+ process.stdout.write("If a sidecar daemon is already running, restart it to sign it in as well.\n");
24252
24418
  break;
24253
24419
  }
24254
24420
  case "auth":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corenel/cli",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "type": "module",
5
5
  "description": "Corenel CLI — runs the harness in node, in-proc, no transport. The headless proof the kernel is host-agnostic. (corenel run/ask/chat/login + start --sidecar to follow.)",
6
6
  "bin": {
@@ -9,9 +9,9 @@
9
9
  "dependencies": {
10
10
  "openai": "^4.77.0",
11
11
  "ws": "^8.18.0",
12
- "@corenel/harness": "0.6.0",
13
- "@corenel/protocol": "0.6.0",
14
- "@corenel/term": "0.2.2"
12
+ "@corenel/harness": "0.6.1",
13
+ "@corenel/term": "0.2.3",
14
+ "@corenel/protocol": "0.6.1"
15
15
  },
16
16
  "devDependencies": {
17
17
  "esbuild": "^0.21.5",
@@ -19,7 +19,7 @@
19
19
  "@types/ws": "^8.5.13",
20
20
  "tsx": "^4.19.2",
21
21
  "typescript": "^5.9.3",
22
- "@corenel/tools-node": "0.3.0",
22
+ "@corenel/tools-node": "0.3.1",
23
23
  "@corenel/crew": "0.0.0"
24
24
  },
25
25
  "publishConfig": {