@opentig/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.
Files changed (120) hide show
  1. package/dist/bin.mjs +58 -7
  2. package/dist/chunks/{cli-bEPXlEpH.mjs → cli-DaYybBfs.mjs} +234 -170
  3. package/dist/chunks/cli-service-CDd3ZvGV.mjs +237 -0
  4. package/dist/client/assets/App-C7Yf3r-l.js +118 -0
  5. package/dist/client/assets/{CreatePullRequestDialog-NPqtWmYL.js → CreatePullRequestDialog-CGnY6nrw.js} +2 -2
  6. package/dist/client/assets/{DiffWorkspace-CZH8jCtn.js → DiffWorkspace-1ZAh7eFX.js} +6 -6
  7. package/dist/client/assets/EditableFileViewer-DOnxE6ty.js +1 -0
  8. package/dist/client/assets/{File-C25AAfq1.js → File-DM0fcPqi.js} +1 -1
  9. package/dist/client/assets/{FilesView-C6qX7ixR.js → FilesView-BuTZiUd4.js} +1 -1
  10. package/dist/client/assets/{GitHubAvatar-DV6lI4Jh.js → GitHubAvatar-BIZ8KlZ9.js} +1 -1
  11. package/dist/client/assets/HistoryView-BTo2h2ee.js +1 -0
  12. package/dist/client/assets/{HtmlFileViewer-CiPZFmIx.js → HtmlFileViewer-tEtrcHdG.js} +1 -1
  13. package/dist/client/assets/{IconEdit-DZPhGn6H.js → IconEdit-DtVD7NDP.js} +1 -1
  14. package/dist/client/assets/{IconFolderPlus-CE1ATVt-.js → IconFolderPlus-D5cPTAyD.js} +1 -1
  15. package/dist/client/assets/ImageFileViewer-C56bQ88S.js +1 -0
  16. package/dist/client/assets/{LocalRefsDialog-DIFaZUiQ.js → LocalRefsDialog-DwhpiSyu.js} +1 -1
  17. package/dist/client/assets/{MarkdownFileViewer-Y9tvTC3N.js → MarkdownFileViewer-BxQfoNnS.js} +2 -2
  18. package/dist/client/assets/{PairingPage-rJKAimg3.js → PairingPage-Cu1KraeJ.js} +1 -1
  19. package/dist/client/assets/{PierreDiffViewer-BRQK_cuH.js → PierreDiffViewer-B9IrAdjM.js} +1 -1
  20. package/dist/client/assets/PierreEditBoundary-DDJlRbaP.js +2 -0
  21. package/dist/client/assets/{PierreWorkerPool-CRVJ90kq.js → PierreWorkerPool-BJhmuZSy.js} +1 -1
  22. package/dist/client/assets/PullRequestDiff-Di1_Wv1o.js +1 -0
  23. package/dist/client/assets/PullRequestViewer-CuH0Ze12.js +2 -0
  24. package/dist/client/assets/PullRequestsView-DRKuy94k.js +1 -0
  25. package/dist/client/assets/{QuickOpenDialog-CciIWIcs.js → QuickOpenDialog-DSdrgS2H.js} +1 -1
  26. package/dist/client/assets/RepositoryProjectsDialog-CWcakQRC.js +1 -0
  27. package/dist/client/assets/SearchView-Btqv-BZZ.js +1 -0
  28. package/dist/client/assets/{SettingsDialog-B0XwVWYT.js → SettingsDialog-CV7hRbtK.js} +2 -2
  29. package/dist/client/assets/Viewer-bchsRyMu.js +3 -0
  30. package/dist/client/assets/{abnfDiagram-N423BO3Z-DRTxjQR7.js → abnfDiagram-N423BO3Z-D6JAOs-X.js} +1 -1
  31. package/dist/client/assets/{arc-DlZI_By6.js → arc-YncG-7Bc.js} +1 -1
  32. package/dist/client/assets/{architectureDiagram-T3A2C74G-BpcQblyb.js → architectureDiagram-T3A2C74G-Ckf8dhax.js} +1 -1
  33. package/dist/client/assets/base-ui-D4MeJpa3.js +1 -0
  34. package/dist/client/assets/{blockDiagram-VBNYF7ZC-BF55JK7V.js → blockDiagram-VBNYF7ZC-CGQa9zHO.js} +1 -1
  35. package/dist/client/assets/{c4Diagram-5PPSVZJV-CkQ8c1Fd.js → c4Diagram-5PPSVZJV-Ctgat2YZ.js} +1 -1
  36. package/dist/client/assets/channel-B31JCSE_.js +1 -0
  37. package/dist/client/assets/checkbox-D5IGxs1d.js +1 -0
  38. package/dist/client/assets/{chunk-2GRJ4B5K-BsVfgXSJ.js → chunk-2GRJ4B5K-CyDC0FQq.js} +1 -1
  39. package/dist/client/assets/{chunk-2Q5K7J3B-hYvR7LDf.js → chunk-2Q5K7J3B-DqYRVN1X.js} +1 -1
  40. package/dist/client/assets/{chunk-5RXB4S5H-B2TcxiXR.js → chunk-5RXB4S5H-BtrE-Jqe.js} +1 -1
  41. package/dist/client/assets/{chunk-5VM5RSS4-TDJFncVJ.js → chunk-5VM5RSS4-CEVyxEEH.js} +1 -1
  42. package/dist/client/assets/{chunk-6Q2QTUOP-BDdSa8Zj.js → chunk-6Q2QTUOP-CR_RRfTb.js} +1 -1
  43. package/dist/client/assets/{chunk-GF5L2VYU-CUHPmM86.js → chunk-GF5L2VYU-Uoc_Vpub.js} +1 -1
  44. package/dist/client/assets/{chunk-JWPE2WC7-Pq05eTDs.js → chunk-JWPE2WC7-BAhDzsCx.js} +1 -1
  45. package/dist/client/assets/{chunk-KBJHAD2P-QSlaIrVy.js → chunk-KBJHAD2P-DkGEUlle.js} +1 -1
  46. package/dist/client/assets/{chunk-RYQCIY6F-C0P7HEQg.js → chunk-RYQCIY6F-doFXXAJb.js} +1 -1
  47. package/dist/client/assets/{chunk-XXDRQBXY-C_oHIOR7.js → chunk-XXDRQBXY-B44pCuJx.js} +1 -1
  48. package/dist/client/assets/classDiagram-JCYQIIEL-C6tU-sLq.js +1 -0
  49. package/dist/client/assets/classDiagram-v2-OCEON4UE-C6tU-sLq.js +1 -0
  50. package/dist/client/assets/{cose-bilkent-JH36ORCC-Du6jXfCO.js → cose-bilkent-JH36ORCC-CymnJ1xN.js} +1 -1
  51. package/dist/client/assets/{cynefin-VYW2F7L2-CTUvB8CJ.js → cynefin-VYW2F7L2-BT5dARHp.js} +1 -1
  52. package/dist/client/assets/{cynefinDiagram-MW4NZA55-BLRqmW6C.js → cynefinDiagram-MW4NZA55-DVrhZ1A4.js} +1 -1
  53. package/dist/client/assets/{dagre-VZM6K2ZE-DTAlXx2k.js → dagre-VZM6K2ZE-BET0T1KQ.js} +1 -1
  54. package/dist/client/assets/{diagram-7IWD3JNH-Coh1c4kx.js → diagram-7IWD3JNH-mIl4bvgD.js} +1 -1
  55. package/dist/client/assets/{diagram-B4RE2ZJO-TahBzLDX.js → diagram-B4RE2ZJO-DKSi3zok.js} +1 -1
  56. package/dist/client/assets/{diagram-LBJQPF4R-2uwXPdqa.js → diagram-LBJQPF4R-DFzvqR_V.js} +1 -1
  57. package/dist/client/assets/{diagram-Q27KOJAE-Bm4UNuoF.js → diagram-Q27KOJAE-C8UUiNSc.js} +1 -1
  58. package/dist/client/assets/{diagram-UB23O5K3-dWga-ccy.js → diagram-UB23O5K3-CYuQnggB.js} +1 -1
  59. package/dist/client/assets/{ebnfDiagram-BXEA7PRR-D0IK6JUi.js → ebnfDiagram-BXEA7PRR-orsOZEt3.js} +1 -1
  60. package/dist/client/assets/edit-ready-context-D77R8XU8.js +1 -0
  61. package/dist/client/assets/{erDiagram-JOGREHBK-DouKd_mX.js → erDiagram-JOGREHBK-BPxNY3Or.js} +1 -1
  62. package/dist/client/assets/{flowDiagram-UKHOOZJN-DQQX82AJ.js → flowDiagram-UKHOOZJN-Dj_tbpt_.js} +1 -1
  63. package/dist/client/assets/{ganttDiagram-PKOTCBZU-CbvAYRQc.js → ganttDiagram-PKOTCBZU-BhG_1m3O.js} +1 -1
  64. package/dist/client/assets/{gh-utils-XOjL-WGw.js → gh-utils-CR5JRkW2.js} +1 -1
  65. package/dist/client/assets/{gitGraphDiagram-DS77QQ5N-D7cqbpF-.js → gitGraphDiagram-DS77QQ5N-B6i118FC.js} +1 -1
  66. package/dist/client/assets/{index-Bm8fY0Qs.css → index-CE9Jcx4e.css} +1 -1
  67. package/dist/client/assets/index-C_D1kOlX.js +3 -0
  68. package/dist/client/assets/{infoDiagram-6WML65LV-C0NfpA-J.js → infoDiagram-6WML65LV-DhHBZSVb.js} +1 -1
  69. package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-DhIW58so.js → ishikawaDiagram-WSZJBQD7-6Vd6XOG_.js} +1 -1
  70. package/dist/client/assets/{journeyDiagram-NVQOT4AX-DqZZm_oH.js → journeyDiagram-NVQOT4AX-uH71A9mr.js} +1 -1
  71. package/dist/client/assets/{kanban-definition-27J2QSJJ-DpSYxBwt.js → kanban-definition-27J2QSJJ-DbxRwp0h.js} +1 -1
  72. package/dist/client/assets/{linear-DRxvRS4v.js → linear-CFzmLtpp.js} +1 -1
  73. package/dist/client/assets/{markdown-ChhkSSkz.css → markdown-BpzjXclZ.css} +1 -1
  74. package/dist/client/assets/{mermaid.core-Bz1ouG4e.js → mermaid.core-B_Mv_eHi.js} +4 -4
  75. package/dist/client/assets/{mindmap-definition-FAOFIHXS-ohtsO4nF.js → mindmap-definition-FAOFIHXS-DQf1s9U3.js} +1 -1
  76. package/dist/client/assets/{pegDiagram-VL7TDLO6-BmDfUXjC.js → pegDiagram-VL7TDLO6-BxFVTcDJ.js} +1 -1
  77. package/dist/client/assets/{pieDiagram-7S7Q4E2Y-CUADuvir.js → pieDiagram-7S7Q4E2Y-CCNbxGSk.js} +1 -1
  78. package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-BGGXYgGq.js → quadrantDiagram-CIZ2JOQS-BVhzcmm1.js} +1 -1
  79. package/dist/client/assets/{railroadDiagram-AXF67PYL-CuQocJeb.js → railroadDiagram-AXF67PYL-D3R2-3Og.js} +1 -1
  80. package/dist/client/assets/{render-markdown-BnNqckxy.js → render-markdown-DcHo7ihS.js} +6 -6
  81. package/dist/client/assets/{requirementDiagram-LRYGKXZP-7w9ufPrL.js → requirementDiagram-LRYGKXZP-B-NkzvnF.js} +1 -1
  82. package/dist/client/assets/{sankeyDiagram-W5VNT64P-DWDWhnZJ.js → sankeyDiagram-W5VNT64P-BRpIzFiP.js} +1 -1
  83. package/dist/client/assets/{sequenceDiagram-SI44F4Z6-BQAnExHF.js → sequenceDiagram-SI44F4Z6-B7cBRL6x.js} +1 -1
  84. package/dist/client/assets/{sizeCapture-X5ZJPWSS-BC2GpN9p.js → sizeCapture-X5ZJPWSS-BZ6DbjJ6.js} +1 -1
  85. package/dist/client/assets/{stateDiagram-OKZ733FA-vXwPDk5C.js → stateDiagram-OKZ733FA-x8ChPNdG.js} +1 -1
  86. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-BlYlvm69.js +1 -0
  87. package/dist/client/assets/{swimlanes-SLNWSIFB-DdBBGgwS.js → swimlanes-SLNWSIFB-DR21moSS.js} +2 -2
  88. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-DbXYPHRv.js +8 -0
  89. package/dist/client/assets/{tanstack-BEeM5REq.js → tanstack-Bsd5UOY4.js} +1 -1
  90. package/dist/client/assets/{timeline-definition-Z64GVDOM-CcXzq7ZH.js → timeline-definition-Z64GVDOM-UlAkswoH.js} +1 -1
  91. package/dist/client/assets/{vennDiagram-T6HMQDX7-PiBQGztV.js → vennDiagram-T6HMQDX7-91kBYEFr.js} +1 -1
  92. package/dist/client/assets/viewer-tabs-BHvOJKLx.js +1 -0
  93. package/dist/client/assets/{wardleyDiagram-T6FBY63Y-R9-CuOPS.js → wardleyDiagram-T6FBY63Y-CqAHibOn.js} +1 -1
  94. package/dist/client/assets/{xychartDiagram-ELKLHX3M-CrK1rvFm.js → xychartDiagram-ELKLHX3M-BMFs9l78.js} +1 -1
  95. package/dist/client/index.html +4 -4
  96. package/dist/dev.mjs +4 -3
  97. package/dist/manifest.json +1 -1
  98. package/dist/server.mjs +59 -3
  99. package/dist/service-update.mjs +77 -0
  100. package/package.json +1 -1
  101. package/dist/client/assets/App-1nMyZ0Eq.js +0 -118
  102. package/dist/client/assets/EditableFileViewer-4ap_dRdR.js +0 -1
  103. package/dist/client/assets/HistoryView-CgKY4Sjl.js +0 -1
  104. package/dist/client/assets/ImageFileViewer-RAXxHOBt.js +0 -1
  105. package/dist/client/assets/PierreEditBoundary-YYf1awXS.js +0 -2
  106. package/dist/client/assets/PullRequestDiff-BWdKV4LM.js +0 -1
  107. package/dist/client/assets/PullRequestViewer-6MhtdrJc.js +0 -2
  108. package/dist/client/assets/PullRequestsView-CJy2RRm7.js +0 -1
  109. package/dist/client/assets/RepositoryProjectsDialog-c_ZJ0g3C.js +0 -1
  110. package/dist/client/assets/SearchView-D5NpA3kT.js +0 -1
  111. package/dist/client/assets/Viewer-u1esMhLs.js +0 -3
  112. package/dist/client/assets/base-ui-BxmCGIFY.js +0 -1
  113. package/dist/client/assets/channel-D-dDbEF9.js +0 -1
  114. package/dist/client/assets/checkbox-vpy6b6Uq.js +0 -1
  115. package/dist/client/assets/classDiagram-JCYQIIEL-DHG_NsyB.js +0 -1
  116. package/dist/client/assets/classDiagram-v2-OCEON4UE-DHG_NsyB.js +0 -1
  117. package/dist/client/assets/index-DwuduZw_.js +0 -3
  118. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-BA-7Q81g.js +0 -1
  119. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-BSDBQ9JF.js +0 -8
  120. package/dist/client/assets/viewer-tabs-B_boC13j.js +0 -1
