@neat.is/core 0.4.27 → 0.4.28-dev.20260708
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/{chunk-A3322JYS.js → chunk-CFDPIMRP.js} +13 -9
- package/dist/chunk-CFDPIMRP.js.map +1 -0
- package/dist/{chunk-UV5WSM7M.js → chunk-I72HTUOG.js} +2 -2
- package/dist/chunk-NY5Q6NE2.js +2566 -0
- package/dist/chunk-NY5Q6NE2.js.map +1 -0
- package/dist/{chunk-QM6BMPVJ.js → chunk-O4MRDWD7.js} +125 -12
- package/dist/chunk-O4MRDWD7.js.map +1 -0
- package/dist/cli.cjs +2454 -378
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +358 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1744 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +4 -4
- package/dist/neatd.cjs +1760 -116
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +3 -3
- package/dist/{otel-grpc-ET5Z6KI6.js → otel-grpc-IDMIH6ZY.js} +3 -3
- package/dist/server.cjs +133 -18
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +3 -3
- package/package.json +3 -3
- package/dist/chunk-A3322JYS.js.map +0 -1
- package/dist/chunk-QM6BMPVJ.js.map +0 -1
- package/dist/chunk-XV4D7A3Z.js +0 -907
- package/dist/chunk-XV4D7A3Z.js.map +0 -1
- /package/dist/{chunk-UV5WSM7M.js.map → chunk-I72HTUOG.js.map} +0 -0
- /package/dist/{otel-grpc-ET5Z6KI6.js.map → otel-grpc-IDMIH6ZY.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
PROVIDER_DISPATCH,
|
|
4
|
+
autoSlugConnectorId,
|
|
3
5
|
clearDaemonRecord,
|
|
6
|
+
describeCredential,
|
|
7
|
+
getProviderDispatch,
|
|
8
|
+
isEnvRef,
|
|
4
9
|
portFromListenAddress,
|
|
10
|
+
readConnectorsConfig,
|
|
5
11
|
readDaemonRecord,
|
|
12
|
+
removeConnectorEntry,
|
|
6
13
|
resolveHost,
|
|
7
14
|
resolveNeatVersion,
|
|
15
|
+
upsertConnectorEntry,
|
|
16
|
+
validateConnectorEntry,
|
|
8
17
|
writeDaemonRecord
|
|
9
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-NY5Q6NE2.js";
|
|
10
19
|
import {
|
|
11
20
|
buildSearchIndex
|
|
12
21
|
} from "./chunk-BIY46Q6U.js";
|
|
@@ -59,10 +68,10 @@ import {
|
|
|
59
68
|
signalDaemonStop,
|
|
60
69
|
startPersistLoop,
|
|
61
70
|
startStalenessLoop
|
|
62
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-O4MRDWD7.js";
|
|
63
72
|
import {
|
|
64
73
|
startOtelGrpcReceiver
|
|
65
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-I72HTUOG.js";
|
|
66
75
|
import {
|
|
67
76
|
__dirname,
|
|
68
77
|
__require,
|
|
@@ -70,7 +79,7 @@ import {
|
|
|
70
79
|
buildOtelReceiver,
|
|
71
80
|
listenSteppingOtlp,
|
|
72
81
|
readAuthEnv
|
|
73
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-CFDPIMRP.js";
|
|
74
83
|
|
|
75
84
|
// src/cli.ts
|
|
76
85
|
import path9 from "path";
|
|
@@ -3497,6 +3506,335 @@ function printSummary(result, graph, dashboardUrl, daemonLog) {
|
|
|
3497
3506
|
}
|
|
3498
3507
|
}
|
|
3499
3508
|
|
|
3509
|
+
// src/connector-cli.ts
|
|
3510
|
+
import readline2 from "readline";
|
|
3511
|
+
async function defaultPrompt(question) {
|
|
3512
|
+
if (!process.stdin.isTTY) return "";
|
|
3513
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
3514
|
+
try {
|
|
3515
|
+
return await new Promise((resolve) => rl.question(`${question} `, resolve));
|
|
3516
|
+
} finally {
|
|
3517
|
+
rl.close();
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
function resolveDeps(deps) {
|
|
3521
|
+
return {
|
|
3522
|
+
home: deps.home,
|
|
3523
|
+
env: deps.env ?? process.env,
|
|
3524
|
+
...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {},
|
|
3525
|
+
prompt: deps.prompt ?? defaultPrompt,
|
|
3526
|
+
interactive: deps.interactive ?? Boolean(process.stdin.isTTY),
|
|
3527
|
+
out: deps.out ?? ((l) => console.log(l)),
|
|
3528
|
+
err: deps.err ?? ((l) => console.error(l))
|
|
3529
|
+
};
|
|
3530
|
+
}
|
|
3531
|
+
function kebabToCamel(name) {
|
|
3532
|
+
return name.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
3533
|
+
}
|
|
3534
|
+
function coerceFieldValue(name, raw) {
|
|
3535
|
+
const t = raw.trim();
|
|
3536
|
+
if (t.startsWith("{") || t.startsWith("[")) {
|
|
3537
|
+
try {
|
|
3538
|
+
return JSON.parse(t);
|
|
3539
|
+
} catch {
|
|
3540
|
+
return raw;
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3543
|
+
if (/Ms$/.test(name) && /^\d+$/.test(t)) return Number(t);
|
|
3544
|
+
return raw;
|
|
3545
|
+
}
|
|
3546
|
+
function parseConnectorArgs(args) {
|
|
3547
|
+
const out = {
|
|
3548
|
+
subcommand: "",
|
|
3549
|
+
positional: [],
|
|
3550
|
+
skipValidate: false,
|
|
3551
|
+
plaintext: false,
|
|
3552
|
+
fields: {}
|
|
3553
|
+
};
|
|
3554
|
+
const positional = [];
|
|
3555
|
+
for (let i = 0; i < args.length; i++) {
|
|
3556
|
+
const arg = args[i];
|
|
3557
|
+
if (arg === "--skip-validate") {
|
|
3558
|
+
out.skipValidate = true;
|
|
3559
|
+
continue;
|
|
3560
|
+
}
|
|
3561
|
+
if (arg === "--plaintext") {
|
|
3562
|
+
out.plaintext = true;
|
|
3563
|
+
continue;
|
|
3564
|
+
}
|
|
3565
|
+
if (arg.startsWith("--")) {
|
|
3566
|
+
let flag = arg;
|
|
3567
|
+
let value;
|
|
3568
|
+
const eq = arg.indexOf("=");
|
|
3569
|
+
if (eq >= 0) {
|
|
3570
|
+
flag = arg.slice(0, eq);
|
|
3571
|
+
value = arg.slice(eq + 1);
|
|
3572
|
+
} else {
|
|
3573
|
+
const next = args[i + 1];
|
|
3574
|
+
if (next === void 0 || next.startsWith("--")) {
|
|
3575
|
+
return { ok: false, error: `${flag} requires a value` };
|
|
3576
|
+
}
|
|
3577
|
+
value = next;
|
|
3578
|
+
i++;
|
|
3579
|
+
}
|
|
3580
|
+
const name = flag.slice(2);
|
|
3581
|
+
if (name === "project") {
|
|
3582
|
+
out.project = value;
|
|
3583
|
+
continue;
|
|
3584
|
+
}
|
|
3585
|
+
if (name === "credential" || name === "token") {
|
|
3586
|
+
out.credential = value;
|
|
3587
|
+
continue;
|
|
3588
|
+
}
|
|
3589
|
+
if (name === "id") {
|
|
3590
|
+
out.id = value;
|
|
3591
|
+
continue;
|
|
3592
|
+
}
|
|
3593
|
+
const field = kebabToCamel(name);
|
|
3594
|
+
out.fields[field] = coerceFieldValue(field, value);
|
|
3595
|
+
continue;
|
|
3596
|
+
}
|
|
3597
|
+
positional.push(arg);
|
|
3598
|
+
}
|
|
3599
|
+
out.subcommand = positional[0] ?? "";
|
|
3600
|
+
out.positional = positional.slice(1);
|
|
3601
|
+
return { ok: true, value: out };
|
|
3602
|
+
}
|
|
3603
|
+
async function buildCredentialRef(dispatch, args, deps) {
|
|
3604
|
+
const hint = "an env-var reference like $PROVIDER_TOKEN is recommended (stored as a pointer, not the secret); a literal value is stored as-is";
|
|
3605
|
+
if (dispatch.requiredCredentialFields.length <= 1) {
|
|
3606
|
+
const key = dispatch.primaryCredentialKey;
|
|
3607
|
+
let value = args.credential;
|
|
3608
|
+
if (value === void 0 && typeof args.fields[key] === "string") {
|
|
3609
|
+
value = args.fields[key];
|
|
3610
|
+
}
|
|
3611
|
+
if (value === void 0 && deps.interactive) {
|
|
3612
|
+
value = await deps.prompt(`Credential for ${dispatch.provider} (${hint}):`);
|
|
3613
|
+
}
|
|
3614
|
+
return (value ?? "").trim();
|
|
3615
|
+
}
|
|
3616
|
+
const out = {};
|
|
3617
|
+
for (const f of dispatch.requiredCredentialFields) {
|
|
3618
|
+
let value;
|
|
3619
|
+
if (typeof args.fields[f] === "string") value = args.fields[f];
|
|
3620
|
+
else if (f === dispatch.primaryCredentialKey && args.credential !== void 0) value = args.credential;
|
|
3621
|
+
if (value === void 0 && deps.interactive) {
|
|
3622
|
+
value = await deps.prompt(`Credential field "${f}" for ${dispatch.provider} (${hint}):`);
|
|
3623
|
+
}
|
|
3624
|
+
out[f] = (value ?? "").trim();
|
|
3625
|
+
}
|
|
3626
|
+
return out;
|
|
3627
|
+
}
|
|
3628
|
+
function credentialComplete(dispatch, ref) {
|
|
3629
|
+
if (typeof ref === "string") {
|
|
3630
|
+
return ref.length > 0 ? [] : [dispatch.primaryCredentialKey];
|
|
3631
|
+
}
|
|
3632
|
+
return dispatch.requiredCredentialFields.filter((f) => !ref[f] || ref[f].length === 0);
|
|
3633
|
+
}
|
|
3634
|
+
function plaintextFields(ref) {
|
|
3635
|
+
if (typeof ref === "string") return isEnvRef(ref) ? [] : ["credential"];
|
|
3636
|
+
return Object.entries(ref).filter(([, v]) => !isEnvRef(v)).map(([k]) => k);
|
|
3637
|
+
}
|
|
3638
|
+
async function connectorAdd(args, deps) {
|
|
3639
|
+
let provider = args.positional[0];
|
|
3640
|
+
if (!provider && deps.interactive) {
|
|
3641
|
+
provider = (await deps.prompt(`Provider (${Object.keys(PROVIDER_DISPATCH).sort().join(", ")}):`)).trim();
|
|
3642
|
+
}
|
|
3643
|
+
if (!provider) {
|
|
3644
|
+
deps.err("neat connector add: a provider is required (e.g. `neat connector add supabase`)");
|
|
3645
|
+
return 2;
|
|
3646
|
+
}
|
|
3647
|
+
const dispatch = getProviderDispatch(provider);
|
|
3648
|
+
if (!dispatch) {
|
|
3649
|
+
deps.err(
|
|
3650
|
+
`neat connector add: unknown provider "${provider}". known providers: ${Object.keys(PROVIDER_DISPATCH).sort().join(", ")}`
|
|
3651
|
+
);
|
|
3652
|
+
return 2;
|
|
3653
|
+
}
|
|
3654
|
+
let project = args.project;
|
|
3655
|
+
if (project === void 0 && deps.interactive) {
|
|
3656
|
+
const answer = (await deps.prompt("Project (blank = the project the daemon is bootstrapping):")).trim();
|
|
3657
|
+
if (answer.length > 0) project = answer;
|
|
3658
|
+
}
|
|
3659
|
+
const credential = await buildCredentialRef(dispatch, args, deps);
|
|
3660
|
+
const options = { ...args.fields };
|
|
3661
|
+
for (const k of dispatch.requiredCredentialFields) delete options[k];
|
|
3662
|
+
for (const field of dispatch.requiredOptionFields) {
|
|
3663
|
+
if (field in options) continue;
|
|
3664
|
+
if (!deps.interactive) continue;
|
|
3665
|
+
const answer = (await deps.prompt(`Option "${field}" for ${provider}:`)).trim();
|
|
3666
|
+
if (answer.length > 0) options[field] = coerceFieldValue(field, answer);
|
|
3667
|
+
}
|
|
3668
|
+
const missingCred = credentialComplete(dispatch, credential);
|
|
3669
|
+
if (missingCred.length > 0) {
|
|
3670
|
+
deps.err(
|
|
3671
|
+
`neat connector add: credential is incomplete \u2014 missing ${missingCred.join(", ")}. Pass it as a flag (e.g. \`--token $PROVIDER_TOKEN\`) or run interactively.`
|
|
3672
|
+
);
|
|
3673
|
+
return 2;
|
|
3674
|
+
}
|
|
3675
|
+
const missingOpts = dispatch.requiredOptionFields.filter((f) => !(f in options));
|
|
3676
|
+
if (missingOpts.length > 0) {
|
|
3677
|
+
deps.err(
|
|
3678
|
+
`neat connector add: missing required option(s) for ${provider}: ${missingOpts.join(", ")}. Pass e.g. \`--${missingOpts[0].replace(/([A-Z])/g, "-$1").toLowerCase()} <value>\`.`
|
|
3679
|
+
);
|
|
3680
|
+
return 2;
|
|
3681
|
+
}
|
|
3682
|
+
const existing = await readConnectorsConfig(deps.home);
|
|
3683
|
+
const existingIds = new Set(existing.connectors.map((c) => c.id));
|
|
3684
|
+
const id = args.id ?? autoSlugConnectorId(provider, project, existingIds);
|
|
3685
|
+
const entry2 = {
|
|
3686
|
+
id,
|
|
3687
|
+
provider,
|
|
3688
|
+
...project ? { project } : {},
|
|
3689
|
+
credential,
|
|
3690
|
+
...Object.keys(options).length > 0 ? { options } : {}
|
|
3691
|
+
};
|
|
3692
|
+
if (!args.skipValidate) {
|
|
3693
|
+
const outcome = await validateConnectorEntry(entry2, deps.env, deps.fetchImpl);
|
|
3694
|
+
const code = reportPreWriteValidation(outcome, deps);
|
|
3695
|
+
if (code !== 0) return code;
|
|
3696
|
+
}
|
|
3697
|
+
const plaintext = plaintextFields(credential);
|
|
3698
|
+
if (plaintext.length > 0 && !args.plaintext) {
|
|
3699
|
+
deps.err(
|
|
3700
|
+
`neat connector add: storing a literal secret at rest for ${plaintext.join(", ")} \u2014 prefer an env-var reference like \`$PROVIDER_TOKEN\` (pass --plaintext to silence this).`
|
|
3701
|
+
);
|
|
3702
|
+
}
|
|
3703
|
+
const { replaced } = await upsertConnectorEntry(entry2, deps.home);
|
|
3704
|
+
const verb = replaced ? "updated" : "added";
|
|
3705
|
+
const where = project ? `project "${project}"` : "the bootstrapping project";
|
|
3706
|
+
deps.out(`${verb} connector "${id}" (${provider}) for ${where}.`);
|
|
3707
|
+
if (args.skipValidate) {
|
|
3708
|
+
deps.out("skipped validation (--skip-validate) \u2014 the credential is checked at the next daemon poll.");
|
|
3709
|
+
}
|
|
3710
|
+
deps.out("Restart the project's daemon (or start it) to begin polling this connector.");
|
|
3711
|
+
return 0;
|
|
3712
|
+
}
|
|
3713
|
+
function reportPreWriteValidation(outcome, deps) {
|
|
3714
|
+
switch (outcome.status) {
|
|
3715
|
+
case "ok":
|
|
3716
|
+
deps.out("credential validated against the provider.");
|
|
3717
|
+
return 0;
|
|
3718
|
+
case "unset-env":
|
|
3719
|
+
deps.err(
|
|
3720
|
+
`neat connector add: ${outcome.reason}. Export the variable before adding, or pass --skip-validate to add now and set it before the daemon runs.`
|
|
3721
|
+
);
|
|
3722
|
+
return 1;
|
|
3723
|
+
case "rejected":
|
|
3724
|
+
deps.err(
|
|
3725
|
+
`neat connector add: the provider rejected the credential \u2014 ${outcome.reason}. Nothing was written. Fix the credential, or pass --skip-validate to store it anyway.`
|
|
3726
|
+
);
|
|
3727
|
+
return 1;
|
|
3728
|
+
case "missing-field":
|
|
3729
|
+
deps.err(`neat connector add: ${outcome.reason}.`);
|
|
3730
|
+
return 2;
|
|
3731
|
+
case "unknown-provider":
|
|
3732
|
+
deps.err(`neat connector add: ${outcome.reason}.`);
|
|
3733
|
+
return 2;
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
async function connectorList(deps, filterProject) {
|
|
3737
|
+
const config = await readConnectorsConfig(deps.home);
|
|
3738
|
+
let entries = config.connectors;
|
|
3739
|
+
if (filterProject) entries = entries.filter((e) => e.project === filterProject);
|
|
3740
|
+
if (entries.length === 0) {
|
|
3741
|
+
deps.out(
|
|
3742
|
+
filterProject ? `no connectors configured for project "${filterProject}".` : "no connectors configured. run `neat connector add <provider>` to add one."
|
|
3743
|
+
);
|
|
3744
|
+
return 0;
|
|
3745
|
+
}
|
|
3746
|
+
deps.out("id provider project credential");
|
|
3747
|
+
for (const e of entries) {
|
|
3748
|
+
const project = e.project ?? "(bootstrapping project)";
|
|
3749
|
+
const credential = describeCredential(e.credential, deps.env).map((c) => {
|
|
3750
|
+
const status = c.status ? ` (${c.status})` : c.kind === "plaintext" ? " (plaintext)" : "";
|
|
3751
|
+
return c.field ? `${c.field}=${c.display}${status}` : `${c.display}${status}`;
|
|
3752
|
+
}).join(", ");
|
|
3753
|
+
deps.out(`${e.id} ${e.provider} ${project} ${credential}`);
|
|
3754
|
+
}
|
|
3755
|
+
return 0;
|
|
3756
|
+
}
|
|
3757
|
+
async function connectorRemove(deps, id) {
|
|
3758
|
+
if (!id) {
|
|
3759
|
+
deps.err("neat connector remove: missing <id>. run `neat connector list` to see configured ids.");
|
|
3760
|
+
return 2;
|
|
3761
|
+
}
|
|
3762
|
+
const removed = await removeConnectorEntry(id, deps.home);
|
|
3763
|
+
if (!removed) {
|
|
3764
|
+
deps.err(`neat connector remove: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
3765
|
+
return 1;
|
|
3766
|
+
}
|
|
3767
|
+
deps.out(`removed connector "${id}" (${removed.provider}).`);
|
|
3768
|
+
return 0;
|
|
3769
|
+
}
|
|
3770
|
+
async function connectorTest(deps, id) {
|
|
3771
|
+
if (!id) {
|
|
3772
|
+
deps.err("neat connector test: missing <id>. run `neat connector list` to see configured ids.");
|
|
3773
|
+
return 2;
|
|
3774
|
+
}
|
|
3775
|
+
const config = await readConnectorsConfig(deps.home);
|
|
3776
|
+
const entry2 = config.connectors.find((c) => c.id === id);
|
|
3777
|
+
if (!entry2) {
|
|
3778
|
+
deps.err(`neat connector test: no connector with id "${id}". run \`neat connector list\` to see configured ids.`);
|
|
3779
|
+
return 1;
|
|
3780
|
+
}
|
|
3781
|
+
const outcome = await validateConnectorEntry(entry2, deps.env, deps.fetchImpl);
|
|
3782
|
+
switch (outcome.status) {
|
|
3783
|
+
case "ok":
|
|
3784
|
+
deps.out(`ok: "${id}" (${entry2.provider}) authenticated against the provider.`);
|
|
3785
|
+
return 0;
|
|
3786
|
+
case "unset-env":
|
|
3787
|
+
deps.err(`unset: "${id}" (${entry2.provider}) \u2014 ${outcome.reason}. Export the variable so the daemon can resolve it.`);
|
|
3788
|
+
return 1;
|
|
3789
|
+
case "rejected":
|
|
3790
|
+
deps.err(`rejected: "${id}" (${entry2.provider}) \u2014 ${outcome.reason}.`);
|
|
3791
|
+
return 1;
|
|
3792
|
+
case "missing-field":
|
|
3793
|
+
deps.err(`incomplete: "${id}" (${entry2.provider}) \u2014 ${outcome.reason}.`);
|
|
3794
|
+
return 2;
|
|
3795
|
+
case "unknown-provider":
|
|
3796
|
+
deps.err(`unknown provider: "${id}" \u2014 ${outcome.reason}.`);
|
|
3797
|
+
return 2;
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
function printConnectorUsage(write) {
|
|
3801
|
+
write("usage: neat connector <add|list|remove|test> [args]");
|
|
3802
|
+
write(" add <provider> add a connector; validates the credential first (--skip-validate to skip)");
|
|
3803
|
+
write(" flags: --project <name> --credential/--token <$VAR|value> --id <id>");
|
|
3804
|
+
write(" --<option> <value> (provider-specific) --plaintext --skip-validate");
|
|
3805
|
+
write(" list list configured connectors (credentials shown redacted)");
|
|
3806
|
+
write(" flags: --project <name>");
|
|
3807
|
+
write(" remove <id> remove a connector by id");
|
|
3808
|
+
write(" test <id> re-run the credential validation for an existing connector");
|
|
3809
|
+
}
|
|
3810
|
+
async function runConnectorCommand(rawArgs, deps = {}) {
|
|
3811
|
+
const resolved = resolveDeps(deps);
|
|
3812
|
+
const parsed = parseConnectorArgs(rawArgs);
|
|
3813
|
+
if (!parsed.ok) {
|
|
3814
|
+
resolved.err(`neat connector: ${parsed.error}`);
|
|
3815
|
+
return 2;
|
|
3816
|
+
}
|
|
3817
|
+
const a = parsed.value;
|
|
3818
|
+
switch (a.subcommand) {
|
|
3819
|
+
case "add":
|
|
3820
|
+
return connectorAdd(a, resolved);
|
|
3821
|
+
case "list":
|
|
3822
|
+
return connectorList(resolved, a.project);
|
|
3823
|
+
case "remove":
|
|
3824
|
+
return connectorRemove(resolved, a.positional[0]);
|
|
3825
|
+
case "test":
|
|
3826
|
+
return connectorTest(resolved, a.positional[0]);
|
|
3827
|
+
case "":
|
|
3828
|
+
resolved.err("neat connector: missing subcommand.");
|
|
3829
|
+
printConnectorUsage(resolved.err);
|
|
3830
|
+
return 2;
|
|
3831
|
+
default:
|
|
3832
|
+
resolved.err(`neat connector: unknown subcommand "${a.subcommand}".`);
|
|
3833
|
+
printConnectorUsage(resolved.err);
|
|
3834
|
+
return 2;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3500
3838
|
// src/cli-verbs.ts
|
|
3501
3839
|
import path8 from "path";
|
|
3502
3840
|
|
|
@@ -4251,6 +4589,17 @@ function usage() {
|
|
|
4251
4589
|
console.log(" --dry-run run extraction in-memory; do not write");
|
|
4252
4590
|
console.log(" --no-instrument skip the SDK install apply step");
|
|
4253
4591
|
console.log(" --json emit the delta summary as JSON");
|
|
4592
|
+
console.log(" connector Configure pull-based OBSERVED connectors (supabase, railway,");
|
|
4593
|
+
console.log(" firebase, cloudflare). Subcommands:");
|
|
4594
|
+
console.log(" add <provider> add a connector; validates the credential");
|
|
4595
|
+
console.log(" against the provider first (--skip-validate to skip)");
|
|
4596
|
+
console.log(" flags: --project <name>, --credential/--token <$VAR|value>,");
|
|
4597
|
+
console.log(" --id <id>, --<option> <value>, --plaintext, --skip-validate");
|
|
4598
|
+
console.log(" list list configured connectors (credentials redacted)");
|
|
4599
|
+
console.log(" remove <id> remove a connector by id");
|
|
4600
|
+
console.log(" test <id> re-check an existing connector's credential");
|
|
4601
|
+
console.log(" Credentials default to an env-var reference ($VAR) resolved at");
|
|
4602
|
+
console.log(" run time; the config file is written owner-only (0600).");
|
|
4254
4603
|
console.log("");
|
|
4255
4604
|
console.log("query commands (mirror the MCP tools, ADR-050):");
|
|
4256
4605
|
console.log(" root-cause <node-id> Walk inbound edges to find what broke first.");
|
|
@@ -4674,6 +5023,11 @@ async function main() {
|
|
|
4674
5023
|
printVersion();
|
|
4675
5024
|
process.exit(0);
|
|
4676
5025
|
}
|
|
5026
|
+
if (cmd0 === "connector") {
|
|
5027
|
+
const code = await runConnectorCommand(argv.slice(1));
|
|
5028
|
+
if (code !== 0) process.exit(code);
|
|
5029
|
+
return;
|
|
5030
|
+
}
|
|
4677
5031
|
const argvParsed = parseArgs(argv);
|
|
4678
5032
|
if (argvParsed.positional.length === 0) {
|
|
4679
5033
|
const orchestratorCode2 = await tryOrchestrator(process.cwd(), argvParsed);
|