@awesomate/sdk 0.62.0 → 0.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +267 -1
- package/dist/index.js +92 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
* Docs: https://hub.awesomate.ai/docs/sdk/
|
|
27
27
|
*/
|
|
28
28
|
/** This package's version, sent to the hub with every server call. */
|
|
29
|
-
export declare const VERSION = "0.
|
|
29
|
+
export declare const VERSION = "0.64.0";
|
|
30
30
|
/** Augmented by the generated awesomate.d.ts, so each kind's rows are typed. */
|
|
31
31
|
export interface Kinds {
|
|
32
32
|
}
|
|
@@ -1128,6 +1128,35 @@ export declare class AwesomateClient {
|
|
|
1128
1128
|
* for one already thrown away answers `already: true`.
|
|
1129
1129
|
*/
|
|
1130
1130
|
discard: (id: string) => Promise<EmailDiscarded>;
|
|
1131
|
+
/**
|
|
1132
|
+
* Ask the account's own writing agent for an email. It writes in the business's voice from its
|
|
1133
|
+
* details, lists and past results. It may ask first (`questions`): put them to the person and
|
|
1134
|
+
* call again with the same `brief`, the `sessionId` it gave and `answers`. Otherwise it saves a
|
|
1135
|
+
* draft (new to `topic`, or written over the draft `email`) for a person to approve at
|
|
1136
|
+
* `reviewUrl`; or answers `pending` with a `jobId` for `briefStatus()`. Needs crm:draft and
|
|
1137
|
+
* the copywriter on the account. Each email is one of the month's drafts on Awesomate's AI,
|
|
1138
|
+
* none on the account's own OpenRouter key. No writing agent throws `unavailable` (server code
|
|
1139
|
+
* `no_copywriter`): the owner adds one in the hub.
|
|
1140
|
+
*
|
|
1141
|
+
* @example
|
|
1142
|
+
* let r = await db.emails.brief({ brief: 'A special offer for our list this month', topic: 'news' });
|
|
1143
|
+
* if (r.questions) r = await db.emails.brief({ brief: 'A special offer for our list this month', topic: 'news',
|
|
1144
|
+
* sessionId: r.sessionId, answers: r.questions.map((q) => ({ q: q.q, a: 'A free 30-minute review' })) });
|
|
1145
|
+
* if (r.reviewUrl) console.log(r.summary, r.reviewUrl);
|
|
1146
|
+
*/
|
|
1147
|
+
brief: (input: {
|
|
1148
|
+
brief: string;
|
|
1149
|
+
topic?: string;
|
|
1150
|
+
email?: string;
|
|
1151
|
+
answers?: Array<{
|
|
1152
|
+
q: string;
|
|
1153
|
+
a: string;
|
|
1154
|
+
}>;
|
|
1155
|
+
sessionId?: string;
|
|
1156
|
+
agent?: string;
|
|
1157
|
+
}) => Promise<EmailBrief>;
|
|
1158
|
+
/** The result of a `brief()` that answered `pending`: still `pending`, or the questions or the draft. */
|
|
1159
|
+
briefStatus: (jobId: string) => Promise<EmailBrief>;
|
|
1131
1160
|
};
|
|
1132
1161
|
/**
|
|
1133
1162
|
* Email series: emails that go to one person at a time over days, starting when something
|
|
@@ -1193,6 +1222,21 @@ export declare class AwesomateClient {
|
|
|
1193
1222
|
}) => Promise<EmailSeriesDraft>;
|
|
1194
1223
|
/** Change one email of a series (by its key); the rest of the draft, or what is live, is kept. */
|
|
1195
1224
|
updateEmail: (id: string, key: string, doc: EmailSeriesEmail["doc"]) => Promise<EmailSeriesDraft>;
|
|
1225
|
+
/**
|
|
1226
|
+
* Ask the account's own writing agent to write a series' emails from one brief: every email
|
|
1227
|
+
* still holding a [gap], or every one with `all`. One draft version is saved, never made live:
|
|
1228
|
+
* an approver approves it at `reviewUrl`. A long write answers `pending` with a `jobId` for
|
|
1229
|
+
* `writeStatus()`. Needs crm:draft and the copywriter on the account; each email is one of
|
|
1230
|
+
* the month's drafts on Awesomate's AI. Several writing agents and none named throws `invalid`
|
|
1231
|
+
* (server code `pick_copywriter`).
|
|
1232
|
+
*/
|
|
1233
|
+
write: (id: string, input: {
|
|
1234
|
+
brief: string;
|
|
1235
|
+
all?: boolean;
|
|
1236
|
+
agent?: string;
|
|
1237
|
+
}) => Promise<EmailSeriesWrite>;
|
|
1238
|
+
/** The result of a `write()` that answered `pending`. */
|
|
1239
|
+
writeStatus: (id: string, jobId: string) => Promise<EmailSeriesWrite>;
|
|
1196
1240
|
};
|
|
1197
1241
|
/**
|
|
1198
1242
|
* Sign-up forms: the business's own form on its website that puts each person into Contacts,
|
|
@@ -1596,6 +1640,52 @@ export declare class AwesomateClient {
|
|
|
1596
1640
|
* await db.archiveLink('vehicle', 'serviced_at');
|
|
1597
1641
|
*/
|
|
1598
1642
|
archiveLink(kind: string, key: string): Promise<KindDescription>;
|
|
1643
|
+
/**
|
|
1644
|
+
* Set this up for me: the hub's suggestion for one sentence about what the owner keeps track of
|
|
1645
|
+
* ("a job management system"), as a whole setup in the owner's words (fields, stages, lists,
|
|
1646
|
+
* links, small related things, the brief). Nothing is saved. Pass `kind` to ask about one of
|
|
1647
|
+
* their things: the suggestion then only adds to it, and `matched` says what it has already.
|
|
1648
|
+
* Edit it if you like, then checkSetup(). The account's token, crm:write, Support Plus and above,
|
|
1649
|
+
* with Set this up for me on the account; a few a person in ten minutes.
|
|
1650
|
+
*
|
|
1651
|
+
* @example
|
|
1652
|
+
* const proposal = await db.proposeSetup('property bookings');
|
|
1653
|
+
* console.log(proposal.sentence);
|
|
1654
|
+
*/
|
|
1655
|
+
proposeSetup(sentence: string, options?: {
|
|
1656
|
+
kind?: string;
|
|
1657
|
+
}): Promise<SetupProposal>;
|
|
1658
|
+
/**
|
|
1659
|
+
* Check a setup exactly as a click would, and read the sentence the owner should agree to before
|
|
1660
|
+
* applySetup(). Nothing is saved. On one of their things it is additions only, and answers
|
|
1661
|
+
* `base`, which applySetup() needs. A setup that does not fit throws `validation` naming the
|
|
1662
|
+
* item (`at`, like `fields.2`). The account's token, crm:write.
|
|
1663
|
+
*
|
|
1664
|
+
* @example
|
|
1665
|
+
* const proposal = await db.proposeSetup('a quote pipeline');
|
|
1666
|
+
* const check = await db.checkSetup(proposal.setup);
|
|
1667
|
+
* if (check.can_apply) console.log(check.sentence);
|
|
1668
|
+
*/
|
|
1669
|
+
checkSetup(setup: SetupSpec, options?: {
|
|
1670
|
+
replaceBrief?: boolean;
|
|
1671
|
+
}): Promise<SetupCheck>;
|
|
1672
|
+
/**
|
|
1673
|
+
* Set it up, all in one go or not at all: the thing, its fields, sections, stages, links, small
|
|
1674
|
+
* related things, lists and the brief. On one of their things it only adds (nothing is removed,
|
|
1675
|
+
* renamed or retyped) and needs `base` from checkSetup() (without it, `validation`, server code
|
|
1676
|
+
* `base_required`); if the thing changed since, it throws
|
|
1677
|
+
* `conflict` (server code `changed`): check again. Only an Owner's key may apply (anyone else's
|
|
1678
|
+
* throws `forbidden`, server code `owner_only`). Apply only what the owner agreed to.
|
|
1679
|
+
*
|
|
1680
|
+
* @example
|
|
1681
|
+
* const { setup } = await db.proposeSetup('property bookings');
|
|
1682
|
+
* const made = await db.applySetup(setup); // once the owner has agreed to checkSetup()'s sentence
|
|
1683
|
+
* console.log(made.kind.key, made.land);
|
|
1684
|
+
*/
|
|
1685
|
+
applySetup(setup: SetupSpec, options?: {
|
|
1686
|
+
base?: SetupBase;
|
|
1687
|
+
replaceBrief?: boolean;
|
|
1688
|
+
}): Promise<SetupApplied>;
|
|
1599
1689
|
/**
|
|
1600
1690
|
* Create a record (returns its id), or change one with options.id. Every value is checked
|
|
1601
1691
|
* against the kind. crm:write, Support Plus and above: the account's token, or an app key made
|
|
@@ -2622,6 +2712,132 @@ export interface LinkSpec {
|
|
|
2622
2712
|
/** Every record must set it. Only when added to a kind with no records yet; a link is never made required later. */
|
|
2623
2713
|
required?: boolean;
|
|
2624
2714
|
}
|
|
2715
|
+
/** A field in a setup, in the owner's words. */
|
|
2716
|
+
export interface SetupField {
|
|
2717
|
+
label: string;
|
|
2718
|
+
type: 'text' | 'long_text' | 'number' | 'money' | 'date' | 'datetime' | 'yes_no' | 'choice' | 'choices' | 'address' | 'phone' | 'email' | 'url';
|
|
2719
|
+
/** For choice and choices. */
|
|
2720
|
+
choices?: string[];
|
|
2721
|
+
required?: boolean;
|
|
2722
|
+
/** Whether AI may read it. Leave names, notes and contact details off unless the owner says so. */
|
|
2723
|
+
ai_may_read?: boolean;
|
|
2724
|
+
/** A section's label. */
|
|
2725
|
+
section?: string;
|
|
2726
|
+
}
|
|
2727
|
+
/**
|
|
2728
|
+
* A setup: what an owner keeps track of, in their own words, never keys. `thing` is a new thing
|
|
2729
|
+
* (`{label, label_plural?}`) or one of theirs to add to (`{kind}`). Limits: 6 sections, 15
|
|
2730
|
+
* fields, 2 to 8 stages, 4 links, 2 related things of up to 6 fields, 3 lists of up to 12
|
|
2731
|
+
* columns, a brief focus of up to 200 characters and 6 pins.
|
|
2732
|
+
*/
|
|
2733
|
+
export interface SetupSpec {
|
|
2734
|
+
thing: {
|
|
2735
|
+
label: string;
|
|
2736
|
+
label_plural?: string;
|
|
2737
|
+
} | {
|
|
2738
|
+
kind: string;
|
|
2739
|
+
};
|
|
2740
|
+
sections?: Array<{
|
|
2741
|
+
label: string;
|
|
2742
|
+
description?: string;
|
|
2743
|
+
}>;
|
|
2744
|
+
fields?: SetupField[];
|
|
2745
|
+
/** The stage field and its stages in order; `role` is a role on the business map, whose turn it is. */
|
|
2746
|
+
stages?: {
|
|
2747
|
+
field_label?: string;
|
|
2748
|
+
stages: Array<{
|
|
2749
|
+
name: string;
|
|
2750
|
+
colour?: 'grey' | 'blue' | 'green' | 'amber' | 'red' | 'purple';
|
|
2751
|
+
closed?: boolean;
|
|
2752
|
+
role?: string;
|
|
2753
|
+
}>;
|
|
2754
|
+
} | null;
|
|
2755
|
+
/** `to` is 'People', 'Companies' or one of the owner's things. */
|
|
2756
|
+
links?: Array<{
|
|
2757
|
+
to: string;
|
|
2758
|
+
label?: string;
|
|
2759
|
+
required?: boolean;
|
|
2760
|
+
}>;
|
|
2761
|
+
/** Small new things, each belonging to the main thing. */
|
|
2762
|
+
new_related?: Array<{
|
|
2763
|
+
label: string;
|
|
2764
|
+
label_plural?: string;
|
|
2765
|
+
fields?: SetupField[];
|
|
2766
|
+
link_label?: string;
|
|
2767
|
+
}>;
|
|
2768
|
+
lists?: Array<{
|
|
2769
|
+
name: string;
|
|
2770
|
+
display: 'table' | 'board' | 'calendar' | 'week' | 'timeline';
|
|
2771
|
+
group_by?: string;
|
|
2772
|
+
date_field?: string;
|
|
2773
|
+
columns?: string[];
|
|
2774
|
+
}>;
|
|
2775
|
+
/** What the summary at the top of each record leads with, and the fields pinned under it. */
|
|
2776
|
+
brief?: {
|
|
2777
|
+
focus?: string;
|
|
2778
|
+
pins?: string[];
|
|
2779
|
+
} | null;
|
|
2780
|
+
}
|
|
2781
|
+
/** What one of the owner's things has already, by the owner's labels. */
|
|
2782
|
+
export interface SetupMatched {
|
|
2783
|
+
fields: string[];
|
|
2784
|
+
links: string[];
|
|
2785
|
+
stages: string[];
|
|
2786
|
+
lists: string[];
|
|
2787
|
+
}
|
|
2788
|
+
/** The versions checkSetup() read a thing at; pass it to applySetup() as it came. */
|
|
2789
|
+
export type SetupBase = Record<string, unknown>;
|
|
2790
|
+
/** proposeSetup()'s answer. */
|
|
2791
|
+
export interface SetupProposal {
|
|
2792
|
+
setup: SetupSpec;
|
|
2793
|
+
/** What it would do, in plain words: show this to the owner. */
|
|
2794
|
+
sentence: string;
|
|
2795
|
+
/** How many suggested parts did not fit and were left out. */
|
|
2796
|
+
dropped: number;
|
|
2797
|
+
/** How many parts the thing has already (adding to one of theirs). */
|
|
2798
|
+
already: number;
|
|
2799
|
+
matched?: SetupMatched;
|
|
2800
|
+
notes?: string[];
|
|
2801
|
+
/** Set when the sentence named one of their things: the setup adds to it. */
|
|
2802
|
+
adds_to?: {
|
|
2803
|
+
key: string;
|
|
2804
|
+
label: string;
|
|
2805
|
+
label_plural: string;
|
|
2806
|
+
};
|
|
2807
|
+
}
|
|
2808
|
+
/** checkSetup()'s answer. */
|
|
2809
|
+
export interface SetupCheck {
|
|
2810
|
+
/** Exactly what applySetup() would do: the owner agrees to this. */
|
|
2811
|
+
sentence: string;
|
|
2812
|
+
setup: SetupSpec;
|
|
2813
|
+
/** Whether this key may apply it (an Owner's). */
|
|
2814
|
+
can_apply: boolean;
|
|
2815
|
+
makes: {
|
|
2816
|
+
key: string;
|
|
2817
|
+
related: string[];
|
|
2818
|
+
lists: number;
|
|
2819
|
+
stages: number;
|
|
2820
|
+
fields?: number;
|
|
2821
|
+
links?: number;
|
|
2822
|
+
};
|
|
2823
|
+
matched?: SetupMatched;
|
|
2824
|
+
notes?: string[];
|
|
2825
|
+
/** Adding to one of theirs: true when there is nothing to add. */
|
|
2826
|
+
nothing?: boolean;
|
|
2827
|
+
base?: SetupBase;
|
|
2828
|
+
}
|
|
2829
|
+
/** applySetup()'s answer: the thing set up, its related things, and the list to open first. */
|
|
2830
|
+
export interface SetupApplied {
|
|
2831
|
+
kind: {
|
|
2832
|
+
key: string;
|
|
2833
|
+
label_plural: string;
|
|
2834
|
+
};
|
|
2835
|
+
related: string[];
|
|
2836
|
+
land: {
|
|
2837
|
+
kind: string;
|
|
2838
|
+
list?: string;
|
|
2839
|
+
};
|
|
2840
|
+
}
|
|
2625
2841
|
/** A kind to define: a table an app keeps. */
|
|
2626
2842
|
export interface KindSpec {
|
|
2627
2843
|
key: string;
|
|
@@ -3376,6 +3592,51 @@ export interface EmailListReach {
|
|
|
3376
3592
|
/** How many are left out, by reason (unsubscribed, bounced, ...). */
|
|
3377
3593
|
leftOut: Record<string, number>;
|
|
3378
3594
|
}
|
|
3595
|
+
/** What `emails.brief()` answers: the agent's questions, a saved draft, or `pending`. */
|
|
3596
|
+
export interface EmailBrief {
|
|
3597
|
+
/** The agent needs these answered first; pass them back as `answers` with this `sessionId`. */
|
|
3598
|
+
questions?: Array<{
|
|
3599
|
+
q: string;
|
|
3600
|
+
choices?: string[];
|
|
3601
|
+
}>;
|
|
3602
|
+
sessionId?: string;
|
|
3603
|
+
/** The draft saved for review. */
|
|
3604
|
+
id?: string;
|
|
3605
|
+
summary?: string;
|
|
3606
|
+
why?: string;
|
|
3607
|
+
reviewUrl?: string;
|
|
3608
|
+
/** Still writing: ask `briefStatus(jobId)`. */
|
|
3609
|
+
pending?: true;
|
|
3610
|
+
jobId?: string;
|
|
3611
|
+
/** This month's drafts on Awesomate's AI. */
|
|
3612
|
+
allowance?: {
|
|
3613
|
+
ownKey: boolean;
|
|
3614
|
+
used: number;
|
|
3615
|
+
limit: number | null;
|
|
3616
|
+
left: number | null;
|
|
3617
|
+
blocked: boolean;
|
|
3618
|
+
};
|
|
3619
|
+
/** What to do next, to tell the owner. */
|
|
3620
|
+
next?: string;
|
|
3621
|
+
}
|
|
3622
|
+
/** What `emailSeries.write()` answers. */
|
|
3623
|
+
export interface EmailSeriesWrite {
|
|
3624
|
+
id?: string;
|
|
3625
|
+
/** How many emails were written and saved. */
|
|
3626
|
+
written?: number;
|
|
3627
|
+
/** The emails that were not, and why. */
|
|
3628
|
+
failed?: Array<{
|
|
3629
|
+
key: string;
|
|
3630
|
+
message: string;
|
|
3631
|
+
}>;
|
|
3632
|
+
/** One sentence for the owner. */
|
|
3633
|
+
sentence?: string;
|
|
3634
|
+
reviewUrl?: string;
|
|
3635
|
+
next?: string;
|
|
3636
|
+
/** Still writing: ask `writeStatus(id, job_id)`. */
|
|
3637
|
+
pending?: true;
|
|
3638
|
+
job_id?: string;
|
|
3639
|
+
}
|
|
3379
3640
|
/** A draft, from `emails.draft()` or `emails.update()`. */
|
|
3380
3641
|
export interface EmailDraft {
|
|
3381
3642
|
id: string;
|
|
@@ -4204,12 +4465,17 @@ export interface KnowledgeAnswer {
|
|
|
4204
4465
|
answer: string | null;
|
|
4205
4466
|
/** True only for an ok answer with sources. An ok answer with none came from the model, not the content. */
|
|
4206
4467
|
grounded: boolean;
|
|
4468
|
+
/**
|
|
4469
|
+
* `watchUrl`: a cited video anyone can watch, its permanent player page at the cited second
|
|
4470
|
+
* (`https://<host>/v/<id>?t=612`). Safe to store or put on a page; null for anything else.
|
|
4471
|
+
*/
|
|
4207
4472
|
sources: Array<{
|
|
4208
4473
|
ref: number;
|
|
4209
4474
|
title: string | null;
|
|
4210
4475
|
kind: string | null;
|
|
4211
4476
|
locator: string | null;
|
|
4212
4477
|
url: string | null;
|
|
4478
|
+
watchUrl: string | null;
|
|
4213
4479
|
}>;
|
|
4214
4480
|
/** What the owner set the assistant to say when it has no answer. */
|
|
4215
4481
|
noAnswerMessage: string | null;
|
package/dist/index.js
CHANGED
|
@@ -26,8 +26,10 @@
|
|
|
26
26
|
* Docs: https://hub.awesomate.ai/docs/sdk/
|
|
27
27
|
*/
|
|
28
28
|
/** This package's version, sent to the hub with every server call. */
|
|
29
|
-
export const VERSION = '0.
|
|
29
|
+
export const VERSION = '0.64.0';
|
|
30
30
|
const DEFAULT_BASE = 'https://hub.awesomate.ai';
|
|
31
|
+
/** The one check for a platform `watch_url` (mirrored in the hub's awesomate-knowledge.ts and its MCP). */
|
|
32
|
+
const WATCH_URL = /^https:\/\/[^\s"'<>]+$/i;
|
|
31
33
|
/** Every record and contact id is a UUID; anything else can name no row. */
|
|
32
34
|
const RECORD_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
33
35
|
const ERROR_CODES = [
|
|
@@ -128,7 +130,8 @@ function errorCode(status, server, body = {}) {
|
|
|
128
130
|
return 'rate_limited';
|
|
129
131
|
// stale: a form or webinar changed since it was read (update with version). not_draft: a webinar's
|
|
130
132
|
// registration is open, so it is fixed in Zoom. not_run: a recording goes only on a webinar that has run.
|
|
131
|
-
|
|
133
|
+
// changed: a thing a setup adds to changed since checkSetup().
|
|
134
|
+
if (server === 'conflict' || server === 'stale' || server === 'not_draft' || server === 'not_run' || server === 'changed')
|
|
132
135
|
return 'conflict';
|
|
133
136
|
if (status === 404 && server !== 'not_live')
|
|
134
137
|
return 'not_found';
|
|
@@ -809,6 +812,30 @@ export class AwesomateClient {
|
|
|
809
812
|
* for one already thrown away answers `already: true`.
|
|
810
813
|
*/
|
|
811
814
|
discard: async (id) => this.request('DELETE', `/api/my-crm/v1/emails/${emailId(id)}`, undefined, false),
|
|
815
|
+
/**
|
|
816
|
+
* Ask the account's own writing agent for an email. It writes in the business's voice from its
|
|
817
|
+
* details, lists and past results. It may ask first (`questions`): put them to the person and
|
|
818
|
+
* call again with the same `brief`, the `sessionId` it gave and `answers`. Otherwise it saves a
|
|
819
|
+
* draft (new to `topic`, or written over the draft `email`) for a person to approve at
|
|
820
|
+
* `reviewUrl`; or answers `pending` with a `jobId` for `briefStatus()`. Needs crm:draft and
|
|
821
|
+
* the copywriter on the account. Each email is one of the month's drafts on Awesomate's AI,
|
|
822
|
+
* none on the account's own OpenRouter key. No writing agent throws `unavailable` (server code
|
|
823
|
+
* `no_copywriter`): the owner adds one in the hub.
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* let r = await db.emails.brief({ brief: 'A special offer for our list this month', topic: 'news' });
|
|
827
|
+
* if (r.questions) r = await db.emails.brief({ brief: 'A special offer for our list this month', topic: 'news',
|
|
828
|
+
* sessionId: r.sessionId, answers: r.questions.map((q) => ({ q: q.q, a: 'A free 30-minute review' })) });
|
|
829
|
+
* if (r.reviewUrl) console.log(r.summary, r.reviewUrl);
|
|
830
|
+
*/
|
|
831
|
+
brief: async (input) => {
|
|
832
|
+
const r = await this.request('POST', '/api/my-crm/v1/emails/brief', {
|
|
833
|
+
brief: input.brief, topic: input.topic, email: input.email, answers: input.answers, session_id: input.sessionId, agent: input.agent,
|
|
834
|
+
}, false);
|
|
835
|
+
return briefFromWire(r);
|
|
836
|
+
},
|
|
837
|
+
/** The result of a `brief()` that answered `pending`: still `pending`, or the questions or the draft. */
|
|
838
|
+
briefStatus: async (jobId) => briefFromWire(await this.request('GET', `/api/my-crm/v1/emails/brief/${encodeURIComponent(jobId)}`)),
|
|
812
839
|
};
|
|
813
840
|
/**
|
|
814
841
|
* Email series: emails that go to one person at a time over days, starting when something
|
|
@@ -872,6 +899,17 @@ export class AwesomateClient {
|
|
|
872
899
|
update: (id, input) => this.request('PUT', `/api/my-crm/v1/series/${encodeURIComponent(id)}/draft`, input, false),
|
|
873
900
|
/** Change one email of a series (by its key); the rest of the draft, or what is live, is kept. */
|
|
874
901
|
updateEmail: (id, key, doc) => this.request('PUT', `/api/my-crm/v1/series/${encodeURIComponent(id)}/emails/${encodeURIComponent(key)}`, { doc }, false),
|
|
902
|
+
/**
|
|
903
|
+
* Ask the account's own writing agent to write a series' emails from one brief: every email
|
|
904
|
+
* still holding a [gap], or every one with `all`. One draft version is saved, never made live:
|
|
905
|
+
* an approver approves it at `reviewUrl`. A long write answers `pending` with a `jobId` for
|
|
906
|
+
* `writeStatus()`. Needs crm:draft and the copywriter on the account; each email is one of
|
|
907
|
+
* the month's drafts on Awesomate's AI. Several writing agents and none named throws `invalid`
|
|
908
|
+
* (server code `pick_copywriter`).
|
|
909
|
+
*/
|
|
910
|
+
write: (id, input) => this.request('POST', `/api/my-crm/v1/series/${encodeURIComponent(id)}/write`, input, false),
|
|
911
|
+
/** The result of a `write()` that answered `pending`. */
|
|
912
|
+
writeStatus: (id, jobId) => this.request('GET', `/api/my-crm/v1/series/${encodeURIComponent(id)}/write/${encodeURIComponent(jobId)}`),
|
|
875
913
|
};
|
|
876
914
|
/**
|
|
877
915
|
* Sign-up forms: the business's own form on its website that puts each person into Contacts,
|
|
@@ -1191,6 +1229,7 @@ export class AwesomateClient {
|
|
|
1191
1229
|
kind: typeof s.kind === 'string' ? s.kind : null,
|
|
1192
1230
|
locator: (typeof s.locator === 'string' ? s.locator : typeof s.section_path === 'string' ? s.section_path : null),
|
|
1193
1231
|
url: typeof s.url === 'string' ? s.url : null,
|
|
1232
|
+
watchUrl: typeof s.watch_url === 'string' && WATCH_URL.test(s.watch_url) ? s.watch_url : null,
|
|
1194
1233
|
}));
|
|
1195
1234
|
const text = typeof r.answer_plain === 'string' && r.answer_plain ? r.answer_plain : typeof r.answer === 'string' ? r.answer : null;
|
|
1196
1235
|
return {
|
|
@@ -1438,6 +1477,53 @@ export class AwesomateClient {
|
|
|
1438
1477
|
async archiveLink(kind, key) {
|
|
1439
1478
|
return (await this.request('DELETE', `/api/my-crm/v1/kinds/${encodeURIComponent(kind)}/links/${encodeURIComponent(key)}`, undefined, false)).kind;
|
|
1440
1479
|
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Set this up for me: the hub's suggestion for one sentence about what the owner keeps track of
|
|
1482
|
+
* ("a job management system"), as a whole setup in the owner's words (fields, stages, lists,
|
|
1483
|
+
* links, small related things, the brief). Nothing is saved. Pass `kind` to ask about one of
|
|
1484
|
+
* their things: the suggestion then only adds to it, and `matched` says what it has already.
|
|
1485
|
+
* Edit it if you like, then checkSetup(). The account's token, crm:write, Support Plus and above,
|
|
1486
|
+
* with Set this up for me on the account; a few a person in ten minutes.
|
|
1487
|
+
*
|
|
1488
|
+
* @example
|
|
1489
|
+
* const proposal = await db.proposeSetup('property bookings');
|
|
1490
|
+
* console.log(proposal.sentence);
|
|
1491
|
+
*/
|
|
1492
|
+
async proposeSetup(sentence, options = {}) {
|
|
1493
|
+
return this.request('POST', '/api/my-crm/v1/setup/propose', { sentence, ...(options.kind ? { kind: options.kind } : {}) }, false);
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Check a setup exactly as a click would, and read the sentence the owner should agree to before
|
|
1497
|
+
* applySetup(). Nothing is saved. On one of their things it is additions only, and answers
|
|
1498
|
+
* `base`, which applySetup() needs. A setup that does not fit throws `validation` naming the
|
|
1499
|
+
* item (`at`, like `fields.2`). The account's token, crm:write.
|
|
1500
|
+
*
|
|
1501
|
+
* @example
|
|
1502
|
+
* const proposal = await db.proposeSetup('a quote pipeline');
|
|
1503
|
+
* const check = await db.checkSetup(proposal.setup);
|
|
1504
|
+
* if (check.can_apply) console.log(check.sentence);
|
|
1505
|
+
*/
|
|
1506
|
+
async checkSetup(setup, options = {}) {
|
|
1507
|
+
return this.request('POST', '/api/my-crm/v1/setup/check', { setup, ...(options.replaceBrief ? { replace_brief: true } : {}) }, false);
|
|
1508
|
+
}
|
|
1509
|
+
/**
|
|
1510
|
+
* Set it up, all in one go or not at all: the thing, its fields, sections, stages, links, small
|
|
1511
|
+
* related things, lists and the brief. On one of their things it only adds (nothing is removed,
|
|
1512
|
+
* renamed or retyped) and needs `base` from checkSetup() (without it, `validation`, server code
|
|
1513
|
+
* `base_required`); if the thing changed since, it throws
|
|
1514
|
+
* `conflict` (server code `changed`): check again. Only an Owner's key may apply (anyone else's
|
|
1515
|
+
* throws `forbidden`, server code `owner_only`). Apply only what the owner agreed to.
|
|
1516
|
+
*
|
|
1517
|
+
* @example
|
|
1518
|
+
* const { setup } = await db.proposeSetup('property bookings');
|
|
1519
|
+
* const made = await db.applySetup(setup); // once the owner has agreed to checkSetup()'s sentence
|
|
1520
|
+
* console.log(made.kind.key, made.land);
|
|
1521
|
+
*/
|
|
1522
|
+
async applySetup(setup, options = {}) {
|
|
1523
|
+
return this.request('POST', '/api/my-crm/v1/setup/apply', {
|
|
1524
|
+
setup, ...(options.base ? { base: options.base } : {}), ...(options.replaceBrief ? { replace_brief: true } : {}),
|
|
1525
|
+
}, false);
|
|
1526
|
+
}
|
|
1441
1527
|
/**
|
|
1442
1528
|
* Create a record (returns its id), or change one with options.id. Every value is checked
|
|
1443
1529
|
* against the kind. crm:write, Support Plus and above: the account's token, or an app key made
|
|
@@ -1701,6 +1787,10 @@ export class AwesomateClient {
|
|
|
1701
1787
|
return { ...r.usage, own_key: r.own_key };
|
|
1702
1788
|
}
|
|
1703
1789
|
}
|
|
1790
|
+
function briefFromWire(r) {
|
|
1791
|
+
const { session_id, job_id, ...rest } = r;
|
|
1792
|
+
return { ...rest, ...(session_id ? { sessionId: session_id } : {}), ...(job_id ? { jobId: job_id } : {}) };
|
|
1793
|
+
}
|
|
1704
1794
|
function memoryStorage() {
|
|
1705
1795
|
const m = new Map();
|
|
1706
1796
|
return { getItem: (k) => m.get(k) ?? null, setItem: (k, v) => { m.set(k, v); }, removeItem: (k) => { m.delete(k); } };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awesomate/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.0",
|
|
4
4
|
"description": "Your own Awesomate data from Node and the browser: query contacts and app data with generated types, and sign your app's own users in",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|