@wingman-ai/gateway 0.5.4 → 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 (41) hide show
  1. package/dist/agent/backend/filtered-backend.cjs +130 -0
  2. package/dist/agent/backend/filtered-backend.d.ts +10 -0
  3. package/dist/agent/backend/filtered-backend.js +87 -0
  4. package/dist/agent/middleware/additional-messages.cjs +4 -1
  5. package/dist/agent/middleware/additional-messages.js +4 -1
  6. package/dist/agent/tools/browser_control.cjs +1 -1
  7. package/dist/agent/tools/browser_control.js +1 -1
  8. package/dist/agent/tools/browser_runtime.cjs +184 -15
  9. package/dist/agent/tools/browser_runtime.d.ts +59 -2
  10. package/dist/agent/tools/browser_runtime.js +182 -16
  11. package/dist/agent/tools/browser_session.cjs +44 -8
  12. package/dist/agent/tools/browser_session.d.ts +68 -123
  13. package/dist/agent/tools/browser_session.js +45 -9
  14. package/dist/agent/tools/browser_session_manager.cjs +15 -4
  15. package/dist/agent/tools/browser_session_manager.d.ts +8 -2
  16. package/dist/agent/tools/browser_session_manager.js +15 -4
  17. package/dist/cli/commands/init.cjs +80 -102
  18. package/dist/cli/commands/init.js +80 -102
  19. package/dist/cli/core/agentInvoker.cjs +4 -2
  20. package/dist/cli/core/agentInvoker.js +4 -2
  21. package/dist/cli/core/sessionManager.cjs +208 -41
  22. package/dist/cli/core/sessionManager.d.ts +20 -0
  23. package/dist/cli/core/sessionManager.js +208 -41
  24. package/dist/cli/index.cjs +16 -1
  25. package/dist/cli/index.js +16 -1
  26. package/dist/cli/services/updateCheck.cjs +212 -0
  27. package/dist/cli/services/updateCheck.d.ts +26 -0
  28. package/dist/cli/services/updateCheck.js +166 -0
  29. package/dist/gateway/server.cjs +7 -0
  30. package/dist/gateway/server.js +7 -0
  31. package/dist/webui/assets/index-D3x3G75t.css +11 -0
  32. package/dist/webui/assets/index-UpMmcU1f.js +215 -0
  33. package/dist/webui/index.html +2 -2
  34. package/package.json +3 -3
  35. package/templates/agents/README.md +1 -0
  36. package/templates/agents/game-dev/agent.md +1 -0
  37. package/templates/agents/main/agent.md +3 -3
  38. package/templates/agents/researcher/agent.md +5 -5
  39. package/dist/webui/assets/index-XrEnkZiq.css +0 -11
  40. package/dist/webui/assets/index-mDs6HbKM.js +0 -215
  41. package/dist/webui/assets/wingman_logo-Cogyt3qm.webp +0 -0
