@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/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: "main",
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
- if (missingDefaults.length === 0) {
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: [...existing.repos, ...missingDefaults],
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
- if (missing.length === 0) {
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: [...existing.repos, ...missing],
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: z.ZodIssueCode.custom,
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: z.ZodIssueCode.custom,
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: z.ZodIssueCode.custom,
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.installed.length === 0) {
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/SchedulerDaemon.ts
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/SchedulerDaemon.ts
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(os6.homedir(), {
5995
- logPath: path20.join(path20.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
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: ${os6.type()} ${os6.release()} (${process.arch})
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(), TICK_INTERVAL2);
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 (!fs19.existsSync(task.workspace.path)) {
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 = parseIntervalMs2(task.schedule);
6094
- if (intervalMs < MIN_SCHEDULE_INTERVAL2) return false;
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 matchesCron2(task.schedule, new Date(now));
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 parseIntervalMs2(schedule) {
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 matchesCron2(expression, date) {
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 matchCronField2(minPart, date.getMinutes()) && matchCronField2(hourPart, date.getHours()) && matchCronField2(dayPart, date.getDate()) && matchCronField2(monthPart, date.getMonth() + 1) && matchCronField2(weekdayPart, date.getDay());
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 matchCronField2(field, value) {
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 fs20 from "fs";
6211
- import * as path21 from "path";
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: path21.basename(workspacePath) || workspacePath
6028
+ name: path20.basename(workspacePath) || workspacePath
6219
6029
  };
6220
6030
  }
6221
6031
  function readV1Config(v1Path) {
6222
6032
  let raw;
6223
6033
  try {
6224
- raw = fs20.readFileSync(v1Path, "utf-8");
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
- fs20.unlinkSync(filePath);
6057
+ fs19.unlinkSync(filePath);
6248
6058
  } catch {
6249
6059
  }
6250
6060
  }
6251
6061
  function ensureDir(filePath) {
6252
- const dir = path21.dirname(filePath);
6253
- if (!fs20.existsSync(dir)) {
6254
- fs20.mkdirSync(dir, { recursive: true });
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 (!fs20.existsSync(filePath)) return null;
6068
+ if (!fs19.existsSync(filePath)) return null;
6259
6069
  try {
6260
- return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
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
- fs20.writeFileSync(filePath, JSON.stringify(data, null, " "), "utf-8");
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 = path21.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
6298
- if (!fs20.existsSync(v1Path)) continue;
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
- fs20.writeFileSync(tmp, JSON.stringify(baseConfig, null, " "), "utf-8");
6341
- fs20.renameSync(tmp, globalConfigPath);
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 fs21 from "fs";
6359
- import * as path22 from "path";
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 = path22.basename(workspacePath) || workspacePath;
6423
- if (!workspacePath || !fs21.existsSync(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 fs22 from "fs/promises";
6576
- import * as path23 from "path";
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 ?? fs22;
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 path23.join(this.userSkillsRoot, skillId);
6421
+ return path22.join(this.userSkillsRoot, skillId);
6612
6422
  }
6613
6423
  async listWorkspaceSkillIds() {
6614
- const skillsRootPath = path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
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
- path23.join(targetDir, USER_SKILL_MARKER_FILE),
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
- path23.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
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 = path23.join(targetDir, USER_SKILL_MARKER_FILE);
6665
- const legacyPath = path23.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
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" ? path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
6679
- const targetDir = path23.join(root, skillId);
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 = path23.join(targetDir, file.path);
6683
- await this.fileSystem.mkdir(path23.dirname(filePath), { recursive: true });
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 fs23 from "fs/promises";
6697
- import * as path24 from "path";
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 = path24.join(localRepoPath, "skills", skillName);
6748
- await fs23.mkdir(targetDir, { recursive: true });
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 = path24.join(targetDir, f.path);
6751
- await fs23.mkdir(path24.dirname(full), { recursive: true });
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 fs23.writeFile(tmp, f.content, "utf8");
6754
- await fs23.rename(tmp, full);
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 path25 from "path";
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 = path25.join(".github", ".serviceme-toolbox.json");
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 = path25.join(path25.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
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 = path25.dirname(filePath);
6913
- const base = path25.basename(filePath);
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: path25.join(dir, n) })).sort((a, b) => {
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(path25.dirname(filePath), { recursive: true });
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 = path25.join(this.dirPath, "pid");
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 path25.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
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,