@sprid/cli 0.1.6 → 0.1.8
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/CHANGELOG.md +20 -0
- package/README.md +13 -2
- package/bin.mjs +1 -1
- package/package.json +3 -3
- package/src/args.mjs +22 -1
- package/src/cli.mjs +27 -5
- package/src/commands/ads.mjs +110 -0
- package/src/commands/connect.mjs +45 -11
- package/src/commands/events.mjs +60 -0
- package/src/commands/inbox.mjs +124 -0
- package/src/commands/post.mjs +2 -0
- package/src/commands/publishing.mjs +109 -0
- package/src/commands/status.mjs +9 -1
- package/src/commands/tool.mjs +59 -0
- package/src/docs/commands.mjs +32 -0
- package/src/docs/guides.generated.mjs +252 -5
- package/src/docs/help.mjs +1 -0
- package/src/telemetry.mjs +140 -0
- package/src/tools.mjs +85 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// sprid post schedule | publish | schedule-batch: putting a post in front of
|
|
2
|
+
// an audience, through the same tools an agent calls. Publishing now is public
|
|
3
|
+
// and cannot be taken back, so it asks unless --yes. A booking can be taken
|
|
4
|
+
// back (`sprid queue cancel <publishId>`), so scheduling does not ask.
|
|
5
|
+
//
|
|
6
|
+
// TikTok's privacy level, comment/duet/stitch permissions and the
|
|
7
|
+
// commercial-content disclosure have no defaults and are never remembered, so
|
|
8
|
+
// a TikTok destination needs them in --options every time.
|
|
9
|
+
|
|
10
|
+
import { UsageError } from "../args.mjs";
|
|
11
|
+
import { C, OK } from "../format.mjs";
|
|
12
|
+
import { callTool, listFlag, readJson, show } from "../tools.mjs";
|
|
13
|
+
import { confirm } from "./reviews.mjs";
|
|
14
|
+
|
|
15
|
+
const POST_REF = /^([1-9]\d*|[A-Za-z][A-Za-z0-9]{1,4}-\d+)$/;
|
|
16
|
+
const TIKTOK_FIELDS = "privacyLevel, disableComment, disableDuet, disableStitch and the commercial-content answers";
|
|
17
|
+
|
|
18
|
+
export const PUBLISHING_VERBS = ["schedule", "publish", "schedule-batch"];
|
|
19
|
+
|
|
20
|
+
export async function publishing(ctx) {
|
|
21
|
+
const [verb, ref] = ctx.positionals;
|
|
22
|
+
if (verb === "schedule-batch") return scheduleBatch(ctx);
|
|
23
|
+
const usage = verb === "publish"
|
|
24
|
+
? "sprid post publish <postId> [--platforms a,b] [--options <options.json>] [--yes]"
|
|
25
|
+
: "sprid post schedule <postId> (--at ISO|--next|--queue-end) [--platforms a,b] [--time HH:MM] [--shift-later] [--options <options.json>]";
|
|
26
|
+
if (!POST_REF.test(String(ref ?? ""))) throw new UsageError(usage);
|
|
27
|
+
const id = /^\d+$/.test(ref) ? Number(ref) : String(ref).toUpperCase();
|
|
28
|
+
const base = { id, ...(await destination(ctx)) };
|
|
29
|
+
return verb === "publish" ? publish(ctx, base) : schedule(ctx, base, usage);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Platforms and per-platform options, shared by every verb here. */
|
|
33
|
+
async function destination(ctx) {
|
|
34
|
+
const platforms = listFlag(ctx.flags.platforms);
|
|
35
|
+
const options = typeof ctx.flags.options === "string" ? await readJson(ctx, ctx.flags.options) : undefined;
|
|
36
|
+
if (platforms?.includes("tiktok") && !options?.tiktok) {
|
|
37
|
+
throw new UsageError(`TikTok needs its answers every time: put ${TIKTOK_FIELDS} under "tiktok" in --options <options.json>.`);
|
|
38
|
+
}
|
|
39
|
+
return { ...(platforms ? { platforms } : {}), ...(options ? { options } : {}) };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function schedule(ctx, base, usage) {
|
|
43
|
+
const when = typeof ctx.flags.at === "string" ? { when: ctx.flags.at }
|
|
44
|
+
: ctx.flags.next ? { nextFreeSlot: true }
|
|
45
|
+
: ctx.flags["queue-end"] ? { queueEnd: true }
|
|
46
|
+
: null;
|
|
47
|
+
if (!when) throw new UsageError(usage);
|
|
48
|
+
const args = {
|
|
49
|
+
...base,
|
|
50
|
+
...when,
|
|
51
|
+
...(typeof ctx.flags.time === "string" ? { time: ctx.flags.time } : {}),
|
|
52
|
+
...(ctx.flags["shift-later"] ? { shiftLater: true } : {}),
|
|
53
|
+
};
|
|
54
|
+
const res = await callTool(ctx, "schedule_post", args);
|
|
55
|
+
if (ctx.json) {
|
|
56
|
+
ctx.out(res);
|
|
57
|
+
return 0;
|
|
58
|
+
}
|
|
59
|
+
const at = String(res?.scheduledFor ?? "").replace("T", " ").slice(0, 16);
|
|
60
|
+
for (const r of res?.scheduled ?? []) ctx.print(` ${C.ok(OK)} ${r.platform} ${at} UTC ${C.dim(`publish ${r.publishId}`)}`);
|
|
61
|
+
for (const e of res?.errors ?? []) ctx.print(` ${C.bad("✗")} ${e.platform ?? ""} ${e.error ?? e.message ?? JSON.stringify(e)}`);
|
|
62
|
+
if (res?.scheduled?.length) ctx.print(C.dim(" Undo one: sprid queue cancel <publishId>"));
|
|
63
|
+
return res?.errors?.length ? 1 : 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function publish(ctx, base) {
|
|
67
|
+
if (!ctx.flags.yes) {
|
|
68
|
+
const where = base.platforms?.join(", ") ?? "every destination the post is for";
|
|
69
|
+
const msg = "Publishing is public and cannot be undone. Re-run with --yes to publish without the prompt.";
|
|
70
|
+
if (ctx.json || !ctx.stdin?.isTTY) {
|
|
71
|
+
if (ctx.json) ctx.out({ ok: false, error: msg, post: base.id, platforms: base.platforms ?? null });
|
|
72
|
+
else {
|
|
73
|
+
ctx.print(` Will publish post ${base.id} to ${where} now.`);
|
|
74
|
+
ctx.warn(` ✗ ${msg}`);
|
|
75
|
+
}
|
|
76
|
+
return 2;
|
|
77
|
+
}
|
|
78
|
+
ctx.print(` Will publish post ${base.id} to ${where} now.\n`);
|
|
79
|
+
if (!(await confirm(ctx, " Publish it? [y/N] "))) {
|
|
80
|
+
ctx.print(" Not published.");
|
|
81
|
+
return 1;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const res = await callTool(ctx, "publish_post", base);
|
|
85
|
+
if (ctx.json) ctx.out(res);
|
|
86
|
+
else {
|
|
87
|
+
show(ctx, res);
|
|
88
|
+
ctx.print(C.dim(` Receipts: sprid post deliveries ${base.id}`));
|
|
89
|
+
}
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function scheduleBatch(ctx) {
|
|
94
|
+
const usage = "sprid post schedule-batch --account <slug> --ids 1,2,3 [--cadence daily|every-other-day|weekdays] [--times 09:00,18:00] [--start YYYY-MM-DD] [--platforms a,b] [--skip-busy] [--dry-run]";
|
|
95
|
+
const ids = listFlag(ctx.flags.ids);
|
|
96
|
+
if (typeof ctx.flags.account !== "string" || !ids) throw new UsageError(usage);
|
|
97
|
+
const args = {
|
|
98
|
+
account: ctx.flags.account,
|
|
99
|
+
postIds: ids.map((v) => (/^\d+$/.test(v) ? Number(v) : v.toUpperCase())),
|
|
100
|
+
...(await destination(ctx)),
|
|
101
|
+
...(typeof ctx.flags.cadence === "string" ? { cadence: ctx.flags.cadence } : {}),
|
|
102
|
+
...(listFlag(ctx.flags.times) ? { times: listFlag(ctx.flags.times) } : {}),
|
|
103
|
+
...(typeof ctx.flags.start === "string" ? { startDayIso: ctx.flags.start } : {}),
|
|
104
|
+
...(ctx.flags["skip-busy"] ? { skipBusyDays: true } : {}),
|
|
105
|
+
...(ctx.flags["dry-run"] ? { dryRun: true } : {}),
|
|
106
|
+
};
|
|
107
|
+
show(ctx, await callTool(ctx, "schedule_batch", args));
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
package/src/commands/status.mjs
CHANGED
|
@@ -194,7 +194,15 @@ export async function queue(ctx) {
|
|
|
194
194
|
else ctx.print(` ${C.ok(OK)} Publish ${id} is back on the queue${row?.scheduledFor ? ` for ${dayTime(row.scheduledFor)}` : ""}.`);
|
|
195
195
|
return 0;
|
|
196
196
|
}
|
|
197
|
-
if (sub
|
|
197
|
+
if (sub === "cancel") {
|
|
198
|
+
const id = Number(arg);
|
|
199
|
+
if (!Number.isInteger(id) || id <= 0) throw usage("sprid queue cancel <publishId>");
|
|
200
|
+
await ctx.api().del(`/api/publishes/${id}`);
|
|
201
|
+
if (ctx.json) ctx.out({ ok: true, publishId: id });
|
|
202
|
+
else ctx.print(` ${C.ok(OK)} Booking ${id} cancelled. The post stays as it is.`);
|
|
203
|
+
return 0;
|
|
204
|
+
}
|
|
205
|
+
if (sub) throw usage("sprid queue [retry|cancel <publishId>]");
|
|
198
206
|
const view = await fetchStatus(ctx);
|
|
199
207
|
const q = view.queue ?? {};
|
|
200
208
|
if (ctx.json) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// sprid tool: every server tool from the terminal, for the ones no named
|
|
2
|
+
// command covers yet. Same handler, scopes and gates as an MCP client's call.
|
|
3
|
+
|
|
4
|
+
import { UsageError } from "../args.mjs";
|
|
5
|
+
import { C } from "../format.mjs";
|
|
6
|
+
import { callTool, listTools, show, toolArgs } from "../tools.mjs";
|
|
7
|
+
|
|
8
|
+
const USAGE = "sprid tool list [--search <words>] | describe <name> | call <name> [--file args.json] [--args '<json>']";
|
|
9
|
+
|
|
10
|
+
export async function tool(ctx) {
|
|
11
|
+
const [sub, name] = ctx.positionals;
|
|
12
|
+
if (sub === "list" || !sub) return list(ctx);
|
|
13
|
+
if (sub === "describe" && name) return describe(ctx, name);
|
|
14
|
+
if (sub === "call" && name) {
|
|
15
|
+
show(ctx, await callTool(ctx, name, await toolArgs(ctx)));
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
throw new UsageError(USAGE);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function list(ctx) {
|
|
22
|
+
const words = typeof ctx.flags.search === "string" ? ctx.flags.search.toLowerCase().split(/\s+/).filter(Boolean) : [];
|
|
23
|
+
const tools = (await listTools(ctx)).filter((t) => {
|
|
24
|
+
const hay = `${t.name} ${t.description ?? ""}`.toLowerCase();
|
|
25
|
+
return words.every((w) => hay.includes(w));
|
|
26
|
+
});
|
|
27
|
+
if (ctx.json) {
|
|
28
|
+
ctx.out({ tools: tools.map(({ name, description }) => ({ name, description })) });
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
if (tools.length === 0) {
|
|
32
|
+
ctx.print(" No tool matches.");
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
const width = Math.max(...tools.map((t) => t.name.length));
|
|
36
|
+
for (const t of tools) ctx.print(` ${t.name.padEnd(width)} ${C.dim(firstSentence(t.description))}`);
|
|
37
|
+
ctx.print(C.dim(`\n ${tools.length} tools. Run one: sprid tool call <name> --file args.json`));
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function describe(ctx, name) {
|
|
42
|
+
const hit = (await listTools(ctx)).find((t) => t.name === name);
|
|
43
|
+
if (!hit) throw new UsageError(`No tool "${name}" for this token. List them: sprid tool list --search <words>`);
|
|
44
|
+
if (ctx.json) {
|
|
45
|
+
ctx.out(hit);
|
|
46
|
+
return 0;
|
|
47
|
+
}
|
|
48
|
+
ctx.print(` ${hit.name}\n`);
|
|
49
|
+
ctx.print(` ${hit.description ?? ""}\n`);
|
|
50
|
+
ctx.print(JSON.stringify(hit.inputSchema ?? {}, null, 2));
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function firstSentence(text = "") {
|
|
55
|
+
const s = String(text).replace(/\s+/g, " ").trim();
|
|
56
|
+
const cut = s.search(/\.\s/);
|
|
57
|
+
const one = cut > 0 ? s.slice(0, cut + 1) : s;
|
|
58
|
+
return one.length > 110 ? one.slice(0, 109) + "…" : one;
|
|
59
|
+
}
|
package/src/docs/commands.mjs
CHANGED
|
@@ -38,6 +38,7 @@ export const COMMAND_GROUPS = [
|
|
|
38
38
|
{ title: 'Installation and updates', entries: [
|
|
39
39
|
['doctor', 'sprid doctor [--json] [--offline] [--apply-updates]', 'Check the running CLI against the npm latest release. --apply-updates is a between-jobs boundary and installs compatible updates only after explicit opt-in.'],
|
|
40
40
|
['update', 'sprid update [--check] [--yes]', 'Update the running installation and verify its new version. --check only reports; potentially breaking releases require --yes. Project installs use their own package manager and lockfile.'],
|
|
41
|
+
['telemetry', 'sprid telemetry [status|on|off]', 'Show or change usage telemetry. When on, each command sends its name, exit code, duration and versions to your Sprid account, never arguments or file contents. DO_NOT_TRACK=1 or SPRID_TELEMETRY=0 turns it off for one shell.'],
|
|
41
42
|
['update', 'sprid update --auto on|off', 'Opt this installation into or out of compatible updates at job boundaries. Default off. Agents must have user approval before enabling.'],
|
|
42
43
|
] },
|
|
43
44
|
{ title: 'Store assets and releases', entries: [
|
|
@@ -68,6 +69,10 @@ export const COMMAND_GROUPS = [
|
|
|
68
69
|
['connect', 'sprid connect <service> --app <slug> --key <file> …', 'Save a store or analytics key. Read sprid docs <service> for the required fields.'],
|
|
69
70
|
['connect', 'sprid connect <service> --app <slug> --key-from-clipboard …', 'Save a key you just copied from the provider. It is read from the clipboard, never printed or written to disk, and the clipboard is cleared after saving. Not for services whose key is a downloaded file.'],
|
|
70
71
|
['connect', 'sprid connect <platform> --account <slug>', 'Connect a publishing account through the platform’s sign-in page.'],
|
|
72
|
+
['connect', 'sprid connect meta-ads --account <slug> [--page <id>] [--ad-account <id>] [--dataset <id>]', 'Connect the Meta ad account Sprid runs campaigns in. When the Facebook profile manages several Pages or ad accounts, Sprid lists their ids; run it again with --page and --ad-account. --dataset is the Events Manager dataset id conversion campaigns report to.'],
|
|
73
|
+
['connect', 'sprid connect google-ads --account <slug> [--ad-account <customer id>]', 'Connect the Google Ads account that promotes YouTube videos. When the Google login reaches several accounts, Sprid lists their ids; run it again with --ad-account.'],
|
|
74
|
+
['connect', 'sprid connect tiktok-ads --account <slug> [--ad-account <advertiser id>]', 'Connect the TikTok ad account that promotes TikTok posts as Spark Ads. When the login reaches several advertisers, Sprid lists their ids; run it again with --ad-account.'],
|
|
75
|
+
['connect', 'sprid connect tiktok-comments --account <slug>', 'Let the Inbox read and answer comments on your TikTok posts. Sign in with the TikTok account that publishes them; this is separate from TikTok publishing and from TikTok Ads.'],
|
|
71
76
|
['connect', 'sprid connect ga4|plausible|umami --app <slug> --key <file> … [--use]', 'PostHog, Google Analytics, Plausible and Umami all count the same website visits, so Sprid reads one of them and never adds them together. --use makes this one the source the traffic card reports from.'],
|
|
72
77
|
['pinterest', 'sprid pinterest boards --account <slug> [--channel <id>] [--bookmark <token>]', 'List boards and sections for the exact connected Pinterest channel; a returned bookmark reads the next page.'],
|
|
73
78
|
['pinterest', 'sprid pinterest create --account <slug> --name <name> [--description <text>] [--privacy public|secret]', 'Create a board in the exact connected Pinterest account. Older connections must reconnect once to grant board creation permission.'],
|
|
@@ -92,10 +97,27 @@ export const COMMAND_GROUPS = [
|
|
|
92
97
|
['marketing-review', 'sprid marketing-review query --app <slug> --query-file <query.sql>', 'Run a read-only PostHog query through the app’s stored connection.'],
|
|
93
98
|
['queue', 'sprid queue', 'Show what is scheduled and what failed.'],
|
|
94
99
|
['queue', 'sprid queue retry <publishId>', 'Retry a failed or missed publish.'],
|
|
100
|
+
['queue', 'sprid queue cancel <publishId>', 'Cancel a booking. The post itself is unchanged and can be scheduled again.'],
|
|
101
|
+
['post', 'sprid post schedule <postId> (--at ISO|--next|--queue-end) [--platforms a,b] [--time HH:MM] [--shift-later] [--options <options.json>]', 'Book a post: at a time, the next free slot, or after everything already booked. --shift-later moves later bookings down a slot. TikTok needs its privacy and interaction answers under "tiktok" in --options every time.'],
|
|
102
|
+
['post', 'sprid post schedule-batch --account <slug> --ids 1,2,3 [--cadence daily|every-other-day|weekdays] [--times 09:00] [--start YYYY-MM-DD] [--platforms a,b] [--skip-busy] [--dry-run]', 'Place a list of posts on a cadence in one call. --dry-run returns the plan without booking anything.'],
|
|
103
|
+
['post', 'sprid post publish <postId> [--platforms a,b] [--options <options.json>] [--yes]', 'Publish now. Public and not reversible, so it asks unless --yes. Read the receipts with sprid post deliveries.'],
|
|
104
|
+
['inbox', 'sprid inbox [--view needs_you|all|done] [--kind all|comment|review] [--accounts a,b] [--app <slug>] [--intent <intent>] [--limit N]', 'Comments on posts and ads plus store reviews, as one ranked queue.'],
|
|
105
|
+
['inbox', 'sprid inbox draft <commentId> [--count N]', 'Draft replies in the account’s reply voice. Nothing is sent.'],
|
|
106
|
+
['inbox', 'sprid inbox reply <commentId> --text "…" [--yes]', 'Send a public reply to a comment. Asks unless --yes. Store reviews are answered with sprid reviews reply.'],
|
|
107
|
+
['inbox', 'sprid inbox done|flag|reopen <c:id|r:id>', 'File an item without replying, mark a comment urgent, or put an item back in Needs you. Nothing is sent to the platform.'],
|
|
95
108
|
['reviews', 'sprid reviews [--app <slug>] [--unanswered] [--max-rating N] [--limit N]', 'Read store reviews.'],
|
|
96
109
|
['reviews', 'sprid reviews draft <id>', 'Draft a reply using the workspace’s model key. Nothing is sent.'],
|
|
97
110
|
['reviews', 'sprid reviews reply <id> [--text "…"] [--yes]', 'Review and send a public reply. --yes skips the confirmation.'],
|
|
98
111
|
] },
|
|
112
|
+
{ title: 'Paid ads', entries: [
|
|
113
|
+
['ads', 'sprid ads [overview] --account <slug>', 'Spend against the cap, what is live and what the connected Meta Ads channel can do.'],
|
|
114
|
+
['ads', 'sprid ads status|council|metrics <adSetId> --account <slug> [--days N]', 'Delivery and rejections from Meta, the weekly judgement, or the numbers for one ad set.'],
|
|
115
|
+
['ads', 'sprid ads create campaign|set|creative|ad|batch --file <args.json> --account <slug>', 'Create paused objects. The file holds the tool’s arguments; sprid tool describe create_ad_set shows them.'],
|
|
116
|
+
['ads', 'sprid ads launch <adSetId> --account <slug> [--yes]', 'Show what launching commits, then start spending. Needs a token holding ads:spend.'],
|
|
117
|
+
['ads', 'sprid ads budget <adSetId> --daily-cents N --account <slug> [--yes]', 'Change a daily budget, checked against the account’s caps. Needs ads:spend.'],
|
|
118
|
+
['ads', 'sprid ads pause <adSetId> --account <slug> [--yes]', 'Pause an ad set. Resuming restarts Meta’s learning phase.'],
|
|
119
|
+
['ads', 'sprid ads close <adSetId> --account <slug>', 'End an ad set and write its post-mortem.'],
|
|
120
|
+
] },
|
|
99
121
|
{ title: 'Build your own media', entries: [
|
|
100
122
|
['post', 'sprid post init [--kind text|composed|stills|own]', 'Choose how this repo builds posts.'],
|
|
101
123
|
['post', 'sprid post new <slug> [--lane <name>]', 'Create a post specification.'],
|
|
@@ -115,6 +137,11 @@ export const COMMAND_GROUPS = [
|
|
|
115
137
|
['invite', 'sprid invite <email> [--role editor|admin] [--account <slug>]', 'Invite a collaborator.'],
|
|
116
138
|
['upgrade', 'sprid upgrade [pulse|push|founding]', 'Open checkout for a plan.'],
|
|
117
139
|
['mcp', 'sprid mcp [--surface core|all]', 'Connect an MCP client through the CLI’s saved login.'],
|
|
140
|
+
['events', 'sprid events [list] [--days N]', 'The milestones Insights draws on its graphs.'],
|
|
141
|
+
['events', 'sprid events add --kind release|listing|screenshots|submission|note --label "…" [--app <slug>] [--areas social,store,search,revenue,product,ads] [--why "…"] [--at ISO]', 'Log a change with the graphs it can explain and why, so the next read of those graphs can be explained.'],
|
|
142
|
+
['tool', 'sprid tool list [--search <words>]', 'Every server tool this token can call, the same set an MCP client sees.'],
|
|
143
|
+
['tool', 'sprid tool describe <name>', 'A tool’s description and input schema.'],
|
|
144
|
+
['tool', 'sprid tool call <name> [--file args.json] [--args \'<json>\']', 'Run any tool with the same scopes and checks as an agent’s call. Prints the result as JSON.'],
|
|
118
145
|
['help', 'sprid help [<command> [<subcommand>]|environment|exit-codes] [--json]', 'Read focused help without authentication. JSON includes the installed command catalog.'],
|
|
119
146
|
['completion', 'sprid completion bash|zsh|fish', 'Print a shell completion script. Source it in your shell; Sprid never edits shell configuration.'],
|
|
120
147
|
] },
|
|
@@ -128,6 +155,7 @@ export const CLI_NOTES = [
|
|
|
128
155
|
'Use -w/--workspace <slug|id> for one command, or sprid use to remember your choice.',
|
|
129
156
|
'sprid login stores its token in ~/.sprid/credentials.json. SPRID_PAT overrides it.',
|
|
130
157
|
'SPRID_URL overrides the API address. SPRID_APP_URL overrides the web app address.',
|
|
158
|
+
'Usage telemetry (command, exit code, duration, versions) goes to your Sprid account when signed in. sprid telemetry off, DO_NOT_TRACK=1 or SPRID_TELEMETRY=0 stops it.',
|
|
131
159
|
];
|
|
132
160
|
|
|
133
161
|
export const COMMAND_EXAMPLES = {
|
|
@@ -138,6 +166,10 @@ export const COMMAND_EXAMPLES = {
|
|
|
138
166
|
post: ['sprid post create --file draft.json', 'sprid post get 123 --json', 'sprid post preview --id 123', 'sprid post check launch --deep'],
|
|
139
167
|
media: ['sprid media upload cover.jpg detail.jpg --account myapp', 'sprid media build launch'],
|
|
140
168
|
'marketing-review': ['sprid marketing-review --app myapp --days 7 --json', 'sprid marketing-review capabilities --app myapp --source gsc --json'],
|
|
169
|
+
ads: ['sprid ads --account myapp', 'sprid ads launch 12 --account myapp'],
|
|
170
|
+
inbox: ['sprid inbox --view needs_you', 'sprid inbox reply 88 --text "Thanks, fixed in 1.4"'],
|
|
171
|
+
events: ['sprid events add --kind release --label "1.4" --areas store,product --why "New onboarding"'],
|
|
172
|
+
tool: ['sprid tool list --search schedule', 'sprid tool call post_queue --args \'{"id":123}\''],
|
|
141
173
|
completion: ['source <(sprid completion bash)', 'source <(sprid completion zsh)', 'sprid completion fish | source'],
|
|
142
174
|
};
|
|
143
175
|
|