@letterstory/cli 0.121.2 → 0.121.3
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/lib/cli.mjs +7 -1
- package/lib/commands/repo.mjs +71 -1
- package/lib/documents.mjs +2 -0
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -60,7 +60,7 @@ import {
|
|
|
60
60
|
} from "./commands.mjs";
|
|
61
61
|
|
|
62
62
|
// Keep in sync with cli/package.json.
|
|
63
|
-
export const VERSION = "0.121.
|
|
63
|
+
export const VERSION = "0.121.3";
|
|
64
64
|
|
|
65
65
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
66
66
|
// can't accidentally swallow the id as --json's value. Every `type: "bool"` flag (and its
|
|
@@ -1261,6 +1261,12 @@ Repo changes (Claude Code edits your repo in a sandbox; the Letterstory GitHub A
|
|
|
1261
1261
|
repo set-site --repo <owner/name> --site <host> [--not] [--branch <name>|none]
|
|
1262
1262
|
Say which site a repo builds (--not: correct a wrong label)
|
|
1263
1263
|
repo reset-site --repo <owner/name> --site <host> Back to automatic detection
|
|
1264
|
+
repo secrets --repo <owner/name> Preview secrets: names and when set (never values)
|
|
1265
|
+
repo set-secret --repo <owner/name> --secret <KEY> --value <v> | --value-file <path|->
|
|
1266
|
+
Set an env var the preview site gets (use test credentials:
|
|
1267
|
+
the preview URL is public to anyone with the link)
|
|
1268
|
+
repo delete-secret --repo <owner/name> --secret <KEY>
|
|
1269
|
+
Delete one
|
|
1264
1270
|
repo status <run-id> Phase, spend, PR link and Claude's summary
|
|
1265
1271
|
repo activity <run-id> [--follow] What the sandbox is doing, line by line (--follow: until it stops)
|
|
1266
1272
|
repo list [--status running|succeeded|no_changes|failed|cancelled] [--limit n] [--audit <id>]
|
package/lib/commands/repo.mjs
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
// repo repos → list_github_app_repos repo branches → list_repo_branches
|
|
8
8
|
// repo prs → list_open_pull_requests (every open PR, whoever opened it)
|
|
9
9
|
// repo install-letterprove → letterprove_open_install_pr
|
|
10
|
+
// repo secrets → list_repo_preview_secrets repo set-secret → set_repo_preview_secret
|
|
11
|
+
// repo delete-secret → delete_repo_preview_secret
|
|
10
12
|
// repo suggestions → list_suggested_fixes
|
|
11
13
|
// repo settings → get_repo_change_settings, or set_repo_change_settings with --action / --branch
|
|
12
14
|
// repo slack-targets → list_slack_fix_targets repo slack-test → send_slack_test_message
|
|
@@ -25,6 +27,14 @@ import {
|
|
|
25
27
|
refuseUnknownFlags,
|
|
26
28
|
} from "./shared.mjs";
|
|
27
29
|
|
|
30
|
+
/** `--value <v>` or `--value-file <path|->`; a file's single trailing newline is dropped. */
|
|
31
|
+
function secretValue(flags) {
|
|
32
|
+
const inline = flagStr(flags.value);
|
|
33
|
+
const value = readTextInput(flags, "value", "value-file");
|
|
34
|
+
if (value === undefined) throw new CliError("Pass the value with --value <value> or --value-file <path|->.");
|
|
35
|
+
return inline === undefined ? value.replace(/\r?\n$/, "") : value;
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
/** `--repo owner/name` → the two schema keys; --owner/--name also work. */
|
|
29
39
|
function splitRepo(flags) {
|
|
30
40
|
const full = flagStr(flags.repo);
|
|
@@ -69,6 +79,21 @@ const ARGS = {
|
|
|
69
79
|
repo_name: { value: (flags) => splitRepo(flags).name },
|
|
70
80
|
site_host: { flag: "site", required: true },
|
|
71
81
|
},
|
|
82
|
+
list_repo_preview_secrets: {
|
|
83
|
+
repo_owner: { value: (flags) => splitRepo(flags).owner },
|
|
84
|
+
repo_name: { value: (flags) => splitRepo(flags).name },
|
|
85
|
+
},
|
|
86
|
+
set_repo_preview_secret: {
|
|
87
|
+
repo_owner: { value: (flags) => splitRepo(flags).owner },
|
|
88
|
+
repo_name: { value: (flags) => splitRepo(flags).name },
|
|
89
|
+
key: { flag: "secret", required: true },
|
|
90
|
+
value: { value: (flags) => secretValue(flags) },
|
|
91
|
+
},
|
|
92
|
+
delete_repo_preview_secret: {
|
|
93
|
+
repo_owner: { value: (flags) => splitRepo(flags).owner },
|
|
94
|
+
repo_name: { value: (flags) => splitRepo(flags).name },
|
|
95
|
+
key: { flag: "secret", required: true },
|
|
96
|
+
},
|
|
72
97
|
fix_audit_issue: {
|
|
73
98
|
// --issue <type>, repeatable or comma-separated: several go into one PR (issue_types).
|
|
74
99
|
issue_type: { value: (flags) => (issueList(flags).length === 1 ? issueList(flags)[0] : undefined) },
|
|
@@ -198,6 +223,12 @@ export async function cmdRepo(ctx) {
|
|
|
198
223
|
return repoSetSite(rest);
|
|
199
224
|
case "reset-site":
|
|
200
225
|
return repoResetSite(rest);
|
|
226
|
+
case "secrets":
|
|
227
|
+
return repoSecrets(rest);
|
|
228
|
+
case "set-secret":
|
|
229
|
+
return repoSetSecret(rest);
|
|
230
|
+
case "delete-secret":
|
|
231
|
+
return repoDeleteSecret(rest);
|
|
201
232
|
case "list":
|
|
202
233
|
return repoList(rest);
|
|
203
234
|
case "cancel":
|
|
@@ -220,7 +251,7 @@ export async function cmdRepo(ctx) {
|
|
|
220
251
|
return repoSlackTest(rest);
|
|
221
252
|
default:
|
|
222
253
|
throw new CliError(
|
|
223
|
-
`Unknown repo subcommand: ${sub ?? "(none)"}. Try: start, fix-audit, install-letterprove, sites, set-site, reset-site, status, list, publish, cancel, repos, branches, prs, settings, suggestions, slack-targets, slack-test`
|
|
254
|
+
`Unknown repo subcommand: ${sub ?? "(none)"}. Try: start, fix-audit, install-letterprove, sites, set-site, reset-site, secrets, set-secret, delete-secret, status, list, publish, cancel, repos, branches, prs, settings, suggestions, slack-targets, slack-test`
|
|
224
255
|
);
|
|
225
256
|
}
|
|
226
257
|
}
|
|
@@ -400,6 +431,45 @@ async function repoResetSite(ctx) {
|
|
|
400
431
|
return 0;
|
|
401
432
|
}
|
|
402
433
|
|
|
434
|
+
/** `repo secrets --repo owner/name`: the repo's preview secrets, names and when each was set (never values). */
|
|
435
|
+
async function repoSecrets(ctx) {
|
|
436
|
+
const { client, flags, io } = ctx;
|
|
437
|
+
refuseUnknownFlags(ctx, {}, ["repo", "owner", "name"]);
|
|
438
|
+
const result = await client.callTool(
|
|
439
|
+
"list_repo_preview_secrets",
|
|
440
|
+
argsFromFlags(ARGS.list_repo_preview_secrets, flags)
|
|
441
|
+
);
|
|
442
|
+
printResult(io, flags, result, (r) =>
|
|
443
|
+
r.secrets.length
|
|
444
|
+
? r.secrets.map((s) => `${s.key} (set ${s.updatedAt})`).join("\n")
|
|
445
|
+
: "No preview secrets for this repo."
|
|
446
|
+
);
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** `repo set-secret --repo owner/name --secret KEY --value v | --value-file path|-`: set or overwrite one. */
|
|
451
|
+
async function repoSetSecret(ctx) {
|
|
452
|
+
const { client, flags, io } = ctx;
|
|
453
|
+
refuseUnknownFlags(ctx, {}, ["repo", "owner", "name", "secret", "value", "value-file"]);
|
|
454
|
+
const result = await client.callTool("set_repo_preview_secret", argsFromFlags(ARGS.set_repo_preview_secret, flags));
|
|
455
|
+
ok(ctx, result.message);
|
|
456
|
+
printResult(io, flags, result.secret);
|
|
457
|
+
return 0;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/** `repo delete-secret --repo owner/name --secret KEY`: delete one. */
|
|
461
|
+
async function repoDeleteSecret(ctx) {
|
|
462
|
+
const { client, flags, io } = ctx;
|
|
463
|
+
refuseUnknownFlags(ctx, {}, ["repo", "owner", "name", "secret"]);
|
|
464
|
+
const result = await client.callTool(
|
|
465
|
+
"delete_repo_preview_secret",
|
|
466
|
+
argsFromFlags(ARGS.delete_repo_preview_secret, flags)
|
|
467
|
+
);
|
|
468
|
+
ok(ctx, result.message);
|
|
469
|
+
printResult(io, flags, result);
|
|
470
|
+
return 0;
|
|
471
|
+
}
|
|
472
|
+
|
|
403
473
|
async function repoStatus(ctx) {
|
|
404
474
|
const { client, flags, io } = ctx;
|
|
405
475
|
const result = await client.callTool(
|
package/lib/documents.mjs
CHANGED
|
@@ -83,6 +83,8 @@ export const JSON_ARG_FLAG_PAIRS = [
|
|
|
83
83
|
["csv", "csv-file"],
|
|
84
84
|
// A site proposal's tool input, parsed as JSON (site.mjs proposalInput).
|
|
85
85
|
["input", "input-file"],
|
|
86
|
+
// A preview secret's value, sent byte for byte (repo.mjs `repo set-secret`); never extracted.
|
|
87
|
+
["value", "value-file"],
|
|
86
88
|
// A Vercel API token read verbatim, so it stays out of shell history (outbound.mjs mail vercel-key set).
|
|
87
89
|
["token", "token-file"],
|
|
88
90
|
];
|