@myapihq/sdk 1.2.8 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +4 -4
- package/dist/config.d.ts +3 -0
- package/dist/config.js +4 -1
- package/dist/email.d.ts +27 -0
- package/dist/email.js +31 -0
- package/dist/git.d.ts +96 -0
- package/dist/git.js +121 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -1
- package/dist/pixel.d.ts +1 -1
- package/dist/pixel.js +4 -2
- package/dist/queue.d.ts +49 -0
- package/dist/queue.js +71 -0
- package/dist/services.d.ts +1 -1
- package/dist/services.js +54 -14
- package/dist/task.d.ts +78 -0
- package/dist/task.js +131 -0
- package/dist/workflow.d.ts +6 -0
- package/package.json +1 -1
- package/src/client.ts +4 -4
- package/src/config.ts +3 -0
- package/src/email.ts +53 -0
- package/src/git.ts +171 -0
- package/src/index.ts +3 -0
- package/src/pixel.ts +4 -2
- package/src/queue.ts +110 -0
- package/src/services.ts +57 -15
- package/src/task.ts +198 -0
- package/src/workflow.ts +7 -1
package/src/services.ts
CHANGED
|
@@ -20,7 +20,8 @@ export type ServiceCategory =
|
|
|
20
20
|
| 'capture' // inbound: webhook, pixel
|
|
21
21
|
| 'data' // read/filter the data layer: people, company, audience, crm
|
|
22
22
|
| 'store' // persist bytes/JSON: storage, database
|
|
23
|
-
| 'compute' // run code or inference: llm,
|
|
23
|
+
| 'compute' // run code or inference: llm, fn
|
|
24
|
+
| 'orchestrate' // control flow: workflow, queue, task
|
|
24
25
|
| 'identity' // auth/billing/account: hq
|
|
25
26
|
| 'infra'; // platform glue: domain
|
|
26
27
|
|
|
@@ -65,16 +66,16 @@ export const SERVICES: readonly ServiceMeta[] = [
|
|
|
65
66
|
|
|
66
67
|
// ── send ────────────────────────────────────────────────────────────
|
|
67
68
|
{
|
|
68
|
-
// Customer-facing email surface
|
|
69
|
-
// warmup
|
|
70
|
-
//
|
|
71
|
-
//
|
|
69
|
+
// Customer-facing email surface — mailboxes, campaigns, templates,
|
|
70
|
+
// warmup. Launched 2026-05-17: the backend lifted the pre-launch gate
|
|
71
|
+
// (the [disabled, pre-launch] / 503 SERVICE_NOT_LAUNCHED state is gone).
|
|
72
|
+
// Sibling `my-email-verify-api` is the sync single-address verifier.
|
|
72
73
|
module: 'email',
|
|
73
74
|
skill: 'my-email-api',
|
|
74
75
|
domain: 'myemailapi.com',
|
|
75
76
|
description: 'Send transactional and bulk email from your own domain. Mailboxes, AI templates, drip campaigns, and warmup.',
|
|
76
77
|
category: 'send',
|
|
77
|
-
status: '
|
|
78
|
+
status: 'ga',
|
|
78
79
|
keywords: k('email', 'transactional', 'campaign', 'smtp'),
|
|
79
80
|
},
|
|
80
81
|
{
|
|
@@ -201,15 +202,6 @@ export const SERVICES: readonly ServiceMeta[] = [
|
|
|
201
202
|
},
|
|
202
203
|
|
|
203
204
|
// ── compute ─────────────────────────────────────────────────────────
|
|
204
|
-
{
|
|
205
|
-
module: 'workflow',
|
|
206
|
-
skill: 'my-workflow-api',
|
|
207
|
-
domain: 'myworkflowapi.com',
|
|
208
|
-
description: 'React to inbound webhooks with step chains: send email, post to Slack, call HTTP URLs.',
|
|
209
|
-
category: 'compute',
|
|
210
|
-
status: 'ga',
|
|
211
|
-
keywords: k('workflow', 'automation', 'orchestration'),
|
|
212
|
-
},
|
|
213
205
|
{
|
|
214
206
|
module: 'llm',
|
|
215
207
|
skill: 'my-llm-api',
|
|
@@ -266,4 +258,54 @@ export const SERVICES: readonly ServiceMeta[] = [
|
|
|
266
258
|
status: 'preview',
|
|
267
259
|
keywords: k('container', 'cloud-run', 'service', 'worker', 'job'),
|
|
268
260
|
},
|
|
261
|
+
{
|
|
262
|
+
// CLI top-level command + SDK namespace are both `git`; skill directory
|
|
263
|
+
// is `my-git-api`. A stateless git-over-HTTP surface (go-git engine;
|
|
264
|
+
// objects in GCS, refs in Postgres) — repos, commits, branches, history.
|
|
265
|
+
module: 'git',
|
|
266
|
+
skill: 'my-git-api',
|
|
267
|
+
domain: 'mygitapi.com',
|
|
268
|
+
description: 'Hosted git repositories over HTTP — create repos, commit files, manage branches and tags, read trees, blobs, history, and diffs. No local clone required.',
|
|
269
|
+
category: 'store',
|
|
270
|
+
status: 'preview',
|
|
271
|
+
keywords: k('git', 'repository', 'version-control', 'commit', 'scm'),
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
// ── orchestrate ─────────────────────────────────────────────────────
|
|
275
|
+
// Control flow, not actions. workflow = event→action, queue = durable
|
|
276
|
+
// async machine work, task = work that needs an agent/human decision.
|
|
277
|
+
// See docs/orchestration-decision-guide.md.
|
|
278
|
+
{
|
|
279
|
+
module: 'workflow',
|
|
280
|
+
skill: 'my-workflow-api',
|
|
281
|
+
domain: 'myworkflowapi.com',
|
|
282
|
+
description: 'React to inbound webhooks with step chains: send email, post to Slack, call HTTP URLs.',
|
|
283
|
+
category: 'orchestrate',
|
|
284
|
+
status: 'ga',
|
|
285
|
+
keywords: k('workflow', 'automation', 'orchestration'),
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
// CLI top-level command + SDK namespace are both `queue`; skill
|
|
289
|
+
// directory is `my-queue-api`. A durable HTTP-consumer job queue —
|
|
290
|
+
// retry/backoff, concurrency caps, dependency DAG.
|
|
291
|
+
module: 'queue',
|
|
292
|
+
skill: 'my-queue-api',
|
|
293
|
+
domain: 'myqueueapi.com',
|
|
294
|
+
description: 'Durable job queue — enqueue work and have it retried against your HTTP consumer, with concurrency caps and a dependency DAG.',
|
|
295
|
+
category: 'orchestrate',
|
|
296
|
+
status: 'preview',
|
|
297
|
+
keywords: k('queue', 'job-queue', 'background-jobs', 'retry', 'async'),
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
// CLI top-level command + SDK namespace are both `task`; skill
|
|
301
|
+
// directory is `my-task-api`. The agent-task queue — the agent-loop
|
|
302
|
+
// hot path: file, claim under a lease, resolve.
|
|
303
|
+
module: 'task',
|
|
304
|
+
skill: 'my-task-api',
|
|
305
|
+
domain: 'mytaskapi.com',
|
|
306
|
+
description: 'Agent-task queue — file units of work, rank them, claim under a lease, then resolve, fail, or cancel. The agent-loop hot path.',
|
|
307
|
+
category: 'orchestrate',
|
|
308
|
+
status: 'preview',
|
|
309
|
+
keywords: k('task', 'task-queue', 'agent-loop', 'work-queue', 'lease'),
|
|
310
|
+
},
|
|
269
311
|
] as const;
|
package/src/task.ts
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { request } from './client';
|
|
2
|
+
import { TASK_BASE as BASE_URL } from './config';
|
|
3
|
+
import type { Exposes } from './exposes';
|
|
4
|
+
|
|
5
|
+
// Backend: my-task-api per myapi-hq/internal/routes/task/. An agent-task
|
|
6
|
+
// queue — the agent-loop hot path. Tasks are created, ranked by score,
|
|
7
|
+
// claimed under a lease, then resolved/failed/cancelled. A task with
|
|
8
|
+
// unresolved depends_on starts blocked; lease expiry auto-reverts a claimed
|
|
9
|
+
// task to open.
|
|
10
|
+
export const EXPOSES: Exposes = [
|
|
11
|
+
'POST /task/orgs/{org_id}/tasks',
|
|
12
|
+
'GET /task/orgs/{org_id}/tasks',
|
|
13
|
+
'GET /task/orgs/{org_id}/tasks/{id}',
|
|
14
|
+
'DELETE /task/orgs/{org_id}/tasks/{id}',
|
|
15
|
+
'GET /task/orgs/{org_id}/tasks/{id}/body',
|
|
16
|
+
'POST /task/orgs/{org_id}/tasks/{id}/claim',
|
|
17
|
+
'POST /task/orgs/{org_id}/tasks/{id}/extend',
|
|
18
|
+
'POST /task/orgs/{org_id}/tasks/{id}/fail',
|
|
19
|
+
'POST /task/orgs/{org_id}/tasks/{id}/resolve',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export type TaskStatus = 'open' | 'claimed' | 'blocked' | 'resolved' | 'failed' | 'cancelled';
|
|
23
|
+
|
|
24
|
+
export type TaskImportance = 'low' | 'normal' | 'high' | 'critical';
|
|
25
|
+
|
|
26
|
+
// Event matcher: a matching platform_events delivery auto-resolves the task.
|
|
27
|
+
export interface ResolveOn {
|
|
28
|
+
event_type: string;
|
|
29
|
+
field?: string;
|
|
30
|
+
value?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The projected list row — list deliberately returns only this triage-sized
|
|
34
|
+
// shape, not the full task. Read the body tier separately via getTaskBody.
|
|
35
|
+
export interface TaskRef {
|
|
36
|
+
id: string;
|
|
37
|
+
description: string;
|
|
38
|
+
score: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Full task object. `claim` and `extend` return this — lease state is read
|
|
42
|
+
// off `lease_expires_at` / `claimed_by`, not a separate lease object.
|
|
43
|
+
export interface Task {
|
|
44
|
+
id: string;
|
|
45
|
+
org_id: string;
|
|
46
|
+
description: string;
|
|
47
|
+
status: TaskStatus;
|
|
48
|
+
importance: TaskImportance;
|
|
49
|
+
score: number;
|
|
50
|
+
tags: string[];
|
|
51
|
+
source: string;
|
|
52
|
+
depends_on: string[];
|
|
53
|
+
payload_url: string;
|
|
54
|
+
assignee?: string;
|
|
55
|
+
resolve_on?: ResolveOn;
|
|
56
|
+
dedup_key?: string;
|
|
57
|
+
claimed_by?: string;
|
|
58
|
+
lease_expires_at?: string;
|
|
59
|
+
due_at?: string;
|
|
60
|
+
fail_reason?: string;
|
|
61
|
+
resolved_at?: string;
|
|
62
|
+
created_at: string;
|
|
63
|
+
updated_at: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Backlog health from the list envelope's meta — the agent's "am I keeping
|
|
67
|
+
// up" signal. `shown` is how many rows came back; `total_open` is the whole
|
|
68
|
+
// open queue depth.
|
|
69
|
+
export interface TaskListMeta {
|
|
70
|
+
shown: number;
|
|
71
|
+
total_open: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface TaskListResult {
|
|
75
|
+
tasks: TaskRef[];
|
|
76
|
+
meta: TaskListMeta;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface CreateTaskOptions {
|
|
80
|
+
description: string;
|
|
81
|
+
// Full Markdown context. Staged to the body tier; read once via getTaskBody.
|
|
82
|
+
body?: string;
|
|
83
|
+
importance?: TaskImportance;
|
|
84
|
+
dueAt?: string;
|
|
85
|
+
assignee?: string;
|
|
86
|
+
tags?: string[];
|
|
87
|
+
// Task ids this task waits on. Immutable — declared only at creation.
|
|
88
|
+
// Same shape as queue.enqueue's depends_on.
|
|
89
|
+
dependsOn?: string[];
|
|
90
|
+
dedupKey?: string;
|
|
91
|
+
resolveOn?: ResolveOn;
|
|
92
|
+
source?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface ListTaskOptions {
|
|
96
|
+
status?: string;
|
|
97
|
+
tag?: string;
|
|
98
|
+
importance?: string;
|
|
99
|
+
assignee?: string;
|
|
100
|
+
source?: string;
|
|
101
|
+
limit?: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function tasksBase(orgId: string): string {
|
|
105
|
+
return `${BASE_URL}/task/orgs/${encodeURIComponent(orgId)}/tasks`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function taskBase(orgId: string, id: string): string {
|
|
109
|
+
return `${tasksBase(orgId)}/${encodeURIComponent(id)}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// createTask is idempotent on (org_id, dedup_key). A task with unresolved
|
|
113
|
+
// depends_on starts blocked; one with an assignee emails them a magic link.
|
|
114
|
+
export async function createTask(apiKey: string, orgId: string, opts: CreateTaskOptions): Promise<Task> {
|
|
115
|
+
const body: Record<string, unknown> = { description: opts.description };
|
|
116
|
+
if (opts.body !== undefined) body.body = opts.body;
|
|
117
|
+
if (opts.importance !== undefined) body.importance = opts.importance;
|
|
118
|
+
if (opts.dueAt !== undefined) body.due_at = opts.dueAt;
|
|
119
|
+
if (opts.assignee !== undefined) body.assignee = opts.assignee;
|
|
120
|
+
if (opts.tags !== undefined) body.tags = opts.tags;
|
|
121
|
+
if (opts.dependsOn !== undefined) body.depends_on = opts.dependsOn;
|
|
122
|
+
if (opts.dedupKey !== undefined) body.dedup_key = opts.dedupKey;
|
|
123
|
+
if (opts.resolveOn !== undefined) body.resolve_on = opts.resolveOn;
|
|
124
|
+
if (opts.source !== undefined) body.source = opts.source;
|
|
125
|
+
return request('POST', tasksBase(orgId), apiKey, body);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// listTasks returns the ranked open queue (projected to TaskRef) plus the
|
|
129
|
+
// backlog counts. Default: top 20 open tasks by score. The backend nests
|
|
130
|
+
// {shown, total_open} inside the response `data.meta` (not the envelope meta).
|
|
131
|
+
export async function listTasks(apiKey: string, orgId: string, opts: ListTaskOptions = {}): Promise<TaskListResult> {
|
|
132
|
+
const q = new URLSearchParams();
|
|
133
|
+
if (opts.status) q.set('status', opts.status);
|
|
134
|
+
if (opts.tag) q.set('tag', opts.tag);
|
|
135
|
+
if (opts.importance) q.set('importance', opts.importance);
|
|
136
|
+
if (opts.assignee) q.set('assignee', opts.assignee);
|
|
137
|
+
if (opts.source) q.set('source', opts.source);
|
|
138
|
+
if (opts.limit !== undefined) q.set('limit', String(opts.limit));
|
|
139
|
+
const qs = q.toString();
|
|
140
|
+
const res = await request<{ tasks?: TaskRef[]; meta?: Partial<TaskListMeta> }>(
|
|
141
|
+
'GET', `${tasksBase(orgId)}${qs ? `?${qs}` : ''}`, apiKey,
|
|
142
|
+
);
|
|
143
|
+
const tasks = res?.tasks ?? [];
|
|
144
|
+
const m = res?.meta ?? {};
|
|
145
|
+
return {
|
|
146
|
+
tasks,
|
|
147
|
+
meta: {
|
|
148
|
+
shown: typeof m.shown === 'number' ? m.shown : tasks.length,
|
|
149
|
+
total_open: typeof m.total_open === 'number' ? m.total_open : tasks.length,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// getTask returns the full task object. It deliberately does NOT fetch the
|
|
155
|
+
// Markdown body tier — call getTaskBody for that, once, on commit.
|
|
156
|
+
export async function getTask(apiKey: string, orgId: string, id: string): Promise<Task> {
|
|
157
|
+
return request('GET', taskBase(orgId, id), apiKey);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// getTaskBody fetches the body tier — the full Markdown context. Separate
|
|
161
|
+
// call by design: it keeps the list/get path token-cheap. The endpoint
|
|
162
|
+
// returns { task_id, body }.
|
|
163
|
+
export async function getTaskBody(apiKey: string, orgId: string, id: string): Promise<string> {
|
|
164
|
+
const res = await request<{ task_id: string; body: string }>('GET', `${taskBase(orgId, id)}/body`, apiKey);
|
|
165
|
+
return res?.body ?? '';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// claimTask takes an atomic lease (default 10 min) and returns the full
|
|
169
|
+
// updated task — lease state is on `lease_expires_at` / `claimed_by`.
|
|
170
|
+
export async function claimTask(apiKey: string, orgId: string, id: string, opts: { leaseSeconds?: number; worker?: string } = {}): Promise<Task> {
|
|
171
|
+
const body: Record<string, unknown> = {};
|
|
172
|
+
if (opts.leaseSeconds !== undefined) body.lease_seconds = opts.leaseSeconds;
|
|
173
|
+
if (opts.worker !== undefined) body.worker = opts.worker;
|
|
174
|
+
return request('POST', `${taskBase(orgId, id)}/claim`, apiKey, body);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// extendTask is a heartbeat for long work — extends a live claim.
|
|
178
|
+
export async function extendTask(apiKey: string, orgId: string, id: string, opts: { leaseSeconds?: number } = {}): Promise<Task> {
|
|
179
|
+
const body: Record<string, unknown> = {};
|
|
180
|
+
if (opts.leaseSeconds !== undefined) body.lease_seconds = opts.leaseSeconds;
|
|
181
|
+
return request('POST', `${taskBase(orgId, id)}/extend`, apiKey, body);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// resolveTask resolves a task (terminal) — unblocks any dependents.
|
|
185
|
+
export async function resolveTask(apiKey: string, orgId: string, id: string): Promise<Task> {
|
|
186
|
+
return request('POST', `${taskBase(orgId, id)}/resolve`, apiKey, {});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// failTask fails a task (terminal, with a reason). Does not auto-retry;
|
|
190
|
+
// dependents that can never proceed are auto-failed.
|
|
191
|
+
export async function failTask(apiKey: string, orgId: string, id: string, reason: string): Promise<Task> {
|
|
192
|
+
return request('POST', `${taskBase(orgId, id)}/fail`, apiKey, { reason });
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// cancelTask cancels a task (terminal, distinct from fail).
|
|
196
|
+
export async function cancelTask(apiKey: string, orgId: string, id: string): Promise<void> {
|
|
197
|
+
return request('DELETE', taskBase(orgId, id), apiKey);
|
|
198
|
+
}
|
package/src/workflow.ts
CHANGED
|
@@ -33,7 +33,13 @@ export type WorkflowStep =
|
|
|
33
33
|
// Backend (2026-05-15): `http_request` step is now accepted (alias: `http`).
|
|
34
34
|
// POSTs (or other method) to `url` with the inbound payload; supports
|
|
35
35
|
// template substitution in url/body the same way email steps do.
|
|
36
|
-
| { type: 'http_request' | 'http'; url: string; method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'; body?: string; headers?: Record<string, string> }
|
|
36
|
+
| { type: 'http_request' | 'http'; url: string; method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'; body?: string; headers?: Record<string, string> }
|
|
37
|
+
// `enqueue_job` (alias: `enqueue`) hands durable work to my-queue-api —
|
|
38
|
+
// the step enqueues a job on `queue` carrying `payload` (template
|
|
39
|
+
// substitution supported, same as email/http steps). The workflow stays
|
|
40
|
+
// the trigger layer; the queue owns retry/durability. Backend: pending —
|
|
41
|
+
// see docs/cross-repo-prompts/backend-orchestration-composition.md.
|
|
42
|
+
| { type: 'enqueue_job' | 'enqueue'; queue: string; payload?: string; dedup_key?: string; delay_seconds?: number };
|
|
37
43
|
|
|
38
44
|
export interface Workflow {
|
|
39
45
|
id: string;
|