@ricsam/r5dctl 0.0.128 → 0.0.130

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
@@ -4,6 +4,8 @@ CLI for managing r5d.dev account instructions, environment variables, secret fil
4
4
 
5
5
  ## Installation
6
6
 
7
+ Account management commands require version **0.0.129 or later**.
8
+
7
9
  ```bash
8
10
  npm install -g @ricsam/r5dctl
9
11
  ```
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var account_exports = {};
30
+ __export(account_exports, {
31
+ ACCOUNT_HELP_ENTRIES: () => ACCOUNT_HELP_ENTRIES,
32
+ executeAccountCommand: () => executeAccountCommand,
33
+ getAccountHelp: () => getAccountHelp,
34
+ parseAccountCommand: () => parseAccountCommand
35
+ });
36
+ module.exports = __toCommonJS(account_exports);
37
+ var import_node_fs = __toESM(require("node:fs"), 1);
38
+ var import_node_os = __toESM(require("node:os"), 1);
39
+ var import_node_path = __toESM(require("node:path"), 1);
40
+ var import_r5d_api = require("@ricsam/r5d-api");
41
+ const ACCOUNT_HELP_ENTRIES = [
42
+ { usage: "account instructions get", description: "Write account AGENTS.md exactly to stdout; --json prints its metadata and content." },
43
+ {
44
+ usage: "account instructions set (--file <path>|--stdin)",
45
+ description: "Replace account AGENTS.md, including clearing it with empty input."
46
+ },
47
+ { usage: "account env list", description: "List account environment variable metadata without revealing values." },
48
+ {
49
+ usage: "account env set <name> [--description <text>] [--value <value>|--file <path>|--stdin]",
50
+ description: "Set an account environment variable, or edit an existing entry's description."
51
+ },
52
+ { usage: "account env revoke <name>", description: "Remove the stored value while retaining its name and description." },
53
+ { usage: "account env delete <name>", description: "Delete an account environment entry." },
54
+ { usage: "account secrets list", description: "List account secret-file metadata without revealing contents." },
55
+ {
56
+ usage: "account secrets set <name> [--description <text>] [--file <path>|--stdin]",
57
+ description: "Store a secret file whose path is delivered through the named environment variable."
58
+ },
59
+ { usage: "account secrets revoke <name>", description: "Remove a stored secret file's contents while retaining its metadata." },
60
+ { usage: "account secrets delete <name>", description: "Delete an account secret-file entry." }
61
+ ];
62
+ const VALUE_FLAGS = /* @__PURE__ */ new Set(["--value", "--file", "--description"]);
63
+ function validateName(name) {
64
+ if (!name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || name.length > 128)
65
+ throw new Error("Account entry names must be valid environment variable names of at most 128 characters");
66
+ if (/^R5D(?:CTL)?_/i.test(name)) throw new Error("R5D_ and R5DCTL_ account entry names are reserved for the platform");
67
+ }
68
+ function parseAccountCommand(args) {
69
+ const [group, action, ...operands] = args;
70
+ if (group !== "instructions" && group !== "env" && group !== "secrets") throw new Error("Unknown account command");
71
+ if (group === "instructions" && action === "get") {
72
+ if (operands.length) throw new Error("account instructions get does not accept arguments");
73
+ return { group, action };
74
+ }
75
+ if (group !== "instructions" && action === "list") {
76
+ if (operands.length) throw new Error("Account list commands do not accept arguments");
77
+ return { group, action };
78
+ }
79
+ if (action !== "set" && (group === "instructions" || action !== "revoke" && action !== "delete"))
80
+ throw new Error("Unknown account command");
81
+ const name = group === "instructions" ? void 0 : operands.shift();
82
+ if (group !== "instructions") validateName(name);
83
+ if (action === "revoke" || action === "delete") {
84
+ if (operands.length) throw new Error("Account revoke and delete commands accept only an entry name");
85
+ return { group, action, name };
86
+ }
87
+ const seen = /* @__PURE__ */ new Set();
88
+ let source;
89
+ let description;
90
+ for (let index = 0; index < operands.length; index += 1) {
91
+ const arg = operands[index];
92
+ const equals = arg.indexOf("=");
93
+ const flag = equals < 0 ? arg : arg.slice(0, equals);
94
+ if (flag !== "--stdin" && !VALUE_FLAGS.has(flag)) throw new Error("Unknown account set flag or extra argument");
95
+ if (seen.has(flag)) throw new Error(`${flag} may only be provided once`);
96
+ seen.add(flag);
97
+ if (flag === "--description" && group === "instructions") throw new Error("Account instructions do not accept --description");
98
+ if (flag === "--value" && group !== "env") throw new Error("--value is only supported by account env set; use --file or --stdin");
99
+ let value;
100
+ if (flag === "--stdin") {
101
+ if (equals >= 0) throw new Error("--stdin does not accept a value");
102
+ } else {
103
+ value = equals >= 0 ? arg.slice(equals + 1) : operands[++index];
104
+ if (value === void 0 || equals < 0 && (VALUE_FLAGS.has(value) || value === "--stdin"))
105
+ throw new Error(`Missing value for ${flag}`);
106
+ }
107
+ if (flag === "--description") {
108
+ if (!value?.trim() || value.trim().length > 2e3) throw new Error("--description must contain 1 to 2000 characters");
109
+ description = value.trim();
110
+ continue;
111
+ }
112
+ if (source) throw new Error("Provide exactly one value source: --value, --file, or --stdin");
113
+ if (flag === "--file") {
114
+ if (!value) throw new Error("Missing path for --file");
115
+ source = { kind: "file", path: value };
116
+ } else source = flag === "--stdin" ? { kind: "stdin" } : { kind: "value", value };
117
+ }
118
+ if (group === "instructions") {
119
+ if (!source) throw new Error("account instructions set requires exactly one of --file or --stdin");
120
+ return { group, action: "set", source };
121
+ }
122
+ return { group, action: "set", name, ...description !== void 0 ? { description } : {}, ...source ? { source } : {} };
123
+ }
124
+ function getAccountHelp(args) {
125
+ const prefix = ["account", ...args].join(" ");
126
+ const entries = ACCOUNT_HELP_ENTRIES.filter((entry) => entry.usage === prefix || entry.usage.startsWith(`${prefix} `));
127
+ if (!entries.length) throw new Error("Unknown account command");
128
+ return [
129
+ "Usage:",
130
+ ...entries.map((entry) => ` r5dctl ${entry.usage}
131
+ ${entry.description}`),
132
+ "",
133
+ "Account settings apply across all of your projects and workers; project, branch, and session flags do not narrow them.",
134
+ "New entries require a description and value source. Omit the value source to preserve an existing value.",
135
+ "Files and stdin are read only when explicitly selected. Input is preserved exactly; empty input clears instructions or sets an empty value.",
136
+ "Lists and mutation responses never print stored values. Revoke affects future command delivery; restart existing shells to refresh their environment.",
137
+ ""
138
+ ].join("\n");
139
+ }
140
+ function readSource(source) {
141
+ if (source.kind === "value") return source.value;
142
+ try {
143
+ const location = source.kind === "stdin" ? 0 : source.path.startsWith("~/") ? import_node_path.default.join(import_node_os.default.homedir(), source.path.slice(2)) : source.path;
144
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(import_node_fs.default.readFileSync(location));
145
+ } catch {
146
+ throw new Error(source.kind === "stdin" ? "Could not read UTF-8 input from stdin" : "Could not read the input file as UTF-8");
147
+ }
148
+ }
149
+ function metadata(entry) {
150
+ return {
151
+ id: entry.id,
152
+ name: entry.name,
153
+ description: entry.description,
154
+ kind: entry.kind,
155
+ configured: entry.configured,
156
+ updatedAt: entry.updatedAt
157
+ };
158
+ }
159
+ async function accountRequest(request) {
160
+ try {
161
+ return await request();
162
+ } catch (error) {
163
+ throw new Error(
164
+ error instanceof import_r5d_api.R5dctlApiError ? `Account settings request failed (HTTP ${error.status})` : "Account settings request failed"
165
+ );
166
+ }
167
+ }
168
+ async function executeAccountCommand(client, command, json) {
169
+ const write = (data, human) => process.stdout.write(json ? `${JSON.stringify(data, null, 2)}
170
+ ` : human);
171
+ if (command.group === "instructions") {
172
+ if (command.action === "get") {
173
+ const result = await accountRequest(() => client.account.instructions.get());
174
+ write({ content: result.content, updatedAt: result.updatedAt }, result.content);
175
+ } else {
176
+ const content = readSource(command.source);
177
+ if (content.length > 1e5) throw new Error("Account instructions exceed the 100000 character limit");
178
+ const result = await accountRequest(() => client.account.instructions.set(content));
179
+ write({ success: true, updatedAt: result.updatedAt }, "Account instructions saved.\n");
180
+ }
181
+ return;
182
+ }
183
+ const kind = command.group === "env" ? "env" : "file";
184
+ const settings = await accountRequest(() => client.account.getSettings());
185
+ if (command.action === "list") {
186
+ const entries = settings.entries.filter((entry2) => entry2.kind === kind).map(metadata);
187
+ write(
188
+ { entries },
189
+ entries.length ? `${entries.map((entry2) => `${entry2.name} ${entry2.configured ? "set" : "unset"} ${JSON.stringify(entry2.description)}`).join("\n")}
190
+ ` : "No account entries.\n"
191
+ );
192
+ return;
193
+ }
194
+ const entry = settings.entries.find((candidate) => candidate.name === command.name);
195
+ if (entry && entry.kind !== kind) throw new Error("This name belongs to the other account entry kind; use its env or secrets commands");
196
+ if (command.action === "set") {
197
+ if (!entry && command.description === void 0) throw new Error("New account entries require --description");
198
+ if (!entry && !command.source) throw new Error("New account entries require a value source");
199
+ if (entry && !command.source && command.description === void 0)
200
+ throw new Error("Provide a value source or --description to update the entry");
201
+ const value = command.source ? readSource(command.source) : void 0;
202
+ if (value !== void 0 && (value.length > 1048576 || value.includes("\0")))
203
+ throw new Error("Account values must contain at most 1048576 characters and cannot contain NUL characters");
204
+ await accountRequest(
205
+ () => client.account.environment.save({
206
+ ...entry ? { id: entry.id } : {},
207
+ name: command.name,
208
+ kind,
209
+ description: command.description ?? entry.description,
210
+ ...value !== void 0 ? { value } : {}
211
+ })
212
+ );
213
+ write({ success: true, name: command.name, kind }, `Account entry ${command.name} saved.
214
+ `);
215
+ } else {
216
+ if (!entry) throw new Error("Account entry not found");
217
+ await accountRequest(
218
+ () => command.action === "revoke" ? client.account.environment.revoke(entry.id) : client.account.environment.delete(entry.id)
219
+ );
220
+ write(
221
+ { success: true, name: command.name, kind },
222
+ `Account entry ${command.name} ${command.action === "revoke" ? "revoked" : "deleted"}.
223
+ `
224
+ );
225
+ }
226
+ }
227
+ // Annotate the CommonJS export names for ESM import in node:
228
+ 0 && (module.exports = {
229
+ ACCOUNT_HELP_ENTRIES,
230
+ executeAccountCommand,
231
+ getAccountHelp,
232
+ parseAccountCommand
233
+ });
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.128",
3
+ "version": "0.0.130",
4
4
  "type": "commonjs"
