@way_marks/server 4.4.2 → 4.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.
package/README.md CHANGED
@@ -52,6 +52,7 @@ All endpoints are served by `dist/api/server.js` on port 3001.
52
52
  | `POST` | `/api/slack/interact` | Slack interactive component handler (approve/reject from Slack message buttons). |
53
53
  | `GET` | `/api/sessions` | List all session IDs that have logged actions. |
54
54
  | `GET` | `/api/config` | Return the current parsed `waymark.config.json` for the active project. |
55
+ | `GET` | `/api/version` | Return version information: `{ currentVersion, latestVersion, updateAvailable }`. Checks npm registry with 24-hour cache. Used by dashboard VersionBanner for update notifications. |
55
56
  | `GET` | `*` | Catch-all — serves the dashboard `index.html`. |
56
57
 
57
58
  ---
@@ -49,6 +49,7 @@ const manager_3 = require("../escalation/manager");
49
49
  const events_1 = require("./events");
50
50
  const multi_collector_1 = require("../collectors/multi-collector");
51
51
  const agent_monitor_1 = require("./routes/agent-monitor");
52
+ const version_1 = require("../services/version");
52
53
  // Import registry for Phase 2 hub navigation
53
54
  const registryPath = path.join(process.env.HOME || process.env.USERPROFILE || '', '.waymark', 'registry.json');
54
55
  function getRegistryProjects() {
@@ -540,6 +541,16 @@ app.get('/api/project', (_req, res) => {
540
541
  res.status(500).json({ error: err.message });
541
542
  }
542
543
  });
544
+ // GET /api/version — returns version information
545
+ app.get('/api/version', async (req, res) => {
546
+ try {
547
+ const versionInfo = await (0, version_1.getVersionInfo)();
548
+ res.json(versionInfo);
549
+ }
550
+ catch (err) {
551
+ res.status(500).json({ error: 'Failed to check version', status: 500 });
552
+ }
553
+ });
543
554
  // GET /api/hub/projects — Phase 2: returns all registered projects (optional hub feature)
