@letterstory/cli 0.2.2 → 0.3.0
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 +18 -1
- package/lib/commands/kernel.mjs +50 -0
- package/lib/commands/phantom-job.mjs +41 -0
- package/lib/commands/strategy.mjs +42 -1
- package/lib/commands.mjs +2 -0
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -25,10 +25,12 @@ import {
|
|
|
25
25
|
cmdStrategy,
|
|
26
26
|
cmdOnboarding,
|
|
27
27
|
cmdInsights,
|
|
28
|
+
cmdKernel,
|
|
29
|
+
cmdPhantomJob,
|
|
28
30
|
} from "./commands.mjs";
|
|
29
31
|
|
|
30
32
|
// Keep in sync with cli/package.json.
|
|
31
|
-
export const VERSION = "0.
|
|
33
|
+
export const VERSION = "0.3.0";
|
|
32
34
|
|
|
33
35
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
34
36
|
// can't accidentally swallow the id as --json's value.
|
|
@@ -170,9 +172,22 @@ Strategy & onboarding:
|
|
|
170
172
|
[--clear-topics] [--clear-stances] [--clear-avoid]
|
|
171
173
|
strategy competitors list | add <name> <domain>
|
|
172
174
|
strategy sitemap --collection <uuid> --url <sitemap_url> [--sub <url> …] [--pattern <glob>]
|
|
175
|
+
strategy topics list --collection <uuid> List the topic map's clusters/spokes
|
|
176
|
+
strategy topics set --collection <uuid> (--topic <topic-id> | --suggestion <suggestion-id>)
|
|
173
177
|
onboarding status Show the onboarding checklist
|
|
174
178
|
onboarding step [--current <step>] [--complete <step>] [--skip <step>] [--status <status>]
|
|
175
179
|
|
|
180
|
+
Writing kernels:
|
|
181
|
+
kernel list List available writing kernels
|
|
182
|
+
kernel run --article <uuid> --kernel <uuid> [--brief <text>|--brief-file <path>]
|
|
183
|
+
Submit a run; takes 15-20 min
|
|
184
|
+
kernel status <kernel-job-id> Check a run's status
|
|
185
|
+
|
|
186
|
+
Phantom orchestrator:
|
|
187
|
+
phantom-job create --topic <text> --collection <uuid> [--kernel <uuid>]
|
|
188
|
+
Topic -> draft -> publish -> rebuild
|
|
189
|
+
phantom-job status <job-id> Check a job's stage
|
|
190
|
+
|
|
176
191
|
Insights:
|
|
177
192
|
insights site [--period 14d|30d|90d] [--collection <uuid>]
|
|
178
193
|
insights post <article-id> [--period 14d|30d|90d]
|
|
@@ -228,6 +243,8 @@ const CLIENT_COMMANDS = {
|
|
|
228
243
|
strategy: cmdStrategy,
|
|
229
244
|
onboarding: cmdOnboarding,
|
|
230
245
|
insights: cmdInsights,
|
|
246
|
+
kernel: cmdKernel,
|
|
247
|
+
"phantom-job": cmdPhantomJob,
|
|
231
248
|
};
|
|
232
249
|
|
|
233
250
|
// LETTERSTORY_POLL_INTERVAL_MS / LETTERSTORY_MAX_POLLS let an operator (or an
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// `kernel` — list writing kernels, submit a run against an article, and check a
|
|
2
|
+
// run's status. A run can take 15-20 minutes, so unlike `deploy create` this does
|
|
3
|
+
// not block waiting for it: `run` returns as soon as the job is queued and `status`
|
|
4
|
+
// checks on demand, same shape as `flows run` / `flows status`.
|
|
5
|
+
|
|
6
|
+
import { CliError } from "../client.mjs";
|
|
7
|
+
import { flagStr, requireFlag, requirePositional, printResult, compact, ok, readBodyInput } from "./shared.mjs";
|
|
8
|
+
|
|
9
|
+
export async function cmdKernel(ctx) {
|
|
10
|
+
const sub = ctx.positionals[0];
|
|
11
|
+
const rest = { ...ctx, positionals: ctx.positionals.slice(1) };
|
|
12
|
+
switch (sub) {
|
|
13
|
+
case "list":
|
|
14
|
+
case "ls":
|
|
15
|
+
return kernelList(rest);
|
|
16
|
+
case "run":
|
|
17
|
+
return kernelRun(rest);
|
|
18
|
+
case "status":
|
|
19
|
+
return kernelStatus(rest);
|
|
20
|
+
default:
|
|
21
|
+
throw new CliError(`Unknown kernel subcommand: ${sub ?? "(none)"}. Try: list, run, status`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function kernelList(ctx) {
|
|
26
|
+
const { client, flags, io } = ctx;
|
|
27
|
+
const result = await client.callTool("list_kernels", {});
|
|
28
|
+
printResult(io, flags, result);
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function kernelRun(ctx) {
|
|
33
|
+
const { client, flags, io } = ctx;
|
|
34
|
+
const article = requireFlag(flags, "article");
|
|
35
|
+
const kernelId = requireFlag(flags, "kernel");
|
|
36
|
+
const brief = flagStr(flags.brief) ?? readBodyInput({ file: flagStr(flags["brief-file"]) });
|
|
37
|
+
const args = compact({ article_id: article, kernel_id: kernelId, brief });
|
|
38
|
+
const result = await client.callTool("submit_kernel_run", args);
|
|
39
|
+
ok(ctx, `Kernel run started. Check progress with: kernel status ${result?.kernel_job_id ?? "<kernel-job-id>"}`);
|
|
40
|
+
printResult(io, flags, result);
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function kernelStatus(ctx) {
|
|
45
|
+
const { client, positionals, flags, io } = ctx;
|
|
46
|
+
const jobId = requirePositional(positionals, 0, "kernel-job-id");
|
|
47
|
+
const result = await client.callTool("get_kernel_run_status", { kernel_job_id: jobId });
|
|
48
|
+
printResult(io, flags, result);
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// `phantom-job` — the topic-to-published-article orchestrator: kick off a job with
|
|
2
|
+
// a topic + collection, then check its stage (topic_set -> kernel_running ->
|
|
3
|
+
// publishing -> rebuilding -> done, or failed). The underlying kernel run alone can
|
|
4
|
+
// take 15-20 minutes, so `create` returns as soon as the job is queued rather than
|
|
5
|
+
// blocking — same shape as `flows run` / `flows status`, not `deploy create`'s
|
|
6
|
+
// wait-until-live loop.
|
|
7
|
+
|
|
8
|
+
import { CliError } from "../client.mjs";
|
|
9
|
+
import { flagStr, requireFlag, requirePositional, printResult, compact, ok } from "./shared.mjs";
|
|
10
|
+
|
|
11
|
+
export async function cmdPhantomJob(ctx) {
|
|
12
|
+
const sub = ctx.positionals[0];
|
|
13
|
+
const rest = { ...ctx, positionals: ctx.positionals.slice(1) };
|
|
14
|
+
switch (sub) {
|
|
15
|
+
case "create":
|
|
16
|
+
return phantomJobCreate(rest);
|
|
17
|
+
case "status":
|
|
18
|
+
return phantomJobStatus(rest);
|
|
19
|
+
default:
|
|
20
|
+
throw new CliError(`Unknown phantom-job subcommand: ${sub ?? "(none)"}. Try: create, status`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function phantomJobCreate(ctx) {
|
|
25
|
+
const { client, flags, io } = ctx;
|
|
26
|
+
const topic = requireFlag(flags, "topic");
|
|
27
|
+
const collection = requireFlag(flags, "collection");
|
|
28
|
+
const args = compact({ topic, collection_id: collection, kernel_id: flagStr(flags.kernel) });
|
|
29
|
+
const result = await client.callTool("create_phantom_from_topic", args);
|
|
30
|
+
ok(ctx, `Phantom job started. Check progress with: phantom-job status ${result?.job_id ?? "<job-id>"}`);
|
|
31
|
+
printResult(io, flags, result);
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function phantomJobStatus(ctx) {
|
|
36
|
+
const { client, positionals, flags, io } = ctx;
|
|
37
|
+
const jobId = requirePositional(positionals, 0, "job-id");
|
|
38
|
+
const result = await client.callTool("get_phantom_job_status", { job_id: jobId });
|
|
39
|
+
printResult(io, flags, result);
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
@@ -27,9 +27,11 @@ export async function cmdStrategy(ctx) {
|
|
|
27
27
|
return strategyCompetitors(rest);
|
|
28
28
|
case "sitemap":
|
|
29
29
|
return strategySitemap(rest);
|
|
30
|
+
case "topics":
|
|
31
|
+
return strategyTopics(rest);
|
|
30
32
|
default:
|
|
31
33
|
throw new CliError(
|
|
32
|
-
`Unknown strategy subcommand: ${sub ?? "(none)"}. Try: company, positioning, competitors, sitemap`
|
|
34
|
+
`Unknown strategy subcommand: ${sub ?? "(none)"}. Try: company, positioning, competitors, sitemap, topics`
|
|
33
35
|
);
|
|
34
36
|
}
|
|
35
37
|
}
|
|
@@ -169,6 +171,45 @@ async function strategySitemap(ctx) {
|
|
|
169
171
|
return 0;
|
|
170
172
|
}
|
|
171
173
|
|
|
174
|
+
// -- topics ---------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
async function strategyTopics(ctx) {
|
|
177
|
+
const sub = ctx.positionals[0];
|
|
178
|
+
const rest = { ...ctx, positionals: ctx.positionals.slice(1) };
|
|
179
|
+
switch (sub) {
|
|
180
|
+
case "list":
|
|
181
|
+
case "ls":
|
|
182
|
+
return topicsList(rest);
|
|
183
|
+
case "set":
|
|
184
|
+
return topicsSet(rest);
|
|
185
|
+
default:
|
|
186
|
+
throw new CliError(`Unknown strategy topics subcommand: ${sub ?? "(none)"}. Try: list, set`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function topicsList(ctx) {
|
|
191
|
+
const { client, flags, io } = ctx;
|
|
192
|
+
const collection = requireFlag(flags, "collection");
|
|
193
|
+
const result = await client.callTool("list_topics", { collection_id: collection });
|
|
194
|
+
printResult(io, flags, result);
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function topicsSet(ctx) {
|
|
199
|
+
const { client, flags, io } = ctx;
|
|
200
|
+
const collection = requireFlag(flags, "collection");
|
|
201
|
+
const topicId = flagStr(flags.topic);
|
|
202
|
+
const suggestionId = flagStr(flags.suggestion);
|
|
203
|
+
if ((topicId === undefined) === (suggestionId === undefined)) {
|
|
204
|
+
throw new CliError("Pass exactly one of --topic <topic-id> or --suggestion <suggestion-id>.");
|
|
205
|
+
}
|
|
206
|
+
const args = compact({ collection_id: collection, topic_id: topicId, suggestion_id: suggestionId });
|
|
207
|
+
const result = await client.callTool("set_topic", args);
|
|
208
|
+
ok(ctx, "Topic queued.");
|
|
209
|
+
printResult(io, flags, result);
|
|
210
|
+
return 0;
|
|
211
|
+
}
|
|
212
|
+
|
|
172
213
|
// --- onboarding ---------------------------------------------------------
|
|
173
214
|
|
|
174
215
|
export async function cmdOnboarding(ctx) {
|
package/lib/commands.mjs
CHANGED