@@ -448,29 +448,52 @@ function toSessionMessage(entry, index, baseTime) {
448
448
  if ("user" !== role && "assistant" !== role) {
449
449
  if (isToolMessage(entry)) {
450
450
  const blocks = extractContentBlocks(entry);
451
- const toolContent = extractMessageContent(entry, blocks);
452
- const ui = extractUiFromPayload(toolContent);
451
+ const toolResult = extractPersistedToolResult(entry, index);
452
+ if (!toolResult) return null;
453
+ const { ui, uiOnly, textFallback, data: toolOutput } = splitPersistedToolPayload(toolResult.output);
453
454
  const attachments = extractAttachments(blocks);
454
- if (ui?.spec || attachments.length > 0) {
455
- const content = toolContent || ui?.textFallback || "";
456
- return {
457
- id: `msg-${index}`,
458
- role: "assistant",
459
- content,
460
- attachments: attachments.length > 0 ? attachments : void 0,
461
- createdAt: baseTime + index,
462
- ...ui?.spec ? {
463
- uiBlocks: [
464
- {
465
- spec: ui.spec,
466
- uiOnly: ui.uiOnly,
467
- textFallback: ui.textFallback
468
- }
469
- ],
470
- uiTextFallback: ui.textFallback
471
- } : {}
472
- };
473
- }
455
+ const eventTimestamp = baseTime + index;
456
+ return {
457
+ id: `msg-${index}`,
458
+ role: "assistant",
459
+ content: "",
460
+ attachments: attachments.length > 0 ? attachments : void 0,
461
+ toolEvents: [
462
+ {
463
+ id: toolResult.id,
464
+ name: toolResult.name || "tool",
465
+ status: toolResult.error ? "error" : "completed",
466
+ output: toolOutput,
467
+ ui,
468
+ uiOnly,
469
+ textFallback,
470
+ error: toolResult.error,
471
+ timestamp: eventTimestamp,
472
+ startedAt: eventTimestamp,
473
+ completedAt: eventTimestamp
474
+ }
475
+ ],
476
+ activityTimeline: [
477
+ {
478
+ id: `tl-tool-${toolResult.id}`,
479
+ kind: "tool",
480
+ order: eventTimestamp,
481
+ toolEventId: toolResult.id
482
+ }
483
+ ],
484
+ createdAt: eventTimestamp,
485
+ ...ui ? {
486
+ uiBlocks: [
487
+ {
488
+ id: toolResult.id,
489
+ spec: ui,
490
+ uiOnly,
491
+ textFallback
492
+ }
493
+ ],
494
+ uiTextFallback: textFallback
495
+ } : {}
496
+ };
474
497
  }
475
498
  return null;
476
499
  }
@@ -641,6 +664,44 @@ function isTextLikeContentType(type) {
641
664
  const normalized = type.toLowerCase();
642
665
  return "text" === normalized || "input_text" === normalized || "output_text" === normalized || "text_delta" === normalized;
643
666
  }
667
+ function extractPersistedToolResult(entry, index) {
668
+ const toolCallId = entry?.tool_call_id ?? entry?.kwargs?.tool_call_id ?? entry?.additional_kwargs?.tool_call_id ?? ("string" == typeof entry?.id ? entry.id : null) ?? ("string" == typeof entry?.kwargs?.id ? entry.kwargs.id : null) ?? ("string" == typeof entry?.additional_kwargs?.id ? entry.additional_kwargs.id : null) ?? `tool-${index}`;
669
+ if (!toolCallId) return null;
670
+ return {
671
+ id: String(toolCallId),
672
+ name: entry?.name ?? entry?.kwargs?.name ?? entry?.additional_kwargs?.name,
673
+ output: entry?.content ?? entry?.kwargs?.content ?? "",
674
+ error: entry?.kwargs?.error ?? entry?.additional_kwargs?.error
675
+ };
676
+ }
677
+ function splitPersistedToolPayload(payload) {
678
+ if ("string" == typeof payload) try {
679
+ const parsed = JSON.parse(payload);
680
+ if (parsed && "object" == typeof parsed) return splitPersistedToolPayload(parsed);
681
+ } catch {
682
+ return {
683
+ data: payload
684
+ };
685
+ }
686
+ if (!payload || "object" != typeof payload) return {
687
+ data: payload
688
+ };
689
+ if (!("ui" in payload)) {
690
+ const content = "string" == typeof payload.content ? payload.content : "string" == typeof payload?.kwargs?.content ? payload.kwargs.content : null;
691
+ if (content) return splitPersistedToolPayload(content);
692
+ return {
693
+ data: payload
694
+ };
695
+ }
696
+ const { ui, uiOnly, textFallback, ...rest } = payload;
697
+ const validUi = ui && "object" == typeof ui && Array.isArray(ui.components) ? ui : void 0;
698
+ return {
699
+ ui: validUi,
700
+ uiOnly: "boolean" == typeof uiOnly ? uiOnly : void 0,
701
+ textFallback: "string" == typeof textFallback ? textFallback : void 0,
702
+ data: validUi ? rest : payload
703
+ };
704
+ }
644
705
  function isToolMessage(entry) {
645
706
  if (!entry || "object" != typeof entry) return false;
646
707
  const role = entry.role || entry?.kwargs?.role || entry?.additional_kwargs?.role;
@@ -657,9 +718,9 @@ function filterUiOnlyAssistantMessages(messages) {
657
718
  const blocks = extractContentBlocks(entry);
658
719
  const content = extractMessageContent(entry, blocks);
659
720
  const ui = extractUiFromPayload(content);
660
- const attachments = extractAttachments(blocks);
721
+ extractAttachments(blocks);
661
722
  if (ui?.uiOnly && ui?.textFallback) pendingFallback = ui.textFallback.trim();
662
- if (ui?.spec || attachments.length > 0) filtered.push(entry);
723
+ filtered.push(entry);
663
724
  continue;
664
725
  }
665
726
  const role = resolveMessageRole(entry);
@@ -753,7 +814,7 @@ function extractImageUrl(block) {
753
814
  }
754
815
  if ("resource_link" === block.type) {
755
816
  const mimeType = "string" == typeof block.mimeType ? block.mimeType.trim().toLowerCase() : "";
756
- const uri = "string" == typeof block.uri ? block.uri.trim() : "";
817
+ const uri = normalizeLocalAttachmentUrl("string" == typeof block.uri ? block.uri.trim() : "");
757
818
  if (uri && (!mimeType || mimeType.startsWith("image/"))) return uri;
758
819
  }
759
820
  if ("resource" === block.type && block.resource) {
@@ -761,7 +822,8 @@ function extractImageUrl(block) {
761
822
  const mimeType = "string" == typeof resource.mimeType ? resource.mimeType.trim().toLowerCase() : "";
762
823
  if (!mimeType || !mimeType.startsWith("image/")) return null;
763
824
  if ("string" == typeof resource.blob && resource.blob.trim()) return `data:${mimeType};base64,${resource.blob.trim()}`;
764
- if ("string" == typeof resource.uri && resource.uri.trim()) return resource.uri.trim();
825
+ const normalizedUri = normalizeLocalAttachmentUrl("string" == typeof resource.uri ? resource.uri.trim() : "");
826
+ if (normalizedUri) return normalizedUri;
765
827
  }
766
828
  return null;
767
829
  }
@@ -804,8 +866,103 @@ function parseDataUrlMime(dataUrl) {
804
866
  function extractString(...values) {
805
867
  for (const value of values)if ("string" == typeof value && value.trim().length > 0) return value.trim();
806
868
  }
869
+ function normalizeLocalAttachmentUrl(value) {
870
+ const trimmed = extractString(value);
871
+ if (!trimmed) return;
872
+ if (trimmed.startsWith("http://") || trimmed.startsWith("https://") || trimmed.startsWith("data:") || trimmed.startsWith("blob:") || trimmed.startsWith("/api/fs/file?")) return trimmed;
873
+ const filesystemPath = extractLocalFilesystemPath(trimmed);
874
+ if (!filesystemPath) return trimmed;
875
+ return `/api/fs/file?path=${encodeURIComponent(filesystemPath)}`;
876
+ }
877
+ function extractLocalFilesystemPath(value) {
878
+ if (value.startsWith("file://")) try {
879
+ const parsed = new URL(value);
880
+ let pathname = decodeURIComponent(parsed.pathname || "");
881
+ if (!pathname) return null;
882
+ if (/^\/[A-Za-z]:[\\/]/.test(pathname)) pathname = pathname.slice(1);
883
+ if (parsed.host && !/^[A-Za-z]:[\\/]/.test(pathname)) return `//${parsed.host}${pathname}`;
884
+ return pathname;
885
+ } catch {
886
+ return null;
887
+ }
888
+ if (/^[A-Za-z]:[\\/]/.test(value)) return value;
889
+ if (value.startsWith("/") && !value.startsWith("//") && !value.startsWith("/api/")) return value;
890
+ return null;
891
+ }
807
892
  function extractFileAttachment(block) {
808
893
  if (!block || "object" != typeof block) return null;
894
+ if ("resource_link" === block.type) {
895
+ const uri = normalizeLocalAttachmentUrl(extractString(block.uri));
896
+ if (!uri) return null;
897
+ return {
898
+ kind: "file",
899
+ dataUrl: uri,
900
+ name: extractString(block.name),
901
+ mimeType: extractString(block.mimeType)
902
+ };
903
+ }
904
+ if ("resource" === block.type && block.resource) {
905
+ const resource = "object" == typeof block.resource ? block.resource : null;
906
+ if (!resource) return null;
907
+ const mimeType = extractString(resource.mimeType);
908
+ const blob = extractString(resource.blob);
909
+ const uri = extractString(resource.uri);
910
+ const name = extractString(resource.name, block.name);
911
+ if (blob && mimeType) return {
912
+ kind: "file",
913
+ dataUrl: `data:${mimeType};base64,${blob}`,
914
+ name,
915
+ mimeType
916
+ };
917
+ const normalizedUri = normalizeLocalAttachmentUrl(uri);
918
+ if (normalizedUri) return {
919
+ kind: "file",
920
+ dataUrl: normalizedUri,
921
+ name,
922
+ mimeType
923
+ };
924
+ }
925
+ if ("video" === block.type) {
926
+ const sourceType = block.source_type || block.sourceType;
927
+ const mimeType = extractString(block.mime_type, block.mimeType, block.media_type, block.mediaType) || "video/webm";
928
+ const name = extractString(block.name, block.filename);
929
+ if ("base64" === sourceType && "string" == typeof block.data) return {
930
+ kind: "file",
931
+ dataUrl: `data:${mimeType};base64,${block.data}`,
932
+ name,
933
+ mimeType
934
+ };
935
+ if ("url" === sourceType && "string" == typeof block.url) {
936
+ const normalizedUrl = normalizeLocalAttachmentUrl(block.url);
937
+ if (!normalizedUrl) return null;
938
+ return {
939
+ kind: "file",
940
+ dataUrl: normalizedUrl,
941
+ name,
942
+ mimeType
943
+ };
944
+ }
945
+ if (block.source && "object" == typeof block.source) {
946
+ const source = block.source;
947
+ const sourceMimeType = extractString(source.media_type, source.mediaType, mimeType);
948
+ if ("string" == typeof source.data && sourceMimeType) return {
949
+ kind: "file",
950
+ dataUrl: `data:${sourceMimeType};base64,${source.data}`,
951
+ name,
952
+ mimeType: sourceMimeType
953
+ };
954
+ if ("string" == typeof source.url) {
955
+ const normalizedUrl = normalizeLocalAttachmentUrl(source.url);
956
+ if (!normalizedUrl) return null;
957
+ return {
958
+ kind: "file",
959
+ dataUrl: normalizedUrl,
960
+ name,
961
+ mimeType: sourceMimeType
962
+ };
963
+ }
964
+ }
965
+ }
809
966
  if ("file" === block.type) {
810
967
  const sourceType = block.source_type || block.sourceType;
811
968
  const metadata = block.metadata && "object" == typeof block.metadata ? block.metadata : {};
@@ -821,10 +978,12 @@ function extractFileAttachment(block) {
821
978
  };
822
979
  }
823
980
  if ("url" === sourceType && "string" == typeof block.url) {
824
- const mimeType = declaredMime || parseDataUrlMime(block.url);
981
+ const normalizedUrl = normalizeLocalAttachmentUrl(block.url);
982
+ if (!normalizedUrl) return null;
983
+ const mimeType = declaredMime || parseDataUrlMime(normalizedUrl);
825
984
  return {
826
985
  kind: "file",
827
- dataUrl: block.url,
986
+ dataUrl: normalizedUrl,
828
987
  name,
829
988
  mimeType
830
989
  };
@@ -840,12 +999,16 @@ function extractFileAttachment(block) {
840
999
  name: fileName,
841
1000
  mimeType: parseDataUrlMime(fileData)
842
1001
  };
843
- if (fileUrl) return {
844
- kind: "file",
845
- dataUrl: fileUrl,
846
- name: fileName,
847
- mimeType: parseDataUrlMime(fileUrl)
848
- };
1002
+ if (fileUrl) {
1003
+ const normalizedUrl = normalizeLocalAttachmentUrl(fileUrl);
1004
+ if (!normalizedUrl) return null;
1005
+ return {
1006
+ kind: "file",
1007
+ dataUrl: normalizedUrl,
1008
+ name: fileName,
1009
+ mimeType: parseDataUrlMime(normalizedUrl)
1010
+ };
1011
+ }
849
1012
  }
850
1013
  }
851
1014
  if ("input_file" === block.type) {
@@ -871,12 +1034,16 @@ function extractFileAttachment(block) {
871
1034
  mimeType
872
1035
  };
873
1036
  }
874
- if ("url" === sourceType && "string" == typeof source.url) return {
875
- kind: "file",
876
- dataUrl: source.url,
877
- name,
878
- mimeType: parseDataUrlMime(source.url)
879
- };
1037
+ if ("url" === sourceType && "string" == typeof source.url) {
1038
+ const normalizedUrl = normalizeLocalAttachmentUrl(source.url);
1039
+ if (!normalizedUrl) return null;
1040
+ return {
1041
+ kind: "file",
1042
+ dataUrl: normalizedUrl,
1043
+ name,
1044
+ mimeType: parseDataUrlMime(normalizedUrl)
1045
+ };
1046
+ }
880
1047
  }
881
1048
  return null;
882
1049
  }
@@ -939,7 +1106,7 @@ function scoreMessages(candidate, baseTime) {
939
1106
  };
940
1107
  }
