@rudderhq/cli 0.2.4-canary.0 → 0.2.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
@@ -155,7 +155,7 @@ var init_constants = __esm({
155
155
  "deliverable",
156
156
  "background"
157
157
  ];
158
- AUTOMATION_STATUSES = ["active", "paused", "archived"];
158
+ AUTOMATION_STATUSES = ["active", "paused"];
159
159
  AUTOMATION_CONCURRENCY_POLICIES = ["coalesce_if_active", "always_enqueue", "skip_if_active"];
160
160
  AUTOMATION_CATCH_UP_POLICIES = ["skip_missed", "enqueue_missed_with_cap"];
161
161
  AUTOMATION_TRIGGER_SIGNING_MODES = ["bearer", "hmac_sha256"];
@@ -6472,6 +6472,8 @@ var DESKTOP_UPDATE_QUIT_ARG = "--rudder-update-quit";
6472
6472
  var STABLE_SEMVER_RE = /^[0-9]+\.[0-9]+\.[0-9]+$/;
6473
6473
  var CANARY_SEMVER_RE = /^[0-9]+\.[0-9]+\.[0-9]+-canary\.[0-9]+$/;
6474
6474
  var CLI_REGISTRY_LATEST_URL = "https://registry.npmjs.org/@rudderhq%2fcli/latest";
6475
+ var LEGACY_UPDATE_QUIT_GRACE_MS = 1e4;
6476
+ var UPDATE_QUIT_FORCE_DELAY_MS = 1e3;
6475
6477
  var DESKTOP_APP_NAME = "Rudder";
6476
6478
  var DESKTOP_METADATA_FILE = ".rudder-desktop-install.json";
6477
6479
  var DESKTOP_CHECKSUM_ASSET_NAME = "SHASUMS256.txt";
@@ -7026,6 +7028,30 @@ async function requestDesktopQuit(executablePath, target) {
7026
7028
  await rm2(responsePath, { force: true });
7027
7029
  }
7028
7030
  }
