@tenderprompt/cli 0.1.13 → 0.1.15

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 CHANGED
@@ -16,6 +16,7 @@ npm install -g @tenderprompt/cli
16
16
  Then run:
17
17
 
18
18
  ```bash
19
+ tender --version
19
20
  tender capabilities --json
20
21
  ```
21
22
 
@@ -25,6 +26,8 @@ Use the CLI to work with Tender Apps from a local checkout or coding-agent
25
26
  session:
26
27
 
27
28
  - sign in with a bounded device token and optional artifact scope
29
+ - print the installed CLI package version with `tender --version`, `tender -v`,
30
+ or `tender version --json`
28
31
  - list, create, and rename apps
29
32
  - bootstrap a local app checkout with Tender project context
30
33
  - configure the Tender artifact Git remote and credential helper
@@ -48,6 +51,7 @@ interactive prompts unless the command is explicitly an auth flow.
48
51
  ## Typical Flow
49
52
 
50
53
  ```bash
54
+ tender --version
51
55
  tender capabilities --json
52
56
 
53
57
  tender auth login --device --profile edit-preview --artifact artifact_123 --ttl 7d
@@ -120,6 +124,7 @@ indexing the accepted commit after upstream Git accepts the push.
120
124
  ## Useful Commands
121
125
 
122
126
  ```bash
127
+ tender --version
123
128
  tender capabilities --json
124
129
  tender app list --json
125
130
  tender app create --name "Exit Intent Widget" --json
@@ -133,9 +138,10 @@ tender app build artifact_123 --commit "$(git rev-parse HEAD)" --json
133
138
  tender app preview artifact_123 --commit "$(git rev-parse HEAD)" --stream --json
134
139
  tender app publish artifact_123 --dry-run --json
135
140
  tender app db capabilities artifact_123 --json
136
- tender app db tables artifact_123 --json
137
- tender app db schema artifact_123 --table votes --json
138
- tender app db query artifact_123 --sql "select * from votes limit 20" --json
141
+ tender app db tables artifact_123 --env published --json
142
+ tender app db schema artifact_123 --env published --table votes --json
143
+ tender app db query artifact_123 --env published --sql "select * from votes limit 20" --json
144
+ tender app db query artifact_123 --env published --sql "select * from votes order by created_at" --limit 50 --offset 50 --json
139
145
  ```
140
146
 
141
147
  ## Analytics
@@ -249,7 +255,8 @@ ids, URLs, or high-cardinality values unless the chart has a clear purpose.
249
255
  App database commands are artifact-scoped and go through Tender's server-side
250
256
  authorization and read-only query checks. The CLI only accepts an app id; it
251
257
  does not accept tenant ids, runner names, provider account ids, or runtime
252
- storage identifiers.
258
+ storage identifiers. Commands that read data require an explicit `--env preview`
259
+ or `--env published`; there is no default environment.
253
260
 
254
261
  Start with capabilities:
255
262
 
@@ -262,24 +269,28 @@ tender app db capabilities artifact_123 --profile db --json
262
269
  Common read-only commands:
263
270
 
264
271
  ```bash
265
- tender app db tables artifact_123 --profile db --json
272
+ tender app db tables artifact_123 --env published --profile db --json
266
273
 
267
- tender app db schema artifact_123 --table votes --profile db --json
274
+ tender app db schema artifact_123 --env published --table votes --profile db --json
268
275
 
269
- tender app db query artifact_123 --sql "select * from votes limit 20" --profile db --json
276
+ tender app db query artifact_123 --env published --sql "select * from votes limit 20" --profile db --json
270
277
 
271
278
  tender app db query artifact_123 \
279
+ --env published \
272
280
  --sql "select * from votes where choice = ?" \
273
281
  --param coffee \
274
282
  --profile db \
275
283
  --json
276
284
 
277
- tender app db query artifact_123 --file ./debug.sql --limit 50 --profile db --json
285
+ tender app db query artifact_123 --env published --file ./debug.sql --limit 50 --offset 100 --profile db --json
278
286
 
279
- tender app db query artifact_123 --file - --profile db --json < debug.sql
287
+ tender app db query artifact_123 --env published --file - --offset 100 --profile db --json < debug.sql
280
288
  ```
281
289
 
282
- The query command sends one read-only statement with positional bindings.
290
+ The query command sends one read-only statement with positional bindings. Query
291
+ responses include `requestedLimit`, `appliedLimit`, `maxInteractiveLimit`,
292
+ `offset`, and `nextOffset`; agents must request each page explicitly and should
293
+ not auto-drain large tables. The interactive row cap is 50 rows per request.
283
294
  Client and server limits include a 100 KB statement cap and at most 100 bound
284
295
  parameters. The server enforces the final policy, row limits, response limits,
285
296
  artifact scope, and audit logging.
package/bin/tender.js CHANGED
@@ -1,14 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { runTenderCli } from "../dist/index.js";
4
-
5
- function shouldReadEntrypointStdin(args) {
6
- return (
7
- args[0] === "artifacts" &&
8
- args[1] === "git" &&
9
- args[2] === "credential"
10
- );
11
- }
3
+ import { runTenderCli, shouldReadEntrypointStdin } from "../dist/index.js";
12
4
 
13
5
  async function readEntrypointStdin() {
14
6
  if (process.stdin.isTTY) {
package/dist/index.d.ts CHANGED
@@ -19,4 +19,5 @@ type TenderCommandResult = {
19
19
  stderr: string;
20
20
  };
21
21
  export declare function runTenderCli(args: string[], io?: TenderCliIo, runtime?: TenderCliRuntime): Promise<number>;
22
+ export declare function shouldReadEntrypointStdin(args: string[]): boolean;
22
23
  export {};
package/dist/index.js CHANGED
@@ -41,18 +41,28 @@ class TenderCliUsageError extends Error {
41
41
  }
42
42
  class TenderCliAnalyticsApiError extends Error {
43
43
  reasonCode;
44
- constructor(message, reasonCode) {
44
+ scope;
45
+ example;
46
+ constructor(message, reasonCode, scope = null, example = null) {
45
47
  super(message);
46
48
  this.name = "TenderCliAnalyticsApiError";
47
49
  this.reasonCode = reasonCode;
50
+ this.scope = scope;
51
+ this.example = example;
48
52
  }
49
53
  }
50
54
  class TenderCliDbApiError extends Error {
51
55
  reasonCode;
52
- constructor(message, reasonCode) {
56
+ scope;
57
+ example;
58
+ nextAction;
59
+ constructor(message, reasonCode, scope, example, nextAction = null) {
53
60
  super(message);
54
61
  this.name = "TenderCliDbApiError";
55
62
  this.reasonCode = reasonCode;
63
+ this.scope = scope;
64
+ this.example = example;
65
+ this.nextAction = nextAction;
56
66
  }
57
67
  }
58
68
  const DEFAULT_PROFILE_NAME = "default";
@@ -65,6 +75,8 @@ function rootHelp() {
65
75
  return `Usage: tender <command> [options]