package/dist/bin.mjs CHANGED
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import process from "node:process";
3
- import { r as runCli } from "./chunks/cli-bEPXlEpH.mjs";
4
- import "node:child_process";
2
+ import process$1 from "node:process";
3
+ import { p as parseCliArguments, C as CliUsageError, r as runCli } from "./chunks/cli-DaYybBfs.mjs";
4
+ import { fileURLToPath } from "node:url";
5
+ import { spawn } from "node:child_process";
6
+ import { realpath } from "node:fs/promises";
7
+ import path from "node:path";
8
+ import { r as readInstallation, n as newerVersion, p as packageDirectory, O as OPEN_TIG_APP_VERSION } from "./chunks/cli-service-CDd3ZvGV.mjs";
5
9
  import "./server.mjs";
6
- import "node:url";
7
- import "node:path";
8
10
  import "node:crypto";
9
- import "node:fs/promises";
10
11
  import "node:string_decoder";
11
12
  import "node:util";
12
13
  import "node:tty";
@@ -25,4 +26,54 @@ import "fs";
25
26
  import "node:net";
26
27
  import "node:http";
27
28
  import "ws";
28
- process.exitCode = await runCli(process.argv.slice(2));
29
+ async function delegateToService(args, currentEntry) {
30
+ if (process.env.OPENTIG_DELEGATED === "1") return null;
31
+ let config;
32
+ try {
33
+ config = parseCliArguments(args);
34
+ } catch (error) {
35
+ if (error instanceof CliUsageError) return null;
36
+ throw error;
37
+ }
38
+ if (config.command === "help" || config.command === "version") {
39
+ const index = args.findIndex((arg) => arg === "--home" || arg.startsWith("--home="));
40
+ const home = index >= 0 ? args[index] === "--home" ? args[index + 1] : args[index].slice(7) : process.env.OPENTIG_HOME;
41
+ if (home) config.home = path.resolve(home);
42
+ }
43
+ const installed = await readInstallation(config.home);
44
+ if (!installed || !newerVersion(installed.version, OPEN_TIG_APP_VERSION)) return null;
45
+ const entry = path.join(packageDirectory(config.home, installed), "dist/bin.mjs");
46
+ if (await realpath(entry) === await realpath(currentEntry)) return null;
47
+ if (config.command === "version") {
48
+ process.stdout.write(`OpenTig service ${installed.version}
49
+ Global launcher ${OPEN_TIG_APP_VERSION}
50
+ `);
51
+ return 0;
52
+ }
53
+ return new Promise((resolve, reject) => {
54
+ const child = spawn(process.execPath, [entry, ...args], { stdio: "inherit", env: { ...process.env, OPENTIG_DELEGATED: "1" } });
55
+ const forward = (signal) => child.kill(signal);
56
+ const interrupt = () => forward("SIGINT"), terminate = () => forward("SIGTERM");
57
+ process.on("SIGINT", interrupt);
58
+ process.on("SIGTERM", terminate);
59
+ const clean = () => {
60
+ process.off("SIGINT", interrupt);
61
+ process.off("SIGTERM", terminate);
62
+ };
63
+ child.once("error", (error) => {
64
+ clean();
65
+ reject(error);
66
+ });
67
+ child.once("exit", (code, signal) => {
68
+ clean();
69
+ resolve(code ?? (signal === "SIGINT" ? 130 : 143));
70
+ });
71
+ });
72
+ }
73
+ try {
74
+ const args = process$1.argv.slice(2);
75
+ process$1.exitCode = await delegateToService(args, fileURLToPath(import.meta.url)) ?? await runCli(args);
76
+ } catch {
77
+ process$1.stderr.write("OpenTig: Could not launch the managed service version. Check the service installation.\n");
78
+ process$1.exitCode = 1;
79
+ }
@@ -3,9 +3,10 @@ import process$1 from "node:process";
3
3
  import { w as writeFileAtomically, r as redactSensitiveText, n as normalizeRuntimePlatform, a as applicationName, runOpenTigServer } from "../server.mjs";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