941
1108
  function filterEmptyAssistantMessages(messages) {
942
- const filtered = messages.filter((message)=>"assistant" !== message.role || message.content.trim().length > 0 || (message.attachments?.length ?? 0) > 0 || Array.isArray(message.uiBlocks) && message.uiBlocks.length > 0);
1109
+ const filtered = messages.filter((message)=>"assistant" !== message.role || message.content.trim().length > 0 || (message.attachments?.length ?? 0) > 0 || (message.uiBlocks?.length ?? 0) > 0 || (message.toolEvents?.length ?? 0) > 0 || (message.activityTimeline?.length ?? 0) > 0);
943
1110
  return filtered.length > 0 ? filtered : messages;
944
1111
  }
945
1112
  export { SessionManager, extractAttachments, extractImageAttachments, extractImageUrl, extractMessagesFromState, resolveMessageRole };
@@ -12,6 +12,7 @@ const skill_cjs_namespaceObject = require("./commands/skill.cjs");
12
12
  const loader_cjs_namespaceObject = require("./config/loader.cjs");
13
13
  const outputManager_cjs_namespaceObject = require("./core/outputManager.cjs");
14
14
  const workspace_cjs_namespaceObject = require("./core/workspace.cjs");
15
+ const updateCheck_cjs_namespaceObject = require("./services/updateCheck.cjs");
15
16
  function parseArgs(argv) {
16
17
  const args = argv.slice(2);
17
18
  if (args.includes("--help") || args.includes("-h")) return {
@@ -149,6 +150,11 @@ For gateway help:
149
150
  wingman gateway --help
150
151
  `);
151
152
  }
153
+ function showCliUpdateNotice(notice) {
154
+ console.warn(`Update available for Wingman: ${notice.currentVersion} -> ${notice.latestVersion}`);
155
+ console.warn(`Upgrade with: ${notice.command}`);
156
+ console.warn("");
157
+ }
152
158
  async function main() {
153
159
  try {
154
160
  const parsed = parseArgs(process.argv);
@@ -163,6 +169,15 @@ async function main() {
163
169
  let outputMode;
164
170
  outputMode = "interactive" === parsed.outputMode || "json" === parsed.outputMode ? parsed.outputMode : "auto" === config.cli.outputMode ? outputManager_cjs_namespaceObject.OutputManager.detectMode() : config.cli.outputMode;
165
171
  const verbosity = determineVerbosity(parsed.verbosity, config.logLevel);
172
+ const logger = (0, external_logger_cjs_namespaceObject.createLogger)(verbosity);
173
+ if ("interactive" === outputMode && !process.env.CI) {
174
+ const updateNotice = await (0, updateCheck_cjs_namespaceObject.checkForCliUpdate)({
175
+ workspace,
176
+ configDir,
177
+ logger
178
+ });
179
+ if (updateNotice) showCliUpdateNotice(updateNotice);
180
+ }
166
181
  if ("agent" === parsed.command) {
167
182
  const commandArgs = {
168
183
  agent: parsed.agent || config.defaultAgent,
@@ -239,7 +254,7 @@ async function main() {
239
254
  });
240
255
  } else {
241
256
  const logFile = (0, external_logger_cjs_namespaceObject.getLogFilePath)();
242
- (0, external_logger_cjs_namespaceObject.createLogger)(verbosity).error(`Unknown command: ${parsed.command}`);
257
+ logger.error(`Unknown command: ${parsed.command}`);
243
258
  console.error(`Unknown command: ${parsed.command}`);
244
259
  console.error('Run "wingman --help" for usage information');
245
260
  console.error(`Logs: ${logFile}`);
package/dist/cli/index.js CHANGED
@@ -10,6 +10,7 @@ import { executeSkillCommand } from "./commands/skill.js";
10
10
  import { WingmanConfigLoader } from "./config/loader.js";
11
11
  import { OutputManager } from "./core/outputManager.js";
12
12
  import { resolveWorkspaceRoot } from "./core/workspace.js";
13
+ import { checkForCliUpdate } from "./services/updateCheck.js";
13
14
  function parseArgs(argv) {
14
15
  const args = argv.slice(2);
15
16
  if (args.includes("--help") || args.includes("-h")) return {
@@ -147,6 +148,11 @@ For gateway help:
147
148
  wingman gateway --help
148
149
  `);
149
150
  }
