exponential-sdk 1.28.0 → 1.31.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/README.md +40 -0
- package/dist/index.d.ts +658 -1
- package/dist/index.js +306 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -519,6 +519,52 @@ var WorkspacesApi = class {
|
|
|
519
519
|
}
|
|
520
520
|
};
|
|
521
521
|
|
|
522
|
+
// src/positions.ts
|
|
523
|
+
var PositionsApi = class {
|
|
524
|
+
constructor(client) {
|
|
525
|
+
this.client = client;
|
|
526
|
+
}
|
|
527
|
+
/** Every Position with its holders, plus each member's coverage. */
|
|
528
|
+
async list(workspaceId) {
|
|
529
|
+
return await this.client.position.list.query({ workspaceId });
|
|
530
|
+
}
|
|
531
|
+
/** One Position by id, from `list` (the router has no single-row read). */
|
|
532
|
+
async get(workspaceId, positionId) {
|
|
533
|
+
const { positions } = await this.list(workspaceId);
|
|
534
|
+
return positions.find((position) => position.id === positionId) ?? null;
|
|
535
|
+
}
|
|
536
|
+
/** How many members have a stated remit, and whether to offer an import. */
|
|
537
|
+
async coverage(workspaceId) {
|
|
538
|
+
return await this.client.position.coverage.query({ workspaceId });
|
|
539
|
+
}
|
|
540
|
+
async create(input) {
|
|
541
|
+
return await this.client.position.create.mutate(input);
|
|
542
|
+
}
|
|
543
|
+
async update(input) {
|
|
544
|
+
const { workspaceId, positionId, ...edits } = input;
|
|
545
|
+
if (Object.values(edits).every((value) => value === void 0)) {
|
|
546
|
+
throw new Error("Nothing to update: pass title, remit, notAccountableFor or holderUserIds");
|
|
547
|
+
}
|
|
548
|
+
return await this.client.position.update.mutate(input);
|
|
549
|
+
}
|
|
550
|
+
/** Holders cascade. */
|
|
551
|
+
async delete(workspaceId, positionId) {
|
|
552
|
+
return await this.client.position.delete.mutate({ workspaceId, positionId });
|
|
553
|
+
}
|
|
554
|
+
/** Replace the holder set; `[]` removes everyone. */
|
|
555
|
+
async setHolders(workspaceId, positionId, userIds) {
|
|
556
|
+
return await this.client.position.setHolders.mutate({ workspaceId, positionId, userIds });
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Upsert many Positions by title (matched case-insensitively, never renamed).
|
|
560
|
+
* An update replaces the remit and only ADDS holders. Run with
|
|
561
|
+
* `dryRun: true` first to see the plan.
|
|
562
|
+
*/
|
|
563
|
+
async importMany(input) {
|
|
564
|
+
return await this.client.position.importMany.mutate(input);
|
|
565
|
+
}
|
|
566
|
+
};
|
|
567
|
+
|
|
522
568
|
// src/ceremonies.ts
|
|
523
569
|
var CeremoniesApi = class {
|
|
524
570
|
constructor(client) {
|
|
@@ -1279,6 +1325,238 @@ ${text}` : text;
|
|
|
1279
1325
|
}
|
|
1280
1326
|
};
|
|
1281
1327
|
|
|
1328
|
+
// src/calendar.ts
|
|
1329
|
+
import { randomUUID } from "crypto";
|
|
1330
|
+
var CALENDAR_MAX_RESULTS = 100;
|
|
1331
|
+
var CALENDAR_MAX_SELECTED = 10;
|
|
1332
|
+
function hostTimeZone() {
|
|
1333
|
+
try {
|
|
1334
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1335
|
+
} catch {
|
|
1336
|
+
return void 0;
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
var CalendarApi = class {
|
|
1340
|
+
constructor(client) {
|
|
1341
|
+
this.client = client;
|
|
1342
|
+
}
|
|
1343
|
+
/** Merged, start-time-sorted events from every connected account. */
|
|
1344
|
+
async listEvents(options = {}) {
|
|
1345
|
+
return await this.client.calendar.getEventsMultiCalendar.query({
|
|
1346
|
+
timeMin: options.from,
|
|
1347
|
+
timeMax: options.to,
|
|
1348
|
+
maxResults: options.maxResults
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Everything on today's date, in the host's local timezone — including
|
|
1353
|
+
* all-day events and meetings that started before now.
|
|
1354
|
+
*/
|
|
1355
|
+
async today(options = {}) {
|
|
1356
|
+
const from = /* @__PURE__ */ new Date();
|
|
1357
|
+
from.setHours(0, 0, 0, 0);
|
|
1358
|
+
const to = new Date(from);
|
|
1359
|
+
to.setHours(23, 59, 59, 999);
|
|
1360
|
+
return this.listEvents({ from, to, maxResults: options.maxResults });
|
|
1361
|
+
}
|
|
1362
|
+
/** Events from now until `days` days ahead (default 7). */
|
|
1363
|
+
async upcoming(options = {}) {
|
|
1364
|
+
const from = /* @__PURE__ */ new Date();
|
|
1365
|
+
const to = new Date(from.getTime() + (options.days ?? 7) * 24 * 60 * 60 * 1e3);
|
|
1366
|
+
return this.listEvents({ from, to, maxResults: options.maxResults });
|
|
1367
|
+
}
|
|
1368
|
+
/** Connected accounts, each with its calendars and current selection. */
|
|
1369
|
+
async accounts() {
|
|
1370
|
+
const result = await this.client.calendar.getCalendarAccounts.query();
|
|
1371
|
+
return result.accounts;
|
|
1372
|
+
}
|
|
1373
|
+
/** Per-provider connection state, including accounts needing re-auth. */
|
|
1374
|
+
async connectionStatuses() {
|
|
1375
|
+
return await this.client.calendar.getAllConnectionStatuses.query();
|
|
1376
|
+
}
|
|
1377
|
+
/** Every calendar on one account, straight from the provider (uncached). */
|
|
1378
|
+
async listCalendars(selector = {}) {
|
|
1379
|
+
return await this.client.calendar.listCalendars.query(selector);
|
|
1380
|
+
}
|
|
1381
|
+
/** One account's selected calendars plus its cached calendar list. */
|
|
1382
|
+
async getPreferences(selector = {}) {
|
|
1383
|
+
return await this.client.calendar.getCalendarPreferences.query(
|
|
1384
|
+
selector
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Replace which calendars on an account feed the merged event list.
|
|
1389
|
+
* This is a replace, not a merge — pass the full set you want.
|
|
1390
|
+
*/
|
|
1391
|
+
async selectCalendars(input) {
|
|
1392
|
+
return await this.client.calendar.updateSelectedCalendars.mutate(input);
|
|
1393
|
+
}
|
|
1394
|
+
/** Re-fetch an account's calendar list from the provider and re-cache it. */
|
|
1395
|
+
async syncCalendars(selector = {}) {
|
|
1396
|
+
return await this.client.calendar.syncCalendarList.mutate(selector);
|
|
1397
|
+
}
|
|
1398
|
+
async createEvent(input) {
|
|
1399
|
+
const timeZone = input.timeZone ?? hostTimeZone();
|
|
1400
|
+
return await this.client.calendar.createEvent.mutate({
|
|
1401
|
+
summary: input.summary,
|
|
1402
|
+
description: input.description,
|
|
1403
|
+
// RFC3339 UTC — unambiguous regardless of host offset. `timeZone` only
|
|
1404
|
+
// tells the provider which zone to render the event in.
|
|
1405
|
+
start: { dateTime: input.start.toISOString(), timeZone },
|
|
1406
|
+
end: { dateTime: input.end.toISOString(), timeZone },
|
|
1407
|
+
attendees: input.attendees?.map((email) => ({ email })),
|
|
1408
|
+
conferenceData: input.meet ? {
|
|
1409
|
+
createRequest: {
|
|
1410
|
+
requestId: randomUUID(),
|
|
1411
|
+
conferenceSolutionKey: { type: "hangoutsMeet" }
|
|
1412
|
+
}
|
|
1413
|
+
} : void 0,
|
|
1414
|
+
calendarId: input.calendarId,
|
|
1415
|
+
provider: input.provider
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Remove a calendar connection. Deletes the stored tokens and calendar
|
|
1420
|
+
* preferences; it never touches the user's ability to sign in with that
|
|
1421
|
+
* account. Reconnecting means redoing the OAuth flow in the web app.
|
|
1422
|
+
*/
|
|
1423
|
+
async disconnect(selector = {}) {
|
|
1424
|
+
return await this.client.calendar.disconnect.mutate(selector);
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
|
|
1428
|
+
// src/cycles.ts
|
|
1429
|
+
function describeCycle(c) {
|
|
1430
|
+
return c.productId ? `"${c.name}" (slug ${c.slug}, product ${c.productId})` : `"${c.name}" (slug ${c.slug}, workspace-shared)`;
|
|
1431
|
+
}
|
|
1432
|
+
var CyclesApi = class {
|
|
1433
|
+
constructor(client) {
|
|
1434
|
+
this.client = client;
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* List cycles in a workspace, newest start date first. Pass `productId` to
|
|
1438
|
+
* scope to one product's timeline (plus legacy workspace-shared cycles).
|
|
1439
|
+
*
|
|
1440
|
+
* Note this is not a pure read: the server reconciles cycle statuses against
|
|
1441
|
+
* today's date and (unless `autoCreate: false`) provisions upcoming cycles —
|
|
1442
|
+
* scoped to `productId` when given.
|
|
1443
|
+
*/
|
|
1444
|
+
async list(options) {
|
|
1445
|
+
return await this.client.product.cycle.list.query({
|
|
1446
|
+
workspaceId: options.workspaceId,
|
|
1447
|
+
productId: options.productId,
|
|
1448
|
+
status: options.status,
|
|
1449
|
+
autoCreate: options.autoCreate
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
async get(id) {
|
|
1453
|
+
return await this.client.product.cycle.getById.query({ id });
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* The current (`ACTIVE`) cycle, or `null` when none is running (e.g. a gap
|
|
1457
|
+
* between cycles).
|
|
1458
|
+
*
|
|
1459
|
+
* Cycles are product-scoped, so a workspace can have one ACTIVE cycle per
|
|
1460
|
+
* product. Pass `product` (a product CUID or slug) to get that product's
|
|
1461
|
+
* current cycle — a cycle scoped to the product wins over a legacy
|
|
1462
|
+
* workspace-shared one.
|
|
1463
|
+
*
|
|
1464
|
+
* Without `product`, this stays backward compatible for workspaces with a
|
|
1465
|
+
* single timeline: if every ACTIVE cycle belongs to the same product (or all
|
|
1466
|
+
* are workspace-shared), the most recently started wins. If ACTIVE cycles
|
|
1467
|
+
* from different products coexist, it throws rather than silently picking
|
|
1468
|
+
* an arbitrary product's cycle.
|
|
1469
|
+
*/
|
|
1470
|
+
async current(workspaceId, product) {
|
|
1471
|
+
if (product) {
|
|
1472
|
+
const productId = await this.resolveProductId(workspaceId, product);
|
|
1473
|
+
const active2 = await this.list({ workspaceId, productId, status: "ACTIVE", autoCreate: false });
|
|
1474
|
+
return active2.find((c) => c.productId === productId) ?? active2[0] ?? null;
|
|
1475
|
+
}
|
|
1476
|
+
const active = await this.list({ workspaceId, status: "ACTIVE", autoCreate: false });
|
|
1477
|
+
if (active.length <= 1) return active[0] ?? null;
|
|
1478
|
+
const timelines = new Set(active.map((c) => c.productId ?? null));
|
|
1479
|
+
if (timelines.size === 1) return active[0] ?? null;
|
|
1480
|
+
throw new Error(
|
|
1481
|
+
`Multiple cycles are ACTIVE in this workspace \u2014 one per product \u2014 so "current" is ambiguous. Pass a product id or slug to pick one. Active: ${active.map(describeCycle).join(", ")}.`
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Resolve what a user typed to a cycle.
|
|
1486
|
+
*
|
|
1487
|
+
* Accepts, in order of precedence:
|
|
1488
|
+
* - `"current"` / `"active"` → the ACTIVE cycle (see `current()`)
|
|
1489
|
+
* - a cycle CUID
|
|
1490
|
+
* - a cycle slug
|
|
1491
|
+
* - a cycle name (case-insensitive)
|
|
1492
|
+
*
|
|
1493
|
+
* Pass `product` (a product CUID or slug) to scope the lookup to that
|
|
1494
|
+
* product's timeline. Cycle auto-numbering is per-product, so two products
|
|
1495
|
+
* can both have a cycle named "Cycle 9" in one workspace: with `product`
|
|
1496
|
+
* set, a cycle scoped to that product beats a legacy workspace-shared one
|
|
1497
|
+
* of the same name; without it, an ambiguous name throws rather than
|
|
1498
|
+
* silently picking one. Ids and slugs are unique per workspace and never
|
|
1499
|
+
* ambiguous.
|
|
1500
|
+
*
|
|
1501
|
+
* Throws rather than returning null so callers never silently file work
|
|
1502
|
+
* against the wrong cycle — or against none at all.
|
|
1503
|
+
*/
|
|
1504
|
+
async resolve(workspaceId, slugOrIdOrName, product) {
|
|
1505
|
+
const candidate = slugOrIdOrName.trim();
|
|
1506
|
+
const lower = candidate.toLowerCase();
|
|
1507
|
+
if (lower === "current" || lower === "active") {
|
|
1508
|
+
const cycle = await this.current(workspaceId, product);
|
|
1509
|
+
if (!cycle) {
|
|
1510
|
+
throw new Error(
|
|
1511
|
+
product ? `No cycle is currently active for product "${product}". Pass an explicit cycle slug or id, or create a cycle first.` : "No cycle is currently active in this workspace. Pass an explicit cycle slug or id, or create a cycle first."
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
return cycle;
|
|
1515
|
+
}
|
|
1516
|
+
const productId = product ? await this.resolveProductId(workspaceId, product) : void 0;
|
|
1517
|
+
const cycles = await this.list({ workspaceId, productId, autoCreate: false });
|
|
1518
|
+
const exact = cycles.find((c) => c.id === candidate) ?? cycles.find((c) => c.slug === candidate);
|
|
1519
|
+
if (exact) return exact;
|
|
1520
|
+
const byName = cycles.filter((c) => c.name.toLowerCase() === lower);
|
|
1521
|
+
if (byName.length === 1) return byName[0];
|
|
1522
|
+
if (byName.length > 1) {
|
|
1523
|
+
if (productId) {
|
|
1524
|
+
const scoped = byName.filter((c) => c.productId === productId);
|
|
1525
|
+
if (scoped.length === 1) return scoped[0];
|
|
1526
|
+
}
|
|
1527
|
+
throw new Error(
|
|
1528
|
+
`Cycle name "${candidate}" is ambiguous: ${byName.map(describeCycle).join(", ")}. Use the slug or id instead${productId ? "" : ", or pass a product to scope the lookup"}.`
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1531
|
+
const known = cycles.map((c) => c.slug).join(", ");
|
|
1532
|
+
throw new Error(
|
|
1533
|
+
`Cycle "${candidate}" not found. Available: ${known || "(none)"}`
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
async create(input) {
|
|
1537
|
+
return await this.client.product.cycle.create.mutate(input);
|
|
1538
|
+
}
|
|
1539
|
+
async update(input) {
|
|
1540
|
+
return await this.client.product.cycle.update.mutate(input);
|
|
1541
|
+
}
|
|
1542
|
+
async delete(id) {
|
|
1543
|
+
return await this.client.product.cycle.delete.mutate({ id });
|
|
1544
|
+
}
|
|
1545
|
+
/**
|
|
1546
|
+
* Turn a product reference (CUID or slug) into the CUID the cycle endpoints
|
|
1547
|
+
* expect. CUIDs pass through without a lookup — the server validates access.
|
|
1548
|
+
*/
|
|
1549
|
+
async resolveProductId(workspaceId, product) {
|
|
1550
|
+
const ref = product.trim();
|
|
1551
|
+
if (/^c[a-z0-9]{20,}$/.test(ref)) return ref;
|
|
1552
|
+
const match = await this.client.product.product.getBySlug.query({
|
|
1553
|
+
workspaceId,
|
|
1554
|
+
slug: ref
|
|
1555
|
+
});
|
|
1556
|
+
return match.id;
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
|
|
1282
1560
|
// src/pages.ts
|
|
1283
1561
|
var PagesApi = class {
|
|
1284
1562
|
constructor(client) {
|
|
@@ -1561,6 +1839,22 @@ var SearchApi = class {
|
|
|
1561
1839
|
}
|
|
1562
1840
|
};
|
|
1563
1841
|
|
|
1842
|
+
// src/users.ts
|
|
1843
|
+
var UsersApi = class {
|
|
1844
|
+
constructor(client) {
|
|
1845
|
+
this.client = client;
|
|
1846
|
+
}
|
|
1847
|
+
/**
|
|
1848
|
+
* The account this client's token signs in as. For an External agent's key
|
|
1849
|
+
* the result is the agent's shadow user: `isAgent` is true and `agent`
|
|
1850
|
+
* carries the agent's name and owner — the identity to grant access to when
|
|
1851
|
+
* a command comes back FORBIDDEN.
|
|
1852
|
+
*/
|
|
1853
|
+
async me() {
|
|
1854
|
+
return await this.client.user.getCurrentUser.query();
|
|
1855
|
+
}
|
|
1856
|
+
};
|
|
1857
|
+
|
|
1564
1858
|
// src/time.ts
|
|
1565
1859
|
function startOfLocalDay(date) {
|
|
1566
1860
|
const d = typeof date === "string" && /^\d{4}-\d{2}-\d{2}$/.test(date) ? /* @__PURE__ */ new Date(`${date}T00:00:00`) : new Date(date);
|
|
@@ -1836,6 +2130,7 @@ var ExponentialClient = class {
|
|
|
1836
2130
|
this.goals = new GoalsApi(this.client, this.keyResults);
|
|
1837
2131
|
this.projects = new ProjectsApi(this.client);
|
|
1838
2132
|
this.workspaces = new WorkspacesApi(this.client);
|
|
2133
|
+
this.positions = new PositionsApi(this.client);
|
|
1839
2134
|
this.ceremonies = new CeremoniesApi(this.client);
|
|
1840
2135
|
this.contacts = new ContactsApi(this.client);
|
|
1841
2136
|
this.decisions = new DecisionsApi(this.client);
|
|
@@ -1849,12 +2144,15 @@ var ExponentialClient = class {
|
|
|
1849
2144
|
this.labels = new LabelsApi(this.client);
|
|
1850
2145
|
this.areas = new AreasApi(this.client);
|
|
1851
2146
|
this.meetings = new MeetingsApi(this.client);
|
|
2147
|
+
this.calendar = new CalendarApi(this.client);
|
|
2148
|
+
this.cycles = new CyclesApi(this.client);
|
|
1852
2149
|
this.pages = new PagesApi(this.client);
|
|
1853
2150
|
this.databases = new DatabasesApi(this.client);
|
|
1854
2151
|
this.resources = new ResourcesApi(this.client);
|
|
1855
2152
|
this.requirements = new RequirementsApi(this.client);
|
|
1856
2153
|
this.scopes = new ScopesApi(this.client);
|
|
1857
2154
|
this.search = new SearchApi(this.client);
|
|
2155
|
+
this.users = new UsersApi(this.client);
|
|
1858
2156
|
this.time = new TimeApi(this.client);
|
|
1859
2157
|
this.agentRuns = new AgentRunsApi(this.client);
|
|
1860
2158
|
}
|
|
@@ -1869,6 +2167,8 @@ var ExponentialClient = class {
|
|
|
1869
2167
|
keyResults;
|
|
1870
2168
|
projects;
|
|
1871
2169
|
workspaces;
|
|
2170
|
+
/** Positions — a workspace's roles & responsibilities (ADR-0068). */
|
|
2171
|
+
positions;
|
|
1872
2172
|
/** Ceremonies and their occurrences (ADR-0059). */
|
|
1873
2173
|
ceremonies;
|
|
1874
2174
|
contacts;
|
|
@@ -1883,6 +2183,8 @@ var ExponentialClient = class {
|
|
|
1883
2183
|
labels;
|
|
1884
2184
|
areas;
|
|
1885
2185
|
meetings;
|
|
2186
|
+
calendar;
|
|
2187
|
+
cycles;
|
|
1886
2188
|
pages;
|
|
1887
2189
|
/** Databases — typed tables inside pages (ADR-0078). */
|
|
1888
2190
|
databases;
|
|
@@ -1890,6 +2192,7 @@ var ExponentialClient = class {
|
|
|
1890
2192
|
requirements;
|
|
1891
2193
|
scopes;
|
|
1892
2194
|
search;
|
|
2195
|
+
users;
|
|
1893
2196
|
/** Time entries — the Daily worklog's write path (ADR-0061). */
|
|
1894
2197
|
time;
|
|
1895
2198
|
/** Agent runs — the local runner's claim/heartbeat/append/finish (ADR-0067); agent-key only. */
|
|
@@ -1897,9 +2200,12 @@ var ExponentialClient = class {
|
|
|
1897
2200
|
};
|
|
1898
2201
|
export {
|
|
1899
2202
|
AgentRunsApi,
|
|
2203
|
+
CALENDAR_MAX_RESULTS,
|
|
2204
|
+
CALENDAR_MAX_SELECTED,
|
|
1900
2205
|
CeremoniesApi,
|
|
1901
2206
|
DEFAULT_GAP_MINUTES,
|
|
1902
2207
|
ExponentialClient,
|
|
2208
|
+
PositionsApi,
|
|
1903
2209
|
ROUND_MINUTES,
|
|
1904
2210
|
TRPCClientError,
|
|
1905
2211
|
TimeApi,
|