@graph8/sdk 0.13.1 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -23,9 +23,12 @@ __export(index_exports, {
23
23
  DEFAULT_APP_API: () => DEFAULT_APP_API,
24
24
  G8Error: () => G8Error,
25
25
  KNOWN_WEBHOOK_EVENTS: () => KNOWN_WEBHOOK_EVENTS,
26
+ MAX_TAIL_LINES: () => MAX_TAIL_LINES,
27
+ MIN_TAIL_LINES: () => MIN_TAIL_LINES,
26
28
  WebhookSignatureError: () => WebhookSignatureError,
27
29
  backoffDelayMs: () => backoffDelayMs,
28
30
  constructEvent: () => constructEvent,
31
+ createAppPlatformClient: () => createAppPlatformClient,
29
32
  createAppRequester: () => createAppRequester,
30
33
  createGraph8AppClient: () => createGraph8AppClient,
31
34
  createGraph8ServiceClient: () => createGraph8ServiceClient,
@@ -888,6 +891,8 @@ var KNOWN_WEBHOOK_EVENTS = [
888
891
  "engagement.email_bounced",
889
892
  "engagement.email_skipped",
890
893
  "engagement.call_dispatched",
894
+ "engagement.call_connected",
895
+ "engagement.call_graded",
891
896
  "engagement.sms_sent",
892
897
  "engagement.sms_replied",
893
898
  "engagement.whatsapp_sent",
@@ -898,7 +903,12 @@ var KNOWN_WEBHOOK_EVENTS = [
898
903
  "engagement.linkedin_connection_accepted",
899
904
  "meeting.booked",
900
905
  "meeting.cancelled",
901
- "meeting.rescheduled"
906
+ "meeting.rescheduled",
907
+ "deal.won",
908
+ "crm.record.created",
909
+ "crm.record.updated",
910
+ "crm.record.archived",
911
+ "crm.record.restored"
902
912
  ];
903
913
  var WebhookSignatureError = class extends Error {
904
914
  constructor(message) {
@@ -1258,10 +1268,284 @@ var createAppsClient = (apiKey, apiUrl) => {
1258
1268
  };
1259
1269
  };
1260
1270
 
1261
- // src/fields.ts
1271
+ // src/appPlatform.ts
1262
1272
  var DEFAULT_API18 = "https://be.graph8.com";
1263
- var createFieldsClient = (apiKey, apiUrl) => {
1273
+ var MIN_TAIL_LINES = 1;
1274
+ var MAX_TAIL_LINES = 2e3;
1275
+ var createAppPlatformClient = (apiKey, apiUrl) => {
1264
1276
  const baseUrl = apiUrl || DEFAULT_API18;
1277
+ const unwrap = (resp) => resp.data ?? resp;
1278
+ return {
1279
+ // ---- source binding -------------------------------------------------
1280
+ /**
1281
+ * Bind the repository an app builds from.
1282
+ *
1283
+ * `repo_url` must be fetchable -- `https://`, `ssh://` or `git@`, with no
1284
+ * whitespace. A `file://` or bare path is refused with 422, because a build
1285
+ * that can read the builder's filesystem is a build that can read ours.
1286
+ *
1287
+ * `credential_ref` is a POINTER into your secret manager, not a token.
1288
+ */
1289
+ async setSource(appId, params) {
1290
+ const resp = await request(
1291
+ baseUrl,
1292
+ `/api/v1/apps/${appId}/source`,
1293
+ apiKey,
1294
+ {
1295
+ method: "PUT",
1296
+ body: {
1297
+ repo_url: params.repo_url,
1298
+ provider: params.provider,
1299
+ default_branch: params.default_branch ?? null,
1300
+ credential_ref: params.credential_ref ?? null
1301
+ }
1302
+ }
1303
+ );
1304
+ return unwrap(resp);
1305
+ },
1306
+ /** Unbind the source. Returns the full app with every source field null. */
1307
+ async clearSource(appId) {
1308
+ const resp = await request(
1309
+ baseUrl,
1310
+ `/api/v1/apps/${appId}/source`,
1311
+ apiKey,
1312
+ { method: "DELETE" }
1313
+ );
1314
+ return unwrap(resp);
1315
+ },
1316
+ // ---- deployments ----------------------------------------------------
1317
+ /** Every deployment for this app, newest first. Never 404s for an app with none. */
1318
+ async listDeployments(appId) {
1319
+ return request(baseUrl, `/api/v1/apps/${appId}/deployments`, apiKey);
1320
+ },
1321
+ /**
1322
+ * Queue a deployment. Returns `201` with `status: "queued"` -- nothing builds
1323
+ * as a side effect of this call; the build controller picks it up.
1324
+ *
1325
+ * NOT idempotent: two identical calls create two deployments.
1326
+ */
1327
+ async deploy(appId, params) {
1328
+ const body = { source_ref: params.source_ref };
1329
+ if (params.schema_version_id) body.schema_version_id = params.schema_version_id;
1330
+ const resp = await request(
1331
+ baseUrl,
1332
+ `/api/v1/apps/${appId}/deployments`,
1333
+ apiKey,
1334
+ { method: "POST", body }
1335
+ );
1336
+ return unwrap(resp);
1337
+ },
1338
+ /** Fetch one deployment. The polling endpoint for a build loop. */
1339
+ async getDeployment(appId, deploymentId) {
1340
+ const resp = await request(
1341
+ baseUrl,
1342
+ `/api/v1/apps/${appId}/deployments/${deploymentId}`,
1343
+ apiKey
1344
+ );
1345
+ return unwrap(resp);
1346
+ },
1347
+ /**
1348
+ * The deployment currently serving traffic, or `null`.
1349
+ *
1350
+ * `null` is a real answer with a 200, not a 404: "this app has never shipped"
1351
+ * is information, while a 404 would read as "no such app".
1352
+ */
1353
+ async activeDeployment(appId) {
1354
+ const resp = await request(
1355
+ baseUrl,
1356
+ `/api/v1/apps/${appId}/deployments/active`,
1357
+ apiKey
1358
+ );
1359
+ return resp.data ?? null;
1360
+ },
1361
+ /**
1362
+ * Promote a built deployment to serve traffic. Anything it displaces moves to
1363
+ * `rolled_back` in the same transaction, so there is never a moment with two
1364
+ * live deployments.
1365
+ *
1366
+ * `409` when the state machine forbids it -- a deployment cannot become
1367
+ * `deployed` without having been built, and a `failed` one cannot be revived.
1368
+ * A retry is a new deployment, not a resurrection.
1369
+ */
1370
+ async promote(appId, deploymentId, imageDigest) {
1371
+ const resp = await request(
1372
+ baseUrl,
1373
+ `/api/v1/apps/${appId}/deployments/${deploymentId}/promote`,
1374
+ apiKey,
1375
+ { method: "POST", body: { image_digest: imageDigest } }
1376
+ );
1377
+ return unwrap(resp);
1378
+ },
1379
+ /** Roll back a deployment that is currently serving. Only a `deployed` one may be. */
1380
+ async rollback(appId, deploymentId) {
1381
+ const resp = await request(
1382
+ baseUrl,
1383
+ `/api/v1/apps/${appId}/deployments/${deploymentId}/rollback`,
1384
+ apiKey,
1385
+ { method: "POST", body: {} }
1386
+ );
1387
+ return unwrap(resp);
1388
+ },
1389
+ // ---- logs -----------------------------------------------------------
1390
+ /**
1391
+ * Why a build failed. Returns every step of the build pod in the order
1392
+ * Kubernetes runs them; a step that has not started yet is omitted rather
1393
+ * than returned empty.
1394
+ *
1395
+ * An empty `containers` list is not an error -- build pods are reaped an hour
1396
+ * after they finish, so logs for an older deployment are genuinely gone.
1397
+ * `last_error_sanitized` on the deployment is what survives.
1398
+ *
1399
+ * `503` means graph8 could not reach the cluster, which is deliberately
1400
+ * different from an empty `200`: one means we could not look, the other means
1401
+ * your build produced no output.
1402
+ */
1403
+ async deploymentLogs(appId, deploymentId, tailLines) {
1404
+ const resp = await request(
1405
+ baseUrl,
1406
+ `/api/v1/apps/${appId}/deployments/${deploymentId}/logs`,
1407
+ apiKey,
1408
+ { query: tailLines === void 0 ? void 0 : { tail_lines: tailLines } }
1409
+ );
1410
+ return unwrap(resp);
1411
+ },
1412
+ /**
1413
+ * What the running app is printing. The app's own pods only -- the per-app
1414
+ * egress proxy shares the namespace and is deliberately excluded.
1415
+ *
1416
+ * Empty until a deployment reaches `deployed`.
1417
+ */
1418
+ async logs(appId, tailLines) {
1419
+ const resp = await request(
1420
+ baseUrl,
1421
+ `/api/v1/apps/${appId}/logs`,
1422
+ apiKey,
1423
+ { query: tailLines === void 0 ? void 0 : { tail_lines: tailLines } }
1424
+ );
1425
+ return unwrap(resp);
1426
+ },
1427
+ // ---- domains --------------------------------------------------------
1428
+ /** Every hostname claimed for this app, whatever its verification state. */
1429
+ async listDomains(appId) {
1430
+ return request(baseUrl, `/api/v1/apps/${appId}/domains`, apiKey);
1431
+ },
1432
+ /**
1433
+ * Claim a hostname and get the TXT record that proves you own it.
1434
+ *
1435
+ * Returns `201`, and the domain is NESTED at `data.domain` -- this is the one
1436
+ * route on the surface whose payload is not the record itself. Hostnames are
1437
+ * globally unique, so a host another app holds is refused.
1438
+ */
1439
+ async claimDomain(appId, hostname) {
1440
+ const resp = await request(
1441
+ baseUrl,
1442
+ `/api/v1/apps/${appId}/domains`,
1443
+ apiKey,
1444
+ { method: "POST", body: { hostname } }
1445
+ );
1446
+ return unwrap(resp);
1447
+ },
1448
+ /**
1449
+ * Check DNS for the TXT record and advance the domain to `verified`.
1450
+ *
1451
+ * Idempotent: verifying an already-verified domain re-checks and stays
1452
+ * verified, so it is safe to re-run after a DNS change. Send no body -- the
1453
+ * record in DNS is the payload.
1454
+ */
1455
+ async verifyDomain(appId, hostname) {
1456
+ const resp = await request(
1457
+ baseUrl,
1458
+ `/api/v1/apps/${appId}/domains/${encodeURIComponent(hostname)}/verify`,
1459
+ apiKey,
1460
+ { method: "POST", body: {} }
1461
+ );
1462
+ return unwrap(resp);
1463
+ },
1464
+ /**
1465
+ * Release a claimed hostname.
1466
+ *
1467
+ * A HARD delete. Hostnames are globally unique, so a row left behind in any
1468
+ * status keeps the host burned for every other builder. Releasing one you
1469
+ * already released is a `404`, because after the first call the claim
1470
+ * genuinely does not exist.
1471
+ *
1472
+ * Returns the NORMALIZED hostname, which may differ from what you passed.
1473
+ */
1474
+ async releaseDomain(appId, hostname) {
1475
+ const resp = await request(
1476
+ baseUrl,
1477
+ `/api/v1/apps/${appId}/domains/${encodeURIComponent(hostname)}`,
1478
+ apiKey,
1479
+ { method: "DELETE" }
1480
+ );
1481
+ return unwrap(resp);
1482
+ },
1483
+ // ---- secrets --------------------------------------------------------
1484
+ /**
1485
+ * Which secrets this app declares, and when each was last rotated.
1486
+ *
1487
+ * NEVER returns a value. graph8 stores a POINTER into your secret manager and
1488
+ * has no column for the secret itself.
1489
+ */
1490
+ async listSecrets(appId) {
1491
+ return request(baseUrl, `/api/v1/apps/${appId}/secrets`, apiKey);
1492
+ },
1493
+ /**
1494
+ * Declare a secret, or rotate the pointer to it.
1495
+ *
1496
+ * `providerRef` is a REFERENCE, and the server rejects anything that looks
1497
+ * like a credential -- a value starting `bearer `, `sk-`, `ghp_`, `xox` and
1498
+ * friends is a 422. That refusal is the feature: it catches the mistake of
1499
+ * pasting the secret where its address belongs.
1500
+ */
1501
+ async putSecret(appId, secretKey, providerRef) {
1502
+ const resp = await request(
1503
+ baseUrl,
1504
+ `/api/v1/apps/${appId}/secrets/${encodeURIComponent(secretKey)}`,
1505
+ apiKey,
1506
+ { method: "PUT", body: { provider_ref: providerRef ?? null } }
1507
+ );
1508
+ return unwrap(resp);
1509
+ },
1510
+ /** Undeclare a secret. A key the app never declared is a 404, so a typo is
1511
+ * never reported as a successful removal. */
1512
+ async deleteSecret(appId, secretKey) {
1513
+ const resp = await request(
1514
+ baseUrl,
1515
+ `/api/v1/apps/${appId}/secrets/${encodeURIComponent(secretKey)}`,
1516
+ apiKey,
1517
+ { method: "DELETE" }
1518
+ );
1519
+ return unwrap(resp);
1520
+ },
1521
+ // ---- schema versions ------------------------------------------------
1522
+ /**
1523
+ * Publish a custom-object schema version. Returns `201`.
1524
+ *
1525
+ * Takes only the `objects` list, not a whole `graph8.app.yaml`: the rest of
1526
+ * that file is app metadata the control plane already holds, and accepting it
1527
+ * here would create a second place for it to disagree.
1528
+ */
1529
+ async publishSchemaVersion(appId, objects) {
1530
+ const resp = await request(
1531
+ baseUrl,
1532
+ `/api/v1/apps/${appId}/schema-versions`,
1533
+ apiKey,
1534
+ { method: "POST", body: { objects } }
1535
+ );
1536
+ return unwrap(resp);
1537
+ },
1538
+ /** Every schema version this app has published. Empty array, never 404. */
1539
+ async listSchemaVersions(appId) {
1540
+ return request(baseUrl, `/api/v1/apps/${appId}/schema-versions`, apiKey);
1541
+ }
1542
+ };
1543
+ };
1544
+
1545
+ // src/fields.ts
1546
+ var DEFAULT_API19 = "https://be.graph8.com";
1547
+ var createFieldsClient = (apiKey, apiUrl) => {
1548
+ const baseUrl = apiUrl || DEFAULT_API19;
1265
1549
  return {
1266
1550
  /** List contact fields (base + custom). Pass listId to include list-specific custom fields. */
1267
1551
  async listContactFields(listId) {
@@ -1303,14 +1587,38 @@ var createFieldsClient = (apiKey, apiUrl) => {
1303
1587
  };
1304
1588
 
1305
1589
  // src/objects.ts
1306
- var DEFAULT_API19 = "https://be.graph8.com";
1590
+ var DEFAULT_API20 = "https://be.graph8.com";
1307
1591
  var createObjectsClient = (apiKey, apiUrl) => {
1308
- const baseUrl = apiUrl || DEFAULT_API19;
1592
+ const baseUrl = apiUrl || DEFAULT_API20;
1309
1593
  const encode = (value) => encodeURIComponent(value);
1310
1594
  return {
1311
1595
  /** List the custom object types in your workspace. */
1312
- async list() {
1313
- return request(baseUrl, "/api/v1/objects", apiKey);
1596
+ async list(params = {}) {
1597
+ const query = params.include_archived ? "?include_archived=true" : "";
1598
+ return request(baseUrl, `/api/v1/objects${query}`, apiKey);
1599
+ },
1600
+ /** Create a native custom object. Requires objects:manage and Admin access. */
1601
+ async create(input) {
1602
+ const resp = await request(baseUrl, "/api/v1/objects", apiKey, {
1603
+ method: "POST",
1604
+ body: input
1605
+ });
1606
+ return resp.data;
1607
+ },
1608
+ /** Rename, archive or restore an object while preserving its slug and data. */
1609
+ async update(objectSlug, input) {
1610
+ const resp = await request(baseUrl, `/api/v1/objects/${encode(objectSlug)}`, apiKey, {
1611
+ method: "PATCH",
1612
+ body: input
1613
+ });
1614
+ return resp.data;
1615
+ },
1616
+ /** Archive the object without deleting records; restore with update({ is_archived: false }). */
1617
+ async archive(objectSlug) {
1618
+ const resp = await request(baseUrl, `/api/v1/objects/${encode(objectSlug)}`, apiKey, {
1619
+ method: "DELETE"
1620
+ });
1621
+ return resp.data;
1314
1622
  },
1315
1623
  /** Fetch one object type by slug. */
1316
1624
  async get(objectSlug) {
@@ -1322,10 +1630,35 @@ var createObjectsClient = (apiKey, apiUrl) => {
1322
1630
  return resp.data ?? resp;
1323
1631
  },
1324
1632
  /** The object's attributes — the schema its records must satisfy. */
1325
- async listAttributes(objectSlug) {
1326
- return request(baseUrl, `/api/v1/objects/${encode(objectSlug)}/attributes`, apiKey);
1633
+ async listAttributes(objectSlug, params = {}) {
1634
+ const query = params.include_archived ? "?include_archived=true" : "";
1635
+ return request(baseUrl, `/api/v1/objects/${encode(objectSlug)}/attributes${query}`, apiKey);
1636
+ },
1637
+ /** Create a field using the same schema rules as the customer UI. */
1638
+ async createAttribute(objectSlug, input) {
1639
+ const resp = await request(baseUrl, `/api/v1/objects/${encode(objectSlug)}/attributes`, apiKey, {
1640
+ method: "POST",
1641
+ body: input
1642
+ });
1643
+ return resp.data;
1327
1644
  },
1328
- /** Paginated records with their current values. Archived records are excluded. */
1645
+ /** Edit or restore a field; its type and slug are immutable. */
1646
+ async updateAttribute(objectSlug, attributeSlug, input) {
1647
+ const resp = await request(baseUrl, `/api/v1/objects/${encode(objectSlug)}/attributes/${encode(attributeSlug)}`, apiKey, {
1648
+ method: "PATCH",
1649
+ body: input
1650
+ });
1651
+ return resp.data;
1652
+ },
1653
+ /** Archive without deleting values; restore with updateAttribute({ is_archived: false }). */
1654
+ async archiveAttribute(objectSlug, attributeSlug) {
1655
+ const resp = await request(baseUrl, `/api/v1/objects/${encode(objectSlug)}/attributes/${encode(attributeSlug)}`, apiKey, {
1656
+ method: "DELETE"
1657
+ });
1658
+ return resp.data;
1659
+ },
1660
+ /** Paginated custom records or canonical deals with current values and revisions.
1661
+ * Deal totals and related references respect current record access. */
1329
1662
  async listRecords(objectSlug, params = {}) {
1330
1663
  const query = {};
1331
1664
  if (params.page != null) query.page = params.page;
@@ -1349,7 +1682,24 @@ var createObjectsClient = (apiKey, apiUrl) => {
1349
1682
  );
1350
1683
  return resp.data ?? resp;
1351
1684
  },
1352
- /** Fetch one record with its currently active values. */
1685
+ /** Match a unique single-value key, creating or updating while preserving omitted fields. */
1686
+ async upsertRecord(objectSlug, matchingAttribute, values, options = {}) {
1687
+ const resp = await request(
1688
+ baseUrl,
1689
+ `/api/v1/objects/${encode(objectSlug)}/records/upsert`,
1690
+ apiKey,
1691
+ { method: "POST", body: {
1692
+ matching_attribute: matchingAttribute,
1693
+ values,
1694
+ ...options.expectedRevision !== void 0 ? { expected_revision: options.expectedRevision } : {}
1695
+ } }
1696
+ );
1697
+ return resp.data;
1698
+ },
1699
+ /** Fetch one custom record, canonical contact/company, or deal by its stable graph8 ID.
1700
+ * Deals retain their UUIDs and expose company, primary-contact and contact_ids references only
1701
+ * when permitted. Generic deal mutations are not yet supported.
1702
+ */
1353
1703
  async getRecord(objectSlug, recordId) {
1354
1704
  const resp = await request(
1355
1705
  baseUrl,
@@ -1363,15 +1713,24 @@ var createObjectsClient = (apiKey, apiUrl) => {
1363
1713
  * required attributes you omit are left alone rather than reported missing.
1364
1714
  * Sending an explicit `null` CLEARS that attribute.
1365
1715
  *
1366
- * Values are versioned rather than overwritten, so the previous value stays
1367
- * readable through `history`.
1716
+ * Custom values retain generations through `history`. Canonical contacts and
1717
+ * companies require expectedRevision from a fresh read and expose recorded
1718
+ * mutations through `changes`. They do not yet support appendValues/removeValues.
1368
1719
  */
1369
1720
  async updateRecord(objectSlug, recordId, values, options) {
1370
1721
  const resp = await request(
1371
1722
  baseUrl,
1372
1723
  `/api/v1/objects/${encode(objectSlug)}/records/${encode(recordId)}`,
1373
1724
  apiKey,
1374
- { method: "PATCH", body: { values, ...options?.expectedRevision === void 0 ? {} : { expected_revision: options.expectedRevision } } }
1725
+ {
1726
+ method: "PATCH",
1727
+ body: {
1728
+ values,
1729
+ ...options?.expectedRevision === void 0 ? {} : { expected_revision: options.expectedRevision },
1730
+ ...options?.appendValues === void 0 ? {} : { append_values: options.appendValues },
1731
+ ...options?.removeValues === void 0 ? {} : { remove_values: options.removeValues }
1732
+ }
1733
+ }
1375
1734
  );
1376
1735
  return resp.data ?? resp;
1377
1736
  },
@@ -1410,14 +1769,27 @@ var createObjectsClient = (apiKey, apiUrl) => {
1410
1769
  { query: limit != null ? { limit } : void 0 }
1411
1770
  );
1412
1771
  return resp.data ?? resp;
1772
+ },
1773
+ /** Recorded custom-record or canonical contact/company mutations. Pass next_cursor to continue.
1774
+ * Canonical history respects current access/privacy; archived records and older
1775
+ * unrecorded writes are not reconstructed. Store unavailability returns 503.
1776
+ */
1777
+ async changes(objectSlug, recordId, options = {}) {
1778
+ const resp = await request(
1779
+ baseUrl,
1780
+ `/api/v1/objects/${encode(objectSlug)}/records/${encode(recordId)}/changes`,
1781
+ apiKey,
1782
+ { query: options }
1783
+ );
1784
+ return resp.data ?? resp;
1413
1785
  }
1414
1786
  };
1415
1787
  };
1416
1788
 
1417
1789
  // src/deals.ts
1418
- var DEFAULT_API20 = "https://be.graph8.com";
1790
+ var DEFAULT_API21 = "https://be.graph8.com";
1419
1791
  var createDealsClient = (apiKey, apiUrl) => {
1420
- const baseUrl = apiUrl || DEFAULT_API20;
1792
+ const baseUrl = apiUrl || DEFAULT_API21;
1421
1793
  return {
1422
1794
  /** List all deal pipelines and their stages. */
1423
1795
  async pipelines() {
@@ -1461,9 +1833,9 @@ var createDealsClient = (apiKey, apiUrl) => {
1461
1833
  };
1462
1834
 
1463
1835
  // src/inbox.ts
1464
- var DEFAULT_API21 = "https://be.graph8.com";
1836
+ var DEFAULT_API22 = "https://be.graph8.com";
1465
1837
  var createInboxClient = (apiKey, apiUrl) => {
1466
- const baseUrl = apiUrl || DEFAULT_API21;
1838
+ const baseUrl = apiUrl || DEFAULT_API22;
1467
1839
  return {
1468
1840
  /** List inbox threads across email, SMS, and LinkedIn. */
1469
1841
  async list(params = {}) {
@@ -1516,9 +1888,9 @@ var createInboxClient = (apiKey, apiUrl) => {
1516
1888
  };
1517
1889
 
1518
1890
  // src/quotes.ts
1519
- var DEFAULT_API22 = "https://be.graph8.com";
1891
+ var DEFAULT_API23 = "https://be.graph8.com";
1520
1892
  var createQuotesClient = (apiKey, apiUrl) => {
1521
- const baseUrl = apiUrl || DEFAULT_API22;
1893
+ const baseUrl = apiUrl || DEFAULT_API23;
1522
1894
  return {
1523
1895
  /** List quotes org-wide with optional filters and pagination. */
1524
1896
  async list(params = {}) {
@@ -1590,9 +1962,9 @@ var createQuotesClient = (apiKey, apiUrl) => {
1590
1962
  };
1591
1963
 
1592
1964
  // src/pipelines.ts
1593
- var DEFAULT_API23 = "https://be.graph8.com";
1965
+ var DEFAULT_API24 = "https://be.graph8.com";
1594
1966
  var createPipelinesClient = (apiKey, apiUrl) => {
1595
- const baseUrl = apiUrl || DEFAULT_API23;
1967
+ const baseUrl = apiUrl || DEFAULT_API24;
1596
1968
  return {
1597
1969
  /** List all stage-checklist pipelines with stages, evidence, scripts. */
1598
1970
  async list() {
@@ -1674,9 +2046,9 @@ var createPipelinesClient = (apiKey, apiUrl) => {
1674
2046
  };
1675
2047
 
1676
2048
  // src/workflows.ts
1677
- var DEFAULT_API24 = "https://be.graph8.com";
2049
+ var DEFAULT_API25 = "https://be.graph8.com";
1678
2050
  var createWorkflowsClient = (apiKey, apiUrl) => {
1679
- const baseUrl = apiUrl || DEFAULT_API24;
2051
+ const baseUrl = apiUrl || DEFAULT_API25;
1680
2052
  return {
1681
2053
  /** List workflows org-wide. */
1682
2054
  async list(params = {}) {
@@ -1792,9 +2164,9 @@ var createWorkflowsClient = (apiKey, apiUrl) => {
1792
2164
  };
1793
2165
 
1794
2166
  // src/skills.ts
1795
- var DEFAULT_API25 = "https://be.graph8.com";
2167
+ var DEFAULT_API26 = "https://be.graph8.com";
1796
2168
  var createSkillsClient = (apiKey, apiUrl) => {
1797
- const baseUrl = apiUrl || DEFAULT_API25;
2169
+ const baseUrl = apiUrl || DEFAULT_API26;
1798
2170
  return {
1799
2171
  /** List skills. */
1800
2172
  async list(params = {}) {
@@ -1881,9 +2253,9 @@ var createSkillsClient = (apiKey, apiUrl) => {
1881
2253
  };
1882
2254
 
1883
2255
  // src/intent.ts
1884
- var DEFAULT_API26 = "https://be.graph8.com";
2256
+ var DEFAULT_API27 = "https://be.graph8.com";
1885
2257
  var createIntentClient = (apiKey, apiUrl) => {
1886
- const baseUrl = apiUrl || DEFAULT_API26;
2258
+ const baseUrl = apiUrl || DEFAULT_API27;
1887
2259
  const get = (path) => request(baseUrl, `/api/v1${path}`, apiKey);
1888
2260
  const post = (path, body = {}) => request(baseUrl, `/api/v1${path}`, apiKey, { method: "POST", body });
1889
2261
  const del = (path) => request(baseUrl, `/api/v1${path}`, apiKey, { method: "DELETE" });
@@ -1958,9 +2330,9 @@ var createIntentClient = (apiKey, apiUrl) => {
1958
2330
  };
1959
2331
 
1960
2332
  // src/studio.ts
1961
- var DEFAULT_API27 = "https://be.graph8.com";
2333
+ var DEFAULT_API28 = "https://be.graph8.com";
1962
2334
  var createStudioClient = (apiKey, apiUrl) => {
1963
- const baseUrl = apiUrl || DEFAULT_API27;
2335
+ const baseUrl = apiUrl || DEFAULT_API28;
1964
2336
  const get = (path, params = {}) => request(baseUrl, `/api/v1${path}`, apiKey, { query: params });
1965
2337
  const post = (path, body = {}) => request(baseUrl, `/api/v1${path}`, apiKey, { method: "POST", body });
1966
2338
  const patch = (path, body = {}) => request(baseUrl, `/api/v1${path}`, apiKey, { method: "PATCH", body });
@@ -2015,9 +2387,9 @@ var createStudioClient = (apiKey, apiUrl) => {
2015
2387
  };
2016
2388
 
2017
2389
  // src/meetings.ts
2018
- var DEFAULT_API28 = "https://be.graph8.com";
2390
+ var DEFAULT_API29 = "https://be.graph8.com";
2019
2391
  var createMeetingsClient = (apiKey, apiUrl) => {
2020
- const baseUrl = apiUrl || DEFAULT_API28;
2392
+ const baseUrl = apiUrl || DEFAULT_API29;
2021
2393
  return {
2022
2394
  /** List meetings with optional filters. Returns summary rows without transcript / analysis. */
2023
2395
  async list(params = {}) {
@@ -2032,9 +2404,9 @@ var createMeetingsClient = (apiKey, apiUrl) => {
2032
2404
  };
2033
2405
 
2034
2406
  // src/audiences.ts
2035
- var DEFAULT_API29 = "https://be.graph8.com";
2407
+ var DEFAULT_API30 = "https://be.graph8.com";
2036
2408
  var createAudiencesClient = (apiKey, apiUrl) => {
2037
- const baseUrl = apiUrl || DEFAULT_API29;
2409
+ const baseUrl = apiUrl || DEFAULT_API30;
2038
2410
  const base = "/api/v1/audience-syncs";
2039
2411
  return {
2040
2412
  /** List all audience syncs for the organization. */
@@ -2082,9 +2454,9 @@ var createAudiencesClient = (apiKey, apiUrl) => {
2082
2454
  };
2083
2455
 
2084
2456
  // src/search.ts
2085
- var DEFAULT_API30 = "https://be.graph8.com";
2457
+ var DEFAULT_API31 = "https://be.graph8.com";
2086
2458
  var createSearchClient = (apiKey, apiUrl) => {
2087
- const baseUrl = apiUrl || DEFAULT_API30;
2459
+ const baseUrl = apiUrl || DEFAULT_API31;
2088
2460
  const body = (p) => ({ filters: [], page: 1, limit: 25, ...p });
2089
2461
  return {
2090
2462
  /** Search open-data contacts by filter. */
@@ -2115,9 +2487,9 @@ var createSearchClient = (apiKey, apiUrl) => {
2115
2487
  };
2116
2488
 
2117
2489
  // src/agency.ts
2118
- var DEFAULT_API31 = "https://be.graph8.com";
2490
+ var DEFAULT_API32 = "https://be.graph8.com";
2119
2491
  var createAgencyClient = (apiKey, apiUrl) => {
2120
- const baseUrl = apiUrl || DEFAULT_API31;
2492
+ const baseUrl = apiUrl || DEFAULT_API32;
2121
2493
  return {
2122
2494
  /** Describe the agency credential: agency org + authorized client count. */
2123
2495
  async me() {
@@ -2132,9 +2504,9 @@ var createAgencyClient = (apiKey, apiUrl) => {
2132
2504
  };
2133
2505
 
2134
2506
  // src/marketplace.ts
2135
- var DEFAULT_API32 = "https://be.graph8.com";
2507
+ var DEFAULT_API33 = "https://be.graph8.com";
2136
2508
  var createMarketplaceClient = (apiKey, apiUrl) => {
2137
- const baseUrl = apiUrl || DEFAULT_API32;
2509
+ const baseUrl = apiUrl || DEFAULT_API33;
2138
2510
  const base = "/api/v1/marketplace";
2139
2511
  return {
2140
2512
  /** Your own marketplace SDR profile. */
@@ -2184,9 +2556,9 @@ var createMarketplaceClient = (apiKey, apiUrl) => {
2184
2556
  };
2185
2557
 
2186
2558
  // src/snippet.ts
2187
- var DEFAULT_API33 = "https://be.graph8.com";
2559
+ var DEFAULT_API34 = "https://be.graph8.com";
2188
2560
  var createSnippetClient = (apiKey, apiUrl) => {
2189
- const baseUrl = apiUrl || DEFAULT_API33;
2561
+ const baseUrl = apiUrl || DEFAULT_API34;
2190
2562
  return {
2191
2563
  /** Get your org's tracking snippet (write key + React/script-tag embeds + config). */
2192
2564
  async get() {
@@ -2198,7 +2570,7 @@ var createSnippetClient = (apiKey, apiUrl) => {
2198
2570
 
2199
2571
  // src/core.ts
2200
2572
  var DEFAULT_HOST = "https://t.graph8.com";
2201
- var DEFAULT_API34 = "https://be.graph8.com";
2573
+ var DEFAULT_API35 = "https://be.graph8.com";
2202
2574
  var G8 = class {
2203
2575
  constructor() {
2204
2576
  /** @internal */
@@ -2242,6 +2614,8 @@ var G8 = class {
2242
2614
  /** @internal */
2243
2615
  this._apps = null;
2244
2616
  /** @internal */
2617
+ this._appPlatform = null;
2618
+ /** @internal */
2245
2619
  this._objects = null;
2246
2620
  /** @internal */
2247
2621
  this._deals = null;
@@ -2285,7 +2659,7 @@ var G8 = class {
2285
2659
  debug: config.debug
2286
2660
  });
2287
2661
  }
2288
- const apiUrl = config.apiUrl || DEFAULT_API34;
2662
+ const apiUrl = config.apiUrl || DEFAULT_API35;
2289
2663
  const writeKey = config.writeKey || "";
2290
2664
  const apiKey = config.apiKey || "";
2291
2665
  if (writeKey) {
@@ -2309,6 +2683,7 @@ var G8 = class {
2309
2683
  this._tasks = createTasksClient(apiKey, apiUrl);
2310
2684
  this._fields = createFieldsClient(apiKey, apiUrl);
2311
2685
  this._apps = createAppsClient(apiKey, apiUrl);
2686
+ this._appPlatform = createAppPlatformClient(apiKey, apiUrl);
2312
2687
  this._objects = createObjectsClient(apiKey, apiUrl);
2313
2688
  this._deals = createDealsClient(apiKey, apiUrl);
2314
2689
  this._inbox = createInboxClient(apiKey, apiUrl);
@@ -2435,6 +2810,18 @@ var G8 = class {
2435
2810
  this._assertKey("apps");
2436
2811
  return this._apps;
2437
2812
  }
2813
+ /**
2814
+ * Ship a hosted app: bind a source, deploy, promote, attach a hostname, read
2815
+ * build logs (requires API key). PREVIEW.
2816
+ *
2817
+ * Separate from `apps` on purpose. `apps` is the app's IDENTITY -- create it,
2818
+ * see who installed it, what it cost. This is its LIFECYCLE, and the two have
2819
+ * different blast radii: a mistake here takes a customer's app down.
2820
+ */
2821
+ get appPlatform() {
2822
+ this._assertKey("appPlatform");
2823
+ return this._appPlatform;
2824
+ }
2438
2825
  /** Custom object types, their schema, and their records (requires API key). PREVIEW. */
2439
2826
  get objects() {
2440
2827
  this._assertKey("objects");
@@ -2692,9 +3079,12 @@ function createGraph8ServiceClient(config) {
2692
3079
  DEFAULT_APP_API,
2693
3080
  G8Error,
2694
3081
  KNOWN_WEBHOOK_EVENTS,
3082
+ MAX_TAIL_LINES,
3083
+ MIN_TAIL_LINES,
2695
3084
  WebhookSignatureError,
2696
3085
  backoffDelayMs,
2697
3086
  constructEvent,
3087
+ createAppPlatformClient,
2698
3088
  createAppRequester,
2699
3089
  createGraph8AppClient,
2700
3090
  createGraph8ServiceClient,