@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.mjs
CHANGED
|
@@ -454,6 +454,50 @@ var AccessControl = class {
|
|
|
454
454
|
}
|
|
455
455
|
};
|
|
456
456
|
|
|
457
|
+
// src/logger.ts
|
|
458
|
+
var noopLogger = {
|
|
459
|
+
debug() {
|
|
460
|
+
},
|
|
461
|
+
info() {
|
|
462
|
+
},
|
|
463
|
+
warn() {
|
|
464
|
+
},
|
|
465
|
+
error() {
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
function formatArgs(args) {
|
|
469
|
+
return args.map((arg) => {
|
|
470
|
+
if (typeof arg === "string") {
|
|
471
|
+
return arg;
|
|
472
|
+
}
|
|
473
|
+
try {
|
|
474
|
+
return JSON.stringify(arg);
|
|
475
|
+
} catch {
|
|
476
|
+
return String(arg);
|
|
477
|
+
}
|
|
478
|
+
}).join(" ");
|
|
479
|
+
}
|
|
480
|
+
function createConsoleLogger(prefix = "serviceme") {
|
|
481
|
+
return {
|
|
482
|
+
debug(message, ...args) {
|
|
483
|
+
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
484
|
+
`);
|
|
485
|
+
},
|
|
486
|
+
info(message, ...args) {
|
|
487
|
+
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
488
|
+
`);
|
|
489
|
+
},
|
|
490
|
+
warn(message, ...args) {
|
|
491
|
+
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
492
|
+
`);
|
|
493
|
+
},
|
|
494
|
+
error(message, ...args) {
|
|
495
|
+
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
496
|
+
`);
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
457
501
|
// src/auth/AuthStateManager.ts
|
|
458
502
|
import { EventEmitter } from "events";
|
|
459
503
|
var AuthStateManager = class {
|
|
@@ -616,6 +660,7 @@ var AuthCore = class {
|
|
|
616
660
|
this.state = opts.stateManager ?? new AuthStateManager();
|
|
617
661
|
this.tokenStore = opts.tokenStore;
|
|
618
662
|
this.accessControl = opts.accessControl;
|
|
663
|
+
this.logger = opts.logger ?? noopLogger;
|
|
619
664
|
}
|
|
620
665
|
/** Snapshot of every account, the active provider, and the last error. */
|
|
621
666
|
status() {
|
|
@@ -637,16 +682,28 @@ var AuthCore = class {
|
|
|
637
682
|
*/
|
|
638
683
|
async login(provider, ui, shouldContinue) {
|
|
639
684
|
const providerImpl = this.registry.get(provider);
|
|
685
|
+
this.logger.info("[AuthCore] Starting device-flow login", { provider });
|
|
640
686
|
try {
|
|
641
687
|
const initial = await providerImpl.requestDeviceFlow();
|
|
642
688
|
await ui(initial);
|
|
689
|
+
if (!initial.deviceCode) {
|
|
690
|
+
this.logger.error("[AuthCore] Provider did not return deviceCode", provider);
|
|
691
|
+
throw new Error("Auth provider did not return deviceCode for device flow completion");
|
|
692
|
+
}
|
|
643
693
|
const session = await providerImpl.completeDeviceFlow(
|
|
644
|
-
initial.
|
|
645
|
-
shouldContinue
|
|
694
|
+
initial.deviceCode,
|
|
695
|
+
shouldContinue,
|
|
696
|
+
initial.pollIntervalMs
|
|
646
697
|
);
|
|
647
|
-
|
|
698
|
+
const result = await this.persistSession(providerImpl, session);
|
|
699
|
+
this.logger.info("[AuthCore] Device-flow login completed", { provider });
|
|
700
|
+
return result;
|
|
648
701
|
} catch (err) {
|
|
649
|
-
|
|
702
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
703
|
+
this.logger.error("[AuthCore] Device-flow login failed", message, {
|
|
704
|
+
provider
|
|
705
|
+
});
|
|
706
|
+
this.state.recordError(message);
|
|
650
707
|
throw err;
|
|
651
708
|
}
|
|
652
709
|
}
|
|
@@ -671,7 +728,10 @@ var AuthCore = class {
|
|
|
671
728
|
if (!provider) return null;
|
|
672
729
|
const account = this.state.getActiveAccount();
|
|
673
730
|
if (!account) return null;
|
|
674
|
-
const envelope = await this.tokenStore.get({
|
|
731
|
+
const envelope = await this.tokenStore.get({
|
|
732
|
+
provider,
|
|
733
|
+
accountId: account.id
|
|
734
|
+
});
|
|
675
735
|
if (!envelope) return null;
|
|
676
736
|
return { provider, account, token: envelope.token };
|
|
677
737
|
}
|
|
@@ -681,9 +741,15 @@ var AuthCore = class {
|
|
|
681
741
|
* every account.
|
|
682
742
|
*/
|
|
683
743
|
async logout(provider) {
|
|
744
|
+
this.logger.info("[AuthCore] Logout requested", {
|
|
745
|
+
provider: provider ?? "all"
|
|
746
|
+
});
|
|
684
747
|
if (!provider) {
|
|
685
748
|
for (const account of this.state.listAccounts()) {
|
|
686
|
-
await this.tokenStore.delete({
|
|
749
|
+
await this.tokenStore.delete({
|
|
750
|
+
provider: account.provider,
|
|
751
|
+
accountId: account.id
|
|
752
|
+
});
|
|
687
753
|
this.state.removeAccount(account.provider, account.id);
|
|
688
754
|
}
|
|
689
755
|
this.state.clearAll();
|
|
@@ -758,6 +824,10 @@ var AuthCore = class {
|
|
|
758
824
|
avatarUrl: meta.avatarUrl,
|
|
759
825
|
expiresAt: meta.expiresAt ?? expiresAt
|
|
760
826
|
};
|
|
827
|
+
this.logger.debug("[AuthCore] Persisting session", {
|
|
828
|
+
provider: meta.provider,
|
|
829
|
+
accountId: meta.id
|
|
830
|
+
});
|
|
761
831
|
await this.tokenStore.set({ provider: meta.provider, accountId: meta.id }, session.token, {
|
|
762
832
|
expiresAt
|
|
763
833
|
});
|
|
@@ -860,11 +930,13 @@ var GitHubAuthProvider = class {
|
|
|
860
930
|
userUrl: config.userUrl ?? DEFAULT_USER_URL,
|
|
861
931
|
scope: config.scope ?? DEFAULT_SCOPE,
|
|
862
932
|
minPollIntervalMs: config.minPollIntervalMs ?? 1e3,
|
|
863
|
-
maxPollIntervalMs: config.maxPollIntervalMs ??
|
|
933
|
+
maxPollIntervalMs: config.maxPollIntervalMs ?? 6e4,
|
|
864
934
|
maxWaitMs: config.maxWaitMs,
|
|
865
935
|
fetchImpl: config.fetchImpl ?? fetch,
|
|
866
936
|
deviceCodeRetryBaseDelayMs: config.deviceCodeRetryBaseDelayMs ?? DEFAULT_DEVICE_CODE_RETRY_BASE_DELAY_MS
|
|
867
937
|
};
|
|
938
|
+
this.logger = config.logger ?? noopLogger;
|
|
939
|
+
this.sleepImpl = config.sleepImpl ?? sleep;
|
|
868
940
|
}
|
|
869
941
|
async requestDeviceFlow(opts) {
|
|
870
942
|
const body = JSON.stringify({
|
|
@@ -873,6 +945,10 @@ var GitHubAuthProvider = class {
|
|
|
873
945
|
});
|
|
874
946
|
let lastNetworkError;
|
|
875
947
|
for (let attempt = 1; attempt <= DEVICE_CODE_MAX_ATTEMPTS; attempt++) {
|
|
948
|
+
this.logger.debug("[GitHubAuthProvider] Requesting device code", {
|
|
949
|
+
attempt,
|
|
950
|
+
maxAttempts: DEVICE_CODE_MAX_ATTEMPTS
|
|
951
|
+
});
|
|
876
952
|
let resp;
|
|
877
953
|
try {
|
|
878
954
|
resp = await this.cfg.fetchImpl(this.cfg.deviceCodeUrl, {
|
|
@@ -886,45 +962,99 @@ var GitHubAuthProvider = class {
|
|
|
886
962
|
});
|
|
887
963
|
} catch (error) {
|
|
888
964
|
if (!isTransientNetworkError(error) || attempt === DEVICE_CODE_MAX_ATTEMPTS) {
|
|
965
|
+
this.logger.error(
|
|
966
|
+
"[GitHubAuthProvider] Device-code request failed (non-retryable)",
|
|
967
|
+
error instanceof Error ? error.message : String(error),
|
|
968
|
+
{ attempt }
|
|
969
|
+
);
|
|
889
970
|
throw error;
|
|
890
971
|
}
|
|
972
|
+
const delayMs = this.cfg.deviceCodeRetryBaseDelayMs * 2 ** (attempt - 1);
|
|
973
|
+
this.logger.warn(
|
|
974
|
+
"[GitHubAuthProvider] Transient network error requesting device code, retrying",
|
|
975
|
+
{
|
|
976
|
+
attempt,
|
|
977
|
+
delayMs,
|
|
978
|
+
error: error instanceof Error ? error.message : String(error)
|
|
979
|
+
}
|
|
980
|
+
);
|
|
891
981
|
lastNetworkError = error;
|
|
892
|
-
await
|
|
982
|
+
await this.sleepImpl(delayMs);
|
|
893
983
|
continue;
|
|
894
984
|
}
|
|
895
985
|
if (!resp.ok) {
|
|
986
|
+
this.logger.error(
|
|
987
|
+
"[GitHubAuthProvider] Device-code request rejected by GitHub",
|
|
988
|
+
`HTTP ${resp.status}`
|
|
989
|
+
);
|
|
896
990
|
throw new Error(`GitHub device-code request failed: HTTP ${resp.status}`);
|
|
897
991
|
}
|
|
898
992
|
const data = await resp.json();
|
|
899
993
|
if (!data.device_code || !data.user_code || !data.verification_uri) {
|
|
994
|
+
this.logger.error(
|
|
995
|
+
"[GitHubAuthProvider] Device-code response missing required fields",
|
|
996
|
+
JSON.stringify(Object.keys(data))
|
|
997
|
+
);
|
|
900
998
|
throw new Error("GitHub device-code response missing required fields");
|
|
901
999
|
}
|
|
1000
|
+
this.logger.info("[GitHubAuthProvider] Device code obtained", {
|
|
1001
|
+
userCode: data.user_code,
|
|
1002
|
+
verificationUri: data.verification_uri,
|
|
1003
|
+
expiresInSec: data.expires_in,
|
|
1004
|
+
pollIntervalSec: data.interval
|
|
1005
|
+
});
|
|
902
1006
|
return {
|
|
903
1007
|
provider: this.providerId,
|
|
1008
|
+
deviceCode: data.device_code,
|
|
904
1009
|
userCode: data.user_code,
|
|
905
1010
|
verificationUrl: data.verification_uri,
|
|
906
1011
|
expiresAt: Date.now() + data.expires_in * 1e3,
|
|
1012
|
+
pollIntervalMs: data.interval * 1e3,
|
|
907
1013
|
message: `Open ${data.verification_uri} and enter ${data.user_code}`
|
|
908
1014
|
};
|
|
909
1015
|
}
|
|
910
1016
|
throw lastNetworkError ?? new Error("GitHub device-code request failed");
|
|
911
1017
|
}
|
|
912
|
-
async completeDeviceFlow(deviceCode, shouldContinue) {
|
|
1018
|
+
async completeDeviceFlow(deviceCode, shouldContinue, initialPollIntervalMs) {
|
|
913
1019
|
const start = Date.now();
|
|
914
|
-
let pollIntervalMs = this.cfg.minPollIntervalMs;
|
|
1020
|
+
let pollIntervalMs = Math.max(this.cfg.minPollIntervalMs, initialPollIntervalMs ?? 0);
|
|
915
1021
|
let consecutiveSlowDown = 0;
|
|
1022
|
+
let pollCount = 0;
|
|
1023
|
+
this.logger.info("[GitHubAuthProvider] Starting device-flow polling", {
|
|
1024
|
+
initialPollIntervalMs: pollIntervalMs,
|
|
1025
|
+
maxWaitMs: this.cfg.maxWaitMs
|
|
1026
|
+
});
|
|
916
1027
|
while (true) {
|
|
917
1028
|
if (shouldContinue && !shouldContinue()) {
|
|
1029
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1030
|
+
elapsedMs: Date.now() - start,
|
|
1031
|
+
pollCount
|
|
1032
|
+
});
|
|
918
1033
|
throw new Error("GitHub device flow cancelled by caller");
|
|
919
1034
|
}
|
|
920
1035
|
const elapsed = Date.now() - start;
|
|
921
1036
|
if (this.cfg.maxWaitMs !== void 0 && elapsed > this.cfg.maxWaitMs) {
|
|
1037
|
+
this.logger.warn("[GitHubAuthProvider] Device flow exceeded max wait time", {
|
|
1038
|
+
elapsedMs: elapsed,
|
|
1039
|
+
maxWaitMs: this.cfg.maxWaitMs,
|
|
1040
|
+
pollCount
|
|
1041
|
+
});
|
|
922
1042
|
throw new Error("GitHub device flow exceeded max wait time");
|
|
923
1043
|
}
|
|
924
|
-
await
|
|
1044
|
+
await this.sleepImpl(pollIntervalMs);
|
|
925
1045
|
if (shouldContinue && !shouldContinue()) {
|
|
1046
|
+
this.logger.info("[GitHubAuthProvider] Device flow cancelled by caller", {
|
|
1047
|
+
elapsedMs: Date.now() - start,
|
|
1048
|
+
pollCount
|
|
1049
|
+
});
|
|
926
1050
|
throw new Error("GitHub device flow cancelled by caller");
|
|
927
1051
|
}
|
|
1052
|
+
pollCount++;
|
|
1053
|
+
this.logger.debug("[GitHubAuthProvider] Polling for authorization", {
|
|
1054
|
+
pollCount,
|
|
1055
|
+
elapsedMs: Date.now() - start,
|
|
1056
|
+
pollIntervalMs
|
|
1057
|
+
});
|
|
928
1058
|
let resp;
|
|
929
1059
|
try {
|
|
930
1060
|
resp = await this.cfg.fetchImpl(this.cfg.tokenUrl, {
|
|
@@ -942,19 +1072,59 @@ var GitHubAuthProvider = class {
|
|
|
942
1072
|
});
|
|
943
1073
|
} catch (error) {
|
|
944
1074
|
if (!isTransientNetworkError(error)) {
|
|
1075
|
+
this.logger.error(
|
|
1076
|
+
"[GitHubAuthProvider] Non-retryable error while polling token endpoint",
|
|
1077
|
+
error instanceof Error ? error.message : String(error),
|
|
1078
|
+
{ pollCount }
|
|
1079
|
+
);
|
|
945
1080
|
throw error;
|
|
946
1081
|
}
|
|
947
|
-
|
|
1082
|
+
const nextPollIntervalMs = Math.min(
|
|
1083
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1084
|
+
this.cfg.maxPollIntervalMs
|
|
1085
|
+
);
|
|
1086
|
+
this.logger.warn(
|
|
1087
|
+
"[GitHubAuthProvider] Transient network error while polling, backing off",
|
|
1088
|
+
{
|
|
1089
|
+
pollCount,
|
|
1090
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1091
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1092
|
+
nextPollIntervalMs
|
|
1093
|
+
}
|
|
1094
|
+
);
|
|
1095
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
948
1096
|
continue;
|
|
949
1097
|
}
|
|
950
1098
|
if (!resp.ok) {
|
|
951
|
-
|
|
1099
|
+
const nextPollIntervalMs = Math.min(
|
|
1100
|
+
Math.round(pollIntervalMs * 1.5),
|
|
1101
|
+
this.cfg.maxPollIntervalMs
|
|
1102
|
+
);
|
|
1103
|
+
this.logger.warn(
|
|
1104
|
+
"[GitHubAuthProvider] Token endpoint returned non-OK status, backing off",
|
|
1105
|
+
{
|
|
1106
|
+
pollCount,
|
|
1107
|
+
status: resp.status,
|
|
1108
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1109
|
+
nextPollIntervalMs
|
|
1110
|
+
}
|
|
1111
|
+
);
|
|
1112
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
952
1113
|
continue;
|
|
953
1114
|
}
|
|
954
1115
|
const data = await resp.json();
|
|
955
1116
|
if (data.access_token) {
|
|
1117
|
+
this.logger.info("[GitHubAuthProvider] Authorization granted, fetching user profile", {
|
|
1118
|
+
pollCount,
|
|
1119
|
+
elapsedMs: Date.now() - start
|
|
1120
|
+
});
|
|
956
1121
|
const rawUser = await this.fetchGitHubUser(data.access_token);
|
|
957
1122
|
const email = await this.resolveEmail(data.access_token, rawUser);
|
|
1123
|
+
this.logger.info("[GitHubAuthProvider] Device-flow login completed", {
|
|
1124
|
+
pollCount,
|
|
1125
|
+
elapsedMs: Date.now() - start,
|
|
1126
|
+
login: rawUser.login
|
|
1127
|
+
});
|
|
958
1128
|
return {
|
|
959
1129
|
token: data.access_token,
|
|
960
1130
|
refreshToken: data.refresh_token,
|
|
@@ -969,22 +1139,42 @@ var GitHubAuthProvider = class {
|
|
|
969
1139
|
};
|
|
970
1140
|
}
|
|
971
1141
|
if (data.error === "authorization_pending") {
|
|
1142
|
+
this.logger.debug("[GitHubAuthProvider] Authorization still pending", {
|
|
1143
|
+
pollCount,
|
|
1144
|
+
elapsedMs: Date.now() - start
|
|
1145
|
+
});
|
|
972
1146
|
continue;
|
|
973
1147
|
}
|
|
974
1148
|
if (data.error === "slow_down") {
|
|
975
1149
|
consecutiveSlowDown++;
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1150
|
+
const nextPollIntervalMs = Math.min(pollIntervalMs + 5e3, this.cfg.maxPollIntervalMs);
|
|
1151
|
+
this.logger.warn("[GitHubAuthProvider] GitHub requested slower polling (slow_down)", {
|
|
1152
|
+
pollCount,
|
|
1153
|
+
consecutiveSlowDown,
|
|
1154
|
+
previousPollIntervalMs: pollIntervalMs,
|
|
1155
|
+
nextPollIntervalMs
|
|
1156
|
+
});
|
|
1157
|
+
pollIntervalMs = nextPollIntervalMs;
|
|
980
1158
|
continue;
|
|
981
1159
|
}
|
|
982
1160
|
if (data.error === "access_denied") {
|
|
1161
|
+
this.logger.warn("[GitHubAuthProvider] User denied authorization", {
|
|
1162
|
+
pollCount
|
|
1163
|
+
});
|
|
983
1164
|
throw new Error("User denied authorization");
|
|
984
1165
|
}
|
|
985
1166
|
if (data.error === "expired_token") {
|
|
1167
|
+
this.logger.warn(
|
|
1168
|
+
"[GitHubAuthProvider] Device code expired before authorization completed",
|
|
1169
|
+
{ pollCount, elapsedMs: Date.now() - start }
|
|
1170
|
+
);
|
|
986
1171
|
throw new Error("GitHub device code expired \u2014 restart the flow");
|
|
987
1172
|
}
|
|
1173
|
+
this.logger.error(
|
|
1174
|
+
"[GitHubAuthProvider] Unexpected device-flow error from token endpoint",
|
|
1175
|
+
data.error ?? "unknown",
|
|
1176
|
+
{ pollCount }
|
|
1177
|
+
);
|
|
988
1178
|
throw new Error(`GitHub device flow error: ${data.error ?? "unknown"}`);
|
|
989
1179
|
}
|
|
990
1180
|
}
|
|
@@ -1671,6 +1861,7 @@ var DRAFTS_SUBDIR = "drafts";
|
|
|
1671
1861
|
var SKILL_DRAFTS_SUBDIR = "skills";
|
|
1672
1862
|
var AGENT_DRAFTS_SUBDIR = "agents";
|
|
1673
1863
|
var REPOS_CONFIG_FILENAME = "repos.json";
|
|
1864
|
+
var SERVER_PROXY_GLOBAL_FILENAME = "server-proxy.json";
|
|
1674
1865
|
var SAFE_REPO_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$/;
|
|
1675
1866
|
var SERVICEME_HOME_ENV = "SERVICEME_HOME";
|
|
1676
1867
|
var activeOverrides = {};
|
|
@@ -1771,6 +1962,9 @@ function getMigrationFailuresPath() {
|
|
|
1771
1962
|
function getKnownWorkspacesPath() {
|
|
1772
1963
|
return path2.join(getServicemeHome(), KNOWN_WORKSPACES_FILENAME);
|
|
1773
1964
|
}
|
|
1965
|
+
function getServerProxyGlobalPath() {
|
|
1966
|
+
return path2.join(getServicemeHome(), SERVER_PROXY_GLOBAL_FILENAME);
|
|
1967
|
+
}
|
|
1774
1968
|
var CREDENTIALS_CONFIG_FILENAME = "credentials.json";
|
|
1775
1969
|
var DEVICE_JSON_FILENAME = "device.json";
|
|
1776
1970
|
var TOOLBOX_JSON_FILENAME = "toolbox.json";
|
|
@@ -1862,10 +2056,20 @@ function isRecord(value) {
|
|
|
1862
2056
|
function isNodeError(value) {
|
|
1863
2057
|
return value instanceof Error && typeof value.code === "string";
|
|
1864
2058
|
}
|
|
2059
|
+
var LOCK_PID_FILE = "pid";
|
|
2060
|
+
function isProcessAlive(pid) {
|
|
2061
|
+
try {
|
|
2062
|
+
process.kill(pid, 0);
|
|
2063
|
+
return true;
|
|
2064
|
+
} catch {
|
|
2065
|
+
return false;
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
1865
2068
|
var FileLock = class {
|
|
1866
2069
|
constructor(filePath, timeoutMs, retryMs) {
|
|
1867
2070
|
this.acquired = false;
|
|
1868
2071
|
this.dirPath = `${filePath}.lock`;
|
|
2072
|
+
this.pidFilePath = path3.join(this.dirPath, LOCK_PID_FILE);
|
|
1869
2073
|
this.timeoutMs = timeoutMs;
|
|
1870
2074
|
this.retryMs = retryMs;
|
|
1871
2075
|
}
|
|
@@ -1874,12 +2078,18 @@ var FileLock = class {
|
|
|
1874
2078
|
while (true) {
|
|
1875
2079
|
try {
|
|
1876
2080
|
await fsp.mkdir(this.dirPath, { mode: LOCK_DIR_MODE });
|
|
2081
|
+
await fsp.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
1877
2082
|
this.acquired = true;
|
|
1878
2083
|
return;
|
|
1879
2084
|
} catch (err) {
|
|
1880
2085
|
if (!isNodeError(err) || err.code !== "EEXIST") {
|
|
1881
2086
|
throw err;
|
|
1882
2087
|
}
|
|
2088
|
+
const stale = await this.isStaleLock();
|
|
2089
|
+
if (stale) {
|
|
2090
|
+
await fsp.rm(this.dirPath, { recursive: true, force: true });
|
|
2091
|
+
continue;
|
|
2092
|
+
}
|
|
1883
2093
|
if (Date.now() - start >= this.timeoutMs) {
|
|
1884
2094
|
throw new Error(`IdentityStore lock acquisition timed out for ${this.dirPath}`);
|
|
1885
2095
|
}
|
|
@@ -1887,6 +2097,16 @@ var FileLock = class {
|
|
|
1887
2097
|
}
|
|
1888
2098
|
}
|
|
1889
2099
|
}
|
|
2100
|
+
async isStaleLock() {
|
|
2101
|
+
try {
|
|
2102
|
+
const pidStr = await fsp.readFile(this.pidFilePath, "utf8");
|
|
2103
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
2104
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
2105
|
+
return !isProcessAlive(pid);
|
|
2106
|
+
} catch {
|
|
2107
|
+
return true;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
1890
2110
|
async release() {
|
|
1891
2111
|
if (!this.acquired) return;
|
|
1892
2112
|
this.acquired = false;
|
|
@@ -2315,6 +2535,14 @@ var TOOL_CHECK_TIMEOUT_MS = {
|
|
|
2315
2535
|
pnpm: 15e3,
|
|
2316
2536
|
nrm: 15e3
|
|
2317
2537
|
};
|
|
2538
|
+
var POSIX_LOGIN_SHELL_FALLBACK_TOOLS = /* @__PURE__ */ new Set([
|
|
2539
|
+
"npm",
|
|
2540
|
+
"pnpm",
|
|
2541
|
+
"nrm",
|
|
2542
|
+
"node",
|
|
2543
|
+
"dotnet",
|
|
2544
|
+
"rtk"
|
|
2545
|
+
]);
|
|
2318
2546
|
var ERROR_CODE_NOT_FOUND = 127;
|
|
2319
2547
|
var ERROR_CODE_TIMEOUT = "ETIMEDOUT";
|
|
2320
2548
|
var EnvironmentInspector = class {
|
|
@@ -2358,6 +2586,23 @@ var EnvironmentInspector = class {
|
|
|
2358
2586
|
timeoutMs: this.getToolTimeout(toolName)
|
|
2359
2587
|
});
|
|
2360
2588
|
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2589
|
+
} catch {
|
|
2590
|
+
if (this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2591
|
+
return this.getToolPathFromLoginShell(toolName);
|
|
2592
|
+
}
|
|
2593
|
+
return void 0;
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
async getToolPathFromLoginShell(toolName) {
|
|
2597
|
+
try {
|
|
2598
|
+
const result = await this.runCommandFn("/bin/bash", {
|
|
2599
|
+
args: [
|
|
2600
|
+
"-lc",
|
|
2601
|
+
'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; command -v ' + toolName
|
|
2602
|
+
],
|
|
2603
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2604
|
+
});
|
|
2605
|
+
return result.stdout.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0);
|
|
2361
2606
|
} catch {
|
|
2362
2607
|
return void 0;
|
|
2363
2608
|
}
|
|
@@ -2382,12 +2627,33 @@ var EnvironmentInspector = class {
|
|
|
2382
2627
|
}
|
|
2383
2628
|
}
|
|
2384
2629
|
async getToolVersion(toolName) {
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2630
|
+
try {
|
|
2631
|
+
const invocation = await this.getVersionInvocation(toolName);
|
|
2632
|
+
const result = await this.runCommandFn(invocation.command, {
|
|
2633
|
+
args: invocation.args,
|
|
2634
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2635
|
+
});
|
|
2636
|
+
return this.parseVersion(toolName, result.stdout || result.stderr);
|
|
2637
|
+
} catch (error) {
|
|
2638
|
+
if (!this.shouldUsePosixLoginShellFallback(toolName)) {
|
|
2639
|
+
throw error;
|
|
2640
|
+
}
|
|
2641
|
+
const fallbackResult = await this.runCommandFn("/bin/bash", {
|
|
2642
|
+
args: [
|
|
2643
|
+
"-lc",
|
|
2644
|
+
`export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; ${toolName} --version`
|
|
2645
|
+
],
|
|
2646
|
+
timeoutMs: this.getToolTimeout(toolName)
|
|
2647
|
+
});
|
|
2648
|
+
const fallbackOutput = (fallbackResult.stdout || fallbackResult.stderr).trim();
|
|
2649
|
+
if (!fallbackOutput) {
|
|
2650
|
+
throw error;
|
|
2651
|
+
}
|
|
2652
|
+
return this.parseVersion(toolName, fallbackOutput);
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
shouldUsePosixLoginShellFallback(toolName) {
|
|
2656
|
+
return this.platform !== "win32" && POSIX_LOGIN_SHELL_FALLBACK_TOOLS.has(toolName);
|
|
2391
2657
|
}
|
|
2392
2658
|
async checkNvm() {
|
|
2393
2659
|
if (this.platform === "win32") {
|
|
@@ -2437,7 +2703,7 @@ var EnvironmentInspector = class {
|
|
|
2437
2703
|
};
|
|
2438
2704
|
}
|
|
2439
2705
|
try {
|
|
2440
|
-
const result = await this.runCommandFn(
|
|
2706
|
+
const result = await this.runCommandFn(dotnetPath, {
|
|
2441
2707
|
args: ["nuget", "list", "source"],
|
|
2442
2708
|
timeoutMs: this.getToolTimeout("nuget")
|
|
2443
2709
|
});
|
|
@@ -2973,54 +3239,85 @@ function detectIndent(text) {
|
|
|
2973
3239
|
return 2;
|
|
2974
3240
|
}
|
|
2975
3241
|
|
|
2976
|
-
// src/
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
return arg;
|
|
2991
|
-
}
|
|
2992
|
-
try {
|
|
2993
|
-
return JSON.stringify(arg);
|
|
2994
|
-
} catch {
|
|
2995
|
-
return String(arg);
|
|
2996
|
-
}
|
|
2997
|
-
}).join(" ");
|
|
2998
|
-
}
|
|
2999
|
-
function createConsoleLogger(prefix = "serviceme") {
|
|
3000
|
-
return {
|
|
3001
|
-
debug(message, ...args) {
|
|
3002
|
-
process.stderr.write(`[${prefix}] DEBUG ${message} ${formatArgs(args)}
|
|
3003
|
-
`);
|
|
3004
|
-
},
|
|
3005
|
-
info(message, ...args) {
|
|
3006
|
-
process.stderr.write(`[${prefix}] INFO ${message} ${formatArgs(args)}
|
|
3007
|
-
`);
|
|
3008
|
-
},
|
|
3009
|
-
warn(message, ...args) {
|
|
3010
|
-
process.stderr.write(`[${prefix}] WARN ${message} ${formatArgs(args)}
|
|
3011
|
-
`);
|
|
3012
|
-
},
|
|
3013
|
-
error(message, ...args) {
|
|
3014
|
-
process.stderr.write(`[${prefix}] ERROR ${message} ${formatArgs(args)}
|
|
3015
|
-
`);
|
|
3242
|
+
// src/paths/serverProxyGlobal.ts
|
|
3243
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
3244
|
+
import * as fs6 from "fs/promises";
|
|
3245
|
+
import { open as open2 } from "fs/promises";
|
|
3246
|
+
import * as path9 from "path";
|
|
3247
|
+
async function readServerProxyGlobal() {
|
|
3248
|
+
const filePath = getServerProxyGlobalPath();
|
|
3249
|
+
try {
|
|
3250
|
+
const raw = await fs6.readFile(filePath, "utf8");
|
|
3251
|
+
const parsed = JSON.parse(raw);
|
|
3252
|
+
if (!isServerProxyGlobalState(parsed)) {
|
|
3253
|
+
throw new Error(
|
|
3254
|
+
`Invalid ${SERVER_PROXY_GLOBAL_FILENAME}: expected {enabled: boolean, lastServerUrl?: string, updatedAt: string}, got ${JSON.stringify(parsed).slice(0, 80)}`
|
|
3255
|
+
);
|
|
3016
3256
|
}
|
|
3257
|
+
return parsed;
|
|
3258
|
+
} catch (err) {
|
|
3259
|
+
if (isENOENT(err)) return null;
|
|
3260
|
+
throw err;
|
|
3261
|
+
}
|
|
3262
|
+
}
|
|
3263
|
+
async function writeServerProxyGlobal(patch) {
|
|
3264
|
+
const filePath = getServerProxyGlobalPath();
|
|
3265
|
+
const dirPath = path9.dirname(filePath);
|
|
3266
|
+
await fs6.mkdir(dirPath, { recursive: true });
|
|
3267
|
+
const current = await readServerProxyGlobal() ?? {
|
|
3268
|
+
enabled: false,
|
|
3269
|
+
allowOverride: false,
|
|
3270
|
+
lastServerUrl: void 0,
|
|
3271
|
+
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
3017
3272
|
};
|
|
3273
|
+
const next = {
|
|
3274
|
+
enabled: patch.enabled !== void 0 ? patch.enabled : current.enabled,
|
|
3275
|
+
allowOverride: patch.allowOverride !== void 0 ? patch.allowOverride : current.allowOverride,
|
|
3276
|
+
lastServerUrl: patch.lastServerUrl === void 0 ? current.lastServerUrl : patch.lastServerUrl === null ? void 0 : patch.lastServerUrl,
|
|
3277
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3278
|
+
};
|
|
3279
|
+
const tmpPath = `${filePath}.tmp-${randomUUID2()}`;
|
|
3280
|
+
const fh = await open2(tmpPath, "w");
|
|
3281
|
+
try {
|
|
3282
|
+
await fh.writeFile(JSON.stringify(next, null, " "), "utf8");
|
|
3283
|
+
await fh.sync();
|
|
3284
|
+
} finally {
|
|
3285
|
+
await fh.close();
|
|
3286
|
+
}
|
|
3287
|
+
await fs6.rename(tmpPath, filePath);
|
|
3288
|
+
return next;
|
|
3289
|
+
}
|
|
3290
|
+
async function migrateLegacyServerProxyEnabled(readLegacy, clearLegacy) {
|
|
3291
|
+
const legacyEnabled = readLegacy();
|
|
3292
|
+
if (legacyEnabled !== true) return null;
|
|
3293
|
+
const existing = await readServerProxyGlobal();
|
|
3294
|
+
if (existing?.enabled === true) {
|
|
3295
|
+
await clearLegacy();
|
|
3296
|
+
return null;
|
|
3297
|
+
}
|
|
3298
|
+
const next = await writeServerProxyGlobal({ enabled: true });
|
|
3299
|
+
await clearLegacy();
|
|
3300
|
+
return next;
|
|
3301
|
+
}
|
|
3302
|
+
function isServerProxyGlobalState(v) {
|
|
3303
|
+
if (!v || typeof v !== "object") return false;
|
|
3304
|
+
const obj = v;
|
|
3305
|
+
if (typeof obj.enabled !== "boolean") return false;
|
|
3306
|
+
if (typeof obj.allowOverride !== "boolean") return false;
|
|
3307
|
+
if (typeof obj.updatedAt !== "string") return false;
|
|
3308
|
+
if (obj.lastServerUrl !== void 0 && obj.lastServerUrl !== null && typeof obj.lastServerUrl !== "string") {
|
|
3309
|
+
return false;
|
|
3310
|
+
}
|
|
3311
|
+
return true;
|
|
3312
|
+
}
|
|
3313
|
+
function isENOENT(err) {
|
|
3314
|
+
return typeof err === "object" && err !== null && "code" in err && err.code === "ENOENT";
|
|
3018
3315
|
}
|
|
3019
3316
|
|
|
3020
3317
|
// src/phase5/bootstrap.ts
|
|
3021
|
-
import { randomUUID as
|
|
3022
|
-
import * as
|
|
3023
|
-
import * as
|
|
3318
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
3319
|
+
import * as fs7 from "fs/promises";
|
|
3320
|
+
import * as path10 from "path";
|
|
3024
3321
|
function getPhase5FileSpecs() {
|
|
3025
3322
|
return [
|
|
3026
3323
|
{
|
|
@@ -3051,7 +3348,7 @@ function getPhase5FileSpecs() {
|
|
|
3051
3348
|
path: getMachineIdPath(),
|
|
3052
3349
|
// Random uuid, written as a bare string. Subsequent
|
|
3053
3350
|
// activations see the file and skip re-randomizing.
|
|
3054
|
-
defaultContent:
|
|
3351
|
+
defaultContent: randomUUID3()
|
|
3055
3352
|
},
|
|
3056
3353
|
{
|
|
3057
3354
|
path: getProfilesJsonPath(),
|
|
@@ -3065,19 +3362,19 @@ async function bootstrapPhase5Placeholders() {
|
|
|
3065
3362
|
const result = { created: [], skipped: [], failed: [] };
|
|
3066
3363
|
const home = getServicemeHome();
|
|
3067
3364
|
try {
|
|
3068
|
-
await
|
|
3365
|
+
await fs7.mkdir(home, { recursive: true });
|
|
3069
3366
|
} catch (err) {
|
|
3070
3367
|
result.failed.push({ path: home, reason: err.message });
|
|
3071
3368
|
return result;
|
|
3072
3369
|
}
|
|
3073
3370
|
for (const spec of getPhase5FileSpecs()) {
|
|
3074
3371
|
try {
|
|
3075
|
-
await
|
|
3372
|
+
await fs7.access(spec.path);
|
|
3076
3373
|
result.skipped.push(spec.path);
|
|
3077
3374
|
} catch {
|
|
3078
3375
|
try {
|
|
3079
|
-
await
|
|
3080
|
-
await
|
|
3376
|
+
await fs7.mkdir(path10.dirname(spec.path), { recursive: true });
|
|
3377
|
+
await fs7.writeFile(spec.path, spec.defaultContent, "utf8");
|
|
3081
3378
|
result.created.push(spec.path);
|
|
3082
3379
|
} catch (writeErr) {
|
|
3083
3380
|
result.failed.push({ path: spec.path, reason: writeErr.message });
|
|
@@ -3088,16 +3385,16 @@ async function bootstrapPhase5Placeholders() {
|
|
|
3088
3385
|
}
|
|
3089
3386
|
|
|
3090
3387
|
// src/project/projectTools.ts
|
|
3091
|
-
import * as
|
|
3092
|
-
import * as
|
|
3388
|
+
import * as fs8 from "fs/promises";
|
|
3389
|
+
import * as path11 from "path";
|
|
3093
3390
|
import {
|
|
3094
3391
|
createServicemeError as createServicemeError7
|
|
3095
3392
|
} from "@serviceme/devtools-protocol";
|
|
3096
3393
|
|
|
3097
3394
|
// src/utils/fileUtils.ts
|
|
3098
3395
|
import { constants, createWriteStream } from "fs";
|
|
3099
|
-
import { access as access5, copyFile, lstat, mkdir as
|
|
3100
|
-
import { dirname as
|
|
3396
|
+
import { access as access5, copyFile, lstat, mkdir as mkdir5, readdir as readdir3, rename as rename4, rm as rm3 } from "fs/promises";
|
|
3397
|
+
import { dirname as dirname7, join as join8 } from "path";
|
|
3101
3398
|
import yauzl from "yauzl";
|
|
3102
3399
|
var unzipFile = (zipPath, dest) => {
|
|
3103
3400
|
return new Promise((resolve3, reject) => {
|
|
@@ -3107,12 +3404,12 @@ var unzipFile = (zipPath, dest) => {
|
|
|
3107
3404
|
zipfile.readEntry();
|
|
3108
3405
|
zipfile.on("entry", (entry) => {
|
|
3109
3406
|
if (/\/$/.test(entry.fileName)) {
|
|
3110
|
-
void
|
|
3407
|
+
void mkdir5(join8(dest, entry.fileName), { recursive: true }).then(() => {
|
|
3111
3408
|
zipfile.readEntry();
|
|
3112
3409
|
}).catch(reject);
|
|
3113
3410
|
} else {
|
|
3114
|
-
const outputPath =
|
|
3115
|
-
void
|
|
3411
|
+
const outputPath = join8(dest, entry.fileName);
|
|
3412
|
+
void mkdir5(dirname7(outputPath), { recursive: true }).then(() => {
|
|
3116
3413
|
zipfile.openReadStream(
|
|
3117
3414
|
entry,
|
|
3118
3415
|
(streamError, readStream) => {
|
|
@@ -3157,10 +3454,10 @@ var mergeEntry = async (sourcePath, destPath, overwrite) => {
|
|
|
3157
3454
|
}
|
|
3158
3455
|
await rm3(destPath, { recursive: true, force: true });
|
|
3159
3456
|
}
|
|
3160
|
-
await
|
|
3457
|
+
await mkdir5(destPath, { recursive: true });
|
|
3161
3458
|
const children = await readdir3(sourcePath);
|
|
3162
3459
|
for (const child of children) {
|
|
3163
|
-
await mergeEntry(
|
|
3460
|
+
await mergeEntry(join8(sourcePath, child), join8(destPath, child), overwrite);
|
|
3164
3461
|
}
|
|
3165
3462
|
await rm3(sourcePath, { recursive: true, force: true });
|
|
3166
3463
|
return;
|
|
@@ -3173,18 +3470,18 @@ var mergeEntry = async (sourcePath, destPath, overwrite) => {
|
|
|
3173
3470
|
await rm3(destPath, { recursive: true, force: true });
|
|
3174
3471
|
}
|
|
3175
3472
|
try {
|
|
3176
|
-
await
|
|
3473
|
+
await rename4(sourcePath, destPath);
|
|
3177
3474
|
} catch {
|
|
3178
3475
|
await copyFile(sourcePath, destPath);
|
|
3179
3476
|
await rm3(sourcePath, { recursive: true, force: true });
|
|
3180
3477
|
}
|
|
3181
3478
|
};
|
|
3182
3479
|
var moveFiles = async (sourceDir, destDir, overwrite = false) => {
|
|
3183
|
-
await
|
|
3480
|
+
await mkdir5(destDir, { recursive: true });
|
|
3184
3481
|
const files = await readdir3(sourceDir);
|
|
3185
3482
|
for (const file of files) {
|
|
3186
|
-
const sourceFile =
|
|
3187
|
-
const destFile =
|
|
3483
|
+
const sourceFile = join8(sourceDir, file);
|
|
3484
|
+
const destFile = join8(destDir, file);
|
|
3188
3485
|
if (!overwrite) {
|
|
3189
3486
|
try {
|
|
3190
3487
|
await access5(destFile, constants.F_OK);
|
|
@@ -3201,10 +3498,10 @@ var moveFiles = async (sourceDir, destDir, overwrite = false) => {
|
|
|
3201
3498
|
var ProjectTools = class {
|
|
3202
3499
|
async extractTemplate(zipPath, workspacePath, tempExtractDir, input) {
|
|
3203
3500
|
await unzipFile(zipPath, tempExtractDir);
|
|
3204
|
-
let sourceDir =
|
|
3501
|
+
let sourceDir = path11.join(tempExtractDir, input.extractedDirName);
|
|
3205
3502
|
let actualDirName = input.extractedDirName;
|
|
3206
3503
|
if (!await this.pathExists(sourceDir)) {
|
|
3207
|
-
const entries = await
|
|
3504
|
+
const entries = await fs8.readdir(tempExtractDir, { withFileTypes: true });
|
|
3208
3505
|
const directories = entries.filter(
|
|
3209
3506
|
(entry) => entry.isDirectory() && !entry.name.startsWith(".")
|
|
3210
3507
|
);
|
|
@@ -3216,7 +3513,7 @@ var ProjectTools = class {
|
|
|
3216
3513
|
);
|
|
3217
3514
|
if (selectedDirectory) {
|
|
3218
3515
|
actualDirName = selectedDirectory;
|
|
3219
|
-
sourceDir =
|
|
3516
|
+
sourceDir = path11.join(tempExtractDir, actualDirName);
|
|
3220
3517
|
} else if (directories.length === 0) {
|
|
3221
3518
|
throw new Error(
|
|
3222
3519
|
`No directory found after extraction. Expected directory: ${input.extractedDirName}`
|
|
@@ -3266,7 +3563,7 @@ var ProjectTools = class {
|
|
|
3266
3563
|
} else {
|
|
3267
3564
|
for (const scriptPath of scripts) {
|
|
3268
3565
|
try {
|
|
3269
|
-
await
|
|
3566
|
+
await fs8.chmod(scriptPath, 493);
|
|
3270
3567
|
updatedCount += 1;
|
|
3271
3568
|
} catch {
|
|
3272
3569
|
}
|
|
@@ -3314,7 +3611,7 @@ var ProjectTools = class {
|
|
|
3314
3611
|
};
|
|
3315
3612
|
}
|
|
3316
3613
|
async ensurePresetManifest(workspacePath, preset) {
|
|
3317
|
-
const presetManifestPath =
|
|
3614
|
+
const presetManifestPath = path11.join(
|
|
3318
3615
|
workspacePath,
|
|
3319
3616
|
".ms-scaffold",
|
|
3320
3617
|
"presets",
|
|
@@ -3323,11 +3620,11 @@ var ProjectTools = class {
|
|
|
3323
3620
|
if (await this.pathExists(presetManifestPath)) {
|
|
3324
3621
|
return;
|
|
3325
3622
|
}
|
|
3326
|
-
const projectModePath =
|
|
3623
|
+
const projectModePath = path11.join(workspacePath, ".ms-scaffold", "project-mode.json");
|
|
3327
3624
|
if (!await this.pathExists(projectModePath)) {
|
|
3328
3625
|
return;
|
|
3329
3626
|
}
|
|
3330
|
-
const projectModeRaw = await
|
|
3627
|
+
const projectModeRaw = await fs8.readFile(projectModePath, "utf8");
|
|
3331
3628
|
const projectMode = JSON.parse(projectModeRaw);
|
|
3332
3629
|
const synthesizedPreset = {
|
|
3333
3630
|
preset,
|
|
@@ -3339,8 +3636,8 @@ var ProjectTools = class {
|
|
|
3339
3636
|
mergeManagedFiles: [],
|
|
3340
3637
|
userOwnedPaths: []
|
|
3341
3638
|
};
|
|
3342
|
-
await
|
|
3343
|
-
await
|
|
3639
|
+
await fs8.mkdir(path11.dirname(presetManifestPath), { recursive: true });
|
|
3640
|
+
await fs8.writeFile(
|
|
3344
3641
|
presetManifestPath,
|
|
3345
3642
|
`${JSON.stringify(synthesizedPreset, null, 2)}
|
|
3346
3643
|
`,
|
|
@@ -3351,12 +3648,12 @@ var ProjectTools = class {
|
|
|
3351
3648
|
const results = [];
|
|
3352
3649
|
let entries;
|
|
3353
3650
|
try {
|
|
3354
|
-
entries = await
|
|
3651
|
+
entries = await fs8.readdir(dir, { withFileTypes: true });
|
|
3355
3652
|
} catch {
|
|
3356
3653
|
return results;
|
|
3357
3654
|
}
|
|
3358
3655
|
for (const entry of entries) {
|
|
3359
|
-
const fullPath =
|
|
3656
|
+
const fullPath = path11.join(dir, entry.name);
|
|
3360
3657
|
if (entry.isDirectory() && entry.name !== "node_modules" && !entry.name.startsWith(".")) {
|
|
3361
3658
|
results.push(...await this.findScripts(fullPath, extensions));
|
|
3362
3659
|
} else if (entry.isFile() && extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
@@ -3367,7 +3664,7 @@ var ProjectTools = class {
|
|
|
3367
3664
|
}
|
|
3368
3665
|
async pathExists(targetPath) {
|
|
3369
3666
|
try {
|
|
3370
|
-
await
|
|
3667
|
+
await fs8.access(targetPath);
|
|
3371
3668
|
return true;
|
|
3372
3669
|
} catch {
|
|
3373
3670
|
return false;
|
|
@@ -3384,7 +3681,7 @@ var ProjectTools = class {
|
|
|
3384
3681
|
const matches = [];
|
|
3385
3682
|
for (const directoryName of directoryNames) {
|
|
3386
3683
|
if (await this.directoryMatchesProjectPattern(
|
|
3387
|
-
|
|
3684
|
+
path11.join(tempExtractDir, directoryName),
|
|
3388
3685
|
projectFilePattern
|
|
3389
3686
|
)) {
|
|
3390
3687
|
matches.push(directoryName);
|
|
@@ -3396,7 +3693,7 @@ var ProjectTools = class {
|
|
|
3396
3693
|
return null;
|
|
3397
3694
|
}
|
|
3398
3695
|
async directoryMatchesProjectPattern(directoryPath, projectFilePattern) {
|
|
3399
|
-
const entries = await
|
|
3696
|
+
const entries = await fs8.readdir(directoryPath);
|
|
3400
3697
|
if (projectFilePattern.includes("*")) {
|
|
3401
3698
|
const regex = new RegExp(`^${projectFilePattern.replace("*", ".*")}$`);
|
|
3402
3699
|
return entries.some((entry) => regex.test(entry));
|
|
@@ -3409,8 +3706,8 @@ function createProjectTools() {
|
|
|
3409
3706
|
}
|
|
3410
3707
|
|
|
3411
3708
|
// src/repo-manager/index.ts
|
|
3412
|
-
import * as
|
|
3413
|
-
import * as
|
|
3709
|
+
import * as fs9 from "fs/promises";
|
|
3710
|
+
import * as path12 from "path";
|
|
3414
3711
|
|
|
3415
3712
|
// src/repos/types.ts
|
|
3416
3713
|
function isDefaultRepo(repo) {
|
|
@@ -3492,11 +3789,11 @@ var RepoManager = class {
|
|
|
3492
3789
|
const exists = await this.pathExists(localPath);
|
|
3493
3790
|
if (exists) {
|
|
3494
3791
|
if (await this.isValidGitRepo(localPath)) continue;
|
|
3495
|
-
await
|
|
3792
|
+
await fs9.rm(localPath, { recursive: true, force: true });
|
|
3496
3793
|
}
|
|
3497
3794
|
try {
|
|
3498
3795
|
if (!this.skipClone) {
|
|
3499
|
-
await
|
|
3796
|
+
await fs9.mkdir(path12.dirname(localPath), { recursive: true });
|
|
3500
3797
|
await this.git.clone(repo.id, repo.url, localPath, repo.branch, true);
|
|
3501
3798
|
}
|
|
3502
3799
|
await this.store.updateRepo(repo.id, {
|
|
@@ -3531,10 +3828,10 @@ var RepoManager = class {
|
|
|
3531
3828
|
const localPath = getRepoDir(repoId);
|
|
3532
3829
|
const exists = await this.pathExists(localPath);
|
|
3533
3830
|
if (exists && !await this.isValidGitRepo(localPath)) {
|
|
3534
|
-
await
|
|
3831
|
+
await fs9.rm(localPath, { recursive: true, force: true });
|
|
3535
3832
|
}
|
|
3536
3833
|
if (!exists || !await this.pathExists(localPath)) {
|
|
3537
|
-
await
|
|
3834
|
+
await fs9.mkdir(path12.dirname(localPath), { recursive: true });
|
|
3538
3835
|
if (!this.skipClone) {
|
|
3539
3836
|
await this.git.clone(proxyId, repo.url, localPath, repo.branch, useProxy);
|
|
3540
3837
|
}
|
|
@@ -3634,7 +3931,7 @@ var RepoManager = class {
|
|
|
3634
3931
|
let cloned = false;
|
|
3635
3932
|
if (!this.skipClone) {
|
|
3636
3933
|
const localPath = getRepoDir(id);
|
|
3637
|
-
await
|
|
3934
|
+
await fs9.mkdir(path12.dirname(localPath), { recursive: true });
|
|
3638
3935
|
await this.git.clone(userProxyId, url, localPath, branch, useProxy);
|
|
3639
3936
|
cloned = true;
|
|
3640
3937
|
}
|
|
@@ -3659,7 +3956,7 @@ var RepoManager = class {
|
|
|
3659
3956
|
await this.store.removeUserRepo(repoId);
|
|
3660
3957
|
const localPath = getRepoDir(repoId);
|
|
3661
3958
|
try {
|
|
3662
|
-
await
|
|
3959
|
+
await fs9.rm(localPath, { recursive: true, force: true });
|
|
3663
3960
|
} catch (err) {
|
|
3664
3961
|
if (err.code !== "ENOENT") throw err;
|
|
3665
3962
|
}
|
|
@@ -3682,7 +3979,7 @@ var RepoManager = class {
|
|
|
3682
3979
|
}
|
|
3683
3980
|
/** Force-create the SERVICEME home directory tree (idempotent). */
|
|
3684
3981
|
async ensureHome() {
|
|
3685
|
-
await
|
|
3982
|
+
await fs9.mkdir(getServicemeHome(), { recursive: true });
|
|
3686
3983
|
}
|
|
3687
3984
|
/**
|
|
3688
3985
|
* Returns `true` when `p` contains a `.git` entry — i.e. it is an
|
|
@@ -3690,11 +3987,11 @@ var RepoManager = class {
|
|
|
3690
3987
|
* (e.g. from an interrupted clone) return `false`.
|
|
3691
3988
|
*/
|
|
3692
3989
|
async isValidGitRepo(p) {
|
|
3693
|
-
return this.pathExists(
|
|
3990
|
+
return this.pathExists(path12.join(p, ".git"));
|
|
3694
3991
|
}
|
|
3695
3992
|
async pathExists(p) {
|
|
3696
3993
|
try {
|
|
3697
|
-
await
|
|
3994
|
+
await fs9.stat(p);
|
|
3698
3995
|
return true;
|
|
3699
3996
|
} catch {
|
|
3700
3997
|
return false;
|
|
@@ -3819,8 +4116,8 @@ function resolveDefaultRepoId(existing, existingIds) {
|
|
|
3819
4116
|
}
|
|
3820
4117
|
|
|
3821
4118
|
// src/repos/loader.ts
|
|
3822
|
-
import * as
|
|
3823
|
-
import * as
|
|
4119
|
+
import * as fs10 from "fs/promises";
|
|
4120
|
+
import * as path13 from "path";
|
|
3824
4121
|
import { z } from "zod";
|
|
3825
4122
|
var ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:?\d{2})$/;
|
|
3826
4123
|
var repoIdSchema = 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}$/");
|
|
@@ -3903,7 +4200,7 @@ var ReposLoader = class {
|
|
|
3903
4200
|
this.configPath = options.configPath;
|
|
3904
4201
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
3905
4202
|
this.randomSuffix = options.randomSuffix ?? defaultRandomSuffix;
|
|
3906
|
-
this.fileSystem = options.fileSystem ??
|
|
4203
|
+
this.fileSystem = options.fileSystem ?? fs10;
|
|
3907
4204
|
}
|
|
3908
4205
|
/** Absolute path of the file this loader reads/writes. */
|
|
3909
4206
|
getConfigPath() {
|
|
@@ -3956,7 +4253,7 @@ var ReposLoader = class {
|
|
|
3956
4253
|
*/
|
|
3957
4254
|
async save(config) {
|
|
3958
4255
|
const validated = reposFileSchema.parse(config);
|
|
3959
|
-
const dir =
|
|
4256
|
+
const dir = path13.dirname(this.configPath);
|
|
3960
4257
|
await this.fileSystem.mkdir(dir, { recursive: true });
|
|
3961
4258
|
const serialized = `${JSON.stringify(validated, null, 2)}
|
|
3962
4259
|
`;
|
|
@@ -3965,7 +4262,7 @@ var ReposLoader = class {
|
|
|
3965
4262
|
try {
|
|
3966
4263
|
await this.fileSystem.rename(tempPath, this.configPath);
|
|
3967
4264
|
} catch (error) {
|
|
3968
|
-
const unlink2 = this.fileSystem.unlink ??
|
|
4265
|
+
const unlink2 = this.fileSystem.unlink ?? fs10.unlink;
|
|
3969
4266
|
await unlink2(tempPath).catch(() => void 0);
|
|
3970
4267
|
throw error;
|
|
3971
4268
|
}
|
|
@@ -4025,7 +4322,7 @@ function narrowRepoConfig(repo) {
|
|
|
4025
4322
|
|
|
4026
4323
|
// src/repos/store.ts
|
|
4027
4324
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
4028
|
-
import * as
|
|
4325
|
+
import * as fs11 from "fs";
|
|
4029
4326
|
var ReposStore = class {
|
|
4030
4327
|
constructor(options = {}) {
|
|
4031
4328
|
this.config = null;
|
|
@@ -4034,7 +4331,7 @@ var ReposStore = class {
|
|
|
4034
4331
|
this.reloadTimer = null;
|
|
4035
4332
|
this.lastLoadResult = null;
|
|
4036
4333
|
this.loader = options.loader ?? new ReposLoader({ configPath: "" });
|
|
4037
|
-
this.fileSystem = options.fileSystem ?? { watch:
|
|
4334
|
+
this.fileSystem = options.fileSystem ?? { watch: fs11.watch };
|
|
4038
4335
|
this.now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
4039
4336
|
this.debounceMs = options.debounceMs ?? 50;
|
|
4040
4337
|
this.createFsWatcher = options.createFsWatcher ?? ((p, cb) => this.defaultCreateFsWatcher(p, cb));
|
|
@@ -4316,17 +4613,17 @@ async function bootstrapDefaults(store) {
|
|
|
4316
4613
|
}
|
|
4317
4614
|
|
|
4318
4615
|
// src/scheduled-tasks/daemon/DaemonLogger.ts
|
|
4319
|
-
import * as
|
|
4320
|
-
import * as
|
|
4616
|
+
import * as fs12 from "fs";
|
|
4617
|
+
import * as path14 from "path";
|
|
4321
4618
|
var CONFIG_DIR = ".serviceme";
|
|
4322
4619
|
var LOG_FILE = "scheduler.log";
|
|
4323
4620
|
var MAX_LOG_SIZE = 1024 * 1024;
|
|
4324
4621
|
var DaemonLogger = class {
|
|
4325
4622
|
constructor(workspacePath, options = {}) {
|
|
4326
|
-
this.logPath = options.logPath ??
|
|
4327
|
-
const dir =
|
|
4328
|
-
if (!
|
|
4329
|
-
|
|
4623
|
+
this.logPath = options.logPath ?? path14.join(workspacePath, CONFIG_DIR, LOG_FILE);
|
|
4624
|
+
const dir = path14.dirname(this.logPath);
|
|
4625
|
+
if (!fs12.existsSync(dir)) {
|
|
4626
|
+
fs12.mkdirSync(dir, { recursive: true });
|
|
4330
4627
|
}
|
|
4331
4628
|
}
|
|
4332
4629
|
getLogPath() {
|
|
@@ -4337,16 +4634,16 @@ var DaemonLogger = class {
|
|
|
4337
4634
|
const line = `[${ts}] [${level.toUpperCase()}] ${message}
|
|
4338
4635
|
`;
|
|
4339
4636
|
this.rotateIfNeeded();
|
|
4340
|
-
|
|
4637
|
+
fs12.appendFileSync(this.logPath, line, "utf-8");
|
|
4341
4638
|
}
|
|
4342
4639
|
rotateIfNeeded() {
|
|
4343
4640
|
try {
|
|
4344
|
-
const stats =
|
|
4641
|
+
const stats = fs12.statSync(this.logPath);
|
|
4345
4642
|
if (stats.size > MAX_LOG_SIZE) {
|
|
4346
|
-
const content =
|
|
4643
|
+
const content = fs12.readFileSync(this.logPath, "utf-8");
|
|
4347
4644
|
const halfIdx = content.indexOf("\n", Math.floor(content.length / 2));
|
|
4348
4645
|
if (halfIdx > 0) {
|
|
4349
|
-
|
|
4646
|
+
fs12.writeFileSync(this.logPath, content.slice(halfIdx + 1), "utf-8");
|
|
4350
4647
|
}
|
|
4351
4648
|
}
|
|
4352
4649
|
} catch {
|
|
@@ -4355,39 +4652,39 @@ var DaemonLogger = class {
|
|
|
4355
4652
|
};
|
|
4356
4653
|
|
|
4357
4654
|
// src/scheduled-tasks/daemon/PidManager.ts
|
|
4358
|
-
import * as
|
|
4359
|
-
import * as
|
|
4655
|
+
import * as fs13 from "fs";
|
|
4656
|
+
import * as path15 from "path";
|
|
4360
4657
|
var CONFIG_DIR2 = ".serviceme";
|
|
4361
4658
|
var PID_FILE = "scheduler.pid";
|
|
4362
4659
|
var PidManager = class {
|
|
4363
4660
|
constructor(workspacePath, options = {}) {
|
|
4364
|
-
this.pidPath = options.pidPath ??
|
|
4661
|
+
this.pidPath = options.pidPath ?? path15.join(workspacePath, CONFIG_DIR2, PID_FILE);
|
|
4365
4662
|
}
|
|
4366
4663
|
getPidPath() {
|
|
4367
4664
|
return this.pidPath;
|
|
4368
4665
|
}
|
|
4369
4666
|
writePid(pid) {
|
|
4370
|
-
const dir =
|
|
4371
|
-
if (!
|
|
4372
|
-
|
|
4667
|
+
const dir = path15.dirname(this.pidPath);
|
|
4668
|
+
if (!fs13.existsSync(dir)) {
|
|
4669
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
4373
4670
|
}
|
|
4374
|
-
|
|
4671
|
+
fs13.writeFileSync(this.pidPath, String(pid), "utf-8");
|
|
4375
4672
|
}
|
|
4376
4673
|
readPid() {
|
|
4377
4674
|
let stat5;
|
|
4378
4675
|
try {
|
|
4379
|
-
stat5 =
|
|
4676
|
+
stat5 = fs13.statSync(this.pidPath);
|
|
4380
4677
|
} catch {
|
|
4381
4678
|
return null;
|
|
4382
4679
|
}
|
|
4383
4680
|
if (!stat5.isFile()) return null;
|
|
4384
|
-
const raw =
|
|
4681
|
+
const raw = fs13.readFileSync(this.pidPath, "utf-8").trim();
|
|
4385
4682
|
const pid = Number.parseInt(raw, 10);
|
|
4386
4683
|
return Number.isNaN(pid) ? null : pid;
|
|
4387
4684
|
}
|
|
4388
4685
|
removePid() {
|
|
4389
|
-
if (
|
|
4390
|
-
|
|
4686
|
+
if (fs13.existsSync(this.pidPath)) {
|
|
4687
|
+
fs13.unlinkSync(this.pidPath);
|
|
4391
4688
|
}
|
|
4392
4689
|
}
|
|
4393
4690
|
isProcessRunning(pid) {
|
|
@@ -4408,13 +4705,13 @@ var PidManager = class {
|
|
|
4408
4705
|
};
|
|
4409
4706
|
|
|
4410
4707
|
// src/scheduled-tasks/daemon/SchedulerDaemon.ts
|
|
4411
|
-
import * as
|
|
4708
|
+
import * as fs18 from "fs";
|
|
4412
4709
|
import * as os5 from "os";
|
|
4413
|
-
import * as
|
|
4710
|
+
import * as path19 from "path";
|
|
4414
4711
|
|
|
4415
4712
|
// src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts
|
|
4416
4713
|
import { spawn as spawn3 } from "child_process";
|
|
4417
|
-
import * as
|
|
4714
|
+
import * as fs14 from "fs";
|
|
4418
4715
|
|
|
4419
4716
|
// src/scheduled-tasks/executors/timeout.ts
|
|
4420
4717
|
function resolveConfiguredTimeoutMs(timeoutSeconds, defaultTimeoutMs) {
|
|
@@ -4449,7 +4746,7 @@ function redactArgs(args) {
|
|
|
4449
4746
|
function writeDiagnostic(message) {
|
|
4450
4747
|
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
|
|
4451
4748
|
if (logPath) {
|
|
4452
|
-
|
|
4749
|
+
fs14.appendFileSync(logPath, message);
|
|
4453
4750
|
return;
|
|
4454
4751
|
}
|
|
4455
4752
|
process.stderr.write(message);
|
|
@@ -4655,15 +4952,15 @@ ${body}`.trim()
|
|
|
4655
4952
|
|
|
4656
4953
|
// src/scheduled-tasks/executors/ShellExecutor.ts
|
|
4657
4954
|
import { spawn as spawn4 } from "child_process";
|
|
4658
|
-
import * as
|
|
4659
|
-
import * as
|
|
4955
|
+
import * as fs15 from "fs";
|
|
4956
|
+
import * as path16 from "path";
|
|
4660
4957
|
var MAX_OUTPUT_BYTES2 = 1024 * 1024;
|
|
4661
4958
|
var DEFAULT_TIMEOUT_MS4 = 6e4;
|
|
4662
4959
|
var POSIX_SHELL_CANDIDATES = ["bash.exe", "sh.exe"];
|
|
4663
4960
|
function resolveShellExecution(script, options = {}) {
|
|
4664
4961
|
const platform3 = options.platform ?? process.platform;
|
|
4665
4962
|
const env = options.env ?? process.env;
|
|
4666
|
-
const fileExists = options.fileExists ??
|
|
4963
|
+
const fileExists = options.fileExists ?? fs15.existsSync;
|
|
4667
4964
|
if (platform3 === "win32") {
|
|
4668
4965
|
const posixShell = usesPosixShellSyntax(script) ? findWindowsPosixShell(env, fileExists) : null;
|
|
4669
4966
|
if (posixShell) {
|
|
@@ -4708,10 +5005,10 @@ function findWindowsPosixShell(env, fileExists) {
|
|
|
4708
5005
|
if (fileExists(candidate)) return candidate;
|
|
4709
5006
|
}
|
|
4710
5007
|
const pathValue = env.Path ?? env.PATH ?? "";
|
|
4711
|
-
for (const dir of pathValue.split(
|
|
5008
|
+
for (const dir of pathValue.split(path16.win32.delimiter)) {
|
|
4712
5009
|
if (!dir) continue;
|
|
4713
5010
|
for (const executable of POSIX_SHELL_CANDIDATES) {
|
|
4714
|
-
const candidate =
|
|
5011
|
+
const candidate = path16.win32.join(dir, executable);
|
|
4715
5012
|
if (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {
|
|
4716
5013
|
return candidate;
|
|
4717
5014
|
}
|
|
@@ -4720,13 +5017,13 @@ function findWindowsPosixShell(env, fileExists) {
|
|
|
4720
5017
|
return null;
|
|
4721
5018
|
}
|
|
4722
5019
|
function isWindowsWslLauncher(candidate) {
|
|
4723
|
-
const normalized =
|
|
5020
|
+
const normalized = path16.win32.normalize(candidate).toLowerCase();
|
|
4724
5021
|
return normalized.endsWith("\\windows\\system32\\bash.exe") || normalized.endsWith("\\windows\\syswow64\\bash.exe");
|
|
4725
5022
|
}
|
|
4726
5023
|
function writeDiagnostic2(message) {
|
|
4727
5024
|
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
|
|
4728
5025
|
if (logPath) {
|
|
4729
|
-
|
|
5026
|
+
fs15.appendFileSync(logPath, message);
|
|
4730
5027
|
return;
|
|
4731
5028
|
}
|
|
4732
5029
|
process.stderr.write(message);
|
|
@@ -4868,10 +5165,10 @@ function getExecutor(taskType) {
|
|
|
4868
5165
|
}
|
|
4869
5166
|
|
|
4870
5167
|
// src/scheduled-tasks/TaskConfigManager.ts
|
|
4871
|
-
import { randomUUID as
|
|
4872
|
-
import * as
|
|
5168
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
5169
|
+
import * as fs16 from "fs";
|
|
4873
5170
|
import * as os4 from "os";
|
|
4874
|
-
import * as
|
|
5171
|
+
import * as path17 from "path";
|
|
4875
5172
|
import {
|
|
4876
5173
|
createServicemeError as createServicemeError8,
|
|
4877
5174
|
isScheduledTasksConfig,
|
|
@@ -4901,7 +5198,7 @@ function v1ContainerShape(value) {
|
|
|
4901
5198
|
}
|
|
4902
5199
|
function defaultWorkspaceContext() {
|
|
4903
5200
|
const home = os4.homedir() || "/";
|
|
4904
|
-
return { path: home, name:
|
|
5201
|
+
return { path: home, name: path17.basename(home) || home };
|
|
4905
5202
|
}
|
|
4906
5203
|
function requireNonEmptyString(payload, field, taskType) {
|
|
4907
5204
|
if (!isRecord2(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
|
|
@@ -4936,10 +5233,10 @@ var TaskConfigManager = class {
|
|
|
4936
5233
|
return this.configPath;
|
|
4937
5234
|
}
|
|
4938
5235
|
readConfig() {
|
|
4939
|
-
if (!
|
|
5236
|
+
if (!fs16.existsSync(this.configPath)) {
|
|
4940
5237
|
return emptyConfig();
|
|
4941
5238
|
}
|
|
4942
|
-
const raw =
|
|
5239
|
+
const raw = fs16.readFileSync(this.configPath, "utf-8");
|
|
4943
5240
|
let parsed;
|
|
4944
5241
|
try {
|
|
4945
5242
|
parsed = JSON.parse(raw);
|
|
@@ -4992,7 +5289,7 @@ var TaskConfigManager = class {
|
|
|
4992
5289
|
const target = this.migrationFailuresPath ?? getMigrationFailuresPath();
|
|
4993
5290
|
const prior = (() => {
|
|
4994
5291
|
try {
|
|
4995
|
-
return JSON.parse(
|
|
5292
|
+
return JSON.parse(fs16.readFileSync(target, "utf-8"));
|
|
4996
5293
|
} catch {
|
|
4997
5294
|
return [];
|
|
4998
5295
|
}
|
|
@@ -5005,8 +5302,8 @@ var TaskConfigManager = class {
|
|
|
5005
5302
|
snippet: raw.slice(0, 500),
|
|
5006
5303
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5007
5304
|
});
|
|
5008
|
-
|
|
5009
|
-
|
|
5305
|
+
fs16.mkdirSync(path17.dirname(target), { recursive: true });
|
|
5306
|
+
fs16.writeFileSync(target, JSON.stringify(failures, null, " "), "utf-8");
|
|
5010
5307
|
} catch (writeError) {
|
|
5011
5308
|
this.warn(
|
|
5012
5309
|
`TaskConfigManager: also failed to write migration-failures log: ${String(writeError)}`
|
|
@@ -5014,13 +5311,13 @@ var TaskConfigManager = class {
|
|
|
5014
5311
|
}
|
|
5015
5312
|
}
|
|
5016
5313
|
writeConfig(config) {
|
|
5017
|
-
const dir =
|
|
5018
|
-
if (!
|
|
5019
|
-
|
|
5314
|
+
const dir = path17.dirname(this.configPath);
|
|
5315
|
+
if (!fs16.existsSync(dir)) {
|
|
5316
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
5020
5317
|
}
|
|
5021
5318
|
const tmp = `${this.configPath}.tmp`;
|
|
5022
|
-
|
|
5023
|
-
|
|
5319
|
+
fs16.writeFileSync(tmp, JSON.stringify(config, null, " "), "utf-8");
|
|
5320
|
+
fs16.renameSync(tmp, this.configPath);
|
|
5024
5321
|
}
|
|
5025
5322
|
listTasks() {
|
|
5026
5323
|
return this.readConfig().tasks;
|
|
@@ -5042,7 +5339,7 @@ var TaskConfigManager = class {
|
|
|
5042
5339
|
const config = this.readConfig();
|
|
5043
5340
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5044
5341
|
const task = {
|
|
5045
|
-
id:
|
|
5342
|
+
id: randomUUID4(),
|
|
5046
5343
|
name: input.name,
|
|
5047
5344
|
description: input.description,
|
|
5048
5345
|
enabled: input.enabled ?? true,
|
|
@@ -5297,9 +5594,9 @@ var TaskExecutionEngine = class {
|
|
|
5297
5594
|
};
|
|
5298
5595
|
|
|
5299
5596
|
// src/scheduled-tasks/TaskLogManager.ts
|
|
5300
|
-
import { randomUUID as
|
|
5301
|
-
import * as
|
|
5302
|
-
import * as
|
|
5597
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
5598
|
+
import * as fs17 from "fs";
|
|
5599
|
+
import * as path18 from "path";
|
|
5303
5600
|
var MAX_LOGS = 200;
|
|
5304
5601
|
function emptyLogFile() {
|
|
5305
5602
|
return { logs: [] };
|
|
@@ -5328,11 +5625,11 @@ var TaskLogManager = class {
|
|
|
5328
5625
|
return this.logPath;
|
|
5329
5626
|
}
|
|
5330
5627
|
readLogFile() {
|
|
5331
|
-
if (!
|
|
5628
|
+
if (!fs17.existsSync(this.logPath)) {
|
|
5332
5629
|
return emptyLogFile();
|
|
5333
5630
|
}
|
|
5334
5631
|
try {
|
|
5335
|
-
const raw =
|
|
5632
|
+
const raw = fs17.readFileSync(this.logPath, "utf-8");
|
|
5336
5633
|
const parsed = JSON.parse(raw);
|
|
5337
5634
|
const file = validateAndRepairLogFile(parsed);
|
|
5338
5635
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.logs) || parsed.logs.length !== file.logs.length) {
|
|
@@ -5348,26 +5645,26 @@ var TaskLogManager = class {
|
|
|
5348
5645
|
}
|
|
5349
5646
|
backupCorruptedFile() {
|
|
5350
5647
|
try {
|
|
5351
|
-
if (
|
|
5648
|
+
if (fs17.existsSync(this.logPath)) {
|
|
5352
5649
|
const backupPath = `${this.logPath}.corrupted.${Date.now()}`;
|
|
5353
|
-
|
|
5650
|
+
fs17.copyFileSync(this.logPath, backupPath);
|
|
5354
5651
|
}
|
|
5355
5652
|
} catch {
|
|
5356
5653
|
}
|
|
5357
5654
|
}
|
|
5358
5655
|
writeLogFile(file) {
|
|
5359
|
-
const dir =
|
|
5360
|
-
if (!
|
|
5361
|
-
|
|
5656
|
+
const dir = path18.dirname(this.logPath);
|
|
5657
|
+
if (!fs17.existsSync(dir)) {
|
|
5658
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
5362
5659
|
}
|
|
5363
5660
|
const tmp = `${this.logPath}.tmp`;
|
|
5364
|
-
|
|
5365
|
-
|
|
5661
|
+
fs17.writeFileSync(tmp, JSON.stringify(file, null, " "), "utf-8");
|
|
5662
|
+
fs17.renameSync(tmp, this.logPath);
|
|
5366
5663
|
}
|
|
5367
5664
|
appendLog(input) {
|
|
5368
5665
|
const file = this.readLogFile();
|
|
5369
5666
|
const log = {
|
|
5370
|
-
id:
|
|
5667
|
+
id: randomUUID5(),
|
|
5371
5668
|
taskId: input.taskId,
|
|
5372
5669
|
taskName: input.taskName,
|
|
5373
5670
|
startedAt: input.startedAt,
|
|
@@ -5422,12 +5719,12 @@ var SchedulerDaemon = class {
|
|
|
5422
5719
|
this.lastRun = /* @__PURE__ */ new Map();
|
|
5423
5720
|
this.taskRunning = /* @__PURE__ */ new Set();
|
|
5424
5721
|
this.workspacePath = workspacePath;
|
|
5425
|
-
const configDir =
|
|
5722
|
+
const configDir = path19.join(workspacePath, ".serviceme");
|
|
5426
5723
|
this.configManager = new TaskConfigManager({
|
|
5427
|
-
configPath:
|
|
5724
|
+
configPath: path19.join(configDir, "scheduled-tasks.json")
|
|
5428
5725
|
});
|
|
5429
5726
|
this.logManager = new TaskLogManager({
|
|
5430
|
-
logPath:
|
|
5727
|
+
logPath: path19.join(configDir, "scheduled-tasks-log.json")
|
|
5431
5728
|
});
|
|
5432
5729
|
this.pidManager = new PidManager(workspacePath);
|
|
5433
5730
|
this.logger = new DaemonLogger(workspacePath);
|
|
@@ -5479,8 +5776,8 @@ var SchedulerDaemon = class {
|
|
|
5479
5776
|
const configPath = this.configManager.getConfigPath();
|
|
5480
5777
|
const dir = configPath.substring(0, configPath.lastIndexOf("/"));
|
|
5481
5778
|
try {
|
|
5482
|
-
if (
|
|
5483
|
-
this.watcher =
|
|
5779
|
+
if (fs18.existsSync(dir)) {
|
|
5780
|
+
this.watcher = fs18.watch(dir, (_eventType, filename) => {
|
|
5484
5781
|
if (filename === "scheduled-tasks.json") {
|
|
5485
5782
|
this.logger.log("info", "Config file changed, reconciling...");
|
|
5486
5783
|
}
|
|
@@ -5633,9 +5930,9 @@ function matchCronField(field, value) {
|
|
|
5633
5930
|
}
|
|
5634
5931
|
|
|
5635
5932
|
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
5636
|
-
import * as
|
|
5933
|
+
import * as fs19 from "fs";
|
|
5637
5934
|
import * as os6 from "os";
|
|
5638
|
-
import * as
|
|
5935
|
+
import * as path20 from "path";
|
|
5639
5936
|
var TICK_INTERVAL2 = 1e3;
|
|
5640
5937
|
var MIN_SCHEDULE_INTERVAL2 = 1e3;
|
|
5641
5938
|
var SCHEDULER_LOG_FILENAME2 = "scheduler.log";
|
|
@@ -5655,7 +5952,7 @@ var SchedulerDaemonV2 = class {
|
|
|
5655
5952
|
this.logManager = options.logManager ?? new TaskLogManager();
|
|
5656
5953
|
this.pidManager = options.pidManager ?? new PidManager("", { pidPath: getSchedulerPidPath() });
|
|
5657
5954
|
this.logger = options.logger ?? new DaemonLogger(os6.homedir(), {
|
|
5658
|
-
logPath:
|
|
5955
|
+
logPath: path20.join(path20.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
|
|
5659
5956
|
});
|
|
5660
5957
|
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
5661
5958
|
this.tryAcquireLock = options.tryAcquireLock ?? (() => true);
|
|
@@ -5713,7 +6010,7 @@ var SchedulerDaemonV2 = class {
|
|
|
5713
6010
|
const now = Date.now();
|
|
5714
6011
|
for (const task of config.tasks) {
|
|
5715
6012
|
if (!task.enabled) continue;
|
|
5716
|
-
if (!
|
|
6013
|
+
if (!fs19.existsSync(task.workspace.path)) {
|
|
5717
6014
|
this.disableTaskForMissingWorkspace(task, config);
|
|
5718
6015
|
continue;
|
|
5719
6016
|
}
|
|
@@ -5870,21 +6167,21 @@ function matchCronField2(field, value) {
|
|
|
5870
6167
|
}
|
|
5871
6168
|
|
|
5872
6169
|
// src/scheduled-tasks/migration/MigrateToGlobal.ts
|
|
5873
|
-
import * as
|
|
5874
|
-
import * as
|
|
6170
|
+
import * as fs20 from "fs";
|
|
6171
|
+
import * as path21 from "path";
|
|
5875
6172
|
import { isScheduledTasksConfigV1 as isScheduledTasksConfigV12, migrateV1ToV2 as migrateV1ToV22 } from "@serviceme/devtools-protocol";
|
|
5876
6173
|
var WORKSPACE_DIR = ".serviceme";
|
|
5877
6174
|
var V1_FILENAME = "scheduled-tasks.json";
|
|
5878
6175
|
function defaultProbe(workspacePath) {
|
|
5879
6176
|
return {
|
|
5880
6177
|
path: workspacePath,
|
|
5881
|
-
name:
|
|
6178
|
+
name: path21.basename(workspacePath) || workspacePath
|
|
5882
6179
|
};
|
|
5883
6180
|
}
|
|
5884
6181
|
function readV1Config(v1Path) {
|
|
5885
6182
|
let raw;
|
|
5886
6183
|
try {
|
|
5887
|
-
raw =
|
|
6184
|
+
raw = fs20.readFileSync(v1Path, "utf-8");
|
|
5888
6185
|
} catch (err) {
|
|
5889
6186
|
return {
|
|
5890
6187
|
ok: false,
|
|
@@ -5907,27 +6204,27 @@ function readV1Config(v1Path) {
|
|
|
5907
6204
|
}
|
|
5908
6205
|
function safeDelete(filePath) {
|
|
5909
6206
|
try {
|
|
5910
|
-
|
|
6207
|
+
fs20.unlinkSync(filePath);
|
|
5911
6208
|
} catch {
|
|
5912
6209
|
}
|
|
5913
6210
|
}
|
|
5914
6211
|
function ensureDir(filePath) {
|
|
5915
|
-
const dir =
|
|
5916
|
-
if (!
|
|
5917
|
-
|
|
6212
|
+
const dir = path21.dirname(filePath);
|
|
6213
|
+
if (!fs20.existsSync(dir)) {
|
|
6214
|
+
fs20.mkdirSync(dir, { recursive: true });
|
|
5918
6215
|
}
|
|
5919
6216
|
}
|
|
5920
6217
|
function readJsonFile(filePath) {
|
|
5921
|
-
if (!
|
|
6218
|
+
if (!fs20.existsSync(filePath)) return null;
|
|
5922
6219
|
try {
|
|
5923
|
-
return JSON.parse(
|
|
6220
|
+
return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
|
|
5924
6221
|
} catch {
|
|
5925
6222
|
return null;
|
|
5926
6223
|
}
|
|
5927
6224
|
}
|
|
5928
6225
|
function writeJsonFile(filePath, data) {
|
|
5929
6226
|
ensureDir(filePath);
|
|
5930
|
-
|
|
6227
|
+
fs20.writeFileSync(filePath, JSON.stringify(data, null, " "), "utf-8");
|
|
5931
6228
|
}
|
|
5932
6229
|
function disambiguateName(task, existingNames, workspaceName) {
|
|
5933
6230
|
if (!existingNames.has(task.name)) {
|
|
@@ -5957,8 +6254,8 @@ async function migrateToGlobal(options) {
|
|
|
5957
6254
|
const conflicts = [];
|
|
5958
6255
|
const issues = [];
|
|
5959
6256
|
for (const workspacePath of options.workspacePaths) {
|
|
5960
|
-
const v1Path =
|
|
5961
|
-
if (!
|
|
6257
|
+
const v1Path = path21.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
|
|
6258
|
+
if (!fs20.existsSync(v1Path)) continue;
|
|
5962
6259
|
const v1 = readV1Config(v1Path);
|
|
5963
6260
|
if (!v1.ok) {
|
|
5964
6261
|
failures.push({
|
|
@@ -6000,8 +6297,8 @@ async function migrateToGlobal(options) {
|
|
|
6000
6297
|
if (migrated > 0) {
|
|
6001
6298
|
ensureDir(globalConfigPath);
|
|
6002
6299
|
const tmp = `${globalConfigPath}.tmp`;
|
|
6003
|
-
|
|
6004
|
-
|
|
6300
|
+
fs20.writeFileSync(tmp, JSON.stringify(baseConfig, null, " "), "utf-8");
|
|
6301
|
+
fs20.renameSync(tmp, globalConfigPath);
|
|
6005
6302
|
}
|
|
6006
6303
|
if (failures.length > priorFailures.length) {
|
|
6007
6304
|
writeJsonFile(migrationFailuresPath, failures);
|
|
@@ -6018,8 +6315,8 @@ async function migrateToGlobal(options) {
|
|
|
6018
6315
|
|
|
6019
6316
|
// src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts
|
|
6020
6317
|
import { spawn as spawn5 } from "child_process";
|
|
6021
|
-
import * as
|
|
6022
|
-
import * as
|
|
6318
|
+
import * as fs21 from "fs";
|
|
6319
|
+
import * as path22 from "path";
|
|
6023
6320
|
var DEFAULT_TIMEOUT_MS5 = 2e3;
|
|
6024
6321
|
var GitTimeoutError = class extends Error {
|
|
6025
6322
|
constructor() {
|
|
@@ -6082,8 +6379,8 @@ var WorkspaceProbe = class {
|
|
|
6082
6379
|
}
|
|
6083
6380
|
}
|
|
6084
6381
|
async probe(workspacePath) {
|
|
6085
|
-
const name =
|
|
6086
|
-
if (!workspacePath || !
|
|
6382
|
+
const name = path22.basename(workspacePath) || workspacePath;
|
|
6383
|
+
if (!workspacePath || !fs21.existsSync(workspacePath)) {
|
|
6087
6384
|
return {
|
|
6088
6385
|
workspace: { path: workspacePath, name },
|
|
6089
6386
|
error: "path-not-found"
|
|
@@ -6235,8 +6532,8 @@ var SkillReconciler = class {
|
|
|
6235
6532
|
};
|
|
6236
6533
|
|
|
6237
6534
|
// src/skills/SkillStore.ts
|
|
6238
|
-
import * as
|
|
6239
|
-
import * as
|
|
6535
|
+
import * as fs22 from "fs/promises";
|
|
6536
|
+
import * as path23 from "path";
|
|
6240
6537
|
var USER_SKILL_MARKER_FILE = ".serviceme-skill.json";
|
|
6241
6538
|
var LEGACY_USER_SKILL_MARKER_FILE = ".ms-devtools-skill.json";
|
|
6242
6539
|
var WORKSPACE_SKILLS_ROOT_RELATIVE = ".github/skills";
|
|
@@ -6252,7 +6549,7 @@ var SkillStore = class {
|
|
|
6252
6549
|
constructor(options) {
|
|
6253
6550
|
this.workspacePath = options.workspacePath;
|
|
6254
6551
|
this.userSkillsRoot = options.userSkillsRoot;
|
|
6255
|
-
this.fileSystem = options.fileSystem ??
|
|
6552
|
+
this.fileSystem = options.fileSystem ?? fs22;
|
|
6256
6553
|
}
|
|
6257
6554
|
normalizeRemoteSkillId(remoteId) {
|
|
6258
6555
|
if (remoteId.startsWith("official/")) {
|
|
@@ -6271,10 +6568,10 @@ var SkillStore = class {
|
|
|
6271
6568
|
return WORKSPACE_SKILLS_MARKER_RELATIVE;
|
|
6272
6569
|
}
|
|
6273
6570
|
getUserSkillPath(skillId) {
|
|
6274
|
-
return
|
|
6571
|
+
return path23.join(this.userSkillsRoot, skillId);
|
|
6275
6572
|
}
|
|
6276
6573
|
async listWorkspaceSkillIds() {
|
|
6277
|
-
const skillsRootPath =
|
|
6574
|
+
const skillsRootPath = path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
|
|
6278
6575
|
try {
|
|
6279
6576
|
const entries = await this.fileSystem.readdir(skillsRootPath, {
|
|
6280
6577
|
withFileTypes: true
|
|
@@ -6298,7 +6595,7 @@ var SkillStore = class {
|
|
|
6298
6595
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6299
6596
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6300
6597
|
await this.fileSystem.writeFile(
|
|
6301
|
-
|
|
6598
|
+
path23.join(targetDir, USER_SKILL_MARKER_FILE),
|
|
6302
6599
|
JSON.stringify({ skillId, installedBy: "serviceme" }, null, 2),
|
|
6303
6600
|
"utf-8"
|
|
6304
6601
|
);
|
|
@@ -6307,7 +6604,7 @@ var SkillStore = class {
|
|
|
6307
6604
|
await this.migrateLegacyUserSkillMarker(skillId);
|
|
6308
6605
|
try {
|
|
6309
6606
|
const marker = await this.fileSystem.readFile(
|
|
6310
|
-
|
|
6607
|
+
path23.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
|
|
6311
6608
|
"utf-8"
|
|
6312
6609
|
);
|
|
6313
6610
|
const parsed = JSON.parse(marker);
|
|
@@ -6324,8 +6621,8 @@ var SkillStore = class {
|
|
|
6324
6621
|
*/
|
|
6325
6622
|
async migrateLegacyUserSkillMarker(skillId) {
|
|
6326
6623
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6327
|
-
const newPath =
|
|
6328
|
-
const legacyPath =
|
|
6624
|
+
const newPath = path23.join(targetDir, USER_SKILL_MARKER_FILE);
|
|
6625
|
+
const legacyPath = path23.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
|
|
6329
6626
|
try {
|
|
6330
6627
|
await this.fileSystem.readFile(newPath, "utf-8");
|
|
6331
6628
|
return;
|
|
@@ -6338,12 +6635,12 @@ var SkillStore = class {
|
|
|
6338
6635
|
}
|
|
6339
6636
|
}
|
|
6340
6637
|
async writeSkillFiles(skillId, scope, files) {
|
|
6341
|
-
const root = scope === "workspace" ?
|
|
6342
|
-
const targetDir =
|
|
6638
|
+
const root = scope === "workspace" ? path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
|
|
6639
|
+
const targetDir = path23.join(root, skillId);
|
|
6343
6640
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6344
6641
|
for (const file of files) {
|
|
6345
|
-
const filePath =
|
|
6346
|
-
await this.fileSystem.mkdir(
|
|
6642
|
+
const filePath = path23.join(targetDir, file.path);
|
|
6643
|
+
await this.fileSystem.mkdir(path23.dirname(filePath), { recursive: true });
|
|
6347
6644
|
await this.fileSystem.writeFile(filePath, file.content, "utf-8");
|
|
6348
6645
|
if (file.executable) {
|
|
6349
6646
|
try {
|
|
@@ -6356,8 +6653,8 @@ var SkillStore = class {
|
|
|
6356
6653
|
};
|
|
6357
6654
|
|
|
6358
6655
|
// src/submit/index.ts
|
|
6359
|
-
import * as
|
|
6360
|
-
import * as
|
|
6656
|
+
import * as fs23 from "fs/promises";
|
|
6657
|
+
import * as path24 from "path";
|
|
6361
6658
|
|
|
6362
6659
|
// src/submit/types.ts
|
|
6363
6660
|
var SubmitError = class extends Error {
|
|
@@ -6407,14 +6704,14 @@ var SubmitClient = class {
|
|
|
6407
6704
|
throw new SubmitError(v.reason ?? "unknown", v.detail ?? "validation denied");
|
|
6408
6705
|
}
|
|
6409
6706
|
const localRepoPath = getRepoDir(repoId);
|
|
6410
|
-
const targetDir =
|
|
6411
|
-
await
|
|
6707
|
+
const targetDir = path24.join(localRepoPath, "skills", skillName);
|
|
6708
|
+
await fs23.mkdir(targetDir, { recursive: true });
|
|
6412
6709
|
for (const f of files) {
|
|
6413
|
-
const full =
|
|
6414
|
-
await
|
|
6710
|
+
const full = path24.join(targetDir, f.path);
|
|
6711
|
+
await fs23.mkdir(path24.dirname(full), { recursive: true });
|
|
6415
6712
|
const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
|
|
6416
|
-
await
|
|
6417
|
-
await
|
|
6713
|
+
await fs23.writeFile(tmp, f.content, "utf8");
|
|
6714
|
+
await fs23.rename(tmp, full);
|
|
6418
6715
|
}
|
|
6419
6716
|
const commitMessage = `feat(skills): add ${skillName}`;
|
|
6420
6717
|
const { commitSha } = await this.git.commit(localRepoPath, commitMessage);
|
|
@@ -6483,7 +6780,7 @@ function touchLastUsedAt(tools, id, when = /* @__PURE__ */ new Date()) {
|
|
|
6483
6780
|
|
|
6484
6781
|
// src/toolbox/ToolboxStore.ts
|
|
6485
6782
|
import * as fsp2 from "fs/promises";
|
|
6486
|
-
import * as
|
|
6783
|
+
import * as path25 from "path";
|
|
6487
6784
|
import { setTimeout as delay2 } from "timers/promises";
|
|
6488
6785
|
|
|
6489
6786
|
// src/toolbox/types.ts
|
|
@@ -6518,11 +6815,11 @@ var LOCK_DIR_MODE2 = 448;
|
|
|
6518
6815
|
var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
|
|
6519
6816
|
var DEFAULT_LOCK_RETRY_MS2 = 25;
|
|
6520
6817
|
var TMP_SUFFIX2 = ".tmp";
|
|
6521
|
-
var WORKSPACE_TOOLBOX_RELATIVE_PATH =
|
|
6818
|
+
var WORKSPACE_TOOLBOX_RELATIVE_PATH = path25.join(".github", ".serviceme-toolbox.json");
|
|
6522
6819
|
var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
|
|
6523
6820
|
async function migrateLegacyWorkspaceToolboxFile(filePath) {
|
|
6524
6821
|
if (!filePath) return;
|
|
6525
|
-
const legacyPath =
|
|
6822
|
+
const legacyPath = path25.join(path25.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
|
|
6526
6823
|
if (legacyPath === filePath) return;
|
|
6527
6824
|
try {
|
|
6528
6825
|
await fsp2.access(filePath);
|
|
@@ -6567,7 +6864,7 @@ var FsToolboxFileBackend = class {
|
|
|
6567
6864
|
}
|
|
6568
6865
|
}
|
|
6569
6866
|
async write(filePath, payload) {
|
|
6570
|
-
await fsp2.mkdir(
|
|
6867
|
+
await fsp2.mkdir(path25.dirname(filePath), { recursive: true });
|
|
6571
6868
|
const tmpPath = `${filePath}${TMP_SUFFIX2}`;
|
|
6572
6869
|
const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
|
|
6573
6870
|
await fsp2.rm(tmpPath, { force: true });
|
|
@@ -6599,10 +6896,19 @@ function coercePersistedToolbox(parsed) {
|
|
|
6599
6896
|
function isNodeError2(value) {
|
|
6600
6897
|
return value instanceof Error && typeof value.code === "string";
|
|
6601
6898
|
}
|
|
6899
|
+
function isProcessAlive2(pid) {
|
|
6900
|
+
try {
|
|
6901
|
+
process.kill(pid, 0);
|
|
6902
|
+
return true;
|
|
6903
|
+
} catch {
|
|
6904
|
+
return false;
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6602
6907
|
var ToolboxFileLock = class {
|
|
6603
6908
|
constructor(filePath, timeoutMs, retryMs) {
|
|
6604
6909
|
this.acquired = false;
|
|
6605
6910
|
this.dirPath = `${filePath}.lock`;
|
|
6911
|
+
this.pidFilePath = path25.join(this.dirPath, "pid");
|
|
6606
6912
|
this.timeoutMs = timeoutMs;
|
|
6607
6913
|
this.retryMs = retryMs;
|
|
6608
6914
|
}
|
|
@@ -6611,10 +6917,16 @@ var ToolboxFileLock = class {
|
|
|
6611
6917
|
while (true) {
|
|
6612
6918
|
try {
|
|
6613
6919
|
await fsp2.mkdir(this.dirPath, { mode: LOCK_DIR_MODE2 });
|
|
6920
|
+
await fsp2.writeFile(this.pidFilePath, String(process.pid), "utf8").catch(() => void 0);
|
|
6614
6921
|
this.acquired = true;
|
|
6615
6922
|
return;
|
|
6616
6923
|
} catch (err) {
|
|
6617
6924
|
if (!isNodeError2(err) || err.code !== "EEXIST") throw err;
|
|
6925
|
+
const stale = await this.isStaleLock();
|
|
6926
|
+
if (stale) {
|
|
6927
|
+
await fsp2.rm(this.dirPath, { recursive: true, force: true });
|
|
6928
|
+
continue;
|
|
6929
|
+
}
|
|
6618
6930
|
if (Date.now() - start >= this.timeoutMs) {
|
|
6619
6931
|
throw new Error(`ToolboxStore lock acquisition timed out for ${this.dirPath}`);
|
|
6620
6932
|
}
|
|
@@ -6622,6 +6934,16 @@ var ToolboxFileLock = class {
|
|
|
6622
6934
|
}
|
|
6623
6935
|
}
|
|
6624
6936
|
}
|
|
6937
|
+
async isStaleLock() {
|
|
6938
|
+
try {
|
|
6939
|
+
const pidStr = await fsp2.readFile(this.pidFilePath, "utf8");
|
|
6940
|
+
const pid = Number.parseInt(pidStr.trim(), 10);
|
|
6941
|
+
if (!Number.isFinite(pid) || pid <= 0) return true;
|
|
6942
|
+
return !isProcessAlive2(pid);
|
|
6943
|
+
} catch {
|
|
6944
|
+
return true;
|
|
6945
|
+
}
|
|
6946
|
+
}
|
|
6625
6947
|
async release() {
|
|
6626
6948
|
if (!this.acquired) return;
|
|
6627
6949
|
this.acquired = false;
|
|
@@ -6630,7 +6952,7 @@ var ToolboxFileLock = class {
|
|
|
6630
6952
|
};
|
|
6631
6953
|
function defaultWorkspacePath() {
|
|
6632
6954
|
if (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === "1") return null;
|
|
6633
|
-
return
|
|
6955
|
+
return path25.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
|
|
6634
6956
|
}
|
|
6635
6957
|
var ToolboxStore = class {
|
|
6636
6958
|
constructor(opts = {}) {
|
|
@@ -6937,6 +7259,7 @@ export {
|
|
|
6937
7259
|
SCHEDULER_LOCK_FILENAME,
|
|
6938
7260
|
SCHEDULER_LOG_FILENAME,
|
|
6939
7261
|
SCHEDULER_PID_FILENAME,
|
|
7262
|
+
SERVER_PROXY_GLOBAL_FILENAME,
|
|
6940
7263
|
SERVICEME_DIR_NAME,
|
|
6941
7264
|
SERVICEME_HOME_ENV,
|
|
6942
7265
|
SKILL_DRAFTS_SUBDIR,
|
|
@@ -7004,6 +7327,7 @@ export {
|
|
|
7004
7327
|
getSchedulerLockPath,
|
|
7005
7328
|
getSchedulerLogPath,
|
|
7006
7329
|
getSchedulerPidPath,
|
|
7330
|
+
getServerProxyGlobalPath,
|
|
7007
7331
|
getServicemeHome,
|
|
7008
7332
|
getSkillDraftsDir,
|
|
7009
7333
|
getToolboxJsonPath,
|
|
@@ -7014,6 +7338,7 @@ export {
|
|
|
7014
7338
|
isUserRepo,
|
|
7015
7339
|
matchesCron,
|
|
7016
7340
|
mergeWithDefaults,
|
|
7341
|
+
migrateLegacyServerProxyEnabled,
|
|
7017
7342
|
migrateToGlobal,
|
|
7018
7343
|
moveFiles,
|
|
7019
7344
|
narrowRepoConfig,
|
|
@@ -7021,6 +7346,7 @@ export {
|
|
|
7021
7346
|
parseAgentToolPermissions,
|
|
7022
7347
|
parseIntervalMs,
|
|
7023
7348
|
randomInstallationId,
|
|
7349
|
+
readServerProxyGlobal,
|
|
7024
7350
|
reindexOrder,
|
|
7025
7351
|
reposFileSchema,
|
|
7026
7352
|
resetUserHomeOverrides,
|
|
@@ -7035,6 +7361,7 @@ export {
|
|
|
7035
7361
|
unzipFile,
|
|
7036
7362
|
userRepoSchema as userRepoConfigSchema,
|
|
7037
7363
|
validateReposFile,
|
|
7038
|
-
validateTaskPayload
|
|
7364
|
+
validateTaskPayload,
|
|
7365
|
+
writeServerProxyGlobal
|
|
7039
7366
|
};
|
|
7040
7367
|
//# sourceMappingURL=index.mjs.map
|