66
76
 
67
77
  Commands:
78
+ tender --version
79
+ Show the installed Tender CLI version
68
80
  tender capabilities --json
69
81
  Describe the CLI command surface for coding agents
70
82
  tender auth login --device
@@ -101,7 +113,7 @@ Commands:
101
113
  Generate useful starter chart suggestions
102
114
  tender app db capabilities <app-id>
103
115
  Describe app database query limits and policy
104
- tender app db query <app-id> --sql <sql>
116
+ tender app db query <app-id> --env <preview|published> --sql <sql>
105
117
  Run one read-only app database query
106
118
  tender app context fetch <app-id>
107
119
  Fetch managed agent context into a checkout
@@ -118,6 +130,7 @@ Commands:
118
130
  Regenerate src/env.d.ts from app.json and bindings
119
131
 
120
132
  Examples:
133
+ tender --version
121
134
  tender capabilities --json
122
135
  tender auth login --device --profile edit-preview --artifact artifact_123 --ttl 7d
123
136
  tender auth status --json
@@ -135,9 +148,9 @@ Examples:
135
148
  tender app analytics query artifact_123 --spec chart.json --json
136
149
  tender app analytics suggestions artifact_123 --range 30d --json
137
150
  tender app db capabilities artifact_123 --json
138
- tender app db tables artifact_123 --json
139
- tender app db schema artifact_123 --table votes --json
140
- tender app db query artifact_123 --sql "select * from votes limit 20" --json
151
+ tender app db tables artifact_123 --env published --json
152
+ tender app db schema artifact_123 --env published --table votes --json
153
+ tender app db query artifact_123 --env published --sql "select * from votes limit 20" --json
141
154
  tender app context fetch artifact_123 --dir ./widget --dry-run --json
142
155
  tender app context status artifact_123 --dir ./widget --json
143
156
  tender app context refresh artifact_123 --dir ./widget --dry-run --json
@@ -238,11 +251,11 @@ Commands:
238
251
  Make a custom event property queryable
239
252
  tender app db capabilities <app-id>
240
253
  Describe app database query limits and policy
241
- tender app db tables <app-id>
254
+ tender app db tables <app-id> --env <preview|published>
242
255
  List app database tables and views
243
- tender app db schema <app-id> --table <name>
256
+ tender app db schema <app-id> --env <preview|published> --table <name>
244
257
  Show table and index schema for one table
245
- tender app db query <app-id> --sql <sql>
258
+ tender app db query <app-id> --env <preview|published> --sql <sql>
246
259
  Run one read-only app database query
247
260
  tender app context fetch <app-id>
248
261
  Fetch AGENTS.md, skills, and Tender App scaffold files
@@ -285,9 +298,9 @@ Examples:
285
298
  tender app analytics properties list artifact_123 --json
286
299
  tender app analytics properties activate artifact_123 --event signup_started --property plan --type dimension --dry-run --json
287
300
  tender app db capabilities artifact_123 --json
288
- tender app db tables artifact_123 --json
289
- tender app db schema artifact_123 --table votes --json
290
- tender app db query artifact_123 --sql "select * from votes limit 20" --json
301
+ tender app db tables artifact_123 --env published --json
302
+ tender app db schema artifact_123 --env published --table votes --json
303
+ tender app db query artifact_123 --env published --sql "select * from votes limit 20" --json
291
304
  tender app context fetch artifact_123 --dir ./widget --dry-run --json
292
305
  tender app context status artifact_123 --dir ./widget --json
293
306
  tender app context refresh artifact_123 --dir ./widget --dry-run --json
@@ -307,6 +320,17 @@ copying long command lists into skill prompts.
307
320
  Examples:
308
321
  tender capabilities --json`;
309
322
  }
323
+ function versionHelp() {
324
+ return `Usage: tender --version
325
+ tender -v
326
+ tender version [--json]
327
+
328
+ Prints the installed Tender CLI package version.
329
+
330
+ Examples:
331
+ tender --version
332
+ tender version --json`;
333
+ }
310
334
  function analyticsHelp() {
311
335
  return `Usage: tender app analytics <command> <app-id> [options]
312
336
 
