@tenderprompt/cli 0.1.3 → 0.1.4

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.
Files changed (3) hide show
  1. package/README.md +6 -1
  2. package/dist/index.js +990 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -64,6 +64,10 @@ A push updates app source and can trigger preview work. Publishing remains
64
64
  an explicit lifecycle command so local agents do not accidentally publish
65
65
  production changes.
66
66
 
67
+ `tender app git setup` configures the checkout to avoid thin/external-delta
68
+ push packs when possible. The server also handles valid external-delta packs by
69
+ indexing the accepted commit after upstream Git accepts the push.
70
+
67
71
  ## Useful Commands
68
72
 
69
73
  ```bash
@@ -84,7 +88,8 @@ tender app publish artifact_123 --dry-run --json
84
88
  - Local agents run in the user's environment, not in Tender.
85
89
  - Source moves through artifact Git.
86
90
  - Preview, build, and publish are explicit API lifecycle operations.
87
- - `write` lets a token edit and preview; `publish` must be explicitly granted.
91
+ - `write` lets a token edit, preview, push source, and publish. `publish`
92
+ remains accepted as an explicit scope for descriptive or older token grants.
88
93
  - `tender app publish` requires `--dry-run` or `--confirm publish`.
89
94
  - Tokens are stored outside the project checkout and are not written into Git
90
95
  remote URLs.
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
4
4
  import os from "node:os";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
- import { validateTenderApp, } from "@tenderprompt/tender-app-validator";
7
+ import { generateTenderAppEnvTypes, validateTenderApp, } from "@tenderprompt/tender-app-validator";
8
8
  import { collectTenderAppFiles } from "./local-files.js";
9
9
  class TenderCliUsageError extends Error {
10
10
  constructor(message) {
@@ -46,6 +46,12 @@ Commands:
46
46
  Show publish job status by job id
47
47
  tender app publish watch <app-id>
48
48
  Stream or print lifecycle events for a publish job
49
+ tender app analytics summary <app-id>
50
+ Read artifact analytics totals and trend points
51
+ tender app analytics query <app-id> --spec <file|->
52
+ Run a server-validated chart spec
53
+ tender app analytics suggestions <app-id>
54
+ Generate useful starter chart suggestions
49
55
  tender app context fetch <app-id>
50
56
  Fetch managed agent context into a checkout
51
57
  tender app context status <app-id>
@@ -55,6 +61,8 @@ Commands:
55
61
  tender app git setup <app-id>
56
62
  Configure a checkout to push to the app remote
57
63
  tender app doctor Validate a local Tender App checkout
64
+ tender app generate-env-types
65
+ Regenerate src/env.d.ts from app.json and bindings
58
66
 
59
67
  Compatibility:
60
68
  tender artifacts ... Legacy alias for Tender App commands
@@ -72,11 +80,15 @@ Examples:
72
80
  tender app preview status artifact_123 --commit $(git rev-parse HEAD) --json
73
81
  tender app preview watch artifact_123 --job job_123 --stream --json
74
82
  tender app publish artifact_123 --commit $(git rev-parse HEAD) --confirm publish --stream --json
83
+ tender app analytics summary artifact_123 --range 30d --json
84
+ tender app analytics query artifact_123 --spec chart.json --json
85
+ tender app analytics suggestions artifact_123 --range 30d --json
75
86
  tender app context fetch artifact_123 --dir ./widget --dry-run --json
76
87
  tender app context status artifact_123 --dir ./widget --json
77
88
  tender app context refresh artifact_123 --dir ./widget --dry-run --json
78
89
  tender app git setup artifact_123 --dir ./widget --json
79
90
  tender app doctor --dir ./widget
91
+ tender app generate-env-types --dir ./widget --json
80
92
  tender app doctor --dir ./widget --json`;
81
93
  }
82
94
  function authHelp() {
@@ -147,6 +159,16 @@ Commands:
147
159
  Show publish job status by job id
148
160
  tender app publish watch <app-id>
149
161
  Replay lifecycle events for a publish job id
162
+ tender app analytics summary <app-id>
163
+ Read artifact analytics totals and trend points
164
+ tender app analytics query <app-id> --spec <file|->
165
+ Run a server-validated chart spec
166
+ tender app analytics suggestions <app-id>
167
+ Generate useful starter chart suggestions
168
+ tender app analytics dashboards <app-id>
169
+ List or create saved dashboards
170
+ tender app analytics charts create <app-id>
171
+ Create a saved chart from a chart spec
150
172
  tender app context fetch <app-id>
151
173
  Fetch AGENTS.md, skills, and Tender App scaffold files
152
174
  tender app context status <app-id>
@@ -156,6 +178,8 @@ Commands:
156
178
  tender app git setup <app-id>
157
179
  Configure the tender Git remote and credential helper
158
180
  tender app doctor Validate local context, TypeScript scaffold, and Tender App rules
181
+ tender app generate-env-types
182
+ Regenerate src/env.d.ts from app.json and bindings
159
183
  tender app git remote <app-id>
160
184
  Show Tender-hosted Git remote metadata
161
185
 
@@ -172,11 +196,18 @@ Examples:
172
196
  tender app publish artifact_123 --dry-run --json
173
197
  tender app publish status artifact_123 --job job_123 --json
174
198
  tender app publish watch artifact_123 --job job_123 --stream --json
199
+ tender app analytics summary artifact_123 --range 30d --json
200
+ tender app analytics query artifact_123 --spec chart.json --json
201
+ tender app analytics suggestions artifact_123 --range 30d --json
202
+ tender app analytics dashboards artifact_123 --json
203
+ tender app analytics dashboards artifact_123 --create "Agent dashboard" --json
204
+ tender app analytics charts create artifact_123 --dashboard analytics_dashboard_123 --title "Signup funnel" --spec funnel.json --json
175
205
  tender app context fetch artifact_123 --dir ./widget --dry-run --json
176
206
  tender app context status artifact_123 --dir ./widget --json
177
207
  tender app context refresh artifact_123 --dir ./widget --dry-run --json
178
208
  tender app git setup artifact_123 --dir ./widget --json
179
209
  tender app doctor --dir ./widget
210
+ tender app generate-env-types --dir ./widget --json
180
211
  tender app doctor --json
181
212
  tender app git remote artifact_123 --ttl 7d --json`;
182
213
  }
@@ -186,6 +217,100 @@ function artifactsHelp() {
186
217
  Legacy alias:
187
218
  tender artifacts ... continues to work for existing automation. Prefer tender app ... for new workflows.`;
188
219
  }