7031
+ function processExists(pid) {
7032
+ try {
7033
+ process.kill(pid, 0);
7034
+ return true;
7035
+ } catch (error) {
7036
+ const code = typeof error === "object" && error && "code" in error ? String(error.code) : "";
7037
+ return code === "EPERM";
7038
+ }
7039
+ }
7040
+ function readUpdateQuitPid(response) {
7041
+ if (!response?.ok || response.status !== "quitting") return null;
7042
+ return typeof response.pid === "number" && Number.isInteger(response.pid) && response.pid > 0 ? response.pid : null;
7043
+ }
7044
+ function isLegacyUnconfirmedUpdateQuit(response) {
7045
+ return Boolean(response?.ok && response.status === "quitting" && !readUpdateQuitPid(response));
7046
+ }
7047
+ async function waitForProcessExit(pid, timeoutMs = 2e4, intervalMs = 250) {
7048
+ const startedAt = Date.now();
7049
+ while (Date.now() - startedAt < timeoutMs) {
7050
+ if (!processExists(pid)) return true;
7051
+ await delay(intervalMs);
7052
+ }
7053
+ return !processExists(pid);
7054
+ }
7029
7055
  async function removePathWithRetry(targetPath, attempts = 5) {
7030
7056
  for (let attempt = 0; attempt < attempts; attempt += 1) {
7031
7057
  try {
@@ -7038,6 +7064,7 @@ async function removePathWithRetry(targetPath, attempts = 5) {
7038
7064
  return false;
7039
7065
  }
7040
7066
  async function prepareForDesktopReplace(paths, target, options = {}) {
7067
+ const forceQuit = options.forceQuitDesktopProcesses ?? forceQuitDesktopProcesses;
7041
7068
  const hasManagedExecutable = await pathExists(paths.executablePath);
7042
7069
  if (hasManagedExecutable) {
7043
7070
  let quitResponse = await requestDesktopQuit(paths.executablePath, target);
@@ -7053,14 +7080,32 @@ async function prepareForDesktopReplace(paths, target, options = {}) {
7053
7080
  `Rudder Desktop has ${quitResponse.totalRuns} active run${quitResponse.totalRuns === 1 ? "" : "s"}. Stop active work, then rerun start.`
7054
7081
  );
7055
7082
  }
7056
- await delay(1e3);
7083
+ const quitPid = readUpdateQuitPid(quitResponse);
7084
+ if (quitPid) {
7085
+ p13.log.info(`Waiting for existing Rudder Desktop process ${quitPid} to exit before replacing it.`);
7086
+ if (!await waitForProcessExit(quitPid)) {
7087
+ p13.log.warn(`Rudder Desktop process ${quitPid} did not exit in time; attempting force-quit fallback.`);
7088
+ forceQuit(target);
7089
+ await delay(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
7090
+ }
7091
+ } else if (isLegacyUnconfirmedUpdateQuit(quitResponse)) {
7092
+ const graceMs = options.legacyUpdateQuitGraceMs ?? LEGACY_UPDATE_QUIT_GRACE_MS;
7093
+ p13.log.warn(
7094
+ `Existing Rudder Desktop acknowledged update quit without a process id; waiting ${Math.ceil(graceMs / 1e3)}s before force-quit fallback.`
7095
+ );
7096
+ await delay(graceMs);
7097
+ forceQuit(target);
7098
+ await delay(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
7099
+ } else {
7100
+ await delay(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
7101
+ }
7057
7102
  } else if (!isRunningInsideDesktopExecutable()) {
7058
- forceQuitDesktopProcesses(target);
7103
+ forceQuit(target);
7059
7104
  }
7060
7105
  const replacePath = target.platform === "windows" ? paths.installRoot : paths.appPath;
7061
7106
  if (await removePathWithRetry(replacePath)) return;
7062
- forceQuitDesktopProcesses(target);
7063
- await delay(1e3);
7107
+ forceQuit(target);
7108
+ await delay(options.updateQuitForceDelayMs ?? UPDATE_QUIT_FORCE_DELAY_MS);
7064
7109
  if (await removePathWithRetry(replacePath, 6)) return;
7065
7110
  throw new Error(`Failed to replace existing Rudder Desktop at ${replacePath}. Close Rudder and rerun start.`);
7066
7111
  }
@@ -9780,6 +9825,18 @@ var AGENT_CLI_CAPABILITIES = [
9780
9825
  requiresRunId: false,
9781
9826
  attachesRunIdWhenAvailable: false
9782
9827
  },
9828
+ {
9829
+ id: "agent.skills.create",
9830
+ command: "rudder agent skills create [agent-id] --name <name> [--enable]",
9831
+ category: "agent",
9832
+ description: "Create an agent-private skill package under AGENT_HOME/skills.",
9833
+ mutating: true,
9834
+ contract: "agent-v1",
9835
+ requiresOrgId: false,
9836
+ requiresAgentId: false,
9837
+ requiresRunId: false,
9838
+ attachesRunIdWhenAvailable: true
9839
+ },
9783
9840
  {
9784
9841
  id: "agent.skills.enable",
9785
9842
  command: "rudder agent skills enable <agent-id> <selection-ref...>",
@@ -9868,7 +9925,7 @@ var AGENT_CLI_CAPABILITIES = [
9868
9925
  id: "agent.icons",
9869
9926
  command: "rudder agent icons",
9870
9927
  category: "agent",
9871
- description: "List allowed agent icon names for create and hire payloads.",
9928
+ description: "List legacy named agent icons for compatibility/debugging; normal create and hire payloads should omit icon.",
9872
9929
  mutating: false,
9873
9930
  contract: "compat",
9874
9931
  requiresOrgId: false,
@@ -10394,6 +10451,9 @@ function registerIssueCommands(program) {
10394
10451
  reopen: opts.reopen
10395
10452
  });
10396
10453
  const comment = await ctx.api.post(`/api/issues/${issueId}/comments`, payload);
10454
+ if (!isIssueCommentResponse(comment)) {
10455
+ throw new Error("Issue comment request completed without a persisted comment response");
10456
+ }
10397
10457
  printOutput(comment, { json: ctx.json });
10398
10458
  } catch (err) {
10399
10459
  handleCommandError(err);
@@ -10681,6 +10741,11 @@ function parseReviewDecision(value) {
10681
10741
  }
10682
10742
  throw new Error("Invalid review decision. Use approve, request_changes, needs_followup, or blocked.");
10683
10743
  }
10744
+ function isIssueCommentResponse(value) {
10745
+ return Boolean(
10746
+ value && typeof value.id === "string" && value.id.trim().length > 0 && typeof value.issueId === "string" && value.issueId.trim().length > 0 && typeof value.body === "string"
10747
+ );
10748
+ }
10684
10749
  function filterIssueRows(rows, match) {
10685
10750
  if (!match?.trim()) return rows;
10686
10751
  const needle = match.trim().toLowerCase();
@@ -11094,6 +11159,58 @@ function registerAgentCommands(program) {
11094
11159
  })
11095
11160
  );
11096
11161
  const skills = agent.command("skills").description("Agent skill selection operations");
11162
+ addCommonClientOptions(
11163
+ skills.command("create").description(getAgentCliCapabilityById("agent.skills.create").description).argument("[agent-id]", "Agent ID; defaults to RUDDER_AGENT_ID").requiredOption("--name <name>", "Skill display name").option("--slug <slug>", "Skill short name").option("--description <text>", "Skill description").option("--markdown <markdown>", "Full SKILL.md content").option("--markdown-file <path>", "Read full SKILL.md content from a local file").option("--enable", "Enable the created private skill for future runs").action(async (agentIdArg, opts) => {
11164
+ try {
11165
+ const ctx = resolveCommandContext(opts);
11166
+ const agentId = agentIdArg?.trim() || ctx.agentId;
11167
+ if (!agentId) {
11168
+ throw new Error("Agent ID is required. Pass [agent-id] or set RUDDER_AGENT_ID.");
11169
+ }
11170
+ if (opts.markdown && opts.markdownFile) {
11171
+ throw new Error("Pass only one of --markdown or --markdown-file.");
11172
+ }
11173
+ const markdown = opts.markdownFile ? await fs14.readFile(path18.resolve(opts.markdownFile), "utf8") : opts.markdown;
11174
+ const payload = organizationSkillCreateSchema.parse({
11175
+ name: opts.name,
11176
+ slug: opts.slug?.trim() || null,
11177
+ description: opts.description?.trim() || null,
11178
+ markdown: markdown ?? null
11179
+ });
11180
+ const created = await ctx.api.post(
11181
+ `/api/agents/${encodeURIComponent(agentId)}/skills/private`,
11182
+ payload
11183
+ );
11184
+ if (!created) {
11185
+ throw new Error("Failed to create agent skill");
11186
+ }
11187
+ let enabledSnapshot = null;
11188
+ if (opts.enable) {
11189
+ enabledSnapshot = await ctx.api.post(
11190
+ `/api/agents/${encodeURIComponent(agentId)}/skills/enable`,
11191
+ { skills: [created.selectionKey] }
11192
+ );
11193
+ }
11194
+ const result = { created, enabledSnapshot };
11195
+ if (ctx.json) {
11196
+ printOutput(result, { json: true });
11197
+ return;
11198
+ }
11199
+ console.log(
11200
+ formatInlineRecord({
11201
+ key: created.key,
11202
+ selectionKey: created.selectionKey,
11203
+ sourceClass: created.sourceClass,
11204
+ sourcePath: created.sourcePath,
11205
+ enabled: Boolean(enabledSnapshot?.desiredSkills.includes(created.selectionKey)),
11206
+ detail: opts.enable ? "Created and enabled. Future runs will load this skill." : "Created, not enabled. Future runs will not load it until enabled."
11207
+ })
11208
+ );
11209
+ } catch (err) {
11210
+ handleCommandError(err);
11211
+ }
11212
+ })
11213
+ );
11097
11214
  addCommonClientOptions(
11098
11215
  skills.command("enable").description(getAgentCliCapabilityById("agent.skills.enable").description).argument("<agentId>", "Agent ID").argument("<selectionRefs...>", "Skill selection refs to enable").action(async (agentId, selectionRefs, opts) => {
11099
11216
  try {
@@ -12031,11 +12148,47 @@ async function runCli(argv = process.argv) {
12031
12148
  }
12032
12149
  }
12033
12150
 
12151
+ // src/stdio.ts
12152
+ async function flushWritableStream(stream) {
12153
+ if (stream.destroyed || stream.writable === false) return;
12154
+ await new Promise((resolve, reject) => {
12155
+ let settled = false;
12156
+ const cleanup = () => {
12157
+ stream.off?.("error", onError);
12158
+ };
12159
+ const finish = (error) => {
12160
+ if (settled) return;
12161
+ settled = true;
12162
+ cleanup();
12163
+ if (error && !isBrokenPipeError(error)) reject(error);
12164
+ else resolve();
12165
+ };
12166
+ const onError = (error) => {
12167
+ finish(error);
12168
+ };
12169
+ stream.once?.("error", onError);
12170
+ try {
12171
+ stream.write("", finish);
12172
+ } catch (error) {
12173
+ finish(error instanceof Error ? error : new Error(String(error)));
12174
+ }
12175
+ });
12176
+ }
12177
+ async function flushProcessOutputBeforeExit(streams = {}) {
12178
+ const stdout = streams.stdout ?? process.stdout;
12179
+ const stderr = streams.stderr ?? process.stderr;
12180
+ await Promise.all([
12181
+ flushWritableStream(stdout),
12182
+ flushWritableStream(stderr)
12183
+ ]);
12184
+ }
12185
+ function isBrokenPipeError(error) {
12186
+ return error.code === "EPIPE";
12187
+ }
12188
+
12034
12189
  // src/index.ts
12035
12190
  void runCli(process.argv).then(async (exitCode) => {
12036
- if (process.stdout.writableNeedDrain) {
12037
- await new Promise((resolve) => process.stdout.once("drain", resolve));
12038
- }
12191
+ await flushProcessOutputBeforeExit();
12039
12192
  process.exit(exitCode);
12040
12193
  });
12041
12194
  export {