@ricsam/r5dctl 0.0.127 → 0.0.128

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ricsam/r5dctl
2
2
 
3
- CLI for managing r5d.dev platform resources (projects, branches, and sessions).
3
+ CLI for managing r5d.dev account instructions, environment variables, secret files, projects, branches, and sessions.
4
4
 
5
5
  ## Installation
6
6
 
@@ -39,6 +39,40 @@ Credentials are stored in:
39
39
  - `-b, --branch <branch>`
40
40
  - `-s, --session <sessionId>`
41
41
 
42
+ ## Account instructions and credentials
43
+
44
+ The same commands work for authenticated users and agents. They manage the authenticated account across all projects, workspaces, and workers. Project, branch, and session flags do not narrow their scope.
45
+
46
+ ```bash
47
+ r5dctl account instructions get > AGENTS.md
48
+ r5dctl account instructions set --file AGENTS.md
49
+ cat AGENTS.md | r5dctl account instructions set --stdin
50
+ printf '' | r5dctl account instructions set --stdin # clear account instructions
51
+ r5dctl --json account instructions get
52
+
53
+ r5dctl account env list
54
+ r5dctl account env set API_TOKEN --description "Service API access" --file ./api-token.txt
55
+ r5dctl account env set REGION --description "Default region" --value eu-north-1
56
+ cat ./api-token.txt | r5dctl account env set API_TOKEN --stdin
57
+ r5dctl account env set API_TOKEN --description "Updated purpose" # preserve its value
58
+ r5dctl account env revoke API_TOKEN # remove the value, retain its metadata
59
+ r5dctl account env delete API_TOKEN # remove the entry
60
+
61
+ r5dctl account secrets list
62
+ r5dctl account secrets set KUBECONFIG --description "Personal Kubernetes access" --file ~/.kube/config
63
+ cat ./private-key.pem | r5dctl account secrets set PRIVATE_KEY_FILE --description "Signing key" --stdin
64
+ r5dctl account secrets revoke PRIVATE_KEY_FILE
65
+ r5dctl account secrets delete PRIVATE_KEY_FILE
66
+ ```
67
+
68
+ `instructions get` writes the saved AGENTS.md content exactly, with no extra newline. With `--json`, it returns an object containing `content` and `updatedAt`. Setting instructions requires exactly one of `--file` or `--stdin`; empty input clears them. File and stdin input must be UTF-8, and leading/trailing whitespace, line endings, and a UTF-8 BOM are preserved. Stdin is read only with an explicit `--stdin` flag.
69
+
70
+ `env` entries are delivered as environment variable values. `secrets` entries are delivered as protected files, with each entry's environment variable containing the file path. Both kinds are encrypted at rest. Their names share one account namespace and must be valid environment variable names, at most 128 characters; `R5D_` and `R5DCTL_` prefixes are reserved. The CLI refuses to change an entry's kind through the other command group.
71
+
72
+ New entries require `--description` and one value source. Existing entries preserve their description when the flag is omitted, and preserve their value when only a description is supplied. `--value` is supported only for `env`; use `--file` or `--stdin` for secret files and sensitive environment values so contents are absent from shell arguments and history. Explicit empty input sets an empty value; use `revoke` to remove the value. Lists show metadata and whether a value is configured, including under `--json`; neither lists nor mutation responses print values. There is no command to read stored credential values.
73
+
74
+ Updates and revocations affect future command delivery. Existing commands and interactive shells keep their current environment until they finish or are restarted. Revoke does not invalidate a credential at its provider or remove a copy already retained by a process; rotate the provider credential when that is needed.
75
+
42
76
  ## Common Commands
43
77
 
