@tiens.nguyen/gu-cli 1.0.841 → 1.0.842

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.
@@ -324,86 +324,6 @@ export function findDevInstalls({ roots = browserRoots(), extDir = extensionDir(
324
324
  return found;
325
325
  }
326
326
 
327
- // THE PUBLISHED EXTENSION, FOUND IN CHROME'S OWN RECORDS (#386, GitHub #176). Its ID is fixed by the
328
- // store (the last part of the store URL), so a profile has it when its preferences list that ID
329
- // from anywhere but an unpacked folder. Disabled is told apart: it is installed but cannot connect.
330
- export const STORE_EXTENSION_ID = "ldfcjapmdbgdkdhlbdkbkiankneabagj";
331
-
332
- export function findStoreInstalls({ roots = browserRoots(), id = STORE_EXTENSION_ID } = {}) {
333
- const found = [];
334
- for (const { browser, dir } of roots) {
335
- let profiles = [];
336
- try {
337
- profiles = readdirSync(dir).filter((p) => p === "Default" || /^Profile \d+$/.test(p));
338
- } catch {
339
- continue;
340
- }
341
- for (const profile of profiles) {
342
- for (const file of ["Secure Preferences", "Preferences"]) {
343
- let e;
344
- try {
345
- e = JSON.parse(readFileSync(join(dir, profile, file), "utf8"))?.extensions?.settings?.[id];
346
- } catch {
347
- continue;
348
- }
349
- if (!e || UNPACKED.has(e.location)) continue;
350
- const reasons = Array.isArray(e.disable_reasons) ? e.disable_reasons.length : Number(e.disable_reasons) || 0;
351
- found.push({ browser, profile, id, disabled: e.state === 0 || reasons > 0 });
352
- break;
353
- }
354
- }
355
- }
356
- return found;
357
- }
358
-
359
- /**
360
- * Where the install stands, and what to tell the user — PURE, so every stage can be tested.
361
- * → { stage, lines }: "remove-dev" (a developer copy would block the store one, #148), "add" (not
362
- * in Chrome yet), "enable" (added but switched off), "connect" (added, not connected yet), "done".
363
- */
364
- export function installStage({ dev = [], store = [], status = {}, storeUrl = STORE_URL } = {}) {
365
- if (status.connected && status.installType === "normal") {
366
- return { stage: "done", lines: [`✓ gu browser is connected (Chrome Web Store, v${status.version ?? "?"}).`] };
367
- }
368
- if (dev.length) {
369
- return {
370
- stage: "remove-dev",
371
- lines: [
372
- "1. First remove the developer copy of gu browser — only one can connect.",
373
- " In chrome://extensions (gu opened it), click Remove on this one:",
374
- ...dev.map((d) => ` ${d.browser} · ${d.profile} · ID ${d.id}`),
375
- " Waiting for it to be removed…",
376
- ],
377
- };
378
- }
379
- if (!store.length) {
380
- return {
381
- stage: "add",
382
- lines: [
383
- "1. Add gu browser to Chrome: on its store page, click \"Add to Chrome\".",
384
- ` ${storeUrl}`,
385
- " (gu opened the page for you.) Waiting for it to be added…",
386
- ],
387
- };
388
- }
389
- if (store.every((s) => s.disabled)) {
390
- return {
391
- stage: "enable",
392
- lines: [
393
- "2. gu browser is added but switched off. In chrome://extensions, turn it on.",
394
- " Waiting for it to be turned on…",
395
- ],
396
- };
397
- }
398
- return {
399
- stage: "connect",
400
- lines: [
401
- "2. gu browser is added. Waiting for it to connect to gu…",
402
- " Keep Chrome open — it connects within 30 s, or click its icon → Connect now.",
403
- ],
404
- };
405
- }
406
-
407
327
  // gu's own leftovers of the developer install, in ~/.gonext.
408
328
  export function localLeftovers() {
409
329
  return [extensionDir(), statePath(), seenPath(), logPath()].filter((p) => existsSync(p));
package/gu-repl.mjs CHANGED
@@ -139,7 +139,7 @@ import {
139
139
  import { completedFlush, splitLiveStream } from "./completed-flush.mjs";
140
140
  import { isScaffoldStep, isGroupableStep, stepGroupMark, stepGroupRows, relativizePaths } from "./step-group.mjs";
141
141
  import { clearLogsNoticeText } from "./clear-logs.mjs";
142
- import { allowIn, allowedIn, bridgeCommand, browserReport, bridgeStatus, extensionDir, findDevInstalls, helpInstall, installExtensionFiles, installSteps, localLeftovers, findStoreInstalls, installStage, openExtensionsPage, openStorePage, removeLocalFiles, STORE_URL, stopBridge } from "./browser-command.mjs";
142
+ import { allowIn, allowedIn, bridgeCommand, browserReport, bridgeStatus, extensionDir, findDevInstalls, helpInstall, installExtensionFiles, installSteps, localLeftovers, openExtensionsPage, removeLocalFiles, STORE_URL, stopBridge } from "./browser-command.mjs";
143
143
  import { commandLogFile, killShell, logTail, markAnnounced, readShells, recapQuestion, shellsToRecap, statusText as shellStatusText, renderShellDetails, shellPanelRows, shellPickerLabel, shellState, shellsNotice } from "./shell-details.mjs";
144
144
  import { discoverSkills, matchSkillCommand } from "./skills.mjs";
145
145
  import { appendNew, archivePathFor, archivedWorkspaceKey, markReset, seedArchive } from "./conversation-archive.mjs";
@@ -9620,100 +9620,30 @@ function printShellDetails(rec) {
9620
9620
  // /browser (#363): the extension's connection, else how to install it.
9621
9621
  async function showBrowser() {
9622
9622
  const st = await bridgeStatus().catch(() => ({ running: false, connected: false }));
9623
- const lines = browserReport(st, { allowed: allowedIn(resolve(process.cwd())), storeInstalled: findStoreInstalls().length > 0 });
9623
+ const lines = browserReport(st, { allowed: allowedIn(resolve(process.cwd())) });
9624
9624
  console.log("");
9625
9625
  for (const [i, l] of lines.entries()) console.log(GUTTER + (i === 0 && st.connected ? accent(l) : l.startsWith("⚠") ? yellow(l) : dim(l)));
9626
9626
  console.log("");
9627
9627
  paintBar();
9628
9628
  }
9629
9629
 
9630
- // /browser install (#150; guided since #386, GitHub #176): the published extension, for the agent to
9631
- // use YOUR Chrome — the way Claude Code sets up its Chrome extension. gu walks it through to the end:
9632
- // a developer copy is removed first (it would block the store one, #148), then the store page is
9633
- // opened, and the panel in place of the input box (#384) shows each stage live until the store copy
9634
- // is CONNECTED — or Esc stops waiting. gu's own window still needs none of this.
9630
+ // /browser install (#150): the extension, for the agent to use YOUR Chrome. Optional — gu's own
9631
+ // window needs nothing. The store page once published; until then the developer way.
9635
9632
  async function browserInstall() {
9636
- if (listPickerActive || approvalActive) return;
9637
- const done = (line) => {
9638
- console.log("\n" + GUTTER + line + "\n");
9639
- paintBar();
9640
- };
9633
+ let folder = extensionDir();
9641
9634
  if (!STORE_URL) {
9642
- // No published listing configured (GU_BROWSER_STORE_URL=""): the developer way, as before.
9643
- let folder = extensionDir();
9644
9635
  try {
9645
9636
  folder = installExtensionFiles();
9646
9637
  } catch (e) {
9647
9638
  console.log(GUTTER + yellow(`could not copy the extension to ${folder}: ${e.message}`));
9648
9639
  }
9649
- await bridgeStatus().catch(() => null);
9650
- console.log("");
9651
- for (const l of installSteps(folder, process.platform, "")) console.log(GUTTER + dim(l));
9652
- if (helpInstall(folder)) console.log(GUTTER + dim("(Opened chrome://extensions and copied the folder to your clipboard.)"));
9653
- console.log("");
9654
- paintBar();
9655
- return;
9656
9640
  }
9657
- let status = await bridgeStatus().catch(() => ({ running: false, connected: false }));
9658
- const opened = new Set();
9659
- const state = () => {
9660
- const st = installStage({ dev: findDevInstalls(), store: findStoreInstalls(), status });
9661
- // Open the page each stage needs, once.
9662
- if (st.stage === "remove-dev" && !opened.has("ext")) { opened.add("ext"); openExtensionsPage(); }
9663
- if (st.stage === "add" && !opened.has("store")) { opened.add("store"); openStorePage(); }
9664
- if (st.stage === "enable" && !opened.has("ext2")) { opened.add("ext2"); openExtensionsPage(); }
9665
- return st;
9666
- };
9667
- let st = state();
9668
- if (st.stage === "done") return done(accent(st.lines[0]));
9669
- if (!barPinned()) {
9670
- // No zone to wait in: say what to do once.
9671
- console.log("");
9672
- for (const l of st.lines) console.log(GUTTER + dim(l));
9673
- console.log(GUTTER + dim("Then type /browser — it should say connected.") + "\n");
9674
- paintBar();
9675
- return;
9676
- }
9677
- let polling = false;
9678
- const poll = setInterval(async () => {
9679
- if (polling) return;
9680
- polling = true;
9681
- try {
9682
- status = await bridgeStatus({ waitMs: 0 }).catch(() => status);
9683
- st = state();
9684
- if (st.stage === "done") {
9685
- clearInterval(poll);
9686
- closeZonePanel();
9687
- done(accent(st.lines[0]) + dim(" The agent can use your Chrome now."));
9688
- }
9689
- } finally {
9690
- polling = false;
9691
- }
9692
- }, 2000);
9693
- openZonePanel({
9694
- refreshMs: 1000,
9695
- rows: (width) => {
9696
- // A row the panel would clip is WRAPPED instead: the store link is 84 characters, and a link
9697
- // cut short at 80 columns cannot be copied.
9698
- const room = Math.max(20, width - 1 - GUTTER.length);
9699
- const wrap = (l) => {
9700
- const out = [];
9701
- for (let rest = l; rest.length; rest = rest.slice(room)) out.push(rest.slice(0, room));
9702
- return out.length ? out : [""];
9703
- };
9704
- return [
9705
- GUTTER + bold(accent("Install gu browser")) + dim(" — so the agent can use your own Chrome"),
9706
- "",
9707
- ...st.lines.flatMap((l) => wrap(/https?:\/\//.test(l) ? l.trim() : l).map((r) => GUTTER + (/^\d\./.test(l) ? r : dim(r)))),
9708
- "",
9709
- GUTTER + dim("Esc to stop waiting · /browser install picks up where you left off"),
9710
- ];
9711
- },
9712
- onClose: () => {
9713
- clearInterval(poll);
9714
- if (st.stage !== "done") done(dim("(stopped waiting — /browser install picks up where you left off)"));
9715
- },
9716
- });
9641
+ await bridgeStatus().catch(() => null); // running, so a fresh install has something to connect to
9642
+ console.log("");
9643
+ for (const l of installSteps(folder)) console.log(GUTTER + dim(l));
9644
+ if (STORE_URL ? openExtensionsPage() : helpInstall(folder)) console.log(GUTTER + dim(STORE_URL ? "(Opened Chrome.)" : "(Opened chrome://extensions and copied the folder to your clipboard.)"));
9645
+ console.log("");
9646
+ paintBar();
9717
9647
  }
9718
9648
 
9719
9649
  // /browser cleanup (#145, #147): remove the developer (unpacked) install so the Chrome Web Store
@@ -10830,11 +10760,7 @@ async function approvalPrompt(command) {
10830
10760
  process.stdout.write(
10831
10761
  "\n" + yellow("🌐 Allow gu to use a web browser?") + dim(" (no answer in ~3 min = Yes, this time)") + "\n" +
10832
10762
  dim(` It wants to: ${what || "open a page"}`) + "\n" +
10833
- dim(" In your Chrome if the gu extension is there, else in gu's own Chrome window.") + "\n" +
10834
- // #386: the way to "your Chrome", said where the question is asked.
10835
- (STORE_URL && !findStoreInstalls().length
10836
- ? dim(" To use your own Chrome (your logins): /browser install adds the gu extension.") + "\n"
10837
- : "")
10763
+ dim(" In your Chrome if the gu extension is there, else in gu's own Chrome window.") + "\n"
10838
10764
  );
10839
10765
  // "Yes, this time" is THE default (#380, GitHub #167): highlighted, so Enter allows it once, and
10840
10766
  // also what an unanswered question becomes — the countdown used to sit on "No", which read as
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tiens.nguyen/gu-cli",
3
- "version": "1.0.841",
3
+ "version": "1.0.842",
4
4
  "description": "Gu CLI — the gu terminal plus the local worker that runs agent / OCR / PDF / embedding jobs on your Mac (Ollama / MLX / OpenAI-compatible).",
5
5
  "type": "module",
6
6
  "license": "MIT",