@orchagent/cli 0.3.117 → 0.3.118

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,319 @@
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 parseJsonArg(arg) {
102
+ // Support @file.json syntax
103
+ if (arg.startsWith('@')) {
104
+ const fs = await Promise.resolve().then(() => __importStar(require('fs/promises')));
105
+ const filePath = arg.slice(1);
106
+ try {
107
+ const content = await fs.readFile(filePath, 'utf-8');
108
+ return JSON.parse(content);
109
+ }
110
+ catch (err) {
111
+ if (err.code === 'ENOENT') {
112
+ throw new errors_1.CliError(`File not found: ${filePath}`);
113
+ }
114
+ throw new errors_1.CliError(`Invalid JSON in ${filePath}: ${err.message}`);
115
+ }
116
+ }
117
+ try {
118
+ return JSON.parse(arg);
119
+ }
120
+ catch {
121
+ throw new errors_1.CliError('Invalid JSON value.\n\n' +
122
+ 'Pass valid JSON or use @file.json to read from a file.\n' +
123
+ 'Example: orch storage set signals 2026-03-05 \'{"pending": [], "done": []}\'');
124
+ }
125
+ }
126
+ // ============================================
127
+ // COMMAND REGISTRATION
128
+ // ============================================
129
+ function registerStorageCommand(program) {
130
+ const storage = program
131
+ .command('storage')
132
+ .description('Manage agent storage (persistent shared key-value documents)')
133
+ .action(() => { storage.help(); });
134
+ // orch storage list [namespace]
135
+ storage
136
+ .command('list [namespace]')
137
+ .description('List namespaces, or list keys in a namespace')
138
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
139
+ .option('--limit <n>', 'Max keys to return (default: 100)', '100')
140
+ .option('--cursor <cursor>', 'Pagination cursor from previous response')
141
+ .option('--json', 'Output as JSON')
142
+ .action(async (namespace, options) => {
143
+ const config = await (0, config_1.getResolvedConfig)();
144
+ if (!config.apiKey) {
145
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
146
+ }
147
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
148
+ const headers = { 'X-Workspace-Id': workspaceId };
149
+ if (!namespace) {
150
+ // List namespaces
151
+ const result = await (0, api_1.request)(config, 'GET', '/storage', { headers });
152
+ if (options.json) {
153
+ (0, output_1.printJson)(result);
154
+ return;
155
+ }
156
+ if (result.namespaces.length === 0) {
157
+ process.stdout.write('No storage namespaces found.\n');
158
+ process.stdout.write(chalk_1.default.gray('\nCreate one: orch storage set my-namespace my-key \'{"hello": "world"}\'\n'));
159
+ return;
160
+ }
161
+ for (const ns of result.namespaces) {
162
+ process.stdout.write(` ${ns}\n`);
163
+ }
164
+ process.stdout.write(chalk_1.default.gray(`\n${result.namespaces.length} namespace(s)\n`));
165
+ }
166
+ else {
167
+ // List keys in namespace
168
+ validateNamespace(namespace);
169
+ const limit = parseInt(options.limit ?? '100', 10);
170
+ let path = `/storage/${namespace}?limit=${limit}`;
171
+ if (options.cursor)
172
+ path += `&cursor=${encodeURIComponent(options.cursor)}`;
173
+ const result = await (0, api_1.request)(config, 'GET', path, { headers });
174
+ if (options.json) {
175
+ (0, output_1.printJson)(result);
176
+ return;
177
+ }
178
+ if (result.keys.length === 0) {
179
+ process.stdout.write(`No keys found in namespace '${namespace}'.\n`);
180
+ return;
181
+ }
182
+ for (const key of result.keys) {
183
+ process.stdout.write(` ${key}\n`);
184
+ }
185
+ const countText = `${result.keys.length} key(s)`;
186
+ process.stdout.write(chalk_1.default.gray(`\n${countText}`));
187
+ if (result.has_more) {
188
+ process.stdout.write(chalk_1.default.gray(` (more available, use --cursor "${result.cursor}")`));
189
+ }
190
+ process.stdout.write('\n');
191
+ }
192
+ });
193
+ // orch storage get <namespace> <key>
194
+ storage
195
+ .command('get <namespace> <key>')
196
+ .description('Get a document by namespace and key')
197
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
198
+ .option('--json', 'Output as JSON (full response with metadata)')
199
+ .option('--raw', 'Output only the value (no metadata)')
200
+ .action(async (namespace, key, options) => {
201
+ const config = await (0, config_1.getResolvedConfig)();
202
+ if (!config.apiKey) {
203
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
204
+ }
205
+ validateNamespace(namespace);
206
+ validateKey(key);
207
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
208
+ const result = await (0, api_1.request)(config, 'GET', `/storage/${namespace}/${key}`, { headers: { 'X-Workspace-Id': workspaceId } });
209
+ if (options.raw) {
210
+ process.stdout.write(JSON.stringify(result.value, null, 2) + '\n');
211
+ return;
212
+ }
213
+ if (options.json) {
214
+ (0, output_1.printJson)(result);
215
+ return;
216
+ }
217
+ // Pretty output
218
+ process.stdout.write(chalk_1.default.gray(`namespace: ${result.namespace} key: ${result.key} `) +
219
+ chalk_1.default.gray(`v${result.version} ${formatBytes(result.size_bytes)} `) +
220
+ chalk_1.default.gray(`updated: ${formatDate(result.updated_at)} by ${result.updated_by}\n\n`));
221
+ process.stdout.write(JSON.stringify(result.value, null, 2) + '\n');
222
+ });
223
+ // orch storage set <namespace> <key> <value>
224
+ storage
225
+ .command('set <namespace> <key> <value>')
226
+ .description('Create or update a document (JSON string or @file.json)')
227
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
228
+ .option('--version <n>', 'Expected version for compare-and-swap (CAS)')
229
+ .action(async (namespace, key, value, options) => {
230
+ const config = await (0, config_1.getResolvedConfig)();
231
+ if (!config.apiKey) {
232
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
233
+ }
234
+ validateNamespace(namespace);
235
+ validateKey(key);
236
+ const parsed = await parseJsonArg(value);
237
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
238
+ const headers = {
239
+ 'X-Workspace-Id': workspaceId,
240
+ 'X-Orchagent-Client': 'cli',
241
+ 'Content-Type': 'application/json',
242
+ };
243
+ if (options.version) {
244
+ headers['If-Match'] = options.version;
245
+ }
246
+ const result = await (0, api_1.request)(config, 'PUT', `/storage/${namespace}/${key}`, {
247
+ body: JSON.stringify(parsed),
248
+ headers,
249
+ });
250
+ process.stdout.write(chalk_1.default.green('\u2713') +
251
+ ` ${namespace}/${key} ` +
252
+ chalk_1.default.gray(`v${result.version} (${formatBytes(result.size_bytes)})\n`));
253
+ });
254
+ // orch storage patch <namespace> <key> <value>
255
+ storage
256
+ .command('patch <namespace> <key> <value>')
257
+ .description('Merge-patch a document (shallow merge JSON into existing)')
258
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
259
+ .action(async (namespace, key, value, options) => {
260
+ const config = await (0, config_1.getResolvedConfig)();
261
+ if (!config.apiKey) {
262
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
263
+ }
264
+ validateNamespace(namespace);
265
+ validateKey(key);
266
+ const parsed = await parseJsonArg(value);
267
+ if (typeof parsed !== 'object' || Array.isArray(parsed) || parsed === null) {
268
+ throw new errors_1.CliError('Patch value must be a JSON object (not array or primitive).');
269
+ }
270
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
271
+ const result = await (0, api_1.request)(config, 'PATCH', `/storage/${namespace}/${key}`, {
272
+ body: JSON.stringify(parsed),
273
+ headers: {
274
+ 'X-Workspace-Id': workspaceId,
275
+ 'X-Orchagent-Client': 'cli',
276
+ 'Content-Type': 'application/json',
277
+ },
278
+ });
279
+ process.stdout.write(chalk_1.default.green('\u2713') +
280
+ ` Patched ${namespace}/${key} ` +
281
+ chalk_1.default.gray(`v${result.version} (${formatBytes(result.size_bytes)})\n`));
282
+ });
283
+ // orch storage delete <namespace> [key]
284
+ storage
285
+ .command('delete <namespace> [key]')
286
+ .description('Delete a document, or all documents in a namespace (with --all)')
287
+ .option('--workspace <slug>', 'Workspace slug (default: current workspace)')
288
+ .option('--all', 'Delete all documents in the namespace')
289
+ .action(async (namespace, key, options) => {
290
+ const config = await (0, config_1.getResolvedConfig)();
291
+ if (!config.apiKey) {
292
+ throw new errors_1.CliError('Missing API key. Run `orch login` first.');
293
+ }
294
+ validateNamespace(namespace);
295
+ const workspaceId = await resolveWorkspaceId(config, options.workspace);
296
+ const headers = {
297
+ 'X-Workspace-Id': workspaceId,
298
+ 'X-Orchagent-Client': 'cli',
299
+ };
300
+ if (options.all) {
301
+ // Delete entire namespace
302
+ const result = await (0, api_1.request)(config, 'DELETE', `/storage/${namespace}`, { headers });
303
+ process.stdout.write(chalk_1.default.green('\u2713') +
304
+ ` Deleted namespace '${namespace}' (${result.documents_deleted ?? 0} document(s))\n`);
305
+ }
306
+ else if (key) {
307
+ // Delete single document
308
+ validateKey(key);
309
+ await (0, api_1.request)(config, 'DELETE', `/storage/${namespace}/${key}`, { headers });
310
+ process.stdout.write(chalk_1.default.green('\u2713') + ` Deleted ${namespace}/${key}\n`);
311
+ }
312
+ else {
313
+ throw new errors_1.CliError('Specify a key to delete, or use --all to delete the entire namespace.\n\n' +
314
+ 'Examples:\n' +
315
+ ` orch storage delete ${namespace} my-key\n` +
316
+ ` orch storage delete ${namespace} --all`);
317
+ }
318
+ });
319
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orchagent/cli",
3
- "version": "0.3.117",
3
+ "version": "0.3.118",
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>",