@letterstory/cli 0.176.62 → 0.176.64
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 +8 -2
- package/lib/commands/domains.mjs +82 -1
- package/lib/commands/replicate.mjs +6 -2
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -85,7 +85,7 @@ import {
|
|
|
85
85
|
import { fetchParkedSections, withoutParked } from "./parked.mjs";
|
|
86
86
|
|
|
87
87
|
// Matches cli/package.json; publish-cli.yml rewrites both to the published version.
|
|
88
|
-
export const VERSION = "0.176.
|
|
88
|
+
export const VERSION = "0.176.64";
|
|
89
89
|
|
|
90
90
|
/** The exit code for a gate refusal (CARD_REQUIRED, PAYMENT_FAILED, OUT_OF_TOKENS): 1 is a plain error. */
|
|
91
91
|
export const EXIT_PAYMENT_REQUIRED = 3;
|
|
@@ -1902,6 +1902,12 @@ Domains (the domains you hold, and free ones that match what people search):
|
|
|
1902
1902
|
Free .com/.org domains on what people search in your
|
|
1903
1903
|
space (brainstormed, or the phrases given), ranked
|
|
1904
1904
|
domains buy <domain> --dry-run | --yes The price and whose account pays; --yes buys it
|
|
1905
|
+
domains plan <domain> [--phrase "<search>"] [--searches <n>]
|
|
1906
|
+
The most strategic site for the domain, every version
|
|
1907
|
+
scored, and whether it can launch; spends nothing
|
|
1908
|
+
domains launch <domain> [--phrase …] [--searches …] [--words "<plan words>"] --yes
|
|
1909
|
+
Build and Launch Site: buys the domain if needed, builds
|
|
1910
|
+
the plan and puts it live
|
|
1905
1911
|
|
|
1906
1912
|
Vary (A/B tests of a page's words and images on your own site):
|
|
1907
1913
|
vary [list] [--status running] [--site <id>] Your variationals and how many visitors each has seen
|
|
@@ -1962,7 +1968,7 @@ Replicate (the Replicator: a website rebuilt as a native Next.js repo, page for
|
|
|
1962
1968
|
replicate [list] Your replications: stage, how closely each matches
|
|
1963
1969
|
replicate show <id> [--failing] Pages with their routes and pixel match; collections; events
|
|
1964
1970
|
replicate attach <id> <repo-name> [--repo-owner <o>] The repo to commit it into (yours; Letter never creates it)
|
|
1965
|
-
replicate advance <id> [--retry]
|
|
1971
|
+
replicate advance <id> [--retry] [--again] Move it on now; --retry once blocked_on is done; --again replicates from the start
|
|
1966
1972
|
replicate archive <id> Stop it; the project stays in storage and in the repo
|
|
1967
1973
|
|
|
1968
1974
|
Evals (how the pipeline is doing on your posts, and the regression cases captured from them):
|
package/lib/commands/domains.mjs
CHANGED
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
// letterstory domains scan "nano banana pro" ["ai headshots" …] [--tld com,org,ai] [--expand none|searches|space]
|
|
8
8
|
// letterstory domains buy nanobananaprofree.com --dry-run the price and whose account pays; buys nothing
|
|
9
9
|
// letterstory domains buy nanobananaprofree.com --yes buys it (spends money; domains:write)
|
|
10
|
+
// letterstory domains plan bestaidatalabs.com [--phrase "best ai data labs"] [--searches 880]
|
|
11
|
+
// the most strategic site for it; spends nothing
|
|
12
|
+
// letterstory domains launch bestaidatalabs.com [--phrase …] [--searches …] --yes
|
|
13
|
+
// Build and Launch Site: buys it if needed, builds the
|
|
14
|
+
// plan and puts it live (spends money; domains:write)
|
|
10
15
|
|
|
11
16
|
import { CliError } from "../client.mjs";
|
|
12
17
|
import { flagCsv, requirePositional, printResult, argsFromFlags } from "./shared.mjs";
|
|
@@ -32,6 +37,19 @@ export const ARGS = {
|
|
|
32
37
|
domain: { value: (flags, positionals) => stripScheme(requirePositional(positionals, 0, "domain")) },
|
|
33
38
|
dry_run: { flag: "dry-run", type: "bool" },
|
|
34
39
|
},
|
|
40
|
+
// `domains plan <domain>` — the scan row's search and its monthly searches, when you have them.
|
|
41
|
+
plan_domain_site: {
|
|
42
|
+
domain: { value: (flags, positionals) => stripScheme(requirePositional(positionals, 0, "domain")) },
|
|
43
|
+
phrase: { flag: "phrase" },
|
|
44
|
+
monthly_searches: { flag: "searches", type: "num" },
|
|
45
|
+
},
|
|
46
|
+
launch_domain_site: {
|
|
47
|
+
domain: { value: (flags, positionals) => stripScheme(requirePositional(positionals, 0, "domain")) },
|
|
48
|
+
phrase: { flag: "phrase" },
|
|
49
|
+
monthly_searches: { flag: "searches", type: "num" },
|
|
50
|
+
// The words of a plan already read (`domains plan … --json`'s plan.words): that plan is built as it was.
|
|
51
|
+
words: { flag: "words" },
|
|
52
|
+
},
|
|
35
53
|
};
|
|
36
54
|
|
|
37
55
|
function stripScheme(value) {
|
|
@@ -61,8 +79,18 @@ export async function cmdDomains(ctx) {
|
|
|
61
79
|
return 1;
|
|
62
80
|
}
|
|
63
81
|
return domainsBuy(rest);
|
|
82
|
+
case "plan":
|
|
83
|
+
return domainsPlan(rest);
|
|
84
|
+
case "launch":
|
|
85
|
+
if (rest.flags.yes !== true) {
|
|
86
|
+
rest.io.error(
|
|
87
|
+
"Launching a site buys its domain (real money) and builds it. Run `domains plan` to see the site and the price, then launch with --yes."
|
|
88
|
+
);
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
return domainsLaunch(rest);
|
|
64
92
|
default:
|
|
65
|
-
throw new CliError(`Unknown domains subcommand: ${sub}. Try: list, scan, buy`);
|
|
93
|
+
throw new CliError(`Unknown domains subcommand: ${sub}. Try: list, scan, buy, plan, launch`);
|
|
66
94
|
}
|
|
67
95
|
}
|
|
68
96
|
|
|
@@ -117,6 +145,59 @@ async function domainsBuy(ctx) {
|
|
|
117
145
|
return 0;
|
|
118
146
|
}
|
|
119
147
|
|
|
148
|
+
/** A plan as the terminal reads it: the version and why, every score, the pages, and what the domain costs. */
|
|
149
|
+
function planLines(plan) {
|
|
150
|
+
return [
|
|
151
|
+
`${plan.domain}: ${plan.label} for "${plan.phrase}"${plan.monthly_searches != null ? ` (${plan.monthly_searches.toLocaleString("en-US")} searches/mo)` : ""}`,
|
|
152
|
+
` ${plan.promise}`,
|
|
153
|
+
` For: ${plan.audience}`,
|
|
154
|
+
` Why it wins: ${plan.angle}`,
|
|
155
|
+
` ${plan.company_role}`,
|
|
156
|
+
" Pages:",
|
|
157
|
+
...plan.pages.map((p) => ` ${p.path.padEnd(16)} ${p.title}: ${p.purpose}`),
|
|
158
|
+
" Scores:",
|
|
159
|
+
...plan.scores.map((s) =>
|
|
160
|
+
` ${String(s.score).padStart(3)} ${s.label.padEnd(18)} ${s.reasons.join("; ")}`.trimEnd()
|
|
161
|
+
),
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function domainsPlan(ctx) {
|
|
166
|
+
const { client, flags, io } = ctx;
|
|
167
|
+
const result = await client.callTool(
|
|
168
|
+
"plan_domain_site",
|
|
169
|
+
argsFromFlags(ARGS.plan_domain_site, flags, ctx.positionals)
|
|
170
|
+
);
|
|
171
|
+
printResult(io, flags, result, (r) => {
|
|
172
|
+
const s = r.domain_state;
|
|
173
|
+
const state = !s.launchable
|
|
174
|
+
? `Can't launch: ${s.reason}`
|
|
175
|
+
: s.outcome === "held"
|
|
176
|
+
? "The domain is yours: nothing to buy. Launch with: letterstory domains launch " +
|
|
177
|
+
r.plan.domain +
|
|
178
|
+
" --yes"
|
|
179
|
+
: `The domain is free: $${s.price_usd} for the first year, billed in tokens. Launch with: letterstory domains launch ${r.plan.domain} --yes`;
|
|
180
|
+
return [...planLines(r.plan), "", state].join("\n");
|
|
181
|
+
});
|
|
182
|
+
return 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function domainsLaunch(ctx) {
|
|
186
|
+
const { client, flags, io } = ctx;
|
|
187
|
+
const result = await client.callTool(
|
|
188
|
+
"launch_domain_site",
|
|
189
|
+
argsFromFlags(ARGS.launch_domain_site, flags, ctx.positionals)
|
|
190
|
+
);
|
|
191
|
+
printResult(io, flags, result, (r) =>
|
|
192
|
+
[
|
|
193
|
+
...(r.plan ? planLines(r.plan) : []),
|
|
194
|
+
"",
|
|
195
|
+
`Launching ${r.app.domain ?? r.app.name} (${r.app.stage}${r.app.stage_note ? `: ${r.app.stage_note}` : ""}). Follow it with: letterstory deploy show ${r.app.id}`,
|
|
196
|
+
].join("\n")
|
|
197
|
+
);
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
120
201
|
const STATUS_MARK = { available: "free", taken: "·", unknown: "?", unchecked: " " };
|
|
121
202
|
|
|
122
203
|
async function domainsScan(ctx) {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// letterstory replicate list
|
|
10
10
|
// letterstory replicate show <id> [--failing]
|
|
11
11
|
// letterstory replicate attach <id> <repo-name> [--repo-owner <account>]
|
|
12
|
-
// letterstory replicate advance <id> [--retry]
|
|
12
|
+
// letterstory replicate advance <id> [--retry] [--again]
|
|
13
13
|
// letterstory replicate archive <id>
|
|
14
14
|
|
|
15
15
|
import { CliError } from "../client.mjs";
|
|
@@ -35,7 +35,11 @@ export const ARGS = {
|
|
|
35
35
|
repo_name: { positional: 1, label: "repo-name", required: true },
|
|
36
36
|
repo_owner: "repo-owner",
|
|
37
37
|
},
|
|
38
|
-
advance_replication: {
|
|
38
|
+
advance_replication: {
|
|
39
|
+
replication_id: replicationId,
|
|
40
|
+
retry: { flag: "retry", type: "bool" },
|
|
41
|
+
again: { flag: "again", type: "bool" },
|
|
42
|
+
},
|
|
39
43
|
archive_replication: { replication_id: replicationId },
|
|
40
44
|
};
|
|
41
45
|
|