151
+ function showCliUpdateNotice(notice) {
152
+ console.warn(`Update available for Wingman: ${notice.currentVersion} -> ${notice.latestVersion}`);
153
+ console.warn(`Upgrade with: ${notice.command}`);
154
+ console.warn("");
155
+ }
150
156
  async function main() {
151
157
  try {
152
158
  const parsed = parseArgs(process.argv);
@@ -161,6 +167,15 @@ async function main() {
161
167
  let outputMode;
162
168
  outputMode = "interactive" === parsed.outputMode || "json" === parsed.outputMode ? parsed.outputMode : "auto" === config.cli.outputMode ? OutputManager.detectMode() : config.cli.outputMode;
163
169
  const verbosity = determineVerbosity(parsed.verbosity, config.logLevel);
170
+ const logger = createLogger(verbosity);
171
+ if ("interactive" === outputMode && !process.env.CI) {
172
+ const updateNotice = await checkForCliUpdate({
173
+ workspace,
174
+ configDir,
175
+ logger
176
+ });
177
+ if (updateNotice) showCliUpdateNotice(updateNotice);
178
+ }
164
179
  if ("agent" === parsed.command) {
165
180
  const commandArgs = {
166
181
  agent: parsed.agent || config.defaultAgent,
@@ -237,7 +252,7 @@ async function main() {
237
252
  });
238
253
  } else {
239
254
  const logFile = getLogFilePath();
240
- createLogger(verbosity).error(`Unknown command: ${parsed.command}`);
255
+ logger.error(`Unknown command: ${parsed.command}`);
241
256
  console.error(`Unknown command: ${parsed.command}`);
242
257
  console.error('Run "wingman --help" for usage information');
243
258
  console.error(`Logs: ${logFile}`);
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ const __rslib_import_meta_url__ = /*#__PURE__*/ function() {
3
+ return "u" < typeof document ? new (require('url'.replace('', ''))).URL('file:' + __filename).href : document.currentScript && document.currentScript.src || new URL('main.js', document.baseURI).href;
4
+ }();
5
+ var __webpack_require__ = {};
6
+ (()=>{
7
+ __webpack_require__.d = (exports1, definition)=>{
8
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
9
+ enumerable: true,
10
+ get: definition[key]
11
+ });
12
+ };
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
16
+ })();
17
+ (()=>{
18
+ __webpack_require__.r = (exports1)=>{
19
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
20
+ value: 'Module'
21
+ });
22
+ Object.defineProperty(exports1, '__esModule', {
23
+ value: true
24
+ });
25
+ };
26
+ })();
27
+ var __webpack_exports__ = {};
28
+ __webpack_require__.r(__webpack_exports__);
29
+ __webpack_require__.d(__webpack_exports__, {
30
+ resolveCliUpdateCheckCachePath: ()=>resolveCliUpdateCheckCachePath,
31
+ compareCliVersions: ()=>compareCliVersions,
32
+ loadCliPackageMetadata: ()=>loadCliPackageMetadata,
33
+ checkForCliUpdate: ()=>checkForCliUpdate
34
+ });
35
+ const external_node_fs_namespaceObject = require("node:fs");
36
+ const promises_namespaceObject = require("node:fs/promises");
37
+ const external_node_os_namespaceObject = require("node:os");
38
+ const external_node_path_namespaceObject = require("node:path");
39
+ const DEFAULT_TIMEOUT_MS = 1500;
40
+ const DEFAULT_CACHE_TTL_MS = 43200000;
41
+ async function loadCliPackageMetadata(packageJsonUrl = new URL("../../../package.json", __rslib_import_meta_url__)) {
42
+ try {
43
+ const raw = await (0, promises_namespaceObject.readFile)(packageJsonUrl, "utf-8");
44
+ const parsed = JSON.parse(raw);
45
+ if ("string" != typeof parsed.name || "string" != typeof parsed.version) return null;
46
+ const name = parsed.name.trim();
47
+ const version = parsed.version.trim();
48
+ if (!name || !version) return null;
49
+ return {
50
+ name,
51
+ version
52
+ };
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+ function compareCliVersions(left, right) {
58
+ const parsedLeft = parseVersion(left);
59
+ const parsedRight = parseVersion(right);
60
+ if (!parsedLeft || !parsedRight) return 0;
61
+ for(let index = 0; index < parsedLeft.core.length; index++){
62
+ const diff = parsedLeft.core[index] - parsedRight.core[index];
63
+ if (0 !== diff) return diff;
64
+ }
65
+ return comparePrerelease(parsedLeft.prerelease, parsedRight.prerelease);
66
+ }
67
+ function resolveCliUpdateCheckCachePath(packageName, options = {}) {
68
+ const workspace = options.workspace?.trim();
69
+ const configDir = options.configDir?.trim() || ".wingman";
70
+ if (workspace) {
71
+ const configRoot = (0, external_node_path_namespaceObject.join)(workspace, configDir);
72
+ if ((0, external_node_fs_namespaceObject.existsSync)(configRoot)) return (0, external_node_path_namespaceObject.join)(configRoot, "cache", "update-check.json");
73
+ }
74
+ return (0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-update-check", sanitizePackageName(packageName), "update-check.json");
75
+ }
76
+ async function checkForCliUpdate(options = {}) {
77
+ const packageMetadata = options.packageMetadata ?? await loadCliPackageMetadata(options.packageJsonUrl);
78
+ if (!packageMetadata) return null;
79
+ const now = options.now ?? new Date();
80
+ const cacheTtlMs = options.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
81
+ const cachePath = resolveCliUpdateCheckCachePath(packageMetadata.name, options);
82
+ const cached = await readCachedUpdateCheck(cachePath);
83
+ if (cached && isCacheUsable(cached, packageMetadata, now, cacheTtlMs)) return toUpdateNotice(packageMetadata.name, packageMetadata.version, cached.latestVersion);
84
+ const latestVersion = await fetchLatestPackageVersion(packageMetadata.name, options.fetchImpl ?? globalThis.fetch, options.timeoutMs ?? DEFAULT_TIMEOUT_MS, options.logger);
85
+ if (latestVersion) {
86
+ const nextCacheEntry = {
87
+ packageName: packageMetadata.name,
88
+ currentVersion: packageMetadata.version,
89
+ latestVersion,
90
+ checkedAt: now.toISOString()
91
+ };
92
+ try {
93
+ await writeCachedUpdateCheck(cachePath, nextCacheEntry);
94
+ } catch (error) {
95
+ options.logger?.debug?.("Failed to persist CLI update-check cache", error instanceof Error ? error.message : String(error));
96
+ }
97
+ return toUpdateNotice(packageMetadata.name, packageMetadata.version, latestVersion);
98
+ }
99
+ if (cached && cached.packageName === packageMetadata.name && cached.currentVersion === packageMetadata.version) return toUpdateNotice(packageMetadata.name, packageMetadata.version, cached.latestVersion);
100
+ return null;
101
+ }
102
+ function parseVersion(version) {
103
+ const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$/.exec(version.trim());
104
+ if (!match) return null;
105
+ return {
106
+ core: [
107
+ Number.parseInt(match[1], 10),
108
+ Number.parseInt(match[2], 10),
109
+ Number.parseInt(match[3], 10)
110
+ ],
111
+ prerelease: match[4] ? match[4].split(".") : []
112
+ };
113
+ }
114
+ function comparePrerelease(left, right) {
115
+ if (0 === left.length && 0 === right.length) return 0;
116
+ if (0 === left.length) return 1;
117
+ if (0 === right.length) return -1;
118
+ const maxLength = Math.max(left.length, right.length);
119
+ for(let index = 0; index < maxLength; index++){
120
+ const leftIdentifier = left[index];
121
+ const rightIdentifier = right[index];
122
+ if (void 0 === leftIdentifier) return -1;
123
+ if (void 0 === rightIdentifier) return 1;
124
+ if (leftIdentifier === rightIdentifier) continue;
125
+ const leftIsNumeric = isNumericIdentifier(leftIdentifier);
126
+ const rightIsNumeric = isNumericIdentifier(rightIdentifier);
127
+ if (leftIsNumeric && rightIsNumeric) return Number.parseInt(leftIdentifier, 10) - Number.parseInt(rightIdentifier, 10);
128
+ if (leftIsNumeric) return -1;
129
+ if (rightIsNumeric) return 1;
130
+ return leftIdentifier < rightIdentifier ? -1 : 1;
131
+ }
132
+ return 0;
133
+ }
134
+ function isNumericIdentifier(value) {
135
+ return /^\d+$/.test(value);
136
+ }
137
+ function sanitizePackageName(packageName) {
138
+ return packageName.replace(/[^a-zA-Z0-9._-]+/g, "-");
139
+ }
140
+ function toUpdateNotice(packageName, currentVersion, latestVersion) {
141
+ if (compareCliVersions(latestVersion, currentVersion) <= 0) return null;
142
+ return {
143
+ packageName,
144
+ currentVersion,
145
+ latestVersion,
146
+ command: `npm install -g ${packageName}@latest`
147
+ };
148
+ }
149
+ function isCacheUsable(cacheEntry, packageMetadata, now, cacheTtlMs) {
150
+ if (cacheEntry.packageName !== packageMetadata.name) return false;
151
+ if (cacheEntry.currentVersion !== packageMetadata.version) return false;
152
+ const checkedAtMs = Date.parse(cacheEntry.checkedAt);
153
+ if (!Number.isFinite(checkedAtMs)) return false;
154
+ return now.getTime() - checkedAtMs <= cacheTtlMs;
155
+ }
156
+ async function readCachedUpdateCheck(cachePath) {
157
+ try {
158
+ const raw = await (0, promises_namespaceObject.readFile)(cachePath, "utf-8");
159
+ const parsed = JSON.parse(raw);
160
+ if ("string" != typeof parsed.packageName || "string" != typeof parsed.currentVersion || "string" != typeof parsed.latestVersion || "string" != typeof parsed.checkedAt) return null;
161
+ return {
162
+ packageName: parsed.packageName,
163
+ currentVersion: parsed.currentVersion,
164
+ latestVersion: parsed.latestVersion,
165
+ checkedAt: parsed.checkedAt
166
+ };
167
+ } catch {
168
+ return null;
169
+ }
170
+ }
171
+ async function writeCachedUpdateCheck(cachePath, cacheEntry) {
172
+ await (0, promises_namespaceObject.mkdir)((0, external_node_path_namespaceObject.dirname)(cachePath), {
173
+ recursive: true
174
+ });
175
+ await (0, promises_namespaceObject.writeFile)(cachePath, `${JSON.stringify(cacheEntry, null, 2)}\n`, "utf-8");
176
+ }
177
+ async function fetchLatestPackageVersion(packageName, fetchImpl, timeoutMs, logger) {
178
+ if ("function" != typeof fetchImpl) return null;
179
+ const controller = new AbortController();
180
+ const timeout = setTimeout(()=>controller.abort(), timeoutMs);
181
+ try {
182
+ const response = await fetchImpl(`https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`, {
183
+ headers: {
184
+ accept: "application/json"
185
+ },
186
+ signal: controller.signal
187
+ });
188
+ if (!response.ok) return null;
189
+ const payload = await response.json();
190
+ if ("string" != typeof payload.version) return null;
191
+ const latestVersion = payload.version.trim();
192
+ return latestVersion || null;
193
+ } catch (error) {
194
+ logger?.debug?.("CLI update check skipped", error instanceof Error ? error.message : String(error));
195
+ return null;
196
+ } finally{
197
+ clearTimeout(timeout);
198
+ }
199
+ }
200
+ exports.checkForCliUpdate = __webpack_exports__.checkForCliUpdate;
201
+ exports.compareCliVersions = __webpack_exports__.compareCliVersions;
202
+ exports.loadCliPackageMetadata = __webpack_exports__.loadCliPackageMetadata;
203
+ exports.resolveCliUpdateCheckCachePath = __webpack_exports__.resolveCliUpdateCheckCachePath;
204
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
205
+ "checkForCliUpdate",
206
+ "compareCliVersions",
207
+ "loadCliPackageMetadata",
208
+ "resolveCliUpdateCheckCachePath"
209
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
210
+ Object.defineProperty(exports, '__esModule', {
211
+ value: true
212
+ });
@@ -0,0 +1,26 @@
1
+ import type { Logger } from "@/logger.js";
2
+ export interface CliPackageMetadata {
3
+ name: string;
4
+ version: string;
5
+ }
6
+ export interface CliUpdateNotice {
7
+ packageName: string;
8
+ currentVersion: string;
9
+ latestVersion: string;
10
+ command: string;
11
+ }
12
+ export interface CliUpdateCheckOptions {
13
+ workspace?: string;
14
+ configDir?: string;
15
+ fetchImpl?: typeof fetch;
16
+ now?: Date;
17
+ timeoutMs?: number;
18
+ cacheTtlMs?: number;
19
+ logger?: Pick<Logger, "debug">;
20
+ packageMetadata?: CliPackageMetadata;
21
+ packageJsonUrl?: URL;
22
+ }
23
+ export declare function loadCliPackageMetadata(packageJsonUrl?: URL): Promise<CliPackageMetadata | null>;
24
+ export declare function compareCliVersions(left: string, right: string): number;
25
+ export declare function resolveCliUpdateCheckCachePath(packageName: string, options?: Pick<CliUpdateCheckOptions, "workspace" | "configDir">): string;
26
+ export declare function checkForCliUpdate(options?: CliUpdateCheckOptions): Promise<CliUpdateNotice | null>;