summrise-agent 1.2.454 → 1.2.456

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/bin/summrise.js CHANGED
@@ -38,6 +38,8 @@ exports.BOOT_TASKS = exports.psq = void 0;
38
38
  exports.psArgv = psArgv;
39
39
  exports.deskShortcutRepairPs = deskShortcutRepairPs;
40
40
  exports.startDesktopPs = startDesktopPs;
41
+ exports.desktopTaskPs = desktopTaskPs;
42
+ exports.desktopStartPs = desktopStartPs;
41
43
  exports.parseAgentPort = parseAgentPort;
42
44
  exports.parseDeviceToken = parseDeviceToken;
43
45
  exports.parseTargetArg = parseTargetArg;
@@ -62,6 +64,9 @@ exports.newestOf = newestOf;
62
64
  exports.latestReleaseVersion = latestReleaseVersion;
63
65
  exports.cdnBase = cdnBase;
64
66
  exports.componentUrl = componentUrl;
67
+ exports.componentKey = componentKey;
68
+ exports.componentPins = componentPins;
69
+ exports.sha256File = sha256File;
65
70
  exports.resolveComponent = resolveComponent;
66
71
  exports.ensureElectron = ensureElectron;
67
72
  exports.statusReport = statusReport;
@@ -302,6 +307,45 @@ function startDesktopPs(deskDirQ) {
302
307
  `& "$dir\\node_modules\\electron\\dist\\electron.exe" .`,
303
308
  ];
304
309
  }
310
+ /** The desktop shell's watchdog pair + its task, as ONE PowerShell script.
311
+ *
312
+ * WHY IT IS A FILE AND NOT AN INLINE -Command: it has to write two other files whose
313
+ * contents contain quotes, and every attempt to do that inside one command string turns
314
+ * into quoting hell — the first version of this shipped as a hand-written file for that
315
+ * reason. `summrise setup` writes it and `summrise desktop` runs it, so a machine that
316
+ * never ran setup can still get a working task by asking for the window. Idempotent:
317
+ * ensure-desktop.ps1 exits when electron is already alive, so the 5-minute trigger never
318
+ * steals focus. unit-tested. */
319
+ function desktopTaskPs(installQ) {
320
+ return [
321
+ "# written by `summrise setup` / `summrise desktop` -- the desktop shell's task + watchdog.",
322
+ "$ErrorActionPreference = 'Stop'",
323
+ `$q = '${installQ}'`,
324
+ `$en = Join-Path $q 'scripts\\ensure-desktop.ps1'`,
325
+ `$vb = Join-Path $q 'scripts\\desktop-pulse.vbs'`,
326
+ `Set-Content -Path $en -Value ('if (Get-Process electron -ErrorAction SilentlyContinue) { exit }; & powershell -NoProfile -ExecutionPolicy Bypass -File "' + $q + '\\scripts\\start-desktop.ps1"') -Force`,
327
+ `Set-Content -Path $vb -Value ('CreateObject("WScript.Shell").Run "powershell -NoProfile -ExecutionPolicy Bypass -File " & Chr(34) & "' + $en + '" & Chr(34), 0, False') -Force`,
328
+ `$da = New-ScheduledTaskAction -Execute 'wscript.exe' -Argument ('"' + $vb + '"') -WorkingDirectory $q`,
329
+ `$dt1 = New-ScheduledTaskTrigger -AtLogOn`,
330
+ `$dw1 = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(3) -RepetitionInterval (New-TimeSpan -Minutes 5)`,
331
+ `$pr = New-ScheduledTaskPrincipal -UserId ('{0}\\{1}' -f $env:USERDOMAIN, $env:USERNAME) -LogonType Interactive -RunLevel Highest`,
332
+ `$st = New-ScheduledTaskSettingsSet -ExecutionTimeLimit (New-TimeSpan -Minutes 10) -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew`,
333
+ `Register-ScheduledTask SummriseDesktop -Action $da -Trigger @($dt1,$dw1) -Principal $pr -Settings $st -Force | Out-Null`,
334
+ `Start-ScheduledTask -TaskName SummriseDesktop`,
335
+ ];
336
+ }
337
+ /** Ask whether the shell is up, and start it if it is not — printing ONE WORD the caller
338
+ * can trust: `already-running`, `started`, or `not-started`. A command that says "ok"
339
+ * without distinguishing those three is how an operator ends up staring at a desktop
340
+ * wondering whether anything happened. Pure; the CLI runs it. */
341
+ function desktopStartPs(installQ) {
342
+ return [
343
+ `if (Get-Process electron -ErrorAction SilentlyContinue) { Write-Output 'already-running'; exit 0 }`,
344
+ `& powershell -NoProfile -ExecutionPolicy Bypass -File '${installQ}\\scripts\\ensure-desktop.ps1'`,
345
+ `Start-Sleep -Seconds 3`,
346
+ `if (Get-Process electron -ErrorAction SilentlyContinue) { Write-Output 'started' } else { Write-Output 'not-started'; exit 1 }`,
347
+ ].join("; ");
348
+ }
305
349
  // exported: agent bind port plumbing (custom-port installs). server.port