44
78
  ```bash
package/dist/cjs/cli.cjs CHANGED
@@ -116,6 +116,7 @@ var import_promises = require("node:timers/promises");
116
116
  var import_dotenv = require("dotenv");
117
117
  var import_r5d_api = require("@ricsam/r5d-api");
118
118
  var import_shell = require("./shell.cjs");
119
+ var import_account = require("./account.cjs");
119
120
  const CHAT_MODES = /* @__PURE__ */ new Set(["plan", "agent", "large_diff_remediation"]);
120
121
  const R5DCTL_SESSION_MODES = /* @__PURE__ */ new Set(["plan", "agent"]);
121
122
  const R5DCTL_PROMPT_DELIVERIES = /* @__PURE__ */ new Set(["queue", "steer", "force"]);
@@ -199,6 +200,7 @@ const SESSION_RUN_HELP_TEXT = [
199
200
  ""
200
201
  ].join("\n");
201
202
  const SHARED_HELP_ENTRIES = [
203
+ ...import_account.ACCOUNT_HELP_ENTRIES.map((entry) => ({ ...entry, section: "account" })),
202
204
  { section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
203
205
  { section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
204
206
  {
@@ -377,6 +379,7 @@ const SHARED_HELP_ENTRIES = [
377
379
  ];
378
380
  const HELP_SECTION_ORDER = [
379
381
  "auth",
382
+ "account",
380
383
  "workspace",
381
384
  "projects",
382
385
  "branches",
@@ -387,6 +390,7 @@ const HELP_SECTION_ORDER = [
387
390
  ];
388
391
  const HELP_SECTION_TITLES = {
389
392
  auth: "Auth",
393
+ account: "Account instructions, environment variables, and secret files",
390
394
  workspace: "Canonical workspace",
391
395
  projects: "Projects",
392
396
  branches: "Branches",
@@ -715,7 +719,10 @@ async function deleteBranchThroughPlatform(client, projectRef, branchName, optio
715
719
  const branchId = branch.branchId;
716
720
  let accepted;
717
721
  try {
718
- accepted = await requireServerRouteSupport("delete branch", () => client.projects.branches.delete(projectRef, branchName, { branchId }));
722
+ accepted = await requireServerRouteSupport(
723
+ "delete branch",
724
+ () => client.projects.branches.delete(projectRef, branchName, { branchId })
725
+ );
719
726
  } catch (error) {
720
727
  const landed = error instanceof import_r5d_api.R5dctlApiError ? await resolveUncertainAcceptance(client, projectRef, branchId, error) : null;
721
728
  if (!landed) throw error;
@@ -1056,12 +1063,31 @@ function parseGlobalArgs(argv) {
1056
1063
  version: false
1057
1064
  };
1058
1065
  const rest = [];
1066
+ const seenGlobalFlags = /* @__PURE__ */ new Set();
1067
+ let duplicateGlobalFlag;
1059
1068
  for (let i = 0; i < argv.length; i += 1) {
1060
1069
  const arg = argv[i];
1070
+ if (rest[0] === "account" && (arg === "--value" || arg === "--file" || arg === "--description")) {
1071
+ rest.push(arg);
1072
+ if (i + 1 < argv.length) rest.push(argv[++i]);
1073
+ continue;
1074
+ }
1075
+ if (rest[0] === "account" && arg === "") {
1076
+ rest.push(arg);
1077
+ continue;
1078
+ }
1061
1079
  if (!arg) {
1062
1080
  continue;
1063
1081
  }
1082
+ const globalName = { "-p": "--project", "-b": "--branch", "-s": "--session", "-h": "--help", "-v": "--version" }[arg] ?? arg.split("=", 1)[0];
1083
+ if (["--base-url", "--json", "--config", "--token", "--api-key", "--project", "--branch", "--session", "--help", "--version"].includes(
1084
+ globalName
1085
+ )) {
1086
+ if (seenGlobalFlags.has(globalName)) duplicateGlobalFlag ??= globalName;
1087
+ seenGlobalFlags.add(globalName);
1088
+ }
1064
1089
  if (arg === "--") {
1090
+ if (rest[0] === "account") rest.push(arg);
1065
1091
  rest.push(...argv.slice(i + 1));
1066
1092
  break;
1067
1093
  }
@@ -1142,6 +1168,7 @@ function parseGlobalArgs(argv) {
1142
1168
  }
1143
1169
  rest.push(arg);
1144
1170
  }
1171
+ if (rest[0] === "account" && duplicateGlobalFlag) throw new Error(`${duplicateGlobalFlag} may only be provided once`);
1145
1172
  return { options, rest };
1146
1173
  }
1147
1174
  function parseConfig(configPath) {
@@ -1820,7 +1847,9 @@ function renderSessionList(sessions, nowMs = Date.now()) {
1820
1847
  if (sessions.length === 0) {
1821
1848
  return "No sessions found.\n";
1822
1849
  }
1823
- return `${sessions.map((session) => [session.id, session.branchName, formatSessionActivity(session, nowMs), session.name ?? "(unnamed)"].filter(Boolean).join(" ")).join("\n")}
1850
+ return `${sessions.map(
1851
+ (session) => [session.id, session.branchName, formatSessionActivity(session, nowMs), session.name ?? "(unnamed)"].filter(Boolean).join(" ")
1852
+ ).join("\n")}
1824
1853
  `;
