@dashai/cli 0.9.0 → 0.10.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/bin.js +116 -23
- package/dist/bin.js.map +1 -1
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -543,8 +543,11 @@ function manifestJson(opts) {
|
|
|
543
543
|
owns: {
|
|
544
544
|
tables: [
|
|
545
545
|
{
|
|
546
|
+
// NOTE: no `name` key here — the backend manifest validator
|
|
547
|
+
// (POST /api/installations/dev) is strict and rejects unrecognized
|
|
548
|
+
// table keys, and it derives the display name from the slug. A
|
|
549
|
+
// `name` here would fail provisioning's table materialization (#4).
|
|
546
550
|
slug: "items",
|
|
547
|
-
name: "Items",
|
|
548
551
|
fields: [
|
|
549
552
|
{ slug: "title", type: "text" },
|
|
550
553
|
{ slug: "done", type: "boolean" }
|
|
@@ -719,10 +722,12 @@ function envLocal(apiUrl = SCAFFOLD_DEFAULT_API_URL) {
|
|
|
719
722
|
"# There is no DASHWISE_INSTALLATION_ID \u2014 the API key resolves the",
|
|
720
723
|
"# install server-side.",
|
|
721
724
|
`DASHWISE_API_URL=${apiUrl}`,
|
|
722
|
-
"# Scoped `dwk_` key.
|
|
723
|
-
"#
|
|
724
|
-
"#
|
|
725
|
-
"
|
|
725
|
+
"# Scoped `dwk_` key. Commented out on purpose: `dashwise dev` injects",
|
|
726
|
+
"# the local-dev key from ~/.config/dashwise/auth.json, and an empty",
|
|
727
|
+
"# `DASHWISE_API_KEY=` here would SHADOW that (and make a bare `next dev`",
|
|
728
|
+
"# fail with a confusing empty-key error). For a standalone `next dev`,",
|
|
729
|
+
"# uncomment + paste a key from `dashwise api-keys create <name>`.",
|
|
730
|
+
"# DASHWISE_API_KEY=",
|
|
726
731
|
""
|
|
727
732
|
].join("\n");
|
|
728
733
|
}
|
|
@@ -1919,6 +1924,29 @@ cached in \`~/.config/dashwise/auth.json#modules[${opts.slug}]\` after
|
|
|
1919
1924
|
as \`DASHWISE_API_KEY\` into the Next.js child env automatically. The
|
|
1920
1925
|
key binds the install server-side \u2014 there is no \`DASHWISE_INSTALLATION_ID\`.
|
|
1921
1926
|
|
|
1927
|
+
### Own tables vs. the shared Data Hub
|
|
1928
|
+
|
|
1929
|
+
The dev key \`dashwise init\` mints gives you **full access to your
|
|
1930
|
+
module's OWN tables** (\`owns.tables\` in \`module.json\`) through \`db\` /
|
|
1931
|
+
\`qb\` \u2014 no grant setup needed. That covers the common case.
|
|
1932
|
+
|
|
1933
|
+
The **shared workspace Data Hub** (other tables in the workspace, reached
|
|
1934
|
+
via \`datahub.table(<id>)\`) is separate: it is default-deny and returns
|
|
1935
|
+
**403** with the dev key until you mint a key that carries an explicit
|
|
1936
|
+
Data Hub grant. Do that with:
|
|
1937
|
+
|
|
1938
|
+
\`\`\`
|
|
1939
|
+
# read a specific Data Hub table:
|
|
1940
|
+
dashwise api-keys create datahub-read --grant datahub:table=<tableId>:read
|
|
1941
|
+
# or a whole database (application):
|
|
1942
|
+
dashwise api-keys create datahub-rw --grant datahub:app=<appId>:read,create,update
|
|
1943
|
+
\`\`\`
|
|
1944
|
+
|
|
1945
|
+
Paste the printed key as \`DASHWISE_API_KEY\` (or, for local dev, set it
|
|
1946
|
+
before \`dashwise dev\` so it wins over the auto-injected own-tables key).
|
|
1947
|
+
So: \`db\`/\`qb\` on your own tables works out of the box; \`datahub\` needs a
|
|
1948
|
+
granted key.
|
|
1949
|
+
|
|
1922
1950
|
## Auth in dev mode
|
|
1923
1951
|
|
|
1924
1952
|
\`dashwise dev\` registers a dev-mode SSO session with the backend so
|
|
@@ -2303,18 +2331,40 @@ async function moduleInitCommand(slugArg, options) {
|
|
|
2303
2331
|
let devInstall = null;
|
|
2304
2332
|
const wantsProvision = options.provision !== false;
|
|
2305
2333
|
if (wantsProvision) {
|
|
2334
|
+
const manifest = (() => {
|
|
2335
|
+
try {
|
|
2336
|
+
return readManifest(dir) ?? void 0;
|
|
2337
|
+
} catch {
|
|
2338
|
+
return void 0;
|
|
2339
|
+
}
|
|
2340
|
+
})();
|
|
2306
2341
|
devInstall = await tryProvisionDevInstall({
|
|
2307
2342
|
slug,
|
|
2308
2343
|
name,
|
|
2309
2344
|
description: options.description,
|
|
2310
2345
|
workspaceFlag: options.workspace,
|
|
2311
|
-
moduleKind: kind
|
|
2346
|
+
moduleKind: kind,
|
|
2347
|
+
...manifest !== void 0 ? { manifest } : {}
|
|
2312
2348
|
});
|
|
2313
2349
|
} else {
|
|
2314
2350
|
info(
|
|
2315
2351
|
`Skipping provisioning (${code("--no-provision")}). ${dim("Run with provisioning later to enable `dashwise dev` data-plane.")}`
|
|
2316
2352
|
);
|
|
2317
2353
|
}
|
|
2354
|
+
const keyCached = !wantsProvision || readModuleEntry(slug)?.apiKey ? true : false;
|
|
2355
|
+
if (wantsProvision && !keyCached) {
|
|
2356
|
+
fail(
|
|
2357
|
+
`NO API KEY WAS CACHED for ${code(slug)} \u2014 the scaffold is written, but ${code("dashwise dev")} will run with NO token (data-plane + sign-in will fail).`
|
|
2358
|
+
);
|
|
2359
|
+
info(
|
|
2360
|
+
`Fix it by re-running provisioning once the cause above is resolved:
|
|
2361
|
+
${code(`dashwise init ${slug} --force --workspace <your-workspace-slug>`)}
|
|
2362
|
+
Confirm success: the line ${code("\u2713 Provisioned dev install id=\u2026 API key cached")} must appear,
|
|
2363
|
+
and ${code("cat ~/.config/dashwise/auth.json")} must show a non-empty ${code(`modules.${slug}.apiKey`)} starting with ${code("dwk_")}.`
|
|
2364
|
+
);
|
|
2365
|
+
outro("Scaffold written, but provisioning failed \u2014 see above.");
|
|
2366
|
+
return 2;
|
|
2367
|
+
}
|
|
2318
2368
|
info(`Next steps:`);
|
|
2319
2369
|
process.stdout.write(`
|
|
2320
2370
|
${dim("1.")} ${code(`cd ${dir}`)}
|
|
@@ -2343,6 +2393,20 @@ async function tryProvisionDevInstall(args) {
|
|
|
2343
2393
|
warn("CLI config disappeared mid-init. Skipping provisioning.");
|
|
2344
2394
|
return null;
|
|
2345
2395
|
}
|
|
2396
|
+
const targetApiUrl = resolveApiUrl();
|
|
2397
|
+
info(`Backend: ${code(targetApiUrl)}`);
|
|
2398
|
+
const hostOf = (u) => {
|
|
2399
|
+
try {
|
|
2400
|
+
return new URL(u).host;
|
|
2401
|
+
} catch {
|
|
2402
|
+
return u;
|
|
2403
|
+
}
|
|
2404
|
+
};
|
|
2405
|
+
if (hostOf(targetApiUrl) !== hostOf(cfg.apiUrl)) {
|
|
2406
|
+
warn(
|
|
2407
|
+
`Your saved login is for ${code(cfg.apiUrl)} but this run targets ${code(targetApiUrl)}. Provisioning uses the saved token, which the target backend will reject (401). Log in against the target first: ${code(`dashwise login --api-url ${targetApiUrl}`)}.`
|
|
2408
|
+
);
|
|
2409
|
+
}
|
|
2346
2410
|
let workspace2;
|
|
2347
2411
|
try {
|
|
2348
2412
|
workspace2 = await selectWorkspace({
|
|
@@ -2358,25 +2422,42 @@ async function tryProvisionDevInstall(args) {
|
|
|
2358
2422
|
info(
|
|
2359
2423
|
`Provisioning dev install in workspace ${code(workspace2.slug)} (id=${workspace2.id})\u2026`
|
|
2360
2424
|
);
|
|
2425
|
+
const baseBody = {
|
|
2426
|
+
slug: args.slug,
|
|
2427
|
+
name: args.name,
|
|
2428
|
+
...args.description !== void 0 ? { description: args.description } : {},
|
|
2429
|
+
workspaceId: workspace2.id,
|
|
2430
|
+
...args.moduleKind !== void 0 ? { moduleKind: args.moduleKind } : {}
|
|
2431
|
+
};
|
|
2432
|
+
const reqOpts = { baseUrl: resolveApiUrl(), auth: { token: cfg.token } };
|
|
2361
2433
|
let response;
|
|
2362
2434
|
try {
|
|
2363
2435
|
response = await createDevInstallation(
|
|
2364
|
-
{
|
|
2365
|
-
|
|
2366
|
-
name: args.name,
|
|
2367
|
-
...args.description !== void 0 ? { description: args.description } : {},
|
|
2368
|
-
workspaceId: workspace2.id,
|
|
2369
|
-
...args.moduleKind !== void 0 ? { moduleKind: args.moduleKind } : {}
|
|
2370
|
-
},
|
|
2371
|
-
{ baseUrl: resolveApiUrl(), auth: { token: cfg.token } }
|
|
2436
|
+
{ ...baseBody, ...args.manifest !== void 0 ? { manifest: args.manifest } : {} },
|
|
2437
|
+
reqOpts
|
|
2372
2438
|
);
|
|
2373
2439
|
} catch (err) {
|
|
2374
|
-
if (err instanceof ApiError) {
|
|
2440
|
+
if (err instanceof ApiError && err.code === "MANIFEST_INVALID" && args.manifest !== void 0) {
|
|
2441
|
+
warn(
|
|
2442
|
+
`Couldn't pre-materialize the seeded table(s) (${err.message}). Provisioning without them; add tables later with ${code("dashwise table create")}.`
|
|
2443
|
+
);
|
|
2444
|
+
try {
|
|
2445
|
+
response = await createDevInstallation(baseBody, reqOpts);
|
|
2446
|
+
} catch (err2) {
|
|
2447
|
+
if (err2 instanceof ApiError) {
|
|
2448
|
+
surfaceProvisionApiError(err2, args.slug);
|
|
2449
|
+
return null;
|
|
2450
|
+
}
|
|
2451
|
+
warn(`Provisioning failed: ${err2.message}`);
|
|
2452
|
+
return null;
|
|
2453
|
+
}
|
|
2454
|
+
} else if (err instanceof ApiError) {
|
|
2375
2455
|
surfaceProvisionApiError(err, args.slug);
|
|
2376
2456
|
return null;
|
|
2457
|
+
} else {
|
|
2458
|
+
warn(`Provisioning failed: ${err.message}`);
|
|
2459
|
+
return null;
|
|
2377
2460
|
}
|
|
2378
|
-
warn(`Provisioning failed: ${err.message}`);
|
|
2379
|
-
return null;
|
|
2380
2461
|
}
|
|
2381
2462
|
try {
|
|
2382
2463
|
upsertModuleEntry(args.slug, {
|
|
@@ -2394,7 +2475,7 @@ async function tryProvisionDevInstall(args) {
|
|
|
2394
2475
|
return response;
|
|
2395
2476
|
}
|
|
2396
2477
|
success(
|
|
2397
|
-
`Provisioned dev install id=${response.installation.id}. API key cached in your CLI config.`
|
|
2478
|
+
`Provisioned dev install id=${response.installation.id}. API key cached in your CLI config.` + (response.tables_applied > 0 ? ` ${response.tables_applied} seeded table(s) materialized.` : "")
|
|
2398
2479
|
);
|
|
2399
2480
|
return response;
|
|
2400
2481
|
}
|
|
@@ -2439,6 +2520,7 @@ var init_init = __esm({
|
|
|
2439
2520
|
init_output();
|
|
2440
2521
|
init_api_client();
|
|
2441
2522
|
init_config();
|
|
2523
|
+
init_manifest_io();
|
|
2442
2524
|
init_workspaces();
|
|
2443
2525
|
}
|
|
2444
2526
|
});
|
|
@@ -3353,8 +3435,13 @@ async function devCommand(options) {
|
|
|
3353
3435
|
` ${dim("api key")} ${maskedToken(apiKeyEnv)} ${dim(`(from ${source}${installId ? `, install id=${installId}` : ""})`)}`
|
|
3354
3436
|
);
|
|
3355
3437
|
} else if (manifestSlug !== void 0 && installationIdEnv === void 0) {
|
|
3356
|
-
|
|
3357
|
-
`No
|
|
3438
|
+
fail(
|
|
3439
|
+
`No ${code("dwk_")} API key for module ${code(manifestSlug)} \u2014 the module will run with NO token.`
|
|
3440
|
+
);
|
|
3441
|
+
info(
|
|
3442
|
+
` Data-plane calls (db, qb) AND sign-in will fail until you provision a dev install:
|
|
3443
|
+
${code(`dashwise init ${manifestSlug} --force --workspace <your-workspace-slug>`)}
|
|
3444
|
+
(Ensure you're logged into the right backend: ${code(`dashwise login --api-url ${apiUrl}`)}.)`
|
|
3358
3445
|
);
|
|
3359
3446
|
}
|
|
3360
3447
|
info("");
|
|
@@ -3528,9 +3615,9 @@ async function resolveModuleApiKey(slug, entry, apiUrl, cliBearer) {
|
|
|
3528
3615
|
if (process.env.DASHWISE_API_KEY) return null;
|
|
3529
3616
|
if (!entry) return null;
|
|
3530
3617
|
if (entry.apiKey) return null;
|
|
3531
|
-
if (!entry.
|
|
3618
|
+
if (!entry.installationId) return null;
|
|
3532
3619
|
info(
|
|
3533
|
-
`
|
|
3620
|
+
`No usable ${code("dwk_")} API key cached for ${code(slug)} \u2014 minting a fresh one against dev install ${entry.installationId} (a stale CLI can cache an empty key)\u2026`
|
|
3534
3621
|
);
|
|
3535
3622
|
let minted;
|
|
3536
3623
|
try {
|
|
@@ -8501,6 +8588,12 @@ program.name("dashwise").description("DashWise CLI \u2014 log in, author modules
|
|
|
8501
8588
|
"--api-url <url>",
|
|
8502
8589
|
"Override DashWise backend URL (default: $DASHWISE_API_URL or saved config or http://localhost:3000)"
|
|
8503
8590
|
);
|
|
8591
|
+
program.hook("preAction", () => {
|
|
8592
|
+
const apiUrl = program.opts().apiUrl?.trim();
|
|
8593
|
+
if (apiUrl) {
|
|
8594
|
+
process.env.DASHWISE_API_URL = apiUrl.replace(/\/+$/, "");
|
|
8595
|
+
}
|
|
8596
|
+
});
|
|
8504
8597
|
program.command("login").description("Sign in via the device-code flow.").option("--no-prompt", 'Skip the "open browser" prompt; just print the URL.').option("--name <name>", "Suggested name for the minted token (default: dashwise-cli).").action(async (cmdOpts) => {
|
|
8505
8598
|
const apiUrl = program.opts().apiUrl;
|
|
8506
8599
|
const exit = await loginCommand({
|
|
@@ -9138,7 +9231,7 @@ fieldCmd.command("set-type <table-slug> <field-slug> <new-type>").description(
|
|
|
9138
9231
|
}
|
|
9139
9232
|
})();
|
|
9140
9233
|
function getVersion() {
|
|
9141
|
-
return "0.
|
|
9234
|
+
return "0.10.0";
|
|
9142
9235
|
}
|
|
9143
9236
|
function parseIntOption(value) {
|
|
9144
9237
|
const n = Number.parseInt(value, 10);
|