@stndrds/cli 1.0.0-alpha.2 → 1.0.0-alpha.3
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/dist/bin.mjs +327 -132
- package/package.json +1 -1
package/dist/bin.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// src/
|
|
4
|
-
import
|
|
3
|
+
// src/program.ts
|
|
4
|
+
import chalk5 from "chalk";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
|
|
7
7
|
// src/client.ts
|
|
@@ -13,7 +13,9 @@ var ApiClientError = class extends Error {
|
|
|
13
13
|
}
|
|
14
14
|
};
|
|
15
15
|
function buildUrl(base, path, params) {
|
|
16
|
-
const
|
|
16
|
+
const normalizedBase = base.endsWith("/") ? base : `${base}/`;
|
|
17
|
+
const normalizedPath = path.replace(/^\/+/, "");
|
|
18
|
+
const url = new URL(normalizedPath, normalizedBase);
|
|
17
19
|
if (params) {
|
|
18
20
|
for (const [key, value] of Object.entries(params)) {
|
|
19
21
|
if (value !== void 0 && value !== null) {
|
|
@@ -75,10 +77,48 @@ function createClient(config) {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
// src/commands/auth.ts
|
|
78
|
-
import
|
|
80
|
+
import chalk from "chalk";
|
|
81
|
+
|
|
82
|
+
// src/commands/common.ts
|
|
83
|
+
function getGlobalOptions(cmd) {
|
|
84
|
+
return cmd.optsWithGlobals();
|
|
85
|
+
}
|
|
86
|
+
function getFormat(cmd) {
|
|
87
|
+
return getGlobalOptions(cmd).format;
|
|
88
|
+
}
|
|
89
|
+
function getClientFromCommand(cmd) {
|
|
90
|
+
const root = getGlobalOptions(cmd);
|
|
91
|
+
if (!(root.apiUrl && root.apiKey)) {
|
|
92
|
+
throw new Error('No Standards instance configured. Run "standards login" or pass --api-key.');
|
|
93
|
+
}
|
|
94
|
+
return createClient({ apiUrl: root.apiUrl, apiKey: root.apiKey });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/commands/auth.ts
|
|
98
|
+
function registerAuthCommand(program) {
|
|
99
|
+
const auth = program.command("auth").description("Inspect CLI authentication");
|
|
100
|
+
auth.command("whoami").description("Show current configuration and validate API key").action(async (_opts, cmd) => {
|
|
101
|
+
const root = getGlobalOptions(cmd);
|
|
102
|
+
const client = getClientFromCommand(cmd);
|
|
103
|
+
const write = (msg) => process.stdout.write(`${msg}
|
|
104
|
+
`);
|
|
105
|
+
write(chalk.bold("Standards CLI Configuration"));
|
|
106
|
+
write(` API URL: ${root.apiUrl}`);
|
|
107
|
+
write(` API Key: ${root.apiKey ? `${root.apiKey.slice(0, 8)}...` : chalk.red("not set")}`);
|
|
108
|
+
write("");
|
|
109
|
+
const keys = await client.get("/api-keys");
|
|
110
|
+
write(chalk.green("\u2713 API key is valid"));
|
|
111
|
+
if (Array.isArray(keys) && keys.length > 0) {
|
|
112
|
+
const activeKeys = keys.filter(
|
|
113
|
+
(key) => typeof key === "object" && key !== null && !("revokedAt" in key && key.revokedAt)
|
|
114
|
+
);
|
|
115
|
+
write(` Active keys: ${activeKeys.length}`);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
79
119
|
|
|
80
120
|
// src/output.ts
|
|
81
|
-
import
|
|
121
|
+
import chalk2 from "chalk";
|
|
82
122
|
import Table from "cli-table3";
|
|
83
123
|
var MAX_CELL_WIDTH = 50;
|
|
84
124
|
function truncate(value, maxLength) {
|
|
@@ -107,7 +147,7 @@ function formatTable(items) {
|
|
|
107
147
|
if (items.length === 0) return "No results.";
|
|
108
148
|
const headers = Object.keys(items[0]);
|
|
109
149
|
const table = new Table({
|
|
110
|
-
head: headers.map((h) =>
|
|
150
|
+
head: headers.map((h) => chalk2.bold(h)),
|
|
111
151
|
style: { head: [], border: [] }
|
|
112
152
|
});
|
|
113
153
|
for (const item of items) {
|
|
@@ -137,224 +177,379 @@ function formatOutput(data, format) {
|
|
|
137
177
|
`);
|
|
138
178
|
}
|
|
139
179
|
|
|
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
180
|
// src/commands/documents.ts
|
|
178
|
-
function
|
|
179
|
-
const
|
|
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");
|
|
181
|
+
function registerDocumentsCommand(program) {
|
|
182
|
+
const documents = program.command("documents").description("Manage documents");
|
|
187
183
|
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 =
|
|
184
|
+
const client = getClientFromCommand(cmd);
|
|
189
185
|
const params = {};
|
|
190
186
|
if (opts.status) params.processingStatus = opts.status;
|
|
191
187
|
if (opts.limit) params.limit = opts.limit;
|
|
192
188
|
if (opts.offset) params.offset = opts.offset;
|
|
193
189
|
const result = await client.get("/documents", params);
|
|
194
|
-
formatOutput(result,
|
|
190
|
+
formatOutput(result, getFormat(cmd));
|
|
195
191
|
});
|
|
196
192
|
documents.command("get").description("Get a document by ID").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
|
|
197
|
-
const client =
|
|
193
|
+
const client = getClientFromCommand(cmd);
|
|
198
194
|
const result = await client.get(`/documents/${id}`);
|
|
199
|
-
formatOutput(result,
|
|
195
|
+
formatOutput(result, getFormat(cmd));
|
|
200
196
|
});
|
|
201
197
|
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 =
|
|
198
|
+
const client = getClientFromCommand(cmd);
|
|
203
199
|
const body = { title: opts.title };
|
|
204
200
|
if (opts.values) body.values = JSON.parse(opts.values);
|
|
205
201
|
const result = await client.post("/documents", body);
|
|
206
|
-
formatOutput(result,
|
|
202
|
+
formatOutput(result, getFormat(cmd));
|
|
207
203
|
});
|
|
208
204
|
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 =
|
|
205
|
+
const client = getClientFromCommand(cmd);
|
|
210
206
|
const body = {};
|
|
211
207
|
if (opts.title) body.title = opts.title;
|
|
212
208
|
if (opts.values) body.values = JSON.parse(opts.values);
|
|
213
209
|
const result = await client.patch(`/documents/${id}`, body);
|
|
214
|
-
formatOutput(result,
|
|
210
|
+
formatOutput(result, getFormat(cmd));
|
|
215
211
|
});
|
|
216
212
|
documents.command("delete").description("Delete a document").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
|
|
217
|
-
const client =
|
|
213
|
+
const client = getClientFromCommand(cmd);
|
|
218
214
|
await client.delete(`/documents/${id}`);
|
|
219
215
|
process.stdout.write(`Document ${id} deleted.
|
|
220
216
|
`);
|
|
221
217
|
});
|
|
222
218
|
documents.command("preview").description("Get preview file for a document").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
|
|
223
|
-
const client =
|
|
219
|
+
const client = getClientFromCommand(cmd);
|
|
224
220
|
const result = await client.get(`/documents/${id}/preview`);
|
|
225
|
-
formatOutput(result,
|
|
221
|
+
formatOutput(result, getFormat(cmd));
|
|
226
222
|
});
|
|
227
223
|
documents.command("slots").description("List slots for a document").argument("<id>", "document ID").action(async (id, _opts, cmd) => {
|
|
228
|
-
const client =
|
|
224
|
+
const client = getClientFromCommand(cmd);
|
|
229
225
|
const result = await client.get(`/documents/${id}/slots`);
|
|
230
|
-
formatOutput(result,
|
|
226
|
+
formatOutput(result, getFormat(cmd));
|
|
231
227
|
});
|
|
232
228
|
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 =
|
|
229
|
+
const client = getClientFromCommand(cmd);
|
|
234
230
|
const result = await client.post(`/documents/${id}/slots`, {
|
|
235
231
|
slotName: opts.slotName,
|
|
236
232
|
fileId: opts.fileId
|
|
237
233
|
});
|
|
238
|
-
formatOutput(result,
|
|
234
|
+
formatOutput(result, getFormat(cmd));
|
|
239
235
|
});
|
|
240
236
|
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 =
|
|
237
|
+
const client = getClientFromCommand(cmd);
|
|
242
238
|
await client.delete(`/documents/${id}/slots/${slotId}`);
|
|
243
239
|
process.stdout.write(`Slot ${slotId} removed.
|
|
244
240
|
`);
|
|
245
241
|
});
|
|
246
242
|
}
|
|
247
243
|
|
|
248
|
-
// src/commands/
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
244
|
+
// src/commands/keys.ts
|
|
245
|
+
import chalk3 from "chalk";
|
|
246
|
+
function registerKeysCommand(program) {
|
|
247
|
+
const keys = program.command("keys").description("Manage Standards API keys");
|
|
248
|
+
keys.command("list").description("List API keys").action(async (_opts, cmd) => {
|
|
249
|
+
const client = getClientFromCommand(cmd);
|
|
250
|
+
const result = await client.get("/api-keys");
|
|
251
|
+
formatOutput(result, getFormat(cmd));
|
|
252
|
+
});
|
|
253
|
+
keys.command("create").description("Create a new API key").requiredOption("--name <name>", "name for the API key").option("--expires <date>", "expiration date (ISO 8601)").option("--yes", "create without confirmation").action(async (opts, cmd) => {
|
|
254
|
+
if (!opts.yes) {
|
|
255
|
+
throw new Error('Creating API keys is explicit. Re-run with "--yes" to confirm.');
|
|
256
|
+
}
|
|
257
|
+
const client = getClientFromCommand(cmd);
|
|
258
|
+
const body = {
|
|
259
|
+
name: opts.name,
|
|
260
|
+
permissions: []
|
|
261
|
+
};
|
|
262
|
+
if (opts.expires) body.expiresAt = opts.expires;
|
|
263
|
+
const result = await client.post("/api-keys", body);
|
|
264
|
+
formatOutput(result, getFormat(cmd));
|
|
265
|
+
});
|
|
266
|
+
keys.command("revoke").description("Revoke an API key").argument("<id>", "API key ID").option("--yes", "revoke without confirmation").action(async (id, opts, cmd) => {
|
|
267
|
+
if (!opts.yes) {
|
|
268
|
+
throw new Error('Revoking API keys is explicit. Re-run with "--yes" to confirm.');
|
|
269
|
+
}
|
|
270
|
+
const client = getClientFromCommand(cmd);
|
|
271
|
+
await client.delete(`/api-keys/${id}`);
|
|
272
|
+
process.stdout.write(`${chalk3.green("\u2713")} API key ${id} revoked.
|
|
273
|
+
`);
|
|
274
|
+
});
|
|
255
275
|
}
|
|
276
|
+
|
|
277
|
+
// src/commands/records.ts
|
|
256
278
|
function parseSortFlag(sort) {
|
|
257
279
|
const [attribute, direction = "asc"] = sort.split(":");
|
|
258
280
|
return JSON.stringify([{ attribute, direction }]);
|
|
259
281
|
}
|
|
260
|
-
function registerRecordsCommand(
|
|
261
|
-
const records =
|
|
282
|
+
function registerRecordsCommand(program) {
|
|
283
|
+
const records = program.command("records").description("Manage records (CRUD + search)");
|
|
262
284
|
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 =
|
|
285
|
+
const client = getClientFromCommand(cmd);
|
|
264
286
|
const params = {};
|
|
265
287
|
if (opts.limit) params.limit = opts.limit;
|
|
266
288
|
if (opts.offset) params.offset = opts.offset;
|
|
267
289
|
if (opts.sort) params.sorts = parseSortFlag(opts.sort);
|
|
268
290
|
if (opts.filter) params.filters = opts.filter;
|
|
269
291
|
const result = await client.get(`/records/${objectName}`, params);
|
|
270
|
-
formatOutput(result,
|
|
292
|
+
formatOutput(result, getFormat(cmd));
|
|
271
293
|
});
|
|
272
294
|
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 =
|
|
295
|
+
const client = getClientFromCommand(cmd);
|
|
274
296
|
const result = await client.get(`/records/${objectName}/${id}`);
|
|
275
|
-
formatOutput(result,
|
|
297
|
+
formatOutput(result, getFormat(cmd));
|
|
276
298
|
});
|
|
277
299
|
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
300
|
const data = opts.data ? JSON.parse(opts.data) : {};
|
|
279
|
-
const client =
|
|
280
|
-
const result = await client.post(`/records/${objectName}`, data);
|
|
281
|
-
formatOutput(result,
|
|
301
|
+
const client = getClientFromCommand(cmd);
|
|
302
|
+
const result = await client.post(`/records/${objectName}`, { data });
|
|
303
|
+
formatOutput(result, getFormat(cmd));
|
|
282
304
|
});
|
|
283
305
|
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
306
|
const data = opts.data ? JSON.parse(opts.data) : {};
|
|
285
|
-
const client =
|
|
307
|
+
const client = getClientFromCommand(cmd);
|
|
286
308
|
const result = await client.put(`/records/${objectName}/${id}`, data);
|
|
287
|
-
formatOutput(result,
|
|
309
|
+
formatOutput(result, getFormat(cmd));
|
|
288
310
|
});
|
|
289
311
|
records.command("delete").description("Delete a record").argument("<object>", "object name").argument("<id>", "record ID").action(async (objectName, id, _opts, cmd) => {
|
|
290
|
-
const client =
|
|
312
|
+
const client = getClientFromCommand(cmd);
|
|
291
313
|
await client.delete(`/records/${objectName}/${id}`);
|
|
292
314
|
process.stdout.write(`Record ${id} deleted.
|
|
293
315
|
`);
|
|
294
316
|
});
|
|
295
317
|
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 =
|
|
318
|
+
const client = getClientFromCommand(cmd);
|
|
297
319
|
const params = { q: opts.query };
|
|
298
320
|
if (opts.limit) params.limit = opts.limit;
|
|
299
321
|
if (opts.offset) params.offset = opts.offset;
|
|
300
322
|
if (opts.sort) params.sorts = parseSortFlag(opts.sort);
|
|
301
323
|
if (opts.filter) params.filters = opts.filter;
|
|
302
324
|
const result = await client.get(`/records/${objectName}/search`, params);
|
|
303
|
-
formatOutput(result,
|
|
325
|
+
formatOutput(result, getFormat(cmd));
|
|
304
326
|
});
|
|
305
327
|
}
|
|
306
328
|
|
|
307
|
-
// src/commands/
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
329
|
+
// src/commands/root.ts
|
|
330
|
+
import { stdin as input, stdout as output } from "process";
|
|
331
|
+
import { createInterface } from "readline/promises";
|
|
332
|
+
import chalk4 from "chalk";
|
|
333
|
+
|
|
334
|
+
// src/config.ts
|
|
335
|
+
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
336
|
+
import { homedir } from "os";
|
|
337
|
+
import { dirname, join } from "path";
|
|
338
|
+
var DEFAULT_API_URL = "http://localhost:4100/v1";
|
|
339
|
+
function getDefaultApiUrl() {
|
|
340
|
+
return DEFAULT_API_URL;
|
|
311
341
|
}
|
|
312
|
-
function
|
|
313
|
-
|
|
342
|
+
function getConfigPath() {
|
|
343
|
+
const configDir = process.env.STANDARDS_CONFIG_DIR ?? join(homedir(), ".standards");
|
|
344
|
+
return join(configDir, "config.json");
|
|
314
345
|
}
|
|
315
|
-
function
|
|
316
|
-
|
|
346
|
+
async function readConfig() {
|
|
347
|
+
try {
|
|
348
|
+
const raw = await readFile(getConfigPath(), "utf8");
|
|
349
|
+
const parsed = JSON.parse(raw);
|
|
350
|
+
return {
|
|
351
|
+
currentProfile: parsed.currentProfile,
|
|
352
|
+
profiles: parsed.profiles ?? {}
|
|
353
|
+
};
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
356
|
+
return { profiles: {} };
|
|
357
|
+
}
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
async function writeConfig(config) {
|
|
362
|
+
const path = getConfigPath();
|
|
363
|
+
await mkdir(dirname(path), { recursive: true, mode: 448 });
|
|
364
|
+
await writeFile(path, `${JSON.stringify(config, null, 2)}
|
|
365
|
+
`, { mode: 384 });
|
|
366
|
+
}
|
|
367
|
+
async function upsertProfile(input2) {
|
|
368
|
+
const config = await readConfig();
|
|
369
|
+
config.profiles[input2.name] = {
|
|
370
|
+
apiUrl: input2.apiUrl,
|
|
371
|
+
apiKey: input2.apiKey
|
|
372
|
+
};
|
|
373
|
+
config.currentProfile = input2.name;
|
|
374
|
+
await writeConfig(config);
|
|
375
|
+
}
|
|
376
|
+
async function setCurrentProfile(name) {
|
|
377
|
+
const config = await readConfig();
|
|
378
|
+
if (!config.profiles[name]) {
|
|
379
|
+
throw new Error(`Unknown Standards instance "${name}"`);
|
|
380
|
+
}
|
|
381
|
+
config.currentProfile = name;
|
|
382
|
+
await writeConfig(config);
|
|
383
|
+
}
|
|
384
|
+
async function removeProfile(name) {
|
|
385
|
+
const config = await readConfig();
|
|
386
|
+
const profileName = name ?? config.currentProfile;
|
|
387
|
+
if (!profileName) return;
|
|
388
|
+
delete config.profiles[profileName];
|
|
389
|
+
if (config.currentProfile === profileName) {
|
|
390
|
+
config.currentProfile = void 0;
|
|
391
|
+
}
|
|
392
|
+
await writeConfig(config);
|
|
393
|
+
}
|
|
394
|
+
async function listProfiles() {
|
|
395
|
+
const config = await readConfig();
|
|
396
|
+
return Object.entries(config.profiles).sort(([a], [b]) => a.localeCompare(b)).map(([name, profile]) => ({
|
|
397
|
+
name,
|
|
398
|
+
apiUrl: profile.apiUrl,
|
|
399
|
+
current: config.currentProfile === name
|
|
400
|
+
}));
|
|
401
|
+
}
|
|
402
|
+
async function getActiveProfile() {
|
|
403
|
+
const config = await readConfig();
|
|
404
|
+
if (!config.currentProfile) return null;
|
|
405
|
+
const profile = config.profiles[config.currentProfile];
|
|
406
|
+
if (!profile) return null;
|
|
407
|
+
return { name: config.currentProfile, ...profile };
|
|
408
|
+
}
|
|
409
|
+
async function resolveCliConfig(options) {
|
|
410
|
+
if (options.apiUrl || options.apiKey) {
|
|
411
|
+
return {
|
|
412
|
+
apiUrl: options.apiUrl ?? process.env.STANDARDS_API_URL ?? DEFAULT_API_URL,
|
|
413
|
+
apiKey: options.apiKey ?? process.env.STANDARDS_API_KEY
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
const config = await readConfig();
|
|
417
|
+
const profileName = options.instance ?? config.currentProfile;
|
|
418
|
+
if (profileName) {
|
|
419
|
+
const profile = config.profiles[profileName];
|
|
420
|
+
if (!profile) throw new Error(`Unknown Standards instance "${profileName}"`);
|
|
421
|
+
return {
|
|
422
|
+
apiUrl: profile.apiUrl,
|
|
423
|
+
apiKey: profile.apiKey,
|
|
424
|
+
profileName
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
return {
|
|
428
|
+
apiUrl: process.env.STANDARDS_API_URL ?? DEFAULT_API_URL,
|
|
429
|
+
apiKey: process.env.STANDARDS_API_KEY
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/commands/root.ts
|
|
434
|
+
async function promptForMissingValue(label) {
|
|
435
|
+
const rl = createInterface({ input, output });
|
|
436
|
+
try {
|
|
437
|
+
const value = await rl.question(`${label}: `);
|
|
438
|
+
return value.trim();
|
|
439
|
+
} finally {
|
|
440
|
+
rl.close();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function registerRootCommands(program) {
|
|
444
|
+
program.command("login").description("Connect the CLI to a Standards instance").option("--url <url>", "Standards API URL", getDefaultApiUrl()).option("--key <key>", "API key to store for this instance").option("--name <name>", "local instance name", "local").action(async (opts) => {
|
|
445
|
+
const apiKey = opts.key ?? await promptForMissingValue("API key");
|
|
446
|
+
if (!apiKey) throw new Error("API key is required");
|
|
447
|
+
const client = createClient({ apiUrl: opts.url, apiKey });
|
|
448
|
+
await client.get("/api-keys");
|
|
449
|
+
await upsertProfile({ name: opts.name, apiUrl: opts.url, apiKey });
|
|
450
|
+
process.stdout.write(
|
|
451
|
+
`${chalk4.green("\u2713")} Standards instance "${opts.name}" saved and selected.
|
|
452
|
+
`
|
|
453
|
+
);
|
|
454
|
+
process.stdout.write(` API URL: ${opts.url}
|
|
455
|
+
`);
|
|
456
|
+
process.stdout.write(` API Key: ${apiKey.slice(0, 8)}...
|
|
457
|
+
`);
|
|
458
|
+
process.stdout.write(` Use it with: standards --instance ${opts.name} <command>
|
|
459
|
+
`);
|
|
460
|
+
});
|
|
461
|
+
program.command("use").description("Select the active Standards instance").argument("<name>", "instance name").action(async (name) => {
|
|
462
|
+
await setCurrentProfile(name);
|
|
463
|
+
process.stdout.write(`${chalk4.green("\u2713")} Standards instance "${name}" selected.
|
|
464
|
+
`);
|
|
465
|
+
});
|
|
466
|
+
program.command("instances").description("List configured Standards instances").action(async (_opts, cmd) => {
|
|
467
|
+
const profiles = await listProfiles();
|
|
468
|
+
formatOutput(profiles, getFormat(cmd));
|
|
469
|
+
});
|
|
470
|
+
program.command("current").description("Show the active Standards instance").action(async () => {
|
|
471
|
+
const profile = await getActiveProfile();
|
|
472
|
+
if (!profile) {
|
|
473
|
+
process.stdout.write(`No Standards instance selected. Run "standards login".
|
|
474
|
+
`);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
process.stdout.write(`${profile.name} ${profile.apiUrl}
|
|
478
|
+
`);
|
|
479
|
+
});
|
|
480
|
+
program.command("logout").description("Remove a Standards instance from local CLI config").argument("[name]", "instance name, defaults to current").action(async (name) => {
|
|
481
|
+
await removeProfile(name);
|
|
482
|
+
process.stdout.write(`${chalk4.green("\u2713")} Standards instance removed.
|
|
483
|
+
`);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// src/commands/schema.ts
|
|
488
|
+
function registerSchemaCommand(program) {
|
|
489
|
+
const schema = program.command("schema").description("Inspect schema objects and attributes");
|
|
317
490
|
schema.command("list").description("List all schema objects").action(async (_opts, cmd) => {
|
|
318
|
-
const client =
|
|
491
|
+
const client = getClientFromCommand(cmd);
|
|
319
492
|
const result = await client.get("/schema/objects");
|
|
320
|
-
formatOutput(result,
|
|
493
|
+
formatOutput(result, getFormat(cmd));
|
|
321
494
|
});
|
|
322
495
|
schema.command("inspect").description("Inspect an object (attributes, types, views)").argument("<object>", "object name or ID").action(async (objectName, _opts, cmd) => {
|
|
323
|
-
const client =
|
|
496
|
+
const client = getClientFromCommand(cmd);
|
|
324
497
|
const result = await client.get(`/schema/objects/${objectName}`);
|
|
325
|
-
formatOutput(result,
|
|
498
|
+
formatOutput(result, getFormat(cmd));
|
|
326
499
|
});
|
|
327
500
|
}
|
|
328
501
|
|
|
329
|
-
// src/
|
|
330
|
-
var
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
"
|
|
334
|
-
|
|
335
|
-
)
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
program
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
502
|
+
// src/program.ts
|
|
503
|
+
var PUBLIC_COMMANDS = /* @__PURE__ */ new Set(["login", "use", "instances", "current", "logout", "help"]);
|
|
504
|
+
function isPublicCommand(actionCommand) {
|
|
505
|
+
if (PUBLIC_COMMANDS.has(actionCommand.name())) return true;
|
|
506
|
+
return actionCommand.parent?.name() === "auth" && actionCommand.name() === "help";
|
|
507
|
+
}
|
|
508
|
+
function createProgram() {
|
|
509
|
+
const program = new Command();
|
|
510
|
+
program.name("standards").description("CLI to interact with Standards API").version("1.0.0-alpha.3").option("--format <format>", "output format (json, table, csv)", "json").option("--api-url <url>", "API base URL").option("--api-key <key>", "API key for authentication").option("--instance <name>", "Standards instance profile to use");
|
|
511
|
+
registerRootCommands(program);
|
|
512
|
+
registerRecordsCommand(program);
|
|
513
|
+
registerSchemaCommand(program);
|
|
514
|
+
registerDocumentsCommand(program);
|
|
515
|
+
registerKeysCommand(program);
|
|
516
|
+
registerAuthCommand(program);
|
|
517
|
+
program.hook("preAction", async (_thisCommand, actionCommand) => {
|
|
518
|
+
const raw = program.opts();
|
|
519
|
+
const resolved = await resolveCliConfig({
|
|
520
|
+
apiUrl: raw.apiUrl,
|
|
521
|
+
apiKey: raw.apiKey,
|
|
522
|
+
instance: raw.instance
|
|
523
|
+
});
|
|
524
|
+
program.setOptionValue("apiUrl", resolved.apiUrl);
|
|
525
|
+
program.setOptionValue("apiKey", resolved.apiKey);
|
|
526
|
+
program.setOptionValue("profileName", resolved.profileName);
|
|
527
|
+
if (!(resolved.apiKey || isPublicCommand(actionCommand))) {
|
|
528
|
+
console.error(
|
|
529
|
+
chalk5.red(
|
|
530
|
+
`\u2717 Error: No Standards instance configured. Run "standards login" or pass --api-key.`
|
|
531
|
+
)
|
|
532
|
+
);
|
|
533
|
+
process.exit(1);
|
|
355
534
|
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
535
|
+
});
|
|
536
|
+
return program;
|
|
537
|
+
}
|
|
538
|
+
async function runProgram(argv = process.argv) {
|
|
539
|
+
const program = createProgram();
|
|
540
|
+
await program.parseAsync(argv).catch((error) => {
|
|
541
|
+
if (error instanceof ApiClientError) {
|
|
542
|
+
if (error.statusCode > 0) {
|
|
543
|
+
console.error(chalk5.red(`\u2717 Error (${error.statusCode}): ${error.message}`));
|
|
544
|
+
} else {
|
|
545
|
+
console.error(chalk5.red(`\u2717 Error: ${error.message}`));
|
|
546
|
+
}
|
|
547
|
+
} else if (error instanceof Error) {
|
|
548
|
+
console.error(chalk5.red(`\u2717 Error: ${error.message}`));
|
|
549
|
+
}
|
|
550
|
+
process.exit(1);
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// src/bin.ts
|
|
555
|
+
await runProgram();
|