🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@graph8/sdk

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@graph8/sdk - npm Package Compare versions

Comparing version
0.3.0
to
0.4.0
+323
-8
dist/index.js

@@ -352,10 +352,41 @@ "use strict";

const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
async function list(pageOrParams, limit) {
let params;
if (typeof pageOrParams === "number") {
params = { page: pageOrParams, limit: limit ?? 50 };
} else {
params = pageOrParams ?? {};
}
const resp = await fetch(`${baseUrl}/api/v1/sequences${toQuery(params)}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
}
return {
async list(page = 1, limit = 50) {
const resp = await fetch(`${baseUrl}/api/v1/sequences?page=${page}&limit=${limit}`, { headers: headers() });
/** List sequences with pagination + optional status filter. */
list,
/** Get full sequence details by ID. */
async get(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
},
/** List contacts enrolled in a sequence. Filter by state (e.g. "active", "replied"). */
async contacts(sequenceId, params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/sequences/${sequenceId}/contacts${toQuery(params)}`,
{ headers: headers() }
);
return resp.json();
},
/** Add contacts to a sequence (V2 queuing). Live or drafted sequences only. */
async add(config) {
await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
method: "POST",

@@ -365,2 +396,86 @@ headers: headers(),

});
const data = await resp.json();
return data.data || data;
},
/** Create a new sequence with optional steps + channels. */
async create(payload) {
const resp = await fetch(`${baseUrl}/api/v1/sequences`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */
async update(sequenceId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Update a single step within a sequence. */
async updateStep(sequenceId, stepId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/steps/${stepId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Soft-delete (archive) a sequence. */
async delete(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "DELETE",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Run/start a DRAFTED sequence (V2 orchestration). */
async run(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/run`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Pause a live sequence. */
async pause(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/pause`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Resume a paused sequence. */
async resume(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/resume`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Read-only sequence preview with all steps + channels (no enrollment). */
async preview(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/preview`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Comprehensive analytics for a sequence. */
async analytics(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/analytics`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
}

@@ -490,4 +605,112 @@ };

const listeners = /* @__PURE__ */ new Map();
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
const dialer = {
/** List parallel-dialer sessions with filters + pagination. */
async listSessions(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */
async createSession(payload) {
const resp = await fetch(`${baseUrl}/api/v1/voice/dialer/sessions`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Pause / resume / stop a dialer session via status flip. */
async updateSessionStatus(sessionId, status) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/status`,
{
method: "PATCH",
headers: headers(),
body: JSON.stringify({ status })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,
* filters already-called + phoneless rows, and forwards to voice's start-session.
* @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.
*/
async resumeSession(sessionId, maxContacts = 4) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/resume`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ max_contacts: maxContacts })
}
);
const data = await resp.json();
return data.data || data;
},
/** Aggregated dialer analytics (daily breakdown or total). */
async stats(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/stats${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List dialer-eligible phone numbers with 7-day stats + daily limits. */
async numbers(userEmail) {
const params = userEmail ? { user_email: userEmail } : {};
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/numbers${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List missed inbound callbacks with caller / contact info. */
async missedCallbacks(limit = 50) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/missed-callbacks${toQuery({ limit })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** AI grading for a single dialer call (returns "pending" while in progress). */
async callGrading(roomName) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List voice agents available for dialer sessions (capped at 100; no pagination). */
async agents(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/agents${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
}
};
return {
/** Start an AI voice session. */
/**
* Start an AI voice session.
* @deprecated Preview surface — for parallel-dialer flows use `voice.dialer.createSession()`.
*/
async start(config) {

@@ -502,3 +725,6 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions`, {

},
/** Get call analysis for a completed session. */
/**
* Get call analysis for a completed session.
* @deprecated Preview surface — for dialer-call grading use `voice.dialer.callGrading(roomName)`.
*/
async analysis(sessionId) {

@@ -513,3 +739,5 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions/${sessionId}/analysis`, { headers: headers() });

listeners.get(event).push(callback);
}
},
/** Parallel-dialer session control + analytics. */
dialer
};

@@ -1043,5 +1271,84 @@ };

// src/inbox.ts
var DEFAULT_API22 = "https://be.graph8.com";
var createInboxClient = (apiKey, apiUrl) => {
const baseUrl = apiUrl || DEFAULT_API22;
const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
return {
/** List inbox threads across email, SMS, and LinkedIn. */
async list(params = {}) {
const resp = await fetch(`${baseUrl}/api/v1/inbox${toQuery(params)}`, { headers: headers() });
return resp.json();
},
/** Get a single inbox thread. Defaults to email channel. */
async get(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Assign a user to an inbox thread. */
async assign(replyId, assigneeEmail, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/assign${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ assignee_email: assigneeEmail })
}
);
const data = await resp.json();
return data.data || data;
},
/** Attach tag IDs to an inbox thread. */
async tag(replyId, tagIds, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/tag${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ tag_ids: tagIds })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Generate an AI draft reply for a thread.
* Charges credits — server returns 402 if balance is insufficient.
*/
async draft(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/draft${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Send a reply through email, SMS, or LinkedIn. */
async send(replyId, payload) {
const resp = await fetch(`${baseUrl}/api/v1/inbox/${replyId}/send`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
}
};
};
// src/core.ts
var DEFAULT_HOST = "https://t.graph8.com";
var DEFAULT_API22 = "https://be.graph8.com";
var DEFAULT_API23 = "https://be.graph8.com";
var G8 = class {

@@ -1095,2 +1402,4 @@ constructor() {

this._deals = null;
/** @internal */
this._inbox = null;
}

@@ -1110,3 +1419,3 @@ /**

}
const apiUrl = config.apiUrl || DEFAULT_API22;
const apiUrl = config.apiUrl || DEFAULT_API23;
const writeKey = config.writeKey || "";

@@ -1138,2 +1447,3 @@ const apiKey = config.apiKey || "";

this._deals = createDealsClient(apiKey, apiUrl);
this._inbox = createInboxClient(apiKey, apiUrl);
this._signals = createSignalsClient(apiKey, true, apiUrl);

@@ -1265,2 +1575,7 @@ }

}
/** Multi-channel inbox — read + reply across email, SMS, LinkedIn (requires API key). */
get inbox() {
this._assertKey("inbox");
return this._inbox;
}
/** Whether the SDK has been initialized. */

@@ -1267,0 +1582,0 @@ get initialized() {

@@ -326,10 +326,41 @@ // src/core.ts

const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
async function list(pageOrParams, limit) {
let params;
if (typeof pageOrParams === "number") {
params = { page: pageOrParams, limit: limit ?? 50 };
} else {
params = pageOrParams ?? {};
}
const resp = await fetch(`${baseUrl}/api/v1/sequences${toQuery(params)}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
}
return {
async list(page = 1, limit = 50) {
const resp = await fetch(`${baseUrl}/api/v1/sequences?page=${page}&limit=${limit}`, { headers: headers() });
/** List sequences with pagination + optional status filter. */
list,
/** Get full sequence details by ID. */
async get(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
},
/** List contacts enrolled in a sequence. Filter by state (e.g. "active", "replied"). */
async contacts(sequenceId, params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/sequences/${sequenceId}/contacts${toQuery(params)}`,
{ headers: headers() }
);
return resp.json();
},
/** Add contacts to a sequence (V2 queuing). Live or drafted sequences only. */
async add(config) {
await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
method: "POST",

@@ -339,2 +370,86 @@ headers: headers(),

});
const data = await resp.json();
return data.data || data;
},
/** Create a new sequence with optional steps + channels. */
async create(payload) {
const resp = await fetch(`${baseUrl}/api/v1/sequences`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */
async update(sequenceId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Update a single step within a sequence. */
async updateStep(sequenceId, stepId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/steps/${stepId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Soft-delete (archive) a sequence. */
async delete(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "DELETE",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Run/start a DRAFTED sequence (V2 orchestration). */
async run(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/run`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Pause a live sequence. */
async pause(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/pause`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Resume a paused sequence. */
async resume(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/resume`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Read-only sequence preview with all steps + channels (no enrollment). */
async preview(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/preview`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Comprehensive analytics for a sequence. */
async analytics(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/analytics`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
}

@@ -464,4 +579,112 @@ };

const listeners = /* @__PURE__ */ new Map();
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
const dialer = {
/** List parallel-dialer sessions with filters + pagination. */
async listSessions(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */
async createSession(payload) {
const resp = await fetch(`${baseUrl}/api/v1/voice/dialer/sessions`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Pause / resume / stop a dialer session via status flip. */
async updateSessionStatus(sessionId, status) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/status`,
{
method: "PATCH",
headers: headers(),
body: JSON.stringify({ status })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,
* filters already-called + phoneless rows, and forwards to voice's start-session.
* @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.
*/
async resumeSession(sessionId, maxContacts = 4) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/resume`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ max_contacts: maxContacts })
}
);
const data = await resp.json();
return data.data || data;
},
/** Aggregated dialer analytics (daily breakdown or total). */
async stats(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/stats${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List dialer-eligible phone numbers with 7-day stats + daily limits. */
async numbers(userEmail) {
const params = userEmail ? { user_email: userEmail } : {};
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/numbers${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List missed inbound callbacks with caller / contact info. */
async missedCallbacks(limit = 50) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/missed-callbacks${toQuery({ limit })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** AI grading for a single dialer call (returns "pending" while in progress). */
async callGrading(roomName) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List voice agents available for dialer sessions (capped at 100; no pagination). */
async agents(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/agents${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
}
};
return {
/** Start an AI voice session. */
/**
* Start an AI voice session.
* @deprecated Preview surface — for parallel-dialer flows use `voice.dialer.createSession()`.
*/
async start(config) {

@@ -476,3 +699,6 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions`, {

},
/** Get call analysis for a completed session. */
/**
* Get call analysis for a completed session.
* @deprecated Preview surface — for dialer-call grading use `voice.dialer.callGrading(roomName)`.
*/
async analysis(sessionId) {

@@ -487,3 +713,5 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions/${sessionId}/analysis`, { headers: headers() });

listeners.get(event).push(callback);
}
},
/** Parallel-dialer session control + analytics. */
dialer
};

@@ -1017,5 +1245,84 @@ };

// src/inbox.ts
var DEFAULT_API22 = "https://be.graph8.com";
var createInboxClient = (apiKey, apiUrl) => {
const baseUrl = apiUrl || DEFAULT_API22;
const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
return {
/** List inbox threads across email, SMS, and LinkedIn. */
async list(params = {}) {
const resp = await fetch(`${baseUrl}/api/v1/inbox${toQuery(params)}`, { headers: headers() });
return resp.json();
},
/** Get a single inbox thread. Defaults to email channel. */
async get(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Assign a user to an inbox thread. */
async assign(replyId, assigneeEmail, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/assign${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ assignee_email: assigneeEmail })
}
);
const data = await resp.json();
return data.data || data;
},
/** Attach tag IDs to an inbox thread. */
async tag(replyId, tagIds, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/tag${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ tag_ids: tagIds })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Generate an AI draft reply for a thread.
* Charges credits — server returns 402 if balance is insufficient.
*/
async draft(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/draft${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Send a reply through email, SMS, or LinkedIn. */
async send(replyId, payload) {
const resp = await fetch(`${baseUrl}/api/v1/inbox/${replyId}/send`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
}
};
};
// src/core.ts
var DEFAULT_HOST = "https://t.graph8.com";
var DEFAULT_API22 = "https://be.graph8.com";
var DEFAULT_API23 = "https://be.graph8.com";
var G8 = class {

@@ -1069,2 +1376,4 @@ constructor() {

this._deals = null;
/** @internal */
this._inbox = null;
}

@@ -1084,3 +1393,3 @@ /**

}
const apiUrl = config.apiUrl || DEFAULT_API22;
const apiUrl = config.apiUrl || DEFAULT_API23;
const writeKey = config.writeKey || "";

@@ -1112,2 +1421,3 @@ const apiKey = config.apiKey || "";

this._deals = createDealsClient(apiKey, apiUrl);
this._inbox = createInboxClient(apiKey, apiUrl);
this._signals = createSignalsClient(apiKey, true, apiUrl);

@@ -1239,2 +1549,7 @@ }

}
/** Multi-channel inbox — read + reply across email, SMS, LinkedIn (requires API key). */
get inbox() {
this._assertKey("inbox");
return this._inbox;
}
/** Whether the SDK has been initialized. */

@@ -1241,0 +1556,0 @@ get initialized() {

@@ -355,10 +355,41 @@ "use strict";

const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
async function list(pageOrParams, limit) {
let params;
if (typeof pageOrParams === "number") {
params = { page: pageOrParams, limit: limit ?? 50 };
} else {
params = pageOrParams ?? {};
}
const resp = await fetch(`${baseUrl}/api/v1/sequences${toQuery(params)}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
}
return {
async list(page = 1, limit = 50) {
const resp = await fetch(`${baseUrl}/api/v1/sequences?page=${page}&limit=${limit}`, { headers: headers() });
/** List sequences with pagination + optional status filter. */
list,
/** Get full sequence details by ID. */
async get(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
},
/** List contacts enrolled in a sequence. Filter by state (e.g. "active", "replied"). */
async contacts(sequenceId, params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/sequences/${sequenceId}/contacts${toQuery(params)}`,
{ headers: headers() }
);
return resp.json();
},
/** Add contacts to a sequence (V2 queuing). Live or drafted sequences only. */
async add(config) {
await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
method: "POST",

@@ -368,2 +399,86 @@ headers: headers(),

});
const data = await resp.json();
return data.data || data;
},
/** Create a new sequence with optional steps + channels. */
async create(payload) {
const resp = await fetch(`${baseUrl}/api/v1/sequences`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */
async update(sequenceId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Update a single step within a sequence. */
async updateStep(sequenceId, stepId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/steps/${stepId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Soft-delete (archive) a sequence. */
async delete(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "DELETE",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Run/start a DRAFTED sequence (V2 orchestration). */
async run(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/run`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Pause a live sequence. */
async pause(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/pause`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Resume a paused sequence. */
async resume(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/resume`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Read-only sequence preview with all steps + channels (no enrollment). */
async preview(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/preview`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Comprehensive analytics for a sequence. */
async analytics(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/analytics`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
}

@@ -493,4 +608,112 @@ };

const listeners = /* @__PURE__ */ new Map();
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
const dialer = {
/** List parallel-dialer sessions with filters + pagination. */
async listSessions(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */
async createSession(payload) {
const resp = await fetch(`${baseUrl}/api/v1/voice/dialer/sessions`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Pause / resume / stop a dialer session via status flip. */
async updateSessionStatus(sessionId, status) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/status`,
{
method: "PATCH",
headers: headers(),
body: JSON.stringify({ status })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,
* filters already-called + phoneless rows, and forwards to voice's start-session.
* @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.
*/
async resumeSession(sessionId, maxContacts = 4) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/resume`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ max_contacts: maxContacts })
}
);
const data = await resp.json();
return data.data || data;
},
/** Aggregated dialer analytics (daily breakdown or total). */
async stats(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/stats${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List dialer-eligible phone numbers with 7-day stats + daily limits. */
async numbers(userEmail) {
const params = userEmail ? { user_email: userEmail } : {};
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/numbers${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List missed inbound callbacks with caller / contact info. */
async missedCallbacks(limit = 50) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/missed-callbacks${toQuery({ limit })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** AI grading for a single dialer call (returns "pending" while in progress). */
async callGrading(roomName) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List voice agents available for dialer sessions (capped at 100; no pagination). */
async agents(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/agents${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
}
};
return {
/** Start an AI voice session. */
/**
* Start an AI voice session.
* @deprecated Preview surface — for parallel-dialer flows use `voice.dialer.createSession()`.
*/
async start(config) {

@@ -505,3 +728,6 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions`, {

},
/** Get call analysis for a completed session. */
/**
* Get call analysis for a completed session.
* @deprecated Preview surface — for dialer-call grading use `voice.dialer.callGrading(roomName)`.
*/
async analysis(sessionId) {

@@ -516,3 +742,5 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions/${sessionId}/analysis`, { headers: headers() });

listeners.get(event).push(callback);
}
},
/** Parallel-dialer session control + analytics. */
dialer
};

@@ -1046,5 +1274,84 @@ };

// src/inbox.ts
var DEFAULT_API22 = "https://be.graph8.com";
var createInboxClient = (apiKey, apiUrl) => {
const baseUrl = apiUrl || DEFAULT_API22;
const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
return {
/** List inbox threads across email, SMS, and LinkedIn. */
async list(params = {}) {
const resp = await fetch(`${baseUrl}/api/v1/inbox${toQuery(params)}`, { headers: headers() });
return resp.json();
},
/** Get a single inbox thread. Defaults to email channel. */
async get(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Assign a user to an inbox thread. */
async assign(replyId, assigneeEmail, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/assign${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ assignee_email: assigneeEmail })
}
);
const data = await resp.json();
return data.data || data;
},
/** Attach tag IDs to an inbox thread. */
async tag(replyId, tagIds, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/tag${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ tag_ids: tagIds })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Generate an AI draft reply for a thread.
* Charges credits — server returns 402 if balance is insufficient.
*/
async draft(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/draft${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Send a reply through email, SMS, or LinkedIn. */
async send(replyId, payload) {
const resp = await fetch(`${baseUrl}/api/v1/inbox/${replyId}/send`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
}
};
};
// src/core.ts
var DEFAULT_HOST = "https://t.graph8.com";
var DEFAULT_API22 = "https://be.graph8.com";
var DEFAULT_API23 = "https://be.graph8.com";
var G8 = class {

@@ -1098,2 +1405,4 @@ constructor() {

this._deals = null;
/** @internal */
this._inbox = null;
}

@@ -1113,3 +1422,3 @@ /**

}
const apiUrl = config.apiUrl || DEFAULT_API22;
const apiUrl = config.apiUrl || DEFAULT_API23;
const writeKey = config.writeKey || "";

@@ -1141,2 +1450,3 @@ const apiKey = config.apiKey || "";

this._deals = createDealsClient(apiKey, apiUrl);
this._inbox = createInboxClient(apiKey, apiUrl);
this._signals = createSignalsClient(apiKey, true, apiUrl);

@@ -1268,2 +1578,7 @@ }

}
/** Multi-channel inbox — read + reply across email, SMS, LinkedIn (requires API key). */
get inbox() {
this._assertKey("inbox");
return this._inbox;
}
/** Whether the SDK has been initialized. */

@@ -1270,0 +1585,0 @@ get initialized() {

@@ -331,10 +331,41 @@ "use client";

const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
async function list(pageOrParams, limit) {
let params;
if (typeof pageOrParams === "number") {
params = { page: pageOrParams, limit: limit ?? 50 };
} else {
params = pageOrParams ?? {};
}
const resp = await fetch(`${baseUrl}/api/v1/sequences${toQuery(params)}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
}
return {
async list(page = 1, limit = 50) {
const resp = await fetch(`${baseUrl}/api/v1/sequences?page=${page}&limit=${limit}`, { headers: headers() });
/** List sequences with pagination + optional status filter. */
list,
/** Get full sequence details by ID. */
async get(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, { headers: headers() });
const data = await resp.json();
return data.data || data;
},
/** List contacts enrolled in a sequence. Filter by state (e.g. "active", "replied"). */
async contacts(sequenceId, params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/sequences/${sequenceId}/contacts${toQuery(params)}`,
{ headers: headers() }
);
return resp.json();
},
/** Add contacts to a sequence (V2 queuing). Live or drafted sequences only. */
async add(config) {
await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${config.sequenceId}/contacts`, {
method: "POST",

@@ -344,2 +375,86 @@ headers: headers(),

});
const data = await resp.json();
return data.data || data;
},
/** Create a new sequence with optional steps + channels. */
async create(payload) {
const resp = await fetch(`${baseUrl}/api/v1/sequences`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Update sequence metadata. Rejected (409) if sequence is in a transitional status. */
async update(sequenceId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Update a single step within a sequence. */
async updateStep(sequenceId, stepId, fields) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/steps/${stepId}`, {
method: "PATCH",
headers: headers(),
body: JSON.stringify(fields)
});
const data = await resp.json();
return data.data || data;
},
/** Soft-delete (archive) a sequence. */
async delete(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}`, {
method: "DELETE",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Run/start a DRAFTED sequence (V2 orchestration). */
async run(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/run`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Pause a live sequence. */
async pause(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/pause`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Resume a paused sequence. */
async resume(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/resume`, {
method: "POST",
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Read-only sequence preview with all steps + channels (no enrollment). */
async preview(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/preview`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
},
/** Comprehensive analytics for a sequence. */
async analytics(sequenceId) {
const resp = await fetch(`${baseUrl}/api/v1/sequences/${sequenceId}/analytics`, {
headers: headers()
});
const data = await resp.json();
return data.data || data;
}

@@ -469,4 +584,112 @@ };

const listeners = /* @__PURE__ */ new Map();
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
const dialer = {
/** List parallel-dialer sessions with filters + pagination. */
async listSessions(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Create a parallel-dialer session in PAUSED state. SDR opens UI to start dialing. */
async createSession(payload) {
const resp = await fetch(`${baseUrl}/api/v1/voice/dialer/sessions`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
},
/** Pause / resume / stop a dialer session via status flip. */
async updateSessionStatus(sessionId, status) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/status`,
{
method: "PATCH",
headers: headers(),
body: JSON.stringify({ status })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Resume a PAUSED dialer session. Auto-fetches the next batch from the source list,
* filters already-called + phoneless rows, and forwards to voice's start-session.
* @param maxContacts 1-4 (voice caps parallel dialing at 4). Default 4.
*/
async resumeSession(sessionId, maxContacts = 4) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/sessions/${sessionId}/resume`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ max_contacts: maxContacts })
}
);
const data = await resp.json();
return data.data || data;
},
/** Aggregated dialer analytics (daily breakdown or total). */
async stats(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/stats${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List dialer-eligible phone numbers with 7-day stats + daily limits. */
async numbers(userEmail) {
const params = userEmail ? { user_email: userEmail } : {};
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/numbers${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List missed inbound callbacks with caller / contact info. */
async missedCallbacks(limit = 50) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/missed-callbacks${toQuery({ limit })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** AI grading for a single dialer call (returns "pending" while in progress). */
async callGrading(roomName) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/calls/${encodeURIComponent(roomName)}/grading`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** List voice agents available for dialer sessions (capped at 100; no pagination). */
async agents(params = {}) {
const resp = await fetch(
`${baseUrl}/api/v1/voice/dialer/agents${toQuery(params)}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
}
};
return {
/** Start an AI voice session. */
/**
* Start an AI voice session.
* @deprecated Preview surface — for parallel-dialer flows use `voice.dialer.createSession()`.
*/
async start(config) {

@@ -481,3 +704,6 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions`, {

},
/** Get call analysis for a completed session. */
/**
* Get call analysis for a completed session.
* @deprecated Preview surface — for dialer-call grading use `voice.dialer.callGrading(roomName)`.
*/
async analysis(sessionId) {

@@ -492,3 +718,5 @@ const resp = await fetch(`${baseUrl}/api/v1/voice/sessions/${sessionId}/analysis`, { headers: headers() });

listeners.get(event).push(callback);
}
},
/** Parallel-dialer session control + analytics. */
dialer
};

@@ -1022,5 +1250,84 @@ };

// src/inbox.ts
var DEFAULT_API22 = "https://be.graph8.com";
var createInboxClient = (apiKey, apiUrl) => {
const baseUrl = apiUrl || DEFAULT_API22;
const headers = () => ({ "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` });
const toQuery = (params) => {
const qs = new URLSearchParams();
for (const [k, v] of Object.entries(params)) {
if (v != null) qs.set(k, String(v));
}
const s = qs.toString();
return s ? `?${s}` : "";
};
return {
/** List inbox threads across email, SMS, and LinkedIn. */
async list(params = {}) {
const resp = await fetch(`${baseUrl}/api/v1/inbox${toQuery(params)}`, { headers: headers() });
return resp.json();
},
/** Get a single inbox thread. Defaults to email channel. */
async get(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Assign a user to an inbox thread. */
async assign(replyId, assigneeEmail, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/assign${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ assignee_email: assigneeEmail })
}
);
const data = await resp.json();
return data.data || data;
},
/** Attach tag IDs to an inbox thread. */
async tag(replyId, tagIds, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/tag${toQuery({ channel })}`,
{
method: "POST",
headers: headers(),
body: JSON.stringify({ tag_ids: tagIds })
}
);
const data = await resp.json();
return data.data || data;
},
/**
* Generate an AI draft reply for a thread.
* Charges credits — server returns 402 if balance is insufficient.
*/
async draft(replyId, channel = "email") {
const resp = await fetch(
`${baseUrl}/api/v1/inbox/${replyId}/draft${toQuery({ channel })}`,
{ headers: headers() }
);
const data = await resp.json();
return data.data || data;
},
/** Send a reply through email, SMS, or LinkedIn. */
async send(replyId, payload) {
const resp = await fetch(`${baseUrl}/api/v1/inbox/${replyId}/send`, {
method: "POST",
headers: headers(),
body: JSON.stringify(payload)
});
const data = await resp.json();
return data.data || data;
}
};
};
// src/core.ts
var DEFAULT_HOST = "https://t.graph8.com";
var DEFAULT_API22 = "https://be.graph8.com";
var DEFAULT_API23 = "https://be.graph8.com";
var G8 = class {

@@ -1074,2 +1381,4 @@ constructor() {

this._deals = null;
/** @internal */
this._inbox = null;
}

@@ -1089,3 +1398,3 @@ /**

}
const apiUrl = config.apiUrl || DEFAULT_API22;
const apiUrl = config.apiUrl || DEFAULT_API23;
const writeKey = config.writeKey || "";

@@ -1117,2 +1426,3 @@ const apiKey = config.apiKey || "";

this._deals = createDealsClient(apiKey, apiUrl);
this._inbox = createInboxClient(apiKey, apiUrl);
this._signals = createSignalsClient(apiKey, true, apiUrl);

@@ -1244,2 +1554,7 @@ }

}
/** Multi-channel inbox — read + reply across email, SMS, LinkedIn (requires API key). */
get inbox() {
this._assertKey("inbox");
return this._inbox;
}
/** Whether the SDK has been initialized. */

@@ -1246,0 +1561,0 @@ get initialized() {

+1
-1
{
"name": "@graph8/sdk",
"version": "0.3.0",
"version": "0.4.0",
"description": "graph8 SDK - CRM, enrichment, sequences, campaigns, and tracking for B2B",

@@ -5,0 +5,0 @@ "license": "MIT",

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display