glop.dev 0.5.0 → 0.6.0

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/index.js +397 -166
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -11,6 +11,70 @@ import fs from "fs";
11
11
  import path from "path";
12
12
  import os from "os";
13
13
  import crypto from "crypto";
14
+
15
+ // src/lib/git.ts
16
+ import { execSync } from "child_process";
17
+ function getRepoRoot() {
18
+ try {
19
+ return execSync("git rev-parse --show-toplevel", {
20
+ encoding: "utf-8",
21
+ stdio: ["pipe", "pipe", "pipe"]
22
+ }).trim();
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
27
+ function getRepoKey() {
28
+ try {
29
+ const remote = execSync("git remote get-url origin", {
30
+ encoding: "utf-8",
31
+ stdio: ["pipe", "pipe", "pipe"]
32
+ }).trim();
33
+ const match = remote.match(
34
+ /(?:github\.com|gitlab\.com|bitbucket\.org)[/:](.+?)(?:\.git)?$/
35
+ );
36
+ if (match) return match[1];
37
+ const parts = remote.split("/").filter(Boolean);
38
+ if (parts.length >= 2) {
39
+ return `${parts[parts.length - 2]}/${parts[parts.length - 1].replace(".git", "")}`;
40
+ }
41
+ return remote;
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ function getBranch() {
47
+ try {
48
+ return execSync("git rev-parse --abbrev-ref HEAD", {
49
+ encoding: "utf-8",
50
+ stdio: ["pipe", "pipe", "pipe"]
51
+ }).trim();
52
+ } catch {
53
+ return "noname";
54
+ }
55
+ }
56
+ function getGitUserName() {
57
+ try {
58
+ return execSync("git config user.name", {
59
+ encoding: "utf-8",
60
+ stdio: ["pipe", "pipe", "pipe"]
61
+ }).trim() || null;
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+ function getGitUserEmail() {
67
+ try {
68
+ return execSync("git config user.email", {
69
+ encoding: "utf-8",
70
+ stdio: ["pipe", "pipe", "pipe"]
71
+ }).trim() || null;
72
+ } catch {
73
+ return null;
74
+ }
75
+ }
76
+
77
+ // src/lib/config.ts
14
78
  var CONFIG_DIR = path.join(os.homedir(), ".glop");
15
79
  var CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
16
80
  function ensureConfigDir() {
@@ -28,7 +92,7 @@ function getMachineId() {
28
92
  fs.writeFileSync(machineIdFile, machineId);
29
93
  return machineId;
30
94
  }
31
- function loadConfig() {
95
+ function loadGlobalConfig() {
32
96
  if (!fs.existsSync(CONFIG_FILE)) return null;
33
97
  try {
34
98
  const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
@@ -37,15 +101,58 @@ function loadConfig() {
37
101
  return null;
38
102
  }
39
103
  }
40
- function saveConfig(config) {
104
+ function saveGlobalConfig(config) {
41
105
  ensureConfigDir();
42
106
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
43
107
  }
108
+ function loadRepoConfig() {
109
+ const repoRoot = getRepoRoot();
110
+ if (!repoRoot) return null;
111
+ const repoConfigFile = path.join(repoRoot, ".glop", "config.json");
112
+ if (!fs.existsSync(repoConfigFile)) return null;
113
+ try {
114
+ const raw = fs.readFileSync(repoConfigFile, "utf-8");
115
+ return JSON.parse(raw);
116
+ } catch {
117
+ return null;
118
+ }
119
+ }
120
+ function saveRepoConfig(config) {
121
+ const repoRoot = getRepoRoot();
122
+ if (!repoRoot) throw new Error("Not in a git repository");
123
+ const repoConfigDir = path.join(repoRoot, ".glop");
124
+ if (!fs.existsSync(repoConfigDir)) {
125
+ fs.mkdirSync(repoConfigDir, { recursive: true });
126
+ }
127
+ fs.writeFileSync(
128
+ path.join(repoConfigDir, "config.json"),
129
+ JSON.stringify(config, null, 2)
130
+ );
131
+ }
132
+ function loadConfig() {
133
+ const global = loadGlobalConfig();
134
+ if (!global || Object.keys(global.workspaces).length === 0) return null;
135
+ const repoConfig = loadRepoConfig();
136
+ const workspaceId = repoConfig?.workspace_id || global.default_workspace || Object.keys(global.workspaces)[0];
137
+ if (!workspaceId) return null;
138
+ const ws = global.workspaces[workspaceId];
139
+ if (!ws) return null;
140
+ return {
141
+ server_url: global.server_url,
142
+ api_key: ws.api_key,
143
+ developer_id: ws.developer_id,
144
+ developer_name: global.developer_name,
145
+ machine_id: global.machine_id,
146
+ workspace_id: workspaceId,
147
+ workspace_name: ws.workspace_name,
148
+ workspace_slug: ws.workspace_slug
149
+ };
150
+ }
44
151
  function getDefaultServerUrl() {
45
152
  return process.env.GLOP_SERVER_URL || "https://www.glop.dev";
46
153
  }
47
154
 
48
- // src/commands/auth.ts
155
+ // src/lib/auth-flow.ts
49
156
  import http from "http";
50
157
  import { exec } from "child_process";
51
158
  function openBrowser(url) {
@@ -67,39 +174,6 @@ function findOpenPort() {
67
174
  });
68
175
  });
69
176
  }
70
- var authCommand = new Command("auth").description("Authenticate with a glop server").option("-s, --server <url>", "Server URL").action(async (opts) => {
71
- const serverUrl = (opts.server || getDefaultServerUrl()).replace(/\/+$/, "");
72
- const port = await findOpenPort();
73
- const machineId = getMachineId();
74
- console.log("Opening browser for authentication...");
75
- console.log(
76
- "If the browser doesn't open, visit this URL manually:"
77
- );
78
- const authUrl = `${serverUrl}/cli-auth?port=${port}`;
79
- console.log(` ${authUrl}
80
- `);
81
- console.log("Waiting for authorization...");
82
- openBrowser(authUrl);
83
- const result = await waitForCallback(port);
84
- saveConfig({
85
- server_url: serverUrl,
86
- api_key: result.api_key,
87
- developer_id: result.developer_id,
88
- developer_name: result.developer_name,
89
- machine_id: machineId
90
- });
91
- console.log("\nAuthenticated successfully!");
92
- console.log(` Developer: ${result.developer_name}`);
93
- console.log(` Server: ${serverUrl}`);
94
- console.log(` Machine: ${machineId.slice(0, 8)}...`);
95
- console.log(`
96
- API key saved to ~/.glop/config.json`);
97
- console.log(
98
- `
99
- \u2192 Run \`glop init\` in a repo to start streaming sessions.`
100
- );
101
- process.exit(0);
102
- });
103
177
  function waitForCallback(port) {
104
178
  return new Promise((resolve, reject) => {
105
179
  const timeout = setTimeout(() => {
@@ -129,7 +203,10 @@ function waitForCallback(port) {
129
203
  resolve({
130
204
  api_key: apiKey,
131
205
  developer_id: developerId,
132
- developer_name: developerName
206
+ developer_name: developerName,
207
+ workspace_id: url.searchParams.get("workspace_id") || void 0,
208
+ workspace_name: url.searchParams.get("workspace_name") || void 0,
209
+ workspace_slug: url.searchParams.get("workspace_slug") || void 0
133
210
  });
134
211
  return;
135
212
  }
@@ -157,72 +234,59 @@ h1{margin:0 0 1rem;font-size:1.25rem}</style></head>
157
234
  </html>`;
158
235
  }
159
236
 
160
- // src/commands/deactivate.ts
161
- import { Command as Command2 } from "commander";
162
-
163
- // src/lib/git.ts
164
- import { execSync } from "child_process";
165
- function getRepoRoot() {
166
- try {
167
- return execSync("git rev-parse --show-toplevel", {
168
- encoding: "utf-8",
169
- stdio: ["pipe", "pipe", "pipe"]
170
- }).trim();
171
- } catch {
172
- return null;
173
- }
174
- }
175
- function getRepoKey() {
176
- try {
177
- const remote = execSync("git remote get-url origin", {
178
- encoding: "utf-8",
179
- stdio: ["pipe", "pipe", "pipe"]
180
- }).trim();
181
- const match = remote.match(
182
- /(?:github\.com|gitlab\.com|bitbucket\.org)[/:](.+?)(?:\.git)?$/
183
- );
184
- if (match) return match[1];
185
- const parts = remote.split("/").filter(Boolean);
186
- if (parts.length >= 2) {
187
- return `${parts[parts.length - 2]}/${parts[parts.length - 1].replace(".git", "")}`;
188
- }
189
- return remote;
190
- } catch {
191
- return null;
192
- }
193
- }
194
- function getBranch() {
195
- try {
196
- return execSync("git rev-parse --abbrev-ref HEAD", {
197
- encoding: "utf-8",
198
- stdio: ["pipe", "pipe", "pipe"]
199
- }).trim();
200
- } catch {
201
- return "noname";
202
- }
203
- }
204
- function getGitUserName() {
205
- try {
206
- return execSync("git config user.name", {
207
- encoding: "utf-8",
208
- stdio: ["pipe", "pipe", "pipe"]
209
- }).trim() || null;
210
- } catch {
211
- return null;
212
- }
213
- }
214
- function getGitUserEmail() {
215
- try {
216
- return execSync("git config user.email", {
217
- encoding: "utf-8",
218
- stdio: ["pipe", "pipe", "pipe"]
219
- }).trim() || null;
220
- } catch {
221
- return null;
237
+ // src/commands/auth.ts
238
+ var authCommand = new Command("auth").description("Authenticate with a glop server").option("-s, --server <url>", "Server URL").action(async (opts) => {
239
+ const serverUrl = (opts.server || getDefaultServerUrl()).replace(/\/+$/, "");
240
+ const port = await findOpenPort();
241
+ const machineId = getMachineId();
242
+ console.log("Opening browser for authentication...");
243
+ console.log(
244
+ "If the browser doesn't open, visit this URL manually:"
245
+ );
246
+ const authUrl = `${serverUrl}/cli-auth?port=${port}`;
247
+ console.log(` ${authUrl}
248
+ `);
249
+ console.log("Waiting for authorization...");
250
+ openBrowser(authUrl);
251
+ const result = await waitForCallback(port);
252
+ const existing = loadGlobalConfig();
253
+ const globalConfig = existing || {
254
+ server_url: serverUrl,
255
+ machine_id: machineId,
256
+ developer_name: result.developer_name,
257
+ workspaces: {}
258
+ };
259
+ globalConfig.server_url = serverUrl;
260
+ globalConfig.machine_id = machineId;
261
+ globalConfig.developer_name = result.developer_name;
262
+ if (result.workspace_id) {
263
+ globalConfig.workspaces[result.workspace_id] = {
264
+ api_key: result.api_key,
265
+ developer_id: result.developer_id,
266
+ workspace_name: result.workspace_name,
267
+ workspace_slug: result.workspace_slug
268
+ };
269
+ globalConfig.default_workspace = result.workspace_id;
270
+ }
271
+ saveGlobalConfig(globalConfig);
272
+ console.log("\nAuthenticated successfully!");
273
+ console.log(` Developer: ${result.developer_name}`);
274
+ if (result.workspace_name) {
275
+ console.log(` Workspace: ${result.workspace_name}`);
222
276
  }
223
- }
277
+ console.log(` Server: ${serverUrl}`);
278
+ console.log(` Machine: ${machineId.slice(0, 8)}...`);
279
+ console.log(`
280
+ API key saved to ~/.glop/config.json`);
281
+ console.log(
282
+ `
283
+ \u2192 Run \`glop init\` in a repo to start streaming sessions.`
284
+ );
285
+ process.exit(0);
286
+ });
224
287
 
225
288
  // src/commands/deactivate.ts
289
+ import { Command as Command2 } from "commander";
226
290
  import fs2 from "fs";
227
291
  import path2 from "path";
228
292
  var HOOK_EVENTS = [
@@ -306,7 +370,10 @@ var doctorCommand = new Command3("doctor").description("Check that glop is set u
306
370
  console.log();
307
371
  process.exit(1);
308
372
  }
309
- check("pass", "Authenticated", `${config.developer_name} on ${config.server_url}`);
373
+ const repoBinding = loadRepoConfig();
374
+ const wsSource = repoBinding?.workspace_id ? "repo binding" : "default";
375
+ const authDetail = config.workspace_name ? `${config.developer_name} on ${config.server_url} (${config.workspace_name}, ${wsSource})` : `${config.developer_name} on ${config.server_url}`;
376
+ check("pass", "Authenticated", authDetail);
310
377
  try {
311
378
  const res = await fetch(`${config.server_url}/api/v1/health`, {
312
379
  headers: {
@@ -529,89 +596,249 @@ var initCommand = new Command5("init").description("Install Claude Code hooks in
529
596
  console.log(`${hadHooks ? "\u2713 glop updated" : "\u2713 glop connected"} \u2014 sessions will appear at ${config.server_url}/live`);
530
597
  });
531
598
 
532
- // src/commands/status.ts
599
+ // src/commands/workspace.ts
533
600
  import { Command as Command6 } from "commander";
534
601
 
535
- // src/lib/api-client.ts
536
- function getConfig() {
537
- const config = loadConfig();
538
- if (!config) {
539
- console.error(
540
- "Not authenticated. Run `glop auth` first."
541
- );
542
- process.exit(1);
543
- }
544
- return config;
545
- }
546
- async function apiRequest(path5, options = {}) {
547
- const config = getConfig();
548
- const url = `${config.server_url}${path5}`;
549
- const headers = {
550
- "Content-Type": "application/json",
551
- Authorization: `Bearer ${config.api_key}`,
552
- "X-Machine-Id": config.machine_id,
553
- ...options.headers
554
- };
555
- return fetch(url, {
556
- ...options,
557
- headers
602
+ // src/lib/select.ts
603
+ function interactiveSelect(items, initialIndex = 0) {
604
+ return new Promise((resolve) => {
605
+ if (!process.stdin.isTTY) {
606
+ resolve(null);
607
+ return;
608
+ }
609
+ let cursor = initialIndex;
610
+ function render() {
611
+ if (rendered) {
612
+ process.stdout.write(`\x1B[${items.length}A`);
613
+ }
614
+ for (let i = 0; i < items.length; i++) {
615
+ const isSelected = i === cursor;
616
+ const prefix = isSelected ? "\x1B[36m\u276F\x1B[0m " : " ";
617
+ process.stdout.write(`\x1B[2K${prefix}${items[i]}
618
+ `);
619
+ }
620
+ rendered = true;
621
+ }
622
+ let rendered = false;
623
+ process.stdin.setRawMode(true);
624
+ process.stdin.resume();
625
+ process.stdin.setEncoding("utf8");
626
+ function cleanup() {
627
+ process.stdin.setRawMode(false);
628
+ process.stdin.pause();
629
+ process.stdin.removeListener("data", onData);
630
+ }
631
+ function onData(key) {
632
+ if (key === "") {
633
+ cleanup();
634
+ resolve(null);
635
+ return;
636
+ }
637
+ if (key === "\x1B" || key === "\x1B\x1B") {
638
+ cleanup();
639
+ resolve(null);
640
+ return;
641
+ }
642
+ if (key === "\r" || key === "\n") {
643
+ cleanup();
644
+ resolve(cursor);
645
+ return;
646
+ }
647
+ if (key === "\x1B[A" || key === "k") {
648
+ cursor = (cursor - 1 + items.length) % items.length;
649
+ render();
650
+ return;
651
+ }
652
+ if (key === "\x1B[B" || key === "j") {
653
+ cursor = (cursor + 1) % items.length;
654
+ render();
655
+ return;
656
+ }
657
+ }
658
+ process.stdin.on("data", onData);
659
+ render();
558
660
  });
559
661
  }
560
662
 
561
- // src/commands/status.ts
562
- function timeAgo(iso) {
563
- const seconds = Math.floor((Date.now() - new Date(iso).getTime()) / 1e3);
564
- if (seconds < 5) return "just now";
565
- if (seconds < 60) return `${seconds}s ago`;
566
- const minutes = Math.floor(seconds / 60);
567
- if (minutes < 60) return `${minutes}m ago`;
568
- const hours = Math.floor(minutes / 60);
569
- if (hours < 24) return `${hours}h ago`;
570
- const days = Math.floor(hours / 24);
571
- return `${days}d ago`;
572
- }
573
- var statusCommand = new Command6("status").description("Show current Run status for this repo").action(async () => {
574
- const repoKey = getRepoKey();
575
- const branch = getBranch();
576
- if (!repoKey) {
577
- console.error("Not in a git repository with a remote.");
663
+ // src/commands/workspace.ts
664
+ var workspaceCommand = new Command6("workspace").description("View or switch workspaces").action(async () => {
665
+ const config = loadConfig();
666
+ if (!config) {
667
+ console.error("Not authenticated. Run `glop auth` first.");
578
668
  process.exit(1);
579
669
  }
670
+ let data;
580
671
  try {
581
- const res = await apiRequest("/api/v1/live");
582
- if (!res.ok) {
583
- console.error("Failed to fetch status:", res.statusText);
672
+ const res = await fetch(`${config.server_url}/api/v1/cli/workspaces`, {
673
+ headers: {
674
+ Authorization: `Bearer ${config.api_key}`,
675
+ "X-Machine-Id": config.machine_id
676
+ },
677
+ signal: AbortSignal.timeout(1e4)
678
+ });
679
+ if (res.status === 401) {
680
+ console.error("API key is invalid. Run `glop auth` to re-authenticate.");
584
681
  process.exit(1);
585
682
  }
586
- const data = await res.json();
587
- const matchingRuns = data.runs.filter(
588
- (r) => r.repo_key.includes(repoKey.split("/").pop() || "") && r.branch_name === branch
589
- );
590
- if (matchingRuns.length === 0) {
591
- console.log(`No active runs for ${repoKey} (${branch})`);
592
- return;
593
- }
594
- for (const run of matchingRuns) {
595
- console.log(`Run: ${run.id.slice(0, 8)}`);
596
- console.log(` Status: ${run.status}`);
597
- console.log(` Phase: ${run.phase}`);
598
- console.log(` Title: ${run.title || "-"}`);
599
- console.log(` Last: ${run.last_action_label || "-"}`);
600
- console.log(` Updated: ${timeAgo(run.last_event_at)}`);
683
+ if (!res.ok) {
684
+ console.error(`Failed to fetch workspaces (HTTP ${res.status}).`);
685
+ process.exit(1);
601
686
  }
687
+ data = await res.json();
602
688
  } catch (err) {
603
- console.error(
604
- "Failed to connect:",
605
- err instanceof Error ? err.message : err
606
- );
689
+ if (err instanceof Error && err.name === "TimeoutError") {
690
+ console.error(`Cannot connect to ${config.server_url}`);
691
+ } else {
692
+ console.error("Failed to fetch workspaces.");
693
+ }
607
694
  process.exit(1);
608
695
  }
696
+ if (data.workspaces.length === 0) {
697
+ console.log("No workspaces found.");
698
+ process.exit(0);
699
+ }
700
+ const repoConfig = loadRepoConfig();
701
+ const currentId = repoConfig?.workspace_id || config.workspace_id || data.current_workspace_id;
702
+ if (!process.stdin.isTTY) {
703
+ const current = data.workspaces.find((w) => w.id === currentId);
704
+ console.log(current ? current.name : currentId);
705
+ process.exit(0);
706
+ }
707
+ if (data.workspaces.length === 1) {
708
+ console.log(` Workspace: ${data.workspaces[0].name}`);
709
+ console.log(" (only workspace)");
710
+ process.exit(0);
711
+ }
712
+ const items = data.workspaces.map((w) => {
713
+ const marker = w.id === currentId ? "\u25CF" : "\u25CB";
714
+ return `${marker} ${w.name}`;
715
+ });
716
+ const currentIndex = data.workspaces.findIndex((w) => w.id === currentId);
717
+ console.log("\n Workspaces:\n");
718
+ console.log(" \x1B[2m\u2191/\u2193 navigate \xB7 Enter select \xB7 Esc cancel\x1B[0m\n");
719
+ const selected = await interactiveSelect(items, Math.max(currentIndex, 0));
720
+ if (selected === null) {
721
+ console.log("\n Cancelled.");
722
+ process.exit(0);
723
+ }
724
+ const selectedWorkspace = data.workspaces[selected];
725
+ if (selectedWorkspace.id === currentId) {
726
+ console.log(`
727
+ Already on ${selectedWorkspace.name}.`);
728
+ process.exit(0);
729
+ }
730
+ const globalConfig = loadGlobalConfig();
731
+ const existingCreds = globalConfig.workspaces[selectedWorkspace.id];
732
+ const repoRoot = getRepoRoot();
733
+ if (existingCreds) {
734
+ if (repoRoot) {
735
+ saveRepoConfig({ workspace_id: selectedWorkspace.id });
736
+ } else {
737
+ globalConfig.default_workspace = selectedWorkspace.id;
738
+ saveGlobalConfig(globalConfig);
739
+ }
740
+ console.log(`
741
+ Switched to ${selectedWorkspace.name}!`);
742
+ process.exit(0);
743
+ }
744
+ console.log(`
745
+ Switching to ${selectedWorkspace.name}...`);
746
+ console.log(" Opening browser for authorization...\n");
747
+ const port = await findOpenPort();
748
+ const machineId = getMachineId();
749
+ const authUrl = `${config.server_url}/cli-auth?port=${port}&workspace_id=${selectedWorkspace.id}`;
750
+ console.log(" If the browser doesn't open, visit this URL manually:");
751
+ console.log(` ${authUrl}
752
+ `);
753
+ console.log(" Waiting for authorization...");
754
+ openBrowser(authUrl);
755
+ const result = await waitForCallback(port);
756
+ const wsId = result.workspace_id || selectedWorkspace.id;
757
+ globalConfig.workspaces[wsId] = {
758
+ api_key: result.api_key,
759
+ developer_id: result.developer_id,
760
+ workspace_name: result.workspace_name,
761
+ workspace_slug: result.workspace_slug
762
+ };
763
+ globalConfig.developer_name = result.developer_name;
764
+ if (repoRoot) {
765
+ saveRepoConfig({ workspace_id: wsId });
766
+ } else {
767
+ globalConfig.default_workspace = wsId;
768
+ }
769
+ saveGlobalConfig(globalConfig);
770
+ console.log(`
771
+ Switched to ${result.workspace_name || selectedWorkspace.name}!`);
772
+ process.exit(0);
609
773
  });
610
774
 
775
+ // src/lib/update-check.ts
776
+ import fs5 from "fs";
777
+ import path5 from "path";
778
+ import os2 from "os";
779
+ var CONFIG_DIR2 = path5.join(os2.homedir(), ".glop");
780
+ var CACHE_FILE = path5.join(CONFIG_DIR2, "update-check.json");
781
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
782
+ function ensureConfigDir2() {
783
+ if (!fs5.existsSync(CONFIG_DIR2)) {
784
+ fs5.mkdirSync(CONFIG_DIR2, { recursive: true });
785
+ }
786
+ }
787
+ function isNewerVersion(current, latest) {
788
+ const currentParts = current.split(".").map(Number);
789
+ const latestParts = latest.split(".").map(Number);
790
+ for (let i = 0; i < 3; i++) {
791
+ const c = currentParts[i] || 0;
792
+ const l = latestParts[i] || 0;
793
+ if (l > c) return true;
794
+ if (l < c) return false;
795
+ }
796
+ return false;
797
+ }
798
+ async function checkForUpdate(currentVersion) {
799
+ try {
800
+ if (process.env.CI) return;
801
+ if (!process.stderr.isTTY) return;
802
+ let latestVersion = null;
803
+ if (fs5.existsSync(CACHE_FILE)) {
804
+ try {
805
+ const raw = fs5.readFileSync(CACHE_FILE, "utf-8");
806
+ const cache = JSON.parse(raw);
807
+ if (Date.now() - cache.last_check < CHECK_INTERVAL_MS) {
808
+ latestVersion = cache.latest_version;
809
+ }
810
+ } catch {
811
+ }
812
+ }
813
+ if (!latestVersion) {
814
+ const response = await fetch(
815
+ "https://registry.npmjs.org/glop.dev/latest",
816
+ { signal: AbortSignal.timeout(3e3) }
817
+ );
818
+ const data = await response.json();
819
+ latestVersion = data.version;
820
+ ensureConfigDir2();
821
+ const cache = {
822
+ last_check: Date.now(),
823
+ latest_version: latestVersion
824
+ };
825
+ fs5.writeFileSync(CACHE_FILE, JSON.stringify(cache));
826
+ }
827
+ if (isNewerVersion(currentVersion, latestVersion)) {
828
+ console.error(
829
+ `
830
+ Update available: ${currentVersion} \u2192 ${latestVersion}. Run \`npm i -g glop.dev\` to update.
831
+ `
832
+ );
833
+ }
834
+ } catch {
835
+ }
836
+ }
837
+
611
838
  // package.json
612
839
  var package_default = {
613
840
  name: "glop.dev",
614
- version: "0.5.0",
841
+ version: "0.6.0",
615
842
  type: "module",
616
843
  bin: {
617
844
  glop: "./dist/index.js"
@@ -644,5 +871,9 @@ program.addCommand(deactivateCommand);
644
871
  program.addCommand(doctorCommand);
645
872
  program.addCommand(hookCommand, { hidden: true });
646
873
  program.addCommand(initCommand);
647
- program.addCommand(statusCommand);
874
+ program.addCommand(workspaceCommand);
875
+ program.hook("postAction", async (_thisCommand, actionCommand) => {
876
+ if (actionCommand.name() === "__hook") return;
877
+ await checkForUpdate(package_default.version);
878
+ });
648
879
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glop.dev",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "glop": "./dist/index.js"