220
+ function analyticsHelp() {
221
+ return `Usage: tender app analytics <command> <app-id> [options]
222
+
223
+ Start here for agents:
224
+ tender app analytics capabilities <app-id> --include-catalog --range 30d --json
225
+
226
+ Commands:
227
+ tender app analytics summary <app-id> Read totals and trend points
228
+ tender app analytics query <app-id> Run one chart spec
229
+ tender app analytics capabilities <app-id> Describe agent-safe analytics capabilities
230
+ tender app analytics suggestions <app-id> Generate starter chart suggestions
231
+ tender app analytics dashboards <app-id> List or create dashboards
232
+ tender app analytics export <kind> <app-id> Export catalog metadata or query rows
233
+ tender app analytics charts create <app-id> Create a saved chart
234
+
235
+ Examples:
236
+ tender app analytics summary artifact_123 --range 30d --json
237
+ tender app analytics capabilities artifact_123 --include-catalog --json
238
+ tender app analytics query artifact_123 --spec chart.json --json
239
+ tender app analytics query artifact_123 --spec - --json < chart.json
240
+ tender app analytics export catalog artifact_123 --range 30d --json
241
+ tender app analytics export query artifact_123 --spec chart.json --format csv
242
+ tender app analytics suggestions artifact_123 --range 2026-04-01..2026-04-30 --json
243
+ tender app analytics dashboards artifact_123 --create "Agent dashboard" --json
244
+ tender app analytics charts create artifact_123 --dashboard analytics_dashboard_123 --title "Signup funnel" --spec funnel.json --json`;
245
+ }
246
+ function analyticsSummaryHelp() {
247
+ return `Usage: tender app analytics summary <app-id> [--range <7d|30d|90d|YYYY-MM-DD..YYYY-MM-DD>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
248
+
249
+ Examples:
250
+ tender app analytics summary artifact_123 --range 30d --json
251
+ tender app analytics summary artifact_123 --range 2026-04-01..2026-04-30 --json`;
252
+ }
253
+ function analyticsQueryHelp() {
254
+ return `Usage: tender app analytics query <app-id> --spec <file|-> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
255
+
256
+ Examples:
257
+ tender app analytics query artifact_123 --spec chart.json --json
258
+ tender app analytics query artifact_123 --spec - --json < chart.json`;
259
+ }
260
+ function analyticsCapabilitiesHelp() {
261
+ return `Usage: tender app analytics capabilities <app-id> [--include-catalog] [--range <7d|30d|90d|YYYY-MM-DD..YYYY-MM-DD>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
262
+
263
+ Use this as the self-documenting entrypoint for coding agents. With --include-catalog,
264
+ the response includes observed events, paths, rough property types, current saved
265
+ dashboards, supported chart spec shapes, retention limits, and safe next commands.
266
+
267
+ Examples:
268
+ tender app analytics capabilities artifact_123 --json
269
+ tender app analytics capabilities artifact_123 --include-catalog --range 30d --json`;
270
+ }
271
+ function analyticsExportHelp() {
272
+ return `Usage: tender app analytics export <catalog|query> <app-id> [options]
273
+
274
+ Examples:
275
+ tender app analytics export catalog artifact_123 --range 30d --json
276
+ tender app analytics export query artifact_123 --spec chart.json --format json
277
+ tender app analytics export query artifact_123 --spec chart.json --format csv`;
278
+ }
279
+ function analyticsExportCatalogHelp() {
280
+ return `Usage: tender app analytics export catalog <app-id> [--range <7d|30d|90d|YYYY-MM-DD..YYYY-MM-DD>] [--format json] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
281
+
282
+ Examples:
283
+ tender app analytics export catalog artifact_123 --range 30d --json`;
284
+ }
285
+ function analyticsExportQueryHelp() {
286
+ return `Usage: tender app analytics export query <app-id> --spec <file|-> [--format <json|csv>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
287
+
288
+ Examples:
289
+ tender app analytics export query artifact_123 --spec chart.json --format json
290
+ tender app analytics export query artifact_123 --spec chart.json --format csv
291
+ tender app analytics export query artifact_123 --spec - --format csv < chart.json`;
292
+ }
293
+ function analyticsSuggestionsHelp() {
294
+ return `Usage: tender app analytics suggestions <app-id> [--range <7d|30d|90d|YYYY-MM-DD..YYYY-MM-DD>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
295
+
296
+ Examples:
297
+ tender app analytics suggestions artifact_123 --range 30d --json`;
298
+ }
299
+ function analyticsDashboardsHelp() {
300
+ return `Usage: tender app analytics dashboards <app-id> [--create <name>] [--dry-run] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
301
+
302
+ Examples:
303
+ tender app analytics dashboards artifact_123 --json
304
+ tender app analytics dashboards artifact_123 --create "Agent dashboard" --dry-run --json
305
+ tender app analytics dashboards artifact_123 --create "Agent dashboard" --json`;
306
+ }
307
+ function analyticsChartsCreateHelp() {
308
+ return `Usage: tender app analytics charts create <app-id> --dashboard <dashboard-id> --title <title> --spec <file|-> [--dry-run] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
309
+
310
+ Examples:
311
+ tender app analytics charts create artifact_123 --dashboard analytics_dashboard_123 --title "Signup funnel" --spec funnel.json --dry-run --json
312
+ tender app analytics charts create artifact_123 --dashboard analytics_dashboard_123 --title "Signup funnel" --spec funnel.json --json`;
313
+ }
189
314
  function artifactsListHelp() {
190
315
  return `Usage: tender app list [--base-url <url>] [--token <token>] [--profile <name>] [--json]
191
316
 
@@ -305,6 +430,17 @@ Examples:
305
430
  tender app doctor --dir ./widget
306
431
  tender app doctor --dir ./widget --json`;
307
432
  }