1825
1854
  }
1826
1855
  function renderRecentSessionList(sessions) {
@@ -2853,7 +2882,9 @@ async function executeR5dctlCommand(client, json, args) {
2853
2882
  });
2854
2883
  write(result2, renderSessionClaimResponse(result2, nowMs));
2855
2884
  if (result2.outcome === "held_by_other") {
2856
- throw new Error(`Claim ${claim.topic} is held by session ${result2.claim.holderSessionId} until ${result2.claim.expiresAt}; not acquired`);
2885
+ throw new Error(
2886
+ `Claim ${claim.topic} is held by session ${result2.claim.holderSessionId} until ${result2.claim.expiresAt}; not acquired`
2887
+ );
2857
2888
  }
2858
2889
  return;
2859
2890
  }
@@ -2873,12 +2904,20 @@ async function executeR5dctlCommand(client, json, args) {
2873
2904
  return;
2874
2905
  }
2875
2906
  if (!current.heldByActor) {
2876
- write({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, renderSessionClaimResponse({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, nowMs));
2907
+ write(
2908
+ { outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current },
2909
+ renderSessionClaimResponse({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, nowMs)
2910
+ );
2877
2911
  throw new Error(`Claim ${claim.topic} is held by session ${current.holderSessionId}, not by ${list.actorSessionId}`);
2878
2912
  }
2879
2913
  generation = current.generation;
2880
2914
  }
2881
- const result = claim.action === "renew" ? await client.claims.renew({ topic: claim.topic, ...scope, generation, ...claim.ttlSeconds !== void 0 ? { ttlSeconds: claim.ttlSeconds } : {} }) : await client.claims.release({ topic: claim.topic, ...scope, generation });
2915
+ const result = claim.action === "renew" ? await client.claims.renew({
2916
+ topic: claim.topic,
2917
+ ...scope,
2918
+ generation,
2919
+ ...claim.ttlSeconds !== void 0 ? { ttlSeconds: claim.ttlSeconds } : {}
2920
+ }) : await client.claims.release({ topic: claim.topic, ...scope, generation });
2882
2921
  write(result, renderSessionClaimResponse(result, nowMs));
2883
2922
  const succeeded = result.outcome === "renewed" || result.outcome === "released" || claim.action === "release" && result.outcome === "missing";
2884
2923
  if (!succeeded) throw new Error(`Claim ${claim.topic}: ${CLAIM_OUTCOME_TEXT[result.outcome] ?? result.outcome}`);
@@ -2995,7 +3034,10 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
2995
3034
  const offset = first === "describe" ? 2 : 3;
2996
3035
  const projectRef = requireValue(args[offset], "Missing project reference");
2997
3036
  const branchId = requireValue(args[offset + 1], "Missing branch id");
2998
- const status = await requireServerRouteSupport("describe branch-deletion", () => client.projects.branches.describeDeletion(projectRef, branchId));
3037
+ const status = await requireServerRouteSupport(
3038
+ "describe branch-deletion",
3039
+ () => client.projects.branches.describeDeletion(projectRef, branchId)
3040
+ );
2999
3041
  write(status, renderBranchDeletionStatus(status));
3000
3042
  return;
3001
3043
  }
@@ -3140,13 +3182,20 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
3140
3182
  throw new Error(`Unknown command: ${args.join(" ")}`);
3141
3183
  }
