@atlasauth/cli 0.5.0 → 0.7.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/CHANGELOG.md +15 -0
- package/README.md +5 -0
- package/dist/args.d.ts +39 -0
- package/dist/args.js +17 -0
- package/dist/args.js.map +1 -1
- package/dist/commands/actions-gallery.d.ts +8 -0
- package/dist/commands/actions-gallery.js +197 -0
- package/dist/commands/actions-gallery.js.map +1 -0
- package/dist/commands/actions.d.ts +2 -0
- package/dist/commands/actions.js +17 -2
- package/dist/commands/actions.js.map +1 -1
- package/dist/commands/import.d.ts +3 -0
- package/dist/commands/import.js +116 -0
- package/dist/commands/import.js.map +1 -0
- package/dist/commands/info.d.ts +75 -0
- package/dist/commands/info.js +362 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/mcp.d.ts +38 -0
- package/dist/commands/mcp.js +108 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/migrate.d.ts +12 -0
- package/dist/commands/migrate.js +65 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/referrals.d.ts +6 -6
- package/dist/commands/referrals.js +28 -28
- package/dist/context.d.ts +1 -1
- package/dist/help.d.ts +1 -1
- package/dist/help.js +111 -7
- package/dist/help.js.map +1 -1
- package/dist/import/csv.d.ts +6 -0
- package/dist/import/csv.js +63 -0
- package/dist/import/csv.js.map +1 -0
- package/dist/import/vendors.d.ts +57 -0
- package/dist/import/vendors.js +568 -0
- package/dist/import/vendors.js.map +1 -0
- package/dist/run.js +8 -0
- package/dist/run.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.migrate = migrate;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const upgrade_1 = require("@atlasauth/upgrade");
|
|
7
|
+
const args_1 = require("../args");
|
|
8
|
+
const errors_1 = require("../errors");
|
|
9
|
+
const output_1 = require("../output");
|
|
10
|
+
const shared_1 = require("./shared");
|
|
11
|
+
/**
|
|
12
|
+
* `atlas migrate code --from <vendor> [path]` — rewrite an app's auth code onto
|
|
13
|
+
* Atlas (Clerk, Auth.js/NextAuth, Supabase Auth, Auth0, Better Auth).
|
|
14
|
+
*
|
|
15
|
+
* Safe by default: without `--yes` it is a dry run that prints the diff of every
|
|
16
|
+
* file it would change plus a report of what it could not rewrite (file:line).
|
|
17
|
+
* `--yes` writes the changes. Running it again over migrated code changes nothing.
|
|
18
|
+
* Users move separately, with `atlas import --from <vendor>`.
|
|
19
|
+
*/
|
|
20
|
+
async function migrate(ctx, args) {
|
|
21
|
+
const sub = args.positionals[1];
|
|
22
|
+
if (sub !== 'code') {
|
|
23
|
+
throw new errors_1.CliError(sub ? `Unknown subcommand 'migrate ${sub}'. Try: code.` : 'migrate needs a subcommand: code.');
|
|
24
|
+
}
|
|
25
|
+
const raw = (0, args_1.str)(args.values, 'from');
|
|
26
|
+
const vendor = (0, upgrade_1.resolveVendor)(raw);
|
|
27
|
+
if (!vendor) {
|
|
28
|
+
throw new errors_1.CliError(raw
|
|
29
|
+
? `Unknown --from '${raw}'. Use one of: ${upgrade_1.MIGRATE_VENDORS.join(', ')}.`
|
|
30
|
+
: `migrate code needs --from <${upgrade_1.MIGRATE_VENDORS.join('|')}>.`);
|
|
31
|
+
}
|
|
32
|
+
const target = (0, node_path_1.resolve)(args.positionals[2] ?? process.cwd());
|
|
33
|
+
if (!(0, node_fs_1.existsSync)(target))
|
|
34
|
+
throw new errors_1.CliError(`Path not found: ${target}`);
|
|
35
|
+
const write = args.values.yes === true && args.values['dry-run'] !== true;
|
|
36
|
+
const result = (0, upgrade_1.runMigration)(target, { from: vendor, write });
|
|
37
|
+
if ((0, shared_1.wantsJson)(args.values)) {
|
|
38
|
+
(0, output_1.printJson)(ctx.io, {
|
|
39
|
+
object: 'code_migration',
|
|
40
|
+
vendor: result.vendor,
|
|
41
|
+
written: write,
|
|
42
|
+
env_style: result.envStyle,
|
|
43
|
+
files_scanned: result.filesScanned,
|
|
44
|
+
files_changed: result.filesChanged,
|
|
45
|
+
rewrites: result.rewrites,
|
|
46
|
+
changes: result.changes.map((c) => ({ path: c.path, diff: (0, upgrade_1.unifiedDiff)(c.before, c.after, c.path) })),
|
|
47
|
+
manual: result.manual,
|
|
48
|
+
packages: result.packages,
|
|
49
|
+
env_to_set: result.envToSet,
|
|
50
|
+
});
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
for (const change of result.changes) {
|
|
54
|
+
if (write)
|
|
55
|
+
ctx.io.out(`updated ${change.path}`);
|
|
56
|
+
else {
|
|
57
|
+
ctx.io.out((0, upgrade_1.unifiedDiff)(change.before, change.after, change.path));
|
|
58
|
+
ctx.io.out('');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const line of (0, upgrade_1.formatMigration)(result, { written: write }))
|
|
62
|
+
ctx.io.out(line);
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../src/commands/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,12 +4,12 @@ import type { RunContext } from '../context';
|
|
|
4
4
|
* `atlas referrals connect | backfill | status`: referral attribution from the
|
|
5
5
|
* terminal.
|
|
6
6
|
*
|
|
7
|
-
* `connect --provider referally` is bring-your-own-key: the
|
|
8
|
-
* key is used for one call (it creates the Atlas connection on
|
|
9
|
-
* is never stored or printed. The connection id and secret
|
|
7
|
+
* `connect --provider referally` is bring-your-own-key: the Referalli merchant
|
|
8
|
+
* key is used for one call (it creates the Atlas connection on Referalli) and
|
|
9
|
+
* is never stored or printed. The connection id and secret Referalli returns
|
|
10
10
|
* go straight onto the instance; the secret is never printed either.
|
|
11
11
|
*/
|
|
12
|
-
/** The
|
|
12
|
+
/** The Referalli API version pinned on every call. */
|
|
13
13
|
export declare const REFERALLY_VERSION = "2026-10-21";
|
|
14
14
|
interface PingRead {
|
|
15
15
|
ok?: boolean;
|
|
@@ -36,12 +36,12 @@ export declare function pingSkewMs(ping: PingRead, receivedAt: number, latencyMs
|
|
|
36
36
|
export declare function describeTestLines(test: TestRead, receivedAt: number): string[];
|
|
37
37
|
/** A `customerRef,code` CSV → map; throws on a bad line. A header row is skipped. */
|
|
38
38
|
export declare function parseCodeCsv(text: string): Record<string, string>;
|
|
39
|
-
/** The
|
|
39
|
+
/** The Referalli topics two-way sync subscribes to (changes to a referral after sign-up). */
|
|
40
40
|
export declare const REFERALLY_SYNC_TOPICS: readonly ["referral.created", "referral.held", "referral.paused", "referral.resumed", "referral.approved", "referral.voided", "referral.reassigned"];
|
|
41
41
|
/**
|
|
42
42
|
* Whether the merchant key reaches live or test data: the org's `livemode`,
|
|
43
43
|
* else the key prefix (rk_test_ = test; rk_live_ and older rk_ keys = live).
|
|
44
|
-
* `known` is false when
|
|
44
|
+
* `known` is false when Referalli reports no mode (no test mode there).
|
|
45
45
|
*/
|
|
46
46
|
export declare function referallyKeyMode(ctx: RunContext, input: {
|
|
47
47
|
baseUrl: string;
|
|
@@ -19,12 +19,12 @@ const shared_1 = require("./shared");
|
|
|
19
19
|
* `atlas referrals connect | backfill | status`: referral attribution from the
|
|
20
20
|
* terminal.
|
|
21
21
|
*
|
|
22
|
-
* `connect --provider referally` is bring-your-own-key: the
|
|
23
|
-
* key is used for one call (it creates the Atlas connection on
|
|
24
|
-
* is never stored or printed. The connection id and secret
|
|
22
|
+
* `connect --provider referally` is bring-your-own-key: the Referalli merchant
|
|
23
|
+
* key is used for one call (it creates the Atlas connection on Referalli) and
|
|
24
|
+
* is never stored or printed. The connection id and secret Referalli returns
|
|
25
25
|
* go straight onto the instance; the secret is never printed either.
|
|
26
26
|
*/
|
|
27
|
-
/** The
|
|
27
|
+
/** The Referalli API version pinned on every call. */
|
|
28
28
|
exports.REFERALLY_VERSION = '2026-10-21';
|
|
29
29
|
const PROVIDERS = ['referally', 'webhook'];
|
|
30
30
|
/** Read a key from a file, or stdin with '-'. A bare key or a dotenv line. Never printed. */
|
|
@@ -113,7 +113,7 @@ function parseCodeCsv(text) {
|
|
|
113
113
|
});
|
|
114
114
|
return out;
|
|
115
115
|
}
|
|
116
|
-
/** One call to the
|
|
116
|
+
/** One call to the Referalli merchant API with the merchant key. Throws a CliError naming `what` on failure. */
|
|
117
117
|
async function referallyRequest(ctx, input) {
|
|
118
118
|
const fetchImpl = ctx.fetch ?? globalThis.fetch;
|
|
119
119
|
let res;
|
|
@@ -130,7 +130,7 @@ async function referallyRequest(ctx, input) {
|
|
|
130
130
|
});
|
|
131
131
|
}
|
|
132
132
|
catch (err) {
|
|
133
|
-
throw new errors_1.CliError(`Could not reach
|
|
133
|
+
throw new errors_1.CliError(`Could not reach Referalli at ${input.baseUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
134
134
|
}
|
|
135
135
|
const text = await res.text();
|
|
136
136
|
let json = {};
|
|
@@ -146,18 +146,18 @@ async function referallyRequest(ctx, input) {
|
|
|
146
146
|
(Array.isArray(err) && err[0] && typeof err[0] === 'object' && 'message' in err[0] ? String(err[0].message) : null) ??
|
|
147
147
|
(typeof json.message === 'string' ? json.message : null);
|
|
148
148
|
const hint = res.status === 401 || res.status === 403 ? ' Check the merchant key.' : '';
|
|
149
|
-
throw new errors_1.CliError(`
|
|
149
|
+
throw new errors_1.CliError(`Referalli refused to ${input.what} (HTTP ${res.status})${msg ? `: ${msg}` : ''}.${hint}`);
|
|
150
150
|
}
|
|
151
151
|
return json;
|
|
152
152
|
}
|
|
153
153
|
async function referallyCreateConnection(ctx, input) {
|
|
154
154
|
const json = await referallyRequest(ctx, { ...input, method: 'POST', path: '/v1/auth/connections', what: 'create the connection' });
|
|
155
155
|
if (typeof json.id !== 'string' || typeof json.secret !== 'string') {
|
|
156
|
-
throw new errors_1.CliError('
|
|
156
|
+
throw new errors_1.CliError('Referalli answered without a connection id and secret.');
|
|
157
157
|
}
|
|
158
158
|
return json;
|
|
159
159
|
}
|
|
160
|
-
/** The
|
|
160
|
+
/** The Referalli topics two-way sync subscribes to (changes to a referral after sign-up). */
|
|
161
161
|
exports.REFERALLY_SYNC_TOPICS = [
|
|
162
162
|
'referral.created',
|
|
163
163
|
'referral.held',
|
|
@@ -170,7 +170,7 @@ exports.REFERALLY_SYNC_TOPICS = [
|
|
|
170
170
|
/**
|
|
171
171
|
* Whether the merchant key reaches live or test data: the org's `livemode`,
|
|
172
172
|
* else the key prefix (rk_test_ = test; rk_live_ and older rk_ keys = live).
|
|
173
|
-
* `known` is false when
|
|
173
|
+
* `known` is false when Referalli reports no mode (no test mode there).
|
|
174
174
|
*/
|
|
175
175
|
async function referallyKeyMode(ctx, input) {
|
|
176
176
|
try {
|
|
@@ -206,12 +206,12 @@ async function connect(ctx, args) {
|
|
|
206
206
|
throw new errors_1.CliError('referrals connect --provider referally needs --program <prg_…>.');
|
|
207
207
|
const baseRaw = (0, args_1.str)(v, 'base-url') ?? ctx.env.REFERALLY_BASE_URL?.trim();
|
|
208
208
|
if (!baseRaw)
|
|
209
|
-
throw new errors_1.CliError('Pass --base-url <your
|
|
209
|
+
throw new errors_1.CliError('Pass --base-url <your Referalli base URL> (or set REFERALLY_BASE_URL).');
|
|
210
210
|
const baseUrl = httpsUrl(baseRaw, '--base-url');
|
|
211
211
|
const keyFile = (0, args_1.str)(v, 'referally-key-file');
|
|
212
|
-
const key = (0, args_1.str)(v, 'referally-key') ?? (keyFile ? readKeyFile(ctx, keyFile, 'the
|
|
212
|
+
const key = (0, args_1.str)(v, 'referally-key') ?? (keyFile ? readKeyFile(ctx, keyFile, 'the Referalli key') : ctx.env.REFERALLY_API_KEY?.trim());
|
|
213
213
|
if (!key) {
|
|
214
|
-
throw new errors_1.CliError('Pass your
|
|
214
|
+
throw new errors_1.CliError('Pass your Referalli merchant key with --referally-key-file <path|-> (or --referally-key, or REFERALLY_API_KEY). It is used once and never stored.');
|
|
215
215
|
}
|
|
216
216
|
const destination = (0, args_1.str)(v, 'destination');
|
|
217
217
|
if (destination)
|
|
@@ -222,7 +222,7 @@ async function connect(ctx, args) {
|
|
|
222
222
|
const instanceLive = instance.environment === 'production';
|
|
223
223
|
const mode = await referallyKeyMode(ctx, { baseUrl, key });
|
|
224
224
|
if (mode.known && mode.live !== instanceLive) {
|
|
225
|
-
const message = `the
|
|
225
|
+
const message = `the Referalli key reaches ${mode.live ? 'live' : 'test'} data but instance ${instance.id} is ` +
|
|
226
226
|
`${instance.environment ?? 'not production'}. Use ${instanceLive ? 'a live (rk_live_…)' : 'a test (rk_test_…)'} key` +
|
|
227
227
|
` for it, or pass --allow-mode-mismatch.`;
|
|
228
228
|
if (v['allow-mode-mismatch'] !== true)
|
|
@@ -230,7 +230,7 @@ async function connect(ctx, args) {
|
|
|
230
230
|
ctx.io.err(`atlas: warning: ${message.replace(/, or pass --allow-mode-mismatch\.$/, '.')}`);
|
|
231
231
|
}
|
|
232
232
|
else if (!mode.known && !instanceLive) {
|
|
233
|
-
ctx.io.err(`atlas: warning:
|
|
233
|
+
ctx.io.err(`atlas: warning: Referalli did not say whether this key reaches test data; instance ${instance.id} is ${instance.environment ?? 'not production'}.`);
|
|
234
234
|
}
|
|
235
235
|
const created = await referallyCreateConnection(ctx, {
|
|
236
236
|
baseUrl,
|
|
@@ -274,21 +274,21 @@ async function connect(ctx, args) {
|
|
|
274
274
|
saved = await http.patch('/v1/instance/referrals', patch);
|
|
275
275
|
}
|
|
276
276
|
catch (err) {
|
|
277
|
-
// The secret is shown once by
|
|
277
|
+
// The secret is shown once by Referalli: keep it so the merchant can finish by hand.
|
|
278
278
|
const saved = (0, secrets_1.deliverSecret)({
|
|
279
279
|
io: out,
|
|
280
280
|
env: ctx.env,
|
|
281
281
|
product,
|
|
282
282
|
fileName: `referally-connection-${created.id}.env`,
|
|
283
283
|
entries: { REFERALLY_CONNECTION_ID: created.id, REFERALLY_CONNECTION_SECRET: created.secret },
|
|
284
|
-
label: 'The
|
|
284
|
+
label: 'The Referalli connection secret (not saved on Atlas)',
|
|
285
285
|
force: v.force === true,
|
|
286
|
-
comment: `
|
|
286
|
+
comment: `Referalli connection ${created.id}: saving it on the Atlas instance failed. Paste it in Users → Referrals.`,
|
|
287
287
|
});
|
|
288
|
-
ctx.io.err(`atlas: the connection ${created.id} was created on
|
|
288
|
+
ctx.io.err(`atlas: the connection ${created.id} was created on Referalli, but saving it on the instance failed.${saved.path ? ` Its secret is in ${saved.path}.` : ''}`);
|
|
289
289
|
throw err;
|
|
290
290
|
}
|
|
291
|
-
// Two-way sync:
|
|
291
|
+
// Two-way sync: Referalli's referral events go to the instance's inbound URL.
|
|
292
292
|
let syncEndpoint = null;
|
|
293
293
|
const inboundUrl = saved?.inbound?.url ?? null;
|
|
294
294
|
if (v['no-sync'] !== true && inboundUrl) {
|
|
@@ -302,7 +302,7 @@ async function connect(ctx, args) {
|
|
|
302
302
|
what: 'register the referral event webhook',
|
|
303
303
|
});
|
|
304
304
|
if (typeof endpoint.id !== 'string' || typeof endpoint.secret !== 'string') {
|
|
305
|
-
throw new errors_1.CliError('
|
|
305
|
+
throw new errors_1.CliError('Referalli answered without a webhook endpoint id and secret.');
|
|
306
306
|
}
|
|
307
307
|
try {
|
|
308
308
|
await http.patch('/v1/instance/referrals', { inbound_secret: endpoint.secret, inbound_endpoint_id: endpoint.id });
|
|
@@ -315,17 +315,17 @@ async function connect(ctx, args) {
|
|
|
315
315
|
product,
|
|
316
316
|
fileName: `referally-webhook-${endpoint.id}.env`,
|
|
317
317
|
entries: { REFERALLY_WEBHOOK_ENDPOINT_ID: endpoint.id, REFERALLY_WEBHOOK_SECRET: endpoint.secret },
|
|
318
|
-
label: 'The
|
|
318
|
+
label: 'The Referalli webhook secret (not saved on Atlas)',
|
|
319
319
|
force: v.force === true,
|
|
320
|
-
comment: `
|
|
320
|
+
comment: `Referalli webhook ${endpoint.id}: saving it on the Atlas instance failed. Paste it as the inbound secret in Users → Referrals.`,
|
|
321
321
|
});
|
|
322
|
-
ctx.io.err(`atlas: warning: the webhook ${endpoint.id} was registered on
|
|
322
|
+
ctx.io.err(`atlas: warning: the webhook ${endpoint.id} was registered on Referalli, but saving its secret failed (${err instanceof Error ? err.message : String(err)}).${kept.path ? ` It is in ${kept.path}.` : ''}`);
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
catch (err) {
|
|
326
326
|
if (!(err instanceof errors_1.CliError))
|
|
327
327
|
throw err;
|
|
328
|
-
ctx.io.err(`atlas: warning: ${err.message} Referral changes on
|
|
328
|
+
ctx.io.err(`atlas: warning: ${err.message} Referral changes on Referalli won't reach Atlas until it is registered (see the docs).`);
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
summary = {
|
|
@@ -340,9 +340,9 @@ async function connect(ctx, args) {
|
|
|
340
340
|
sync_endpoint_id: syncEndpoint,
|
|
341
341
|
};
|
|
342
342
|
if (!json) {
|
|
343
|
-
out.out(`Created
|
|
343
|
+
out.out(`Created Referalli connection ${created.id} for instance ${instance.id} and saved it (the secret was not printed).`);
|
|
344
344
|
if (syncEndpoint)
|
|
345
|
-
out.out(`Referral changes on
|
|
345
|
+
out.out(`Referral changes on Referalli sync back to Atlas (webhook ${syncEndpoint}).`);
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
else {
|
|
@@ -516,7 +516,7 @@ async function status(ctx, args) {
|
|
|
516
516
|
lines.push(`Failed (7 days): ${c.failed_calls_7d ?? 0} Retrying: ${c.retrying_calls ?? 0}`);
|
|
517
517
|
const inbound = settings.inbound;
|
|
518
518
|
if (inbound?.url) {
|
|
519
|
-
//
|
|
519
|
+
// Referalli needs its own webhook secret; the Atlas protocol falls back to the signing secret.
|
|
520
520
|
const syncing = inbound.secret_set || (settings.provider === 'webhook' && settings.signing_secret_set);
|
|
521
521
|
lines.push(`Two-way sync: ${syncing ? 'on' : 'off (no inbound secret)'}${inbound.endpoint_id ? ` (webhook ${inbound.endpoint_id})` : ''}`);
|
|
522
522
|
lines.push(`Inbound URL: ${inbound.url}`);
|
package/dist/context.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { type Prompter } from './prompt';
|
|
|
9
9
|
* enforces — that the command set maps only onto namespaces that really exist,
|
|
10
10
|
* and it keeps the fake a test injects small (five namespaces, not forty).
|
|
11
11
|
*/
|
|
12
|
-
export type ManagementClient = Pick<AtlasClient, 'users' | 'organizations' | 'apiKeys' | 'webhooks' | 'sessions' | 'actions'>;
|
|
12
|
+
export type ManagementClient = Pick<AtlasClient, 'users' | 'organizations' | 'apiKeys' | 'webhooks' | 'sessions' | 'actions' | 'actionGallery' | 'actionTemplates'>;
|
|
13
13
|
/**
|
|
14
14
|
* Everything a command touches the outside world through, in one injectable
|
|
15
15
|
* bag. Production wires the real IO/config/prompt/client; every test builds
|
package/dist/help.d.ts
CHANGED
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* out of the handlers so the dispatch code stays about control flow and the
|
|
4
4
|
* copy can be read (and kept consistent) in one place.
|
|
5
5
|
*/
|
|
6
|
-
export declare const TOP_HELP = "atlas \u2014 manage an Atlas instance from the terminal\n\nUsage: atlas <command> [subcommand] [arguments] [options]\n\nCommands:\n login Authenticate and store a profile credential\n logout Remove a stored profile credential\n whoami Show the active profile and instance\n users list|get|create|delete\n orgs list|get|members\n api-keys list|create|revoke\n webhooks list|create\n sessions revoke\n config export|import Config-as-code: round-trip the instance's config\n actions list|deploy|test|logs\n init Create or complete an app's setup (idempotent); secrets to 0600 files\n preflight Check an instance's setup; non-zero exit on errors (for CI)\n env Print deploy variables: public values + secret file paths\n client rotate Rotate an OAuth client secret straight into its file\n referrals connect|backfill|status\n Connect a referral platform, backfill, check the connection\n\nGlobal options:\n --profile <name> Use a named profile (default: active, else \"default\")\n --json Emit raw JSON instead of a human table\n --limit <n> Page size for list commands that paginate\n --starting-after <cursor> Pagination cursor (from a prior page's hint)\n -h, --help Show this help, or help for a command\n -v, --version Print the version\n\nEnvironment overrides:\n ATLAS_SECRET_KEY Secret key to use instead of the stored one\n ATLAS_API_URL API URL to use instead of the stored one\n ATLAS_PLATFORM_KEY Platform key (psk_) for atlas init\n REFERALLY_BASE_URL
|
|
6
|
+
export declare const TOP_HELP = "atlas \u2014 manage an Atlas instance from the terminal\n\nUsage: atlas <command> [subcommand] [arguments] [options]\n\nCommands:\n login Authenticate and store a profile credential\n logout Remove a stored profile credential\n whoami Show the active profile and instance\n users list|get|create|delete\n orgs list|get|members\n api-keys list|create|revoke\n webhooks list|create\n sessions revoke\n config export|import Config-as-code: round-trip the instance's config\n actions list|deploy|test|logs|gallery|show|install|installs|update|publish\n init Create or complete an app's setup (idempotent); secrets to 0600 files\n preflight Check an instance's setup; non-zero exit on errors (for CI)\n env Print deploy variables: public values + secret file paths\n client rotate Rotate an OAuth client secret straight into its file\n referrals connect|backfill|status\n Connect a referral platform, backfill, check the connection\n import --from <vendor> <file>\n Move users from Auth0, Clerk, Supabase, Firebase, Cognito,\n Auth.js, WorkOS or Better Auth (resumable; --dry-run first)\n migrate code --from <vendor> [path]\n Rewrite an app's auth code onto Atlas (Clerk, Auth.js,\n Supabase, Auth0, Better Auth); a dry run with a diff unless --yes\n info [path] Print the environment for a support ticket (versions, SDKs,\n instance, API latency, preflight); secrets redacted\n mcp install Add the Atlas MCP server (docs search) to Cursor, Claude Code,\n Codex or VS Code\n\nGlobal options:\n --profile <name> Use a named profile (default: active, else \"default\")\n --json Emit raw JSON instead of a human table\n --limit <n> Page size for list commands that paginate\n --starting-after <cursor> Pagination cursor (from a prior page's hint)\n -h, --help Show this help, or help for a command\n -v, --version Print the version\n\nEnvironment overrides:\n ATLAS_SECRET_KEY Secret key to use instead of the stored one\n ATLAS_API_URL API URL to use instead of the stored one\n ATLAS_PLATFORM_KEY Platform key (psk_) for atlas init\n REFERALLY_BASE_URL Referalli base URL for atlas referrals connect\n REFERALLY_API_KEY Referalli merchant key for atlas referrals connect (never stored)\n\nSecrets: a command that receives a secret writes it to a 0600 file under\n~/.config/<product>/ and prints only the path. --print-secret prints it instead.\n\nRun 'atlas <command> --help' for command-specific help.";
|
|
7
7
|
/** Per-command help, keyed by the resource word. */
|
|
8
8
|
export declare const COMMAND_HELP: Record<string, string>;
|
package/dist/help.js
CHANGED
|
@@ -20,13 +20,23 @@ Commands:
|
|
|
20
20
|
webhooks list|create
|
|
21
21
|
sessions revoke
|
|
22
22
|
config export|import Config-as-code: round-trip the instance's config
|
|
23
|
-
actions list|deploy|test|logs
|
|
23
|
+
actions list|deploy|test|logs|gallery|show|install|installs|update|publish
|
|
24
24
|
init Create or complete an app's setup (idempotent); secrets to 0600 files
|
|
25
25
|
preflight Check an instance's setup; non-zero exit on errors (for CI)
|
|
26
26
|
env Print deploy variables: public values + secret file paths
|
|
27
27
|
client rotate Rotate an OAuth client secret straight into its file
|
|
28
28
|
referrals connect|backfill|status
|
|
29
29
|
Connect a referral platform, backfill, check the connection
|
|
30
|
+
import --from <vendor> <file>
|
|
31
|
+
Move users from Auth0, Clerk, Supabase, Firebase, Cognito,
|
|
32
|
+
Auth.js, WorkOS or Better Auth (resumable; --dry-run first)
|
|
33
|
+
migrate code --from <vendor> [path]
|
|
34
|
+
Rewrite an app's auth code onto Atlas (Clerk, Auth.js,
|
|
35
|
+
Supabase, Auth0, Better Auth); a dry run with a diff unless --yes
|
|
36
|
+
info [path] Print the environment for a support ticket (versions, SDKs,
|
|
37
|
+
instance, API latency, preflight); secrets redacted
|
|
38
|
+
mcp install Add the Atlas MCP server (docs search) to Cursor, Claude Code,
|
|
39
|
+
Codex or VS Code
|
|
30
40
|
|
|
31
41
|
Global options:
|
|
32
42
|
--profile <name> Use a named profile (default: active, else "default")
|
|
@@ -40,8 +50,8 @@ Environment overrides:
|
|
|
40
50
|
ATLAS_SECRET_KEY Secret key to use instead of the stored one
|
|
41
51
|
ATLAS_API_URL API URL to use instead of the stored one
|
|
42
52
|
ATLAS_PLATFORM_KEY Platform key (psk_) for atlas init
|
|
43
|
-
REFERALLY_BASE_URL
|
|
44
|
-
REFERALLY_API_KEY
|
|
53
|
+
REFERALLY_BASE_URL Referalli base URL for atlas referrals connect
|
|
54
|
+
REFERALLY_API_KEY Referalli merchant key for atlas referrals connect (never stored)
|
|
45
55
|
|
|
46
56
|
Secrets: a command that receives a secret writes it to a 0600 file under
|
|
47
57
|
~/.config/<product>/ and prints only the path. --print-secret prints it instead.
|
|
@@ -49,6 +59,85 @@ Secrets: a command that receives a secret writes it to a 0600 file under
|
|
|
49
59
|
Run 'atlas <command> --help' for command-specific help.`;
|
|
50
60
|
/** Per-command help, keyed by the resource word. */
|
|
51
61
|
exports.COMMAND_HELP = {
|
|
62
|
+
migrate: `atlas migrate code — rewrite an app's auth code onto Atlas
|
|
63
|
+
|
|
64
|
+
Usage:
|
|
65
|
+
atlas migrate code --from <vendor> [path] [--yes] [--json]
|
|
66
|
+
|
|
67
|
+
Vendors:
|
|
68
|
+
clerk @clerk/* SDKs
|
|
69
|
+
authjs Auth.js / NextAuth (next-auth v4 and v5, @auth/*)
|
|
70
|
+
supabase Supabase Auth (supabase.auth.*, auth-helpers, auth-ui); the
|
|
71
|
+
Supabase client itself stays for your database
|
|
72
|
+
auth0 @auth0/auth0-react, @auth0/nextjs-auth0 (v3 and v4), @auth0/auth0-spa-js
|
|
73
|
+
better-auth better-auth (client, server, Next.js handler, cookies)
|
|
74
|
+
|
|
75
|
+
What it rewrites: imports, providers, hooks, server session helpers, middleware,
|
|
76
|
+
sign-in/out calls and env var names, onto native Atlas APIs and the drop-in
|
|
77
|
+
compat helpers in @atlasauth/react/compat and @atlasauth/nextjs/compat that keep the
|
|
78
|
+
old shapes. Everything it cannot rewrite is listed with file:line and marked in
|
|
79
|
+
code with TODO(atlas-migrate).
|
|
80
|
+
|
|
81
|
+
Options:
|
|
82
|
+
--yes Write the changes (without it: a dry run printing the diff)
|
|
83
|
+
--dry-run Force a dry run even with --yes
|
|
84
|
+
--json Machine-readable result (diffs, report, packages, env)
|
|
85
|
+
|
|
86
|
+
Safe to re-run: migrated code is left as it is. Move your users with
|
|
87
|
+
'atlas import --from <vendor> <export-file>'.`,
|
|
88
|
+
info: `atlas info — the environment, for support tickets
|
|
89
|
+
|
|
90
|
+
Usage:
|
|
91
|
+
atlas info [path] [--json] [--product <name> | --instance <id>]
|
|
92
|
+
|
|
93
|
+
Prints the CLI, Node and OS versions; the project's framework and Atlas SDK
|
|
94
|
+
packages with the installed version and whether npm has a newer one; the
|
|
95
|
+
configured instance (key prefixes only, never a key) and whether the app's
|
|
96
|
+
publishable key belongs to it; API reachability and latency; and the
|
|
97
|
+
instance's preflight summary. Network steps time out after 5 s and are
|
|
98
|
+
reported, not fatal. Anything shaped like a secret is redacted.`,
|
|
99
|
+
import: `atlas import — move users from another auth provider
|
|
100
|
+
|
|
101
|
+
Usage:
|
|
102
|
+
atlas import --from <vendor> <export-file> [--dry-run]
|
|
103
|
+
|
|
104
|
+
Vendors and the file each one reads:
|
|
105
|
+
auth0 the user export job (NDJSON/JSON); --hashes <file> merges the password export
|
|
106
|
+
clerk the dashboard CSV export, or users JSON from the Backend API
|
|
107
|
+
supabase auth.users rows as CSV or JSON (with an identities array for social links)
|
|
108
|
+
firebase firebase auth:export --format=json; needs --hash-config <file>
|
|
109
|
+
cognito aws cognito-idp list-users JSON, or the user-pool CSV
|
|
110
|
+
authjs the adapter's users + accounts tables as JSON
|
|
111
|
+
workos User Management users JSON (+ organization_memberships)
|
|
112
|
+
better-auth JSON of the user, account, member and organization tables
|
|
113
|
+
|
|
114
|
+
Options:
|
|
115
|
+
--dry-run Map and summarise only; nothing is sent
|
|
116
|
+
--batch-size <n> Users per request, 1-100 (default 100)
|
|
117
|
+
--state-file <file> Progress file (default <export-file>.atlas-import.json); re-running resumes
|
|
118
|
+
--hash-config <file> Firebase hash parameters: {signer_key, salt_separator, rounds, mem_cost}
|
|
119
|
+
--hashes <file> Password hashes keyed by email, merged onto the users
|
|
120
|
+
--create-orgs Create organizations that memberships name but that do not exist yet
|
|
121
|
+
--json Machine-readable summary
|
|
122
|
+
|
|
123
|
+
Batches are sent with upsert, so a resumed or repeated run never duplicates a
|
|
124
|
+
user or a membership. Exit code 2 means some rows were reported as issues.`,
|
|
125
|
+
mcp: `atlas mcp — connect AI coding agents to Atlas
|
|
126
|
+
|
|
127
|
+
Usage:
|
|
128
|
+
atlas mcp install --cursor | --claude-code | --codex | --vscode [--dir <project>] [--global]
|
|
129
|
+
|
|
130
|
+
Adds the Atlas MCP server (npx -y @atlasauth/mcp) to the client's config so the
|
|
131
|
+
agent can search and read the Atlas docs. Servers already in the file are kept.
|
|
132
|
+
|
|
133
|
+
--cursor .cursor/mcp.json in the project (~/.cursor/mcp.json with --global)
|
|
134
|
+
--claude-code .mcp.json in the project
|
|
135
|
+
--codex ~/.codex/config.toml (or $CODEX_HOME/config.toml)
|
|
136
|
+
--vscode .vscode/mcp.json in the project
|
|
137
|
+
--dir <path> The project directory (default: the current directory)
|
|
138
|
+
|
|
139
|
+
No key is written. To also manage your instance from the agent, add
|
|
140
|
+
ATLAS_SECRET_KEY to the server's env yourself.`,
|
|
52
141
|
referrals: `atlas referrals — referral attribution
|
|
53
142
|
|
|
54
143
|
Usage:
|
|
@@ -62,15 +151,15 @@ Usage:
|
|
|
62
151
|
[--metadata-path public_metadata.referral_code] [--wait]
|
|
63
152
|
atlas referrals status
|
|
64
153
|
|
|
65
|
-
connect --provider referally creates the Atlas connection on
|
|
154
|
+
connect --provider referally creates the Atlas connection on Referalli with
|
|
66
155
|
your merchant key (bring your own key: used for this one call, never stored or
|
|
67
156
|
printed), saves the connection id and secret on the instance, and runs a ping.
|
|
68
157
|
It requires v2 signatures on the connection from the start and registers a
|
|
69
|
-
|
|
158
|
+
Referalli webhook that sends referral changes (voided, approved, reassigned…)
|
|
70
159
|
back to Atlas; --no-sync skips the webhook. A test key (rk_test_) belongs with a
|
|
71
160
|
development instance and a live key with production: a mismatch is refused
|
|
72
161
|
unless --allow-mode-mismatch. --auto-enroll and --destination are passed to
|
|
73
|
-
|
|
162
|
+
Referalli. --enable turns referrals on when the ping passes. Prefer --referally-key-file (or
|
|
74
163
|
REFERALLY_API_KEY) to --referally-key: a key on the command line can end up in
|
|
75
164
|
your shell history.
|
|
76
165
|
|
|
@@ -211,9 +300,24 @@ Usage:
|
|
|
211
300
|
atlas actions test <file> [--trigger <t>] [--json]
|
|
212
301
|
atlas actions logs <action-id> [--json]
|
|
213
302
|
|
|
303
|
+
atlas actions gallery [query] [--trigger <t>] [--json]
|
|
304
|
+
atlas actions show <slug> [--json]
|
|
305
|
+
atlas actions install <slug> [--name <n>] [--secrets-file <dotenv>] [--json]
|
|
306
|
+
atlas actions installs [--json]
|
|
307
|
+
atlas actions update <action-id> [--secrets-file <dotenv>] [--json]
|
|
308
|
+
atlas actions publish <template.json> [--draft] [--json]
|
|
309
|
+
|
|
214
310
|
deploy uploads the file's code verbatim as a new action (created, not yet bound).
|
|
215
311
|
test runs the file OFFLINE through the @atlasauth/actions harness — no network,
|
|
216
312
|
no real users — and exits non-zero if the action throws. logs explains where an
|
|
217
|
-
action's execution history is recorded (Atlas has no live log-stream endpoint)
|
|
313
|
+
action's execution history is recorded (Atlas has no live log-stream endpoint).
|
|
314
|
+
|
|
315
|
+
The gallery: gallery lists first-party and community templates; install copies
|
|
316
|
+
one into the instance as a DISABLED, unbound action (secret values come from a
|
|
317
|
+
dotenv file, never argv); installs shows which actions came from the gallery
|
|
318
|
+
and whether a newer version exists; update moves one to the latest version.
|
|
319
|
+
publish reads a manifest {slug, name, author_name, summary, description,
|
|
320
|
+
category, tags, trigger, license, version, code_file, secrets, changelog},
|
|
321
|
+
creates the template or adds the version, and submits it for review.`,
|
|
218
322
|
};
|
|
219
323
|
//# sourceMappingURL=help.js.map
|
package/dist/help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEU,QAAA,QAAQ,GAAG
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEU,QAAA,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wDAkDgC,CAAC;AAEzD,oDAAoD;AACvC,QAAA,YAAY,GAA2B;IAClD,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;8CAyBmC;IAC5C,IAAI,EAAE;;;;;;;;;;gEAUwD;IAC9D,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;2EAyBiE;IACzE,GAAG,EAAE;;;;;;;;;;;;;;;+CAewC;IAC7C,SAAS,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEAmC4D;IAEvE,KAAK,EAAE;;;;;;mCAM0B;IAEjC,MAAM,EAAE;;uCAE6B;IAErC,MAAM,EAAE;;gDAEsC;IAE9C,KAAK,EAAE;;;;;;wCAM+B;IAEtC,IAAI,EAAE;;;;;uCAK+B;IAErC,UAAU,EAAE;;;;;;8CAMgC;IAE5C,QAAQ,EAAE;;;;;;0EAM8D;IAExE,IAAI,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;yEA4BiE;IAEvE,SAAS,EAAE;;;;;;oDAMuC;IAElD,GAAG,EAAE;;;;;;;qDAO8C;IAEnD,MAAM,EAAE;;;;;;;;wDAQ8C;IAEtD,QAAQ,EAAE;;;8CAGkC;IAE5C,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;8EAoBoE;IAC5E,OAAO,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;qEA0B0D;CACpE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small RFC 4180 CSV reader for vendor exports: quoted fields, doubled quotes,
|
|
3
|
+
* embedded commas and newlines, CRLF. Returns one object per data row keyed by
|
|
4
|
+
* the header row. No dependency, because the CLI ships none it does not need.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseCsv(text: string): Array<Record<string, string>>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseCsv = parseCsv;
|
|
4
|
+
/**
|
|
5
|
+
* A small RFC 4180 CSV reader for vendor exports: quoted fields, doubled quotes,
|
|
6
|
+
* embedded commas and newlines, CRLF. Returns one object per data row keyed by
|
|
7
|
+
* the header row. No dependency, because the CLI ships none it does not need.
|
|
8
|
+
*/
|
|
9
|
+
function parseCsv(text) {
|
|
10
|
+
const rows = [];
|
|
11
|
+
let row = [];
|
|
12
|
+
let field = '';
|
|
13
|
+
let quoted = false;
|
|
14
|
+
const src = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
15
|
+
for (let i = 0; i < src.length; i++) {
|
|
16
|
+
const c = src[i];
|
|
17
|
+
if (quoted) {
|
|
18
|
+
if (c === '"') {
|
|
19
|
+
if (src[i + 1] === '"') {
|
|
20
|
+
field += '"';
|
|
21
|
+
i++;
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
quoted = false;
|
|
25
|
+
}
|
|
26
|
+
else
|
|
27
|
+
field += c;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (c === '"')
|
|
31
|
+
quoted = true;
|
|
32
|
+
else if (c === ',') {
|
|
33
|
+
row.push(field);
|
|
34
|
+
field = '';
|
|
35
|
+
}
|
|
36
|
+
else if (c === '\n' || c === '\r') {
|
|
37
|
+
if (c === '\r' && src[i + 1] === '\n')
|
|
38
|
+
i++;
|
|
39
|
+
row.push(field);
|
|
40
|
+
field = '';
|
|
41
|
+
if (row.some((v) => v !== ''))
|
|
42
|
+
rows.push(row);
|
|
43
|
+
row = [];
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
field += c;
|
|
47
|
+
}
|
|
48
|
+
row.push(field);
|
|
49
|
+
if (row.some((v) => v !== ''))
|
|
50
|
+
rows.push(row);
|
|
51
|
+
const [header, ...data] = rows;
|
|
52
|
+
if (!header)
|
|
53
|
+
return [];
|
|
54
|
+
const keys = header.map((h) => h.trim());
|
|
55
|
+
return data.map((cells) => {
|
|
56
|
+
const out = {};
|
|
57
|
+
keys.forEach((k, idx) => {
|
|
58
|
+
out[k] = cells[idx] ?? '';
|
|
59
|
+
});
|
|
60
|
+
return out;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=csv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../../src/import/csv.ts"],"names":[],"mappings":";;AAKA,4BA2CC;AAhDD;;;;GAIG;AACH,SAAgB,QAAQ,CAAC,IAAY;IACnC,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,IAAI,GAAG,GAAa,EAAE,CAAC;IACvB,IAAI,KAAK,GAAG,EAAE,CAAC;IACf,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEjE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC,CAAE,CAAC;QAClB,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;gBACd,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;oBACvB,KAAK,IAAI,GAAG,CAAC;oBACb,CAAC,EAAE,CAAC;gBACN,CAAC;;oBAAM,MAAM,GAAG,KAAK,CAAC;YACxB,CAAC;;gBAAM,KAAK,IAAI,CAAC,CAAC;YAClB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,KAAK,GAAG;YAAE,MAAM,GAAG,IAAI,CAAC;aACxB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YACnB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChB,KAAK,GAAG,EAAE,CAAC;QACb,CAAC;aAAM,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACpC,IAAI,CAAC,KAAK,IAAI,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI;gBAAE,CAAC,EAAE,CAAC;YAC3C,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChB,KAAK,GAAG,EAAE,CAAC;YACX,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9C,GAAG,GAAG,EAAE,CAAC;QACX,CAAC;;YAAM,KAAK,IAAI,CAAC,CAAC;IACpB,CAAC;IACD,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAE9C,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxB,MAAM,GAAG,GAA2B,EAAE,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE;YACtB,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,CAAC;IACb,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendor export → Atlas bulk-import rows. Each mapper reads the file format the
|
|
3
|
+
* vendor's own export produces and returns the rows `POST /v1/user_imports`
|
|
4
|
+
* accepts, plus warnings for what could not be carried over. Pure functions of
|
|
5
|
+
* the parsed file, so every mapping is tested from a fixture with no network.
|
|
6
|
+
*/
|
|
7
|
+
export declare const VENDORS: readonly ["auth0", "clerk", "supabase", "firebase", "cognito", "authjs", "workos", "better-auth"];
|
|
8
|
+
export type Vendor = (typeof VENDORS)[number];
|
|
9
|
+
export interface ImportMembershipRow {
|
|
10
|
+
organization_id?: string;
|
|
11
|
+
organization_external_id?: string;
|
|
12
|
+
organization_slug?: string;
|
|
13
|
+
organization_name?: string;
|
|
14
|
+
role?: string;
|
|
15
|
+
}
|
|
16
|
+
/** One row of the Atlas import body. Only fields with a value are set. */
|
|
17
|
+
export interface ImportRow {
|
|
18
|
+
email_address: string;
|
|
19
|
+
email_verified?: boolean;
|
|
20
|
+
first_name?: string;
|
|
21
|
+
last_name?: string;
|
|
22
|
+
username?: string;
|
|
23
|
+
external_id?: string;
|
|
24
|
+
created_at?: string | number;
|
|
25
|
+
password_hash?: string;
|
|
26
|
+
password_salt?: string;
|
|
27
|
+
password_hasher?: string;
|
|
28
|
+
phone_numbers?: Array<{
|
|
29
|
+
phone_number: string;
|
|
30
|
+
verified?: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
external_accounts?: Array<{
|
|
33
|
+
provider: string;
|
|
34
|
+
provider_user_id: string;
|
|
35
|
+
email?: string;
|
|
36
|
+
}>;
|
|
37
|
+
organization_memberships?: ImportMembershipRow[];
|
|
38
|
+
public_metadata?: Record<string, unknown>;
|
|
39
|
+
private_metadata?: Record<string, unknown>;
|
|
40
|
+
unsafe_metadata?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export interface MappedExport {
|
|
43
|
+
rows: ImportRow[];
|
|
44
|
+
/** Human-readable notes: records skipped, data that cannot move, follow-ups. */
|
|
45
|
+
warnings: string[];
|
|
46
|
+
}
|
|
47
|
+
/** Parse the file text: a JSON document, newline-delimited JSON, or CSV. */
|
|
48
|
+
export declare function parseExport(text: string, fileName?: string): unknown;
|
|
49
|
+
export declare function atlasProvider(raw: string): string;
|
|
50
|
+
/**
|
|
51
|
+
* Map a parsed vendor export. `hashes` is an optional second file of password
|
|
52
|
+
* hashes keyed by email (Auth0's support export, WorkOS's, or any `{email,
|
|
53
|
+
* password_hash}` list), merged onto the rows.
|
|
54
|
+
*/
|
|
55
|
+
export declare function mapExport(vendor: Vendor, doc: unknown, hashes?: unknown): MappedExport;
|
|
56
|
+
/** A one-screen summary of what an import would do (the dry run). */
|
|
57
|
+
export declare function summarize(rows: ImportRow[]): Record<string, number>;
|