@serviceme/devtools-core 0.4.2 → 0.4.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/index.d.mts +21 -69
- package/dist/index.d.ts +21 -69
- package/dist/index.js +63 -295
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +63 -292
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -101,7 +101,6 @@ __export(src_exports, {
|
|
|
101
101
|
SERVICEME_DIR_NAME: () => SERVICEME_DIR_NAME,
|
|
102
102
|
SERVICEME_HOME_ENV: () => SERVICEME_HOME_ENV,
|
|
103
103
|
SKILL_DRAFTS_SUBDIR: () => SKILL_DRAFTS_SUBDIR,
|
|
104
|
-
SchedulerDaemon: () => SchedulerDaemon,
|
|
105
104
|
SchedulerDaemonV2: () => SchedulerDaemonV2,
|
|
106
105
|
ShellExecutor: () => ShellExecutor,
|
|
107
106
|
SkillCatalogClient: () => SkillCatalogClient,
|
|
@@ -175,7 +174,6 @@ __export(src_exports, {
|
|
|
175
174
|
isGitHubLocalEmail: () => isGitHubLocalEmail,
|
|
176
175
|
isStreamingTaskExecutor: () => isStreamingTaskExecutor,
|
|
177
176
|
isUserRepo: () => isUserRepo,
|
|
178
|
-
matchesCron: () => matchesCron,
|
|
179
177
|
mergeWithDefaults: () => mergeWithDefaults,
|
|
180
178
|
migrateLegacyServerProxyEnabled: () => migrateLegacyServerProxyEnabled,
|
|
181
179
|
migrateToGlobal: () => migrateToGlobal,
|
|
@@ -183,7 +181,6 @@ __export(src_exports, {
|
|
|
183
181
|
narrowRepoConfig: () => narrowRepoConfig,
|
|
184
182
|
noopLogger: () => noopLogger,
|
|
185
183
|
parseAgentToolPermissions: () => parseAgentToolPermissions,
|
|
186
|
-
parseIntervalMs: () => parseIntervalMs,
|
|
187
184
|
randomInstallationId: () => randomInstallationId,
|
|
188
185
|
readServerProxyGlobal: () => readServerProxyGlobal,
|
|
189
186
|
reindexOrder: () => reindexOrder,
|
|
@@ -4927,7 +4924,7 @@ var PidManager = class {
|
|
|
4927
4924
|
}
|
|
4928
4925
|
};
|
|
4929
4926
|
|
|
4930
|
-
// src/scheduled-tasks/daemon/
|
|
4927
|
+
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
4931
4928
|
var fs18 = __toESM(require("fs"));
|
|
4932
4929
|
var os5 = __toESM(require("os"));
|
|
4933
4930
|
var path19 = __toESM(require("path"));
|
|
@@ -5923,235 +5920,9 @@ var TaskLogManager = class {
|
|
|
5923
5920
|
}
|
|
5924
5921
|
};
|
|
5925
5922
|
|
|
5926
|
-
// src/scheduled-tasks/daemon/
|
|
5923
|
+
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
5927
5924
|
var TICK_INTERVAL = 1e3;
|
|
5928
5925
|
var MIN_SCHEDULE_INTERVAL = 1e3;
|
|
5929
|
-
var SchedulerDaemon = class {
|
|
5930
|
-
constructor(workspacePath, options = {}) {
|
|
5931
|
-
this.tickTimer = null;
|
|
5932
|
-
this.watcher = null;
|
|
5933
|
-
this.running = false;
|
|
5934
|
-
this.startTime = 0;
|
|
5935
|
-
// Track last execution time and running state per task
|
|
5936
|
-
this.lastRun = /* @__PURE__ */ new Map();
|
|
5937
|
-
this.taskRunning = /* @__PURE__ */ new Set();
|
|
5938
|
-
this.workspacePath = workspacePath;
|
|
5939
|
-
const configDir = path19.join(workspacePath, ".serviceme");
|
|
5940
|
-
this.configManager = new TaskConfigManager({
|
|
5941
|
-
configPath: path19.join(configDir, "scheduled-tasks.json")
|
|
5942
|
-
});
|
|
5943
|
-
this.logManager = new TaskLogManager({
|
|
5944
|
-
logPath: path19.join(configDir, "scheduled-tasks-log.json")
|
|
5945
|
-
});
|
|
5946
|
-
this.pidManager = new PidManager(workspacePath);
|
|
5947
|
-
this.logger = new DaemonLogger(workspacePath);
|
|
5948
|
-
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
5949
|
-
}
|
|
5950
|
-
start() {
|
|
5951
|
-
if (this.running) return;
|
|
5952
|
-
this.running = true;
|
|
5953
|
-
this.startTime = Date.now();
|
|
5954
|
-
this.pidManager.writePid(process.pid);
|
|
5955
|
-
const configPath = this.configManager.getConfigPath();
|
|
5956
|
-
const logPath = this.logger.getLogPath();
|
|
5957
|
-
this.logger.log("info", "=".repeat(60));
|
|
5958
|
-
this.logger.log(
|
|
5959
|
-
"info",
|
|
5960
|
-
`Scheduler daemon started
|
|
5961
|
-
PID: ${process.pid}
|
|
5962
|
-
Node: ${process.version}
|
|
5963
|
-
OS: ${os5.type()} ${os5.release()} (${process.arch})
|
|
5964
|
-
Platform: ${process.platform}
|
|
5965
|
-
Hostname: ${os5.hostname()}
|
|
5966
|
-
User: ${os5.userInfo().username}
|
|
5967
|
-
Workspace: ${this.workspacePath}
|
|
5968
|
-
Config: ${configPath}
|
|
5969
|
-
LogPath: ${logPath}
|
|
5970
|
-
ExecPath: ${process.execPath}`
|
|
5971
|
-
);
|
|
5972
|
-
this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);
|
|
5973
|
-
this.setupConfigWatch();
|
|
5974
|
-
process.on("SIGTERM", () => this.stop());
|
|
5975
|
-
process.on("SIGINT", () => this.stop());
|
|
5976
|
-
}
|
|
5977
|
-
stop() {
|
|
5978
|
-
if (!this.running) return;
|
|
5979
|
-
this.running = false;
|
|
5980
|
-
if (this.tickTimer) {
|
|
5981
|
-
clearInterval(this.tickTimer);
|
|
5982
|
-
this.tickTimer = null;
|
|
5983
|
-
}
|
|
5984
|
-
if (this.watcher) {
|
|
5985
|
-
this.watcher.close();
|
|
5986
|
-
this.watcher = null;
|
|
5987
|
-
}
|
|
5988
|
-
this.pidManager.removePid();
|
|
5989
|
-
this.logger.log("info", "Scheduler daemon stopped");
|
|
5990
|
-
process.exit(0);
|
|
5991
|
-
}
|
|
5992
|
-
setupConfigWatch() {
|
|
5993
|
-
const configPath = this.configManager.getConfigPath();
|
|
5994
|
-
const dir = configPath.substring(0, configPath.lastIndexOf("/"));
|
|
5995
|
-
try {
|
|
5996
|
-
if (fs18.existsSync(dir)) {
|
|
5997
|
-
this.watcher = fs18.watch(dir, (_eventType, filename) => {
|
|
5998
|
-
if (filename === "scheduled-tasks.json") {
|
|
5999
|
-
this.logger.log("info", "Config file changed, reconciling...");
|
|
6000
|
-
}
|
|
6001
|
-
});
|
|
6002
|
-
}
|
|
6003
|
-
} catch {
|
|
6004
|
-
this.logger.log("warn", "Could not watch config directory");
|
|
6005
|
-
}
|
|
6006
|
-
}
|
|
6007
|
-
tick() {
|
|
6008
|
-
const config = this.configManager.readConfig();
|
|
6009
|
-
const now = Date.now();
|
|
6010
|
-
for (const task of config.tasks) {
|
|
6011
|
-
if (!task.enabled) continue;
|
|
6012
|
-
if (this.taskRunning.has(task.id)) continue;
|
|
6013
|
-
let lastExec = this.lastRun.get(task.id);
|
|
6014
|
-
if (lastExec === void 0) {
|
|
6015
|
-
this.lastRun.set(task.id, now);
|
|
6016
|
-
lastExec = now;
|
|
6017
|
-
}
|
|
6018
|
-
if (this.shouldRun(task, lastExec, now)) {
|
|
6019
|
-
this.executeTask(task, now);
|
|
6020
|
-
}
|
|
6021
|
-
}
|
|
6022
|
-
}
|
|
6023
|
-
shouldRun(task, lastExec, now) {
|
|
6024
|
-
if (task.scheduleType === "interval") {
|
|
6025
|
-
const intervalMs = parseIntervalMs(task.schedule);
|
|
6026
|
-
if (intervalMs < MIN_SCHEDULE_INTERVAL) return false;
|
|
6027
|
-
return now - lastExec >= intervalMs;
|
|
6028
|
-
}
|
|
6029
|
-
if (task.scheduleType === "cron") {
|
|
6030
|
-
if (now - lastExec < 6e4) return false;
|
|
6031
|
-
return matchesCron(task.schedule, new Date(now));
|
|
6032
|
-
}
|
|
6033
|
-
return false;
|
|
6034
|
-
}
|
|
6035
|
-
async executeTask(task, now) {
|
|
6036
|
-
this.taskRunning.add(task.id);
|
|
6037
|
-
this.lastRun.set(task.id, now);
|
|
6038
|
-
const startedAt = new Date(now).toISOString();
|
|
6039
|
-
const startMs = Date.now();
|
|
6040
|
-
this.logger.log(
|
|
6041
|
-
"info",
|
|
6042
|
-
`Executing task: ${task.name} (${task.id})
|
|
6043
|
-
type: ${task.taskType}
|
|
6044
|
-
schedule: ${task.schedule} (${task.scheduleType})
|
|
6045
|
-
enabled: ${task.enabled}`
|
|
6046
|
-
);
|
|
6047
|
-
try {
|
|
6048
|
-
const executionPayload = resolveTaskExecutionPayload(
|
|
6049
|
-
task.taskType,
|
|
6050
|
-
task.payload,
|
|
6051
|
-
this.workspacePath
|
|
6052
|
-
);
|
|
6053
|
-
validateTaskPayload(task.taskType, executionPayload);
|
|
6054
|
-
const executor = this.getExecutor(task.taskType);
|
|
6055
|
-
const result = await executor.execute(executionPayload);
|
|
6056
|
-
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6057
|
-
const durationMs = Date.now() - startMs;
|
|
6058
|
-
this.logManager.appendLog({
|
|
6059
|
-
taskId: task.id,
|
|
6060
|
-
taskName: task.name,
|
|
6061
|
-
startedAt,
|
|
6062
|
-
finishedAt,
|
|
6063
|
-
status: result.status === "running" ? "failure" : result.status,
|
|
6064
|
-
output: result.output,
|
|
6065
|
-
error: result.error
|
|
6066
|
-
});
|
|
6067
|
-
const outputBytes = result.output ? Buffer.byteLength(result.output) : 0;
|
|
6068
|
-
this.logger.log(
|
|
6069
|
-
"info",
|
|
6070
|
-
`Task completed: ${task.name} (${task.id})
|
|
6071
|
-
status: ${result.status}
|
|
6072
|
-
duration: ${durationMs}ms
|
|
6073
|
-
outputBytes: ${outputBytes}`
|
|
6074
|
-
);
|
|
6075
|
-
} catch (err) {
|
|
6076
|
-
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6077
|
-
const durationMs = Date.now() - startMs;
|
|
6078
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
6079
|
-
this.logManager.appendLog({
|
|
6080
|
-
taskId: task.id,
|
|
6081
|
-
taskName: task.name,
|
|
6082
|
-
startedAt,
|
|
6083
|
-
finishedAt,
|
|
6084
|
-
status: "failure",
|
|
6085
|
-
error: message
|
|
6086
|
-
});
|
|
6087
|
-
this.logger.log(
|
|
6088
|
-
"error",
|
|
6089
|
-
`Task failed: ${task.name} (${task.id})
|
|
6090
|
-
duration: ${durationMs}ms
|
|
6091
|
-
error: ${message}`
|
|
6092
|
-
);
|
|
6093
|
-
} finally {
|
|
6094
|
-
this.taskRunning.delete(task.id);
|
|
6095
|
-
}
|
|
6096
|
-
}
|
|
6097
|
-
getStatus() {
|
|
6098
|
-
const config = this.configManager.readConfig();
|
|
6099
|
-
return {
|
|
6100
|
-
running: this.running,
|
|
6101
|
-
pid: process.pid,
|
|
6102
|
-
uptimeSeconds: this.running ? Math.floor((Date.now() - this.startTime) / 1e3) : null,
|
|
6103
|
-
tasksRegistered: config.tasks.length,
|
|
6104
|
-
tasksEnabled: config.tasks.filter((t) => t.enabled).length,
|
|
6105
|
-
workspacePath: this.workspacePath,
|
|
6106
|
-
pidFile: this.pidManager.getPidPath()
|
|
6107
|
-
};
|
|
6108
|
-
}
|
|
6109
|
-
};
|
|
6110
|
-
function parseIntervalMs(schedule) {
|
|
6111
|
-
const match = /^every\s+(\d+)\s*(s|sec|m|min|h|hr|d|day)s?$/i.exec(schedule);
|
|
6112
|
-
if (!match) return 0;
|
|
6113
|
-
const [, numStr, unit] = match;
|
|
6114
|
-
const num = Number.parseInt(numStr ?? "0", 10);
|
|
6115
|
-
switch (unit?.toLowerCase()) {
|
|
6116
|
-
case "s":
|
|
6117
|
-
case "sec":
|
|
6118
|
-
return num * 1e3;
|
|
6119
|
-
case "m":
|
|
6120
|
-
case "min":
|
|
6121
|
-
return num * 60 * 1e3;
|
|
6122
|
-
case "h":
|
|
6123
|
-
case "hr":
|
|
6124
|
-
return num * 60 * 60 * 1e3;
|
|
6125
|
-
case "d":
|
|
6126
|
-
case "day":
|
|
6127
|
-
return num * 24 * 60 * 60 * 1e3;
|
|
6128
|
-
default:
|
|
6129
|
-
return 0;
|
|
6130
|
-
}
|
|
6131
|
-
}
|
|
6132
|
-
function matchesCron(expression, date) {
|
|
6133
|
-
const parts = expression.trim().split(/\s+/);
|
|
6134
|
-
if (parts.length < 5) return false;
|
|
6135
|
-
const [minPart, hourPart, dayPart, monthPart, weekdayPart] = parts;
|
|
6136
|
-
if (!minPart || !hourPart || !dayPart || !monthPart || !weekdayPart) return false;
|
|
6137
|
-
return matchCronField(minPart, date.getMinutes()) && matchCronField(hourPart, date.getHours()) && matchCronField(dayPart, date.getDate()) && matchCronField(monthPart, date.getMonth() + 1) && matchCronField(weekdayPart, date.getDay());
|
|
6138
|
-
}
|
|
6139
|
-
function matchCronField(field, value) {
|
|
6140
|
-
if (field === "*") return true;
|
|
6141
|
-
if (field.startsWith("*/")) {
|
|
6142
|
-
const step = Number.parseInt(field.slice(2), 10);
|
|
6143
|
-
return step > 0 && value % step === 0;
|
|
6144
|
-
}
|
|
6145
|
-
const values = field.split(",");
|
|
6146
|
-
return values.some((v) => Number.parseInt(v, 10) === value);
|
|
6147
|
-
}
|
|
6148
|
-
|
|
6149
|
-
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
6150
|
-
var fs19 = __toESM(require("fs"));
|
|
6151
|
-
var os6 = __toESM(require("os"));
|
|
6152
|
-
var path20 = __toESM(require("path"));
|
|
6153
|
-
var TICK_INTERVAL2 = 1e3;
|
|
6154
|
-
var MIN_SCHEDULE_INTERVAL2 = 1e3;
|
|
6155
5926
|
var SCHEDULER_LOG_FILENAME2 = "scheduler.log";
|
|
6156
5927
|
var SchedulerDaemonV2 = class {
|
|
6157
5928
|
constructor(options = {}) {
|
|
@@ -6168,8 +5939,8 @@ var SchedulerDaemonV2 = class {
|
|
|
6168
5939
|
this.configManager = options.configManager ?? new TaskConfigManager();
|
|
6169
5940
|
this.logManager = options.logManager ?? new TaskLogManager();
|
|
6170
5941
|
this.pidManager = options.pidManager ?? new PidManager("", { pidPath: getSchedulerPidPath() });
|
|
6171
|
-
this.logger = options.logger ?? new DaemonLogger(
|
|
6172
|
-
logPath:
|
|
5942
|
+
this.logger = options.logger ?? new DaemonLogger(os5.homedir(), {
|
|
5943
|
+
logPath: path19.join(path19.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
|
|
6173
5944
|
});
|
|
6174
5945
|
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
6175
5946
|
this.tryAcquireLock = options.tryAcquireLock ?? (() => true);
|
|
@@ -6199,12 +5970,12 @@ var SchedulerDaemonV2 = class {
|
|
|
6199
5970
|
`SchedulerDaemonV2 started
|
|
6200
5971
|
PID: ${process.pid}
|
|
6201
5972
|
Node: ${process.version}
|
|
6202
|
-
OS: ${
|
|
5973
|
+
OS: ${os5.type()} ${os5.release()} (${process.arch})
|
|
6203
5974
|
Config: ${this.configManager.getConfigPath()}
|
|
6204
5975
|
LogPath: ${this.logger.getLogPath()}
|
|
6205
5976
|
PidPath: ${this.pidManager.getPidPath()}`
|
|
6206
5977
|
);
|
|
6207
|
-
this.tickTimer = setInterval(() => this.tick(),
|
|
5978
|
+
this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);
|
|
6208
5979
|
process.on("SIGTERM", this.sigtermHandler);
|
|
6209
5980
|
process.on("SIGINT", this.sigintHandler);
|
|
6210
5981
|
}
|
|
@@ -6227,7 +5998,7 @@ var SchedulerDaemonV2 = class {
|
|
|
6227
5998
|
const now = Date.now();
|
|
6228
5999
|
for (const task of config.tasks) {
|
|
6229
6000
|
if (!task.enabled) continue;
|
|
6230
|
-
if (!
|
|
6001
|
+
if (!fs18.existsSync(task.workspace.path)) {
|
|
6231
6002
|
this.disableTaskForMissingWorkspace(task, config);
|
|
6232
6003
|
continue;
|
|
6233
6004
|
}
|
|
@@ -6267,13 +6038,13 @@ var SchedulerDaemonV2 = class {
|
|
|
6267
6038
|
}
|
|
6268
6039
|
shouldRun(task, lastExec, now) {
|
|
6269
6040
|
if (task.scheduleType === "interval") {
|
|
6270
|
-
const intervalMs =
|
|
6271
|
-
if (intervalMs <
|
|
6041
|
+
const intervalMs = parseIntervalMs(task.schedule);
|
|
6042
|
+
if (intervalMs < MIN_SCHEDULE_INTERVAL) return false;
|
|
6272
6043
|
return now - lastExec >= intervalMs;
|
|
6273
6044
|
}
|
|
6274
6045
|
if (task.scheduleType === "cron") {
|
|
6275
6046
|
if (now - lastExec < 6e4) return false;
|
|
6276
|
-
return
|
|
6047
|
+
return matchesCron(task.schedule, new Date(now));
|
|
6277
6048
|
}
|
|
6278
6049
|
return false;
|
|
6279
6050
|
}
|
|
@@ -6344,7 +6115,7 @@ var SchedulerDaemonV2 = class {
|
|
|
6344
6115
|
};
|
|
6345
6116
|
}
|
|
6346
6117
|
};
|
|
6347
|
-
function
|
|
6118
|
+
function parseIntervalMs(schedule) {
|
|
6348
6119
|
const match = /^every\s+(\d+)\s*(s|sec|m|min|h|hr|d|day)s?$/i.exec(schedule);
|
|
6349
6120
|
if (!match) return 0;
|
|
6350
6121
|
const [, numStr, unit] = match;
|
|
@@ -6366,14 +6137,14 @@ function parseIntervalMs2(schedule) {
|
|
|
6366
6137
|
return 0;
|
|
6367
6138
|
}
|
|
6368
6139
|
}
|
|
6369
|
-
function
|
|
6140
|
+
function matchesCron(expression, date) {
|
|
6370
6141
|
const parts = expression.trim().split(/\s+/);
|
|
6371
6142
|
if (parts.length < 5) return false;
|
|
6372
6143
|
const [minPart, hourPart, dayPart, monthPart, weekdayPart] = parts;
|
|
6373
6144
|
if (!minPart || !hourPart || !dayPart || !monthPart || !weekdayPart) return false;
|
|
6374
|
-
return
|
|
6145
|
+
return matchCronField(minPart, date.getMinutes()) && matchCronField(hourPart, date.getHours()) && matchCronField(dayPart, date.getDate()) && matchCronField(monthPart, date.getMonth() + 1) && matchCronField(weekdayPart, date.getDay());
|
|
6375
6146
|
}
|
|
6376
|
-
function
|
|
6147
|
+
function matchCronField(field, value) {
|
|
6377
6148
|
if (field === "*") return true;
|
|
6378
6149
|
if (field.startsWith("*/")) {
|
|
6379
6150
|
const step = Number.parseInt(field.slice(2), 10);
|
|
@@ -6384,21 +6155,21 @@ function matchCronField2(field, value) {
|
|
|
6384
6155
|
}
|
|
6385
6156
|
|
|
6386
6157
|
// src/scheduled-tasks/migration/MigrateToGlobal.ts
|
|
6387
|
-
var
|
|
6388
|
-
var
|
|
6158
|
+
var fs19 = __toESM(require("fs"));
|
|
6159
|
+
var path20 = __toESM(require("path"));
|
|
6389
6160
|
var import_devtools_protocol9 = require("@serviceme/devtools-protocol");
|
|
6390
6161
|
var WORKSPACE_DIR = ".serviceme";
|
|
6391
6162
|
var V1_FILENAME = "scheduled-tasks.json";
|
|
6392
6163
|
function defaultProbe(workspacePath) {
|
|
6393
6164
|
return {
|
|
6394
6165
|
path: workspacePath,
|
|
6395
|
-
name:
|
|
6166
|
+
name: path20.basename(workspacePath) || workspacePath
|
|
6396
6167
|
};
|
|
6397
6168
|
}
|
|
6398
6169
|
function readV1Config(v1Path) {
|
|
6399
6170
|
let raw;
|
|
6400
6171
|
try {
|
|
6401
|
-
raw =
|
|
6172
|
+
raw = fs19.readFileSync(v1Path, "utf-8");
|
|
6402
6173
|
} catch (err) {
|
|
6403
6174
|
return {
|
|
6404
6175
|
ok: false,
|
|
@@ -6421,27 +6192,27 @@ function readV1Config(v1Path) {
|
|
|
6421
6192
|
}
|
|
6422
6193
|
function safeDelete(filePath) {
|
|
6423
6194
|
try {
|
|
6424
|
-
|
|
6195
|
+
fs19.unlinkSync(filePath);
|
|
6425
6196
|
} catch {
|
|
6426
6197
|
}
|
|
6427
6198
|
}
|
|
6428
6199
|
function ensureDir(filePath) {
|
|
6429
|
-
const dir =
|
|
6430
|
-
if (!
|
|
6431
|
-
|
|
6200
|
+
const dir = path20.dirname(filePath);
|
|
6201
|
+
if (!fs19.existsSync(dir)) {
|
|
6202
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
6432
6203
|
}
|
|
6433
6204
|
}
|
|
6434
6205
|
function readJsonFile(filePath) {
|
|
6435
|
-
if (!
|
|
6206
|
+
if (!fs19.existsSync(filePath)) return null;
|
|
6436
6207
|
try {
|
|
6437
|
-
return JSON.parse(
|
|
6208
|
+
return JSON.parse(fs19.readFileSync(filePath, "utf-8"));
|
|
6438
6209
|
} catch {
|
|
6439
6210
|
return null;
|
|
6440
6211
|
}
|
|
6441
6212
|
}
|
|
6442
6213
|
function writeJsonFile(filePath, data) {
|
|
6443
6214
|
ensureDir(filePath);
|
|
6444
|
-
|
|
6215
|
+
fs19.writeFileSync(filePath, JSON.stringify(data, null, " "), "utf-8");
|
|
6445
6216
|
}
|
|
6446
6217
|
function disambiguateName(task, existingNames, workspaceName) {
|
|
6447
6218
|
if (!existingNames.has(task.name)) {
|
|
@@ -6471,8 +6242,8 @@ async function migrateToGlobal(options) {
|
|
|
6471
6242
|
const conflicts = [];
|
|
6472
6243
|
const issues = [];
|
|
6473
6244
|
for (const workspacePath of options.workspacePaths) {
|
|
6474
|
-
const v1Path =
|
|
6475
|
-
if (!
|
|
6245
|
+
const v1Path = path20.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
|
|
6246
|
+
if (!fs19.existsSync(v1Path)) continue;
|
|
6476
6247
|
const v1 = readV1Config(v1Path);
|
|
6477
6248
|
if (!v1.ok) {
|
|
6478
6249
|
failures.push({
|
|
@@ -6514,8 +6285,8 @@ async function migrateToGlobal(options) {
|
|
|
6514
6285
|
if (migrated > 0) {
|
|
6515
6286
|
ensureDir(globalConfigPath);
|
|
6516
6287
|
const tmp = `${globalConfigPath}.tmp`;
|
|
6517
|
-
|
|
6518
|
-
|
|
6288
|
+
fs19.writeFileSync(tmp, JSON.stringify(baseConfig, null, " "), "utf-8");
|
|
6289
|
+
fs19.renameSync(tmp, globalConfigPath);
|
|
6519
6290
|
}
|
|
6520
6291
|
if (failures.length > priorFailures.length) {
|
|
6521
6292
|
writeJsonFile(migrationFailuresPath, failures);
|
|
@@ -6532,8 +6303,8 @@ async function migrateToGlobal(options) {
|
|
|
6532
6303
|
|
|
6533
6304
|
// src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts
|
|
6534
6305
|
var import_node_child_process5 = require("child_process");
|
|
6535
|
-
var
|
|
6536
|
-
var
|
|
6306
|
+
var fs20 = __toESM(require("fs"));
|
|
6307
|
+
var path21 = __toESM(require("path"));
|
|
6537
6308
|
var DEFAULT_TIMEOUT_MS5 = 2e3;
|
|
6538
6309
|
var GitTimeoutError = class extends Error {
|
|
6539
6310
|
constructor() {
|
|
@@ -6596,8 +6367,8 @@ var WorkspaceProbe = class {
|
|
|
6596
6367
|
}
|
|
6597
6368
|
}
|
|
6598
6369
|
async probe(workspacePath) {
|
|
6599
|
-
const name =
|
|
6600
|
-
if (!workspacePath || !
|
|
6370
|
+
const name = path21.basename(workspacePath) || workspacePath;
|
|
6371
|
+
if (!workspacePath || !fs20.existsSync(workspacePath)) {
|
|
6601
6372
|
return {
|
|
6602
6373
|
workspace: { path: workspacePath, name },
|
|
6603
6374
|
error: "path-not-found"
|
|
@@ -6749,8 +6520,8 @@ var SkillReconciler = class {
|
|
|
6749
6520
|
};
|
|
6750
6521
|
|
|
6751
6522
|
// src/skills/SkillStore.ts
|
|
6752
|
-
var
|
|
6753
|
-
var
|
|
6523
|
+
var fs21 = __toESM(require("fs/promises"));
|
|
6524
|
+
var path22 = __toESM(require("path"));
|
|
6754
6525
|
var USER_SKILL_MARKER_FILE = ".serviceme-skill.json";
|
|
6755
6526
|
var LEGACY_USER_SKILL_MARKER_FILE = ".ms-devtools-skill.json";
|
|
6756
6527
|
var WORKSPACE_SKILLS_ROOT_RELATIVE = ".github/skills";
|
|
@@ -6766,7 +6537,7 @@ var SkillStore = class {
|
|
|
6766
6537
|
constructor(options) {
|
|
6767
6538
|
this.workspacePath = options.workspacePath;
|
|
6768
6539
|
this.userSkillsRoot = options.userSkillsRoot;
|
|
6769
|
-
this.fileSystem = options.fileSystem ??
|
|
6540
|
+
this.fileSystem = options.fileSystem ?? fs21;
|
|
6770
6541
|
}
|
|
6771
6542
|
normalizeRemoteSkillId(remoteId) {
|
|
6772
6543
|
if (remoteId.startsWith("official/")) {
|
|
@@ -6785,10 +6556,10 @@ var SkillStore = class {
|
|
|
6785
6556
|
return WORKSPACE_SKILLS_MARKER_RELATIVE;
|
|
6786
6557
|
}
|
|
6787
6558
|
getUserSkillPath(skillId) {
|
|
6788
|
-
return
|
|
6559
|
+
return path22.join(this.userSkillsRoot, skillId);
|
|
6789
6560
|
}
|
|
6790
6561
|
async listWorkspaceSkillIds() {
|
|
6791
|
-
const skillsRootPath =
|
|
6562
|
+
const skillsRootPath = path22.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
|
|
6792
6563
|
try {
|
|
6793
6564
|
const entries = await this.fileSystem.readdir(skillsRootPath, {
|
|
6794
6565
|
withFileTypes: true
|
|
@@ -6812,7 +6583,7 @@ var SkillStore = class {
|
|
|
6812
6583
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6813
6584
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6814
6585
|
await this.fileSystem.writeFile(
|
|
6815
|
-
|
|
6586
|
+
path22.join(targetDir, USER_SKILL_MARKER_FILE),
|
|
6816
6587
|
JSON.stringify({ skillId, installedBy: "serviceme" }, null, 2),
|
|
6817
6588
|
"utf-8"
|
|
6818
6589
|
);
|
|
@@ -6821,7 +6592,7 @@ var SkillStore = class {
|
|
|
6821
6592
|
await this.migrateLegacyUserSkillMarker(skillId);
|
|
6822
6593
|
try {
|
|
6823
6594
|
const marker = await this.fileSystem.readFile(
|
|
6824
|
-
|
|
6595
|
+
path22.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
|
|
6825
6596
|
"utf-8"
|
|
6826
6597
|
);
|
|
6827
6598
|
const parsed = JSON.parse(marker);
|
|
@@ -6838,8 +6609,8 @@ var SkillStore = class {
|
|
|
6838
6609
|
*/
|
|
6839
6610
|
async migrateLegacyUserSkillMarker(skillId) {
|
|
6840
6611
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6841
|
-
const newPath =
|
|
6842
|
-
const legacyPath =
|
|
6612
|
+
const newPath = path22.join(targetDir, USER_SKILL_MARKER_FILE);
|
|
6613
|
+
const legacyPath = path22.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
|
|
6843
6614
|
try {
|
|
6844
6615
|
await this.fileSystem.readFile(newPath, "utf-8");
|
|
6845
6616
|
return;
|
|
@@ -6852,12 +6623,12 @@ var SkillStore = class {
|
|
|
6852
6623
|
}
|
|
6853
6624
|
}
|
|
6854
6625
|
async writeSkillFiles(skillId, scope, files) {
|
|
6855
|
-
const root = scope === "workspace" ?
|
|
6856
|
-
const targetDir =
|
|
6626
|
+
const root = scope === "workspace" ? path22.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
|
|
6627
|
+
const targetDir = path22.join(root, skillId);
|
|
6857
6628
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6858
6629
|
for (const file of files) {
|
|
6859
|
-
const filePath =
|
|
6860
|
-
await this.fileSystem.mkdir(
|
|
6630
|
+
const filePath = path22.join(targetDir, file.path);
|
|
6631
|
+
await this.fileSystem.mkdir(path22.dirname(filePath), { recursive: true });
|
|
6861
6632
|
await this.fileSystem.writeFile(filePath, file.content, "utf-8");
|
|
6862
6633
|
if (file.executable) {
|
|
6863
6634
|
try {
|
|
@@ -6870,8 +6641,8 @@ var SkillStore = class {
|
|
|
6870
6641
|
};
|
|
6871
6642
|
|
|
6872
6643
|
// src/submit/index.ts
|
|
6873
|
-
var
|
|
6874
|
-
var
|
|
6644
|
+
var fs22 = __toESM(require("fs/promises"));
|
|
6645
|
+
var path23 = __toESM(require("path"));
|
|
6875
6646
|
|
|
6876
6647
|
// src/submit/types.ts
|
|
6877
6648
|
var SubmitError = class extends Error {
|
|
@@ -6921,14 +6692,14 @@ var SubmitClient = class {
|
|
|
6921
6692
|
throw new SubmitError(v.reason ?? "unknown", v.detail ?? "validation denied");
|
|
6922
6693
|
}
|
|
6923
6694
|
const localRepoPath = getRepoDir(repoId);
|
|
6924
|
-
const targetDir =
|
|
6925
|
-
await
|
|
6695
|
+
const targetDir = path23.join(localRepoPath, "skills", skillName);
|
|
6696
|
+
await fs22.mkdir(targetDir, { recursive: true });
|
|
6926
6697
|
for (const f of files) {
|
|
6927
|
-
const full =
|
|
6928
|
-
await
|
|
6698
|
+
const full = path23.join(targetDir, f.path);
|
|
6699
|
+
await fs22.mkdir(path23.dirname(full), { recursive: true });
|
|
6929
6700
|
const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
|
|
6930
|
-
await
|
|
6931
|
-
await
|
|
6701
|
+
await fs22.writeFile(tmp, f.content, "utf8");
|
|
6702
|
+
await fs22.rename(tmp, full);
|
|
6932
6703
|
}
|
|
6933
6704
|
const commitMessage = `feat(skills): add ${skillName}`;
|
|
6934
6705
|
const { commitSha } = await this.git.commit(localRepoPath, commitMessage);
|
|
@@ -6997,7 +6768,7 @@ function touchLastUsedAt(tools, id, when = /* @__PURE__ */ new Date()) {
|
|
|
6997
6768
|
|
|
6998
6769
|
// src/toolbox/ToolboxStore.ts
|
|
6999
6770
|
var fsp2 = __toESM(require("fs/promises"));
|
|
7000
|
-
var
|
|
6771
|
+
var path24 = __toESM(require("path"));
|
|
7001
6772
|
var import_promises4 = require("timers/promises");
|
|
7002
6773
|
|
|
7003
6774
|
// src/toolbox/types.ts
|
|
@@ -7033,11 +6804,11 @@ var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
|
|
|
7033
6804
|
var DEFAULT_LOCK_RETRY_MS2 = 25;
|
|
7034
6805
|
var LOCK_STALE_GRACE_MS2 = 200;
|
|
7035
6806
|
var TMP_SUFFIX2 = ".tmp";
|
|
7036
|
-
var WORKSPACE_TOOLBOX_RELATIVE_PATH =
|
|
6807
|
+
var WORKSPACE_TOOLBOX_RELATIVE_PATH = path24.join(".github", ".serviceme-toolbox.json");
|
|
7037
6808
|
var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
|
|
7038
6809
|
async function migrateLegacyWorkspaceToolboxFile(filePath) {
|
|
7039
6810
|
if (!filePath) return;
|
|
7040
|
-
const legacyPath =
|
|
6811
|
+
const legacyPath = path24.join(path24.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
|
|
7041
6812
|
if (legacyPath === filePath) return;
|
|
7042
6813
|
try {
|
|
7043
6814
|
await fsp2.access(filePath);
|
|
@@ -7086,15 +6857,15 @@ var FsToolboxFileBackend = class {
|
|
|
7086
6857
|
}
|
|
7087
6858
|
}
|
|
7088
6859
|
async purgeExcessBackups(filePath) {
|
|
7089
|
-
const dir =
|
|
7090
|
-
const base =
|
|
6860
|
+
const dir = path24.dirname(filePath);
|
|
6861
|
+
const base = path24.basename(filePath);
|
|
7091
6862
|
let entries;
|
|
7092
6863
|
try {
|
|
7093
6864
|
entries = await fsp2.readdir(dir);
|
|
7094
6865
|
} catch {
|
|
7095
6866
|
return;
|
|
7096
6867
|
}
|
|
7097
|
-
const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath:
|
|
6868
|
+
const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path24.join(dir, n) })).sort((a, b) => {
|
|
7098
6869
|
return a.name.localeCompare(b.name);
|
|
7099
6870
|
});
|
|
7100
6871
|
const excess = backups.length - this.maxBackupCount;
|
|
@@ -7104,7 +6875,7 @@ var FsToolboxFileBackend = class {
|
|
|
7104
6875
|
);
|
|
7105
6876
|
}
|
|
7106
6877
|
async write(filePath, payload) {
|
|
7107
|
-
await fsp2.mkdir(
|
|
6878
|
+
await fsp2.mkdir(path24.dirname(filePath), { recursive: true });
|
|
7108
6879
|
const tmpPath = `${filePath}${TMP_SUFFIX2}`;
|
|
7109
6880
|
const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
|
|
7110
6881
|
await fsp2.rm(tmpPath, { force: true });
|
|
@@ -7148,7 +6919,7 @@ var ToolboxFileLock = class {
|
|
|
7148
6919
|
constructor(filePath, timeoutMs, retryMs) {
|
|
7149
6920
|
this.acquired = false;
|
|
7150
6921
|
this.dirPath = `${filePath}.lock`;
|
|
7151
|
-
this.pidFilePath =
|
|
6922
|
+
this.pidFilePath = path24.join(this.dirPath, "pid");
|
|
7152
6923
|
this.timeoutMs = timeoutMs;
|
|
7153
6924
|
this.retryMs = retryMs;
|
|
7154
6925
|
}
|
|
@@ -7198,7 +6969,7 @@ var ToolboxFileLock = class {
|
|
|
7198
6969
|
};
|
|
7199
6970
|
function defaultWorkspacePath() {
|
|
7200
6971
|
if (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === "1") return null;
|
|
7201
|
-
return
|
|
6972
|
+
return path24.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
|
|
7202
6973
|
}
|
|
7203
6974
|
var ToolboxStore = class {
|
|
7204
6975
|
constructor(opts = {}) {
|
|
@@ -7511,7 +7282,6 @@ var ToolboxCore = class {
|
|
|
7511
7282
|
SERVICEME_DIR_NAME,
|
|
7512
7283
|
SERVICEME_HOME_ENV,
|
|
7513
7284
|
SKILL_DRAFTS_SUBDIR,
|
|
7514
|
-
SchedulerDaemon,
|
|
7515
7285
|
SchedulerDaemonV2,
|
|
7516
7286
|
ShellExecutor,
|
|
7517
7287
|
SkillCatalogClient,
|
|
@@ -7585,7 +7355,6 @@ var ToolboxCore = class {
|
|
|
7585
7355
|
isGitHubLocalEmail,
|
|
7586
7356
|
isStreamingTaskExecutor,
|
|
7587
7357
|
isUserRepo,
|
|
7588
|
-
matchesCron,
|
|
7589
7358
|
mergeWithDefaults,
|
|
7590
7359
|
migrateLegacyServerProxyEnabled,
|
|
7591
7360
|
migrateToGlobal,
|
|
@@ -7593,7 +7362,6 @@ var ToolboxCore = class {
|
|
|
7593
7362
|
narrowRepoConfig,
|
|
7594
7363
|
noopLogger,
|
|
7595
7364
|
parseAgentToolPermissions,
|
|
7596
|
-
parseIntervalMs,
|
|
7597
7365
|
randomInstallationId,
|
|
7598
7366
|
readServerProxyGlobal,
|
|
7599
7367
|
reindexOrder,
|