@tenderprompt/cli 0.1.25 → 0.1.26
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/README.md +27 -0
- package/dist/index.js +760 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -39,6 +39,8 @@ session:
|
|
|
39
39
|
- inspect artifact-scoped analytics without SQL or platform credentials
|
|
40
40
|
- inspect artifact-scoped app database tables, schema, and bounded read-only
|
|
41
41
|
query output
|
|
42
|
+
- create private app admin dashboard, resource, view, and widget definitions
|
|
43
|
+
from structured app database specs
|
|
42
44
|
- share app setup between Tender accounts without copying customer data,
|
|
43
45
|
analytics history, app database rows, secrets, or release history
|
|
44
46
|
- run server-validated chart specs and export aggregate rows
|
|
@@ -156,6 +158,11 @@ tender app db tables artifact_123 --env published --json
|
|
|
156
158
|
tender app db schema artifact_123 --env published --table votes --json
|
|
157
159
|
tender app db query artifact_123 --env published --sql "select * from votes limit 20" --json
|
|
158
160
|
tender app db query artifact_123 --env published --sql "select * from votes order by created_at" --limit 50 --offset 50 --json
|
|
161
|
+
tender app db dashboards artifact_123 --create "App admin" --default-env published --dry-run --json
|
|
162
|
+
tender app db resources suggest artifact_123 --env published --json
|
|
163
|
+
tender app db dashboards render artifact_123 --dashboard app_db_dashboard_123 --env published --json
|
|
164
|
+
tender app db resources create artifact_123 --dashboard app_db_dashboard_123 --spec resource.json --dry-run --json
|
|
165
|
+
tender app db widgets create artifact_123 --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --dry-run --json
|
|
159
166
|
```
|
|
160
167
|
|
|
161
168
|
## Shared app setup
|
|
@@ -340,6 +347,26 @@ Client and server limits include a 100 KB statement cap and at most 100 bound
|
|
|
340
347
|
parameters. The server enforces the final policy, row limits, response limits,
|
|
341
348
|
artifact scope, and audit logging.
|
|
342
349
|
|
|
350
|
+
Private app admin dashboards are definition-only in the CLI. Use the same
|
|
351
|
+
artifact-scoped `db-read` profile to save dashboards, table-backed resources,
|
|
352
|
+
saved views, and widgets; raw app rows stay in the generated app database.
|
|
353
|
+
Logged-in users can open saved dashboards at `/#/workspace/<artifact-id>/admin`.
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
tender app db resources suggest artifact_123 --profile db --env published --json
|
|
357
|
+
tender app db dashboards artifact_123 --profile db --create "Coffee Matcher Admin" --default-env published --json
|
|
358
|
+
tender app db resources create artifact_123 --profile db --dashboard app_db_dashboard_123 --spec resource.json --view recent.json --json
|
|
359
|
+
tender app db widgets create artifact_123 --profile db --dashboard app_db_dashboard_123 --title "Votes by choice" --spec widget.json --json
|
|
360
|
+
tender app db dashboards render artifact_123 --profile db --dashboard app_db_dashboard_123 --env published --json
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Suggested app database resources may include `dateRange: { "source":
|
|
364
|
+
"dashboard", "column": "created_at" }` so saved admin dashboards behave like a
|
|
365
|
+
Grafana dashboard: one top-level date range applies to charts and tables by
|
|
366
|
+
default, while resource filters stay available for local drill-downs. Use
|
|
367
|
+
`dateRange: { "source": "off" }` or a fixed override only for panels that should
|
|
368
|
+
not follow the dashboard date range.
|
|
369
|
+
|
|
343
370
|
## Safety Model
|
|
344
371
|
|
|
345
372
|
- Local agents run in the user's environment, not in Tender.
|
package/dist/index.js
CHANGED
|
@@ -189,6 +189,8 @@ Commands:
|
|
|
189
189
|
Describe app database query limits and policy
|
|
190
190
|
tender app db query <app-id> --env <preview|published> --sql <sql>
|
|
191
191
|
Run one read-only app database query
|
|
192
|
+
tender app db dashboards <app-id>
|
|
193
|
+
List or create private app admin dashboards
|
|
192
194
|
tender app context fetch <app-id>
|
|
193
195
|
Fetch managed agent context into a checkout
|
|
194
196
|
tender app context status <app-id>
|
|
@@ -451,6 +453,12 @@ Commands:
|
|
|
451
453
|
Show table and index schema for one table
|
|
452
454
|
tender app db query <app-id> --env <preview|published> --sql <sql>
|
|
453
455
|
Run one read-only app database query
|
|
456
|
+
tender app db dashboards <app-id>
|
|
457
|
+
List or create private app admin dashboards
|
|
458
|
+
tender app db resources create <app-id>
|
|
459
|
+
Add a table-backed dashboard resource
|
|
460
|
+
tender app db widgets create <app-id>
|
|
461
|
+
Add a saved dashboard widget
|
|
454
462
|
tender app context fetch <app-id>
|
|
455
463
|
Fetch AGENTS.md, skills, and Tender project context
|
|
456
464
|
tender app context status <app-id>
|
|
@@ -792,12 +800,27 @@ Commands:
|
|
|
792
800
|
Show schema for one table
|
|
793
801
|
tender app db query <app-id> --env <preview|published>
|
|
794
802
|
Run one read-only SQL query
|
|
803
|
+
tender app db dashboards <app-id>
|
|
804
|
+
List or create private app admin dashboards
|
|
805
|
+
tender app db resources suggest <app-id>
|
|
806
|
+
Suggest private app admin resources from schema
|
|
807
|
+
tender app db dashboards render <app-id>
|
|
808
|
+
Render saved private app admin queries
|
|
809
|
+
tender app db resources create <app-id>
|
|
810
|
+
Add a table-backed dashboard resource from JSON
|
|
811
|
+
tender app db widgets create <app-id>
|
|
812
|
+
Add a saved dashboard widget from JSON
|
|
795
813
|
|
|
796
814
|
Examples:
|
|
797
815
|
tender app db capabilities artifact_123 --json
|
|
798
816
|
tender app db tables artifact_123 --env published --json
|
|
799
817
|
tender app db schema artifact_123 --env published --table votes --json
|
|
800
818
|
tender app db query artifact_123 --env published --sql "select * from votes limit 20" --json
|
|
819
|
+
tender app db resources suggest artifact_123 --env published --json
|
|
820
|
+
tender app db dashboards artifact_123 --create "Coffee Matcher Admin" --json
|
|
821
|
+
tender app db dashboards render artifact_123 --dashboard app_db_dashboard_123 --env published --json
|
|
822
|
+
tender app db resources create artifact_123 --dashboard app_db_dashboard_123 --spec resource.json --json
|
|
823
|
+
tender app db widgets create artifact_123 --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --json
|
|
801
824
|
tender app db query artifact_123 --env published --file ./debug.sql --param active --limit 50 --json`;
|
|
802
825
|
}
|
|
803
826
|
function dbCapabilitiesHelp() {
|
|
@@ -837,6 +860,38 @@ Examples:
|
|
|
837
860
|
tender app db query artifact_123 --env published --file ./debug.sql --limit 50 --offset 100 --json
|
|
838
861
|
tender app db query artifact_123 --env published --file - --json < debug.sql`;
|
|
839
862
|
}
|
|
863
|
+
function dbDashboardsHelp() {
|
|
864
|
+
return `Usage: tender app db dashboards <app-id> [--create <name>] [--default-env <preview|published>] [--dry-run] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
865
|
+
|
|
866
|
+
Examples:
|
|
867
|
+
tender app db dashboards artifact_123 --json
|
|
868
|
+
tender app db dashboards artifact_123 --create "Coffee Matcher Admin" --default-env published --json`;
|
|
869
|
+
}
|
|
870
|
+
function dbDashboardsRenderHelp() {
|
|
871
|
+
return `Usage: tender app db dashboards render <app-id> --dashboard <dashboard-id> --env <preview|published> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
872
|
+
|
|
873
|
+
Examples:
|
|
874
|
+
tender app db dashboards render artifact_123 --dashboard app_db_dashboard_123 --env published --json`;
|
|
875
|
+
}
|
|
876
|
+
function dbResourcesSuggestHelp() {
|
|
877
|
+
return `Usage: tender app db resources suggest <app-id> --env <preview|published> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
878
|
+
|
|
879
|
+
Examples:
|
|
880
|
+
tender app db resources suggest artifact_123 --env published --json`;
|
|
881
|
+
}
|
|
882
|
+
function dbResourcesCreateHelp() {
|
|
883
|
+
return `Usage: tender app db resources create <app-id> --dashboard <dashboard-id> --spec <resource.json|-> [--view <view.json|->] [--dry-run] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
884
|
+
|
|
885
|
+
Examples:
|
|
886
|
+
tender app db resources create artifact_123 --dashboard app_db_dashboard_123 --spec resource.json --json
|
|
887
|
+
tender app db resources create artifact_123 --dashboard app_db_dashboard_123 --spec resource.json --view needs-review.json --json`;
|
|
888
|
+
}
|
|
889
|
+
function dbWidgetsCreateHelp() {
|
|
890
|
+
return `Usage: tender app db widgets create <app-id> --dashboard <dashboard-id> --title <title> --spec <widget.json|-> [--dry-run] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
891
|
+
|
|
892
|
+
Examples:
|
|
893
|
+
tender app db widgets create artifact_123 --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --json`;
|
|
894
|
+
}
|
|
840
895
|
function artifactsListHelp() {
|
|
841
896
|
return `Usage: tender app list [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
842
897
|
|
|
@@ -1408,11 +1463,14 @@ function tenderCliCapabilities() {
|
|
|
1408
1463
|
'tender app db schema <artifact-id> --env published --table <table-name> --profile db --json',
|
|
1409
1464
|
'tender app db query <artifact-id> --env published --sql "select * from <table-name> limit 20" --profile db --json',
|
|
1410
1465
|
'tender app db query <artifact-id> --env published --file ./debug.sql --profile db --json',
|
|
1466
|
+
'tender app db resources suggest <artifact-id> --env published --profile db --json',
|
|
1467
|
+
'tender app db dashboards render <artifact-id> --dashboard <dashboard-id> --env published --profile db --json',
|
|
1411
1468
|
],
|
|
1412
1469
|
notes: [
|
|
1413
1470
|
'The CLI only accepts artifact ids. It never accepts tenant ids, runner names, provider account ids, or runtime storage identifiers.',
|
|
1414
1471
|
'Read-only policy is enforced by the server before the runtime shim executes SQL.',
|
|
1415
1472
|
'Use --file - to pipe SQL from stdin without prompts.',
|
|
1473
|
+
'Use resources suggest and dashboards render for private operator/admin surfaces instead of building database tables into shopper UI.',
|
|
1416
1474
|
],
|
|
1417
1475
|
},
|
|
1418
1476
|
{
|
|
@@ -1611,6 +1669,39 @@ function tenderCliCapabilities() {
|
|
|
1611
1669
|
requiresAuth: true,
|
|
1612
1670
|
writes: false,
|
|
1613
1671
|
},
|
|
1672
|
+
{
|
|
1673
|
+
command: 'tender app db resources suggest <artifact-id> --env published --json',
|
|
1674
|
+
purpose: 'Suggest private app admin resources, saved views, and widgets from app database schema and samples.',
|
|
1675
|
+
requiresAuth: true,
|
|
1676
|
+
writes: false,
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
command: 'tender app db dashboards <artifact-id> --create "App admin" --dry-run --json',
|
|
1680
|
+
purpose: 'Preview or create a private app database dashboard definition for one artifact.',
|
|
1681
|
+
requiresAuth: true,
|
|
1682
|
+
writes: true,
|
|
1683
|
+
dryRun: true,
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
command: 'tender app db dashboards render <artifact-id> --dashboard <dashboard-id> --env published --json',
|
|
1687
|
+
purpose: 'Render saved private app admin resource and widget queries for completion proof.',
|
|
1688
|
+
requiresAuth: true,
|
|
1689
|
+
writes: false,
|
|
1690
|
+
},
|
|
1691
|
+
{
|
|
1692
|
+
command: 'tender app db resources create <artifact-id> --dashboard <dashboard-id> --spec resource.json --dry-run --json',
|
|
1693
|
+
purpose: 'Preview or save a table-backed resource definition for a private app admin dashboard.',
|
|
1694
|
+
requiresAuth: true,
|
|
1695
|
+
writes: true,
|
|
1696
|
+
dryRun: true,
|
|
1697
|
+
},
|
|
1698
|
+
{
|
|
1699
|
+
command: 'tender app db widgets create <artifact-id> --dashboard <dashboard-id> --title <title> --spec widget.json --dry-run --json',
|
|
1700
|
+
purpose: 'Preview or save a bounded widget definition for a private app admin dashboard.',
|
|
1701
|
+
requiresAuth: true,
|
|
1702
|
+
writes: true,
|
|
1703
|
+
dryRun: true,
|
|
1704
|
+
},
|
|
1614
1705
|
],
|
|
1615
1706
|
security: [
|
|
1616
1707
|
'Prefer artifact-scoped device tokens.',
|
|
@@ -5414,6 +5505,239 @@ function parseDbQueryArgs(args) {
|
|
|
5414
5505
|
...parsed,
|
|
5415
5506
|
};
|
|
5416
5507
|
}
|
|
5508
|
+
function parseDbDashboardsArgs(args) {
|
|
5509
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
5510
|
+
return { command: 'help', topic: 'app db dashboards' };
|
|
5511
|
+
}
|
|
5512
|
+
const artifactId = args[0];
|
|
5513
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
5514
|
+
throw new TenderCliUsageError('app id is required. Example: tender app db dashboards <app-id> --json');
|
|
5515
|
+
}
|
|
5516
|
+
const parsed = parseDbCommonArgs(args, 1);
|
|
5517
|
+
let name = null;
|
|
5518
|
+
let defaultEnvironment = null;
|
|
5519
|
+
let dryRun = false;
|
|
5520
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
5521
|
+
const arg = parsed.remaining[index];
|
|
5522
|
+
const defaultEnvAssignment = parseFlagAssignment(arg, '--default-env');
|
|
5523
|
+
if (defaultEnvAssignment !== null) {
|
|
5524
|
+
defaultEnvironment = parseDbEnvironment(defaultEnvAssignment);
|
|
5525
|
+
continue;
|
|
5526
|
+
}
|
|
5527
|
+
if (arg === '--create') {
|
|
5528
|
+
name = parseRequiredFlagValue(parsed.remaining[index + 1], '--create');
|
|
5529
|
+
index += 1;
|
|
5530
|
+
continue;
|
|
5531
|
+
}
|
|
5532
|
+
if (arg === '--default-env') {
|
|
5533
|
+
defaultEnvironment = parseDbEnvironment(parsed.remaining[index + 1]);
|
|
5534
|
+
index += 1;
|
|
5535
|
+
continue;
|
|
5536
|
+
}
|
|
5537
|
+
if (arg === '--dry-run') {
|
|
5538
|
+
dryRun = true;
|
|
5539
|
+
continue;
|
|
5540
|
+
}
|
|
5541
|
+
throw new TenderCliUsageError(`Unknown app db dashboards option: ${arg}`);
|
|
5542
|
+
}
|
|
5543
|
+
if (dryRun && !name) {
|
|
5544
|
+
throw new TenderCliUsageError('--dry-run is only valid with --create. Example: tender app db dashboards artifact_123 --create "App admin" --dry-run --json');
|
|
5545
|
+
}
|
|
5546
|
+
return { command: 'app db dashboards', artifactId, name, defaultEnvironment, dryRun, ...parsed };
|
|
5547
|
+
}
|
|
5548
|
+
function parseDbDashboardsRenderArgs(args) {
|
|
5549
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
5550
|
+
return { command: 'help', topic: 'app db dashboards render' };
|
|
5551
|
+
}
|
|
5552
|
+
const artifactId = args[0];
|
|
5553
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
5554
|
+
throw new TenderCliUsageError('app id is required. Example: tender app db dashboards render <app-id> --dashboard app_db_dashboard_123 --env published --json');
|
|
5555
|
+
}
|
|
5556
|
+
const parsed = parseDbCommonArgs(args, 1);
|
|
5557
|
+
let dashboardId = null;
|
|
5558
|
+
let environment = null;
|
|
5559
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
5560
|
+
const arg = parsed.remaining[index];
|
|
5561
|
+
const dashboardAssignment = parseFlagAssignment(arg, '--dashboard');
|
|
5562
|
+
if (dashboardAssignment !== null) {
|
|
5563
|
+
dashboardId = parseRequiredFlagValue(dashboardAssignment, '--dashboard');
|
|
5564
|
+
continue;
|
|
5565
|
+
}
|
|
5566
|
+
const envAssignment = parseFlagAssignment(arg, '--env');
|
|
5567
|
+
if (envAssignment !== null) {
|
|
5568
|
+
environment = parseDbEnvironment(envAssignment);
|
|
5569
|
+
continue;
|
|
5570
|
+
}
|
|
5571
|
+
if (arg === '--dashboard') {
|
|
5572
|
+
dashboardId = parseRequiredFlagValue(parsed.remaining[index + 1], '--dashboard');
|
|
5573
|
+
index += 1;
|
|
5574
|
+
continue;
|
|
5575
|
+
}
|
|
5576
|
+
if (arg === '--env') {
|
|
5577
|
+
environment = parseDbEnvironment(parsed.remaining[index + 1]);
|
|
5578
|
+
index += 1;
|
|
5579
|
+
continue;
|
|
5580
|
+
}
|
|
5581
|
+
throw new TenderCliUsageError(`Unknown app db dashboards render option: ${arg}`);
|
|
5582
|
+
}
|
|
5583
|
+
if (!dashboardId) {
|
|
5584
|
+
throw new TenderCliUsageError('--dashboard is required. Example: tender app db dashboards render artifact_123 --dashboard app_db_dashboard_123 --env published --json');
|
|
5585
|
+
}
|
|
5586
|
+
if (!environment) {
|
|
5587
|
+
throw new TenderCliUsageError('--env is required. Example: tender app db dashboards render artifact_123 --dashboard app_db_dashboard_123 --env published --json');
|
|
5588
|
+
}
|
|
5589
|
+
return { command: 'app db dashboards render', artifactId, dashboardId, environment, ...parsed };
|
|
5590
|
+
}
|
|
5591
|
+
function parseDbResourcesSuggestArgs(args) {
|
|
5592
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
5593
|
+
return { command: 'help', topic: 'app db resources suggest' };
|
|
5594
|
+
}
|
|
5595
|
+
const artifactId = args[0];
|
|
5596
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
5597
|
+
throw new TenderCliUsageError('app id is required. Example: tender app db resources suggest <app-id> --env published --json');
|
|
5598
|
+
}
|
|
5599
|
+
const parsed = parseDbCommonArgs(args, 1);
|
|
5600
|
+
let environment = null;
|
|
5601
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
5602
|
+
const arg = parsed.remaining[index];
|
|
5603
|
+
const envAssignment = parseFlagAssignment(arg, '--env');
|
|
5604
|
+
if (envAssignment !== null) {
|
|
5605
|
+
environment = parseDbEnvironment(envAssignment);
|
|
5606
|
+
continue;
|
|
5607
|
+
}
|
|
5608
|
+
if (arg === '--env') {
|
|
5609
|
+
environment = parseDbEnvironment(parsed.remaining[index + 1]);
|
|
5610
|
+
index += 1;
|
|
5611
|
+
continue;
|
|
5612
|
+
}
|
|
5613
|
+
throw new TenderCliUsageError(`Unknown app db resources suggest option: ${arg}`);
|
|
5614
|
+
}
|
|
5615
|
+
if (!environment) {
|
|
5616
|
+
throw new TenderCliUsageError('--env is required. Example: tender app db resources suggest artifact_123 --env published --json');
|
|
5617
|
+
}
|
|
5618
|
+
return { command: 'app db resources suggest', artifactId, environment, ...parsed };
|
|
5619
|
+
}
|
|
5620
|
+
function parseDbResourcesCreateArgs(args) {
|
|
5621
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
5622
|
+
return { command: 'help', topic: 'app db resources create' };
|
|
5623
|
+
}
|
|
5624
|
+
const artifactId = args[0];
|
|
5625
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
5626
|
+
throw new TenderCliUsageError('app id is required. Example: tender app db resources create <app-id> --dashboard app_db_dashboard_123 --spec resource.json --json');
|
|
5627
|
+
}
|
|
5628
|
+
const parsed = parseDbCommonArgs(args, 1);
|
|
5629
|
+
let dashboardId = null;
|
|
5630
|
+
let specPath = null;
|
|
5631
|
+
const viewPaths = [];
|
|
5632
|
+
let dryRun = false;
|
|
5633
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
5634
|
+
const arg = parsed.remaining[index];
|
|
5635
|
+
const dashboardAssignment = parseFlagAssignment(arg, '--dashboard');
|
|
5636
|
+
if (dashboardAssignment !== null) {
|
|
5637
|
+
dashboardId = parseRequiredFlagValue(dashboardAssignment, '--dashboard');
|
|
5638
|
+
continue;
|
|
5639
|
+
}
|
|
5640
|
+
const specAssignment = parseFlagAssignment(arg, '--spec');
|
|
5641
|
+
if (specAssignment !== null) {
|
|
5642
|
+
specPath = parseSpecPath(specAssignment);
|
|
5643
|
+
continue;
|
|
5644
|
+
}
|
|
5645
|
+
const viewAssignment = parseFlagAssignment(arg, '--view');
|
|
5646
|
+
if (viewAssignment !== null) {
|
|
5647
|
+
viewPaths.push(parseSpecPath(viewAssignment));
|
|
5648
|
+
continue;
|
|
5649
|
+
}
|
|
5650
|
+
if (arg === '--dashboard') {
|
|
5651
|
+
dashboardId = parseRequiredFlagValue(parsed.remaining[index + 1], '--dashboard');
|
|
5652
|
+
index += 1;
|
|
5653
|
+
continue;
|
|
5654
|
+
}
|
|
5655
|
+
if (arg === '--spec') {
|
|
5656
|
+
specPath = parseSpecPath(parsed.remaining[index + 1]);
|
|
5657
|
+
index += 1;
|
|
5658
|
+
continue;
|
|
5659
|
+
}
|
|
5660
|
+
if (arg === '--view') {
|
|
5661
|
+
viewPaths.push(parseSpecPath(parsed.remaining[index + 1]));
|
|
5662
|
+
index += 1;
|
|
5663
|
+
continue;
|
|
5664
|
+
}
|
|
5665
|
+
if (arg === '--dry-run') {
|
|
5666
|
+
dryRun = true;
|
|
5667
|
+
continue;
|
|
5668
|
+
}
|
|
5669
|
+
throw new TenderCliUsageError(`Unknown app db resources create option: ${arg}`);
|
|
5670
|
+
}
|
|
5671
|
+
if (!dashboardId) {
|
|
5672
|
+
throw new TenderCliUsageError('--dashboard is required. Example: tender app db resources create artifact_123 --dashboard app_db_dashboard_123 --spec resource.json --json');
|
|
5673
|
+
}
|
|
5674
|
+
if (!specPath) {
|
|
5675
|
+
throw new TenderCliUsageError('--spec is required. Example: tender app db resources create artifact_123 --dashboard app_db_dashboard_123 --spec resource.json --json');
|
|
5676
|
+
}
|
|
5677
|
+
return { command: 'app db resources create', artifactId, dashboardId, specPath, viewPaths, dryRun, ...parsed };
|
|
5678
|
+
}
|
|
5679
|
+
function parseDbWidgetsCreateArgs(args) {
|
|
5680
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
5681
|
+
return { command: 'help', topic: 'app db widgets create' };
|
|
5682
|
+
}
|
|
5683
|
+
const artifactId = args[0];
|
|
5684
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
5685
|
+
throw new TenderCliUsageError('app id is required. Example: tender app db widgets create <app-id> --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --json');
|
|
5686
|
+
}
|
|
5687
|
+
const parsed = parseDbCommonArgs(args, 1);
|
|
5688
|
+
let dashboardId = null;
|
|
5689
|
+
let title = null;
|
|
5690
|
+
let specPath = null;
|
|
5691
|
+
let dryRun = false;
|
|
5692
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
5693
|
+
const arg = parsed.remaining[index];
|
|
5694
|
+
const dashboardAssignment = parseFlagAssignment(arg, '--dashboard');
|
|
5695
|
+
if (dashboardAssignment !== null) {
|
|
5696
|
+
dashboardId = parseRequiredFlagValue(dashboardAssignment, '--dashboard');
|
|
5697
|
+
continue;
|
|
5698
|
+
}
|
|
5699
|
+
const titleAssignment = parseFlagAssignment(arg, '--title');
|
|
5700
|
+
if (titleAssignment !== null) {
|
|
5701
|
+
title = parseRequiredFlagValue(titleAssignment, '--title');
|
|
5702
|
+
continue;
|
|
5703
|
+
}
|
|
5704
|
+
const specAssignment = parseFlagAssignment(arg, '--spec');
|
|
5705
|
+
if (specAssignment !== null) {
|
|
5706
|
+
specPath = parseSpecPath(specAssignment);
|
|
5707
|
+
continue;
|
|
5708
|
+
}
|
|
5709
|
+
if (arg === '--dashboard') {
|
|
5710
|
+
dashboardId = parseRequiredFlagValue(parsed.remaining[index + 1], '--dashboard');
|
|
5711
|
+
index += 1;
|
|
5712
|
+
continue;
|
|
5713
|
+
}
|
|
5714
|
+
if (arg === '--title') {
|
|
5715
|
+
title = parseRequiredFlagValue(parsed.remaining[index + 1], '--title');
|
|
5716
|
+
index += 1;
|
|
5717
|
+
continue;
|
|
5718
|
+
}
|
|
5719
|
+
if (arg === '--spec') {
|
|
5720
|
+
specPath = parseSpecPath(parsed.remaining[index + 1]);
|
|
5721
|
+
index += 1;
|
|
5722
|
+
continue;
|
|
5723
|
+
}
|
|
5724
|
+
if (arg === '--dry-run') {
|
|
5725
|
+
dryRun = true;
|
|
5726
|
+
continue;
|
|
5727
|
+
}
|
|
5728
|
+
throw new TenderCliUsageError(`Unknown app db widgets create option: ${arg}`);
|
|
5729
|
+
}
|
|
5730
|
+
if (!dashboardId) {
|
|
5731
|
+
throw new TenderCliUsageError('--dashboard is required. Example: tender app db widgets create artifact_123 --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --json');
|
|
5732
|
+
}
|
|
5733
|
+
if (!title) {
|
|
5734
|
+
throw new TenderCliUsageError('--title is required. Example: tender app db widgets create artifact_123 --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --json');
|
|
5735
|
+
}
|
|
5736
|
+
if (!specPath) {
|
|
5737
|
+
throw new TenderCliUsageError('--spec is required. Example: tender app db widgets create artifact_123 --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --json');
|
|
5738
|
+
}
|
|
5739
|
+
return { command: 'app db widgets create', artifactId, dashboardId, title, specPath, dryRun, ...parsed };
|
|
5740
|
+
}
|
|
5417
5741
|
const AGENT_HEARTBEAT_SOURCE_VALUES = new Set(['codex', 'claude-code', 'tender-cli', 'git', 'other']);
|
|
5418
5742
|
const AGENT_HEARTBEAT_STATUS_VALUES = new Set(['connected', 'working', 'waiting_for_user', 'needs_attention']);
|
|
5419
5743
|
const AGENT_HEARTBEAT_PHASE_VALUES = new Set([
|
|
@@ -5859,6 +6183,31 @@ function parseTenderArgs(args) {
|
|
|
5859
6183
|
if (resourceArgs[1] === 'db' && resourceArgs[2] === 'query') {
|
|
5860
6184
|
return parseDbQueryArgs(resourceArgs.slice(3));
|
|
5861
6185
|
}
|
|
6186
|
+
if (resourceArgs[1] === 'db' && resourceArgs[2] === 'dashboards' && resourceArgs[3] === 'render') {
|
|
6187
|
+
return parseDbDashboardsRenderArgs(resourceArgs.slice(4));
|
|
6188
|
+
}
|
|
6189
|
+
if (resourceArgs[1] === 'db' && resourceArgs[2] === 'dashboards') {
|
|
6190
|
+
return parseDbDashboardsArgs(resourceArgs.slice(3));
|
|
6191
|
+
}
|
|
6192
|
+
if (resourceArgs[1] === 'db' &&
|
|
6193
|
+
resourceArgs[2] === 'resources' &&
|
|
6194
|
+
(resourceArgs[3] === undefined || resourceArgs[3] === '--help' || resourceArgs[3] === '-h')) {
|
|
6195
|
+
return { command: 'help', topic: 'app db resources create' };
|
|
6196
|
+
}
|
|
6197
|
+
if (resourceArgs[1] === 'db' && resourceArgs[2] === 'resources' && resourceArgs[3] === 'suggest') {
|
|
6198
|
+
return parseDbResourcesSuggestArgs(resourceArgs.slice(4));
|
|
6199
|
+
}
|
|
6200
|
+
if (resourceArgs[1] === 'db' && resourceArgs[2] === 'resources' && resourceArgs[3] === 'create') {
|
|
6201
|
+
return parseDbResourcesCreateArgs(resourceArgs.slice(4));
|
|
6202
|
+
}
|
|
6203
|
+
if (resourceArgs[1] === 'db' &&
|
|
6204
|
+
resourceArgs[2] === 'widgets' &&
|
|
6205
|
+
(resourceArgs[3] === undefined || resourceArgs[3] === '--help' || resourceArgs[3] === '-h')) {
|
|
6206
|
+
return { command: 'help', topic: 'app db widgets create' };
|
|
6207
|
+
}
|
|
6208
|
+
if (resourceArgs[1] === 'db' && resourceArgs[2] === 'widgets' && resourceArgs[3] === 'create') {
|
|
6209
|
+
return parseDbWidgetsCreateArgs(resourceArgs.slice(4));
|
|
6210
|
+
}
|
|
5862
6211
|
if (resourceArgs[1] === 'db') {
|
|
5863
6212
|
throw new TenderCliUsageError(`Unknown app db command: ${resourceArgs[2]}`);
|
|
5864
6213
|
}
|
|
@@ -6300,6 +6649,66 @@ async function requestDbApi(input) {
|
|
|
6300
6649
|
}
|
|
6301
6650
|
return payload;
|
|
6302
6651
|
}
|
|
6652
|
+
function appDbDashboardApiSuffix(input) {
|
|
6653
|
+
if (input.path === 'dashboards') {
|
|
6654
|
+
return '';
|
|
6655
|
+
}
|
|
6656
|
+
if (input.path === 'suggest') {
|
|
6657
|
+
return '/suggest';
|
|
6658
|
+
}
|
|
6659
|
+
if (!input.dashboardId) {
|
|
6660
|
+
throw new TenderCliUsageError('--dashboard is required.');
|
|
6661
|
+
}
|
|
6662
|
+
if (input.path === 'dashboard') {
|
|
6663
|
+
return `/${encodeURIComponent(input.dashboardId)}`;
|
|
6664
|
+
}
|
|
6665
|
+
if (input.path === 'resources') {
|
|
6666
|
+
return `/${encodeURIComponent(input.dashboardId)}/resources`;
|
|
6667
|
+
}
|
|
6668
|
+
if (input.path === 'resourceQuery') {
|
|
6669
|
+
if (!input.resourceId) {
|
|
6670
|
+
throw new TenderCliUsageError('--resource is required.');
|
|
6671
|
+
}
|
|
6672
|
+
return `/${encodeURIComponent(input.dashboardId)}/resources/${encodeURIComponent(input.resourceId)}/query`;
|
|
6673
|
+
}
|
|
6674
|
+
if (input.path === 'widgets') {
|
|
6675
|
+
return `/${encodeURIComponent(input.dashboardId)}/widgets`;
|
|
6676
|
+
}
|
|
6677
|
+
if (!input.widgetId) {
|
|
6678
|
+
throw new TenderCliUsageError('--widget is required.');
|
|
6679
|
+
}
|
|
6680
|
+
return `/${encodeURIComponent(input.dashboardId)}/widgets/${encodeURIComponent(input.widgetId)}/query`;
|
|
6681
|
+
}
|
|
6682
|
+
async function requestAppDbDashboardApi(input) {
|
|
6683
|
+
const suffix = appDbDashboardApiSuffix(input);
|
|
6684
|
+
const response = await input.fetcher(`${input.baseUrl}/api/v1/artifacts/${encodeURIComponent(input.artifactId)}/app-db-dashboards${suffix}`, {
|
|
6685
|
+
method: input.method,
|
|
6686
|
+
headers: {
|
|
6687
|
+
authorization: `Bearer ${input.token}`,
|
|
6688
|
+
accept: 'application/json',
|
|
6689
|
+
...(input.body === undefined ? {} : { 'content-type': 'application/json' }),
|
|
6690
|
+
},
|
|
6691
|
+
body: input.body === undefined ? undefined : JSON.stringify(input.body),
|
|
6692
|
+
});
|
|
6693
|
+
const payload = await parseJsonResponse(response);
|
|
6694
|
+
if (!response.ok || !payload?.ok) {
|
|
6695
|
+
const reasonCode = (typeof payload?.reasonCode === 'string' && payload.reasonCode) || `http_${response.status}`;
|
|
6696
|
+
const message = (typeof payload?.message === 'string' && payload.message) ||
|
|
6697
|
+
(typeof payload?.reasonCode === 'string' && payload.reasonCode) ||
|
|
6698
|
+
`App database dashboard request failed with HTTP ${response.status}.`;
|
|
6699
|
+
const example = appDbDashboardCorrectedExample(input.artifactId, input.path);
|
|
6700
|
+
throw new TenderCliDbApiError([
|
|
6701
|
+
message,
|
|
6702
|
+
`Scope: artifact ${input.artifactId}, app-db-dashboards.`,
|
|
6703
|
+
`Reason: ${reasonCode}.`,
|
|
6704
|
+
`Example: ${example}.`,
|
|
6705
|
+
].join('\n'), reasonCode, {
|
|
6706
|
+
artifactId: input.artifactId,
|
|
6707
|
+
surface: 'db/dashboards',
|
|
6708
|
+
}, example);
|
|
6709
|
+
}
|
|
6710
|
+
return payload;
|
|
6711
|
+
}
|
|
6303
6712
|
function encodePathSegments(filePath) {
|
|
6304
6713
|
return filePath.split('/').map(encodeURIComponent).join('/');
|
|
6305
6714
|
}
|
|
@@ -6341,6 +6750,21 @@ function dbCorrectedExample(artifactId, dbPath) {
|
|
|
6341
6750
|
}
|
|
6342
6751
|
return `tender app db query ${artifactId} --env published --sql "select * from <table-name> limit 20" --json`;
|
|
6343
6752
|
}
|
|
6753
|
+
function appDbDashboardCorrectedExample(artifactId, path) {
|
|
6754
|
+
if (path === 'suggest') {
|
|
6755
|
+
return `tender app db resources suggest ${artifactId} --env published --json`;
|
|
6756
|
+
}
|
|
6757
|
+
if (path === 'dashboard' || path === 'resourceQuery' || path === 'widgetQuery') {
|
|
6758
|
+
return `tender app db dashboards render ${artifactId} --dashboard app_db_dashboard_123 --env published --json`;
|
|
6759
|
+
}
|
|
6760
|
+
if (path === 'resources') {
|
|
6761
|
+
return `tender app db resources create ${artifactId} --dashboard app_db_dashboard_123 --spec resource.json --json`;
|
|
6762
|
+
}
|
|
6763
|
+
if (path === 'widgets') {
|
|
6764
|
+
return `tender app db widgets create ${artifactId} --dashboard app_db_dashboard_123 --title "Total signups" --spec widget.json --json`;
|
|
6765
|
+
}
|
|
6766
|
+
return `tender app db dashboards ${artifactId} --json`;
|
|
6767
|
+
}
|
|
6344
6768
|
function analyticsCorrectedExample(artifactId, analyticsPath) {
|
|
6345
6769
|
if (analyticsPath === 'capabilities') {
|
|
6346
6770
|
return `tender app analytics capabilities ${artifactId} --include-catalog --range 30d --json`;
|
|
@@ -9755,6 +10179,310 @@ async function runDbQuery(command, io, runtime) {
|
|
|
9755
10179
|
]);
|
|
9756
10180
|
return 0;
|
|
9757
10181
|
}
|
|
10182
|
+
async function runDbDashboards(command, io, runtime) {
|
|
10183
|
+
if (command.name && command.dryRun) {
|
|
10184
|
+
const payload = {
|
|
10185
|
+
ok: true,
|
|
10186
|
+
dryRun: true,
|
|
10187
|
+
artifactId: command.artifactId,
|
|
10188
|
+
request: {
|
|
10189
|
+
name: command.name,
|
|
10190
|
+
defaultEnvironment: command.defaultEnvironment ?? 'published',
|
|
10191
|
+
},
|
|
10192
|
+
};
|
|
10193
|
+
printDbPayload(command, io, payload, [`app_id: ${command.artifactId}`, `name: ${command.name}`, 'dry_run: true']);
|
|
10194
|
+
return 0;
|
|
10195
|
+
}
|
|
10196
|
+
const credentials = await resolveDbCredentials(command, runtime);
|
|
10197
|
+
const payload = await requestAppDbDashboardApi({
|
|
10198
|
+
artifactId: command.artifactId,
|
|
10199
|
+
path: 'dashboards',
|
|
10200
|
+
method: command.name ? 'POST' : 'GET',
|
|
10201
|
+
baseUrl: credentials.baseUrl,
|
|
10202
|
+
token: credentials.token,
|
|
10203
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10204
|
+
body: command.name
|
|
10205
|
+
? {
|
|
10206
|
+
name: command.name,
|
|
10207
|
+
defaultEnvironment: command.defaultEnvironment ?? 'published',
|
|
10208
|
+
}
|
|
10209
|
+
: undefined,
|
|
10210
|
+
});
|
|
10211
|
+
const dashboards = Array.isArray(payload.dashboards) ? payload.dashboards : [];
|
|
10212
|
+
printDbPayload(command, io, payload, [
|
|
10213
|
+
`app_id: ${command.artifactId}`,
|
|
10214
|
+
command.name
|
|
10215
|
+
? `dashboard_id: ${String(payload.dashboard?.id ?? '')}`
|
|
10216
|
+
: `dashboards: ${dashboards.length}`,
|
|
10217
|
+
]);
|
|
10218
|
+
return 0;
|
|
10219
|
+
}
|
|
10220
|
+
function isAppDbRecord(value) {
|
|
10221
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
10222
|
+
}
|
|
10223
|
+
function appDbRecordArray(value) {
|
|
10224
|
+
return Array.isArray(value) ? value.filter(isAppDbRecord) : [];
|
|
10225
|
+
}
|
|
10226
|
+
function appDbStringField(record, field) {
|
|
10227
|
+
const value = record[field];
|
|
10228
|
+
return typeof value === 'string' && value.trim() ? value : null;
|
|
10229
|
+
}
|
|
10230
|
+
function appDbResourcePageSize(resource) {
|
|
10231
|
+
const spec = isAppDbRecord(resource.spec) ? resource.spec : null;
|
|
10232
|
+
const pageSize = spec && typeof spec.pageSize === 'number' && Number.isInteger(spec.pageSize) ? spec.pageSize : 50;
|
|
10233
|
+
return Math.min(Math.max(pageSize, 1), 50);
|
|
10234
|
+
}
|
|
10235
|
+
function appDbDateRangeDays(value) {
|
|
10236
|
+
if (value === 'last_7_days') {
|
|
10237
|
+
return 7;
|
|
10238
|
+
}
|
|
10239
|
+
if (value === 'last_90_days') {
|
|
10240
|
+
return 90;
|
|
10241
|
+
}
|
|
10242
|
+
return 30;
|
|
10243
|
+
}
|
|
10244
|
+
function appDbDashboardDateRangeRequest(dashboard, now) {
|
|
10245
|
+
const spec = isAppDbRecord(dashboard.spec) ? dashboard.spec : {};
|
|
10246
|
+
const controls = isAppDbRecord(spec.controls) ? spec.controls : {};
|
|
10247
|
+
const dateRange = isAppDbRecord(controls.dateRange) ? controls.dateRange : {};
|
|
10248
|
+
const enabled = dateRange.enabled === undefined ? true : dateRange.enabled === true;
|
|
10249
|
+
const defaultRange = dateRange.defaultRange ?? 'last_30_days';
|
|
10250
|
+
if (enabled &&
|
|
10251
|
+
isAppDbRecord(defaultRange) &&
|
|
10252
|
+
defaultRange.type === 'fixed' &&
|
|
10253
|
+
(typeof defaultRange.start === 'string' || typeof defaultRange.start === 'number') &&
|
|
10254
|
+
(typeof defaultRange.end === 'string' || typeof defaultRange.end === 'number')) {
|
|
10255
|
+
return { start: defaultRange.start, end: defaultRange.end };
|
|
10256
|
+
}
|
|
10257
|
+
const preset = typeof defaultRange === 'string' ? defaultRange : 'last_30_days';
|
|
10258
|
+
if (!enabled || preset === 'all_time') {
|
|
10259
|
+
return null;
|
|
10260
|
+
}
|
|
10261
|
+
const end = new Date(now);
|
|
10262
|
+
const start = new Date(end);
|
|
10263
|
+
start.setDate(start.getDate() - (appDbDateRangeDays(preset) - 1));
|
|
10264
|
+
return {
|
|
10265
|
+
start: start.toISOString().slice(0, 10),
|
|
10266
|
+
end: end.toISOString().slice(0, 10),
|
|
10267
|
+
};
|
|
10268
|
+
}
|
|
10269
|
+
async function runDbResourcesSuggest(command, io, runtime) {
|
|
10270
|
+
const credentials = await resolveDbCredentials(command, runtime);
|
|
10271
|
+
const payload = await requestAppDbDashboardApi({
|
|
10272
|
+
artifactId: command.artifactId,
|
|
10273
|
+
path: 'suggest',
|
|
10274
|
+
method: 'POST',
|
|
10275
|
+
baseUrl: credentials.baseUrl,
|
|
10276
|
+
token: credentials.token,
|
|
10277
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10278
|
+
body: { environment: command.environment },
|
|
10279
|
+
});
|
|
10280
|
+
const resources = Array.isArray(payload.resources) ? payload.resources : [];
|
|
10281
|
+
printDbPayload(command, io, payload, [
|
|
10282
|
+
`app_id: ${command.artifactId}`,
|
|
10283
|
+
`environment: ${command.environment}`,
|
|
10284
|
+
`suggested_resources: ${resources.length}`,
|
|
10285
|
+
]);
|
|
10286
|
+
return 0;
|
|
10287
|
+
}
|
|
10288
|
+
async function runDbDashboardsRender(command, io, runtime) {
|
|
10289
|
+
const credentials = await resolveDbCredentials(command, runtime);
|
|
10290
|
+
const dashboardPayload = await requestAppDbDashboardApi({
|
|
10291
|
+
artifactId: command.artifactId,
|
|
10292
|
+
path: 'dashboard',
|
|
10293
|
+
dashboardId: command.dashboardId,
|
|
10294
|
+
method: 'GET',
|
|
10295
|
+
baseUrl: credentials.baseUrl,
|
|
10296
|
+
token: credentials.token,
|
|
10297
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10298
|
+
});
|
|
10299
|
+
const dashboard = isAppDbRecord(dashboardPayload.dashboard) ? dashboardPayload.dashboard : {};
|
|
10300
|
+
const dateRange = appDbDashboardDateRangeRequest(dashboard, runtime.now ? runtime.now() : Date.now());
|
|
10301
|
+
const resources = appDbRecordArray(dashboard.resources).slice(0, 6);
|
|
10302
|
+
const widgets = appDbRecordArray(dashboard.widgets).slice(0, 12);
|
|
10303
|
+
const resourceResults = [];
|
|
10304
|
+
for (const resource of resources) {
|
|
10305
|
+
const resourceId = appDbStringField(resource, 'id');
|
|
10306
|
+
if (!resourceId) {
|
|
10307
|
+
continue;
|
|
10308
|
+
}
|
|
10309
|
+
const views = [
|
|
10310
|
+
{ viewId: null, name: 'Default' },
|
|
10311
|
+
...appDbRecordArray(resource.views)
|
|
10312
|
+
.slice(0, 4)
|
|
10313
|
+
.map((view) => ({
|
|
10314
|
+
viewId: appDbStringField(view, 'id'),
|
|
10315
|
+
name: appDbStringField(view, 'name') ?? 'Saved view',
|
|
10316
|
+
}))
|
|
10317
|
+
.filter((view) => view.viewId !== null),
|
|
10318
|
+
];
|
|
10319
|
+
const viewResults = [];
|
|
10320
|
+
for (const view of views) {
|
|
10321
|
+
const result = await requestAppDbDashboardApi({
|
|
10322
|
+
artifactId: command.artifactId,
|
|
10323
|
+
path: 'resourceQuery',
|
|
10324
|
+
dashboardId: command.dashboardId,
|
|
10325
|
+
resourceId,
|
|
10326
|
+
method: 'POST',
|
|
10327
|
+
baseUrl: credentials.baseUrl,
|
|
10328
|
+
token: credentials.token,
|
|
10329
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10330
|
+
body: {
|
|
10331
|
+
environment: command.environment,
|
|
10332
|
+
...(view.viewId ? { viewId: view.viewId } : {}),
|
|
10333
|
+
...(dateRange ? { dateRange } : {}),
|
|
10334
|
+
limit: appDbResourcePageSize(resource),
|
|
10335
|
+
offset: 0,
|
|
10336
|
+
},
|
|
10337
|
+
});
|
|
10338
|
+
viewResults.push({
|
|
10339
|
+
viewId: view.viewId,
|
|
10340
|
+
name: view.name,
|
|
10341
|
+
result,
|
|
10342
|
+
});
|
|
10343
|
+
}
|
|
10344
|
+
resourceResults.push({
|
|
10345
|
+
resourceId,
|
|
10346
|
+
label: appDbStringField(resource, 'label') ?? resourceId,
|
|
10347
|
+
views: viewResults,
|
|
10348
|
+
});
|
|
10349
|
+
}
|
|
10350
|
+
const widgetResults = [];
|
|
10351
|
+
for (const widget of widgets) {
|
|
10352
|
+
const widgetId = appDbStringField(widget, 'id');
|
|
10353
|
+
if (!widgetId) {
|
|
10354
|
+
continue;
|
|
10355
|
+
}
|
|
10356
|
+
const spec = isAppDbRecord(widget.spec) ? widget.spec : null;
|
|
10357
|
+
const specLimit = spec && typeof spec.limit === 'number' && Number.isInteger(spec.limit) ? spec.limit : 20;
|
|
10358
|
+
const result = await requestAppDbDashboardApi({
|
|
10359
|
+
artifactId: command.artifactId,
|
|
10360
|
+
path: 'widgetQuery',
|
|
10361
|
+
dashboardId: command.dashboardId,
|
|
10362
|
+
widgetId,
|
|
10363
|
+
method: 'POST',
|
|
10364
|
+
baseUrl: credentials.baseUrl,
|
|
10365
|
+
token: credentials.token,
|
|
10366
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10367
|
+
body: {
|
|
10368
|
+
environment: command.environment,
|
|
10369
|
+
...(dateRange ? { dateRange } : {}),
|
|
10370
|
+
limit: Math.min(Math.max(specLimit, 1), 50),
|
|
10371
|
+
offset: 0,
|
|
10372
|
+
},
|
|
10373
|
+
});
|
|
10374
|
+
widgetResults.push({
|
|
10375
|
+
widgetId,
|
|
10376
|
+
title: appDbStringField(widget, 'title') ?? widgetId,
|
|
10377
|
+
result,
|
|
10378
|
+
});
|
|
10379
|
+
}
|
|
10380
|
+
const payload = {
|
|
10381
|
+
ok: true,
|
|
10382
|
+
artifactId: command.artifactId,
|
|
10383
|
+
environment: command.environment,
|
|
10384
|
+
dashboard,
|
|
10385
|
+
resources: resourceResults,
|
|
10386
|
+
widgets: widgetResults,
|
|
10387
|
+
};
|
|
10388
|
+
printDbPayload(command, io, payload, [
|
|
10389
|
+
`app_id: ${command.artifactId}`,
|
|
10390
|
+
`dashboard_id: ${command.dashboardId}`,
|
|
10391
|
+
`environment: ${command.environment}`,
|
|
10392
|
+
`resources_rendered: ${resourceResults.length}`,
|
|
10393
|
+
`widgets_rendered: ${widgetResults.length}`,
|
|
10394
|
+
]);
|
|
10395
|
+
return 0;
|
|
10396
|
+
}
|
|
10397
|
+
async function readJsonSpecFile(input) {
|
|
10398
|
+
return readAnalyticsSpec({
|
|
10399
|
+
specPath: input.specPath,
|
|
10400
|
+
stdin: input.stdin,
|
|
10401
|
+
});
|
|
10402
|
+
}
|
|
10403
|
+
async function runDbResourcesCreate(command, io, runtime) {
|
|
10404
|
+
const spec = await readJsonSpecFile({
|
|
10405
|
+
specPath: command.specPath,
|
|
10406
|
+
stdin: runtime.stdin,
|
|
10407
|
+
});
|
|
10408
|
+
const views = [];
|
|
10409
|
+
for (const viewPath of command.viewPaths) {
|
|
10410
|
+
views.push({
|
|
10411
|
+
spec: await readJsonSpecFile({
|
|
10412
|
+
specPath: viewPath,
|
|
10413
|
+
stdin: runtime.stdin,
|
|
10414
|
+
}),
|
|
10415
|
+
});
|
|
10416
|
+
}
|
|
10417
|
+
const body = {
|
|
10418
|
+
spec,
|
|
10419
|
+
...(views.length > 0 ? { views } : {}),
|
|
10420
|
+
};
|
|
10421
|
+
if (command.dryRun) {
|
|
10422
|
+
printDbPayload(command, io, {
|
|
10423
|
+
ok: true,
|
|
10424
|
+
dryRun: true,
|
|
10425
|
+
artifactId: command.artifactId,
|
|
10426
|
+
dashboardId: command.dashboardId,
|
|
10427
|
+
request: body,
|
|
10428
|
+
}, [`app_id: ${command.artifactId}`, `dashboard_id: ${command.dashboardId}`, 'dry_run: true']);
|
|
10429
|
+
return 0;
|
|
10430
|
+
}
|
|
10431
|
+
const credentials = await resolveDbCredentials(command, runtime);
|
|
10432
|
+
const payload = await requestAppDbDashboardApi({
|
|
10433
|
+
artifactId: command.artifactId,
|
|
10434
|
+
path: 'resources',
|
|
10435
|
+
dashboardId: command.dashboardId,
|
|
10436
|
+
method: 'POST',
|
|
10437
|
+
baseUrl: credentials.baseUrl,
|
|
10438
|
+
token: credentials.token,
|
|
10439
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10440
|
+
body,
|
|
10441
|
+
});
|
|
10442
|
+
printDbPayload(command, io, payload, [
|
|
10443
|
+
`app_id: ${command.artifactId}`,
|
|
10444
|
+
`dashboard_id: ${command.dashboardId}`,
|
|
10445
|
+
`resource_id: ${String(payload.resource?.id ?? '')}`,
|
|
10446
|
+
]);
|
|
10447
|
+
return 0;
|
|
10448
|
+
}
|
|
10449
|
+
async function runDbWidgetsCreate(command, io, runtime) {
|
|
10450
|
+
const spec = await readJsonSpecFile({
|
|
10451
|
+
specPath: command.specPath,
|
|
10452
|
+
stdin: runtime.stdin,
|
|
10453
|
+
});
|
|
10454
|
+
const body = {
|
|
10455
|
+
title: command.title,
|
|
10456
|
+
spec,
|
|
10457
|
+
};
|
|
10458
|
+
if (command.dryRun) {
|
|
10459
|
+
printDbPayload(command, io, {
|
|
10460
|
+
ok: true,
|
|
10461
|
+
dryRun: true,
|
|
10462
|
+
artifactId: command.artifactId,
|
|
10463
|
+
dashboardId: command.dashboardId,
|
|
10464
|
+
request: body,
|
|
10465
|
+
}, [`app_id: ${command.artifactId}`, `dashboard_id: ${command.dashboardId}`, `title: ${command.title}`, 'dry_run: true']);
|
|
10466
|
+
return 0;
|
|
10467
|
+
}
|
|
10468
|
+
const credentials = await resolveDbCredentials(command, runtime);
|
|
10469
|
+
const payload = await requestAppDbDashboardApi({
|
|
10470
|
+
artifactId: command.artifactId,
|
|
10471
|
+
path: 'widgets',
|
|
10472
|
+
dashboardId: command.dashboardId,
|
|
10473
|
+
method: 'POST',
|
|
10474
|
+
baseUrl: credentials.baseUrl,
|
|
10475
|
+
token: credentials.token,
|
|
10476
|
+
fetcher: runtime.fetcher ?? fetch,
|
|
10477
|
+
body,
|
|
10478
|
+
});
|
|
10479
|
+
printDbPayload(command, io, payload, [
|
|
10480
|
+
`app_id: ${command.artifactId}`,
|
|
10481
|
+
`dashboard_id: ${command.dashboardId}`,
|
|
10482
|
+
`widget_id: ${String(payload.widget?.id ?? '')}`,
|
|
10483
|
+
]);
|
|
10484
|
+
return 0;
|
|
10485
|
+
}
|
|
9758
10486
|
async function runAnalyticsSummary(command, io, runtime) {
|
|
9759
10487
|
const credentials = await resolveAnalyticsCredentials(command, runtime);
|
|
9760
10488
|
const payload = await requestAnalyticsApi({
|
|
@@ -10466,6 +11194,21 @@ export async function runTenderCli(args, io = {
|
|
|
10466
11194
|
else if (command.topic === 'app db query') {
|
|
10467
11195
|
io.stdout(dbQueryHelp());
|
|
10468
11196
|
}
|
|
11197
|
+
else if (command.topic === 'app db dashboards') {
|
|
11198
|
+
io.stdout(dbDashboardsHelp());
|
|
11199
|
+
}
|
|
11200
|
+
else if (command.topic === 'app db dashboards render') {
|
|
11201
|
+
io.stdout(dbDashboardsRenderHelp());
|
|
11202
|
+
}
|
|
11203
|
+
else if (command.topic === 'app db resources suggest') {
|
|
11204
|
+
io.stdout(dbResourcesSuggestHelp());
|
|
11205
|
+
}
|
|
11206
|
+
else if (command.topic === 'app db resources create') {
|
|
11207
|
+
io.stdout(dbResourcesCreateHelp());
|
|
11208
|
+
}
|
|
11209
|
+
else if (command.topic === 'app db widgets create') {
|
|
11210
|
+
io.stdout(dbWidgetsCreateHelp());
|
|
11211
|
+
}
|
|
10469
11212
|
else if (command.topic === 'app') {
|
|
10470
11213
|
io.stdout(appHelp());
|
|
10471
11214
|
}
|
|
@@ -10657,6 +11400,21 @@ export async function runTenderCli(args, io = {
|
|
|
10657
11400
|
if (command.command === 'app db query') {
|
|
10658
11401
|
return await runDbQuery(command, io, runtime);
|
|
10659
11402
|
}
|
|
11403
|
+
if (command.command === 'app db dashboards') {
|
|
11404
|
+
return await runDbDashboards(command, io, runtime);
|
|
11405
|
+
}
|
|
11406
|
+
if (command.command === 'app db resources suggest') {
|
|
11407
|
+
return await runDbResourcesSuggest(command, io, runtime);
|
|
11408
|
+
}
|
|
11409
|
+
if (command.command === 'app db dashboards render') {
|
|
11410
|
+
return await runDbDashboardsRender(command, io, runtime);
|
|
11411
|
+
}
|
|
11412
|
+
if (command.command === 'app db resources create') {
|
|
11413
|
+
return await runDbResourcesCreate(command, io, runtime);
|
|
11414
|
+
}
|
|
11415
|
+
if (command.command === 'app db widgets create') {
|
|
11416
|
+
return await runDbWidgetsCreate(command, io, runtime);
|
|
11417
|
+
}
|
|
10660
11418
|
if (command.command === 'app git remote') {
|
|
10661
11419
|
return await runGitRemote(command, io, runtime);
|
|
10662
11420
|
}
|
|
@@ -10685,6 +11443,8 @@ export function shouldReadEntrypointStdin(args) {
|
|
|
10685
11443
|
((args[1] === 'git' && args[2] === 'credential') ||
|
|
10686
11444
|
(args[1] === 'analytics' && hasFlagValue(args, '--spec', '-')) ||
|
|
10687
11445
|
(args[1] === 'agent' && args[2] === 'heartbeat' && hasFlagValue(args, '--input', '-')) ||
|
|
11446
|
+
(args[1] === 'db' && (args[2] === 'resources' || args[2] === 'widgets') && hasFlagValue(args, '--spec', '-')) ||
|
|
11447
|
+
(args[1] === 'db' && args[2] === 'resources' && hasFlagValue(args, '--view', '-')) ||
|
|
10688
11448
|
(args[1] === 'db' && args[2] === 'query' && hasFlagValue(args, '--file', '-'))));
|
|
10689
11449
|
}
|
|
10690
11450
|
async function readEntrypointStdin() {
|