@tenderprompt/cli 0.1.24 → 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 +42 -0
- package/dist/artifact-memory.js +4 -3
- package/dist/index.js +1002 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,6 +34,61 @@ const CLI_PACKAGE_VERSION = readCliPackageVersion();
|
|
|
34
34
|
const ANALYTICS_AUTHORING_PLAYBOOK_ID = 'analytics-event-authoring';
|
|
35
35
|
const ANALYTICS_AUTHORING_COMMAND = 'tender app analytics authoring --json';
|
|
36
36
|
const ANALYTICS_AUTHORING_PLAYBOOK_COMMAND = `tender playbooks get ${ANALYTICS_AUTHORING_PLAYBOOK_ID} --json`;
|
|
37
|
+
const ANALYTICS_AGENT_GUIDANCE = {
|
|
38
|
+
generatedAppDelivery: {
|
|
39
|
+
defaultAnalytics: 'Generated apps should emit useful Tender analytics events and provision saved starter dashboard charts by default unless the user opts out or a concrete blocker is recorded.',
|
|
40
|
+
skipOnlyWhen: ['The user explicitly opts out.', 'A concrete platform or data blocker is reported.'],
|
|
41
|
+
},
|
|
42
|
+
replacementBaseline: {
|
|
43
|
+
requiredBeforeSourceEdits: true,
|
|
44
|
+
guidance: 'Replacement work must inspect and interact with the existing customer-facing surface before source edits, or record the concrete blocker.',
|
|
45
|
+
browserToolPriority: [
|
|
46
|
+
'Codex in-app browser:browser skill',
|
|
47
|
+
'agent-browser',
|
|
48
|
+
'Chrome DevTools MCP',
|
|
49
|
+
'Playwright',
|
|
50
|
+
'another browser-capable tool',
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
shopperOperatorBoundary: {
|
|
54
|
+
guidance: 'Tender analytics dashboards, A/B reports, sessions, and internal counters are operator-facing Tender surfaces, not shopper-facing app UI.',
|
|
55
|
+
shopperUiMustNotInclude: [
|
|
56
|
+
'analytics dashboards',
|
|
57
|
+
'A/B reports',
|
|
58
|
+
'sessions tables',
|
|
59
|
+
'internal counters',
|
|
60
|
+
'dashboard controls',
|
|
61
|
+
'shopper-callable analytics summary routes',
|
|
62
|
+
],
|
|
63
|
+
adminSurfaceException: 'Only build an admin or operator surface when the user explicitly asks for one, and secure it so shopper traffic cannot read internal analytics.',
|
|
64
|
+
},
|
|
65
|
+
dashboardCompletion: {
|
|
66
|
+
completeWhen: [
|
|
67
|
+
'A Tender dashboard exists or is reused.',
|
|
68
|
+
'Required queryable properties are active, already queryable, or blocked with an actionable reason.',
|
|
69
|
+
'Saved starter charts exist for the app journey.',
|
|
70
|
+
'The saved chart list has been queried and returns the chart names or ids.',
|
|
71
|
+
],
|
|
72
|
+
notCompleteWhen: ['Only an empty dashboard shell exists.'],
|
|
73
|
+
proofCommand: 'tender app analytics charts list <app-id> --dashboard <dashboard-id> --json',
|
|
74
|
+
},
|
|
75
|
+
starterDashboardCommands: [
|
|
76
|
+
'tender app analytics authoring --json',
|
|
77
|
+
'tender playbooks get analytics-event-authoring --json',
|
|
78
|
+
'tender app analytics charts templates --json',
|
|
79
|
+
'tender app analytics properties activate <app-id> --event <event-name> --property <property-key> --type dimension --dry-run --json',
|
|
80
|
+
'tender app analytics dashboards <app-id> --create "Agent dashboard" --dry-run --json',
|
|
81
|
+
'tender app analytics charts create <app-id> --dashboard <dashboard-id> --title <title> --spec chart.json --dry-run --json',
|
|
82
|
+
'tender app analytics charts list <app-id> --dashboard <dashboard-id> --json',
|
|
83
|
+
],
|
|
84
|
+
finalAnswerEvidence: [
|
|
85
|
+
'baseline inspection scope or blocker',
|
|
86
|
+
'published URL or preview status',
|
|
87
|
+
'Tender dashboard id',
|
|
88
|
+
'saved chart names or ids from charts list',
|
|
89
|
+
'clear split between shopper-facing UI and operator-facing analytics',
|
|
90
|
+
],
|
|
91
|
+
};
|
|
37
92
|
class TenderCliUsageError extends Error {
|
|
38
93
|
constructor(message) {
|
|
39
94
|
super(message);
|
|
@@ -134,6 +189,8 @@ Commands:
|
|
|
134
189
|
Describe app database query limits and policy
|
|
135
190
|
tender app db query <app-id> --env <preview|published> --sql <sql>
|
|
136
191
|
Run one read-only app database query
|
|
192
|
+
tender app db dashboards <app-id>
|
|
193
|
+
List or create private app admin dashboards
|
|
137
194
|
tender app context fetch <app-id>
|
|
138
195
|
Fetch managed agent context into a checkout
|
|
139
196
|
tender app context status <app-id>
|
|
@@ -350,12 +407,12 @@ Commands:
|
|
|
350
407
|
Rebuild the preview and optionally stream lifecycle events
|
|
351
408
|
tender app preview status <app-id>
|
|
352
409
|
Show preview job status by latest, commit, or job id
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
410
|
+
tender app preview watch <app-id>
|
|
411
|
+
Replay lifecycle events by latest, commit, or job id
|
|
412
|
+
tender app agent heartbeat <app-id>
|
|
413
|
+
Send a concise agent status update to the workspace
|
|
414
|
+
tender app publish <app-id>
|
|
415
|
+
Publish after dry-run or explicit confirmation
|
|
359
416
|
tender app publish status <app-id>
|
|
360
417
|
Show publish job status by job id
|
|
361
418
|
tender app publish watch <app-id>
|
|
@@ -372,6 +429,8 @@ Commands:
|
|
|
372
429
|
Run a server-validated chart spec
|
|
373
430
|
tender app analytics suggestions <app-id>
|
|
374
431
|
Generate useful starter chart suggestions
|
|
432
|
+
tender app analytics provision <app-id>
|
|
433
|
+
Print a starter dashboard provisioning plan
|
|
375
434
|
tender app analytics dashboards <app-id>
|
|
376
435
|
List or create saved dashboards
|
|
377
436
|
tender app analytics charts create <app-id>
|
|
@@ -394,6 +453,12 @@ Commands:
|
|
|
394
453
|
Show table and index schema for one table
|
|
395
454
|
tender app db query <app-id> --env <preview|published> --sql <sql>
|
|
396
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
|
|
397
462
|
tender app context fetch <app-id>
|
|
398
463
|
Fetch AGENTS.md, skills, and Tender project context
|
|
399
464
|
tender app context status <app-id>
|
|
@@ -537,6 +602,7 @@ Commands:
|
|
|
537
602
|
tender app analytics capabilities <app-id> Describe agent-safe analytics capabilities
|
|
538
603
|
tender app analytics suggestions <app-id> Generate starter chart suggestions
|
|
539
604
|
tender app analytics exact-funnel <app-id> Run exact unit funnel analysis
|
|
605
|
+
tender app analytics provision <app-id> Print a starter dashboard provisioning plan
|
|
540
606
|
tender app analytics dashboards <app-id> List or create dashboards
|
|
541
607
|
tender app analytics export <kind> <app-id> Export catalog metadata or query rows
|
|
542
608
|
tender app analytics charts templates Print reusable chart spec templates
|
|
@@ -557,6 +623,7 @@ Examples:
|
|
|
557
623
|
tender app analytics export catalog artifact_123 --range 30d --json
|
|
558
624
|
tender app analytics export query artifact_123 --spec chart.json --format csv
|
|
559
625
|
tender app analytics suggestions artifact_123 --range 2026-04-01..2026-04-30 --json
|
|
626
|
+
tender app analytics provision artifact_123 --from-app --json
|
|
560
627
|
tender app analytics dashboards artifact_123 --create "Agent dashboard" --json
|
|
561
628
|
tender app analytics charts templates --json
|
|
562
629
|
tender app analytics charts create artifact_123 --dashboard analytics_dashboard_123 --title "Signup funnel" --spec unit-funnel.json --json
|
|
@@ -572,6 +639,10 @@ function analyticsAuthoringHelp() {
|
|
|
572
639
|
Prints the current generated-app analytics event authoring contract. This command
|
|
573
640
|
does not require an app id, checkout, API token, or installed skill.
|
|
574
641
|
|
|
642
|
+
Agent guidance includes default generated-app analytics, replacement baseline
|
|
643
|
+
inspection, shopper/operator surface separation, and dashboard chart-list
|
|
644
|
+
completion criteria.
|
|
645
|
+
|
|
575
646
|
Examples:
|
|
576
647
|
tender app analytics authoring --json`;
|
|
577
648
|
}
|
|
@@ -638,6 +709,18 @@ dashboard page loads.
|
|
|
638
709
|
Examples:
|
|
639
710
|
tender app analytics exact-funnel artifact_123 --flow checkout_flow --steps started,completed --range 30d --json`;
|
|
640
711
|
}
|
|
712
|
+
function analyticsProvisionHelp() {
|
|
713
|
+
return `Usage: tender app analytics provision <app-id> --from-app [--dashboard-name <name>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
714
|
+
|
|
715
|
+
Prints a machine-readable provisioning plan for the default generated-app
|
|
716
|
+
analytics delivery contract. This command does not mutate dashboards yet; it
|
|
717
|
+
returns the exact commands, starter chart specs, completion criteria, and
|
|
718
|
+
blockers an external coding agent should follow.
|
|
719
|
+
|
|
720
|
+
Examples:
|
|
721
|
+
tender app analytics provision artifact_123 --from-app --json
|
|
722
|
+
tender app analytics provision artifact_123 --from-app --dashboard-name "Recommendation dashboard" --json`;
|
|
723
|
+
}
|
|
641
724
|
function analyticsDashboardsHelp() {
|
|
642
725
|
return `Usage: tender app analytics dashboards <app-id> [--create <name>] [--dry-run] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
643
726
|
|
|
@@ -717,12 +800,27 @@ Commands:
|
|
|
717
800
|
Show schema for one table
|
|
718
801
|
tender app db query <app-id> --env <preview|published>
|
|
719
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
|
|
720
813
|
|
|
721
814
|
Examples:
|
|
722
815
|
tender app db capabilities artifact_123 --json
|
|
723
816
|
tender app db tables artifact_123 --env published --json
|
|
724
817
|
tender app db schema artifact_123 --env published --table votes --json
|
|
725
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
|
|
726
824
|
tender app db query artifact_123 --env published --file ./debug.sql --param active --limit 50 --json`;
|
|
727
825
|
}
|
|
728
826
|
function dbCapabilitiesHelp() {
|
|
@@ -762,6 +860,38 @@ Examples:
|
|
|
762
860
|
tender app db query artifact_123 --env published --file ./debug.sql --limit 50 --offset 100 --json
|
|
763
861
|
tender app db query artifact_123 --env published --file - --json < debug.sql`;
|
|
764
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
|
+
}
|
|
765
895
|
function artifactsListHelp() {
|
|
766
896
|
return `Usage: tender app list [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
767
897
|
|
|
@@ -1097,6 +1227,15 @@ Examples:
|
|
|
1097
1227
|
tender app agent heartbeat artifact_123 --source claude-code --status needs_attention --summary "Waiting for an API key decision" --requested-action answer_question --json
|
|
1098
1228
|
tender app agent heartbeat artifact_123 --input - --json < heartbeat.json`;
|
|
1099
1229
|
}
|
|
1230
|
+
function appAgentHelp() {
|
|
1231
|
+
return `Usage: tender app agent <command>
|
|
1232
|
+
|
|
1233
|
+
Commands:
|
|
1234
|
+
heartbeat <app-id> Send a concise agent status update to the workspace.
|
|
1235
|
+
|
|
1236
|
+
Examples:
|
|
1237
|
+
tender app agent heartbeat artifact_123 --source codex --status working --summary "Reading files" --json`;
|
|
1238
|
+
}
|
|
1100
1239
|
function publishStatusHelp() {
|
|
1101
1240
|
return `Usage: tender app publish status <app-id> --job <job-id> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
|
|
1102
1241
|
|
|
@@ -1174,6 +1313,7 @@ function tenderCliCapabilities() {
|
|
|
1174
1313
|
playbookCommand: ANALYTICS_AUTHORING_PLAYBOOK_COMMAND,
|
|
1175
1314
|
doctorCommand: 'tender app doctor --dir <dir> --json',
|
|
1176
1315
|
purpose: 'Keep generated app analytics events compatible with Tender dashboards and unit funnels.',
|
|
1316
|
+
guidance: ANALYTICS_AGENT_GUIDANCE,
|
|
1177
1317
|
},
|
|
1178
1318
|
description: 'Agent-safe Tender App source, lifecycle, analytics, and local scaffold controls.',
|
|
1179
1319
|
discovery: {
|
|
@@ -1185,6 +1325,7 @@ function tenderCliCapabilities() {
|
|
|
1185
1325
|
'tender memory --help',
|
|
1186
1326
|
'tender playbooks --help',
|
|
1187
1327
|
'tender app --help',
|
|
1328
|
+
'tender app agent --help',
|
|
1188
1329
|
'tender app agent heartbeat --help',
|
|
1189
1330
|
'tender app share --help',
|
|
1190
1331
|
'tender app claim --help',
|
|
@@ -1286,6 +1427,7 @@ function tenderCliCapabilities() {
|
|
|
1286
1427
|
commands: [
|
|
1287
1428
|
ANALYTICS_AUTHORING_COMMAND,
|
|
1288
1429
|
ANALYTICS_AUTHORING_PLAYBOOK_COMMAND,
|
|
1430
|
+
'tender app analytics provision <artifact-id> --from-app --json',
|
|
1289
1431
|
'tender app analytics capabilities <artifact-id> --include-catalog --range 30d --json',
|
|
1290
1432
|
'tender app analytics charts templates --json',
|
|
1291
1433
|
'tender app analytics query <artifact-id> --spec chart.json --json',
|
|
@@ -1300,10 +1442,14 @@ function tenderCliCapabilities() {
|
|
|
1300
1442
|
],
|
|
1301
1443
|
notes: [
|
|
1302
1444
|
'Run authoring before adding or repairing analytics instrumentation; use capabilities for artifact-scoped read/query details.',
|
|
1445
|
+
'Generated apps should get useful Tender analytics events and saved starter dashboard charts by default unless the user opts out or a concrete blocker is recorded.',
|
|
1446
|
+
'For replacement work, inspect the baseline customer-facing page or widget before source edits; Codex should prefer browser:browser when available.',
|
|
1447
|
+
'Tender analytics dashboards are operator-facing surfaces; do not render dashboards, A/B reports, sessions, or internal counters in shopper UI unless the user explicitly requests a secured admin surface.',
|
|
1303
1448
|
'Combine CLI capabilities with local source inspection of analytics calls.',
|
|
1304
1449
|
'Validate chart specs with query before creating or updating saved charts.',
|
|
1305
1450
|
'Property chart create/update commands automatically make missing custom event properties queryable when the spec has an event_name = filter.',
|
|
1306
1451
|
'Ad hoc query is read-only; if it reports a missing dashboard-query property, activate the property first or save the chart through charts create/update.',
|
|
1452
|
+
'Dashboard setup is not complete until saved charts exist and tender app analytics charts list returns chart names or ids.',
|
|
1307
1453
|
'Use list before writes so agents can avoid duplicates and explain the exact chart they are changing.',
|
|
1308
1454
|
],
|
|
1309
1455
|
},
|
|
@@ -1317,11 +1463,14 @@ function tenderCliCapabilities() {
|
|
|
1317
1463
|
'tender app db schema <artifact-id> --env published --table <table-name> --profile db --json',
|
|
1318
1464
|
'tender app db query <artifact-id> --env published --sql "select * from <table-name> limit 20" --profile db --json',
|
|
1319
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',
|
|
1320
1468
|
],
|
|
1321
1469
|
notes: [
|
|
1322
1470
|
'The CLI only accepts artifact ids. It never accepts tenant ids, runner names, provider account ids, or runtime storage identifiers.',
|
|
1323
1471
|
'Read-only policy is enforced by the server before the runtime shim executes SQL.',
|
|
1324
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.',
|
|
1325
1474
|
],
|
|
1326
1475
|
},
|
|
1327
1476
|
{
|
|
@@ -1450,6 +1599,12 @@ function tenderCliCapabilities() {
|
|
|
1450
1599
|
requiresAuth: false,
|
|
1451
1600
|
writes: false,
|
|
1452
1601
|
},
|
|
1602
|
+
{
|
|
1603
|
+
command: 'tender app analytics provision <artifact-id> --from-app --json',
|
|
1604
|
+
purpose: 'Print a guided starter dashboard provisioning plan with chart-list completion proof.',
|
|
1605
|
+
requiresAuth: false,
|
|
1606
|
+
writes: false,
|
|
1607
|
+
},
|
|
1453
1608
|
{
|
|
1454
1609
|
command: 'tender app analytics properties list <artifact-id> --json',
|
|
1455
1610
|
purpose: 'List custom event properties available to dashboard queries.',
|
|
@@ -1514,6 +1669,39 @@ function tenderCliCapabilities() {
|
|
|
1514
1669
|
requiresAuth: true,
|
|
1515
1670
|
writes: false,
|
|
1516
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
|
+
},
|
|
1517
1705
|
],
|
|
1518
1706
|
security: [
|
|
1519
1707
|
'Prefer artifact-scoped device tokens.',
|
|
@@ -4103,6 +4291,35 @@ function parseAnalyticsDashboardsArgs(args) {
|
|
|
4103
4291
|
}
|
|
4104
4292
|
return { command: 'app analytics dashboards', artifactId, name, dryRun, ...parsed };
|
|
4105
4293
|
}
|
|
4294
|
+
function parseAnalyticsProvisionArgs(args) {
|
|
4295
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
4296
|
+
return { command: 'help', topic: 'app analytics provision' };
|
|
4297
|
+
}
|
|
4298
|
+
const artifactId = args[0];
|
|
4299
|
+
if (!artifactId || artifactId.startsWith('-')) {
|
|
4300
|
+
throw new TenderCliUsageError('app id is required. Example: tender app analytics provision <app-id> --from-app --json');
|
|
4301
|
+
}
|
|
4302
|
+
const parsed = parseAnalyticsCommonArgs(args, 1);
|
|
4303
|
+
let fromApp = false;
|
|
4304
|
+
let dashboardName = 'Agent dashboard';
|
|
4305
|
+
for (let index = 0; index < parsed.remaining.length; index += 1) {
|
|
4306
|
+
const arg = parsed.remaining[index];
|
|
4307
|
+
if (arg === '--from-app') {
|
|
4308
|
+
fromApp = true;
|
|
4309
|
+
continue;
|
|
4310
|
+
}
|
|
4311
|
+
if (arg === '--dashboard-name') {
|
|
4312
|
+
dashboardName = parseRequiredFlagValue(parsed.remaining[index + 1], '--dashboard-name');
|
|
4313
|
+
index += 1;
|
|
4314
|
+
continue;
|
|
4315
|
+
}
|
|
4316
|
+
throw new TenderCliUsageError(`Unknown analytics provision option: ${arg}`);
|
|
4317
|
+
}
|
|
4318
|
+
if (!fromApp) {
|
|
4319
|
+
throw new TenderCliUsageError('--from-app is required. Example: tender app analytics provision artifact_123 --from-app --json');
|
|
4320
|
+
}
|
|
4321
|
+
return { command: 'app analytics provision', artifactId, fromApp, dashboardName, ...parsed };
|
|
4322
|
+
}
|
|
4106
4323
|
function parseAnalyticsChartsTemplatesArgs(args) {
|
|
4107
4324
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4108
4325
|
return { command: 'help', topic: 'app analytics charts templates' };
|
|
@@ -4222,6 +4439,7 @@ function analyticsAuthoringContract() {
|
|
|
4222
4439
|
'analytics_unstable_unit_id',
|
|
4223
4440
|
],
|
|
4224
4441
|
},
|
|
4442
|
+
agentGuidance: ANALYTICS_AGENT_GUIDANCE,
|
|
4225
4443
|
analyticsAuthoring: {
|
|
4226
4444
|
payloadShape: {
|
|
4227
4445
|
event: 'custom_event_name',
|
|
@@ -4293,6 +4511,8 @@ function analyticsAuthoringContract() {
|
|
|
4293
4511
|
},
|
|
4294
4512
|
],
|
|
4295
4513
|
charting: {
|
|
4514
|
+
starterDashboardCommands: ANALYTICS_AGENT_GUIDANCE.starterDashboardCommands,
|
|
4515
|
+
completionProof: ANALYTICS_AGENT_GUIDANCE.dashboardCompletion.proofCommand,
|
|
4296
4516
|
unitFunnel: 'After traffic exists, use tender app analytics capabilities <app-id> --include-catalog --json and create version 2 unit_step_count funnel specs from observedFlows and observedUnitTypes.',
|
|
4297
4517
|
exactAudit: 'Use tender app analytics exact-funnel <app-id> --flow <flow-id> --steps <step-a,step-b> --json for repair and low-volume audits.',
|
|
4298
4518
|
},
|
|
@@ -4648,6 +4868,100 @@ function runAnalyticsChartsTemplates(command, io) {
|
|
|
4648
4868
|
}
|
|
4649
4869
|
return 0;
|
|
4650
4870
|
}
|
|
4871
|
+
function quoteCliArgument(value) {
|
|
4872
|
+
return JSON.stringify(value);
|
|
4873
|
+
}
|
|
4874
|
+
function buildAnalyticsProvisionPayload(command) {
|
|
4875
|
+
const starterTemplateIds = new Set(['daily_custom_events', 'top_custom_events']);
|
|
4876
|
+
const starterCharts = analyticsChartTemplates()
|
|
4877
|
+
.filter((template) => starterTemplateIds.has(template.id))
|
|
4878
|
+
.map((template) => ({
|
|
4879
|
+
templateId: template.id,
|
|
4880
|
+
title: template.title,
|
|
4881
|
+
purpose: template.purpose,
|
|
4882
|
+
spec: template.spec,
|
|
4883
|
+
}));
|
|
4884
|
+
const dashboardName = quoteCliArgument(command.dashboardName);
|
|
4885
|
+
return {
|
|
4886
|
+
ok: true,
|
|
4887
|
+
command: command.command,
|
|
4888
|
+
version: 1,
|
|
4889
|
+
artifactId: command.artifactId,
|
|
4890
|
+
mode: 'guided_provisioning_plan',
|
|
4891
|
+
writes: false,
|
|
4892
|
+
fromApp: command.fromApp,
|
|
4893
|
+
dashboardName: command.dashboardName,
|
|
4894
|
+
description: 'Machine-readable starter dashboard provisioning plan for external coding agents. This command does not mutate dashboards yet.',
|
|
4895
|
+
agentGuidance: ANALYTICS_AGENT_GUIDANCE,
|
|
4896
|
+
starterCharts,
|
|
4897
|
+
steps: [
|
|
4898
|
+
{
|
|
4899
|
+
id: 'read_authoring_contract',
|
|
4900
|
+
command: ANALYTICS_AUTHORING_COMMAND,
|
|
4901
|
+
completion: 'Generated app analytics payload contract and surface boundary are loaded.',
|
|
4902
|
+
},
|
|
4903
|
+
{
|
|
4904
|
+
id: 'inspect_app_analytics',
|
|
4905
|
+
commands: [
|
|
4906
|
+
`tender app analytics capabilities ${command.artifactId} --include-catalog --range 30d --json`,
|
|
4907
|
+
'tender app doctor --dir . --json',
|
|
4908
|
+
],
|
|
4909
|
+
completion: 'Observed events, unit/flow metadata, existing dashboards, and local analytics diagnostics are known.',
|
|
4910
|
+
},
|
|
4911
|
+
{
|
|
4912
|
+
id: 'choose_starter_charts',
|
|
4913
|
+
command: 'tender app analytics charts templates --json',
|
|
4914
|
+
defaultTemplates: ['daily_custom_events', 'top_custom_events'],
|
|
4915
|
+
completion: 'Starter chart specs are adapted to observed events or accepted as generic app-owned event charts.',
|
|
4916
|
+
},
|
|
4917
|
+
{
|
|
4918
|
+
id: 'create_or_reuse_dashboard',
|
|
4919
|
+
dryRunCommand: `tender app analytics dashboards ${command.artifactId} --create ${dashboardName} --dry-run --json`,
|
|
4920
|
+
writeCommand: `tender app analytics dashboards ${command.artifactId} --create ${dashboardName} --json`,
|
|
4921
|
+
completion: 'A dashboard id is available, either reused from capabilities or returned by the create command.',
|
|
4922
|
+
},
|
|
4923
|
+
{
|
|
4924
|
+
id: 'validate_chart_specs',
|
|
4925
|
+
command: `tender app analytics query ${command.artifactId} --spec chart.json --json`,
|
|
4926
|
+
completion: 'Each chart spec is server-validated before it is saved.',
|
|
4927
|
+
},
|
|
4928
|
+
{
|
|
4929
|
+
id: 'create_saved_charts',
|
|
4930
|
+
dryRunCommand: `tender app analytics charts create ${command.artifactId} --dashboard <dashboard-id> --title <title> --spec chart.json --dry-run --json`,
|
|
4931
|
+
writeCommand: `tender app analytics charts create ${command.artifactId} --dashboard <dashboard-id> --title <title> --spec chart.json --json`,
|
|
4932
|
+
completion: 'Saved chart ids exist for starter charts, or a concrete blocker is reported.',
|
|
4933
|
+
},
|
|
4934
|
+
{
|
|
4935
|
+
id: 'prove_completion',
|
|
4936
|
+
command: `tender app analytics charts list ${command.artifactId} --dashboard <dashboard-id> --json`,
|
|
4937
|
+
completion: 'The chart list returns saved chart names or ids; an empty dashboard shell is not complete.',
|
|
4938
|
+
},
|
|
4939
|
+
],
|
|
4940
|
+
completionCriteria: ANALYTICS_AGENT_GUIDANCE.dashboardCompletion.completeWhen,
|
|
4941
|
+
blockers: [
|
|
4942
|
+
'User explicitly opted out of analytics.',
|
|
4943
|
+
'No meaningful generated-app journey exists to instrument yet.',
|
|
4944
|
+
'App source cannot be inspected from the current checkout.',
|
|
4945
|
+
'Dashboard/chart API returns an actionable auth, validation, or property activation error.',
|
|
4946
|
+
],
|
|
4947
|
+
finalAnswerEvidence: ANALYTICS_AGENT_GUIDANCE.finalAnswerEvidence,
|
|
4948
|
+
};
|
|
4949
|
+
}
|
|
4950
|
+
function runAnalyticsProvision(command, io) {
|
|
4951
|
+
const payload = buildAnalyticsProvisionPayload(command);
|
|
4952
|
+
if (command.json) {
|
|
4953
|
+
io.stdout(JSON.stringify(payload, null, 2));
|
|
4954
|
+
}
|
|
4955
|
+
else {
|
|
4956
|
+
io.stdout([
|
|
4957
|
+
`app_id: ${command.artifactId}`,
|
|
4958
|
+
'mode: guided_provisioning_plan',
|
|
4959
|
+
`dashboard: ${command.dashboardName}`,
|
|
4960
|
+
`proof: ${ANALYTICS_AGENT_GUIDANCE.dashboardCompletion.proofCommand}`,
|
|
4961
|
+
].join('\n'));
|
|
4962
|
+
}
|
|
4963
|
+
return 0;
|
|
4964
|
+
}
|
|
4651
4965
|
function parseAnalyticsChartsCreateArgs(args) {
|
|
4652
4966
|
if (args.includes('--help') || args.includes('-h')) {
|
|
4653
4967
|
return { command: 'help', topic: 'app analytics charts create' };
|
|
@@ -5191,6 +5505,239 @@ function parseDbQueryArgs(args) {
|
|
|
5191
5505
|
...parsed,
|
|
5192
5506
|
};
|
|
5193
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
|
+
}
|
|
5194
5741
|
const AGENT_HEARTBEAT_SOURCE_VALUES = new Set(['codex', 'claude-code', 'tender-cli', 'git', 'other']);
|
|
5195
5742
|
const AGENT_HEARTBEAT_STATUS_VALUES = new Set(['connected', 'working', 'waiting_for_user', 'needs_attention']);
|
|
5196
5743
|
const AGENT_HEARTBEAT_PHASE_VALUES = new Set([
|
|
@@ -5526,7 +6073,7 @@ function parseTenderArgs(args) {
|
|
|
5526
6073
|
return parseContextRefreshArgs(resourceArgs.slice(3));
|
|
5527
6074
|
}
|
|
5528
6075
|
if (resourceArgs[1] === 'agent' && (resourceArgs[2] === undefined || resourceArgs[2] === '--help' || resourceArgs[2] === '-h')) {
|
|
5529
|
-
return { command: 'help', topic: 'app agent
|
|
6076
|
+
return { command: 'help', topic: 'app agent' };
|
|
5530
6077
|
}
|
|
5531
6078
|
if (resourceArgs[1] === 'agent' && resourceArgs[2] === 'heartbeat') {
|
|
5532
6079
|
return parseAgentHeartbeatArgs(resourceArgs.slice(3));
|
|
@@ -5576,6 +6123,9 @@ function parseTenderArgs(args) {
|
|
|
5576
6123
|
if (resourceArgs[1] === 'analytics' && resourceArgs[2] === 'dashboards') {
|
|
5577
6124
|
return parseAnalyticsDashboardsArgs(resourceArgs.slice(3));
|
|
5578
6125
|
}
|
|
6126
|
+
if (resourceArgs[1] === 'analytics' && resourceArgs[2] === 'provision') {
|
|
6127
|
+
return parseAnalyticsProvisionArgs(resourceArgs.slice(3));
|
|
6128
|
+
}
|
|
5579
6129
|
if (resourceArgs[1] === 'analytics' &&
|
|
5580
6130
|
resourceArgs[2] === 'properties' &&
|
|
5581
6131
|
(resourceArgs[3] === undefined || resourceArgs[3] === '--help' || resourceArgs[3] === '-h')) {
|
|
@@ -5633,6 +6183,31 @@ function parseTenderArgs(args) {
|
|
|
5633
6183
|
if (resourceArgs[1] === 'db' && resourceArgs[2] === 'query') {
|
|
5634
6184
|
return parseDbQueryArgs(resourceArgs.slice(3));
|
|
5635
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
|
+
}
|
|
5636
6211
|
if (resourceArgs[1] === 'db') {
|
|
5637
6212
|
throw new TenderCliUsageError(`Unknown app db command: ${resourceArgs[2]}`);
|
|
5638
6213
|
}
|
|
@@ -6074,6 +6649,66 @@ async function requestDbApi(input) {
|
|
|
6074
6649
|
}
|
|
6075
6650
|
return payload;
|
|
6076
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
|
+
}
|
|
6077
6712
|
function encodePathSegments(filePath) {
|
|
6078
6713
|
return filePath.split('/').map(encodeURIComponent).join('/');
|
|
6079
6714
|
}
|
|
@@ -6115,6 +6750,21 @@ function dbCorrectedExample(artifactId, dbPath) {
|
|
|
6115
6750
|
}
|
|
6116
6751
|
return `tender app db query ${artifactId} --env published --sql "select * from <table-name> limit 20" --json`;
|
|
6117
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
|
+
}
|
|
6118
6768
|
function analyticsCorrectedExample(artifactId, analyticsPath) {
|
|
6119
6769
|
if (analyticsPath === 'capabilities') {
|
|
6120
6770
|
return `tender app analytics capabilities ${artifactId} --include-catalog --range 30d --json`;
|
|
@@ -9529,6 +10179,310 @@ async function runDbQuery(command, io, runtime) {
|
|
|
9529
10179
|
]);
|
|
9530
10180
|
return 0;
|
|
9531
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
|
+
}
|
|
9532
10486
|
async function runAnalyticsSummary(command, io, runtime) {
|
|
9533
10487
|
const credentials = await resolveAnalyticsCredentials(command, runtime);
|
|
9534
10488
|
const payload = await requestAnalyticsApi({
|
|
@@ -10150,6 +11104,9 @@ export async function runTenderCli(args, io = {
|
|
|
10150
11104
|
else if (command.topic === 'app preview watch') {
|
|
10151
11105
|
io.stdout(previewWatchHelp());
|
|
10152
11106
|
}
|
|
11107
|
+
else if (command.topic === 'app agent') {
|
|
11108
|
+
io.stdout(appAgentHelp());
|
|
11109
|
+
}
|
|
10153
11110
|
else if (command.topic === 'app agent heartbeat') {
|
|
10154
11111
|
io.stdout(agentHeartbeatHelp());
|
|
10155
11112
|
}
|
|
@@ -10189,6 +11146,9 @@ export async function runTenderCli(args, io = {
|
|
|
10189
11146
|
else if (command.topic === 'app analytics dashboards') {
|
|
10190
11147
|
io.stdout(analyticsDashboardsHelp());
|
|
10191
11148
|
}
|
|
11149
|
+
else if (command.topic === 'app analytics provision') {
|
|
11150
|
+
io.stdout(analyticsProvisionHelp());
|
|
11151
|
+
}
|
|
10192
11152
|
else if (command.topic === 'app analytics export') {
|
|
10193
11153
|
io.stdout(analyticsExportHelp());
|
|
10194
11154
|
}
|
|
@@ -10234,6 +11194,21 @@ export async function runTenderCli(args, io = {
|
|
|
10234
11194
|
else if (command.topic === 'app db query') {
|
|
10235
11195
|
io.stdout(dbQueryHelp());
|
|
10236
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
|
+
}
|
|
10237
11212
|
else if (command.topic === 'app') {
|
|
10238
11213
|
io.stdout(appHelp());
|
|
10239
11214
|
}
|
|
@@ -10383,6 +11358,9 @@ export async function runTenderCli(args, io = {
|
|
|
10383
11358
|
if (command.command === 'app analytics dashboards') {
|
|
10384
11359
|
return await runAnalyticsDashboards(command, io, runtime);
|
|
10385
11360
|
}
|
|
11361
|
+
if (command.command === 'app analytics provision') {
|
|
11362
|
+
return runAnalyticsProvision(command, io);
|
|
11363
|
+
}
|
|
10386
11364
|
if (command.command === 'app analytics export catalog') {
|
|
10387
11365
|
return await runAnalyticsExportCatalog(command, io, runtime);
|
|
10388
11366
|
}
|
|
@@ -10422,6 +11400,21 @@ export async function runTenderCli(args, io = {
|
|
|
10422
11400
|
if (command.command === 'app db query') {
|
|
10423
11401
|
return await runDbQuery(command, io, runtime);
|
|
10424
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
|
+
}
|
|
10425
11418
|
if (command.command === 'app git remote') {
|
|
10426
11419
|
return await runGitRemote(command, io, runtime);
|
|
10427
11420
|
}
|
|
@@ -10450,6 +11443,8 @@ export function shouldReadEntrypointStdin(args) {
|
|
|
10450
11443
|
((args[1] === 'git' && args[2] === 'credential') ||
|
|
10451
11444
|
(args[1] === 'analytics' && hasFlagValue(args, '--spec', '-')) ||
|
|
10452
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', '-')) ||
|
|
10453
11448
|
(args[1] === 'db' && args[2] === 'query' && hasFlagValue(args, '--file', '-'))));
|
|
10454
11449
|
}
|
|
10455
11450
|
async function readEntrypointStdin() {
|