@opentdf/ctl 0.1.0-beta.1701

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 ADDED
@@ -0,0 +1,34 @@
1
+ # OpenTDF command line tool (for node)
2
+
3
+ A sample application using node & ESM to import and test a project
4
+
5
+ ## Usage
6
+
7
+ ```sh
8
+ opentdf.mjs <auth options> <policy options> [encrypt|decrypt] [input file]
9
+ ```
10
+
11
+ Sample round trip execution:
12
+
13
+ ```sh
14
+ echo hello-world >sample.txt
15
+ bin/opentdf.mjs encrypt \
16
+ --kasEndpoint http://localhost:65432/api/kas \
17
+ --oidcEndpoint http://localhost:65432/auth/realms/tdf \
18
+ --auth tdf-client:123-456 \
19
+ --containerType tdf3 \
20
+ --output sample.tdf \
21
+ sample.txt
22
+ bin/opentdf.mjs \
23
+ --kasEndpoint http://localhost:65432/api/kas \
24
+ --oidcEndpoint http://localhost:65432/auth/realms/tdf \
25
+ --auth tdf-client:123-456 \
26
+ --containerType tdf3 \
27
+ --userId alice@somewhere.there \
28
+ decrypt sample.tdf
29
+ ```
30
+
31
+ ### References
32
+
33
+ - [yargs](http://yargs.js.org)
34
+ - [typescript CLI starter](https://github.com/khalidx/typescript-cli-starter)
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ // To debug, add `--inspect-brk` to the above line and open chrome://inspect
3
+
4
+ import '../dist/src/cli.js';
@@ -0,0 +1,511 @@
1
+ import './polyfills.js';
2
+ import { createWriteStream, openAsBlob } from 'node:fs';
3
+ import { readFile, stat, writeFile } from 'node:fs/promises';
4
+ import { Writable } from 'node:stream';
5
+ import yargs from 'yargs';
6
+ import { hideBin } from 'yargs/helpers';
7
+ import { AuthProviders, NanoTDFClient, NanoTDFDatasetClient, TDF3Client, version, EncryptParamsBuilder, DecryptParamsBuilder, } from '@opentdf/sdk';
8
+ import { CLIError, log } from './logger.js';
9
+ import * as assertions from '@opentdf/sdk/assertions';
10
+ import { attributeFQNsAsValues } from '@opentdf/sdk/nano';
11
+ import { base64 } from '@opentdf/sdk/encodings';
12
+ const bindingTypes = ['ecdsa', 'gmac'];
13
+ const containerTypes = ['tdf3', 'nano', 'dataset', 'ztdf'];
14
+ const parseJwt = (jwt, field = 1) => {
15
+ return JSON.parse(base64.decode(jwt.split('.')[field]));
16
+ };
17
+ const parseJwtComplete = (jwt) => {
18
+ return { header: parseJwt(jwt, 0), payload: parseJwt(jwt) };
19
+ };
20
+ async function processAuth({ auth, clientId, clientSecret, oidcEndpoint, userId, }) {
21
+ log('DEBUG', 'Processing auth params');
22
+ if (auth) {
23
+ log('DEBUG', 'Processing an auth string');
24
+ const authParts = auth.split(':');
25
+ if (authParts.length !== 2) {
26
+ throw new CLIError('CRITICAL', `Auth expects <clientId>:<clientSecret>, received ${auth}`);
27
+ }
28
+ [clientId, clientSecret] = authParts;
29
+ }
30
+ else if (!clientId || !clientSecret) {
31
+ throw new CLIError('CRITICAL', 'Auth expects clientId and clientSecret, or combined auth param');
32
+ }
33
+ const actual = await AuthProviders.clientSecretAuthProvider({
34
+ clientId,
35
+ oidcOrigin: oidcEndpoint,
36
+ exchange: 'client',
37
+ clientSecret,
38
+ });
39
+ const requestLog = [];
40
+ return {
41
+ requestLog,
42
+ updateClientPublicKey: async (signingKey) => {
43
+ actual.updateClientPublicKey(signingKey);
44
+ log('DEBUG', `updateClientPublicKey: [${signingKey?.publicKey}]`);
45
+ },
46
+ withCreds: async (httpReq) => {
47
+ const credible = await actual.withCreds(httpReq);
48
+ if (userId) {
49
+ const url = new URL(credible.url);
50
+ url.searchParams.set('userId', userId);
51
+ credible.url = url.href;
52
+ }
53
+ log('DEBUG', `HTTP Requesting: ${JSON.stringify(credible)}`);
54
+ requestLog.push(credible);
55
+ return credible;
56
+ },
57
+ };
58
+ }
59
+ const rstrip = (str, suffix = ' ') => {
60
+ while (str && suffix && str.endsWith(suffix)) {
61
+ str = str.slice(0, -suffix.length);
62
+ }
63
+ return str;
64
+ };
65
+ function addParams(client, argv) {
66
+ if (argv.attributes?.length) {
67
+ client.dataAttributes = argv.attributes.split(',');
68
+ }
69
+ if (argv.usersWithAccess?.length) {
70
+ client.dissems = argv.usersWithAccess.split(',');
71
+ }
72
+ log('SILLY', `Built encrypt params dissems: ${client.dissems}, attrs: ${client.dataAttributes}`);
73
+ }
74
+ async function tdf3DecryptParamsFor(argv) {
75
+ const c = new DecryptParamsBuilder();
76
+ if (argv.noVerifyAssertions) {
77
+ c.withNoVerifyAssertions(true);
78
+ }
79
+ c.setFileSource(await openAsBlob(argv.file));
80
+ return c.build();
81
+ }
82
+ function parseAssertionConfig(s) {
83
+ const u = JSON.parse(s);
84
+ // if u is null or empty, return an empty array
85
+ if (!u) {
86
+ return [];
87
+ }
88
+ const a = Array.isArray(u) ? u : [u];
89
+ for (const assertion of a) {
90
+ if (!assertions.isAssertionConfig(assertion)) {
91
+ throw new CLIError('CRITICAL', `invalid assertion config ${JSON.stringify(assertion)}`);
92
+ }
93
+ }
94
+ return a;
95
+ }
96
+ async function tdf3EncryptParamsFor(argv) {
97
+ const c = new EncryptParamsBuilder();
98
+ if (argv.assertions?.length) {
99
+ c.withAssertions(parseAssertionConfig(argv.assertions));
100
+ }
101
+ if (argv.attributes?.length) {
102
+ c.setAttributes(argv.attributes.split(','));
103
+ }
104
+ if (argv.usersWithAccess?.length) {
105
+ c.setUsersWithAccess(argv.usersWithAccess.split(','));
106
+ }
107
+ if (argv.mimeType?.length) {
108
+ c.setMimeType(argv.mimeType);
109
+ }
110
+ if (argv.autoconfigure) {
111
+ c.withAutoconfigure();
112
+ }
113
+ // use offline mode, we do not have upsert for v2
114
+ c.setOffline();
115
+ // FIXME TODO must call file.close() after we are done
116
+ const buffer = await processDataIn(argv.file);
117
+ c.setBufferSource(buffer);
118
+ return c.build();
119
+ }
120
+ async function processDataIn(file) {
121
+ if (!file) {
122
+ throw new CLIError('CRITICAL', 'Must specify file or pipe');
123
+ }
124
+ try {
125
+ const stats = await stat(file);
126
+ if (!stats?.isFile()) {
127
+ throw new CLIError('CRITICAL', `File does not exist [${file}]`);
128
+ }
129
+ }
130
+ catch (e) {
131
+ throw new CLIError('CRITICAL', `File is not accessable [${file}]`);
132
+ }
133
+ log('DEBUG', `Using input from file [${file}]`);
134
+ return readFile(file);
135
+ }
136
+ export const handleArgs = (args) => {
137
+ return (yargs(args)
138
+ .middleware((argv) => {
139
+ if (argv.silent) {
140
+ log.level = 'CRITICAL';
141
+ }
142
+ else if (argv.logLevel) {
143
+ const ll = argv.logLevel;
144
+ log.level = ll.toUpperCase();
145
+ }
146
+ })
147
+ .fail((msg, err, yargs) => {
148
+ if (err instanceof CLIError) {
149
+ log(err);
150
+ process.exit(1);
151
+ }
152
+ else if (err) {
153
+ log(err);
154
+ process.exit(2);
155
+ }
156
+ else {
157
+ console.error(`${msg}\n\n${yargs.help()}`);
158
+ process.exit(1);
159
+ }
160
+ })
161
+ // AUTH OPTIONS
162
+ .option('kasEndpoint', {
163
+ demandOption: true,
164
+ group: 'Server Endpoints:',
165
+ type: 'string',
166
+ description: 'URL to non-default KAS instance (https://mykas.net)',
167
+ })
168
+ .option('oidcEndpoint', {
169
+ demandOption: true,
170
+ group: 'Server Endpoints:',
171
+ type: 'string',
172
+ description: 'URL to non-default OIDC IdP (https://myidp.net)',
173
+ })
174
+ .option('policyEndpoint', {
175
+ group: 'Server Endpoints:',
176
+ type: 'string',
177
+ description: 'Attribute and key grant service endpoint',
178
+ })
179
+ .option('allowList', {
180
+ group: 'Security:',
181
+ desc: 'allowed KAS origins, comma separated; defaults to [kasEndpoint]',
182
+ type: 'string',
183
+ validate: (uris) => uris.split(','),
184
+ })
185
+ .option('ignoreAllowList', {
186
+ group: 'Security:',
187
+ desc: 'disable KAS allowlist feature for decrypt',
188
+ type: 'boolean',
189
+ })
190
+ .option('noVerifyAssertions', {
191
+ alias: 'no-verify-assertions',
192
+ group: 'Security',
193
+ desc: 'Do not verify assertions',
194
+ type: 'boolean',
195
+ })
196
+ .option('auth', {
197
+ group: 'OAuth and OIDC:',
198
+ type: 'string',
199
+ description: 'Combined OAuth Client Credentials (<clientId>:<clientSecret>)',
200
+ })
201
+ .option('dpop', {
202
+ group: 'Security:',
203
+ desc: 'Use DPoP for token binding',
204
+ type: 'boolean',
205
+ })
206
+ .implies('auth', '--no-clientId')
207
+ .implies('auth', '--no-clientSecret')
208
+ .option('clientId', {
209
+ group: 'OAuth and OIDC:',
210
+ alias: 'cid',
211
+ type: 'string',
212
+ description: 'OAuth Client Credentials: IdP-issued Client ID',
213
+ })
214
+ .implies('clientId', 'clientSecret')
215
+ .option('clientSecret', {
216
+ group: 'OAuth and OIDC:',
217
+ alias: 'cs',
218
+ type: 'string',
219
+ description: 'OAuth Client Credentials: IdP-issued Client Secret',
220
+ })
221
+ .implies('clientSecret', 'clientId')
222
+ .option('exchangeToken', {
223
+ group: 'OAuth and OIDC:',
224
+ alias: 'et',
225
+ type: 'string',
226
+ description: 'OAuth Token Exchange: Token issued by trusted external IdP',
227
+ })
228
+ .implies('exchangeToken', 'clientId')
229
+ // Examples
230
+ .example('$0 --auth ClientID123:Cli3nt$ecret', '# OIDC client credentials')
231
+ .example('$0 --clientId ClientID123 --clientSecret Cli3nt$ecret', '# OIDC client credentials')
232
+ // Policy, encryption, and container options
233
+ .options({
234
+ assertions: {
235
+ group: 'Encrypt Options:',
236
+ desc: 'ZTDF assertion config objects',
237
+ type: 'string',
238
+ default: '',
239
+ validate: parseAssertionConfig,
240
+ },
241
+ attributes: {
242
+ group: 'Encrypt Options:',
243
+ desc: 'Data attributes for the policy',
244
+ type: 'string',
245
+ default: '',
246
+ validate: (attributes) => attributes.split(','),
247
+ },
248
+ autoconfigure: {
249
+ group: 'Encrypt Options:',
250
+ desc: 'Enable automatic configuration from attributes using policy service',
251
+ type: 'boolean',
252
+ default: false,
253
+ },
254
+ containerType: {
255
+ group: 'Encrypt Options:',
256
+ alias: 't',
257
+ choices: containerTypes,
258
+ description: 'Container format',
259
+ default: 'nano',
260
+ },
261
+ policyBinding: {
262
+ group: 'Encrypt Options:',
263
+ choices: bindingTypes,
264
+ description: 'Policy Binding Type (nano only)',
265
+ default: 'gmac',
266
+ },
267
+ mimeType: {
268
+ group: 'Encrypt Options:',
269
+ desc: 'Mime type for the plain text file (only supported for ztdf)',
270
+ type: 'string',
271
+ default: '',
272
+ },
273
+ userId: {
274
+ group: 'Encrypt Options:',
275
+ type: 'string',
276
+ description: 'Owner email address',
277
+ },
278
+ usersWithAccess: {
279
+ alias: 'users-with-access',
280
+ group: 'Encrypt Options:',
281
+ desc: 'Add users to the policy',
282
+ type: 'string',
283
+ default: '',
284
+ validate: (users) => users.split(','),
285
+ },
286
+ })
287
+ // COMMANDS
288
+ .options({
289
+ logLevel: {
290
+ group: 'Verbosity:',
291
+ alias: 'log-level',
292
+ type: 'string',
293
+ default: 'info',
294
+ desc: 'Set logging level',
295
+ },
296
+ silent: {
297
+ group: 'Verbosity:',
298
+ type: 'boolean',
299
+ default: false,
300
+ desc: 'Disable logging',
301
+ },
302
+ })
303
+ .option('output', {
304
+ type: 'string',
305
+ description: 'output file',
306
+ })
307
+ .command('attrs', 'Look up defintions of attributes', (yargs) => {
308
+ yargs.strict();
309
+ }, async (argv) => {
310
+ log('DEBUG', 'attribute value lookup');
311
+ const authProvider = await processAuth(argv);
312
+ const signingKey = await crypto.subtle.generateKey({
313
+ name: 'RSASSA-PKCS1-v1_5',
314
+ hash: 'SHA-256',
315
+ modulusLength: 2048,
316
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
317
+ }, true, ['sign', 'verify']);
318
+ authProvider.updateClientPublicKey(signingKey);
319
+ log('DEBUG', `Initialized auth provider ${JSON.stringify(authProvider)}`);
320
+ const policyUrl = guessPolicyUrl(argv);
321
+ const defs = await attributeFQNsAsValues(policyUrl, authProvider, ...argv.attributes.split(','));
322
+ console.log(JSON.stringify(defs, null, 2));
323
+ })
324
+ .command('decrypt [file]', 'Decrypt TDF to string',
325
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
326
+ (yargs) => {
327
+ yargs.strict().positional('file', {
328
+ describe: 'path to plain text file',
329
+ type: 'string',
330
+ });
331
+ }, async (argv) => {
332
+ log('DEBUG', 'Running decrypt command');
333
+ const allowedKases = argv.allowList?.split(',');
334
+ const ignoreAllowList = !!argv.ignoreAllowList;
335
+ const authProvider = await processAuth(argv);
336
+ log('DEBUG', `Initialized auth provider ${JSON.stringify(authProvider)}`);
337
+ const kasEndpoint = argv.kasEndpoint;
338
+ if (argv.containerType === 'tdf3' || argv.containerType == 'ztdf') {
339
+ log('DEBUG', `TDF3 Client`);
340
+ const client = new TDF3Client({
341
+ allowedKases,
342
+ ignoreAllowList,
343
+ authProvider,
344
+ kasEndpoint,
345
+ dpopEnabled: argv.dpop,
346
+ });
347
+ log('SILLY', `Initialized client ${JSON.stringify(client)}`);
348
+ log('DEBUG', `About to decrypt [${argv.file}]`);
349
+ const ct = await client.decrypt(await tdf3DecryptParamsFor(argv));
350
+ if (argv.output) {
351
+ const destination = createWriteStream(argv.output);
352
+ await ct.stream.pipeTo(Writable.toWeb(destination));
353
+ }
354
+ else {
355
+ console.log(await ct.toString());
356
+ }
357
+ }
358
+ else {
359
+ const dpopEnabled = !!argv.dpop;
360
+ const client = argv.containerType === 'nano'
361
+ ? new NanoTDFClient({
362
+ allowedKases,
363
+ ignoreAllowList,
364
+ authProvider,
365
+ kasEndpoint,
366
+ dpopEnabled,
367
+ })
368
+ : new NanoTDFDatasetClient({
369
+ allowedKases,
370
+ ignoreAllowList,
371
+ authProvider,
372
+ kasEndpoint,
373
+ dpopEnabled,
374
+ });
375
+ const buffer = await processDataIn(argv.file);
376
+ log('DEBUG', 'Decrypt data.');
377
+ const plaintext = await client.decrypt(buffer);
378
+ log('DEBUG', 'Handle output.');
379
+ if (argv.output) {
380
+ await writeFile(argv.output, new Uint8Array(plaintext));
381
+ }
382
+ else {
383
+ console.log(new TextDecoder().decode(plaintext));
384
+ }
385
+ }
386
+ const lastRequest = authProvider.requestLog[authProvider.requestLog.length - 1];
387
+ let accessToken = null;
388
+ let dpopToken = null;
389
+ for (const h of Object.keys(lastRequest.headers)) {
390
+ switch (h.toLowerCase()) {
391
+ case 'dpop':
392
+ console.assert(!dpopToken, 'Multiple dpop headers found');
393
+ dpopToken = parseJwtComplete(lastRequest.headers[h]);
394
+ log('INFO', `dpop: ${JSON.stringify(dpopToken)}`);
395
+ break;
396
+ case 'authorization':
397
+ console.assert(!accessToken, 'Multiple authorization headers found');
398
+ accessToken = parseJwt(lastRequest.headers[h].split(' ')[1]);
399
+ log('INFO', `Access Token: ${JSON.stringify(accessToken)}`);
400
+ if (argv.dpop) {
401
+ console.assert(accessToken.cnf?.jkt, 'Access token must have a cnf.jkt');
402
+ }
403
+ break;
404
+ }
405
+ }
406
+ console.assert(accessToken, 'No access_token found');
407
+ console.assert(!argv.dpop || dpopToken, 'DPoP requested but absent');
408
+ })
409
+ .command('encrypt [file]', 'Encrypt file or pipe to a TDF',
410
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
411
+ (yargs) => {
412
+ yargs.strict().positional('file', {
413
+ describe: 'path to plain text file',
414
+ type: 'string',
415
+ });
416
+ }, async (argv) => {
417
+ log('DEBUG', 'Running encrypt command');
418
+ const authProvider = await processAuth(argv);
419
+ log('DEBUG', `Initialized auth provider ${JSON.stringify(authProvider)}`);
420
+ const kasEndpoint = argv.kasEndpoint;
421
+ const ignoreAllowList = !!argv.ignoreAllowList;
422
+ const allowedKases = argv.allowList?.split(',');
423
+ if ('tdf3' === argv.containerType || 'ztdf' === argv.containerType) {
424
+ log('DEBUG', `TDF3 Client`);
425
+ const policyEndpoint = guessPolicyUrl(argv);
426
+ const client = new TDF3Client({
427
+ allowedKases,
428
+ ignoreAllowList,
429
+ authProvider,
430
+ kasEndpoint,
431
+ policyEndpoint,
432
+ dpopEnabled: argv.dpop,
433
+ });
434
+ log('SILLY', `Initialized client ${JSON.stringify(client)}`);
435
+ const ct = await client.encrypt(await tdf3EncryptParamsFor(argv));
436
+ if (!ct) {
437
+ throw new CLIError('CRITICAL', 'Encrypt configuration error: No output?');
438
+ }
439
+ if (argv.output) {
440
+ const destination = createWriteStream(argv.output);
441
+ await ct.stream.pipeTo(Writable.toWeb(destination));
442
+ }
443
+ else {
444
+ console.log(await ct.toString());
445
+ }
446
+ }
447
+ else {
448
+ const dpopEnabled = !!argv.dpop;
449
+ const ecdsaBinding = argv.policyBinding.toLowerCase() == 'ecdsa';
450
+ const client = argv.containerType === 'nano'
451
+ ? new NanoTDFClient({ allowedKases, authProvider, dpopEnabled, kasEndpoint })
452
+ : new NanoTDFDatasetClient({
453
+ allowedKases,
454
+ authProvider,
455
+ dpopEnabled,
456
+ kasEndpoint,
457
+ });
458
+ log('SILLY', `Initialized client ${JSON.stringify(client)}`);
459
+ addParams(client, argv);
460
+ const buffer = await processDataIn(argv.file);
461
+ const cyphertext = await client.encrypt(buffer, { ecdsaBinding });
462
+ log('DEBUG', `Handle cyphertext output ${JSON.stringify(cyphertext)}`);
463
+ if (argv.output) {
464
+ await writeFile(argv.output, new Uint8Array(cyphertext));
465
+ }
466
+ else {
467
+ console.log(base64.encodeArrayBuffer(cyphertext));
468
+ }
469
+ }
470
+ })
471
+ .usage('openTDF CLI\n\nUsage: $0 [options]')
472
+ .alias('help', 'h')
473
+ .demandCommand()
474
+ .recommendCommands()
475
+ .help('help')
476
+ .options({
477
+ env: {
478
+ desc: 'Set the environment',
479
+ },
480
+ })
481
+ .version('version', JSON.stringify({
482
+ '@opentdf/ctl': process.env.npm_package_version || 'UNRELEASED',
483
+ '@opentdf/sdk': version,
484
+ }))
485
+ .alias('version', 'V')
486
+ .strict()
487
+ .parseAsync());
488
+ };
489
+ export const main = async (argsPromise) => {
490
+ argsPromise;
491
+ };
492
+ handleArgs(hideBin(process.argv))
493
+ .then(main)
494
+ .then(() => {
495
+ // Nothing;
496
+ })
497
+ .catch((err) => {
498
+ console.error(err);
499
+ });
500
+ function guessPolicyUrl({ kasEndpoint, policyEndpoint, }) {
501
+ let policyUrl;
502
+ if (policyEndpoint) {
503
+ policyUrl = rstrip(policyEndpoint, '/');
504
+ }
505
+ else {
506
+ const uNoSlash = rstrip(kasEndpoint, '/');
507
+ policyUrl = uNoSlash.endsWith('/kas') ? uNoSlash.slice(0, -4) : uNoSlash;
508
+ }
509
+ return policyUrl;
510
+ }
511
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxVQUFVLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDeEQsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFDN0QsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUN2QyxPQUFPLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDMUIsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN4QyxPQUFPLEVBSUwsYUFBYSxFQUNiLGFBQWEsRUFDYixvQkFBb0IsRUFDcEIsVUFBVSxFQUNWLE9BQU8sRUFDUCxvQkFBb0IsRUFFcEIsb0JBQW9CLEdBQ3JCLE1BQU0sY0FBYyxDQUFDO0FBQ3RCLE9BQU8sRUFBRSxRQUFRLEVBQVMsR0FBRyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRW5ELE9BQU8sS0FBSyxVQUFVLE1BQU0seUJBQXlCLENBQUM7QUFDdEQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDMUQsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBY2hELE1BQU0sWUFBWSxHQUFHLENBQUMsT0FBTyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBRXZDLE1BQU0sY0FBYyxHQUFHLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFFM0QsTUFBTSxRQUFRLEdBQUcsQ0FBQyxHQUFXLEVBQUUsS0FBSyxHQUFHLENBQUMsRUFBRSxFQUFFO0lBQzFDLE9BQU8sSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFD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
@@ -0,0 +1,64 @@
1
+ export const Levels = ['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'SILLY'];
2
+ export class CLIError extends Error {
3
+ constructor(prefix, message, cause) {
4
+ super(message, { cause });
5
+ this.prefix = prefix;
6
+ this.message = message;
7
+ }
8
+ }
9
+ export const log = Object.assign((thing, message) => {
10
+ let e;
11
+ let m;
12
+ let l;
13
+ if (thing instanceof CLIError) {
14
+ l = thing.prefix;
15
+ m = thing.message;
16
+ e = thing;
17
+ }
18
+ else if (thing instanceof Error) {
19
+ l = 'CRITICAL';
20
+ m = thing.message;
21
+ e = thing;
22
+ }
23
+ else {
24
+ l = thing;
25
+ m = message || '';
26
+ e = null;
27
+ }
28
+ const prefixed = `[${l}] ${m}`;
29
+ const i = Levels.indexOf(l);
30
+ if (i <= Levels.indexOf(log.level)) {
31
+ switch (l) {
32
+ case 'CRITICAL':
33
+ case 'ERROR':
34
+ if (e && !(e instanceof CLIError)) {
35
+ console.error(prefixed, e);
36
+ }
37
+ else {
38
+ console.error(prefixed);
39
+ }
40
+ break;
41
+ case 'WARNING':
42
+ if (e) {
43
+ console.warn(prefixed, e);
44
+ }
45
+ else {
46
+ console.warn(prefixed);
47
+ }
48
+ break;
49
+ case 'INFO':
50
+ console.info(prefixed);
51
+ break;
52
+ default:
53
+ console.log(prefixed);
54
+ break;
55
+ }
56
+ }
57
+ if (log.level === 'SILLY') {
58
+ console.error(e);
59
+ }
60
+ if (l === 'CRITICAL') {
61
+ process.exit(1);
62
+ }
63
+ }, { level: 'INFO' });
64
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9nZ2VyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2xvZ2dlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxNQUFNLENBQUMsTUFBTSxNQUFNLEdBQVksQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsT0FBTyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBRTFGLE1BQU0sT0FBTyxRQUFTLFNBQVEsS0FBSztJQUVqQyxZQUFZLE1BQWEsRUFBRSxPQUFlLEVBQUUsS0FBYTtRQUN2RCxLQUFLLENBQUMsT0FBTyxFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUMxQixJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUNyQixJQUFJLENBQUMsT0FBTyxHQUFHLE9BQU8sQ0FBQztJQUN6QixDQUFDO0NBQ0Y7QUFPRCxNQUFNLENBQUMsTUFBTSxHQUFHLEdBQVcsTUFBTSxDQUFDLE1BQU0sQ0FDdEMsQ0FBQyxLQUErQixFQUFFLE9BQWdCLEVBQUUsRUFBRTtJQUNwRCxJQUFJLENBQWUsQ0FBQztJQUNwQixJQUFJLENBQVMsQ0FBQztJQUNkLElBQUksQ0FBUSxDQUFDO0lBRWIsSUFBSSxLQUFLLFlBQVksUUFBUSxFQUFFLENBQUM7UUFDOUIsQ0FBQyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUM7UUFDakIsQ0FBQyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUM7UUFDbEIsQ0FBQyxHQUFHLEtBQWMsQ0FBQztJQUNyQixDQUFDO1NBQU0sSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7UUFDbEMsQ0FBQyxHQUFHLFVBQVUsQ0FBQztRQUNmLENBQUMsR0FBRyxLQUFLLENBQUMsT0FBTyxDQUFDO1FBQ2xCLENBQUMsR0FBRyxLQUFjLENBQUM7SUFDckIsQ0FBQztTQUFNLENBQUM7UUFDTixDQUFDLEdBQUcsS0FBYyxDQUFDO1FBQ25CLENBQUMsR0FBRyxPQUFPLElBQUksRUFBRSxDQUFDO1FBQ2xCLENBQUMsR0FBRyxJQUFJLENBQUM7SUFDWCxDQUFDO0lBQ0QsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUM7SUFDL0IsTUFBTSxDQUFDLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUM1QixJQUFJLENBQUMsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ25DLFFBQVEsQ0FBQyxFQUFFLENBQUM7WUFDVixLQUFLLFVBQVUsQ0FBQztZQUNoQixLQUFLLE9BQU87Z0JBQ1YsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsWUFBWSxRQUFRLENBQUMsRUFBRSxDQUFDO29CQUNsQyxPQUFPLENBQUMsS0FBSyxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUMsQ0FBQztnQkFDN0IsQ0FBQztxQkFBTSxDQUFDO29CQUNOLE9BQU8sQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQzFCLENBQUM7Z0JBQ0QsTUFBTTtZQUNSLEtBQUssU0FBUztnQkFDWixJQUFJLENBQUMsRUFBRSxDQUFDO29CQUNOLE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUM1QixDQUFDO3FCQUFNLENBQUM7b0JBQ04sT0FBTyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQztnQkFDekIsQ0FBQztnQkFDRCxNQUFNO1lBQ1IsS0FBSyxNQUFNO2dCQUNULE9BQU8sQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQ3ZCLE1BQU07WUFDUjtnQkFDRSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO2dCQUN0QixNQUFNO1FBQ1YsQ0FBQztJQUNILENBQUM7SUFDRCxJQUFJLEdBQUcsQ0FBQyxLQUFLLEtBQUssT0FBTyxFQUFFLENBQUM7UUFDMUIsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsSUFBSSxDQUFDLEtBQUssVUFBVSxFQUFFLENBQUM7UUFDckIsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsQixDQUFDO0FBQ0gsQ0FBQyxFQUNELEVBQUUsS0FBSyxFQUFFLE1BQWUsRUFBRSxDQUMzQixDQUFDIn0=
@@ -0,0 +1,6 @@
1
+ if (!('document' in globalThis)) {
2
+ // @ts-expect-error // document is either undefined or readonly
3
+ globalThis.document = { documentElement: { style: {} } };
4
+ }
5
+ export {};
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9seWZpbGxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BvbHlmaWxscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxJQUFJLENBQUMsQ0FBQyxVQUFVLElBQUksVUFBVSxDQUFDLEVBQUUsQ0FBQztJQUNoQywrREFBK0Q7SUFDL0QsVUFBVSxDQUFDLFFBQVEsR0FBRyxFQUFFLGVBQWUsRUFBRSxFQUFFLEtBQUssRUFBRSxFQUFFLEVBQUUsRUFBRSxDQUFDO0FBQzNELENBQUMifQ==
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@opentdf/ctl",
3
+ "version": "0.1.0-beta.1701",
4
+ "description": "Node based CLI for opentdf",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/opentdf/web-sdk.git",
8
+ "directory": "cli"
9
+ },
10
+ "license": "BSD-3-Clause-Clear",
11
+ "author": "Virtru",
12
+ "main": "dist/src/cli.js",
13
+ "type": "module",
14
+ "files": [
15
+ "dist/src/**",
16
+ "bin/opentdf.mjs"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "build:watch": "tsc --watch",
21
+ "prepack": "npm run build",
22
+ "test": "npm run build && mocha dist/**/*.spec.js",
23
+ "watch": "(trap 'kill 0' SIGINT; npm run build && (npm run build:watch & npm run test -- --watch))",
24
+ "format": "prettier --write \"{src,tests}/**/*.ts\"",
25
+ "license-check": "license-checker-rseidelsohn --production --onlyAllow 'Apache-2.0; BSD; CC-BY-4.0; ISC; MIT'",
26
+ "lint": "eslint ./{src,tests}/**/*.ts"
27
+ },
28
+ "bin": {
29
+ "opentdf": "./bin/opentdf.mjs"
30
+ },
31
+ "devDependencies": {
32
+ "@esm-bundle/chai": "4.3.4-fix.0",
33
+ "@types/mocha": "10.0.9",
34
+ "@types/node": "^22.9.0",
35
+ "@types/readable-stream": "^4.0.18",
36
+ "@types/sinon": "^17.0.3",
37
+ "@types/yargs": "^17.0.33",
38
+ "@typescript-eslint/eslint-plugin": "^6.2.1",
39
+ "@typescript-eslint/parser": "^6.2.1",
40
+ "chai": "^5.1.2",
41
+ "eslint": "^8.9.0",
42
+ "eslint-config-prettier": "^8.9.0",
43
+ "license-checker-rseidelsohn": "^4.4.2",
44
+ "mocha": "^10.8.2",
45
+ "prettier": "^3.3.3",
46
+ "sinon": "^19.0.2",
47
+ "ts-node": "^10.9.2",
48
+ "typescript": "^5.1.6"
49
+ },
50
+ "dependencies": {
51
+ "@opentdf/sdk": "^0.1.0-beta.1701",
52
+ "yargs": "^17.7.2"
53
+ }
54
+ }