waitspin 0.1.13 → 0.1.14
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 +31 -10
- package/dist/cli-format.js +60 -10
- package/dist/cli-format.js.map +1 -1
- package/dist/cli.js +449 -11
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -84,8 +84,44 @@ const QODER_HOOK_TIMEOUT_SECONDS = 15;
|
|
|
84
84
|
const QODER_HOOK_STATUS_MESSAGE = "WaitSpin sponsor check";
|
|
85
85
|
const QODER_HOOK_EVENTS = ["UserPromptSubmit", "Stop"];
|
|
86
86
|
const QODER_ACCEPTANCE_HINT = "Run a real Qoder TUI prompt and keep the sponsored system message visible for at least 5 seconds. WaitSpin schedules a delayed visibility check after display; Stop or next-prompt hooks refresh the same managed state.";
|
|
87
|
+
const WAITSPIN_EDITOR_EXTENSION_ID = "waitspin.waitspin-vscode";
|
|
88
|
+
const EDITOR_PUBLISHER_TARGET = "status-bar-fallback";
|
|
87
89
|
const extensionTargets = {
|
|
88
|
-
vscode:
|
|
90
|
+
vscode: {
|
|
91
|
+
label: "VS Code",
|
|
92
|
+
mode: "bundled",
|
|
93
|
+
publisherTarget: EDITOR_PUBLISHER_TARGET,
|
|
94
|
+
},
|
|
95
|
+
cursor: {
|
|
96
|
+
label: "Cursor",
|
|
97
|
+
mode: "editor-cli",
|
|
98
|
+
publisherTarget: EDITOR_PUBLISHER_TARGET,
|
|
99
|
+
registryLabel: "VS Code Marketplace",
|
|
100
|
+
registryUrl: "https://marketplace.visualstudio.com/items?itemName=waitspin.waitspin-vscode",
|
|
101
|
+
binaryEnv: "WAITSPIN_CURSOR_EDITOR_BIN",
|
|
102
|
+
defaultBinary: "cursor",
|
|
103
|
+
windowsDefaultBinaries: ["cursor"],
|
|
104
|
+
windowsRelativeBinaries: [["Programs", "cursor", "Cursor.exe"]],
|
|
105
|
+
productMarker: "cursor",
|
|
106
|
+
executableBasenames: ["cursor", "cursor-editor"],
|
|
107
|
+
macosAppBinary: "/Applications/Cursor.app/Contents/Resources/app/bin/cursor",
|
|
108
|
+
macosUserAppBinary: path.join(os.homedir(), "Applications", "Cursor.app", "Contents", "Resources", "app", "bin", "cursor"),
|
|
109
|
+
},
|
|
110
|
+
devin: {
|
|
111
|
+
label: "Devin Desktop",
|
|
112
|
+
mode: "editor-cli",
|
|
113
|
+
publisherTarget: EDITOR_PUBLISHER_TARGET,
|
|
114
|
+
registryLabel: "Open VSX",
|
|
115
|
+
registryUrl: "https://open-vsx.org/extension/waitspin/waitspin-vscode",
|
|
116
|
+
binaryEnv: "WAITSPIN_DEVIN_DESKTOP_BIN",
|
|
117
|
+
defaultBinary: "devin-desktop",
|
|
118
|
+
windowsDefaultBinaries: ["devin", "devin-desktop"],
|
|
119
|
+
windowsRelativeBinaries: [["devin", "bin", "devin.exe"]],
|
|
120
|
+
productMarker: "devin",
|
|
121
|
+
executableBasenames: ["devin", "devin-desktop"],
|
|
122
|
+
macosAppBinary: "/Applications/Devin.app/Contents/Resources/app/bin/devin-desktop",
|
|
123
|
+
macosUserAppBinary: path.join(os.homedir(), "Applications", "Devin.app", "Contents", "Resources", "app", "bin", "devin-desktop"),
|
|
124
|
+
},
|
|
89
125
|
};
|
|
90
126
|
export function usageText() {
|
|
91
127
|
return ([
|
|
@@ -100,9 +136,9 @@ export function usageText() {
|
|
|
100
136
|
" waitspin wallet ledger [--limit N] [--json] [--base-url URL] [--api-key KEY]",
|
|
101
137
|
" waitspin wallet payout --dry-run [--json] [--base-url URL] [--api-key KEY]",
|
|
102
138
|
" waitspin wallet payout --confirm-test-transfer [--json] [--base-url URL] [--api-key KEY]",
|
|
103
|
-
" waitspin extension install [--target vscode] [--json] [--base-url URL] [--api-key KEY] [--dry-run]",
|
|
104
|
-
" waitspin extension status [--target vscode] [--json]",
|
|
105
|
-
" waitspin extension uninstall [--target vscode] [--json] [--dry-run]",
|
|
139
|
+
" waitspin extension install [--target vscode|cursor|devin] [--json] [--base-url URL] [--api-key KEY] [--dry-run]",
|
|
140
|
+
" waitspin extension status [--target vscode|cursor|devin] [--json]",
|
|
141
|
+
" waitspin extension uninstall [--target vscode|cursor|devin] [--json] [--dry-run]",
|
|
106
142
|
" waitspin install --all [--json] [--api-key KEY] [--compose-existing] [--dry-run]",
|
|
107
143
|
" waitspin status --all [--json] [--demo]",
|
|
108
144
|
" waitspin claude-code install [--json] [--api-key KEY] [--compose-existing] [--dry-run]",
|
|
@@ -730,7 +766,377 @@ export function generateInstallId() {
|
|
|
730
766
|
return `wins_${randomUUID().replace(/-/g, "")}`;
|
|
731
767
|
}
|
|
732
768
|
export function publisherTargetForExtension(target) {
|
|
733
|
-
return target
|
|
769
|
+
return extensionTargets[target].publisherTarget;
|
|
770
|
+
}
|
|
771
|
+
function isEditorCliExtensionTarget(target) {
|
|
772
|
+
return extensionTargets[target].mode === "editor-cli";
|
|
773
|
+
}
|
|
774
|
+
const EDITOR_PROCESS_ENV_KEYS = [
|
|
775
|
+
"APPDATA",
|
|
776
|
+
"COLORTERM",
|
|
777
|
+
"COMSPEC",
|
|
778
|
+
"HOME",
|
|
779
|
+
"LANG",
|
|
780
|
+
"LC_ALL",
|
|
781
|
+
"LC_CTYPE",
|
|
782
|
+
"LOCALAPPDATA",
|
|
783
|
+
"LOGNAME",
|
|
784
|
+
"NODE_EXTRA_CA_CERTS",
|
|
785
|
+
"NO_PROXY",
|
|
786
|
+
"PATH",
|
|
787
|
+
"PATHEXT",
|
|
788
|
+
"SHELL",
|
|
789
|
+
"SSL_CERT_DIR",
|
|
790
|
+
"SSL_CERT_FILE",
|
|
791
|
+
"SystemRoot",
|
|
792
|
+
"TEMP",
|
|
793
|
+
"TERM",
|
|
794
|
+
"TMP",
|
|
795
|
+
"TMPDIR",
|
|
796
|
+
"USER",
|
|
797
|
+
"USERPROFILE",
|
|
798
|
+
"XDG_CACHE_HOME",
|
|
799
|
+
"XDG_CONFIG_HOME",
|
|
800
|
+
"XDG_DATA_HOME",
|
|
801
|
+
];
|
|
802
|
+
function editorProxyEnvValue(value) {
|
|
803
|
+
if (!value)
|
|
804
|
+
return undefined;
|
|
805
|
+
try {
|
|
806
|
+
const url = new URL(value);
|
|
807
|
+
if (!["http:", "https:"].includes(url.protocol) ||
|
|
808
|
+
url.username ||
|
|
809
|
+
url.password ||
|
|
810
|
+
url.search ||
|
|
811
|
+
url.hash) {
|
|
812
|
+
return undefined;
|
|
813
|
+
}
|
|
814
|
+
return value;
|
|
815
|
+
}
|
|
816
|
+
catch {
|
|
817
|
+
return undefined;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
function editorProcessEnv() {
|
|
821
|
+
const env = { NODE_ENV: process.env.NODE_ENV };
|
|
822
|
+
for (const key of EDITOR_PROCESS_ENV_KEYS) {
|
|
823
|
+
const value = process.env[key];
|
|
824
|
+
if (value !== undefined)
|
|
825
|
+
env[key] = value;
|
|
826
|
+
}
|
|
827
|
+
for (const key of ["HTTP_PROXY", "HTTPS_PROXY"]) {
|
|
828
|
+
const value = editorProxyEnvValue(process.env[key]);
|
|
829
|
+
if (value !== undefined)
|
|
830
|
+
env[key] = value;
|
|
831
|
+
}
|
|
832
|
+
return env;
|
|
833
|
+
}
|
|
834
|
+
function editorExecOptions(timeout) {
|
|
835
|
+
return { encoding: "utf8", timeout, env: editorProcessEnv() };
|
|
836
|
+
}
|
|
837
|
+
function uniqueNonEmpty(values) {
|
|
838
|
+
return Array.from(new Set(values.map((value) => value?.trim()).filter(Boolean)));
|
|
839
|
+
}
|
|
840
|
+
function editorCliCandidates(target) {
|
|
841
|
+
const descriptor = extensionTargets[target];
|
|
842
|
+
const localAppData = process.env.LOCALAPPDATA?.trim();
|
|
843
|
+
const windowsCandidates = process.platform === "win32"
|
|
844
|
+
? [
|
|
845
|
+
...descriptor.windowsDefaultBinaries,
|
|
846
|
+
...(localAppData
|
|
847
|
+
? descriptor.windowsRelativeBinaries.map((segments) => path.win32.join(localAppData, ...segments))
|
|
848
|
+
: []),
|
|
849
|
+
]
|
|
850
|
+
: [];
|
|
851
|
+
return uniqueNonEmpty([
|
|
852
|
+
process.env[descriptor.binaryEnv],
|
|
853
|
+
...(process.platform === "win32"
|
|
854
|
+
? windowsCandidates
|
|
855
|
+
: [descriptor.defaultBinary]),
|
|
856
|
+
...(process.platform === "darwin"
|
|
857
|
+
? [descriptor.macosAppBinary, descriptor.macosUserAppBinary]
|
|
858
|
+
: []),
|
|
859
|
+
]);
|
|
860
|
+
}
|
|
861
|
+
function isWindowsEditorCommandScript(binary) {
|
|
862
|
+
return process.platform === "win32" && /\.(?:cmd|bat)$/i.test(binary);
|
|
863
|
+
}
|
|
864
|
+
function assertSafeWindowsEditorCommandScript(binary) {
|
|
865
|
+
if (/["\r\n\u0000%&|<>^!]/.test(binary)) {
|
|
866
|
+
throw new Error("Refusing unsafe Windows editor command shim path.");
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
async function resolveWindowsEditorCommand(binary) {
|
|
870
|
+
if (process.platform !== "win32")
|
|
871
|
+
return binary;
|
|
872
|
+
if (path.win32.isAbsolute(binary) ||
|
|
873
|
+
binary.includes("\\") ||
|
|
874
|
+
binary.includes("/") ||
|
|
875
|
+
path.win32.extname(binary)) {
|
|
876
|
+
return binary;
|
|
877
|
+
}
|
|
878
|
+
const result = await execFileText("where.exe", [binary], editorExecOptions(5_000)).catch(() => null);
|
|
879
|
+
if (!result)
|
|
880
|
+
return binary;
|
|
881
|
+
return (result.stdout
|
|
882
|
+
.split(/\r?\n/)
|
|
883
|
+
.map((line) => line.trim())
|
|
884
|
+
.find((line) => path.win32.isAbsolute(line) &&
|
|
885
|
+
/\.(?:exe|com|cmd|bat)$/i.test(path.win32.extname(line))) || binary);
|
|
886
|
+
}
|
|
887
|
+
async function editorCommandForBinary(binary) {
|
|
888
|
+
const resolvedBinary = await resolveWindowsEditorCommand(binary);
|
|
889
|
+
if (!isWindowsEditorCommandScript(resolvedBinary)) {
|
|
890
|
+
return {
|
|
891
|
+
binary: resolvedBinary,
|
|
892
|
+
executable: resolvedBinary,
|
|
893
|
+
argumentPrefix: [],
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
assertSafeWindowsEditorCommandScript(resolvedBinary);
|
|
897
|
+
return {
|
|
898
|
+
binary: resolvedBinary,
|
|
899
|
+
executable: process.env.ComSpec || "cmd.exe",
|
|
900
|
+
argumentPrefix: [
|
|
901
|
+
"/d",
|
|
902
|
+
"/v:off",
|
|
903
|
+
"/s",
|
|
904
|
+
"/c",
|
|
905
|
+
"call",
|
|
906
|
+
resolvedBinary,
|
|
907
|
+
],
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
function execEditorCommand(editor, args, timeout) {
|
|
911
|
+
return execFileText(editor.executable, [...editor.argumentPrefix, ...args], editorExecOptions(timeout));
|
|
912
|
+
}
|
|
913
|
+
function editorCliMatchesProduct(target, binary, version, help) {
|
|
914
|
+
const descriptor = extensionTargets[target];
|
|
915
|
+
const identityText = `${version}\n${help}`.toLowerCase();
|
|
916
|
+
if (identityText.includes(descriptor.productMarker))
|
|
917
|
+
return true;
|
|
918
|
+
const knownOtherProduct = ["cursor", "devin", "visual studio code"].some((marker) => marker !== descriptor.productMarker && identityText.includes(marker));
|
|
919
|
+
if (knownOtherProduct)
|
|
920
|
+
return false;
|
|
921
|
+
const basename = path.basename(binary).toLowerCase().replace(/\.(exe|cmd|bat)$/, "");
|
|
922
|
+
return descriptor.executableBasenames.includes(basename);
|
|
923
|
+
}
|
|
924
|
+
async function resolveEditorCli(target) {
|
|
925
|
+
const descriptor = extensionTargets[target];
|
|
926
|
+
let lastError;
|
|
927
|
+
for (const binary of editorCliCandidates(target)) {
|
|
928
|
+
try {
|
|
929
|
+
const editor = await editorCommandForBinary(binary);
|
|
930
|
+
const result = await execEditorCommand(editor, ["--version"], 5_000);
|
|
931
|
+
const helpResult = await execEditorCommand(editor, ["--help"], 5_000);
|
|
932
|
+
const help = `${helpResult.stdout}\n${helpResult.stderr}`;
|
|
933
|
+
if (![
|
|
934
|
+
"--install-extension",
|
|
935
|
+
"--list-extensions",
|
|
936
|
+
"--uninstall-extension",
|
|
937
|
+
].every((option) => help.includes(option))) {
|
|
938
|
+
lastError = new Error(`${binary} does not expose editor extension management.`);
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
941
|
+
const version = `${result.stdout || result.stderr || descriptor.label}`.trim();
|
|
942
|
+
if (!editorCliMatchesProduct(target, editor.binary, version, help)) {
|
|
943
|
+
lastError = new Error(`${editor.binary} does not identify as ${descriptor.label}.`);
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
return {
|
|
947
|
+
...editor,
|
|
948
|
+
version,
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
catch (error) {
|
|
952
|
+
lastError = error;
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
throw new Error(`${descriptor.label} was not detected. Install ${descriptor.label}, add ${process.platform === "win32" ? descriptor.windowsDefaultBinaries[0] : descriptor.defaultBinary} to PATH, or set ${descriptor.binaryEnv} to the editor executable path.`, { cause: lastError });
|
|
956
|
+
}
|
|
957
|
+
function parseEditorExtensionStatus(output) {
|
|
958
|
+
for (const rawLine of output.split(/\r?\n/)) {
|
|
959
|
+
const line = rawLine.trim();
|
|
960
|
+
const match = /^waitspin\.waitspin-vscode(?:@([^\s]+))?$/i.exec(line);
|
|
961
|
+
if (match) {
|
|
962
|
+
return { installed: true, version: match[1] || null };
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
return { installed: false, version: null };
|
|
966
|
+
}
|
|
967
|
+
async function readEditorExtensionStatus(editor) {
|
|
968
|
+
const result = await execEditorCommand(editor, ["--list-extensions", "--show-versions"], 10_000);
|
|
969
|
+
return parseEditorExtensionStatus(`${result.stdout}\n${result.stderr}`);
|
|
970
|
+
}
|
|
971
|
+
function editorActivationCommand(target) {
|
|
972
|
+
return `WaitSpin: Connect and earn inside ${extensionTargets[target].label}`;
|
|
973
|
+
}
|
|
974
|
+
async function runEditorExtensionInstall(target, flags) {
|
|
975
|
+
const descriptor = extensionTargets[target];
|
|
976
|
+
const editor = await resolveEditorCli(target);
|
|
977
|
+
const before = await readEditorExtensionStatus(editor);
|
|
978
|
+
const summary = {
|
|
979
|
+
ok: true,
|
|
980
|
+
target,
|
|
981
|
+
mode: EDITOR_PUBLISHER_TARGET,
|
|
982
|
+
detected: true,
|
|
983
|
+
editor: descriptor.label,
|
|
984
|
+
editor_version: editor.version,
|
|
985
|
+
editor_binary: editor.binary,
|
|
986
|
+
extension: WAITSPIN_EDITOR_EXTENSION_ID,
|
|
987
|
+
version: before.version,
|
|
988
|
+
installed: before.installed,
|
|
989
|
+
registry: descriptor.registryLabel,
|
|
990
|
+
registry_url: descriptor.registryUrl,
|
|
991
|
+
publisher_target: descriptor.publisherTarget,
|
|
992
|
+
publisher_registration_managed_by: "editor-extension",
|
|
993
|
+
activation_required: true,
|
|
994
|
+
next: { command: editorActivationCommand(target) },
|
|
995
|
+
next_command: editorActivationCommand(target),
|
|
996
|
+
};
|
|
997
|
+
if (booleanFlag(flags, "dry-run")) {
|
|
998
|
+
const dryRunPayload = {
|
|
999
|
+
...summary,
|
|
1000
|
+
dry_run: true,
|
|
1001
|
+
planned_argv: [
|
|
1002
|
+
editor.binary,
|
|
1003
|
+
"--install-extension",
|
|
1004
|
+
WAITSPIN_EDITOR_EXTENSION_ID,
|
|
1005
|
+
"--force",
|
|
1006
|
+
],
|
|
1007
|
+
};
|
|
1008
|
+
printCliOutput(flags, dryRunPayload, formatTargetInstallResult(dryRunPayload));
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
await execEditorCommand(editor, ["--install-extension", WAITSPIN_EDITOR_EXTENSION_ID, "--force"], 30_000);
|
|
1012
|
+
const after = await readEditorExtensionStatus(editor);
|
|
1013
|
+
if (!after.installed) {
|
|
1014
|
+
throw new Error(`${descriptor.label} did not report ${WAITSPIN_EDITOR_EXTENSION_ID} after installation.`);
|
|
1015
|
+
}
|
|
1016
|
+
const output = {
|
|
1017
|
+
...summary,
|
|
1018
|
+
installed: true,
|
|
1019
|
+
version: after.version,
|
|
1020
|
+
};
|
|
1021
|
+
printCliOutput(flags, output, formatTargetInstallResult(output));
|
|
1022
|
+
}
|
|
1023
|
+
async function runEditorExtensionStatus(target, flags) {
|
|
1024
|
+
const descriptor = extensionTargets[target];
|
|
1025
|
+
let editor;
|
|
1026
|
+
try {
|
|
1027
|
+
editor = await resolveEditorCli(target);
|
|
1028
|
+
}
|
|
1029
|
+
catch (error) {
|
|
1030
|
+
const output = {
|
|
1031
|
+
ok: true,
|
|
1032
|
+
target,
|
|
1033
|
+
mode: EDITOR_PUBLISHER_TARGET,
|
|
1034
|
+
detected: false,
|
|
1035
|
+
installed: false,
|
|
1036
|
+
version: null,
|
|
1037
|
+
publisher_target: descriptor.publisherTarget,
|
|
1038
|
+
publisher_registration_managed_by: "editor-extension",
|
|
1039
|
+
activation_required: false,
|
|
1040
|
+
detection_error: redactCliSecretText(error instanceof Error ? error.message : String(error)),
|
|
1041
|
+
next: {
|
|
1042
|
+
command: `waitspin extension install --target ${target}`,
|
|
1043
|
+
},
|
|
1044
|
+
next_command: `waitspin extension install --target ${target}`,
|
|
1045
|
+
};
|
|
1046
|
+
printCliOutput(flags, output, formatTargetStatusResult(output));
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const status = await readEditorExtensionStatus(editor);
|
|
1050
|
+
const output = {
|
|
1051
|
+
ok: true,
|
|
1052
|
+
target,
|
|
1053
|
+
mode: EDITOR_PUBLISHER_TARGET,
|
|
1054
|
+
detected: true,
|
|
1055
|
+
editor: descriptor.label,
|
|
1056
|
+
editor_version: editor.version,
|
|
1057
|
+
editor_binary: editor.binary,
|
|
1058
|
+
extension: WAITSPIN_EDITOR_EXTENSION_ID,
|
|
1059
|
+
version: status.version,
|
|
1060
|
+
installed: status.installed,
|
|
1061
|
+
publisher_target: descriptor.publisherTarget,
|
|
1062
|
+
publisher_registration_managed_by: "editor-extension",
|
|
1063
|
+
activation_required: status.installed,
|
|
1064
|
+
next: status.installed
|
|
1065
|
+
? { command: editorActivationCommand(target) }
|
|
1066
|
+
: {
|
|
1067
|
+
command: `waitspin extension install --target ${target}`,
|
|
1068
|
+
},
|
|
1069
|
+
next_command: status.installed
|
|
1070
|
+
? editorActivationCommand(target)
|
|
1071
|
+
: `waitspin extension install --target ${target}`,
|
|
1072
|
+
};
|
|
1073
|
+
printCliOutput(flags, output, formatTargetStatusResult(output));
|
|
1074
|
+
}
|
|
1075
|
+
async function runEditorExtensionUninstall(target, flags) {
|
|
1076
|
+
const descriptor = extensionTargets[target];
|
|
1077
|
+
let editor;
|
|
1078
|
+
try {
|
|
1079
|
+
editor = await resolveEditorCli(target);
|
|
1080
|
+
}
|
|
1081
|
+
catch (error) {
|
|
1082
|
+
const output = {
|
|
1083
|
+
ok: true,
|
|
1084
|
+
target,
|
|
1085
|
+
detected: false,
|
|
1086
|
+
installed: false,
|
|
1087
|
+
uninstalled: false,
|
|
1088
|
+
dry_run: booleanFlag(flags, "dry-run"),
|
|
1089
|
+
publisher_target: descriptor.publisherTarget,
|
|
1090
|
+
detection_error: redactCliSecretText(error instanceof Error ? error.message : String(error)),
|
|
1091
|
+
};
|
|
1092
|
+
printCliOutput(flags, output, formatTargetUninstallResult(output));
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
const before = await readEditorExtensionStatus(editor);
|
|
1096
|
+
const summary = {
|
|
1097
|
+
ok: true,
|
|
1098
|
+
target,
|
|
1099
|
+
detected: true,
|
|
1100
|
+
installed: before.installed,
|
|
1101
|
+
version: before.version,
|
|
1102
|
+
extension: WAITSPIN_EDITOR_EXTENSION_ID,
|
|
1103
|
+
publisher_target: descriptor.publisherTarget,
|
|
1104
|
+
};
|
|
1105
|
+
if (booleanFlag(flags, "dry-run")) {
|
|
1106
|
+
const output = {
|
|
1107
|
+
...summary,
|
|
1108
|
+
dry_run: true,
|
|
1109
|
+
planned_argv: before.installed
|
|
1110
|
+
? [
|
|
1111
|
+
editor.binary,
|
|
1112
|
+
"--uninstall-extension",
|
|
1113
|
+
WAITSPIN_EDITOR_EXTENSION_ID,
|
|
1114
|
+
]
|
|
1115
|
+
: null,
|
|
1116
|
+
would_remove_extension: before.installed
|
|
1117
|
+
? WAITSPIN_EDITOR_EXTENSION_ID
|
|
1118
|
+
: null,
|
|
1119
|
+
};
|
|
1120
|
+
printCliOutput(flags, output, formatTargetUninstallResult(output));
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (!before.installed) {
|
|
1124
|
+
const output = { ...summary, uninstalled: false };
|
|
1125
|
+
printCliOutput(flags, output, formatTargetUninstallResult(output));
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
await execEditorCommand(editor, ["--uninstall-extension", WAITSPIN_EDITOR_EXTENSION_ID], 30_000);
|
|
1129
|
+
const after = await readEditorExtensionStatus(editor);
|
|
1130
|
+
if (after.installed) {
|
|
1131
|
+
throw new Error(`${descriptor.label} still reports ${WAITSPIN_EDITOR_EXTENSION_ID} after uninstall.`);
|
|
1132
|
+
}
|
|
1133
|
+
const output = {
|
|
1134
|
+
...summary,
|
|
1135
|
+
installed: false,
|
|
1136
|
+
uninstalled: true,
|
|
1137
|
+
removed_extension: WAITSPIN_EDITOR_EXTENSION_ID,
|
|
1138
|
+
};
|
|
1139
|
+
printCliOutput(flags, output, formatTargetUninstallResult(output));
|
|
734
1140
|
}
|
|
735
1141
|
function extensionInstallDir(_target) {
|
|
736
1142
|
return path.join(os.homedir(), ".vscode", "extensions");
|
|
@@ -755,8 +1161,8 @@ function extensionMarkerPath(target) {
|
|
|
755
1161
|
}
|
|
756
1162
|
function resolveExtensionTarget(flags) {
|
|
757
1163
|
const target = optionalFlag(flags, "target") || "vscode";
|
|
758
|
-
if (!(target
|
|
759
|
-
throw new Error(`Unsupported extension target: ${target}. Public WaitSpin installs
|
|
1164
|
+
if (!Object.prototype.hasOwnProperty.call(extensionTargets, target)) {
|
|
1165
|
+
throw new Error(`Unsupported extension target: ${target}. Public WaitSpin installs support --target vscode, cursor, or devin.`);
|
|
760
1166
|
}
|
|
761
1167
|
return target;
|
|
762
1168
|
}
|
|
@@ -986,6 +1392,10 @@ async function validateVscodeExtensionRuntimeSource(sourceDir) {
|
|
|
986
1392
|
}
|
|
987
1393
|
export async function runExtensionInstall(flags) {
|
|
988
1394
|
const target = resolveExtensionTarget(flags);
|
|
1395
|
+
if (isEditorCliExtensionTarget(target)) {
|
|
1396
|
+
await runEditorExtensionInstall(target, flags);
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
989
1399
|
const baseUrl = resolveCredentialedBaseUrl(flags);
|
|
990
1400
|
const publisherTarget = publisherTargetForExtension(target);
|
|
991
1401
|
const extensionDir = await resolveExtensionDir(flags);
|
|
@@ -1094,6 +1504,10 @@ function managedInstalledPathStatus(marker) {
|
|
|
1094
1504
|
}
|
|
1095
1505
|
export async function runExtensionStatus(flags) {
|
|
1096
1506
|
const target = resolveExtensionTarget(flags);
|
|
1507
|
+
if (isEditorCliExtensionTarget(target)) {
|
|
1508
|
+
await runEditorExtensionStatus(target, flags);
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1097
1511
|
const statePath = installStatePath(target);
|
|
1098
1512
|
const markerPath = extensionMarkerPath(target);
|
|
1099
1513
|
const [state, marker] = await Promise.all([
|
|
@@ -1127,6 +1541,10 @@ export async function runExtensionStatus(flags) {
|
|
|
1127
1541
|
}
|
|
1128
1542
|
export async function runExtensionUninstall(flags) {
|
|
1129
1543
|
const target = resolveExtensionTarget(flags);
|
|
1544
|
+
if (isEditorCliExtensionTarget(target)) {
|
|
1545
|
+
await runEditorExtensionUninstall(target, flags);
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1130
1548
|
const statePath = installStatePath(target);
|
|
1131
1549
|
const markerPath = extensionMarkerPath(target);
|
|
1132
1550
|
const marker = await loadInstallMarker(markerPath);
|
|
@@ -5981,9 +6399,9 @@ function jsonFlags(flags) {
|
|
|
5981
6399
|
next.set("json", ["true"]);
|
|
5982
6400
|
return next;
|
|
5983
6401
|
}
|
|
5984
|
-
function extensionAllFlags(flags) {
|
|
6402
|
+
function extensionAllFlags(flags, target) {
|
|
5985
6403
|
const next = cloneFlags(flags);
|
|
5986
|
-
next.set("target", [
|
|
6404
|
+
next.set("target", [target]);
|
|
5987
6405
|
return next;
|
|
5988
6406
|
}
|
|
5989
6407
|
function executableFromEnv(envName, defaultBin) {
|
|
@@ -6005,6 +6423,10 @@ async function executableVersion(envName, defaultBin, label) {
|
|
|
6005
6423
|
async function preflightVscode(flags) {
|
|
6006
6424
|
return resolveExtensionDir(flags);
|
|
6007
6425
|
}
|
|
6426
|
+
async function preflightEditorExtension(target) {
|
|
6427
|
+
const editor = await resolveEditorCli(target);
|
|
6428
|
+
return `${extensionTargets[target].label} ${editor.version} (${editor.binary})`;
|
|
6429
|
+
}
|
|
6008
6430
|
async function preflightClaudeCode() {
|
|
6009
6431
|
return assertSupportedClaudeCodeVersion();
|
|
6010
6432
|
}
|
|
@@ -6043,8 +6465,24 @@ function allInstallTargets(flags) {
|
|
|
6043
6465
|
command: "waitspin extension install --target vscode",
|
|
6044
6466
|
statusCommand: "waitspin extension status --target vscode",
|
|
6045
6467
|
preflight: preflightVscode,
|
|
6046
|
-
install: (flags) => runExtensionInstall(extensionAllFlags(flags)),
|
|
6047
|
-
status: (flags) => runExtensionStatus(extensionAllFlags(flags)),
|
|
6468
|
+
install: (flags) => runExtensionInstall(extensionAllFlags(flags, "vscode")),
|
|
6469
|
+
status: (flags) => runExtensionStatus(extensionAllFlags(flags, "vscode")),
|
|
6470
|
+
},
|
|
6471
|
+
{
|
|
6472
|
+
target: "cursor",
|
|
6473
|
+
command: "waitspin extension install --target cursor",
|
|
6474
|
+
statusCommand: "waitspin extension status --target cursor",
|
|
6475
|
+
preflight: () => preflightEditorExtension("cursor"),
|
|
6476
|
+
install: (flags) => runExtensionInstall(extensionAllFlags(flags, "cursor")),
|
|
6477
|
+
status: (flags) => runExtensionStatus(extensionAllFlags(flags, "cursor")),
|
|
6478
|
+
},
|
|
6479
|
+
{
|
|
6480
|
+
target: "devin",
|
|
6481
|
+
command: "waitspin extension install --target devin",
|
|
6482
|
+
statusCommand: "waitspin extension status --target devin",
|
|
6483
|
+
preflight: () => preflightEditorExtension("devin"),
|
|
6484
|
+
install: (flags) => runExtensionInstall(extensionAllFlags(flags, "devin")),
|
|
6485
|
+
status: (flags) => runExtensionStatus(extensionAllFlags(flags, "devin")),
|
|
6048
6486
|
},
|
|
6049
6487
|
{
|
|
6050
6488
|
target: CLAUDE_CODE_PUBLISHER_TARGET,
|