@stndrds/cli 1.0.0-alpha.1

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.
Files changed (2) hide show
  1. package/dist/bin.mjs +360 -0
  2. package/package.json +46 -0
package/dist/bin.mjs ADDED
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/bin.ts
4
+ import chalk3 from "chalk";
5
+ import { Command } from "commander";
6
+
7
+ // src/client.ts
8
+ var ApiClientError = class extends Error {
9
+ constructor(statusCode, message) {
10
+ super(message);
11
+ this.statusCode = statusCode;
12
+ this.name = "ApiClientError";
13
+ }
14
+ };
15
+ function buildUrl(base, path, params) {
16
+ const url = new URL(path, base);
17
+ if (params) {
18
+ for (const [key, value] of Object.entries(params)) {
19
+ if (value !== void 0 && value !== null) {
20
+ url.searchParams.set(key, String(value));
21
+ }
22
+ }
23
+ }
24
+ return url.toString();
25
+ }
26
+ function createClient(config) {
27
+ const { apiUrl, apiKey } = config;
28
+ const headers = {
29
+ Authorization: `Bearer ${apiKey}`,
30
+ "Content-Type": "application/json"
31
+ };
32
+ async function request(method, path, options) {
33
+ const url = buildUrl(apiUrl, path, options?.params);
34
+ let response;
35
+ try {
36
+ response = await fetch(url, {
37
+ method,
38
+ headers,
39
+ body: options?.body ? JSON.stringify(options.body) : void 0,
40
+ signal: AbortSignal.timeout(3e4)
41
+ });
42
+ } catch (error) {
43
+ if (error instanceof TypeError || error instanceof DOMException && error.name === "TimeoutError") {
44
+ throw new ApiClientError(0, `Could not connect to ${apiUrl}. Is the server running?`);
45
+ }
46
+ throw error;
47
+ }
48
+ if (response.status === 204) {
49
+ return null;
50
+ }
51
+ const data = await response.json();
52
+ if (!response.ok) {
53
+ const message = data.message ?? `Request failed with status ${response.status}`;
54
+ throw new ApiClientError(response.status, message);
55
+ }
56
+ return data;
57
+ }
58
+ return {
59
+ get(path, params) {
60
+ return request("GET", path, { params });
61
+ },
62
+ post(path, body) {
63
+ return request("POST", path, { body });
64
+ },
65
+ patch(path, body) {
66
+ return request("PATCH", path, { body });
67
+ },
68
+ put(path, body) {
69
+ return request("PUT", path, { body });
70
+ },
71
+ delete(path) {
72
+ return request("DELETE", path);
73
+ }
74
+ };
75
+ }
76
+
77
+ // src/commands/auth.ts
78
+ import chalk2 from "chalk";
79
+
80
+ // src/output.ts
81
+ import chalk from "chalk";
82
+ import Table from "cli-table3";
83
+ var MAX_CELL_WIDTH = 50;
84
+ function truncate(value, maxLength) {
85
+ if (value.length <= maxLength) return value;
86
+ return `${value.slice(0, maxLength - 1)}\u2026`;
87
+ }
88
+ function escapeCsvValue(value) {
89
+ if (value === null || value === void 0) return "";
90
+ const str = typeof value === "object" ? JSON.stringify(value) : String(value);
91
+ if (str.includes(",") || str.includes('"') || str.includes("\n")) {
92
+ return `"${str.replace(/"/g, '""')}"`;
93
+ }
94
+ return str;
95
+ }
96
+ function formatJson(data) {
97
+ return JSON.stringify(data, null, 2);
98
+ }
99
+ function formatCsv(items) {
100
+ if (items.length === 0) return "";
101
+ const headers = Object.keys(items[0]);
102
+ const headerLine = headers.join(",");
103
+ const rows = items.map((item) => headers.map((h) => escapeCsvValue(item[h])).join(","));
104
+ return [headerLine, ...rows].join("\n");
105
+ }
106
+ function formatTable(items) {
107
+ if (items.length === 0) return "No results.";
108
+ const headers = Object.keys(items[0]);
109
+ const table = new Table({
110
+ head: headers.map((h) => chalk.bold(h)),
111
+ style: { head: [], border: [] }
112
+ });
113
+ for (const item of items) {
114
+ const row = headers.map((h) => {
115
+ const val = item[h];
116
+ if (val === null || val === void 0) return "";
117
+ const str = typeof val === "object" ? JSON.stringify(val) : String(val);
118
+ return truncate(str, MAX_CELL_WIDTH);
119
+ });
120
+ table.push(row);
121
+ }
122
+ return table.toString();
123
+ }
124
+ function formatOutput(data, format) {
125
+ if (format === "json") {
126
+ process.stdout.write(`${formatJson(data)}
127
+ `);
128
+ return;
129
+ }
130
+ const items = Array.isArray(data) ? data : [data];
131
+ if (format === "csv") {
132
+ process.stdout.write(`${formatCsv(items)}
133
+ `);
134
+ return;
135
+ }
136
+ process.stdout.write(`${formatTable(items)}
137
+ `);
138
+ }
139
+
140
+ // src/commands/auth.ts
141
+ function getClientFromCommand(cmd) {
142
+ const root = cmd.optsWithGlobals();
143
+ return createClient({ apiUrl: root.apiUrl, apiKey: root.apiKey });
144
+ }
145
+ function getFormat(cmd) {
146
+ return cmd.optsWithGlobals().format;
147
+ }
148
+ function registerAuthCommand(program2) {
149
+ const auth = program2.command("auth").description("Manage authentication and API keys");
150
+ auth.command("whoami").description("Show current configuration and validate API key").action(async (_opts, cmd) => {
151
+ const root = cmd.optsWithGlobals();
152
+ const client = getClientFromCommand(cmd);
153
+ const write = (msg) => process.stdout.write(`${msg}
154
+ `);
155
+ write(chalk2.bold("Standards CLI Configuration"));
156
+ write(` API URL: ${root.apiUrl}`);
157
+ write(` API Key: ${root.apiKey ? `${root.apiKey.slice(0, 8)}...` : chalk2.red("not set")}`);
158
+ write("");
159
+ const keys = await client.get("/api-keys");
160
+ write(chalk2.green("\u2713 API key is valid"));
161
+ if (Array.isArray(keys) && keys.length > 0) {
162
+ write(` Active keys: ${keys.length}`);
163
+ }
164
+ });
165
+ auth.command("create-key").description("Create a new API key").requiredOption("--name <name>", "name for the API key").option("--expires <date>", "expiration date (ISO 8601)").action(async (opts, cmd) => {
166
+ const client = getClientFromCommand(cmd);
167
+ const body = {
168
+ name: opts.name,
169
+ permissions: []
170
+ };
171
+ if (opts.expires) body.expiresAt = opts.expires;
172
+ const result = await client.post("/api-keys", body);
173
+ formatOutput(result, getFormat(cmd));
174
+ });
175
+ }
176
+
177
+ // src/commands/documents.ts
178
+ function getClientFromCommand2(cmd) {
179
+ const root = cmd.optsWithGlobals();
180
+ return createClient({ apiUrl: root.apiUrl, apiKey: root.apiKey });
181
+ }
182
+ function getFormat2(cmd) {
183
+ return cmd.optsWithGlobals().format;
184
+ }
185
+ function registerDocumentsCommand(program2) {
186
+ const documents = program2.command("documents").description("Manage documents");
187
+ documents.command("list").description("List documents").option("--status <status>", "filter by processing status").option("--limit <n>", "max documents to return").option("--offset <n>", "number of documents to skip").action(async (opts, cmd) => {
188
+ const client = getClientFromCommand2(cmd);
189
+ const params = {};
190
+ if (opts.status) params.processingStatus = opts.status;
191
+ if (opts.limit) params.limit = opts.limit;
192
+ if (opts.offset) params.offset = opts.offset;
193
+ const result = await client.get("/documents", params);
194
+ formatOutput(result, getFormat2(cmd));
195
+ });
196
+ documents.command("get").description("Get a document by ID").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
197
+ const client = getClientFromCommand2(cmd);
198
+ const result = await client.get(`/documents/${id}`);
199
+ formatOutput(result, getFormat2(cmd));
200
+ });
201
+ documents.command("create").description("Create a new document").requiredOption("--title <title>", "document title").option("--values <json>", "document values as JSON string").action(async (opts, cmd) => {
202
+ const client = getClientFromCommand2(cmd);
203
+ const body = { title: opts.title };
204
+ if (opts.values) body.values = JSON.parse(opts.values);
205
+ const result = await client.post("/documents", body);
206
+ formatOutput(result, getFormat2(cmd));
207
+ });
208
+ documents.command("update").description("Update a document").argument("<id>", "document ID").option("--title <title>", "new title").option("--values <json>", "new values as JSON string").action(async (id, opts, cmd) => {
209
+ const client = getClientFromCommand2(cmd);
210
+ const body = {};
211
+ if (opts.title) body.title = opts.title;
212
+ if (opts.values) body.values = JSON.parse(opts.values);
213
+ const result = await client.patch(`/documents/${id}`, body);
214
+ formatOutput(result, getFormat2(cmd));
215
+ });
216
+ documents.command("delete").description("Delete a document").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
217
+ const client = getClientFromCommand2(cmd);
218
+ await client.delete(`/documents/${id}`);
219
+ process.stdout.write(`Document ${id} deleted.
220
+ `);
221
+ });
222
+ documents.command("preview").description("Get preview file for a document").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
223
+ const client = getClientFromCommand2(cmd);
224
+ const result = await client.get(`/documents/${id}/preview`);
225
+ formatOutput(result, getFormat2(cmd));
226
+ });
227
+ documents.command("slots").description("List slots for a document").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
228
+ const client = getClientFromCommand2(cmd);
229
+ const result = await client.get(`/documents/${id}/slots`);
230
+ formatOutput(result, getFormat2(cmd));
231
+ });
232
+ documents.command("add-slot").description("Add a file slot to a document").argument("<id>", "document ID").requiredOption("--slot-name <name>", "slot name").requiredOption("--file-id <fileId>", "file ID").action(async (id, opts, cmd) => {
233
+ const client = getClientFromCommand2(cmd);
234
+ const result = await client.post(`/documents/${id}/slots`, {
235
+ slotName: opts.slotName,
236
+ fileId: opts.fileId
237
+ });
238
+ formatOutput(result, getFormat2(cmd));
239
+ });
240
+ documents.command("remove-slot").description("Remove a slot from a document").argument("<id>", "document ID").argument("<slotId>", "slot ID").action(async (id, slotId, _opts, cmd) => {
241
+ const client = getClientFromCommand2(cmd);
242
+ await client.delete(`/documents/${id}/slots/${slotId}`);
243
+ process.stdout.write(`Slot ${slotId} removed.
244
+ `);
245
+ });
246
+ }
247
+
248
+ // src/commands/records.ts
249
+ function getClientFromCommand3(cmd) {
250
+ const root = cmd.optsWithGlobals();
251
+ return createClient({ apiUrl: root.apiUrl, apiKey: root.apiKey });
252
+ }
253
+ function getFormat3(cmd) {
254
+ return cmd.optsWithGlobals().format;
255
+ }
256
+ function parseSortFlag(sort) {
257
+ const [attribute, direction = "asc"] = sort.split(":");
258
+ return JSON.stringify([{ attribute, direction }]);
259
+ }
260
+ function registerRecordsCommand(program2) {
261
+ const records = program2.command("records").description("Manage records (CRUD + search)");
262
+ records.command("list").description("List records for an object").argument("<object>", "object name (e.g. contacts)").option("--limit <n>", "max records to return").option("--offset <n>", "number of records to skip").option("--sort <sort>", 'sort rule as "attribute:direction"').option("--filter <json>", "filter state as JSON string").action(async (objectName, opts, cmd) => {
263
+ const client = getClientFromCommand3(cmd);
264
+ const params = {};
265
+ if (opts.limit) params.limit = opts.limit;
266
+ if (opts.offset) params.offset = opts.offset;
267
+ if (opts.sort) params.sorts = parseSortFlag(opts.sort);
268
+ if (opts.filter) params.filters = opts.filter;
269
+ const result = await client.get(`/records/${objectName}`, params);
270
+ formatOutput(result, getFormat3(cmd));
271
+ });
272
+ records.command("get").description("Get a record by ID").argument("<object>", "object name").argument("<id>", "record ID").action(async (objectName, id, _opts, cmd) => {
273
+ const client = getClientFromCommand3(cmd);
274
+ const result = await client.get(`/records/${objectName}/${id}`);
275
+ formatOutput(result, getFormat3(cmd));
276
+ });
277
+ records.command("create").description("Create a new record").argument("<object>", "object name").option("--data <json>", "record data as JSON string").action(async (objectName, opts, cmd) => {
278
+ const data = opts.data ? JSON.parse(opts.data) : {};
279
+ const client = getClientFromCommand3(cmd);
280
+ const result = await client.post(`/records/${objectName}`, data);
281
+ formatOutput(result, getFormat3(cmd));
282
+ });
283
+ records.command("update").description("Update a record").argument("<object>", "object name").argument("<id>", "record ID").option("--data <json>", "fields to update as JSON string").action(async (objectName, id, opts, cmd) => {
284
+ const data = opts.data ? JSON.parse(opts.data) : {};
285
+ const client = getClientFromCommand3(cmd);
286
+ const result = await client.put(`/records/${objectName}/${id}`, data);
287
+ formatOutput(result, getFormat3(cmd));
288
+ });
289
+ records.command("delete").description("Delete a record").argument("<object>", "object name").argument("<id>", "record ID").action(async (objectName, id, _opts, cmd) => {
290
+ const client = getClientFromCommand3(cmd);
291
+ await client.delete(`/records/${objectName}/${id}`);
292
+ process.stdout.write(`Record ${id} deleted.
293
+ `);
294
+ });
295
+ records.command("search").description("Full-text search records").argument("<object>", "object name").requiredOption("--query <q>", "search query string").option("--limit <n>", "max records to return").option("--offset <n>", "number of records to skip").option("--sort <sort>", 'sort rule as "attribute:direction"').option("--filter <json>", "filter state as JSON string").action(async (objectName, opts, cmd) => {
296
+ const client = getClientFromCommand3(cmd);
297
+ const params = { q: opts.query };
298
+ if (opts.limit) params.limit = opts.limit;
299
+ if (opts.offset) params.offset = opts.offset;
300
+ if (opts.sort) params.sorts = parseSortFlag(opts.sort);
301
+ if (opts.filter) params.filters = opts.filter;
302
+ const result = await client.get(`/records/${objectName}/search`, params);
303
+ formatOutput(result, getFormat3(cmd));
304
+ });
305
+ }
306
+
307
+ // src/commands/schema.ts
308
+ function getClientFromCommand4(cmd) {
309
+ const root = cmd.optsWithGlobals();
310
+ return createClient({ apiUrl: root.apiUrl, apiKey: root.apiKey });
311
+ }
312
+ function getFormat4(cmd) {
313
+ return cmd.optsWithGlobals().format;
314
+ }
315
+ function registerSchemaCommand(program2) {
316
+ const schema = program2.command("schema").description("Inspect schema objects and attributes");
317
+ schema.command("list").description("List all schema objects").action(async (_opts, cmd) => {
318
+ const client = getClientFromCommand4(cmd);
319
+ const result = await client.get("/schema/objects");
320
+ formatOutput(result, getFormat4(cmd));
321
+ });
322
+ schema.command("inspect").description("Inspect an object (attributes, types, views)").argument("<object>", "object name or ID").action(async (objectName, _opts, cmd) => {
323
+ const client = getClientFromCommand4(cmd);
324
+ const result = await client.get(`/schema/objects/${objectName}`);
325
+ formatOutput(result, getFormat4(cmd));
326
+ });
327
+ }
328
+
329
+ // src/bin.ts
330
+ var program = new Command();
331
+ program.name("standards").description("CLI to interact with Standards API").version("1.0.0-alpha.1").option("--format <format>", "output format (json, table, csv)", "json").option(
332
+ "--api-url <url>",
333
+ "API base URL",
334
+ process.env.STANDARDS_API_URL ?? "http://localhost:4100"
335
+ ).option("--api-key <key>", "API key for authentication", process.env.STANDARDS_API_KEY);
336
+ registerRecordsCommand(program);
337
+ registerSchemaCommand(program);
338
+ registerDocumentsCommand(program);
339
+ registerAuthCommand(program);
340
+ program.hook("preAction", (_thisCommand, _actionCommand) => {
341
+ const opts = program.opts();
342
+ if (!opts.apiKey) {
343
+ console.error(
344
+ chalk3.red("\u2717 Error: No API key configured. Set STANDARDS_API_KEY or use --api-key")
345
+ );
346
+ process.exit(1);
347
+ }
348
+ });
349
+ program.parseAsync(process.argv).catch((error) => {
350
+ if (error instanceof ApiClientError) {
351
+ if (error.statusCode > 0) {
352
+ console.error(chalk3.red(`\u2717 Error (${error.statusCode}): ${error.message}`));
353
+ } else {
354
+ console.error(chalk3.red(`\u2717 Error: ${error.message}`));
355
+ }
356
+ } else if (error instanceof Error) {
357
+ console.error(chalk3.red(`\u2717 Error: ${error.message}`));
358
+ }
359
+ process.exit(1);
360
+ });
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@stndrds/cli",
3
+ "version": "1.0.0-alpha.1",
4
+ "description": "CLI tool to interact with Standards API",
5
+ "type": "module",
6
+ "bin": {
7
+ "standards": "./dist/bin.mjs"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "dependencies": {
13
+ "chalk": "^5.4.1",
14
+ "cli-table3": "^0.6.5",
15
+ "commander": "^13.1.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^25.6.0",
19
+ "tsup": "^8.3.5",
20
+ "typescript": "^5.6.3",
21
+ "vitest": "^2.1.9"
22
+ },
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "registry": "https://registry.npmjs.org/"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/andyoucreate/standards.git",
30
+ "directory": "packages/cli"
31
+ },
32
+ "license": "MIT",
33
+ "author": "AndYouCreate",
34
+ "bugs": {
35
+ "url": "https://github.com/andyoucreate/standards/issues"
36
+ },
37
+ "homepage": "https://github.com/andyoucreate/standards#readme",
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "dev": "tsup --watch",
41
+ "lint": "biome check .",
42
+ "test": "vitest run",
43
+ "typecheck": "tsc --noEmit",
44
+ "clean": "rm -rf dist"
45
+ }
46
+ }