chatroom-cli 1.38.5 → 1.38.7

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
@@ -75146,33 +75146,59 @@ async function getCommitStatusChecks(cwd, ref) {
75146
75146
  if (!repoSlug)
75147
75147
  return null;
75148
75148
  try {
75149
- const [checkRunsResult, statusResult] = await Promise.all([
75149
+ const [checkRunsResult, legacyStatusesResult] = await Promise.all([
75150
75150
  runCommand(`gh api repos/${repoSlug}/commits/${encodeURIComponent(ref)}/check-runs --jq '{check_runs: [.check_runs[] | {name: .name, status: .status, conclusion: .conclusion}], total_count: .total_count}'`, cwd),
75151
- runCommand(`gh api repos/${repoSlug}/commits/${encodeURIComponent(ref)}/status --jq '.state'`, cwd)
75151
+ runCommand(`gh api repos/${repoSlug}/commits/${encodeURIComponent(ref)}/statuses --jq '[group_by(.context)[] | max_by(.created_at) | {context: .context, state: .state, target_url: .target_url}]'`, cwd)
75152
75152
  ]);
75153
- if ("error" in checkRunsResult || "error" in statusResult)
75153
+ if ("error" in checkRunsResult || "error" in legacyStatusesResult)
75154
75154
  return null;
75155
75155
  const checkRunsData = JSON.parse(checkRunsResult.stdout.trim());
75156
- const combinedState = statusResult.stdout.trim() || "pending";
75157
- let state = combinedState;
75158
- if (checkRunsData.total_count > 0) {
75159
- const conclusions = checkRunsData.check_runs.map((cr) => cr.conclusion);
75160
- if (conclusions.some((c) => c === "failure" || c === "timed_out")) {
75161
- state = "failure";
75162
- } else if (conclusions.every((c) => c === "success" || c === "skipped" || c === "neutral")) {
75163
- state = "success";
75164
- } else if (checkRunsData.check_runs.some((cr) => cr.status !== "completed")) {
75165
- state = "pending";
75156
+ const legacyStatuses = JSON.parse(legacyStatusesResult.stdout.trim());
75157
+ const modernEntries = checkRunsData.check_runs.map((cr) => ({
75158
+ name: cr.name,
75159
+ status: cr.status,
75160
+ conclusion: cr.conclusion,
75161
+ source: "check-run"
75162
+ }));
75163
+ const legacyEntries = legacyStatuses.map((s) => {
75164
+ let status3;
75165
+ let conclusion;
75166
+ switch (s.state) {
75167
+ case "success":
75168
+ status3 = "completed";
75169
+ conclusion = "success";
75170
+ break;
75171
+ case "failure":
75172
+ case "error":
75173
+ status3 = "completed";
75174
+ conclusion = "failure";
75175
+ break;
75176
+ default:
75177
+ status3 = "in_progress";
75178
+ conclusion = null;
75166
75179
  }
75180
+ return {
75181
+ name: s.context,
75182
+ status: status3,
75183
+ conclusion,
75184
+ source: "status",
75185
+ url: s.target_url ?? null
75186
+ };
75187
+ });
75188
+ const merged = [...modernEntries, ...legacyEntries];
75189
+ const FAILURE_CONCLUSIONS = new Set(["failure", "timed_out", "cancelled", "error"]);
75190
+ let state;
75191
+ if (merged.some((e) => e.conclusion !== null && FAILURE_CONCLUSIONS.has(e.conclusion))) {
75192
+ state = "failure";
75193
+ } else if (merged.some((e) => e.status !== "completed")) {
75194
+ state = "pending";
75195
+ } else {
75196
+ state = "success";
75167
75197
  }
75168
75198
  return {
75169
75199
  state,
75170
- checkRuns: checkRunsData.check_runs.map((cr) => ({
75171
- name: cr.name,
75172
- status: cr.status,
75173
- conclusion: cr.conclusion
75174
- })),
75175
- totalCount: checkRunsData.total_count
75200
+ checkRuns: merged,
75201
+ totalCount: merged.length
75176
75202
  };
75177
75203
  } catch {
75178
75204
  return null;
@@ -75858,6 +75884,9 @@ function evictStalePendingStops() {
75858
75884
  pendingStops.delete(runId);
75859
75885
  }
75860
75886
  }
75887
+ function buildCommandKey(machineId, workingDir, commandName) {
75888
+ return `${machineId}|${workingDir}|${commandName}`;
75889
+ }
75861
75890
  async function reportRunFailed(ctx, runId, reason) {
75862
75891
  try {
75863
75892
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
@@ -75895,9 +75924,42 @@ function appendToBuffer(ctx, tracked, data) {
75895
75924
  flushOutput(ctx, tracked).catch(() => {});
75896
75925
  }
75897
75926
  }
75927
+ function killProcess(child, signal) {
75928
+ try {
75929
+ child.kill(signal);
75930
+ } catch {}
75931
+ }
75932
+ function waitForExit(runIdStr, ms) {
75933
+ return new Promise((resolve5) => {
75934
+ const interval = 100;
75935
+ let elapsed3 = 0;
75936
+ const timer = setInterval(() => {
75937
+ if (!runningProcesses.has(runIdStr)) {
75938
+ clearInterval(timer);
75939
+ resolve5(true);
75940
+ return;
75941
+ }
75942
+ elapsed3 += interval;
75943
+ if (elapsed3 >= ms) {
75944
+ clearInterval(timer);
75945
+ resolve5(false);
75946
+ }
75947
+ }, interval);
75948
+ });
75949
+ }
75950
+ async function killTrackedProcess(tracked) {
75951
+ killProcess(tracked.process, "SIGTERM");
75952
+ const exited = await waitForExit(tracked.runId, SIGTERM_GRACE_PERIOD_MS);
75953
+ if (!exited) {
75954
+ console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing process: ${tracked.runId}`);
75955
+ killProcess(tracked.process, "SIGKILL");
75956
+ await waitForExit(tracked.runId, 1000);
75957
+ }
75958
+ }
75898
75959
  async function onCommandRun(ctx, event) {
75899
75960
  const { workingDir, commandName, script, runId } = event;
75900
75961
  const runIdStr = runId.toString();
75962
+ const commandKey = buildCommandKey(ctx.machineId, workingDir, commandName);
75901
75963
  if (runningProcesses.has(runIdStr)) {
75902
75964
  console.log(`[${formatTimestamp()}] ⚠️ Command already running: ${runIdStr}`);
75903
75965
  return;
@@ -75917,6 +75979,19 @@ async function onCommandRun(ctx, event) {
75917
75979
  }
75918
75980
  return;
75919
75981
  }
75982
+ const priorRunId = runningProcessesByCommand.get(commandKey);
75983
+ if (priorRunId) {
75984
+ const priorTracked = runningProcesses.get(priorRunId);
75985
+ if (priorTracked) {
75986
+ console.log(`[${formatTimestamp()}] \uD83D\uDD04 Replacing prior run ${priorRunId} with ${runIdStr} for ${commandName}`);
75987
+ clearInterval(priorTracked.flushTimer);
75988
+ if (priorTracked.softTimeoutTimer)
75989
+ clearTimeout(priorTracked.softTimeoutTimer);
75990
+ await killTrackedProcess(priorTracked);
75991
+ runningProcesses.delete(priorRunId);
75992
+ runningProcessesByCommand.delete(commandKey);
75993
+ }
75994
+ }
75920
75995
  console.log(`[${formatTimestamp()}] \uD83D\uDE80 Running command: ${commandName} → ${script}`);
75921
75996
  if (!workingDir.startsWith("/")) {
75922
75997
  console.error(`[${formatTimestamp()}] ❌ Rejected command: workingDir is not absolute: ${workingDir}`);
@@ -75933,49 +76008,48 @@ async function onCommandRun(ctx, event) {
75933
76008
  const child = spawn4("sh", ["-c", script], {
75934
76009
  cwd: workingDir,
75935
76010
  env: { ...process.env },
75936
- stdio: ["ignore", "pipe", "pipe"],
75937
- detached: true
76011
+ stdio: ["ignore", "pipe", "pipe"]
75938
76012
  });
75939
- const timeoutTimer = setTimeout(() => {
75940
- console.log(`[${formatTimestamp()}] ⏰ Command timed out after ${DEFAULT_COMMAND_TIMEOUT_MS / 60000} minutes: ${commandName} (runId: ${runIdStr})`);
75941
- const pid = child.pid;
75942
- if (pid) {
75943
- try {
75944
- process.kill(-pid, "SIGTERM");
75945
- } catch {
75946
- child.kill("SIGTERM");
75947
- }
75948
- } else {
75949
- child.kill("SIGTERM");
75950
- }
75951
- setTimeout(() => {
75952
- if (!runningProcesses.has(runIdStr))
75953
- return;
75954
- console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing timed-out process: ${runIdStr}`);
75955
- if (pid) {
75956
- try {
75957
- process.kill(-pid, "SIGKILL");
75958
- } catch {
75959
- child.kill("SIGKILL");
75960
- }
75961
- } else {
75962
- child.kill("SIGKILL");
75963
- }
75964
- }, SIGTERM_GRACE_PERIOD_MS);
75965
- }, DEFAULT_COMMAND_TIMEOUT_MS);
75966
- timeoutTimer.unref?.();
75967
76013
  const tracked = {
75968
76014
  process: child,
75969
76015
  runId: runIdStr,
76016
+ commandKey,
75970
76017
  outputBuffer: "",
75971
76018
  chunkIndex: 0,
75972
76019
  flushTimer: setInterval(() => {
75973
76020
  flushOutput(ctx, tracked).catch(() => {});
75974
76021
  }, OUTPUT_FLUSH_INTERVAL_MS),
75975
- timeoutTimer
76022
+ softTimeoutTimer: null
75976
76023
  };
75977
76024
  tracked.flushTimer.unref?.();
75978
76025
  runningProcesses.set(runIdStr, tracked);
76026
+ runningProcessesByCommand.set(commandKey, runIdStr);
76027
+ const softTimeoutTimer = setTimeout(async () => {
76028
+ console.log(`[${formatTimestamp()}] ⏰ Command soft timeout (24h): ${commandName} (runId: ${runIdStr})`);
76029
+ const currentTracked = runningProcesses.get(runIdStr);
76030
+ if (!currentTracked)
76031
+ return;
76032
+ try {
76033
+ await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
76034
+ sessionId: ctx.sessionId,
76035
+ machineId: ctx.machineId,
76036
+ runId,
76037
+ status: "killed",
76038
+ terminationReason: "timeout-24h"
76039
+ });
76040
+ } catch (err) {
76041
+ console.warn(`[${formatTimestamp()}] ⚠️ Failed to mark run as killed (timeout): ${getErrorMessage(err)}`);
76042
+ }
76043
+ killProcess(child, "SIGTERM");
76044
+ setTimeout(() => {
76045
+ if (!runningProcesses.has(runIdStr))
76046
+ return;
76047
+ console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing timed-out process: ${runIdStr}`);
76048
+ killProcess(child, "SIGKILL");
76049
+ }, SIGTERM_GRACE_PERIOD_MS);
76050
+ }, SOFT_TIMEOUT_MS);
76051
+ softTimeoutTimer.unref?.();
76052
+ tracked.softTimeoutTimer = softTimeoutTimer;
75979
76053
  try {
75980
76054
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
75981
76055
  sessionId: ctx.sessionId,
@@ -75997,9 +76071,12 @@ async function onCommandRun(ctx, event) {
75997
76071
  console.log(`[${formatTimestamp()}] \uD83C\uDFC1 Command exited: ${commandName} (code=${code2}, signal=${signal})`);
75998
76072
  await flushOutput(ctx, tracked).catch(() => {});
75999
76073
  clearInterval(tracked.flushTimer);
76000
- if (tracked.timeoutTimer)
76001
- clearTimeout(tracked.timeoutTimer);
76074
+ if (tracked.softTimeoutTimer)
76075
+ clearTimeout(tracked.softTimeoutTimer);
76002
76076
  runningProcesses.delete(runIdStr);
76077
+ if (runningProcessesByCommand.get(commandKey) === runIdStr) {
76078
+ runningProcessesByCommand.delete(commandKey);
76079
+ }
76003
76080
  const status3 = code2 === 0 ? "completed" : signal ? "stopped" : "failed";
76004
76081
  try {
76005
76082
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
@@ -76016,9 +76093,12 @@ async function onCommandRun(ctx, event) {
76016
76093
  child.on("error", async (err) => {
76017
76094
  console.error(`[${formatTimestamp()}] ❌ Command spawn failed: ${commandName}: ${err.message}`);
76018
76095
  clearInterval(tracked.flushTimer);
76019
- if (tracked.timeoutTimer)
76020
- clearTimeout(tracked.timeoutTimer);
76096
+ if (tracked.softTimeoutTimer)
76097
+ clearTimeout(tracked.softTimeoutTimer);
76021
76098
  runningProcesses.delete(runIdStr);
76099
+ if (runningProcessesByCommand.get(commandKey) === runIdStr) {
76100
+ runningProcessesByCommand.delete(commandKey);
76101
+ }
76022
76102
  try {
76023
76103
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
76024
76104
  sessionId: ctx.sessionId,
@@ -76031,36 +76111,6 @@ async function onCommandRun(ctx, event) {
76031
76111
  }
76032
76112
  });
76033
76113
  }
76034
- function killProcess(child, signal) {
76035
- const pid = child.pid;
76036
- if (pid) {
76037
- try {
76038
- process.kill(-pid, signal);
76039
- return;
76040
- } catch {}
76041
- }
76042
- try {
76043
- child.kill(signal);
76044
- } catch {}
76045
- }
76046
- function waitForExit(runIdStr, ms) {
76047
- return new Promise((resolve5) => {
76048
- const interval = 100;
76049
- let elapsed3 = 0;
76050
- const timer = setInterval(() => {
76051
- if (!runningProcesses.has(runIdStr)) {
76052
- clearInterval(timer);
76053
- resolve5(true);
76054
- return;
76055
- }
76056
- elapsed3 += interval;
76057
- if (elapsed3 >= ms) {
76058
- clearInterval(timer);
76059
- resolve5(false);
76060
- }
76061
- }, interval);
76062
- });
76063
- }
76064
76114
  async function onCommandStop(ctx, event) {
76065
76115
  const runIdStr = event.runId.toString();
76066
76116
  const tracked = runningProcesses.get(runIdStr);
@@ -76081,19 +76131,16 @@ async function onCommandStop(ctx, event) {
76081
76131
  return;
76082
76132
  }
76083
76133
  console.log(`[${formatTimestamp()}] \uD83D\uDED1 Stopping command run: ${runIdStr}`);
76084
- if (tracked.timeoutTimer) {
76085
- clearTimeout(tracked.timeoutTimer);
76086
- tracked.timeoutTimer = null;
76134
+ if (tracked.softTimeoutTimer) {
76135
+ clearTimeout(tracked.softTimeoutTimer);
76136
+ tracked.softTimeoutTimer = null;
76087
76137
  }
76088
76138
  killProcess(tracked.process, "SIGTERM");
76089
76139
  const exitedAfterSigterm = await waitForExit(runIdStr, SIGTERM_GRACE_PERIOD_MS);
76090
76140
  if (!exitedAfterSigterm) {
76091
76141
  console.log(`[${formatTimestamp()}] \uD83D\uDD2A Force-killing process: ${runIdStr}`);
76092
76142
  killProcess(tracked.process, "SIGKILL");
76093
- const exitedAfterSigkill = await waitForExit(runIdStr, 1000);
76094
- if (!exitedAfterSigkill) {
76095
- console.error(`[${formatTimestamp()}] ❌ Failed to stop process for run: ${runIdStr} — process did not exit after SIGKILL`);
76096
- }
76143
+ await waitForExit(runIdStr, 1000);
76097
76144
  }
76098
76145
  try {
76099
76146
  await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
@@ -76110,49 +76157,47 @@ async function shutdownAllCommands(ctx) {
76110
76157
  if (runningProcesses.size === 0)
76111
76158
  return;
76112
76159
  console.log(`[${formatTimestamp()}] Shutting down ${runningProcesses.size} running command(s)...`);
76113
- for (const [, tracked] of runningProcesses) {
76160
+ const trackedEntries = [...runningProcesses.entries()];
76161
+ for (const [, tracked] of trackedEntries) {
76114
76162
  clearInterval(tracked.flushTimer);
76115
- if (tracked.timeoutTimer)
76116
- clearTimeout(tracked.timeoutTimer);
76163
+ if (tracked.softTimeoutTimer)
76164
+ clearTimeout(tracked.softTimeoutTimer);
76117
76165
  await flushOutput(ctx, tracked).catch(() => {});
76118
- const pid = tracked.process.pid;
76119
- if (pid) {
76120
- try {
76121
- process.kill(-pid, "SIGTERM");
76122
- } catch {
76123
- tracked.process.kill("SIGTERM");
76124
- }
76125
- } else {
76126
- tracked.process.kill("SIGTERM");
76166
+ try {
76167
+ await ctx.deps.backend.mutation(api.commands.updateRunStatus, {
76168
+ sessionId: ctx.sessionId,
76169
+ machineId: ctx.machineId,
76170
+ runId: tracked.runId,
76171
+ status: "killed",
76172
+ terminationReason: "daemon-shutdown"
76173
+ });
76174
+ } catch (err) {
76175
+ console.warn(`[${formatTimestamp()}] ⚠️ Failed to mark run as killed on shutdown: ${getErrorMessage(err)}`);
76127
76176
  }
76177
+ killProcess(tracked.process, "SIGTERM");
76128
76178
  }
76129
76179
  await new Promise((resolve5) => {
76130
76180
  const t = setTimeout(resolve5, 3000);
76131
76181
  t.unref?.();
76132
76182
  });
76133
- for (const [, tracked] of runningProcesses) {
76134
- const pid = tracked.process.pid;
76135
- if (pid) {
76136
- try {
76137
- process.kill(-pid, "SIGKILL");
76138
- } catch {}
76139
- } else {
76140
- try {
76141
- tracked.process.kill("SIGKILL");
76142
- } catch {}
76183
+ for (const [, tracked] of trackedEntries) {
76184
+ if (runningProcesses.has(tracked.runId)) {
76185
+ killProcess(tracked.process, "SIGKILL");
76143
76186
  }
76144
76187
  }
76145
76188
  runningProcesses.clear();
76189
+ runningProcessesByCommand.clear();
76146
76190
  console.log(`[${formatTimestamp()}] All commands stopped`);
76147
76191
  }
76148
- var runningProcesses, pendingStops, PENDING_STOP_TTL_MS = 60000, OUTPUT_FLUSH_INTERVAL_MS = 3000, MAX_BUFFER_SIZE, DEFAULT_COMMAND_TIMEOUT_MS, SIGTERM_GRACE_PERIOD_MS = 5000;
76192
+ var runningProcesses, runningProcessesByCommand, pendingStops, PENDING_STOP_TTL_MS = 60000, OUTPUT_FLUSH_INTERVAL_MS = 3000, MAX_BUFFER_SIZE, SOFT_TIMEOUT_MS, SIGTERM_GRACE_PERIOD_MS = 5000;
76149
76193
  var init_command_runner = __esm(() => {
76150
76194
  init_api3();
76151
76195
  init_convex_error();
76152
76196
  runningProcesses = new Map;
76197
+ runningProcessesByCommand = new Map;
76153
76198
  pendingStops = new Map;
76154
76199
  MAX_BUFFER_SIZE = 100 * 1024;
76155
- DEFAULT_COMMAND_TIMEOUT_MS = 30 * 60 * 1000;
76200
+ SOFT_TIMEOUT_MS = 24 * 60 * 60 * 1000;
76156
76201
  });
76157
76202
 
76158
76203
  // src/commands/machine/daemon-start/handlers/ping.ts
@@ -79289,5 +79334,5 @@ program2.hook("preAction", async (_thisCommand, actionCommand) => {
79289
79334
  });
79290
79335
  program2.parse();
79291
79336
 
79292
- //# debugId=A3C30103B208BB9B64756E2164756E21
79337
+ //# debugId=5B0DF926AD89205364756E2164756E21
79293
79338
  //# sourceMappingURL=index.js.map