@serviceme/devtools-core 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/device.d.mts +0 -1
- package/dist/device.d.ts +0 -1
- package/dist/device.js +0 -1
- package/dist/device.js.map +1 -1
- package/dist/device.mjs +0 -1
- package/dist/device.mjs.map +1 -1
- package/dist/index.d.mts +45 -377
- package/dist/index.d.ts +45 -377
- package/dist/index.js +110 -306
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +110 -303
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -2127,7 +2127,6 @@ var IdentityStore = class {
|
|
|
2127
2127
|
this.hooks = opts.hooks ?? {};
|
|
2128
2128
|
this.lockTimeoutMs = opts.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;
|
|
2129
2129
|
this.lockRetryMs = opts.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;
|
|
2130
|
-
this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
|
|
2131
2130
|
}
|
|
2132
2131
|
/** Absolute path to the underlying JSON file (test seam). */
|
|
2133
2132
|
getFilePath() {
|
|
@@ -3961,7 +3960,7 @@ var RepoManager = class {
|
|
|
3961
3960
|
name: input.name?.trim() || id,
|
|
3962
3961
|
url,
|
|
3963
3962
|
branch,
|
|
3964
|
-
enabled: true,
|
|
3963
|
+
enabled: input.enabled ?? true,
|
|
3965
3964
|
useProxy,
|
|
3966
3965
|
writeEnabled: false,
|
|
3967
3966
|
source: "user",
|
|
@@ -4091,7 +4090,7 @@ var DEFAULT_REPO_SEEDS = [
|
|
|
4091
4090
|
id: "composiohq-awesome-claude-skills",
|
|
4092
4091
|
name: "Composio Awesome Claude Skills",
|
|
4093
4092
|
url: "https://github.com/ComposioHQ/awesome-claude-skills.git",
|
|
4094
|
-
branch: "
|
|
4093
|
+
branch: "master",
|
|
4095
4094
|
enabled: true,
|
|
4096
4095
|
useProxy: true,
|
|
4097
4096
|
writeEnabled: false,
|
|
@@ -4124,12 +4123,13 @@ function buildDefaultReposFile(now = () => (/* @__PURE__ */ new Date()).toISOStr
|
|
|
4124
4123
|
}
|
|
4125
4124
|
const existingIds = new Set(existing.repos.map((r) => r.id));
|
|
4126
4125
|
const missingDefaults = defaults.filter((d) => !existingIds.has(d.id));
|
|
4127
|
-
|
|
4126
|
+
const { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);
|
|
4127
|
+
if (missingDefaults.length === 0 && !changed) {
|
|
4128
4128
|
return existing;
|
|
4129
4129
|
}
|
|
4130
4130
|
return {
|
|
4131
4131
|
...existing,
|
|
4132
|
-
repos: [...
|
|
4132
|
+
repos: [...refreshedRepos, ...missingDefaults],
|
|
4133
4133
|
defaultRepoId: resolveDefaultRepoId(existing, existingIds)
|
|
4134
4134
|
};
|
|
4135
4135
|
}
|
|
@@ -4137,16 +4137,52 @@ function ensureDefaultsInstalled(existing, now = () => (/* @__PURE__ */ new Date
|
|
|
4137
4137
|
const defaults = getAllDefaultRepoConfigs(now);
|
|
4138
4138
|
const existingIds = new Set(existing.repos.map((r) => r.id));
|
|
4139
4139
|
const missing = defaults.filter((d) => !existingIds.has(d.id));
|
|
4140
|
-
|
|
4140
|
+
const { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);
|
|
4141
|
+
if (missing.length === 0 && !changed) {
|
|
4141
4142
|
return { config: existing, installed: [] };
|
|
4142
4143
|
}
|
|
4143
4144
|
const next = {
|
|
4144
4145
|
...existing,
|
|
4145
|
-
repos: [...
|
|
4146
|
+
repos: [...refreshedRepos, ...missing],
|
|
4146
4147
|
defaultRepoId: resolveDefaultRepoId(existing, existingIds)
|
|
4147
4148
|
};
|
|
4148
4149
|
return { config: next, installed: missing.map((d) => d.id) };
|
|
4149
4150
|
}
|
|
4151
|
+
var SYNCED_METADATA_KEYS = [
|
|
4152
|
+
"name",
|
|
4153
|
+
"url",
|
|
4154
|
+
"branch",
|
|
4155
|
+
"description",
|
|
4156
|
+
"useProxy",
|
|
4157
|
+
"writeEnabled"
|
|
4158
|
+
];
|
|
4159
|
+
function refreshDefaultsMetadata(repos, defaults) {
|
|
4160
|
+
const seedById = new Map(defaults.map((d) => [d.id, d]));
|
|
4161
|
+
let changed = false;
|
|
4162
|
+
const next = repos.map((repo) => {
|
|
4163
|
+
if (!isDefaultRepo(repo)) {
|
|
4164
|
+
return repo;
|
|
4165
|
+
}
|
|
4166
|
+
const seed = seedById.get(repo.id);
|
|
4167
|
+
if (!seed) {
|
|
4168
|
+
return repo;
|
|
4169
|
+
}
|
|
4170
|
+
const isStale = SYNCED_METADATA_KEYS.some((key) => repo[key] !== seed[key]);
|
|
4171
|
+
if (!isStale) {
|
|
4172
|
+
return repo;
|
|
4173
|
+
}
|
|
4174
|
+
changed = true;
|
|
4175
|
+
return { ...repo, ...pick(seed, SYNCED_METADATA_KEYS) };
|
|
4176
|
+
});
|
|
4177
|
+
return { repos: next, changed };
|
|
4178
|
+
}
|
|
4179
|
+
function pick(source, keys) {
|
|
4180
|
+
const result = {};
|
|
4181
|
+
for (const key of keys) {
|
|
4182
|
+
result[key] = source[key];
|
|
4183
|
+
}
|
|
4184
|
+
return result;
|
|
4185
|
+
}
|
|
4150
4186
|
function resolveDefaultRepoId(existing, existingIds) {
|
|
4151
4187
|
const placeholder = existing.defaultRepoId;
|
|
4152
4188
|
if (placeholder && existingIds.has(placeholder)) {
|
|
@@ -4200,7 +4236,7 @@ var reposFileSchema = z.object({
|
|
|
4200
4236
|
for (const repo of value.repos) {
|
|
4201
4237
|
if (ids.has(repo.id)) {
|
|
4202
4238
|
ctx.addIssue({
|
|
4203
|
-
code:
|
|
4239
|
+
code: "custom",
|
|
4204
4240
|
path: ["repos"],
|
|
4205
4241
|
message: `Duplicate repo id: ${repo.id}`
|
|
4206
4242
|
});
|
|
@@ -4211,7 +4247,7 @@ var reposFileSchema = z.object({
|
|
|
4211
4247
|
if (value.repos.length === 0) {
|
|
4212
4248
|
if (value.defaultRepoId !== "") {
|
|
4213
4249
|
ctx.addIssue({
|
|
4214
|
-
code:
|
|
4250
|
+
code: "custom",
|
|
4215
4251
|
path: ["defaultRepoId"],
|
|
4216
4252
|
message: "defaultRepoId must be '' when repos[] is empty"
|
|
4217
4253
|
});
|
|
@@ -4220,7 +4256,7 @@ var reposFileSchema = z.object({
|
|
|
4220
4256
|
}
|
|
4221
4257
|
if (!ids.has(value.defaultRepoId)) {
|
|
4222
4258
|
ctx.addIssue({
|
|
4223
|
-
code:
|
|
4259
|
+
code: "custom",
|
|
4224
4260
|
path: ["defaultRepoId"],
|
|
4225
4261
|
message: `defaultRepoId '${value.defaultRepoId}' not present in repos[]`
|
|
4226
4262
|
});
|
|
@@ -4645,7 +4681,7 @@ var CannotRemoveDefaultRepoError = class extends Error {
|
|
|
4645
4681
|
async function bootstrapDefaults(store) {
|
|
4646
4682
|
const config = store.getConfig() ?? await store.ensureLoaded();
|
|
4647
4683
|
const result = ensureDefaultsInstalled(config, store.getNow());
|
|
4648
|
-
if (result.
|
|
4684
|
+
if (result.config === config) {
|
|
4649
4685
|
return config;
|
|
4650
4686
|
}
|
|
4651
4687
|
await store.replaceConfig(result.config);
|
|
@@ -4744,7 +4780,7 @@ var PidManager = class {
|
|
|
4744
4780
|
}
|
|
4745
4781
|
};
|
|
4746
4782
|
|
|
4747
|
-
// src/scheduled-tasks/daemon/
|
|
4783
|
+
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
4748
4784
|
import * as fs18 from "fs";
|
|
4749
4785
|
import * as os5 from "os";
|
|
4750
4786
|
import * as path19 from "path";
|
|
@@ -5746,235 +5782,9 @@ var TaskLogManager = class {
|
|
|
5746
5782
|
}
|
|
5747
5783
|
};
|
|
5748
5784
|
|
|
5749
|
-
// src/scheduled-tasks/daemon/
|
|
5785
|
+
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
5750
5786
|
var TICK_INTERVAL = 1e3;
|
|
5751
5787
|
var MIN_SCHEDULE_INTERVAL = 1e3;
|
|
5752
|
-
var SchedulerDaemon = class {
|
|
5753
|
-
constructor(workspacePath, options = {}) {
|
|
5754
|
-
this.tickTimer = null;
|
|
5755
|
-
this.watcher = null;
|
|
5756
|
-
this.running = false;
|
|
5757
|
-
this.startTime = 0;
|
|
5758
|
-
// Track last execution time and running state per task
|
|
5759
|
-
this.lastRun = /* @__PURE__ */ new Map();
|
|
5760
|
-
this.taskRunning = /* @__PURE__ */ new Set();
|
|
5761
|
-
this.workspacePath = workspacePath;
|
|
5762
|
-
const configDir = path19.join(workspacePath, ".serviceme");
|
|
5763
|
-
this.configManager = new TaskConfigManager({
|
|
5764
|
-
configPath: path19.join(configDir, "scheduled-tasks.json")
|
|
5765
|
-
});
|
|
5766
|
-
this.logManager = new TaskLogManager({
|
|
5767
|
-
logPath: path19.join(configDir, "scheduled-tasks-log.json")
|
|
5768
|
-
});
|
|
5769
|
-
this.pidManager = new PidManager(workspacePath);
|
|
5770
|
-
this.logger = new DaemonLogger(workspacePath);
|
|
5771
|
-
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
5772
|
-
}
|
|
5773
|
-
start() {
|
|
5774
|
-
if (this.running) return;
|
|
5775
|
-
this.running = true;
|
|
5776
|
-
this.startTime = Date.now();
|
|
5777
|
-
this.pidManager.writePid(process.pid);
|
|
5778
|
-
const configPath = this.configManager.getConfigPath();
|
|
5779
|
-
const logPath = this.logger.getLogPath();
|
|
5780
|
-
this.logger.log("info", "=".repeat(60));
|
|
5781
|
-
this.logger.log(
|
|
5782
|
-
"info",
|
|
5783
|
-
`Scheduler daemon started
|
|
5784
|
-
PID: ${process.pid}
|
|
5785
|
-
Node: ${process.version}
|
|
5786
|
-
OS: ${os5.type()} ${os5.release()} (${process.arch})
|
|
5787
|
-
Platform: ${process.platform}
|
|
5788
|
-
Hostname: ${os5.hostname()}
|
|
5789
|
-
User: ${os5.userInfo().username}
|
|
5790
|
-
Workspace: ${this.workspacePath}
|
|
5791
|
-
Config: ${configPath}
|
|
5792
|
-
LogPath: ${logPath}
|
|
5793
|
-
ExecPath: ${process.execPath}`
|
|
5794
|
-
);
|
|
5795
|
-
this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);
|
|
5796
|
-
this.setupConfigWatch();
|
|
5797
|
-
process.on("SIGTERM", () => this.stop());
|
|
5798
|
-
process.on("SIGINT", () => this.stop());
|
|
5799
|
-
}
|
|
5800
|
-
stop() {
|
|
5801
|
-
if (!this.running) return;
|
|
5802
|
-
this.running = false;
|
|
5803
|
-
if (this.tickTimer) {
|
|
5804
|
-
clearInterval(this.tickTimer);
|
|
5805
|
-
this.tickTimer = null;
|
|
5806
|
-
}
|
|
5807
|
-
if (this.watcher) {
|
|
5808
|
-
this.watcher.close();
|
|
5809
|
-
this.watcher = null;
|
|
5810
|
-
}
|
|
5811
|
-
this.pidManager.removePid();
|
|
5812
|
-
this.logger.log("info", "Scheduler daemon stopped");
|
|
5813
|
-
process.exit(0);
|
|
5814
|
-
}
|
|
5815
|
-
setupConfigWatch() {
|
|
5816
|
-
const configPath = this.configManager.getConfigPath();
|
|
5817
|
-
const dir = configPath.substring(0, configPath.lastIndexOf("/"));
|
|
5818
|
-
try {
|
|
5819
|
-
if (fs18.existsSync(dir)) {
|
|
5820
|
-
this.watcher = fs18.watch(dir, (_eventType, filename) => {
|
|
5821
|
-
if (filename === "scheduled-tasks.json") {
|
|
5822
|
-
this.logger.log("info", "Config file changed, reconciling...");
|
|
5823
|
-
}
|
|
5824
|
-
});
|
|
5825
|
-
}
|
|
5826
|
-
} catch {
|
|
5827
|
-
this.logger.log("warn", "Could not watch config directory");
|
|
5828
|
-
}
|
|
5829
|
-
}
|
|
5830
|
-
tick() {
|
|
5831
|
-
const config = this.configManager.readConfig();
|
|
5832
|
-
const now = Date.now();
|
|
5833
|
-
for (const task of config.tasks) {
|
|
5834
|
-
if (!task.enabled) continue;
|
|
5835
|
-
if (this.taskRunning.has(task.id)) continue;
|
|
5836
|
-
let lastExec = this.lastRun.get(task.id);
|
|
5837
|
-
if (lastExec === void 0) {
|
|
5838
|
-
this.lastRun.set(task.id, now);
|
|
5839
|
-
lastExec = now;
|
|
5840
|
-
}
|
|
5841
|
-
if (this.shouldRun(task, lastExec, now)) {
|
|
5842
|
-
this.executeTask(task, now);
|
|
5843
|
-
}
|
|
5844
|
-
}
|
|
5845
|
-
}
|
|
5846
|
-
shouldRun(task, lastExec, now) {
|
|
5847
|
-
if (task.scheduleType === "interval") {
|
|
5848
|
-
const intervalMs = parseIntervalMs(task.schedule);
|
|
5849
|
-
if (intervalMs < MIN_SCHEDULE_INTERVAL) return false;
|
|
5850
|
-
return now - lastExec >= intervalMs;
|
|
5851
|
-
}
|
|
5852
|
-
if (task.scheduleType === "cron") {
|
|
5853
|
-
if (now - lastExec < 6e4) return false;
|
|
5854
|
-
return matchesCron(task.schedule, new Date(now));
|
|
5855
|
-
}
|
|
5856
|
-
return false;
|
|
5857
|
-
}
|
|
5858
|
-
async executeTask(task, now) {
|
|
5859
|
-
this.taskRunning.add(task.id);
|
|
5860
|
-
this.lastRun.set(task.id, now);
|
|
5861
|
-
const startedAt = new Date(now).toISOString();
|
|
5862
|
-
const startMs = Date.now();
|
|
5863
|
-
this.logger.log(
|
|
5864
|
-
"info",
|
|
5865
|
-
`Executing task: ${task.name} (${task.id})
|
|
5866
|
-
type: ${task.taskType}
|
|
5867
|
-
schedule: ${task.schedule} (${task.scheduleType})
|
|
5868
|
-
enabled: ${task.enabled}`
|
|
5869
|
-
);
|
|
5870
|
-
try {
|
|
5871
|
-
const executionPayload = resolveTaskExecutionPayload(
|
|
5872
|
-
task.taskType,
|
|
5873
|
-
task.payload,
|
|
5874
|
-
this.workspacePath
|
|
5875
|
-
);
|
|
5876
|
-
validateTaskPayload(task.taskType, executionPayload);
|
|
5877
|
-
const executor = this.getExecutor(task.taskType);
|
|
5878
|
-
const result = await executor.execute(executionPayload);
|
|
5879
|
-
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5880
|
-
const durationMs = Date.now() - startMs;
|
|
5881
|
-
this.logManager.appendLog({
|
|
5882
|
-
taskId: task.id,
|
|
5883
|
-
taskName: task.name,
|
|
5884
|
-
startedAt,
|
|
5885
|
-
finishedAt,
|
|
5886
|
-
status: result.status === "running" ? "failure" : result.status,
|
|
5887
|
-
output: result.output,
|
|
5888
|
-
error: result.error
|
|
5889
|
-
});
|
|
5890
|
-
const outputBytes = result.output ? Buffer.byteLength(result.output) : 0;
|
|
5891
|
-
this.logger.log(
|
|
5892
|
-
"info",
|
|
5893
|
-
`Task completed: ${task.name} (${task.id})
|
|
5894
|
-
status: ${result.status}
|
|
5895
|
-
duration: ${durationMs}ms
|
|
5896
|
-
outputBytes: ${outputBytes}`
|
|
5897
|
-
);
|
|
5898
|
-
} catch (err) {
|
|
5899
|
-
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5900
|
-
const durationMs = Date.now() - startMs;
|
|
5901
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
5902
|
-
this.logManager.appendLog({
|
|
5903
|
-
taskId: task.id,
|
|
5904
|
-
taskName: task.name,
|
|
5905
|
-
startedAt,
|
|
5906
|
-
finishedAt,
|
|
5907
|
-
status: "failure",
|
|
5908
|
-
error: message
|
|
5909
|
-
});
|
|
5910
|
-
this.logger.log(
|
|
5911
|
-
"error",
|
|
5912
|
-
`Task failed: ${task.name} (${task.id})
|
|
5913
|
-
duration: ${durationMs}ms
|
|
5914
|
-
error: ${message}`
|
|
5915
|
-
);
|
|
5916
|
-
} finally {
|
|
5917
|
-
this.taskRunning.delete(task.id);
|
|
5918
|
-
}
|
|
5919
|
-
}
|
|
5920
|
-
getStatus() {
|
|
5921
|
-
const config = this.configManager.readConfig();
|
|
5922
|
-
return {
|
|
5923
|
-
running: this.running,
|
|
5924
|
-
pid: process.pid,
|
|
5925
|
-
uptimeSeconds: this.running ? Math.floor((Date.now() - this.startTime) / 1e3) : null,
|
|
5926
|
-
tasksRegistered: config.tasks.length,
|
|
5927
|
-
tasksEnabled: config.tasks.filter((t) => t.enabled).length,
|
|
5928
|
-
workspacePath: this.workspacePath,
|
|
5929
|
-
pidFile: this.pidManager.getPidPath()
|
|
5930
|
-
};
|
|
5931
|
-
}
|
|
5932
|
-
};
|
|
5933
|
-
function parseIntervalMs(schedule) {
|
|
5934
|
-
const match = /^every\s+(\d+)\s*(s|sec|m|min|h|hr|d|day)s?$/i.exec(schedule);
|
|
5935
|
-
if (!match) return 0;
|
|
5936
|
-
const [, numStr, unit] = match;
|
|
5937
|
-
const num = Number.parseInt(numStr ?? "0", 10);
|
|
5938
|
-
switch (unit?.toLowerCase()) {
|
|
5939
|
-
case "s":
|
|
5940
|
-
case "sec":
|
|
5941
|
-
return num * 1e3;
|
|
5942
|
-
case "m":
|
|
5943
|
-
case "min":
|
|
5944
|
-
return num * 60 * 1e3;
|
|
5945
|
-
case "h":
|
|
5946
|
-
case "hr":
|
|
5947
|
-
return num * 60 * 60 * 1e3;
|
|
5948
|
-
case "d":
|
|
5949
|
-
case "day":
|
|
5950
|
-
return num * 24 * 60 * 60 * 1e3;
|
|
5951
|
-
default:
|
|
5952
|
-
return 0;
|
|
5953
|
-
}
|
|
5954
|
-
}
|
|
5955
|
-
function matchesCron(expression, date) {
|
|
5956
|
-
const parts = expression.trim().split(/\s+/);
|
|
5957
|
-
if (parts.length < 5) return false;
|
|
5958
|
-
const [minPart, hourPart, dayPart, monthPart, weekdayPart] = parts;
|
|
5959
|
-
if (!minPart || !hourPart || !dayPart || !monthPart || !weekdayPart) return false;
|
|
5960
|
-
return matchCronField(minPart, date.getMinutes()) && matchCronField(hourPart, date.getHours()) && matchCronField(dayPart, date.getDate()) && matchCronField(monthPart, date.getMonth() + 1) && matchCronField(weekdayPart, date.getDay());
|
|
5961
|
-
}
|
|
5962
|
-
function matchCronField(field, value) {
|
|
5963
|
-
if (field === "*") return true;
|
|
5964
|
-
if (field.startsWith("*/")) {
|
|
5965
|
-
const step = Number.parseInt(field.slice(2), 10);
|
|
5966
|
-
return step > 0 && value % step === 0;
|
|
5967
|
-
}
|
|
5968
|
-
const values = field.split(",");
|
|
5969
|
-
return values.some((v) => Number.parseInt(v, 10) === value);
|
|
5970
|
-
}
|
|
5971
|
-
|
|
5972
|
-
// src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
|
|
5973
|
-
import * as fs19 from "fs";
|
|
5974
|
-
import * as os6 from "os";
|
|
5975
|
-
import * as path20 from "path";
|
|
5976
|
-
var TICK_INTERVAL2 = 1e3;
|
|
5977
|
-
var MIN_SCHEDULE_INTERVAL2 = 1e3;
|
|
5978
5788
|
var SCHEDULER_LOG_FILENAME2 = "scheduler.log";
|
|
5979
5789
|
var SchedulerDaemonV2 = class {
|
|
5980
5790
|
constructor(options = {}) {
|
|
@@ -5991,8 +5801,8 @@ var SchedulerDaemonV2 = class {
|
|
|
5991
5801
|
this.configManager = options.configManager ?? new TaskConfigManager();
|
|
5992
5802
|
this.logManager = options.logManager ?? new TaskLogManager();
|
|
5993
5803
|
this.pidManager = options.pidManager ?? new PidManager("", { pidPath: getSchedulerPidPath() });
|
|
5994
|
-
this.logger = options.logger ?? new DaemonLogger(
|
|
5995
|
-
logPath:
|
|
5804
|
+
this.logger = options.logger ?? new DaemonLogger(os5.homedir(), {
|
|
5805
|
+
logPath: path19.join(path19.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
|
|
5996
5806
|
});
|
|
5997
5807
|
this.getExecutor = options.getExecutor ?? getExecutor;
|
|
5998
5808
|
this.tryAcquireLock = options.tryAcquireLock ?? (() => true);
|
|
@@ -6022,12 +5832,12 @@ var SchedulerDaemonV2 = class {
|
|
|
6022
5832
|
`SchedulerDaemonV2 started
|
|
6023
5833
|
PID: ${process.pid}
|
|
6024
5834
|
Node: ${process.version}
|
|
6025
|
-
OS: ${
|
|
5835
|
+
OS: ${os5.type()} ${os5.release()} (${process.arch})
|
|
6026
5836
|
Config: ${this.configManager.getConfigPath()}
|
|
6027
5837
|
LogPath: ${this.logger.getLogPath()}
|
|
6028
5838
|
PidPath: ${this.pidManager.getPidPath()}`
|
|
6029
5839
|
);
|
|
6030
|
-
this.tickTimer = setInterval(() => this.tick(),
|
|
5840
|
+
this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);
|
|
6031
5841
|
process.on("SIGTERM", this.sigtermHandler);
|
|
6032
5842
|
process.on("SIGINT", this.sigintHandler);
|
|
6033
5843
|
}
|
|
@@ -6050,7 +5860,7 @@ var SchedulerDaemonV2 = class {
|
|
|
6050
5860
|
const now = Date.now();
|
|
6051
5861
|
for (const task of config.tasks) {
|
|
6052
5862
|
if (!task.enabled) continue;
|
|
6053
|
-
if (!
|
|
5863
|
+
if (!fs18.existsSync(task.workspace.path)) {
|
|
6054
5864
|
this.disableTaskForMissingWorkspace(task, config);
|
|
6055
5865
|
continue;
|
|
6056
5866
|
}
|
|
@@ -6090,13 +5900,13 @@ var SchedulerDaemonV2 = class {
|
|
|
6090
5900
|
}
|
|
6091
5901
|
shouldRun(task, lastExec, now) {
|
|
6092
5902
|
if (task.scheduleType === "interval") {
|
|
6093
|
-
const intervalMs =
|
|
6094
|
-
if (intervalMs <
|
|
5903
|
+
const intervalMs = parseIntervalMs(task.schedule);
|
|
5904
|
+
if (intervalMs < MIN_SCHEDULE_INTERVAL) return false;
|
|
6095
5905
|
return now - lastExec >= intervalMs;
|
|
6096
5906
|
}
|
|
6097
5907
|
if (task.scheduleType === "cron") {
|
|
6098
5908
|
if (now - lastExec < 6e4) return false;
|
|
6099
|
-
return
|
|
5909
|
+
return matchesCron(task.schedule, new Date(now));
|
|
6100
5910
|
}
|
|
6101
5911
|
return false;
|
|
6102
5912
|
}
|
|
@@ -6167,7 +5977,7 @@ var SchedulerDaemonV2 = class {
|
|
|
6167
5977
|
};
|
|
6168
5978
|
}
|
|
6169
5979
|
};
|
|
6170
|
-
function
|
|
5980
|
+
function parseIntervalMs(schedule) {
|
|
6171
5981
|
const match = /^every\s+(\d+)\s*(s|sec|m|min|h|hr|d|day)s?$/i.exec(schedule);
|
|
6172
5982
|
if (!match) return 0;
|
|
6173
5983
|
const [, numStr, unit] = match;
|
|
@@ -6189,14 +5999,14 @@ function parseIntervalMs2(schedule) {
|
|
|
6189
5999
|
return 0;
|
|
6190
6000
|
}
|
|
6191
6001
|
}
|
|
6192
|
-
function
|
|
6002
|
+
function matchesCron(expression, date) {
|
|
6193
6003
|
const parts = expression.trim().split(/\s+/);
|
|
6194
6004
|
if (parts.length < 5) return false;
|
|
6195
6005
|
const [minPart, hourPart, dayPart, monthPart, weekdayPart] = parts;
|
|
6196
6006
|
if (!minPart || !hourPart || !dayPart || !monthPart || !weekdayPart) return false;
|
|
6197
|
-
return
|
|
6007
|
+
return matchCronField(minPart, date.getMinutes()) && matchCronField(hourPart, date.getHours()) && matchCronField(dayPart, date.getDate()) && matchCronField(monthPart, date.getMonth() + 1) && matchCronField(weekdayPart, date.getDay());
|
|
6198
6008
|
}
|
|
6199
|
-
function
|
|
6009
|
+
function matchCronField(field, value) {
|
|
6200
6010
|
if (field === "*") return true;
|
|
6201
6011
|
if (field.startsWith("*/")) {
|
|
6202
6012
|
const step = Number.parseInt(field.slice(2), 10);
|
|
@@ -6207,21 +6017,21 @@ function matchCronField2(field, value) {
|
|
|
6207
6017
|
}
|
|
6208
6018
|
|
|
6209
6019
|
// src/scheduled-tasks/migration/MigrateToGlobal.ts
|
|
6210
|
-
import * as
|
|
6211
|
-
import * as
|
|
6020
|
+
import * as fs19 from "fs";
|
|
6021
|
+
import * as path20 from "path";
|
|
6212
6022
|
import { isScheduledTasksConfigV1 as isScheduledTasksConfigV12, migrateV1ToV2 as migrateV1ToV22 } from "@serviceme/devtools-protocol";
|
|
6213
6023
|
var WORKSPACE_DIR = ".serviceme";
|
|
6214
6024
|
var V1_FILENAME = "scheduled-tasks.json";
|
|
6215
6025
|
function defaultProbe(workspacePath) {
|
|
6216
6026
|
return {
|
|
6217
6027
|
path: workspacePath,
|
|
6218
|
-
name:
|
|
6028
|
+
name: path20.basename(workspacePath) || workspacePath
|
|
6219
6029
|
};
|
|
6220
6030
|
}
|
|
6221
6031
|
function readV1Config(v1Path) {
|
|
6222
6032
|
let raw;
|
|
6223
6033
|
try {
|
|
6224
|
-
raw =
|
|
6034
|
+
raw = fs19.readFileSync(v1Path, "utf-8");
|
|
6225
6035
|
} catch (err) {
|
|
6226
6036
|
return {
|
|
6227
6037
|
ok: false,
|
|
@@ -6244,27 +6054,27 @@ function readV1Config(v1Path) {
|
|
|
6244
6054
|
}
|
|
6245
6055
|
function safeDelete(filePath) {
|
|
6246
6056
|
try {
|
|
6247
|
-
|
|
6057
|
+
fs19.unlinkSync(filePath);
|
|
6248
6058
|
} catch {
|
|
6249
6059
|
}
|
|
6250
6060
|
}
|
|
6251
6061
|
function ensureDir(filePath) {
|
|
6252
|
-
const dir =
|
|
6253
|
-
if (!
|
|
6254
|
-
|
|
6062
|
+
const dir = path20.dirname(filePath);
|
|
6063
|
+
if (!fs19.existsSync(dir)) {
|
|
6064
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
6255
6065
|
}
|
|
6256
6066
|
}
|
|
6257
6067
|
function readJsonFile(filePath) {
|
|
6258
|
-
if (!
|
|
6068
|
+
if (!fs19.existsSync(filePath)) return null;
|
|
6259
6069
|
try {
|
|
6260
|
-
return JSON.parse(
|
|
6070
|
+
return JSON.parse(fs19.readFileSync(filePath, "utf-8"));
|
|
6261
6071
|
} catch {
|
|
6262
6072
|
return null;
|
|
6263
6073
|
}
|
|
6264
6074
|
}
|
|
6265
6075
|
function writeJsonFile(filePath, data) {
|
|
6266
6076
|
ensureDir(filePath);
|
|
6267
|
-
|
|
6077
|
+
fs19.writeFileSync(filePath, JSON.stringify(data, null, " "), "utf-8");
|
|
6268
6078
|
}
|
|
6269
6079
|
function disambiguateName(task, existingNames, workspaceName) {
|
|
6270
6080
|
if (!existingNames.has(task.name)) {
|
|
@@ -6294,8 +6104,8 @@ async function migrateToGlobal(options) {
|
|
|
6294
6104
|
const conflicts = [];
|
|
6295
6105
|
const issues = [];
|
|
6296
6106
|
for (const workspacePath of options.workspacePaths) {
|
|
6297
|
-
const v1Path =
|
|
6298
|
-
if (!
|
|
6107
|
+
const v1Path = path20.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
|
|
6108
|
+
if (!fs19.existsSync(v1Path)) continue;
|
|
6299
6109
|
const v1 = readV1Config(v1Path);
|
|
6300
6110
|
if (!v1.ok) {
|
|
6301
6111
|
failures.push({
|
|
@@ -6337,8 +6147,8 @@ async function migrateToGlobal(options) {
|
|
|
6337
6147
|
if (migrated > 0) {
|
|
6338
6148
|
ensureDir(globalConfigPath);
|
|
6339
6149
|
const tmp = `${globalConfigPath}.tmp`;
|
|
6340
|
-
|
|
6341
|
-
|
|
6150
|
+
fs19.writeFileSync(tmp, JSON.stringify(baseConfig, null, " "), "utf-8");
|
|
6151
|
+
fs19.renameSync(tmp, globalConfigPath);
|
|
6342
6152
|
}
|
|
6343
6153
|
if (failures.length > priorFailures.length) {
|
|
6344
6154
|
writeJsonFile(migrationFailuresPath, failures);
|
|
@@ -6355,8 +6165,8 @@ async function migrateToGlobal(options) {
|
|
|
6355
6165
|
|
|
6356
6166
|
// src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts
|
|
6357
6167
|
import { spawn as spawn5 } from "child_process";
|
|
6358
|
-
import * as
|
|
6359
|
-
import * as
|
|
6168
|
+
import * as fs20 from "fs";
|
|
6169
|
+
import * as path21 from "path";
|
|
6360
6170
|
var DEFAULT_TIMEOUT_MS5 = 2e3;
|
|
6361
6171
|
var GitTimeoutError = class extends Error {
|
|
6362
6172
|
constructor() {
|
|
@@ -6419,8 +6229,8 @@ var WorkspaceProbe = class {
|
|
|
6419
6229
|
}
|
|
6420
6230
|
}
|
|
6421
6231
|
async probe(workspacePath) {
|
|
6422
|
-
const name =
|
|
6423
|
-
if (!workspacePath || !
|
|
6232
|
+
const name = path21.basename(workspacePath) || workspacePath;
|
|
6233
|
+
if (!workspacePath || !fs20.existsSync(workspacePath)) {
|
|
6424
6234
|
return {
|
|
6425
6235
|
workspace: { path: workspacePath, name },
|
|
6426
6236
|
error: "path-not-found"
|
|
@@ -6572,8 +6382,8 @@ var SkillReconciler = class {
|
|
|
6572
6382
|
};
|
|
6573
6383
|
|
|
6574
6384
|
// src/skills/SkillStore.ts
|
|
6575
|
-
import * as
|
|
6576
|
-
import * as
|
|
6385
|
+
import * as fs21 from "fs/promises";
|
|
6386
|
+
import * as path22 from "path";
|
|
6577
6387
|
var USER_SKILL_MARKER_FILE = ".serviceme-skill.json";
|
|
6578
6388
|
var LEGACY_USER_SKILL_MARKER_FILE = ".ms-devtools-skill.json";
|
|
6579
6389
|
var WORKSPACE_SKILLS_ROOT_RELATIVE = ".github/skills";
|
|
@@ -6589,7 +6399,7 @@ var SkillStore = class {
|
|
|
6589
6399
|
constructor(options) {
|
|
6590
6400
|
this.workspacePath = options.workspacePath;
|
|
6591
6401
|
this.userSkillsRoot = options.userSkillsRoot;
|
|
6592
|
-
this.fileSystem = options.fileSystem ??
|
|
6402
|
+
this.fileSystem = options.fileSystem ?? fs21;
|
|
6593
6403
|
}
|
|
6594
6404
|
normalizeRemoteSkillId(remoteId) {
|
|
6595
6405
|
if (remoteId.startsWith("official/")) {
|
|
@@ -6608,10 +6418,10 @@ var SkillStore = class {
|
|
|
6608
6418
|
return WORKSPACE_SKILLS_MARKER_RELATIVE;
|
|
6609
6419
|
}
|
|
6610
6420
|
getUserSkillPath(skillId) {
|
|
6611
|
-
return
|
|
6421
|
+
return path22.join(this.userSkillsRoot, skillId);
|
|
6612
6422
|
}
|
|
6613
6423
|
async listWorkspaceSkillIds() {
|
|
6614
|
-
const skillsRootPath =
|
|
6424
|
+
const skillsRootPath = path22.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
|
|
6615
6425
|
try {
|
|
6616
6426
|
const entries = await this.fileSystem.readdir(skillsRootPath, {
|
|
6617
6427
|
withFileTypes: true
|
|
@@ -6635,7 +6445,7 @@ var SkillStore = class {
|
|
|
6635
6445
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6636
6446
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6637
6447
|
await this.fileSystem.writeFile(
|
|
6638
|
-
|
|
6448
|
+
path22.join(targetDir, USER_SKILL_MARKER_FILE),
|
|
6639
6449
|
JSON.stringify({ skillId, installedBy: "serviceme" }, null, 2),
|
|
6640
6450
|
"utf-8"
|
|
6641
6451
|
);
|
|
@@ -6644,7 +6454,7 @@ var SkillStore = class {
|
|
|
6644
6454
|
await this.migrateLegacyUserSkillMarker(skillId);
|
|
6645
6455
|
try {
|
|
6646
6456
|
const marker = await this.fileSystem.readFile(
|
|
6647
|
-
|
|
6457
|
+
path22.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
|
|
6648
6458
|
"utf-8"
|
|
6649
6459
|
);
|
|
6650
6460
|
const parsed = JSON.parse(marker);
|
|
@@ -6661,8 +6471,8 @@ var SkillStore = class {
|
|
|
6661
6471
|
*/
|
|
6662
6472
|
async migrateLegacyUserSkillMarker(skillId) {
|
|
6663
6473
|
const targetDir = this.getUserSkillPath(skillId);
|
|
6664
|
-
const newPath =
|
|
6665
|
-
const legacyPath =
|
|
6474
|
+
const newPath = path22.join(targetDir, USER_SKILL_MARKER_FILE);
|
|
6475
|
+
const legacyPath = path22.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
|
|
6666
6476
|
try {
|
|
6667
6477
|
await this.fileSystem.readFile(newPath, "utf-8");
|
|
6668
6478
|
return;
|
|
@@ -6675,12 +6485,12 @@ var SkillStore = class {
|
|
|
6675
6485
|
}
|
|
6676
6486
|
}
|
|
6677
6487
|
async writeSkillFiles(skillId, scope, files) {
|
|
6678
|
-
const root = scope === "workspace" ?
|
|
6679
|
-
const targetDir =
|
|
6488
|
+
const root = scope === "workspace" ? path22.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
|
|
6489
|
+
const targetDir = path22.join(root, skillId);
|
|
6680
6490
|
await this.fileSystem.mkdir(targetDir, { recursive: true });
|
|
6681
6491
|
for (const file of files) {
|
|
6682
|
-
const filePath =
|
|
6683
|
-
await this.fileSystem.mkdir(
|
|
6492
|
+
const filePath = path22.join(targetDir, file.path);
|
|
6493
|
+
await this.fileSystem.mkdir(path22.dirname(filePath), { recursive: true });
|
|
6684
6494
|
await this.fileSystem.writeFile(filePath, file.content, "utf-8");
|
|
6685
6495
|
if (file.executable) {
|
|
6686
6496
|
try {
|
|
@@ -6693,8 +6503,8 @@ var SkillStore = class {
|
|
|
6693
6503
|
};
|
|
6694
6504
|
|
|
6695
6505
|
// src/submit/index.ts
|
|
6696
|
-
import * as
|
|
6697
|
-
import * as
|
|
6506
|
+
import * as fs22 from "fs/promises";
|
|
6507
|
+
import * as path23 from "path";
|
|
6698
6508
|
|
|
6699
6509
|
// src/submit/types.ts
|
|
6700
6510
|
var SubmitError = class extends Error {
|
|
@@ -6744,14 +6554,14 @@ var SubmitClient = class {
|
|
|
6744
6554
|
throw new SubmitError(v.reason ?? "unknown", v.detail ?? "validation denied");
|
|
6745
6555
|
}
|
|
6746
6556
|
const localRepoPath = getRepoDir(repoId);
|
|
6747
|
-
const targetDir =
|
|
6748
|
-
await
|
|
6557
|
+
const targetDir = path23.join(localRepoPath, "skills", skillName);
|
|
6558
|
+
await fs22.mkdir(targetDir, { recursive: true });
|
|
6749
6559
|
for (const f of files) {
|
|
6750
|
-
const full =
|
|
6751
|
-
await
|
|
6560
|
+
const full = path23.join(targetDir, f.path);
|
|
6561
|
+
await fs22.mkdir(path23.dirname(full), { recursive: true });
|
|
6752
6562
|
const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
|
|
6753
|
-
await
|
|
6754
|
-
await
|
|
6563
|
+
await fs22.writeFile(tmp, f.content, "utf8");
|
|
6564
|
+
await fs22.rename(tmp, full);
|
|
6755
6565
|
}
|
|
6756
6566
|
const commitMessage = `feat(skills): add ${skillName}`;
|
|
6757
6567
|
const { commitSha } = await this.git.commit(localRepoPath, commitMessage);
|
|
@@ -6820,7 +6630,7 @@ function touchLastUsedAt(tools, id, when = /* @__PURE__ */ new Date()) {
|
|
|
6820
6630
|
|
|
6821
6631
|
// src/toolbox/ToolboxStore.ts
|
|
6822
6632
|
import * as fsp2 from "fs/promises";
|
|
6823
|
-
import * as
|
|
6633
|
+
import * as path24 from "path";
|
|
6824
6634
|
import { setTimeout as delay2 } from "timers/promises";
|
|
6825
6635
|
|
|
6826
6636
|
// src/toolbox/types.ts
|
|
@@ -6856,11 +6666,11 @@ var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
|
|
|
6856
6666
|
var DEFAULT_LOCK_RETRY_MS2 = 25;
|
|
6857
6667
|
var LOCK_STALE_GRACE_MS2 = 200;
|
|
6858
6668
|
var TMP_SUFFIX2 = ".tmp";
|
|
6859
|
-
var WORKSPACE_TOOLBOX_RELATIVE_PATH =
|
|
6669
|
+
var WORKSPACE_TOOLBOX_RELATIVE_PATH = path24.join(".github", ".serviceme-toolbox.json");
|
|
6860
6670
|
var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
|
|
6861
6671
|
async function migrateLegacyWorkspaceToolboxFile(filePath) {
|
|
6862
6672
|
if (!filePath) return;
|
|
6863
|
-
const legacyPath =
|
|
6673
|
+
const legacyPath = path24.join(path24.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
|
|
6864
6674
|
if (legacyPath === filePath) return;
|
|
6865
6675
|
try {
|
|
6866
6676
|
await fsp2.access(filePath);
|
|
@@ -6909,15 +6719,15 @@ var FsToolboxFileBackend = class {
|
|
|
6909
6719
|
}
|
|
6910
6720
|
}
|
|
6911
6721
|
async purgeExcessBackups(filePath) {
|
|
6912
|
-
const dir =
|
|
6913
|
-
const base =
|
|
6722
|
+
const dir = path24.dirname(filePath);
|
|
6723
|
+
const base = path24.basename(filePath);
|
|
6914
6724
|
let entries;
|
|
6915
6725
|
try {
|
|
6916
6726
|
entries = await fsp2.readdir(dir);
|
|
6917
6727
|
} catch {
|
|
6918
6728
|
return;
|
|
6919
6729
|
}
|
|
6920
|
-
const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath:
|
|
6730
|
+
const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path24.join(dir, n) })).sort((a, b) => {
|
|
6921
6731
|
return a.name.localeCompare(b.name);
|
|
6922
6732
|
});
|
|
6923
6733
|
const excess = backups.length - this.maxBackupCount;
|
|
@@ -6927,7 +6737,7 @@ var FsToolboxFileBackend = class {
|
|
|
6927
6737
|
);
|
|
6928
6738
|
}
|
|
6929
6739
|
async write(filePath, payload) {
|
|
6930
|
-
await fsp2.mkdir(
|
|
6740
|
+
await fsp2.mkdir(path24.dirname(filePath), { recursive: true });
|
|
6931
6741
|
const tmpPath = `${filePath}${TMP_SUFFIX2}`;
|
|
6932
6742
|
const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
|
|
6933
6743
|
await fsp2.rm(tmpPath, { force: true });
|
|
@@ -6971,7 +6781,7 @@ var ToolboxFileLock = class {
|
|
|
6971
6781
|
constructor(filePath, timeoutMs, retryMs) {
|
|
6972
6782
|
this.acquired = false;
|
|
6973
6783
|
this.dirPath = `${filePath}.lock`;
|
|
6974
|
-
this.pidFilePath =
|
|
6784
|
+
this.pidFilePath = path24.join(this.dirPath, "pid");
|
|
6975
6785
|
this.timeoutMs = timeoutMs;
|
|
6976
6786
|
this.retryMs = retryMs;
|
|
6977
6787
|
}
|
|
@@ -7021,7 +6831,7 @@ var ToolboxFileLock = class {
|
|
|
7021
6831
|
};
|
|
7022
6832
|
function defaultWorkspacePath() {
|
|
7023
6833
|
if (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === "1") return null;
|
|
7024
|
-
return
|
|
6834
|
+
return path24.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
|
|
7025
6835
|
}
|
|
7026
6836
|
var ToolboxStore = class {
|
|
7027
6837
|
constructor(opts = {}) {
|
|
@@ -7333,7 +7143,6 @@ export {
|
|
|
7333
7143
|
SERVICEME_DIR_NAME,
|
|
7334
7144
|
SERVICEME_HOME_ENV,
|
|
7335
7145
|
SKILL_DRAFTS_SUBDIR,
|
|
7336
|
-
SchedulerDaemon,
|
|
7337
7146
|
SchedulerDaemonV2,
|
|
7338
7147
|
ShellExecutor,
|
|
7339
7148
|
SkillCatalogClient,
|
|
@@ -7407,7 +7216,6 @@ export {
|
|
|
7407
7216
|
isGitHubLocalEmail,
|
|
7408
7217
|
isStreamingTaskExecutor,
|
|
7409
7218
|
isUserRepo,
|
|
7410
|
-
matchesCron,
|
|
7411
7219
|
mergeWithDefaults,
|
|
7412
7220
|
migrateLegacyServerProxyEnabled,
|
|
7413
7221
|
migrateToGlobal,
|
|
@@ -7415,7 +7223,6 @@ export {
|
|
|
7415
7223
|
narrowRepoConfig,
|
|
7416
7224
|
noopLogger,
|
|
7417
7225
|
parseAgentToolPermissions,
|
|
7418
|
-
parseIntervalMs,
|
|
7419
7226
|
randomInstallationId,
|
|
7420
7227
|
readServerProxyGlobal,
|
|
7421
7228
|
reindexOrder,
|