@@ -471,16 +495,19 @@ function dbHelp() {
471
495
 
472
496
  Commands:
473
497
  tender app db capabilities <app-id> Describe app database policy and limits
474
- tender app db tables <app-id> List app database tables and views
475
- tender app db schema <app-id> Show schema for one table
476
- tender app db query <app-id> Run one read-only SQL query
498
+ tender app db tables <app-id> --env <preview|published>
499
+ List app database tables and views
500
+ tender app db schema <app-id> --env <preview|published>
501
+ Show schema for one table
502
+ tender app db query <app-id> --env <preview|published>
503
+ Run one read-only SQL query
477
504
 
478
505
  Examples:
479
506
  tender app db capabilities artifact_123 --json
480
- tender app db tables artifact_123 --json
481
- tender app db schema artifact_123 --table votes --json
482
- tender app db query artifact_123 --sql "select * from votes limit 20" --json
483
- tender app db query artifact_123 --file ./debug.sql --param active --limit 50 --json`;
507
+ tender app db tables artifact_123 --env published --json
508
+ tender app db schema artifact_123 --env published --table votes --json
509
+ tender app db query artifact_123 --env published --sql "select * from votes limit 20" --json
510
+ tender app db query artifact_123 --env published --file ./debug.sql --param active --limit 50 --json`;
484
511
  }
485
512
  function dbCapabilitiesHelp() {
486
513
  return `Usage: tender app db capabilities <app-id> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
@@ -489,33 +516,35 @@ Examples:
489
516
  tender app db capabilities artifact_123 --json`;
490
517
  }
491
518
  function dbTablesHelp() {
492
- return `Usage: tender app db tables <app-id> [--env preview] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
519
+ return `Usage: tender app db tables <app-id> --env <preview|published> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
493
520
 
494
521
  Examples:
495
- tender app db tables artifact_123 --json
522
+ tender app db tables artifact_123 --env published --json
496
523
  tender app db tables artifact_123 --env preview --json`;
497
524
  }
498
525
  function dbSchemaHelp() {
499
- return `Usage: tender app db schema <app-id> --table <name> [--env preview] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
526
+ return `Usage: tender app db schema <app-id> --env <preview|published> --table <name> [--base-url <url>] [--token <token>] [--profile <name>] [--json]
500
527
 
501
528
  Examples:
502
- tender app db schema artifact_123 --table votes --json
529
+ tender app db schema artifact_123 --env published --table votes --json
503
530
  tender app db schema artifact_123 --table preference_votes --env preview --json`;
504
531
  }
505
532
  function dbQueryHelp() {
506
- return `Usage: tender app db query <app-id> (--sql <sql> | --file <path|->) [--param <value>] [--limit <n>] [--env preview] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
533
+ return `Usage: tender app db query <app-id> --env <preview|published> (--sql <sql> | --file <path|->) [--param <value>] [--limit <n>] [--offset <n>] [--base-url <url>] [--token <token>] [--profile <name>] [--json]
507
534
 
508
535
  Options:
536
+ --env <value> Target app environment: preview or published. Required.
509
537
  --sql <sql> SQL statement. Must be one read-only statement.
510
538
  --file <path|-> Read SQL from a file or stdin with -.
511
- --param <value> Bind one positional parameter. Repeat for multiple params. Values parse as JSON scalars, falling back to string.
512
- --limit <n> Interactive row limit. Server caps the maximum.
539
+ --param <value> Bind one positional parameter. Repeat for multiple params. Values parse as numbers or null; other values are passed as strings.
540
+ --limit <n> Interactive row limit. Server caps the maximum at 50.
541
+ --offset <n> Skip rows before returning results. Server caps the maximum offset.
513
542
 
514
543
  Examples:
515
- tender app db query artifact_123 --sql "select * from votes limit 20" --json
516
- tender app db query artifact_123 --sql "select * from votes where choice = ?" --param coffee --json
517
- tender app db query artifact_123 --file ./debug.sql --limit 50 --json
518
- tender app db query artifact_123 --file - --json < debug.sql`;
544
+ tender app db query artifact_123 --env published --sql "select * from votes limit 20" --json
545
+ tender app db query artifact_123 --env published --sql "select * from votes where choice = ?" --param coffee --json
546
+ tender app db query artifact_123 --env published --file ./debug.sql --limit 50 --offset 100 --json
547
+ tender app db query artifact_123 --env published --file - --json < debug.sql`;
519
548
  }
