@series-a/cli 0.6.0 → 0.6.2
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 +4 -0
- package/README.md +37 -2
- package/package.json +2 -3
- package/src/commands/resources.mjs +68 -5
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,10 @@ All notable changes to this package are documented here. This project follows
|
|
|
4
4
|
[Semantic Versioning](https://semver.org/) and uses
|
|
5
5
|
[Changesets](https://github.com/changesets/changesets) to manage releases.
|
|
6
6
|
|
|
7
|
+
## 0.6.1 — Campaign editing
|
|
8
|
+
|
|
9
|
+
- `campaigns` — added `update`/`edit` (name, description, goal, sequence, sender accounts, daily send limit, launch stage, status). Mirrors the new `update_campaign` MCP tool.
|
|
10
|
+
|
|
7
11
|
## 0.2.0 — Write coverage expansion
|
|
8
12
|
|
|
9
13
|
- `inbox` — added `send`, `reply` (dry-run by default, `--confirm` to dispatch, 7,900 char guard), `mark-read` (bulk), `snooze` (`--for`, `--until`, `--clear`).
|
package/README.md
CHANGED
|
@@ -73,11 +73,28 @@ advisoros leads list [--status new] [--persona founder] [--search acme] [--limit
|
|
|
73
73
|
advisoros leads get <id>
|
|
74
74
|
advisoros leads set-status <id> <new|engaged|won|lost>
|
|
75
75
|
advisoros leads import --file leads.csv [--persona founder]
|
|
76
|
+
advisoros leads import-from <source> [--query ...] [--post <url>] [--file <path>] [--audience <name>] [--campaign <id>] [--limit 50] [--no-enrich]
|
|
77
|
+
advisoros leads import-status <jobId>
|
|
78
|
+
advisoros leads update <id> [--name ...] [--title ...] [--company ...] [--email ...] [--linkedin ...] [--persona ...] [--score ...] [--status ...] [--notes ...]
|
|
79
|
+
advisoros leads delete <id>
|
|
76
80
|
advisoros leads verify <id>
|
|
77
81
|
```
|
|
78
82
|
|
|
79
83
|
`leads import` accepts CSV (header row), JSON array, or JSONL. Upserts on `(workspace_id, linkedin_url)`. Bulk requests are auto-chunked at 200/call (server cap: 500/call).
|
|
80
84
|
|
|
85
|
+
`leads import-from` runs any of the six import sources in **Get Leads → Import Leads**:
|
|
86
|
+
|
|
87
|
+
| source | required params | what it does |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `linkedin_search` | `--query` | keyword / title / company people search via Unipile |
|
|
90
|
+
| `company_search` | `--query` | LinkedIn company search |
|
|
91
|
+
| `post_reactors` | `--post <url>` | everyone who reacted to a LinkedIn post |
|
|
92
|
+
| `paste_profiles` | `--file <path>` | name + LinkedIn URL lines; rest is enriched |
|
|
93
|
+
| `csv` | `--file <path>` | same CSV parser the UI uses |
|
|
94
|
+
| `heyreach` | `--file <path>` `--campaign <id>` | HeyReach campaign CSV, statuses preserved |
|
|
95
|
+
|
|
96
|
+
All sources accept `--audience` to attach leads to a segment (created if missing). LinkedIn-backed sources run at bulk read priority so live campaigns are not starved. Long-running imports return a `job_id`; poll with `advisoros leads import-status <jobId>`.
|
|
97
|
+
|
|
81
98
|
### Campaigns
|
|
82
99
|
|
|
83
100
|
```bash
|
|
@@ -96,7 +113,7 @@ advisoros campaigns resume <id>
|
|
|
96
113
|
|
|
97
114
|
`launch` flips status to `active` — the `enforce_campaign_char_limits` trigger validates every step's copy against LinkedIn limits before allowing the transition.
|
|
98
115
|
|
|
99
|
-
### Sequences / Inbox / Content / Agents
|
|
116
|
+
### Sequences / Inbox / Content / Agents / Meetings / Newsletter / Integrations
|
|
100
117
|
|
|
101
118
|
```bash
|
|
102
119
|
advisoros sequences list
|
|
@@ -110,11 +127,29 @@ advisoros content publish <id>
|
|
|
110
127
|
|
|
111
128
|
advisoros agents list
|
|
112
129
|
advisoros agents run <name> --input '{"foo":"bar"}'
|
|
130
|
+
advisoros agents trigger <name> --input '{"foo":"bar"}'
|
|
131
|
+
advisoros agents history [--agent <name>] [--days 30] [--limit 50]
|
|
113
132
|
advisoros agents job <jobId>
|
|
133
|
+
|
|
134
|
+
advisoros meetings list [--limit 50]
|
|
135
|
+
advisoros meetings transcripts [--provider fireflies|roam] [--since 2026-01-01] [--limit 50]
|
|
136
|
+
advisoros meetings transcript <transcriptId>
|
|
137
|
+
|
|
138
|
+
advisoros newsletter list
|
|
139
|
+
advisoros newsletter create --subject "Weekly roundup" --body "<html>..." [--preview-text "..."]
|
|
140
|
+
advisoros newsletter edit <id> [--subject ...] [--body ...] [--preview-text ...] [--status draft|scheduled]
|
|
141
|
+
advisoros newsletter delete <id>
|
|
142
|
+
|
|
143
|
+
advisoros integrations status
|
|
144
|
+
advisoros integrations check <platform>
|
|
114
145
|
```
|
|
115
146
|
|
|
116
147
|
> **Heads-up:** `inbox send`/`reply` are intentionally **not** in v1. Outbound goes through `campaigns launch` (sender resolution + char limits + per-sender daily budget). For one-off DMs, use the web inbox.
|
|
117
148
|
|
|
149
|
+
`meetings transcript` only returns full text once the transcript has been anonymised; otherwise metadata is returned with the body redacted.
|
|
150
|
+
|
|
151
|
+
`integrations status` is read-only and never returns credentials. `integrations check <platform>` runs a live health probe for the requested platform (e.g. `linkedin_tool`).
|
|
152
|
+
|
|
118
153
|
### Obsidian vault mirror (`kb`) — one-way
|
|
119
154
|
|
|
120
155
|
```bash
|
|
@@ -183,7 +218,7 @@ const { campaigns } = await call("campaigns", "list", { status: "active" }, {
|
|
|
183
218
|
```bash
|
|
184
219
|
cd tools/cli
|
|
185
220
|
npx changeset # describe the change
|
|
186
|
-
npm run version
|
|
221
|
+
npm run version-packages # bump package.json + CHANGELOG.md
|
|
187
222
|
git commit -am "chore(cli): release v$(node -p "require('./package.json').version")"
|
|
188
223
|
git tag "cli-v$(node -p "require('./package.json').version")"
|
|
189
224
|
git push --follow-tags
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@series-a/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "AdvisorPro command-line interface — drive leads, campaigns, sequences and agents from your terminal.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"smoke": "node bin/advisoros.mjs --help >/dev/null && node bin/advisoros.mjs --version >/dev/null && node -e \"import('node:fs').then(({readFileSync})=>{const s=readFileSync('bin/advisoros.mjs','utf8');if(!s.startsWith('#!/usr/bin/env node')){console.error('Missing shebang in bin/advisoros.mjs');process.exit(1)}})\"",
|
|
22
22
|
"prepublishOnly": "npm run smoke",
|
|
23
23
|
"changeset": "changeset",
|
|
24
|
-
"version": "changeset version",
|
|
24
|
+
"version-packages": "changeset version",
|
|
25
25
|
"release": "npm run smoke && changeset publish --access public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
@@ -58,4 +58,3 @@
|
|
|
58
58
|
"author": "AdvisorPro",
|
|
59
59
|
"license": "UNLICENSED"
|
|
60
60
|
}
|
|
61
|
-
|
|
@@ -246,6 +246,33 @@ export function registerCampaigns(program) {
|
|
|
246
246
|
} catch (e) { fail(e); }
|
|
247
247
|
});
|
|
248
248
|
|
|
249
|
+
withCommonOpts(c.command("update <campaignId>"))
|
|
250
|
+
.description("Edit an existing campaign")
|
|
251
|
+
.option("--name <name>", "rename campaign")
|
|
252
|
+
.option("--description <text>", "description")
|
|
253
|
+
.option("--goal <text>", "campaign goal")
|
|
254
|
+
.option("--sequence <id>", "attach a sequence")
|
|
255
|
+
.option("--senders <csv>", "comma-separated sender_account_ids")
|
|
256
|
+
.option("--daily-limit <n>", "daily send cap")
|
|
257
|
+
.option("--status <status>", "draft|active|paused|completed|archived")
|
|
258
|
+
.action(async function (campaignId, opts) {
|
|
259
|
+
try {
|
|
260
|
+
const payload = { id: campaignId };
|
|
261
|
+
if (opts.name !== undefined) payload.name = opts.name;
|
|
262
|
+
if (opts.description !== undefined) payload.description = opts.description;
|
|
263
|
+
if (opts.goal !== undefined) payload.goal = opts.goal;
|
|
264
|
+
if (opts.sequence !== undefined) payload.sequence_id = opts.sequence;
|
|
265
|
+
if (opts.senders !== undefined) {
|
|
266
|
+
payload.sender_accounts = opts.senders.split(",").map((s) => s.trim()).filter(Boolean);
|
|
267
|
+
}
|
|
268
|
+
if (opts.dailyLimit !== undefined) payload.daily_send_limit = Number(opts.dailyLimit);
|
|
269
|
+
if (opts.status !== undefined) payload.status = opts.status;
|
|
270
|
+
const r = await call("campaigns", "update", payload, ctxFrom(this));
|
|
271
|
+
emit(this, r, (d) => process.stdout.write(`✓ Updated campaign ${d.campaign?.id}\n`));
|
|
272
|
+
} catch (e) { fail(e); }
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
|
|
249
276
|
withCommonOpts(c.command("add-leads <campaignId>"))
|
|
250
277
|
.description("Attach leads to a campaign")
|
|
251
278
|
.option("--lead-ids <csv>", "comma-separated lead UUIDs")
|
|
@@ -373,15 +400,17 @@ export function registerSequences(program) {
|
|
|
373
400
|
.description("Append a step to a sequence")
|
|
374
401
|
.requiredOption(
|
|
375
402
|
"--action <action>",
|
|
376
|
-
"viewProfile|addReaction|
|
|
403
|
+
"viewProfile|addReaction|sendInvitation|sendMessage|loopBack|createOpportunity|enrichLead|ifAccepted|ifReplied|ifConnection|ifNetworkDistance|stopOnReply|wait|wait_3h|wait_1d|wait_2d|wait_3d|wait_5d",
|
|
377
404
|
)
|
|
378
|
-
.option("--text <message>", "message_content (required for
|
|
405
|
+
.option("--text <message>", "message_content (required for sendInvitation/sendMessage)")
|
|
379
406
|
.option("--delay <days>", "delay_days before this step", "0")
|
|
380
407
|
.option("--variant <v>", "variant label", "A")
|
|
381
408
|
.option("--step-type <t>", "action|condition|delay (defaults from --action)")
|
|
382
409
|
.option("--channel <c>", "channel (defaults from --action)")
|
|
383
|
-
.option("--branch <label>", "branch_label: yes|no (
|
|
384
|
-
.option("--parent-step <id>", "parent_step_id (
|
|
410
|
+
.option("--branch <label>", "branch_label: yes|no|first|second_or_third|follower|out_of_network (children of a condition)")
|
|
411
|
+
.option("--parent-step <id>", "parent_step_id (defaults to the tail of the sequence)")
|
|
412
|
+
.option("--root", "start a new root step instead of appending to the tail (rarely correct)")
|
|
413
|
+
|
|
385
414
|
.option("--order <n>", "explicit step_order (defaults to max+1)")
|
|
386
415
|
.action(async function (id, opts) {
|
|
387
416
|
try {
|
|
@@ -395,15 +424,49 @@ export function registerSequences(program) {
|
|
|
395
424
|
channel: opts.channel,
|
|
396
425
|
branch_label: opts.branch,
|
|
397
426
|
parent_step_id: opts.parentStep,
|
|
427
|
+
root: opts.root === true,
|
|
398
428
|
step_order: opts.order ? Number(opts.order) : undefined,
|
|
399
429
|
}, ctxFrom(this));
|
|
400
430
|
emit(this, r, (d) => {
|
|
401
431
|
const st = d.step || {};
|
|
402
|
-
process.stdout.write(`✓ Added step ${st.id}\n #${st.step_order} ${st.action} (${st.channel}) delay=${st.delay_days}d variant=${st.variant}\n`);
|
|
432
|
+
process.stdout.write(`✓ Added step ${st.id}\n #${st.step_order} ${st.action} (${st.channel}) delay=${st.delay_days}d variant=${st.variant}\n parent=${st.parent_step_id || "root"}${st.branch_label ? ` branch=${st.branch_label}` : ""}\n`);
|
|
433
|
+
for (const w of d.graph?.warnings || []) process.stdout.write(` ⚠ ${w.message}\n`);
|
|
434
|
+
});
|
|
435
|
+
} catch (e) { fail(e); }
|
|
436
|
+
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
withCommonOpts(s.command("replace-steps <id>"))
|
|
440
|
+
.description("Replace every step of a sequence with a validated tree from a JSON file (atomic)")
|
|
441
|
+
.requiredOption("--file <path>", "JSON file: [{ temp_id, parent, branch_label, action, message_content, delay_days }]")
|
|
442
|
+
.option("--dry-run", "validate the graph without writing")
|
|
443
|
+
.action(async function (id, opts) {
|
|
444
|
+
try {
|
|
445
|
+
const raw = await readFile(opts.file, "utf8");
|
|
446
|
+
const parsed = JSON.parse(raw);
|
|
447
|
+
const steps = Array.isArray(parsed) ? parsed : parsed.steps;
|
|
448
|
+
if (!Array.isArray(steps)) throw new Error("file must contain an array of steps or { steps: [...] }");
|
|
449
|
+
const r = await call("sequences", "replace_steps", {
|
|
450
|
+
sequence_id: id,
|
|
451
|
+
steps,
|
|
452
|
+
dry_run: opts.dryRun === true,
|
|
453
|
+
}, ctxFrom(this));
|
|
454
|
+
emit(this, r, (d) => {
|
|
455
|
+
if (d.dry_run) {
|
|
456
|
+
process.stdout.write(`✓ Graph valid — ${d.planned_steps} step(s), nothing written\n`);
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
process.stdout.write(`✓ Replaced steps on ${d.sequence_id} — ${d.replaced_steps} step(s)\n`);
|
|
460
|
+
for (const st of d.steps || []) {
|
|
461
|
+
process.stdout.write(` #${st.step_order} ${st.action}${st.branch_label ? ` [${st.branch_label}]` : ""} parent=${st.parent_step_id || "root"}\n`);
|
|
462
|
+
}
|
|
463
|
+
for (const w of d.graph?.warnings || []) process.stdout.write(` ⚠ ${w.message}\n`);
|
|
403
464
|
});
|
|
404
465
|
} catch (e) { fail(e); }
|
|
405
466
|
});
|
|
406
467
|
|
|
468
|
+
|
|
469
|
+
|
|
407
470
|
withCommonOpts(s.command("clone <id>"))
|
|
408
471
|
.description("Duplicate a sequence (and all its steps) as a new draft")
|
|
409
472
|
.option("--name <name>", "name for the clone (default: '<src> (copy)')")
|