@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.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,
@@ -2320,7 +2317,6 @@ var IdentityStore = class {
2320
2317
  this.hooks = opts.hooks ?? {};
2321
2318
  this.lockTimeoutMs = opts.lockTimeoutMs ?? DEFAULT_LOCK_TIMEOUT_MS;
2322
2319
  this.lockRetryMs = opts.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;
2323
- this.now = opts.now ?? (() => /* @__PURE__ */ new Date());
2324
2320
  }
2325
2321
  /** Absolute path to the underlying JSON file (test seam). */
2326
2322
  getFilePath() {
@@ -4144,7 +4140,7 @@ var RepoManager = class {
4144
4140
  name: input.name?.trim() || id,
4145
4141
  url,
4146
4142
  branch,
4147
- enabled: true,
4143
+ enabled: input.enabled ?? true,
4148
4144
  useProxy,
4149
4145
  writeEnabled: false,
4150
4146
  source: "user",
@@ -4274,7 +4270,7 @@ var DEFAULT_REPO_SEEDS = [
4274
4270
  id: "composiohq-awesome-claude-skills",
4275
4271
  name: "Composio Awesome Claude Skills",
4276
4272
  url: "https://github.com/ComposioHQ/awesome-claude-skills.git",
4277
- branch: "main",
4273
+ branch: "master",
4278
4274
  enabled: true,
4279
4275
  useProxy: true,
4280
4276
  writeEnabled: false,
@@ -4307,12 +4303,13 @@ function buildDefaultReposFile(now = () => (/* @__PURE__ */ new Date()).toISOStr
4307
4303
  }
4308
4304
  const existingIds = new Set(existing.repos.map((r) => r.id));
4309
4305
  const missingDefaults = defaults.filter((d) => !existingIds.has(d.id));
4310
- if (missingDefaults.length === 0) {
4306
+ const { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);
4307
+ if (missingDefaults.length === 0 && !changed) {
4311
4308
  return existing;
4312
4309
  }
4313
4310
  return {
4314
4311
  ...existing,
4315
- repos: [...existing.repos, ...missingDefaults],
4312
+ repos: [...refreshedRepos, ...missingDefaults],
4316
4313
  defaultRepoId: resolveDefaultRepoId(existing, existingIds)
4317
4314
  };
4318
4315
  }
@@ -4320,16 +4317,52 @@ function ensureDefaultsInstalled(existing, now = () => (/* @__PURE__ */ new Date
4320
4317
  const defaults = getAllDefaultRepoConfigs(now);
4321
4318
  const existingIds = new Set(existing.repos.map((r) => r.id));
4322
4319
  const missing = defaults.filter((d) => !existingIds.has(d.id));
4323
- if (missing.length === 0) {
4320
+ const { repos: refreshedRepos, changed } = refreshDefaultsMetadata(existing.repos, defaults);
4321
+ if (missing.length === 0 && !changed) {
4324
4322
  return { config: existing, installed: [] };
4325
4323
  }
4326
4324
  const next = {
4327
4325
  ...existing,
4328
- repos: [...existing.repos, ...missing],
4326
+ repos: [...refreshedRepos, ...missing],
4329
4327
  defaultRepoId: resolveDefaultRepoId(existing, existingIds)
4330
4328
  };
4331
4329
  return { config: next, installed: missing.map((d) => d.id) };
4332
4330
  }
4331
+ var SYNCED_METADATA_KEYS = [
4332
+ "name",
4333
+ "url",
4334
+ "branch",
4335
+ "description",
4336
+ "useProxy",
4337
+ "writeEnabled"
4338
+ ];
4339
+ function refreshDefaultsMetadata(repos, defaults) {
4340
+ const seedById = new Map(defaults.map((d) => [d.id, d]));
4341
+ let changed = false;
4342
+ const next = repos.map((repo) => {
4343
+ if (!isDefaultRepo(repo)) {
4344
+ return repo;
4345
+ }
4346
+ const seed = seedById.get(repo.id);
4347
+ if (!seed) {
4348
+ return repo;
4349
+ }
4350
+ const isStale = SYNCED_METADATA_KEYS.some((key) => repo[key] !== seed[key]);
4351
+ if (!isStale) {
4352
+ return repo;
4353
+ }
4354
+ changed = true;
4355
+ return { ...repo, ...pick(seed, SYNCED_METADATA_KEYS) };
4356
+ });
4357
+ return { repos: next, changed };
4358
+ }
4359
+ function pick(source, keys) {
4360
+ const result = {};
4361
+ for (const key of keys) {
4362
+ result[key] = source[key];
4363
+ }
4364
+ return result;
4365
+ }
4333
4366
  function resolveDefaultRepoId(existing, existingIds) {
4334
4367
  const placeholder = existing.defaultRepoId;
4335
4368
  if (placeholder && existingIds.has(placeholder)) {
@@ -4383,7 +4416,7 @@ var reposFileSchema = import_zod.z.object({
4383
4416
  for (const repo of value.repos) {
4384
4417
  if (ids.has(repo.id)) {
4385
4418
  ctx.addIssue({
4386
- code: import_zod.z.ZodIssueCode.custom,
4419
+ code: "custom",
4387
4420
  path: ["repos"],
4388
4421
  message: `Duplicate repo id: ${repo.id}`
4389
4422
  });
@@ -4394,7 +4427,7 @@ var reposFileSchema = import_zod.z.object({
4394
4427
  if (value.repos.length === 0) {
4395
4428
  if (value.defaultRepoId !== "") {
4396
4429
  ctx.addIssue({
4397
- code: import_zod.z.ZodIssueCode.custom,
4430
+ code: "custom",
4398
4431
  path: ["defaultRepoId"],
4399
4432
  message: "defaultRepoId must be '' when repos[] is empty"
4400
4433
  });
@@ -4403,7 +4436,7 @@ var reposFileSchema = import_zod.z.object({
4403
4436
  }
4404
4437
  if (!ids.has(value.defaultRepoId)) {
4405
4438
  ctx.addIssue({
4406
- code: import_zod.z.ZodIssueCode.custom,
4439
+ code: "custom",
4407
4440
  path: ["defaultRepoId"],
4408
4441
  message: `defaultRepoId '${value.defaultRepoId}' not present in repos[]`
4409
4442
  });
@@ -4828,7 +4861,7 @@ var CannotRemoveDefaultRepoError = class extends Error {
4828
4861
  async function bootstrapDefaults(store) {
4829
4862
  const config = store.getConfig() ?? await store.ensureLoaded();
4830
4863
  const result = ensureDefaultsInstalled(config, store.getNow());
4831
- if (result.installed.length === 0) {
4864
+ if (result.config === config) {
4832
4865
  return config;
4833
4866
  }
4834
4867
  await store.replaceConfig(result.config);
@@ -4927,7 +4960,7 @@ var PidManager = class {
4927
4960
  }
4928
4961
  };
4929
4962
 
4930
- // src/scheduled-tasks/daemon/SchedulerDaemon.ts
4963
+ // src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
4931
4964
  var fs18 = __toESM(require("fs"));
4932
4965
  var os5 = __toESM(require("os"));
4933
4966
  var path19 = __toESM(require("path"));
@@ -5923,235 +5956,9 @@ var TaskLogManager = class {
5923
5956
  }
5924
5957
  };
5925
5958
 
5926
- // src/scheduled-tasks/daemon/SchedulerDaemon.ts
5959
+ // src/scheduled-tasks/daemon/SchedulerDaemonV2.ts
5927
5960
  var TICK_INTERVAL = 1e3;
5928
5961
  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
5962
  var SCHEDULER_LOG_FILENAME2 = "scheduler.log";
6156
5963
  var SchedulerDaemonV2 = class {
6157
5964
  constructor(options = {}) {
@@ -6168,8 +5975,8 @@ var SchedulerDaemonV2 = class {
6168
5975
  this.configManager = options.configManager ?? new TaskConfigManager();
6169
5976
  this.logManager = options.logManager ?? new TaskLogManager();
6170
5977
  this.pidManager = options.pidManager ?? new PidManager("", { pidPath: getSchedulerPidPath() });
6171
- this.logger = options.logger ?? new DaemonLogger(os6.homedir(), {
6172
- logPath: path20.join(path20.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
5978
+ this.logger = options.logger ?? new DaemonLogger(os5.homedir(), {
5979
+ logPath: path19.join(path19.dirname(this.pidManager.getPidPath()), SCHEDULER_LOG_FILENAME2)
6173
5980
  });
6174
5981
  this.getExecutor = options.getExecutor ?? getExecutor;
6175
5982
  this.tryAcquireLock = options.tryAcquireLock ?? (() => true);
@@ -6199,12 +6006,12 @@ var SchedulerDaemonV2 = class {
6199
6006
  `SchedulerDaemonV2 started
6200
6007
  PID: ${process.pid}
6201
6008
  Node: ${process.version}
6202
- OS: ${os6.type()} ${os6.release()} (${process.arch})
6009
+ OS: ${os5.type()} ${os5.release()} (${process.arch})
6203
6010
  Config: ${this.configManager.getConfigPath()}
6204
6011
  LogPath: ${this.logger.getLogPath()}
6205
6012
  PidPath: ${this.pidManager.getPidPath()}`
6206
6013
  );
6207
- this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL2);
6014
+ this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);
6208
6015
  process.on("SIGTERM", this.sigtermHandler);
6209
6016
  process.on("SIGINT", this.sigintHandler);
6210
6017
  }
@@ -6227,7 +6034,7 @@ var SchedulerDaemonV2 = class {
6227
6034
  const now = Date.now();
6228
6035
  for (const task of config.tasks) {
6229
6036
  if (!task.enabled) continue;
6230
- if (!fs19.existsSync(task.workspace.path)) {
6037
+ if (!fs18.existsSync(task.workspace.path)) {
6231
6038
  this.disableTaskForMissingWorkspace(task, config);
6232
6039
  continue;
6233
6040
  }
@@ -6267,13 +6074,13 @@ var SchedulerDaemonV2 = class {
6267
6074
  }
6268
6075
  shouldRun(task, lastExec, now) {
6269
6076
  if (task.scheduleType === "interval") {
6270
- const intervalMs = parseIntervalMs2(task.schedule);
6271
- if (intervalMs < MIN_SCHEDULE_INTERVAL2) return false;
6077
+ const intervalMs = parseIntervalMs(task.schedule);
6078
+ if (intervalMs < MIN_SCHEDULE_INTERVAL) return false;
6272
6079
  return now - lastExec >= intervalMs;
6273
6080
  }
6274
6081
  if (task.scheduleType === "cron") {
6275
6082
  if (now - lastExec < 6e4) return false;
6276
- return matchesCron2(task.schedule, new Date(now));
6083
+ return matchesCron(task.schedule, new Date(now));
6277
6084
  }
6278
6085
  return false;
6279
6086
  }
@@ -6344,7 +6151,7 @@ var SchedulerDaemonV2 = class {
6344
6151
  };
6345
6152
  }
6346
6153
  };
6347
- function parseIntervalMs2(schedule) {
6154
+ function parseIntervalMs(schedule) {
6348
6155
  const match = /^every\s+(\d+)\s*(s|sec|m|min|h|hr|d|day)s?$/i.exec(schedule);
6349
6156
  if (!match) return 0;
6350
6157
  const [, numStr, unit] = match;
@@ -6366,14 +6173,14 @@ function parseIntervalMs2(schedule) {
6366
6173
  return 0;
6367
6174
  }
6368
6175
  }
6369
- function matchesCron2(expression, date) {
6176
+ function matchesCron(expression, date) {
6370
6177
  const parts = expression.trim().split(/\s+/);
6371
6178
  if (parts.length < 5) return false;
6372
6179
  const [minPart, hourPart, dayPart, monthPart, weekdayPart] = parts;
6373
6180
  if (!minPart || !hourPart || !dayPart || !monthPart || !weekdayPart) return false;
6374
- return matchCronField2(minPart, date.getMinutes()) && matchCronField2(hourPart, date.getHours()) && matchCronField2(dayPart, date.getDate()) && matchCronField2(monthPart, date.getMonth() + 1) && matchCronField2(weekdayPart, date.getDay());
6181
+ return matchCronField(minPart, date.getMinutes()) && matchCronField(hourPart, date.getHours()) && matchCronField(dayPart, date.getDate()) && matchCronField(monthPart, date.getMonth() + 1) && matchCronField(weekdayPart, date.getDay());
6375
6182
  }
6376
- function matchCronField2(field, value) {
6183
+ function matchCronField(field, value) {
6377
6184
  if (field === "*") return true;
6378
6185
  if (field.startsWith("*/")) {
6379
6186
  const step = Number.parseInt(field.slice(2), 10);
@@ -6384,21 +6191,21 @@ function matchCronField2(field, value) {
6384
6191
  }
6385
6192
 
6386
6193
  // src/scheduled-tasks/migration/MigrateToGlobal.ts
6387
- var fs20 = __toESM(require("fs"));
6388
- var path21 = __toESM(require("path"));
6194
+ var fs19 = __toESM(require("fs"));
6195
+ var path20 = __toESM(require("path"));
6389
6196
  var import_devtools_protocol9 = require("@serviceme/devtools-protocol");
6390
6197
  var WORKSPACE_DIR = ".serviceme";
6391
6198
  var V1_FILENAME = "scheduled-tasks.json";
6392
6199
  function defaultProbe(workspacePath) {
6393
6200
  return {
6394
6201
  path: workspacePath,
6395
- name: path21.basename(workspacePath) || workspacePath
6202
+ name: path20.basename(workspacePath) || workspacePath
6396
6203
  };
6397
6204
  }
6398
6205
  function readV1Config(v1Path) {
6399
6206
  let raw;
6400
6207
  try {
6401
- raw = fs20.readFileSync(v1Path, "utf-8");
6208
+ raw = fs19.readFileSync(v1Path, "utf-8");
6402
6209
  } catch (err) {
6403
6210
  return {
6404
6211
  ok: false,
@@ -6421,27 +6228,27 @@ function readV1Config(v1Path) {
6421
6228
  }
6422
6229
  function safeDelete(filePath) {
6423
6230
  try {
6424
- fs20.unlinkSync(filePath);
6231
+ fs19.unlinkSync(filePath);
6425
6232
  } catch {
6426
6233
  }
6427
6234
  }
6428
6235
  function ensureDir(filePath) {
6429
- const dir = path21.dirname(filePath);
6430
- if (!fs20.existsSync(dir)) {
6431
- fs20.mkdirSync(dir, { recursive: true });
6236
+ const dir = path20.dirname(filePath);
6237
+ if (!fs19.existsSync(dir)) {
6238
+ fs19.mkdirSync(dir, { recursive: true });
6432
6239
  }
6433
6240
  }
6434
6241
  function readJsonFile(filePath) {
6435
- if (!fs20.existsSync(filePath)) return null;
6242
+ if (!fs19.existsSync(filePath)) return null;
6436
6243
  try {
6437
- return JSON.parse(fs20.readFileSync(filePath, "utf-8"));
6244
+ return JSON.parse(fs19.readFileSync(filePath, "utf-8"));
6438
6245
  } catch {
6439
6246
  return null;
6440
6247
  }
6441
6248
  }
6442
6249
  function writeJsonFile(filePath, data) {
6443
6250
  ensureDir(filePath);
6444
- fs20.writeFileSync(filePath, JSON.stringify(data, null, " "), "utf-8");
6251
+ fs19.writeFileSync(filePath, JSON.stringify(data, null, " "), "utf-8");
6445
6252
  }
6446
6253
  function disambiguateName(task, existingNames, workspaceName) {
6447
6254
  if (!existingNames.has(task.name)) {
@@ -6471,8 +6278,8 @@ async function migrateToGlobal(options) {
6471
6278
  const conflicts = [];
6472
6279
  const issues = [];
6473
6280
  for (const workspacePath of options.workspacePaths) {
6474
- const v1Path = path21.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
6475
- if (!fs20.existsSync(v1Path)) continue;
6281
+ const v1Path = path20.join(workspacePath, WORKSPACE_DIR, V1_FILENAME);
6282
+ if (!fs19.existsSync(v1Path)) continue;
6476
6283
  const v1 = readV1Config(v1Path);
6477
6284
  if (!v1.ok) {
6478
6285
  failures.push({
@@ -6514,8 +6321,8 @@ async function migrateToGlobal(options) {
6514
6321
  if (migrated > 0) {
6515
6322
  ensureDir(globalConfigPath);
6516
6323
  const tmp = `${globalConfigPath}.tmp`;
6517
- fs20.writeFileSync(tmp, JSON.stringify(baseConfig, null, " "), "utf-8");
6518
- fs20.renameSync(tmp, globalConfigPath);
6324
+ fs19.writeFileSync(tmp, JSON.stringify(baseConfig, null, " "), "utf-8");
6325
+ fs19.renameSync(tmp, globalConfigPath);
6519
6326
  }
6520
6327
  if (failures.length > priorFailures.length) {
6521
6328
  writeJsonFile(migrationFailuresPath, failures);
@@ -6532,8 +6339,8 @@ async function migrateToGlobal(options) {
6532
6339
 
6533
6340
  // src/scheduled-tasks/workspace-probe/WorkspaceProbe.ts
6534
6341
  var import_node_child_process5 = require("child_process");
6535
- var fs21 = __toESM(require("fs"));
6536
- var path22 = __toESM(require("path"));
6342
+ var fs20 = __toESM(require("fs"));
6343
+ var path21 = __toESM(require("path"));
6537
6344
  var DEFAULT_TIMEOUT_MS5 = 2e3;
6538
6345
  var GitTimeoutError = class extends Error {
6539
6346
  constructor() {
@@ -6596,8 +6403,8 @@ var WorkspaceProbe = class {
6596
6403
  }
6597
6404
  }
6598
6405
  async probe(workspacePath) {
6599
- const name = path22.basename(workspacePath) || workspacePath;
6600
- if (!workspacePath || !fs21.existsSync(workspacePath)) {
6406
+ const name = path21.basename(workspacePath) || workspacePath;
6407
+ if (!workspacePath || !fs20.existsSync(workspacePath)) {
6601
6408
  return {
6602
6409
  workspace: { path: workspacePath, name },
6603
6410
  error: "path-not-found"
@@ -6749,8 +6556,8 @@ var SkillReconciler = class {
6749
6556
  };
6750
6557
 
6751
6558
  // src/skills/SkillStore.ts
6752
- var fs22 = __toESM(require("fs/promises"));
6753
- var path23 = __toESM(require("path"));
6559
+ var fs21 = __toESM(require("fs/promises"));
6560
+ var path22 = __toESM(require("path"));
6754
6561
  var USER_SKILL_MARKER_FILE = ".serviceme-skill.json";
6755
6562
  var LEGACY_USER_SKILL_MARKER_FILE = ".ms-devtools-skill.json";
6756
6563
  var WORKSPACE_SKILLS_ROOT_RELATIVE = ".github/skills";
@@ -6766,7 +6573,7 @@ var SkillStore = class {
6766
6573
  constructor(options) {
6767
6574
  this.workspacePath = options.workspacePath;
6768
6575
  this.userSkillsRoot = options.userSkillsRoot;
6769
- this.fileSystem = options.fileSystem ?? fs22;
6576
+ this.fileSystem = options.fileSystem ?? fs21;
6770
6577
  }
6771
6578
  normalizeRemoteSkillId(remoteId) {
6772
6579
  if (remoteId.startsWith("official/")) {
@@ -6785,10 +6592,10 @@ var SkillStore = class {
6785
6592
  return WORKSPACE_SKILLS_MARKER_RELATIVE;
6786
6593
  }
6787
6594
  getUserSkillPath(skillId) {
6788
- return path23.join(this.userSkillsRoot, skillId);
6595
+ return path22.join(this.userSkillsRoot, skillId);
6789
6596
  }
6790
6597
  async listWorkspaceSkillIds() {
6791
- const skillsRootPath = path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
6598
+ const skillsRootPath = path22.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE);
6792
6599
  try {
6793
6600
  const entries = await this.fileSystem.readdir(skillsRootPath, {
6794
6601
  withFileTypes: true
@@ -6812,7 +6619,7 @@ var SkillStore = class {
6812
6619
  const targetDir = this.getUserSkillPath(skillId);
6813
6620
  await this.fileSystem.mkdir(targetDir, { recursive: true });
6814
6621
  await this.fileSystem.writeFile(
6815
- path23.join(targetDir, USER_SKILL_MARKER_FILE),
6622
+ path22.join(targetDir, USER_SKILL_MARKER_FILE),
6816
6623
  JSON.stringify({ skillId, installedBy: "serviceme" }, null, 2),
6817
6624
  "utf-8"
6818
6625
  );
@@ -6821,7 +6628,7 @@ var SkillStore = class {
6821
6628
  await this.migrateLegacyUserSkillMarker(skillId);
6822
6629
  try {
6823
6630
  const marker = await this.fileSystem.readFile(
6824
- path23.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
6631
+ path22.join(this.getUserSkillPath(skillId), USER_SKILL_MARKER_FILE),
6825
6632
  "utf-8"
6826
6633
  );
6827
6634
  const parsed = JSON.parse(marker);
@@ -6838,8 +6645,8 @@ var SkillStore = class {
6838
6645
  */
6839
6646
  async migrateLegacyUserSkillMarker(skillId) {
6840
6647
  const targetDir = this.getUserSkillPath(skillId);
6841
- const newPath = path23.join(targetDir, USER_SKILL_MARKER_FILE);
6842
- const legacyPath = path23.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
6648
+ const newPath = path22.join(targetDir, USER_SKILL_MARKER_FILE);
6649
+ const legacyPath = path22.join(targetDir, LEGACY_USER_SKILL_MARKER_FILE);
6843
6650
  try {
6844
6651
  await this.fileSystem.readFile(newPath, "utf-8");
6845
6652
  return;
@@ -6852,12 +6659,12 @@ var SkillStore = class {
6852
6659
  }
6853
6660
  }
6854
6661
  async writeSkillFiles(skillId, scope, files) {
6855
- const root = scope === "workspace" ? path23.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
6856
- const targetDir = path23.join(root, skillId);
6662
+ const root = scope === "workspace" ? path22.join(this.workspacePath, WORKSPACE_SKILLS_ROOT_RELATIVE) : this.userSkillsRoot;
6663
+ const targetDir = path22.join(root, skillId);
6857
6664
  await this.fileSystem.mkdir(targetDir, { recursive: true });
6858
6665
  for (const file of files) {
6859
- const filePath = path23.join(targetDir, file.path);
6860
- await this.fileSystem.mkdir(path23.dirname(filePath), { recursive: true });
6666
+ const filePath = path22.join(targetDir, file.path);
6667
+ await this.fileSystem.mkdir(path22.dirname(filePath), { recursive: true });
6861
6668
  await this.fileSystem.writeFile(filePath, file.content, "utf-8");
6862
6669
  if (file.executable) {
6863
6670
  try {
@@ -6870,8 +6677,8 @@ var SkillStore = class {
6870
6677
  };
6871
6678
 
6872
6679
  // src/submit/index.ts
6873
- var fs23 = __toESM(require("fs/promises"));
6874
- var path24 = __toESM(require("path"));
6680
+ var fs22 = __toESM(require("fs/promises"));
6681
+ var path23 = __toESM(require("path"));
6875
6682
 
6876
6683
  // src/submit/types.ts
6877
6684
  var SubmitError = class extends Error {
@@ -6921,14 +6728,14 @@ var SubmitClient = class {
6921
6728
  throw new SubmitError(v.reason ?? "unknown", v.detail ?? "validation denied");
6922
6729
  }
6923
6730
  const localRepoPath = getRepoDir(repoId);
6924
- const targetDir = path24.join(localRepoPath, "skills", skillName);
6925
- await fs23.mkdir(targetDir, { recursive: true });
6731
+ const targetDir = path23.join(localRepoPath, "skills", skillName);
6732
+ await fs22.mkdir(targetDir, { recursive: true });
6926
6733
  for (const f of files) {
6927
- const full = path24.join(targetDir, f.path);
6928
- await fs23.mkdir(path24.dirname(full), { recursive: true });
6734
+ const full = path23.join(targetDir, f.path);
6735
+ await fs22.mkdir(path23.dirname(full), { recursive: true });
6929
6736
  const tmp = `${full}.${process.pid}.${Date.now()}.tmp`;
6930
- await fs23.writeFile(tmp, f.content, "utf8");
6931
- await fs23.rename(tmp, full);
6737
+ await fs22.writeFile(tmp, f.content, "utf8");
6738
+ await fs22.rename(tmp, full);
6932
6739
  }
6933
6740
  const commitMessage = `feat(skills): add ${skillName}`;
6934
6741
  const { commitSha } = await this.git.commit(localRepoPath, commitMessage);
@@ -6997,7 +6804,7 @@ function touchLastUsedAt(tools, id, when = /* @__PURE__ */ new Date()) {
6997
6804
 
6998
6805
  // src/toolbox/ToolboxStore.ts
6999
6806
  var fsp2 = __toESM(require("fs/promises"));
7000
- var path25 = __toESM(require("path"));
6807
+ var path24 = __toESM(require("path"));
7001
6808
  var import_promises4 = require("timers/promises");
7002
6809
 
7003
6810
  // src/toolbox/types.ts
@@ -7033,11 +6840,11 @@ var DEFAULT_LOCK_TIMEOUT_MS2 = 5e3;
7033
6840
  var DEFAULT_LOCK_RETRY_MS2 = 25;
7034
6841
  var LOCK_STALE_GRACE_MS2 = 200;
7035
6842
  var TMP_SUFFIX2 = ".tmp";
7036
- var WORKSPACE_TOOLBOX_RELATIVE_PATH = path25.join(".github", ".serviceme-toolbox.json");
6843
+ var WORKSPACE_TOOLBOX_RELATIVE_PATH = path24.join(".github", ".serviceme-toolbox.json");
7037
6844
  var LEGACY_WORKSPACE_TOOLBOX_FILENAME = ".ms-devtools-toolbox.json";
7038
6845
  async function migrateLegacyWorkspaceToolboxFile(filePath) {
7039
6846
  if (!filePath) return;
7040
- const legacyPath = path25.join(path25.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
6847
+ const legacyPath = path24.join(path24.dirname(filePath), LEGACY_WORKSPACE_TOOLBOX_FILENAME);
7041
6848
  if (legacyPath === filePath) return;
7042
6849
  try {
7043
6850
  await fsp2.access(filePath);
@@ -7086,15 +6893,15 @@ var FsToolboxFileBackend = class {
7086
6893
  }
7087
6894
  }
7088
6895
  async purgeExcessBackups(filePath) {
7089
- const dir = path25.dirname(filePath);
7090
- const base = path25.basename(filePath);
6896
+ const dir = path24.dirname(filePath);
6897
+ const base = path24.basename(filePath);
7091
6898
  let entries;
7092
6899
  try {
7093
6900
  entries = await fsp2.readdir(dir);
7094
6901
  } catch {
7095
6902
  return;
7096
6903
  }
7097
- const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path25.join(dir, n) })).sort((a, b) => {
6904
+ const backups = entries.filter((n) => n.startsWith(base) && n.endsWith(".bak")).map((n) => ({ name: n, filePath: path24.join(dir, n) })).sort((a, b) => {
7098
6905
  return a.name.localeCompare(b.name);
7099
6906
  });
7100
6907
  const excess = backups.length - this.maxBackupCount;
@@ -7104,7 +6911,7 @@ var FsToolboxFileBackend = class {
7104
6911
  );
7105
6912
  }
7106
6913
  async write(filePath, payload) {
7107
- await fsp2.mkdir(path25.dirname(filePath), { recursive: true });
6914
+ await fsp2.mkdir(path24.dirname(filePath), { recursive: true });
7108
6915
  const tmpPath = `${filePath}${TMP_SUFFIX2}`;
7109
6916
  const bytes = Buffer.from(JSON.stringify(payload, null, " "), "utf8");
7110
6917
  await fsp2.rm(tmpPath, { force: true });
@@ -7148,7 +6955,7 @@ var ToolboxFileLock = class {
7148
6955
  constructor(filePath, timeoutMs, retryMs) {
7149
6956
  this.acquired = false;
7150
6957
  this.dirPath = `${filePath}.lock`;
7151
- this.pidFilePath = path25.join(this.dirPath, "pid");
6958
+ this.pidFilePath = path24.join(this.dirPath, "pid");
7152
6959
  this.timeoutMs = timeoutMs;
7153
6960
  this.retryMs = retryMs;
7154
6961
  }
@@ -7198,7 +7005,7 @@ var ToolboxFileLock = class {
7198
7005
  };
7199
7006
  function defaultWorkspacePath() {
7200
7007
  if (process.env.SERVICEME_NO_WORKSPACE_TOOLBOX === "1") return null;
7201
- return path25.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
7008
+ return path24.join(process.cwd(), WORKSPACE_TOOLBOX_RELATIVE_PATH);
7202
7009
  }
7203
7010
  var ToolboxStore = class {
7204
7011
  constructor(opts = {}) {
@@ -7511,7 +7318,6 @@ var ToolboxCore = class {
7511
7318
  SERVICEME_DIR_NAME,
7512
7319
  SERVICEME_HOME_ENV,
7513
7320
  SKILL_DRAFTS_SUBDIR,
7514
- SchedulerDaemon,
7515
7321
  SchedulerDaemonV2,
7516
7322
  ShellExecutor,
7517
7323
  SkillCatalogClient,
@@ -7585,7 +7391,6 @@ var ToolboxCore = class {
7585
7391
  isGitHubLocalEmail,
7586
7392
  isStreamingTaskExecutor,
7587
7393
  isUserRepo,
7588
- matchesCron,
7589
7394
  mergeWithDefaults,
7590
7395
  migrateLegacyServerProxyEnabled,
7591
7396
  migrateToGlobal,
@@ -7593,7 +7398,6 @@ var ToolboxCore = class {
7593
7398
  narrowRepoConfig,
7594
7399
  noopLogger,
7595
7400
  parseAgentToolPermissions,
7596
- parseIntervalMs,
7597
7401
  randomInstallationId,
7598
7402
  readServerProxyGlobal,
7599
7403
  reindexOrder,