520
549
  function artifactsListHelp() {
521
550
  return `Usage: tender app list [--base-url <url>] [--token <token>] [--profile <name>] [--json]
@@ -845,6 +874,7 @@ function tenderCliCapabilities() {
845
874
  discovery: {
846
875
  primary: "npm exec --yes @tenderprompt/cli@latest -- capabilities --json",
847
876
  layeredHelp: [
877
+ "tender --version",
848
878
  "tender --help",
849
879
  "tender auth --help",
850
880
  "tender app --help",
@@ -921,10 +951,10 @@ function tenderCliCapabilities() {
921
951
  commands: [
922
952
  "tender auth login --device --profile db-read --artifact <artifact-id> --ttl 7d --save-profile db --json",
923
953
  "tender app db capabilities <artifact-id> --profile db --json",
924
- "tender app db tables <artifact-id> --profile db --json",
925
- "tender app db schema <artifact-id> --table <table-name> --profile db --json",
926
- "tender app db query <artifact-id> --sql \"select * from <table-name> limit 20\" --profile db --json",
927
- "tender app db query <artifact-id> --file ./debug.sql --profile db --json",
954
+ "tender app db tables <artifact-id> --env published --profile db --json",
955
+ "tender app db schema <artifact-id> --env published --table <table-name> --profile db --json",
956
+ "tender app db query <artifact-id> --env published --sql \"select * from <table-name> limit 20\" --profile db --json",
957
+ "tender app db query <artifact-id> --env published --file ./debug.sql --profile db --json",
928
958
  ],
929
959
  notes: [
930
960
  "The CLI only accepts artifact ids. It never accepts tenant ids, runner names, provider account ids, or runtime storage identifiers.",
@@ -947,6 +977,18 @@ function tenderCliCapabilities() {
947
977
  },
948
978
  ],
949
979
  commands: [
980
+ {
981
+ command: "tender --version",
982
+ purpose: "Print the installed Tender CLI package version.",
983
+ requiresAuth: false,
984
+ writes: false,
985
+ },
986
+ {
987
+ command: "tender version --json",
988
+ purpose: "Print the installed Tender CLI package version in a stable JSON shape.",
989
+ requiresAuth: false,
990
+ writes: false,
991
+ },
950
992
  {
951
993
  command: "tender capabilities --json",
952
994
  purpose: "Discover this CLI surface in a machine-readable shape.",
@@ -1032,19 +1074,19 @@ function tenderCliCapabilities() {
1032
1074
  writes: false,
1033
1075
  },
1034
1076
  {
1035
- command: "tender app db tables <artifact-id> --json",
1077
+ command: "tender app db tables <artifact-id> --env published --json",
1036
1078
  purpose: "List app database tables and views for one artifact.",
1037
1079
  requiresAuth: true,
1038
1080
  writes: false,
1039
1081
  },
1040
1082
  {
1041
- command: "tender app db schema <artifact-id> --table <table-name> --json",
1083
+ command: "tender app db schema <artifact-id> --env published --table <table-name> --json",
1042
1084
  purpose: "Inspect table, index, and trigger schema for one app database table.",
1043
1085
  requiresAuth: true,
1044
1086
  writes: false,
1045
1087
  },
1046
1088
  {
1047
- command: "tender app db query <artifact-id> --sql \"select * from <table-name> limit 20\" --json",
1089
+ command: "tender app db query <artifact-id> --env published --sql \"select * from <table-name> limit 20\" --json",
1048
1090
  purpose: "Run one bounded read-only app database query.",
1049
1091
  requiresAuth: true,
1050
1092
  writes: false,
@@ -1078,6 +1120,19 @@ function runCapabilities(command, io) {
1078
1120
  }
1079
1121
  return 0;
1080
1122
  }
1123
+ function runVersion(command, io) {
1124
+ if (command.json) {
1125
+ io.stdout(JSON.stringify({
1126
+ ok: true,
1127
+ name: CLI_PACKAGE_NAME,
1128
+ version: CLI_PACKAGE_VERSION,
1129
+ }, null, 2));
1130
+ }
1131
+ else {
1132
+ io.stdout(CLI_PACKAGE_VERSION);
1133
+ }
1134
+ return 0;
1135
+ }
1081
1136
  function normalizeBaseUrl(value) {
1082
1137
  return (value ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
1083
1138
  }
@@ -2402,6 +2457,13 @@ function parsePositiveIntegerFlag(value, flag) {
2402
2457
  }
2403
2458
  return parsed;
2404
2459
  }
2460
+ function parseNonNegativeIntegerFlag(value, flag) {
2461
+ const parsed = Number(value);
2462
+ if (!Number.isInteger(parsed) || parsed < 0) {
2463
+ throw new TenderCliUsageError(`${flag} requires a non-negative integer.`);
2464
+ }
2465
+ return parsed;
2466
+ }
2405
2467
  function parseAuthLoginArgs(args) {
2406
2468
  if (args.includes("--help") || args.includes("-h")) {
2407
2469
  return { command: "help", topic: "auth login" };
@@ -2575,6 +2637,20 @@ function parseCapabilitiesArgs(args) {
2575
2637
  }
2576
2638
  return { command: "capabilities", json };
2577
2639
  }
2640
+ function parseVersionArgs(args) {
2641
+ if (args.includes("--help") || args.includes("-h")) {
2642
+ return { command: "help", topic: "version" };
2643
+ }
2644
+ let json = false;
2645
+ for (const arg of args) {
2646
+ if (arg === "--json") {
2647
+ json = true;
2648
+ continue;
2649
+ }
2650
+ throw new TenderCliUsageError(`Unknown version option: ${arg}`);
2651
+ }
2652
+ return { command: "version", json };
2653
+ }
2578
2654
  function parseBindingId(value) {
2579
2655
  if (!value || value.startsWith("-")) {
2580
2656
  throw new TenderCliUsageError("--id requires a binding id.");
@@ -3457,10 +3533,23 @@ function parseDbCommonArgs(args, startIndex) {
3457
3533
  return { baseUrl, flagToken, profileName, json, remaining };
3458
3534
  }
3459
3535
  function parseDbEnvironment(value) {
3460
- if (value === undefined || value === "preview") {
3536
+ if (!value || value.startsWith("--")) {
3537
+ throw new TenderCliUsageError("--env requires a value. Use preview or published.");
3538
+ }
3539
+ if (value === "preview") {
3461
3540
  return "preview";
3462
3541
  }
3463
- throw new TenderCliUsageError("--env must be preview.");
3542
+ if (value === "published" ||
3543
+ value === "live" ||
3544
+ value === "production" ||
3545
+ value === "prod") {
3546
+ return "published";
3547
+ }
3548
+ throw new TenderCliUsageError("--env must be preview or published.");
3549
+ }
3550
+ function parseFlagAssignment(arg, flag) {
3551
+ const prefix = `${flag}=`;
3552
+ return arg.startsWith(prefix) ? arg.slice(prefix.length) : null;
3464
3553
  }
3465
3554
  function parseDbFlagValue(value, flag) {
3466
3555
  if (!value || value.startsWith("--")) {
@@ -3468,6 +3557,12 @@ function parseDbFlagValue(value, flag) {
3468
3557
  }
3469
3558
  return value;
3470
3559
  }
3560
+ function parseDbSqlFlagValue(value) {
3561
+ if (value === undefined || value.length === 0) {
3562
+ throw new TenderCliUsageError("--sql requires a value.");
3563
+ }
3564
+ return value;
3565
+ }
3471
3566
  function parseDbFilePath(value) {
3472
3567
  if (!value || (value !== "-" && value.startsWith("-"))) {
3473
3568
  throw new TenderCliUsageError("--file requires a SQL file path or - for stdin.");
@@ -3509,12 +3604,17 @@ function parseDbTablesArgs(args) {
3509
3604
  }
3510
3605
  const artifactId = args[0];
3511
3606
  if (!artifactId || artifactId.startsWith("-")) {
3512
- throw new TenderCliUsageError("app id is required. Example: tender app db tables <app-id> --json");
3607
+ throw new TenderCliUsageError("app id is required. Example: tender app db tables <app-id> --env published --json");
3513
3608
  }
3514
3609
  const parsed = parseDbCommonArgs(args, 1);
3515
- let environment = "preview";
3610
+ let environment = null;
3516
3611
  for (let index = 0; index < parsed.remaining.length; index += 1) {
3517
3612
  const arg = parsed.remaining[index];
3613
+ const envAssignment = parseFlagAssignment(arg, "--env");
3614
+ if (envAssignment !== null) {
3615
+ environment = parseDbEnvironment(envAssignment);
3616
+ continue;
3617
+ }
3518
3618
  if (arg === "--env") {
3519
3619
  environment = parseDbEnvironment(parsed.remaining[index + 1]);
3520
3620
  index += 1;
@@ -3522,6 +3622,9 @@ function parseDbTablesArgs(args) {
3522
3622
  }
3523
3623
  throw new TenderCliUsageError(`Unknown app db tables option: ${arg}`);
3524
3624
  }
3625
+ if (!environment) {
3626
+ throw new TenderCliUsageError("--env is required. Example: tender app db tables artifact_123 --env published --json");
3627
+ }
3525
3628
  return { command: "app db tables", artifactId, environment, ...parsed };
3526
3629
  }
3527
3630
  function parseDbSchemaArgs(args) {
@@ -3530,13 +3633,23 @@ function parseDbSchemaArgs(args) {
3530
3633
  }
3531
3634
  const artifactId = args[0];
3532
3635
  if (!artifactId || artifactId.startsWith("-")) {
3533
- throw new TenderCliUsageError("app id is required. Example: tender app db schema <app-id> --table votes --json");
3636
+ throw new TenderCliUsageError("app id is required. Example: tender app db schema <app-id> --env published --table votes --json");
3534
3637
  }
3535
3638
  const parsed = parseDbCommonArgs(args, 1);
3536
- let environment = "preview";
3639
+ let environment = null;
3537
3640
  let tableName = null;
3538
3641
  for (let index = 0; index < parsed.remaining.length; index += 1) {
3539
3642
  const arg = parsed.remaining[index];
3643
+ const envAssignment = parseFlagAssignment(arg, "--env");
3644
+ if (envAssignment !== null) {
3645
+ environment = parseDbEnvironment(envAssignment);
3646
+ continue;
3647
+ }
3648
+ const tableAssignment = parseFlagAssignment(arg, "--table");
3649
+ if (tableAssignment !== null) {
3650
+ tableName = parseRequiredFlagValue(tableAssignment, "--table");
3651
+ continue;
3652
+ }
3540
3653
  if (arg === "--env") {
3541
3654
  environment = parseDbEnvironment(parsed.remaining[index + 1]);
3542
3655
  index += 1;
@@ -3549,8 +3662,11 @@ function parseDbSchemaArgs(args) {
3549
3662
  }
3550
3663
  throw new TenderCliUsageError(`Unknown app db schema option: ${arg}`);
3551
3664
  }
3665
+ if (!environment) {
3666
+ throw new TenderCliUsageError("--env is required. Example: tender app db schema artifact_123 --env published --table votes --json");
3667
+ }
3552
3668
  if (!tableName) {
3553
- throw new TenderCliUsageError("--table is required. Example: tender app db schema artifact_123 --table votes --json");
3669
+ throw new TenderCliUsageError("--table is required. Example: tender app db schema artifact_123 --env published --table votes --json");
3554
3670
  }
3555
3671
  return {
3556
3672
  command: "app db schema",
@@ -3566,15 +3682,58 @@ function parseDbQueryArgs(args) {
3566
3682
  }
3567
3683
  const artifactId = args[0];
3568
3684
  if (!artifactId || artifactId.startsWith("-")) {
3569
- throw new TenderCliUsageError("app id is required. Example: tender app db query <app-id> --sql \"select * from votes limit 20\" --json");
3685
+ throw new TenderCliUsageError("app id is required. Example: tender app db query <app-id> --env published --sql \"select * from votes limit 20\" --json");
3570
3686
  }
3571
3687
  const parsed = parseDbCommonArgs(args, 1);
3572
- let environment = "preview";
3688
+ let environment = null;
3573
3689
  let sqlSource = null;
3574
3690
  const params = [];
3575
3691
  let limit = null;
3692
+ let offset = 0;
3576
3693
  for (let index = 0; index < parsed.remaining.length; index += 1) {
3577
3694
  const arg = parsed.remaining[index];
3695
+ const envAssignment = parseFlagAssignment(arg, "--env");
3696
+ if (envAssignment !== null) {
3697
+ environment = parseDbEnvironment(envAssignment);
3698
+ continue;
3699
+ }
3700
+ const sqlAssignment = parseFlagAssignment(arg, "--sql");
3701
+ if (sqlAssignment !== null) {
3702
+ if (sqlSource) {
3703
+ throw new TenderCliUsageError("--sql and --file cannot be repeated or combined.");
3704
+ }
3705
+ sqlSource = {
3706
+ kind: "inline",
3707
+ value: parseDbSqlFlagValue(sqlAssignment),
3708
+ };
3709
+ continue;
3710
+ }
3711
+ const fileAssignment = parseFlagAssignment(arg, "--file");
3712
+ if (fileAssignment !== null) {
3713
+ if (sqlSource) {
3714
+ throw new TenderCliUsageError("--sql and --file cannot be repeated or combined.");
3715
+ }
3716
+ sqlSource = {
3717
+ kind: "file",
3718
+ value: parseDbFilePath(fileAssignment),
3719
+ };
3720
+ continue;
3721
+ }
3722
+ const paramAssignment = parseFlagAssignment(arg, "--param");
3723
+ if (paramAssignment !== null) {
3724
+ params.push(parseDbParameter(paramAssignment));
3725
+ continue;
3726
+ }
3727
+ const limitAssignment = parseFlagAssignment(arg, "--limit");
3728
+ if (limitAssignment !== null) {
3729
+ limit = parsePositiveIntegerFlag(limitAssignment, "--limit");
3730
+ continue;
3731
+ }
3732
+ const offsetAssignment = parseFlagAssignment(arg, "--offset");
3733
+ if (offsetAssignment !== null) {
3734
+ offset = parseNonNegativeIntegerFlag(offsetAssignment, "--offset");
3735
+ continue;
3736
+ }
3578
3737
  if (arg === "--env") {
3579
3738
  environment = parseDbEnvironment(parsed.remaining[index + 1]);
3580
3739
  index += 1;
@@ -3586,7 +3745,7 @@ function parseDbQueryArgs(args) {
3586
3745
  }
3587
3746
  sqlSource = {
3588
3747
  kind: "inline",
3589
- value: parseDbFlagValue(parsed.remaining[index + 1], "--sql"),
3748
+ value: parseDbSqlFlagValue(parsed.remaining[index + 1]),
3590
3749
  };
3591
3750
  index += 1;
3592
3751
  continue;
@@ -3612,10 +3771,18 @@ function parseDbQueryArgs(args) {
3612
3771
  index += 1;
3613
3772
  continue;
3614
3773
  }
3774
+ if (arg === "--offset") {
3775
+ offset = parseNonNegativeIntegerFlag(parsed.remaining[index + 1], "--offset");
3776
+ index += 1;
3777
+ continue;
3778
+ }
3615
3779
  throw new TenderCliUsageError(`Unknown app db query option: ${arg}`);
3616
3780
  }
3781
+ if (!environment) {
3782
+ throw new TenderCliUsageError("--env is required. Example: tender app db query artifact_123 --env published --sql \"select * from votes limit 20\" --json");
3783
+ }
3617
3784
  if (!sqlSource) {
3618
- throw new TenderCliUsageError("--sql or --file is required. Example: tender app db query artifact_123 --sql \"select * from votes limit 20\" --json");
3785
+ throw new TenderCliUsageError("--sql or --file is required. Example: tender app db query artifact_123 --env published --sql \"select * from votes limit 20\" --json");
3619
3786
  }
3620
3787
  if (params.length > DB_MAX_BOUND_PARAMETERS) {
3621
3788
  throw new TenderCliUsageError(`App database queries are limited to ${DB_MAX_BOUND_PARAMETERS} bound parameters.`);
@@ -3627,6 +3794,7 @@ function parseDbQueryArgs(args) {
3627
3794
  sqlSource,
3628
3795
  params,
3629
3796
  limit,
3797
+ offset,
3630
3798
  ...parsed,
3631
3799
  };
3632
3800
  }
@@ -3634,6 +3802,12 @@ function parseTenderArgs(args) {
3634
3802
  if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
3635
3803
  return { command: "help", topic: "root" };
3636
3804
  }
3805
+ if (args[0] === "--version" || args[0] === "-v") {
3806
+ return parseVersionArgs(args.slice(1));
3807
+ }
3808
+ if (args[0] === "version") {
3809
+ return parseVersionArgs(args.slice(1));
3810
+ }
3637
3811
  if (args[0] === "capabilities") {
3638
3812
  return parseCapabilitiesArgs(args.slice(1));
3639
3813
  }
@@ -4192,12 +4366,16 @@ async function requestAnalyticsApi(input) {
4192
4366
  const message = (typeof payload?.message === "string" && payload.message) ||
4193
4367
  (typeof payload?.reasonCode === "string" && payload.reasonCode) ||
4194
4368
  `Analytics request failed with HTTP ${response.status}.`;
4369
+ const example = analyticsCorrectedExample(input.artifactId, input.analyticsPath);
4195
4370
  throw new TenderCliAnalyticsApiError([
4196
4371
  message,
4197
4372
  `Scope: artifact ${input.artifactId}, analytics/${input.analyticsPath || "summary"}.`,
4198
4373
  `Reason: ${reasonCode}.`,
4199
- `Example: ${analyticsCorrectedExample(input.artifactId, input.analyticsPath)}.`,
4200
- ].join("\n"), reasonCode);
4374
+ `Example: ${example}.`,
4375
+ ].join("\n"), reasonCode, {
4376
+ artifactId: input.artifactId,
4377
+ surface: `analytics/${input.analyticsPath || "summary"}`,
4378
+ }, example);
4201
4379
  }
4202
4380
  return payload;
4203
4381
  }
@@ -4229,12 +4407,22 @@ async function requestDbApi(input) {
4229
4407
  const message = (typeof payload?.message === "string" && payload.message) ||
4230
4408
  (typeof payload?.reasonCode === "string" && payload.reasonCode) ||
4231
4409
  `App database request failed with HTTP ${response.status}.`;
4410
+ const nextAction = typeof payload?.nextAction === "string" && payload.nextAction
4411
+ ? payload.nextAction
4412
+ : null;
4413
+ const example = dbCorrectedExample(input.artifactId, input.dbPath);
4232
4414
  throw new TenderCliDbApiError([
4233
4415
  message,
4234
4416
  `Scope: artifact ${input.artifactId}, db/${input.dbPath}.`,
4235
4417
  `Reason: ${reasonCode}.`,
4236
- `Example: ${dbCorrectedExample(input.artifactId, input.dbPath)}.`,
4237
- ].join("\n"), reasonCode);
4418
+ `Example: ${example}.`,
4419
+ ].join("\n"), reasonCode, {
4420
+ artifactId: input.artifactId,
4421
+ surface: `db/${input.dbPath}`,
4422
+ ...(input.scopeEnvironment ?? input.environment
4423
+ ? { environment: input.scopeEnvironment ?? input.environment }
4424
+ : {}),
4425
+ }, example, nextAction);
4238
4426
  }
4239
4427
  return payload;
4240
4428
  }
@@ -4243,12 +4431,12 @@ function dbCorrectedExample(artifactId, dbPath) {
4243
4431
  return `tender app db capabilities ${artifactId} --json`;
4244
4432
  }
4245
4433
  if (dbPath === "tables") {
4246
- return `tender app db tables ${artifactId} --json`;
4434
+ return `tender app db tables ${artifactId} --env published --json`;
4247
4435
  }
4248
4436
  if (dbPath === "schema") {
4249
- return `tender app db schema ${artifactId} --table <table-name> --json`;
4437
+ return `tender app db schema ${artifactId} --env published --table <table-name> --json`;
4250
4438
  }
4251
- return `tender app db query ${artifactId} --sql "select * from <table-name> limit 20" --json`;
4439
+ return `tender app db query ${artifactId} --env published --sql "select * from <table-name> limit 20" --json`;
4252
4440
  }
4253
4441
  function analyticsCorrectedExample(artifactId, analyticsPath) {
4254
4442
  if (analyticsPath === "capabilities") {
@@ -4497,7 +4685,7 @@ async function requestAnalyticsQueryWithPropertyGuidance(input) {
4497
4685
  spec: input.spec,
4498
4686
  });
4499
4687
  if (guidance) {
4500
- throw new TenderCliAnalyticsApiError(`${error.message}\n\n${guidance}`, error.reasonCode);
4688
+ throw new TenderCliAnalyticsApiError(`${error.message}\n\n${guidance}`, error.reasonCode, error.scope, error.example);
4501
4689
  }
4502
4690
  }
4503
4691
  throw error;
@@ -6641,6 +6829,7 @@ async function runDbQuery(command, io, runtime) {
6641
6829
  sql,
6642
6830
  params: command.params,
6643
6831
  ...(command.limit === null ? {} : { limit: command.limit }),
6832
+ offset: command.offset,
6644
6833
  };
6645
6834
  const payload = await requestDbApi({
6646
6835
  artifactId: command.artifactId,
@@ -6649,6 +6838,7 @@ async function runDbQuery(command, io, runtime) {
6649
6838
  baseUrl: credentials.baseUrl,
6650
6839
  token: credentials.token,
6651
6840
  fetcher: runtime.fetcher ?? fetch,
6841
+ scopeEnvironment: command.environment,
6652
6842
  body,
6653
6843
  });
6654
6844
  const rows = Array.isArray(payload.rows) ? payload.rows : [];
@@ -6656,7 +6846,12 @@ async function runDbQuery(command, io, runtime) {
6656
6846
  `app_id: ${command.artifactId}`,
6657
6847
  `environment: ${command.environment}`,
6658
6848
  `rows: ${rows.length}`,
6849
+ `offset: ${String(payload.offset ?? command.offset)}`,
6850
+ `applied_limit: ${String(payload.appliedLimit ?? command.limit ?? "")}`,
6659
6851
  `truncated: ${String(payload.truncated ?? false)}`,
6852
+ ...(typeof payload.nextOffset === "number"
6853
+ ? [`next_offset: ${payload.nextOffset}`]
6854
+ : []),
6660
6855
  ]);
6661
6856
  return 0;
6662
6857
  }
@@ -7063,16 +7258,118 @@ async function runAnalyticsChartsDelete(command, io, runtime) {
7063
7258
  ]);
7064
7259
  return 0;
7065
7260
  }
7261
+ function cliErrorMessage(error) {
7262
+ if (error instanceof Error) {
7263
+ return error.message.split("\n")[0] ?? error.message;
7264
+ }
7265
+ return String(error);
7266
+ }
7267
+ function cliErrorCode(error) {
7268
+ if (error instanceof TenderCliDbApiError || error instanceof TenderCliAnalyticsApiError) {
7269
+ return error.reasonCode;
7270
+ }
7271
+ if (error && typeof error === "object" && "code" in error) {
7272
+ const code = error.code;
7273
+ if (code === "ENOENT") {
7274
+ return "file_not_found";
7275
+ }
7276
+ if (typeof code === "string" && code.length > 0) {
7277
+ return code.toLowerCase();
7278
+ }
7279
+ }
7280
+ return error instanceof TenderCliUsageError ? "cli_usage_error" : "cli_error";
7281
+ }
7282
+ function commandNameForError(args, command) {
7283
+ if (command) {
7284
+ return command.command === "help" ? `help ${command.topic}` : command.command;
7285
+ }
7286
+ if (args[0] === "app" && args[1] === "db" && args[2]) {
7287
+ return `app db ${args[2]}`;
7288
+ }
7289
+ if (args[0] === "app" && args[1]) {
7290
+ return `app ${args[1]}`;
7291
+ }
7292
+ return args[0] ?? "unknown";
7293
+ }
7294
+ function dbPathFromCommandName(commandName) {
7295
+ if (commandName === "app db capabilities") {
7296
+ return "capabilities";
7297
+ }
7298
+ if (commandName === "app db tables") {
7299
+ return "tables";
7300
+ }
7301
+ if (commandName === "app db schema") {
7302
+ return "schema";
7303
+ }
7304
+ if (commandName === "app db query") {
7305
+ return "query";
7306
+ }
7307
+ return null;
7308
+ }
7309
+ function artifactIdForError(args, command) {
7310
+ if (command && "artifactId" in command) {
7311
+ return command.artifactId;
7312
+ }
7313
+ if (args[0] === "app" && args[1] === "db" && args[3] && !args[3].startsWith("-")) {
7314
+ return args[3];
7315
+ }
7316
+ return null;
7317
+ }
7318
+ function scopeForError(error, args, command) {
7319
+ if (error instanceof TenderCliDbApiError || error instanceof TenderCliAnalyticsApiError) {
7320
+ return error.scope;
7321
+ }
7322
+ const commandName = commandNameForError(args, command);
7323
+ const dbPath = dbPathFromCommandName(commandName);
7324
+ const artifactId = artifactIdForError(args, command);
7325
+ if (dbPath && artifactId) {
7326
+ return {
7327
+ artifactId,
7328
+ surface: `db/${dbPath}`,
7329
+ };
7330
+ }
7331
+ return null;
7332
+ }
7333
+ function exampleForError(error, args, command) {
7334
+ if (error instanceof TenderCliDbApiError || error instanceof TenderCliAnalyticsApiError) {
7335
+ return error.example;
7336
+ }
7337
+ const commandName = commandNameForError(args, command);
7338
+ const dbPath = dbPathFromCommandName(commandName);
7339
+ const artifactId = artifactIdForError(args, command) ?? "artifact_123";
7340
+ return dbPath ? dbCorrectedExample(artifactId, dbPath) : null;
7341
+ }
7342
+ function nextActionForError(error) {
7343
+ return error instanceof TenderCliDbApiError ? error.nextAction : null;
7344
+ }
7345
+ function cliErrorPayload(error, args, command) {
7346
+ const scope = scopeForError(error, args, command);
7347
+ const example = exampleForError(error, args, command);
7348
+ const nextAction = nextActionForError(error);
7349
+ return {
7350
+ ok: false,
7351
+ command: commandNameForError(args, command),
7352
+ reasonCode: cliErrorCode(error),
7353
+ message: cliErrorMessage(error),
7354
+ ...(scope ? { scope } : {}),
7355
+ ...(nextAction ? { nextAction } : {}),
7356
+ ...(example ? { example } : {}),
7357
+ };
7358
+ }
7066
7359
  export async function runTenderCli(args, io = {
7067
7360
  stdout: (message) => console.log(message),
7068
7361
  stderr: (message) => console.error(message),
7069
7362
  }, runtime = {}) {
7363
+ let command = null;
7070
7364
  try {
7071
- const command = parseTenderArgs(args);
7365
+ command = parseTenderArgs(args);
7072
7366
  if (command.command === "help") {
7073
7367
  if (command.topic === "capabilities") {
7074
7368
  io.stdout(capabilitiesHelp());
7075
7369
  }
7370
+ else if (command.topic === "version") {
7371
+ io.stdout(versionHelp());
7372
+ }
7076
7373
  else if (command.topic === "auth login") {
7077
7374
  io.stdout(authLoginHelp());
7078
7375
  }
@@ -7219,6 +7516,9 @@ export async function runTenderCli(args, io = {
7219
7516
  if (command.command === "capabilities") {
7220
7517
  return runCapabilities(command, io);
7221
7518
  }
7519
+ if (command.command === "version") {
7520
+ return runVersion(command, io);
7521
+ }
7222
7522
  if (command.command === "auth login") {
7223
7523
  return await runAuthLogin(command, io, runtime);
7224
7524
  }
@@ -7342,6 +7642,10 @@ export async function runTenderCli(args, io = {
7342
7642
  return await runDoctor(command, io);
7343
7643
  }
7344
7644
  catch (error) {
7645
+ if (args.includes("--json") && args[0] === "app" && args[1] === "db") {
7646
+ io.stdout(JSON.stringify(cliErrorPayload(error, args, command), null, 2));
7647
+ return error instanceof TenderCliUsageError ? 2 : 1;
7648
+ }
7345
7649
  io.stderr(error instanceof Error ? error.message : String(error));
7346
7650
  io.stderr("Run `tender --help` for examples.");
7347
7651
  return error instanceof TenderCliUsageError ? 2 : 1;
@@ -7349,9 +7653,9 @@ export async function runTenderCli(args, io = {
7349
7653
  }
7350
7654
  const currentFile = fileURLToPath(import.meta.url);
7351
7655
  function hasFlagValue(args, flag, value) {
7352
- return args.some((arg, index) => arg === flag && args[index + 1] === value);
7656
+ return args.some((arg, index) => (arg === flag && args[index + 1] === value) || arg === `${flag}=${value}`);
7353
7657
  }
7354
- function shouldReadEntrypointStdin(args) {
7658
+ export function shouldReadEntrypointStdin(args) {
7355
7659
  return (args[0] === "app" &&
7356
7660
  ((args[1] === "git" && args[2] === "credential") ||
7357
7661
  (args[1] === "analytics" && hasFlagValue(args, "--spec", "-")) ||
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tenderprompt/cli",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "tender": "bin/tender.js"