doomain 0.1.9 → 0.1.11
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/commands/domains/list.d.ts +1 -0
- package/dist/commands/domains/list.js +33 -11
- package/dist/commands/link.d.ts +1 -0
- package/dist/commands/link.js +3 -1
- package/dist/commands/providers/connect.d.ts +1 -0
- package/dist/commands/providers/connect.js +18 -4
- package/dist/commands/providers/disconnect.d.ts +1 -0
- package/dist/commands/providers/disconnect.js +35 -5
- package/dist/commands/providers/status.js +4 -2
- package/dist/commands/providers/verify.d.ts +1 -0
- package/dist/commands/providers/verify.js +6 -3
- package/dist/commands/wizard.js +25 -18
- package/dist/lib/command-schema.d.ts +1 -1
- package/dist/lib/command-schema.js +22 -3
- package/dist/lib/config.d.ts +4 -1
- package/dist/lib/flags.d.ts +1 -0
- package/dist/lib/flags.js +1 -0
- package/dist/lib/link-domain.d.ts +4 -0
- package/dist/lib/link-domain.js +74 -23
- package/dist/lib/providers/core/config.d.ts +16 -3
- package/dist/lib/providers/core/config.js +53 -11
- package/dist/lib/providers/registry.d.ts +3 -1
- package/dist/lib/providers/registry.js +2 -2
- package/dist/lib/providers/status.d.ts +5 -1
- package/dist/lib/providers/status.js +28 -21
- package/dist/lib/validate.d.ts +1 -0
- package/dist/lib/validate.js +7 -0
- package/oclif.manifest.json +80 -37
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ import { Command } from '@oclif/core';
|
|
|
2
2
|
export default class DomainsList extends Command {
|
|
3
3
|
static description: string;
|
|
4
4
|
static flags: {
|
|
5
|
+
account: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
6
|
domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
6
7
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
8
|
provider: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Command } from '@oclif/core';
|
|
2
2
|
import { loadConfig } from '../../lib/config.js';
|
|
3
|
-
import { domainFlag, jsonFlag, providerFlag } from '../../lib/flags.js';
|
|
3
|
+
import { accountFlag, domainFlag, jsonFlag, providerFlag } from '../../lib/flags.js';
|
|
4
4
|
import { createOutput, outputError } from '../../lib/output.js';
|
|
5
|
-
import {
|
|
5
|
+
import { DEFAULT_PROVIDER_ACCOUNT, isDefaultProviderAccount, listConfiguredProviderAccounts, normalizeProviderAccount, } from '../../lib/providers/core/config.js';
|
|
6
|
+
import { createProvider, getProviderDefinition } from '../../lib/providers/registry.js';
|
|
6
7
|
import { normalizeDomain } from '../../lib/validate.js';
|
|
7
8
|
async function resolveZones(provider, domain) {
|
|
8
9
|
if (!domain)
|
|
@@ -16,6 +17,7 @@ async function resolveZones(provider, domain) {
|
|
|
16
17
|
export default class DomainsList extends Command {
|
|
17
18
|
static description = 'List DNS zones and records for a provider.';
|
|
18
19
|
static flags = {
|
|
20
|
+
account: accountFlag,
|
|
19
21
|
domain: domainFlag,
|
|
20
22
|
json: jsonFlag,
|
|
21
23
|
provider: providerFlag,
|
|
@@ -25,17 +27,37 @@ export default class DomainsList extends Command {
|
|
|
25
27
|
const out = createOutput({ json: flags.json });
|
|
26
28
|
try {
|
|
27
29
|
const config = await loadConfig();
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
+
const providerId = flags.provider ?? process.env.DOOMAIN_PROVIDER ?? config.defaults?.provider ?? 'spaceship';
|
|
31
|
+
const definition = getProviderDefinition(providerId);
|
|
32
|
+
const account = flags.account ? normalizeProviderAccount(flags.account) : undefined;
|
|
33
|
+
const accounts = account
|
|
34
|
+
? [{ account, isDefaultAccount: isDefaultProviderAccount(account), providerId: definition.id }]
|
|
35
|
+
: listConfiguredProviderAccounts(config, definition);
|
|
36
|
+
const selectedAccounts = accounts.length > 0 ? accounts : [{ account: DEFAULT_PROVIDER_ACCOUNT, isDefaultAccount: true, providerId: definition.id }];
|
|
30
37
|
const results = [];
|
|
31
|
-
for (const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
for (const selectedAccount of selectedAccounts) {
|
|
39
|
+
const provider = await createProvider(definition.id, { account: selectedAccount.account });
|
|
40
|
+
const zones = await resolveZones(provider, flags.domain);
|
|
41
|
+
for (const zone of zones) {
|
|
42
|
+
const records = await provider.listRecords(zone);
|
|
43
|
+
results.push({
|
|
44
|
+
account: selectedAccount.account,
|
|
45
|
+
isDefaultAccount: selectedAccount.isDefaultAccount,
|
|
46
|
+
provider: provider.id,
|
|
47
|
+
records,
|
|
48
|
+
zone,
|
|
49
|
+
});
|
|
50
|
+
const accountLabel = selectedAccount.isDefaultAccount ? provider.id : `${provider.id}/${selectedAccount.account}`;
|
|
51
|
+
out.info(`${zone.name} (${records.length} records) via ${accountLabel}`);
|
|
52
|
+
for (const record of records)
|
|
53
|
+
out.info(` ${record.type} ${record.name} -> ${record.value}`);
|
|
54
|
+
}
|
|
37
55
|
}
|
|
38
|
-
out.result({
|
|
56
|
+
out.result({
|
|
57
|
+
...(selectedAccounts.length === 1 ? { account: selectedAccounts[0].account, isDefaultAccount: selectedAccounts[0].isDefaultAccount } : {}),
|
|
58
|
+
provider: definition.id,
|
|
59
|
+
zones: results,
|
|
60
|
+
});
|
|
39
61
|
}
|
|
40
62
|
catch (error) {
|
|
41
63
|
outputError(out.json, error, 'DOMAIN_LINK_FAILED');
|
package/dist/commands/link.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export default class Link extends Command {
|
|
|
6
6
|
domain: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
7
7
|
};
|
|
8
8
|
static flags: {
|
|
9
|
+
account: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
apex: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
10
11
|
domain: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
12
|
'dry-run': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
package/dist/commands/link.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
2
|
import { DoomainError } from '../lib/errors.js';
|
|
3
|
-
import { apexFlag, domainFlag, jsonFlag, projectFlag, providerFlag, subdomainFlag } from '../lib/flags.js';
|
|
3
|
+
import { accountFlag, apexFlag, domainFlag, jsonFlag, projectFlag, providerFlag, subdomainFlag } from '../lib/flags.js';
|
|
4
4
|
import { linkDomain } from '../lib/link-domain.js';
|
|
5
5
|
import { createOutput, outputError } from '../lib/output.js';
|
|
6
6
|
export default class Link extends Command {
|
|
@@ -11,11 +11,13 @@ export default class Link extends Command {
|
|
|
11
11
|
'<%= config.bin %> <%= command.id %> --domain app.example.com --project my-app --json',
|
|
12
12
|
'<%= config.bin %> <%= command.id %> --domain example.com --subdomain app --project my-app',
|
|
13
13
|
'<%= config.bin %> <%= command.id %> --provider spaceship --domain example.com --apex --project my-app --json',
|
|
14
|
+
'<%= config.bin %> <%= command.id %> app.example.com --provider spaceship --account work --project my-app --json',
|
|
14
15
|
];
|
|
15
16
|
static args = {
|
|
16
17
|
domain: Args.string({ description: 'Target domain to link, for example app.example.com.', required: false }),
|
|
17
18
|
};
|
|
18
19
|
static flags = {
|
|
20
|
+
account: accountFlag,
|
|
19
21
|
apex: apexFlag,
|
|
20
22
|
domain: domainFlag,
|
|
21
23
|
'dry-run': Flags.boolean({ description: 'Preview changes without writing to Vercel or DNS.' }),
|
|
@@ -5,6 +5,7 @@ export default class ProvidersConnect extends Command {
|
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static flags: {
|
|
8
|
+
account: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
10
|
'api-secret': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
credential: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
2
|
import * as p from '@clack/prompts';
|
|
3
3
|
import { getConfigPath, loadConfig, maskSecret, updateConfig } from '../../lib/config.js';
|
|
4
|
-
import { jsonFlag } from '../../lib/flags.js';
|
|
4
|
+
import { accountFlag, jsonFlag } from '../../lib/flags.js';
|
|
5
5
|
import { createOutput, outputError } from '../../lib/output.js';
|
|
6
|
+
import { isDefaultProviderAccount, listConfiguredProviderAccounts, normalizeProviderAccount } from '../../lib/providers/core/config.js';
|
|
6
7
|
import { getProviderDefinition, listProviderDefinitions } from '../../lib/providers/registry.js';
|
|
7
|
-
import { isProviderConfigured } from '../../lib/providers/status.js';
|
|
8
8
|
function requireString(value, message) {
|
|
9
9
|
if (typeof value === 'string' && value.trim())
|
|
10
10
|
return value.trim();
|
|
@@ -63,7 +63,7 @@ async function promptProvider() {
|
|
|
63
63
|
const selected = await p.select({
|
|
64
64
|
message: 'Choose DNS provider',
|
|
65
65
|
options: listProviderDefinitions().map((definition) => ({
|
|
66
|
-
hint:
|
|
66
|
+
hint: listConfiguredProviderAccounts(config, definition).length > 0 ? 'Connected' : 'Not connected',
|
|
67
67
|
label: definition.displayName,
|
|
68
68
|
value: definition.id,
|
|
69
69
|
})),
|
|
@@ -91,6 +91,7 @@ export default class ProvidersConnect extends Command {
|
|
|
91
91
|
};
|
|
92
92
|
static description = 'Save DNS provider credentials locally.';
|
|
93
93
|
static flags = {
|
|
94
|
+
account: accountFlag,
|
|
94
95
|
'api-key': Flags.string({ description: 'Compatibility alias for Spaceship apiKey.' }),
|
|
95
96
|
'api-secret': Flags.string({ description: 'Compatibility alias for Spaceship apiSecret.' }),
|
|
96
97
|
credential: Flags.string({ char: 'c', description: 'Provider credential as key=value.', multiple: true }),
|
|
@@ -107,6 +108,8 @@ export default class ProvidersConnect extends Command {
|
|
|
107
108
|
const definition = args.provider ? getProviderDefinition(args.provider) : await promptProvider();
|
|
108
109
|
if (!definition)
|
|
109
110
|
return;
|
|
111
|
+
const account = normalizeProviderAccount(flags.account);
|
|
112
|
+
const isDefaultAccount = isDefaultProviderAccount(account);
|
|
110
113
|
const passedCredentials = parseCredentialFlags(flags.credential);
|
|
111
114
|
const credentials = {};
|
|
112
115
|
const detectedPublicIp = !out.json && usesClientIp(definition) ? await fetchPublicIp() : undefined;
|
|
@@ -162,13 +165,24 @@ export default class ProvidersConnect extends Command {
|
|
|
162
165
|
defaults: setDefault ? { ...config.defaults, provider: definition.id } : config.defaults,
|
|
163
166
|
providers: {
|
|
164
167
|
...config.providers,
|
|
165
|
-
[definition.id]:
|
|
168
|
+
[definition.id]: isDefaultAccount
|
|
169
|
+
? { ...config.providers?.[definition.id], credentials }
|
|
170
|
+
: {
|
|
171
|
+
...config.providers?.[definition.id],
|
|
172
|
+
accounts: {
|
|
173
|
+
...config.providers?.[definition.id]?.accounts,
|
|
174
|
+
[account]: { credentials },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
166
177
|
},
|
|
167
178
|
}));
|
|
168
179
|
out.result({
|
|
180
|
+
account,
|
|
169
181
|
configPath: getConfigPath(),
|
|
170
182
|
credentials: Object.fromEntries(Object.entries(credentials).map(([key, value]) => [key, maskSecret(value)])),
|
|
183
|
+
defaultAccount: isDefaultAccount,
|
|
171
184
|
domainCount,
|
|
185
|
+
isDefaultAccount,
|
|
172
186
|
provider: definition.id,
|
|
173
187
|
verified: !flags['no-verify'],
|
|
174
188
|
});
|
|
@@ -7,6 +7,7 @@ export default class ProvidersDisconnect extends Command {
|
|
|
7
7
|
static description: string;
|
|
8
8
|
static examples: string[];
|
|
9
9
|
static flags: {
|
|
10
|
+
account: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<void>;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Args, Command } from '@oclif/core';
|
|
2
2
|
import { getConfigPath, updateConfig } from '../../lib/config.js';
|
|
3
|
-
import { jsonFlag } from '../../lib/flags.js';
|
|
3
|
+
import { accountFlag, jsonFlag } from '../../lib/flags.js';
|
|
4
4
|
import { createOutput, outputError } from '../../lib/output.js';
|
|
5
|
+
import { isDefaultProviderAccount, normalizeProviderAccount } from '../../lib/providers/core/config.js';
|
|
5
6
|
import { getProviderDefinition } from '../../lib/providers/registry.js';
|
|
6
7
|
function envOverrides(definition) {
|
|
7
8
|
return definition.credentials.flatMap((credential) => (process.env[credential.env] ? [credential.env] : []));
|
|
@@ -17,6 +18,7 @@ export default class ProvidersDisconnect extends Command {
|
|
|
17
18
|
'<%= config.bin %> <%= command.id %> cloudflare --json',
|
|
18
19
|
];
|
|
19
20
|
static flags = {
|
|
21
|
+
account: accountFlag,
|
|
20
22
|
json: jsonFlag,
|
|
21
23
|
};
|
|
22
24
|
async run() {
|
|
@@ -24,13 +26,41 @@ export default class ProvidersDisconnect extends Command {
|
|
|
24
26
|
const out = createOutput({ json: flags.json });
|
|
25
27
|
try {
|
|
26
28
|
const definition = getProviderDefinition(args.provider);
|
|
29
|
+
const account = flags.account ? normalizeProviderAccount(flags.account) : undefined;
|
|
27
30
|
let removed = false;
|
|
28
31
|
await updateConfig((config) => {
|
|
29
32
|
const providers = { ...config.providers };
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
const provider = providers[definition.id];
|
|
34
|
+
if (!account) {
|
|
35
|
+
removed = provider !== undefined;
|
|
36
|
+
delete providers[definition.id];
|
|
37
|
+
}
|
|
38
|
+
else if (provider) {
|
|
39
|
+
const nextProvider = { ...provider };
|
|
40
|
+
if (isDefaultProviderAccount(account)) {
|
|
41
|
+
removed = nextProvider.credentials !== undefined || (definition.id === 'spaceship' && ('apiKey' in nextProvider || 'apiSecret' in nextProvider));
|
|
42
|
+
delete nextProvider.credentials;
|
|
43
|
+
if (definition.id === 'spaceship') {
|
|
44
|
+
delete nextProvider.apiKey;
|
|
45
|
+
delete nextProvider.apiSecret;
|
|
46
|
+
delete nextProvider.domains;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const accounts = { ...nextProvider.accounts };
|
|
51
|
+
removed = accounts[account] !== undefined;
|
|
52
|
+
delete accounts[account];
|
|
53
|
+
nextProvider.accounts = Object.keys(accounts).length > 0 ? accounts : undefined;
|
|
54
|
+
}
|
|
55
|
+
if (nextProvider.credentials || nextProvider.settings || (nextProvider.accounts && Object.keys(nextProvider.accounts).length > 0)) {
|
|
56
|
+
providers[definition.id] = nextProvider;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
delete providers[definition.id];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
32
62
|
const defaults = { ...config.defaults };
|
|
33
|
-
if (defaults.provider === definition.id)
|
|
63
|
+
if (defaults.provider === definition.id && providers[definition.id] === undefined)
|
|
34
64
|
delete defaults.provider;
|
|
35
65
|
return {
|
|
36
66
|
...config,
|
|
@@ -39,7 +69,7 @@ export default class ProvidersDisconnect extends Command {
|
|
|
39
69
|
};
|
|
40
70
|
});
|
|
41
71
|
const overrides = envOverrides(definition);
|
|
42
|
-
out.result({ configPath: getConfigPath(), environmentOverrides: overrides, provider: definition.id, removed });
|
|
72
|
+
out.result({ account, configPath: getConfigPath(), environmentOverrides: overrides, provider: definition.id, removed });
|
|
43
73
|
if (overrides.length > 0)
|
|
44
74
|
out.warn(`${definition.displayName} environment credentials are still set: ${overrides.join(', ')}.`);
|
|
45
75
|
out.success(removed ? `${definition.displayName} credentials removed from ${getConfigPath()}.` : `${definition.displayName} was not connected.`);
|
|
@@ -24,8 +24,10 @@ export default class ProvidersStatus extends Command {
|
|
|
24
24
|
spinner?.start('Checking DNS providers');
|
|
25
25
|
const providers = await listProviderStatuses({ verify: !flags['no-verify'] });
|
|
26
26
|
spinner?.stop('Checked DNS providers');
|
|
27
|
-
for (const provider of providers)
|
|
28
|
-
|
|
27
|
+
for (const provider of providers) {
|
|
28
|
+
const account = provider.isDefaultAccount ? provider.id : `${provider.id}/${provider.account}`;
|
|
29
|
+
out.info(`${provider.displayName} (${account}) - ${formatStatus(provider)}${provider.default ? ' [default]' : ''}`);
|
|
30
|
+
}
|
|
29
31
|
out.result({ providers });
|
|
30
32
|
}
|
|
31
33
|
}
|
|
@@ -5,6 +5,7 @@ export default class ProvidersVerify extends Command {
|
|
|
5
5
|
};
|
|
6
6
|
static description: string;
|
|
7
7
|
static flags: {
|
|
8
|
+
account: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
9
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
};
|
|
10
11
|
run(): Promise<void>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Args, Command } from '@oclif/core';
|
|
2
|
-
import { jsonFlag } from '../../lib/flags.js';
|
|
2
|
+
import { accountFlag, jsonFlag } from '../../lib/flags.js';
|
|
3
3
|
import { createOutput, outputError } from '../../lib/output.js';
|
|
4
|
+
import { isDefaultProviderAccount, normalizeProviderAccount } from '../../lib/providers/core/config.js';
|
|
4
5
|
import { createProvider } from '../../lib/providers/registry.js';
|
|
5
6
|
export default class ProvidersVerify extends Command {
|
|
6
7
|
static args = {
|
|
@@ -8,15 +9,17 @@ export default class ProvidersVerify extends Command {
|
|
|
8
9
|
};
|
|
9
10
|
static description = 'Verify saved DNS provider credentials.';
|
|
10
11
|
static flags = {
|
|
12
|
+
account: accountFlag,
|
|
11
13
|
json: jsonFlag,
|
|
12
14
|
};
|
|
13
15
|
async run() {
|
|
14
16
|
const { args, flags } = await this.parse(ProvidersVerify);
|
|
15
17
|
const out = createOutput({ json: flags.json });
|
|
16
18
|
try {
|
|
17
|
-
const
|
|
19
|
+
const account = normalizeProviderAccount(flags.account);
|
|
20
|
+
const provider = await createProvider(args.provider, { account });
|
|
18
21
|
const health = await provider.verifyCredentials();
|
|
19
|
-
out.result({ health, provider: provider.id });
|
|
22
|
+
out.result({ account, health, isDefaultAccount: isDefaultProviderAccount(account), provider: provider.id });
|
|
20
23
|
out.success(`${provider.name} credentials verified.`);
|
|
21
24
|
}
|
|
22
25
|
catch (error) {
|
package/dist/commands/wizard.js
CHANGED
|
@@ -6,8 +6,8 @@ import { jsonFlag } from '../lib/flags.js';
|
|
|
6
6
|
import { createLinkPlan, linkDomain } from '../lib/link-domain.js';
|
|
7
7
|
import { detectLocalVercelProject } from '../lib/local-vercel.js';
|
|
8
8
|
import { createOutput, outputError } from '../lib/output.js';
|
|
9
|
+
import { DEFAULT_PROVIDER_ACCOUNT, listConfiguredProviderAccounts } from '../lib/providers/core/config.js';
|
|
9
10
|
import { createProvider, getProviderDefinition, listProviderDefinitions } from '../lib/providers/registry.js';
|
|
10
|
-
import { isProviderConfigured } from '../lib/providers/status.js';
|
|
11
11
|
import { listGlobalVercelTokens } from '../lib/vercel-auth.js';
|
|
12
12
|
import { createVercelClient } from '../lib/vercel.js';
|
|
13
13
|
const PERSONAL_ACCOUNT = '__personal__';
|
|
@@ -72,7 +72,7 @@ async function promptProviderDefinition(definitions, config) {
|
|
|
72
72
|
const selected = await p.select({
|
|
73
73
|
message: 'Choose DNS provider',
|
|
74
74
|
options: definitions.map((definition) => ({
|
|
75
|
-
hint:
|
|
75
|
+
hint: listConfiguredProviderAccounts(config, definition).length > 0 ? 'Connected' : 'Not connected',
|
|
76
76
|
label: definition.displayName,
|
|
77
77
|
value: definition.id,
|
|
78
78
|
})),
|
|
@@ -87,19 +87,24 @@ function showProviderSetup(definition) {
|
|
|
87
87
|
return;
|
|
88
88
|
p.note(definition.setup.notes.join('\n'), `${definition.displayName} setup`);
|
|
89
89
|
}
|
|
90
|
-
async function listProviderDomainOptions(definition) {
|
|
91
|
-
const provider = await createProvider(definition.id);
|
|
90
|
+
async function listProviderDomainOptions(definition, account) {
|
|
91
|
+
const provider = await createProvider(definition.id, { account: account.account });
|
|
92
92
|
const zones = await provider.listZones();
|
|
93
|
-
return zones.map((zone) => toProviderDomainOption(definition, zone));
|
|
93
|
+
return zones.map((zone) => toProviderDomainOption(definition, account, zone));
|
|
94
94
|
}
|
|
95
|
-
function toProviderDomainOption(definition, zone) {
|
|
95
|
+
function toProviderDomainOption(definition, account, zone) {
|
|
96
96
|
return {
|
|
97
|
+
account: account.account,
|
|
97
98
|
domain: zone.name,
|
|
98
|
-
id: `${definition.id}:${zone.name}`,
|
|
99
|
+
id: `${definition.id}:${account.account}:${zone.name}`,
|
|
100
|
+
isDefaultAccount: account.isDefaultAccount,
|
|
99
101
|
providerId: definition.id,
|
|
100
102
|
providerName: definition.displayName,
|
|
101
103
|
};
|
|
102
104
|
}
|
|
105
|
+
function providerAccountLabel(option) {
|
|
106
|
+
return option.isDefaultAccount ? option.providerName : `${option.providerName}/${option.account}`;
|
|
107
|
+
}
|
|
103
108
|
function projectLabel(project) {
|
|
104
109
|
return project.name ? `${project.name} (${project.id})` : project.id;
|
|
105
110
|
}
|
|
@@ -237,10 +242,10 @@ export default class Wizard extends Command {
|
|
|
237
242
|
const project = resolved;
|
|
238
243
|
const projectDisplay = projectLabel(projects.find((item) => item.id === project) ?? { id: project });
|
|
239
244
|
p.log.success(`Vercel ready: ${projectDisplay}`);
|
|
240
|
-
const
|
|
245
|
+
const configuredProviderAccounts = providerDefinitions.flatMap((definition) => listConfiguredProviderAccounts(config, definition).map((account) => ({ account, definition })));
|
|
241
246
|
const providerFailures = [];
|
|
242
247
|
const domainOptions = [];
|
|
243
|
-
if (
|
|
248
|
+
if (configuredProviderAccounts.length === 0) {
|
|
244
249
|
p.log.info('No DNS provider is configured yet. Connect one to continue.');
|
|
245
250
|
const selectedDefinition = await promptProviderDefinition(providerDefinitions, config);
|
|
246
251
|
if (!selectedDefinition)
|
|
@@ -256,7 +261,7 @@ export default class Wizard extends Command {
|
|
|
256
261
|
const zones = await provider.listZones();
|
|
257
262
|
domainSpinner.stop(`Connected ${selectedDefinition.displayName} and loaded ${zones.length} domain${zones.length === 1 ? '' : 's'}`);
|
|
258
263
|
activeSpinner = undefined;
|
|
259
|
-
domainOptions.push(...zones.map((zone) => toProviderDomainOption(selectedDefinition, zone)));
|
|
264
|
+
domainOptions.push(...zones.map((zone) => toProviderDomainOption(selectedDefinition, { account: DEFAULT_PROVIDER_ACCOUNT, isDefaultAccount: true, providerId: selectedDefinition.id }, zone)));
|
|
260
265
|
await updateConfig((current) => ({
|
|
261
266
|
...current,
|
|
262
267
|
defaults: { ...current.defaults, provider: selectedDefinition.id },
|
|
@@ -267,13 +272,14 @@ export default class Wizard extends Command {
|
|
|
267
272
|
else {
|
|
268
273
|
const domainSpinner = p.spinner();
|
|
269
274
|
activeSpinner = domainSpinner;
|
|
270
|
-
domainSpinner.start(`Loading domains from ${
|
|
271
|
-
for (const definition of
|
|
275
|
+
domainSpinner.start(`Loading domains from ${configuredProviderAccounts.length} provider account${configuredProviderAccounts.length === 1 ? '' : 's'}`);
|
|
276
|
+
for (const { account, definition } of configuredProviderAccounts) {
|
|
272
277
|
try {
|
|
273
|
-
domainOptions.push(...(await listProviderDomainOptions(definition)));
|
|
278
|
+
domainOptions.push(...(await listProviderDomainOptions(definition, account)));
|
|
274
279
|
}
|
|
275
280
|
catch (error) {
|
|
276
|
-
|
|
281
|
+
const label = account.isDefaultAccount ? definition.displayName : `${definition.displayName}/${account.account}`;
|
|
282
|
+
providerFailures.push(`${label}: ${error instanceof Error ? error.message : String(error)}`);
|
|
277
283
|
}
|
|
278
284
|
}
|
|
279
285
|
domainSpinner.stop(`Loaded ${domainOptions.length} domain${domainOptions.length === 1 ? '' : 's'}`);
|
|
@@ -299,14 +305,14 @@ export default class Wizard extends Command {
|
|
|
299
305
|
placeholder: 'Type to filter domains...',
|
|
300
306
|
maxItems: 10,
|
|
301
307
|
initialValue,
|
|
302
|
-
options: domainOptions.map((option) => ({ label: option.domain, value: option.id, hint: option
|
|
308
|
+
options: domainOptions.map((option) => ({ label: option.domain, value: option.id, hint: providerAccountLabel(option) })),
|
|
303
309
|
});
|
|
304
310
|
const selectedId = cancelIfNeeded(selectedDomainId);
|
|
305
311
|
if (selectedId === null)
|
|
306
312
|
return;
|
|
307
313
|
selectedDomain = domainOptions.find((option) => option.id === selectedId) ?? domainOptions[0];
|
|
308
314
|
}
|
|
309
|
-
p.log.info(`Using domain ${selectedDomain.domain} from ${selectedDomain
|
|
315
|
+
p.log.info(`Using domain ${selectedDomain.domain} from ${providerAccountLabel(selectedDomain)}.`);
|
|
310
316
|
const domain = selectedDomain.domain;
|
|
311
317
|
await updateConfig((current) => ({
|
|
312
318
|
...current,
|
|
@@ -330,8 +336,8 @@ export default class Wizard extends Command {
|
|
|
330
336
|
return;
|
|
331
337
|
}
|
|
332
338
|
const fullDomain = apex ? domain : `${subdomain}.${domain}`;
|
|
333
|
-
const preview = await createLinkPlan({ provider: selectedDomain.providerId, domain, subdomain, apex, project });
|
|
334
|
-
p.note([`Vercel: add ${preview.domain} to ${projectDisplay}`, ...preview.records.map((record) => recordPreview(record, selectedDomain
|
|
339
|
+
const preview = await createLinkPlan({ account: selectedDomain.account, provider: selectedDomain.providerId, domain, subdomain, apex, project });
|
|
340
|
+
p.note([`Vercel: add ${preview.domain} to ${projectDisplay}`, ...preview.records.map((record) => recordPreview(record, providerAccountLabel(selectedDomain)))].join('\n'), 'Preview');
|
|
335
341
|
const confirmed = await p.confirm({
|
|
336
342
|
message: `Link ${fullDomain} via ${selectedDomain.providerName} to Vercel project ${projectDisplay}?`,
|
|
337
343
|
initialValue: true,
|
|
@@ -343,6 +349,7 @@ export default class Wizard extends Command {
|
|
|
343
349
|
activeSpinner = spinner;
|
|
344
350
|
spinner.start('Adding domain to Vercel');
|
|
345
351
|
const result = await linkDomain({
|
|
352
|
+
account: selectedDomain.account,
|
|
346
353
|
provider: selectedDomain.providerId,
|
|
347
354
|
domain,
|
|
348
355
|
subdomain,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ProviderStatus } from './providers/status.js';
|
|
2
|
-
export type ProviderConnectionStatus = Pick<ProviderStatus, 'configured' | 'default' | 'displayName' | 'docsUrl' | 'id'>;
|
|
2
|
+
export type ProviderConnectionStatus = Pick<ProviderStatus, 'account' | 'configured' | 'default' | 'displayName' | 'docsUrl' | 'id' | 'isDefaultAccount'>;
|
|
3
3
|
export interface CommandSchema {
|
|
4
4
|
name: string;
|
|
5
5
|
description: string;
|
|
@@ -9,6 +9,7 @@ export const commandSchemas = [
|
|
|
9
9
|
'doomain link --domain app.example.com --project my-app --json',
|
|
10
10
|
'doomain link --domain example.com --subdomain app --project my-app --json',
|
|
11
11
|
'doomain link --provider spaceship --domain example.com --apex --project my-app --dry-run --json',
|
|
12
|
+
'doomain link app.example.com --provider spaceship --account work --project my-app --json',
|
|
12
13
|
],
|
|
13
14
|
agentHint: 'For agent use, try `doomain link <domain> --json` first. Do not inspect project files, run provider status, or use --dry-run unless the user explicitly asks for a preview. The command infers project/provider and returns structured recovery errors when inference fails.',
|
|
14
15
|
agentInstructions: [
|
|
@@ -25,6 +26,7 @@ export const commandSchemas = [
|
|
|
25
26
|
flags: [
|
|
26
27
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
27
28
|
{ name: 'provider', type: 'string', description: 'DNS provider id. Inferred from the target domain when omitted.' },
|
|
29
|
+
{ name: 'account', type: 'string', description: 'DNS provider account alias. Defaults to the provider default account.' },
|
|
28
30
|
{ name: 'domain', type: 'string', description: 'Target domain or base zone, for example app.example.com or example.com.' },
|
|
29
31
|
{ name: 'subdomain', type: 'string', description: 'Subdomain to add.' },
|
|
30
32
|
{ name: 'apex', type: 'boolean', description: 'Use the root/apex domain.' },
|
|
@@ -41,12 +43,14 @@ export const commandSchemas = [
|
|
|
41
43
|
examples: [
|
|
42
44
|
'doomain providers connect',
|
|
43
45
|
'doomain providers connect spaceship --credential apiKey=key --credential apiSecret=secret --json',
|
|
46
|
+
'doomain providers connect spaceship --account work --credential apiKey=key --credential apiSecret=secret --json',
|
|
44
47
|
'doomain providers connect namecheap --credential apiUser=user --credential apiKey=key --credential clientIp=127.0.0.1 --json',
|
|
45
48
|
'doomain providers connect cloudflare --credential apiToken=token --credential accountId=account_id --json',
|
|
46
49
|
'doomain providers connect hostinger --credential apiToken=token --json',
|
|
47
50
|
],
|
|
48
51
|
flags: [
|
|
49
52
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
53
|
+
{ name: 'account', type: 'string', description: 'DNS provider account alias. Defaults to the provider default account.' },
|
|
50
54
|
{ name: 'credential', type: 'string', description: 'Provider credential as key=value. Can be repeated.' },
|
|
51
55
|
{ name: 'api-key', type: 'string', description: 'Spaceship API key.' },
|
|
52
56
|
{ name: 'api-secret', type: 'string', description: 'Spaceship API secret.' },
|
|
@@ -56,9 +60,10 @@ export const commandSchemas = [
|
|
|
56
60
|
{
|
|
57
61
|
name: 'providers add',
|
|
58
62
|
description: 'Alias for providers connect.',
|
|
59
|
-
examples: ['doomain providers add', 'doomain providers add namecheap'],
|
|
63
|
+
examples: ['doomain providers add', 'doomain providers add namecheap', 'doomain providers add spaceship --account work'],
|
|
60
64
|
flags: [
|
|
61
65
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
66
|
+
{ name: 'account', type: 'string', description: 'DNS provider account alias. Defaults to the provider default account.' },
|
|
62
67
|
{ name: 'credential', type: 'string', description: 'Provider credential as key=value. Can be repeated.' },
|
|
63
68
|
{ name: 'no-verify', type: 'boolean', description: 'Save credentials without verifying them first.' },
|
|
64
69
|
],
|
|
@@ -75,17 +80,29 @@ export const commandSchemas = [
|
|
|
75
80
|
{
|
|
76
81
|
name: 'providers disconnect',
|
|
77
82
|
description: 'Remove saved DNS provider credentials locally.',
|
|
78
|
-
examples: [
|
|
83
|
+
examples: [
|
|
84
|
+
'doomain providers disconnect namecheap --json',
|
|
85
|
+
'doomain providers disconnect cloudflare --json',
|
|
86
|
+
'doomain providers disconnect spaceship --account work --json',
|
|
87
|
+
'doomain providers disconnect hostinger --json',
|
|
88
|
+
],
|
|
79
89
|
flags: [
|
|
80
90
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
91
|
+
{ name: 'account', type: 'string', description: 'DNS provider account alias. Omit to remove all accounts for the provider.' },
|
|
81
92
|
],
|
|
82
93
|
},
|
|
83
94
|
{
|
|
84
95
|
name: 'providers verify',
|
|
85
96
|
description: 'Verify saved DNS provider credentials.',
|
|
86
|
-
examples: [
|
|
97
|
+
examples: [
|
|
98
|
+
'doomain providers verify spaceship --json',
|
|
99
|
+
'doomain providers verify spaceship --account work --json',
|
|
100
|
+
'doomain providers verify namecheap --json',
|
|
101
|
+
'doomain providers verify hostinger --json',
|
|
102
|
+
],
|
|
87
103
|
flags: [
|
|
88
104
|
{ name: 'json', type: 'boolean', description: 'Output a single JSON object and never prompt.' },
|
|
105
|
+
{ name: 'account', type: 'string', description: 'DNS provider account alias. Defaults to the provider default account.' },
|
|
89
106
|
],
|
|
90
107
|
},
|
|
91
108
|
{
|
|
@@ -114,11 +131,13 @@ export function getCommandSchema(name) {
|
|
|
114
131
|
}
|
|
115
132
|
async function configuredProviders() {
|
|
116
133
|
return (await listProviderStatuses({ verify: false })).map((provider) => ({
|
|
134
|
+
account: provider.account,
|
|
117
135
|
configured: provider.configured,
|
|
118
136
|
default: provider.default,
|
|
119
137
|
displayName: provider.displayName,
|
|
120
138
|
docsUrl: provider.docsUrl,
|
|
121
139
|
id: provider.id,
|
|
140
|
+
isDefaultAccount: provider.isDefaultAccount,
|
|
122
141
|
}));
|
|
123
142
|
}
|
|
124
143
|
function withProviderConnections(schema, providers) {
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -6,10 +6,13 @@ export interface VercelConfig {
|
|
|
6
6
|
token?: string;
|
|
7
7
|
teamId?: string;
|
|
8
8
|
}
|
|
9
|
-
export interface
|
|
9
|
+
export interface ProviderAccountConfig {
|
|
10
10
|
credentials?: Record<string, string>;
|
|
11
11
|
settings?: Record<string, unknown>;
|
|
12
12
|
}
|
|
13
|
+
export interface ProviderConfig extends ProviderAccountConfig {
|
|
14
|
+
accounts?: Record<string, ProviderAccountConfig | undefined>;
|
|
15
|
+
}
|
|
13
16
|
export interface SpaceshipProviderConfig extends ProviderConfig {
|
|
14
17
|
apiKey?: string;
|
|
15
18
|
apiSecret?: string;
|
package/dist/lib/flags.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const jsonFlag: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
2
2
|
export declare const providerFlag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
3
|
+
export declare const accountFlag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
3
4
|
export declare const domainFlag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
4
5
|
export declare const subdomainFlag: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
5
6
|
export declare const apexFlag: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
package/dist/lib/flags.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
2
|
export const jsonFlag = Flags.boolean({ description: 'Output a single JSON object and never prompt.' });
|
|
3
3
|
export const providerFlag = Flags.string({ description: 'DNS provider id. Inferred from the target domain when omitted.' });
|
|
4
|
+
export const accountFlag = Flags.string({ description: 'DNS provider account alias. Defaults to the provider default account.' });
|
|
4
5
|
export const domainFlag = Flags.string({ description: 'Target domain or base zone, for example app.example.com or example.com.' });
|
|
5
6
|
export const subdomainFlag = Flags.string({ description: 'Subdomain to add, for example app for app.example.com.' });
|
|
6
7
|
export const apexFlag = Flags.boolean({ description: 'Use the root/apex domain instead of a subdomain.' });
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DnsRecordInput } from './providers/types.js';
|
|
2
2
|
export interface LinkDomainInput {
|
|
3
3
|
provider?: string;
|
|
4
|
+
account?: string;
|
|
4
5
|
domain?: string;
|
|
5
6
|
subdomain?: string;
|
|
6
7
|
apex?: boolean;
|
|
@@ -21,6 +22,9 @@ export type LinkDomainProgressCallback = (progress: LinkDomainProgress) => void;
|
|
|
21
22
|
export interface LinkDomainPlan {
|
|
22
23
|
provider: string;
|
|
23
24
|
providerInferred: boolean;
|
|
25
|
+
account: string;
|
|
26
|
+
accountInferred: boolean;
|
|
27
|
+
isDefaultAccount: boolean;
|
|
24
28
|
project: string;
|
|
25
29
|
projectSource: LinkDomainProjectSource;
|
|
26
30
|
recordName: string;
|
package/dist/lib/link-domain.js
CHANGED
|
@@ -4,8 +4,9 @@ import { dirname, join, parse } from 'node:path';
|
|
|
4
4
|
import { loadConfig } from './config.js';
|
|
5
5
|
import { DoomainError } from './errors.js';
|
|
6
6
|
import { detectLocalVercelProject } from './local-vercel.js';
|
|
7
|
+
import { DEFAULT_PROVIDER_ACCOUNT, isDefaultProviderAccount, listConfiguredProviderAccounts, normalizeProviderAccount, } from './providers/core/config.js';
|
|
7
8
|
import { createProvider, getProviderDefinition, listProviderDefinitions } from './providers/registry.js';
|
|
8
|
-
import {
|
|
9
|
+
import { listProviderStatuses } from './providers/status.js';
|
|
9
10
|
import { normalizeDomain, normalizeSubdomain } from './validate.js';
|
|
10
11
|
import { createVercelClient, resolveVercelConfig, VERCEL_APEX_A_RECORD, VERCEL_CNAME_RECORD } from './vercel.js';
|
|
11
12
|
function cleanDnsValue(value) {
|
|
@@ -118,10 +119,12 @@ async function resolveZone(provider, zoneDomain) {
|
|
|
118
119
|
async function providerConnectionDetails() {
|
|
119
120
|
return (await listProviderStatuses({ verify: false })).map((provider) => ({
|
|
120
121
|
configured: provider.configured,
|
|
122
|
+
account: provider.account,
|
|
121
123
|
default: provider.default,
|
|
122
124
|
displayName: provider.displayName,
|
|
123
125
|
docsUrl: provider.docsUrl,
|
|
124
126
|
id: provider.id,
|
|
127
|
+
isDefaultAccount: provider.isDefaultAccount,
|
|
125
128
|
}));
|
|
126
129
|
}
|
|
127
130
|
function resolveRequestedDomain(opts) {
|
|
@@ -155,51 +158,91 @@ function targetFromZone(fullDomain, zoneDomain) {
|
|
|
155
158
|
}
|
|
156
159
|
function candidateDetails(candidates) {
|
|
157
160
|
return candidates.map((candidate) => ({
|
|
161
|
+
account: candidate.account,
|
|
162
|
+
isDefaultAccount: candidate.isDefaultAccount,
|
|
158
163
|
provider: candidate.provider,
|
|
159
164
|
providerName: candidate.providerName,
|
|
160
165
|
zoneDomain: candidate.zone.name,
|
|
161
166
|
}));
|
|
162
167
|
}
|
|
163
|
-
|
|
164
|
-
|
|
168
|
+
function defaultAccountRef(providerId) {
|
|
169
|
+
return { account: DEFAULT_PROVIDER_ACCOUNT, isDefaultAccount: true, providerId };
|
|
170
|
+
}
|
|
171
|
+
function explicitAccountRef(providerId, account) {
|
|
172
|
+
const normalized = normalizeProviderAccount(account);
|
|
173
|
+
return { account: normalized, isDefaultAccount: isDefaultProviderAccount(normalized), providerId };
|
|
174
|
+
}
|
|
175
|
+
async function loadProviderZones(definition, account) {
|
|
176
|
+
const provider = await createProvider(definition.id, { account: account.account });
|
|
165
177
|
const zones = await provider.listZones();
|
|
166
178
|
return {
|
|
167
|
-
candidates: zones.map((zone) => ({
|
|
168
|
-
|
|
179
|
+
candidates: zones.map((zone) => ({
|
|
180
|
+
account: account.account,
|
|
181
|
+
isDefaultAccount: account.isDefaultAccount,
|
|
182
|
+
provider: definition.id,
|
|
183
|
+
providerName: definition.displayName,
|
|
184
|
+
zone,
|
|
185
|
+
})),
|
|
186
|
+
search: {
|
|
187
|
+
account: account.account,
|
|
188
|
+
displayName: definition.displayName,
|
|
189
|
+
id: definition.id,
|
|
190
|
+
isDefaultAccount: account.isDefaultAccount,
|
|
191
|
+
zones: zones.map((zone) => zone.name),
|
|
192
|
+
},
|
|
169
193
|
};
|
|
170
194
|
}
|
|
171
|
-
async function loadConfiguredProviderZones(providerId) {
|
|
195
|
+
async function loadConfiguredProviderZones(providerId, accountInput) {
|
|
196
|
+
const config = await loadConfig();
|
|
197
|
+
const account = accountInput ? normalizeProviderAccount(accountInput) : undefined;
|
|
172
198
|
if (providerId) {
|
|
173
199
|
const definition = getProviderDefinition(providerId);
|
|
174
|
-
const
|
|
175
|
-
|
|
200
|
+
const accounts = account ? [explicitAccountRef(definition.id, account)] : listConfiguredProviderAccounts(config, definition);
|
|
201
|
+
const selectedAccounts = accounts.length > 0 ? accounts : [defaultAccountRef(definition.id)];
|
|
202
|
+
const results = await Promise.all(selectedAccounts.map((ref) => loadProviderZones(definition, ref)));
|
|
203
|
+
return {
|
|
204
|
+
accountInferred: account === undefined,
|
|
205
|
+
candidates: results.flatMap((result) => result.candidates),
|
|
206
|
+
providerInferred: false,
|
|
207
|
+
searched: results.map((result) => result.search),
|
|
208
|
+
};
|
|
176
209
|
}
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
210
|
+
const providerAccounts = listProviderDefinitions().flatMap((definition) => listConfiguredProviderAccounts(config, definition)
|
|
211
|
+
.filter((ref) => !account || ref.account === account)
|
|
212
|
+
.map((ref) => ({ definition, ref })));
|
|
213
|
+
if (providerAccounts.length === 0) {
|
|
214
|
+
const message = account
|
|
215
|
+
? `No DNS provider account named ${account} is configured. Run \`doomain providers connect <provider> --account ${account}\` first.`
|
|
216
|
+
: 'No DNS provider is configured. Run `doomain providers connect` first.';
|
|
217
|
+
throw new DoomainError('CONFIG_NOT_FOUND', message, {
|
|
218
|
+
account,
|
|
181
219
|
configuredProviders: await providerConnectionDetails(),
|
|
182
220
|
recovery: 'Connect the DNS provider that owns this domain, then retry `doomain link <domain> --json`.',
|
|
183
|
-
suggestedCommands:
|
|
221
|
+
suggestedCommands: account
|
|
222
|
+
? [`doomain providers connect <provider> --account ${account}`, 'doomain link <domain> --json']
|
|
223
|
+
: ['doomain providers connect', 'doomain link <domain> --json'],
|
|
184
224
|
});
|
|
185
225
|
}
|
|
186
|
-
const results = await Promise.all(
|
|
226
|
+
const results = await Promise.all(providerAccounts.map(async ({ definition, ref }) => {
|
|
187
227
|
try {
|
|
188
|
-
return await loadProviderZones(definition);
|
|
228
|
+
return await loadProviderZones(definition, ref);
|
|
189
229
|
}
|
|
190
230
|
catch (error) {
|
|
191
231
|
return {
|
|
192
232
|
candidates: [],
|
|
193
233
|
search: {
|
|
234
|
+
account: ref.account,
|
|
194
235
|
displayName: definition.displayName,
|
|
195
236
|
error: error instanceof Error ? error.message : String(error),
|
|
196
237
|
id: definition.id,
|
|
238
|
+
isDefaultAccount: ref.isDefaultAccount,
|
|
197
239
|
zones: [],
|
|
198
240
|
},
|
|
199
241
|
};
|
|
200
242
|
}
|
|
201
243
|
}));
|
|
202
244
|
return {
|
|
245
|
+
accountInferred: account === undefined,
|
|
203
246
|
candidates: results.flatMap((result) => result.candidates),
|
|
204
247
|
providerInferred: true,
|
|
205
248
|
searched: results.map((result) => result.search),
|
|
@@ -211,30 +254,35 @@ async function resolveProviderTarget(input) {
|
|
|
211
254
|
domain: await resolveConfiguredDomain(input.domain),
|
|
212
255
|
subdomain: input.subdomain,
|
|
213
256
|
});
|
|
214
|
-
const zones = await loadConfiguredProviderZones(input.provider);
|
|
257
|
+
const zones = await loadConfiguredProviderZones(input.provider, input.account);
|
|
215
258
|
const matches = zones.candidates
|
|
216
259
|
.filter((candidate) => zoneMatchesDomain(requested.fullDomain, candidate.zone.name, requested.forceExactZone))
|
|
217
260
|
.sort((a, b) => b.zone.name.length - a.zone.name.length);
|
|
218
261
|
if (matches.length === 0) {
|
|
262
|
+
const account = input.account ? normalizeProviderAccount(input.account) : undefined;
|
|
219
263
|
const providerMessage = input.provider
|
|
220
|
-
? `${getProviderDefinition(input.provider).displayName} does not have a matching DNS zone for ${requested.fullDomain}.`
|
|
264
|
+
? `${getProviderDefinition(input.provider).displayName}${account ? ` account ${account}` : ''} does not have a matching DNS zone for ${requested.fullDomain}.`
|
|
221
265
|
: `No configured DNS provider has a matching DNS zone for ${requested.fullDomain}.`;
|
|
222
266
|
throw new DoomainError('PROVIDER_ZONE_NOT_FOUND', providerMessage, {
|
|
267
|
+
account,
|
|
223
268
|
configuredProviders: await providerConnectionDetails(),
|
|
224
269
|
domain: requested.fullDomain,
|
|
225
|
-
recovery: 'Retry with --provider <id> only if another configured provider owns this zone. Otherwise connect the DNS provider that owns this domain.',
|
|
270
|
+
recovery: 'Retry with --provider <id> --account <alias> only if another configured provider account owns this zone. Otherwise connect the DNS provider account that owns this domain.',
|
|
226
271
|
searchedZones: zones.searched,
|
|
227
|
-
suggestedCommands: [`doomain link ${requested.fullDomain} --provider <id> --json`, 'doomain providers connect'],
|
|
272
|
+
suggestedCommands: [`doomain link ${requested.fullDomain} --provider <id> --account <alias> --json`, 'doomain providers connect'],
|
|
228
273
|
});
|
|
229
274
|
}
|
|
230
275
|
const bestLength = matches[0].zone.name.length;
|
|
231
276
|
const bestMatches = matches.filter((candidate) => candidate.zone.name.length === bestLength);
|
|
232
|
-
const uniqueBestMatches = bestMatches.filter((candidate, index, candidates) => candidates.findIndex((item) => item.provider === candidate.provider && item.zone.name === candidate.zone.name) === index);
|
|
277
|
+
const uniqueBestMatches = bestMatches.filter((candidate, index, candidates) => candidates.findIndex((item) => item.provider === candidate.provider && item.account === candidate.account && item.zone.name === candidate.zone.name) === index);
|
|
233
278
|
if (uniqueBestMatches.length > 1) {
|
|
234
|
-
throw new DoomainError('PROVIDER_ZONE_AMBIGUOUS', `Multiple DNS
|
|
279
|
+
throw new DoomainError('PROVIDER_ZONE_AMBIGUOUS', `Multiple DNS provider accounts have a matching DNS zone for ${requested.fullDomain}. Pass --provider and --account to choose one.`, { candidates: candidateDetails(uniqueBestMatches), domain: requested.fullDomain });
|
|
235
280
|
}
|
|
236
281
|
const selected = uniqueBestMatches[0];
|
|
237
282
|
return {
|
|
283
|
+
account: selected.account,
|
|
284
|
+
accountInferred: zones.accountInferred,
|
|
285
|
+
isDefaultAccount: selected.isDefaultAccount,
|
|
238
286
|
provider: selected.provider,
|
|
239
287
|
providerInferred: zones.providerInferred,
|
|
240
288
|
target: targetFromZone(requested.fullDomain, selected.zone.name),
|
|
@@ -414,9 +462,12 @@ function reportProgress(input, stage, message) {
|
|
|
414
462
|
export async function createLinkPlan(input) {
|
|
415
463
|
const project = await resolveProject(input.project);
|
|
416
464
|
const resolved = await resolveProviderTarget(input);
|
|
417
|
-
const { provider, providerInferred, target } = resolved;
|
|
465
|
+
const { account, accountInferred, isDefaultAccount, provider, providerInferred, target } = resolved;
|
|
418
466
|
const record = planBaseRecord({ isApex: target.isApex, provider, recordName: target.recordName });
|
|
419
467
|
return {
|
|
468
|
+
account,
|
|
469
|
+
accountInferred,
|
|
470
|
+
isDefaultAccount,
|
|
420
471
|
provider,
|
|
421
472
|
providerInferred,
|
|
422
473
|
project: project.project,
|
|
@@ -441,7 +492,7 @@ export async function linkDomain(input) {
|
|
|
441
492
|
};
|
|
442
493
|
}
|
|
443
494
|
const vercel = createVercelClient(await resolveVercelConfig());
|
|
444
|
-
const provider = await createProvider(plan.provider);
|
|
495
|
+
const provider = await createProvider(plan.provider, { account: plan.account });
|
|
445
496
|
reportProgress(input, 'dns:resolve-zone', `Finding ${provider.name} DNS zone`);
|
|
446
497
|
const zone = await resolveZone(provider, plan.zoneDomain);
|
|
447
498
|
reportProgress(input, 'vercel:add-domain', 'Adding domain to Vercel');
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { type DoomainConfig } from '../../config.js';
|
|
2
2
|
import type { CredentialDefinition, DnsProviderDefinition, ProviderContext } from './types.js';
|
|
3
|
-
export declare
|
|
4
|
-
export
|
|
5
|
-
|
|
3
|
+
export declare const DEFAULT_PROVIDER_ACCOUNT = "default";
|
|
4
|
+
export interface ProviderAccountRef {
|
|
5
|
+
account: string;
|
|
6
|
+
isDefaultAccount: boolean;
|
|
7
|
+
providerId: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ProviderAccountOptions {
|
|
10
|
+
account?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function normalizeProviderAccount(account?: string): string;
|
|
13
|
+
export declare function isDefaultProviderAccount(account?: string): boolean;
|
|
14
|
+
export declare function getProviderCredentials(config: DoomainConfig, providerId: string, opts?: ProviderAccountOptions): Record<string, string>;
|
|
15
|
+
export declare function getProviderCredential(config: DoomainConfig, providerId: string, credential: CredentialDefinition, opts?: ProviderAccountOptions): string | undefined;
|
|
16
|
+
export declare function isProviderAccountConfigured(definition: DnsProviderDefinition, config: DoomainConfig, opts?: ProviderAccountOptions): boolean;
|
|
17
|
+
export declare function listConfiguredProviderAccounts(config: DoomainConfig, definition: DnsProviderDefinition): ProviderAccountRef[];
|
|
18
|
+
export declare function createProviderContext(definition: DnsProviderDefinition, opts?: ProviderAccountOptions): Promise<ProviderContext>;
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { loadConfig } from '../../config.js';
|
|
2
2
|
import { DoomainError } from '../../errors.js';
|
|
3
|
+
import { ensureProviderAccount } from '../../validate.js';
|
|
4
|
+
export const DEFAULT_PROVIDER_ACCOUNT = 'default';
|
|
5
|
+
export function normalizeProviderAccount(account) {
|
|
6
|
+
if (!account?.trim())
|
|
7
|
+
return DEFAULT_PROVIDER_ACCOUNT;
|
|
8
|
+
return ensureProviderAccount(account);
|
|
9
|
+
}
|
|
10
|
+
export function isDefaultProviderAccount(account) {
|
|
11
|
+
return normalizeProviderAccount(account) === DEFAULT_PROVIDER_ACCOUNT;
|
|
12
|
+
}
|
|
13
|
+
function providerConfig(config, providerId) {
|
|
14
|
+
const value = config.providers?.[providerId];
|
|
15
|
+
return value && typeof value === 'object' ? value : undefined;
|
|
16
|
+
}
|
|
3
17
|
function legacyCredential(config, providerId, key) {
|
|
4
18
|
if (providerId !== 'spaceship')
|
|
5
19
|
return undefined;
|
|
@@ -8,25 +22,53 @@ function legacyCredential(config, providerId, key) {
|
|
|
8
22
|
return undefined;
|
|
9
23
|
return legacy[key];
|
|
10
24
|
}
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
function credentialFromSavedConfig(config, providerId, account, key) {
|
|
26
|
+
const credentials = getProviderCredentials(config, providerId, { account });
|
|
27
|
+
if (credentials[key])
|
|
28
|
+
return credentials[key];
|
|
29
|
+
if (account === DEFAULT_PROVIDER_ACCOUNT)
|
|
30
|
+
return legacyCredential(config, providerId, key);
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
export function getProviderCredentials(config, providerId, opts = {}) {
|
|
34
|
+
const account = normalizeProviderAccount(opts.account);
|
|
35
|
+
const current = providerConfig(config, providerId);
|
|
36
|
+
const credentials = account === DEFAULT_PROVIDER_ACCOUNT ? current?.credentials : current?.accounts?.[account]?.credentials;
|
|
37
|
+
return { ...(credentials ?? {}) };
|
|
17
38
|
}
|
|
18
|
-
export function getProviderCredential(config, providerId, credential) {
|
|
19
|
-
|
|
39
|
+
export function getProviderCredential(config, providerId, credential, opts = {}) {
|
|
40
|
+
const account = normalizeProviderAccount(opts.account);
|
|
41
|
+
return process.env[credential.env] || credentialFromSavedConfig(config, providerId, account, credential.key);
|
|
42
|
+
}
|
|
43
|
+
export function isProviderAccountConfigured(definition, config, opts = {}) {
|
|
44
|
+
return definition.credentials.every((credential) => credential.required === false || Boolean(getProviderCredential(config, definition.id, credential, opts)));
|
|
45
|
+
}
|
|
46
|
+
export function listConfiguredProviderAccounts(config, definition) {
|
|
47
|
+
const accounts = [];
|
|
48
|
+
if (isProviderAccountConfigured(definition, config, { account: DEFAULT_PROVIDER_ACCOUNT })) {
|
|
49
|
+
accounts.push({ account: DEFAULT_PROVIDER_ACCOUNT, isDefaultAccount: true, providerId: definition.id });
|
|
50
|
+
}
|
|
51
|
+
for (const account of Object.keys(providerConfig(config, definition.id)?.accounts ?? {}).sort()) {
|
|
52
|
+
const normalized = normalizeProviderAccount(account);
|
|
53
|
+
if (normalized === DEFAULT_PROVIDER_ACCOUNT)
|
|
54
|
+
continue;
|
|
55
|
+
if (!isProviderAccountConfigured(definition, config, { account: normalized }))
|
|
56
|
+
continue;
|
|
57
|
+
accounts.push({ account: normalized, isDefaultAccount: false, providerId: definition.id });
|
|
58
|
+
}
|
|
59
|
+
return accounts;
|
|
20
60
|
}
|
|
21
|
-
export async function createProviderContext(definition) {
|
|
61
|
+
export async function createProviderContext(definition, opts = {}) {
|
|
22
62
|
const config = await loadConfig();
|
|
23
63
|
const credentials = {};
|
|
64
|
+
const account = normalizeProviderAccount(opts.account);
|
|
24
65
|
for (const credential of definition.credentials) {
|
|
25
|
-
const value = getProviderCredential(config, definition.id, credential);
|
|
66
|
+
const value = getProviderCredential(config, definition.id, credential, { account });
|
|
26
67
|
if (value)
|
|
27
68
|
credentials[credential.key] = value;
|
|
28
69
|
else if (credential.required !== false) {
|
|
29
|
-
|
|
70
|
+
const accountHint = account === DEFAULT_PROVIDER_ACCOUNT ? '' : ` for account ${account}`;
|
|
71
|
+
throw new DoomainError('MISSING_CREDENTIALS', `Missing ${definition.displayName} ${credential.label}${accountHint}. Run \`doomain providers connect ${definition.id}${account === DEFAULT_PROVIDER_ACCOUNT ? '' : ` --account ${account}`}\` or set ${credential.env}.`, { account, provider: definition.id });
|
|
30
72
|
}
|
|
31
73
|
}
|
|
32
74
|
return { credentials, debug: process.env.DOOMAIN_DEBUG === '1' };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DnsProvider, DnsProviderDefinition } from './types.js';
|
|
2
2
|
export declare function listProviderDefinitions(): DnsProviderDefinition[];
|
|
3
3
|
export declare function getProviderDefinition(id: string): DnsProviderDefinition;
|
|
4
|
-
export declare function createProvider(id: string
|
|
4
|
+
export declare function createProvider(id: string, opts?: {
|
|
5
|
+
account?: string;
|
|
6
|
+
}): Promise<DnsProvider>;
|
|
@@ -16,7 +16,7 @@ export function getProviderDefinition(id) {
|
|
|
16
16
|
throw new DoomainError('PROVIDER_NOT_FOUND', `Unsupported DNS provider: ${id}`);
|
|
17
17
|
return definition;
|
|
18
18
|
}
|
|
19
|
-
export async function createProvider(id) {
|
|
19
|
+
export async function createProvider(id, opts = {}) {
|
|
20
20
|
const definition = getProviderDefinition(id);
|
|
21
|
-
return definition.create(await createProviderContext(definition));
|
|
21
|
+
return definition.create(await createProviderContext(definition, opts));
|
|
22
22
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DoomainConfig } from '../config.js';
|
|
2
2
|
import type { DnsProviderDefinition } from './types.js';
|
|
3
3
|
export interface ProviderStatus {
|
|
4
|
+
account: string;
|
|
4
5
|
configured: boolean;
|
|
5
6
|
default: boolean;
|
|
6
7
|
displayName: string;
|
|
@@ -8,9 +9,12 @@ export interface ProviderStatus {
|
|
|
8
9
|
domainCount?: number;
|
|
9
10
|
error?: string;
|
|
10
11
|
id: string;
|
|
12
|
+
isDefaultAccount: boolean;
|
|
11
13
|
verified?: boolean;
|
|
12
14
|
}
|
|
13
|
-
export declare function isProviderConfigured(definition: DnsProviderDefinition, config: DoomainConfig
|
|
15
|
+
export declare function isProviderConfigured(definition: DnsProviderDefinition, config: DoomainConfig, opts?: {
|
|
16
|
+
account?: string;
|
|
17
|
+
}): boolean;
|
|
14
18
|
export declare function listProviderStatuses(opts?: {
|
|
15
19
|
verify?: boolean;
|
|
16
20
|
}): Promise<ProviderStatus[]>;
|
|
@@ -1,33 +1,40 @@
|
|
|
1
1
|
import { loadConfig } from '../config.js';
|
|
2
|
-
import {
|
|
2
|
+
import { DEFAULT_PROVIDER_ACCOUNT, isProviderAccountConfigured, listConfiguredProviderAccounts, normalizeProviderAccount, } from './core/config.js';
|
|
3
3
|
import { createProvider, listProviderDefinitions } from './registry.js';
|
|
4
|
-
export function isProviderConfigured(definition, config) {
|
|
5
|
-
return definition
|
|
4
|
+
export function isProviderConfigured(definition, config, opts = {}) {
|
|
5
|
+
return isProviderAccountConfigured(definition, config, opts);
|
|
6
6
|
}
|
|
7
7
|
export async function listProviderStatuses(opts = {}) {
|
|
8
8
|
const config = await loadConfig();
|
|
9
9
|
const statuses = [];
|
|
10
10
|
for (const definition of listProviderDefinitions()) {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
const accounts = listConfiguredProviderAccounts(config, definition);
|
|
12
|
+
const refs = accounts.length > 0 ? accounts : [{ account: DEFAULT_PROVIDER_ACCOUNT, isDefaultAccount: true, providerId: definition.id }];
|
|
13
|
+
for (const ref of refs) {
|
|
14
|
+
const account = normalizeProviderAccount(ref.account);
|
|
15
|
+
const configured = accounts.some((item) => item.account === account);
|
|
16
|
+
const status = {
|
|
17
|
+
account,
|
|
18
|
+
configured,
|
|
19
|
+
default: config.defaults?.provider === definition.id,
|
|
20
|
+
displayName: definition.displayName,
|
|
21
|
+
docsUrl: definition.docsUrl,
|
|
22
|
+
id: definition.id,
|
|
23
|
+
isDefaultAccount: ref.isDefaultAccount,
|
|
24
|
+
};
|
|
25
|
+
if (configured && opts.verify) {
|
|
26
|
+
try {
|
|
27
|
+
const zones = await (await createProvider(definition.id, { account })).listZones();
|
|
28
|
+
status.domainCount = zones.length;
|
|
29
|
+
status.verified = true;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
status.error = error instanceof Error ? error.message : String(error);
|
|
33
|
+
status.verified = false;
|
|
34
|
+
}
|
|
28
35
|
}
|
|
36
|
+
statuses.push(status);
|
|
29
37
|
}
|
|
30
|
-
statuses.push(status);
|
|
31
38
|
}
|
|
32
39
|
return statuses;
|
|
33
40
|
}
|
package/dist/lib/validate.d.ts
CHANGED
|
@@ -12,4 +12,5 @@ export declare function resolveDomainTarget(opts: {
|
|
|
12
12
|
apex?: boolean;
|
|
13
13
|
}): DomainTarget;
|
|
14
14
|
export declare function ensureProviderId(value: string): string;
|
|
15
|
+
export declare function ensureProviderAccount(value: string): string;
|
|
15
16
|
export declare function ensureProject(value?: string): string;
|
package/dist/lib/validate.js
CHANGED
|
@@ -54,6 +54,13 @@ export function ensureProviderId(value) {
|
|
|
54
54
|
}
|
|
55
55
|
return provider;
|
|
56
56
|
}
|
|
57
|
+
export function ensureProviderAccount(value) {
|
|
58
|
+
const account = value.trim().toLowerCase();
|
|
59
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(account)) {
|
|
60
|
+
throw new DoomainError('INVALID_INPUT', `Invalid provider account alias: ${value}`);
|
|
61
|
+
}
|
|
62
|
+
return account;
|
|
63
|
+
}
|
|
57
64
|
export function ensureProject(value) {
|
|
58
65
|
const project = value?.trim();
|
|
59
66
|
if (!project)
|
package/oclif.manifest.json
CHANGED
|
@@ -15,9 +15,17 @@
|
|
|
15
15
|
"<%= config.bin %> <%= command.id %> app.example.com --project my-app --json",
|
|
16
16
|
"<%= config.bin %> <%= command.id %> --domain app.example.com --project my-app --json",
|
|
17
17
|
"<%= config.bin %> <%= command.id %> --domain example.com --subdomain app --project my-app",
|
|
18
|
-
"<%= config.bin %> <%= command.id %> --provider spaceship --domain example.com --apex --project my-app --json"
|
|
18
|
+
"<%= config.bin %> <%= command.id %> --provider spaceship --domain example.com --apex --project my-app --json",
|
|
19
|
+
"<%= config.bin %> <%= command.id %> app.example.com --provider spaceship --account work --project my-app --json"
|
|
19
20
|
],
|
|
20
21
|
"flags": {
|
|
22
|
+
"account": {
|
|
23
|
+
"description": "DNS provider account alias. Defaults to the provider default account.",
|
|
24
|
+
"name": "account",
|
|
25
|
+
"hasDynamicHelp": false,
|
|
26
|
+
"multiple": false,
|
|
27
|
+
"type": "option"
|
|
28
|
+
},
|
|
21
29
|
"apex": {
|
|
22
30
|
"description": "Use the root/apex domain instead of a subdomain.",
|
|
23
31
|
"name": "apex",
|
|
@@ -264,6 +272,13 @@
|
|
|
264
272
|
"args": {},
|
|
265
273
|
"description": "List DNS zones and records for a provider.",
|
|
266
274
|
"flags": {
|
|
275
|
+
"account": {
|
|
276
|
+
"description": "DNS provider account alias. Defaults to the provider default account.",
|
|
277
|
+
"name": "account",
|
|
278
|
+
"hasDynamicHelp": false,
|
|
279
|
+
"multiple": false,
|
|
280
|
+
"type": "option"
|
|
281
|
+
},
|
|
267
282
|
"domain": {
|
|
268
283
|
"description": "Target domain or base zone, for example app.example.com or example.com.",
|
|
269
284
|
"name": "domain",
|
|
@@ -301,6 +316,41 @@
|
|
|
301
316
|
"list.js"
|
|
302
317
|
]
|
|
303
318
|
},
|
|
319
|
+
"projects:list": {
|
|
320
|
+
"aliases": [],
|
|
321
|
+
"args": {},
|
|
322
|
+
"description": "List Vercel projects.",
|
|
323
|
+
"flags": {
|
|
324
|
+
"json": {
|
|
325
|
+
"description": "Output a single JSON object and never prompt.",
|
|
326
|
+
"name": "json",
|
|
327
|
+
"allowNo": false,
|
|
328
|
+
"type": "boolean"
|
|
329
|
+
},
|
|
330
|
+
"search": {
|
|
331
|
+
"description": "Filter projects by search term.",
|
|
332
|
+
"name": "search",
|
|
333
|
+
"hasDynamicHelp": false,
|
|
334
|
+
"multiple": false,
|
|
335
|
+
"type": "option"
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
"hasDynamicHelp": false,
|
|
339
|
+
"hiddenAliases": [],
|
|
340
|
+
"id": "projects:list",
|
|
341
|
+
"pluginAlias": "doomain",
|
|
342
|
+
"pluginName": "doomain",
|
|
343
|
+
"pluginType": "core",
|
|
344
|
+
"strict": true,
|
|
345
|
+
"enableJsonFlag": false,
|
|
346
|
+
"isESM": true,
|
|
347
|
+
"relativePath": [
|
|
348
|
+
"dist",
|
|
349
|
+
"commands",
|
|
350
|
+
"projects",
|
|
351
|
+
"list.js"
|
|
352
|
+
]
|
|
353
|
+
},
|
|
304
354
|
"providers:add": {
|
|
305
355
|
"aliases": [],
|
|
306
356
|
"args": {
|
|
@@ -312,6 +362,13 @@
|
|
|
312
362
|
},
|
|
313
363
|
"description": "Save DNS provider credentials locally.",
|
|
314
364
|
"flags": {
|
|
365
|
+
"account": {
|
|
366
|
+
"description": "DNS provider account alias. Defaults to the provider default account.",
|
|
367
|
+
"name": "account",
|
|
368
|
+
"hasDynamicHelp": false,
|
|
369
|
+
"multiple": false,
|
|
370
|
+
"type": "option"
|
|
371
|
+
},
|
|
315
372
|
"api-key": {
|
|
316
373
|
"description": "Compatibility alias for Spaceship apiKey.",
|
|
317
374
|
"name": "api-key",
|
|
@@ -374,6 +431,13 @@
|
|
|
374
431
|
},
|
|
375
432
|
"description": "Save DNS provider credentials locally.",
|
|
376
433
|
"flags": {
|
|
434
|
+
"account": {
|
|
435
|
+
"description": "DNS provider account alias. Defaults to the provider default account.",
|
|
436
|
+
"name": "account",
|
|
437
|
+
"hasDynamicHelp": false,
|
|
438
|
+
"multiple": false,
|
|
439
|
+
"type": "option"
|
|
440
|
+
},
|
|
377
441
|
"api-key": {
|
|
378
442
|
"description": "Compatibility alias for Spaceship apiKey.",
|
|
379
443
|
"name": "api-key",
|
|
@@ -442,6 +506,13 @@
|
|
|
442
506
|
"<%= config.bin %> <%= command.id %> cloudflare --json"
|
|
443
507
|
],
|
|
444
508
|
"flags": {
|
|
509
|
+
"account": {
|
|
510
|
+
"description": "DNS provider account alias. Defaults to the provider default account.",
|
|
511
|
+
"name": "account",
|
|
512
|
+
"hasDynamicHelp": false,
|
|
513
|
+
"multiple": false,
|
|
514
|
+
"type": "option"
|
|
515
|
+
},
|
|
445
516
|
"json": {
|
|
446
517
|
"description": "Output a single JSON object and never prompt.",
|
|
447
518
|
"name": "json",
|
|
@@ -538,6 +609,13 @@
|
|
|
538
609
|
},
|
|
539
610
|
"description": "Verify saved DNS provider credentials.",
|
|
540
611
|
"flags": {
|
|
612
|
+
"account": {
|
|
613
|
+
"description": "DNS provider account alias. Defaults to the provider default account.",
|
|
614
|
+
"name": "account",
|
|
615
|
+
"hasDynamicHelp": false,
|
|
616
|
+
"multiple": false,
|
|
617
|
+
"type": "option"
|
|
618
|
+
},
|
|
541
619
|
"json": {
|
|
542
620
|
"description": "Output a single JSON object and never prompt.",
|
|
543
621
|
"name": "json",
|
|
@@ -561,41 +639,6 @@
|
|
|
561
639
|
"verify.js"
|
|
562
640
|
]
|
|
563
641
|
},
|
|
564
|
-
"projects:list": {
|
|
565
|
-
"aliases": [],
|
|
566
|
-
"args": {},
|
|
567
|
-
"description": "List Vercel projects.",
|
|
568
|
-
"flags": {
|
|
569
|
-
"json": {
|
|
570
|
-
"description": "Output a single JSON object and never prompt.",
|
|
571
|
-
"name": "json",
|
|
572
|
-
"allowNo": false,
|
|
573
|
-
"type": "boolean"
|
|
574
|
-
},
|
|
575
|
-
"search": {
|
|
576
|
-
"description": "Filter projects by search term.",
|
|
577
|
-
"name": "search",
|
|
578
|
-
"hasDynamicHelp": false,
|
|
579
|
-
"multiple": false,
|
|
580
|
-
"type": "option"
|
|
581
|
-
}
|
|
582
|
-
},
|
|
583
|
-
"hasDynamicHelp": false,
|
|
584
|
-
"hiddenAliases": [],
|
|
585
|
-
"id": "projects:list",
|
|
586
|
-
"pluginAlias": "doomain",
|
|
587
|
-
"pluginName": "doomain",
|
|
588
|
-
"pluginType": "core",
|
|
589
|
-
"strict": true,
|
|
590
|
-
"enableJsonFlag": false,
|
|
591
|
-
"isESM": true,
|
|
592
|
-
"relativePath": [
|
|
593
|
-
"dist",
|
|
594
|
-
"commands",
|
|
595
|
-
"projects",
|
|
596
|
-
"list.js"
|
|
597
|
-
]
|
|
598
|
-
},
|
|
599
642
|
"auth:logout:vercel": {
|
|
600
643
|
"aliases": [],
|
|
601
644
|
"args": {},
|
|
@@ -630,5 +673,5 @@
|
|
|
630
673
|
]
|
|
631
674
|
}
|
|
632
675
|
},
|
|
633
|
-
"version": "0.1.
|
|
676
|
+
"version": "0.1.11"
|
|
634
677
|
}
|