@orchagent/cli 0.3.117 → 0.3.119

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.
@@ -45,6 +45,7 @@ const metrics_1 = require("./metrics");
45
45
  const dag_1 = require("./dag");
46
46
  const completion_1 = require("./completion");
47
47
  const validate_1 = require("./validate");
48
+ const storage_1 = require("./storage");
48
49
  function registerCommands(program) {
49
50
  (0, login_1.registerLoginCommand)(program);
50
51
  (0, logout_1.registerLogoutCommand)(program);
@@ -90,4 +91,5 @@ function registerCommands(program) {
90
91
  (0, dag_1.registerDagCommand)(program);
91
92
  (0, completion_1.registerCompletionCommand)(program);
92
93
  (0, validate_1.registerValidateCommand)(program);
94
+ (0, storage_1.registerStorageCommand)(program);
93
95
  }
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.registerStorageCommand = registerStorageCommand;
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const config_1 = require("../lib/config");
42
+ const api_1 = require("../lib/api");
43
+ const errors_1 = require("../lib/errors");
44
+ const output_1 = require("../lib/output");
45
+ // ============================================
46
+ // HELPERS
47
+ // ============================================
48
+ const NAMESPACE_RE = /^[a-z][a-z0-9-]{0,63}$/;
49
+ const KEY_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,255}$/;
50
+ async function resolveWorkspaceId(config, slug) {
51
+ const configFile = await (0, config_1.loadConfig)();
52
+ const targetSlug = slug ?? configFile.workspace;
53
+ const response = await (0, api_1.request)(config, 'GET', '/workspaces');
54
+ if (targetSlug) {
55
+ const workspace = response.workspaces.find((w) => w.slug === targetSlug);
56
+ if (!workspace) {
57
+ throw new errors_1.CliError(`Workspace '${targetSlug}' not found.`);
58
+ }
59
+ return workspace.id;
60
+ }
61
+ if (response.workspaces.length === 0) {
62
+ throw new errors_1.CliError('No workspaces found. Create one with `orch workspace create <name>`.');
63
+ }
64
+ if (response.workspaces.length === 1) {
65
+ return response.workspaces[0].id;
66
+ }
67
+ const slugs = response.workspaces.map((w) => w.slug).join(', ');
68
+ throw new errors_1.CliError(`Multiple workspaces available: ${slugs}\n` +
69
+ 'Specify one with --workspace <slug> or run `orch workspace use <slug>`.');
70
+ }
71
+ function validateNamespace(ns) {
72
+ if (!NAMESPACE_RE.test(ns)) {
73
+ throw new errors_1.CliError(`Invalid namespace '${ns}'.\n\n` +
74
+ 'Namespaces must:\n' +
75
+ ' - Start with a lowercase letter\n' +
76
+ ' - Be 1-64 chars of lowercase letters, digits, and hyphens\n\n' +
77
+ 'Examples: signals, my-data, competitors');
78
+ }
79
+ }
80
+ function validateKey(key) {
81
+ if (!KEY_RE.test(key)) {
82
+ throw new errors_1.CliError(`Invalid key '${key}'.\n\n` +
83
+ 'Keys must:\n' +
84
+ ' - Start with a letter or digit\n' +
85
+ ' - Be 1-256 chars of letters, digits, dots, hyphens, and underscores\n\n' +
86
+ 'Examples: 2026-03-05, config.v2, weekly-report');
87
+ }
88
+ }
89
+ function formatDate(iso) {
90
+ if (!iso)
91
+ return '-';
92
+ return new Date(iso).toLocaleString();
93
+ }
94
+ function formatBytes(bytes) {
95
+ if (bytes < 1024)
96
+ return `${bytes} B`;
97
+ if (bytes < 1024 * 1024)
98
+ return `${(bytes / 1024).toFixed(1)} KB`;
99
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
100
+ }
101
+ async function readStdin() {
102
+ if (process.stdin.isTTY) {
103
+ throw new errors_1.CliError('No JSON value provided.\n\n' +
104
+ 'Usage:\n' +
105
+ ' orch storage set <ns> <key> \'{"k":"v"}\' Inline JSON\n' +
106
+ ' orch storage set <ns> <key> @file.json Read from file\n' +
107
+ ' echo \'{"k":"v"}\' | orch storage set <ns> <key> - Read from stdin\n' +
108
+ ' cat data.json | orch storage set <ns> <key> Pipe (implicit stdin)');
109
+ }
110
+ const chunks = [];
111
+ for await (const chunk of process.stdin) {
112
+ chunks.push(Buffer.from(chunk));
113
+ }
114
+ if (!chunks.length) {
115
+ throw new errors_1.CliError('No data received on stdin.');
116
+ }
117
+ return Buffer.concat(chunks).toString('utf8');
118
+ }
119
+ async function parseJsonArg(arg) {
120
+ let raw;
121
+ if (!arg || arg === '-') {
122
+ // Read from stdin
123
+ raw = await readStdin();
124
+ }
125
+ else if (arg.startsWith('@')) {
126
+ // Support @file.json and @- syntax
127
+ const source = arg.slice(1);
128
+ if (source === '-') {
129
+ raw = await readStdin();
130
+ }
131
+ else {
132
+ const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
133
+ try {
134
+ raw = await fs.readFile(source, 'utf-8');
135
+ }
136
+ catch (err) {
137
+ if (err.code === 'ENOENT') {
138
+ throw new errors_1.CliError(`File not found: ${source}`);
139
+ }
140
+ throw new errors_1.CliError(`Failed to read ${source}: ${err.message}`);
141
+ }
142
+ }
143
+ }
144
+ else {
145
+ raw = arg;
146
+ }
147
+ try {
148
+ return JSON.parse(raw);
149
+ }
150
+ catch {
151
+ throw new errors_1.CliError('Invalid JSON value.\n\n' +
152
+ 'Pass valid JSON, @file.json, or pipe via stdin:\n' +
153
+ ' orch storage set signals 2026-03-05 \'{"pending": []}\'\n' +
154
+ ' orch storage set signals 2026-03-05 @data.json\n' +
155
+ ' echo \'{"pending": []}\' | orch storage set signals 2026-03-05 -');
156
+ }
157
+ }
158
+ // ============================================
159
+ // COMMAND REGISTRATION
160
+ // ============================================
161
+ function registerStorageCommand(program) {
162
+ const storage = program
163
+ .command('storage')
164
+ .description('Manage agent storage (persistent shared key-value documents)')
165
+ .action(() => { storage.help(); });
166
+ // orch storage list [namespace]
167
+ storage
168
+ .command('list [namespace]')
169
+ .description('List namespaces, or list keys in a namespace')
170
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
171
+ .option('--limit <n>', 'Max keys to return (default: 100)', '100')
172
+ .option('--cursor <cursor>', 'Pagination cursor from previous response')
173
+ .option('--json', 'Output as JSON')
174
+ .action(async (namespace, options) => {
175
+ const config = await (0, config_1.getResolvedConfig)();
176
+ if (!config.apiKey) {
177
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
178
+ }
179
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
180
+ const headers = { 'X-Workspace-Id': workspaceId };
181
+ if (!namespace) {
182
+ // List namespaces
183
+ const result = await (0, api_1.request)(config, 'GET', '/storage', { headers });
184
+ if (options.json) {
185
+ (0, output_1.printJson)(result);
186
+ return;
187
+ }
188
+ if (result.namespaces.length === 0) {
189
+ process.stdout.write('No storage namespaces found.\n');
190
+ process.stdout.write(chalk_1.default.gray('\nCreate one: orch storage set my-namespace my-key \'{"hello": "world"}\'\n'));
191
+ return;
192
+ }
193
+ for (const ns of result.namespaces) {
194
+ process.stdout.write(` ${ns}\n`);
195
+ }
196
+ process.stdout.write(chalk_1.default.gray(`\n${result.namespaces.length} namespace(s)\n`));
197
+ }
198
+ else {
199
+ // List keys in namespace
200
+ validateNamespace(namespace);
201
+ const limit = parseInt(options.limit ?? '100', 10);
202
+ let path = `/storage/${namespace}?limit=${limit}`;
203
+ if (options.cursor)
204
+ path += `&cursor=${encodeURIComponent(options.cursor)}`;
205
+ const result = await (0, api_1.request)(config, 'GET', path, { headers });
206
+ if (options.json) {
207
+ (0, output_1.printJson)(result);
208
+ return;
209
+ }
210
+ if (result.keys.length === 0) {
211
+ process.stdout.write(`No keys found in namespace '${namespace}'.\n`);
212
+ return;
213
+ }
214
+ for (const key of result.keys) {
215
+ process.stdout.write(` ${key}\n`);
216
+ }
217
+ const countText = `${result.keys.length} key(s)`;
218
+ process.stdout.write(chalk_1.default.gray(`\n${countText}`));
219
+ if (result.has_more) {
220
+ process.stdout.write(chalk_1.default.gray(` (more available, use --cursor "${result.cursor}")`));
221
+ }
222
+ process.stdout.write('\n');
223
+ }
224
+ });
225
+ // orch storage get <namespace> <key>
226
+ storage
227
+ .command('get <namespace> <key>')
228
+ .description('Get a document by namespace and key')
229
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
230
+ .option('--json', 'Output as JSON (full response with metadata)')
231
+ .option('--raw', 'Output only the value (no metadata)')
232
+ .action(async (namespace, key, options) => {
233
+ const config = await (0, config_1.getResolvedConfig)();
234
+ if (!config.apiKey) {
235
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
236
+ }
237
+ validateNamespace(namespace);
238
+ validateKey(key);
239
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
240
+ const result = await (0, api_1.request)(config, 'GET', `/storage/${namespace}/${key}`, { headers: { 'X-Workspace-Id': workspaceId } });
241
+ if (options.raw) {
242
+ process.stdout.write(JSON.stringify(result.value, null, 2) + '\n');
243
+ return;
244
+ }
245
+ if (options.json) {
246
+ (0, output_1.printJson)(result);
247
+ return;
248
+ }
249
+ // Pretty output
250
+ process.stdout.write(chalk_1.default.gray(`namespace: ${result.namespace} key: ${result.key} `) +
251
+ chalk_1.default.gray(`v${result.version} ${formatBytes(result.size_bytes)} `) +
252
+ chalk_1.default.gray(`updated: ${formatDate(result.updated_at)} by ${result.updated_by}\n\n`));
253
+ process.stdout.write(JSON.stringify(result.value, null, 2) + '\n');
254
+ });
255
+ // orch storage set <namespace> <key> [value]
256
+ storage
257
+ .command('set <namespace> <key> [value]')
258
+ .description('Create or update a document (JSON string, @file.json, or - for stdin)')
259
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
260
+ .option('--version <n>', 'Expected version for compare-and-swap (CAS)')
261
+ .action(async (namespace, key, value, options) => {
262
+ const config = await (0, config_1.getResolvedConfig)();
263
+ if (!config.apiKey) {
264
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
265
+ }
266
+ validateNamespace(namespace);
267
+ validateKey(key);
268
+ const parsed = await parseJsonArg(value);
269
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
270
+ const headers = {
271
+ 'X-Workspace-Id': workspaceId,
272
+ 'X-Orchagent-Client': 'cli',
273
+ 'Content-Type': 'application/json',
274
+ };
275
+ if (options.version) {
276
+ headers['If-Match'] = options.version;
277
+ }
278
+ const result = await (0, api_1.request)(config, 'PUT', `/storage/${namespace}/${key}`, {
279
+ body: JSON.stringify(parsed),
280
+ headers,
281
+ });
282
+ process.stdout.write(chalk_1.default.green('\u2713') +
283
+ ` ${namespace}/${key} ` +
284
+ chalk_1.default.gray(`v${result.version} (${formatBytes(result.size_bytes)})\n`));
285
+ });
286
+ // orch storage patch <namespace> <key> [value]
287
+ storage
288
+ .command('patch <namespace> <key> [value]')
289
+ .description('Merge-patch a document (JSON string, @file.json, or - for stdin)')
290
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
291
+ .action(async (namespace, key, value, options) => {
292
+ const config = await (0, config_1.getResolvedConfig)();
293
+ if (!config.apiKey) {
294
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
295
+ }
296
+ validateNamespace(namespace);
297
+ validateKey(key);
298
+ const parsed = await parseJsonArg(value);
299
+ if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null) {
300
+ throw new errors_1.CliError('Patch value must be a JSON object (not array or primitive).');
301
+ }
302
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
303
+ const result = await (0, api_1.request)(config, 'PATCH', `/storage/${namespace}/${key}`, {
304
+ body: JSON.stringify(parsed),
305
+ headers: {
306
+ 'X-Workspace-Id': workspaceId,
307
+ 'X-Orchagent-Client': 'cli',
308
+ 'Content-Type': 'application/json',
309
+ },
310
+ });
311
+ process.stdout.write(chalk_1.default.green('\u2713') +
312
+ ` Patched ${namespace}/${key} ` +
313
+ chalk_1.default.gray(`v${result.version} (${formatBytes(result.size_bytes)})\n`));
314
+ });
315
+ // orch storage delete <namespace> [key]
316
+ storage
317
+ .command('delete <namespace> [key]')
318
+ .description('Delete a document, or all documents in a namespace (with --all)')
319
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
320
+ .option('--all', 'Delete all documents in the namespace')
321
+ .action(async (namespace, key, options) => {
322
+ const config = await (0, config_1.getResolvedConfig)();
323
+ if (!config.apiKey) {
324
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
325
+ }
326
+ validateNamespace(namespace);
327
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
328
+ const headers = {
329
+ 'X-Workspace-Id': workspaceId,
330
+ 'X-Orchagent-Client': 'cli',
331
+ };
332
+ if (options.all) {
333
+ // Delete entire namespace
334
+ const result = await (0, api_1.request)(config, 'DELETE', `/storage/${namespace}`, { headers });
335
+ process.stdout.write(chalk_1.default.green('\u2713') +
336
+ ` Deleted namespace '${namespace}' (${result.documents_deleted ?? 0} document(s))\n`);
337
+ }
338
+ else if (key) {
339
+ // Delete single document
340
+ validateKey(key);
341
+ await (0, api_1.request)(config, 'DELETE', `/storage/${namespace}/${key}`, { headers });
342
+ process.stdout.write(chalk_1.default.green('\u2713') + ` Deleted ${namespace}/${key}\n`);
343
+ }
344
+ else {
345
+ throw new errors_1.CliError('Specify a key to delete, or use --all to delete the entire namespace.\n\n' +
346
+ 'Examples:\n' +
347
+ ` orch storage delete ${namespace} my-key\n` +
348
+ ` orch storage delete ${namespace} --all`);
349
+ }
350
+ });
351
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.117",
3
+ "version": "0.3.119",
4
4
  "description": "Command-line interface for orchagent — deploy and run AI agents for your team",
5
5
  "license": "MIT",
6
6
  "author": "orchagent <hello@orchagent.io>",