433
+ function generateEnvTypesHelp() {
434
+ return `Usage: tender app generate-env-types [--dir <path>] [--json]
435
+
436
+ Options:
437
+ --dir <path> Local Tender App checkout to update. Defaults to current directory.
438
+ --json Emit stable JSON for coding agents.
439
+
440
+ Examples:
441
+ tender app generate-env-types --dir ./widget
442
+ tender app generate-env-types --dir ./widget --json`;
443
+ }
308
444
  function gitRemoteHelp() {
309
445
  return `Usage: tender app git remote <app-id> [--base-url <url>] [--token <token>] [--profile <name>] [--ttl <7d|14d|30d>] [--json]
310
446
 
@@ -1882,6 +2018,343 @@ function parseAuthStatusArgs(args) {
1882
2018
  json,
1883
2019
  };
1884
2020
  }
2021
+ function parseGenerateEnvTypesArgs(args) {
2022
+ if (args.includes("--help") || args.includes("-h")) {
2023
+ return { command: "help", topic: "artifacts generate-env-types" };
2024
+ }
2025
+ let dir = ".";
2026
+ let json = false;
2027
+ for (let index = 0; index < args.length; index += 1) {
2028
+ const arg = args[index];
2029
+ if (arg === "--json") {
2030
+ json = true;
2031
+ continue;
2032
+ }
2033
+ if (arg === "--dir") {
2034
+ const value = args[index + 1];
2035
+ if (!value) {
2036
+ throw new TenderCliUsageError("--dir requires a path.");
2037
+ }
2038
+ dir = value;
2039
+ index += 1;
2040
+ continue;
2041
+ }
2042
+ throw new TenderCliUsageError(`Unknown generate-env-types option: ${arg}`);
2043
+ }
2044
+ return {
2045
+ command: "artifacts generate-env-types",
2046
+ dir,
2047
+ json,
2048
+ };
2049
+ }
2050
+ function parseAnalyticsRange(value) {
2051
+ if (!value || value.startsWith("-")) {
2052
+ throw new TenderCliUsageError("--range requires 7d, 30d, 90d, or YYYY-MM-DD..YYYY-MM-DD.");
2053
+ }
2054
+ if (value === "7d" || value === "30d" || value === "90d") {
2055
+ return value;
2056
+ }
2057
+ if (/^\d{4}-\d{2}-\d{2}\.\.\d{4}-\d{2}-\d{2}$/.test(value)) {
2058
+ return value;
2059
+ }
2060
+ throw new TenderCliUsageError("--range must be 7d, 30d, 90d, or YYYY-MM-DD..YYYY-MM-DD.");
2061
+ }
2062
+ function parseSpecPath(value) {
2063
+ if (!value || (value !== "-" && value.startsWith("-"))) {
2064
+ throw new TenderCliUsageError("--spec requires a JSON file path or - for stdin.");
2065
+ }
2066
+ return value;
2067
+ }
2068
+ function parseRequiredFlagValue(value, flag) {
2069
+ if (!value || value.startsWith("-")) {
2070
+ throw new TenderCliUsageError(`${flag} requires a value.`);
2071
+ }
2072
+ return value;
2073
+ }
2074
+ function parseAnalyticsCommonArgs(args, startIndex) {
2075
+ let baseUrl = null;
2076
+ let flagToken = null;
2077
+ let profileName = null;
2078
+ let json = false;
2079
+ const remaining = [];
2080
+ for (let index = startIndex; index < args.length; index += 1) {
2081
+ const arg = args[index];
2082
+ if (arg === "--json") {
2083
+ json = true;
2084
+ continue;
2085
+ }
2086
+ if (arg === "--base-url") {
2087
+ baseUrl = parseBaseUrlFlag(args, index);
2088
+ index += 1;
2089
+ continue;
2090
+ }
2091
+ if (arg === "--token") {
2092
+ flagToken = parseTokenFlag(args, index);
2093
+ index += 1;
2094
+ continue;
2095
+ }
2096
+ if (arg === "--profile") {
2097
+ profileName = parseProfileName(args[index + 1], "--profile");
2098
+ index += 1;
2099
+ continue;
2100
+ }
2101
+ remaining.push(arg);
2102
+ }
2103
+ return { baseUrl, flagToken, profileName, json, remaining };
2104
+ }
2105
+ function parseAnalyticsSummaryArgs(args) {
2106
+ if (args.includes("--help") || args.includes("-h")) {
2107
+ return { command: "help", topic: "artifacts analytics summary" };
2108
+ }
2109
+ const artifactId = args[0];
2110
+ if (!artifactId || artifactId.startsWith("-")) {
2111
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics summary <app-id> --range 30d --json");
2112
+ }
2113
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2114
+ let range = "7d";
2115
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2116
+ const arg = parsed.remaining[index];
2117
+ if (arg === "--range") {
2118
+ range = parseAnalyticsRange(parsed.remaining[index + 1]);
2119
+ index += 1;
2120
+ continue;
2121
+ }
2122
+ throw new TenderCliUsageError(`Unknown analytics summary option: ${arg}`);
2123
+ }
2124
+ return { command: "artifacts analytics summary", artifactId, range, ...parsed };
2125
+ }
2126
+ function parseAnalyticsQueryArgs(args) {
2127
+ if (args.includes("--help") || args.includes("-h")) {
2128
+ return { command: "help", topic: "artifacts analytics query" };
2129
+ }
2130
+ const artifactId = args[0];
2131
+ if (!artifactId || artifactId.startsWith("-")) {
2132
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics query <app-id> --spec chart.json --json");
2133
+ }
2134
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2135
+ let specPath = null;
2136
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2137
+ const arg = parsed.remaining[index];
2138
+ if (arg === "--spec") {
2139
+ specPath = parseSpecPath(parsed.remaining[index + 1]);
2140
+ index += 1;
2141
+ continue;
2142
+ }
2143
+ throw new TenderCliUsageError(`Unknown analytics query option: ${arg}`);
2144
+ }
2145
+ if (!specPath) {
2146
+ throw new TenderCliUsageError("--spec is required. Example: tender app analytics query artifact_123 --spec chart.json --json");
2147
+ }
2148
+ return { command: "artifacts analytics query", artifactId, specPath, ...parsed };
2149
+ }
2150
+ function parseAnalyticsCapabilitiesArgs(args) {
2151
+ if (args.includes("--help") || args.includes("-h")) {
2152
+ return { command: "help", topic: "artifacts analytics capabilities" };
2153
+ }
2154
+ const artifactId = args[0];
2155
+ if (!artifactId || artifactId.startsWith("-")) {
2156
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics capabilities <app-id> --include-catalog --json");
2157
+ }
2158
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2159
+ let range = "7d";
2160
+ let includeCatalog = false;
2161
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2162
+ const arg = parsed.remaining[index];
2163
+ if (arg === "--include-catalog") {
2164
+ includeCatalog = true;
2165
+ continue;
2166
+ }
2167
+ if (arg === "--range") {
2168
+ range = parseAnalyticsRange(parsed.remaining[index + 1]);
2169
+ index += 1;
2170
+ continue;
2171
+ }
2172
+ throw new TenderCliUsageError(`Unknown analytics capabilities option: ${arg}`);
2173
+ }
2174
+ return {
2175
+ command: "artifacts analytics capabilities",
2176
+ artifactId,
2177
+ includeCatalog,
2178
+ range,
2179
+ ...parsed,
2180
+ };
2181
+ }
2182
+ function parseAnalyticsSuggestionsArgs(args) {
2183
+ if (args.includes("--help") || args.includes("-h")) {
2184
+ return { command: "help", topic: "artifacts analytics suggestions" };
2185
+ }
2186
+ const artifactId = args[0];
2187
+ if (!artifactId || artifactId.startsWith("-")) {
2188
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics suggestions <app-id> --range 30d --json");
2189
+ }
2190
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2191
+ let range = "7d";
2192
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2193
+ const arg = parsed.remaining[index];
2194
+ if (arg === "--range") {
2195
+ range = parseAnalyticsRange(parsed.remaining[index + 1]);
2196
+ index += 1;
2197
+ continue;
2198
+ }
2199
+ throw new TenderCliUsageError(`Unknown analytics suggestions option: ${arg}`);
2200
+ }
2201
+ return { command: "artifacts analytics suggestions", artifactId, range, ...parsed };
2202
+ }
2203
+ function parseAnalyticsDashboardsArgs(args) {
2204
+ if (args.includes("--help") || args.includes("-h")) {
2205
+ return { command: "help", topic: "artifacts analytics dashboards" };
2206
+ }
2207
+ const artifactId = args[0];
2208
+ if (!artifactId || artifactId.startsWith("-")) {
2209
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics dashboards <app-id> --json");
2210
+ }
2211
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2212
+ let name = null;
2213
+ let dryRun = false;
2214
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2215
+ const arg = parsed.remaining[index];
2216
+ if (arg === "--create") {
2217
+ name = parseRequiredFlagValue(parsed.remaining[index + 1], "--create");
2218
+ index += 1;
2219
+ continue;
2220
+ }
2221
+ if (arg === "--dry-run") {
2222
+ dryRun = true;
2223
+ continue;
2224
+ }
2225
+ throw new TenderCliUsageError(`Unknown analytics dashboards option: ${arg}`);
2226
+ }
2227
+ if (dryRun && !name) {
2228
+ throw new TenderCliUsageError("--dry-run is only valid with --create. Example: tender app analytics dashboards artifact_123 --create \"Agent dashboard\" --dry-run --json");
2229
+ }
2230
+ return { command: "artifacts analytics dashboards", artifactId, name, dryRun, ...parsed };
2231
+ }
2232
+ function parseAnalyticsExportCatalogArgs(args) {
2233
+ if (args.includes("--help") || args.includes("-h")) {
2234
+ return { command: "help", topic: "artifacts analytics export catalog" };
2235
+ }
2236
+ const artifactId = args[0];
2237
+ if (!artifactId || artifactId.startsWith("-")) {
2238
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics export catalog <app-id> --range 30d --json");
2239
+ }
2240
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2241
+ let range = "7d";
2242
+ let format = "json";
2243
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2244
+ const arg = parsed.remaining[index];
2245
+ if (arg === "--range") {
2246
+ range = parseAnalyticsRange(parsed.remaining[index + 1]);
2247
+ index += 1;
2248
+ continue;
2249
+ }
2250
+ if (arg === "--format") {
2251
+ const value = parseRequiredFlagValue(parsed.remaining[index + 1], "--format");
2252
+ if (value !== "json") {
2253
+ throw new TenderCliUsageError("--format for export catalog must be json.");
2254
+ }
2255
+ format = value;
2256
+ index += 1;
2257
+ continue;
2258
+ }
2259
+ throw new TenderCliUsageError(`Unknown analytics export catalog option: ${arg}`);
2260
+ }
2261
+ return {
2262
+ command: "artifacts analytics export catalog",
2263
+ artifactId,
2264
+ format,
2265
+ range,
2266
+ ...parsed,
2267
+ };
2268
+ }
2269
+ function parseAnalyticsExportQueryArgs(args) {
2270
+ if (args.includes("--help") || args.includes("-h")) {
2271
+ return { command: "help", topic: "artifacts analytics export query" };
2272
+ }
2273
+ const artifactId = args[0];
2274
+ if (!artifactId || artifactId.startsWith("-")) {
2275
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics export query <app-id> --spec chart.json --format csv");
2276
+ }
2277
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2278
+ let specPath = null;
2279
+ let format = "json";
2280
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2281
+ const arg = parsed.remaining[index];
2282
+ if (arg === "--spec") {
2283
+ specPath = parseSpecPath(parsed.remaining[index + 1]);
2284
+ index += 1;
2285
+ continue;
2286
+ }
2287
+ if (arg === "--format") {
2288
+ const value = parseRequiredFlagValue(parsed.remaining[index + 1], "--format");
2289
+ if (value !== "json" && value !== "csv") {
2290
+ throw new TenderCliUsageError("--format must be json or csv.");
2291
+ }
2292
+ format = value;
2293
+ index += 1;
2294
+ continue;
2295
+ }
2296
+ throw new TenderCliUsageError(`Unknown analytics export query option: ${arg}`);
2297
+ }
2298
+ if (!specPath) {
2299
+ throw new TenderCliUsageError("--spec is required. Example: tender app analytics export query artifact_123 --spec chart.json --format csv");
2300
+ }
2301
+ return {
2302
+ command: "artifacts analytics export query",
2303
+ artifactId,
2304
+ specPath,
2305
+ format,
2306
+ ...parsed,
2307
+ };
2308
+ }
2309
+ function parseAnalyticsChartsCreateArgs(args) {
2310
+ if (args.includes("--help") || args.includes("-h")) {
2311
+ return { command: "help", topic: "artifacts analytics charts create" };
2312
+ }
2313
+ const artifactId = args[0];
2314
+ if (!artifactId || artifactId.startsWith("-")) {
2315
+ throw new TenderCliUsageError("app id is required. Example: tender app analytics charts create <app-id> --dashboard <dashboard-id> --title <title> --spec chart.json --json");
2316
+ }
2317
+ const parsed = parseAnalyticsCommonArgs(args, 1);
2318
+ let dashboardId = null;
2319
+ let title = null;
2320
+ let specPath = null;
2321
+ let dryRun = false;
2322
+ for (let index = 0; index < parsed.remaining.length; index += 1) {
2323
+ const arg = parsed.remaining[index];
2324
+ if (arg === "--dashboard") {
2325
+ dashboardId = parseRequiredFlagValue(parsed.remaining[index + 1], "--dashboard");
2326
+ index += 1;
2327
+ continue;
2328
+ }
2329
+ if (arg === "--title") {
2330
+ title = parseRequiredFlagValue(parsed.remaining[index + 1], "--title");
2331
+ index += 1;
2332
+ continue;
2333
+ }
2334
+ if (arg === "--spec") {
2335
+ specPath = parseSpecPath(parsed.remaining[index + 1]);
2336
+ index += 1;
2337
+ continue;
2338
+ }
2339
+ if (arg === "--dry-run") {
2340
+ dryRun = true;
2341
+ continue;
2342
+ }
2343
+ throw new TenderCliUsageError(`Unknown analytics charts create option: ${arg}`);
2344
+ }
2345
+ if (!dashboardId || !title || !specPath) {
2346
+ throw new TenderCliUsageError("--dashboard, --title, and --spec are required. Example: tender app analytics charts create artifact_123 --dashboard analytics_dashboard_123 --title \"Signup funnel\" --spec chart.json --json");
2347
+ }
2348
+ return {
2349
+ command: "artifacts analytics charts create",
2350
+ artifactId,
2351
+ dashboardId,
2352
+ title,
2353
+ specPath,
2354
+ dryRun,
2355
+ ...parsed,
2356
+ };
2357
+ }
1885
2358
  function parseTenderArgs(args) {
1886
2359
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
1887
2360
  return { command: "help", topic: "root" };
@@ -1933,6 +2406,49 @@ function parseTenderArgs(args) {
1933
2406
  if (resourceArgs[1] === "context" && resourceArgs[2] === "refresh") {
1934
2407
  return parseContextRefreshArgs(resourceArgs.slice(3));
1935
2408
  }
2409
+ if (resourceArgs[1] === "analytics" &&
2410
+ (resourceArgs[2] === undefined ||
2411
+ resourceArgs[2] === "--help" ||
2412
+ resourceArgs[2] === "-h")) {
2413
+ return { command: "help", topic: "artifacts analytics" };
2414
+ }
2415
+ if (resourceArgs[1] === "analytics" && resourceArgs[2] === "summary") {
2416
+ return parseAnalyticsSummaryArgs(resourceArgs.slice(3));
2417
+ }
2418
+ if (resourceArgs[1] === "analytics" && resourceArgs[2] === "query") {
2419
+ return parseAnalyticsQueryArgs(resourceArgs.slice(3));
2420
+ }
2421
+ if (resourceArgs[1] === "analytics" && resourceArgs[2] === "capabilities") {
2422
+ return parseAnalyticsCapabilitiesArgs(resourceArgs.slice(3));
2423
+ }
2424
+ if (resourceArgs[1] === "analytics" && resourceArgs[2] === "suggestions") {
2425
+ return parseAnalyticsSuggestionsArgs(resourceArgs.slice(3));
2426
+ }
2427
+ if (resourceArgs[1] === "analytics" && resourceArgs[2] === "dashboards") {
2428
+ return parseAnalyticsDashboardsArgs(resourceArgs.slice(3));
2429
+ }
2430
+ if (resourceArgs[1] === "analytics" &&
2431
+ resourceArgs[2] === "export" &&
2432
+ (resourceArgs[3] === undefined ||
2433
+ resourceArgs[3] === "--help" ||
2434
+ resourceArgs[3] === "-h")) {
2435
+ return { command: "help", topic: "artifacts analytics export" };
2436
+ }
2437
+ if (resourceArgs[1] === "analytics" &&
2438
+ resourceArgs[2] === "export" &&
2439
+ resourceArgs[3] === "catalog") {
2440
+ return parseAnalyticsExportCatalogArgs(resourceArgs.slice(4));
2441
+ }
2442
+ if (resourceArgs[1] === "analytics" &&
2443
+ resourceArgs[2] === "export" &&
2444
+ resourceArgs[3] === "query") {
2445
+ return parseAnalyticsExportQueryArgs(resourceArgs.slice(4));
2446
+ }
2447
+ if (resourceArgs[1] === "analytics" &&
2448
+ resourceArgs[2] === "charts" &&
2449
+ resourceArgs[3] === "create") {
2450
+ return parseAnalyticsChartsCreateArgs(resourceArgs.slice(4));
2451
+ }
1936
2452
  if (resourceArgs[1] === "context" &&
1937
2453
  (resourceArgs[2] === undefined ||
1938
2454
  resourceArgs[2] === "--help" ||
@@ -1948,6 +2464,9 @@ function parseTenderArgs(args) {
1948
2464
  if (resourceArgs[1] === "git" && resourceArgs[2] === "credential") {
1949
2465
  return parseGitCredentialArgs(resourceArgs.slice(3));
1950
2466
  }
2467
+ if (resourceArgs[1] === "generate-env-types") {
2468
+ return parseGenerateEnvTypesArgs(resourceArgs.slice(2));
2469
+ }
1951
2470
  if (resourceArgs[1] === "validate") {
1952
2471
  return parseValidateArgs(resourceArgs.slice(2));
1953
2472
  }
@@ -2077,6 +2596,69 @@ async function runDoctor(command, io) {
2077
2596
  }
2078
2597
  return result.ok ? 0 : 1;
2079
2598
  }
2599
+ function formatEnvTypesGenerationFailure(result) {
2600
+ if (result.ok || result.diagnostics.length === 0) {
2601
+ return "Could not generate src/env.d.ts.";
2602
+ }
2603
+ return formatDoctorHuman({
2604
+ ok: false,
2605
+ diagnostics: result.diagnostics,
2606
+ });
2607
+ }
2608
+ async function runGenerateEnvTypes(command, io) {
2609
+ const root = path.resolve(command.dir);
2610
+ const files = await collectTenderAppFiles({ root, current: root });
2611
+ const result = generateTenderAppEnvTypes({
2612
+ files,
2613
+ mode: "local",
2614
+ });
2615
+ if (!result.ok) {
2616
+ if (command.json) {
2617
+ io.stdout(JSON.stringify({
2618
+ ok: false,
2619
+ command: command.command,
2620
+ dir: root,
2621
+ path: result.path,
2622
+ diagnostics: result.diagnostics,
2623
+ }, null, 2));
2624
+ }
2625
+ else {
2626
+ io.stdout(formatEnvTypesGenerationFailure(result));
2627
+ }
2628
+ return 1;
2629
+ }
2630
+ const absolutePath = path.join(root, result.path);
2631
+ const previous = await readFile(absolutePath, "utf8").catch((error) => {
2632
+ if (error &&
2633
+ typeof error === "object" &&
2634
+ error.code === "ENOENT") {
2635
+ return null;
2636
+ }
2637
+ throw error;
2638
+ });
2639
+ const changed = previous !== result.content;
2640
+ await mkdir(path.dirname(absolutePath), { recursive: true });
2641
+ await writeFile(absolutePath, result.content);
2642
+ if (command.json) {
2643
+ io.stdout(JSON.stringify({
2644
+ ok: true,
2645
+ command: command.command,
2646
+ dir: root,
2647
+ path: result.path,
2648
+ absolutePath,
2649
+ changed,
2650
+ bytes: Buffer.byteLength(result.content),
2651
+ diagnostics: result.diagnostics,
2652
+ }, null, 2));
2653
+ }
2654
+ else {
2655
+ io.stdout([
2656
+ `generated: ${path.join(command.dir, result.path)}`,
2657
+ `changed: ${changed ? "yes" : "no"}`,
2658
+ ].join("\n"));
2659
+ }
2660
+ return 0;
2661
+ }
2080
2662
  async function parseJsonResponse(response) {
2081
2663
  return (await response.json().catch(() => null));
2082
2664
  }
@@ -2114,6 +2696,119 @@ function parseSseLifecycleBlock(block) {
2114
2696
  return { raw: dataLines.join("\n") };
2115
2697
  }
2116
2698
  }
2699
+ function appendAnalyticsRange(params, range) {
2700
+ if (range === "7d" || range === "30d" || range === "90d") {
2701
+ params.set("rangePreset", range);
2702
+ return;
2703
+ }
2704
+ const [startDate, endDate] = range.split("..");
2705
+ if (startDate && endDate) {
2706
+ params.set("startDate", startDate);
2707
+ params.set("endDate", endDate);
2708
+ }
2709
+ }
2710
+ async function readAnalyticsSpec(input) {
2711
+ const raw = input.specPath === "-"
2712
+ ? input.stdin
2713
+ : await readFile(path.resolve(input.specPath), "utf8");
2714
+ if (!raw) {
2715
+ throw new TenderCliUsageError("--spec - requires JSON on stdin.");
2716
+ }
2717
+ try {
2718
+ return JSON.parse(raw);
2719
+ }
2720
+ catch {
2721
+ throw new TenderCliUsageError("--spec must contain valid JSON.");
2722
+ }
2723
+ }
2724
+ async function requestAnalyticsApi(input) {
2725
+ const params = new URLSearchParams();
2726
+ if (input.range) {
2727
+ appendAnalyticsRange(params, input.range);
2728
+ }
2729
+ if (input.includeCatalog) {
2730
+ params.set("includeCatalog", "1");
2731
+ }
2732
+ const suffix = input.analyticsPath ? `/${input.analyticsPath}` : "";
2733
+ const query = params.size > 0 ? `?${params.toString()}` : "";
2734
+ const response = await input.fetcher(`${input.baseUrl}/api/v1/artifacts/${encodeURIComponent(input.artifactId)}/analytics${suffix}${query}`, {
2735
+ method: input.method,
2736
+ headers: {
2737
+ authorization: `Bearer ${input.token}`,
2738
+ accept: "application/json",
2739
+ ...(input.body === undefined ? {} : { "content-type": "application/json" }),
2740
+ },
2741
+ body: input.body === undefined ? undefined : JSON.stringify(input.body),
2742
+ });
2743
+ const payload = await parseJsonResponse(response);
2744
+ if (!response.ok || !payload?.ok) {
2745
+ const reasonCode = (typeof payload?.reasonCode === "string" && payload.reasonCode) ||
2746
+ `http_${response.status}`;
2747
+ const message = (typeof payload?.message === "string" && payload.message) ||
2748
+ (typeof payload?.reasonCode === "string" && payload.reasonCode) ||
2749
+ `Analytics request failed with HTTP ${response.status}.`;
2750
+ throw new Error([
2751
+ message,
2752
+ `Scope: artifact ${input.artifactId}, analytics/${input.analyticsPath || "summary"}.`,
2753
+ `Reason: ${reasonCode}.`,
2754
+ `Example: ${analyticsCorrectedExample(input.artifactId, input.analyticsPath)}.`,
2755
+ ].join("\n"));
2756
+ }
2757
+ return payload;
2758
+ }
2759
+ function analyticsCorrectedExample(artifactId, analyticsPath) {
2760
+ if (analyticsPath === "capabilities") {
2761
+ return `tender app analytics capabilities ${artifactId} --include-catalog --range 30d --json`;
2762
+ }
2763
+ if (analyticsPath === "query") {
2764
+ return `tender app analytics query ${artifactId} --spec chart.json --json`;
2765
+ }
2766
+ if (analyticsPath === "suggestions") {
2767
+ return `tender app analytics suggestions ${artifactId} --range 30d --json`;
2768
+ }
2769
+ if (analyticsPath === "dashboards") {
2770
+ return `tender app analytics dashboards ${artifactId} --json`;
2771
+ }
2772
+ if (analyticsPath === "charts") {
2773
+ return `tender app analytics charts create ${artifactId} --dashboard analytics_dashboard_123 --title "Signup funnel" --spec chart.json --json`;
2774
+ }
2775
+ return `tender app analytics summary ${artifactId} --range 30d --json`;
2776
+ }
2777
+ function printAnalyticsPayload(command, io, payload, fallbackLines) {
2778
+ if (command.json) {
2779
+ io.stdout(JSON.stringify(payload, null, 2));
2780
+ }
2781
+ else {
2782
+ io.stdout(fallbackLines.join("\n"));
2783
+ }
2784
+ }
2785
+ function csvCell(value) {
2786
+ if (value === null || value === undefined) {
2787
+ return "";
2788
+ }
2789
+ const json = JSON.stringify(value);
2790
+ const text = typeof value === "string" ? value : typeof json === "string" ? json : String(value);
2791
+ if (/[",\n\r]/.test(text)) {
2792
+ return `"${text.replaceAll("\"", "\"\"")}"`;
2793
+ }
2794
+ return text;
2795
+ }
2796
+ function rowsToCsv(rows) {
2797
+ const records = rows.filter((row) => Boolean(row) && typeof row === "object" && !Array.isArray(row));
2798
+ const headers = Array.from(records.reduce((set, row) => {
2799
+ for (const key of Object.keys(row)) {
2800
+ set.add(key);
2801
+ }
2802
+ return set;
2803
+ }, new Set()));
2804
+ if (headers.length === 0) {
2805
+ return "";
2806
+ }
2807
+ return [
2808
+ headers.map(csvCell).join(","),
2809
+ ...records.map((row) => headers.map((header) => csvCell(row[header])).join(",")),
2810
+ ].join("\n");
2811
+ }
2117
2812
  function findSseBlockSeparator(buffer) {
2118
2813
  const unixIndex = buffer.indexOf("\n\n");
2119
2814
  const windowsIndex = buffer.indexOf("\r\n\r\n");
@@ -2943,6 +3638,8 @@ function createGitSetupCommandPlan(input) {
2943
3638
  input.credentialHelper,
2944
3639
  ],
2945
3640
  ["git", "config", "--local", "credential.useHttpPath", "true"],
3641
+ ["git", "config", "--local", "pack.window", "0"],
3642
+ ["git", "config", "--local", "pack.depth", "0"],
2946
3643
  ];
