@audienti/cli 0.2.0-exora.2 → 0.2.0-exora.4
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/cli/agents.mjs +23 -5
- package/cli/approach-operations.mjs +4 -2
- package/cli/assignment-operations.mjs +12 -12
- package/cli/assignment.mjs +6 -6
- package/cli/audience-operations.mjs +5 -0
- package/cli/audiences.mjs +7 -4
- package/cli/audienti.mjs +77 -11
- package/cli/audit-operations.mjs +17 -0
- package/cli/audit.mjs +39 -0
- package/cli/budget-operations.mjs +8 -0
- package/cli/budgets.mjs +34 -0
- package/cli/campaign-operations.mjs +26 -0
- package/cli/{experiments.mjs → campaigns.mjs} +6 -6
- package/cli/cohort-operations.mjs +22 -0
- package/cli/cohorts.mjs +45 -0
- package/cli/connection-registry-operations.mjs +2 -1
- package/cli/discovery-operations.mjs +3 -3
- package/cli/discovery.mjs +2 -2
- package/cli/file-operations.mjs +29 -0
- package/cli/files.mjs +14 -0
- package/cli/guardrails-operations.mjs +12 -0
- package/cli/guardrails.mjs +37 -0
- package/cli/json-file.mjs +1 -1
- package/cli/mcp.mjs +1 -1
- package/cli/notification-operations.mjs +8 -0
- package/cli/offer-operations.mjs +1 -1
- package/cli/outbound-operations.mjs +16 -0
- package/cli/outbound.mjs +54 -0
- package/cli/progress.mjs +18 -6
- package/cli/question-operations.mjs +12 -0
- package/cli/questions.mjs +32 -0
- package/cli/queue-operations.mjs +11 -0
- package/cli/queue.mjs +24 -0
- package/cli/recipient-operations.mjs +1 -1
- package/cli/rooms-operations.mjs +28 -0
- package/cli/rooms.mjs +135 -0
- package/cli/search-operations.mjs +9 -0
- package/cli/skills-mcp.mjs +13 -6
- package/cli/skills.mjs +73 -16
- package/cli/usage-operations.mjs +8 -0
- package/cli/usage.mjs +41 -0
- package/cli/work-operations.mjs +4 -3
- package/cli/work.mjs +11 -6
- package/cli/workspaces.mjs +49 -7
- package/package.json +1 -1
- package/skills/agent-rooms/SKILL.md +102 -0
- package/skills/agent-rooms/references/commands.md +58 -0
- package/skills/approach-design/SKILL.md +104 -0
- package/skills/approach-design/references/fields.md +83 -0
- package/skills/audienti/SKILL.md +32 -6
- package/skills/audienti/references/helpers.md +15 -2
- package/skills/audienti/references/profile-workflows.md +6 -4
- package/skills/audienti/scripts/contracts.mjs +1 -1
- package/skills/getting-started/SKILL.md +199 -0
- package/skills/getting-started/references/audience.md +78 -0
- package/skills/getting-started/references/campaign-draft.md +38 -0
- package/skills/getting-started/references/inventory.md +52 -0
- package/source-manifest.json +59 -29
- package/cli/experiment-operations.mjs +0 -25
package/cli/agents.mjs
CHANGED
|
@@ -2,8 +2,13 @@ import { readProfile, writeProfile, validateUrl, configuration, WorkspaceRequest
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import {readJsonFile} from './json-file.mjs';
|
|
4
4
|
|
|
5
|
-
export const agentManagementOperations = ['agent_list','agent_revoke','agent_permission','agent_capabilities_get','agent_capability_sharing_get','agent_capability_sharing_set','agent_account_authorizations_get','agent_account_authorization_set'];
|
|
5
|
+
export const agentManagementOperations = ['agent_list','agent_revoke','agent_permission','agent_capabilities_set','agent_capabilities_get','agent_inventory_get','agent_capability_sharing_get','agent_capability_sharing_set','agent_account_authorizations_get','agent_account_authorization_set'];
|
|
6
|
+
const inventoryLabel=z.string().trim().min(1).max(80);
|
|
7
|
+
const inventoryEntry=z.object({category:z.enum(['web_search','web_browse','people_data','company_data','email','linkedin','social','crm','spreadsheet','docs','chat','calendar','tasks','files','code','scheduling','memory','other']),name:inventoryLabel,access:z.array(z.enum(['read','write','send'])).min(1).max(3),status:z.enum(['connected','needs_sign_in','unavailable']),via:inventoryLabel.optional(),tools:z.array(z.string().min(1).max(128)).max(16).optional()}).strict();
|
|
6
8
|
export const agentOperations = {
|
|
9
|
+
agent_inventory_report:{description:'Getting started step 2: report the tools and connectors this agent already has (search, mailboxes, CRM, sheets, chat, scheduling, memory). Replaces the whole inventory. List only what is already available; never connect anything new and never include credentials. Requires an active agent profile.',schema:z.object({runtime:z.object({name:inventoryLabel,model:inventoryLabel.optional()}).strict(),entries:z.array(inventoryEntry).max(64)}).strict()},
|
|
10
|
+
agent_inventory_show:{description:'Read this agent\u2019s own reported tool inventory. Requires an active agent profile.',schema:z.object({}).strict()},
|
|
11
|
+
agent_inventory_get:{schema:z.object({id:z.string().uuid()}).strict()},
|
|
7
12
|
agent_account_authorizations_get:{schema:z.object({id:z.string().uuid()}).strict()},
|
|
8
13
|
agent_account_authorizations_show:{schema:z.object({}).strict()},
|
|
9
14
|
agent_account_authorization_set:{schema:z.object({id:z.string().uuid(),provider:z.enum(['email','linkedin']),accountRef:z.string().min(1).max(200),authorized:z.boolean()}).strict()},
|
|
@@ -13,7 +18,8 @@ export const agentOperations = {
|
|
|
13
18
|
agent_capabilities_show: {schema:z.object({}).strict()},
|
|
14
19
|
agent_capabilities_get: {schema:z.object({id:z.string().uuid()}).strict()},
|
|
15
20
|
agent_permission: {schema:z.object({id:z.string().uuid(),scope:z.enum(['workspace:read','workspace:access'])}).strict()},
|
|
16
|
-
|
|
21
|
+
agent_capabilities_set: {schema:z.object({id:z.string().uuid(),capabilities:z.array(z.enum(['find','enrich','listen','approve'])).min(1).max(4)}).strict()},
|
|
22
|
+
agent_connect: { schema: z.object({ name: z.string().trim().min(1).max(80), capabilities: z.array(z.enum(['find','enrich','listen','approve'])).max(4).optional() }).strict() },
|
|
17
23
|
agent_poll: { schema: z.object({}).strict() },
|
|
18
24
|
agent_cancel: { schema: z.object({}).strict() },
|
|
19
25
|
agent_list: { schema: z.object({}).strict() },
|
|
@@ -44,6 +50,11 @@ export async function callAgent(name, args, config, profilePath) {
|
|
|
44
50
|
}
|
|
45
51
|
if (name === 'agent_capability_sharing_get') return {data:await request(config,`/agents/${input.data.id}/capability-sharing`,undefined,true)};
|
|
46
52
|
if (name === 'agent_capability_sharing_set') {const {id,...body}=input.data;return {data:await request(config,`/agents/${id}/capability-sharing`,body,true)};}
|
|
53
|
+
if (name === 'agent_inventory_get') return {data:await request(config,`/agents/${input.data.id}/inventory`,undefined,true)};
|
|
54
|
+
if (name === 'agent_inventory_show' || name === 'agent_inventory_report') {
|
|
55
|
+
if (!config.profile?.agent || !/^agt_/.test(config.token ?? '')) throw new Error('Use an active agent profile for tool inventory report and self readback.');
|
|
56
|
+
return {data:await request(config,'/agent-inventory',name==='agent_inventory_report' ? input.data : undefined,true,name==='agent_inventory_report' ? 'PUT' : 'GET')};
|
|
57
|
+
}
|
|
47
58
|
if (name === 'agent_capabilities_get') return {data:await request(config,`/agents/${input.data.id}/capabilities`,undefined,true)};
|
|
48
59
|
if (name === 'agent_capabilities_show' || name === 'agent_capabilities_report') {
|
|
49
60
|
if (!config.profile?.agent || !/^agt_/.test(config.token ?? '')) throw new Error('Use an active agent profile for capability check-in and self readback.');
|
|
@@ -51,6 +62,7 @@ export async function callAgent(name, args, config, profilePath) {
|
|
|
51
62
|
}
|
|
52
63
|
if (name === 'agent_list') return { data: await request(config, '/agents', undefined, true) };
|
|
53
64
|
if (name === 'agent_permission') return {data:await request(config, `/agents/${input.data.id}/permission`, {scope:input.data.scope}, true)};
|
|
65
|
+
if (name === 'agent_capabilities_set') return {data:await request(config, `/agents/${input.data.id}/approved-capabilities`, {capabilities:input.data.capabilities}, true)};
|
|
54
66
|
if (name === 'agent_revoke') return { data: await request(config, `/agents/${input.data.id}/revoke`, {}, true) };
|
|
55
67
|
if (!profilePath) throw new Error('Use a separate private --profile PATH for each agent.');
|
|
56
68
|
const profile = readProfile(profilePath);
|
|
@@ -83,19 +95,25 @@ export async function agentCli(args) {
|
|
|
83
95
|
if (args[i] === '--json') continue;
|
|
84
96
|
if (args[i].startsWith('--')) {
|
|
85
97
|
const key = args[i].slice(2);
|
|
86
|
-
if (!['profile', 'name','scope','file','id','provider','account','workspace','shared','authorized'].includes(key) || !args[i+1] || args[i+1].startsWith('--')) throw new Error('Unknown or incomplete agent option.');
|
|
98
|
+
if (!['profile', 'name','capabilities','scope','file','id','provider','account','workspace','shared','authorized','set'].includes(key) || !args[i+1] || args[i+1].startsWith('--')) throw new Error('Unknown or incomplete agent option.');
|
|
87
99
|
flags[key] = args[++i];
|
|
88
100
|
} else positions.push(args[i]);
|
|
89
101
|
}
|
|
90
102
|
let name = `agent_${positions[1]}`;
|
|
91
|
-
let input = positions[1] === 'permission' ? {id:positions[2],scope:flags.scope} : positions[1] === 'connect' ? { name: flags.name } : positions[1] === 'revoke' ? { id: positions[2] } : {};
|
|
103
|
+
let input = positions[1] === 'permission' ? {id:positions[2],scope:flags.scope} : positions[1] === 'connect' ? { name: flags.name, ...(flags.capabilities ? { capabilities: flags.capabilities.split(',').map(key => key.trim()).filter(Boolean) } : {}) } : positions[1] === 'revoke' ? { id: positions[2] } : {};
|
|
92
104
|
if(positions[1]==='accounts') {
|
|
93
105
|
if(positions[2]==='show') {name=flags.id ? 'agent_account_authorizations_get' : 'agent_account_authorizations_show';input=flags.id ? {id:flags.id} : {};}
|
|
94
106
|
else if(positions[2]==='authorize' && ['true','false'].includes(flags.authorized)) {name='agent_account_authorization_set';input={id:flags.id,provider:flags.provider,accountRef:flags.account,authorized:flags.authorized==='true'};}
|
|
95
107
|
else throw new Error('Use agent accounts show [--id ID] or authorize --id ID --provider PROVIDER --account REF --authorized true|false.');
|
|
96
108
|
}
|
|
109
|
+
if (positions[1] === 'inventory') {
|
|
110
|
+
if (positions[2] === 'report' && flags.file && !flags.id) {name='agent_inventory_report';input=readJsonFile(flags.file,32768,'Inventory file');}
|
|
111
|
+
else if (positions[2] === 'show' && !flags.file) {name=flags.id ? 'agent_inventory_get' : 'agent_inventory_show';input=flags.id ? {id:flags.id} : {};}
|
|
112
|
+
else throw new Error('Use agent inventory report --file PATH or show [--id ID].');
|
|
113
|
+
}
|
|
97
114
|
if (positions[1] === 'capabilities') {
|
|
98
|
-
if (
|
|
115
|
+
if (flags.set !== undefined && positions[2]) {name='agent_capabilities_set';input={id:positions[2],capabilities:flags.set.split(',').map(key => key.trim()).filter(Boolean)};}
|
|
116
|
+
else if (positions[2] === 'sharing' && flags.id) {name='agent_capability_sharing_get';input={id:flags.id};}
|
|
99
117
|
else if (positions[2] === 'share' && flags.id && ['true','false'].includes(flags.shared)) {name='agent_capability_sharing_set';input={id:flags.id,provider:flags.provider,accountRef:flags.account,workspaceId:flags.workspace,shared:flags.shared==='true'};}
|
|
100
118
|
else if (positions[2] === 'report' && flags.file && !flags.id) {name='agent_capabilities_report';input=readCapabilityFile(flags.file);}
|
|
101
119
|
else if (positions[2] === 'show' && !flags.file) {name=flags.id ? 'agent_capabilities_get' : 'agent_capabilities_show';input=flags.id ? {id:flags.id} : {};}
|
|
@@ -13,10 +13,12 @@ const base=a=>`/workspaces/${a.workspaceId}`,query=(a,skip=[])=>new URLSearchPar
|
|
|
13
13
|
export const approachOperations={
|
|
14
14
|
approach_list:{schema:z.object({...scope,cursor:uuid.optional(),limit:z.number().int().min(1).max(100).optional()}).strict(),method:'GET',path:a=>`${base(a)}/approaches?${query(a)}`,scoped:true,description:'List immutable versioned approaches in the current workspace. Free CRM read.'},
|
|
15
15
|
approach_get:{schema:z.object({...scope,id:uuid,revision:z.number().int().positive().optional()}).strict(),method:'GET',path:a=>`${base(a)}/approaches/${a.id}?${query(a,['id'])}`,scoped:true,description:'Read an approach version: stage plays, step waits, completion, goal, voice and agent rules. Instructions are executed by the external agent, never by a hosted sequence runner.'},
|
|
16
|
-
approach_save:{schema:z.object({...scope,id:uuid.optional(),requestId:uuid,expectedRevision:z.number().int().nonnegative(),fields}).strict(),method:'POST',path:a=>`${base(a)}/approaches`,body:({workspaceId,...a})=>a,scoped:true,description:'Create or revise an approach with optimistic version and idempotent request. Does not change existing
|
|
16
|
+
approach_save:{schema:z.object({...scope,id:uuid.optional(),requestId:uuid,expectedRevision:z.number().int().nonnegative(),fields}).strict(),method:'POST',path:a=>`${base(a)}/approaches`,body:({workspaceId,...a})=>a,scoped:true,description:'Create or revise an approach with optimistic version and idempotent request. Does not change existing campaign or participation pins.'},
|
|
17
|
+
workspace_pipeline_get:{schema:z.object({...scope,campaignId:uuid.optional(),pinId:uuid.optional()}).strict(),method:'GET',path:a=>`${base(a)}/pipeline?${query(a)}`,scoped:true,description:'Read the pipeline board: stages of one approach version, the people currently in each stage, and counts of people closed out through each off-ramp. Free CRM read.'},
|
|
17
18
|
approach_progress_get:{schema:z.object({...scope,participationId:uuid}).strict(),method:'GET',path:a=>`${base(a)}/participations/${a.participationId}/progress`,scoped:true,description:'Read the participation’s admitted approach pin and recorded stage/outcome history.'},
|
|
18
19
|
approach_progress_record:{schema:z.object({...scope,participationId:uuid,expectedVersion:z.number().int().nonnegative(),requestId:uuid,stageKey:uuid.optional(),offRampKey:uuid.optional(),finishStage:z.literal(true).optional(),reason:z.string().trim().min(1).max(2000),evidence:z.array(z.object({kind:z.enum(['observation','conversation']),id:uuid}).strict()).max(20)}).strict(),method:'POST',path:a=>`${base(a)}/participations/${a.participationId}/progress`,body:({workspaceId,participationId,...a})=>a,scoped:true,description:'Report a stage, off-ramp, or finishStage:true after externally completing the current play and waits. Finish resolves next/stay/off-ramp from the admitted version. Stored evidence required for agents; human-review and backward-move rules apply. Assigned owner/Admin only. Goal ends prospecting; post-goal forward reporting never reactivates. No send or provider call.'},
|
|
19
20
|
approach_progress_suggest:{schema:z.object({...scope,participationId:uuid,expectedVersion:z.number().int().nonnegative(),requestId:uuid,stageKey:uuid.optional(),offRampKey:uuid.optional(),finishStage:z.literal(true).optional(),reason:z.string().trim().min(1).max(2000),evidence:z.array(z.object({kind:z.enum(['observation','conversation']),id:uuid}).strict()).max(20)}).strict(),method:'POST',path:a=>`${base(a)}/participations/${a.participationId}/suggestions`,body:({workspaceId,participationId,...a})=>a,scoped:true,description:'Suggest a pinned stage move, off-ramp or play completion for human review in Operator. Stored evidence required. Does not change progress or send anything. Assigned owner/Admin; current human approval revalidates progress.'},
|
|
21
|
+
approach_suggestion_decide:{schema:z.object({...scope,id:uuid,requestId:uuid,decision:z.enum(['approve','dismiss'])}).strict(),method:'POST',path:a=>`${base(a)}/approach-suggestions/${a.id}/decision`,body:({workspaceId,id,...a})=>a,scoped:true,description:'Approve or dismiss a pending stage suggestion. People only: the current owner or an Admin. Approving moves the participation to the suggested stage.'},
|
|
20
22
|
approach_suggestions_list:{schema:z.object({...scope,cursor:uuid.optional(),limit:z.number().int().min(1).max(100).optional()}).strict(),method:'GET',path:a=>`${base(a)}/approach-suggestions?${query(a)}`,scoped:true,description:'List pending stage suggestions visible to the current owner or Admin. Humans approve or dismiss in Operator; agents cannot approve.'},
|
|
21
|
-
workspace_analytics_get:{schema:z.object({...scope,
|
|
23
|
+
workspace_analytics_get:{schema:z.object({...scope,campaignId:uuid.optional(),ownerUserId:uuid.optional(),offerId:uuid.optional(),audienceId:uuid.optional(),tag:z.string().min(1).max(64).optional(),pinId:uuid.optional(),from:z.string().datetime(),to:z.string().datetime()}).strict(),method:'GET',path:a=>`${base(a)}/analytics?${query(a)}`,scoped:true,description:'Read UTC-period distinct outcomes and explicit stage events. Missing activity or cost attribution is not tracked, never fabricated zero. Optional filters: campaignId, ownerUserId (campaign owner), offerId, audienceId, tag (campaign tag); list the values with workspace_analytics_filters. Maximum 366 days; narrow large scopes.'}
|
|
22
24
|
};
|
|
@@ -18,7 +18,7 @@ const location = z
|
|
|
18
18
|
.strict();
|
|
19
19
|
const criteria = z
|
|
20
20
|
.object({
|
|
21
|
-
|
|
21
|
+
campaignIds: z.array(uuid).max(50).optional(),
|
|
22
22
|
companyLocations: z.array(location).max(50).optional(),
|
|
23
23
|
personLocations: z.array(location).max(50).optional(),
|
|
24
24
|
personTitles: z.array(z.string()).max(50).optional(),
|
|
@@ -38,7 +38,7 @@ const person = (a) => `${root(a)}/prospects/${a.prospectId}`;
|
|
|
38
38
|
const omit = (a, keys) =>
|
|
39
39
|
Object.fromEntries(Object.entries(a).filter(([k]) => !keys.includes(k)));
|
|
40
40
|
const body = (a) => omit(a, ["workspaceId", "prospectId"]);
|
|
41
|
-
function qs(a, keys = ["workspaceId", "prospectId", "
|
|
41
|
+
function qs(a, keys = ["workspaceId", "prospectId", "campaignId"]) {
|
|
42
42
|
const values = {};
|
|
43
43
|
for (const [k, v] of Object.entries(omit(a, keys))) {
|
|
44
44
|
if (v === undefined) continue;
|
|
@@ -56,7 +56,7 @@ export const assignmentOperations = {
|
|
|
56
56
|
...workspace,
|
|
57
57
|
assignedToUserId: uuid.optional(),
|
|
58
58
|
unassigned: z.boolean().optional(),
|
|
59
|
-
|
|
59
|
+
campaignId: uuid.optional(),
|
|
60
60
|
search: z.string().optional(),
|
|
61
61
|
cursor: uuid.optional(),
|
|
62
62
|
limit: z.number().int().min(1).max(100).optional(),
|
|
@@ -81,17 +81,17 @@ export const assignmentOperations = {
|
|
|
81
81
|
path: (a) => person(a) + "/assignment?" + qs(a),
|
|
82
82
|
scoped: true,
|
|
83
83
|
},
|
|
84
|
-
|
|
84
|
+
workspace_prospect_campaign_fit: {
|
|
85
85
|
schema: z
|
|
86
86
|
.object({
|
|
87
87
|
...workspace,
|
|
88
88
|
prospectId: uuid,
|
|
89
|
-
|
|
89
|
+
campaignId: uuid,
|
|
90
90
|
overrides: page.optional(),
|
|
91
91
|
})
|
|
92
92
|
.strict(),
|
|
93
93
|
method: "GET",
|
|
94
|
-
path: (a) => person(a) + `/
|
|
94
|
+
path: (a) => person(a) + `/campaigns/${a.campaignId}/fit?` + qs(a),
|
|
95
95
|
scoped: true,
|
|
96
96
|
},
|
|
97
97
|
workspace_prospect_assignment_set: {
|
|
@@ -133,7 +133,7 @@ export const assignmentOperations = {
|
|
|
133
133
|
.object({
|
|
134
134
|
...request,
|
|
135
135
|
prospectId: uuid,
|
|
136
|
-
|
|
136
|
+
campaignId: uuid.optional(),
|
|
137
137
|
expectedAssignmentVersion: version,
|
|
138
138
|
rulesVersion: version,
|
|
139
139
|
outcomes: z
|
|
@@ -160,8 +160,8 @@ export const assignmentOperations = {
|
|
|
160
160
|
.object({
|
|
161
161
|
...request,
|
|
162
162
|
prospectId: uuid,
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
campaignId: uuid,
|
|
164
|
+
campaignRevision: z.number().int().positive(),
|
|
165
165
|
expectedOverrideVersion: version,
|
|
166
166
|
action: z.enum(["set", "clear"]),
|
|
167
167
|
value: z.enum(["high", "moderate", "low", "no_fit"]).optional(),
|
|
@@ -179,9 +179,9 @@ export const assignmentOperations = {
|
|
|
179
179
|
...request,
|
|
180
180
|
prospectId: uuid,
|
|
181
181
|
expectedSourceParticipationId: uuid,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
sourceCampaignId: uuid,
|
|
183
|
+
destinationCampaignId: uuid,
|
|
184
|
+
destinationCampaignRevision: z.number().int().positive(),
|
|
185
185
|
destinationQualificationReceiptId: uuid,
|
|
186
186
|
expectedAssignmentVersion: version,
|
|
187
187
|
reason: z.string(),
|
package/cli/assignment.mjs
CHANGED
|
@@ -25,7 +25,7 @@ export async function assignmentCli(args) {
|
|
|
25
25
|
"file",
|
|
26
26
|
"request-id",
|
|
27
27
|
"prospect-id",
|
|
28
|
-
"
|
|
28
|
+
"campaign-id",
|
|
29
29
|
"assigned-to-user-id",
|
|
30
30
|
"unassigned",
|
|
31
31
|
"search",
|
|
@@ -54,8 +54,8 @@ export async function assignmentCli(args) {
|
|
|
54
54
|
"workspace assignments list": "workspace_prospect_assignments",
|
|
55
55
|
"workspace prospect assignment get": "workspace_prospect_assignment",
|
|
56
56
|
"workspace prospect assignment set": "workspace_prospect_assignment_set",
|
|
57
|
-
"workspace prospect
|
|
58
|
-
"
|
|
57
|
+
"workspace prospect campaign-fit get":
|
|
58
|
+
"workspace_prospect_campaign_fit",
|
|
59
59
|
"workspace routing-rules get": "workspace_routing_rules_get",
|
|
60
60
|
"workspace routing-rules replace": "workspace_routing_rules_replace",
|
|
61
61
|
"workspace routing apply": "workspace_routing_apply",
|
|
@@ -68,12 +68,12 @@ export async function assignmentCli(args) {
|
|
|
68
68
|
const name = names[command];
|
|
69
69
|
if (!name)
|
|
70
70
|
throw new Error(
|
|
71
|
-
"Unknown assignment command. Use workspace assignments list, prospect assignment get/set, routing-rules get/replace, routing apply, prospect
|
|
71
|
+
"Unknown assignment command. Use workspace assignments list, prospect assignment get/set, routing-rules get/replace, routing apply, prospect campaign-fit get, prospect fit-override set/clear, prospect move, member remove, or leave. Mutations require --file INPUT.json.",
|
|
72
72
|
);
|
|
73
73
|
const read = [
|
|
74
74
|
"workspace_prospect_assignments",
|
|
75
75
|
"workspace_prospect_assignment",
|
|
76
|
-
"
|
|
76
|
+
"workspace_prospect_campaign_fit",
|
|
77
77
|
"workspace_routing_rules_get",
|
|
78
78
|
].includes(name);
|
|
79
79
|
let input = read ? {} : readJsonFile(flags.file, 65536, "Assignment file");
|
|
@@ -81,7 +81,7 @@ export async function assignmentCli(args) {
|
|
|
81
81
|
["workspace", "workspaceId"],
|
|
82
82
|
["request-id", "requestId"],
|
|
83
83
|
["prospect-id", "prospectId"],
|
|
84
|
-
["
|
|
84
|
+
["campaign-id", "campaignId"],
|
|
85
85
|
["assigned-to-user-id", "assignedToUserId"],
|
|
86
86
|
["search", "search"],
|
|
87
87
|
["cursor", "cursor"],
|
|
@@ -13,4 +13,9 @@ export const audienceOperations={
|
|
|
13
13
|
audience_update:{schema:z.object({...locator,expectedRevision:z.number().int(),fields}).strict(),method:'POST',path:a=>path(a)+`/${a.audienceId}/revisions`,body:a=>({expectedRevision:a.expectedRevision,fields:a.fields}),scoped:true},
|
|
14
14
|
audience_archive:{schema:z.object(locator).strict(),method:'POST',path:a=>path(a)+`/${a.audienceId}/archive`,scoped:true},
|
|
15
15
|
audience_restore:{schema:z.object(locator).strict(),method:'POST',path:a=>path(a)+`/${a.audienceId}/restore`,scoped:true},
|
|
16
|
+
audience_summary:{schema:z.object({...workspace,audienceId:uuid.optional()}).strict(),method:'GET',path:a=>`/workspaces/${a.workspaceId}/audience-summary`+(a.audienceId?`?audienceId=${a.audienceId}`:''),scoped:true,description:'Counts of active and archived Audiences, the campaigns using each (with people, engaged and at-goal counts), and people found per Audience (recent means admitted in the last 7 days).'},
|
|
17
|
+
audience_people:{schema:z.object(locator).strict(),method:'GET',path:a=>path(a)+`/${a.audienceId}/people`,scoped:true,description:'People found by any campaign on this Audience, newest first, at most 100, with their latest participation.'},
|
|
18
|
+
audience_companies:{schema:z.object(locator).strict(),method:'GET',path:a=>path(a)+`/${a.audienceId}/companies`,scoped:true,description:'Companies where this Audience has found people, most people first, at most 100; withoutCompany counts people with no company.'},
|
|
19
|
+
audience_fit_check_improve:{schema:z.object({...workspace,text:z.string().trim().min(1).max(5000)}).strict(),method:'POST',path:a=>`/workspaces/${a.workspaceId}/audience-fit-checks`,body:a=>({text:a.text}),scoped:true,description:'Review a smart rule (gut check) and suggest a clearer rewrite a reviewer can judge from a profile. Returns whether it is usable, what counts as a match and a contradiction, and the rewrite. Saves nothing.'},
|
|
20
|
+
audience_prospect_test:{schema:z.object({...locator,url:z.string(),requestId:uuid,revision:z.number().int().positive().optional()}).strict(),method:'POST',path:a=>path(a)+`/${a.audienceId}/prospect-tests`,body:a=>({url:a.url,requestId:a.requestId,...(a.revision?{revision:a.revision}:{})}),scoped:true,description:'Check one LinkedIn person URL against the Audience rules and name the deciding rule. Read-only: never admits the person. Saved profiles are reused free; otherwise the person and company fetches spend hosted credits. Generate requestId once and reuse it after an interruption so a retry never pays twice.'},
|
|
16
21
|
};
|
package/cli/audiences.mjs
CHANGED
|
@@ -3,11 +3,11 @@ import {readJsonFile} from './json-file.mjs';
|
|
|
3
3
|
export async function audienceCli(args){
|
|
4
4
|
const position=[],flags={};for(let i=0;i<args.length;i++){
|
|
5
5
|
if(!args[i].startsWith('--')){position.push(args[i]);continue}
|
|
6
|
-
const key=args[i].slice(2);if(!['profile','workspace','file','request-id','expected-revision','revision','archived','limit','after','q','kind'].includes(key)||!args[i+1]||args[i+1].startsWith('--')||Object.hasOwn(flags,key))throw new Error('Unknown or incomplete audience option.');
|
|
6
|
+
const key=args[i].slice(2);if(!['profile','workspace','file','request-id','expected-revision','revision','archived','limit','after','q','kind','url','audience','text'].includes(key)||!args[i+1]||args[i+1].startsWith('--')||Object.hasOwn(flags,key))throw new Error('Unknown or incomplete audience option.');
|
|
7
7
|
flags[key]=args[++i];
|
|
8
8
|
}
|
|
9
|
-
const command=position[1],name={options:'audience_options',list:'audience_list',show:'audience_get',create:'audience_create',update:'audience_update',archive:'audience_archive',restore:'audience_restore'}[command];
|
|
10
|
-
if(!name||position.length!==(['list','create','options'].includes(command)?2:3))throw new Error('Use audience options --kind KIND [--q TEXT]|list|show ID|create|update ID|archive ID|restore ID.');
|
|
9
|
+
const command=position[1],name={options:'audience_options',list:'audience_list',show:'audience_get',create:'audience_create',update:'audience_update',archive:'audience_archive',restore:'audience_restore',summary:'audience_summary',people:'audience_people',companies:'audience_companies',test:'audience_prospect_test',improve:'audience_fit_check_improve'}[command];
|
|
10
|
+
if(!name||position.length!==(['list','create','options','summary','improve'].includes(command)?2:3))throw new Error('Use audience options --kind KIND [--q TEXT]|list|show ID|create|update ID|archive ID|restore ID|summary|people ID|companies ID|test ID --url URL --request-id UUID [--revision N]|improve --text RULE.');
|
|
11
11
|
const input={workspaceId:flags.workspace};
|
|
12
12
|
if(position[2])input.audienceId=position[2];
|
|
13
13
|
if(command==='options'){input.kind=flags.kind;if(flags.q!==undefined)input.q=flags.q;}
|
|
@@ -18,7 +18,10 @@ export async function audienceCli(args){
|
|
|
18
18
|
if(flags.after)input.after=flags.after;
|
|
19
19
|
if(flags.q!==undefined)input.q=flags.q;
|
|
20
20
|
}
|
|
21
|
-
if(command==='
|
|
21
|
+
if(command==='summary'&&flags.audience!==undefined)input.audienceId=flags.audience;
|
|
22
|
+
if(command==='test'){if(!flags.url)throw new Error('Supply --url with a LinkedIn profile URL.');input.url=flags.url;input.requestId=flags['request-id'];}
|
|
23
|
+
if(command==='improve'){if(!flags.text)throw new Error('Supply the smart rule with --text.');input.text=flags.text;}
|
|
24
|
+
if(['show','test'].includes(command)&&flags.revision!==undefined)input.revision=Number(flags.revision);
|
|
22
25
|
if(['create','update'].includes(command)){if(!flags.file)throw new Error('Supply --file PATH containing Audience fields.');input.fields=readJsonFile(flags.file,65536,'Audience file');}
|
|
23
26
|
if(command==='create')input.requestId=flags['request-id'];
|
|
24
27
|
if(command==='update')input.expectedRevision=Number(flags['expected-revision']);
|
package/cli/audienti.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { isAssignmentCommand, assignmentCli } from './assignment.mjs';
|
|
|
3
3
|
// The CLI is an HTTP client. It has no database access or separate business rules.
|
|
4
4
|
const args = process.argv.slice(2);
|
|
5
5
|
const usage = `Profiles: audienti profiles list|search|get ID | match evaluate|apply SOURCE CANDIDATE | history RESOLUTION (saved workspace records)
|
|
6
|
-
Skills: audienti skills list|show
|
|
6
|
+
Skills: audienti skills list [--compact]|show SKILL|read SKILL RESOURCE|helper NAME --file INPUT.json|- [--json]
|
|
7
7
|
Tools: audienti tools list|describe|decide|runs|balance [--help]
|
|
8
8
|
Sender: audienti sender accounts list|get|alias|register|bind|unbind|state|automation|activity report [--profile PATH]
|
|
9
9
|
Prospecting: audienti prospecting settings show|save|accounts|account-show|account-save|history [--profile PATH]
|
|
@@ -12,7 +12,7 @@ Usage: audienti whoami [--json]
|
|
|
12
12
|
audienti workspace prospect assignment get/set --prospect-id ID [--file INPUT.json]
|
|
13
13
|
audienti workspace routing-rules get/replace [--file INPUT.json]
|
|
14
14
|
audienti workspace routing apply --file INPUT.json
|
|
15
|
-
audienti workspace prospect
|
|
15
|
+
audienti workspace prospect campaign-fit get --prospect-id ID --campaign-id ID
|
|
16
16
|
audienti workspace prospect fit-override set/clear --file INPUT.json
|
|
17
17
|
audienti workspace prospect move --file INPUT.json
|
|
18
18
|
audienti workspace member remove --file INPUT.json
|
|
@@ -23,10 +23,13 @@ Required environment:
|
|
|
23
23
|
AUDIENTI_API_TOKEN Account API token (create it from your Account page)
|
|
24
24
|
|
|
25
25
|
Use HTTPS for remote hosts. Local HTTP is supported on loopback only.
|
|
26
|
-
Agent commands: agent connect --name NAME|poll|cancel|list|revoke ID --profile PATH
|
|
26
|
+
Agent commands: agent connect --name NAME [--capabilities find,enrich,listen,approve]|poll|cancel|list|revoke ID --profile PATH
|
|
27
27
|
Owner permission change: agent permission ID --scope workspace:read|workspace:access --profile USER_PROFILE
|
|
28
|
+
Owner capability change: agent capabilities ID --set find,enrich,listen,approve --profile USER_PROFILE (agents are refused outside this set)
|
|
28
29
|
Agent capabilities: agent capabilities report --file REPORT.json --profile AGENT_PROFILE
|
|
29
30
|
Read capabilities: agent capabilities show [--id AGENT_ID] --profile PROFILE
|
|
31
|
+
Getting started: skills show getting-started, then agent inventory report --file INVENTORY.json --profile AGENT_PROFILE
|
|
32
|
+
Read tool inventory: agent inventory show [--id AGENT_ID] --profile PROFILE
|
|
30
33
|
Account authorization: agent accounts show [--id AGENT_ID] --profile PROFILE
|
|
31
34
|
Authorize/revoke: agent accounts authorize --id AGENT_ID --provider email|linkedin --account REF --authorized true|false --profile OWNER_PROFILE
|
|
32
35
|
Sharing: agent capabilities sharing --id AGENT_ID --profile OWNER_PROFILE
|
|
@@ -35,25 +38,53 @@ Team member: workspace member USER_ID [--workspace ID] --profile PROFILE
|
|
|
35
38
|
Work execution: work execution claim|start|get PLAN --action ID; release|lookup --claim ID; report --attempt ID
|
|
36
39
|
Work feedback: work changes request|list|get|respond|withdraw PLAN --workspace ID
|
|
37
40
|
Work: work plan save|list|show|approve|withdraw-approval|cancel|eligibility
|
|
41
|
+
Activity: work activity --workspace ID [--search TEXT] [--cursor-at ISO --cursor-id ID]
|
|
42
|
+
Work list: work list --workspace ID [--order oldest|priority] [--queue review|blocked|activity]
|
|
38
43
|
Work writes: --file INPUT.json; human review needs a human workspace:access token
|
|
39
44
|
Audiences: audience options --kind industries|locations|sizes|types|seniorities|titles|functions|industry_groups [--q TEXT]
|
|
40
45
|
audience list|show ID|create|update ID|archive ID|restore ID
|
|
46
|
+
audience summary [--audience ID]|people ID|companies ID
|
|
47
|
+
audience test ID --url LINKEDIN_URL --request-id UUID (spends hosted credits unless the profile is saved)
|
|
48
|
+
Files: file upload PATH --type MIME|list|prepare|register|download|delete ID (personal files, human token only)
|
|
49
|
+
Queue: audienti queue [list]|next|claim ID|done ID [--note TEXT]|skip ID --note TEXT|release ID (start here after you connect)
|
|
50
|
+
Questions: audienti ask "QUESTION" [--context TEXT] [--option TEXT]... [--campaign ID] [--task ID] (questions for your owner go to Operator, not chat)
|
|
51
|
+
audienti questions [list] [--state open|answered|withdrawn|all] | show ID | withdraw ID | answer ID --answer TEXT
|
|
52
|
+
Rooms: audienti rooms directory|inbox|ack|send|thread|threads|create|list|read|retire|restore|redact|resume|pause|profile|hook|watch|wait (audienti rooms help)
|
|
41
53
|
Offers: offer list|show ID|create|update ID|archive ID|restore ID
|
|
42
54
|
Offer options: --file FIELDS.json --request-id UUID --expected-revision N --revision N --archived true|false
|
|
43
55
|
audienti prospects replies set|get|list PERSON --workspace ID [--evidence EVIDENCE] [--file INPUT.json]
|
|
44
56
|
Account intake: prospects intake --workspace ID --file INPUT.json
|
|
45
57
|
prospects conversation append|list PERSON --workspace ID
|
|
46
58
|
Discovery: prospects list|get ID|observation create|citation create|evidence create
|
|
47
|
-
prospects contacts list|record PERSON --workspace ID
|
|
59
|
+
prospects contacts list|record PERSON [--file INPUT.json] --workspace ID
|
|
48
60
|
prospects contacts get|retire PERSON CONTACT --workspace ID
|
|
49
61
|
prospects suppression get|set ID --workspace ID
|
|
50
|
-
|
|
62
|
+
guardrails show|pause|resume --workspace ID
|
|
63
|
+
dnc list|add VALUE|remove VALUE --workspace ID
|
|
64
|
+
dnc import --file LIST.csv --workspace ID (at most 5,000 rows; reports added, skipped and invalid rows)
|
|
65
|
+
dnc export [--output FILE.csv] --workspace ID
|
|
66
|
+
budgets show --workspace ID
|
|
67
|
+
budgets alerts --workspace ID [--limit N]
|
|
68
|
+
usage breakdown [--workspace ID] [--since month|7d|30d|90d] [--by tool|provider|model|caller|day] [--format json|csv]
|
|
69
|
+
audit list [--workspace ID] [--since 7d] [--actor agent|person] [--category NAME] [--format json|csv]
|
|
70
|
+
budgets set --workspace ID [--agent ID --cap N|none] [--new-agent-cap N|none] [--at-limit pause|ask] [--alert80 on|off] (alert80 covers Operator and email at 80%)
|
|
71
|
+
Qualification: campaigns qualification submit --file INPUT.json
|
|
51
72
|
Approaches: approach list|show ID|save --file REQUEST.json|progress ID|record --file REQUEST.json
|
|
52
|
-
Analytics: analytics show --from ISO --to ISO [--
|
|
73
|
+
Analytics: analytics show --from ISO --to ISO [--campaign ID] [--approach-version ID] [--format json|csv]
|
|
74
|
+
analytics outbound [--workspace ID] [--days N | --from ISO --to ISO] [--by sender|action|day] [--format json|csv]
|
|
75
|
+
analytics activity [--workspace ID] [--days N | --from ISO --to ISO] [--format json|csv]
|
|
76
|
+
analytics cohorts [--workspace ID] [--stage goal|KEY] [--cohorts N] [--horizon N] [filters] [--format json|csv]
|
|
77
|
+
analytics aging [--workspace ID] [--threshold-days N] [filters] [--format json|csv]
|
|
78
|
+
analytics filters [--workspace ID] (lists campaign, owner, offer, audience and tag values)
|
|
79
|
+
(filters: --campaign ID --owner ID --offer ID --audience ID --tag TAG; also accepted by analytics show)
|
|
80
|
+
Pipeline: pipeline show [--campaign ID] [--approach-version ID]
|
|
53
81
|
Connections: connections list|register --file REQUEST.json|report --file REQUEST.json
|
|
54
82
|
Discovery writes use --file INPUT.json, --workspace ID and stable --request-id UUID
|
|
55
|
-
Workspace commands: workspace init|list|create|show|invite|invitations|use
|
|
56
|
-
|
|
83
|
+
Workspace commands: workspace init|list|create|show|search|invite|invitations|use
|
|
84
|
+
Notifications: workspace notifications --workspace ID [--unread-only true] [--cursor FILE.json]
|
|
85
|
+
Read state: workspace notification-read --workspace ID --key KEY --read true|false [--through OBSERVED_AT]
|
|
86
|
+
Search: workspace search --workspace ID --query TEXT [--people-only true] [--recent FILE.json]
|
|
87
|
+
Invitation commands: workspace invitation show|accept|decline|retry|cancel ID
|
|
57
88
|
Options: --workspace ID --name NAME --request-id UUID --email EMAIL --generation N
|
|
58
89
|
Optional private profile: --profile PATH (outside this repository)
|
|
59
90
|
Save an environment token: profile save --scope account:read|workspace:access|agents:manage --profile PATH
|
|
@@ -61,22 +92,56 @@ Start local MCP stdio server: mcp
|
|
|
61
92
|
--json writes the API response envelope.
|
|
62
93
|
`;
|
|
63
94
|
|
|
95
|
+
// Example inputs for the commands that save prospects, so `--help` shows a working shape.
|
|
96
|
+
const examples = {
|
|
97
|
+
'prospects observation create': { requestId: 'UUID', kind: 'person', source: { provider: 'web', url: 'https://example.com/team/jane' }, fields: { name: 'Jane Doe', title: 'VP Sales', imageUrl: 'https://media.licdn.com/dms/image/PHOTO_ID' }, provenance: { url: 'https://example.com/team/jane' }, observedAt: '2026-01-01T00:00:00Z', skill: { id: 'signal-prospect-research', version: '1' } },
|
|
98
|
+
'prospects citation create': { requestId: 'UUID', source: { kind: 'prospect', id: 'PERSON_PROSPECT_ID' }, target: { kind: 'prospect', id: 'COMPANY_PROSPECT_ID' }, edgeType: 'works_at', roles: [{ roleId: 'role-1', title: 'VP Sales', current: true, observedAt: '2026-01-01T00:00:00Z' }] },
|
|
99
|
+
'prospects evidence create': { requestId: 'UUID', subject: { kind: 'person', prospectId: 'PROSPECT_ID' }, campaignId: 'CAMPAIGN_ID', campaignRevision: 1, kind: 'interpretation', data: { conclusion: 'Leads a 40-person sales team', reason: 'Team page lists her as VP Sales' }, sourceUrl: 'https://example.com/team', observedAt: '2026-01-01T00:00:00Z', skill: { id: 'signal-prospect-research', version: '1' } },
|
|
100
|
+
'prospects contacts record': { kind: 'email|linkedin|mobile', value: 'jane@example.com, or +15551234567 for mobile', reason: 'Published on https://example.com/contact', requestId: 'UUID', provenance: { observationId: 'OBSERVATION_ID' } },
|
|
101
|
+
'campaigns qualification submit': { requestId: 'UUID', campaignId: 'CAMPAIGN_ID', campaignRevision: 1, prospectId: 'PERSON_PROSPECT_ID', companyProspectId: 'COMPANY_PROSPECT_ID', companyIcp: { status: 'pass|fail|unverified', evidenceIds: ['COMPANY_EVIDENCE_ID'] }, personIcp: { status: 'pass|fail|unverified', evidenceIds: ['PERSON_EVIDENCE_ID'] }, signal: { status: 'match|no_match|unverified|not_required', evidenceIds: [] }, fit: { status: 'high|moderate|low|no_fit|unscored', evidenceIds: ['PERSON_EVIDENCE_ID'], reason: 'why' }, skill: { id: 'signal-prospect-research', version: '1' } },
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
function commandHelp() {
|
|
105
|
+
const words = args.slice(0, args.findIndex(value => value.startsWith('-'))).filter(Boolean);
|
|
106
|
+
const lines = usage.split('\n');
|
|
107
|
+
let shown = [];
|
|
108
|
+
for (let n = Math.min(words.length, 2); n > 0 && shown.length === 0; n--) {
|
|
109
|
+
const prefix = words.slice(0, n).join(' ');
|
|
110
|
+
shown = lines.filter(line => line.includes(prefix));
|
|
111
|
+
}
|
|
112
|
+
console.log(shown.length ? shown.map(line => line.trim()).join('\n') : usage);
|
|
113
|
+
const example = Object.entries(examples).find(([command]) => words.join(' ').startsWith(command));
|
|
114
|
+
if (example) console.log(`\nExample --file INPUT.json for ${example[0]}:\n${JSON.stringify(example[1], null, 1)}\nSteps and rules: the "Save prospects and contacts" section of llms.txt.`);
|
|
115
|
+
}
|
|
116
|
+
|
|
64
117
|
async function main() {
|
|
118
|
+
if (args.length > 1 && args.slice(1).some(value => value === '--help' || value === '-h') && !['tools', 'skills'].includes(args[0])) { commandHelp(); return; }
|
|
65
119
|
if(args[0]==='prospects'&&['intake','conversation'].includes(args[1])){const {accountIntakeCli}=await import('./account-intake.mjs');await accountIntakeCli(args);return;}
|
|
66
120
|
if(args[0]==='sender'){const {senderAccountsCli}=await import('./sender-accounts.mjs');await senderAccountsCli(args);return;}
|
|
67
121
|
if(args[0]==='prospecting'){const {prospectingSettingsCli}=await import('./prospecting-settings.mjs');await prospectingSettingsCli(args);return;}
|
|
68
122
|
if(args[0]==='prospects'&&args[1]==='replies'){const {accountRepliesCli}=await import('./account-replies.mjs');await accountRepliesCli(args);return;}
|
|
69
123
|
if(args[0]==='prospects'&&args[1]==='contacts'){const {recipientsCli}=await import('./recipients.mjs');await recipientsCli(args);return;}
|
|
124
|
+
if(args[0]==='usage'){const {usageCli}=await import('./usage.mjs');await usageCli(args);return;}
|
|
125
|
+
if(args[0]==='audit'){const {auditCli}=await import('./audit.mjs');await auditCli(args);return;}
|
|
126
|
+
if(args[0]==='budgets'){const {budgetsCli}=await import('./budgets.mjs');await budgetsCli(args);return;}
|
|
127
|
+
if(args[0]==='guardrails'||args[0]==='dnc'){const {guardrailsCli}=await import('./guardrails.mjs');await guardrailsCli(args);return;}
|
|
70
128
|
if(args[0]==='prospects'&&args[1]==='suppression'){const {suppressionCli}=await import('./suppression.mjs');await suppressionCli(args);return;}
|
|
71
129
|
if(args[0]==='tools'){const {toolsCli}=await import('./tools.mjs');await toolsCli(args);return;}
|
|
72
130
|
if(['topics','posts'].includes(args[0])){const {contentCli}=await import('./content.mjs');await contentCli(args);return;}
|
|
73
131
|
if(args[0]==='work'&&args[1]==='execution'){const {workExecutionCli}=await import('./work-execution.mjs');await workExecutionCli(args);return;}
|
|
74
132
|
if(args[0]==='work'&&args[1]==='changes'){const {workFeedbackCli}=await import('./work-feedback.mjs');await workFeedbackCli(args);return;}
|
|
75
133
|
if(args[0]==='work'){const {workCli}=await import('./work.mjs');await workCli(args);return;}
|
|
76
|
-
if(['prospects','
|
|
77
|
-
if(['
|
|
78
|
-
if(args[0]==='
|
|
134
|
+
if(['prospects','campaigns'].includes(args[0])){const {discoveryCli}=await import('./discovery.mjs');await discoveryCli(args);return;}
|
|
135
|
+
if(args[0]==='analytics'&&['cohorts','aging','filters'].includes(args[1])){const {cohortCli}=await import('./cohorts.mjs');await cohortCli(args);return;}
|
|
136
|
+
if(args[0]==='analytics'&&['outbound','activity'].includes(args[1])){const {outboundCli}=await import('./outbound.mjs');await outboundCli(args);return;}
|
|
137
|
+
if(['approach','analytics','pipeline','connections'].includes(args[0])){const {progressCli}=await import('./progress.mjs');await progressCli(args);return;}
|
|
138
|
+
if(args[0]==='campaign'){const {campaignCli}=await import('./campaigns.mjs');await campaignCli(args);return;}
|
|
79
139
|
if(args[0]==='audience'){const {audienceCli}=await import('./audiences.mjs');await audienceCli(args);return;}
|
|
140
|
+
if(args[0]==='file'){const {fileCli}=await import('./files.mjs');await fileCli(args);return;}
|
|
141
|
+
if(args[0]==='ask'){const {askCli}=await import('./questions.mjs');await askCli(args);return;}
|
|
142
|
+
if(args[0]==='questions'){const {questionsCli}=await import('./questions.mjs');await questionsCli(args);return;}
|
|
143
|
+
if(args[0]==='queue'){const {queueCli}=await import('./queue.mjs');await queueCli(args);return;}
|
|
144
|
+
if(args[0]==='rooms'){const {roomsCli}=await import('./rooms.mjs');await roomsCli(args);return;}
|
|
80
145
|
if(args[0]==='offer'){const {offerCli}=await import('./offers.mjs');await offerCli(args);return;}
|
|
81
146
|
if (args[0] === 'agent') { const { agentCli } = await import('./agents.mjs'); await agentCli(args); return; }
|
|
82
147
|
if (isAssignmentCommand(args)) { await assignmentCli(args); return; }
|
|
@@ -90,6 +155,7 @@ async function main() {
|
|
|
90
155
|
if (args[0] === 'whoami' && profileIndex > 0 && args[profileIndex + 1]) {
|
|
91
156
|
const { configuration } = await import('./workspaces.mjs');
|
|
92
157
|
profileConfig = configuration(args[profileIndex + 1], 'account:read');
|
|
158
|
+
if (!profileConfig.token) throw new Error('whoami needs a human account token. Agents confirm their connection with: audienti workspace list --profile PATH');
|
|
93
159
|
args.splice(profileIndex, 2);
|
|
94
160
|
}
|
|
95
161
|
if (args[0] !== 'whoami' || args.length > 2 || (args[1] && args[1] !== '--json')) throw new Error('Unknown command or option. Use audienti --help.');
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const query = a => new URLSearchParams(Object.entries(a).filter(([k, v]) => k !== 'workspaceId' && v !== undefined).map(([k, v]) => [k, String(v)]));
|
|
3
|
+
export const auditOperations = {
|
|
4
|
+
workspace_audit_log_list: {
|
|
5
|
+
schema: z.object({
|
|
6
|
+
workspaceId: z.string().uuid().optional(),
|
|
7
|
+
actor: z.enum(['person', 'agent']).optional(),
|
|
8
|
+
category: z.enum(['guardrails', 'budgets', 'approvals', 'suppression', 'members', 'agents']).optional(),
|
|
9
|
+
since: z.string().datetime().optional(),
|
|
10
|
+
until: z.string().datetime().optional(),
|
|
11
|
+
cursor: z.string().max(500).optional(),
|
|
12
|
+
limit: z.number().int().min(1).max(200).optional(),
|
|
13
|
+
}).strict(),
|
|
14
|
+
method: 'GET', path: a => `/workspaces/${a.workspaceId}/audit-events?${query(a)}`, scoped: true,
|
|
15
|
+
description: 'List the read-only workspace audit log: who changed guardrails, budgets, approvals, suppression, members and agent access, newest first. Admin only. Page with cursor.',
|
|
16
|
+
},
|
|
17
|
+
};
|
package/cli/audit.mjs
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { callWorkspace, configuration } from './workspaces.mjs';
|
|
2
|
+
|
|
3
|
+
const UNITS = { m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000 };
|
|
4
|
+
export function sinceToIso(value, now = Date.now()) {
|
|
5
|
+
const match = /^(\d{1,4})([mhdw])$/.exec(value);
|
|
6
|
+
if (match) return new Date(now - Number(match[1]) * UNITS[match[2]]).toISOString();
|
|
7
|
+
const date = new Date(value);
|
|
8
|
+
if (Number.isNaN(date.getTime())) throw Error('--since must look like 7d, 12h or an ISO date.');
|
|
9
|
+
return date.toISOString();
|
|
10
|
+
}
|
|
11
|
+
const cell = value => /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value;
|
|
12
|
+
export function auditCsv(items) {
|
|
13
|
+
const rows = items.map(item => [item.at, item.actor?.kind ?? '', item.actor?.name ?? '', item.category, item.action, item.summary, item.object?.label ?? '', item.object?.href ?? '']);
|
|
14
|
+
return [['when', 'actor_kind', 'actor', 'category', 'action', 'summary', 'object', 'link'], ...rows].map(row => row.map(cell).join(',')).join('\n') + '\n';
|
|
15
|
+
}
|
|
16
|
+
export async function auditCli(args) {
|
|
17
|
+
const flags = {}, positions = [];
|
|
18
|
+
for (let i = 0; i < args.length; i++) {
|
|
19
|
+
const arg = args[i];
|
|
20
|
+
if (arg === '--json') continue;
|
|
21
|
+
if (!arg.startsWith('--')) { positions.push(arg); continue; }
|
|
22
|
+
const key = arg.slice(2);
|
|
23
|
+
if (!['workspace', 'profile', 'since', 'until', 'actor', 'category', 'limit', 'cursor', 'format'].includes(key) || Object.hasOwn(flags, key) || i + 1 >= args.length) throw Error(`Invalid flag --${key}.`);
|
|
24
|
+
flags[key] = args[++i];
|
|
25
|
+
}
|
|
26
|
+
if (positions.length !== 2 || positions[1] !== 'list') throw Error('Use audit list [--workspace ID] [--since 7d] [--actor agent|person] [--format json|csv].');
|
|
27
|
+
if (flags.actor && !['agent', 'person'].includes(flags.actor)) throw Error('--actor must be agent or person.');
|
|
28
|
+
if (flags.format && !['json', 'csv'].includes(flags.format)) throw Error('--format must be json or csv.');
|
|
29
|
+
const input = { workspaceId: flags.workspace };
|
|
30
|
+
if (flags.actor) input.actor = flags.actor;
|
|
31
|
+
if (flags.category) input.category = flags.category;
|
|
32
|
+
if (flags.since) input.since = sinceToIso(flags.since);
|
|
33
|
+
if (flags.until) input.until = sinceToIso(flags.until);
|
|
34
|
+
if (flags.cursor) input.cursor = flags.cursor;
|
|
35
|
+
if (flags.limit) { input.limit = Number(flags.limit); if (!Number.isInteger(input.limit)) throw Error('--limit must be a whole number.'); }
|
|
36
|
+
const result = await callWorkspace('workspace_audit_log_list', input, configuration(flags.profile));
|
|
37
|
+
if (flags.format === 'csv') { process.stdout.write(auditCsv((result?.data ?? result)?.items ?? [])); return; }
|
|
38
|
+
console.log(JSON.stringify(result));
|
|
39
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const workspace = { workspaceId: z.string().uuid().optional() };
|
|
3
|
+
const cap = z.number().int().min(0).max(10000000).nullable();
|
|
4
|
+
export const budgetOperations = {
|
|
5
|
+
workspace_budgets_get: { schema: z.object(workspace).strict(), method: 'GET', path: a => `/workspaces/${a.workspaceId}/budgets`, scoped: true, description: 'Read spend budgets: credits used this UTC month, each agent’s monthly cap (null means unlimited), the cap for newly registered agents, what happens at a cap and the 80% warning switch (Operator and email) and the ten latest alerts.' },
|
|
6
|
+
workspace_budget_alerts: { schema: z.object({ ...workspace, limit: z.number().int().min(1).max(200).optional() }).strict(), method: 'GET', path: a => `/workspaces/${a.workspaceId}/budget-alerts${a.limit ? `?limit=${a.limit}` : ''}`, scoped: true, description: 'List recent low-credit and budget alerts, newest first. Workspace credits and each agent’s monthly cap alert once per UTC month at 80% and at 100%; owners and admins are emailed and see them in Operator.' },
|
|
7
|
+
workspace_budgets_set: { schema: z.object({ ...workspace, agentCaps: z.array(z.object({ agentId: z.string().uuid(), monthlyCap: cap }).strict()).max(200).optional(), newAgentCap: cap.optional(), atLimit: z.enum(['pause', 'ask']).optional(), alert80: z.boolean().optional(), expectedVersion: z.number().int().min(0).optional() }).strict(), method: 'POST', path: a => `/workspaces/${a.workspaceId}/budgets`, body: ({ workspaceId, ...rest }) => rest, scoped: true, description: 'Change spend budgets. Send only the fields to change; null removes a cap. Admin only and only a person: an agent cannot raise its own cap. An agent at its cap gets budget_exceeded on hosted tool calls until the month resets or the cap is raised.' },
|
|
8
|
+
};
|
package/cli/budgets.mjs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { callWorkspace, configuration } from './workspaces.mjs';
|
|
2
|
+
const cap = (name, value) => {
|
|
3
|
+
if (value === 'none' || value === 'unlimited') return null;
|
|
4
|
+
if (!/^\d+$/.test(value ?? '')) throw Error(`--${name} needs a whole number of credits, or "none" for no cap.`);
|
|
5
|
+
return Number(value);
|
|
6
|
+
};
|
|
7
|
+
export async function budgetsCli(args) {
|
|
8
|
+
const flags = {}, positions = [];
|
|
9
|
+
for (let i = 0; i < args.length; i++) {
|
|
10
|
+
const arg = args[i];
|
|
11
|
+
if (arg === '--json') continue;
|
|
12
|
+
if (!arg.startsWith('--')) { positions.push(arg); continue; }
|
|
13
|
+
const key = arg.slice(2);
|
|
14
|
+
if (!['workspace', 'profile', 'agent', 'cap', 'new-agent-cap', 'at-limit', 'alert80', 'limit'].includes(key) || Object.hasOwn(flags, key) || i + 1 >= args.length) throw Error(`Invalid flag --${key}.`);
|
|
15
|
+
flags[key] = args[++i];
|
|
16
|
+
}
|
|
17
|
+
if (positions.length !== 2 || positions[0] !== 'budgets' || !['show', 'set', 'alerts'].includes(positions[1])) throw Error('Use budgets show|set|alerts --workspace ID. See audienti help.');
|
|
18
|
+
const input = { workspaceId: flags.workspace };
|
|
19
|
+
let name = 'workspace_budgets_get';
|
|
20
|
+
if (positions[1] === 'alerts') {
|
|
21
|
+
name = 'workspace_budget_alerts';
|
|
22
|
+
if (flags.limit !== undefined) { if (!/^\d+$/.test(flags.limit) || Number(flags.limit) < 1 || Number(flags.limit) > 200) throw Error('--limit is a whole number from 1 to 200.'); input.limit = Number(flags.limit); }
|
|
23
|
+
}
|
|
24
|
+
if (positions[1] === 'set') {
|
|
25
|
+
name = 'workspace_budgets_set';
|
|
26
|
+
if ((flags.agent === undefined) !== (flags.cap === undefined)) throw Error('--agent and --cap go together.');
|
|
27
|
+
if (flags.agent) input.agentCaps = [{ agentId: flags.agent, monthlyCap: cap('cap', flags.cap) }];
|
|
28
|
+
if (flags['new-agent-cap'] !== undefined) input.newAgentCap = cap('new-agent-cap', flags['new-agent-cap']);
|
|
29
|
+
if (flags['at-limit'] !== undefined) { if (!['pause', 'ask'].includes(flags['at-limit'])) throw Error('--at-limit is pause or ask.'); input.atLimit = flags['at-limit']; }
|
|
30
|
+
if (flags.alert80 !== undefined) { if (!['on', 'off'].includes(flags.alert80)) throw Error('--alert80 is on or off.'); input.alert80 = flags.alert80 === 'on'; }
|
|
31
|
+
if (Object.keys(input).length < 2) throw Error('Nothing to change. Use --agent ID --cap N, --new-agent-cap N, --at-limit pause|ask or --alert80 on|off.');
|
|
32
|
+
}
|
|
33
|
+
console.log(JSON.stringify(await callWorkspace(name, input, configuration(flags.profile))));
|
|
34
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {z} from 'zod';
|
|
2
|
+
const uuid=z.string().uuid(),workspace={workspaceId:uuid.optional()},locator={...workspace,campaignId:uuid};
|
|
3
|
+
const state=z.enum(['Draft','Preparing','Active','Paused','Closing','Archived']);
|
|
4
|
+
const signalBase={id:uuid,scope:z.enum(['company','person']),details:z.string().optional()};
|
|
5
|
+
const signal=z.discriminatedUnion('kind',[
|
|
6
|
+
z.object({...signalBase,kind:z.literal('research'),question:z.string(),expected:z.boolean().optional()}).strict(),
|
|
7
|
+
z.object({...signalBase,kind:z.literal('topic'),topic:z.string()}).strict(),
|
|
8
|
+
z.object({...signalBase,kind:z.literal('profile'),url:z.string(),track:z.enum(['audience','activity']).optional(),interactions:z.array(z.enum(['comments','reactions'])).optional()}).strict(),
|
|
9
|
+
]);
|
|
10
|
+
const fields=z.object({name:z.string(),audienceId:uuid,audienceRevision:z.number().int(),offerId:uuid,offerRevision:z.number().int(),premise:z.string().optional(),approach:z.string().optional(),approachPin:z.object({id:uuid,revision:z.number().int().positive()}).strict().optional(),notes:z.string().optional(),ownerUserId:uuid.nullable().optional(),tags:z.array(z.string()).optional(),signalMatch:z.enum(['any','all']).optional(),signals:z.array(signal).optional(),targets:z.discriminatedUnion('kind',[
|
|
11
|
+
z.object({kind:z.literal('none'),values:z.array(z.never())}).strict(),
|
|
12
|
+
z.object({kind:z.literal('companies'),values:z.array(z.object({type:z.enum(['domain','linkedin-company']),value:z.string()}).strict())}).strict(),
|
|
13
|
+
z.object({kind:z.literal('people'),values:z.array(z.object({type:z.literal('linkedin-person'),value:z.string()}).strict())}).strict(),
|
|
14
|
+
]).optional(),channels:z.array(z.enum(['email','linkedin'])).optional(),approvalMode:z.literal('manual').optional(),contentOpsEnabled:z.boolean().optional(),schedule:z.object({startAt:z.string().nullable().optional(),endAt:z.string().nullable().optional(),maxCompanies:z.number().int().nullable().optional()}).strict().optional()}).strict();
|
|
15
|
+
const path=a=>`/workspaces/${a.workspaceId}/campaigns`,detail=a=>path(a)+`/${a.campaignId}`;
|
|
16
|
+
const qs=a=>new URLSearchParams(Object.fromEntries(Object.entries(a).filter(([k,v])=>!['workspaceId','campaignId'].includes(k)&&v!==undefined).map(([k,v])=>[k,String(v)])));
|
|
17
|
+
export const campaignOperations={
|
|
18
|
+
campaign_summary:{schema:z.object({...workspace}).strict(),method:'GET',path:a=>`/workspaces/${a.workspaceId}/campaign-summary`,scoped:true,description:'Counts of Campaigns per state, and per Campaign its Audience, offer, approach, goal stage, methods, people, engaged and at-goal counts since start.'},
|
|
19
|
+
campaign_list:{schema:z.object({...workspace,state:state.optional(),archived:z.boolean().optional(),q:z.string().optional(),limit:z.number().int().optional(),after:uuid.optional()}).strict(),method:'GET',path:a=>path(a)+'?'+qs(a),scoped:true},
|
|
20
|
+
campaign_get:{schema:z.object({...locator,revision:z.number().int().optional()}).strict(),method:'GET',path:a=>detail(a)+'?'+qs(a),scoped:true},
|
|
21
|
+
campaign_events:{schema:z.object({...locator,limit:z.number().int().optional(),after:uuid.optional()}).strict(),method:'GET',path:a=>detail(a)+'/events?'+qs(a),scoped:true},
|
|
22
|
+
campaign_create:{schema:z.object({...workspace,requestId:uuid,fields}).strict(),method:'POST',path,body:a=>({requestId:a.requestId,fields:a.fields}),scoped:true},
|
|
23
|
+
campaign_update:{schema:z.object({...locator,expectedRevision:z.number().int(),fields}).strict(),method:'POST',path:a=>detail(a)+'/revisions',body:a=>({expectedRevision:a.expectedRevision,fields:a.fields}),scoped:true},
|
|
24
|
+
campaign_transition:{schema:z.object({...locator,expectedState:state,operation:z.enum(['prepare','activate','pause','resume','close','archive','restore']),reason:z.string().optional()}).strict(),method:'POST',path:a=>detail(a)+'/transitions',body:a=>({expectedState:a.expectedState,operation:a.operation,...(a.reason===undefined?{}:{reason:a.reason})}),scoped:true,description:'Move a Campaign through its lifecycle. prepare: Draft to Preparing (signal tracking and prospect finding run, no engagement). activate: Draft or Preparing to Active (finding and engagement run). pause: stop automation. resume: back to the paused state. close: Active to Closing (finding stops, engagement continues). archive: stop everything and hide it. restore: Archived to Paused, or Draft for a draft. Agents do not start or activate campaigns; the owner does.'},
|
|
25
|
+
campaign_successor:{schema:z.object({...locator,requestId:uuid,expectedRevision:z.number().int(),fields}).strict(),method:'POST',path:a=>detail(a)+'/successors',body:a=>({requestId:a.requestId,expectedRevision:a.expectedRevision,fields:a.fields}),scoped:true},
|
|
26
|
+
};
|