3142
3184
  function resolveCommandExecution(options, rest) {
3143
- const trailingHelp = isHelpFlag(rest.at(-1));
3185
+ const accountLiteralHelp = rest[0] === "account" && ["--value", "--file", "--description"].includes(rest.at(-2) ?? "");
3186
+ const trailingHelp = isHelpFlag(rest.at(-1)) && !accountLiteralHelp;
3144
3187
  const normalizedRest = trailingHelp ? rest.slice(0, -1) : rest;
3145
3188
  const command = normalizedRest[0];
3146
3189
  if (!command) {
3147
3190
  throw new Error("Missing command");
3148
3191
  }
3149
3192
  const commandArgs = normalizedRest.slice(1);
3193
+ if (command === "account") {
3194
+ if (trailingHelp || commandArgs.length <= 1) {
3195
+ return { kind: "cli-help", text: (0, import_account.getAccountHelp)(commandArgs.slice(0, 2)) };
3196
+ }
3197
+ return { kind: "account", command: (0, import_account.parseAccountCommand)(commandArgs) };
3198
+ }
3150
3199
  if (command === "ps" && commandArgs.length === 0) {
3151
3200
  return {
3152
3201
  kind: "cli-help",
@@ -3658,6 +3707,10 @@ async function runCommand(argv) {
3658
3707
  const clientOptions = resolveClientOptions(options, config);
3659
3708
  const client = new import_r5d_api.R5dctlClient(clientOptions);
3660
3709
  const execution = resolveCommandExecution(options, rest);
3710
+ if (execution.kind === "account") {
3711
+ await (0, import_account.executeAccountCommand)(client, execution.command, options.json);
3712
+ return 0;
3713
+ }
3661
3714
  if (execution.kind === "auth-login") {
3662
3715
  await handleAuthLogin(client, options, execution.commandArgs, config);
3663
3716
  return 0;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.127",
3
+ "version": "0.0.128",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/cli.mjs CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  R5dctlClient
12
12
  } from "@ricsam/r5d-api";
13
13
  import { runR5dctlShell } from "./shell.mjs";
14
+ import { ACCOUNT_HELP_ENTRIES, executeAccountCommand, getAccountHelp, parseAccountCommand } from "./account.mjs";
14
15
  const CHAT_MODES = /* @__PURE__ */ new Set(["plan", "agent", "large_diff_remediation"]);
15
16
  const R5DCTL_SESSION_MODES = /* @__PURE__ */ new Set(["plan", "agent"]);
16
17
  const R5DCTL_PROMPT_DELIVERIES = /* @__PURE__ */ new Set(["queue", "steer", "force"]);
@@ -94,6 +95,7 @@ const SESSION_RUN_HELP_TEXT = [
94
95
  ""
95
96
  ].join("\n");
96
97
  const SHARED_HELP_ENTRIES = [
98
+ ...ACCOUNT_HELP_ENTRIES.map((entry) => ({ ...entry, section: "account" })),
97
99
  { section: "auth", usage: "auth status", description: "Show the current authenticated user and credential source." },
98
100
  { section: "auth", usage: "auth logout", description: "Revoke the current credential and clear saved auth." },
99
101
  {
@@ -272,6 +274,7 @@ const SHARED_HELP_ENTRIES = [
272
274
  ];
273
275
  const HELP_SECTION_ORDER = [
274
276
  "auth",
277
+ "account",
275
278
  "workspace",
276
279
  "projects",
277
280
  "branches",
@@ -282,6 +285,7 @@ const HELP_SECTION_ORDER = [
282
285
  ];
283
286
  const HELP_SECTION_TITLES = {
284
287
  auth: "Auth",
288
+ account: "Account instructions, environment variables, and secret files",
285
289
  workspace: "Canonical workspace",
286
290
  projects: "Projects",
287
291
  branches: "Branches",
@@ -610,7 +614,10 @@ async function deleteBranchThroughPlatform(client, projectRef, branchName, optio
610
614
  const branchId = branch.branchId;
611
615
  let accepted;
612
616
  try {
613
- accepted = await requireServerRouteSupport("delete branch", () => client.projects.branches.delete(projectRef, branchName, { branchId }));
617
+ accepted = await requireServerRouteSupport(
618
+ "delete branch",
619
+ () => client.projects.branches.delete(projectRef, branchName, { branchId })
620
+ );
614
621
  } catch (error) {
615
622
  const landed = error instanceof R5dctlApiError ? await resolveUncertainAcceptance(client, projectRef, branchId, error) : null;
616
623
  if (!landed) throw error;
@@ -951,12 +958,31 @@ function parseGlobalArgs(argv) {
951
958
  version: false
952
959
  };
953
960
  const rest = [];
961
+ const seenGlobalFlags = /* @__PURE__ */ new Set();
962
+ let duplicateGlobalFlag;
954
963
  for (let i = 0; i < argv.length; i += 1) {
955
964
  const arg = argv[i];
965
+ if (rest[0] === "account" && (arg === "--value" || arg === "--file" || arg === "--description")) {
966
+ rest.push(arg);
967
+ if (i + 1 < argv.length) rest.push(argv[++i]);
968
+ continue;
969
+ }
970
+ if (rest[0] === "account" && arg === "") {
971
+ rest.push(arg);
972
+ continue;
973
+ }
956
974
  if (!arg) {
957
975
  continue;
958
976
  }
977
+ const globalName = { "-p": "--project", "-b": "--branch", "-s": "--session", "-h": "--help", "-v": "--version" }[arg] ?? arg.split("=", 1)[0];
978
+ if (["--base-url", "--json", "--config", "--token", "--api-key", "--project", "--branch", "--session", "--help", "--version"].includes(
979
+ globalName
980
+ )) {
981
+ if (seenGlobalFlags.has(globalName)) duplicateGlobalFlag ??= globalName;
982
+ seenGlobalFlags.add(globalName);
983
+ }
959
984
  if (arg === "--") {
985
+ if (rest[0] === "account") rest.push(arg);
960
986
  rest.push(...argv.slice(i + 1));
961
987
  break;
962
988
  }
@@ -1037,6 +1063,7 @@ function parseGlobalArgs(argv) {
1037
1063
  }
1038
1064
  rest.push(arg);
1039
1065
  }
1066
+ if (rest[0] === "account" && duplicateGlobalFlag) throw new Error(`${duplicateGlobalFlag} may only be provided once`);
1040
1067
  return { options, rest };
1041
1068
  }
1042
1069
  function parseConfig(configPath) {
@@ -1715,7 +1742,9 @@ function renderSessionList(sessions, nowMs = Date.now()) {
1715
1742
  if (sessions.length === 0) {
1716
1743
  return "No sessions found.\n";
1717
1744
  }
1718
- return `${sessions.map((session) => [session.id, session.branchName, formatSessionActivity(session, nowMs), session.name ?? "(unnamed)"].filter(Boolean).join(" ")).join("\n")}
1745
+ return `${sessions.map(
1746
+ (session) => [session.id, session.branchName, formatSessionActivity(session, nowMs), session.name ?? "(unnamed)"].filter(Boolean).join(" ")
1747
+ ).join("\n")}
1719
1748
  `;
1720
1749
  }
1721
1750
  function renderRecentSessionList(sessions) {
@@ -2748,7 +2777,9 @@ async function executeR5dctlCommand(client, json, args) {
2748
2777
  });
2749
2778
  write(result2, renderSessionClaimResponse(result2, nowMs));
2750
2779
  if (result2.outcome === "held_by_other") {
2751
- throw new Error(`Claim ${claim.topic} is held by session ${result2.claim.holderSessionId} until ${result2.claim.expiresAt}; not acquired`);
2780
+ throw new Error(
2781
+ `Claim ${claim.topic} is held by session ${result2.claim.holderSessionId} until ${result2.claim.expiresAt}; not acquired`
2782
+ );
2752
2783
  }
2753
2784
  return;
2754
2785
  }
@@ -2768,12 +2799,20 @@ async function executeR5dctlCommand(client, json, args) {
2768
2799
  return;
2769
2800
  }
2770
2801
  if (!current.heldByActor) {
2771
- write({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, renderSessionClaimResponse({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, nowMs));
2802
+ write(
2803
+ { outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current },
2804
+ renderSessionClaimResponse({ outcome: "not_holder", actorSessionId: list.actorSessionId, claim: current }, nowMs)
2805
+ );
2772
2806
  throw new Error(`Claim ${claim.topic} is held by session ${current.holderSessionId}, not by ${list.actorSessionId}`);
2773
2807
  }
2774
2808
  generation = current.generation;
2775
2809
  }
2776
- const result = claim.action === "renew" ? await client.claims.renew({ topic: claim.topic, ...scope, generation, ...claim.ttlSeconds !== void 0 ? { ttlSeconds: claim.ttlSeconds } : {} }) : await client.claims.release({ topic: claim.topic, ...scope, generation });
2810
+ const result = claim.action === "renew" ? await client.claims.renew({
2811
+ topic: claim.topic,
2812
+ ...scope,
2813
+ generation,
2814
+ ...claim.ttlSeconds !== void 0 ? { ttlSeconds: claim.ttlSeconds } : {}
2815
+ }) : await client.claims.release({ topic: claim.topic, ...scope, generation });
2777
2816
  write(result, renderSessionClaimResponse(result, nowMs));
2778
2817
  const succeeded = result.outcome === "renewed" || result.outcome === "released" || claim.action === "release" && result.outcome === "missing";
2779
2818
  if (!succeeded) throw new Error(`Claim ${claim.topic}: ${CLAIM_OUTCOME_TEXT[result.outcome] ?? result.outcome}`);
@@ -2890,7 +2929,10 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
2890
2929
  const offset = first === "describe" ? 2 : 3;
2891
2930
  const projectRef = requireValue(args[offset], "Missing project reference");
2892
2931
  const branchId = requireValue(args[offset + 1], "Missing branch id");
2893
- const status = await requireServerRouteSupport("describe branch-deletion", () => client.projects.branches.describeDeletion(projectRef, branchId));
2932
+ const status = await requireServerRouteSupport(
2933
+ "describe branch-deletion",
2934
+ () => client.projects.branches.describeDeletion(projectRef, branchId)
2935
+ );
2894
2936
  write(status, renderBranchDeletionStatus(status));
2895
2937
  return;
2896
2938
  }
@@ -3035,13 +3077,20 @@ ${result.deletion ? `Deletion: ${result.deletion.status}${result.deletion.error
3035
3077
  throw new Error(`Unknown command: ${args.join(" ")}`);
3036
3078
  }
3037
3079
  function resolveCommandExecution(options, rest) {
3038
- const trailingHelp = isHelpFlag(rest.at(-1));
3080
+ const accountLiteralHelp = rest[0] === "account" && ["--value", "--file", "--description"].includes(rest.at(-2) ?? "");
3081
+ const trailingHelp = isHelpFlag(rest.at(-1)) && !accountLiteralHelp;
3039
3082
  const normalizedRest = trailingHelp ? rest.slice(0, -1) : rest;
3040
3083
  const command = normalizedRest[0];
3041
3084
  if (!command) {
3042
3085
  throw new Error("Missing command");
3043
3086
  }
3044
3087
  const commandArgs = normalizedRest.slice(1);
3088
+ if (command === "account") {
3089
+ if (trailingHelp || commandArgs.length <= 1) {
3090
+ return { kind: "cli-help", text: getAccountHelp(commandArgs.slice(0, 2)) };
3091
+ }
3092
+ return { kind: "account", command: parseAccountCommand(commandArgs) };
3093
+ }
3045
3094
  if (command === "ps" && commandArgs.length === 0) {
3046
3095
  return {
3047
3096
  kind: "cli-help",
@@ -3553,6 +3602,10 @@ async function runCommand(argv) {
3553
3602
  const clientOptions = resolveClientOptions(options, config);
3554
3603
  const client = new R5dctlClient(clientOptions);
3555
3604
  const execution = resolveCommandExecution(options, rest);
3605
+ if (execution.kind === "account") {
3606
+ await executeAccountCommand(client, execution.command, options.json);
3607
+ return 0;
3608
+ }
3556
3609
  if (execution.kind === "auth-login") {
3557
3610
  await handleAuthLogin(client, options, execution.commandArgs, config);
3558
3611
  return 0;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.127",
3
+ "version": "0.0.128",
4
4
  "type": "module"
5
5
  }
@@ -0,0 +1,67 @@
1
+ import { type R5dctlClient } from "@ricsam/r5d-api";
2
+ export declare const ACCOUNT_HELP_ENTRIES: readonly [{
3
+ readonly usage: "account instructions get";
4
+ readonly description: "Write account AGENTS.md exactly to stdout; --json prints its metadata and content.";
5
+ }, {
6
+ readonly usage: "account instructions set (--file <path>|--stdin)";
7
+ readonly description: "Replace account AGENTS.md, including clearing it with empty input.";
8
+ }, {
9
+ readonly usage: "account env list";
10
+ readonly description: "List account environment variable metadata without revealing values.";
11
+ }, {
12
+ readonly usage: "account env set <name> [--description <text>] [--value <value>|--file <path>|--stdin]";
13
+ readonly description: "Set an account environment variable, or edit an existing entry's description.";
14
+ }, {
15
+ readonly usage: "account env revoke <name>";
16
+ readonly description: "Remove the stored value while retaining its name and description.";
17
+ }, {
18
+ readonly usage: "account env delete <name>";
19
+ readonly description: "Delete an account environment entry.";
20
+ }, {
21
+ readonly usage: "account secrets list";
22
+ readonly description: "List account secret-file metadata without revealing contents.";
23
+ }, {
24
+ readonly usage: "account secrets set <name> [--description <text>] [--file <path>|--stdin]";
25
+ readonly description: "Store a secret file whose path is delivered through the named environment variable.";
26
+ }, {
27
+ readonly usage: "account secrets revoke <name>";
28
+ readonly description: "Remove a stored secret file's contents while retaining its metadata.";
29
+ }, {
30
+ readonly usage: "account secrets delete <name>";
31
+ readonly description: "Delete an account secret-file entry.";
32
+ }];
33
+ type ValueSource = {
34
+ kind: "value";
35
+ value: string;
36
+ } | {
37
+ kind: "file";
38
+ path: string;
39
+ } | {
40
+ kind: "stdin";
41
+ };
42
+ export type AccountCommand = {
43
+ group: "instructions";
44
+ action: "get";
45
+ } | {
46
+ group: "instructions";
47
+ action: "set";
48
+ source: ValueSource;
49
+ } | {
50
+ group: "env" | "secrets";
51
+ action: "list";
52
+ } | {
53
+ group: "env" | "secrets";
54
+ action: "set";
55
+ name: string;
56
+ description?: string;
57
+ source?: ValueSource;
58
+ } | {
59
+ group: "env" | "secrets";
60
+ action: "revoke" | "delete";
61
+ name: string;
62
+ };
63
+ /** Keep input values out of every parser error, including unknown flags and operands. */
64
+ export declare function parseAccountCommand(args: string[]): AccountCommand;
65
+ export declare function getAccountHelp(args: string[]): string;
66
+ export declare function executeAccountCommand(client: R5dctlClient, command: AccountCommand, json: boolean): Promise<void>;
67
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { R5dctlApiError, R5dctlClient, type R5dctlConversationForkResponse, type R5dctlConversationHeadResponse, type R5dctlConversationRenderOptions, type R5dctlConversationResponse, type R5dctlConversationNodeResponse, type R5dctlConversationOverviewResponse, type R5dctlConversationWorkDetail, type R5dctlConversationWorkResponse, type R5dctlEnvData, type R5dctlProcessInspection, type R5dctlProcessListInput, type R5dctlProcessLog, type R5dctlProcessRun, type R5dctlWorkerCapacityReport, type R5dctlBranch, type R5dctlBranchDeletionStatus, type R5dctlBranchDescription, type R5dctlBranchOperation, type R5dctlSessionSummary, type R5dctlSessionClaim, type R5dctlSessionClaimListResponse, type R5dctlSessionEvent, type R5dctlSessionStartInput, type R5dctlWorkspaceStatus, type R5dctlWorkspaceSyncResult, type R5dctlWorkingTreeMode, type R5dctlWorktreeSource, type R5dctlPromptDelivery, type R5dctlSessionMode, type ChatMode, type ModelTier } from "@ricsam/r5d-api";
2
+ import { type AccountCommand } from "./account";
2
3
  export type GlobalOptions = {
3
4
  baseUrl?: string;
4
5
  json: boolean;
@@ -17,6 +18,9 @@ export type R5dctlConfig = {
17
18
  apiKey?: string;
18
19
  };
19
20
  type CommandExecutionPlan = {
21
+ kind: "account";
22
+ command: AccountCommand;
23
+ } | {
20
24
  kind: "plugin";
21
25
  pluginArgs: string[];
22
26
  clearAuthOnSuccess?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.127",
3
+ "version": "0.0.128",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/cli.cjs",
6
6
  "module": "./dist/mjs/cli.mjs",
@@ -26,7 +26,7 @@
26
26
  "r5dctl": "dist/cjs/main.cjs"
27
27
  },
28
28
  "dependencies": {
29
- "@ricsam/r5d-api": "^0.0.127",
29
+ "@ricsam/r5d-api": "^0.0.128",
30
30
  "dotenv": "^17",
31
31
  "qrcode": "^1.5.4",
32
32
  "ws": "^8.18.3"