@serviceme/devtools-cli 0.1.3 → 0.1.5

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/cli.js CHANGED
@@ -4,8 +4,9 @@
4
4
  var child_process = require('child_process');
5
5
  var fs2 = require('fs/promises');
6
6
  var path = require('path');
7
- var fs6 = require('fs');
7
+ var fs8 = require('fs');
8
8
  var crypto = require('crypto');
9
+ var os = require('os');
9
10
  var readline = require('readline');
10
11
 
11
12
  function _interopNamespace(e) {
@@ -28,7 +29,8 @@ function _interopNamespace(e) {
28
29
 
29
30
  var fs2__namespace = /*#__PURE__*/_interopNamespace(fs2);
30
31
  var path__namespace = /*#__PURE__*/_interopNamespace(path);
31
- var fs6__namespace = /*#__PURE__*/_interopNamespace(fs6);
32
+ var fs8__namespace = /*#__PURE__*/_interopNamespace(fs8);
33
+ var os__namespace = /*#__PURE__*/_interopNamespace(os);
32
34
  var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
33
35
 
34
36
  var __create = Object.create;
@@ -157,6 +159,7 @@ var init_env = __esm({
157
159
  "pnpm",
158
160
  "nvm",
159
161
  "nrm",
162
+ "rtk",
160
163
  "dotnet",
161
164
  "nuget"
162
165
  ];
@@ -360,7 +363,8 @@ async function runCommand(command, options2 = {}) {
360
363
  cwd: options2.cwd,
361
364
  env: options2.env,
362
365
  shell: options2.shell,
363
- stdio: "pipe"
366
+ stdio: "pipe",
367
+ windowsHide: true
364
368
  });
365
369
  let stdout = "";
366
370
  let stderr = "";
@@ -582,13 +586,19 @@ var init_copilot2 = __esm({
582
586
  });
583
587
 
584
588
  // ../../packages/serviceme-core/src/env/environmentInspector.ts
585
- var TOOL_CHECK_TIMEOUT_MS, ERROR_CODE_NOT_FOUND, EnvironmentInspector;
589
+ var DEFAULT_TOOL_CHECK_TIMEOUT_MS, TOOL_CHECK_TIMEOUT_MS, ERROR_CODE_NOT_FOUND, ERROR_CODE_TIMEOUT, EnvironmentInspector;
586
590
  var init_environmentInspector = __esm({
587
591
  "../../packages/serviceme-core/src/env/environmentInspector.ts"() {
588
592
  init_src();
589
593
  init_runCommand();
590
- TOOL_CHECK_TIMEOUT_MS = 5e3;
594
+ DEFAULT_TOOL_CHECK_TIMEOUT_MS = 5e3;
595
+ TOOL_CHECK_TIMEOUT_MS = {
596
+ nuget: 12e3,
597
+ nvm: 8e3,
598
+ dotnet: 8e3
599
+ };
591
600
  ERROR_CODE_NOT_FOUND = 127;
601
+ ERROR_CODE_TIMEOUT = "ETIMEDOUT";
592
602
  EnvironmentInspector = class {
593
603
  async checkEnvironment() {
594
604
  const results = await Promise.all(
@@ -625,19 +635,18 @@ var init_environmentInspector = __esm({
625
635
  const isWindows = process.platform === "win32";
626
636
  const result = await runCommand(isWindows ? "where" : "which", {
627
637
  args: [toolName],
628
- timeoutMs: TOOL_CHECK_TIMEOUT_MS
638
+ timeoutMs: this.getToolTimeout(toolName)
629
639
  });
630
- return result.stdout.trim().split("\n")[0];
640
+ return result.stdout.split(/\r?\n/).map((line2) => line2.trim()).find((line2) => line2.length > 0);
631
641
  } catch {
632
642
  return void 0;
633
643
  }
634
644
  }
635
645
  async getToolVersion(toolName) {
636
- const command = this.getVersionCommand(toolName);
637
- const isWindows = process.platform === "win32";
638
- const result = await runCommand(isWindows ? "cmd.exe" : "/bin/bash", {
639
- args: isWindows ? ["/c", command] : ["-lc", command],
640
- timeoutMs: TOOL_CHECK_TIMEOUT_MS
646
+ const invocation = this.getVersionInvocation(toolName);
647
+ const result = await runCommand(invocation.command, {
648
+ args: invocation.args,
649
+ timeoutMs: this.getToolTimeout(toolName)
641
650
  });
642
651
  return this.parseVersion(toolName, result.stdout || result.stderr);
643
652
  }
@@ -646,7 +655,7 @@ var init_environmentInspector = __esm({
646
655
  try {
647
656
  const result = await runCommand("cmd.exe", {
648
657
  args: ["/c", "nvm version"],
649
- timeoutMs: TOOL_CHECK_TIMEOUT_MS
658
+ timeoutMs: this.getToolTimeout("nvm")
650
659
  });
651
660
  return {
652
661
  installed: true,
@@ -666,7 +675,7 @@ var init_environmentInspector = __esm({
666
675
  "-lc",
667
676
  'export NVM_DIR="$HOME/.nvm"; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; nvm --version'
668
677
  ],
669
- timeoutMs: TOOL_CHECK_TIMEOUT_MS
678
+ timeoutMs: this.getToolTimeout("nvm")
670
679
  });
671
680
  return {
672
681
  installed: true,
@@ -691,7 +700,7 @@ var init_environmentInspector = __esm({
691
700
  try {
692
701
  const result = await runCommand("dotnet", {
693
702
  args: ["nuget", "list", "source"],
694
- timeoutMs: TOOL_CHECK_TIMEOUT_MS
703
+ timeoutMs: this.getToolTimeout("nuget")
695
704
  });
696
705
  const privateSourceUrl = "http://192.168.20.209:10010/nuget";
697
706
  if (result.stdout.includes(privateSourceUrl)) {
@@ -709,17 +718,28 @@ var init_environmentInspector = __esm({
709
718
  return this.handleToolCheckError(error);
710
719
  }
711
720
  }
712
- getVersionCommand(toolName) {
721
+ getVersionInvocation(toolName) {
722
+ const isWindows = process.platform === "win32";
723
+ if (isWindows && ["npm", "pnpm", "nrm"].includes(toolName)) {
724
+ return {
725
+ command: "cmd.exe",
726
+ args: ["/d", "/s", "/c", `${toolName} --version`]
727
+ };
728
+ }
713
729
  const commands = {
714
- git: "git --version",
715
- node: "node --version",
716
- npm: "npm --version",
717
- pnpm: "pnpm --version",
718
- nvm: "nvm --version",
719
- nrm: "nrm --version",
720
- dotnet: "dotnet --version"
730
+ git: { command: "git", args: ["--version"] },
731
+ node: { command: "node", args: ["--version"] },
732
+ npm: { command: "npm", args: ["--version"] },
733
+ pnpm: { command: "pnpm", args: ["--version"] },
734
+ nvm: { command: "nvm", args: ["--version"] },
735
+ nrm: { command: "nrm", args: ["--version"] },
736
+ rtk: { command: "rtk", args: ["--version"] },
737
+ dotnet: { command: "dotnet", args: ["--version"] }
721
738
  };
722
- return commands[toolName] || `${toolName} --version`;
739
+ return commands[toolName] || { command: toolName, args: ["--version"] };
740
+ }
741
+ getToolTimeout(toolName) {
742
+ return TOOL_CHECK_TIMEOUT_MS[toolName] ?? DEFAULT_TOOL_CHECK_TIMEOUT_MS;
723
743
  }
724
744
  parseVersion(toolName, output) {
725
745
  const cleaned = output.trim();
@@ -746,10 +766,11 @@ var init_environmentInspector = __esm({
746
766
  const execError = error;
747
767
  const message = execError.message || String(error);
748
768
  const code = execError.code;
749
- const isNotFound = message.includes("command not found") || message.includes("not recognized") || code === ERROR_CODE_NOT_FOUND;
769
+ const isNotFound = message.includes("command not found") || message.includes("not recognized") || message.includes("ENOENT") || code === "ENOENT" || code === ERROR_CODE_NOT_FOUND;
770
+ const isTimeout = code === ERROR_CODE_TIMEOUT || message.toLowerCase().includes("timed out");
750
771
  return {
751
772
  installed: false,
752
- error: isNotFound ? "Not installed" : message
773
+ error: isNotFound ? "Not installed" : isTimeout ? "Check timed out" : message
753
774
  };
754
775
  }
755
776
  };
@@ -1126,9 +1147,9 @@ var require_esprima = __commonJS({
1126
1147
  });
1127
1148
  };
1128
1149
  CommentHandler2.prototype.visitComment = function(node, metadata) {
1129
- var type = node.type[0] === "L" ? "Line" : "Block";
1150
+ var type2 = node.type[0] === "L" ? "Line" : "Block";
1130
1151
  var comment = {
1131
- type,
1152
+ type: type2,
1132
1153
  value: node.value
1133
1154
  };
1134
1155
  if (node.range) {
@@ -1141,7 +1162,7 @@ var require_esprima = __commonJS({
1141
1162
  if (this.attach) {
1142
1163
  var entry = {
1143
1164
  comment: {
1144
- type,
1165
+ type: type2,
1145
1166
  value: node.value,
1146
1167
  range: [metadata.start.offset, metadata.end.offset]
1147
1168
  },
@@ -1150,7 +1171,7 @@ var require_esprima = __commonJS({
1150
1171
  if (node.loc) {
1151
1172
  entry.comment.loc = node.loc;
1152
1173
  }
1153
- node.type = type;
1174
+ node.type = type2;
1154
1175
  this.leading.push(entry);
1155
1176
  this.trailing.push(entry);
1156
1177
  }
@@ -5965,28 +5986,28 @@ var require_esprima = __commonJS({
5965
5986
  };
5966
5987
  };
5967
5988
  Scanner2.prototype.scanIdentifier = function() {
5968
- var type;
5989
+ var type2;
5969
5990
  var start = this.index;
5970
5991
  var id = this.source.charCodeAt(start) === 92 ? this.getComplexIdentifier() : this.getIdentifier();
5971
5992
  if (id.length === 1) {
5972
- type = 3;
5993
+ type2 = 3;
5973
5994
  } else if (this.isKeyword(id)) {
5974
- type = 4;
5995
+ type2 = 4;
5975
5996
  } else if (id === "null") {
5976
- type = 5;
5997
+ type2 = 5;
5977
5998
  } else if (id === "true" || id === "false") {
5978
- type = 1;
5999
+ type2 = 1;
5979
6000
  } else {
5980
- type = 3;
6001
+ type2 = 3;
5981
6002
  }
5982
- if (type !== 3 && start + id.length !== this.index) {
6003
+ if (type2 !== 3 && start + id.length !== this.index) {
5983
6004
  var restore = this.index;
5984
6005
  this.index = start;
5985
6006
  this.tolerateUnexpectedToken(messages_1.Messages.InvalidEscapedReservedWord);
5986
6007
  this.index = restore;
5987
6008
  }
5988
6009
  return {
5989
- type,
6010
+ type: type2,
5990
6011
  value: id,
5991
6012
  lineNumber: this.lineNumber,
5992
6013
  lineStart: this.lineStart,
@@ -8437,13 +8458,13 @@ var require_parse = __commonJS({
8437
8458
  last = current;
8438
8459
  current = new_token;
8439
8460
  };
8440
- var type = () => {
8461
+ var type2 = () => {
8441
8462
  if (!current) {
8442
8463
  unexpected_end();
8443
8464
  }
8444
8465
  return current.type === "Punctuator" ? current.value : current.type;
8445
8466
  };
8446
- var is = (t) => type() === t;
8467
+ var is = (t) => type2() === t;
8447
8468
  var expect = (a) => {
8448
8469
  if (!is(a)) {
8449
8470
  unexpected();
@@ -8598,7 +8619,7 @@ var require_parse = __commonJS({
8598
8619
  return array;
8599
8620
  };
8600
8621
  function walk() {
8601
- let tt = type();
8622
+ let tt = type2();
8602
8623
  if (tt === CURLY_BRACKET_OPEN) {
8603
8624
  next();
8604
8625
  return {
@@ -8614,7 +8635,7 @@ var require_parse = __commonJS({
8614
8635
  let negative = EMPTY;
8615
8636
  if (tt === MINUS) {
8616
8637
  next();
8617
- tt = type();
8638
+ tt = type2();
8618
8639
  negative = MINUS;
8619
8640
  }
8620
8641
  let v;
@@ -8740,11 +8761,11 @@ var require_stringify = __commonJS({
8740
8761
  let is_line_comment = false;
8741
8762
  const str = comments.reduce((prev, {
8742
8763
  inline,
8743
- type,
8764
+ type: type2,
8744
8765
  value
8745
8766
  }) => {
8746
8767
  const delimiter = inline ? SPACE : LF + deeper_gap;
8747
- is_line_comment = type === "LineComment";
8768
+ is_line_comment = type2 === "LineComment";
8748
8769
  return prev + delimiter + comment_stringify(value, is_line_comment);
8749
8770
  }, EMPTY);
8750
8771
  return display_block || is_line_comment ? str + LF + deeper_gap : str;
@@ -8984,9 +9005,9 @@ function read() {
8984
9005
  }
8985
9006
  return c2;
8986
9007
  }
8987
- function newToken(type, value) {
9008
+ function newToken(type2, value) {
8988
9009
  return {
8989
- type,
9010
+ type: type2,
8990
9011
  value,
8991
9012
  line,
8992
9013
  column
@@ -10192,7 +10213,7 @@ var require_pend = __commonJS({
10192
10213
  // ../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/fd-slicer.js
10193
10214
  var require_fd_slicer = __commonJS({
10194
10215
  "../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/fd-slicer.js"(exports$1) {
10195
- var fs11 = __require("fs");
10216
+ var fs13 = __require("fs");
10196
10217
  var util2 = __require("util");
10197
10218
  var stream = __require("stream");
10198
10219
  var Readable = stream.Readable;
@@ -10217,7 +10238,7 @@ var require_fd_slicer = __commonJS({
10217
10238
  FdSlicer.prototype.read = function(buffer2, offset, length, position, callback) {
10218
10239
  var self = this;
10219
10240
  self.pend.go(function(cb) {
10220
- fs11.read(self.fd, buffer2, offset, length, position, function(err, bytesRead, buffer3) {
10241
+ fs13.read(self.fd, buffer2, offset, length, position, function(err, bytesRead, buffer3) {
10221
10242
  cb();
10222
10243
  callback(err, bytesRead, buffer3);
10223
10244
  });
@@ -10226,7 +10247,7 @@ var require_fd_slicer = __commonJS({
10226
10247
  FdSlicer.prototype.write = function(buffer2, offset, length, position, callback) {
10227
10248
  var self = this;
10228
10249
  self.pend.go(function(cb) {
10229
- fs11.write(self.fd, buffer2, offset, length, position, function(err, written, buffer3) {
10250
+ fs13.write(self.fd, buffer2, offset, length, position, function(err, written, buffer3) {
10230
10251
  cb();
10231
10252
  callback(err, written, buffer3);
10232
10253
  });
@@ -10247,7 +10268,7 @@ var require_fd_slicer = __commonJS({
10247
10268
  if (self.refCount > 0) return;
10248
10269
  if (self.refCount < 0) throw new Error("invalid unref");
10249
10270
  if (self.autoClose) {
10250
- fs11.close(self.fd, onCloseDone);
10271
+ fs13.close(self.fd, onCloseDone);
10251
10272
  }
10252
10273
  function onCloseDone(err) {
10253
10274
  if (err) {
@@ -10284,7 +10305,7 @@ var require_fd_slicer = __commonJS({
10284
10305
  self.context.pend.go(function(cb) {
10285
10306
  if (self.destroyed) return cb();
10286
10307
  var buffer2 = Buffer.allocUnsafe(toRead);
10287
- fs11.read(self.context.fd, buffer2, 0, toRead, self.pos, function(err, bytesRead) {
10308
+ fs13.read(self.context.fd, buffer2, 0, toRead, self.pos, function(err, bytesRead) {
10288
10309
  if (err) {
10289
10310
  self.destroy(err);
10290
10311
  } else if (bytesRead === 0) {
@@ -10331,7 +10352,7 @@ var require_fd_slicer = __commonJS({
10331
10352
  }
10332
10353
  self.context.pend.go(function(cb) {
10333
10354
  if (self.destroyed) return cb();
10334
- fs11.write(self.context.fd, buffer2, 0, buffer2.length, self.pos, function(err2, bytes) {
10355
+ fs13.write(self.context.fd, buffer2, 0, buffer2.length, self.pos, function(err2, bytes) {
10335
10356
  if (err2) {
10336
10357
  self.destroy();
10337
10358
  cb();
@@ -10769,7 +10790,7 @@ var require_buffer_crc32 = __commonJS({
10769
10790
  // ../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/index.js
10770
10791
  var require_yauzl = __commonJS({
10771
10792
  "../../node_modules/.pnpm/yauzl@3.2.0/node_modules/yauzl/index.js"(exports$1) {
10772
- var fs11 = __require("fs");
10793
+ var fs13 = __require("fs");
10773
10794
  var zlib = __require("zlib");
10774
10795
  var fd_slicer = require_fd_slicer();
10775
10796
  var crc32 = require_buffer_crc32();
@@ -10790,7 +10811,7 @@ var require_yauzl = __commonJS({
10790
10811
  exports$1.Entry = Entry;
10791
10812
  exports$1.LocalFileHeader = LocalFileHeader;
10792
10813
  exports$1.RandomAccessReader = RandomAccessReader;
10793
- function open2(path8, options2, callback) {
10814
+ function open2(path9, options2, callback) {
10794
10815
  if (typeof options2 === "function") {
10795
10816
  callback = options2;
10796
10817
  options2 = null;
@@ -10802,10 +10823,10 @@ var require_yauzl = __commonJS({
10802
10823
  if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
10803
10824
  if (options2.strictFileNames == null) options2.strictFileNames = false;
10804
10825
  if (callback == null) callback = defaultCallback;
10805
- fs11.open(path8, "r", function(err, fd) {
10826
+ fs13.open(path9, "r", function(err, fd) {
10806
10827
  if (err) return callback(err);
10807
10828
  fromFd(fd, options2, function(err2, zipfile) {
10808
- if (err2) fs11.close(fd, defaultCallback);
10829
+ if (err2) fs13.close(fd, defaultCallback);
10809
10830
  callback(err2, zipfile);
10810
10831
  });
10811
10832
  });
@@ -10822,7 +10843,7 @@ var require_yauzl = __commonJS({
10822
10843
  if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
10823
10844
  if (options2.strictFileNames == null) options2.strictFileNames = false;
10824
10845
  if (callback == null) callback = defaultCallback;
10825
- fs11.fstat(fd, function(err, stats) {
10846
+ fs13.fstat(fd, function(err, stats) {
10826
10847
  if (err) return callback(err);
10827
10848
  var reader = fd_slicer.createFromFd(fd, { autoClose: true });
10828
10849
  fromRandomAccessReader(reader, stats.size, options2, callback);
@@ -11534,7 +11555,7 @@ var init_fileUtils = __esm({
11534
11555
  return reject(
11535
11556
  new Error("Failed to open zip entry stream.")
11536
11557
  );
11537
- const writeStream = fs6.createWriteStream(outputPath);
11558
+ const writeStream = fs8.createWriteStream(outputPath);
11538
11559
  readStream.on("error", reject);
11539
11560
  writeStream.on("error", reject);
11540
11561
  writeStream.on("close", () => {
@@ -11608,7 +11629,7 @@ var init_fileUtils = __esm({
11608
11629
  const destFile = path.join(destDir, file);
11609
11630
  if (!overwrite) {
11610
11631
  try {
11611
- await fs2.access(destFile, fs6.constants.F_OK);
11632
+ await fs2.access(destFile, fs8.constants.F_OK);
11612
11633
  await fs2.rm(sourceFile, { recursive: true, force: true });
11613
11634
  continue;
11614
11635
  } catch {
@@ -11819,8 +11840,8 @@ var init_DaemonLogger = __esm({
11819
11840
  constructor(workspacePath) {
11820
11841
  this.logPath = path__namespace.join(workspacePath, CONFIG_DIR, LOG_FILE);
11821
11842
  const dir = path__namespace.dirname(this.logPath);
11822
- if (!fs6__namespace.existsSync(dir)) {
11823
- fs6__namespace.mkdirSync(dir, { recursive: true });
11843
+ if (!fs8__namespace.existsSync(dir)) {
11844
+ fs8__namespace.mkdirSync(dir, { recursive: true });
11824
11845
  }
11825
11846
  }
11826
11847
  getLogPath() {
@@ -11831,16 +11852,16 @@ var init_DaemonLogger = __esm({
11831
11852
  const line2 = `[${ts}] [${level.toUpperCase()}] ${message}
11832
11853
  `;
11833
11854
  this.rotateIfNeeded();
11834
- fs6__namespace.appendFileSync(this.logPath, line2, "utf-8");
11855
+ fs8__namespace.appendFileSync(this.logPath, line2, "utf-8");
11835
11856
  }
11836
11857
  rotateIfNeeded() {
11837
11858
  try {
11838
- const stats = fs6__namespace.statSync(this.logPath);
11859
+ const stats = fs8__namespace.statSync(this.logPath);
11839
11860
  if (stats.size > MAX_LOG_SIZE) {
11840
- const content = fs6__namespace.readFileSync(this.logPath, "utf-8");
11861
+ const content = fs8__namespace.readFileSync(this.logPath, "utf-8");
11841
11862
  const halfIdx = content.indexOf("\n", Math.floor(content.length / 2));
11842
11863
  if (halfIdx > 0) {
11843
- fs6__namespace.writeFileSync(this.logPath, content.slice(halfIdx + 1), "utf-8");
11864
+ fs8__namespace.writeFileSync(this.logPath, content.slice(halfIdx + 1), "utf-8");
11844
11865
  }
11845
11866
  }
11846
11867
  } catch {
@@ -11863,20 +11884,20 @@ var init_PidManager = __esm({
11863
11884
  }
11864
11885
  writePid(pid) {
11865
11886
  const dir = path__namespace.dirname(this.pidPath);
11866
- if (!fs6__namespace.existsSync(dir)) {
11867
- fs6__namespace.mkdirSync(dir, { recursive: true });
11887
+ if (!fs8__namespace.existsSync(dir)) {
11888
+ fs8__namespace.mkdirSync(dir, { recursive: true });
11868
11889
  }
11869
- fs6__namespace.writeFileSync(this.pidPath, String(pid), "utf-8");
11890
+ fs8__namespace.writeFileSync(this.pidPath, String(pid), "utf-8");
11870
11891
  }
11871
11892
  readPid() {
11872
- if (!fs6__namespace.existsSync(this.pidPath)) return null;
11873
- const raw = fs6__namespace.readFileSync(this.pidPath, "utf-8").trim();
11893
+ if (!fs8__namespace.existsSync(this.pidPath)) return null;
11894
+ const raw = fs8__namespace.readFileSync(this.pidPath, "utf-8").trim();
11874
11895
  const pid = Number.parseInt(raw, 10);
11875
11896
  return Number.isNaN(pid) ? null : pid;
11876
11897
  }
11877
11898
  removePid() {
11878
- if (fs6__namespace.existsSync(this.pidPath)) {
11879
- fs6__namespace.unlinkSync(this.pidPath);
11899
+ if (fs8__namespace.existsSync(this.pidPath)) {
11900
+ fs8__namespace.unlinkSync(this.pidPath);
11880
11901
  }
11881
11902
  }
11882
11903
  isProcessRunning(pid) {
@@ -11897,12 +11918,76 @@ var init_PidManager = __esm({
11897
11918
  };
11898
11919
  }
11899
11920
  });
11900
- var DEFAULT_TIMEOUT, MAX_OUTPUT_BYTES, GithubCopilotCliExecutor;
11921
+
11922
+ // ../../packages/serviceme-core/src/scheduled-tasks/executors/timeout.ts
11923
+ function resolveConfiguredTimeoutMs(timeoutSeconds, defaultTimeoutMs) {
11924
+ if (timeoutSeconds === 0) {
11925
+ return void 0;
11926
+ }
11927
+ if (typeof timeoutSeconds !== "number" || !Number.isFinite(timeoutSeconds) || timeoutSeconds < 0) {
11928
+ return defaultTimeoutMs;
11929
+ }
11930
+ return timeoutSeconds * 1e3;
11931
+ }
11932
+ var init_timeout = __esm({
11933
+ "../../packages/serviceme-core/src/scheduled-tasks/executors/timeout.ts"() {
11934
+ }
11935
+ });
11936
+ function redactArgs(args) {
11937
+ const redacted = [];
11938
+ let redactNext = false;
11939
+ for (const arg of args) {
11940
+ if (redactNext) {
11941
+ redacted.push("<redacted>");
11942
+ redactNext = false;
11943
+ continue;
11944
+ }
11945
+ redacted.push(arg);
11946
+ if (arg === "--prompt") {
11947
+ redactNext = true;
11948
+ }
11949
+ }
11950
+ return redacted;
11951
+ }
11952
+ function writeDiagnostic(message) {
11953
+ const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
11954
+ if (logPath) {
11955
+ fs8__namespace.appendFileSync(logPath, message);
11956
+ return;
11957
+ }
11958
+ process.stderr.write(message);
11959
+ }
11960
+ function resolveGithubCopilotCliExecution(payload) {
11961
+ const args = ["copilot", "prompt", "--prompt", payload.prompt];
11962
+ if (payload.autopilot) {
11963
+ args.push("--autopilot");
11964
+ }
11965
+ if (payload.allowTools && payload.allowTools.length > 0) {
11966
+ args.push("--allow-tools", payload.allowTools.join(","));
11967
+ }
11968
+ if (payload.model) {
11969
+ args.push("--model", payload.model);
11970
+ }
11971
+ if (payload.agent) {
11972
+ args.push("--agent", payload.agent);
11973
+ }
11974
+ args.push("--timeout", String(payload.timeout ?? 0));
11975
+ return {
11976
+ command: "serviceme",
11977
+ args,
11978
+ cwd: payload.workspace,
11979
+ timeoutMs: resolveConfiguredTimeoutMs(payload.timeout, DEFAULT_TIMEOUT_MS2),
11980
+ diagnosticArgs: redactArgs(args),
11981
+ promptLen: payload.prompt.length
11982
+ };
11983
+ }
11984
+ var MAX_OUTPUT_BYTES, DEFAULT_TIMEOUT_MS2, GithubCopilotCliExecutor;
11901
11985
  var init_GithubCopilotCliExecutor = __esm({
11902
11986
  "../../packages/serviceme-core/src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts"() {
11903
11987
  init_doctor();
11904
- DEFAULT_TIMEOUT = 3e5;
11988
+ init_timeout();
11905
11989
  MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
11990
+ DEFAULT_TIMEOUT_MS2 = 3e5;
11906
11991
  GithubCopilotCliExecutor = class {
11907
11992
  async execute(payload, abortSignal) {
11908
11993
  const authenticated = await isCopilotAuthenticated();
@@ -11925,7 +12010,7 @@ var init_GithubCopilotCliExecutor = __esm({
11925
12010
  }
11926
12011
  executeStreaming(payload, onOutput, abortSignal) {
11927
12012
  const p = payload;
11928
- const timeout = (p.timeout != null ? p.timeout * 1e3 : null) ?? DEFAULT_TIMEOUT;
12013
+ const execution = resolveGithubCopilotCliExecution(p);
11929
12014
  let resolve;
11930
12015
  const resultPromise = new Promise((r) => {
11931
12016
  resolve = r;
@@ -11934,7 +12019,7 @@ var init_GithubCopilotCliExecutor = __esm({
11934
12019
  const settle = (result) => {
11935
12020
  if (settled) return;
11936
12021
  settled = true;
11937
- clearTimeout(timer);
12022
+ if (timer) clearTimeout(timer);
11938
12023
  resolve?.(result);
11939
12024
  };
11940
12025
  if (abortSignal?.aborted) {
@@ -11947,36 +12032,32 @@ var init_GithubCopilotCliExecutor = __esm({
11947
12032
  }
11948
12033
  };
11949
12034
  }
11950
- const args = ["copilot", "prompt", "--prompt", p.prompt];
11951
- if (p.autopilot) {
11952
- args.push("--autopilot");
11953
- }
11954
- if (p.allowTools && p.allowTools.length > 0) {
11955
- args.push("--allow-tools", p.allowTools.join(","));
11956
- }
11957
- if (p.model) {
11958
- args.push("--model", p.model);
11959
- }
11960
- if (p.agent) {
11961
- args.push("--agent", p.agent);
11962
- }
11963
- if (p.timeout != null) {
11964
- args.push("--timeout", String(p.timeout));
11965
- }
11966
- const child = child_process.spawn("serviceme", args, {
11967
- cwd: p.workspace,
11968
- stdio: ["ignore", "pipe", "pipe"]
12035
+ writeDiagnostic(
12036
+ `[GithubCopilotCliExecutor] spawn: command=${execution.command}, args=${JSON.stringify(execution.diagnosticArgs)}, promptLen=${execution.promptLen}, cwd=${execution.cwd ?? "(default)"}, platform=${process.platform}, windowsHide=true, pid=${process.pid}
12037
+ `
12038
+ );
12039
+ const child = child_process.spawn(execution.command, execution.args, {
12040
+ cwd: execution.cwd,
12041
+ stdio: ["ignore", "pipe", "pipe"],
12042
+ windowsHide: true
11969
12043
  });
11970
- const timer = setTimeout(() => {
12044
+ if (child.pid) {
12045
+ writeDiagnostic(
12046
+ `[GithubCopilotCliExecutor] spawned child PID=${child.pid}
12047
+ `
12048
+ );
12049
+ }
12050
+ const timeoutMs = execution.timeoutMs;
12051
+ const timer = timeoutMs != null ? setTimeout(() => {
11971
12052
  child.kill("SIGTERM");
11972
12053
  setTimeout(() => {
11973
12054
  if (!child.killed) child.kill("SIGKILL");
11974
12055
  }, 5e3);
11975
12056
  settle({
11976
12057
  status: "timeout",
11977
- error: `Copilot CLI execution timed out after ${timeout / 1e3}s`
12058
+ error: `Copilot CLI execution timed out after ${timeoutMs / 1e3}s`
11978
12059
  });
11979
- }, timeout);
12060
+ }, timeoutMs) : void 0;
11980
12061
  let stdoutBuf = "";
11981
12062
  let stderrBuf = "";
11982
12063
  child.stdout.on("data", (chunk) => {
@@ -12022,20 +12103,23 @@ var init_GithubCopilotCliExecutor = __esm({
12022
12103
  });
12023
12104
 
12024
12105
  // ../../packages/serviceme-core/src/scheduled-tasks/executors/HttpRequestExecutor.ts
12025
- var DEFAULT_TIMEOUT2, HttpRequestExecutor;
12106
+ var DEFAULT_TIMEOUT_MS3, HttpRequestExecutor;
12026
12107
  var init_HttpRequestExecutor = __esm({
12027
12108
  "../../packages/serviceme-core/src/scheduled-tasks/executors/HttpRequestExecutor.ts"() {
12028
- DEFAULT_TIMEOUT2 = 3e4;
12109
+ init_timeout();
12110
+ DEFAULT_TIMEOUT_MS3 = 3e4;
12029
12111
  HttpRequestExecutor = class {
12030
12112
  async execute(payload, abortSignal) {
12031
12113
  const p = payload;
12032
- const timeout = (p.timeout != null ? p.timeout * 1e3 : null) ?? DEFAULT_TIMEOUT2;
12114
+ const timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS3);
12033
12115
  const ac = new AbortController();
12034
- const timer = setTimeout(() => {
12116
+ let timedOut = false;
12117
+ const timer = timeoutMs != null ? setTimeout(() => {
12118
+ timedOut = true;
12035
12119
  ac.abort();
12036
- }, timeout);
12120
+ }, timeoutMs) : void 0;
12037
12121
  if (abortSignal?.aborted) {
12038
- clearTimeout(timer);
12122
+ if (timer) clearTimeout(timer);
12039
12123
  return { status: "cancelled", error: "Execution aborted" };
12040
12124
  }
12041
12125
  let externalAbort = false;
@@ -12043,7 +12127,7 @@ var init_HttpRequestExecutor = __esm({
12043
12127
  "abort",
12044
12128
  () => {
12045
12129
  externalAbort = true;
12046
- clearTimeout(timer);
12130
+ if (timer) clearTimeout(timer);
12047
12131
  ac.abort();
12048
12132
  },
12049
12133
  { once: true }
@@ -12055,7 +12139,7 @@ var init_HttpRequestExecutor = __esm({
12055
12139
  body: p.body,
12056
12140
  signal: ac.signal
12057
12141
  });
12058
- clearTimeout(timer);
12142
+ if (timer) clearTimeout(timer);
12059
12143
  const body = await response.text();
12060
12144
  if (response.ok) {
12061
12145
  return {
@@ -12070,14 +12154,17 @@ ${body}`.trim()
12070
12154
  ${body}`.trim()
12071
12155
  };
12072
12156
  } catch (err) {
12073
- clearTimeout(timer);
12157
+ if (timer) clearTimeout(timer);
12074
12158
  if (err instanceof Error && err.name === "AbortError") {
12075
12159
  if (externalAbort) {
12076
12160
  return { status: "cancelled", error: "Execution cancelled" };
12077
12161
  }
12162
+ if (!timedOut || timeoutMs == null) {
12163
+ return { status: "failure", error: err.message };
12164
+ }
12078
12165
  return {
12079
12166
  status: "timeout",
12080
- error: `HTTP request timed out after ${timeout / 1e3}s`
12167
+ error: `HTTP request timed out after ${timeoutMs / 1e3}s`
12081
12168
  };
12082
12169
  }
12083
12170
  const message = err instanceof Error ? err.message : String(err);
@@ -12087,11 +12174,84 @@ ${body}`.trim()
12087
12174
  };
12088
12175
  }
12089
12176
  });
12090
- var DEFAULT_TIMEOUT3, MAX_OUTPUT_BYTES2, ShellExecutor;
12177
+ function resolveShellExecution(script, options2 = {}) {
12178
+ const platform = options2.platform ?? process.platform;
12179
+ const env = options2.env ?? process.env;
12180
+ const fileExists = options2.fileExists ?? fs8__namespace.existsSync;
12181
+ if (platform === "win32") {
12182
+ const posixShell = usesPosixShellSyntax(script) ? findWindowsPosixShell(env, fileExists) : null;
12183
+ if (posixShell) {
12184
+ return {
12185
+ command: posixShell,
12186
+ args: ["-s"],
12187
+ stdinScript: script,
12188
+ outputEncoding: "utf8",
12189
+ shellKind: "posix"
12190
+ };
12191
+ }
12192
+ return {
12193
+ command: env.ComSpec ?? env.COMSPEC ?? "cmd.exe",
12194
+ args: ["/d", "/s", "/c", script],
12195
+ outputEncoding: "utf8",
12196
+ shellKind: "cmd"
12197
+ };
12198
+ }
12199
+ return {
12200
+ command: "sh",
12201
+ args: ["-s"],
12202
+ stdinScript: script,
12203
+ outputEncoding: "utf8",
12204
+ shellKind: "posix"
12205
+ };
12206
+ }
12207
+ function usesPosixShellSyntax(script) {
12208
+ return /\$\([^)]*\)|`[^`]*`/.test(script);
12209
+ }
12210
+ function findWindowsPosixShell(env, fileExists) {
12211
+ const explicitShell = env.SERVICEME_POSIX_SHELL;
12212
+ if (explicitShell && fileExists(explicitShell)) {
12213
+ return explicitShell;
12214
+ }
12215
+ const knownGitBashPaths = [
12216
+ "C:\\Program Files\\Git\\bin\\bash.exe",
12217
+ "C:\\Program Files\\Git\\usr\\bin\\bash.exe",
12218
+ "C:\\Program Files (x86)\\Git\\bin\\bash.exe",
12219
+ "C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
12220
+ ];
12221
+ for (const candidate of knownGitBashPaths) {
12222
+ if (fileExists(candidate)) return candidate;
12223
+ }
12224
+ const pathValue = env.Path ?? env.PATH ?? "";
12225
+ for (const dir of pathValue.split(path__namespace.win32.delimiter)) {
12226
+ if (!dir) continue;
12227
+ for (const executable of POSIX_SHELL_CANDIDATES) {
12228
+ const candidate = path__namespace.win32.join(dir, executable);
12229
+ if (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {
12230
+ return candidate;
12231
+ }
12232
+ }
12233
+ }
12234
+ return null;
12235
+ }
12236
+ function isWindowsWslLauncher(candidate) {
12237
+ const normalized = path__namespace.win32.normalize(candidate).toLowerCase();
12238
+ return normalized.endsWith("\\windows\\system32\\bash.exe") || normalized.endsWith("\\windows\\syswow64\\bash.exe");
12239
+ }
12240
+ function writeDiagnostic2(message) {
12241
+ const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
12242
+ if (logPath) {
12243
+ fs8__namespace.appendFileSync(logPath, message);
12244
+ return;
12245
+ }
12246
+ process.stderr.write(message);
12247
+ }
12248
+ var MAX_OUTPUT_BYTES2, DEFAULT_TIMEOUT_MS4, POSIX_SHELL_CANDIDATES, ShellExecutor;
12091
12249
  var init_ShellExecutor = __esm({
12092
12250
  "../../packages/serviceme-core/src/scheduled-tasks/executors/ShellExecutor.ts"() {
12093
- DEFAULT_TIMEOUT3 = 6e4;
12251
+ init_timeout();
12094
12252
  MAX_OUTPUT_BYTES2 = 1024 * 1024;
12253
+ DEFAULT_TIMEOUT_MS4 = 6e4;
12254
+ POSIX_SHELL_CANDIDATES = ["bash.exe", "sh.exe"];
12095
12255
  ShellExecutor = class {
12096
12256
  async execute(payload, abortSignal) {
12097
12257
  let output = "";
@@ -12107,7 +12267,7 @@ var init_ShellExecutor = __esm({
12107
12267
  }
12108
12268
  executeStreaming(payload, onOutput, abortSignal) {
12109
12269
  const p = payload;
12110
- const timeout = (p.timeout != null ? p.timeout * 1e3 : null) ?? DEFAULT_TIMEOUT3;
12270
+ const timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS4);
12111
12271
  let resolve;
12112
12272
  const resultPromise = new Promise((r) => {
12113
12273
  resolve = r;
@@ -12116,7 +12276,7 @@ var init_ShellExecutor = __esm({
12116
12276
  const settle = (result) => {
12117
12277
  if (settled) return;
12118
12278
  settled = true;
12119
- clearTimeout(timer);
12279
+ if (timer) clearTimeout(timer);
12120
12280
  resolve?.(result);
12121
12281
  };
12122
12282
  if (abortSignal?.aborted) {
@@ -12129,37 +12289,65 @@ var init_ShellExecutor = __esm({
12129
12289
  }
12130
12290
  };
12131
12291
  }
12132
- const child = child_process.spawn("sh", ["-c", p.script], {
12292
+ const shellExecution = resolveShellExecution(p.script);
12293
+ const spawnOpts = {
12133
12294
  cwd: p.cwd,
12134
- stdio: ["ignore", "pipe", "pipe"]
12135
- });
12136
- const timer = setTimeout(() => {
12295
+ stdio: [shellExecution.stdinScript ? "pipe" : "ignore", "pipe", "pipe"],
12296
+ windowsHide: true
12297
+ };
12298
+ writeDiagnostic2(
12299
+ `[ShellExecutor] spawn:
12300
+ platform=${process.platform}
12301
+ shell=${shellExecution.shellKind}
12302
+ command=${shellExecution.command}
12303
+ args=${JSON.stringify(shellExecution.args.filter((a) => a !== p.script))}
12304
+ stdin=${shellExecution.stdinScript ? "true" : "false"}
12305
+ cwd=${p.cwd ?? "(default)"}
12306
+ timeout=${timeoutMs != null ? `${timeoutMs}ms` : "unlimited"}
12307
+ scriptLen=${p.script.length}
12308
+ windowsHide=true
12309
+ daemonPid=${process.pid}
12310
+ `
12311
+ );
12312
+ const child = child_process.spawn(shellExecution.command, shellExecution.args, spawnOpts);
12313
+ if (shellExecution.stdinScript && child.stdin) {
12314
+ child.stdin.end(shellExecution.stdinScript);
12315
+ }
12316
+ if (child.pid) {
12317
+ writeDiagnostic2(`[ShellExecutor] spawned child PID=${child.pid}
12318
+ `);
12319
+ }
12320
+ const timer = timeoutMs != null ? setTimeout(() => {
12137
12321
  child.kill("SIGTERM");
12138
12322
  setTimeout(() => {
12139
12323
  if (!child.killed) child.kill("SIGKILL");
12140
12324
  }, 5e3);
12141
12325
  settle({
12142
12326
  status: "timeout",
12143
- error: `Shell execution timed out after ${timeout / 1e3}s`
12327
+ error: `Shell execution timed out after ${timeoutMs / 1e3}s`
12144
12328
  });
12145
- }, timeout);
12329
+ }, timeoutMs) : void 0;
12146
12330
  let stdoutBuf = "";
12147
12331
  let stderrBuf = "";
12148
- child.stdout.on("data", (chunk) => {
12149
- const data = chunk.toString();
12332
+ child.stdout?.on("data", (chunk) => {
12333
+ const data = chunk.toString(shellExecution.outputEncoding);
12150
12334
  stdoutBuf += data;
12151
12335
  if (stdoutBuf.length > MAX_OUTPUT_BYTES2)
12152
12336
  stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES2);
12153
12337
  onOutput("stdout", data);
12154
12338
  });
12155
- child.stderr.on("data", (chunk) => {
12156
- const data = chunk.toString();
12339
+ child.stderr?.on("data", (chunk) => {
12340
+ const data = chunk.toString(shellExecution.outputEncoding);
12157
12341
  stderrBuf += data;
12158
12342
  if (stderrBuf.length > MAX_OUTPUT_BYTES2)
12159
12343
  stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES2);
12160
12344
  onOutput("stderr", data);
12161
12345
  });
12162
12346
  child.on("close", (code) => {
12347
+ writeDiagnostic2(
12348
+ `[ShellExecutor] child PID=${child.pid} exited: code=${code}
12349
+ `
12350
+ );
12163
12351
  if (code === 0) {
12164
12352
  settle({ status: "success", output: stdoutBuf || void 0 });
12165
12353
  } else {
@@ -12221,9 +12409,38 @@ var init_executors = __esm({
12221
12409
  function emptyConfig() {
12222
12410
  return { version: 1, tasks: [] };
12223
12411
  }
12412
+ function isRecord3(value) {
12413
+ return value !== null && typeof value === "object" && !Array.isArray(value);
12414
+ }
12415
+ function requireNonEmptyString(payload, field, taskType) {
12416
+ if (!isRecord3(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
12417
+ throw createServicemeError(
12418
+ "invalid_payload",
12419
+ `${taskType} payload ${field} is required`
12420
+ );
12421
+ }
12422
+ }
12423
+ function validateTaskPayload(taskType, payload) {
12424
+ switch (taskType) {
12425
+ case "command":
12426
+ requireNonEmptyString(payload, "command", taskType);
12427
+ return;
12428
+ case "shell":
12429
+ requireNonEmptyString(payload, "script", taskType);
12430
+ return;
12431
+ case "http_request":
12432
+ requireNonEmptyString(payload, "url", taskType);
12433
+ requireNonEmptyString(payload, "method", taskType);
12434
+ return;
12435
+ case "github_copilot_cli":
12436
+ requireNonEmptyString(payload, "prompt", taskType);
12437
+ return;
12438
+ }
12439
+ }
12224
12440
  var CONFIG_DIR3, CONFIG_FILE, TaskConfigManager;
12225
12441
  var init_TaskConfigManager = __esm({
12226
12442
  "../../packages/serviceme-core/src/scheduled-tasks/TaskConfigManager.ts"() {
12443
+ init_src();
12227
12444
  CONFIG_DIR3 = ".serviceme";
12228
12445
  CONFIG_FILE = "scheduled-tasks.json";
12229
12446
  TaskConfigManager = class {
@@ -12234,21 +12451,21 @@ var init_TaskConfigManager = __esm({
12234
12451
  return this.configPath;
12235
12452
  }
12236
12453
  readConfig() {
12237
- if (!fs6__namespace.existsSync(this.configPath)) {
12454
+ if (!fs8__namespace.existsSync(this.configPath)) {
12238
12455
  return emptyConfig();
12239
12456
  }
12240
- const raw = fs6__namespace.readFileSync(this.configPath, "utf-8");
12457
+ const raw = fs8__namespace.readFileSync(this.configPath, "utf-8");
12241
12458
  const parsed = JSON.parse(raw);
12242
12459
  return parsed;
12243
12460
  }
12244
12461
  writeConfig(config) {
12245
12462
  const dir = path__namespace.dirname(this.configPath);
12246
- if (!fs6__namespace.existsSync(dir)) {
12247
- fs6__namespace.mkdirSync(dir, { recursive: true });
12463
+ if (!fs8__namespace.existsSync(dir)) {
12464
+ fs8__namespace.mkdirSync(dir, { recursive: true });
12248
12465
  }
12249
12466
  const tmp = `${this.configPath}.tmp`;
12250
- fs6__namespace.writeFileSync(tmp, JSON.stringify(config, null, " "), "utf-8");
12251
- fs6__namespace.renameSync(tmp, this.configPath);
12467
+ fs8__namespace.writeFileSync(tmp, JSON.stringify(config, null, " "), "utf-8");
12468
+ fs8__namespace.renameSync(tmp, this.configPath);
12252
12469
  }
12253
12470
  listTasks() {
12254
12471
  return this.readConfig().tasks;
@@ -12260,6 +12477,7 @@ var init_TaskConfigManager = __esm({
12260
12477
  return this.readConfig().tasks.find((t) => t.name === name);
12261
12478
  }
12262
12479
  createTask(input) {
12480
+ validateTaskPayload(input.taskType, input.payload);
12263
12481
  const config = this.readConfig();
12264
12482
  const now = (/* @__PURE__ */ new Date()).toISOString();
12265
12483
  const task = {
@@ -12288,6 +12506,9 @@ var init_TaskConfigManager = __esm({
12288
12506
  if (!existing) {
12289
12507
  throw new Error(`Task '${id}' not found`);
12290
12508
  }
12509
+ const nextTaskType = input.taskType ?? existing.taskType;
12510
+ const nextPayload = input.payload ?? existing.payload;
12511
+ validateTaskPayload(nextTaskType, nextPayload);
12291
12512
  const updated = {
12292
12513
  ...existing,
12293
12514
  ...input.name !== void 0 && { name: input.name },
@@ -12327,6 +12548,200 @@ var init_TaskConfigManager = __esm({
12327
12548
  };
12328
12549
  }
12329
12550
  });
12551
+
12552
+ // ../../packages/serviceme-core/src/scheduled-tasks/TaskExecutionEngine.ts
12553
+ function hasNonEmptyString(value) {
12554
+ return typeof value === "string" && value.trim().length > 0;
12555
+ }
12556
+ function resolveTaskExecutionPayload(taskType, payload, workspacePath) {
12557
+ if (!hasNonEmptyString(workspacePath)) {
12558
+ return payload;
12559
+ }
12560
+ if (taskType === "shell") {
12561
+ const shellPayload = payload;
12562
+ if (hasNonEmptyString(shellPayload.cwd)) {
12563
+ return shellPayload;
12564
+ }
12565
+ return { ...shellPayload, cwd: workspacePath };
12566
+ }
12567
+ if (taskType === "github_copilot_cli") {
12568
+ const copilotPayload = payload;
12569
+ if (hasNonEmptyString(copilotPayload.workspace)) {
12570
+ return copilotPayload;
12571
+ }
12572
+ return { ...copilotPayload, workspace: workspacePath };
12573
+ }
12574
+ return payload;
12575
+ }
12576
+ var TaskExecutionEngine;
12577
+ var init_TaskExecutionEngine = __esm({
12578
+ "../../packages/serviceme-core/src/scheduled-tasks/TaskExecutionEngine.ts"() {
12579
+ init_types();
12580
+ init_TaskConfigManager();
12581
+ TaskExecutionEngine = class {
12582
+ constructor(getExecutor2) {
12583
+ this.getExecutor = getExecutor2;
12584
+ this.running = /* @__PURE__ */ new Map();
12585
+ this.taskExecutions = /* @__PURE__ */ new Map();
12586
+ this.listener = null;
12587
+ }
12588
+ setListener(listener) {
12589
+ this.listener = listener;
12590
+ }
12591
+ async execute(snapshot) {
12592
+ const policy = snapshot.concurrencyPolicy ?? "reject";
12593
+ if (policy === "reject") {
12594
+ const existingIds = this.taskExecutions.get(snapshot.taskId);
12595
+ if (existingIds && existingIds.size > 0) {
12596
+ throw new Error(
12597
+ `TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`
12598
+ );
12599
+ }
12600
+ }
12601
+ const startedAt = (/* @__PURE__ */ new Date()).toISOString();
12602
+ if (!this.taskExecutions.has(snapshot.taskId)) {
12603
+ this.taskExecutions.set(snapshot.taskId, /* @__PURE__ */ new Set());
12604
+ }
12605
+ this.taskExecutions.get(snapshot.taskId)?.add(snapshot.executionId);
12606
+ const ac = new AbortController();
12607
+ const runningExec = {
12608
+ executionId: snapshot.executionId,
12609
+ taskId: snapshot.taskId,
12610
+ startedAt,
12611
+ cancel: () => ac.abort()
12612
+ };
12613
+ this.running.set(snapshot.executionId, runningExec);
12614
+ this.listener?.onStarted({
12615
+ executionId: snapshot.executionId,
12616
+ taskId: snapshot.taskId,
12617
+ startedAt
12618
+ });
12619
+ try {
12620
+ const executionPayload = resolveTaskExecutionPayload(
12621
+ snapshot.type,
12622
+ snapshot.payload,
12623
+ snapshot.workspacePath
12624
+ );
12625
+ validateTaskPayload(snapshot.type, executionPayload);
12626
+ const executor = this.getExecutor(snapshot.type);
12627
+ let result;
12628
+ if (isStreamingTaskExecutor(executor)) {
12629
+ const handle = executor.executeStreaming(
12630
+ executionPayload,
12631
+ (stream, data) => {
12632
+ this.listener?.onOutput({
12633
+ executionId: snapshot.executionId,
12634
+ stream,
12635
+ data
12636
+ });
12637
+ },
12638
+ ac.signal
12639
+ );
12640
+ runningExec.cancel = () => {
12641
+ handle.cancel();
12642
+ ac.abort();
12643
+ };
12644
+ result = await handle.result;
12645
+ } else {
12646
+ result = await executor.execute(executionPayload, ac.signal);
12647
+ }
12648
+ const log = {
12649
+ id: snapshot.executionId,
12650
+ taskId: snapshot.taskId,
12651
+ taskName: snapshot.name,
12652
+ startedAt,
12653
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
12654
+ status: result.status === "running" ? "failure" : result.status,
12655
+ output: result.output,
12656
+ error: result.error
12657
+ };
12658
+ switch (log.status) {
12659
+ case "success":
12660
+ this.listener?.onCompleted({
12661
+ executionId: snapshot.executionId,
12662
+ log
12663
+ });
12664
+ break;
12665
+ case "cancelled":
12666
+ this.listener?.onCancelled({
12667
+ executionId: snapshot.executionId,
12668
+ log
12669
+ });
12670
+ break;
12671
+ case "timeout":
12672
+ this.listener?.onFailed({
12673
+ executionId: snapshot.executionId,
12674
+ status: "timeout",
12675
+ reason: "timeout",
12676
+ log
12677
+ });
12678
+ break;
12679
+ default:
12680
+ this.listener?.onFailed({
12681
+ executionId: snapshot.executionId,
12682
+ status: "failure",
12683
+ reason: "error",
12684
+ log
12685
+ });
12686
+ break;
12687
+ }
12688
+ } catch (err) {
12689
+ const log = {
12690
+ id: snapshot.executionId,
12691
+ taskId: snapshot.taskId,
12692
+ taskName: snapshot.name,
12693
+ startedAt,
12694
+ finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
12695
+ status: ac.signal.aborted ? "cancelled" : "failure",
12696
+ error: err instanceof Error ? err.message : String(err)
12697
+ };
12698
+ if (ac.signal.aborted) {
12699
+ this.listener?.onCancelled({
12700
+ executionId: snapshot.executionId,
12701
+ log
12702
+ });
12703
+ } else {
12704
+ this.listener?.onFailed({
12705
+ executionId: snapshot.executionId,
12706
+ status: "failure",
12707
+ reason: "error",
12708
+ log
12709
+ });
12710
+ }
12711
+ } finally {
12712
+ this.running.delete(snapshot.executionId);
12713
+ const taskExecIds = this.taskExecutions.get(snapshot.taskId);
12714
+ if (taskExecIds) {
12715
+ taskExecIds.delete(snapshot.executionId);
12716
+ if (taskExecIds.size === 0) {
12717
+ this.taskExecutions.delete(snapshot.taskId);
12718
+ }
12719
+ }
12720
+ }
12721
+ }
12722
+ cancel(executionId) {
12723
+ const exec = this.running.get(executionId);
12724
+ if (!exec) return false;
12725
+ exec.cancel();
12726
+ return true;
12727
+ }
12728
+ listRunning() {
12729
+ return Array.from(this.running.values()).map((e) => ({
12730
+ executionId: e.executionId,
12731
+ taskId: e.taskId,
12732
+ startedAt: e.startedAt
12733
+ }));
12734
+ }
12735
+ dispose() {
12736
+ for (const exec of this.running.values()) {
12737
+ exec.cancel();
12738
+ }
12739
+ this.running.clear();
12740
+ this.taskExecutions.clear();
12741
+ }
12742
+ };
12743
+ }
12744
+ });
12330
12745
  function emptyLogFile() {
12331
12746
  return { logs: [] };
12332
12747
  }
@@ -12360,11 +12775,11 @@ var init_TaskLogManager = __esm({
12360
12775
  return this.logPath;
12361
12776
  }
12362
12777
  readLogFile() {
12363
- if (!fs6__namespace.existsSync(this.logPath)) {
12778
+ if (!fs8__namespace.existsSync(this.logPath)) {
12364
12779
  return emptyLogFile();
12365
12780
  }
12366
12781
  try {
12367
- const raw = fs6__namespace.readFileSync(this.logPath, "utf-8");
12782
+ const raw = fs8__namespace.readFileSync(this.logPath, "utf-8");
12368
12783
  const parsed = JSON.parse(raw);
12369
12784
  const file = validateAndRepairLogFile(parsed);
12370
12785
  if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.logs) || parsed.logs.length !== file.logs.length) {
@@ -12380,21 +12795,21 @@ var init_TaskLogManager = __esm({
12380
12795
  }
12381
12796
  backupCorruptedFile() {
12382
12797
  try {
12383
- if (fs6__namespace.existsSync(this.logPath)) {
12798
+ if (fs8__namespace.existsSync(this.logPath)) {
12384
12799
  const backupPath = `${this.logPath}.corrupted.${Date.now()}`;
12385
- fs6__namespace.copyFileSync(this.logPath, backupPath);
12800
+ fs8__namespace.copyFileSync(this.logPath, backupPath);
12386
12801
  }
12387
12802
  } catch {
12388
12803
  }
12389
12804
  }
12390
12805
  writeLogFile(file) {
12391
12806
  const dir = path__namespace.dirname(this.logPath);
12392
- if (!fs6__namespace.existsSync(dir)) {
12393
- fs6__namespace.mkdirSync(dir, { recursive: true });
12807
+ if (!fs8__namespace.existsSync(dir)) {
12808
+ fs8__namespace.mkdirSync(dir, { recursive: true });
12394
12809
  }
12395
12810
  const tmp = `${this.logPath}.tmp`;
12396
- fs6__namespace.writeFileSync(tmp, JSON.stringify(file, null, " "), "utf-8");
12397
- fs6__namespace.renameSync(tmp, this.logPath);
12811
+ fs8__namespace.writeFileSync(tmp, JSON.stringify(file, null, " "), "utf-8");
12812
+ fs8__namespace.renameSync(tmp, this.logPath);
12398
12813
  }
12399
12814
  appendLog(input) {
12400
12815
  const file = this.readLogFile();
@@ -12486,6 +12901,7 @@ var init_SchedulerDaemon = __esm({
12486
12901
  "../../packages/serviceme-core/src/scheduled-tasks/daemon/SchedulerDaemon.ts"() {
12487
12902
  init_executors();
12488
12903
  init_TaskConfigManager();
12904
+ init_TaskExecutionEngine();
12489
12905
  init_TaskLogManager();
12490
12906
  init_DaemonLogger();
12491
12907
  init_PidManager();
@@ -12511,7 +12927,23 @@ var init_SchedulerDaemon = __esm({
12511
12927
  this.running = true;
12512
12928
  this.startTime = Date.now();
12513
12929
  this.pidManager.writePid(process.pid);
12514
- this.logger.log("info", `Scheduler daemon started (PID: ${process.pid})`);
12930
+ const configPath = this.configManager.getConfigPath();
12931
+ const logPath = this.logger.getLogPath();
12932
+ this.logger.log("info", "=".repeat(60));
12933
+ this.logger.log(
12934
+ "info",
12935
+ `Scheduler daemon started
12936
+ PID: ${process.pid}
12937
+ Node: ${process.version}
12938
+ OS: ${os__namespace.type()} ${os__namespace.release()} (${process.arch})
12939
+ Platform: ${process.platform}
12940
+ Hostname: ${os__namespace.hostname()}
12941
+ User: ${os__namespace.userInfo().username}
12942
+ Workspace: ${this.workspacePath}
12943
+ Config: ${configPath}
12944
+ LogPath: ${logPath}
12945
+ ExecPath: ${process.execPath}`
12946
+ );
12515
12947
  this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);
12516
12948
  this.setupConfigWatch();
12517
12949
  process.on("SIGTERM", () => this.stop());
@@ -12536,8 +12968,8 @@ var init_SchedulerDaemon = __esm({
12536
12968
  const configPath = this.configManager.getConfigPath();
12537
12969
  const dir = configPath.substring(0, configPath.lastIndexOf("/"));
12538
12970
  try {
12539
- if (fs6__namespace.existsSync(dir)) {
12540
- this.watcher = fs6__namespace.watch(dir, (_eventType, filename) => {
12971
+ if (fs8__namespace.existsSync(dir)) {
12972
+ this.watcher = fs8__namespace.watch(dir, (_eventType, filename) => {
12541
12973
  if (filename === "scheduled-tasks.json") {
12542
12974
  this.logger.log("info", "Config file changed, reconciling...");
12543
12975
  }
@@ -12553,7 +12985,11 @@ var init_SchedulerDaemon = __esm({
12553
12985
  for (const task of config.tasks) {
12554
12986
  if (!task.enabled) continue;
12555
12987
  if (this.taskRunning.has(task.id)) continue;
12556
- const lastExec = this.lastRun.get(task.id) ?? 0;
12988
+ let lastExec = this.lastRun.get(task.id);
12989
+ if (lastExec === void 0) {
12990
+ this.lastRun.set(task.id, now);
12991
+ lastExec = now;
12992
+ }
12557
12993
  if (this.shouldRun(task, lastExec, now)) {
12558
12994
  this.executeTask(task, now);
12559
12995
  }
@@ -12575,11 +13011,25 @@ var init_SchedulerDaemon = __esm({
12575
13011
  this.taskRunning.add(task.id);
12576
13012
  this.lastRun.set(task.id, now);
12577
13013
  const startedAt = new Date(now).toISOString();
12578
- this.logger.log("info", `Executing task: ${task.name} (${task.id})`);
13014
+ const startMs = Date.now();
13015
+ this.logger.log(
13016
+ "info",
13017
+ `Executing task: ${task.name} (${task.id})
13018
+ type: ${task.taskType}
13019
+ schedule: ${task.schedule} (${task.scheduleType})
13020
+ enabled: ${task.enabled}`
13021
+ );
12579
13022
  try {
13023
+ const executionPayload = resolveTaskExecutionPayload(
13024
+ task.taskType,
13025
+ task.payload,
13026
+ this.workspacePath
13027
+ );
13028
+ validateTaskPayload(task.taskType, executionPayload);
12580
13029
  const executor = getExecutor(task.taskType);
12581
- const result = await executor.execute(task.payload);
13030
+ const result = await executor.execute(executionPayload);
12582
13031
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
13032
+ const durationMs = Date.now() - startMs;
12583
13033
  this.logManager.appendLog({
12584
13034
  taskId: task.id,
12585
13035
  taskName: task.name,
@@ -12589,9 +13039,17 @@ var init_SchedulerDaemon = __esm({
12589
13039
  output: result.output,
12590
13040
  error: result.error
12591
13041
  });
12592
- this.logger.log("info", `Task ${task.name} completed: ${result.status}`);
13042
+ const outputBytes = result.output ? Buffer.byteLength(result.output) : 0;
13043
+ this.logger.log(
13044
+ "info",
13045
+ `Task completed: ${task.name} (${task.id})
13046
+ status: ${result.status}
13047
+ duration: ${durationMs}ms
13048
+ outputBytes: ${outputBytes}`
13049
+ );
12593
13050
  } catch (err) {
12594
13051
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
13052
+ const durationMs = Date.now() - startMs;
12595
13053
  const message = err instanceof Error ? err.message : String(err);
12596
13054
  this.logManager.appendLog({
12597
13055
  taskId: task.id,
@@ -12601,7 +13059,12 @@ var init_SchedulerDaemon = __esm({
12601
13059
  status: "failure",
12602
13060
  error: message
12603
13061
  });
12604
- this.logger.log("error", `Task ${task.name} failed: ${message}`);
13062
+ this.logger.log(
13063
+ "error",
13064
+ `Task failed: ${task.name} (${task.id})
13065
+ duration: ${durationMs}ms
13066
+ error: ${message}`
13067
+ );
12605
13068
  } finally {
12606
13069
  this.taskRunning.delete(task.id);
12607
13070
  }
@@ -12631,170 +13094,6 @@ var init_daemon = __esm({
12631
13094
  }
12632
13095
  });
12633
13096
 
12634
- // ../../packages/serviceme-core/src/scheduled-tasks/TaskExecutionEngine.ts
12635
- var TaskExecutionEngine;
12636
- var init_TaskExecutionEngine = __esm({
12637
- "../../packages/serviceme-core/src/scheduled-tasks/TaskExecutionEngine.ts"() {
12638
- init_types();
12639
- TaskExecutionEngine = class {
12640
- constructor(getExecutor2) {
12641
- this.getExecutor = getExecutor2;
12642
- this.running = /* @__PURE__ */ new Map();
12643
- this.taskExecutions = /* @__PURE__ */ new Map();
12644
- this.listener = null;
12645
- }
12646
- setListener(listener) {
12647
- this.listener = listener;
12648
- }
12649
- async execute(snapshot) {
12650
- const policy = snapshot.concurrencyPolicy ?? "reject";
12651
- if (policy === "reject") {
12652
- const existingIds = this.taskExecutions.get(snapshot.taskId);
12653
- if (existingIds && existingIds.size > 0) {
12654
- throw new Error(
12655
- `TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`
12656
- );
12657
- }
12658
- }
12659
- const executor = this.getExecutor(snapshot.type);
12660
- const startedAt = (/* @__PURE__ */ new Date()).toISOString();
12661
- if (!this.taskExecutions.has(snapshot.taskId)) {
12662
- this.taskExecutions.set(snapshot.taskId, /* @__PURE__ */ new Set());
12663
- }
12664
- this.taskExecutions.get(snapshot.taskId)?.add(snapshot.executionId);
12665
- const ac = new AbortController();
12666
- const runningExec = {
12667
- executionId: snapshot.executionId,
12668
- taskId: snapshot.taskId,
12669
- startedAt,
12670
- cancel: () => ac.abort()
12671
- };
12672
- this.running.set(snapshot.executionId, runningExec);
12673
- this.listener?.onStarted({
12674
- executionId: snapshot.executionId,
12675
- taskId: snapshot.taskId,
12676
- startedAt
12677
- });
12678
- try {
12679
- let result;
12680
- if (isStreamingTaskExecutor(executor)) {
12681
- const handle = executor.executeStreaming(
12682
- snapshot.payload,
12683
- (stream, data) => {
12684
- this.listener?.onOutput({
12685
- executionId: snapshot.executionId,
12686
- stream,
12687
- data
12688
- });
12689
- },
12690
- ac.signal
12691
- );
12692
- runningExec.cancel = () => {
12693
- handle.cancel();
12694
- ac.abort();
12695
- };
12696
- result = await handle.result;
12697
- } else {
12698
- result = await executor.execute(snapshot.payload, ac.signal);
12699
- }
12700
- const log = {
12701
- id: snapshot.executionId,
12702
- taskId: snapshot.taskId,
12703
- taskName: snapshot.name,
12704
- startedAt,
12705
- finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
12706
- status: result.status === "running" ? "failure" : result.status,
12707
- output: result.output,
12708
- error: result.error
12709
- };
12710
- switch (log.status) {
12711
- case "success":
12712
- this.listener?.onCompleted({
12713
- executionId: snapshot.executionId,
12714
- log
12715
- });
12716
- break;
12717
- case "cancelled":
12718
- this.listener?.onCancelled({
12719
- executionId: snapshot.executionId,
12720
- log
12721
- });
12722
- break;
12723
- case "timeout":
12724
- this.listener?.onFailed({
12725
- executionId: snapshot.executionId,
12726
- status: "timeout",
12727
- reason: "timeout",
12728
- log
12729
- });
12730
- break;
12731
- default:
12732
- this.listener?.onFailed({
12733
- executionId: snapshot.executionId,
12734
- status: "failure",
12735
- reason: "error",
12736
- log
12737
- });
12738
- break;
12739
- }
12740
- } catch (err) {
12741
- const log = {
12742
- id: snapshot.executionId,
12743
- taskId: snapshot.taskId,
12744
- taskName: snapshot.name,
12745
- startedAt,
12746
- finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
12747
- status: ac.signal.aborted ? "cancelled" : "failure",
12748
- error: err instanceof Error ? err.message : String(err)
12749
- };
12750
- if (ac.signal.aborted) {
12751
- this.listener?.onCancelled({
12752
- executionId: snapshot.executionId,
12753
- log
12754
- });
12755
- } else {
12756
- this.listener?.onFailed({
12757
- executionId: snapshot.executionId,
12758
- status: "failure",
12759
- reason: "error",
12760
- log
12761
- });
12762
- }
12763
- } finally {
12764
- this.running.delete(snapshot.executionId);
12765
- const taskExecIds = this.taskExecutions.get(snapshot.taskId);
12766
- if (taskExecIds) {
12767
- taskExecIds.delete(snapshot.executionId);
12768
- if (taskExecIds.size === 0) {
12769
- this.taskExecutions.delete(snapshot.taskId);
12770
- }
12771
- }
12772
- }
12773
- }
12774
- cancel(executionId) {
12775
- const exec = this.running.get(executionId);
12776
- if (!exec) return false;
12777
- exec.cancel();
12778
- return true;
12779
- }
12780
- listRunning() {
12781
- return Array.from(this.running.values()).map((e) => ({
12782
- executionId: e.executionId,
12783
- taskId: e.taskId,
12784
- startedAt: e.startedAt
12785
- }));
12786
- }
12787
- dispose() {
12788
- for (const exec of this.running.values()) {
12789
- exec.cancel();
12790
- }
12791
- this.running.clear();
12792
- this.taskExecutions.clear();
12793
- }
12794
- };
12795
- }
12796
- });
12797
-
12798
13097
  // ../../packages/serviceme-core/src/scheduled-tasks/index.ts
12799
13098
  var init_scheduled_tasks2 = __esm({
12800
13099
  "../../packages/serviceme-core/src/scheduled-tasks/index.ts"() {
@@ -12836,7 +13135,9 @@ __export(src_exports, {
12836
13135
  moveFiles: () => moveFiles,
12837
13136
  noopLogger: () => noopLogger,
12838
13137
  parseIntervalMs: () => parseIntervalMs,
12839
- unzipFile: () => unzipFile
13138
+ resolveTaskExecutionPayload: () => resolveTaskExecutionPayload,
13139
+ unzipFile: () => unzipFile,
13140
+ validateTaskPayload: () => validateTaskPayload
12840
13141
  });
12841
13142
  var init_src2 = __esm({
12842
13143
  "../../packages/serviceme-core/src/index.ts"() {
@@ -12905,7 +13206,7 @@ init_src();
12905
13206
 
12906
13207
  // src/version.ts
12907
13208
  var SERVICEME_CLI_NAME = "serviceme";
12908
- var SERVICEME_CLI_VERSION = "0.1.3";
13209
+ var SERVICEME_CLI_VERSION = "0.1.5";
12909
13210
 
12910
13211
  // src/bridge/ndjson.ts
12911
13212
  function writeBridgeMessage(message) {
@@ -13432,7 +13733,7 @@ function requireWorkspace(parsed) {
13432
13733
  "Missing required flag: --workspacePath"
13433
13734
  );
13434
13735
  }
13435
- if (!fs6__namespace.existsSync(wp)) {
13736
+ if (!fs8__namespace.existsSync(wp)) {
13436
13737
  throw createServicemeError(
13437
13738
  "workspace_not_found",
13438
13739
  `Workspace path does not exist: ${wp}`
@@ -13765,8 +14066,14 @@ async function handleTrigger(parsed) {
13765
14066
  );
13766
14067
  }
13767
14068
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
14069
+ const executionPayload = resolveTaskExecutionPayload(
14070
+ task.taskType,
14071
+ task.payload,
14072
+ wp
14073
+ );
14074
+ validateTaskPayload(task.taskType, executionPayload);
13768
14075
  const executor = getExecutor(task.taskType);
13769
- const result = await executor.execute(task.payload);
14076
+ const result = await executor.execute(executionPayload);
13770
14077
  const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
13771
14078
  const logMgr = new TaskLogManager(wp);
13772
14079
  const log = logMgr.appendLog({
@@ -13995,7 +14302,7 @@ function requireWorkspace2(parsed) {
13995
14302
  "Missing required flag: --workspacePath"
13996
14303
  );
13997
14304
  }
13998
- if (!fs6__namespace.existsSync(wp)) {
14305
+ if (!fs8__namespace.existsSync(wp)) {
13999
14306
  throw createServicemeError(
14000
14307
  "workspace_not_found",
14001
14308
  `Workspace path does not exist: ${wp}`
@@ -14024,20 +14331,43 @@ function handleStart(parsed) {
14024
14331
  }
14025
14332
  const logPath = path__namespace.join(wp, ".serviceme", "scheduler.log");
14026
14333
  const logDir = path__namespace.dirname(logPath);
14027
- if (!fs6__namespace.existsSync(logDir)) {
14028
- fs6__namespace.mkdirSync(logDir, { recursive: true });
14029
- }
14030
- const out = fs6__namespace.openSync(logPath, "a");
14031
- const err = fs6__namespace.openSync(logPath, "a");
14032
- const child = child_process.spawn(
14033
- process.execPath,
14034
- [cliPath, "scheduler", "__daemon", "--workspacePath", wp],
14035
- {
14036
- detached: true,
14037
- stdio: ["ignore", out, err],
14038
- env: { ...process.env }
14039
- }
14334
+ if (!fs8__namespace.existsSync(logDir)) {
14335
+ fs8__namespace.mkdirSync(logDir, { recursive: true });
14336
+ }
14337
+ const spawnCmd = process.execPath;
14338
+ const spawnArgs = [
14339
+ cliPath,
14340
+ "scheduler",
14341
+ "__daemon",
14342
+ "--workspacePath",
14343
+ wp,
14344
+ "--logPath",
14345
+ logPath
14346
+ ];
14347
+ if (process.platform === "win32") {
14348
+ fs8__namespace.appendFileSync(
14349
+ logPath,
14350
+ `[scheduler:start] spawning daemon via hidden PowerShell Start-Process: cmd=${spawnCmd}, args=${JSON.stringify(spawnArgs)}, platform=${process.platform}, windowsHide=true
14351
+ `
14352
+ );
14353
+ const pid2 = startWindowsDaemon(spawnCmd, spawnArgs);
14354
+ pidMgr.writePid(pid2);
14355
+ writeSuccess({ pid: pid2, status: "started", workspacePath: wp });
14356
+ return;
14357
+ }
14358
+ const out = fs8__namespace.openSync(logPath, "a");
14359
+ const err = fs8__namespace.openSync(logPath, "a");
14360
+ fs8__namespace.appendFileSync(
14361
+ logPath,
14362
+ `[scheduler:start] spawning daemon: cmd=${spawnCmd}, args=${JSON.stringify(spawnArgs)}, platform=${process.platform}, detached=true, windowsHide=true
14363
+ `
14040
14364
  );
14365
+ const child = child_process.spawn(spawnCmd, spawnArgs, {
14366
+ detached: true,
14367
+ stdio: ["ignore", out, err],
14368
+ env: { ...process.env },
14369
+ windowsHide: true
14370
+ });
14041
14371
  child.unref();
14042
14372
  const pid = child.pid;
14043
14373
  if (pid === void 0) {
@@ -14049,6 +14379,60 @@ function handleStart(parsed) {
14049
14379
  pidMgr.writePid(pid);
14050
14380
  writeSuccess({ pid, status: "started", workspacePath: wp });
14051
14381
  }
14382
+ function startWindowsDaemon(command, args) {
14383
+ const argumentLine = args.map(quoteWindowsArgument).join(" ");
14384
+ const script = [
14385
+ "$ErrorActionPreference = 'Stop'",
14386
+ `$process = Start-Process -FilePath ${quotePowerShellString(command)} -ArgumentList ${quotePowerShellString(argumentLine)} -WindowStyle Hidden -PassThru`,
14387
+ "Write-Output $process.Id"
14388
+ ].join("; ");
14389
+ const encodedCommand = Buffer.from(script, "utf16le").toString("base64");
14390
+ const result = child_process.spawnSync(
14391
+ "powershell.exe",
14392
+ [
14393
+ "-NoProfile",
14394
+ "-NonInteractive",
14395
+ "-ExecutionPolicy",
14396
+ "Bypass",
14397
+ "-WindowStyle",
14398
+ "Hidden",
14399
+ "-EncodedCommand",
14400
+ encodedCommand
14401
+ ],
14402
+ {
14403
+ encoding: "utf8",
14404
+ stdio: ["ignore", "pipe", "pipe"],
14405
+ windowsHide: true
14406
+ }
14407
+ );
14408
+ if (result.error) {
14409
+ throw createServicemeError(
14410
+ "internal_error",
14411
+ `Failed to start scheduler daemon: ${result.error.message}`
14412
+ );
14413
+ }
14414
+ if (result.status !== 0) {
14415
+ const stderr = result.stderr.trim();
14416
+ throw createServicemeError(
14417
+ "internal_error",
14418
+ `Failed to start scheduler daemon${stderr ? `: ${stderr}` : ""}`
14419
+ );
14420
+ }
14421
+ const pid = Number.parseInt(result.stdout.trim(), 10);
14422
+ if (!Number.isFinite(pid)) {
14423
+ throw createServicemeError(
14424
+ "internal_error",
14425
+ `Failed to parse scheduler daemon PID: ${result.stdout.trim()}`
14426
+ );
14427
+ }
14428
+ return pid;
14429
+ }
14430
+ function quotePowerShellString(value) {
14431
+ return `'${value.replace(/'/g, "''")}'`;
14432
+ }
14433
+ function quoteWindowsArgument(value) {
14434
+ return `"${value.replace(/(\\*)"/g, '$1$1\\"').replace(/\\+$/g, "$&$&")}"`;
14435
+ }
14052
14436
  function handleStop(parsed) {
14053
14437
  const wp = requireWorkspace2(parsed);
14054
14438
  if (!getBooleanFlag(parsed, "yes")) {
@@ -14084,7 +14468,7 @@ function handleStatus(parsed) {
14084
14468
  let uptimeSeconds = null;
14085
14469
  if (pid !== null) {
14086
14470
  try {
14087
- const stat2 = fs6__namespace.statSync(pidMgr.getPidPath());
14471
+ const stat2 = fs8__namespace.statSync(pidMgr.getPidPath());
14088
14472
  uptimeSeconds = Math.floor((Date.now() - stat2.mtimeMs) / 1e3);
14089
14473
  } catch {
14090
14474
  uptimeSeconds = null;
@@ -14103,6 +14487,10 @@ function handleStatus(parsed) {
14103
14487
  }
14104
14488
  async function runDaemon(parsed) {
14105
14489
  const wp = requireWorkspace2(parsed);
14490
+ const logPath = getStringFlag(parsed, "logPath");
14491
+ if (logPath) {
14492
+ process.env.SERVICEME_SCHEDULER_LOG_PATH = logPath;
14493
+ }
14106
14494
  const pidMgr = new PidManager(wp);
14107
14495
  const existingPid = pidMgr.getRunningPid();
14108
14496
  if (existingPid !== null && existingPid !== process.pid) {