@abloatai/cli 0.45.0 → 0.46.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/dist/cli.cjs +110 -19
- package/package.json +3 -3
package/dist/cli.cjs
CHANGED
|
@@ -4538,6 +4538,7 @@ async function requestRemoteValidation(input) {
|
|
|
4538
4538
|
ok: true,
|
|
4539
4539
|
reachable: verdict.reachable,
|
|
4540
4540
|
ready: verdict.ready,
|
|
4541
|
+
...verdict.initial_snapshot !== void 0 ? { initialSnapshot: verdict.initial_snapshot } : {},
|
|
4541
4542
|
...verdict.reason !== void 0 ? { reason: verdict.reason } : {},
|
|
4542
4543
|
failures: verdict.failures
|
|
4543
4544
|
};
|
|
@@ -4565,7 +4566,10 @@ var init_remoteValidation = __esm({
|
|
|
4565
4566
|
wal_level: () => `your database isn't set up to share changes as they happen yet`,
|
|
4566
4567
|
publication: () => `none of your tables are shared with Ablo yet`,
|
|
4567
4568
|
replication_role: () => `the login Ablo reads with can't follow your changes yet`,
|
|
4568
|
-
replica_identity: (f) => withActual(
|
|
4569
|
+
replica_identity: (f) => withActual(
|
|
4570
|
+
`some shared tables don't record enough for Ablo to track edits and deletes`,
|
|
4571
|
+
f.actual
|
|
4572
|
+
),
|
|
4569
4573
|
table_select: (f) => withActual(`the login Ablo reads with can't read some shared tables`, f.actual),
|
|
4570
4574
|
write_role: () => `the login Ablo writes with isn't set up yet`,
|
|
4571
4575
|
row_security: () => `the writer login isn't set to honor your row-level security`,
|
|
@@ -4911,7 +4915,8 @@ async function registerDirectDataSource(opts) {
|
|
|
4911
4915
|
`
|
|
4912
4916
|
${import_picocolors7.default.green("\u2713")} Registered${body.host ? ` ${import_picocolors7.default.dim(body.host)}` : ""}${body.id ? ` ${import_picocolors7.default.dim(`(${body.id})`)}` : ""} as a direct DataSource (${statusNote}).
|
|
4913
4917
|
Your database is connected. Reads follow its replication stream; writes go through Ablo
|
|
4914
|
-
and land in your own tables.
|
|
4918
|
+
and land in your own tables. Rows that already exist load automatically \u2014 no manual
|
|
4919
|
+
backfill or row updates. Check their progress with ${import_picocolors7.default.cyan("ablo connect check")}.
|
|
4915
4920
|
`
|
|
4916
4921
|
);
|
|
4917
4922
|
return true;
|
|
@@ -6525,10 +6530,9 @@ async function auditTenantSyncInfra(sql) {
|
|
|
6525
6530
|
const artifacts = [];
|
|
6526
6531
|
for (const artifact of import_footprint2.ABLO_FOOTPRINT) {
|
|
6527
6532
|
const key = artifact.kind === "table" || artifact.kind === "type" ? `public.${artifact.name}` : artifact.name;
|
|
6528
|
-
const rows = await sql.unsafe(
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
);
|
|
6533
|
+
const rows = await sql.unsafe(FOOTPRINT_LOOKUP[artifact.kind], [
|
|
6534
|
+
key
|
|
6535
|
+
]);
|
|
6532
6536
|
artifacts.push({
|
|
6533
6537
|
kind: artifact.kind,
|
|
6534
6538
|
name: artifact.name,
|
|
@@ -6637,6 +6641,22 @@ ${ambient}` : ""}`,
|
|
|
6637
6641
|
const { label, fix } = describeRemoteFailure(failure);
|
|
6638
6642
|
printCheckItem({ ok: false, label, fix });
|
|
6639
6643
|
}
|
|
6644
|
+
if (result.initialSnapshot?.status === "complete") {
|
|
6645
|
+
printCheckItem({ ok: true, label: "Rows that existed before connecting are loaded" });
|
|
6646
|
+
} else if (result.initialSnapshot?.status === "loading") {
|
|
6647
|
+
printCheckItem({
|
|
6648
|
+
ok: false,
|
|
6649
|
+
label: "Rows that existed before connecting are still loading",
|
|
6650
|
+
fix: "No SQL or row-touch script is needed. Ablo snapshots published tables automatically; wait a moment, then rerun `ablo connect check`."
|
|
6651
|
+
});
|
|
6652
|
+
} else if (result.initialSnapshot?.status === "retrying") {
|
|
6653
|
+
printCheckItem({
|
|
6654
|
+
ok: false,
|
|
6655
|
+
label: "Loading existing rows hit an error and Ablo is retrying",
|
|
6656
|
+
fix: `${result.initialSnapshot.detail ?? "The replication worker reported an error."}
|
|
6657
|
+
No row updates are needed. Fix the reported connection or database issue, then rerun \`ablo connect check\`.`
|
|
6658
|
+
});
|
|
6659
|
+
}
|
|
6640
6660
|
console.log();
|
|
6641
6661
|
if (result.ready) {
|
|
6642
6662
|
console.log(
|
|
@@ -6645,6 +6665,13 @@ ${ambient}` : ""}`,
|
|
|
6645
6665
|
);
|
|
6646
6666
|
process.exit(0);
|
|
6647
6667
|
}
|
|
6668
|
+
if ((result.initialSnapshot?.status === "loading" || result.initialSnapshot?.status === "retrying") && result.failures.length === 0) {
|
|
6669
|
+
console.log(
|
|
6670
|
+
` ${import_picocolors12.default.yellow("\u2014")} Not ready yet ${import_picocolors12.default.dim(`\u2014 Ablo is loading existing rows. Re-run ${import_picocolors12.default.bold("ablo connect check")} shortly.`)}
|
|
6671
|
+
`
|
|
6672
|
+
);
|
|
6673
|
+
process.exit(1);
|
|
6674
|
+
}
|
|
6648
6675
|
const count = result.failures.length;
|
|
6649
6676
|
console.log(
|
|
6650
6677
|
` ${import_picocolors12.default.red(`${count} item${count === 1 ? "" : "s"} to fix`)} ${import_picocolors12.default.dim(`\u2014 apply the fixes above, then re-run ${import_picocolors12.default.bold("ablo connect check")}.`)}
|
|
@@ -284738,7 +284765,9 @@ async function ping(apiUrl3) {
|
|
|
284738
284765
|
}
|
|
284739
284766
|
function formatConflict(conflict) {
|
|
284740
284767
|
if (!conflict) return "";
|
|
284741
|
-
const parts = import_schema8.participantKindSchema.options.flatMap(
|
|
284768
|
+
const parts = import_schema8.participantKindSchema.options.flatMap(
|
|
284769
|
+
(k3) => conflict[k3] ? [`${k3}:${conflict[k3]}`] : []
|
|
284770
|
+
);
|
|
284742
284771
|
return parts.length ? `{${parts.join(",")}}` : "";
|
|
284743
284772
|
}
|
|
284744
284773
|
function printTargetLines(target, localProject, storedOrganizationId, storedOrganizationSlug) {
|
|
@@ -284783,13 +284812,21 @@ async function status(args = []) {
|
|
|
284783
284812
|
const cfg = readConfig();
|
|
284784
284813
|
const mode = getMode();
|
|
284785
284814
|
const runtimeKey = resolveRuntimeApiKey();
|
|
284786
|
-
const target = runtimeKey.key ? await resolveTarget({
|
|
284815
|
+
const target = runtimeKey.key ? await resolveTarget({
|
|
284816
|
+
url: apiUrl3,
|
|
284817
|
+
apiKey: runtimeKey.key,
|
|
284818
|
+
keySource: runtimeKey.source ?? "stored"
|
|
284819
|
+
}) : null;
|
|
284787
284820
|
if (args.includes("--json")) {
|
|
284788
284821
|
const entry = getKeyEntry(mode);
|
|
284789
284822
|
const activeProject2 = getActiveProject();
|
|
284790
284823
|
const pushed2 = await fetchPushedSchema(apiUrl3, runtimeKey.key);
|
|
284791
284824
|
const reachableForJson = await ping(apiUrl3);
|
|
284792
|
-
const
|
|
284825
|
+
const routingForJson = reachableForJson ? await fetchRoutingState(apiUrl3, runtimeKey.key) : {
|
|
284826
|
+
source: { kind: "unknown", detail: "unreachable" },
|
|
284827
|
+
validation: null
|
|
284828
|
+
};
|
|
284829
|
+
const dataSource2 = routingForJson.source;
|
|
284793
284830
|
const driftForJson = schemaDrift(await readLocalSchemaHash(), pushed2?.hash);
|
|
284794
284831
|
const out = {
|
|
284795
284832
|
// The locally-active project (`ablo projects use`); null = org-default.
|
|
@@ -284835,6 +284872,10 @@ async function status(args = []) {
|
|
|
284835
284872
|
// form of the human verdict: a caller can gate on it in CI rather than
|
|
284836
284873
|
// discovering the same facts from a failed request later.
|
|
284837
284874
|
dataSource: dataSource2,
|
|
284875
|
+
// Existing rows are copied into Ablo automatically after a direct source
|
|
284876
|
+
// is connected. Agents can gate a read cutover on this field instead of
|
|
284877
|
+
// probing for one row or inventing a row-touch backfill.
|
|
284878
|
+
initialSnapshot: routingForJson.validation?.ok === true ? routingForJson.validation.initialSnapshot ?? null : null,
|
|
284838
284879
|
drift: driftForJson,
|
|
284839
284880
|
blockers: blockers({
|
|
284840
284881
|
reachable: reachableForJson,
|
|
@@ -284868,7 +284909,9 @@ async function status(args = []) {
|
|
|
284868
284909
|
);
|
|
284869
284910
|
const management = getManagementKeyEntry();
|
|
284870
284911
|
console.log(
|
|
284871
|
-
` ${import_picocolors19.default.dim("\u25CB")} ${"management".padEnd(12)} ${management ? import_picocolors19.default.dim(
|
|
284912
|
+
` ${import_picocolors19.default.dim("\u25CB")} ${"management".padEnd(12)} ${management ? import_picocolors19.default.dim(
|
|
284913
|
+
`${management.apiKey.slice(0, 12)}\u2026${management.expiresAt ? ` \xB7 ${expiryLabel(management.expiresAt)}` : ""}`
|
|
284914
|
+
) : import_picocolors19.default.dim("\u2014 no key")}`
|
|
284872
284915
|
);
|
|
284873
284916
|
for (const { key: m2, label } of [
|
|
284874
284917
|
{ key: "sandbox", label: "legacy child" },
|
|
@@ -284901,7 +284944,9 @@ async function status(args = []) {
|
|
|
284901
284944
|
const how = [...new Set(dataSource.connections)].join(" + ");
|
|
284902
284945
|
const pooled = detectPoolerIn(dataSource.hosts);
|
|
284903
284946
|
const unreachable = validation && !validation.ok ? validation.message : void 0;
|
|
284904
|
-
console.log(
|
|
284947
|
+
console.log(
|
|
284948
|
+
` ${import_picocolors19.default.dim("data")} ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim(`database connected to this plane (${how})`)}`
|
|
284949
|
+
);
|
|
284905
284950
|
if (pooled) {
|
|
284906
284951
|
console.log(
|
|
284907
284952
|
` ${import_picocolors19.default.yellow("\u26A0")} ${import_picocolors19.default.dim(
|
|
@@ -284912,12 +284957,27 @@ async function status(args = []) {
|
|
|
284912
284957
|
if (unreachable) {
|
|
284913
284958
|
console.log(` ${import_picocolors19.default.red("\u2717")} ${import_picocolors19.default.dim(`Ablo could not reach it \u2014 ${unreachable}`)}`);
|
|
284914
284959
|
}
|
|
284960
|
+
if (validation?.ok && validation.initialSnapshot?.status === "loading") {
|
|
284961
|
+
console.log(
|
|
284962
|
+
` ${import_picocolors19.default.yellow("\u25CC")} ${import_picocolors19.default.dim(
|
|
284963
|
+
`loading rows that predate the connection \u2014 automatic; check with ${import_picocolors19.default.bold("ablo connect check")}`
|
|
284964
|
+
)}`
|
|
284965
|
+
);
|
|
284966
|
+
} else if (validation?.ok && validation.initialSnapshot?.status === "retrying") {
|
|
284967
|
+
console.log(
|
|
284968
|
+
` ${import_picocolors19.default.red("\u2717")} ${import_picocolors19.default.dim(
|
|
284969
|
+
`loading existing rows is retrying${validation.initialSnapshot.detail ? ` \u2014 ${validation.initialSnapshot.detail}` : ""}`
|
|
284970
|
+
)}`
|
|
284971
|
+
);
|
|
284972
|
+
}
|
|
284915
284973
|
} else if (dataSource.kind === "none") {
|
|
284916
284974
|
console.log(
|
|
284917
284975
|
` ${import_picocolors19.default.dim("data")} ${import_picocolors19.default.red("\u2717 no database connected to this plane")} ${import_picocolors19.default.dim("\u2014 writes are held")}`
|
|
284918
284976
|
);
|
|
284919
284977
|
} else if (reachable) {
|
|
284920
|
-
console.log(
|
|
284978
|
+
console.log(
|
|
284979
|
+
` ${import_picocolors19.default.dim("data")} ${import_picocolors19.default.yellow("?")} ${import_picocolors19.default.dim(`could not read the plane's databases (${dataSource.detail})`)}`
|
|
284980
|
+
);
|
|
284921
284981
|
}
|
|
284922
284982
|
const pushed = reachable ? await fetchPushedSchema(apiUrl3, introspectKey) : null;
|
|
284923
284983
|
if (reachable) {
|
|
@@ -284925,7 +284985,9 @@ async function status(args = []) {
|
|
|
284925
284985
|
const when = pushed.pushedAt ? ` ${import_picocolors19.default.dim(`@ ${pushed.pushedAt.slice(0, 10)}`)}` : "";
|
|
284926
284986
|
const ver = pushed.version != null ? ` ${import_picocolors19.default.dim(`(rev ${pushed.version})`)}` : "";
|
|
284927
284987
|
const hashLabel = pushed.hash ? ` ${import_picocolors19.default.dim(`hash ${pushed.hash}`)}` : "";
|
|
284928
|
-
console.log(
|
|
284988
|
+
console.log(
|
|
284989
|
+
` ${import_picocolors19.default.dim("schema")} ${import_picocolors19.default.bold(`${pushed.models.length} models pushed`)}${ver}${hashLabel}${when}`
|
|
284990
|
+
);
|
|
284929
284991
|
for (const m2 of pushed.models) {
|
|
284930
284992
|
const tn = m2.typename === m2.key ? import_picocolors19.default.dim(`typename=${m2.typename}`) : import_picocolors19.default.yellow(`typename=${m2.typename}`);
|
|
284931
284993
|
const conflict = formatConflict(m2.conflict);
|
|
@@ -284933,7 +284995,9 @@ async function status(args = []) {
|
|
|
284933
284995
|
console.log(` ${import_picocolors19.default.dim("\u2022")} ${m2.key.padEnd(14)} ${tn}${conflictStr2}`);
|
|
284934
284996
|
}
|
|
284935
284997
|
} else if (pushed && !pushed.active) {
|
|
284936
|
-
console.log(
|
|
284998
|
+
console.log(
|
|
284999
|
+
` ${import_picocolors19.default.dim("schema")} ${import_picocolors19.default.yellow("none pushed")} ${import_picocolors19.default.dim(`(run ${import_picocolors19.default.bold("ablo push")} or ${import_picocolors19.default.bold("ablo dev")})`)}`
|
|
285000
|
+
);
|
|
284937
285001
|
}
|
|
284938
285002
|
}
|
|
284939
285003
|
const drift = schemaDrift(await readLocalSchemaHash(), pushed?.hash);
|
|
@@ -284978,7 +285042,9 @@ init_target();
|
|
|
284978
285042
|
init_readiness();
|
|
284979
285043
|
function render(check2) {
|
|
284980
285044
|
const mark = check2.state === "ok" ? import_picocolors20.default.green("\u2713") : check2.state === "fail" ? import_picocolors20.default.red("\u2717") : import_picocolors20.default.dim("\u2013");
|
|
284981
|
-
console.log(
|
|
285045
|
+
console.log(
|
|
285046
|
+
` ${mark} ${check2.label.padEnd(10)} ${check2.state === "fail" ? check2.detail : import_picocolors20.default.dim(check2.detail)}`
|
|
285047
|
+
);
|
|
284982
285048
|
if (check2.fix) console.log(` ${" ".repeat(11)}${import_picocolors20.default.dim(`\u2192 ${check2.fix}`)}`);
|
|
284983
285049
|
}
|
|
284984
285050
|
async function ping2(apiUrl3) {
|
|
@@ -285025,7 +285091,11 @@ async function doctor() {
|
|
|
285025
285091
|
fix: "check your connection, then re-run `ablo doctor`"
|
|
285026
285092
|
}
|
|
285027
285093
|
);
|
|
285028
|
-
const target = runtimeKey.key ? await resolveTarget({
|
|
285094
|
+
const target = runtimeKey.key ? await resolveTarget({
|
|
285095
|
+
url: apiUrl3,
|
|
285096
|
+
apiKey: runtimeKey.key,
|
|
285097
|
+
keySource: runtimeKey.source ?? "stored"
|
|
285098
|
+
}) : null;
|
|
285029
285099
|
const confirmed = target?.confirmed ?? null;
|
|
285030
285100
|
if (confirmed) {
|
|
285031
285101
|
const project = confirmed.project ? confirmed.project.isDefault ? "default" : confirmed.project.name === null ? `${confirmed.project.id} (unnamed \u2014 ${confirmed.project.unnamedReason ?? "the project list did not answer"})` : `${confirmed.project.slug} (${confirmed.project.id})` : "default";
|
|
@@ -285071,7 +285141,12 @@ async function doctor() {
|
|
|
285071
285141
|
label: "schema",
|
|
285072
285142
|
state: "ok",
|
|
285073
285143
|
detail: `${pushed.models.length} models active${pushed.hash ? `, hash ${pushed.hash}` : ""}`
|
|
285074
|
-
} : reachable && runtimeKey.key ? {
|
|
285144
|
+
} : reachable && runtimeKey.key ? {
|
|
285145
|
+
label: "schema",
|
|
285146
|
+
state: "fail",
|
|
285147
|
+
detail: "none active for this key",
|
|
285148
|
+
fix: "run `ablo push`"
|
|
285149
|
+
} : { label: "schema", state: "skip", detail: "not determined" }
|
|
285075
285150
|
);
|
|
285076
285151
|
const drift = schemaDrift(await readLocalSchemaHash(), pushed?.hash);
|
|
285077
285152
|
if (drift) {
|
|
@@ -285084,7 +285159,21 @@ async function doctor() {
|
|
|
285084
285159
|
}
|
|
285085
285160
|
if (dataSource.kind === "connected" && validation) {
|
|
285086
285161
|
checks.push(
|
|
285087
|
-
validation.ok ? validation.ready ? {
|
|
285162
|
+
validation.ok ? validation.ready ? {
|
|
285163
|
+
label: "database",
|
|
285164
|
+
state: "ok",
|
|
285165
|
+
detail: "reachable from Ablo, and ready to replicate"
|
|
285166
|
+
} : validation.initialSnapshot?.status === "loading" && validation.failures.length === 0 ? {
|
|
285167
|
+
label: "database",
|
|
285168
|
+
state: "fail",
|
|
285169
|
+
detail: "reachable; rows that predate the connection are still loading",
|
|
285170
|
+
fix: "wait a moment and rerun `ablo doctor` \u2014 Ablo snapshots them automatically; do not update every row or write a manual backfill"
|
|
285171
|
+
} : validation.initialSnapshot?.status === "retrying" && validation.failures.length === 0 ? {
|
|
285172
|
+
label: "database",
|
|
285173
|
+
state: "fail",
|
|
285174
|
+
detail: `the initial row load is retrying${validation.initialSnapshot.detail ? ` \u2014 ${validation.initialSnapshot.detail}` : ""}`,
|
|
285175
|
+
fix: "fix the reported connection or database issue, then rerun `ablo doctor`; no row-touch backfill is needed"
|
|
285176
|
+
} : {
|
|
285088
285177
|
label: "database",
|
|
285089
285178
|
state: "fail",
|
|
285090
285179
|
detail: `reachable, but not ready (${validation.failures.length} check${validation.failures.length === 1 ? "" : "s"} failing)`,
|
|
@@ -285114,7 +285203,9 @@ async function doctor() {
|
|
|
285114
285203
|
console.log(
|
|
285115
285204
|
` ${import_picocolors20.default.red("\u2717")} ${import_picocolors20.default.bold(`${failed} problem${failed === 1 ? "" : "s"}`)}` + import_picocolors20.default.dim(skipped > 0 ? `, ${skipped} check${skipped === 1 ? "" : "s"} skipped` : "")
|
|
285116
285205
|
);
|
|
285117
|
-
console.log(
|
|
285206
|
+
console.log(
|
|
285207
|
+
import_picocolors20.default.dim(" Fix them in the order above \u2014 an earlier one often explains a later one.")
|
|
285208
|
+
);
|
|
285118
285209
|
} else if (skipped > 0) {
|
|
285119
285210
|
console.log(
|
|
285120
285211
|
` ${import_picocolors20.default.yellow("?")} ${import_picocolors20.default.dim(`nothing is blocking a write, but ${skipped} check${skipped === 1 ? "" : "s"} could not be run`)}`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abloatai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "The ablo command line: set up Ablo, connect your database, and push your schema from the terminal.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"directory": "packages/cli"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@abloatai/transaction": "^0.
|
|
33
|
+
"@abloatai/transaction": "^0.46.0",
|
|
34
34
|
"jiti": "^2.7.0",
|
|
35
35
|
"zod": "^4.4.3",
|
|
36
|
-
"@abloatai/humans": "^0.
|
|
36
|
+
"@abloatai/humans": "^0.46.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@clack/prompts": "^0.11.0",
|