5
5
  }
@@ -0,0 +1,196 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { R5dctlApiError } from "@ricsam/r5d-api";
5
+ const ACCOUNT_HELP_ENTRIES = [
6
+ { usage: "account instructions get", description: "Write account AGENTS.md exactly to stdout; --json prints its metadata and content." },
7
+ {
8
+ usage: "account instructions set (--file <path>|--stdin)",
9
+ description: "Replace account AGENTS.md, including clearing it with empty input."
10
+ },
11
+ { usage: "account env list", description: "List account environment variable metadata without revealing values." },
12
+ {
13
+ usage: "account env set <name> [--description <text>] [--value <value>|--file <path>|--stdin]",
14
+ description: "Set an account environment variable, or edit an existing entry's description."
15
+ },
16
+ { usage: "account env revoke <name>", description: "Remove the stored value while retaining its name and description." },
17
+ { usage: "account env delete <name>", description: "Delete an account environment entry." },
18
+ { usage: "account secrets list", description: "List account secret-file metadata without revealing contents." },
19
+ {
20
+ usage: "account secrets set <name> [--description <text>] [--file <path>|--stdin]",
21
+ description: "Store a secret file whose path is delivered through the named environment variable."
22
+ },
23
+ { usage: "account secrets revoke <name>", description: "Remove a stored secret file's contents while retaining its metadata." },
24
+ { usage: "account secrets delete <name>", description: "Delete an account secret-file entry." }
25
+ ];
26
+ const VALUE_FLAGS = /* @__PURE__ */ new Set(["--value", "--file", "--description"]);
27
+ function validateName(name) {
28
+ if (!name || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || name.length > 128)
29
+ throw new Error("Account entry names must be valid environment variable names of at most 128 characters");
30
+ if (/^R5D(?:CTL)?_/i.test(name)) throw new Error("R5D_ and R5DCTL_ account entry names are reserved for the platform");
31
+ }
32
+ function parseAccountCommand(args) {
33
+ const [group, action, ...operands] = args;
34
+ if (group !== "instructions" && group !== "env" && group !== "secrets") throw new Error("Unknown account command");
35
+ if (group === "instructions" && action === "get") {
36
+ if (operands.length) throw new Error("account instructions get does not accept arguments");
37
+ return { group, action };
38
+ }
39
+ if (group !== "instructions" && action === "list") {
40
+ if (operands.length) throw new Error("Account list commands do not accept arguments");
41
+ return { group, action };
42
+ }
43
+ if (action !== "set" && (group === "instructions" || action !== "revoke" && action !== "delete"))
44
+ throw new Error("Unknown account command");
45
+ const name = group === "instructions" ? void 0 : operands.shift();
46
+ if (group !== "instructions") validateName(name);
47
+ if (action === "revoke" || action === "delete") {
48
+ if (operands.length) throw new Error("Account revoke and delete commands accept only an entry name");
49
+ return { group, action, name };
50
+ }
51
+ const seen = /* @__PURE__ */ new Set();
52
+ let source;
53
+ let description;
54
+ for (let index = 0; index < operands.length; index += 1) {
55
+ const arg = operands[index];
56
+ const equals = arg.indexOf("=");
57
+ const flag = equals < 0 ? arg : arg.slice(0, equals);
58
+ if (flag !== "--stdin" && !VALUE_FLAGS.has(flag)) throw new Error("Unknown account set flag or extra argument");
59
+ if (seen.has(flag)) throw new Error(`${flag} may only be provided once`);
60
+ seen.add(flag);
61
+ if (flag === "--description" && group === "instructions") throw new Error("Account instructions do not accept --description");
62
+ if (flag === "--value" && group !== "env") throw new Error("--value is only supported by account env set; use --file or --stdin");
63
+ let value;
64
+ if (flag === "--stdin") {
65
+ if (equals >= 0) throw new Error("--stdin does not accept a value");
66
+ } else {
67
+ value = equals >= 0 ? arg.slice(equals + 1) : operands[++index];
68
+ if (value === void 0 || equals < 0 && (VALUE_FLAGS.has(value) || value === "--stdin"))
69
+ throw new Error(`Missing value for ${flag}`);
70
+ }
71
+ if (flag === "--description") {
72
+ if (!value?.trim() || value.trim().length > 2e3) throw new Error("--description must contain 1 to 2000 characters");
73
+ description = value.trim();
74
+ continue;
75
+ }
76
+ if (source) throw new Error("Provide exactly one value source: --value, --file, or --stdin");
77
+ if (flag === "--file") {
78
+ if (!value) throw new Error("Missing path for --file");
79
+ source = { kind: "file", path: value };
80
+ } else source = flag === "--stdin" ? { kind: "stdin" } : { kind: "value", value };
81
+ }
82
+ if (group === "instructions") {
83
+ if (!source) throw new Error("account instructions set requires exactly one of --file or --stdin");
84
+ return { group, action: "set", source };
85
+ }
86
+ return { group, action: "set", name, ...description !== void 0 ? { description } : {}, ...source ? { source } : {} };
87
+ }
88
+ function getAccountHelp(args) {
89
+ const prefix = ["account", ...args].join(" ");
90
+ const entries = ACCOUNT_HELP_ENTRIES.filter((entry) => entry.usage === prefix || entry.usage.startsWith(`${prefix} `));
91
+ if (!entries.length) throw new Error("Unknown account command");
92
+ return [
93
+ "Usage:",
94
+ ...entries.map((entry) => ` r5dctl ${entry.usage}
95
+ ${entry.description}`),
96
+ "",
97
+ "Account settings apply across all of your projects and workers; project, branch, and session flags do not narrow them.",
98
+ "New entries require a description and value source. Omit the value source to preserve an existing value.",
99
+ "Files and stdin are read only when explicitly selected. Input is preserved exactly; empty input clears instructions or sets an empty value.",
100
+ "Lists and mutation responses never print stored values. Revoke affects future command delivery; restart existing shells to refresh their environment.",
101
+ ""
102
+ ].join("\n");
103
+ }
104
+ function readSource(source) {
105
+ if (source.kind === "value") return source.value;
106
+ try {
107
+ const location = source.kind === "stdin" ? 0 : source.path.startsWith("~/") ? path.join(os.homedir(), source.path.slice(2)) : source.path;
108
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(fs.readFileSync(location));
109
+ } catch {
110
+ throw new Error(source.kind === "stdin" ? "Could not read UTF-8 input from stdin" : "Could not read the input file as UTF-8");
111
+ }
112
+ }
113
+ function metadata(entry) {
114
+ return {
115
+ id: entry.id,
116
+ name: entry.name,
117
+ description: entry.description,
118
+ kind: entry.kind,
119
+ configured: entry.configured,
120
+ updatedAt: entry.updatedAt
121
+ };
122
+ }
123
+ async function accountRequest(request) {
124
+ try {
125
+ return await request();
126
+ } catch (error) {
127
+ throw new Error(
128
+ error instanceof R5dctlApiError ? `Account settings request failed (HTTP ${error.status})` : "Account settings request failed"
129
+ );
130
+ }
131
+ }
132
+ async function executeAccountCommand(client, command, json) {
133
+ const write = (data, human) => process.stdout.write(json ? `${JSON.stringify(data, null, 2)}
134
+ ` : human);
135
+ if (command.group === "instructions") {
136
+ if (command.action === "get") {
137
+ const result = await accountRequest(() => client.account.instructions.get());
138
+ write({ content: result.content, updatedAt: result.updatedAt }, result.content);
139
+ } else {
140
+ const content = readSource(command.source);
141
+ if (content.length > 1e5) throw new Error("Account instructions exceed the 100000 character limit");
142
+ const result = await accountRequest(() => client.account.instructions.set(content));
143
+ write({ success: true, updatedAt: result.updatedAt }, "Account instructions saved.\n");
144
+ }
145
+ return;
146
+ }
147
+ const kind = command.group === "env" ? "env" : "file";
148
+ const settings = await accountRequest(() => client.account.getSettings());
149
+ if (command.action === "list") {
150
+ const entries = settings.entries.filter((entry2) => entry2.kind === kind).map(metadata);
151
+ write(
152
+ { entries },
153
+ entries.length ? `${entries.map((entry2) => `${entry2.name} ${entry2.configured ? "set" : "unset"} ${JSON.stringify(entry2.description)}`).join("\n")}
154
+ ` : "No account entries.\n"
155
+ );
156
+ return;
157
+ }
158
+ const entry = settings.entries.find((candidate) => candidate.name === command.name);
159
+ if (entry && entry.kind !== kind) throw new Error("This name belongs to the other account entry kind; use its env or secrets commands");
160
+ if (command.action === "set") {
161
+ if (!entry && command.description === void 0) throw new Error("New account entries require --description");
162
+ if (!entry && !command.source) throw new Error("New account entries require a value source");
163
+ if (entry && !command.source && command.description === void 0)
164
+ throw new Error("Provide a value source or --description to update the entry");
165
+ const value = command.source ? readSource(command.source) : void 0;
166
+ if (value !== void 0 && (value.length > 1048576 || value.includes("\0")))
167
+ throw new Error("Account values must contain at most 1048576 characters and cannot contain NUL characters");
168
+ await accountRequest(
169
+ () => client.account.environment.save({
170
+ ...entry ? { id: entry.id } : {},
171
+ name: command.name,
172
+ kind,
173
+ description: command.description ?? entry.description,
174
+ ...value !== void 0 ? { value } : {}
175
+ })
176
+ );
177
+ write({ success: true, name: command.name, kind }, `Account entry ${command.name} saved.
178
+ `);
179
+ } else {
180
+ if (!entry) throw new Error("Account entry not found");
181
+ await accountRequest(
182
+ () => command.action === "revoke" ? client.account.environment.revoke(entry.id) : client.account.environment.delete(entry.id)
183
+ );
184
+ write(
185
+ { success: true, name: command.name, kind },
186
+ `Account entry ${command.name} ${command.action === "revoke" ? "revoked" : "deleted"}.
187
+ `
188
+ );
189
+ }
190
+ }
191
+ export {
192
+ ACCOUNT_HELP_ENTRIES,
193
+ executeAccountCommand,
194
+ getAccountHelp,
195
+ parseAccountCommand
196
+ };
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.128",
3
+ "version": "0.0.130",
4
4
  "type": "module"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.128",
3
+ "version": "0.0.130",
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.128",
29
+ "@ricsam/r5d-api": "^0.0.130",
30
30
  "dotenv": "^17",
31
31
  "qrcode": "^1.5.4",
32
32
  "ws": "^8.18.3"