fluxmail 0.7.0 → 0.8.0
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/LICENSE.md +66 -84
- package/README.md +1 -1
- package/dist/accounts/gmailConnection.d.ts +5 -0
- package/dist/accounts/gmailConnection.d.ts.map +1 -1
- package/dist/accounts/gmailConnection.js +24 -6
- package/dist/accounts/gmailConnection.js.map +1 -1
- package/dist/accounts/googleAuth.d.ts.map +1 -1
- package/dist/accounts/googleAuth.js +6 -1
- package/dist/accounts/googleAuth.js.map +1 -1
- package/dist/accounts/microsoftAuth.d.ts +1 -0
- package/dist/accounts/microsoftAuth.d.ts.map +1 -1
- package/dist/accounts/microsoftAuth.js +12 -0
- package/dist/accounts/microsoftAuth.js.map +1 -1
- package/dist/accounts/registry.d.ts +3 -1
- package/dist/accounts/registry.d.ts.map +1 -1
- package/dist/accounts/registry.js +11 -0
- package/dist/accounts/registry.js.map +1 -1
- package/dist/accounts/telemetry.d.ts +27 -0
- package/dist/accounts/telemetry.d.ts.map +1 -0
- package/dist/accounts/telemetry.js +42 -0
- package/dist/accounts/telemetry.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +141 -125
- package/dist/cli.js.map +1 -1
- package/dist/cliInstances.d.ts.map +1 -1
- package/dist/cliInstances.js +5 -1
- package/dist/cliInstances.js.map +1 -1
- package/dist/http/admin.d.ts.map +1 -1
- package/dist/http/admin.js +17 -0
- package/dist/http/admin.js.map +1 -1
- package/dist/http/app.d.ts +1 -1
- package/dist/http/app.d.ts.map +1 -1
- package/dist/http/app.js +76 -12
- package/dist/http/app.js.map +1 -1
- package/dist/http/identity.d.ts.map +1 -1
- package/dist/http/identity.js +12 -0
- package/dist/http/identity.js.map +1 -1
- package/dist/http/operationTelemetry.d.ts +14 -0
- package/dist/http/operationTelemetry.d.ts.map +1 -0
- package/dist/http/operationTelemetry.js +8 -0
- package/dist/http/operationTelemetry.js.map +1 -0
- package/dist/http/rest.d.ts +2 -1
- package/dist/http/rest.d.ts.map +1 -1
- package/dist/http/rest.js +3 -0
- package/dist/http/rest.js.map +1 -1
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -16,7 +16,7 @@ import { createApp } from './http/app.js';
|
|
|
16
16
|
import { buildMcpServer } from './mcp/buildServer.js';
|
|
17
17
|
import { runLoopbackFlow } from './accounts/googleAuth.js';
|
|
18
18
|
import { runMicrosoftLoopbackFlow } from './accounts/microsoftAuth.js';
|
|
19
|
-
import { prepareHostedGmailConnection, prepareHostedOutlookConnection, selectGmailConnectionMode, validateAccountConnectionFlags, } from './accounts/gmailConnection.js';
|
|
19
|
+
import { assertHostedConnectionReady, prepareHostedGmailConnection, prepareHostedOutlookConnection, selectGmailConnectionMode, validateAccountConnectionFlags, } from './accounts/gmailConnection.js';
|
|
20
20
|
import { listApiKeys } from './storage/apiKeys.js';
|
|
21
21
|
import { findMember } from './storage/members.js';
|
|
22
22
|
import { LICENSE_KEY_PATTERN } from './licensing/client.js';
|
|
@@ -25,12 +25,13 @@ import { VERSION } from './version.js';
|
|
|
25
25
|
import { inspectStoreCompatibility, MIN_SUPPORTED_STORE_FORMAT, openDb, openReadonlyDb, } from './storage/db.js';
|
|
26
26
|
import { customPermissionPolicy, ADMIN_CAPABILITIES, FULL_PERMISSION_POLICY, isNamedPermissionProfile, MCP_CAPABILITIES, NAMED_PERMISSION_PROFILES, permissionPolicyForProfile, } from './permissions.js';
|
|
27
27
|
import { captureOperation, getTelemetry, installTelemetryStreamEndHandler, isTelemetryEnabled, setTelemetryEnabled, shutdownTelemetry, telemetryDisabledInAnyEnvironment, } from './telemetry.js';
|
|
28
|
-
import { clearSessionToken, instanceClient, loadInstanceConfig, removeInstance, resolveInstance, saveLocalInstance, saveRemoteInstance, saveSessionToken, useInstance, } from './cliInstances.js';
|
|
28
|
+
import { apiErrorCode, clearSessionToken, instanceClient, loadInstanceConfig, removeInstance, resolveInstance, saveLocalInstance, saveRemoteInstance, saveSessionToken, useInstance, } from './cliInstances.js';
|
|
29
29
|
import { authenticateBearer, normalizeAndValidatePassword, recoverAdminPassword, setupInitialAdmin } from './auth.js';
|
|
30
30
|
import { recordAdminAuditEvent } from './storage/adminAudit.js';
|
|
31
31
|
import { canManageOwnedAccount } from './authorization.js';
|
|
32
32
|
import { createCliUpdateNotifier } from './updateNotifier.js';
|
|
33
33
|
import { registerMailCommands } from './cliMail.js';
|
|
34
|
+
import { accountInventoryProperties, configuredOAuthAppKind, connectionProperties } from './accounts/telemetry.js';
|
|
34
35
|
import { flushLogging, escapeLogTextForConsole, getLogger, logCodedFailure, logFailure, readLocalLogs, shutdownLogging, } from './logging.js';
|
|
35
36
|
function collectOption(value, previous) {
|
|
36
37
|
return [...previous, value];
|
|
@@ -177,6 +178,8 @@ function readSecretFile(file) {
|
|
|
177
178
|
async function secretInput(file, label) {
|
|
178
179
|
return file === undefined ? hiddenPrompt(label) : readSecretFile(file);
|
|
179
180
|
}
|
|
181
|
+
/** Offered when a configured public URL, not an explicit --hosted, chose the hosted flow. */
|
|
182
|
+
const LOOPBACK_FLOW_REMEDY = 'Pass --local to use the loopback flow on this computer instead.';
|
|
180
183
|
function planLine(ent) {
|
|
181
184
|
const members = `${ent.maxMembers} member${ent.maxMembers === 1 ? '' : 's'}`;
|
|
182
185
|
return `${ent.plan} (up to ${ent.maxAccounts} mailboxes, ${members})`;
|
|
@@ -189,12 +192,45 @@ function warnLicense(db, log = console.error) {
|
|
|
189
192
|
}
|
|
190
193
|
const activeCliOperations = new WeakMap();
|
|
191
194
|
const activeUpdateNotifiers = new WeakMap();
|
|
195
|
+
/** Attach safe, aggregate properties to the telemetry event for the running command. */
|
|
196
|
+
function recordCliOperationProperties(program, properties) {
|
|
197
|
+
const active = activeCliOperations.get(program);
|
|
198
|
+
if (active)
|
|
199
|
+
active.properties = { ...active.properties, ...properties };
|
|
200
|
+
}
|
|
201
|
+
/** Read local mailbox state without letting telemetry affect the command. */
|
|
202
|
+
async function inspectLocalRegistry(inspect) {
|
|
203
|
+
let context;
|
|
204
|
+
try {
|
|
205
|
+
context = createContext();
|
|
206
|
+
return inspect(context.registry);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
if (context) {
|
|
213
|
+
try {
|
|
214
|
+
await context.registry.close();
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// Telemetry cleanup must not affect the command.
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
context.db.$client.close();
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
// Telemetry cleanup must not affect the command.
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
192
228
|
function finishCliOperation(program, outcome, errorCode, error) {
|
|
193
229
|
const active = activeCliOperations.get(program);
|
|
194
230
|
if (!active)
|
|
195
231
|
return;
|
|
196
232
|
activeCliOperations.delete(program);
|
|
197
|
-
if (outcome === 'error'
|
|
233
|
+
if (outcome === 'error') {
|
|
198
234
|
const context = { productSurface: 'cli', operation: active.operation, skipConsole: true };
|
|
199
235
|
if (error !== undefined)
|
|
200
236
|
logFailure(active.logger, 'cli.operation_failed', error, context);
|
|
@@ -207,18 +243,14 @@ function finishCliOperation(program, outcome, errorCode, error) {
|
|
|
207
243
|
outcome,
|
|
208
244
|
durationMs: performance.now() - active.startedAt,
|
|
209
245
|
errorCode,
|
|
246
|
+
...(active.properties ? { properties: active.properties } : {}),
|
|
210
247
|
});
|
|
211
248
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
logFailure(active.logger, 'cli.operation_failed', error, {
|
|
218
|
-
productSurface: 'cli',
|
|
219
|
-
operation: active.operation,
|
|
220
|
-
skipConsole: true,
|
|
221
|
-
});
|
|
249
|
+
/** End a failed command with the error's own telemetry code instead of a generic one. */
|
|
250
|
+
function failCliOperation(program, error, prefix = 'Error: ') {
|
|
251
|
+
finishCliOperation(program, 'error', apiErrorCode(error), error);
|
|
252
|
+
console.error(`${prefix}${error instanceof Error ? error.message : String(error)}`);
|
|
253
|
+
process.exitCode = 1;
|
|
222
254
|
}
|
|
223
255
|
export function waitForServerListening(server) {
|
|
224
256
|
return new Promise((resolve, reject) => {
|
|
@@ -415,9 +447,7 @@ export function createCliProgram(options = {}) {
|
|
|
415
447
|
console.log(`Fluxmail is ready. Logged in to local as ${result.member.name}.`);
|
|
416
448
|
}
|
|
417
449
|
catch (err) {
|
|
418
|
-
|
|
419
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
420
|
-
process.exitCode = 1;
|
|
450
|
+
failCliOperation(program, err);
|
|
421
451
|
}
|
|
422
452
|
});
|
|
423
453
|
program
|
|
@@ -465,9 +495,7 @@ export function createCliProgram(options = {}) {
|
|
|
465
495
|
console.log(`Logged in to ${instanceName} as ${result.member.name}.`);
|
|
466
496
|
}
|
|
467
497
|
catch (err) {
|
|
468
|
-
|
|
469
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
470
|
-
process.exitCode = 1;
|
|
498
|
+
failCliOperation(program, err);
|
|
471
499
|
}
|
|
472
500
|
});
|
|
473
501
|
program
|
|
@@ -484,9 +512,7 @@ export function createCliProgram(options = {}) {
|
|
|
484
512
|
console.log(`Logged out of ${selected.name}.`);
|
|
485
513
|
}
|
|
486
514
|
catch (err) {
|
|
487
|
-
|
|
488
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
489
|
-
process.exitCode = 1;
|
|
515
|
+
failCliOperation(program, err);
|
|
490
516
|
}
|
|
491
517
|
});
|
|
492
518
|
const instances = program.command('instances').description('Manage local and remote CLI instances');
|
|
@@ -509,9 +535,7 @@ export function createCliProgram(options = {}) {
|
|
|
509
535
|
console.log(`Using ${name}.`);
|
|
510
536
|
}
|
|
511
537
|
catch (err) {
|
|
512
|
-
|
|
513
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
514
|
-
process.exitCode = 1;
|
|
538
|
+
failCliOperation(program, err);
|
|
515
539
|
}
|
|
516
540
|
});
|
|
517
541
|
instances
|
|
@@ -524,9 +548,7 @@ export function createCliProgram(options = {}) {
|
|
|
524
548
|
console.log(`Removed CLI instance ${name}. Server data was not changed.`);
|
|
525
549
|
}
|
|
526
550
|
catch (err) {
|
|
527
|
-
|
|
528
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
529
|
-
process.exitCode = 1;
|
|
551
|
+
failCliOperation(program, err);
|
|
530
552
|
}
|
|
531
553
|
});
|
|
532
554
|
const authCommand = program.command('auth').description('Manage interactive authentication');
|
|
@@ -554,9 +576,7 @@ export function createCliProgram(options = {}) {
|
|
|
554
576
|
console.log(`Reset ${member.name}'s password and revoked existing sessions.`);
|
|
555
577
|
}
|
|
556
578
|
catch (err) {
|
|
557
|
-
|
|
558
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
559
|
-
process.exitCode = 1;
|
|
579
|
+
failCliOperation(program, err);
|
|
560
580
|
}
|
|
561
581
|
});
|
|
562
582
|
authCommand
|
|
@@ -570,9 +590,7 @@ export function createCliProgram(options = {}) {
|
|
|
570
590
|
}
|
|
571
591
|
}
|
|
572
592
|
catch (err) {
|
|
573
|
-
|
|
574
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
575
|
-
process.exitCode = 1;
|
|
593
|
+
failCliOperation(program, err);
|
|
576
594
|
}
|
|
577
595
|
});
|
|
578
596
|
authCommand
|
|
@@ -587,9 +605,7 @@ export function createCliProgram(options = {}) {
|
|
|
587
605
|
console.log(`Revoked ${sessionId}.`);
|
|
588
606
|
}
|
|
589
607
|
catch (err) {
|
|
590
|
-
|
|
591
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
592
|
-
process.exitCode = 1;
|
|
608
|
+
failCliOperation(program, err);
|
|
593
609
|
}
|
|
594
610
|
});
|
|
595
611
|
program
|
|
@@ -708,33 +724,54 @@ export function createCliProgram(options = {}) {
|
|
|
708
724
|
return;
|
|
709
725
|
}
|
|
710
726
|
const selected = resolveInstance(selectedInstance());
|
|
727
|
+
// A remote instance reconnects an IMAP mailbox that matches the address,
|
|
728
|
+
// so without --reauthorize only the server knows which one this is.
|
|
729
|
+
const reauthorize = opts.reauthorize !== undefined
|
|
730
|
+
? true
|
|
731
|
+
: selected.profile.kind === 'remote' && provider === 'imap'
|
|
732
|
+
? undefined
|
|
733
|
+
: false;
|
|
734
|
+
recordCliOperationProperties(program, connectionProperties({ provider, reauthorize }));
|
|
711
735
|
if (!selected.token) {
|
|
712
|
-
|
|
713
|
-
process.exitCode = 1;
|
|
736
|
+
failCliOperation(program, new EmailError('permission_denied', `Log in to instance "${selected.name}" before connecting a mailbox.`));
|
|
714
737
|
return;
|
|
715
738
|
}
|
|
716
739
|
try {
|
|
717
740
|
validateAccountConnectionFlags(provider, opts);
|
|
718
741
|
}
|
|
719
742
|
catch (err) {
|
|
720
|
-
|
|
721
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
722
|
-
process.exitCode = 1;
|
|
743
|
+
failCliOperation(program, err);
|
|
723
744
|
return;
|
|
724
745
|
}
|
|
725
746
|
let useControlPlane = selected.profile.kind === 'remote' || provider === 'imap';
|
|
747
|
+
if (selected.profile.kind === 'remote' && provider !== 'imap') {
|
|
748
|
+
// A remote instance always completes OAuth through its own public URL.
|
|
749
|
+
recordCliOperationProperties(program, connectionProperties({ provider, reauthorize, flow: 'hosted' }));
|
|
750
|
+
}
|
|
726
751
|
if (selected.profile.kind === 'local' && provider !== 'imap') {
|
|
727
752
|
try {
|
|
728
|
-
|
|
753
|
+
const { config } = createContext();
|
|
754
|
+
const mode = selectGmailConnectionMode(config, opts);
|
|
755
|
+
recordCliOperationProperties(program, connectionProperties({
|
|
756
|
+
provider,
|
|
757
|
+
reauthorize,
|
|
758
|
+
flow: mode === 'hosted' ? 'hosted' : 'loopback',
|
|
759
|
+
oauthApp: configuredOAuthAppKind(config, provider),
|
|
760
|
+
}));
|
|
761
|
+
// A configured public URL selects the hosted flow on its own, so report a
|
|
762
|
+
// missing OAuth app here instead of after a connection grant is minted.
|
|
763
|
+
if (mode === 'hosted') {
|
|
764
|
+
assertHostedConnectionReady(config, provider, opts.hosted ? undefined : LOOPBACK_FLOW_REMEDY);
|
|
765
|
+
}
|
|
766
|
+
useControlPlane = mode === 'hosted';
|
|
729
767
|
}
|
|
730
768
|
catch (err) {
|
|
731
|
-
|
|
732
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
733
|
-
process.exitCode = 1;
|
|
769
|
+
failCliOperation(program, err);
|
|
734
770
|
return;
|
|
735
771
|
}
|
|
736
772
|
}
|
|
737
773
|
if (useControlPlane) {
|
|
774
|
+
let localImapReauthorize = reauthorize ?? false;
|
|
738
775
|
try {
|
|
739
776
|
if (selected.profile.kind === 'remote' && opts.local) {
|
|
740
777
|
throw new EmailError('invalid_request', '--local is only available for the local instance.');
|
|
@@ -751,6 +788,10 @@ export function createCliProgram(options = {}) {
|
|
|
751
788
|
throw new EmailError('invalid_request', '--imap-host is required for IMAP accounts.');
|
|
752
789
|
if (!opts.smtpHost)
|
|
753
790
|
throw new EmailError('invalid_request', '--smtp-host is required for IMAP accounts.');
|
|
791
|
+
if (selected.profile.kind === 'local' && !localImapReauthorize) {
|
|
792
|
+
localImapReauthorize =
|
|
793
|
+
(await inspectLocalRegistry((registry) => registry.listAccounts().some((account) => account.email.toLowerCase() === email.toLowerCase()))) ?? false;
|
|
794
|
+
}
|
|
754
795
|
const imapPassword = await accountSecret(opts.imapPasswordEnv, 'IMAP password');
|
|
755
796
|
const smtpPassword = opts.smtpPasswordEnv
|
|
756
797
|
? await accountSecret(opts.smtpPasswordEnv, 'SMTP password')
|
|
@@ -792,15 +833,20 @@ export function createCliProgram(options = {}) {
|
|
|
792
833
|
if (result.connectionUrl)
|
|
793
834
|
console.log(`Open this URL in your browser:\n\n ${result.connectionUrl}\n`);
|
|
794
835
|
if (result.account) {
|
|
836
|
+
if (selected.profile.kind === 'local' && provider === 'imap') {
|
|
837
|
+
const inventory = await inspectLocalRegistry((registry) => accountInventoryProperties(registry));
|
|
838
|
+
recordCliOperationProperties(program, {
|
|
839
|
+
reauthorize: localImapReauthorize,
|
|
840
|
+
...inventory,
|
|
841
|
+
});
|
|
842
|
+
}
|
|
795
843
|
console.log(`Connected ${result.account.email} (account id: ${result.account.id})`);
|
|
796
844
|
for (const warning of result.warnings ?? [])
|
|
797
845
|
console.log(`Warning: ${warning.message}.`);
|
|
798
846
|
}
|
|
799
847
|
}
|
|
800
848
|
catch (err) {
|
|
801
|
-
|
|
802
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
803
|
-
process.exitCode = 1;
|
|
849
|
+
failCliOperation(program, err);
|
|
804
850
|
}
|
|
805
851
|
return;
|
|
806
852
|
}
|
|
@@ -822,6 +868,7 @@ export function createCliProgram(options = {}) {
|
|
|
822
868
|
if (!existing)
|
|
823
869
|
ctx.registry.assertCanAddAccount();
|
|
824
870
|
const access = { sharedWithAll: false, grantedMemberIds: [] };
|
|
871
|
+
const accountIdsBeforeConnection = new Set(ctx.registry.listAccounts().map((account) => account.id));
|
|
825
872
|
const auditConnection = (operation, accountId) => {
|
|
826
873
|
try {
|
|
827
874
|
recordAdminAuditEvent(ctx.db, {
|
|
@@ -868,6 +915,10 @@ export function createCliProgram(options = {}) {
|
|
|
868
915
|
return ctx.registry.addOutlookAccount(result.email, result.credentials, result.displayName, owner.id, existing?.id, access);
|
|
869
916
|
});
|
|
870
917
|
auditConnection('account.connection.complete', account.id);
|
|
918
|
+
recordCliOperationProperties(program, {
|
|
919
|
+
reauthorize: accountIdsBeforeConnection.has(account.id),
|
|
920
|
+
...accountInventoryProperties(ctx.registry),
|
|
921
|
+
});
|
|
871
922
|
console.log(`\nConnected ${account.email} (account id: ${account.id})` +
|
|
872
923
|
(account.ownerMemberId === owner.id ? ` for member ${owner.name}` : ''));
|
|
873
924
|
return;
|
|
@@ -903,6 +954,10 @@ export function createCliProgram(options = {}) {
|
|
|
903
954
|
return ctx.registry.addGmailAccount(result.email, result.tokens, result.displayName, owner.id, access, result.oauthClient);
|
|
904
955
|
});
|
|
905
956
|
auditConnection('account.connection.complete', account.id);
|
|
957
|
+
recordCliOperationProperties(program, {
|
|
958
|
+
reauthorize: accountIdsBeforeConnection.has(account.id),
|
|
959
|
+
...accountInventoryProperties(ctx.registry),
|
|
960
|
+
});
|
|
906
961
|
console.log(`\nConnected ${account.email} (account id: ${account.id})` +
|
|
907
962
|
(account.ownerMemberId === owner.id ? ` for member ${owner.name}` : ''));
|
|
908
963
|
if (account.ownerMemberId !== owner.id) {
|
|
@@ -912,9 +967,7 @@ export function createCliProgram(options = {}) {
|
|
|
912
967
|
}
|
|
913
968
|
}
|
|
914
969
|
catch (err) {
|
|
915
|
-
|
|
916
|
-
console.error(`\nError: ${err instanceof Error ? err.message : String(err)}`);
|
|
917
|
-
process.exitCode = 1;
|
|
970
|
+
failCliOperation(program, err, '\nError: ');
|
|
918
971
|
}
|
|
919
972
|
});
|
|
920
973
|
accounts
|
|
@@ -953,9 +1006,7 @@ export function createCliProgram(options = {}) {
|
|
|
953
1006
|
console.log(`Warning: ${warning.message}.`);
|
|
954
1007
|
}
|
|
955
1008
|
catch (err) {
|
|
956
|
-
|
|
957
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
958
|
-
process.exitCode = 1;
|
|
1009
|
+
failCliOperation(program, err);
|
|
959
1010
|
}
|
|
960
1011
|
});
|
|
961
1012
|
accounts
|
|
@@ -978,9 +1029,7 @@ export function createCliProgram(options = {}) {
|
|
|
978
1029
|
}
|
|
979
1030
|
}
|
|
980
1031
|
catch (err) {
|
|
981
|
-
|
|
982
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
983
|
-
process.exitCode = 1;
|
|
1032
|
+
failCliOperation(program, err);
|
|
984
1033
|
}
|
|
985
1034
|
});
|
|
986
1035
|
accounts
|
|
@@ -989,15 +1038,24 @@ export function createCliProgram(options = {}) {
|
|
|
989
1038
|
.description('Disconnect an account and delete its stored tokens')
|
|
990
1039
|
.action(async (accountId) => {
|
|
991
1040
|
try {
|
|
992
|
-
|
|
1041
|
+
const selected = resolveInstance(selectedInstance());
|
|
1042
|
+
const provider = selected.profile.kind === 'local'
|
|
1043
|
+
? await inspectLocalRegistry((registry) => registry.getAccount(accountId).provider)
|
|
1044
|
+
: undefined;
|
|
1045
|
+
await instanceClient(selected.name).json(`/api/v1/accounts/${encodeURIComponent(accountId)}/connection`, {
|
|
993
1046
|
method: 'DELETE',
|
|
994
1047
|
});
|
|
1048
|
+
if (selected.profile.kind === 'local') {
|
|
1049
|
+
const inventory = await inspectLocalRegistry((registry) => accountInventoryProperties(registry));
|
|
1050
|
+
recordCliOperationProperties(program, {
|
|
1051
|
+
...(provider ? { provider } : {}),
|
|
1052
|
+
...inventory,
|
|
1053
|
+
});
|
|
1054
|
+
}
|
|
995
1055
|
console.log(`Removed ${accountId}`);
|
|
996
1056
|
}
|
|
997
1057
|
catch (err) {
|
|
998
|
-
|
|
999
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1000
|
-
process.exitCode = 1;
|
|
1058
|
+
failCliOperation(program, err);
|
|
1001
1059
|
}
|
|
1002
1060
|
});
|
|
1003
1061
|
accounts
|
|
@@ -1015,9 +1073,7 @@ export function createCliProgram(options = {}) {
|
|
|
1015
1073
|
console.log(`${account.email} is now owned by ${account.ownerMemberId}.`);
|
|
1016
1074
|
}
|
|
1017
1075
|
catch (err) {
|
|
1018
|
-
|
|
1019
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1020
|
-
process.exitCode = 1;
|
|
1076
|
+
failCliOperation(program, err);
|
|
1021
1077
|
}
|
|
1022
1078
|
});
|
|
1023
1079
|
accounts
|
|
@@ -1041,9 +1097,7 @@ export function createCliProgram(options = {}) {
|
|
|
1041
1097
|
console.log(`Updated access for ${account.email}: ${account.sharedWithAll ? 'all members' : `${account.grantedMemberIds.length} explicit grant(s)`}.`);
|
|
1042
1098
|
}
|
|
1043
1099
|
catch (err) {
|
|
1044
|
-
|
|
1045
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1046
|
-
process.exitCode = 1;
|
|
1100
|
+
failCliOperation(program, err);
|
|
1047
1101
|
}
|
|
1048
1102
|
});
|
|
1049
1103
|
const membersCmd = program.command('members').description('Manage members (people using this instance)');
|
|
@@ -1068,9 +1122,7 @@ export function createCliProgram(options = {}) {
|
|
|
1068
1122
|
console.log(`Expires: ${new Date(member.invitation.expiresAt).toISOString()}`);
|
|
1069
1123
|
}
|
|
1070
1124
|
catch (err) {
|
|
1071
|
-
|
|
1072
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1073
|
-
process.exitCode = 1;
|
|
1125
|
+
failCliOperation(program, err);
|
|
1074
1126
|
}
|
|
1075
1127
|
});
|
|
1076
1128
|
membersCmd
|
|
@@ -1088,9 +1140,7 @@ export function createCliProgram(options = {}) {
|
|
|
1088
1140
|
}
|
|
1089
1141
|
}
|
|
1090
1142
|
catch (err) {
|
|
1091
|
-
|
|
1092
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1093
|
-
process.exitCode = 1;
|
|
1143
|
+
failCliOperation(program, err);
|
|
1094
1144
|
}
|
|
1095
1145
|
});
|
|
1096
1146
|
membersCmd
|
|
@@ -1105,9 +1155,7 @@ export function createCliProgram(options = {}) {
|
|
|
1105
1155
|
console.log(`Removed ${memberId}.`);
|
|
1106
1156
|
}
|
|
1107
1157
|
catch (err) {
|
|
1108
|
-
|
|
1109
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1110
|
-
process.exitCode = 1;
|
|
1158
|
+
failCliOperation(program, err);
|
|
1111
1159
|
}
|
|
1112
1160
|
});
|
|
1113
1161
|
membersCmd
|
|
@@ -1128,9 +1176,7 @@ export function createCliProgram(options = {}) {
|
|
|
1128
1176
|
console.log(`${member.name} now has the ${member.role} role.`);
|
|
1129
1177
|
}
|
|
1130
1178
|
catch (err) {
|
|
1131
|
-
|
|
1132
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1133
|
-
process.exitCode = 1;
|
|
1179
|
+
failCliOperation(program, err);
|
|
1134
1180
|
}
|
|
1135
1181
|
});
|
|
1136
1182
|
membersCmd
|
|
@@ -1151,9 +1197,7 @@ export function createCliProgram(options = {}) {
|
|
|
1151
1197
|
console.log(`${member.name} is now ${member.status}.`);
|
|
1152
1198
|
}
|
|
1153
1199
|
catch (err) {
|
|
1154
|
-
|
|
1155
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1156
|
-
process.exitCode = 1;
|
|
1200
|
+
failCliOperation(program, err);
|
|
1157
1201
|
}
|
|
1158
1202
|
});
|
|
1159
1203
|
for (const [commandName, endpoint, label] of [
|
|
@@ -1171,9 +1215,7 @@ export function createCliProgram(options = {}) {
|
|
|
1171
1215
|
console.log(`Expires: ${new Date(token.expiresAt).toISOString()}`);
|
|
1172
1216
|
}
|
|
1173
1217
|
catch (err) {
|
|
1174
|
-
|
|
1175
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1176
|
-
process.exitCode = 1;
|
|
1218
|
+
failCliOperation(program, err);
|
|
1177
1219
|
}
|
|
1178
1220
|
});
|
|
1179
1221
|
}
|
|
@@ -1189,9 +1231,7 @@ export function createCliProgram(options = {}) {
|
|
|
1189
1231
|
}
|
|
1190
1232
|
}
|
|
1191
1233
|
catch (err) {
|
|
1192
|
-
|
|
1193
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1194
|
-
process.exitCode = 1;
|
|
1234
|
+
failCliOperation(program, err);
|
|
1195
1235
|
}
|
|
1196
1236
|
});
|
|
1197
1237
|
membersCmd
|
|
@@ -1205,9 +1245,7 @@ export function createCliProgram(options = {}) {
|
|
|
1205
1245
|
console.log(`Revoked ${sessionId}.`);
|
|
1206
1246
|
}
|
|
1207
1247
|
catch (err) {
|
|
1208
|
-
|
|
1209
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1210
|
-
process.exitCode = 1;
|
|
1248
|
+
failCliOperation(program, err);
|
|
1211
1249
|
}
|
|
1212
1250
|
});
|
|
1213
1251
|
const apikey = program.command('apikey').description('Manage API keys for the HTTP MCP and REST APIs');
|
|
@@ -1262,9 +1300,7 @@ export function createCliProgram(options = {}) {
|
|
|
1262
1300
|
console.log('Store it now; it cannot be shown again.');
|
|
1263
1301
|
}
|
|
1264
1302
|
catch (err) {
|
|
1265
|
-
|
|
1266
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1267
|
-
process.exitCode = 1;
|
|
1303
|
+
failCliOperation(program, err);
|
|
1268
1304
|
}
|
|
1269
1305
|
});
|
|
1270
1306
|
apikey
|
|
@@ -1289,9 +1325,7 @@ export function createCliProgram(options = {}) {
|
|
|
1289
1325
|
}
|
|
1290
1326
|
}
|
|
1291
1327
|
catch (err) {
|
|
1292
|
-
|
|
1293
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1294
|
-
process.exitCode = 1;
|
|
1328
|
+
failCliOperation(program, err);
|
|
1295
1329
|
}
|
|
1296
1330
|
});
|
|
1297
1331
|
apikey
|
|
@@ -1316,9 +1350,7 @@ export function createCliProgram(options = {}) {
|
|
|
1316
1350
|
: `Updated ${keyId} to ${accountIds.length} mailbox(es).`);
|
|
1317
1351
|
}
|
|
1318
1352
|
catch (err) {
|
|
1319
|
-
|
|
1320
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1321
|
-
process.exitCode = 1;
|
|
1353
|
+
failCliOperation(program, err);
|
|
1322
1354
|
}
|
|
1323
1355
|
});
|
|
1324
1356
|
apikey
|
|
@@ -1351,9 +1383,7 @@ export function createCliProgram(options = {}) {
|
|
|
1351
1383
|
console.log(`Updated ${keyId} to profile ${permissions.profile}`);
|
|
1352
1384
|
}
|
|
1353
1385
|
catch (err) {
|
|
1354
|
-
|
|
1355
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1356
|
-
process.exitCode = 1;
|
|
1386
|
+
failCliOperation(program, err);
|
|
1357
1387
|
}
|
|
1358
1388
|
});
|
|
1359
1389
|
apikey
|
|
@@ -1371,9 +1401,7 @@ export function createCliProgram(options = {}) {
|
|
|
1371
1401
|
console.log(`Revoked ${keyId}`);
|
|
1372
1402
|
}
|
|
1373
1403
|
catch (err) {
|
|
1374
|
-
|
|
1375
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1376
|
-
process.exitCode = 1;
|
|
1404
|
+
failCliOperation(program, err);
|
|
1377
1405
|
}
|
|
1378
1406
|
});
|
|
1379
1407
|
const license = program.command('license').description('Manage the paid-tier license');
|
|
@@ -1413,9 +1441,7 @@ export function createCliProgram(options = {}) {
|
|
|
1413
1441
|
}
|
|
1414
1442
|
}
|
|
1415
1443
|
catch (err) {
|
|
1416
|
-
|
|
1417
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1418
|
-
process.exitCode = 1;
|
|
1444
|
+
failCliOperation(program, err);
|
|
1419
1445
|
}
|
|
1420
1446
|
});
|
|
1421
1447
|
license
|
|
@@ -1436,9 +1462,7 @@ export function createCliProgram(options = {}) {
|
|
|
1436
1462
|
console.log(`Warning: ${status.warning}`);
|
|
1437
1463
|
}
|
|
1438
1464
|
catch (err) {
|
|
1439
|
-
|
|
1440
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1441
|
-
process.exitCode = 1;
|
|
1465
|
+
failCliOperation(program, err);
|
|
1442
1466
|
}
|
|
1443
1467
|
});
|
|
1444
1468
|
license
|
|
@@ -1453,9 +1477,7 @@ export function createCliProgram(options = {}) {
|
|
|
1453
1477
|
console.log('This instance is back to Personal plan limits.');
|
|
1454
1478
|
}
|
|
1455
1479
|
catch (err) {
|
|
1456
|
-
|
|
1457
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1458
|
-
process.exitCode = 1;
|
|
1480
|
+
failCliOperation(program, err);
|
|
1459
1481
|
}
|
|
1460
1482
|
});
|
|
1461
1483
|
const configCmd = program.command('config').description('Inspect and initialize deployment configuration');
|
|
@@ -1470,9 +1492,7 @@ export function createCliProgram(options = {}) {
|
|
|
1470
1492
|
console.log(`Created ${file}`);
|
|
1471
1493
|
}
|
|
1472
1494
|
catch (err) {
|
|
1473
|
-
|
|
1474
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1475
|
-
process.exitCode = 1;
|
|
1495
|
+
failCliOperation(program, err);
|
|
1476
1496
|
}
|
|
1477
1497
|
});
|
|
1478
1498
|
configCmd
|
|
@@ -1665,9 +1685,7 @@ export function createCliProgram(options = {}) {
|
|
|
1665
1685
|
console.log(JSON.stringify(status, null, 2));
|
|
1666
1686
|
}
|
|
1667
1687
|
catch (err) {
|
|
1668
|
-
|
|
1669
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1670
|
-
process.exitCode = 1;
|
|
1688
|
+
failCliOperation(program, err);
|
|
1671
1689
|
}
|
|
1672
1690
|
});
|
|
1673
1691
|
return program;
|
|
@@ -1678,9 +1696,7 @@ export async function runCli(argv = process.argv) {
|
|
|
1678
1696
|
await program.parseAsync([...argv]);
|
|
1679
1697
|
}
|
|
1680
1698
|
catch (err) {
|
|
1681
|
-
|
|
1682
|
-
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1683
|
-
process.exitCode = 1;
|
|
1699
|
+
failCliOperation(program, err);
|
|
1684
1700
|
await Promise.all([shutdownTelemetry(), shutdownLogging()]);
|
|
1685
1701
|
}
|
|
1686
1702
|
}
|