@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/dist/task.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Exposes } from './exposes';
|
|
2
|
+
export declare const EXPOSES: Exposes;
|
|
3
|
+
export type TaskStatus = 'open' | 'claimed' | 'blocked' | 'resolved' | 'failed' | 'cancelled';
|
|
4
|
+
export type TaskImportance = 'low' | 'normal' | 'high' | 'critical';
|
|
5
|
+
export interface ResolveOn {
|
|
6
|
+
event_type: string;
|
|
7
|
+
field?: string;
|
|
8
|
+
value?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface TaskRef {
|
|
11
|
+
id: string;
|
|
12
|
+
description: string;
|
|
13
|
+
score: number;
|
|
14
|
+
}
|
|
15
|
+
export interface Task {
|
|
16
|
+
id: string;
|
|
17
|
+
org_id: string;
|
|
18
|
+
description: string;
|
|
19
|
+
status: TaskStatus;
|
|
20
|
+
importance: TaskImportance;
|
|
21
|
+
score: number;
|
|
22
|
+
tags: string[];
|
|
23
|
+
source: string;
|
|
24
|
+
depends_on: string[];
|
|
25
|
+
payload_url: string;
|
|
26
|
+
assignee?: string;
|
|
27
|
+
resolve_on?: ResolveOn;
|
|
28
|
+
dedup_key?: string;
|
|
29
|
+
claimed_by?: string;
|
|
30
|
+
lease_expires_at?: string;
|
|
31
|
+
due_at?: string;
|
|
32
|
+
fail_reason?: string;
|
|
33
|
+
resolved_at?: string;
|
|
34
|
+
created_at: string;
|
|
35
|
+
updated_at: string;
|
|
36
|
+
}
|
|
37
|
+
export interface TaskListMeta {
|
|
38
|
+
shown: number;
|
|
39
|
+
total_open: number;
|
|
40
|
+
}
|
|
41
|
+
export interface TaskListResult {
|
|
42
|
+
tasks: TaskRef[];
|
|
43
|
+
meta: TaskListMeta;
|
|
44
|
+
}
|
|
45
|
+
export interface CreateTaskOptions {
|
|
46
|
+
description: string;
|
|
47
|
+
body?: string;
|
|
48
|
+
importance?: TaskImportance;
|
|
49
|
+
dueAt?: string;
|
|
50
|
+
assignee?: string;
|
|
51
|
+
tags?: string[];
|
|
52
|
+
dependsOn?: string[];
|
|
53
|
+
dedupKey?: string;
|
|
54
|
+
resolveOn?: ResolveOn;
|
|
55
|
+
source?: string;
|
|
56
|
+
}
|
|
57
|
+
export interface ListTaskOptions {
|
|
58
|
+
status?: string;
|
|
59
|
+
tag?: string;
|
|
60
|
+
importance?: string;
|
|
61
|
+
assignee?: string;
|
|
62
|
+
source?: string;
|
|
63
|
+
limit?: number;
|
|
64
|
+
}
|
|
65
|
+
export declare function createTask(apiKey: string, orgId: string, opts: CreateTaskOptions): Promise<Task>;
|
|
66
|
+
export declare function listTasks(apiKey: string, orgId: string, opts?: ListTaskOptions): Promise<TaskListResult>;
|
|
67
|
+
export declare function getTask(apiKey: string, orgId: string, id: string): Promise<Task>;
|
|
68
|
+
export declare function getTaskBody(apiKey: string, orgId: string, id: string): Promise<string>;
|
|
69
|
+
export declare function claimTask(apiKey: string, orgId: string, id: string, opts?: {
|
|
70
|
+
leaseSeconds?: number;
|
|
71
|
+
worker?: string;
|
|
72
|
+
}): Promise<Task>;
|
|
73
|
+
export declare function extendTask(apiKey: string, orgId: string, id: string, opts?: {
|
|
74
|
+
leaseSeconds?: number;
|
|
75
|
+
}): Promise<Task>;
|
|
76
|
+
export declare function resolveTask(apiKey: string, orgId: string, id: string): Promise<Task>;
|
|
77
|
+
export declare function failTask(apiKey: string, orgId: string, id: string, reason: string): Promise<Task>;
|
|
78
|
+
export declare function cancelTask(apiKey: string, orgId: string, id: string): Promise<void>;
|
package/dist/task.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EXPOSES = void 0;
|
|
4
|
+
exports.createTask = createTask;
|
|
5
|
+
exports.listTasks = listTasks;
|
|
6
|
+
exports.getTask = getTask;
|
|
7
|
+
exports.getTaskBody = getTaskBody;
|
|
8
|
+
exports.claimTask = claimTask;
|
|
9
|
+
exports.extendTask = extendTask;
|
|
10
|
+
exports.resolveTask = resolveTask;
|
|
11
|
+
exports.failTask = failTask;
|
|
12
|
+
exports.cancelTask = cancelTask;
|
|
13
|
+
const client_1 = require("./client");
|
|
14
|
+
const config_1 = require("./config");
|
|
15
|
+
// Backend: my-task-api per myapi-hq/internal/routes/task/. An agent-task
|
|
16
|
+
// queue — the agent-loop hot path. Tasks are created, ranked by score,
|
|
17
|
+
// claimed under a lease, then resolved/failed/cancelled. A task with
|
|
18
|
+
// unresolved depends_on starts blocked; lease expiry auto-reverts a claimed
|
|
19
|
+
// task to open.
|
|
20
|
+
exports.EXPOSES = [
|
|
21
|
+
'POST /task/orgs/{org_id}/tasks',
|
|
22
|
+
'GET /task/orgs/{org_id}/tasks',
|
|
23
|
+
'GET /task/orgs/{org_id}/tasks/{id}',
|
|
24
|
+
'DELETE /task/orgs/{org_id}/tasks/{id}',
|
|
25
|
+
'GET /task/orgs/{org_id}/tasks/{id}/body',
|
|
26
|
+
'POST /task/orgs/{org_id}/tasks/{id}/claim',
|
|
27
|
+
'POST /task/orgs/{org_id}/tasks/{id}/extend',
|
|
28
|
+
'POST /task/orgs/{org_id}/tasks/{id}/fail',
|
|
29
|
+
'POST /task/orgs/{org_id}/tasks/{id}/resolve',
|
|
30
|
+
];
|
|
31
|
+
function tasksBase(orgId) {
|
|
32
|
+
return `${config_1.TASK_BASE}/task/orgs/${encodeURIComponent(orgId)}/tasks`;
|
|
33
|
+
}
|
|
34
|
+
function taskBase(orgId, id) {
|
|
35
|
+
return `${tasksBase(orgId)}/${encodeURIComponent(id)}`;
|
|
36
|
+
}
|
|
37
|
+
// createTask is idempotent on (org_id, dedup_key). A task with unresolved
|
|
38
|
+
// depends_on starts blocked; one with an assignee emails them a magic link.
|
|
39
|
+
async function createTask(apiKey, orgId, opts) {
|
|
40
|
+
const body = { description: opts.description };
|
|
41
|
+
if (opts.body !== undefined)
|
|
42
|
+
body.body = opts.body;
|
|
43
|
+
if (opts.importance !== undefined)
|
|
44
|
+
body.importance = opts.importance;
|
|
45
|
+
if (opts.dueAt !== undefined)
|
|
46
|
+
body.due_at = opts.dueAt;
|
|
47
|
+
if (opts.assignee !== undefined)
|
|
48
|
+
body.assignee = opts.assignee;
|
|
49
|
+
if (opts.tags !== undefined)
|
|
50
|
+
body.tags = opts.tags;
|
|
51
|
+
if (opts.dependsOn !== undefined)
|
|
52
|
+
body.depends_on = opts.dependsOn;
|
|
53
|
+
if (opts.dedupKey !== undefined)
|
|
54
|
+
body.dedup_key = opts.dedupKey;
|
|
55
|
+
if (opts.resolveOn !== undefined)
|
|
56
|
+
body.resolve_on = opts.resolveOn;
|
|
57
|
+
if (opts.source !== undefined)
|
|
58
|
+
body.source = opts.source;
|
|
59
|
+
return (0, client_1.request)('POST', tasksBase(orgId), apiKey, body);
|
|
60
|
+
}
|
|
61
|
+
// listTasks returns the ranked open queue (projected to TaskRef) plus the
|
|
62
|
+
// backlog counts. Default: top 20 open tasks by score. The backend nests
|
|
63
|
+
// {shown, total_open} inside the response `data.meta` (not the envelope meta).
|
|
64
|
+
async function listTasks(apiKey, orgId, opts = {}) {
|
|
65
|
+
const q = new URLSearchParams();
|
|
66
|
+
if (opts.status)
|
|
67
|
+
q.set('status', opts.status);
|
|
68
|
+
if (opts.tag)
|
|
69
|
+
q.set('tag', opts.tag);
|
|
70
|
+
if (opts.importance)
|
|
71
|
+
q.set('importance', opts.importance);
|
|
72
|
+
if (opts.assignee)
|
|
73
|
+
q.set('assignee', opts.assignee);
|
|
74
|
+
if (opts.source)
|
|
75
|
+
q.set('source', opts.source);
|
|
76
|
+
if (opts.limit !== undefined)
|
|
77
|
+
q.set('limit', String(opts.limit));
|
|
78
|
+
const qs = q.toString();
|
|
79
|
+
const res = await (0, client_1.request)('GET', `${tasksBase(orgId)}${qs ? `?${qs}` : ''}`, apiKey);
|
|
80
|
+
const tasks = res?.tasks ?? [];
|
|
81
|
+
const m = res?.meta ?? {};
|
|
82
|
+
return {
|
|
83
|
+
tasks,
|
|
84
|
+
meta: {
|
|
85
|
+
shown: typeof m.shown === 'number' ? m.shown : tasks.length,
|
|
86
|
+
total_open: typeof m.total_open === 'number' ? m.total_open : tasks.length,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
// getTask returns the full task object. It deliberately does NOT fetch the
|
|
91
|
+
// Markdown body tier — call getTaskBody for that, once, on commit.
|
|
92
|
+
async function getTask(apiKey, orgId, id) {
|
|
93
|
+
return (0, client_1.request)('GET', taskBase(orgId, id), apiKey);
|
|
94
|
+
}
|
|
95
|
+
// getTaskBody fetches the body tier — the full Markdown context. Separate
|
|
96
|
+
// call by design: it keeps the list/get path token-cheap. The endpoint
|
|
97
|
+
// returns { task_id, body }.
|
|
98
|
+
async function getTaskBody(apiKey, orgId, id) {
|
|
99
|
+
const res = await (0, client_1.request)('GET', `${taskBase(orgId, id)}/body`, apiKey);
|
|
100
|
+
return res?.body ?? '';
|
|
101
|
+
}
|
|
102
|
+
// claimTask takes an atomic lease (default 10 min) and returns the full
|
|
103
|
+
// updated task — lease state is on `lease_expires_at` / `claimed_by`.
|
|
104
|
+
async function claimTask(apiKey, orgId, id, opts = {}) {
|
|
105
|
+
const body = {};
|
|
106
|
+
if (opts.leaseSeconds !== undefined)
|
|
107
|
+
body.lease_seconds = opts.leaseSeconds;
|
|
108
|
+
if (opts.worker !== undefined)
|
|
109
|
+
body.worker = opts.worker;
|
|
110
|
+
return (0, client_1.request)('POST', `${taskBase(orgId, id)}/claim`, apiKey, body);
|
|
111
|
+
}
|
|
112
|
+
// extendTask is a heartbeat for long work — extends a live claim.
|
|
113
|
+
async function extendTask(apiKey, orgId, id, opts = {}) {
|
|
114
|
+
const body = {};
|
|
115
|
+
if (opts.leaseSeconds !== undefined)
|
|
116
|
+
body.lease_seconds = opts.leaseSeconds;
|
|
117
|
+
return (0, client_1.request)('POST', `${taskBase(orgId, id)}/extend`, apiKey, body);
|
|
118
|
+
}
|
|
119
|
+
// resolveTask resolves a task (terminal) — unblocks any dependents.
|
|
120
|
+
async function resolveTask(apiKey, orgId, id) {
|
|
121
|
+
return (0, client_1.request)('POST', `${taskBase(orgId, id)}/resolve`, apiKey, {});
|
|
122
|
+
}
|
|
123
|
+
// failTask fails a task (terminal, with a reason). Does not auto-retry;
|
|
124
|
+
// dependents that can never proceed are auto-failed.
|
|
125
|
+
async function failTask(apiKey, orgId, id, reason) {
|
|
126
|
+
return (0, client_1.request)('POST', `${taskBase(orgId, id)}/fail`, apiKey, { reason });
|
|
127
|
+
}
|
|
128
|
+
// cancelTask cancels a task (terminal, distinct from fail).
|
|
129
|
+
async function cancelTask(apiKey, orgId, id) {
|
|
130
|
+
return (0, client_1.request)('DELETE', taskBase(orgId, id), apiKey);
|
|
131
|
+
}
|
package/dist/workflow.d.ts
CHANGED
|
@@ -28,6 +28,12 @@ export type WorkflowStep = {
|
|
|
28
28
|
method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
|
|
29
29
|
body?: string;
|
|
30
30
|
headers?: Record<string, string>;
|
|
31
|
+
} | {
|
|
32
|
+
type: 'enqueue_job' | 'enqueue';
|
|
33
|
+
queue: string;
|
|
34
|
+
payload?: string;
|
|
35
|
+
dedup_key?: string;
|
|
36
|
+
delay_seconds?: number;
|
|
31
37
|
};
|
|
32
38
|
export interface Workflow {
|
|
33
39
|
id: string;
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -72,8 +72,8 @@ export async function request<T>(
|
|
|
72
72
|
const err = result?.error;
|
|
73
73
|
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
74
74
|
const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
|
|
75
|
-
const
|
|
76
|
-
throw new MyApiError(code, response.status, detail,
|
|
75
|
+
const errBody = typeof err === 'object' ? err : undefined;
|
|
76
|
+
throw new MyApiError(code, response.status, detail, errBody);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
const apiResponse = result as ApiResponse<T>;
|
|
@@ -81,8 +81,8 @@ export async function request<T>(
|
|
|
81
81
|
const err = apiResponse.error as any;
|
|
82
82
|
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
83
83
|
const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
|
|
84
|
-
const
|
|
85
|
-
throw new MyApiError(code, response.status, detail,
|
|
84
|
+
const errBody = typeof err === 'object' ? err : undefined;
|
|
85
|
+
throw new MyApiError(code, response.status, detail, errBody);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
return apiResponse.data as T;
|
package/src/config.ts
CHANGED
|
@@ -17,6 +17,9 @@ export const FUNNEL_BASE = process.env.MYAPI_FUNNEL_URL ?? 'https://api.myfunn
|
|
|
17
17
|
export const FUNCTION_BASE= process.env.MYAPI_FUNCTION_URL?? GATEWAY;
|
|
18
18
|
export const PAYMENTS_BASE= process.env.MYAPI_PAYMENTS_URL?? GATEWAY;
|
|
19
19
|
export const CONTAINER_BASE=process.env.MYAPI_CONTAINER_URL?? GATEWAY;
|
|
20
|
+
export const GIT_BASE = process.env.MYAPI_GIT_URL ?? GATEWAY;
|
|
21
|
+
export const QUEUE_BASE = process.env.MYAPI_QUEUE_URL ?? GATEWAY;
|
|
22
|
+
export const TASK_BASE = process.env.MYAPI_TASK_URL ?? GATEWAY;
|
|
20
23
|
export const IMAGE_BASE = process.env.MYAPI_IMAGE_URL ?? 'https://api.myimageapi.com';
|
|
21
24
|
export const WEBHOOK_BASE = process.env.MYAPI_WEBHOOK_URL ?? 'https://api.mywebhookapi.com';
|
|
22
25
|
export const WORKFLOW_BASE= process.env.MYAPI_WORKFLOW_URL?? 'https://api.myworkflowapi.com';
|
package/src/email.ts
CHANGED
|
@@ -6,6 +6,8 @@ export const EXPOSES: Exposes = [
|
|
|
6
6
|
// Mailbox / sending activation
|
|
7
7
|
'POST /email/mailboxes/create',
|
|
8
8
|
'GET /email/mailboxes',
|
|
9
|
+
'PUT /email/mailboxes/{address}/forwarding',
|
|
10
|
+
'DELETE /email/mailboxes/{address}/forwarding',
|
|
9
11
|
'POST /email/sending/activate',
|
|
10
12
|
// Send + read
|
|
11
13
|
'POST /email/send',
|
|
@@ -43,6 +45,8 @@ export const EXPOSES: Exposes = [
|
|
|
43
45
|
'GET /email/orgs/{org_id}/campaigns/{campaign_id}/stats',
|
|
44
46
|
// Verify
|
|
45
47
|
'POST /email/orgs/{org_id}/verify',
|
|
48
|
+
'POST /email/orgs/{org_id}/verify-bulk',
|
|
49
|
+
'GET /email/orgs/{org_id}/verify-jobs/{id}',
|
|
46
50
|
];
|
|
47
51
|
|
|
48
52
|
export interface EmailMessage { message_id: string; from: string; subject: string; body?: string; received_at: string }
|
|
@@ -90,6 +94,43 @@ export async function verifyEmail(apiKey: string, orgId: string, email: string):
|
|
|
90
94
|
return request('POST', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/verify`, apiKey, { email });
|
|
91
95
|
}
|
|
92
96
|
|
|
97
|
+
// One address's outcome in a bulk-verification job.
|
|
98
|
+
export interface BulkVerifyResult {
|
|
99
|
+
email: string;
|
|
100
|
+
verdict: string; // deliverable | undeliverable | risky | unknown
|
|
101
|
+
confidence: number;
|
|
102
|
+
source: string; // phase1 | strategist | smtp
|
|
103
|
+
classification?: string;
|
|
104
|
+
mx?: string;
|
|
105
|
+
rcpt_code?: number;
|
|
106
|
+
catch_all?: boolean;
|
|
107
|
+
detail?: string;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface VerifyJob {
|
|
111
|
+
job_id: string;
|
|
112
|
+
status: string; // pending | running | done | failed
|
|
113
|
+
total: number;
|
|
114
|
+
completed?: number;
|
|
115
|
+
catch_all?: boolean;
|
|
116
|
+
results?: BulkVerifyResult[];
|
|
117
|
+
error?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// verifyBulk kicks off an asynchronous bulk verification (1-500 addresses)
|
|
121
|
+
// and returns immediately with a job_id. `catchAll` toggles the catch-all
|
|
122
|
+
// check on SMTP-probed addresses (default true). Poll getVerifyJob for
|
|
123
|
+
// status + results.
|
|
124
|
+
export async function verifyBulk(apiKey: string, orgId: string, emails: string[], catchAll?: boolean): Promise<VerifyJob> {
|
|
125
|
+
const body: { emails: string[]; catch_all?: boolean } = { emails };
|
|
126
|
+
if (catchAll !== undefined) body.catch_all = catchAll;
|
|
127
|
+
return request('POST', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/verify-bulk`, apiKey, body);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function getVerifyJob(apiKey: string, orgId: string, jobId: string): Promise<VerifyJob> {
|
|
131
|
+
return request('GET', `${BASE_URL}/email/orgs/${encodeURIComponent(orgId)}/verify-jobs/${encodeURIComponent(jobId)}`, apiKey);
|
|
132
|
+
}
|
|
133
|
+
|
|
93
134
|
// ── Mailbox ops (account-scoped) ─────────────────────────────────────────────
|
|
94
135
|
|
|
95
136
|
export async function createMailbox(apiKey: string, domain: string, username: string, displayName?: string): Promise<{ address: string; created_at: string }> {
|
|
@@ -112,6 +153,18 @@ export async function activateSending(apiKey: string, address: string): Promise<
|
|
|
112
153
|
return request('POST', `${BASE_URL}/email/sending/activate`, apiKey, { address });
|
|
113
154
|
}
|
|
114
155
|
|
|
156
|
+
// setForwarding redirects a copy of every incoming message to an external
|
|
157
|
+
// address (server-side; the original is kept in the mailbox). `forwardTo`
|
|
158
|
+
// must be a valid address and cannot equal the mailbox itself.
|
|
159
|
+
export async function setForwarding(apiKey: string, address: string, forwardTo: string): Promise<{ address: string; forward_to: string }> {
|
|
160
|
+
return request('PUT', `${BASE_URL}/email/mailboxes/${encodeURIComponent(address)}/forwarding`, apiKey, { forward_to: forwardTo });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// deleteForwarding stops forwarding for a mailbox.
|
|
164
|
+
export async function deleteForwarding(apiKey: string, address: string): Promise<void> {
|
|
165
|
+
return request('DELETE', `${BASE_URL}/email/mailboxes/${encodeURIComponent(address)}/forwarding`, apiKey);
|
|
166
|
+
}
|
|
167
|
+
|
|
115
168
|
// ── Sending and reading (account-scoped) ─────────────────────────────────────
|
|
116
169
|
|
|
117
170
|
export async function sendEmail(apiKey: string, payload: { from: string; to: string[]; subject: string; html?: string; text?: string; template_id?: string; template_vars?: Record<string, string> }): Promise<{ message_id: string }> {
|
package/src/git.ts
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { request } from './client';
|
|
2
|
+
import { GIT_BASE as BASE_URL } from './config';
|
|
3
|
+
import type { Exposes } from './exposes';
|
|
4
|
+
|
|
5
|
+
// Backend: my-git-api per myapi-hq/internal/routes/git/. A stateless
|
|
6
|
+
// git-over-HTTP surface — repositories, refs, commits, and history served
|
|
7
|
+
// by a go-git engine (objects in GCS packfiles, refs in Postgres).
|
|
8
|
+
export const EXPOSES: Exposes = [
|
|
9
|
+
'POST /git/orgs/{org_id}/repos',
|
|
10
|
+
'GET /git/orgs/{org_id}/repos',
|
|
11
|
+
'GET /git/orgs/{org_id}/repos/{repo}',
|
|
12
|
+
'DELETE /git/orgs/{org_id}/repos/{repo}',
|
|
13
|
+
'GET /git/orgs/{org_id}/repos/{repo}/refs',
|
|
14
|
+
'GET /git/orgs/{org_id}/repos/{repo}/tree/{ref}',
|
|
15
|
+
'GET /git/orgs/{org_id}/repos/{repo}/blob/{ref}/{path}',
|
|
16
|
+
'GET /git/orgs/{org_id}/repos/{repo}/commits',
|
|
17
|
+
'GET /git/orgs/{org_id}/repos/{repo}/commits/{sha}',
|
|
18
|
+
'GET /git/orgs/{org_id}/repos/{repo}/diff',
|
|
19
|
+
'POST /git/orgs/{org_id}/repos/{repo}/commits',
|
|
20
|
+
'POST /git/orgs/{org_id}/repos/{repo}/branches',
|
|
21
|
+
'DELETE /git/orgs/{org_id}/repos/{repo}/branches/{branch}',
|
|
22
|
+
'POST /git/orgs/{org_id}/repos/{repo}/tags',
|
|
23
|
+
'POST /git/orgs/{org_id}/repos/{repo}/merges',
|
|
24
|
+
'POST /git/orgs/{org_id}/repos/{repo}/repack',
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export interface RepoSummary { name: string }
|
|
28
|
+
export interface Repo {
|
|
29
|
+
name: string;
|
|
30
|
+
default_branch: string;
|
|
31
|
+
branches: number;
|
|
32
|
+
tags: number;
|
|
33
|
+
}
|
|
34
|
+
export interface RefInfo { name: string; sha: string }
|
|
35
|
+
export interface Refs {
|
|
36
|
+
head: string; // symbolic HEAD target, e.g. "refs/heads/main"
|
|
37
|
+
branches: RefInfo[];
|
|
38
|
+
tags: RefInfo[];
|
|
39
|
+
}
|
|
40
|
+
export interface TreeEntry {
|
|
41
|
+
name: string;
|
|
42
|
+
path: string;
|
|
43
|
+
type: 'file' | 'dir';
|
|
44
|
+
mode: string;
|
|
45
|
+
sha: string;
|
|
46
|
+
size: number;
|
|
47
|
+
}
|
|
48
|
+
export interface CommitInfo {
|
|
49
|
+
sha: string;
|
|
50
|
+
message: string;
|
|
51
|
+
author: string;
|
|
52
|
+
email: string;
|
|
53
|
+
when: string;
|
|
54
|
+
parents: string[];
|
|
55
|
+
}
|
|
56
|
+
export interface CommitResult { sha: string; tree: string; branch: string }
|
|
57
|
+
export interface RepackResult { packs_before: number; packs_after: number; objects: number }
|
|
58
|
+
export interface Blob { path: string; size: number; content_base64: string }
|
|
59
|
+
|
|
60
|
+
// One file edit in an atomic commit. Either `content` (UTF-8 text) or
|
|
61
|
+
// `content_base64` (binary); set `delete` to remove the path instead.
|
|
62
|
+
export interface FileChange {
|
|
63
|
+
path: string;
|
|
64
|
+
content?: string;
|
|
65
|
+
content_base64?: string;
|
|
66
|
+
delete?: boolean;
|
|
67
|
+
mode?: string; // "100644" (default), "100755" (exec), "120000" (symlink)
|
|
68
|
+
}
|
|
69
|
+
export interface CommitPayload {
|
|
70
|
+
branch: string; // required — short name or full ref
|
|
71
|
+
base?: string; // expected branch tip; "" => branch must not exist
|
|
72
|
+
message: string;
|
|
73
|
+
author?: { name: string; email: string };
|
|
74
|
+
changes: FileChange[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function repoBase(orgId: string, repo: string): string {
|
|
78
|
+
return `${BASE_URL}/git/orgs/${encodeURIComponent(orgId)}/repos/${encodeURIComponent(repo)}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ── Repos ────────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
export async function createRepo(apiKey: string, orgId: string, name: string, defaultBranch?: string): Promise<{ name: string; default_branch: string }> {
|
|
84
|
+
const body: Record<string, string> = { name };
|
|
85
|
+
if (defaultBranch) body.default_branch = defaultBranch;
|
|
86
|
+
return request('POST', `${BASE_URL}/git/orgs/${encodeURIComponent(orgId)}/repos`, apiKey, body);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function listRepos(apiKey: string, orgId: string): Promise<RepoSummary[]> {
|
|
90
|
+
const res = await request<{ repos?: RepoSummary[] }>('GET', `${BASE_URL}/git/orgs/${encodeURIComponent(orgId)}/repos`, apiKey);
|
|
91
|
+
return res?.repos ?? [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function getRepo(apiKey: string, orgId: string, repo: string): Promise<Repo> {
|
|
95
|
+
return request('GET', repoBase(orgId, repo), apiKey);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function deleteRepo(apiKey: string, orgId: string, repo: string): Promise<void> {
|
|
99
|
+
return request('DELETE', repoBase(orgId, repo), apiKey);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── Refs / history ───────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
export async function listRefs(apiKey: string, orgId: string, repo: string): Promise<Refs> {
|
|
105
|
+
return request('GET', `${repoBase(orgId, repo)}/refs`, apiKey);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// listCommits walks history from `ref` (default HEAD); `limit` is 1-500.
|
|
109
|
+
export async function listCommits(apiKey: string, orgId: string, repo: string, opts: { ref?: string; limit?: number } = {}): Promise<CommitInfo[]> {
|
|
110
|
+
const q = new URLSearchParams();
|
|
111
|
+
if (opts.ref) q.set('ref', opts.ref);
|
|
112
|
+
if (opts.limit !== undefined) q.set('limit', String(opts.limit));
|
|
113
|
+
const qs = q.toString();
|
|
114
|
+
const res = await request<{ commits?: CommitInfo[] }>('GET', `${repoBase(orgId, repo)}/commits${qs ? `?${qs}` : ''}`, apiKey);
|
|
115
|
+
return res?.commits ?? [];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function getCommit(apiKey: string, orgId: string, repo: string, sha: string): Promise<CommitInfo> {
|
|
119
|
+
return request('GET', `${repoBase(orgId, repo)}/commits/${encodeURIComponent(sha)}`, apiKey);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// getDiff returns the unified diff text between two refs.
|
|
123
|
+
export async function getDiff(apiKey: string, orgId: string, repo: string, base: string, head: string): Promise<string> {
|
|
124
|
+
const q = new URLSearchParams({ base, head });
|
|
125
|
+
const res = await request<{ diff?: string }>('GET', `${repoBase(orgId, repo)}/diff?${q}`, apiKey);
|
|
126
|
+
return res?.diff ?? '';
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// listTree lists a ref's tree; `path` scopes it to a subdirectory.
|
|
130
|
+
export async function listTree(apiKey: string, orgId: string, repo: string, ref: string, path?: string): Promise<TreeEntry[]> {
|
|
131
|
+
const qs = path ? `?path=${encodeURIComponent(path)}` : '';
|
|
132
|
+
const res = await request<{ entries?: TreeEntry[] }>('GET', `${repoBase(orgId, repo)}/tree/${encodeURIComponent(ref)}${qs}`, apiKey);
|
|
133
|
+
return res?.entries ?? [];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// readBlob returns a file's content (base64-encoded — safe for binary).
|
|
137
|
+
export async function readBlob(apiKey: string, orgId: string, repo: string, ref: string, path: string): Promise<Blob> {
|
|
138
|
+
// The backend route is /blob/{ref}/{path...} — a catch-all — so slashes
|
|
139
|
+
// in the path must survive; encode each segment, not the separators.
|
|
140
|
+
const encPath = path.split('/').map(encodeURIComponent).join('/');
|
|
141
|
+
return request('GET', `${repoBase(orgId, repo)}/blob/${encodeURIComponent(ref)}/${encPath}`, apiKey);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ── Writes ───────────────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
// commit applies all `changes` atomically onto `branch`.
|
|
147
|
+
export async function commit(apiKey: string, orgId: string, repo: string, payload: CommitPayload): Promise<CommitResult> {
|
|
148
|
+
return request('POST', `${repoBase(orgId, repo)}/commits`, apiKey, payload);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function createBranch(apiKey: string, orgId: string, repo: string, name: string, from: string): Promise<{ name: string }> {
|
|
152
|
+
return request('POST', `${repoBase(orgId, repo)}/branches`, apiKey, { name, from });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export async function deleteBranch(apiKey: string, orgId: string, repo: string, branch: string): Promise<void> {
|
|
156
|
+
return request('DELETE', `${repoBase(orgId, repo)}/branches/${encodeURIComponent(branch)}`, apiKey);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function createTag(apiKey: string, orgId: string, repo: string, name: string, ref: string): Promise<{ name: string }> {
|
|
160
|
+
return request('POST', `${repoBase(orgId, repo)}/tags`, apiKey, { name, ref });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// merge fast-forwards `target` to `source` (fast-forward only).
|
|
164
|
+
export async function merge(apiKey: string, orgId: string, repo: string, target: string, source: string): Promise<{ sha: string }> {
|
|
165
|
+
return request('POST', `${repoBase(orgId, repo)}/merges`, apiKey, { target, source });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// repack compacts the repository's incremental packfiles into one.
|
|
169
|
+
export async function repack(apiKey: string, orgId: string, repo: string): Promise<RepackResult> {
|
|
170
|
+
return request('POST', `${repoBase(orgId, repo)}/repack`, apiKey);
|
|
171
|
+
}
|
package/src/index.ts
CHANGED
package/src/pixel.ts
CHANGED
|
@@ -66,8 +66,10 @@ export async function getEvents(apiKey: string, orgId: string, params: { campaig
|
|
|
66
66
|
return request('GET', url, apiKey);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// getIdentity resolves a pixel's identity graph. `website` is required —
|
|
70
|
+
// the backend scopes the lookup to a domain the org owns.
|
|
71
|
+
export async function getIdentity(apiKey: string, orgId: string, pixelId: string, website: string): Promise<{ uuid: string; is_resolved: boolean; nodes: Record<string, { type: string; probability: number }>; latency_ms: number }> {
|
|
72
|
+
return request('GET', `${BASE_URL}/pixel/orgs/${encodeURIComponent(orgId)}/identity/${encodeURIComponent(pixelId)}?website=${encodeURIComponent(website)}`, apiKey);
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
// Sample of geographic distribution of the org's pixel audience. Backend
|
package/src/queue.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { request } from './client';
|
|
2
|
+
import { QUEUE_BASE as BASE_URL } from './config';
|
|
3
|
+
import type { Exposes } from './exposes';
|
|
4
|
+
|
|
5
|
+
// Backend: my-queue-api per myapi-hq/internal/routes/queue/. A durable
|
|
6
|
+
// HTTP-consumer job queue — each named queue POSTs its jobs to a configured
|
|
7
|
+
// consumer_url, with retry/backoff to max_attempts, a concurrency cap, and a
|
|
8
|
+
// dependency DAG (a job with unsucceeded depends_on starts blocked).
|
|
9
|
+
export const EXPOSES: Exposes = [
|
|
10
|
+
'POST /queue/orgs/{org_id}/queues',
|
|
11
|
+
'GET /queue/orgs/{org_id}/queues',
|
|
12
|
+
'GET /queue/orgs/{org_id}/queues/{name}',
|
|
13
|
+
'POST /queue/orgs/{org_id}/queues/{name}/jobs',
|
|
14
|
+
'GET /queue/orgs/{org_id}/queues/{name}/jobs',
|
|
15
|
+
'GET /queue/orgs/{org_id}/jobs/{id}',
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
export interface Queue {
|
|
19
|
+
id: string;
|
|
20
|
+
org_id: string;
|
|
21
|
+
name: string;
|
|
22
|
+
consumer_url: string;
|
|
23
|
+
max_attempts: number;
|
|
24
|
+
max_concurrency: number;
|
|
25
|
+
created_at: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A job that exhausts max_attempts goes straight to `dead` — there is no
|
|
29
|
+
// separate `failed` state.
|
|
30
|
+
export type JobStatus = 'pending' | 'blocked' | 'running' | 'succeeded' | 'dead';
|
|
31
|
+
|
|
32
|
+
export interface Job {
|
|
33
|
+
id: string;
|
|
34
|
+
queue_id: string;
|
|
35
|
+
org_id: string;
|
|
36
|
+
payload: unknown;
|
|
37
|
+
status: JobStatus;
|
|
38
|
+
attempt: number;
|
|
39
|
+
max_attempts: number;
|
|
40
|
+
not_before: string;
|
|
41
|
+
depends_on: string[];
|
|
42
|
+
dedup_key?: string;
|
|
43
|
+
last_error?: string;
|
|
44
|
+
started_at?: string;
|
|
45
|
+
created_at: string;
|
|
46
|
+
updated_at: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface CreateQueueOptions {
|
|
50
|
+
name: string;
|
|
51
|
+
consumerUrl: string;
|
|
52
|
+
maxAttempts?: number;
|
|
53
|
+
maxConcurrency?: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface EnqueueOptions {
|
|
57
|
+
payload?: unknown;
|
|
58
|
+
dedupKey?: string;
|
|
59
|
+
delaySeconds?: number;
|
|
60
|
+
// Job ids this job waits on. Immutable — the DAG is declared at enqueue.
|
|
61
|
+
// Same shape as task.create's depends_on.
|
|
62
|
+
dependsOn?: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function queuesBase(orgId: string): string {
|
|
66
|
+
return `${BASE_URL}/queue/orgs/${encodeURIComponent(orgId)}/queues`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// createQueue registers a named queue with its retry + concurrency policy
|
|
70
|
+
// and the HTTP consumer that runs its jobs.
|
|
71
|
+
export async function createQueue(apiKey: string, orgId: string, opts: CreateQueueOptions): Promise<Queue> {
|
|
72
|
+
const body: Record<string, unknown> = { name: opts.name, consumer_url: opts.consumerUrl };
|
|
73
|
+
if (opts.maxAttempts !== undefined) body.max_attempts = opts.maxAttempts;
|
|
74
|
+
if (opts.maxConcurrency !== undefined) body.max_concurrency = opts.maxConcurrency;
|
|
75
|
+
return request('POST', queuesBase(orgId), apiKey, body);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function listQueues(apiKey: string, orgId: string): Promise<Queue[]> {
|
|
79
|
+
const res = await request<{ queues?: Queue[] }>('GET', queuesBase(orgId), apiKey);
|
|
80
|
+
return res?.queues ?? [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function getQueue(apiKey: string, orgId: string, name: string): Promise<Queue> {
|
|
84
|
+
return request('GET', `${queuesBase(orgId)}/${encodeURIComponent(name)}`, apiKey);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// enqueueJob is idempotent on (queue, dedup_key). A job with unsucceeded
|
|
88
|
+
// depends_on starts blocked.
|
|
89
|
+
export async function enqueueJob(apiKey: string, orgId: string, name: string, opts: EnqueueOptions = {}): Promise<Job> {
|
|
90
|
+
const body: Record<string, unknown> = {};
|
|
91
|
+
if (opts.payload !== undefined) body.payload = opts.payload;
|
|
92
|
+
if (opts.dedupKey !== undefined) body.dedup_key = opts.dedupKey;
|
|
93
|
+
if (opts.delaySeconds !== undefined) body.delay_seconds = opts.delaySeconds;
|
|
94
|
+
if (opts.dependsOn !== undefined) body.depends_on = opts.dependsOn;
|
|
95
|
+
return request('POST', `${queuesBase(orgId)}/${encodeURIComponent(name)}/jobs`, apiKey, body);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// listJobs lists a queue's jobs, newest first. `limit` is 1-200.
|
|
99
|
+
export async function listJobs(apiKey: string, orgId: string, name: string, opts: { status?: string; limit?: number } = {}): Promise<Job[]> {
|
|
100
|
+
const q = new URLSearchParams();
|
|
101
|
+
if (opts.status) q.set('status', opts.status);
|
|
102
|
+
if (opts.limit !== undefined) q.set('limit', String(opts.limit));
|
|
103
|
+
const qs = q.toString();
|
|
104
|
+
const res = await request<{ jobs?: Job[] }>('GET', `${queuesBase(orgId)}/${encodeURIComponent(name)}/jobs${qs ? `?${qs}` : ''}`, apiKey);
|
|
105
|
+
return res?.jobs ?? [];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function getJob(apiKey: string, orgId: string, jobId: string): Promise<Job> {
|
|
109
|
+
return request('GET', `${BASE_URL}/queue/orgs/${encodeURIComponent(orgId)}/jobs/${encodeURIComponent(jobId)}`, apiKey);
|
|
110
|
+
}
|