306
350
  // out of <dir>/config.yaml (first `port:` under top-level `server:`),
307
351
  // canonical 18080 when absent/invalid/missing (fresh installs have no
@@ -810,6 +854,37 @@ function cdnBase() {
810
854
  function componentUrl(name) {
811
855
  return `${cdnBase()}/summrise-agent/${name}`;
812
856
  }
857
+ /** The release manifest's key for a component's FILE name (they are not the same string). */
858
+ function componentKey(fileName) {
859
+ if (fileName.startsWith("summrise-playwright"))
860
+ return "playwright";
861
+ if (fileName.startsWith("electron-"))
862
+ return "electron";
863
+ if (fileName.startsWith("cloudflared"))
864
+ return "cloudflared";
865
+ return null; // a file this release does not pin (e.g. fix-tunnel.ps1)
866
+ }
867
+ /** The manifest's component pins, or {} when it carries none (an older release). */
868
+ function componentPins() {
869
+ const r = (0, child_process_1.spawnSync)("curl", ["-s", "-m", "5", `${cdnBase()}/api/version`], {
870
+ encoding: "utf8",
871
+ timeout: 8000,
872
+ });
873
+ if (r.status !== 0 || !r.stdout)
874
+ return {};
875
+ try {
876
+ const j = JSON.parse(r.stdout);
877
+ const c = j && typeof j.components === "object" ? j.components : null;
878
+ return c || {};
879
+ }
880
+ catch {
881
+ return {};
882
+ }
883
+ }
884
+ /** sha256 of a file. Read whole: the electron runtime is 115 MB and node's heap is fine with it. */
885
+ function sha256File(p) {
886
+ return crypto.createHash("sha256").update(fs.readFileSync(p)).digest("hex");
887
+ }
813
888
  /**
814
889
  * A boxed component's local path: the copy inside the package if it is there,
815
890
  * otherwise the release host's route for it, downloaded to a temp file.
@@ -837,6 +912,27 @@ function resolveComponent(name, pkgPath) {
837
912
  if (r.status !== 0 || !fs.existsSync(dest) || fs.statSync(dest).size === 0) {
838
913
  return null;
839
914
  }
915
+ // VERIFY WHAT WAS FETCHED (grilling Q4). Until this existed, a worker serving
916
+ // different bytes would have been staged without complaint — the components are
917
+ // executed or loaded, so "the host said so" is not an anchor. The manifest that
918
+ // /api/version serves carries a sha256 per component (index/components.json,
919
+ // copied in at publish time); a pin that does not match REFUSES the component,
920
+ // because the failure it prevents is a device running bytes nobody published.
921
+ // No pin at all (an older release) is a warning, not a refusal: it must not make
922
+ // an install impossible.
923
+ const key = componentKey(name);
924
+ const pin = key ? (componentPins()[key] || {}).sha256 || "" : "";
925
+ if (pin) {
926
+ const got = sha256File(dest);
927
+ if (got !== pin) {
928
+ console.error(`setup: REFUSING ${name} — sha256 ${got.slice(0, 12)}… does not match the release manifest's ${pin.slice(0, 12)}… (the host served different bytes than it published)`);
929
+ return null;
930
+ }
931
+ console.log(`setup: ${name} verified against the release manifest (${pin.slice(0, 12)}…)`);
932
+ }
933
+ else if (key) {
934
+ console.log(`setup: ${name} fetched WITHOUT a manifest pin — not verified`);
935
+ }
840
936
  console.log(`setup: ${name} fetched from the release host (not in the package)`);
841
937
  return dest;
842
938
  }
@@ -1638,6 +1734,53 @@ const commands = {
1638
1734
  console.log(elDist
1639
1735
  ? "setup: electron runtime ready for the desktop shell"
1640
1736
  : "setup: WARNING -- the electron runtime could not be obtained (not in the package, and the release host did not serve it); start-desktop.ps1 will not open a window until it can be fetched.");
1737
+ // ── Q9: leave a `summrise` COMMAND behind ──────────────────────────────────
1738
+ // `npx summrise-agent setup` installs the service and nothing puts the CLI on
1739
+ // PATH, so the very commands this output recommends (`summrise update`,
1740
+ // `summrise status`) are command-not-found — measured on d1 on 2026-09-23.
1741
+ // Installing THIS version by name keeps it deterministic and uses npm's own
1742
+ // shims rather than hand-written ones; when npm cannot, say the command.
1743
+ {
1744
+ const selfVer = String(require("../package.json").version || "");
1745
+ const pfx = (0, child_process_1.spawnSync)("npm", ["prefix", "-g"], { encoding: "utf8", shell: true });
1746
+ const pre = pfx.status === 0 ? String(pfx.stdout || "").trim() : "";
1747
+ if (selfVer && pre) {
1748
+ const inst = (0, child_process_1.spawnSync)("npm", [
1749
+ "i",
1750
+ "-g",
1751
+ `summrise-agent@${selfVer}`,
1752
+ "--prefix",
1753
+ pre,
1754
+ "--registry=https://registry.npmjs.org/",
1755
+ "--no-audit",
1756
+ "--no-fund",
1757
+ ], { encoding: "utf8", shell: true, timeout: 300000 });
1758
+ console.log(inst.status === 0
1759
+ ? `setup: ${selfVer} installed globally -- \`summrise status\` works from any shell`
1760
+ : `setup: WARNING -- could not install the CLI globally; run: npm i -g summrise-agent@${selfVer}`);
1761
+ }
1762
+ else {
1763
+ console.log(`setup: WARNING -- could not resolve npm's global prefix; run: npm i -g summrise-agent@${selfVer || "<version>"}`);
1764
+ }
1765
+ }
1766
+ // ── the desktop shell's task: ONLOGON + a 5-minute watchdog ────────────────
1767
+ // `summrise autostart` has always said "the desktop task comes from the
1768
+ // installer", and the NSIS installer is RETIRED — so NOTHING created it. Measured
1769
+ // on d1: the window vanished with the console that launched it and
1770
+ // SummriseDesktop did not exist at all. The shape is the app's own, written to a
1771
+ // .ps1 so it can be read rather than escaped into one unreadable line:
1772
+ // ensure-desktop.ps1 exits when electron is already alive (the watchdog never
1773
+ // steals focus), the .vbs wrapper runs it with no console flash, and the 5-minute
1774
+ // repetition is what reborns a dead shell.
1775
+ try {
1776
+ const reg = path.join(SCRIPTS_DIR, "register-desktop-task.ps1");
1777
+ fs.writeFileSync(reg, desktopTaskPs(DIR).join("\r\n") + "\r\n");
1778
+ sh(`powershell -NoProfile -ExecutionPolicy Bypass -File '${(0, exports.psq)(reg)}'`);
1779
+ console.log("setup: SummriseDesktop registered (logon + a 5-minute watchdog) and started");
1780
+ }
1781
+ catch {
1782
+ console.log(`setup: WARNING -- could not register SummriseDesktop; run: powershell -File "${SCRIPTS_DIR}\\register-desktop-task.ps1"`);
1783
+ }
1641
1784
  // Layout v2: write the start-desktop.ps1 launcher into scripts\ (the
1642
1785
  // SummriseDesktop onlogon task + desktop Summrise.lnk both call it). Was never
1643
1786
  // written before — a real gap that left the shell unlaunchable.
@@ -1836,6 +1979,41 @@ const commands = {
1836
1979
  },
1837
1980
  // Live view: redraw in place every few seconds until Ctrl+C. `summrise watch
1838
1981
  // <host:port[/path]>` narrows it to one target and adds its outage log.
1982
+ // `summrise desktop` = put the window back, or say it is already there.
1983
+ //
1984
+ // WHY IT EXISTS. The shell is normally started by the SummriseDesktop task, but the LAUNCHER
1985
+ // dies with the console that started it — measured twice on 2026-09-23, both times arriving
1986
+ // as "electron 又不见了" — and until now there was no command to ask for it, only a .ps1 path
1987
+ // an operator had to know. Idempotent by construction (the task's ensure-desktop.ps1 exits
1988
+ // when electron is alive, so a running shell is never disturbed), and it distinguishes the
1989
+ // three outcomes. "ok" without saying WHICH is how an operator ends up staring at a desktop.
1990
+ desktop() {
1991
+ const reg = path.join(SCRIPTS_DIR, "register-desktop-task.ps1");
1992
+ fs.mkdirSync(SCRIPTS_DIR, { recursive: true });
1993
+ if (!fs.existsSync(reg)) {
1994
+ // A machine that never ran setup still gets a working task: the same script setup
1995
+ // writes, from the same builder, so the two cannot drift.
1996
+ fs.writeFileSync(reg, desktopTaskPs(DIR).join("\r\n") + "\r\n");
1997
+ }
1998
+ try {
1999
+ sh(`powershell -NoProfile -ExecutionPolicy Bypass -File '${(0, exports.psq)(reg)}'`);
2000
+ }
2001
+ catch {
2002
+ /* the task may already exist and be healthy; the check below is the verdict */
2003
+ }
2004
+ const r = (0, child_process_1.spawnSync)("powershell", ["-NoProfile", "-Command", desktopStartPs(DIR)], { encoding: "utf8", shell: true });
2005
+ const out = String(r.stdout || "").trim();
2006
+ if (out.endsWith("already-running")) {
2007
+ console.log("desktop: the Summrise shell is ALREADY RUNNING (look for the window, or the tray icon)");
2008
+ return;
2009
+ }
2010
+ if (out.endsWith("started")) {
2011
+ console.log("desktop: started -- the window should be on your desktop now");
2012
+ return;
2013
+ }
2014
+ console.error(`desktop: the shell did NOT come up. Try it directly to see why: powershell -File "${path.join(SCRIPTS_DIR, "start-desktop.ps1")}"`);
2015
+ process.exit(1);
2016
+ },
1839
2017
  status() {
1840
2018
  // NOT via shell: `shell: true` concatenates argv into one cmd.exe string,
1841
2019
  // so the unquoted filter "IMAGENAME eq …" was split at its spaces, tasklist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "summrise-agent",
3
- "version": "1.2.454",
3
+ "version": "1.2.456",
4
4
  "description": "Summrise Agent — Windows device MCP server (terminal/SSH/serial) + panel. Self-hosted npm distribution.",
5
5
  "//build": "唯一构建路径:在本目录执行 npm run build。它是自包含的:经 npm exec 按需拉取 typescript@5,无需预装任何 node_modules(干净检出可跑;旧脚本引用 ../summrise-desktop-electron/node_modules/.bin/tsc,干净检出必 ENOENT)。必须用 npm exec --package … -- tsc 的形式:npx 会吃掉传给 tsc 的 -p/--outDir 等参数(round-435:npx 写法从未真正跑通过)。先编 electron TS(--noCheck 纯 emit,与 release.yml 一致)同步两处 src/*.js,再编 src/summrise.ts 到 bin/summrise.js 并校验 round-298 标记(弱 oracle,只证某次构建发生,新鲜性由 publish-release.sh 的 tsc 重编 + cmp 门保证)。",
6
6
  "bin": {
Binary file