@serviceme/devtools-core 0.3.2 → 0.3.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/dist/auth.d.mts +2 -592
- package/dist/auth.d.ts +2 -592
- package/dist/auth.js +175 -17
- package/dist/auth.js.map +1 -1
- package/dist/auth.mjs +175 -17
- package/dist/auth.mjs.map +1 -1
- package/dist/device.js +26 -0
- package/dist/device.js.map +1 -1
- package/dist/device.mjs +26 -0
- package/dist/device.mjs.map +1 -1
- package/dist/index-87-_JvQi.d.mts +626 -0
- package/dist/index-87-_JvQi.d.ts +626 -0
- package/dist/{index-BmxS1A9G.d.ts → index-CR1hbAnO.d.ts} +19 -1
- package/dist/{index-GAigCT0j.d.mts → index-CXvNx2fp.d.mts} +19 -1
- package/dist/index.d.mts +34 -33
- package/dist/index.d.ts +34 -33
- package/dist/index.js +358 -78
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +362 -84
- package/dist/index.mjs.map +1 -1
- package/dist/submit.d.mts +1 -1
- package/dist/submit.d.ts +1 -1
- package/dist/toolbox.js +25 -0
- package/dist/toolbox.js.map +1 -1
- package/dist/toolbox.mjs +25 -0
- package/dist/toolbox.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -454,6 +454,50 @@ var AccessControl = class {
|
|
|
454
454
|
}
|
|
455
455
|
};
|
|
456
456
|
|
|
457
|
+
// src/logger.ts
|
|
458
|
+
var noopLogger = {
|
|
459
|
+
debug() {
|
|
460
|
+
},
|
|
461
|
+
info() {
|
|
462
|
+
},
|
|
463
|
+
warn() {
|
|
464
|
+
},
|
|
465
|
+
error() {
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
function formatArgs(args) {
|
|
469
|
+
return args.map((arg) => {
|
|
470
|
+
if (typeof arg === "string") {
|
|
471
|
+
return arg;
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
return JSON.stringify(arg);
|
|
475
|
+
} catch {
|
|
476
|
+
return String(arg);
|
|
477
|
+
}
|
|
478
|
+
}).join(" ");
|
|
479
|
+
}
|
|
480
|
+
function createConsoleLogger(prefix = "serviceme") {
|
|
481
|
+
return {
|
|
482
|
+
debug(message, ...args) {
|
|
483
|
+
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
484
|
+
`);
|
|
485
|
+
},
|
|
486
|
+
info(message, ...args) {
|
|
487
|
+
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
488
|
+
`);
|
|
489
|
+
},
|
|
490
|
+
warn(message, ...args) {
|
|
491
|
+
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
492
|
+
`);
|
|
493
|
+
},
|
|
494
|
+
error(message, ...args) {
|
|
495
|
+
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
496
|
+
`);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
457
501
|
// src/auth/AuthStateManager.ts
|
|
458
502
|
import { EventEmitter } from "events";
|
|
459
503
|
var AuthStateManager = class {
|
|
@@ -616,6 +660,7 @@ var AuthCore = class {
|
|
|
616
660
|
this.state = opts.stateManager ?? new AuthStateManager();
|
|
617
661
|
this.tokenStore = opts.tokenStore;
|
|
618
662
|
this.accessControl = opts.accessControl;
|
|
663
|
+
this.logger = opts.logger ?? noopLogger;
|
|
619
664
|
}
|
|
620
665
|
/** Snapshot of every account, the active provider, and the last error. */
|
|
621
666
|
status() {
|
|
@@ -637,16 +682,28 @@ var AuthCore = class {
|
|
|
637
682
|
*/
|
|
638
683
|
async login(provider, ui, shouldContinue) {
|
|
639
684
|
const providerImpl = this.registry.get(provider);
|
|
685
|
+
this.logger.info("[AuthCore] Starting device-flow login", { provider });
|
|
640
686
|
try {
|
|
641
687
|
const initial = await providerImpl.requestDeviceFlow();
|
|
642
688
|
await ui(initial);
|
|
689
|
+
if (!initial.deviceCode) {
|
|
690
|
+
this.logger.error("[AuthCore] Provider did not return deviceCode", provider);
|
|
691
|
+
throw new Error("Auth provider did not return deviceCode for device flow completion");
|
|
692
|
+
}
|
|
643
693
|
const session = await providerImpl.completeDeviceFlow(
|
|
644
|
-
initial.
|
|
645
|
-
shouldContinue
|
|
694
|
+
initial.deviceCode,
|
|
695
|
+
shouldContinue,
|
|
696
|
+
initial.pollIntervalMs
|
|
646
697
|
);
|
|
647
|
-
|
|
698
|
+
const result = await this.persistSession(providerImpl, session);
|
|
699
|
+
this.logger.info("[AuthCore] Device-flow login completed", { provider });
|
|
700
|
+
return result;
|
|
648
701
|
} catch (err) {
|
|
649
|
-
|
|
702
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
703
|
+
this.logger.error("[AuthCore] Device-flow login failed", message, {
|
|
704
|
+
provider
|
|
705
|
+
});
|
|
706
|
+
this.state.recordError(message);
|
|
650
707
|
throw err;
|
|
651
708
|
}
|
|
652
709
|
}
|
|
@@ -671,7 +728,10 @@ var AuthCore = class {
|
|
|
671
728
|
if (!provider) return null;
|
|
672
729
|
const account = this.state.getActiveAccount();
|
|
673
730
|
if (!account) return null;
|
|
674
|
-
const envelope = await this.tokenStore.get({
|
|
731
|
+
const envelope = await this.tokenStore.get({
|
|
732
|
+
provider,
|
|
733
|
+
accountId: account.id
|
|
734
|
+
});
|
|
675
735
|
if (!envelope) return null;
|
|
676
736
|
return { provider, account, token: envelope.token };
|
|
677
737
|
}
|
|
@@ -681,9 +741,15 @@ var AuthCore = class {
|
|
|
681
741
|
* every account.
|
|
682
742
|
*/
|
|
683
743
|
async logout(provider) {
|
|
744
|
+
this.logger.info("[AuthCore] Logout requested", {
|
|
745
|
+
provider: provider ?? "all"
|
|
746
|
+
});
|
|
684
747
|
if (!provider) {
|
|
685
748
|
for (const account of this.state.listAccounts()) {
|
|
686
|
-
await this.tokenStore.delete({
|
|
749
|
+
await this.tokenStore.delete({
|
|
750
|
+
provider: account.provider,
|
|
751
|
+
accountId: account.id
|
|
752
|
+
});
|
|
687
753
|
this.state.removeAccount(account.provider, account.id);
|
|
688
754
|
}
|
|
689
755
|
this.state.clearAll();
|
|
@@ -758,6 +824,10 @@ var AuthCore = class {
|
|
|
758
824
|
avatarUrl: meta.avatarUrl,
|
|
759
825
|
expiresAt: meta.expiresAt ?? expiresAt
|
|
760
826
|
};
|
|
827
|
+
this.logger.debug("[AuthCore] Persisting session", {
|
|
828
|
+
provider: meta.provider,
|
|
829
|
+
accountId: meta.id
|
|
830
|
+
});
|
|
761
831
|
await this.tokenStore.set({ provider: meta.provider, accountId: meta.id }, session.token, {
|
|
762
832
|
expiresAt
|
|
763
833
|
});
|
|
@@ -860,11 +930,13 @@ var GitHubAuthProvider = class {
|
|
|
860
930
|
userUrl: config.userUrl ?? DEFAULT_USER_URL,
|
|
861
931
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
862
932
|
minPollIntervalMs: config.minPollIntervalMs ?? 1e3,
|
|
863
|
-
maxPollIntervalMs: config.maxPollIntervalMs ??
|
|
933
|
+
maxPollIntervalMs: config.maxPollIntervalMs ?? 6e4,
|
|
864
934
|
maxWaitMs: config.maxWaitMs,
|
|
865
935
|
fetchImpl: config.fetchImpl ?? fetch,
|
|
866
936
|
deviceCodeRetryBaseDelayMs: config.deviceCodeRetryBaseDelayMs ?? DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS
|
|
867
937
|
};
|
|
938
|
+
this.logger = config.logger ?? noopLogger;
|
|
939
|
+
this.sleepImpl = config.sleepImpl ?? sleep;
|
|
868
940
|
}
|
|
869
941
|
async requestDeviceFlow(opts) {
|
|
870
942
|
const body = JSON.stringify({
|
|
@@ -873,6 +945,10 @@ var GitHubAuthProvider = class {
|
|
|
873
945
|
});
|
|
874
946
|
let lastNetworkError;
|
|
875
947
|
for (let attempt = 1; attempt <= DEVICE_CODE_MAX_ATTEMPTS; attempt++) {
|
|
948
|
+
this.logger.debug("[GitHubAuthProvider] Requesting device code", {
|
|
949
|
+
attempt,
|
|
950
|
+
maxAttempts: DEVICE_CODE_MAX_ATTEMPTS
|
|
951
|
+
});
|
|
876
952
|
let resp;
|
|
877
953
|
try {
|
|
878
954
|
resp = await this.cfg.fetchImpl(this.cfg.deviceCodeUrl, {
|
|
@@ -886,45 +962,99 @@ var GitHubAuthProvider = class {
|
|
|
886
962
|
});
|
|
887
963
|
} catch (error) {
|
|
888
964
|
if (!isTransientNetworkError(error) || attempt === DEVICE_CODE_MAX_ATTEMPTS) {
|
|
965
|
+
this.logger.error(
|
|
966
|
+
"[GitHubAuthProvider] Device-code request failed (non-retryable)",
|
|
967
|
+
error instanceof Error ? error.message : String(error),
|
|
968
|
+
{ attempt }
|
|
969
|
+
);
|
|
889
970
|
throw error;
|
|
890
971
|
}
|
|
972
|
+
const delayMs = this.cfg.deviceCodeRetryBaseDelayMs * 2 ** (attempt - 1);
|
|
973
|
+
this.logger.warn(
|
|
974
|
+
"[GitHubAuthProvider] Transient network error requesting device code, retrying",
|
|
975
|
+
{
|
|
976
|
+
attempt,
|
|
977
|
+
delayMs,
|
|
978
|
+
error: error instanceof Error ? error.message : String(error)
|
|
979
|
+
}
|
|
980
|
+
);
|
|
891
981
|
lastNetworkError = error;
|
|
892
|
-
await
|
|
982
|
+
await this.sleepImpl(delayMs);
|
|
893
983
|
continue;
|
|
894
984
|
}
|
|
895
985
|
if (!resp.ok) {
|
|
986
|
+
this.logger.error(
|
|
987
|
+
"[GitHubAuthProvider] Device-code request rejected by GitHub",
|
|
988
|
+
`HTTP ${resp.status}`
|
|
989
|
+
);
|
|
896
990
|
throw new Error(`GitHub device-code request failed: HTTP ${resp.status}`);
|
|
897
991
|
}
|
|
898
992
|
const data = await resp.json();
|
|
899
993
|
if (!data.device_code || !data.user_code || !data.verification_uri) {
|
|
994
|
+
this.logger.error(
|
|
995
|
+
"[GitHubAuthProvider] Device-code response missing required fields",
|
|
996
|
+
JSON.stringify(Object.keys(data))
|
|
997
|
+
);
|
|
900
998
|
throw new Error("GitHub device-code response missing required fields");
|
|
901
999
|
}
|
|
1000
|
+
this.logger.info("[GitHubAuthProvider] Device code obtained", {
|
|
1001
|
+
userCode: data.user_code,
|
|
1002
|
+
verificationUri: data.verification_uri,
|
|
1003
|
+
expiresInSec: data.expires_in,
|
|
1004
|
+
pollIntervalSec: data.interval
|
|
1005
|
+
});
|
|
902
1006
|
return {
|
|
903
1007
|
provider: this.providerId,
|
|
1008
|
+
deviceCode: data.device_code,
|
|
904
1009
|
userCode: data.user_code,
|
|
905
1010
|
verificationUrl: data.verification_uri,
|
|
906
1011
|
expiresAt: Date.now() + data.expires_in * 1e3,
|
|
1012
|
+
pollIntervalMs: data.interval * 1e3,
|
|
907
1013
|
message: `Open ${data.verification_uri} and enter ${data.user_code}`
|
|
908
1014
|
};
|
|
909
1015
|
}
|
|
910
1016
|
throw lastNetworkError ?? new Error("GitHub device-code request failed");
|
|
911
1017
|
}
|
|
912
|
-
async completeDeviceFlow(deviceCode, shouldContinue) {
|
|
1018
|
+
async completeDeviceFlow(deviceCode, shouldContinue, initialPollIntervalMs) {
|
|
913
1019
|
const start = Date.now();
|
|
914
|
-
let pollIntervalMs = this.cfg.minPollIntervalMs;
|
|
1020
|
+
let pollIntervalMs = Math.max(this.cfg.minPollIntervalMs, initialPollIntervalMs ?? 0);
|
|
915
1021
|
let consecutiveSlowDown = 0;
|
|
1022
|
+
let pollCount = 0;
|
|
1023
|
+
this.logger.info("[GitHubAuthProvider] Starting device-flow polling", {
|
|
1024
|
+
initialPollIntervalMs: pollIntervalMs,
|
|
1025
|
+
maxWaitMs: this.cfg.maxWaitMs
|
|
1026
|
+
});
|
|
916
1027
|
while (true) {
|
|
917
1028
|
if (shouldContinue && !shouldContinue()) {
|
|
1029
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1030
|
+
elapsedMs: Date.now() - start,
|
|
1031
|
+
pollCount
|
|
1032
|
+
});
|
|
918
1033
|
throw new Error("GitHub device flow cancelled by caller");
|
|
919
1034
|
}
|
|
920
1035
|
const elapsed = Date.now() - start;
|
|
921
1036
|
if (this.cfg.maxWaitMs !== void 0 && elapsed > this.cfg.maxWaitMs) {
|
|
1037
|
+
this.logger.warn("[GitHubAuthProvider] Device flow exceeded max wait time", {
|
|
1038
|
+
elapsedMs: elapsed,
|
|
1039
|
+
maxWaitMs: this.cfg.maxWaitMs,
|
|
1040
|
+
pollCount
|
|
1041
|
+
});
|
|
922
1042
|
throw new Error("GitHub device flow exceeded max wait time");
|
|
923
1043
|
}
|
|
924
|
-
await
|
|
1044
|
+
await this.sleepImpl(pollIntervalMs);
|
|
925
1045
|
if (shouldContinue && !shouldContinue()) {
|
|
1046
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1047
|
+
elapsedMs: Date.now() - start,
|
|
1048
|
+
pollCount
|
|
1049
|
+
});
|
|
926
1050
|
throw new Error("GitHub device flow cancelled by caller");
|
|
927
1051
|
}
|
|
1052
|
+
pollCount++;
|
|
1053
|
+
this.logger.debug("[GitHubAuthProvider] Polling for authorization", {
|
|
1054
|
+
pollCount,
|
|
1055
|
+
elapsedMs: Date.now() - start,
|
|
1056
|
+
pollIntervalMs
|
|
1057
|
+
});
|
|
928
1058
|
let resp;
|
|
929
1059
|
try {
|
|
930
1060
|
resp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {
|
|
@@ -942,19 +1072,59 @@ var GitHubAuthProvider = class {
|
|
|
942
1072
|
});
|
|
943
1073
|
} catch (error) {
|
|
944
1074
|
if (!isTransientNetworkError(error)) {
|
|
1075
|
+
this.logger.error(
|
|
1076
|
+
"[GitHubAuthProvider] Non-retryable error while polling token endpoint",
|
|
1077
|
+
error instanceof Error ? error.message : String(error),
|
|
1078
|
+
{ pollCount }
|
|
1079
|
+
);
|
|
945
1080
|
throw error;
|
|
946
1081
|
}
|
|
947
|
-
|
|
1082
|
+
const nextPollIntervalMs = Math.min(
|
|
1083
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1084
|
+
this.cfg.maxPollIntervalMs
|
|
1085
|
+
);
|
|
1086
|
+
this.logger.warn(
|
|
1087
|
+
"[GitHubAuthProvider] Transient network error while polling, backing off",
|
|
1088
|
+
{
|
|
1089
|
+
pollCount,
|
|
1090
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1091
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1092
|
+
nextPollIntervalMs
|
|
1093
|
+
}
|
|
1094
|
+
);
|
|
1095
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
948
1096
|
continue;
|
|
949
1097
|
}
|
|
950
1098
|
if (!resp.ok) {
|
|
951
|
-
|
|
1099
|
+
const nextPollIntervalMs = Math.min(
|
|
1100
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1101
|
+
this.cfg.maxPollIntervalMs
|
|
1102
|
+
);
|
|
1103
|
+
this.logger.warn(
|
|
1104
|
+
"[GitHubAuthProvider] Token endpoint returned non-OK status, backing off",
|
|
1105
|
+
{
|
|
1106
|
+
pollCount,
|
|
1107
|
+
status: resp.status,
|
|
1108
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1109
|
+
nextPollIntervalMs
|
|
1110
|
+
}
|
|
1111
|
+
);
|
|
1112
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
952
1113
|
continue;
|
|
953
1114
|
}
|
|
954
1115
|
const data = await resp.json();
|
|
955
1116
|
if (data.access_token) {
|
|
1117
|
+
this.logger.info("[GitHubAuthProvider] Authorization granted, fetching user profile", {
|
|
1118
|
+
pollCount,
|
|
1119
|
+
elapsedMs: Date.now() - start
|
|
1120
|
+
});
|
|
956
1121
|
const rawUser = await this.fetchGitHubUser(data.access_token);
|
|
957
1122
|
const email = await this.resolveEmail(data.access_token, rawUser);
|
|
1123
|
+
this.logger.info("[GitHubAuthProvider] Device-flow login completed", {
|
|
1124
|
+
pollCount,
|
|
1125
|
+
elapsedMs: Date.now() - start,
|
|
1126
|
+
login: rawUser.login
|
|
1127
|
+
});
|
|
958
1128
|
return {
|
|
959
1129
|
token: data.access_token,
|
|
960
1130
|
refreshToken: data.refresh_token,
|
|
@@ -969,22 +1139,42 @@ var GitHubAuthProvider = class {
|
|
|
969
1139
|
};
|
|
970
1140
|
}
|
|
971
1141
|
if (data.error === "authorization_pending") {
|
|
1142
|
+
this.logger.debug("[GitHubAuthProvider] Authorization still pending", {
|
|
1143
|
+
pollCount,
|
|
1144
|
+
elapsedMs: Date.now() - start
|
|
1145
|
+
});
|
|
972
1146
|
continue;
|
|
973
1147
|
}
|
|
974
1148
|
if (data.error === "slow_down") {
|
|
975
1149
|
consecutiveSlowDown++;
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1150
|
+
const nextPollIntervalMs = Math.min(pollIntervalMs + 5e3, this.cfg.maxPollIntervalMs);
|
|
1151
|
+
this.logger.warn("[GitHubAuthProvider] GitHub requested slower polling (slow_down)", {
|
|
1152
|
+
pollCount,
|
|
1153
|
+
consecutiveSlowDown,
|
|
1154
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1155
|
+
nextPollIntervalMs
|
|
1156
|
+
});
|
|
1157
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
980
1158
|
continue;
|
|
981
1159
|
}
|
|
982
1160
|
if (data.error === "access_denied") {
|
|
1161
|
+
this.logger.warn("[GitHubAuthProvider] User denied authorization", {
|
|
1162
|
+
pollCount
|
|
1163
|
+
});
|
|
983
1164
|
throw new Error("User denied authorization");
|
|
984
1165
|
}
|
|
985
1166
|
if (data.error === "expired_token") {
|
|
1167
|
+
this.logger.warn(
|
|
1168
|
+
"[GitHubAuthProvider] Device code expired before authorization completed",
|
|
1169
|
+
{ pollCount, elapsedMs: Date.now() - start }
|
|
1170
|
+
);
|
|
986
1171
|
throw new Error("GitHub device code expired \u2014 restart the flow");
|
|
987
1172
|
}
|
|
1173
|
+
this.logger.error(
|
|
1174
|
+
"[GitHubAuthProvider] Unexpected device-flow error from token endpoint",
|
|
1175
|
+
data.error ?? "unknown",
|
|
1176
|
+
{ pollCount }
|
|
1177
|
+
);
|
|
988
1178
|
throw new Error(`GitHub device flow error: ${data.error ?? "unknown"}`);
|
|
989
1179
|
}
|
|
990
1180
|
}
|
|
@@ -1866,10 +2056,20 @@ function isRecord(value) {
|
|
|
1866
2056
|
function isNodeError(value) {
|
|
1867
2057
|
return value instanceof Error && typeof value.code === "string";
|
|
1868
2058
|
}
|
|
2059
|
+
var LOCK_PID_FILE = "pid";
|
|
2060
|
+
function isProcessAlive(pid) {
|
|
2061
|
+
try {
|
|
2062
|
+
process.kill(pid, 0);
|
|
2063
|
+
return true;
|
|
2064
|
+
} catch {
|
|
2065
|
+
return false;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
1869
2068
|
var FileLock = class {
|
|
1870
2069
|
constructor(filePath, timeoutMs, retryMs) {
|
|
1871
2070
|
this.acquired = false;
|
|
1872
2071
|
this.dirPath = `${filePath}.lock`;
|
|
2072
|
+
this.pidFilePath = path3.join(this.dirPath, LOCK_PID_FILE);
|
|
1873
2073
|
this.timeoutMs = timeoutMs;
|
|
1874
2074
|
this.retryMs = retryMs;
|
|
1875
2075
|
}
|
|
@@ -1878,12 +2078,18 @@ var FileLock = class {
|
|
|
1878
2078
|
while (true) {
|
|
1879
2079
|
try {
|
|
1880
2080
|
await fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });
|
|
2081
|
+
await fsp.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
1881
2082
|
this.acquired = true;
|
|
1882
2083
|
return;
|
|
1883
2084
|
} catch (err) {
|
|
1884
2085
|
if (!isNodeError(err) || err.code !== "EEXIST") {
|
|
1885
2086
|
throw err;
|
|
1886
2087
|
}
|
|
2088
|
+
const stale = await this.isStaleLock();
|
|
2089
|
+
if (stale) {
|
|
2090
|
+
await fsp.rm(this.dirPath, { recursive: true, force: true });
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
1887
2093
|
if (Date.now() - start >= this.timeoutMs) {
|
|
1888
2094
|
throw new Error(`IdentityStore lock acquisition timed out for ${this.dirPath}`);
|
|
1889
2095
|
}
|
|
@@ -1891,6 +2097,16 @@ var FileLock = class {
|
|
|
1891
2097
|
}
|
|
1892
2098
|
}
|
|
1893
2099
|
}
|
|
2100
|
+
async isStaleLock() {
|
|
2101
|
+
try {
|
|
2102
|
+
const pidStr = await fsp.readFile(this.pidFilePath, "utf8");
|
|
2103
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
2104
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
2105
|
+
return !isProcessAlive(pid);
|
|
2106
|
+
} catch {
|
|
2107
|
+
return true;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1894
2110
|
async release() {
|
|
1895
2111
|
if (!this.acquired) return;
|
|
1896
2112
|
this.acquired = false;
|
|
@@ -2319,12 +2535,43 @@ var TOOL_CHECK_TIMEOUT_MS = {
|
|
|
2319
2535
|
pnpm: 15e3,
|
|
2320
2536
|
nrm: 15e3
|
|
2321
2537
|
};
|
|
2538
|
+
var POSIX_LOGIN_SHELL_FALLBACK_TOOLS = /* @__PURE__ */ new Set([
|
|
2539
|
+
"npm",
|
|
2540
|
+
"pnpm",
|
|
2541
|
+
"nrm",
|
|
2542
|
+
"node",
|
|
2543
|
+
"dotnet",
|
|
2544
|
+
"rtk"
|
|
2545
|
+
]);
|
|
2322
2546
|
var ERROR_CODE_NOT_FOUND = 127;
|
|
2323
2547
|
var ERROR_CODE_TIMEOUT = "ETIMEDOUT";
|
|
2548
|
+
var NVM_FALLBACK_DIRS = [
|
|
2549
|
+
"/opt/homebrew/opt/nvm",
|
|
2550
|
+
"/usr/local/opt/nvm",
|
|
2551
|
+
"$HOME/.nvm"
|
|
2552
|
+
];
|
|
2553
|
+
function resolveUserLoginShell(platform3, envShell) {
|
|
2554
|
+
const shell = envShell?.trim();
|
|
2555
|
+
if (shell && shell.length > 0) {
|
|
2556
|
+
return shell;
|
|
2557
|
+
}
|
|
2558
|
+
return platform3 === "darwin" ? "/bin/zsh" : "/bin/bash";
|
|
2559
|
+
}
|
|
2560
|
+
function buildNvmSourcingSnippet() {
|
|
2561
|
+
const fallbackList = NVM_FALLBACK_DIRS.map((dir) => `"${dir}"`).join(" ");
|
|
2562
|
+
return `for d in $NVM_DIR ${fallbackList}; do [ -n "$d" ] && [ -s "$d/nvm.sh" ] && export NVM_DIR="$d" && . "$d/nvm.sh" && break; done`;
|
|
2563
|
+
}
|
|
2564
|
+
function posixLoginShellArgs(platform3, envShell, command) {
|
|
2565
|
+
return {
|
|
2566
|
+
command: resolveUserLoginShell(platform3, envShell),
|
|
2567
|
+
args: ["-lc", `${buildNvmSourcingSnippet()}; ${command}`]
|
|
2568
|
+
};
|
|
2569
|
+
}
|
|
2324
2570
|
var EnvironmentInspector = class {
|
|
2325
2571
|
constructor(options = {}) {
|
|
2326
2572
|
this.runCommandFn = options.runCommand ?? runCommand;
|
|
2327
2573
|
this.platform = options.platform ?? process.platform;
|
|
2574
|
+
this.shell = options.shell ?? process.env.SHELL;
|
|
2328
2575
|
}
|
|
2329
2576
|
async checkEnvironment() {
|
|
2330
2577
|
const results = await Promise.all(
|
|
@@ -2362,6 +2609,21 @@ var EnvironmentInspector = class {
|
|
|
2362
2609
|
timeoutMs: this.getToolTimeout(toolName)
|
|
2363
2610
|
});
|
|
2364
2611
|
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2612
|
+
} catch {
|
|
2613
|
+
if (this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2614
|
+
return this.getToolPathFromLoginShell(toolName);
|
|
2615
|
+
}
|
|
2616
|
+
return void 0;
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
async getToolPathFromLoginShell(toolName) {
|
|
2620
|
+
try {
|
|
2621
|
+
const spawnArgs = posixLoginShellArgs(this.platform, this.shell, `command -v ${toolName}`);
|
|
2622
|
+
const result = await this.runCommandFn(spawnArgs.command, {
|
|
2623
|
+
args: spawnArgs.args,
|
|
2624
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2625
|
+
});
|
|
2626
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2365
2627
|
} catch {
|
|
2366
2628
|
return void 0;
|
|
2367
2629
|
}
|
|
@@ -2386,12 +2648,31 @@ var EnvironmentInspector = class {
|
|
|
2386
2648
|
}
|
|
2387
2649
|
}
|
|
2388
2650
|
async getToolVersion(toolName) {
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2651
|
+
try {
|
|
2652
|
+
const invocation = await this.getVersionInvocation(toolName);
|
|
2653
|
+
const result = await this.runCommandFn(invocation.command, {
|
|
2654
|
+
args: invocation.args,
|
|
2655
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2656
|
+
});
|
|
2657
|
+
return this.parseVersion(toolName, result.stdout || result.stderr);
|
|
2658
|
+
} catch (error) {
|
|
2659
|
+
if (!this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2660
|
+
throw error;
|
|
2661
|
+
}
|
|
2662
|
+
const fallbackArgs = posixLoginShellArgs(this.platform, this.shell, `${toolName} --version`);
|
|
2663
|
+
const fallbackResult = await this.runCommandFn(fallbackArgs.command, {
|
|
2664
|
+
args: fallbackArgs.args,
|
|
2665
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2666
|
+
});
|
|
2667
|
+
const fallbackOutput = (fallbackResult.stdout || fallbackResult.stderr).trim();
|
|
2668
|
+
if (!fallbackOutput) {
|
|
2669
|
+
throw error;
|
|
2670
|
+
}
|
|
2671
|
+
return this.parseVersion(toolName, fallbackOutput);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
shouldUsePosixLoginShellFallback(toolName) {
|
|
2675
|
+
return this.platform !== "win32" && POSIX_LOGIN_SHELL_FALLBACK_TOOLS.has(toolName);
|
|
2395
2676
|
}
|
|
2396
2677
|
async checkNvm() {
|
|
2397
2678
|
if (this.platform === "win32") {
|
|
@@ -2413,11 +2694,9 @@ var EnvironmentInspector = class {
|
|
|
2413
2694
|
}
|
|
2414
2695
|
}
|
|
2415
2696
|
try {
|
|
2416
|
-
const
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; nvm --version'
|
|
2420
|
-
],
|
|
2697
|
+
const nvmArgs = posixLoginShellArgs(this.platform, this.shell, "nvm --version");
|
|
2698
|
+
const result = await this.runCommandFn(nvmArgs.command, {
|
|
2699
|
+
args: nvmArgs.args,
|
|
2421
2700
|
timeoutMs: this.getToolTimeout("nvm")
|
|
2422
2701
|
});
|
|
2423
2702
|
return {
|
|
@@ -2441,7 +2720,7 @@ var EnvironmentInspector = class {
|
|
|
2441
2720
|
};
|
|
2442
2721
|
}
|
|
2443
2722
|
try {
|
|
2444
|
-
const result = await this.runCommandFn(
|
|
2723
|
+
const result = await this.runCommandFn(dotnetPath, {
|
|
2445
2724
|
args: ["nuget", "list", "source"],
|
|
2446
2725
|
timeoutMs: this.getToolTimeout("nuget")
|
|
2447
2726
|
});
|
|
@@ -2604,7 +2883,9 @@ var GitClient = class {
|
|
|
2604
2883
|
}
|
|
2605
2884
|
const targetUrl = useProxy ? remoteUrl : originalUrl ?? remoteUrl;
|
|
2606
2885
|
const proxyUrl = this.rewriteRemoteUrl(repoId, targetUrl, useProxy);
|
|
2607
|
-
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
2886
|
+
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
2887
|
+
cwd: localPath
|
|
2888
|
+
});
|
|
2608
2889
|
const before = await this.safeRevParse(localPath);
|
|
2609
2890
|
await this.runOrThrow(["fetch", "origin"], { cwd: localPath });
|
|
2610
2891
|
const after = await this.safeRevParse(localPath);
|
|
@@ -2640,7 +2921,9 @@ var GitClient = class {
|
|
|
2640
2921
|
throw new Error(`no 'origin' remote configured at ${localPath}`);
|
|
2641
2922
|
}
|
|
2642
2923
|
const proxyUrl = this.rewriteRemoteUrl(repoId, remoteUrl, useProxy);
|
|
2643
|
-
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
2924
|
+
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
2925
|
+
cwd: localPath
|
|
2926
|
+
});
|
|
2644
2927
|
const result = await this.spawner.spawn(
|
|
2645
2928
|
["push", "origin", `refs/heads/${branch}:refs/heads/${branch}`],
|
|
2646
2929
|
{ cwd: localPath }
|
|
@@ -2694,7 +2977,9 @@ var GitClient = class {
|
|
|
2694
2977
|
return { commitSha: sha };
|
|
2695
2978
|
}
|
|
2696
2979
|
async runOrThrow(args, opts) {
|
|
2697
|
-
const result = await this.spawner.spawn(args, {
|
|
2980
|
+
const result = await this.spawner.spawn(args, {
|
|
2981
|
+
cwd: opts.cwd ?? process.cwd()
|
|
2982
|
+
});
|
|
2698
2983
|
if (result.code !== 0) {
|
|
2699
2984
|
throw new GitError(args, result);
|
|
2700
2985
|
}
|
|
@@ -2709,7 +2994,9 @@ var GitClient = class {
|
|
|
2709
2994
|
return url.length > 0 ? url : void 0;
|
|
2710
2995
|
}
|
|
2711
2996
|
async safeRevParse(localPath) {
|
|
2712
|
-
const result = await this.spawner.spawn(["rev-parse", "HEAD"], {
|
|
2997
|
+
const result = await this.spawner.spawn(["rev-parse", "HEAD"], {
|
|
2998
|
+
cwd: localPath
|
|
2999
|
+
});
|
|
2713
3000
|
if (result.code !== 0) return "";
|
|
2714
3001
|
return result.stdout.trim();
|
|
2715
3002
|
}
|
|
@@ -2756,7 +3043,11 @@ var StubGitSpawner = class {
|
|
|
2756
3043
|
this.calls.push({ args, cwd: opts.cwd });
|
|
2757
3044
|
const next = this.script.shift();
|
|
2758
3045
|
if (!next) {
|
|
2759
|
-
return {
|
|
3046
|
+
return {
|
|
3047
|
+
stdout: "",
|
|
3048
|
+
stderr: `stub: no scripted response for ${args.join(" ")}`,
|
|
3049
|
+
code: 1
|
|
3050
|
+
};
|
|
2760
3051
|
}
|
|
2761
3052
|
return next;
|
|
2762
3053
|
}
|
|
@@ -2768,6 +3059,10 @@ function toFileUrl(absolutePath) {
|
|
|
2768
3059
|
}
|
|
2769
3060
|
return `file://${normalized}`;
|
|
2770
3061
|
}
|
|
3062
|
+
var GIT_PROXY_PATH_SUFFIX = "/git-proxy";
|
|
3063
|
+
function buildGitProxyBase(serverBaseUrl) {
|
|
3064
|
+
return `${serverBaseUrl.replace(/\/+$/, "")}${GIT_PROXY_PATH_SUFFIX}`;
|
|
3065
|
+
}
|
|
2771
3066
|
|
|
2772
3067
|
// src/image/imageTools.ts
|
|
2773
3068
|
import * as fs5 from "fs/promises";
|
|
@@ -2977,50 +3272,6 @@ function detectIndent(text) {
|
|
|
2977
3272
|
return 2;
|
|
2978
3273
|
}
|
|
2979
3274
|
|
|
2980
|
-
// src/logger.ts
|
|
2981
|
-
var noopLogger = {
|
|
2982
|
-
debug() {
|
|
2983
|
-
},
|
|
2984
|
-
info() {
|
|
2985
|
-
},
|
|
2986
|
-
warn() {
|
|
2987
|
-
},
|
|
2988
|
-
error() {
|
|
2989
|
-
}
|
|
2990
|
-
};
|
|
2991
|
-
function formatArgs(args) {
|
|
2992
|
-
return args.map((arg) => {
|
|
2993
|
-
if (typeof arg === "string") {
|
|
2994
|
-
return arg;
|
|
2995
|
-
}
|
|
2996
|
-
try {
|
|
2997
|
-
return JSON.stringify(arg);
|
|
2998
|
-
} catch {
|
|
2999
|
-
return String(arg);
|
|
3000
|
-
}
|
|
3001
|
-
}).join(" ");
|
|
3002
|
-
}
|
|
3003
|
-
function createConsoleLogger(prefix = "serviceme") {
|
|
3004
|
-
return {
|
|
3005
|
-
debug(message, ...args) {
|
|
3006
|
-
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
3007
|
-
`);
|
|
3008
|
-
},
|
|
3009
|
-
info(message, ...args) {
|
|
3010
|
-
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
3011
|
-
`);
|
|
3012
|
-
},
|
|
3013
|
-
warn(message, ...args) {
|
|
3014
|
-
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
3015
|
-
`);
|
|
3016
|
-
},
|
|
3017
|
-
error(message, ...args) {
|
|
3018
|
-
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
3019
|
-
`);
|
|
3020
|
-
}
|
|
3021
|
-
};
|
|
3022
|
-
}
|
|
3023
|
-
|
|
3024
3275
|
// src/paths/serverProxyGlobal.ts
|
|
3025
3276
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3026
3277
|
import * as fs6 from "fs/promises";
|
|
@@ -3176,7 +3427,7 @@ import {
|
|
|
3176
3427
|
// src/utils/fileUtils.ts
|
|
3177
3428
|
import { constants, createWriteStream } from "fs";
|
|
3178
3429
|
import { access as access5, copyFile, lstat, mkdir as mkdir5, readdir as readdir3, rename as rename4, rm as rm3 } from "fs/promises";
|
|
3179
|
-
import { dirname as dirname7, join as
|
|
3430
|
+
import { dirname as dirname7, join as join8 } from "path";
|
|
3180
3431
|
import yauzl from "yauzl";
|
|
3181
3432
|
var unzipFile = (zipPath, dest) => {
|
|
3182
3433
|
return new Promise((resolve3, reject) => {
|
|
@@ -3186,11 +3437,11 @@ var unzipFile = (zipPath, dest) => {
|
|
|
3186
3437
|
zipfile.readEntry();
|
|
3187
3438
|
zipfile.on("entry", (entry) => {
|
|
3188
3439
|
if (/\/$/.test(entry.fileName)) {
|
|
3189
|
-
void mkdir5(
|
|
3440
|
+
void mkdir5(join8(dest, entry.fileName), { recursive: true }).then(() => {
|
|
3190
3441
|
zipfile.readEntry();
|
|
3191
3442
|
}).catch(reject);
|
|
3192
3443
|
} else {
|
|
3193
|
-
const outputPath =
|
|
3444
|
+
const outputPath = join8(dest, entry.fileName);
|
|
3194
3445
|
void mkdir5(dirname7(outputPath), { recursive: true }).then(() => {
|
|
3195
3446
|
zipfile.openReadStream(
|
|
3196
3447
|
entry,
|
|
@@ -3239,7 +3490,7 @@ var mergeEntry = async (sourcePath, destPath, overwrite) => {
|
|
|
3239
3490
|
await mkdir5(destPath, { recursive: true });
|
|
3240
3491
|
const children = await readdir3(sourcePath);
|
|
3241
3492
|
for (const child of children) {
|
|
3242
|
-
await mergeEntry(
|
|
3493
|
+
await mergeEntry(join8(sourcePath, child), join8(destPath, child), overwrite);
|
|
3243
3494
|
}
|
|
3244
3495
|
await rm3(sourcePath, { recursive: true, force: true });
|
|
3245
3496
|
return;
|
|
@@ -3262,8 +3513,8 @@ var moveFiles = async (sourceDir, destDir, overwrite = false) => {
|
|
|
3262
3513
|
await mkdir5(destDir, { recursive: true });
|
|
3263
3514
|
const files = await readdir3(sourceDir);
|
|
3264
3515
|
for (const file of files) {
|
|
3265
|
-
const sourceFile =
|
|
3266
|
-
const destFile =
|
|
3516
|
+
const sourceFile = join8(sourceDir, file);
|
|
3517
|
+
const destFile = join8(destDir, file);
|
|
3267
3518
|
if (!overwrite) {
|
|
3268
3519
|
try {
|
|
3269
3520
|
await access5(destFile, constants.F_OK);
|
|
@@ -6678,10 +6929,19 @@ function coercePersistedToolbox(parsed) {
|
|
|
6678
6929
|
function isNodeError2(value) {
|
|
6679
6930
|
return value instanceof Error && typeof value.code === "string";
|
|
6680
6931
|
}
|
|
6932
|
+
function isProcessAlive2(pid) {
|
|
6933
|
+
try {
|
|
6934
|
+
process.kill(pid, 0);
|
|
6935
|
+
return true;
|
|
6936
|
+
} catch {
|
|
6937
|
+
return false;
|
|
6938
|
+
}
|
|
6939
|
+
}
|
|
6681
6940
|
var ToolboxFileLock = class {
|
|
6682
6941
|
constructor(filePath, timeoutMs, retryMs) {
|
|
6683
6942
|
this.acquired = false;
|
|
6684
6943
|
this.dirPath = `${filePath}.lock`;
|
|
6944
|
+
this.pidFilePath = path25.join(this.dirPath, "pid");
|
|
6685
6945
|
this.timeoutMs = timeoutMs;
|
|
6686
6946
|
this.retryMs = retryMs;
|
|
6687
6947
|
}
|
|
@@ -6690,10 +6950,16 @@ var ToolboxFileLock = class {
|
|
|
6690
6950
|
while (true) {
|
|
6691
6951
|
try {
|
|
6692
6952
|
await fsp2.mkdir(this.dirPath, { mode: LOCK_DIR_MODE2 });
|
|
6953
|
+
await fsp2.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
6693
6954
|
this.acquired = true;
|
|
6694
6955
|
return;
|
|
6695
6956
|
} catch (err) {
|
|
6696
6957
|
if (!isNodeError2(err) || err.code !== "EEXIST") throw err;
|
|
6958
|
+
const stale = await this.isStaleLock();
|
|
6959
|
+
if (stale) {
|
|
6960
|
+
await fsp2.rm(this.dirPath, { recursive: true, force: true });
|
|
6961
|
+
continue;
|
|
6962
|
+
}
|
|
6697
6963
|
if (Date.now() - start >= this.timeoutMs) {
|
|
6698
6964
|
throw new Error(`ToolboxStore lock acquisition timed out for ${this.dirPath}`);
|
|
6699
6965
|
}
|
|
@@ -6701,6 +6967,16 @@ var ToolboxFileLock = class {
|
|
|
6701
6967
|
}
|
|
6702
6968
|
}
|
|
6703
6969
|
}
|
|
6970
|
+
async isStaleLock() {
|
|
6971
|
+
try {
|
|
6972
|
+
const pidStr = await fsp2.readFile(this.pidFilePath, "utf8");
|
|
6973
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
6974
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
6975
|
+
return !isProcessAlive2(pid);
|
|
6976
|
+
} catch {
|
|
6977
|
+
return true;
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6704
6980
|
async release() {
|
|
6705
6981
|
if (!this.acquired) return;
|
|
6706
6982
|
this.acquired = false;
|
|
@@ -6979,6 +7255,7 @@ export {
|
|
|
6979
7255
|
EnvironmentInspector,
|
|
6980
7256
|
FsIdentityFileBackend,
|
|
6981
7257
|
FsToolboxFileBackend,
|
|
7258
|
+
GIT_PROXY_PATH_SUFFIX,
|
|
6982
7259
|
GitClient,
|
|
6983
7260
|
GitError,
|
|
6984
7261
|
GitHubAuthProvider,
|
|
@@ -7045,6 +7322,7 @@ export {
|
|
|
7045
7322
|
bootstrapPhase5Placeholders,
|
|
7046
7323
|
buildDefaultReposFile,
|
|
7047
7324
|
buildGitHubLocalEmail,
|
|
7325
|
+
buildGitProxyBase,
|
|
7048
7326
|
buildSignedHeaders,
|
|
7049
7327
|
copilotDoctor,
|
|
7050
7328
|
copilotPrompt,
|