@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.
- package/dist/agent/backend/filtered-backend.cjs +130 -0
- package/dist/agent/backend/filtered-backend.d.ts +10 -0
- package/dist/agent/backend/filtered-backend.js +87 -0
- package/dist/agent/middleware/additional-messages.cjs +4 -1
- package/dist/agent/middleware/additional-messages.js +4 -1
- package/dist/agent/tools/browser_control.cjs +1 -1
- package/dist/agent/tools/browser_control.js +1 -1
- package/dist/agent/tools/browser_runtime.cjs +184 -15
- package/dist/agent/tools/browser_runtime.d.ts +59 -2
- package/dist/agent/tools/browser_runtime.js +182 -16
- package/dist/agent/tools/browser_session.cjs +44 -8
- package/dist/agent/tools/browser_session.d.ts +68 -123
- package/dist/agent/tools/browser_session.js +45 -9
- package/dist/agent/tools/browser_session_manager.cjs +15 -4
- package/dist/agent/tools/browser_session_manager.d.ts +8 -2
- package/dist/agent/tools/browser_session_manager.js +15 -4
- package/dist/cli/commands/init.cjs +80 -102
- package/dist/cli/commands/init.js +80 -102
- package/dist/cli/core/agentInvoker.cjs +4 -2
- package/dist/cli/core/agentInvoker.js +4 -2
- package/dist/cli/core/sessionManager.cjs +208 -41
- package/dist/cli/core/sessionManager.d.ts +20 -0
- package/dist/cli/core/sessionManager.js +208 -41
- package/dist/cli/index.cjs +16 -1
- package/dist/cli/index.js +16 -1
- package/dist/cli/services/updateCheck.cjs +212 -0
- package/dist/cli/services/updateCheck.d.ts +26 -0
- package/dist/cli/services/updateCheck.js +166 -0
- package/dist/gateway/server.cjs +7 -0
- package/dist/gateway/server.js +7 -0
- package/dist/webui/assets/index-D3x3G75t.css +11 -0
- package/dist/webui/assets/index-UpMmcU1f.js +215 -0
- package/dist/webui/index.html +2 -2
- package/package.json +3 -3
- package/templates/agents/README.md +1 -0
- package/templates/agents/game-dev/agent.md +1 -0
- package/templates/agents/main/agent.md +3 -3
- package/templates/agents/researcher/agent.md +5 -5
- package/dist/webui/assets/index-XrEnkZiq.css +0 -11
- package/dist/webui/assets/index-mDs6HbKM.js +0 -215
- 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
|
|
452
|
-
|
|
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
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
-
|
|
721
|
+
extractAttachments(blocks);
|
|
661
722
|
if (ui?.uiOnly && ui?.textFallback) pendingFallback = ui.textFallback.trim();
|
|
662
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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)
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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)
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
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 ||
|
|
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 };
|
package/dist/cli/index.cjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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>;
|