@invarn/cli 0.2.1 → 0.2.3

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/sdk.mjs CHANGED
@@ -6995,10 +6995,11 @@ async function cancelBuild(buildId) {
6995
6995
  body: "{}"
6996
6996
  });
6997
6997
  }
6998
- async function registerAgent({ name, type, description, repository, commands, overwrite }) {
6998
+ async function registerAgent({ name, type, description, repository, commands, overwrite, triggers }) {
6999
6999
  const body = { name, type, description, commands };
7000
7000
  if (repository) body.repository = repository;
7001
7001
  if (overwrite) body.overwrite = true;
7002
+ if (Array.isArray(triggers) && triggers.length > 0) body.triggers = triggers;
7002
7003
  const res = await request("/agent/register", {
7003
7004
  method: "POST",
7004
7005
  body: JSON.stringify(body)
@@ -7090,17 +7091,19 @@ async function getAgentBuildResult(sessionToken, buildId, { wait } = {}) {
7090
7091
  }
7091
7092
  return res.json();
7092
7093
  }
7093
- async function listSecrets({ pipelineId, kind } = {}) {
7094
+ async function listSecrets({ pipelineId, agentId, kind } = {}) {
7094
7095
  const url = new URL("/secrets", "http://placeholder");
7095
7096
  if (pipelineId) url.searchParams.set("pipeline", pipelineId);
7097
+ if (agentId) url.searchParams.set("agent", agentId);
7096
7098
  if (kind) url.searchParams.set("kind", kind);
7097
7099
  const res = await request(`${url.pathname}${url.search}`);
7098
7100
  const body = await res.json();
7099
7101
  return body.entries || [];
7100
7102
  }
7101
- async function createSecret({ scope, pipelineId, kind, key, value }) {
7103
+ async function createSecret({ scope, pipelineId, agentId, kind, key, value }) {
7102
7104
  const body = { scope, kind, key, value };
7103
7105
  if (pipelineId) body.pipelineId = pipelineId;
7106
+ if (agentId) body.agentId = agentId;
7104
7107
  const res = await request("/secrets", {
7105
7108
  method: "POST",
7106
7109
  body: JSON.stringify(body)
@@ -8820,7 +8823,8 @@ async function handleAgentRegister(args) {
8820
8823
  description: args.description,
8821
8824
  repository: args.repository,
8822
8825
  commands,
8823
- overwrite: args.overwrite
8826
+ overwrite: args.overwrite,
8827
+ triggers: args.triggers
8824
8828
  });
8825
8829
  if (result.pipelinePath && result.yamlContent) {
8826
8830
  try {
@@ -8969,12 +8973,19 @@ ${lines.join("\n")}`;
8969
8973
  }
8970
8974
  async function handleSecretsList(args) {
8971
8975
  try {
8976
+ if (args.pipeline_id && args.agent_id) {
8977
+ return {
8978
+ content: [{ type: "text", text: "Error: pass either pipeline_id or agent_id, not both." }],
8979
+ isError: true
8980
+ };
8981
+ }
8972
8982
  const entries = await listSecrets({
8973
8983
  pipelineId: args.pipeline_id || void 0,
8984
+ agentId: args.agent_id || void 0,
8974
8985
  kind: args.kind
8975
8986
  });
8976
8987
  if (entries.length === 0) {
8977
- const scope = args.pipeline_id ? "pipeline" : "org";
8988
+ const scope = args.pipeline_id || args.agent_id ? "pipeline" : "org";
8978
8989
  return {
8979
8990
  content: [
8980
8991
  { type: "text", text: `No ${args.kind?.toLowerCase() ?? "entries"} at ${scope} scope.` }
@@ -8994,12 +9005,19 @@ ${lines.join("\n")}` }] };
8994
9005
  }
8995
9006
  async function handleSecretsSet(args) {
8996
9007
  try {
9008
+ if (args.pipeline_id && args.agent_id) {
9009
+ return {
9010
+ content: [{ type: "text", text: "Error: pass either pipeline_id or agent_id, not both." }],
9011
+ isError: true
9012
+ };
9013
+ }
8997
9014
  const kind = args.kind === "VARIABLE" ? "VARIABLE" : "SECRET";
8998
- const scope = args.pipeline_id ? "PIPELINE" : "ORG";
9015
+ const scope = args.pipeline_id || args.agent_id ? "PIPELINE" : "ORG";
8999
9016
  try {
9000
9017
  await createSecret({
9001
9018
  scope,
9002
9019
  pipelineId: args.pipeline_id,
9020
+ agentId: args.agent_id,
9003
9021
  kind,
9004
9022
  key: args.key,
9005
9023
  value: args.value
@@ -9014,7 +9032,11 @@ async function handleSecretsSet(args) {
9014
9032
  };
9015
9033
  } catch (err) {
9016
9034
  if (err.statusCode !== 409) throw err;
9017
- const existing = (await listSecrets({ pipelineId: args.pipeline_id, kind })).find((e) => e.key === args.key);
9035
+ const existing = (await listSecrets({
9036
+ pipelineId: args.pipeline_id,
9037
+ agentId: args.agent_id,
9038
+ kind
9039
+ })).find((e) => e.key === args.key);
9018
9040
  if (!existing) throw err;
9019
9041
  await updateSecretValue(existing.id, args.value);
9020
9042
  return {
@@ -9144,7 +9166,13 @@ var TOOL_SPECS = [
9144
9166
  name: z2.string().describe('Command name (e.g. "compile")'),
9145
9167
  run: z2.string().describe('Shell command to execute (e.g. "./gradlew assembleDebug")')
9146
9168
  })).describe("List of commands this agent can run"),
9147
- overwrite: z2.boolean().optional().describe("Set true to replace a pipeline file that already exists at .ci/pipelines/agent-<name>.yml in the repo. Use this when re-registering an agent \u2014 e.g. the same repo is connected under another org, or a prior registration was deleted without cleaning up the committed file. Do NOT set this when the existing file is hand-authored YAML unrelated to a prior agent.")
9169
+ overwrite: z2.boolean().optional().describe("Set true to replace a pipeline file that already exists at .ci/pipelines/agent-<name>.yml in the repo. Use this when re-registering an agent \u2014 e.g. the same repo is connected under another org, or a prior registration was deleted without cleaning up the committed file. Do NOT set this when the existing file is hand-authored YAML unrelated to a prior agent."),
9170
+ triggers: z2.array(z2.object({
9171
+ push_branch: z2.string().optional().describe('Match push events where the pushed branch matches this pattern (glob). Use "*" for any branch.'),
9172
+ pull_request_source_branch: z2.string().optional().describe("Match PR events where the source (head) branch matches this pattern."),
9173
+ pull_request_target_branch: z2.string().optional().describe("Match PR events where the target (base) branch matches this pattern."),
9174
+ workflow: z2.string().describe('Workflow to run on match \u2014 must be one of the command names, or the combined "a-and-b-and-c" workflow when multiple commands exist.')
9175
+ })).optional().describe("Events that should trigger this agent automatically. Each rule mirrors cibuild.yml trigger_map entries. Omit to keep the agent manual-only (runnable only via invarn_agent_run). See docs/AGENTS-ROADMAP.md item #2.")
9148
9176
  },
9149
9177
  handler: async (args) => handleAgentRegister(args)
9150
9178
  },
@@ -9183,16 +9211,17 @@ var TOOL_SPECS = [
9183
9211
  },
9184
9212
  {
9185
9213
  name: "invarn_secrets_list",
9186
- description: "List secret and variable metadata for the organization or a specific pipeline. Returns keys, kinds, scopes, and timestamps \u2014 NEVER values. Use this to check whether a secret exists before calling invarn_agent_run.",
9214
+ description: "List secret and variable metadata for the organization or a specific pipeline. Returns keys, kinds, scopes, and timestamps \u2014 NEVER values. Use this to check whether a secret exists before calling invarn_agent_run. To scope to an agent's pipeline, pass agent_id \u2014 simpler than looking up pipeline_id.",
9187
9215
  inputSchema: {
9188
- pipeline_id: z2.string().optional().describe("Pipeline ID. Omit for org-scoped entries."),
9216
+ pipeline_id: z2.string().optional().describe("Pipeline ID. Omit for org-scoped entries. Mutually exclusive with agent_id."),
9217
+ agent_id: z2.string().optional().describe("Agent ID \u2014 resolves to the agent's owned pipeline server-side. Mutually exclusive with pipeline_id."),
9189
9218
  kind: z2.enum(["SECRET", "VARIABLE"]).optional().describe("Filter to a specific kind.")
9190
9219
  },
9191
9220
  handler: async (args) => handleSecretsList(args ?? {})
9192
9221
  },
9193
9222
  {
9194
9223
  name: "invarn_secrets_set",
9195
- description: "Create or update a secret or variable. Idempotent \u2014 if the key already exists at this scope, the value is replaced. Requires a human (inv_human_\u2026) token with OWNER or ADMIN role in the org; agent tokens receive 403. There is no read tool \u2014 values cannot be retrieved via MCP.",
9224
+ description: "Create or update a secret or variable. Idempotent \u2014 if the key already exists at this scope, the value is replaced. Requires a human (inv_human_\u2026) token with OWNER or ADMIN role in the org; agent tokens receive 403. There is no read tool \u2014 values cannot be retrieved via MCP. To target an agent's pipeline, pass agent_id instead of pipeline_id.",
9196
9225
  inputSchema: {
9197
9226
  key: z2.string().describe(
9198
9227
  "Uppercase env-var-safe key (/^[A-Z_][A-Z0-9_]{0,63}$/, not starting with INVARN_)."
@@ -9201,7 +9230,8 @@ var TOOL_SPECS = [
9201
9230
  kind: z2.enum(["SECRET", "VARIABLE"]).describe(
9202
9231
  "SECRET: encrypted at rest, redacted from build logs. VARIABLE: plaintext, visible in dashboard and logs when echoed."
9203
9232
  ),
9204
- pipeline_id: z2.string().optional().describe("Pipeline ID for a pipeline-scoped entry. Omit for org scope.")
9233
+ pipeline_id: z2.string().optional().describe("Pipeline ID for a pipeline-scoped entry. Omit for org scope. Mutually exclusive with agent_id."),
9234
+ agent_id: z2.string().optional().describe("Agent ID \u2014 resolves to the agent's owned pipeline server-side. Mutually exclusive with pipeline_id. Omit both for org scope.")
9205
9235
  },
9206
9236
  handler: async (args) => handleSecretsSet(args)
9207
9237
  }
@@ -9311,19 +9341,27 @@ function buildHandlers(ctx, getSession) {
9311
9341
  return result;
9312
9342
  },
9313
9343
  invarn_secrets_list: async (args) => {
9344
+ if (args.pipeline_id && args.agent_id) {
9345
+ throw new Error("pass either pipeline_id or agent_id, not both");
9346
+ }
9314
9347
  const entries = await listSecrets({
9315
9348
  pipelineId: args.pipeline_id || void 0,
9349
+ agentId: args.agent_id || void 0,
9316
9350
  kind: args.kind
9317
9351
  });
9318
9352
  return { entries };
9319
9353
  },
9320
9354
  invarn_secrets_set: async (args) => {
9355
+ if (args.pipeline_id && args.agent_id) {
9356
+ throw new Error("pass either pipeline_id or agent_id, not both");
9357
+ }
9321
9358
  const kind = args.kind === "VARIABLE" ? "VARIABLE" : "SECRET";
9322
- const scope = args.pipeline_id ? "PIPELINE" : "ORG";
9359
+ const scope = args.pipeline_id || args.agent_id ? "PIPELINE" : "ORG";
9323
9360
  try {
9324
9361
  await createSecret({
9325
9362
  scope,
9326
9363
  pipelineId: args.pipeline_id,
9364
+ agentId: args.agent_id,
9327
9365
  kind,
9328
9366
  key: args.key,
9329
9367
  value: args.value
@@ -9331,7 +9369,11 @@ function buildHandlers(ctx, getSession) {
9331
9369
  return { action: "created", key: args.key, kind, scope: scope.toLowerCase() };
9332
9370
  } catch (err) {
9333
9371
  if (err.statusCode !== 409) throw err;
9334
- const existing = (await listSecrets({ pipelineId: args.pipeline_id, kind })).find((e) => e.key === args.key);
9372
+ const existing = (await listSecrets({
9373
+ pipelineId: args.pipeline_id,
9374
+ agentId: args.agent_id,
9375
+ kind
9376
+ })).find((e) => e.key === args.key);
9335
9377
  if (!existing) throw err;
9336
9378
  await updateSecretValue(existing.id, args.value);
9337
9379
  return { action: "updated", key: args.key, kind, scope: scope.toLowerCase() };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invarn/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Invarn CLI — run builds, check artifacts, and ship from your terminal",
5
5
  "type": "module",
6
6
  "main": "dist/invarn.cjs",