544
555
  app.get('/api/hub/projects', (req, res) => {
545
556
  try {
@@ -0,0 +1,213 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.setProjectRoot = setProjectRoot;
37
+ exports.resetState = resetState;
38
+ exports.getVersionInfo = getVersionInfo;
39
+ const fs = __importStar(require("fs"));
40
+ const path = __importStar(require("path"));
41
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
42
+ const NPM_TIMEOUT_MS = 5000; // 5 seconds
43
+ const CACHE_FILE_NAME = 'version-cache.json';
44
+ let projectRoot = process.env.WAYMARK_PROJECT_ROOT || process.cwd();
45
+ function setProjectRoot(root) {
46
+ projectRoot = root;
47
+ }
48
+ function getCachePath() {
49
+ return path.join(projectRoot, '.waymark', CACHE_FILE_NAME);
50
+ }
51
+ function readCacheFile() {
52
+ const cachePath = getCachePath();
53
+ try {
54
+ if (!fs.existsSync(cachePath)) {
55
+ return null;
56
+ }
57
+ const content = fs.readFileSync(cachePath, 'utf-8');
58
+ const data = JSON.parse(content);
59
+ // Validate cache structure
60
+ if (!data.latestVersion || typeof data.timestamp !== 'number') {
61
+ return null;
62
+ }
63
+ return data;
64
+ }
65
+ catch (error) {
66
+ // Ignore any read/parse errors
67
+ return null;
68
+ }
69
+ }
70
+ function writeCacheFile(latestVersion) {
71
+ const cachePath = getCachePath();
72
+ const cacheDir = path.dirname(cachePath);
73
+ try {
74
+ // Ensure directory exists
75
+ if (!fs.existsSync(cacheDir)) {
76
+ fs.mkdirSync(cacheDir, { recursive: true });
77
+ }
78
+ const data = {
79
+ latestVersion,
80
+ timestamp: Date.now(),
81
+ };
82
+ fs.writeFileSync(cachePath, JSON.stringify(data, null, 2), 'utf-8');
83
+ }
84
+ catch (error) {
85
+ // Silently ignore write errors
86
+ }
87
+ }
88
+ function isCacheValid(cache) {
89
+ const age = Date.now() - cache.timestamp;
90
+ return age < CACHE_TTL_MS;
91
+ }
92
+ function compareVersions(current, latest) {
93
+ try {
94
+ const currentParts = current.split('.').map(Number);
95
+ const latestParts = latest.split('.').map(Number);
96
+ // Pad with zeros if needed
97
+ const maxLength = Math.max(currentParts.length, latestParts.length);
98
+ while (currentParts.length < maxLength)
99
+ currentParts.push(0);
100
+ while (latestParts.length < maxLength)
101
+ latestParts.push(0);
102
+ // Compare parts
103
+ for (let i = 0; i < maxLength; i++) {
104
+ if (latestParts[i] > currentParts[i]) {
105
+ return true;
106
+ }
107
+ if (latestParts[i] < currentParts[i]) {
108
+ return false;
109
+ }
110
+ }
111
+ return false; // Versions are equal
112
+ }
113
+ catch (error) {
114
+ // If comparison fails, assume no update
115
+ return false;
116
+ }
117
+ }
118
+ async function fetchLatestVersionFromNpm() {
119
+ try {
120
+ const controller = new AbortController();
121
+ const timeoutId = setTimeout(() => controller.abort(), NPM_TIMEOUT_MS);
122
+ const response = await fetch('https://registry.npmjs.org/@way_marks/cli/latest', {
123
+ signal: controller.signal,
124
+ });
125
+ clearTimeout(timeoutId);
126
+ if (!response.ok) {
127
+ return null;
128
+ }
129
+ const data = (await response.json());
130
+ if (!data.version || typeof data.version !== 'string') {
131
+ return null;
132
+ }
133
+ return data.version;
134
+ }
135
+ catch (error) {
136
+ // Ignore any fetch errors (timeout, network, etc.)
137
+ return null;
138
+ }
139
+ }
140
+ function getCurrentVersion() {
141
+ try {
142
+ const cliPackageJsonPath = path.join(projectRoot, 'packages', 'cli', 'package.json');
143
+ if (!fs.existsSync(cliPackageJsonPath)) {
144
+ return '0.0.0';
145
+ }
146
+ const content = fs.readFileSync(cliPackageJsonPath, 'utf-8');
147
+ const packageJson = JSON.parse(content);
148
+ return packageJson.version || '0.0.0';
149
+ }
150
+ catch (error) {
151
+ return '0.0.0';
152
+ }
153
+ }
154
+ let lastFetchPromise = null;
155
+ let lastFetchTime = 0;
156
+ function resetState() {
157
+ lastFetchPromise = null;
158
+ lastFetchTime = 0;
159
+ }
160
+ async function getVersionInfo() {
161
+ const currentVersion = getCurrentVersion();
162
+ // Check cache first
163
+ const cache = readCacheFile();
164
+ if (cache && isCacheValid(cache)) {
165
+ return {
166
+ currentVersion,
167
+ latestVersion: cache.latestVersion,
168
+ updateAvailable: compareVersions(currentVersion, cache.latestVersion),
169
+ };
170
+ }
171
+ // Prevent multiple concurrent fetches within a short window
172
+ const now = Date.now();
173
+ const timeSinceLastFetch = now - lastFetchTime;
174
+ if (lastFetchPromise && timeSinceLastFetch < 1000) {
175
+ // Return the pending fetch result
176
+ const latestVersion = await lastFetchPromise;
177
+ if (latestVersion) {
178
+ writeCacheFile(latestVersion);
179
+ return {
180
+ currentVersion,
181
+ latestVersion,
182
+ updateAvailable: compareVersions(currentVersion, latestVersion),
183
+ };
184
+ }
185
+ }
186
+ // Fetch from npm
187
+ lastFetchTime = now;
188
+ lastFetchPromise = fetchLatestVersionFromNpm();
189
+ const latestVersion = await lastFetchPromise;
190
+ if (latestVersion) {
191
+ // Successfully fetched, cache it
192
+ writeCacheFile(latestVersion);
193
+ return {
194
+ currentVersion,
195
+ latestVersion,
196
+ updateAvailable: compareVersions(currentVersion, latestVersion),
197
+ };
198
+ }
199
+ // Fetch failed, try to use existing cache
200
+ if (cache) {
201
+ return {
202
+ currentVersion,
203
+ latestVersion: cache.latestVersion,
204
+ updateAvailable: compareVersions(currentVersion, cache.latestVersion),
205
+ };
206
+ }
207
+ // No cache available, return current version as fallback
208
+ return {
209
+ currentVersion,
210
+ latestVersion: currentVersion,
211
+ updateAvailable: false,
212
+ };
213
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@way_marks/server",
3
- "version": "4.4.2",
3
+ "version": "4.4.4",
4
4
  "description": "Waymark MCP server and dashboard",
5
5
  "author": "Waymark <hello@waymarks.dev>",
6
6
  "license": "MIT",