@rudderhq/cli 0.2.4-canary.0 → 0.2.5-canary.0
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 +162 -9
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
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"
|
|
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
|
-
|
|
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
|
-
|
|
7103
|
+
forceQuit(target);
|
|
7059
7104
|
}
|
|
7060
7105
|
const replacePath = target.platform === "windows" ? paths.installRoot : paths.appPath;
|
|
7061
7106
|
if (await removePathWithRetry(replacePath)) return;
|
|
7062
|
-
|
|
7063
|
-
await delay(
|
|
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
|
|
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
|
-
|
|
12037
|
-
await new Promise((resolve) => process.stdout.once("drain", resolve));
|
|
12038
|
-
}
|
|
12191
|
+
await flushProcessOutputBeforeExit();
|
|
12039
12192
|
process.exit(exitCode);
|
|
12040
12193
|
});
|
|
12041
12194
|
export {
|