2947
3644
  }
2948
3645
  async function applyGitSetup(input) {
@@ -2998,6 +3695,16 @@ async function applyGitSetup(input) {
2998
3695
  cwd: input.root,
2999
3696
  args: ["config", "--local", "credential.useHttpPath", "true"],
3000
3697
  });
3698
+ await runGitCommand({
3699
+ runtime: input.runtime,
3700
+ cwd: input.root,
3701
+ args: ["config", "--local", "pack.window", "0"],
3702
+ });
3703
+ await runGitCommand({
3704
+ runtime: input.runtime,
3705
+ cwd: input.root,
3706
+ args: ["config", "--local", "pack.depth", "0"],
3707
+ });
3001
3708
  }
3002
3709
  async function maybeCheckoutInitialArtifactSource(input) {
3003
3710
  const localHead = await runGitCommand({
@@ -4038,6 +4745,226 @@ async function runGitRemote(command, io, runtime) {
4038
4745
  }
4039
4746
  return 0;
4040
4747
  }
4748
+ async function resolveAnalyticsCredentials(command, runtime) {
4749
+ return await resolveApiCredentials({
4750
+ baseUrl: command.baseUrl,
4751
+ flagToken: command.flagToken,
4752
+ profileName: command.profileName,
4753
+ env: runtime.env ?? process.env,
4754
+ });
4755
+ }
4756
+ async function runAnalyticsSummary(command, io, runtime) {
4757
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4758
+ const payload = await requestAnalyticsApi({
4759
+ artifactId: command.artifactId,
4760
+ analyticsPath: "",
4761
+ method: "GET",
4762
+ baseUrl: credentials.baseUrl,
4763
+ token: credentials.token,
4764
+ fetcher: runtime.fetcher ?? fetch,
4765
+ range: command.range,
4766
+ });
4767
+ const points = Array.isArray(payload.points) ? payload.points : [];
4768
+ printAnalyticsPayload(command, io, payload, [
4769
+ `app_id: ${command.artifactId}`,
4770
+ `source: ${String(payload.source ?? "unknown")}`,
4771
+ `points: ${points.length}`,
4772
+ ]);
4773
+ return 0;
4774
+ }
4775
+ async function runAnalyticsQuery(command, io, runtime) {
4776
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4777
+ const spec = await readAnalyticsSpec({
4778
+ specPath: command.specPath,
4779
+ stdin: runtime.stdin,
4780
+ });
4781
+ const payload = await requestAnalyticsApi({
4782
+ artifactId: command.artifactId,
4783
+ analyticsPath: "query",
4784
+ method: "POST",
4785
+ baseUrl: credentials.baseUrl,
4786
+ token: credentials.token,
4787
+ fetcher: runtime.fetcher ?? fetch,
4788
+ body: { spec },
4789
+ });
4790
+ const rows = Array.isArray(payload.rows) ? payload.rows : [];
4791
+ printAnalyticsPayload(command, io, payload, [
4792
+ `app_id: ${command.artifactId}`,
4793
+ `source: ${String(payload.source ?? "unknown")}`,
4794
+ `rows: ${rows.length}`,
4795
+ ]);
4796
+ return 0;
4797
+ }
4798
+ async function runAnalyticsCapabilities(command, io, runtime) {
4799
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4800
+ const payload = await requestAnalyticsApi({
4801
+ artifactId: command.artifactId,
4802
+ analyticsPath: "capabilities",
4803
+ method: "GET",
4804
+ baseUrl: credentials.baseUrl,
4805
+ token: credentials.token,
4806
+ fetcher: runtime.fetcher ?? fetch,
4807
+ range: command.range,
4808
+ includeCatalog: command.includeCatalog,
4809
+ });
4810
+ const commands = Array.isArray(payload.commands) ? payload.commands : [];
4811
+ const catalog = payload.catalog && typeof payload.catalog === "object"
4812
+ ? payload.catalog
4813
+ : null;
4814
+ const observedEvents = Array.isArray(catalog?.observedEvents)
4815
+ ? catalog.observedEvents
4816
+ : [];
4817
+ printAnalyticsPayload(command, io, payload, [
4818
+ `app_id: ${command.artifactId}`,
4819
+ `commands: ${commands.length}`,
4820
+ `catalog_events: ${observedEvents.length}`,
4821
+ ]);
4822
+ return 0;
4823
+ }
4824
+ async function runAnalyticsSuggestions(command, io, runtime) {
4825
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4826
+ const payload = await requestAnalyticsApi({
4827
+ artifactId: command.artifactId,
4828
+ analyticsPath: "suggestions",
4829
+ method: "POST",
4830
+ baseUrl: credentials.baseUrl,
4831
+ token: credentials.token,
4832
+ fetcher: runtime.fetcher ?? fetch,
4833
+ range: command.range,
4834
+ body: {},
4835
+ });
4836
+ const suggestions = Array.isArray(payload.suggestions) ? payload.suggestions : [];
4837
+ printAnalyticsPayload(command, io, payload, [
4838
+ `app_id: ${command.artifactId}`,
4839
+ `suggestions: ${suggestions.length}`,
4840
+ ]);
4841
+ return 0;
4842
+ }
4843
+ async function runAnalyticsDashboards(command, io, runtime) {
4844
+ if (command.name && command.dryRun) {
4845
+ const payload = {
4846
+ ok: true,
4847
+ dryRun: true,
4848
+ artifactId: command.artifactId,
4849
+ request: {
4850
+ name: command.name,
4851
+ },
4852
+ };
4853
+ printAnalyticsPayload(command, io, payload, [
4854
+ `app_id: ${command.artifactId}`,
4855
+ `name: ${command.name}`,
4856
+ "dry_run: true",
4857
+ ]);
4858
+ return 0;
4859
+ }
4860
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4861
+ const payload = await requestAnalyticsApi({
4862
+ artifactId: command.artifactId,
4863
+ analyticsPath: "dashboards",
4864
+ method: command.name ? "POST" : "GET",
4865
+ baseUrl: credentials.baseUrl,
4866
+ token: credentials.token,
4867
+ fetcher: runtime.fetcher ?? fetch,
4868
+ body: command.name ? { name: command.name } : undefined,
4869
+ });
4870
+ const dashboards = Array.isArray(payload.dashboards) ? payload.dashboards : [];
4871
+ printAnalyticsPayload(command, io, payload, [
4872
+ `app_id: ${command.artifactId}`,
4873
+ command.name
4874
+ ? `dashboard_id: ${String(payload.dashboard?.id ?? "")}`
4875
+ : `dashboards: ${dashboards.length}`,
4876
+ ]);
4877
+ return 0;
4878
+ }
4879
+ async function runAnalyticsExportCatalog(command, io, runtime) {
4880
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4881
+ const payload = await requestAnalyticsApi({
4882
+ artifactId: command.artifactId,
4883
+ analyticsPath: "capabilities",
4884
+ method: "GET",
4885
+ baseUrl: credentials.baseUrl,
4886
+ token: credentials.token,
4887
+ fetcher: runtime.fetcher ?? fetch,
4888
+ range: command.range,
4889
+ includeCatalog: true,
4890
+ });
4891
+ const catalog = payload.catalog && typeof payload.catalog === "object"
4892
+ ? payload.catalog
4893
+ : null;
4894
+ const exportPayload = {
4895
+ ok: true,
4896
+ artifactId: command.artifactId,
4897
+ format: command.format,
4898
+ catalog,
4899
+ };
4900
+ io.stdout(JSON.stringify(exportPayload, null, 2));
4901
+ return 0;
4902
+ }
4903
+ async function runAnalyticsExportQuery(command, io, runtime) {
4904
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4905
+ const spec = await readAnalyticsSpec({
4906
+ specPath: command.specPath,
4907
+ stdin: runtime.stdin,
4908
+ });
4909
+ const payload = await requestAnalyticsApi({
4910
+ artifactId: command.artifactId,
4911
+ analyticsPath: "query",
4912
+ method: "POST",
4913
+ baseUrl: credentials.baseUrl,
4914
+ token: credentials.token,
4915
+ fetcher: runtime.fetcher ?? fetch,
4916
+ body: { spec },
4917
+ });
4918
+ const rows = Array.isArray(payload.rows) ? payload.rows : [];
4919
+ if (command.format === "csv") {
4920
+ io.stdout(rowsToCsv(rows));
4921
+ return 0;
4922
+ }
4923
+ io.stdout(JSON.stringify(payload, null, 2));
4924
+ return 0;
4925
+ }
4926
+ async function runAnalyticsChartsCreate(command, io, runtime) {
4927
+ const spec = await readAnalyticsSpec({
4928
+ specPath: command.specPath,
4929
+ stdin: runtime.stdin,
4930
+ });
4931
+ const body = {
4932
+ dashboardId: command.dashboardId,
4933
+ title: command.title,
4934
+ spec,
4935
+ layout: {},
4936
+ };
4937
+ if (command.dryRun) {
4938
+ const payload = {
4939
+ ok: true,
4940
+ dryRun: true,
4941
+ artifactId: command.artifactId,
4942
+ request: body,
4943
+ };
4944
+ printAnalyticsPayload(command, io, payload, [
4945
+ `app_id: ${command.artifactId}`,
4946
+ `dashboard_id: ${command.dashboardId}`,
4947
+ `title: ${command.title}`,
4948
+ "dry_run: true",
4949
+ ]);
4950
+ return 0;
4951
+ }
4952
+ const credentials = await resolveAnalyticsCredentials(command, runtime);
4953
+ const payload = await requestAnalyticsApi({
4954
+ artifactId: command.artifactId,
4955
+ analyticsPath: "charts",
4956
+ method: "POST",
4957
+ baseUrl: credentials.baseUrl,
4958
+ token: credentials.token,
4959
+ fetcher: runtime.fetcher ?? fetch,
4960
+ body,
4961
+ });
4962
+ printAnalyticsPayload(command, io, payload, [
4963
+ `app_id: ${command.artifactId}`,
4964
+ `chart_id: ${String(payload.chart?.id ?? "")}`,
4965
+ ]);
4966
+ return 0;
4967
+ }
4041
4968
  export async function runTenderCli(args, io = {
4042
4969
  stdout: (message) => console.log(message),
4043
4970
  stderr: (message) => console.error(message),
@@ -4066,6 +4993,9 @@ export async function runTenderCli(args, io = {
4066
4993
  else if (command.topic === "artifacts doctor") {
4067
4994
  io.stdout(doctorHelp());
4068
4995
  }
4996
+ else if (command.topic === "artifacts generate-env-types") {
4997
+ io.stdout(generateEnvTypesHelp());
4998
+ }
4069
4999
  else if (command.topic === "artifacts list") {
4070
5000
  io.stdout(artifactsListHelp());
4071
5001
  }
@@ -4111,6 +5041,36 @@ export async function runTenderCli(args, io = {
4111
5041
  else if (command.topic === "artifacts publish watch") {
4112
5042
  io.stdout(publishWatchHelp());
4113
5043
  }
5044
+ else if (command.topic === "artifacts analytics") {
5045
+ io.stdout(analyticsHelp());
5046
+ }
5047
+ else if (command.topic === "artifacts analytics summary") {
5048
+ io.stdout(analyticsSummaryHelp());
5049
+ }
5050
+ else if (command.topic === "artifacts analytics query") {
5051
+ io.stdout(analyticsQueryHelp());
5052
+ }
5053
+ else if (command.topic === "artifacts analytics capabilities") {
5054
+ io.stdout(analyticsCapabilitiesHelp());
5055
+ }
5056
+ else if (command.topic === "artifacts analytics suggestions") {
5057
+ io.stdout(analyticsSuggestionsHelp());
5058
+ }
5059
+ else if (command.topic === "artifacts analytics dashboards") {
5060
+ io.stdout(analyticsDashboardsHelp());
5061
+ }
5062
+ else if (command.topic === "artifacts analytics export") {
5063
+ io.stdout(analyticsExportHelp());
5064
+ }
5065
+ else if (command.topic === "artifacts analytics export catalog") {
5066
+ io.stdout(analyticsExportCatalogHelp());
5067
+ }
5068
+ else if (command.topic === "artifacts analytics export query") {
5069
+ io.stdout(analyticsExportQueryHelp());
5070
+ }
5071
+ else if (command.topic === "artifacts analytics charts create") {
5072
+ io.stdout(analyticsChartsCreateHelp());
5073
+ }
4114
5074
  else if (command.topic === "artifacts") {
4115
5075
  io.stdout(artifactsHelp());
4116
5076
  }
@@ -4137,6 +5097,9 @@ export async function runTenderCli(args, io = {
4137
5097
  if (command.command === "artifacts init") {
4138
5098
  return await runArtifactsInit(command, io, runtime);
4139
5099
  }
5100
+ if (command.command === "artifacts generate-env-types") {
5101
+ return await runGenerateEnvTypes(command, io);
5102
+ }
4140
5103
  if (command.command === "artifacts context fetch") {
4141
5104
  return await runContextFetch(command, io, runtime);
4142
5105
  }
@@ -4176,6 +5139,30 @@ export async function runTenderCli(args, io = {
4176
5139
  if (command.command === "artifacts publish watch") {
4177
5140
  return await runPublishWatch(command, io, runtime);
4178
5141
  }
5142
+ if (command.command === "artifacts analytics summary") {
5143
+ return await runAnalyticsSummary(command, io, runtime);
5144
+ }
5145
+ if (command.command === "artifacts analytics query") {
5146
+ return await runAnalyticsQuery(command, io, runtime);
5147
+ }
5148
+ if (command.command === "artifacts analytics capabilities") {
5149
+ return await runAnalyticsCapabilities(command, io, runtime);
5150
+ }
5151
+ if (command.command === "artifacts analytics suggestions") {
5152
+ return await runAnalyticsSuggestions(command, io, runtime);
5153
+ }
5154
+ if (command.command === "artifacts analytics dashboards") {
5155
+ return await runAnalyticsDashboards(command, io, runtime);
5156
+ }
5157
+ if (command.command === "artifacts analytics export catalog") {
5158
+ return await runAnalyticsExportCatalog(command, io, runtime);
5159
+ }
5160
+ if (command.command === "artifacts analytics export query") {
5161
+ return await runAnalyticsExportQuery(command, io, runtime);
5162
+ }
5163
+ if (command.command === "artifacts analytics charts create") {
5164
+ return await runAnalyticsChartsCreate(command, io, runtime);
5165
+ }
4179
5166
  if (command.command === "artifacts git remote") {
4180
5167
  return await runGitRemote(command, io, runtime);
4181
5168
  }
@@ -4190,8 +5177,8 @@ export async function runTenderCli(args, io = {
4190
5177
  const currentFile = fileURLToPath(import.meta.url);
4191
5178
  function shouldReadEntrypointStdin(args) {
4192
5179
  return ((args[0] === "artifacts" || args[0] === "app") &&
4193
- args[1] === "git" &&
4194
- args[2] === "credential");
5180
+ ((args[1] === "git" && args[2] === "credential") ||
5181
+ (args[1] === "analytics" && args.includes("--spec") && args.includes("-"))));
4195
5182
  }
4196
5183
  async function readEntrypointStdin() {
4197
5184
  if (process.stdin.isTTY) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenderprompt/cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tender": "./bin/tender.js"