@serviceme/devtools-core 0.3.1 → 0.3.3
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.d.mts +83 -11
- package/dist/index.d.ts +83 -11
- package/dist/index.js +575 -243
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +559 -232
- package/dist/index.mjs.map +1 -1
- package/dist/skill-linker.js.map +1 -1
- package/dist/skill-linker.mjs.map +1 -1
- package/dist/submit.js.map +1 -1
- package/dist/submit.mjs.map +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
|
@@ -96,6 +96,7 @@ __export(src_exports, {
|
|
|
96
96
|
SCHEDULER_LOCK_FILENAME: () => SCHEDULER_LOCK_FILENAME,
|
|
97
97
|
SCHEDULER_LOG_FILENAME: () => SCHEDULER_LOG_FILENAME,
|
|
98
98
|
SCHEDULER_PID_FILENAME: () => SCHEDULER_PID_FILENAME,
|
|
99
|
+
SERVER_PROXY_GLOBAL_FILENAME: () => SERVER_PROXY_GLOBAL_FILENAME,
|
|
99
100
|
SERVICEME_DIR_NAME: () => SERVICEME_DIR_NAME,
|
|
100
101
|
SERVICEME_HOME_ENV: () => SERVICEME_HOME_ENV,
|
|
101
102
|
SKILL_DRAFTS_SUBDIR: () => SKILL_DRAFTS_SUBDIR,
|
|
@@ -163,6 +164,7 @@ __export(src_exports, {
|
|
|
163
164
|
getSchedulerLockPath: () => getSchedulerLockPath,
|
|
164
165
|
getSchedulerLogPath: () => getSchedulerLogPath,
|
|
165
166
|
getSchedulerPidPath: () => getSchedulerPidPath,
|
|
167
|
+
getServerProxyGlobalPath: () => getServerProxyGlobalPath,
|
|
166
168
|
getServicemeHome: () => getServicemeHome,
|
|
167
169
|
getSkillDraftsDir: () => getSkillDraftsDir,
|
|
168
170
|
getToolboxJsonPath: () => getToolboxJsonPath,
|
|
@@ -173,6 +175,7 @@ __export(src_exports, {
|
|
|
173
175
|
isUserRepo: () => isUserRepo,
|
|
174
176
|
matchesCron: () => matchesCron,
|
|
175
177
|
mergeWithDefaults: () => mergeWithDefaults,
|
|
178
|
+
migrateLegacyServerProxyEnabled: () => migrateLegacyServerProxyEnabled,
|
|
176
179
|
migrateToGlobal: () => migrateToGlobal,
|
|
177
180
|
moveFiles: () => moveFiles,
|
|
178
181
|
narrowRepoConfig: () => narrowRepoConfig,
|
|
@@ -180,6 +183,7 @@ __export(src_exports, {
|
|
|
180
183
|
parseAgentToolPermissions: () => parseAgentToolPermissions,
|
|
181
184
|
parseIntervalMs: () => parseIntervalMs,
|
|
182
185
|
randomInstallationId: () => randomInstallationId,
|
|
186
|
+
readServerProxyGlobal: () => readServerProxyGlobal,
|
|
183
187
|
reindexOrder: () => reindexOrder,
|
|
184
188
|
reposFileSchema: () => reposFileSchema,
|
|
185
189
|
resetUserHomeOverrides: () => resetUserHomeOverrides,
|
|
@@ -194,7 +198,8 @@ __export(src_exports, {
|
|
|
194
198
|
unzipFile: () => unzipFile,
|
|
195
199
|
userRepoConfigSchema: () => userRepoSchema,
|
|
196
200
|
validateReposFile: () => validateReposFile,
|
|
197
|
-
validateTaskPayload: () => validateTaskPayload
|
|
201
|
+
validateTaskPayload: () => validateTaskPayload,
|
|
202
|
+
writeServerProxyGlobal: () => writeServerProxyGlobal
|
|
198
203
|
});
|
|
199
204
|
module.exports = __toCommonJS(src_exports);
|
|
200
205
|
|
|
@@ -647,6 +652,50 @@ var AccessControl = class {
|
|
|
647
652
|
}
|
|
648
653
|
};
|
|
649
654
|
|
|
655
|
+
// src/logger.ts
|
|
656
|
+
var noopLogger = {
|
|
657
|
+
debug() {
|
|
658
|
+
},
|
|
659
|
+
info() {
|
|
660
|
+
},
|
|
661
|
+
warn() {
|
|
662
|
+
},
|
|
663
|
+
error() {
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
function formatArgs(args) {
|
|
667
|
+
return args.map((arg) => {
|
|
668
|
+
if (typeof arg === "string") {
|
|
669
|
+
return arg;
|
|
670
|
+
}
|
|
671
|
+
try {
|
|
672
|
+
return JSON.stringify(arg);
|
|
673
|
+
} catch {
|
|
674
|
+
return String(arg);
|
|
675
|
+
}
|
|
676
|
+
}).join(" ");
|
|
677
|
+
}
|
|
678
|
+
function createConsoleLogger(prefix = "serviceme") {
|
|
679
|
+
return {
|
|
680
|
+
debug(message, ...args) {
|
|
681
|
+
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
682
|
+
`);
|
|
683
|
+
},
|
|
684
|
+
info(message, ...args) {
|
|
685
|
+
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
686
|
+
`);
|
|
687
|
+
},
|
|
688
|
+
warn(message, ...args) {
|
|
689
|
+
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
690
|
+
`);
|
|
691
|
+
},
|
|
692
|
+
error(message, ...args) {
|
|
693
|
+
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
694
|
+
`);
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
650
699
|
// src/auth/AuthStateManager.ts
|
|
651
700
|
var import_node_events = require("events");
|
|
652
701
|
var AuthStateManager = class {
|
|
@@ -809,6 +858,7 @@ var AuthCore = class {
|
|
|
809
858
|
this.state = opts.stateManager ?? new AuthStateManager();
|
|
810
859
|
this.tokenStore = opts.tokenStore;
|
|
811
860
|
this.accessControl = opts.accessControl;
|
|
861
|
+
this.logger = opts.logger ?? noopLogger;
|
|
812
862
|
}
|
|
813
863
|
/** Snapshot of every account, the active provider, and the last error. */
|
|
814
864
|
status() {
|
|
@@ -830,16 +880,28 @@ var AuthCore = class {
|
|
|
830
880
|
*/
|
|
831
881
|
async login(provider, ui, shouldContinue) {
|
|
832
882
|
const providerImpl = this.registry.get(provider);
|
|
883
|
+
this.logger.info("[AuthCore] Starting device-flow login", { provider });
|
|
833
884
|
try {
|
|
834
885
|
const initial = await providerImpl.requestDeviceFlow();
|
|
835
886
|
await ui(initial);
|
|
887
|
+
if (!initial.deviceCode) {
|
|
888
|
+
this.logger.error("[AuthCore] Provider did not return deviceCode", provider);
|
|
889
|
+
throw new Error("Auth provider did not return deviceCode for device flow completion");
|
|
890
|
+
}
|
|
836
891
|
const session = await providerImpl.completeDeviceFlow(
|
|
837
|
-
initial.
|
|
838
|
-
shouldContinue
|
|
892
|
+
initial.deviceCode,
|
|
893
|
+
shouldContinue,
|
|
894
|
+
initial.pollIntervalMs
|
|
839
895
|
);
|
|
840
|
-
|
|
896
|
+
const result = await this.persistSession(providerImpl, session);
|
|
897
|
+
this.logger.info("[AuthCore] Device-flow login completed", { provider });
|
|
898
|
+
return result;
|
|
841
899
|
} catch (err) {
|
|
842
|
-
|
|
900
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
901
|
+
this.logger.error("[AuthCore] Device-flow login failed", message, {
|
|
902
|
+
provider
|
|
903
|
+
});
|
|
904
|
+
this.state.recordError(message);
|
|
843
905
|
throw err;
|
|
844
906
|
}
|
|
845
907
|
}
|
|
@@ -864,7 +926,10 @@ var AuthCore = class {
|
|
|
864
926
|
if (!provider) return null;
|
|
865
927
|
const account = this.state.getActiveAccount();
|
|
866
928
|
if (!account) return null;
|
|
867
|
-
const envelope = await this.tokenStore.get({
|
|
929
|
+
const envelope = await this.tokenStore.get({
|
|
930
|
+
provider,
|
|
931
|
+
accountId: account.id
|
|
932
|
+
});
|
|
868
933
|
if (!envelope) return null;
|
|
869
934
|
return { provider, account, token: envelope.token };
|
|
870
935
|
}
|
|
@@ -874,9 +939,15 @@ var AuthCore = class {
|
|
|
874
939
|
* every account.
|
|
875
940
|
*/
|
|
876
941
|
async logout(provider) {
|
|
942
|
+
this.logger.info("[AuthCore] Logout requested", {
|
|
943
|
+
provider: provider ?? "all"
|
|
944
|
+
});
|
|
877
945
|
if (!provider) {
|
|
878
946
|
for (const account of this.state.listAccounts()) {
|
|
879
|
-
await this.tokenStore.delete({
|
|
947
|
+
await this.tokenStore.delete({
|
|
948
|
+
provider: account.provider,
|
|
949
|
+
accountId: account.id
|
|
950
|
+
});
|
|
880
951
|
this.state.removeAccount(account.provider, account.id);
|
|
881
952
|
}
|
|
882
953
|
this.state.clearAll();
|
|
@@ -951,6 +1022,10 @@ var AuthCore = class {
|
|
|
951
1022
|
avatarUrl: meta.avatarUrl,
|
|
952
1023
|
expiresAt: meta.expiresAt ?? expiresAt
|
|
953
1024
|
};
|
|
1025
|
+
this.logger.debug("[AuthCore] Persisting session", {
|
|
1026
|
+
provider: meta.provider,
|
|
1027
|
+
accountId: meta.id
|
|
1028
|
+
});
|
|
954
1029
|
await this.tokenStore.set({ provider: meta.provider, accountId: meta.id }, session.token, {
|
|
955
1030
|
expiresAt
|
|
956
1031
|
});
|
|
@@ -1053,11 +1128,13 @@ var GitHubAuthProvider = class {
|
|
|
1053
1128
|
userUrl: config.userUrl ?? DEFAULT_USER_URL,
|
|
1054
1129
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
1055
1130
|
minPollIntervalMs: config.minPollIntervalMs ?? 1e3,
|
|
1056
|
-
maxPollIntervalMs: config.maxPollIntervalMs ??
|
|
1131
|
+
maxPollIntervalMs: config.maxPollIntervalMs ?? 6e4,
|
|
1057
1132
|
maxWaitMs: config.maxWaitMs,
|
|
1058
1133
|
fetchImpl: config.fetchImpl ?? fetch,
|
|
1059
1134
|
deviceCodeRetryBaseDelayMs: config.deviceCodeRetryBaseDelayMs ?? DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS
|
|
1060
1135
|
};
|
|
1136
|
+
this.logger = config.logger ?? noopLogger;
|
|
1137
|
+
this.sleepImpl = config.sleepImpl ?? sleep;
|
|
1061
1138
|
}
|
|
1062
1139
|
async requestDeviceFlow(opts) {
|
|
1063
1140
|
const body = JSON.stringify({
|
|
@@ -1066,6 +1143,10 @@ var GitHubAuthProvider = class {
|
|
|
1066
1143
|
});
|
|
1067
1144
|
let lastNetworkError;
|
|
1068
1145
|
for (let attempt = 1; attempt <= DEVICE_CODE_MAX_ATTEMPTS; attempt++) {
|
|
1146
|
+
this.logger.debug("[GitHubAuthProvider] Requesting device code", {
|
|
1147
|
+
attempt,
|
|
1148
|
+
maxAttempts: DEVICE_CODE_MAX_ATTEMPTS
|
|
1149
|
+
});
|
|
1069
1150
|
let resp;
|
|
1070
1151
|
try {
|
|
1071
1152
|
resp = await this.cfg.fetchImpl(this.cfg.deviceCodeUrl, {
|
|
@@ -1079,45 +1160,99 @@ var GitHubAuthProvider = class {
|
|
|
1079
1160
|
});
|
|
1080
1161
|
} catch (error) {
|
|
1081
1162
|
if (!isTransientNetworkError(error) || attempt === DEVICE_CODE_MAX_ATTEMPTS) {
|
|
1163
|
+
this.logger.error(
|
|
1164
|
+
"[GitHubAuthProvider] Device-code request failed (non-retryable)",
|
|
1165
|
+
error instanceof Error ? error.message : String(error),
|
|
1166
|
+
{ attempt }
|
|
1167
|
+
);
|
|
1082
1168
|
throw error;
|
|
1083
1169
|
}
|
|
1170
|
+
const delayMs = this.cfg.deviceCodeRetryBaseDelayMs * 2 ** (attempt - 1);
|
|
1171
|
+
this.logger.warn(
|
|
1172
|
+
"[GitHubAuthProvider] Transient network error requesting device code, retrying",
|
|
1173
|
+
{
|
|
1174
|
+
attempt,
|
|
1175
|
+
delayMs,
|
|
1176
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1177
|
+
}
|
|
1178
|
+
);
|
|
1084
1179
|
lastNetworkError = error;
|
|
1085
|
-
await
|
|
1180
|
+
await this.sleepImpl(delayMs);
|
|
1086
1181
|
continue;
|
|
1087
1182
|
}
|
|
1088
1183
|
if (!resp.ok) {
|
|
1184
|
+
this.logger.error(
|
|
1185
|
+
"[GitHubAuthProvider] Device-code request rejected by GitHub",
|
|
1186
|
+
`HTTP ${resp.status}`
|
|
1187
|
+
);
|
|
1089
1188
|
throw new Error(`GitHub device-code request failed: HTTP ${resp.status}`);
|
|
1090
1189
|
}
|
|
1091
1190
|
const data = await resp.json();
|
|
1092
1191
|
if (!data.device_code || !data.user_code || !data.verification_uri) {
|
|
1192
|
+
this.logger.error(
|
|
1193
|
+
"[GitHubAuthProvider] Device-code response missing required fields",
|
|
1194
|
+
JSON.stringify(Object.keys(data))
|
|
1195
|
+
);
|
|
1093
1196
|
throw new Error("GitHub device-code response missing required fields");
|
|
1094
1197
|
}
|
|
1198
|
+
this.logger.info("[GitHubAuthProvider] Device code obtained", {
|
|
1199
|
+
userCode: data.user_code,
|
|
1200
|
+
verificationUri: data.verification_uri,
|
|
1201
|
+
expiresInSec: data.expires_in,
|
|
1202
|
+
pollIntervalSec: data.interval
|
|
1203
|
+
});
|
|
1095
1204
|
return {
|
|
1096
1205
|
provider: this.providerId,
|
|
1206
|
+
deviceCode: data.device_code,
|
|
1097
1207
|
userCode: data.user_code,
|
|
1098
1208
|
verificationUrl: data.verification_uri,
|
|
1099
1209
|
expiresAt: Date.now() + data.expires_in * 1e3,
|
|
1210
|
+
pollIntervalMs: data.interval * 1e3,
|
|
1100
1211
|
message: `Open ${data.verification_uri} and enter ${data.user_code}`
|
|
1101
1212
|
};
|
|
1102
1213
|
}
|
|
1103
1214
|
throw lastNetworkError ?? new Error("GitHub device-code request failed");
|
|
1104
1215
|
}
|
|
1105
|
-
async completeDeviceFlow(deviceCode, shouldContinue) {
|
|
1216
|
+
async completeDeviceFlow(deviceCode, shouldContinue, initialPollIntervalMs) {
|
|
1106
1217
|
const start = Date.now();
|
|
1107
|
-
let pollIntervalMs = this.cfg.minPollIntervalMs;
|
|
1218
|
+
let pollIntervalMs = Math.max(this.cfg.minPollIntervalMs, initialPollIntervalMs ?? 0);
|
|
1108
1219
|
let consecutiveSlowDown = 0;
|
|
1220
|
+
let pollCount = 0;
|
|
1221
|
+
this.logger.info("[GitHubAuthProvider] Starting device-flow polling", {
|
|
1222
|
+
initialPollIntervalMs: pollIntervalMs,
|
|
1223
|
+
maxWaitMs: this.cfg.maxWaitMs
|
|
1224
|
+
});
|
|
1109
1225
|
while (true) {
|
|
1110
1226
|
if (shouldContinue && !shouldContinue()) {
|
|
1227
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1228
|
+
elapsedMs: Date.now() - start,
|
|
1229
|
+
pollCount
|
|
1230
|
+
});
|
|
1111
1231
|
throw new Error("GitHub device flow cancelled by caller");
|
|
1112
1232
|
}
|
|
1113
1233
|
const elapsed = Date.now() - start;
|
|
1114
1234
|
if (this.cfg.maxWaitMs !== void 0 && elapsed > this.cfg.maxWaitMs) {
|
|
1235
|
+
this.logger.warn("[GitHubAuthProvider] Device flow exceeded max wait time", {
|
|
1236
|
+
elapsedMs: elapsed,
|
|
1237
|
+
maxWaitMs: this.cfg.maxWaitMs,
|
|
1238
|
+
pollCount
|
|
1239
|
+
});
|
|
1115
1240
|
throw new Error("GitHub device flow exceeded max wait time");
|
|
1116
1241
|
}
|
|
1117
|
-
await
|
|
1242
|
+
await this.sleepImpl(pollIntervalMs);
|
|
1118
1243
|
if (shouldContinue && !shouldContinue()) {
|
|
1244
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1245
|
+
elapsedMs: Date.now() - start,
|
|
1246
|
+
pollCount
|
|
1247
|
+
});
|
|
1119
1248
|
throw new Error("GitHub device flow cancelled by caller");
|
|
1120
1249
|
}
|
|
1250
|
+
pollCount++;
|
|
1251
|
+
this.logger.debug("[GitHubAuthProvider] Polling for authorization", {
|
|
1252
|
+
pollCount,
|
|
1253
|
+
elapsedMs: Date.now() - start,
|
|
1254
|
+
pollIntervalMs
|
|
1255
|
+
});
|
|
1121
1256
|
let resp;
|
|
1122
1257
|
try {
|
|
1123
1258
|
resp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {
|
|
@@ -1135,19 +1270,59 @@ var GitHubAuthProvider = class {
|
|
|
1135
1270
|
});
|
|
1136
1271
|
} catch (error) {
|
|
1137
1272
|
if (!isTransientNetworkError(error)) {
|
|
1273
|
+
this.logger.error(
|
|
1274
|
+
"[GitHubAuthProvider] Non-retryable error while polling token endpoint",
|
|
1275
|
+
error instanceof Error ? error.message : String(error),
|
|
1276
|
+
{ pollCount }
|
|
1277
|
+
);
|
|
1138
1278
|
throw error;
|
|
1139
1279
|
}
|
|
1140
|
-
|
|
1280
|
+
const nextPollIntervalMs = Math.min(
|
|
1281
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1282
|
+
this.cfg.maxPollIntervalMs
|
|
1283
|
+
);
|
|
1284
|
+
this.logger.warn(
|
|
1285
|
+
"[GitHubAuthProvider] Transient network error while polling, backing off",
|
|
1286
|
+
{
|
|
1287
|
+
pollCount,
|
|
1288
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1289
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1290
|
+
nextPollIntervalMs
|
|
1291
|
+
}
|
|
1292
|
+
);
|
|
1293
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
1141
1294
|
continue;
|
|
1142
1295
|
}
|
|
1143
1296
|
if (!resp.ok) {
|
|
1144
|
-
|
|
1297
|
+
const nextPollIntervalMs = Math.min(
|
|
1298
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1299
|
+
this.cfg.maxPollIntervalMs
|
|
1300
|
+
);
|
|
1301
|
+
this.logger.warn(
|
|
1302
|
+
"[GitHubAuthProvider] Token endpoint returned non-OK status, backing off",
|
|
1303
|
+
{
|
|
1304
|
+
pollCount,
|
|
1305
|
+
status: resp.status,
|
|
1306
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1307
|
+
nextPollIntervalMs
|
|
1308
|
+
}
|
|
1309
|
+
);
|
|
1310
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
1145
1311
|
continue;
|
|
1146
1312
|
}
|
|
1147
1313
|
const data = await resp.json();
|
|
1148
1314
|
if (data.access_token) {
|
|
1315
|
+
this.logger.info("[GitHubAuthProvider] Authorization granted, fetching user profile", {
|
|
1316
|
+
pollCount,
|
|
1317
|
+
elapsedMs: Date.now() - start
|
|
1318
|
+
});
|
|
1149
1319
|
const rawUser = await this.fetchGitHubUser(data.access_token);
|
|
1150
1320
|
const email = await this.resolveEmail(data.access_token, rawUser);
|
|
1321
|
+
this.logger.info("[GitHubAuthProvider] Device-flow login completed", {
|
|
1322
|
+
pollCount,
|
|
1323
|
+
elapsedMs: Date.now() - start,
|
|
1324
|
+
login: rawUser.login
|
|
1325
|
+
});
|
|
1151
1326
|
return {
|
|
1152
1327
|
token: data.access_token,
|
|
1153
1328
|
refreshToken: data.refresh_token,
|
|
@@ -1162,22 +1337,42 @@ var GitHubAuthProvider = class {
|
|
|
1162
1337
|
};
|
|
1163
1338
|
}
|
|
1164
1339
|
if (data.error === "authorization_pending") {
|
|
1340
|
+
this.logger.debug("[GitHubAuthProvider] Authorization still pending", {
|
|
1341
|
+
pollCount,
|
|
1342
|
+
elapsedMs: Date.now() - start
|
|
1343
|
+
});
|
|
1165
1344
|
continue;
|
|
1166
1345
|
}
|
|
1167
1346
|
if (data.error === "slow_down") {
|
|
1168
1347
|
consecutiveSlowDown++;
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1348
|
+
const nextPollIntervalMs = Math.min(pollIntervalMs + 5e3, this.cfg.maxPollIntervalMs);
|
|
1349
|
+
this.logger.warn("[GitHubAuthProvider] GitHub requested slower polling (slow_down)", {
|
|
1350
|
+
pollCount,
|
|
1351
|
+
consecutiveSlowDown,
|
|
1352
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1353
|
+
nextPollIntervalMs
|
|
1354
|
+
});
|
|
1355
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
1173
1356
|
continue;
|
|
1174
1357
|
}
|
|
1175
1358
|
if (data.error === "access_denied") {
|
|
1359
|
+
this.logger.warn("[GitHubAuthProvider] User denied authorization", {
|
|
1360
|
+
pollCount
|
|
1361
|
+
});
|
|
1176
1362
|
throw new Error("User denied authorization");
|
|
1177
1363
|
}
|
|
1178
1364
|
if (data.error === "expired_token") {
|
|
1365
|
+
this.logger.warn(
|
|
1366
|
+
"[GitHubAuthProvider] Device code expired before authorization completed",
|
|
1367
|
+
{ pollCount, elapsedMs: Date.now() - start }
|
|
1368
|
+
);
|
|
1179
1369
|
throw new Error("GitHub device code expired \u2014 restart the flow");
|
|
1180
1370
|
}
|
|
1371
|
+
this.logger.error(
|
|
1372
|
+
"[GitHubAuthProvider] Unexpected device-flow error from token endpoint",
|
|
1373
|
+
data.error ?? "unknown",
|
|
1374
|
+
{ pollCount }
|
|
1375
|
+
);
|
|
1181
1376
|
throw new Error(`GitHub device flow error: ${data.error ?? "unknown"}`);
|
|
1182
1377
|
}
|
|
1183
1378
|
}
|
|
@@ -1857,6 +2052,7 @@ var DRAFTS_SUBDIR = "drafts";
|
|
|
1857
2052
|
var SKILL_DRAFTS_SUBDIR = "skills";
|
|
1858
2053
|
var AGENT_DRAFTS_SUBDIR = "agents";
|
|
1859
2054
|
var REPOS_CONFIG_FILENAME = "repos.json";
|
|
2055
|
+
var SERVER_PROXY_GLOBAL_FILENAME = "server-proxy.json";
|
|
1860
2056
|
var SAFE_REPO_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
1861
2057
|
var SERVICEME_HOME_ENV = "SERVICEME_HOME";
|
|
1862
2058
|
var activeOverrides = {};
|
|
@@ -1957,6 +2153,9 @@ function getMigrationFailuresPath() {
|
|
|
1957
2153
|
function getKnownWorkspacesPath() {
|
|
1958
2154
|
return path2.join(getServicemeHome(), KNOWN_WORKSPACES_FILENAME);
|
|
1959
2155
|
}
|
|
2156
|
+
function getServerProxyGlobalPath() {
|
|
2157
|
+
return path2.join(getServicemeHome(), SERVER_PROXY_GLOBAL_FILENAME);
|
|
2158
|
+
}
|
|
1960
2159
|
var CREDENTIALS_CONFIG_FILENAME = "credentials.json";
|
|
1961
2160
|
var DEVICE_JSON_FILENAME = "device.json";
|
|
1962
2161
|
var TOOLBOX_JSON_FILENAME = "toolbox.json";
|
|
@@ -2048,10 +2247,20 @@ function isRecord(value) {
|
|
|
2048
2247
|
function isNodeError(value) {
|
|
2049
2248
|
return value instanceof Error && typeof value.code === "string";
|
|
2050
2249
|
}
|
|
2250
|
+
var LOCK_PID_FILE = "pid";
|
|
2251
|
+
function isProcessAlive(pid) {
|
|
2252
|
+
try {
|
|
2253
|
+
process.kill(pid, 0);
|
|
2254
|
+
return true;
|
|
2255
|
+
} catch {
|
|
2256
|
+
return false;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2051
2259
|
var FileLock = class {
|
|
2052
2260
|
constructor(filePath, timeoutMs, retryMs) {
|
|
2053
2261
|
this.acquired = false;
|
|
2054
2262
|
this.dirPath = `${filePath}.lock`;
|
|
2263
|
+
this.pidFilePath = path3.join(this.dirPath, LOCK_PID_FILE);
|
|
2055
2264
|
this.timeoutMs = timeoutMs;
|
|
2056
2265
|
this.retryMs = retryMs;
|
|
2057
2266
|
}
|
|
@@ -2060,12 +2269,18 @@ var FileLock = class {
|
|
|
2060
2269
|
while (true) {
|
|
2061
2270
|
try {
|
|
2062
2271
|
await fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });
|
|
2272
|
+
await fsp.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
2063
2273
|
this.acquired = true;
|
|
2064
2274
|
return;
|
|
2065
2275
|
} catch (err) {
|
|
2066
2276
|
if (!isNodeError(err) || err.code !== "EEXIST") {
|
|
2067
2277
|
throw err;
|
|
2068
2278
|
}
|
|
2279
|
+
const stale = await this.isStaleLock();
|
|
2280
|
+
if (stale) {
|
|
2281
|
+
await fsp.rm(this.dirPath, { recursive: true, force: true });
|
|
2282
|
+
continue;
|
|
2283
|
+
}
|
|
2069
2284
|
if (Date.now() - start >= this.timeoutMs) {
|
|
2070
2285
|
throw new Error(`IdentityStore lock acquisition timed out for ${this.dirPath}`);
|
|
2071
2286
|
}
|
|
@@ -2073,6 +2288,16 @@ var FileLock = class {
|
|
|
2073
2288
|
}
|
|
2074
2289
|
}
|
|
2075
2290
|
}
|
|
2291
|
+
async isStaleLock() {
|
|
2292
|
+
try {
|
|
2293
|
+
const pidStr = await fsp.readFile(this.pidFilePath, "utf8");
|
|
2294
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
2295
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
2296
|
+
return !isProcessAlive(pid);
|
|
2297
|
+
} catch {
|
|
2298
|
+
return true;
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2076
2301
|
async release() {
|
|
2077
2302
|
if (!this.acquired) return;
|
|
2078
2303
|
this.acquired = false;
|
|
@@ -2498,6 +2723,14 @@ var TOOL_CHECK_TIMEOUT_MS = {
|
|
|
2498
2723
|
pnpm: 15e3,
|
|
2499
2724
|
nrm: 15e3
|
|
2500
2725
|
};
|
|
2726
|
+
var POSIX_LOGIN_SHELL_FALLBACK_TOOLS = /* @__PURE__ */ new Set([
|
|
2727
|
+
"npm",
|
|
2728
|
+
"pnpm",
|
|
2729
|
+
"nrm",
|
|
2730
|
+
"node",
|
|
2731
|
+
"dotnet",
|
|
2732
|
+
"rtk"
|
|
2733
|
+
]);
|
|
2501
2734
|
var ERROR_CODE_NOT_FOUND = 127;
|
|
2502
2735
|
var ERROR_CODE_TIMEOUT = "ETIMEDOUT";
|
|
2503
2736
|
var EnvironmentInspector = class {
|
|
@@ -2541,6 +2774,23 @@ var EnvironmentInspector = class {
|
|
|
2541
2774
|
timeoutMs: this.getToolTimeout(toolName)
|
|
2542
2775
|
});
|
|
2543
2776
|
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2777
|
+
} catch {
|
|
2778
|
+
if (this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2779
|
+
return this.getToolPathFromLoginShell(toolName);
|
|
2780
|
+
}
|
|
2781
|
+
return void 0;
|
|
2782
|
+
}
|
|
2783
|
+
}
|
|
2784
|
+
async getToolPathFromLoginShell(toolName) {
|
|
2785
|
+
try {
|
|
2786
|
+
const result = await this.runCommandFn("/bin/bash", {
|
|
2787
|
+
args: [
|
|
2788
|
+
"-lc",
|
|
2789
|
+
'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; command -v ' + toolName
|
|
2790
|
+
],
|
|
2791
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2792
|
+
});
|
|
2793
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2544
2794
|
} catch {
|
|
2545
2795
|
return void 0;
|
|
2546
2796
|
}
|
|
@@ -2565,12 +2815,33 @@ var EnvironmentInspector = class {
|
|
|
2565
2815
|
}
|
|
2566
2816
|
}
|
|
2567
2817
|
async getToolVersion(toolName) {
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2818
|
+
try {
|
|
2819
|
+
const invocation = await this.getVersionInvocation(toolName);
|
|
2820
|
+
const result = await this.runCommandFn(invocation.command, {
|
|
2821
|
+
args: invocation.args,
|
|
2822
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2823
|
+
});
|
|
2824
|
+
return this.parseVersion(toolName, result.stdout || result.stderr);
|
|
2825
|
+
} catch (error) {
|
|
2826
|
+
if (!this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2827
|
+
throw error;
|
|
2828
|
+
}
|
|
2829
|
+
const fallbackResult = await this.runCommandFn("/bin/bash", {
|
|
2830
|
+
args: [
|
|
2831
|
+
"-lc",
|
|
2832
|
+
`export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; ${toolName} --version`
|
|
2833
|
+
],
|
|
2834
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2835
|
+
});
|
|
2836
|
+
const fallbackOutput = (fallbackResult.stdout || fallbackResult.stderr).trim();
|
|
2837
|
+
if (!fallbackOutput) {
|
|
2838
|
+
throw error;
|
|
2839
|
+
}
|
|
2840
|
+
return this.parseVersion(toolName, fallbackOutput);
|
|
2841
|
+
}
|
|
2842
|
+
}
|
|
2843
|
+
shouldUsePosixLoginShellFallback(toolName) {
|
|
2844
|
+
return this.platform !== "win32" && POSIX_LOGIN_SHELL_FALLBACK_TOOLS.has(toolName);
|
|
2574
2845
|
}
|
|
2575
2846
|
async checkNvm() {
|
|
2576
2847
|
if (this.platform === "win32") {
|
|
@@ -2620,7 +2891,7 @@ var EnvironmentInspector = class {
|
|
|
2620
2891
|
};
|
|
2621
2892
|
}
|
|
2622
2893
|
try {
|
|
2623
|
-
const result = await this.runCommandFn(
|
|
2894
|
+
const result = await this.runCommandFn(dotnetPath, {
|
|
2624
2895
|
args: ["nuget", "list", "source"],
|
|
2625
2896
|
timeoutMs: this.getToolTimeout("nuget")
|
|
2626
2897
|
});
|
|
@@ -3151,54 +3422,85 @@ function detectIndent(text) {
|
|
|
3151
3422
|
return 2;
|
|
3152
3423
|
}
|
|
3153
3424
|
|
|
3154
|
-
// src/
|
|
3155
|
-
var
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
return arg;
|
|
3169
|
-
}
|
|
3170
|
-
try {
|
|
3171
|
-
return JSON.stringify(arg);
|
|
3172
|
-
} catch {
|
|
3173
|
-
return String(arg);
|
|
3174
|
-
}
|
|
3175
|
-
}).join(" ");
|
|
3176
|
-
}
|
|
3177
|
-
function createConsoleLogger(prefix = "serviceme") {
|
|
3178
|
-
return {
|
|
3179
|
-
debug(message, ...args) {
|
|
3180
|
-
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
3181
|
-
`);
|
|
3182
|
-
},
|
|
3183
|
-
info(message, ...args) {
|
|
3184
|
-
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
3185
|
-
`);
|
|
3186
|
-
},
|
|
3187
|
-
warn(message, ...args) {
|
|
3188
|
-
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
3189
|
-
`);
|
|
3190
|
-
},
|
|
3191
|
-
error(message, ...args) {
|
|
3192
|
-
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
3193
|
-
`);
|
|
3425
|
+
// src/paths/serverProxyGlobal.ts
|
|
3426
|
+
var import_node_crypto4 = require("crypto");
|
|
3427
|
+
var fs6 = __toESM(require("fs/promises"));
|
|
3428
|
+
var import_promises2 = require("fs/promises");
|
|
3429
|
+
var path9 = __toESM(require("path"));
|
|
3430
|
+
async function readServerProxyGlobal() {
|
|
3431
|
+
const filePath = getServerProxyGlobalPath();
|
|
3432
|
+
try {
|
|
3433
|
+
const raw = await fs6.readFile(filePath, "utf8");
|
|
3434
|
+
const parsed = JSON.parse(raw);
|
|
3435
|
+
if (!isServerProxyGlobalState(parsed)) {
|
|
3436
|
+
throw new Error(
|
|
3437
|
+
`Invalid ${SERVER_PROXY_GLOBAL_FILENAME}: expected {enabled: boolean, lastServerUrl?: string, updatedAt: string}, got ${JSON.stringify(parsed).slice(0, 80)}`
|
|
3438
|
+
);
|
|
3194
3439
|
}
|
|
3440
|
+
return parsed;
|
|
3441
|
+
} catch (err) {
|
|
3442
|
+
if (isENOENT(err)) return null;
|
|
3443
|
+
throw err;
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
async function writeServerProxyGlobal(patch) {
|
|
3447
|
+
const filePath = getServerProxyGlobalPath();
|
|
3448
|
+
const dirPath = path9.dirname(filePath);
|
|
3449
|
+
await fs6.mkdir(dirPath, { recursive: true });
|
|
3450
|
+
const current = await readServerProxyGlobal() ?? {
|
|
3451
|
+
enabled: false,
|
|
3452
|
+
allowOverride: false,
|
|
3453
|
+
lastServerUrl: void 0,
|
|
3454
|
+
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
3195
3455
|
};
|
|
3456
|
+
const next = {
|
|
3457
|
+
enabled: patch.enabled !== void 0 ? patch.enabled : current.enabled,
|
|
3458
|
+
allowOverride: patch.allowOverride !== void 0 ? patch.allowOverride : current.allowOverride,
|
|
3459
|
+
lastServerUrl: patch.lastServerUrl === void 0 ? current.lastServerUrl : patch.lastServerUrl === null ? void 0 : patch.lastServerUrl,
|
|
3460
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3461
|
+
};
|
|
3462
|
+
const tmpPath = `${filePath}.tmp-${(0, import_node_crypto4.randomUUID)()}`;
|
|
3463
|
+
const fh = await (0, import_promises2.open)(tmpPath, "w");
|
|
3464
|
+
try {
|
|
3465
|
+
await fh.writeFile(JSON.stringify(next, null, " "), "utf8");
|
|
3466
|
+
await fh.sync();
|
|
3467
|
+
} finally {
|
|
3468
|
+
await fh.close();
|
|
3469
|
+
}
|
|
3470
|
+
await fs6.rename(tmpPath, filePath);
|
|
3471
|
+
return next;
|
|
3472
|
+
}
|
|
3473
|
+
async function migrateLegacyServerProxyEnabled(readLegacy, clearLegacy) {
|
|
3474
|
+
const legacyEnabled = readLegacy();
|
|
3475
|
+
if (legacyEnabled !== true) return null;
|
|
3476
|
+
const existing = await readServerProxyGlobal();
|
|
3477
|
+
if (existing?.enabled === true) {
|
|
3478
|
+
await clearLegacy();
|
|
3479
|
+
return null;
|
|
3480
|
+
}
|
|
3481
|
+
const next = await writeServerProxyGlobal({ enabled: true });
|
|
3482
|
+
await clearLegacy();
|
|
3483
|
+
return next;
|
|
3484
|
+
}
|
|
3485
|
+
function isServerProxyGlobalState(v) {
|
|
3486
|
+
if (!v || typeof v !== "object") return false;
|
|
3487
|
+
const obj = v;
|
|
3488
|
+
if (typeof obj.enabled !== "boolean") return false;
|
|
3489
|
+
if (typeof obj.allowOverride !== "boolean") return false;
|
|
3490
|
+
if (typeof obj.updatedAt !== "string") return false;
|
|
3491
|
+
if (obj.lastServerUrl !== void 0 && obj.lastServerUrl !== null && typeof obj.lastServerUrl !== "string") {
|
|
3492
|
+
return false;
|
|
3493
|
+
}
|
|
3494
|
+
return true;
|
|
3495
|
+
}
|
|
3496
|
+
function isENOENT(err) {
|
|
3497
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
3196
3498
|
}
|
|
3197
3499
|
|
|
3198
3500
|
// src/phase5/bootstrap.ts
|
|
3199
|
-
var
|
|
3200
|
-
var
|
|
3201
|
-
var
|
|
3501
|
+
var import_node_crypto5 = require("crypto");
|
|
3502
|
+
var fs7 = __toESM(require("fs/promises"));
|
|
3503
|
+
var path10 = __toESM(require("path"));
|
|
3202
3504
|
function getPhase5FileSpecs() {
|
|
3203
3505
|
return [
|
|
3204
3506
|
{
|
|
@@ -3229,7 +3531,7 @@ function getPhase5FileSpecs() {
|
|
|
3229
3531
|
path: getMachineIdPath(),
|
|
3230
3532
|
// Random uuid, written as a bare string. Subsequent
|
|
3231
3533
|
// activations see the file and skip re-randomizing.
|
|
3232
|
-
defaultContent: (0,
|
|
3534
|
+
defaultContent: (0, import_node_crypto5.randomUUID)()
|
|
3233
3535
|
},
|
|
3234
3536
|
{
|
|
3235
3537
|
path: getProfilesJsonPath(),
|
|
@@ -3243,19 +3545,19 @@ async function bootstrapPhase5Placeholders() {
|
|
|
3243
3545
|
const result = { created: [], skipped: [], failed: [] };
|
|
3244
3546
|
const home = getServicemeHome();
|
|
3245
3547
|
try {
|
|
3246
|
-
await
|
|
3548
|
+
await fs7.mkdir(home, { recursive: true });
|
|
3247
3549
|
} catch (err) {
|
|
3248
3550
|
result.failed.push({ path: home, reason: err.message });
|
|
3249
3551
|
return result;
|
|
3250
3552
|
}
|
|
3251
3553
|
for (const spec of getPhase5FileSpecs()) {
|
|
3252
3554
|
try {
|
|
3253
|
-
await
|
|
3555
|
+
await fs7.access(spec.path);
|
|
3254
3556
|
result.skipped.push(spec.path);
|
|
3255
3557
|
} catch {
|
|
3256
3558
|
try {
|
|
3257
|
-
await
|
|
3258
|
-
await
|
|
3559
|
+
await fs7.mkdir(path10.dirname(spec.path), { recursive: true });
|
|
3560
|
+
await fs7.writeFile(spec.path, spec.defaultContent, "utf8");
|
|
3259
3561
|
result.created.push(spec.path);
|
|
3260
3562
|
} catch (writeErr) {
|
|
3261
3563
|
result.failed.push({ path: spec.path, reason: writeErr.message });
|
|
@@ -3266,13 +3568,13 @@ async function bootstrapPhase5Placeholders() {
|
|
|
3266
3568
|
}
|
|
3267
3569
|
|
|
3268
3570
|
// src/project/projectTools.ts
|
|
3269
|
-
var
|
|
3270
|
-
var
|
|
3571
|
+
var fs8 = __toESM(require("fs/promises"));
|
|
3572
|
+
var path11 = __toESM(require("path"));
|
|
3271
3573
|
var import_devtools_protocol7 = require("@serviceme/devtools-protocol");
|
|
3272
3574
|
|
|
3273
3575
|
// src/utils/fileUtils.ts
|
|
3274
3576
|
var import_node_fs = require("fs");
|
|
3275
|
-
var
|
|
3577
|
+
var import_promises3 = require("fs/promises");
|
|
3276
3578
|
var import_node_path = require("path");
|
|
3277
3579
|
var import_yauzl = __toESM(require("yauzl"));
|
|
3278
3580
|
var unzipFile = (zipPath, dest) => {
|
|
@@ -3283,12 +3585,12 @@ var unzipFile = (zipPath, dest) => {
|
|
|
3283
3585
|
zipfile.readEntry();
|
|
3284
3586
|
zipfile.on("entry", (entry) => {
|
|
3285
3587
|
if (/\/$/.test(entry.fileName)) {
|
|
3286
|
-
void (0,
|
|
3588
|
+
void (0, import_promises3.mkdir)((0, import_node_path.join)(dest, entry.fileName), { recursive: true }).then(() => {
|
|
3287
3589
|
zipfile.readEntry();
|
|
3288
3590
|
}).catch(reject);
|
|
3289
3591
|
} else {
|
|
3290
3592
|
const outputPath = (0, import_node_path.join)(dest, entry.fileName);
|
|
3291
|
-
void (0,
|
|
3593
|
+
void (0, import_promises3.mkdir)((0, import_node_path.dirname)(outputPath), { recursive: true }).then(() => {
|
|
3292
3594
|
zipfile.openReadStream(
|
|
3293
3595
|
entry,
|
|
3294
3596
|
(streamError, readStream) => {
|
|
@@ -3317,54 +3619,54 @@ var unzipFile = (zipPath, dest) => {
|
|
|
3317
3619
|
};
|
|
3318
3620
|
var tryLstat = async (targetPath) => {
|
|
3319
3621
|
try {
|
|
3320
|
-
return await (0,
|
|
3622
|
+
return await (0, import_promises3.lstat)(targetPath);
|
|
3321
3623
|
} catch {
|
|
3322
3624
|
return null;
|
|
3323
3625
|
}
|
|
3324
3626
|
};
|
|
3325
3627
|
var mergeEntry = async (sourcePath, destPath, overwrite) => {
|
|
3326
|
-
const sourceStat = await (0,
|
|
3628
|
+
const sourceStat = await (0, import_promises3.lstat)(sourcePath);
|
|
3327
3629
|
const destStat = await tryLstat(destPath);
|
|
3328
3630
|
if (sourceStat.isDirectory()) {
|
|
3329
3631
|
if (destStat && !destStat.isDirectory()) {
|
|
3330
3632
|
if (!overwrite) {
|
|
3331
|
-
await (0,
|
|
3633
|
+
await (0, import_promises3.rm)(sourcePath, { recursive: true, force: true });
|
|
3332
3634
|
return;
|
|
3333
3635
|
}
|
|
3334
|
-
await (0,
|
|
3636
|
+
await (0, import_promises3.rm)(destPath, { recursive: true, force: true });
|
|
3335
3637
|
}
|
|
3336
|
-
await (0,
|
|
3337
|
-
const children = await (0,
|
|
3638
|
+
await (0, import_promises3.mkdir)(destPath, { recursive: true });
|
|
3639
|
+
const children = await (0, import_promises3.readdir)(sourcePath);
|
|
3338
3640
|
for (const child of children) {
|
|
3339
3641
|
await mergeEntry((0, import_node_path.join)(sourcePath, child), (0, import_node_path.join)(destPath, child), overwrite);
|
|
3340
3642
|
}
|
|
3341
|
-
await (0,
|
|
3643
|
+
await (0, import_promises3.rm)(sourcePath, { recursive: true, force: true });
|
|
3342
3644
|
return;
|
|
3343
3645
|
}
|
|
3344
3646
|
if (destStat) {
|
|
3345
3647
|
if (!overwrite) {
|
|
3346
|
-
await (0,
|
|
3648
|
+
await (0, import_promises3.rm)(sourcePath, { recursive: true, force: true });
|
|
3347
3649
|
return;
|
|
3348
3650
|
}
|
|
3349
|
-
await (0,
|
|
3651
|
+
await (0, import_promises3.rm)(destPath, { recursive: true, force: true });
|
|
3350
3652
|
}
|
|
3351
3653
|
try {
|
|
3352
|
-
await (0,
|
|
3654
|
+
await (0, import_promises3.rename)(sourcePath, destPath);
|
|
3353
3655
|
} catch {
|
|
3354
|
-
await (0,
|
|
3355
|
-
await (0,
|
|
3656
|
+
await (0, import_promises3.copyFile)(sourcePath, destPath);
|
|
3657
|
+
await (0, import_promises3.rm)(sourcePath, { recursive: true, force: true });
|
|
3356
3658
|
}
|
|
3357
3659
|
};
|
|
3358
3660
|
var moveFiles = async (sourceDir, destDir, overwrite = false) => {
|
|
3359
|
-
await (0,
|
|
3360
|
-
const files = await (0,
|
|
3661
|
+
await (0, import_promises3.mkdir)(destDir, { recursive: true });
|
|
3662
|
+
const files = await (0, import_promises3.readdir)(sourceDir);
|
|
3361
3663
|
for (const file of files) {
|
|
3362
3664
|
const sourceFile = (0, import_node_path.join)(sourceDir, file);
|
|
3363
3665
|
const destFile = (0, import_node_path.join)(destDir, file);
|
|
3364
3666
|
if (!overwrite) {
|
|
3365
3667
|
try {
|
|
3366
|
-
await (0,
|
|
3367
|
-
await (0,
|
|
3668
|
+
await (0, import_promises3.access)(destFile, import_node_fs.constants.F_OK);
|
|
3669
|
+
await (0, import_promises3.rm)(sourceFile, { recursive: true, force: true });
|
|
3368
3670
|
continue;
|
|
3369
3671
|
} catch {
|
|
3370
3672
|
}
|
|
@@ -3377,10 +3679,10 @@ var moveFiles = async (sourceDir, destDir, overwrite = false) => {
|
|
|
3377
3679
|
var ProjectTools = class {
|
|
3378
3680
|
async extractTemplate(zipPath, workspacePath, tempExtractDir, input) {
|
|
3379
3681
|
await unzipFile(zipPath, tempExtractDir);
|
|
3380
|
-
let sourceDir =
|
|
3682
|
+
let sourceDir = path11.join(tempExtractDir, input.extractedDirName);
|
|
3381
3683
|
let actualDirName = input.extractedDirName;
|
|
3382
3684
|
if (!await this.pathExists(sourceDir)) {
|
|
3383
|
-
const entries = await
|
|
3685
|
+
const entries = await fs8.readdir(tempExtractDir, { withFileTypes: true });
|
|
3384
3686
|
const directories = entries.filter(
|
|
3385
3687
|
(entry) => entry.isDirectory() && !entry.name.startsWith(".")
|
|
3386
3688
|
);
|
|
@@ -3392,7 +3694,7 @@ var ProjectTools = class {
|
|
|
3392
3694
|
);
|
|
3393
3695
|
if (selectedDirectory) {
|
|
3394
3696
|
actualDirName = selectedDirectory;
|
|
3395
|
-
sourceDir =
|
|
3697
|
+
sourceDir = path11.join(tempExtractDir, actualDirName);
|
|
3396
3698
|
} else if (directories.length === 0) {
|
|
3397
3699
|
throw new Error(
|
|
3398
3700
|
`No directory found after extraction. Expected directory: ${input.extractedDirName}`
|
|
@@ -3442,7 +3744,7 @@ var ProjectTools = class {
|
|
|
3442
3744
|
} else {
|
|
3443
3745
|
for (const scriptPath of scripts) {
|
|
3444
3746
|
try {
|
|
3445
|
-
await
|
|
3747
|
+
await fs8.chmod(scriptPath, 493);
|
|
3446
3748
|
updatedCount += 1;
|
|
3447
3749
|
} catch {
|
|
3448
3750
|
}
|
|
@@ -3490,7 +3792,7 @@ var ProjectTools = class {
|
|
|
3490
3792
|
};
|
|
3491
3793
|
}
|
|
3492
3794
|
async ensurePresetManifest(workspacePath, preset) {
|
|
3493
|
-
const presetManifestPath =
|
|
3795
|
+
const presetManifestPath = path11.join(
|
|
3494
3796
|
workspacePath,
|
|
3495
3797
|
".ms-scaffold",
|
|
3496
3798
|
"presets",
|
|
@@ -3499,11 +3801,11 @@ var ProjectTools = class {
|
|
|
3499
3801
|
if (await this.pathExists(presetManifestPath)) {
|
|
3500
3802
|
return;
|
|
3501
3803
|
}
|
|
3502
|
-
const projectModePath =
|
|
3804
|
+
const projectModePath = path11.join(workspacePath, ".ms-scaffold", "project-mode.json");
|
|
3503
3805
|
if (!await this.pathExists(projectModePath)) {
|
|
3504
3806
|
return;
|
|
3505
3807
|
}
|
|
3506
|
-
const projectModeRaw = await
|
|
3808
|
+
const projectModeRaw = await fs8.readFile(projectModePath, "utf8");
|
|
3507
3809
|
const projectMode = JSON.parse(projectModeRaw);
|
|
3508
3810
|
const synthesizedPreset = {
|
|
3509
3811
|
preset,
|
|
@@ -3515,8 +3817,8 @@ var ProjectTools = class {
|
|
|
3515
3817
|
mergeManagedFiles: [],
|
|
3516
3818
|
userOwnedPaths: []
|
|
3517
3819
|
};
|
|
3518
|
-
await
|
|
3519
|
-
await
|
|
3820
|
+
await fs8.mkdir(path11.dirname(presetManifestPath), { recursive: true });
|
|
3821
|
+
await fs8.writeFile(
|
|
3520
3822
|
presetManifestPath,
|
|
3521
3823
|
`${JSON.stringify(synthesizedPreset, null, 2)}
|
|
3522
3824
|
`,
|
|
@@ -3527,12 +3829,12 @@ var ProjectTools = class {
|
|
|
3527
3829
|
const results = [];
|
|
3528
3830
|
let entries;
|
|
3529
3831
|
try {
|
|
3530
|
-
entries = await
|
|
3832
|
+
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
3531
3833
|
} catch {
|
|
3532
3834
|
return results;
|
|
3533
3835
|
}
|
|
3534
3836
|
for (const entry of entries) {
|
|
3535
|
-
const fullPath =
|
|
3837
|
+
const fullPath = path11.join(dir, entry.name);
|
|
3536
3838
|
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
3537
3839
|
results.push(...await this.findScripts(fullPath, extensions));
|
|
3538
3840
|
} else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
@@ -3543,7 +3845,7 @@ var ProjectTools = class {
|
|
|
3543
3845
|
}
|
|
3544
3846
|
async pathExists(targetPath) {
|
|
3545
3847
|
try {
|
|
3546
|
-
await
|
|
3848
|
+
await fs8.access(targetPath);
|
|
3547
3849
|
return true;
|
|
3548
3850
|
} catch {
|
|
3549
3851
|
return false;
|
|
@@ -3560,7 +3862,7 @@ var ProjectTools = class {
|
|
|
3560
3862
|
const matches = [];
|
|
3561
3863
|
for (const directoryName of directoryNames) {
|
|
3562
3864
|
if (await this.directoryMatchesProjectPattern(
|
|
3563
|
-
|
|
3865
|
+
path11.join(tempExtractDir, directoryName),
|
|
3564
3866
|
projectFilePattern
|
|
3565
3867
|
)) {
|
|
3566
3868
|
matches.push(directoryName);
|
|
@@ -3572,7 +3874,7 @@ var ProjectTools = class {
|
|
|
3572
3874
|
return null;
|
|
3573
3875
|
}
|
|
3574
3876
|
async directoryMatchesProjectPattern(directoryPath, projectFilePattern) {
|
|
3575
|
-
const entries = await
|
|
3877
|
+
const entries = await fs8.readdir(directoryPath);
|
|
3576
3878
|
if (projectFilePattern.includes("*")) {
|
|
3577
3879
|
const regex = new RegExp(`^${projectFilePattern.replace("*", ".*")}$`);
|
|
3578
3880
|
return entries.some((entry) => regex.test(entry));
|
|
@@ -3585,8 +3887,8 @@ function createProjectTools() {
|
|
|
3585
3887
|
}
|
|
3586
3888
|
|
|
3587
3889
|
// src/repo-manager/index.ts
|
|
3588
|
-
var
|
|
3589
|
-
var
|
|
3890
|
+
var fs9 = __toESM(require("fs/promises"));
|
|
3891
|
+
var path12 = __toESM(require("path"));
|
|
3590
3892
|
|
|
3591
3893
|
// src/repos/types.ts
|
|
3592
3894
|
function isDefaultRepo(repo) {
|
|
@@ -3668,11 +3970,11 @@ var RepoManager = class {
|
|
|
3668
3970
|
const exists = await this.pathExists(localPath);
|
|
3669
3971
|
if (exists) {
|
|
3670
3972
|
if (await this.isValidGitRepo(localPath)) continue;
|
|
3671
|
-
await
|
|
3973
|
+
await fs9.rm(localPath, { recursive: true, force: true });
|
|
3672
3974
|
}
|
|
3673
3975
|
try {
|
|
3674
3976
|
if (!this.skipClone) {
|
|
3675
|
-
await
|
|
3977
|
+
await fs9.mkdir(path12.dirname(localPath), { recursive: true });
|
|
3676
3978
|
await this.git.clone(repo.id, repo.url, localPath, repo.branch, true);
|
|
3677
3979
|
}
|
|
3678
3980
|
await this.store.updateRepo(repo.id, {
|
|
@@ -3707,10 +4009,10 @@ var RepoManager = class {
|
|
|
3707
4009
|
const localPath = getRepoDir(repoId);
|
|
3708
4010
|
const exists = await this.pathExists(localPath);
|
|
3709
4011
|
if (exists && !await this.isValidGitRepo(localPath)) {
|
|
3710
|
-
await
|
|
4012
|
+
await fs9.rm(localPath, { recursive: true, force: true });
|
|
3711
4013
|
}
|
|
3712
4014
|
if (!exists || !await this.pathExists(localPath)) {
|
|
3713
|
-
await
|
|
4015
|
+
await fs9.mkdir(path12.dirname(localPath), { recursive: true });
|
|
3714
4016
|
if (!this.skipClone) {
|
|
3715
4017
|
await this.git.clone(proxyId, repo.url, localPath, repo.branch, useProxy);
|
|
3716
4018
|
}
|
|
@@ -3810,7 +4112,7 @@ var RepoManager = class {
|
|
|
3810
4112
|
let cloned = false;
|
|
3811
4113
|
if (!this.skipClone) {
|
|
3812
4114
|
const localPath = getRepoDir(id);
|
|
3813
|
-
await
|
|
4115
|
+
await fs9.mkdir(path12.dirname(localPath), { recursive: true });
|
|
3814
4116
|
await this.git.clone(userProxyId, url, localPath, branch, useProxy);
|
|
3815
4117
|
cloned = true;
|
|
3816
4118
|
}
|
|
@@ -3835,7 +4137,7 @@ var RepoManager = class {
|
|
|
3835
4137
|
await this.store.removeUserRepo(repoId);
|
|
3836
4138
|
const localPath = getRepoDir(repoId);
|
|
3837
4139
|
try {
|
|
3838
|
-
await
|
|
4140
|
+
await fs9.rm(localPath, { recursive: true, force: true });
|
|
3839
4141
|
} catch (err) {
|
|
3840
4142
|
if (err.code !== "ENOENT") throw err;
|
|
3841
4143
|
}
|
|
@@ -3858,7 +4160,7 @@ var RepoManager = class {
|
|
|
3858
4160
|
}
|
|
3859
4161
|
/** Force-create the SERVICEME home directory tree (idempotent). */
|
|
3860
4162
|
async ensureHome() {
|
|
3861
|
-
await
|
|
4163
|
+
await fs9.mkdir(getServicemeHome(), { recursive: true });
|
|
3862
4164
|
}
|
|
3863
4165
|
/**
|
|
3864
4166
|
* Returns `true` when `p` contains a `.git` entry — i.e. it is an
|
|
@@ -3866,11 +4168,11 @@ var RepoManager = class {
|
|
|
3866
4168
|
* (e.g. from an interrupted clone) return `false`.
|
|
3867
4169
|
*/
|
|
3868
4170
|
async isValidGitRepo(p) {
|
|
3869
|
-
return this.pathExists(
|
|
4171
|
+
return this.pathExists(path12.join(p, ".git"));
|
|
3870
4172
|
}
|
|
3871
4173
|
async pathExists(p) {
|
|
3872
4174
|
try {
|
|
3873
|
-
await
|
|
4175
|
+
await fs9.stat(p);
|
|
3874
4176
|
return true;
|
|
3875
4177
|
} catch {
|
|
3876
4178
|
return false;
|
|
@@ -3995,8 +4297,8 @@ function resolveDefaultRepoId(existing, existingIds) {
|
|
|
3995
4297
|
}
|
|
3996
4298
|
|
|
3997
4299
|
// src/repos/loader.ts
|
|
3998
|
-
var
|
|
3999
|
-
var
|
|
4300
|
+
var fs10 = __toESM(require("fs/promises"));
|
|
4301
|
+
var path13 = __toESM(require("path"));
|
|
4000
4302
|
var import_zod = require("zod");
|
|
4001
4303
|
var ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})$/;
|
|
4002
4304
|
var repoIdSchema = import_zod.z.string().min(1).max(64).regex(SAFE_REPO_ID_PATTERN, "repo id must match /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/");
|
|
@@ -4079,7 +4381,7 @@ var ReposLoader = class {
|
|
|
4079
4381
|
this.configPath = options.configPath;
|
|
4080
4382
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
4081
4383
|
this.randomSuffix = options.randomSuffix ?? defaultRandomSuffix;
|
|
4082
|
-
this.fileSystem = options.fileSystem ??
|
|
4384
|
+
this.fileSystem = options.fileSystem ?? fs10;
|
|
4083
4385
|
}
|
|
4084
4386
|
/** Absolute path of the file this loader reads/writes. */
|
|
4085
4387
|
getConfigPath() {
|
|
@@ -4132,7 +4434,7 @@ var ReposLoader = class {
|
|
|
4132
4434
|
*/
|
|
4133
4435
|
async save(config) {
|
|
4134
4436
|
const validated = reposFileSchema.parse(config);
|
|
4135
|
-
const dir =
|
|
4437
|
+
const dir = path13.dirname(this.configPath);
|
|
4136
4438
|
await this.fileSystem.mkdir(dir, { recursive: true });
|
|
4137
4439
|
const serialized = `${JSON.stringify(validated, null, 2)}
|
|
4138
4440
|
`;
|
|
@@ -4141,7 +4443,7 @@ var ReposLoader = class {
|
|
|
4141
4443
|
try {
|
|
4142
4444
|
await this.fileSystem.rename(tempPath, this.configPath);
|
|
4143
4445
|
} catch (error) {
|
|
4144
|
-
const unlink2 = this.fileSystem.unlink ??
|
|
4446
|
+
const unlink2 = this.fileSystem.unlink ?? fs10.unlink;
|
|
4145
4447
|
await unlink2(tempPath).catch(() => void 0);
|
|
4146
4448
|
throw error;
|
|
4147
4449
|
}
|
|
@@ -4201,7 +4503,7 @@ function narrowRepoConfig(repo) {
|
|
|
4201
4503
|
|
|
4202
4504
|
// src/repos/store.ts
|
|
4203
4505
|
var import_node_events2 = require("events");
|
|
4204
|
-
var
|
|
4506
|
+
var fs11 = __toESM(require("fs"));
|
|
4205
4507
|
var ReposStore = class {
|
|
4206
4508
|
constructor(options = {}) {
|
|
4207
4509
|
this.config = null;
|
|
@@ -4210,7 +4512,7 @@ var ReposStore = class {
|
|
|
4210
4512
|
this.reloadTimer = null;
|
|
4211
4513
|
this.lastLoadResult = null;
|
|
4212
4514
|
this.loader = options.loader ?? new ReposLoader({ configPath: "" });
|
|
4213
|
-
this.fileSystem = options.fileSystem ?? { watch:
|
|
4515
|
+
this.fileSystem = options.fileSystem ?? { watch: fs11.watch };
|
|
4214
4516
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
4215
4517
|
this.debounceMs = options.debounceMs ?? 50;
|
|
4216
4518
|
this.createFsWatcher = options.createFsWatcher ?? ((p, cb) => this.defaultCreateFsWatcher(p, cb));
|
|
@@ -4492,17 +4794,17 @@ async function bootstrapDefaults(store) {
|
|
|
4492
4794
|
}
|
|
4493
4795
|
|
|
4494
4796
|
// src/scheduled-tasks/daemon/DaemonLogger.ts
|
|
4495
|
-
var
|
|
4496
|
-
var
|
|
4797
|
+
var fs12 = __toESM(require("fs"));
|
|
4798
|
+
var path14 = __toESM(require("path"));
|
|
4497
4799
|
var CONFIG_DIR = ".serviceme";
|
|
4498
4800
|
var LOG_FILE = "scheduler.log";
|
|
4499
4801
|
var MAX_LOG_SIZE = 1024 * 1024;
|
|
4500
4802
|
var DaemonLogger = class {
|
|
4501
4803
|
constructor(workspacePath, options = {}) {
|
|
4502
|
-
this.logPath = options.logPath ??
|
|
4503
|
-
const dir =
|
|
4504
|
-
if (!
|
|
4505
|
-
|
|
4804
|
+
this.logPath = options.logPath ?? path14.join(workspacePath, CONFIG_DIR, LOG_FILE);
|
|
4805
|
+
const dir = path14.dirname(this.logPath);
|
|
4806
|
+
if (!fs12.existsSync(dir)) {
|
|
4807
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
4506
4808
|
}
|
|
4507
4809
|
}
|
|
4508
4810
|
getLogPath() {
|
|
@@ -4513,16 +4815,16 @@ var DaemonLogger = class {
|
|
|
4513
4815
|
const line = `[${ts}] [${level.toUpperCase()}] ${message}
|
|
4514
4816
|
`;
|
|
4515
4817
|
this.rotateIfNeeded();
|
|
4516
|
-
|
|
4818
|
+
fs12.appendFileSync(this.logPath, line, "utf-8");
|
|
4517
4819
|
}
|
|
4518
4820
|
rotateIfNeeded() {
|
|
4519
4821
|
try {
|
|
4520
|
-
const stats =
|
|
4822
|
+
const stats = fs12.statSync(this.logPath);
|
|
4521
4823
|
if (stats.size > MAX_LOG_SIZE) {
|
|
4522
|
-
const content =
|
|
4824
|
+
const content = fs12.readFileSync(this.logPath, "utf-8");
|
|
4523
4825
|
const halfIdx = content.indexOf("\n", Math.floor(content.length / 2));
|
|
4524
4826
|
if (halfIdx > 0) {
|
|
4525
|
-
|
|
4827
|
+
fs12.writeFileSync(this.logPath, content.slice(halfIdx + 1), "utf-8");
|
|
4526
4828
|
}
|
|
4527
4829
|
}
|
|
4528
4830
|
} catch {
|
|
@@ -4531,39 +4833,39 @@ var DaemonLogger = class {
|
|
|
4531
4833
|
};
|
|
4532
4834
|
|
|
4533
4835
|
// src/scheduled-tasks/daemon/PidManager.ts
|
|
4534
|
-
var
|
|
4535
|
-
var
|
|
4836
|
+
var fs13 = __toESM(require("fs"));
|
|
4837
|
+
var path15 = __toESM(require("path"));
|
|
4536
4838
|
var CONFIG_DIR2 = ".serviceme";
|
|
4537
4839
|
var PID_FILE = "scheduler.pid";
|
|
4538
4840
|
var PidManager = class {
|
|
4539
4841
|
constructor(workspacePath, options = {}) {
|
|
4540
|
-
this.pidPath = options.pidPath ??
|
|
4842
|
+
this.pidPath = options.pidPath ?? path15.join(workspacePath, CONFIG_DIR2, PID_FILE);
|
|
4541
4843
|
}
|
|
4542
4844
|
getPidPath() {
|
|
4543
4845
|
return this.pidPath;
|
|
4544
4846
|
}
|
|
4545
4847
|
writePid(pid) {
|
|
4546
|
-
const dir =
|
|
4547
|
-
if (!
|
|
4548
|
-
|
|
4848
|
+
const dir = path15.dirname(this.pidPath);
|
|
4849
|
+
if (!fs13.existsSync(dir)) {
|
|
4850
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
4549
4851
|
}
|
|
4550
|
-
|
|
4852
|
+
fs13.writeFileSync(this.pidPath, String(pid), "utf-8");
|
|
4551
4853
|
}
|
|
4552
4854
|
readPid() {
|
|
4553
4855
|
let stat5;
|
|
4554
4856
|
try {
|
|
4555
|
-
stat5 =
|
|
4857
|
+
stat5 = fs13.statSync(this.pidPath);
|
|
4556
4858
|
} catch {
|
|
4557
4859
|
return null;
|
|
4558
4860
|
}
|
|
4559
4861
|
if (!stat5.isFile()) return null;
|
|
4560
|
-
const raw =
|
|
4862
|
+
const raw = fs13.readFileSync(this.pidPath, "utf-8").trim();
|
|
4561
4863
|
const pid = Number.parseInt(raw, 10);
|
|
4562
4864
|
return Number.isNaN(pid) ? null : pid;
|
|
4563
4865
|
}
|
|
4564
4866
|
removePid() {
|
|
4565
|
-
if (
|
|
4566
|
-
|
|
4867
|
+
if (fs13.existsSync(this.pidPath)) {
|
|
4868
|
+
fs13.unlinkSync(this.pidPath);
|
|
4567
4869
|
}
|
|
4568
4870
|
}
|
|
4569
4871
|
isProcessRunning(pid) {
|
|
@@ -4584,13 +4886,13 @@ var PidManager = class {
|
|
|
4584
4886
|
};
|
|
4585
4887
|
|
|
4586
4888
|
// src/scheduled-tasks/daemon/SchedulerDaemon.ts
|
|
4587
|
-
var
|
|
4889
|
+
var fs18 = __toESM(require("fs"));
|
|
4588
4890
|
var os5 = __toESM(require("os"));
|
|
4589
|
-
var
|
|
4891
|
+
var path19 = __toESM(require("path"));
|
|
4590
4892
|
|
|
4591
4893
|
// src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts
|
|
4592
4894
|
var import_node_child_process3 = require("child_process");
|
|
4593
|
-
var
|
|
4895
|
+
var fs14 = __toESM(require("fs"));
|
|
4594
4896
|
|
|
4595
4897
|
// src/scheduled-tasks/executors/timeout.ts
|
|
4596
4898
|
function resolveConfiguredTimeoutMs(timeoutSeconds, defaultTimeoutMs) {
|
|
@@ -4625,7 +4927,7 @@ function redactArgs(args) {
|
|
|
4625
4927
|
function writeDiagnostic(message) {
|
|
4626
4928
|
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
|
|
4627
4929
|
if (logPath) {
|
|
4628
|
-
|
|
4930
|
+
fs14.appendFileSync(logPath, message);
|
|
4629
4931
|
return;
|
|
4630
4932
|
}
|
|
4631
4933
|
process.stderr.write(message);
|
|
@@ -4831,15 +5133,15 @@ ${body}`.trim()
|
|
|
4831
5133
|
|
|
4832
5134
|
// src/scheduled-tasks/executors/ShellExecutor.ts
|
|
4833
5135
|
var import_node_child_process4 = require("child_process");
|
|
4834
|
-
var
|
|
4835
|
-
var
|
|
5136
|
+
var fs15 = __toESM(require("fs"));
|
|
5137
|
+
var path16 = __toESM(require("path"));
|
|
4836
5138
|
var MAX_OUTPUT_BYTES2 = 1024 * 1024;
|
|
4837
5139
|
var DEFAULT_TIMEOUT_MS4 = 6e4;
|
|
4838
5140
|
var POSIX_SHELL_CANDIDATES = ["bash.exe", "sh.exe"];
|
|
4839
5141
|
function resolveShellExecution(script, options = {}) {
|
|
4840
5142
|
const platform3 = options.platform ?? process.platform;
|
|
4841
5143
|
const env = options.env ?? process.env;
|
|
4842
|
-
const fileExists = options.fileExists ??
|
|
5144
|
+
const fileExists = options.fileExists ?? fs15.existsSync;
|
|
4843
5145
|
if (platform3 === "win32") {
|
|
4844
5146
|
const posixShell = usesPosixShellSyntax(script) ? findWindowsPosixShell(env, fileExists) : null;
|
|
4845
5147
|
if (posixShell) {
|
|
@@ -4884,10 +5186,10 @@ function findWindowsPosixShell(env, fileExists) {
|
|
|
4884
5186
|
if (fileExists(candidate)) return candidate;
|
|
4885
5187
|
}
|
|
4886
5188
|
const pathValue = env.Path ?? env.PATH ?? "";
|
|
4887
|
-
for (const dir of pathValue.split(
|
|
5189
|
+
for (const dir of pathValue.split(path16.win32.delimiter)) {
|
|
4888
5190
|
if (!dir) continue;
|
|
4889
5191
|
for (const executable of POSIX_SHELL_CANDIDATES) {
|
|
4890
|
-
const candidate =
|
|
5192
|
+
const candidate = path16.win32.join(dir, executable);
|
|
4891
5193
|
if (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {
|
|
4892
5194
|
return candidate;
|
|
4893
5195
|
}
|
|
@@ -4896,13 +5198,13 @@ function findWindowsPosixShell(env, fileExists) {
|
|
|
4896
5198
|
return null;
|
|
4897
5199
|
}
|
|
4898
5200
|
function isWindowsWslLauncher(candidate) {
|
|
4899
|
-
const normalized =
|
|
5201
|
+
const normalized = path16.win32.normalize(candidate).toLowerCase();
|
|
4900
5202
|
return normalized.endsWith("\\windows\\system32\\bash.exe") || normalized.endsWith("\\windows\\syswow64\\bash.exe");
|
|
4901
5203
|
}
|
|
4902
5204
|
function writeDiagnostic2(message) {
|
|
4903
5205
|
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
|
|
4904
5206
|
if (logPath) {
|
|
4905
|
-
|
|
5207
|
+
fs15.appendFileSync(logPath, message);
|
|
4906
5208
|
return;
|
|
4907
5209
|
}
|
|
4908
5210
|
process.stderr.write(message);
|
|
@@ -5044,10 +5346,10 @@ function getExecutor(taskType) {
|
|
|
5044
5346
|
}
|
|
5045
5347
|
|
|
5046
5348
|
// src/scheduled-tasks/TaskConfigManager.ts
|
|
5047
|
-
var
|
|
5048
|
-
var
|
|
5349
|
+
var import_node_crypto6 = require("crypto");
|
|
5350
|
+
var fs16 = __toESM(require("fs"));
|
|
5049
5351
|
var os4 = __toESM(require("os"));
|
|
5050
|
-
var
|
|
5352
|
+
var path17 = __toESM(require("path"));
|
|
5051
5353
|
var import_devtools_protocol8 = require("@serviceme/devtools-protocol");
|
|
5052
5354
|
function emptyConfig() {
|
|
5053
5355
|
return { version: 2, tasks: [] };
|
|
@@ -5071,7 +5373,7 @@ function v1ContainerShape(value) {
|
|
|
5071
5373
|
}
|
|
5072
5374
|
function defaultWorkspaceContext() {
|
|
5073
5375
|
const home = os4.homedir() || "/";
|
|
5074
|
-
return { path: home, name:
|
|
5376
|
+
return { path: home, name: path17.basename(home) || home };
|
|
5075
5377
|
}
|
|
5076
5378
|
function requireNonEmptyString(payload, field, taskType) {
|
|
5077
5379
|
if (!isRecord2(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
|
|
@@ -5106,10 +5408,10 @@ var TaskConfigManager = class {
|
|
|
5106
5408
|
return this.configPath;
|
|
5107
5409
|
}
|
|
5108
5410
|
readConfig() {
|
|
5109
|
-
if (!
|
|
5411
|
+
if (!fs16.existsSync(this.configPath)) {
|
|
5110
5412
|
return emptyConfig();
|
|
5111
5413
|
}
|
|
5112
|
-
const raw =
|
|
5414
|
+
const raw = fs16.readFileSync(this.configPath, "utf-8");
|
|
5113
5415
|
let parsed;
|
|
5114
5416
|
try {
|
|
5115
5417
|
parsed = JSON.parse(raw);
|
|
@@ -5162,7 +5464,7 @@ var TaskConfigManager = class {
|
|
|
5162
5464
|
const target = this.migrationFailuresPath ?? getMigrationFailuresPath();
|
|
5163
5465
|
const prior = (() => {
|
|
5164
5466
|
try {
|
|
5165
|
-
return JSON.parse(
|
|
5467
|
+
return JSON.parse(fs16.readFileSync(target, "utf-8"));
|
|
5166
5468
|
} catch {
|
|
5167
5469
|
return [];
|
|
5168
5470
|
}
|
|
@@ -5175,8 +5477,8 @@ var TaskConfigManager = class {
|
|
|
5175
5477
|
snippet: raw.slice(0, 500),
|
|
5176
5478
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5177
5479
|
});
|
|
5178
|
-
|
|
5179
|
-
|
|
5480
|
+
fs16.mkdirSync(path17.dirname(target), { recursive: true });
|
|
5481
|
+
fs16.writeFileSync(target, JSON.stringify(failures, null, " "), "utf-8");
|
|
5180
5482
|
} catch (writeError) {
|
|
5181
5483
|
this.warn(
|
|
5182
5484
|
`TaskConfigManager: also failed to write migration-failures log: ${String(writeError)}`
|
|
@@ -5184,13 +5486,13 @@ var TaskConfigManager = class {
|
|
|
5184
5486
|
}
|
|
5185
5487
|
}
|
|
5186
5488
|
writeConfig(config) {
|
|
5187
|
-
const dir =
|
|
5188
|
-
if (!
|
|
5189
|
-
|
|
5489
|
+
const dir = path17.dirname(this.configPath);
|
|
5490
|
+
if (!fs16.existsSync(dir)) {
|
|
5491
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
5190
5492
|
}
|
|
5191
5493
|
const tmp = `${this.configPath}.tmp`;
|
|
5192
|
-
|
|
5193
|
-
|
|
5494
|
+
fs16.writeFileSync(tmp, JSON.stringify(config, null, " "), "utf-8");
|
|
5495
|
+
fs16.renameSync(tmp, this.configPath);
|
|
5194
5496
|
}
|
|
5195
5497
|
listTasks() {
|
|
5196
5498
|
return this.readConfig().tasks;
|
|
@@ -5212,7 +5514,7 @@ var TaskConfigManager = class {
|
|
|
5212
5514
|
const config = this.readConfig();
|
|
5213
5515
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5214
5516
|
const task = {
|
|
5215
|
-
id: (0,
|
|
5517
|
+
id: (0, import_node_crypto6.randomUUID)(),
|
|
5216
5518
|
name: input.name,
|
|
5217
5519
|
description: input.description,
|
|
5218
5520
|
enabled: input.enabled ?? true,
|
|
@@ -5467,9 +5769,9 @@ var TaskExecutionEngine = class {
|
|
|
5467
5769
|
};
|
|
5468
5770
|
|
|
5469
5771
|
// src/scheduled-tasks/TaskLogManager.ts
|
|
5470
|
-
var
|
|
5471
|
-
var
|
|
5472
|
-
var
|
|
5772
|
+
var import_node_crypto7 = require("crypto");
|
|
5773
|
+
var fs17 = __toESM(require("fs"));
|
|
5774
|
+
var path18 = __toESM(require("path"));
|
|
5473
5775
|
var MAX_LOGS = 200;
|
|
5474
5776
|
function emptyLogFile() {
|
|
5475
5777
|
return { logs: [] };
|
|
@@ -5498,11 +5800,11 @@ var TaskLogManager = class {
|
|
|
5498
5800
|
return this.logPath;
|
|
5499
5801
|
}
|
|
5500
5802
|
readLogFile() {
|
|
5501
|
-
if (!
|
|
5803
|
+
if (!fs17.existsSync(this.logPath)) {
|
|
5502
5804
|
return emptyLogFile();
|
|
5503
5805
|
}
|
|
5504
5806
|
try {
|
|
5505
|
-
const raw =
|
|
5807
|
+
const raw = fs17.readFileSync(this.logPath, "utf-8");
|
|
5506
5808
|
const parsed = JSON.parse(raw);
|
|
5507
5809
|
const file = validateAndRepairLogFile(parsed);
|
|
5508
5810
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.logs) || parsed.logs.length !== file.logs.length) {
|
|
@@ -5518,26 +5820,26 @@ var TaskLogManager = class {
|
|
|
5518
5820
|
}
|
|
5519
5821
|
backupCorruptedFile() {
|
|
5520
5822
|
try {
|
|
5521
|
-
if (
|
|
5823
|
+
if (fs17.existsSync(this.logPath)) {
|
|
5522
5824
|
const backupPath = `${this.logPath}.corrupted.${Date.now()}`;
|
|
5523
|
-
|
|
5825
|
+
fs17.copyFileSync(this.logPath, backupPath);
|
|
5524
5826
|
}
|
|
5525
5827
|
} catch {
|
|
5526
5828
|
}
|
|
5527
5829
|
}
|
|
5528
5830
|
writeLogFile(file) {
|
|
5529
|
-
const dir =
|
|
5530
|
-
if (!
|
|
5531
|
-
|
|
5831
|
+
const dir = path18.dirname(this.logPath);
|
|
5832
|
+
if (!fs17.existsSync(dir)) {
|
|
5833
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
5532
5834
|
}
|
|
5533
5835
|
const tmp = `${this.logPath}.tmp`;
|
|
5534
|
-
|
|
5535
|
-
|
|
5836
|
+
fs17.writeFileSync(tmp, JSON.stringify(file, null, " "), "utf-8");
|
|
5837
|
+
fs17.renameSync(tmp, this.logPath);
|
|
5536
5838
|
}
|
|
5537
5839
|
appendLog(input) {
|
|
5538
5840
|
const file = this.readLogFile();
|
|
5539
5841
|
const log = {
|
|
5540
|
-
id: (0,
|
|
5842
|
+
id: (0, import_node_crypto7.randomUUID)(),
|
|
5541
5843
|
taskId: input.taskId,
|
|
5542
5844
|
taskName: input.taskName,
|
|
5543
5845
|
startedAt: input.startedAt,
|
|
@@ -5592,12 +5894,12 @@ var SchedulerDaemon = class {
|
|
|
5592
5894
|
this.lastRun = /* @__PURE__ */ new Map();
|
|
5593
5895
|
this.taskRunning = /* @__PURE__ */ new Set();
|
|
5594
5896
|
this.workspacePath = workspacePath;
|
|
5595
|
-
const configDir =
|
|
5897
|
+
const configDir = path19.join(workspacePath, ".serviceme");
|
|
5596
5898
|
this.configManager = new TaskConfigManager({
|
|
5597
|
-
configPath:
|
|
5899
|
+
configPath: path19.join(configDir, "scheduled-tasks.json")
|
|
5598
5900
|
});
|
|
5599
5901
|
this.logManager = new TaskLogManager({
|
|
5600
|
-
logPath:
|
|
5902
|
+
logPath: path19.join(configDir, "scheduled-tasks-log.json")
|
|
5601
5903
|
});
|
|
5602
5904
|
this.pidManager = new PidManager(workspacePath);
|
|
5603
5905
|
this.logger = new DaemonLogger(workspacePath);
|
|
@@ -5649,8 +5951,8 @@ var SchedulerDaemon = class {
|
|
|
5649
5951
|
const configPath = this.configManager.getConfigPath();
|
|
5650
5952
|
const dir = configPath.substring(0, configPath.lastIndexOf("/"));
|
|
5651
5953
|
try {
|
|
5652
|
-
if (
|
|
5653
|
-
this.watcher =
|
|
5954
|
+
if (fs18.existsSync(dir)) {
|
|
5955
|
+
this.watcher = fs18.watch(dir, (_eventType, filename) => {
|
|
5654
5956
|
if (filename === "scheduled-tasks.json") {
|
|
5655
5957
|
this.logger.log("info", "Config file changed, reconciling...");
|
|
5656
5958
|
}
|
|
@@ -5803,9 +6105,9 @@ function matchCronField(field, value) {
|
|
|
5803
6105
|
}
|
|
5804
6106
|
|
|
5805
6107
|
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
5806
|
-
var
|
|
6108
|
+
var fs19 = __toESM(require("fs"));
|
|
5807
6109
|
var os6 = __toESM(require("os"));
|
|
5808
|
-
var
|
|
6110
|
+
var path20 = __toESM(require("path"));
|
|
5809
6111
|
var TICK_INTERVAL2 = 1e3;
|
|
5810
6112
|
var MIN_SCHEDULE_INTERVAL2 = 1e3;
|
|
5811
6113
|
var SCHEDULER_LOG_FILENAME2 = "scheduler.log";
|
|
@@ -5825,7 +6127,7 @@ var SchedulerDaemonV2 = class {
|
|
|
5825
6127
|
this.logManager = options.logManager ?? new TaskLogManager();
|
|
5826
6128
|
this.pidManager = options.pidManager ?? new PidManager("", { pidPath: getSchedulerPidPath() });
|
|
5827
6129
|
this.logger = options.logger ?? new DaemonLogger(os6.homedir(), {
|
|
5828
|
-
logPath:
|
|
6130
|
+
logPath: path20.join(path20.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
|
|
5829
6131
|
});
|
|
5830
6132
|
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
5831
6133
|
this.tryAcquireLock = options.tryAcquireLock ?? (() => true);
|
|
@@ -5883,7 +6185,7 @@ var SchedulerDaemonV2 = class {
|
|
|
5883
6185
|
const now = Date.now();
|
|
5884
6186
|
for (const task of config.tasks) {
|
|
5885
6187
|
if (!task.enabled) continue;
|
|
5886
|
-
if (!
|
|
6188
|
+
if (!fs19.existsSync(task.workspace.path)) {
|
|
5887
6189
|
this.disableTaskForMissingWorkspace(task, config);
|
|
5888
6190
|
continue;
|
|
5889
6191
|
}
|
|
@@ -6040,21 +6342,21 @@ function matchCronField2(field, value) {
|
|
|
6040
6342
|
}
|
|
6041
6343
|
|
|
6042
6344
|
// src/scheduled-tasks/migration/MigrateToGlobal.ts
|
|
6043
|
-
var
|
|
6044
|
-
var
|
|
6345
|
+
var fs20 = __toESM(require("fs"));
|
|
6346
|
+
var path21 = __toESM(require("path"));
|
|
6045
6347
|
var import_devtools_protocol9 = require("@serviceme/devtools-protocol");
|
|
6046
6348
|
var WORKSPACE_DIR = ".serviceme";
|
|
6047
6349
|
var V1_FILENAME = "scheduled-tasks.json";
|
|
6048
6350
|
function defaultProbe(workspacePath) {
|
|
6049
6351
|
return {
|
|
6050
6352
|
path: workspacePath,
|
|
6051
|
-
name:
|
|
6353
|
+
name: path21.basename(workspacePath) || workspacePath
|
|
6052
6354
|
};
|
|
6053
6355
|
}
|
|
6054
6356
|
function readV1Config(v1Path) {
|
|
6055
6357
|
let raw;
|
|
6056
6358
|
try {
|
|
6057
|
-
raw =
|
|
6359
|
+
raw = fs20.readFileSync(v1Path, "utf-8");
|
|
6058
6360
|
} catch (err) {
|
|
6059
6361
|
return {
|
|
6060
6362
|
ok: false,
|
|
@@ -6077,27 +6379,27 @@ function readV1Config(v1Path) {
|
|
|
6077
6379
|
}
|
|
6078
6380
|
function safeDelete(filePath) {
|
|
6079
6381
|
try {
|
|
6080
|
-
|
|
6382
|
+
fs20.unlinkSync(filePath);
|
|
6081
6383
|
} catch {
|
|
6082
6384
|
}
|
|
6083
6385
|
}
|
|
6084
6386
|
function ensureDir(filePath) {
|
|
6085
|
-
const dir =
|
|
6086
|
-
if (!
|
|
6087
|
-
|
|
6387
|
+
const dir = path21.dirname(filePath);
|
|
6388
|
+
if (!fs20.existsSync(dir)) {
|
|
6389
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
6088
6390
|
}
|
|
6089
6391
|
}
|
|
6090
6392
|
function readJsonFile(filePath) {
|
|
6091
|
-
if (!
|
|
6393
|
+
if (!fs20.existsSync(filePath)) return null;
|
|
6092
6394
|
try {
|
|
6093
|
-
return JSON.parse(
|
|
6395
|
+
return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
|
|
6094
6396
|
} catch {
|
|
6095
6397
|
return null;
|
|
6096
6398
|
}
|
|
6097
6399
|
}
|
|
6098
6400
|
function writeJsonFile(filePath, data) {
|
|
6099
6401
|
ensureDir(filePath);
|
|
6100
|
-
|
|
6402
|
+
fs20.writeFileSync(filePath, JSON.stringify(data, null, " "), "utf-8");
|
|
6101
6403
|
}
|
|
6102
6404
|
function disambiguateName(task, existingNames, workspaceName) {
|
|
6103
6405
|
if (!existingNames.has(task.name)) {
|
|
@@ -6127,8 +6429,8 @@ async function migrateToGlobal(options) {
|
|
|
6127
6429
|
const conflicts = [];
|
|
6128
6430
|
const issues = [];
|
|
6129
6431
|
for (const workspacePath of options.workspacePaths) {
|
|
6130
|
-
const v1Path =
|
|
6131
|
-
if (!
|
|
6432
|
+
const v1Path = path21.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
|
|
6433
|
+
if (!fs20.existsSync(v1Path)) continue;
|
|
6132
6434
|
const v1 = readV1Config(v1Path);
|
|
6133
6435
|
if (!v1.ok) {
|
|
6134
6436
|
failures.push({
|
|
@@ -6170,8 +6472,8 @@ async function migrateToGlobal(options) {
|
|
|
6170
6472
|
if (migrated > 0) {
|
|
6171
6473
|
ensureDir(globalConfigPath);
|
|
6172
6474
|
const tmp = `${globalConfigPath}.tmp`;
|
|
6173
|
-
|
|
6174
|
-
|
|
6475
|
+
fs20.writeFileSync(tmp, JSON.stringify(baseConfig, null, " "), "utf-8");
|
|
6476
|
+
fs20.renameSync(tmp, globalConfigPath);
|
|
6175
6477
|
}
|
|
6176
6478
|
if (failures.length > priorFailures.length) {
|
|
6177
6479
|
writeJsonFile(migrationFailuresPath, failures);
|
|
@@ -6188,8 +6490,8 @@ async function migrateToGlobal(options) {
|
|
|
6188
6490
|
|
|
6189
6491
|
// src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts
|
|
6190
6492
|
var import_node_child_process5 = require("child_process");
|
|
6191
|
-
var
|
|
6192
|
-
var
|
|
6493
|
+
var fs21 = __toESM(require("fs"));
|
|
6494
|
+
var path22 = __toESM(require("path"));
|
|
6193
6495
|
var DEFAULT_TIMEOUT_MS5 = 2e3;
|
|
6194
6496
|
var GitTimeoutError = class extends Error {
|
|
6195
6497
|
constructor() {
|
|
@@ -6252,8 +6554,8 @@ var WorkspaceProbe = class {
|
|
|
6252
6554
|
}
|
|
6253
6555
|
}
|
|
6254
6556
|
async probe(workspacePath) {
|
|
6255
|
-
const name =
|
|
6256
|
-
if (!workspacePath || !
|
|
6557
|
+
const name = path22.basename(workspacePath) || workspacePath;
|
|
6558
|
+
if (!workspacePath || !fs21.existsSync(workspacePath)) {
|
|
6257
6559
|
return {
|
|
6258
6560
|
workspace: { path: workspacePath, name },
|
|
6259
6561
|
error: "path-not-found"
|
|
@@ -6405,8 +6707,8 @@ var SkillReconciler = class {
|
|
|
6405
6707
|
};
|
|
6406
6708
|
|
|
6407
6709
|
// src/skills/SkillStore.ts
|
|
6408
|
-
var
|
|
6409
|
-
var
|
|
6710
|
+
var fs22 = __toESM(require("fs/promises"));
|
|
6711
|
+
var path23 = __toESM(require("path"));
|
|
6410
6712
|
var USER_SKILL_MARKER_FILE = ".serviceme-skill.json";
|
|
6411
6713
|
var LEGACY_USER_SKILL_MARKER_FILE = ".ms-devtools-skill.json";
|
|
6412
6714
|
var WORKSPACE_SKILLS_ROOT_RELATIVE = ".github/skills";
|
|
@@ -6422,7 +6724,7 @@ var SkillStore = class {
|
|
|
6422
6724
|
constructor(options) {
|
|
6423
6725
|
this.workspacePath = options.workspacePath;
|
|
6424
6726
|
this.userSkillsRoot = options.userSkillsRoot;
|
|
6425
|
-
this.fileSystem = options.fileSystem ??
|
|
6727
|
+
this.fileSystem = options.fileSystem ?? fs22;
|
|
6426
6728
|
}
|
|
6427
6729
|
normalizeRemoteSkillId(remoteId) {
|
|
6428
6730
|
if (remoteId.startsWith("official/")) {
|
|
@@ -6441,10 +6743,10 @@ var SkillStore = class {
|
|
|
6441
6743
|
return WORKSPACE_SKILLS_MARKER_RELATIVE;
|
|
6442
6744
|
}
|
|
6443
6745
|
getUserSkillPath(skillId) {
|
|
6444
|
-
return
|
|
6746
|
+
return path23.join(this.userSkillsRoot, skillId);
|
|
6445
6747
|
}
|
|
6446
6748
|
async listWorkspaceSkillIds() {
|
|
6447
|
-
const skillsRootPath =
|
|
6749
|
+
const skillsRootPath = path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
|
|
6448
6750
|
try {
|
|
6449
6751
|
const entries = await this.fileSystem.readdir(skillsRootPath, {
|
|
6450
6752
|
withFileTypes: true
|
|
@@ -6468,7 +6770,7 @@ var SkillStore = class {
|
|
|
6468
6770
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6469
6771
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6470
6772
|
await this.fileSystem.writeFile(
|
|
6471
|
-
|
|
6773
|
+
path23.join(targetDir, USER_SKILL_MARKER_FILE),
|
|
6472
6774
|
JSON.stringify({ skillId, installedBy: "serviceme" }, null, 2),
|
|
6473
6775
|
"utf-8"
|
|
6474
6776
|
);
|
|
@@ -6477,7 +6779,7 @@ var SkillStore = class {
|
|
|
6477
6779
|
await this.migrateLegacyUserSkillMarker(skillId);
|
|
6478
6780
|
try {
|
|
6479
6781
|
const marker = await this.fileSystem.readFile(
|
|
6480
|
-
|
|
6782
|
+
path23.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
|
|
6481
6783
|
"utf-8"
|
|
6482
6784
|
);
|
|
6483
6785
|
const parsed = JSON.parse(marker);
|
|
@@ -6494,8 +6796,8 @@ var SkillStore = class {
|
|
|
6494
6796
|
*/
|
|
6495
6797
|
async migrateLegacyUserSkillMarker(skillId) {
|
|
6496
6798
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6497
|
-
const newPath =
|
|
6498
|
-
const legacyPath =
|
|
6799
|
+
const newPath = path23.join(targetDir, USER_SKILL_MARKER_FILE);
|
|
6800
|
+
const legacyPath = path23.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
|
|
6499
6801
|
try {
|
|
6500
6802
|
await this.fileSystem.readFile(newPath, "utf-8");
|
|
6501
6803
|
return;
|
|
@@ -6508,12 +6810,12 @@ var SkillStore = class {
|
|
|
6508
6810
|
}
|
|
6509
6811
|
}
|
|
6510
6812
|
async writeSkillFiles(skillId, scope, files) {
|
|
6511
|
-
const root = scope === "workspace" ?
|
|
6512
|
-
const targetDir =
|
|
6813
|
+
const root = scope === "workspace" ? path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
|
|
6814
|
+
const targetDir = path23.join(root, skillId);
|
|
6513
6815
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6514
6816
|
for (const file of files) {
|
|
6515
|
-
const filePath =
|
|
6516
|
-
await this.fileSystem.mkdir(
|
|
6817
|
+
const filePath = path23.join(targetDir, file.path);
|
|
6818
|
+
await this.fileSystem.mkdir(path23.dirname(filePath), { recursive: true });
|
|
6517
6819
|
await this.fileSystem.writeFile(filePath, file.content, "utf-8");
|
|
6518
6820
|
if (file.executable) {
|
|
6519
6821
|
try {
|
|
@@ -6526,8 +6828,8 @@ var SkillStore = class {
|
|
|
6526
6828
|
};
|
|
6527
6829
|
|
|
6528
6830
|
// src/submit/index.ts
|
|
6529
|
-
var
|
|
6530
|
-
var
|
|
6831
|
+
var fs23 = __toESM(require("fs/promises"));
|
|
6832
|
+
var path24 = __toESM(require("path"));
|
|
6531
6833
|
|
|
6532
6834
|
// src/submit/types.ts
|
|
6533
6835
|
var SubmitError = class extends Error {
|
|
@@ -6577,14 +6879,14 @@ var SubmitClient = class {
|
|
|
6577
6879
|
throw new SubmitError(v.reason ?? "unknown", v.detail ?? "validation denied");
|
|
6578
6880
|
}
|
|
6579
6881
|
const localRepoPath = getRepoDir(repoId);
|
|
6580
|
-
const targetDir =
|
|
6581
|
-
await
|
|
6882
|
+
const targetDir = path24.join(localRepoPath, "skills", skillName);
|
|
6883
|
+
await fs23.mkdir(targetDir, { recursive: true });
|
|
6582
6884
|
for (const f of files) {
|
|
6583
|
-
const full =
|
|
6584
|
-
await
|
|
6885
|
+
const full = path24.join(targetDir, f.path);
|
|
6886
|
+
await fs23.mkdir(path24.dirname(full), { recursive: true });
|
|
6585
6887
|
const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
|
|
6586
|
-
await
|
|
6587
|
-
await
|
|
6888
|
+
await fs23.writeFile(tmp, f.content, "utf8");
|
|
6889
|
+
await fs23.rename(tmp, full);
|
|
6588
6890
|
}
|
|
6589
6891
|
const commitMessage = `feat(skills): add ${skillName}`;
|
|
6590
6892
|
const { commitSha } = await this.git.commit(localRepoPath, commitMessage);
|
|
@@ -6653,8 +6955,8 @@ function touchLastUsedAt(tools, id, when = /* @__PURE__ */ new Date()) {
|
|
|
6653
6955
|
|
|
6654
6956
|
// src/toolbox/ToolboxStore.ts
|
|
6655
6957
|
var fsp2 = __toESM(require("fs/promises"));
|
|
6656
|
-
var
|
|
6657
|
-
var
|
|
6958
|
+
var path25 = __toESM(require("path"));
|
|
6959
|
+
var import_promises4 = require("timers/promises");
|
|
6658
6960
|
|
|
6659
6961
|
// src/toolbox/types.ts
|
|
6660
6962
|
var TOOLBOX_JSON_SCHEMA_VERSION = 1;
|
|
@@ -6688,11 +6990,11 @@ var LOCK_DIR_MODE2 = 448;
|
|
|
6688
6990
|
var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
|
|
6689
6991
|
var DEFAULT_LOCK_RETRY_MS2 = 25;
|
|
6690
6992
|
var TMP_SUFFIX2 = ".tmp";
|
|
6691
|
-
var WORKSPACE_TOOLBOX_RELATIVE_PATH =
|
|
6993
|
+
var WORKSPACE_TOOLBOX_RELATIVE_PATH = path25.join(".github", ".serviceme-toolbox.json");
|
|
6692
6994
|
var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
|
|
6693
6995
|
async function migrateLegacyWorkspaceToolboxFile(filePath) {
|
|
6694
6996
|
if (!filePath) return;
|
|
6695
|
-
const legacyPath =
|
|
6997
|
+
const legacyPath = path25.join(path25.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
|
|
6696
6998
|
if (legacyPath === filePath) return;
|
|
6697
6999
|
try {
|
|
6698
7000
|
await fsp2.access(filePath);
|
|
@@ -6737,7 +7039,7 @@ var FsToolboxFileBackend = class {
|
|
|
6737
7039
|
}
|
|
6738
7040
|
}
|
|
6739
7041
|
async write(filePath, payload) {
|
|
6740
|
-
await fsp2.mkdir(
|
|
7042
|
+
await fsp2.mkdir(path25.dirname(filePath), { recursive: true });
|
|
6741
7043
|
const tmpPath = `${filePath}${TMP_SUFFIX2}`;
|
|
6742
7044
|
const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
|
|
6743
7045
|
await fsp2.rm(tmpPath, { force: true });
|
|
@@ -6769,10 +7071,19 @@ function coercePersistedToolbox(parsed) {
|
|
|
6769
7071
|
function isNodeError2(value) {
|
|
6770
7072
|
return value instanceof Error && typeof value.code === "string";
|
|
6771
7073
|
}
|
|
7074
|
+
function isProcessAlive2(pid) {
|
|
7075
|
+
try {
|
|
7076
|
+
process.kill(pid, 0);
|
|
7077
|
+
return true;
|
|
7078
|
+
} catch {
|
|
7079
|
+
return false;
|
|
7080
|
+
}
|
|
7081
|
+
}
|
|
6772
7082
|
var ToolboxFileLock = class {
|
|
6773
7083
|
constructor(filePath, timeoutMs, retryMs) {
|
|
6774
7084
|
this.acquired = false;
|
|
6775
7085
|
this.dirPath = `${filePath}.lock`;
|
|
7086
|
+
this.pidFilePath = path25.join(this.dirPath, "pid");
|
|
6776
7087
|
this.timeoutMs = timeoutMs;
|
|
6777
7088
|
this.retryMs = retryMs;
|
|
6778
7089
|
}
|
|
@@ -6781,17 +7092,33 @@ var ToolboxFileLock = class {
|
|
|
6781
7092
|
while (true) {
|
|
6782
7093
|
try {
|
|
6783
7094
|
await fsp2.mkdir(this.dirPath, { mode: LOCK_DIR_MODE2 });
|
|
7095
|
+
await fsp2.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
6784
7096
|
this.acquired = true;
|
|
6785
7097
|
return;
|
|
6786
7098
|
} catch (err) {
|
|
6787
7099
|
if (!isNodeError2(err) || err.code !== "EEXIST") throw err;
|
|
7100
|
+
const stale = await this.isStaleLock();
|
|
7101
|
+
if (stale) {
|
|
7102
|
+
await fsp2.rm(this.dirPath, { recursive: true, force: true });
|
|
7103
|
+
continue;
|
|
7104
|
+
}
|
|
6788
7105
|
if (Date.now() - start >= this.timeoutMs) {
|
|
6789
7106
|
throw new Error(`ToolboxStore lock acquisition timed out for ${this.dirPath}`);
|
|
6790
7107
|
}
|
|
6791
|
-
await (0,
|
|
7108
|
+
await (0, import_promises4.setTimeout)(this.retryMs);
|
|
6792
7109
|
}
|
|
6793
7110
|
}
|
|
6794
7111
|
}
|
|
7112
|
+
async isStaleLock() {
|
|
7113
|
+
try {
|
|
7114
|
+
const pidStr = await fsp2.readFile(this.pidFilePath, "utf8");
|
|
7115
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
7116
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
7117
|
+
return !isProcessAlive2(pid);
|
|
7118
|
+
} catch {
|
|
7119
|
+
return true;
|
|
7120
|
+
}
|
|
7121
|
+
}
|
|
6795
7122
|
async release() {
|
|
6796
7123
|
if (!this.acquired) return;
|
|
6797
7124
|
this.acquired = false;
|
|
@@ -6800,7 +7127,7 @@ var ToolboxFileLock = class {
|
|
|
6800
7127
|
};
|
|
6801
7128
|
function defaultWorkspacePath() {
|
|
6802
7129
|
if (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === "1") return null;
|
|
6803
|
-
return
|
|
7130
|
+
return path25.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
|
|
6804
7131
|
}
|
|
6805
7132
|
var ToolboxStore = class {
|
|
6806
7133
|
constructor(opts = {}) {
|
|
@@ -7108,6 +7435,7 @@ var ToolboxCore = class {
|
|
|
7108
7435
|
SCHEDULER_LOCK_FILENAME,
|
|
7109
7436
|
SCHEDULER_LOG_FILENAME,
|
|
7110
7437
|
SCHEDULER_PID_FILENAME,
|
|
7438
|
+
SERVER_PROXY_GLOBAL_FILENAME,
|
|
7111
7439
|
SERVICEME_DIR_NAME,
|
|
7112
7440
|
SERVICEME_HOME_ENV,
|
|
7113
7441
|
SKILL_DRAFTS_SUBDIR,
|
|
@@ -7175,6 +7503,7 @@ var ToolboxCore = class {
|
|
|
7175
7503
|
getSchedulerLockPath,
|
|
7176
7504
|
getSchedulerLogPath,
|
|
7177
7505
|
getSchedulerPidPath,
|
|
7506
|
+
getServerProxyGlobalPath,
|
|
7178
7507
|
getServicemeHome,
|
|
7179
7508
|
getSkillDraftsDir,
|
|
7180
7509
|
getToolboxJsonPath,
|
|
@@ -7185,6 +7514,7 @@ var ToolboxCore = class {
|
|
|
7185
7514
|
isUserRepo,
|
|
7186
7515
|
matchesCron,
|
|
7187
7516
|
mergeWithDefaults,
|
|
7517
|
+
migrateLegacyServerProxyEnabled,
|
|
7188
7518
|
migrateToGlobal,
|
|
7189
7519
|
moveFiles,
|
|
7190
7520
|
narrowRepoConfig,
|
|
@@ -7192,6 +7522,7 @@ var ToolboxCore = class {
|
|
|
7192
7522
|
parseAgentToolPermissions,
|
|
7193
7523
|
parseIntervalMs,
|
|
7194
7524
|
randomInstallationId,
|
|
7525
|
+
readServerProxyGlobal,
|
|
7195
7526
|
reindexOrder,
|
|
7196
7527
|
reposFileSchema,
|
|
7197
7528
|
resetUserHomeOverrides,
|
|
@@ -7206,6 +7537,7 @@ var ToolboxCore = class {
|
|
|
7206
7537
|
unzipFile,
|
|
7207
7538
|
userRepoConfigSchema,
|
|
7208
7539
|
validateReposFile,
|
|
7209
|
-
validateTaskPayload
|
|
7540
|
+
validateTaskPayload,
|
|
7541
|
+
writeServerProxyGlobal
|
|
7210
7542
|
});
|
|
7211
7543
|
//# sourceMappingURL=index.js.map
|