@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.js
CHANGED
|
@@ -59,6 +59,7 @@ __export(src_exports, {
|
|
|
59
59
|
EnvironmentInspector: () => EnvironmentInspector,
|
|
60
60
|
FsIdentityFileBackend: () => FsIdentityFileBackend,
|
|
61
61
|
FsToolboxFileBackend: () => FsToolboxFileBackend,
|
|
62
|
+
GIT_PROXY_PATH_SUFFIX: () => GIT_PROXY_PATH_SUFFIX,
|
|
62
63
|
GitClient: () => GitClient,
|
|
63
64
|
GitError: () => GitError,
|
|
64
65
|
GitHubAuthProvider: () => GitHubAuthProvider,
|
|
@@ -125,6 +126,7 @@ __export(src_exports, {
|
|
|
125
126
|
bootstrapPhase5Placeholders: () => bootstrapPhase5Placeholders,
|
|
126
127
|
buildDefaultReposFile: () => buildDefaultReposFile,
|
|
127
128
|
buildGitHubLocalEmail: () => buildGitHubLocalEmail,
|
|
129
|
+
buildGitProxyBase: () => buildGitProxyBase,
|
|
128
130
|
buildSignedHeaders: () => buildSignedHeaders,
|
|
129
131
|
copilotDoctor: () => copilotDoctor,
|
|
130
132
|
copilotPrompt: () => copilotPrompt,
|
|
@@ -652,6 +654,50 @@ var AccessControl = class {
|
|
|
652
654
|
}
|
|
653
655
|
};
|
|
654
656
|
|
|
657
|
+
// src/logger.ts
|
|
658
|
+
var noopLogger = {
|
|
659
|
+
debug() {
|
|
660
|
+
},
|
|
661
|
+
info() {
|
|
662
|
+
},
|
|
663
|
+
warn() {
|
|
664
|
+
},
|
|
665
|
+
error() {
|
|
666
|
+
}
|
|
667
|
+
};
|
|
668
|
+
function formatArgs(args) {
|
|
669
|
+
return args.map((arg) => {
|
|
670
|
+
if (typeof arg === "string") {
|
|
671
|
+
return arg;
|
|
672
|
+
}
|
|
673
|
+
try {
|
|
674
|
+
return JSON.stringify(arg);
|
|
675
|
+
} catch {
|
|
676
|
+
return String(arg);
|
|
677
|
+
}
|
|
678
|
+
}).join(" ");
|
|
679
|
+
}
|
|
680
|
+
function createConsoleLogger(prefix = "serviceme") {
|
|
681
|
+
return {
|
|
682
|
+
debug(message, ...args) {
|
|
683
|
+
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
684
|
+
`);
|
|
685
|
+
},
|
|
686
|
+
info(message, ...args) {
|
|
687
|
+
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
688
|
+
`);
|
|
689
|
+
},
|
|
690
|
+
warn(message, ...args) {
|
|
691
|
+
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
692
|
+
`);
|
|
693
|
+
},
|
|
694
|
+
error(message, ...args) {
|
|
695
|
+
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
696
|
+
`);
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
|
|
655
701
|
// src/auth/AuthStateManager.ts
|
|
656
702
|
var import_node_events = require("events");
|
|
657
703
|
var AuthStateManager = class {
|
|
@@ -814,6 +860,7 @@ var AuthCore = class {
|
|
|
814
860
|
this.state = opts.stateManager ?? new AuthStateManager();
|
|
815
861
|
this.tokenStore = opts.tokenStore;
|
|
816
862
|
this.accessControl = opts.accessControl;
|
|
863
|
+
this.logger = opts.logger ?? noopLogger;
|
|
817
864
|
}
|
|
818
865
|
/** Snapshot of every account, the active provider, and the last error. */
|
|
819
866
|
status() {
|
|
@@ -835,16 +882,28 @@ var AuthCore = class {
|
|
|
835
882
|
*/
|
|
836
883
|
async login(provider, ui, shouldContinue) {
|
|
837
884
|
const providerImpl = this.registry.get(provider);
|
|
885
|
+
this.logger.info("[AuthCore] Starting device-flow login", { provider });
|
|
838
886
|
try {
|
|
839
887
|
const initial = await providerImpl.requestDeviceFlow();
|
|
840
888
|
await ui(initial);
|
|
889
|
+
if (!initial.deviceCode) {
|
|
890
|
+
this.logger.error("[AuthCore] Provider did not return deviceCode", provider);
|
|
891
|
+
throw new Error("Auth provider did not return deviceCode for device flow completion");
|
|
892
|
+
}
|
|
841
893
|
const session = await providerImpl.completeDeviceFlow(
|
|
842
|
-
initial.
|
|
843
|
-
shouldContinue
|
|
894
|
+
initial.deviceCode,
|
|
895
|
+
shouldContinue,
|
|
896
|
+
initial.pollIntervalMs
|
|
844
897
|
);
|
|
845
|
-
|
|
898
|
+
const result = await this.persistSession(providerImpl, session);
|
|
899
|
+
this.logger.info("[AuthCore] Device-flow login completed", { provider });
|
|
900
|
+
return result;
|
|
846
901
|
} catch (err) {
|
|
847
|
-
|
|
902
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
903
|
+
this.logger.error("[AuthCore] Device-flow login failed", message, {
|
|
904
|
+
provider
|
|
905
|
+
});
|
|
906
|
+
this.state.recordError(message);
|
|
848
907
|
throw err;
|
|
849
908
|
}
|
|
850
909
|
}
|
|
@@ -869,7 +928,10 @@ var AuthCore = class {
|
|
|
869
928
|
if (!provider) return null;
|
|
870
929
|
const account = this.state.getActiveAccount();
|
|
871
930
|
if (!account) return null;
|
|
872
|
-
const envelope = await this.tokenStore.get({
|
|
931
|
+
const envelope = await this.tokenStore.get({
|
|
932
|
+
provider,
|
|
933
|
+
accountId: account.id
|
|
934
|
+
});
|
|
873
935
|
if (!envelope) return null;
|
|
874
936
|
return { provider, account, token: envelope.token };
|
|
875
937
|
}
|
|
@@ -879,9 +941,15 @@ var AuthCore = class {
|
|
|
879
941
|
* every account.
|
|
880
942
|
*/
|
|
881
943
|
async logout(provider) {
|
|
944
|
+
this.logger.info("[AuthCore] Logout requested", {
|
|
945
|
+
provider: provider ?? "all"
|
|
946
|
+
});
|
|
882
947
|
if (!provider) {
|
|
883
948
|
for (const account of this.state.listAccounts()) {
|
|
884
|
-
await this.tokenStore.delete({
|
|
949
|
+
await this.tokenStore.delete({
|
|
950
|
+
provider: account.provider,
|
|
951
|
+
accountId: account.id
|
|
952
|
+
});
|
|
885
953
|
this.state.removeAccount(account.provider, account.id);
|
|
886
954
|
}
|
|
887
955
|
this.state.clearAll();
|
|
@@ -956,6 +1024,10 @@ var AuthCore = class {
|
|
|
956
1024
|
avatarUrl: meta.avatarUrl,
|
|
957
1025
|
expiresAt: meta.expiresAt ?? expiresAt
|
|
958
1026
|
};
|
|
1027
|
+
this.logger.debug("[AuthCore] Persisting session", {
|
|
1028
|
+
provider: meta.provider,
|
|
1029
|
+
accountId: meta.id
|
|
1030
|
+
});
|
|
959
1031
|
await this.tokenStore.set({ provider: meta.provider, accountId: meta.id }, session.token, {
|
|
960
1032
|
expiresAt
|
|
961
1033
|
});
|
|
@@ -1058,11 +1130,13 @@ var GitHubAuthProvider = class {
|
|
|
1058
1130
|
userUrl: config.userUrl ?? DEFAULT_USER_URL,
|
|
1059
1131
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
1060
1132
|
minPollIntervalMs: config.minPollIntervalMs ?? 1e3,
|
|
1061
|
-
maxPollIntervalMs: config.maxPollIntervalMs ??
|
|
1133
|
+
maxPollIntervalMs: config.maxPollIntervalMs ?? 6e4,
|
|
1062
1134
|
maxWaitMs: config.maxWaitMs,
|
|
1063
1135
|
fetchImpl: config.fetchImpl ?? fetch,
|
|
1064
1136
|
deviceCodeRetryBaseDelayMs: config.deviceCodeRetryBaseDelayMs ?? DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS
|
|
1065
1137
|
};
|
|
1138
|
+
this.logger = config.logger ?? noopLogger;
|
|
1139
|
+
this.sleepImpl = config.sleepImpl ?? sleep;
|
|
1066
1140
|
}
|
|
1067
1141
|
async requestDeviceFlow(opts) {
|
|
1068
1142
|
const body = JSON.stringify({
|
|
@@ -1071,6 +1145,10 @@ var GitHubAuthProvider = class {
|
|
|
1071
1145
|
});
|
|
1072
1146
|
let lastNetworkError;
|
|
1073
1147
|
for (let attempt = 1; attempt <= DEVICE_CODE_MAX_ATTEMPTS; attempt++) {
|
|
1148
|
+
this.logger.debug("[GitHubAuthProvider] Requesting device code", {
|
|
1149
|
+
attempt,
|
|
1150
|
+
maxAttempts: DEVICE_CODE_MAX_ATTEMPTS
|
|
1151
|
+
});
|
|
1074
1152
|
let resp;
|
|
1075
1153
|
try {
|
|
1076
1154
|
resp = await this.cfg.fetchImpl(this.cfg.deviceCodeUrl, {
|
|
@@ -1084,45 +1162,99 @@ var GitHubAuthProvider = class {
|
|
|
1084
1162
|
});
|
|
1085
1163
|
} catch (error) {
|
|
1086
1164
|
if (!isTransientNetworkError(error) || attempt === DEVICE_CODE_MAX_ATTEMPTS) {
|
|
1165
|
+
this.logger.error(
|
|
1166
|
+
"[GitHubAuthProvider] Device-code request failed (non-retryable)",
|
|
1167
|
+
error instanceof Error ? error.message : String(error),
|
|
1168
|
+
{ attempt }
|
|
1169
|
+
);
|
|
1087
1170
|
throw error;
|
|
1088
1171
|
}
|
|
1172
|
+
const delayMs = this.cfg.deviceCodeRetryBaseDelayMs * 2 ** (attempt - 1);
|
|
1173
|
+
this.logger.warn(
|
|
1174
|
+
"[GitHubAuthProvider] Transient network error requesting device code, retrying",
|
|
1175
|
+
{
|
|
1176
|
+
attempt,
|
|
1177
|
+
delayMs,
|
|
1178
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1179
|
+
}
|
|
1180
|
+
);
|
|
1089
1181
|
lastNetworkError = error;
|
|
1090
|
-
await
|
|
1182
|
+
await this.sleepImpl(delayMs);
|
|
1091
1183
|
continue;
|
|
1092
1184
|
}
|
|
1093
1185
|
if (!resp.ok) {
|
|
1186
|
+
this.logger.error(
|
|
1187
|
+
"[GitHubAuthProvider] Device-code request rejected by GitHub",
|
|
1188
|
+
`HTTP ${resp.status}`
|
|
1189
|
+
);
|
|
1094
1190
|
throw new Error(`GitHub device-code request failed: HTTP ${resp.status}`);
|
|
1095
1191
|
}
|
|
1096
1192
|
const data = await resp.json();
|
|
1097
1193
|
if (!data.device_code || !data.user_code || !data.verification_uri) {
|
|
1194
|
+
this.logger.error(
|
|
1195
|
+
"[GitHubAuthProvider] Device-code response missing required fields",
|
|
1196
|
+
JSON.stringify(Object.keys(data))
|
|
1197
|
+
);
|
|
1098
1198
|
throw new Error("GitHub device-code response missing required fields");
|
|
1099
1199
|
}
|
|
1200
|
+
this.logger.info("[GitHubAuthProvider] Device code obtained", {
|
|
1201
|
+
userCode: data.user_code,
|
|
1202
|
+
verificationUri: data.verification_uri,
|
|
1203
|
+
expiresInSec: data.expires_in,
|
|
1204
|
+
pollIntervalSec: data.interval
|
|
1205
|
+
});
|
|
1100
1206
|
return {
|
|
1101
1207
|
provider: this.providerId,
|
|
1208
|
+
deviceCode: data.device_code,
|
|
1102
1209
|
userCode: data.user_code,
|
|
1103
1210
|
verificationUrl: data.verification_uri,
|
|
1104
1211
|
expiresAt: Date.now() + data.expires_in * 1e3,
|
|
1212
|
+
pollIntervalMs: data.interval * 1e3,
|
|
1105
1213
|
message: `Open ${data.verification_uri} and enter ${data.user_code}`
|
|
1106
1214
|
};
|
|
1107
1215
|
}
|
|
1108
1216
|
throw lastNetworkError ?? new Error("GitHub device-code request failed");
|
|
1109
1217
|
}
|
|
1110
|
-
async completeDeviceFlow(deviceCode, shouldContinue) {
|
|
1218
|
+
async completeDeviceFlow(deviceCode, shouldContinue, initialPollIntervalMs) {
|
|
1111
1219
|
const start = Date.now();
|
|
1112
|
-
let pollIntervalMs = this.cfg.minPollIntervalMs;
|
|
1220
|
+
let pollIntervalMs = Math.max(this.cfg.minPollIntervalMs, initialPollIntervalMs ?? 0);
|
|
1113
1221
|
let consecutiveSlowDown = 0;
|
|
1222
|
+
let pollCount = 0;
|
|
1223
|
+
this.logger.info("[GitHubAuthProvider] Starting device-flow polling", {
|
|
1224
|
+
initialPollIntervalMs: pollIntervalMs,
|
|
1225
|
+
maxWaitMs: this.cfg.maxWaitMs
|
|
1226
|
+
});
|
|
1114
1227
|
while (true) {
|
|
1115
1228
|
if (shouldContinue && !shouldContinue()) {
|
|
1229
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1230
|
+
elapsedMs: Date.now() - start,
|
|
1231
|
+
pollCount
|
|
1232
|
+
});
|
|
1116
1233
|
throw new Error("GitHub device flow cancelled by caller");
|
|
1117
1234
|
}
|
|
1118
1235
|
const elapsed = Date.now() - start;
|
|
1119
1236
|
if (this.cfg.maxWaitMs !== void 0 && elapsed > this.cfg.maxWaitMs) {
|
|
1237
|
+
this.logger.warn("[GitHubAuthProvider] Device flow exceeded max wait time", {
|
|
1238
|
+
elapsedMs: elapsed,
|
|
1239
|
+
maxWaitMs: this.cfg.maxWaitMs,
|
|
1240
|
+
pollCount
|
|
1241
|
+
});
|
|
1120
1242
|
throw new Error("GitHub device flow exceeded max wait time");
|
|
1121
1243
|
}
|
|
1122
|
-
await
|
|
1244
|
+
await this.sleepImpl(pollIntervalMs);
|
|
1123
1245
|
if (shouldContinue && !shouldContinue()) {
|
|
1246
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1247
|
+
elapsedMs: Date.now() - start,
|
|
1248
|
+
pollCount
|
|
1249
|
+
});
|
|
1124
1250
|
throw new Error("GitHub device flow cancelled by caller");
|
|
1125
1251
|
}
|
|
1252
|
+
pollCount++;
|
|
1253
|
+
this.logger.debug("[GitHubAuthProvider] Polling for authorization", {
|
|
1254
|
+
pollCount,
|
|
1255
|
+
elapsedMs: Date.now() - start,
|
|
1256
|
+
pollIntervalMs
|
|
1257
|
+
});
|
|
1126
1258
|
let resp;
|
|
1127
1259
|
try {
|
|
1128
1260
|
resp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {
|
|
@@ -1140,19 +1272,59 @@ var GitHubAuthProvider = class {
|
|
|
1140
1272
|
});
|
|
1141
1273
|
} catch (error) {
|
|
1142
1274
|
if (!isTransientNetworkError(error)) {
|
|
1275
|
+
this.logger.error(
|
|
1276
|
+
"[GitHubAuthProvider] Non-retryable error while polling token endpoint",
|
|
1277
|
+
error instanceof Error ? error.message : String(error),
|
|
1278
|
+
{ pollCount }
|
|
1279
|
+
);
|
|
1143
1280
|
throw error;
|
|
1144
1281
|
}
|
|
1145
|
-
|
|
1282
|
+
const nextPollIntervalMs = Math.min(
|
|
1283
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1284
|
+
this.cfg.maxPollIntervalMs
|
|
1285
|
+
);
|
|
1286
|
+
this.logger.warn(
|
|
1287
|
+
"[GitHubAuthProvider] Transient network error while polling, backing off",
|
|
1288
|
+
{
|
|
1289
|
+
pollCount,
|
|
1290
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1291
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1292
|
+
nextPollIntervalMs
|
|
1293
|
+
}
|
|
1294
|
+
);
|
|
1295
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
1146
1296
|
continue;
|
|
1147
1297
|
}
|
|
1148
1298
|
if (!resp.ok) {
|
|
1149
|
-
|
|
1299
|
+
const nextPollIntervalMs = Math.min(
|
|
1300
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1301
|
+
this.cfg.maxPollIntervalMs
|
|
1302
|
+
);
|
|
1303
|
+
this.logger.warn(
|
|
1304
|
+
"[GitHubAuthProvider] Token endpoint returned non-OK status, backing off",
|
|
1305
|
+
{
|
|
1306
|
+
pollCount,
|
|
1307
|
+
status: resp.status,
|
|
1308
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1309
|
+
nextPollIntervalMs
|
|
1310
|
+
}
|
|
1311
|
+
);
|
|
1312
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
1150
1313
|
continue;
|
|
1151
1314
|
}
|
|
1152
1315
|
const data = await resp.json();
|
|
1153
1316
|
if (data.access_token) {
|
|
1317
|
+
this.logger.info("[GitHubAuthProvider] Authorization granted, fetching user profile", {
|
|
1318
|
+
pollCount,
|
|
1319
|
+
elapsedMs: Date.now() - start
|
|
1320
|
+
});
|
|
1154
1321
|
const rawUser = await this.fetchGitHubUser(data.access_token);
|
|
1155
1322
|
const email = await this.resolveEmail(data.access_token, rawUser);
|
|
1323
|
+
this.logger.info("[GitHubAuthProvider] Device-flow login completed", {
|
|
1324
|
+
pollCount,
|
|
1325
|
+
elapsedMs: Date.now() - start,
|
|
1326
|
+
login: rawUser.login
|
|
1327
|
+
});
|
|
1156
1328
|
return {
|
|
1157
1329
|
token: data.access_token,
|
|
1158
1330
|
refreshToken: data.refresh_token,
|
|
@@ -1167,22 +1339,42 @@ var GitHubAuthProvider = class {
|
|
|
1167
1339
|
};
|
|
1168
1340
|
}
|
|
1169
1341
|
if (data.error === "authorization_pending") {
|
|
1342
|
+
this.logger.debug("[GitHubAuthProvider] Authorization still pending", {
|
|
1343
|
+
pollCount,
|
|
1344
|
+
elapsedMs: Date.now() - start
|
|
1345
|
+
});
|
|
1170
1346
|
continue;
|
|
1171
1347
|
}
|
|
1172
1348
|
if (data.error === "slow_down") {
|
|
1173
1349
|
consecutiveSlowDown++;
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1350
|
+
const nextPollIntervalMs = Math.min(pollIntervalMs + 5e3, this.cfg.maxPollIntervalMs);
|
|
1351
|
+
this.logger.warn("[GitHubAuthProvider] GitHub requested slower polling (slow_down)", {
|
|
1352
|
+
pollCount,
|
|
1353
|
+
consecutiveSlowDown,
|
|
1354
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1355
|
+
nextPollIntervalMs
|
|
1356
|
+
});
|
|
1357
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
1178
1358
|
continue;
|
|
1179
1359
|
}
|
|
1180
1360
|
if (data.error === "access_denied") {
|
|
1361
|
+
this.logger.warn("[GitHubAuthProvider] User denied authorization", {
|
|
1362
|
+
pollCount
|
|
1363
|
+
});
|
|
1181
1364
|
throw new Error("User denied authorization");
|
|
1182
1365
|
}
|
|
1183
1366
|
if (data.error === "expired_token") {
|
|
1367
|
+
this.logger.warn(
|
|
1368
|
+
"[GitHubAuthProvider] Device code expired before authorization completed",
|
|
1369
|
+
{ pollCount, elapsedMs: Date.now() - start }
|
|
1370
|
+
);
|
|
1184
1371
|
throw new Error("GitHub device code expired \u2014 restart the flow");
|
|
1185
1372
|
}
|
|
1373
|
+
this.logger.error(
|
|
1374
|
+
"[GitHubAuthProvider] Unexpected device-flow error from token endpoint",
|
|
1375
|
+
data.error ?? "unknown",
|
|
1376
|
+
{ pollCount }
|
|
1377
|
+
);
|
|
1186
1378
|
throw new Error(`GitHub device flow error: ${data.error ?? "unknown"}`);
|
|
1187
1379
|
}
|
|
1188
1380
|
}
|
|
@@ -2057,10 +2249,20 @@ function isRecord(value) {
|
|
|
2057
2249
|
function isNodeError(value) {
|
|
2058
2250
|
return value instanceof Error && typeof value.code === "string";
|
|
2059
2251
|
}
|
|
2252
|
+
var LOCK_PID_FILE = "pid";
|
|
2253
|
+
function isProcessAlive(pid) {
|
|
2254
|
+
try {
|
|
2255
|
+
process.kill(pid, 0);
|
|
2256
|
+
return true;
|
|
2257
|
+
} catch {
|
|
2258
|
+
return false;
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2060
2261
|
var FileLock = class {
|
|
2061
2262
|
constructor(filePath, timeoutMs, retryMs) {
|
|
2062
2263
|
this.acquired = false;
|
|
2063
2264
|
this.dirPath = `${filePath}.lock`;
|
|
2265
|
+
this.pidFilePath = path3.join(this.dirPath, LOCK_PID_FILE);
|
|
2064
2266
|
this.timeoutMs = timeoutMs;
|
|
2065
2267
|
this.retryMs = retryMs;
|
|
2066
2268
|
}
|
|
@@ -2069,12 +2271,18 @@ var FileLock = class {
|
|
|
2069
2271
|
while (true) {
|
|
2070
2272
|
try {
|
|
2071
2273
|
await fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });
|
|
2274
|
+
await fsp.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
2072
2275
|
this.acquired = true;
|
|
2073
2276
|
return;
|
|
2074
2277
|
} catch (err) {
|
|
2075
2278
|
if (!isNodeError(err) || err.code !== "EEXIST") {
|
|
2076
2279
|
throw err;
|
|
2077
2280
|
}
|
|
2281
|
+
const stale = await this.isStaleLock();
|
|
2282
|
+
if (stale) {
|
|
2283
|
+
await fsp.rm(this.dirPath, { recursive: true, force: true });
|
|
2284
|
+
continue;
|
|
2285
|
+
}
|
|
2078
2286
|
if (Date.now() - start >= this.timeoutMs) {
|
|
2079
2287
|
throw new Error(`IdentityStore lock acquisition timed out for ${this.dirPath}`);
|
|
2080
2288
|
}
|
|
@@ -2082,6 +2290,16 @@ var FileLock = class {
|
|
|
2082
2290
|
}
|
|
2083
2291
|
}
|
|
2084
2292
|
}
|
|
2293
|
+
async isStaleLock() {
|
|
2294
|
+
try {
|
|
2295
|
+
const pidStr = await fsp.readFile(this.pidFilePath, "utf8");
|
|
2296
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
2297
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
2298
|
+
return !isProcessAlive(pid);
|
|
2299
|
+
} catch {
|
|
2300
|
+
return true;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2085
2303
|
async release() {
|
|
2086
2304
|
if (!this.acquired) return;
|
|
2087
2305
|
this.acquired = false;
|
|
@@ -2507,12 +2725,43 @@ var TOOL_CHECK_TIMEOUT_MS = {
|
|
|
2507
2725
|
pnpm: 15e3,
|
|
2508
2726
|
nrm: 15e3
|
|
2509
2727
|
};
|
|
2728
|
+
var POSIX_LOGIN_SHELL_FALLBACK_TOOLS = /* @__PURE__ */ new Set([
|
|
2729
|
+
"npm",
|
|
2730
|
+
"pnpm",
|
|
2731
|
+
"nrm",
|
|
2732
|
+
"node",
|
|
2733
|
+
"dotnet",
|
|
2734
|
+
"rtk"
|
|
2735
|
+
]);
|
|
2510
2736
|
var ERROR_CODE_NOT_FOUND = 127;
|
|
2511
2737
|
var ERROR_CODE_TIMEOUT = "ETIMEDOUT";
|
|
2738
|
+
var NVM_FALLBACK_DIRS = [
|
|
2739
|
+
"/opt/homebrew/opt/nvm",
|
|
2740
|
+
"/usr/local/opt/nvm",
|
|
2741
|
+
"$HOME/.nvm"
|
|
2742
|
+
];
|
|
2743
|
+
function resolveUserLoginShell(platform3, envShell) {
|
|
2744
|
+
const shell = envShell?.trim();
|
|
2745
|
+
if (shell && shell.length > 0) {
|
|
2746
|
+
return shell;
|
|
2747
|
+
}
|
|
2748
|
+
return platform3 === "darwin" ? "/bin/zsh" : "/bin/bash";
|
|
2749
|
+
}
|
|
2750
|
+
function buildNvmSourcingSnippet() {
|
|
2751
|
+
const fallbackList = NVM_FALLBACK_DIRS.map((dir) => `"${dir}"`).join(" ");
|
|
2752
|
+
return `for d in $NVM_DIR ${fallbackList}; do [ -n "$d" ] && [ -s "$d/nvm.sh" ] && export NVM_DIR="$d" && . "$d/nvm.sh" && break; done`;
|
|
2753
|
+
}
|
|
2754
|
+
function posixLoginShellArgs(platform3, envShell, command) {
|
|
2755
|
+
return {
|
|
2756
|
+
command: resolveUserLoginShell(platform3, envShell),
|
|
2757
|
+
args: ["-lc", `${buildNvmSourcingSnippet()}; ${command}`]
|
|
2758
|
+
};
|
|
2759
|
+
}
|
|
2512
2760
|
var EnvironmentInspector = class {
|
|
2513
2761
|
constructor(options = {}) {
|
|
2514
2762
|
this.runCommandFn = options.runCommand ?? runCommand;
|
|
2515
2763
|
this.platform = options.platform ?? process.platform;
|
|
2764
|
+
this.shell = options.shell ?? process.env.SHELL;
|
|
2516
2765
|
}
|
|
2517
2766
|
async checkEnvironment() {
|
|
2518
2767
|
const results = await Promise.all(
|
|
@@ -2550,6 +2799,21 @@ var EnvironmentInspector = class {
|
|
|
2550
2799
|
timeoutMs: this.getToolTimeout(toolName)
|
|
2551
2800
|
});
|
|
2552
2801
|
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2802
|
+
} catch {
|
|
2803
|
+
if (this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2804
|
+
return this.getToolPathFromLoginShell(toolName);
|
|
2805
|
+
}
|
|
2806
|
+
return void 0;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
async getToolPathFromLoginShell(toolName) {
|
|
2810
|
+
try {
|
|
2811
|
+
const spawnArgs = posixLoginShellArgs(this.platform, this.shell, `command -v ${toolName}`);
|
|
2812
|
+
const result = await this.runCommandFn(spawnArgs.command, {
|
|
2813
|
+
args: spawnArgs.args,
|
|
2814
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2815
|
+
});
|
|
2816
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2553
2817
|
} catch {
|
|
2554
2818
|
return void 0;
|
|
2555
2819
|
}
|
|
@@ -2574,12 +2838,31 @@ var EnvironmentInspector = class {
|
|
|
2574
2838
|
}
|
|
2575
2839
|
}
|
|
2576
2840
|
async getToolVersion(toolName) {
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2841
|
+
try {
|
|
2842
|
+
const invocation = await this.getVersionInvocation(toolName);
|
|
2843
|
+
const result = await this.runCommandFn(invocation.command, {
|
|
2844
|
+
args: invocation.args,
|
|
2845
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2846
|
+
});
|
|
2847
|
+
return this.parseVersion(toolName, result.stdout || result.stderr);
|
|
2848
|
+
} catch (error) {
|
|
2849
|
+
if (!this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2850
|
+
throw error;
|
|
2851
|
+
}
|
|
2852
|
+
const fallbackArgs = posixLoginShellArgs(this.platform, this.shell, `${toolName} --version`);
|
|
2853
|
+
const fallbackResult = await this.runCommandFn(fallbackArgs.command, {
|
|
2854
|
+
args: fallbackArgs.args,
|
|
2855
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2856
|
+
});
|
|
2857
|
+
const fallbackOutput = (fallbackResult.stdout || fallbackResult.stderr).trim();
|
|
2858
|
+
if (!fallbackOutput) {
|
|
2859
|
+
throw error;
|
|
2860
|
+
}
|
|
2861
|
+
return this.parseVersion(toolName, fallbackOutput);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
shouldUsePosixLoginShellFallback(toolName) {
|
|
2865
|
+
return this.platform !== "win32" && POSIX_LOGIN_SHELL_FALLBACK_TOOLS.has(toolName);
|
|
2583
2866
|
}
|
|
2584
2867
|
async checkNvm() {
|
|
2585
2868
|
if (this.platform === "win32") {
|
|
@@ -2601,11 +2884,9 @@ var EnvironmentInspector = class {
|
|
|
2601
2884
|
}
|
|
2602
2885
|
}
|
|
2603
2886
|
try {
|
|
2604
|
-
const
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; nvm --version'
|
|
2608
|
-
],
|
|
2887
|
+
const nvmArgs = posixLoginShellArgs(this.platform, this.shell, "nvm --version");
|
|
2888
|
+
const result = await this.runCommandFn(nvmArgs.command, {
|
|
2889
|
+
args: nvmArgs.args,
|
|
2609
2890
|
timeoutMs: this.getToolTimeout("nvm")
|
|
2610
2891
|
});
|
|
2611
2892
|
return {
|
|
@@ -2629,7 +2910,7 @@ var EnvironmentInspector = class {
|
|
|
2629
2910
|
};
|
|
2630
2911
|
}
|
|
2631
2912
|
try {
|
|
2632
|
-
const result = await this.runCommandFn(
|
|
2913
|
+
const result = await this.runCommandFn(dotnetPath, {
|
|
2633
2914
|
args: ["nuget", "list", "source"],
|
|
2634
2915
|
timeoutMs: this.getToolTimeout("nuget")
|
|
2635
2916
|
});
|
|
@@ -2792,7 +3073,9 @@ var GitClient = class {
|
|
|
2792
3073
|
}
|
|
2793
3074
|
const targetUrl = useProxy ? remoteUrl : originalUrl ?? remoteUrl;
|
|
2794
3075
|
const proxyUrl = this.rewriteRemoteUrl(repoId, targetUrl, useProxy);
|
|
2795
|
-
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
3076
|
+
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
3077
|
+
cwd: localPath
|
|
3078
|
+
});
|
|
2796
3079
|
const before = await this.safeRevParse(localPath);
|
|
2797
3080
|
await this.runOrThrow(["fetch", "origin"], { cwd: localPath });
|
|
2798
3081
|
const after = await this.safeRevParse(localPath);
|
|
@@ -2828,7 +3111,9 @@ var GitClient = class {
|
|
|
2828
3111
|
throw new Error(`no 'origin' remote configured at ${localPath}`);
|
|
2829
3112
|
}
|
|
2830
3113
|
const proxyUrl = this.rewriteRemoteUrl(repoId, remoteUrl, useProxy);
|
|
2831
|
-
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
3114
|
+
await this.runOrThrow(["remote", "set-url", "origin", proxyUrl], {
|
|
3115
|
+
cwd: localPath
|
|
3116
|
+
});
|
|
2832
3117
|
const result = await this.spawner.spawn(
|
|
2833
3118
|
["push", "origin", `refs/heads/${branch}:refs/heads/${branch}`],
|
|
2834
3119
|
{ cwd: localPath }
|
|
@@ -2882,7 +3167,9 @@ var GitClient = class {
|
|
|
2882
3167
|
return { commitSha: sha };
|
|
2883
3168
|
}
|
|
2884
3169
|
async runOrThrow(args, opts) {
|
|
2885
|
-
const result = await this.spawner.spawn(args, {
|
|
3170
|
+
const result = await this.spawner.spawn(args, {
|
|
3171
|
+
cwd: opts.cwd ?? process.cwd()
|
|
3172
|
+
});
|
|
2886
3173
|
if (result.code !== 0) {
|
|
2887
3174
|
throw new GitError(args, result);
|
|
2888
3175
|
}
|
|
@@ -2897,7 +3184,9 @@ var GitClient = class {
|
|
|
2897
3184
|
return url.length > 0 ? url : void 0;
|
|
2898
3185
|
}
|
|
2899
3186
|
async safeRevParse(localPath) {
|
|
2900
|
-
const result = await this.spawner.spawn(["rev-parse", "HEAD"], {
|
|
3187
|
+
const result = await this.spawner.spawn(["rev-parse", "HEAD"], {
|
|
3188
|
+
cwd: localPath
|
|
3189
|
+
});
|
|
2901
3190
|
if (result.code !== 0) return "";
|
|
2902
3191
|
return result.stdout.trim();
|
|
2903
3192
|
}
|
|
@@ -2944,7 +3233,11 @@ var StubGitSpawner = class {
|
|
|
2944
3233
|
this.calls.push({ args, cwd: opts.cwd });
|
|
2945
3234
|
const next = this.script.shift();
|
|
2946
3235
|
if (!next) {
|
|
2947
|
-
return {
|
|
3236
|
+
return {
|
|
3237
|
+
stdout: "",
|
|
3238
|
+
stderr: `stub: no scripted response for ${args.join(" ")}`,
|
|
3239
|
+
code: 1
|
|
3240
|
+
};
|
|
2948
3241
|
}
|
|
2949
3242
|
return next;
|
|
2950
3243
|
}
|
|
@@ -2956,6 +3249,10 @@ function toFileUrl(absolutePath) {
|
|
|
2956
3249
|
}
|
|
2957
3250
|
return `file://${normalized}`;
|
|
2958
3251
|
}
|
|
3252
|
+
var GIT_PROXY_PATH_SUFFIX = "/git-proxy";
|
|
3253
|
+
function buildGitProxyBase(serverBaseUrl) {
|
|
3254
|
+
return `${serverBaseUrl.replace(/\/+$/, "")}${GIT_PROXY_PATH_SUFFIX}`;
|
|
3255
|
+
}
|
|
2959
3256
|
|
|
2960
3257
|
// src/image/imageTools.ts
|
|
2961
3258
|
var fs5 = __toESM(require("fs/promises"));
|
|
@@ -3160,50 +3457,6 @@ function detectIndent(text) {
|
|
|
3160
3457
|
return 2;
|
|
3161
3458
|
}
|
|
3162
3459
|
|
|
3163
|
-
// src/logger.ts
|
|
3164
|
-
var noopLogger = {
|
|
3165
|
-
debug() {
|
|
3166
|
-
},
|
|
3167
|
-
info() {
|
|
3168
|
-
},
|
|
3169
|
-
warn() {
|
|
3170
|
-
},
|
|
3171
|
-
error() {
|
|
3172
|
-
}
|
|
3173
|
-
};
|
|
3174
|
-
function formatArgs(args) {
|
|
3175
|
-
return args.map((arg) => {
|
|
3176
|
-
if (typeof arg === "string") {
|
|
3177
|
-
return arg;
|
|
3178
|
-
}
|
|
3179
|
-
try {
|
|
3180
|
-
return JSON.stringify(arg);
|
|
3181
|
-
} catch {
|
|
3182
|
-
return String(arg);
|
|
3183
|
-
}
|
|
3184
|
-
}).join(" ");
|
|
3185
|
-
}
|
|
3186
|
-
function createConsoleLogger(prefix = "serviceme") {
|
|
3187
|
-
return {
|
|
3188
|
-
debug(message, ...args) {
|
|
3189
|
-
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
3190
|
-
`);
|
|
3191
|
-
},
|
|
3192
|
-
info(message, ...args) {
|
|
3193
|
-
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
3194
|
-
`);
|
|
3195
|
-
},
|
|
3196
|
-
warn(message, ...args) {
|
|
3197
|
-
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
3198
|
-
`);
|
|
3199
|
-
},
|
|
3200
|
-
error(message, ...args) {
|
|
3201
|
-
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
3202
|
-
`);
|
|
3203
|
-
}
|
|
3204
|
-
};
|
|
3205
|
-
}
|
|
3206
|
-
|
|
3207
3460
|
// src/paths/serverProxyGlobal.ts
|
|
3208
3461
|
var import_node_crypto4 = require("crypto");
|
|
3209
3462
|
var fs6 = __toESM(require("fs/promises"));
|
|
@@ -6853,10 +7106,19 @@ function coercePersistedToolbox(parsed) {
|
|
|
6853
7106
|
function isNodeError2(value) {
|
|
6854
7107
|
return value instanceof Error && typeof value.code === "string";
|
|
6855
7108
|
}
|
|
7109
|
+
function isProcessAlive2(pid) {
|
|
7110
|
+
try {
|
|
7111
|
+
process.kill(pid, 0);
|
|
7112
|
+
return true;
|
|
7113
|
+
} catch {
|
|
7114
|
+
return false;
|
|
7115
|
+
}
|
|
7116
|
+
}
|
|
6856
7117
|
var ToolboxFileLock = class {
|
|
6857
7118
|
constructor(filePath, timeoutMs, retryMs) {
|
|
6858
7119
|
this.acquired = false;
|
|
6859
7120
|
this.dirPath = `${filePath}.lock`;
|
|
7121
|
+
this.pidFilePath = path25.join(this.dirPath, "pid");
|
|
6860
7122
|
this.timeoutMs = timeoutMs;
|
|
6861
7123
|
this.retryMs = retryMs;
|
|
6862
7124
|
}
|
|
@@ -6865,10 +7127,16 @@ var ToolboxFileLock = class {
|
|
|
6865
7127
|
while (true) {
|
|
6866
7128
|
try {
|
|
6867
7129
|
await fsp2.mkdir(this.dirPath, { mode: LOCK_DIR_MODE2 });
|
|
7130
|
+
await fsp2.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
6868
7131
|
this.acquired = true;
|
|
6869
7132
|
return;
|
|
6870
7133
|
} catch (err) {
|
|
6871
7134
|
if (!isNodeError2(err) || err.code !== "EEXIST") throw err;
|
|
7135
|
+
const stale = await this.isStaleLock();
|
|
7136
|
+
if (stale) {
|
|
7137
|
+
await fsp2.rm(this.dirPath, { recursive: true, force: true });
|
|
7138
|
+
continue;
|
|
7139
|
+
}
|
|
6872
7140
|
if (Date.now() - start >= this.timeoutMs) {
|
|
6873
7141
|
throw new Error(`ToolboxStore lock acquisition timed out for ${this.dirPath}`);
|
|
6874
7142
|
}
|
|
@@ -6876,6 +7144,16 @@ var ToolboxFileLock = class {
|
|
|
6876
7144
|
}
|
|
6877
7145
|
}
|
|
6878
7146
|
}
|
|
7147
|
+
async isStaleLock() {
|
|
7148
|
+
try {
|
|
7149
|
+
const pidStr = await fsp2.readFile(this.pidFilePath, "utf8");
|
|
7150
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
7151
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
7152
|
+
return !isProcessAlive2(pid);
|
|
7153
|
+
} catch {
|
|
7154
|
+
return true;
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
6879
7157
|
async release() {
|
|
6880
7158
|
if (!this.acquired) return;
|
|
6881
7159
|
this.acquired = false;
|
|
@@ -7155,6 +7433,7 @@ var ToolboxCore = class {
|
|
|
7155
7433
|
EnvironmentInspector,
|
|
7156
7434
|
FsIdentityFileBackend,
|
|
7157
7435
|
FsToolboxFileBackend,
|
|
7436
|
+
GIT_PROXY_PATH_SUFFIX,
|
|
7158
7437
|
GitClient,
|
|
7159
7438
|
GitError,
|
|
7160
7439
|
GitHubAuthProvider,
|
|
@@ -7221,6 +7500,7 @@ var ToolboxCore = class {
|
|
|
7221
7500
|
bootstrapPhase5Placeholders,
|
|
7222
7501
|
buildDefaultReposFile,
|
|
7223
7502
|
buildGitHubLocalEmail,
|
|
7503
|
+
buildGitProxyBase,
|
|
7224
7504
|
buildSignedHeaders,
|
|
7225
7505
|
copilotDoctor,
|
|
7226
7506
|
copilotPrompt,
|