- import { access, rm, mkdir, cp, rename, writeFile, chmod, readFile, appendFile, stat } from "node:fs/promises";
7
- import { fileURLToPath } from "node:url";
8
- import { randomBytes } from "node:crypto";
6
+ import { r as readInstallation, s as systemctl, S as SERVICE_NAME, a as serviceUnitPath, p as packageDirectory, n as newerVersion, b as atomicWrite, e as execute, c as stableVersion, O as OPEN_TIG_APP_VERSION, m as manageCliService } from "./cli-service-CDd3ZvGV.mjs";
7
+ import { randomUUID, createHash, randomBytes } from "node:crypto";
8
+ import { realpath, readFile, mkdir, rm, mkdtemp, writeFile, rename, chmod, appendFile, stat } from "node:fs/promises";
9
+ import { setTimeout as setTimeout$1 } from "node:timers/promises";
9
10
  var QrCodeDataType = /* @__PURE__ */ ((QrCodeDataType2) => {
10
11
  QrCodeDataType2[QrCodeDataType2["Border"] = -1] = "Border";
11
12
  QrCodeDataType2[QrCodeDataType2["Data"] = 0] = "Data";
@@ -64,14 +65,14 @@ class QrCode {
64
65
  // error correction level, data codeword bytes, and mask number.
65
66
  // This is a low-level API that most users should not use directly.
66
67
  // A mid-level API is the encodeSegments() function.
67
- constructor(version2, ecc, dataCodewords, msk) {
68
- this.version = version2;
68
+ constructor(version, ecc, dataCodewords, msk) {
69
+ this.version = version;
69
70
  this.ecc = ecc;
70
- if (version2 < MIN_VERSION || version2 > MAX_VERSION)
71
+ if (version < MIN_VERSION || version > MAX_VERSION)
71
72
  throw new RangeError("Version value out of range");
72
73
  if (msk < -1 || msk > 7)
73
74
  throw new RangeError("Mask value out of range");
74
- this.size = version2 * 4 + 17;
75
+ this.size = version * 4 + 17;
75
76
  const row = Array.from({ length: this.size }).fill(false);
76
77
  for (let i = 0; i < this.size; i++) {
77
78
  this.modules.push(row.slice());
@@ -489,10 +490,10 @@ function isNumeric(text) {
489
490
  function isAlphanumeric(text) {
490
491
  return ALPHANUMERIC_REGEX.test(text);
491
492
  }
492
- function getTotalBits(segs, version2) {
493
+ function getTotalBits(segs, version) {
493
494
  let result = 0;
494
495
  for (const seg of segs) {
495
- const ccbits = numCharCountBits(seg.mode, version2);
496
+ const ccbits = numCharCountBits(seg.mode, version);
496
497
  if (seg.numChars >= 1 << ccbits)
497
498
  return Number.POSITIVE_INFINITY;
498
499
  result += 4 + ccbits + seg.bitData.length;
@@ -568,37 +569,37 @@ function encodeSegments(segs, ecl, minVersion = 1, maxVersion = 40, mask = -1, b
568
569
  if (!(MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= MAX_VERSION) || mask < -1 || mask > 7) {
569
570
  throw new RangeError("Invalid value");
570
571
  }
571
- let version2;
572
+ let version;
572
573
  let dataUsedBits;
573
- for (version2 = minVersion; ; version2++) {
574
- const dataCapacityBits2 = getNumDataCodewords(version2, ecl) * 8;
575
- const usedBits = getTotalBits(segs, version2);
574
+ for (version = minVersion; ; version++) {
575
+ const dataCapacityBits2 = getNumDataCodewords(version, ecl) * 8;
576
+ const usedBits = getTotalBits(segs, version);
576
577
  if (usedBits <= dataCapacityBits2) {
577
578
  dataUsedBits = usedBits;
578
579
  break;
579
580
  }
580
- if (version2 >= maxVersion)
581
+ if (version >= maxVersion)
581
582
  throw new RangeError("Data too long");
582
583
  }
583
584
  for (const newEcl of [MEDIUM, QUARTILE, HIGH]) {
584
- if (boostEcl && dataUsedBits <= getNumDataCodewords(version2, newEcl) * 8)
585
+ if (boostEcl && dataUsedBits <= getNumDataCodewords(version, newEcl) * 8)
585
586
  ecl = newEcl;
586
587
  }
587
588
  const bb = [];
588
589
  for (const seg of segs) {
589
590
  appendBits(seg.mode[0], 4, bb);
590
- appendBits(seg.numChars, numCharCountBits(seg.mode, version2), bb);
591
+ appendBits(seg.numChars, numCharCountBits(seg.mode, version), bb);
591
592
  for (const b of seg.getData())
592
593
  bb.push(b);
593
594
  }
594
- const dataCapacityBits = getNumDataCodewords(version2, ecl) * 8;
595
+ const dataCapacityBits = getNumDataCodewords(version, ecl) * 8;
595
596
  appendBits(0, Math.min(4, dataCapacityBits - bb.length), bb);
596
597
  appendBits(0, (8 - bb.length % 8) % 8, bb);
597
598
  for (let padByte = 236; bb.length < dataCapacityBits; padByte ^= 236 ^ 17)
598
599
  appendBits(padByte, 8, bb);
599
600
  const dataCodewords = Array.from({ length: Math.ceil(bb.length / 8) }, () => 0);
600
601
  bb.forEach((b, i) => dataCodewords[i >>> 3] |= b << 7 - (i & 7));
601
- return new QrCode(version2, ecl, dataCodewords, mask);
602
+ return new QrCode(version, ecl, dataCodewords, mask);
602
603
  }
603
604
  function encode(data, options) {
604
605
  const {
@@ -806,8 +807,8 @@ function parseCliArguments(args, environment = process.env, currentDirectory = p
806
807
  openBrowser: command === "start" && !noBrowser
807
808
  };
808
809
  }
809
- function cliHelp(version2) {
810
- return `OpenTig ${version2}
810
+ function cliHelp(version) {
811
+ return `OpenTig ${version}
811
812
 
812
813
  Usage:
813
814
  opentig [options]
@@ -897,166 +898,223 @@ function resolvePath(value, currentDirectory, label) {
897
898
  if (!value || value.includes("\0")) throw new CliUsageError(`Invalid ${label}.`);
898
899
  return path.resolve(currentDirectory, value);
899
900
  }
900
- const version = "0.1.2";
901
- const packageMetadata = {
902
- version
903
- };
904
- const OPEN_TIG_APP_VERSION = packageMetadata.version;
905
- const UNIT_NAME = "opentig.service";
906
- async function manageCliService(config, io) {
907
- if (process$1.platform !== "linux") {
908
- throw new Error("Background service management currently supports Linux with systemd. On this platform, start OpenTig with your preferred service manager.");
901
+ const DESKTOP_UPDATE_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
902
+ const REPOSITORY = "jorgefl8/opentig";
903
+ const REGISTRY = "https://registry.npmjs.org";
904
+ const MAX_PACKAGE_BYTES = 100 * 1024 * 1024;
905
+ class ServiceUpdater {
906
+ constructor(version, backend, failedUpdate = false) {
907
+ this.backend = backend;
908
+ this.status = {
909
+ phase: backend ? failedUpdate ? "error" : "idle" : "unavailable",
910
+ currentVersion: version,
911
+ availableVersion: null,
912
+ progress: null,
913
+ checkedAt: null,
914
+ releaseUrl: null,
915
+ releaseNotes: null,
916
+ message: backend ? failedUpdate ? "The new server could not start. The previous version was restored. Check for updates to retry." : null : "Browser updates require an OpenTig Linux service. Install with opentig service install; temporary CLI and Dev instances are updated from the terminal."
917
+ };
909
918
  }
910
- const action = config.serviceAction;
911
- if (!action) throw new Error("A service action is required.");
912
- const unitPath = path.join(os.homedir(), ".config", "systemd", "user", UNIT_NAME);
913
- if (action === "status") return serviceStatus(unitPath, io);
914
- if (action === "uninstall") return uninstallService(config.home, unitPath, io);
915
- return installService(config, unitPath, io);
916
- }
917
- async function installService(config, unitPath, io) {
918
- const packageRoot = await findPackageRoot(fileURLToPath(import.meta.url));
919
- await assertPackageBuild(packageRoot);
920
- const serviceRoot = path.join(config.home, "service");
921
- const target = path.join(serviceRoot, `app-${OPEN_TIG_APP_VERSION}`);
922
- const temporary = path.join(serviceRoot, `.install-${process$1.pid}-${Date.now()}`);
923
- await mkdir(serviceRoot, { recursive: true, mode: 448 });
924
- await rm(temporary, { recursive: true, force: true });
925
- try {
926
- await mkdir(temporary, { recursive: true, mode: 448 });
927
- await cp(path.join(packageRoot, "dist"), path.join(temporary, "dist"), { recursive: true, force: true });
928
- for (const name of ["package.json", "README.md", "LICENSE", "THIRD_PARTY_NOTICES.md"]) {
929
- await cp(path.join(packageRoot, name), path.join(temporary, name), { force: true });
930
- }
931
- await installProductionDependencies(temporary);
932
- await rm(target, { recursive: true, force: true });
933
- await rename(temporary, target);
934
- } catch (error) {
935
- await rm(temporary, { recursive: true, force: true });
936
- throw error;
919
+ backend;
920
+ status;
921
+ offered = null;
922
+ timer;
923
+ stopped = false;
924
+ work = null;
925
+ getStatus = async () => ({ ...this.status });
926
+ start() {
927
+ if (this.backend && this.status.phase !== "error") this.schedule(1e4);
937
928
  }
938
- const unit = renderSystemdUnit({
939
- nodeExecutable: process$1.execPath,
940
- cliEntrypoint: path.join(target, "dist", "bin.mjs"),
941
- host: config.host,
942
- port: config.port,
943
- home: config.home
944
- });
945
- await mkdir(path.dirname(unitPath), { recursive: true, mode: 448 });
946
- const temporaryUnit = `${unitPath}.${process$1.pid}.tmp`;
947
- await writeFile(temporaryUnit, unit, { encoding: "utf8", mode: 384 });
948
- await rename(temporaryUnit, unitPath);
949
- await chmod(unitPath, 384);
950
- await runRequired("systemctl", ["--user", "daemon-reload"]);
951
- await runRequired("systemctl", ["--user", "enable", "--now", UNIT_NAME]);
952
- await runRequired("loginctl", ["enable-linger"]);
953
- io.out(`OpenTig service installed and started on ${config.host}:${config.port}.`);
954
- io.out(`Unit: ${unitPath}`);
955
- io.out(`Pair another browser with: opentig pair --home ${config.home}`);
956
- return 0;
957
- }
958
- async function serviceStatus(unitPath, io) {
959
- try {
960
- await access(unitPath);
961
- } catch {
962
- io.out("OpenTig service is not installed.");
963
- return 1;
929
+ async stop() {
930
+ this.stopped = true;
931
+ clearTimeout(this.timer);
932
+ await this.work;
964
933
  }
965
- const enabled = await run("systemctl", ["--user", "is-enabled", UNIT_NAME]);
966
- const active = await run("systemctl", ["--user", "is-active", UNIT_NAME]);
967
- io.out(`OpenTig service: ${active.stdout.trim() || "unknown"} (${enabled.stdout.trim() || "unknown"}).`);
968
- io.out(`Unit: ${unitPath}`);
969
- return active.code === 0 ? 0 : 1;
970
- }
971
- async function uninstallService(home, unitPath, io) {
972
- await run("systemctl", ["--user", "disable", "--now", UNIT_NAME]);
973
- await rm(unitPath, { force: true });
974
- await runRequired("systemctl", ["--user", "daemon-reload"]);
975
- await rm(path.join(home, "service"), { recursive: true, force: true });
976
- io.out("OpenTig service removed. Repository settings and paired sessions were kept.");
977
- return 0;
978
- }
979
- function renderSystemdUnit(input) {
980
- const args = [
981
- input.nodeExecutable,
982
- input.cliEntrypoint,
983
- "serve",
984
- "--host",
985
- input.host,
986
- "--port",
987
- String(input.port),
988
- "--home",
989
- input.home
990
- ];
991
- return `[Unit]
992
- Description=OpenTig browser Git client
993
- After=network.target
994
-
995
- [Service]
996
- Type=simple
997
- WorkingDirectory=${systemdQuote(input.home)}
998
- ExecStart=${args.map(systemdQuote).join(" ")}
999
- Environment=NODE_ENV=production
1000
- Restart=on-failure
1001
- RestartSec=3
1002
- KillSignal=SIGTERM
1003
- TimeoutStopSec=10
1004
-
1005
- [Install]
1006
- WantedBy=default.target
1007
- `;
1008
- }
1009
- function systemdQuote(value) {
1010
- if (value.includes("\0") || value.includes("\r") || value.includes("\n")) throw new Error("Service paths and arguments cannot contain control characters.");
1011
- return `"${value.replaceAll("%", "%%").replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
1012
- }
1013
- async function assertPackageBuild(packageRoot) {
1014
- await Promise.all([
1015
- access(path.join(packageRoot, "dist", "bin.mjs")),
1016
- access(path.join(packageRoot, "dist", "client", "index.html")),
1017
- readFile(path.join(packageRoot, "package.json"), "utf8")
1018
- ]).catch(() => {
1019
- throw new Error("The OpenTig package is incomplete and cannot be installed as a service.");
1020
- });
1021
- }
1022
- async function findPackageRoot(modulePath) {
1023
- let directory = path.dirname(modulePath);
1024
- for (let depth = 0; depth < 4; depth += 1) {
934
+ schedule(delay2 = DESKTOP_UPDATE_CHECK_INTERVAL_MS) {
935
+ clearTimeout(this.timer);
936
+ if (!this.stopped) {
937
+ this.timer = setTimeout(() => void this.check(), delay2);
938
+ this.timer.unref();
939
+ }
940
+ }
941
+ check = async () => {
942
+ if (!this.backend || this.stopped || !["idle", "error"].includes(this.status.phase)) return this.getStatus();
943
+ this.status = { ...this.status, phase: "checking", message: null, progress: null };
1025
944
  try {
1026
- await Promise.all([
1027
- access(path.join(directory, "package.json")),
1028
- access(path.join(directory, "dist", "bin.mjs"))
1029
- ]);
1030
- return directory;
945
+ const release = await this.backend.release();
946
+ this.offered = release && newerVersion(release.version, this.status.currentVersion) ? release : null;
947
+ this.status = {
948
+ ...this.status,
949
+ phase: this.offered ? "available" : "idle",
950
+ availableVersion: this.offered?.version ?? null,
951
+ releaseUrl: this.offered?.url ?? null,
952
+ releaseNotes: this.offered?.notes ?? null,
953
+ checkedAt: (/* @__PURE__ */ new Date()).toISOString()
954
+ };
1031
955
  } catch {
956
+ this.fail("Could not check for updates. Check your connection and try again.");
957
+ } finally {
958
+ this.schedule();
1032
959
  }
1033
- const parent = path.dirname(directory);
1034
- if (parent === directory) break;
1035
- directory = parent;
960
+ return this.getStatus();
961
+ };
962
+ download = async () => {
963
+ if (!this.backend || !this.offered || this.stopped || this.status.phase !== "available") return this.getStatus();
964
+ this.status = { ...this.status, phase: "downloading", progress: 0, message: null };
965
+ this.work = this.backend.stage(this.offered, (progress) => {
966
+ this.status.progress = progress;
967
+ }).then(() => {
968
+ this.status = { ...this.status, phase: "ready", progress: 100 };
969
+ }).catch(() => this.fail("Could not prepare the update. The running service has not been replaced."));
970
+ return this.getStatus();
971
+ };
972
+ install = async () => {
973
+ if (!this.backend || !this.offered || this.stopped || this.status.phase !== "ready") return this.getStatus();
974
+ this.status = { ...this.status, phase: "installing", message: "Restarting the server. This browser will reconnect shortly." };
975
+ void this.backend.install(this.offered).catch(() => {
976
+ this.fail("Could not apply the update. Check the service status and try again.");
977
+ });
978
+ return this.getStatus();
979
+ };
980
+ fail(message) {
981
+ this.status = { ...this.status, phase: "error", progress: null, message };
1036
982
  }
1037
- throw new Error("Could not locate the installed OpenTig package.");
1038
983
  }
1039
- async function installProductionDependencies(directory) {
1040
- const npmCli = process$1.env.npm_execpath;
1041
- if (npmCli) {
1042
- await runRequired(process$1.execPath, [npmCli, "install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"], directory);
1043
- return;
1044
- }
1045
- await runRequired("npm", ["install", "--omit=dev", "--ignore-scripts", "--no-audit", "--no-fund"], directory);
984
+ async function json(url) {
985
+ const response = await fetch(url, { headers: { Accept: "application/json" }, signal: AbortSignal.timeout(15e3) });
986
+ if (response.status === 404) return null;
987
+ if (!response.ok) throw new Error("Release metadata request failed.");
988
+ const text = await response.text();
989
+ if (text.length > 1e6) throw new Error("Release metadata is too large.");
990
+ return JSON.parse(text);
991
+ }
992
+ async function latestCliRelease() {
993
+ const release = await json(`https://api.github.com/repos/${REPOSITORY}/releases/latest`);
994
+ if (!release || release.draft || release.prerelease || typeof release.tag_name !== "string") return null;
995
+ const version = release.tag_name.replace(/^v/, "");
996
+ if (!stableVersion(version) || release.tag_name !== `v${version}`) return null;
997
+ const pkg = await json(`${REGISTRY}/@opentig%2fcli/${version}`);
998
+ if (!pkg) return null;
999
+ const dist = pkg.dist;
1000
+ const tarball = `${REGISTRY}/@opentig/cli/-/cli-${version}.tgz`;
1001
+ if (pkg.name !== "@opentig/cli" || pkg.version !== version || dist?.tarball !== tarball || typeof dist.integrity !== "string" || !/^sha512-[A-Za-z0-9+/]{86}==$/.test(dist.integrity)) throw new Error("Invalid CLI release metadata.");
1002
+ return {
1003
+ version,
1004
+ tarball,
1005
+ integrity: dist.integrity,
1006
+ url: `https://github.com/${REPOSITORY}/releases/tag/v${version}`,
1007
+ notes: typeof release.body === "string" ? release.body.slice(0, 64e3) : null
1008
+ };
1046
1009
  }
1047
- function runRequired(command, args, cwd) {
1048
- return run(command, args, cwd).then((result) => {
1049
- if (result.code !== 0) throw new Error(`${command} failed: ${result.stderr.trim() || result.stdout.trim() || `exit code ${result.code}`}`);
1050
- });
1010
+ function verifyTarball(bytes, integrity) {
1011
+ if (`sha512-${createHash("sha512").update(bytes).digest("base64")}` !== integrity) throw new Error("CLI download integrity mismatch.");
1051
1012
  }
1052
- function run(command, args, cwd) {
1053
- return new Promise((resolve, reject) => {
1054
- execFile(command, args, { cwd, timeout: 12e4, windowsHide: true }, (error, stdout, stderr) => {
1055
- const code = typeof error?.code === "number" ? error.code : error ? 1 : 0;
1056
- if (error && error.code === "ENOENT") return reject(new Error(`${command} is required but was not found.`));
1057
- resolve({ code, stdout, stderr });
1013
+ async function stageCliRelease(home, release, progress) {
1014
+ if (!stableVersion(release.version)) throw new Error("Invalid update version.");
1015
+ const existing = path.join(home, "service", `app-${release.version}`);
1016
+ try {
1017
+ const marker = JSON.parse(await readFile(path.join(existing, "update-integrity.json"), "utf8"));
1018
+ if (marker.version !== release.version || marker.integrity !== release.integrity) throw new Error("Existing candidate does not match the release.");
1019
+ progress(100);
1020
+ return;
1021
+ } catch (error) {
1022
+ if (error.code !== "ENOENT") throw error;
1023
+ }
1024
+ const temporary = await mkdtemp(path.join(home, "service/.download-"));
1025
+ try {
1026
+ const response = await fetch(release.tarball, { signal: AbortSignal.timeout(12e4), redirect: "error" });
1027
+ if (!response.ok || !response.body) throw new Error("CLI download failed.");
1028
+ const total = Number(response.headers.get("content-length"));
1029
+ if (total > MAX_PACKAGE_BYTES) throw new Error("CLI package too large.");
1030
+ let size = 0;
1031
+ const chunks = [];
1032
+ for await (const chunk of response.body) {
1033
+ size += chunk.length;
1034
+ if (size > MAX_PACKAGE_BYTES) throw new Error("CLI package too large.");
1035
+ chunks.push(chunk);
1036
+ progress(Number.isFinite(total) && total > 0 ? Math.min(85, size / total * 85) : 0);
1037
+ }
1038
+ const bytes = Buffer.concat(chunks);
1039
+ verifyTarball(bytes, release.integrity);
1040
+ const tarball = path.join(temporary, "cli.tgz");
1041
+ await writeFile(tarball, bytes, { mode: 384 });
1042
+ const target = path.join(temporary, "app");
1043
+ await mkdir(target);
1044
+ await execute("npm", ["install", "--prefix", target, "--ignore-scripts", "--omit=dev", "--no-audit", "--no-fund", "--registry", REGISTRY, tarball], { timeout: 18e4, maxBuffer: 2e6 });
1045
+ const pkgRoot = path.join(target, "node_modules/@opentig/cli");
1046
+ const pkg = JSON.parse(await readFile(path.join(pkgRoot, "package.json"), "utf8"));
1047
+ if (pkg.name !== "@opentig/cli" || pkg.version !== release.version) throw new Error("Installed CLI identity mismatch.");
1048
+ const result = await execute(process.execPath, [path.join(pkgRoot, "dist/bin.mjs"), "--version"], {
1049
+ timeout: 15e3,
1050
+ env: { ...process.env, OPENTIG_DELEGATED: "1" }
1058
1051
  });
1059
- });
1052
+ if (result.stdout.trim() !== release.version) throw new Error("New CLI cannot run.");
1053
+ await readFile(path.join(pkgRoot, "dist/client/index.html"));
1054
+ await readFile(path.join(pkgRoot, "dist/service-update.mjs"));
1055
+ progress(95);
1056
+ const destination = path.join(home, "service", `app-${release.version}`);
1057
+ await atomicWrite(path.join(target, "update-integrity.json"), JSON.stringify({ version: release.version, integrity: release.integrity }));
1058
+ await rename(target, destination);
1059
+ } finally {
1060
+ await rm(temporary, { recursive: true, force: true });
1061
+ }
1062
+ }
1063
+ async function createServiceUpdater(home, version, profile, address) {
1064
+ let installation = null;
1065
+ try {
1066
+ if (profile === "production" && process.platform === "linux" && process.env.INVOCATION_ID) {
1067
+ installation = await readInstallation(home);
1068
+ if (!installation || installation.version !== version || installation.host !== address.host || installation.port !== address.port || await systemctl("show", SERVICE_NAME, "--property=FragmentPath", "--value") !== serviceUnitPath() || await realpath(process.argv[1]) !== await realpath(path.join(packageDirectory(home, installation), "dist/bin.mjs")) || await systemctl("show", SERVICE_NAME, "--property=MainPID", "--value") !== String(process.pid)) installation = null;
1069
+ }
1070
+ } catch {
1071
+ installation = null;
1072
+ }
1073
+ if (!installation) return new ServiceUpdater(version, null);
1074
+ const current = installation;
1075
+ let failedUpdate = false;
1076
+ try {
1077
+ const result = JSON.parse(await readFile(path.join(home, "service/update-result.json"), "utf8"));
1078
+ failedUpdate = result.success === false && result.version === version;
1079
+ } catch {
1080
+ }
1081
+ const updater = new ServiceUpdater(version, {
1082
+ release: latestCliRelease,
1083
+ stage: (release, progress) => stageCliRelease(home, release, progress),
1084
+ install: async (release) => {
1085
+ const lock = path.join(home, "service/update-lock");
1086
+ await mkdir(lock, { mode: 448 });
1087
+ const unit = `opentig-update-${randomUUID()}`;
1088
+ try {
1089
+ const next = { ...current, version: release.version, layout: "npm" };
1090
+ const previousUnit = await readFile(serviceUnitPath(), "utf8");
1091
+ await atomicWrite(path.join(lock, "plan.json"), JSON.stringify({ home, current, next, previousUnit, integrity: release.integrity }));
1092
+ await execute("systemd-run", [
1093
+ "--user",
1094
+ "--collect",
1095
+ "--unit",
1096
+ unit,
1097
+ "--property=Type=exec",
1098
+ process.execPath,
1099
+ path.join(packageDirectory(home, current), "dist/service-update.mjs"),
1100
+ home
1101
+ ], { timeout: 15e3 });
1102
+ } catch (error) {
1103
+ await rm(lock, { recursive: true, force: true });
1104
+ throw error;
1105
+ }
1106
+ for (let attempt = 0; attempt < 120; attempt++) {
1107
+ await setTimeout$1(1e3);
1108
+ const state = await systemctl("show", unit, "--property=ActiveState", "--value").catch(() => "inactive");
1109
+ if (!["active", "activating", "deactivating"].includes(state)) {
1110
+ await rm(lock, { recursive: true, force: true });
1111
+ throw new Error("Update worker exited without replacing the server.");
1112
+ }
1113
+ }
1114
+ throw new Error("Update worker did not restart the service.");
1115
+ }
1116
+ }, failedUpdate);
1117
+ return updater;
1060
1118
  }
1061
1119
  const RUNTIME_STATE_VERSION = 1;
1062
1120
  function resolveCliPaths(home) {
@@ -1114,7 +1172,7 @@ async function readRuntimeState(filePath) {
1114
1172
  try {
1115
1173
  value = JSON.parse(await readFile(filePath, "utf8"));
1116
1174
  } catch (error) {
1117
- if (error.code === "ENOENT") throw new Error("No running OpenTig server was found for this home.");
1175
+ if (error.code === "ENOENT") throw new Error("No running OpenTig server was found for this home.", { cause: error });
1118
1176
  throw new Error("OpenTig runtime state is unreadable.", { cause: error });
1119
1177
  }
1120
1178
  if (!isRuntimeState(value)) throw new Error("OpenTig runtime state is invalid.");
@@ -1219,10 +1277,12 @@ async function startCliServer(config, io, profile) {
1219
1277
  const adminToken = await loadOrCreateAdminToken(paths.adminToken);
1220
1278
  const instanceId = newInstanceId();
1221
1279
  const log = new CliServerLog(paths.serverLog);
1280
+ const updates = await createServiceUpdater(config.home, OPEN_TIG_APP_VERSION, profile, config);
1222
1281
  let server = null;
1223
1282
  try {
1224
1283
  server = await startOnConfiguredPort({
1225
1284
  appVersion: OPEN_TIG_APP_VERSION,
1285
+ updates,
1226
1286
  profile,
1227
1287
  auth: { consumeDesktopSecret: () => false },
1228
1288
  settingsPath: paths.settings,
@@ -1261,11 +1321,14 @@ async function startCliServer(config, io, profile) {
1261
1321
  io.error("Could not open the system browser. Open the one-time pairing link shown above manually.");
1262
1322
  });
1263
1323
  }
1324
+ updates.start();
1264
1325
  return await waitForShutdown(server, async () => {
1326
+ await updates.stop();
1265
1327
  await clearRuntimeState(paths.runtimeState, instanceId);
1266
1328
  await log.close();
1267
1329
  }, io);
1268
1330
  } catch (error) {
1331
+ await updates.stop();
1269
1332
  if (server) await server.close().catch(() => void 0);
1270
1333
  await clearRuntimeState(paths.runtimeState, instanceId).catch(() => void 0);
1271
1334
  await log.close();
@@ -1403,5 +1466,6 @@ function assertGitAvailable() {
1403
1466
  }
1404
1467
  export {
1405
1468
  CliUsageError as C,
1469
+ parseCliArguments as p,
1406
1470
  runCli as r
1407
1471
  };