@wayai/cli 0.3.220 → 0.3.222
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 +798 -621
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4604,6 +4604,38 @@ var init_zod = __esm({
|
|
|
4604
4604
|
}
|
|
4605
4605
|
});
|
|
4606
4606
|
|
|
4607
|
+
// ../../packages/core/dist/chunk-VXEAR36B.js
|
|
4608
|
+
var orgTagIdParam, listOrgTagsQuery, ORG_TAG_NAME_REGEX, ORG_TAG_NAME_MAX_LENGTH, createOrgTagBody, updateOrgTagBody;
|
|
4609
|
+
var init_chunk_VXEAR36B = __esm({
|
|
4610
|
+
"../../packages/core/dist/chunk-VXEAR36B.js"() {
|
|
4611
|
+
"use strict";
|
|
4612
|
+
init_zod();
|
|
4613
|
+
orgTagIdParam = external_exports.object({
|
|
4614
|
+
id: external_exports.string().uuid()
|
|
4615
|
+
});
|
|
4616
|
+
listOrgTagsQuery = external_exports.object({
|
|
4617
|
+
organization_id: external_exports.string().uuid()
|
|
4618
|
+
});
|
|
4619
|
+
ORG_TAG_NAME_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
4620
|
+
ORG_TAG_NAME_MAX_LENGTH = 50;
|
|
4621
|
+
createOrgTagBody = external_exports.object({
|
|
4622
|
+
organization_id: external_exports.string().uuid(),
|
|
4623
|
+
name: external_exports.string().min(1).max(ORG_TAG_NAME_MAX_LENGTH).regex(ORG_TAG_NAME_REGEX, {
|
|
4624
|
+
message: 'Must be lowercase alphanumeric with hyphens (e.g. "my-tag")'
|
|
4625
|
+
}),
|
|
4626
|
+
display_name: external_exports.string().max(100).nullish(),
|
|
4627
|
+
color: external_exports.string().max(20).nullish()
|
|
4628
|
+
});
|
|
4629
|
+
updateOrgTagBody = external_exports.object({
|
|
4630
|
+
// Required so the route targets the org the UI is managing (which may differ
|
|
4631
|
+
// from the token's default `auth.org_id` for multi-org users) — mirrors create/list.
|
|
4632
|
+
organization_id: external_exports.string().uuid(),
|
|
4633
|
+
display_name: external_exports.string().max(100).nullish(),
|
|
4634
|
+
color: external_exports.string().max(20).nullish()
|
|
4635
|
+
});
|
|
4636
|
+
}
|
|
4637
|
+
});
|
|
4638
|
+
|
|
4607
4639
|
// ../../packages/core/dist/chunk-HZ6CADSY.js
|
|
4608
4640
|
function optionalTrimmedText(max) {
|
|
4609
4641
|
return external_exports.string().trim().max(max).optional().transform((value) => value ? value : void 0);
|
|
@@ -5251,34 +5283,21 @@ var init_chunk_FK6HBXM7 = __esm({
|
|
|
5251
5283
|
}
|
|
5252
5284
|
});
|
|
5253
5285
|
|
|
5254
|
-
// ../../packages/core/dist/chunk-
|
|
5255
|
-
var
|
|
5256
|
-
var
|
|
5257
|
-
"../../packages/core/dist/chunk-
|
|
5286
|
+
// ../../packages/core/dist/chunk-GPM7PE44.js
|
|
5287
|
+
var bootstrapQuery;
|
|
5288
|
+
var init_chunk_GPM7PE44 = __esm({
|
|
5289
|
+
"../../packages/core/dist/chunk-GPM7PE44.js"() {
|
|
5258
5290
|
"use strict";
|
|
5259
5291
|
init_zod();
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
organization_id: external_exports.string().uuid(),
|
|
5270
|
-
name: external_exports.string().min(1).max(ORG_TAG_NAME_MAX_LENGTH).regex(ORG_TAG_NAME_REGEX, {
|
|
5271
|
-
message: 'Must be lowercase alphanumeric with hyphens (e.g. "my-tag")'
|
|
5272
|
-
}),
|
|
5273
|
-
display_name: external_exports.string().max(100).nullish(),
|
|
5274
|
-
color: external_exports.string().max(20).nullish()
|
|
5275
|
-
});
|
|
5276
|
-
updateOrgTagBody = external_exports.object({
|
|
5277
|
-
// Required so the route targets the org the UI is managing (which may differ
|
|
5278
|
-
// from the token's default `auth.org_id` for multi-org users) — mirrors create/list.
|
|
5279
|
-
organization_id: external_exports.string().uuid(),
|
|
5280
|
-
display_name: external_exports.string().max(100).nullish(),
|
|
5281
|
-
color: external_exports.string().max(20).nullish()
|
|
5292
|
+
bootstrapQuery = external_exports.object({
|
|
5293
|
+
active_hub_id: external_exports.string().uuid().optional(),
|
|
5294
|
+
active_conversation_id: external_exports.string().uuid().optional(),
|
|
5295
|
+
active_nav: external_exports.enum(["chat", "task", "support"]).optional(),
|
|
5296
|
+
/**
|
|
5297
|
+
* The people thread the URL names (`/chat/threads/:threadId`, gh #4661): the snapshot
|
|
5298
|
+
* preloads it as `people.active_thread` when the viewer is a member.
|
|
5299
|
+
*/
|
|
5300
|
+
active_thread_id: external_exports.string().uuid().optional()
|
|
5282
5301
|
});
|
|
5283
5302
|
}
|
|
5284
5303
|
});
|
|
@@ -5553,6 +5572,583 @@ var init_chunk_5DGHIY7L = __esm({
|
|
|
5553
5572
|
}
|
|
5554
5573
|
});
|
|
5555
5574
|
|
|
5575
|
+
// ../../packages/core/dist/chunk-F3JHI4Y4.js
|
|
5576
|
+
var DATA_PROXY_MOUNT, DATA_PROXY_PREFIX, BASE_DESTRUCTION_MOUNT, BASE_DESTRUCTION_PREFIX, BASE_DESTRUCTION_CONFIRM_PATH, COMMUNITY_EMAIL_UNSUBSCRIBE_PATH, CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH, AUTOMATION_WEBHOOK_MOUNT;
|
|
5577
|
+
var init_chunk_F3JHI4Y4 = __esm({
|
|
5578
|
+
"../../packages/core/dist/chunk-F3JHI4Y4.js"() {
|
|
5579
|
+
"use strict";
|
|
5580
|
+
DATA_PROXY_MOUNT = "/data";
|
|
5581
|
+
DATA_PROXY_PREFIX = `/api${DATA_PROXY_MOUNT}`;
|
|
5582
|
+
BASE_DESTRUCTION_MOUNT = "/bases";
|
|
5583
|
+
BASE_DESTRUCTION_PREFIX = `/api${BASE_DESTRUCTION_MOUNT}`;
|
|
5584
|
+
BASE_DESTRUCTION_CONFIRM_PATH = `${BASE_DESTRUCTION_PREFIX}/destruction/confirm`;
|
|
5585
|
+
COMMUNITY_EMAIL_UNSUBSCRIBE_PATH = "/api/communities/email-unsubscribe";
|
|
5586
|
+
CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH = "/api/contact-book/email-unsubscribe";
|
|
5587
|
+
AUTOMATION_WEBHOOK_MOUNT = "/webhooks/automations";
|
|
5588
|
+
}
|
|
5589
|
+
});
|
|
5590
|
+
|
|
5591
|
+
// ../../packages/core/dist/chunk-IEWJ5MZH.js
|
|
5592
|
+
function matchPath(pattern, path42) {
|
|
5593
|
+
const normalizedPath = path42.replace(/\/$/, "");
|
|
5594
|
+
if (pattern.endsWith("/*")) {
|
|
5595
|
+
const prefix = pattern.slice(0, -2);
|
|
5596
|
+
return normalizedPath === prefix || normalizedPath.startsWith(prefix + "/");
|
|
5597
|
+
}
|
|
5598
|
+
return pattern === normalizedPath;
|
|
5599
|
+
}
|
|
5600
|
+
function getEndpointConfig(path42) {
|
|
5601
|
+
for (const excluded of EXCLUDED_PATHS) {
|
|
5602
|
+
if (matchPath(excluded, path42)) {
|
|
5603
|
+
return null;
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
const normalizedPath = path42.replace(/\/$/, "");
|
|
5607
|
+
if (!normalizedPath.endsWith("/*") && Object.prototype.hasOwnProperty.call(ENDPOINT_CONFIGS, normalizedPath)) {
|
|
5608
|
+
return ENDPOINT_CONFIGS[normalizedPath];
|
|
5609
|
+
}
|
|
5610
|
+
const sortedPatterns = Object.keys(ENDPOINT_CONFIGS).sort((a, b) => b.length - a.length);
|
|
5611
|
+
for (const pattern of sortedPatterns) {
|
|
5612
|
+
if (matchPath(pattern, path42)) {
|
|
5613
|
+
return ENDPOINT_CONFIGS[pattern];
|
|
5614
|
+
}
|
|
5615
|
+
}
|
|
5616
|
+
return { tier: "STANDARD" };
|
|
5617
|
+
}
|
|
5618
|
+
var ENDPOINT_CONFIGS, EXCLUDED_PATHS;
|
|
5619
|
+
var init_chunk_IEWJ5MZH = __esm({
|
|
5620
|
+
"../../packages/core/dist/chunk-IEWJ5MZH.js"() {
|
|
5621
|
+
"use strict";
|
|
5622
|
+
init_chunk_F3JHI4Y4();
|
|
5623
|
+
ENDPOINT_CONFIGS = {
|
|
5624
|
+
// OAuth callbacks - strict, fail-closed (prevent brute force)
|
|
5625
|
+
"/oauth/callback/*": { tier: "STRICT", failClosed: true },
|
|
5626
|
+
// Auth endpoints - strict, fail-closed
|
|
5627
|
+
"/auth/phone": { tier: "STRICT", failClosed: true },
|
|
5628
|
+
"/auth/mcp/exchange": { tier: "MODERATE", failClosed: true },
|
|
5629
|
+
// Native magic-code login (pre-auth) — STRICT per-IP bounds a single host; WorkOS owns
|
|
5630
|
+
// the per-email lockout / TTL / single-use guarantees.
|
|
5631
|
+
"/api/auth/magic-code/*": { tier: "STRICT", failClosed: true },
|
|
5632
|
+
// Native password login (pre-auth) — same posture as magic-code above. STRICT per-IP bounds a
|
|
5633
|
+
// single host but FAILS OPEN on a binding error (like every IP limit); the `failClosed` flag
|
|
5634
|
+
// here is inert on a pre-auth route (it's only enforced on the per-user branch, which needs an
|
|
5635
|
+
// authenticated caller). The fail-CLOSED brute-force guarantee for the static credential comes
|
|
5636
|
+
// from the route's per-email caps (burst + cumulative hourly), not this flag.
|
|
5637
|
+
"/api/auth/password/*": { tier: "STRICT", failClosed: true },
|
|
5638
|
+
// Every `/api/*` group whose paths carry ids pins `userLimitKey` (gh #4756), held there by
|
|
5639
|
+
// `security/endpoint-invariants.test.ts`.
|
|
5640
|
+
// Billing - moderate, fail-closed with user limits
|
|
5641
|
+
"/api/billing/*": {
|
|
5642
|
+
tier: "MODERATE",
|
|
5643
|
+
userLimit: 100,
|
|
5644
|
+
failClosed: true,
|
|
5645
|
+
skipAuthenticatedUsers: true,
|
|
5646
|
+
userLimitKey: "/api/billing/*"
|
|
5647
|
+
},
|
|
5648
|
+
"/webhooks/stripe": { tier: "MODERATE", failClosed: true },
|
|
5649
|
+
"/webhooks/workos": { tier: "MODERATE", failClosed: true },
|
|
5650
|
+
// API endpoints - standard with user limits
|
|
5651
|
+
// 500 because the web's settings live updates refetch per hub in every open tab, whatever page
|
|
5652
|
+
// it shows (`settingsLiveUpdateStore.ts`): one batch of changes costs about 10 + 4 per agent
|
|
5653
|
+
// whose tools were opened + 2 per loaded resource, so a CLI push and publish (both hubs) with
|
|
5654
|
+
// two tabs open passes 150 in a minute. A multi-file resource upload adds 2–5 per file.
|
|
5655
|
+
"/api/setup/*": {
|
|
5656
|
+
tier: "STANDARD",
|
|
5657
|
+
userLimit: 500,
|
|
5658
|
+
skipAuthenticatedUsers: true,
|
|
5659
|
+
userLimitKey: "/api/setup/*"
|
|
5660
|
+
},
|
|
5661
|
+
// 500 because an eval run's drill-down (`SessionResults.tsx`) fetches its list and then every
|
|
5662
|
+
// LLM turn's record at once: up to 201 requests (`OBSERVABILITY_LIST_CAP`, 200). Sending keeps
|
|
5663
|
+
// the 150 it had when each path held its own budget, on the exact key below.
|
|
5664
|
+
"/api/conversations/*": {
|
|
5665
|
+
tier: "STANDARD",
|
|
5666
|
+
userLimit: 500,
|
|
5667
|
+
skipAuthenticatedUsers: true,
|
|
5668
|
+
userLimitKey: "/api/conversations/*"
|
|
5669
|
+
},
|
|
5670
|
+
"/api/conversations/send-message": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
5671
|
+
"/api/users/*": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
5672
|
+
"/api/analytics/*": {
|
|
5673
|
+
tier: "STANDARD",
|
|
5674
|
+
userLimit: 150,
|
|
5675
|
+
skipAuthenticatedUsers: true,
|
|
5676
|
+
userLimitKey: "/api/analytics/*"
|
|
5677
|
+
},
|
|
5678
|
+
// 500 because `wayai eval-results --eval` reads completed sessions one by one, newest first,
|
|
5679
|
+
// until one holds the eval (up to the 1000 the list returns). Tenant SQL keeps the 150 it had
|
|
5680
|
+
// when each path held its own budget: each query may run 30 s on shared-tenant ClickHouse
|
|
5681
|
+
// after its caller has gone (`sql-validator.ts`, stage 10b).
|
|
5682
|
+
"/api/evals/*": {
|
|
5683
|
+
tier: "STANDARD",
|
|
5684
|
+
userLimit: 500,
|
|
5685
|
+
skipAuthenticatedUsers: true,
|
|
5686
|
+
userLimitKey: "/api/evals/*"
|
|
5687
|
+
},
|
|
5688
|
+
"/api/evals/sql": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
5689
|
+
// An eval run's calls (gh #4418), kept off the end user's call budgets below. Create keeps the
|
|
5690
|
+
// 150 it had: it opens a provider session on the org's own key, billed per second. The routes
|
|
5691
|
+
// on one call take a budget of their own so no eval read can spend it: a refused hang-up
|
|
5692
|
+
// leaves the call running, and billing. 500 because a call suite polls each run's record
|
|
5693
|
+
// every second, about 64 requests a minute per suite.
|
|
5694
|
+
"/api/evals/calls": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
5695
|
+
"/api/evals/calls/*": {
|
|
5696
|
+
tier: "STANDARD",
|
|
5697
|
+
userLimit: 500,
|
|
5698
|
+
skipAuthenticatedUsers: true,
|
|
5699
|
+
userLimitKey: "/api/evals/calls/*"
|
|
5700
|
+
},
|
|
5701
|
+
"/api/files/*": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
5702
|
+
// The org contact book (wayai#4931 PR 2b). A CSV import sends one request per
|
|
5703
|
+
// `CONTACT_IMPORT_BATCH_MAX` rows, so 150 a minute imports 75,000 rows a minute.
|
|
5704
|
+
"/api/contact-book/*": {
|
|
5705
|
+
tier: "STANDARD",
|
|
5706
|
+
userLimit: 150,
|
|
5707
|
+
skipAuthenticatedUsers: true,
|
|
5708
|
+
userLimitKey: "/api/contact-book/*"
|
|
5709
|
+
},
|
|
5710
|
+
// An automation email's one-click unsubscribe (wayai#4931 PR 2d) — public: its token is its one
|
|
5711
|
+
// credential, so this per-IP limit is its one bound, and runs before the route reads anything.
|
|
5712
|
+
// MODERATE (30/min per IP), as the community email's unsubscribe, for the same reason: mail
|
|
5713
|
+
// clients' one-click unsubscribe POSTs it from their own servers, which many recipients' clicks
|
|
5714
|
+
// share. An exact key, so it wins over the wildcard above.
|
|
5715
|
+
[CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH]: { tier: "MODERATE" },
|
|
5716
|
+
"/api/automations/*": {
|
|
5717
|
+
tier: "STANDARD",
|
|
5718
|
+
userLimit: 150,
|
|
5719
|
+
skipAuthenticatedUsers: true,
|
|
5720
|
+
userLimitKey: "/api/automations/*"
|
|
5721
|
+
},
|
|
5722
|
+
// A hub's own contact lists (wayai#4931 PR 2c): config edits, at the automations' rate.
|
|
5723
|
+
"/api/hub-contact-lists/*": {
|
|
5724
|
+
tier: "STANDARD",
|
|
5725
|
+
userLimit: 150,
|
|
5726
|
+
skipAuthenticatedUsers: true,
|
|
5727
|
+
userLimitKey: "/api/hub-contact-lists/*"
|
|
5728
|
+
},
|
|
5729
|
+
"/api/message-templates/*": {
|
|
5730
|
+
tier: "STANDARD",
|
|
5731
|
+
userLimit: 150,
|
|
5732
|
+
skipAuthenticatedUsers: true,
|
|
5733
|
+
userLimitKey: "/api/message-templates/*"
|
|
5734
|
+
},
|
|
5735
|
+
"/api/live-updates/*": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
5736
|
+
"/api/hub-users/*": {
|
|
5737
|
+
tier: "STANDARD",
|
|
5738
|
+
userLimit: 150,
|
|
5739
|
+
skipAuthenticatedUsers: true,
|
|
5740
|
+
userLimitKey: "/api/hub-users/*"
|
|
5741
|
+
},
|
|
5742
|
+
"/api/conversation-state/*": {
|
|
5743
|
+
tier: "STANDARD",
|
|
5744
|
+
userLimit: 150,
|
|
5745
|
+
skipAuthenticatedUsers: true,
|
|
5746
|
+
userLimitKey: "/api/conversation-state/*"
|
|
5747
|
+
},
|
|
5748
|
+
// The authenticated byte route (`routes/files/r2.ts`), mounted after its `authMiddleware` so
|
|
5749
|
+
// the caller is known: each request reads the HubDO its key names, and a hub id never created
|
|
5750
|
+
// still wakes a Durable Object (gh #4747; since gh #4775 T5 one that writes nothing). The key is pinned
|
|
5751
|
+
// because the paths are file keys, which would otherwise each get a fresh budget (and one
|
|
5752
|
+
// over 256 characters would be refused outright, `checkUserLimitNative`).
|
|
5753
|
+
//
|
|
5754
|
+
// 500 a minute because the web chat fetches every attachment of every loaded message at
|
|
5755
|
+
// once, with no list virtualization: up to 50 per 50-message page, 200 per ended
|
|
5756
|
+
// conversation or history entry at one file per message, and 4 per message at most in its
|
|
5757
|
+
// image grid. Repeats come from the browser's HTTP cache (`private, max-age=3600`); mobile
|
|
5758
|
+
// renders through a disk cache and plays audio from `/files/signed`. The budget is shared by
|
|
5759
|
+
// a caller's tabs and devices. The other two byte routes, `/files/signed/*` and
|
|
5760
|
+
// `/files/public/*`, mount no limiter (the reason is at their entries in
|
|
5761
|
+
// `security/endpoint-invariants.test.ts`'s route registry).
|
|
5762
|
+
"/files/auth/*": {
|
|
5763
|
+
tier: "STANDARD",
|
|
5764
|
+
userLimit: 500,
|
|
5765
|
+
skipAuthenticatedUsers: true,
|
|
5766
|
+
userLimitKey: "/files/auth/*"
|
|
5767
|
+
},
|
|
5768
|
+
// Channel webhooks - high throughput (external systems, never authenticated)
|
|
5769
|
+
"/webhooks/whatsapp/*": { tier: "HIGH" },
|
|
5770
|
+
"/webhooks/instagram/*": { tier: "HIGH" },
|
|
5771
|
+
// An automation's webhook deliveries (wayai#4931 D13) — no bearer token: the automation's
|
|
5772
|
+
// secret is the credential, checked against one HubDO read. This per-IP limit runs first, before
|
|
5773
|
+
// anything is read. HIGH, as the channel webhooks: senders are servers, and an integration
|
|
5774
|
+
// platform's egress addresses carry many tenants' deliveries. The per-automation budget is
|
|
5775
|
+
// `AUTOMATION_WEBHOOK_PER_AUTOMATION_LIMIT`, counted after the delivery is verified.
|
|
5776
|
+
[`${AUTOMATION_WEBHOOK_MOUNT}/*`]: { tier: "HIGH" },
|
|
5777
|
+
// Reports (CLI → platform_report triage queue). One per-user budget covers intake
|
|
5778
|
+
// (POST), the content edit (PATCH /:id), the reporter read-back (GET, GET /:id), and the
|
|
5779
|
+
// verification loop (POST /:id/accept, /:id/contest). The limiter is path-based, so a
|
|
5780
|
+
// method-level split (loose reads, strict writes) isn't expressible — GET /:id and PATCH
|
|
5781
|
+
// /:id share a path. So the budget is sized for the read-heavy poll loop (a reporter agent
|
|
5782
|
+
// polling `report get`/`list` while it waits for the fix), at 60/hour. Intake-flooding stays
|
|
5783
|
+
// bounded regardless: identical reports collapse on `fingerprint` (ON CONFLICT DO NOTHING),
|
|
5784
|
+
// so a runaway create loop merges rather than floods. Still STRICT tier + failClosed so a
|
|
5785
|
+
// genuinely runaway distinct-report loop surfaces as 429. The `/*` is load-bearing — an
|
|
5786
|
+
// exact `/api/reports` key would leave the `/:id` paths unmatched (→ unthrottled).
|
|
5787
|
+
// `userLimitKey` is equally load-bearing: without it, middleware falls back to each request's
|
|
5788
|
+
// path and report IDs/actions receive independent counters instead of this shared budget.
|
|
5789
|
+
"/api/reports/*": {
|
|
5790
|
+
tier: "STRICT",
|
|
5791
|
+
userLimit: 60,
|
|
5792
|
+
userWindow: 3600,
|
|
5793
|
+
failClosed: true,
|
|
5794
|
+
skipAuthenticatedUsers: true,
|
|
5795
|
+
userLimitKey: "/api/reports/*"
|
|
5796
|
+
},
|
|
5797
|
+
// Admin report mutations (transition/group/ungroup/edit) — platform-admin
|
|
5798
|
+
// gated at the route, rate-limited here as defense-in-depth. Generous STANDARD
|
|
5799
|
+
// cap so legitimate triage bursts aren't throttled (grouping is one request
|
|
5800
|
+
// even for many members); deliberately NOT failClosed so a Redis blip never
|
|
5801
|
+
// blocks an admin's triage. The bare GET reads share this budget harmlessly.
|
|
5802
|
+
"/api/admin/reports/*": { tier: "STANDARD", userLimit: 150, userLimitKey: "/api/admin/reports/*" },
|
|
5803
|
+
// GitHub webhook (issues.closed → platform_report addressed). MODERATE +
|
|
5804
|
+
// fail-closed; GitHub retries on transient failures so a brief Redis blip
|
|
5805
|
+
// doesn't lose state.
|
|
5806
|
+
"/webhooks/github": { tier: "MODERATE", failClosed: true },
|
|
5807
|
+
// Account deletion — strict on the destructive POST + public undo. The
|
|
5808
|
+
// /api/users/* default tier above ALSO matches these, but `getEndpointConfig`
|
|
5809
|
+
// returns the longest-prefix match, so the more specific entries here win.
|
|
5810
|
+
// `skipAuthenticatedUsers` deliberately omitted: every caller is authenticated,
|
|
5811
|
+
// so including it would make `userLimit` a no-op — the per-user cap IS the gate.
|
|
5812
|
+
"/api/users/me/deletion": { tier: "STRICT", userLimit: 3, userWindow: 3600, failClosed: true },
|
|
5813
|
+
"/api/users/me/deletion/preflight": { tier: "STANDARD", userLimit: 30, failClosed: true },
|
|
5814
|
+
"/api/users/deletion/undo": { tier: "STRICT", failClosed: true },
|
|
5815
|
+
// Production-base destruction.
|
|
5816
|
+
//
|
|
5817
|
+
// Keyed off the CONTRACT constants, not literals: `BASE_DESTRUCTION_CONFIRM_PATH`'s own
|
|
5818
|
+
// docstring promises that the mount, the `AUTH_BYPASS_PATHS` entry, the rate-limit key and
|
|
5819
|
+
// the URL the email embeds move together, and a literal here is exactly how that promise
|
|
5820
|
+
// goes quietly false — leaving an unauthenticated HMAC endpoint on the default tier.
|
|
5821
|
+
//
|
|
5822
|
+
// The emailed GET is PUBLIC, so it gets the IP-tier bound the pre-auth routes get.
|
|
5823
|
+
// `failClosed` is deliberately NOT set: it is only consulted on the per-USER branch, which
|
|
5824
|
+
// needs an authenticated caller, so on a pre-auth route it is inert — the same thing
|
|
5825
|
+
// `/api/auth/magic-code/send` above says about itself. The IP limiter fails OPEN on a
|
|
5826
|
+
// limiter error by design (`packages/runtime/src/rate-limiter.ts`), and claiming otherwise
|
|
5827
|
+
// would tell the next auditor a protection exists that does not.
|
|
5828
|
+
[BASE_DESTRUCTION_CONFIRM_PATH]: { tier: "STRICT" },
|
|
5829
|
+
// The console's read, which every production base's settings pane issues on mount. Without
|
|
5830
|
+
// its own entry it inherits the mount-wide budget below, and an admin clicking through
|
|
5831
|
+
// thirty bases in an hour would be 429'd by a limit sized for irreversible writes.
|
|
5832
|
+
[`${BASE_DESTRUCTION_PREFIX}/destructions`]: { tier: "STANDARD", userLimit: 150 },
|
|
5833
|
+
// The mutating routes: initiate, the authenticated confirm, and cancel. Per user rather than
|
|
5834
|
+
// per IP — all three are authenticated and org-admin gated — and hourly, since a single human
|
|
5835
|
+
// touching thirty production-deletion operations in an hour is already far past normal.
|
|
5836
|
+
// Longest-prefix wins, so the two entries above are not widened by this one. The key is
|
|
5837
|
+
// pinned so those thirty are counted across every base the caller touches (gh #4756).
|
|
5838
|
+
[`${BASE_DESTRUCTION_PREFIX}/*`]: {
|
|
5839
|
+
tier: "STANDARD",
|
|
5840
|
+
userLimit: 30,
|
|
5841
|
+
userWindow: 3600,
|
|
5842
|
+
failClosed: true,
|
|
5843
|
+
userLimitKey: `${BASE_DESTRUCTION_PREFIX}/*`
|
|
5844
|
+
},
|
|
5845
|
+
// Email-invite preview — public, fail-closed. MODERATE (30/min IP) leaves
|
|
5846
|
+
// headroom for the signup-page server-fetch + reloads + prefetches; STRICT
|
|
5847
|
+
// would throttle legitimate users opening the link in multiple tabs.
|
|
5848
|
+
"/api/invites/preview": { tier: "MODERATE", failClosed: true },
|
|
5849
|
+
// "Don't email me again" (gh #4661) — public; one POST per click on its confirm page. STRICT
|
|
5850
|
+
// per IP. No `failClosed`: it is consulted only on the per-user branch, which a pre-auth route
|
|
5851
|
+
// never reaches (see `BASE_DESTRUCTION_CONFIRM_PATH` above).
|
|
5852
|
+
"/api/invites/unsubscribe": { tier: "STRICT" },
|
|
5853
|
+
// Admin DO debug reads — the ONE deliberate deviation from the Data Explorer
|
|
5854
|
+
// catch-all below, and the only reason a per-route key still exists here.
|
|
5855
|
+
// Platform-admin is enforced at the route; this is the independent
|
|
5856
|
+
// defense-in-depth signal so a runaway investigation script surfaces as 429.
|
|
5857
|
+
//
|
|
5858
|
+
// STRICT is kept ON PURPOSE even though the catch-all moved off it: per IP, at 5/min vs
|
|
5859
|
+
// STANDARD's 100/min, walking 10k ids takes ~33h instead of ~100min — a detection window
|
|
5860
|
+
// worth keeping on the routes that return RAW DO ROWS.
|
|
5861
|
+
//
|
|
5862
|
+
// The cost, stated rather than elided: STRICT is one bucket shared with
|
|
5863
|
+
// `/oauth/callback/*` and `/api/auth/magic-code/*`, so a debug session from a
|
|
5864
|
+
// NAT'd office IP can 429 a colleague's login for up to a minute. That trade is
|
|
5865
|
+
// accepted for a rare, deliberate investigation surface — and is exactly why the
|
|
5866
|
+
// browse routes below are NOT here.
|
|
5867
|
+
//
|
|
5868
|
+
// The key is pinned, so a sweep of `/debug/do/hub/<id>/tables` across ids spends one
|
|
5869
|
+
// per-admin budget (gh #4756). 120 an hour: an agent's investigation of a dozen
|
|
5870
|
+
// conversations made 37 debug reads in its busiest hour.
|
|
5871
|
+
"/api/admin/data-explorer/debug/*": {
|
|
5872
|
+
tier: "STRICT",
|
|
5873
|
+
userLimit: 120,
|
|
5874
|
+
userWindow: 3600,
|
|
5875
|
+
failClosed: true,
|
|
5876
|
+
userLimitKey: "/api/admin/data-explorer/debug/*"
|
|
5877
|
+
},
|
|
5878
|
+
// Catch-all for the rest of the Data Explorer (`/search`, `/entity/*`, `/do/*`,
|
|
5879
|
+
// `/kv/*`, the DELETE routes). Added as a catch-all rather than another per-route
|
|
5880
|
+
// key because this budget kept lagging behind routes joining the debug boundary:
|
|
5881
|
+
// with no match the path falls to the default, which sets no `userLimit`, so the
|
|
5882
|
+
// per-user layer never runs at all. `getEndpointConfig` takes the longest match,
|
|
5883
|
+
// so `debug/*` above still wins.
|
|
5884
|
+
//
|
|
5885
|
+
// `/entity/*` deliberately has NO key of its own: it needs precisely this config,
|
|
5886
|
+
// and a duplicate entry is how the two drift apart on the next edit.
|
|
5887
|
+
//
|
|
5888
|
+
// `tier` is deliberately STANDARD, not STRICT. `tier` is the per-IP knob and
|
|
5889
|
+
// STRICT is 5/min keyed on IP ALONE — one bucket shared across every STRICT
|
|
5890
|
+
// endpoint. On these browse routes that breaks the console (open Browse KV and
|
|
5891
|
+
// inspect four values = 6 requests; expand six org rows in the search results =
|
|
5892
|
+
// 6 `/entity/org/:id` calls) and would drain the same IP's budget for
|
|
5893
|
+
// `/oauth/callback/*` and `/api/auth/magic-code/*`, which on a NAT'd office IP
|
|
5894
|
+
// means all admins. The per-user hourly cap is the leg that matters here.
|
|
5895
|
+
//
|
|
5896
|
+
// The canonical key is deliberate: request paths contain DO ids and arbitrary
|
|
5897
|
+
// malformed requests can contain credential material. Keying on the request path
|
|
5898
|
+
// both stores that material in Redis/logs and gives every target a fresh counter.
|
|
5899
|
+
// One stable identity makes every browse request consume the same per-admin
|
|
5900
|
+
// budget without storing request targets.
|
|
5901
|
+
//
|
|
5902
|
+
// 600/hour preserves the original pagination requirement (about 30k rows at
|
|
5903
|
+
// 50/page) while now genuinely bounding id/key enumeration across the surface.
|
|
5904
|
+
"/api/admin/data-explorer/*": {
|
|
5905
|
+
tier: "STANDARD",
|
|
5906
|
+
userLimit: 600,
|
|
5907
|
+
userWindow: 3600,
|
|
5908
|
+
failClosed: true,
|
|
5909
|
+
userLimitKey: "/api/admin/data-explorer/*"
|
|
5910
|
+
},
|
|
5911
|
+
// Admin harness incident-response (harness-agents PR 2.9b) — mass-destroy reaps
|
|
5912
|
+
// EVERY live harness session/token/slot in scope, so it gets a strict budget
|
|
5913
|
+
// (STRICT + a low per-user hourly cap + fail-closed). Platform-admin is enforced
|
|
5914
|
+
// at the route; this is the independent defense-in-depth signal so a runaway
|
|
5915
|
+
// reap loop surfaces as 429. The break-glass disable/enable rides through
|
|
5916
|
+
// `/api/admin/config` (its own STANDARD budget), not this key.
|
|
5917
|
+
"/api/admin/harness/*": {
|
|
5918
|
+
tier: "STRICT",
|
|
5919
|
+
userLimit: 10,
|
|
5920
|
+
userWindow: 3600,
|
|
5921
|
+
failClosed: true,
|
|
5922
|
+
userLimitKey: "/api/admin/harness/*"
|
|
5923
|
+
},
|
|
5924
|
+
// Platform-wide team-orphan repair — a single call can WRITE across every hub of
|
|
5925
|
+
// every org (HubDO purge + UserDO mirror fan-out), so it gets the same strict,
|
|
5926
|
+
// fail-closed budget as the other cross-platform admin write. Exact path (not a
|
|
5927
|
+
// prefix): the sibling `/api/admin/hubs/:hubId/harness` flag flips stay on the
|
|
5928
|
+
// default admin budget. Platform-admin is enforced at the route; this is the
|
|
5929
|
+
// defense-in-depth signal that a repair loop surfaces as 429.
|
|
5930
|
+
"/api/admin/hubs/repair-team-orphans": { tier: "STRICT", userLimit: 10, userWindow: 3600, failClosed: true },
|
|
5931
|
+
// Branching-orphan repair (#4577) — deletes named production rows, and its report mode
|
|
5932
|
+
// fans out over every production hub of every org, so it takes the same strict,
|
|
5933
|
+
// fail-closed budget as the team-orphan repair. Exact path, for the same reason.
|
|
5934
|
+
"/api/admin/hubs/repair-branching-orphans": { tier: "STRICT", userLimit: 10, userWindow: 3600, failClosed: true },
|
|
5935
|
+
// CI/GitOps endpoints - standard with user limits. 500 because `wayai push` makes four requests
|
|
5936
|
+
// (pull, diff, push, pull) and a GitOps job may push every hub at once with one token; when
|
|
5937
|
+
// each path held its own 100, that ran to 100 hubs a minute.
|
|
5938
|
+
"/api/ci/*": { tier: "STANDARD", userLimit: 500, skipAuthenticatedUsers: true, userLimitKey: "/api/ci/*" },
|
|
5939
|
+
// Attested `/v1` proxy onto the Rekor Worker. `userLimitKey` is load-bearing here: the
|
|
5940
|
+
// per-user budget defaults to the full request path, and this surface's paths carry base,
|
|
5941
|
+
// record, and file ids — so without a pinned key every distinct resource would get its own
|
|
5942
|
+
// fresh budget and the per-user limit would bound nothing. One shared budget across the
|
|
5943
|
+
// whole data surface is the intent.
|
|
5944
|
+
[`${DATA_PROXY_PREFIX}/*`]: {
|
|
5945
|
+
tier: "STANDARD",
|
|
5946
|
+
userLimit: 150,
|
|
5947
|
+
skipAuthenticatedUsers: true,
|
|
5948
|
+
userLimitKey: `${DATA_PROXY_PREFIX}/*`
|
|
5949
|
+
},
|
|
5950
|
+
// Voice calls — the end user's call routes (`routes/api/calls`). Every caller is signed
|
|
5951
|
+
// in, so the per-IP tier is skipped for them: Brazilian mobile carriers put many callers
|
|
5952
|
+
// behind one IP, and a shared bucket would refuse one caller for another's traffic.
|
|
5953
|
+
//
|
|
5954
|
+
// Create is the only route that spends: it opens a provider session on the org's own
|
|
5955
|
+
// key, billed per second. So it takes its own exact key and an hourly per-user cap, which
|
|
5956
|
+
// lives on Redis and is fail-closed like the other hourly caps. The exact key wins over
|
|
5957
|
+
// the wildcard below, which also matches `/api/calls` (see `getEndpointConfig`).
|
|
5958
|
+
"/api/calls": {
|
|
5959
|
+
tier: "STANDARD",
|
|
5960
|
+
userLimit: 30,
|
|
5961
|
+
userWindow: 3600,
|
|
5962
|
+
failClosed: true,
|
|
5963
|
+
skipAuthenticatedUsers: true,
|
|
5964
|
+
userLimitKey: "/api/calls"
|
|
5965
|
+
},
|
|
5966
|
+
// The routes on one call: `ready`, hang-up, the delegation report and the status read; and
|
|
5967
|
+
// the opening read (`/api/calls/openings/:language`), one per call, before it exists.
|
|
5968
|
+
// NEVER fail-closed: a refused hang-up leaves the call running, and billing, until a
|
|
5969
|
+
// limit ends it. The 60 s budget runs on the native binding, which fails open. The key is
|
|
5970
|
+
// pinned because the paths carry the call id, which would otherwise give every call a
|
|
5971
|
+
// fresh budget.
|
|
5972
|
+
"/api/calls/*": {
|
|
5973
|
+
tier: "STANDARD",
|
|
5974
|
+
userLimit: 150,
|
|
5975
|
+
skipAuthenticatedUsers: true,
|
|
5976
|
+
userLimitKey: "/api/calls/*"
|
|
5977
|
+
},
|
|
5978
|
+
// Contacts between accounts (gh #4661, `routes/api/contacts`). Every caller is signed in, so
|
|
5979
|
+
// the per-IP tier is skipped. The keys are pinned because the paths carry user and invite
|
|
5980
|
+
// ids, which would otherwise give each id a fresh budget. Blocks take their own, lower
|
|
5981
|
+
// budget (#4661 Limits: blocks are rate-limited). The invites-per-day cap and every list
|
|
5982
|
+
// cap are counted in the caller's UserDO, in the same write they bound, not here.
|
|
5983
|
+
"/api/contacts/*": {
|
|
5984
|
+
tier: "STANDARD",
|
|
5985
|
+
userLimit: 60,
|
|
5986
|
+
skipAuthenticatedUsers: true,
|
|
5987
|
+
userLimitKey: "/api/contacts/*"
|
|
5988
|
+
},
|
|
5989
|
+
"/api/contacts/blocks/*": {
|
|
5990
|
+
tier: "STANDARD",
|
|
5991
|
+
userLimit: 30,
|
|
5992
|
+
skipAuthenticatedUsers: true,
|
|
5993
|
+
userLimitKey: "/api/contacts/blocks/*"
|
|
5994
|
+
},
|
|
5995
|
+
// Person-to-person threads (gh #4661, `routes/api/threads`): the per-user send limit, 60 a
|
|
5996
|
+
// minute on the native binding. Every thread route shares the one budget, reads included:
|
|
5997
|
+
// the paths carry thread and message ids, so the key is pinned, and a pattern here cannot
|
|
5998
|
+
// tell a send from a read of the same path. Like every 60 s budget it fails open on a
|
|
5999
|
+
// binding error, so it bounds sends in the steady state only.
|
|
6000
|
+
"/api/threads/*": {
|
|
6001
|
+
tier: "STANDARD",
|
|
6002
|
+
userLimit: 60,
|
|
6003
|
+
skipAuthenticatedUsers: true,
|
|
6004
|
+
userLimitKey: "/api/threads/*"
|
|
6005
|
+
},
|
|
6006
|
+
// An org admin's erasure of one person's thread messages (gh #4661 owner decision 7): any user
|
|
6007
|
+
// can own a free org, so each request arms that org's alarm, and this caps how fast one person
|
|
6008
|
+
// can ask. The org's own daily cap (`PEOPLE_ORG_ERASURES_PER_DAY`, counted in its OrgDO in the
|
|
6009
|
+
// write it bounds) is the fail-closed bound; this budget, on the native binding, fails open.
|
|
6010
|
+
"/api/threads/org-erasures": {
|
|
6011
|
+
tier: "STANDARD",
|
|
6012
|
+
userLimit: 10,
|
|
6013
|
+
skipAuthenticatedUsers: true,
|
|
6014
|
+
userLimitKey: "/api/threads/org-erasures"
|
|
6015
|
+
},
|
|
6016
|
+
// A person's report of a message, thread or person (`people-reports.ts`): each one written
|
|
6017
|
+
// arms the PlatformDO's alarm and emails the platform admins, so it takes its own, lower
|
|
6018
|
+
// budget. The reporter's daily cap (`PEOPLE_REPORTS_PER_DAY`, counted in the report store in
|
|
6019
|
+
// the write it bounds) is the fail-closed bound; this budget, on the native binding, fails open.
|
|
6020
|
+
"/api/threads/reports": {
|
|
6021
|
+
tier: "STANDARD",
|
|
6022
|
+
userLimit: 10,
|
|
6023
|
+
skipAuthenticatedUsers: true,
|
|
6024
|
+
userLimitKey: "/api/threads/reports"
|
|
6025
|
+
},
|
|
6026
|
+
// Communities (gh #4872, `routes/api/communities`): every signed-in route under the mount (the
|
|
6027
|
+
// preview below excepted) shares one per-user budget, which is also each member's send limit and
|
|
6028
|
+
// bounds the reads that scan a community's rows (a title search, the stats); a community's own
|
|
6029
|
+
// limits (its rate, the daily caps, slow mode) are counted in its CommunityDO, in the write they
|
|
6030
|
+
// bound. Every caller is signed in, so the per-IP tier is skipped; the key is pinned because the
|
|
6031
|
+
// paths carry community, room and message ids.
|
|
6032
|
+
"/api/communities/*": {
|
|
6033
|
+
tier: "STANDARD",
|
|
6034
|
+
userLimit: 60,
|
|
6035
|
+
skipAuthenticatedUsers: true,
|
|
6036
|
+
userLimitKey: "/api/communities/*"
|
|
6037
|
+
},
|
|
6038
|
+
// The join page's preview of a link — public, fetched from the visitor's browser, so this
|
|
6039
|
+
// per-IP limit is its one bound and runs before the route reads anything (#4872
|
|
6040
|
+
// owner-delegated decision 9). MODERATE (30/min per IP) leaves room for reloads and several
|
|
6041
|
+
// tabs. An exact key, so it wins over the wildcard above.
|
|
6042
|
+
"/api/communities/join-preview": { tier: "MODERATE" },
|
|
6043
|
+
// An announcement email's unsubscribe (#4872 PR 8a) — public: its token is its one credential, so
|
|
6044
|
+
// this per-IP limit is its one bound, and runs before the route reads anything. MODERATE (30/min
|
|
6045
|
+
// per IP), not STRICT: mail clients' one-click unsubscribe POSTs it from their own servers, which
|
|
6046
|
+
// many members' clicks share, and STRICT's 5/min bucket is shared with sign-in. An exact key, so
|
|
6047
|
+
// it wins over the wildcard above.
|
|
6048
|
+
[COMMUNITY_EMAIL_UNSUBSCRIBE_PATH]: { tier: "MODERATE" },
|
|
6049
|
+
// The WayAI Guide's help entry (wayai#4948, `routes/api/guide.ts`): one POST per click by a viewer
|
|
6050
|
+
// the snapshot does not show enrolled, which reads the person's row on the Guide hub and writes only
|
|
6051
|
+
// what is missing (an enrolled viewer's click opens the hub without a request). Every caller is
|
|
6052
|
+
// signed in, so the per-IP tier is skipped; 10 a minute per person on the native binding (fails
|
|
6053
|
+
// open, like every 60 s budget) bounds what a looping client costs the one Guide HubDO.
|
|
6054
|
+
"/api/guide/access": {
|
|
6055
|
+
tier: "STANDARD",
|
|
6056
|
+
userLimit: 10,
|
|
6057
|
+
skipAuthenticatedUsers: true,
|
|
6058
|
+
userLimitKey: "/api/guide/access"
|
|
6059
|
+
},
|
|
6060
|
+
// App channel - high throughput with user limits
|
|
6061
|
+
"/channels/app/*": { tier: "HIGH", userLimit: 500, skipAuthenticatedUsers: true },
|
|
6062
|
+
// API channel (api-channel-connector) - machine-to-machine, high throughput
|
|
6063
|
+
"/channels/api/*": { tier: "HIGH", userLimit: 500, skipAuthenticatedUsers: true },
|
|
6064
|
+
// System/scheduled - high throughput (internal)
|
|
6065
|
+
"/channels/system/*": { tier: "HIGH", skipAuthenticatedUsers: true },
|
|
6066
|
+
"/scheduled/*": { tier: "HIGH", skipAuthenticatedUsers: true },
|
|
6067
|
+
// WebSocket - standard with user limit to prevent connection spam
|
|
6068
|
+
"/live-updates/ws": { tier: "STANDARD", userLimit: 100, failClosed: true },
|
|
6069
|
+
// A community's live socket (gh #4872 PR 5, `GET /communities/:communityId/live`): outside
|
|
6070
|
+
// `/api/*`, with no bearer token, so its per-IP tier is its one bound before the CommunityDO
|
|
6071
|
+
// checks the ticket. STANDARD, as `/live-updates/ws`: a client opens one per community it shows
|
|
6072
|
+
// and reconnects within the hour.
|
|
6073
|
+
"/communities/*": { tier: "STANDARD" },
|
|
6074
|
+
// WS ticket mint - per-user 60/min accommodates aggressive reconnect patterns
|
|
6075
|
+
// without opening abuse vectors; fail-closed so Redis outages don't let
|
|
6076
|
+
// runaway clients mint unbounded tickets.
|
|
6077
|
+
"/api/auth/ws-ticket": { tier: "MODERATE", userLimit: 60, failClosed: true, skipAuthenticatedUsers: true },
|
|
6078
|
+
"/api/auth/session-check": { tier: "STANDARD", userLimit: 120, skipAuthenticatedUsers: true },
|
|
6079
|
+
"/api/auth/logout": { tier: "MODERATE", userLimit: 10, failClosed: true, skipAuthenticatedUsers: true }
|
|
6080
|
+
};
|
|
6081
|
+
EXCLUDED_PATHS = /* @__PURE__ */ new Set(["/health"]);
|
|
6082
|
+
}
|
|
6083
|
+
});
|
|
6084
|
+
|
|
6085
|
+
// ../../packages/core/dist/chunk-CGOLULK2.js
|
|
6086
|
+
function fileSnapshotChanged(run) {
|
|
6087
|
+
return run.written + run.removed > 0;
|
|
6088
|
+
}
|
|
6089
|
+
async function runFileSnapshot(opts) {
|
|
6090
|
+
const run = { pages: 0, written: 0, removed: 0, unchanged: 0 };
|
|
6091
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6092
|
+
let cursor;
|
|
6093
|
+
while (run.pages < FILE_SNAPSHOT_MAX_PAGES) {
|
|
6094
|
+
const page = await sendWithRetry(opts, cursor);
|
|
6095
|
+
run.pages++;
|
|
6096
|
+
run.written += page.written;
|
|
6097
|
+
run.removed += page.removed;
|
|
6098
|
+
run.unchanged += page.unchanged;
|
|
6099
|
+
opts.onPage?.(page, run.pages);
|
|
6100
|
+
if (page.next_cursor === null) return run;
|
|
6101
|
+
if (seen.has(page.next_cursor)) {
|
|
6102
|
+
throw new Error(`The preview file snapshot repeated its cursor after page ${run.pages}, so it would never end.`);
|
|
6103
|
+
}
|
|
6104
|
+
seen.add(page.next_cursor);
|
|
6105
|
+
cursor = page.next_cursor;
|
|
6106
|
+
}
|
|
6107
|
+
throw new Error(`The preview file snapshot did not end after ${FILE_SNAPSHOT_MAX_PAGES} pages.`);
|
|
6108
|
+
}
|
|
6109
|
+
async function sendWithRetry(opts, cursor) {
|
|
6110
|
+
for (let attempt = 1; ; attempt++) {
|
|
6111
|
+
let body;
|
|
6112
|
+
try {
|
|
6113
|
+
body = await opts.sendPage(cursor);
|
|
6114
|
+
} catch (error) {
|
|
6115
|
+
if (attempt < FILE_SNAPSHOT_CONFLICT_ATTEMPTS && opts.isConflict(error)) continue;
|
|
6116
|
+
throw error;
|
|
6117
|
+
}
|
|
6118
|
+
const parsed = rekorFileSnapshotAnswerSchema.safeParse(body);
|
|
6119
|
+
if (!parsed.success) {
|
|
6120
|
+
throw new Error("The data product answered a preview file snapshot page WayAI cannot read: nothing more was sent.");
|
|
6121
|
+
}
|
|
6122
|
+
return parsed.data.data;
|
|
6123
|
+
}
|
|
6124
|
+
}
|
|
6125
|
+
var rekorFileSnapshotAnswerSchema, FILE_SNAPSHOT_MAX_PAGES, FILE_SNAPSHOT_CONFLICT_ATTEMPTS, FILE_SNAPSHOT_PAGE_SPACING_MS, FILE_SNAPSHOT_CONFLICT_STATUS;
|
|
6126
|
+
var init_chunk_CGOLULK2 = __esm({
|
|
6127
|
+
"../../packages/core/dist/chunk-CGOLULK2.js"() {
|
|
6128
|
+
"use strict";
|
|
6129
|
+
init_chunk_IEWJ5MZH();
|
|
6130
|
+
init_chunk_F3JHI4Y4();
|
|
6131
|
+
init_zod();
|
|
6132
|
+
rekorFileSnapshotAnswerSchema = external_exports.object({
|
|
6133
|
+
data: external_exports.object({
|
|
6134
|
+
next_cursor: external_exports.string().nullable(),
|
|
6135
|
+
/** Files the page created or replaced on the preview. */
|
|
6136
|
+
written: external_exports.number().int(),
|
|
6137
|
+
/** The preview's own files, ones production does not hold, the page removed. */
|
|
6138
|
+
removed: external_exports.number().int(),
|
|
6139
|
+
/** Files the page found already equal to production's. */
|
|
6140
|
+
unchanged: external_exports.number().int()
|
|
6141
|
+
})
|
|
6142
|
+
});
|
|
6143
|
+
FILE_SNAPSHOT_MAX_PAGES = 1e4;
|
|
6144
|
+
FILE_SNAPSHOT_CONFLICT_ATTEMPTS = 3;
|
|
6145
|
+
FILE_SNAPSHOT_PAGE_SPACING_MS = Math.ceil(
|
|
6146
|
+
6e4 / Math.max(1, Math.floor((getEndpointConfig(`${DATA_PROXY_PREFIX}/files/snapshot`)?.userLimit ?? 60) * 4 / 5))
|
|
6147
|
+
);
|
|
6148
|
+
FILE_SNAPSHOT_CONFLICT_STATUS = 409;
|
|
6149
|
+
}
|
|
6150
|
+
});
|
|
6151
|
+
|
|
5556
6152
|
// ../../packages/core/dist/chunk-BEOHBREG.js
|
|
5557
6153
|
var REKOR_ACTOR_TYPE_HEADER, REKOR_ACTOR_ID_HEADER, REKOR_ACTOR_LABEL_HEADER, REKOR_ACTOR_ORG_HEADER, REKOR_ACTOR_ADMIN_HEADER, REKOR_CLIENT_HEADER, REKOR_TOOL_ID_HEADER, REKOR_CLIENTS, rekorClientSchema, MAX_REKOR_TOOL_ID_LENGTH, rekorToolIdSchema, REKOR_V1_PREFIX, DATA_PROXY_ORG_QUERY_PARAM, MAX_PROVISIONING_BODY_BYTES, dataProxyQuery, rekorAttestation, REQUIRED_REKOR_ATTESTATION_HEADERS, REKOR_BOUND_BASE_HEADER, REKOR_BOUND_TOOLSET_HEADER, REKOR_BOUND_TOKEN_HEADER, REKOR_BOUND_CUSTOMER_HEADER, DATA_BASE_GENERATION_HEADER, REKOR_MAX_ACTOR_ID_LENGTH, REKOR_MAX_ACTOR_LABEL_LENGTH;
|
|
5558
6154
|
var init_chunk_BEOHBREG = __esm({
|
|
@@ -5596,10 +6192,10 @@ var init_chunk_BEOHBREG = __esm({
|
|
|
5596
6192
|
}
|
|
5597
6193
|
});
|
|
5598
6194
|
|
|
5599
|
-
// ../../packages/core/dist/chunk-
|
|
6195
|
+
// ../../packages/core/dist/chunk-4MRBBR5L.js
|
|
5600
6196
|
var TOOLSET_SLUG_PATTERN, REKOR_STRUCTURAL_ID_PATTERN, baseEnvironmentSchema, TOOLSET_FILE_OPERATIONS, toolsetFileToolSchema, toolsetSurfaceSchema, toolsetConnectionBindingSchema, rekorToolsetToolsSchema, rekorBoundAttestation, rekorResolvedToolsetSchema, resolvedFileToolSchema, rekorToolsetFileSchema, rekorToolsetFileListSchema, rekorToolsetFileGetSchema, rekorToolsetFilePutSchema, TOOLSET_CUSTOMER_FACING_REFUSAL_MARK, TOOLSET_CUSTOMER_FACING_REFUSAL;
|
|
5601
|
-
var
|
|
5602
|
-
"../../packages/core/dist/chunk-
|
|
6197
|
+
var init_chunk_4MRBBR5L = __esm({
|
|
6198
|
+
"../../packages/core/dist/chunk-4MRBBR5L.js"() {
|
|
5603
6199
|
"use strict";
|
|
5604
6200
|
init_chunk_BEOHBREG();
|
|
5605
6201
|
init_chunk_DHPUPI4Y();
|
|
@@ -5900,25 +6496,6 @@ var init_chunk_IPDOHAZ5 = __esm({
|
|
|
5900
6496
|
}
|
|
5901
6497
|
});
|
|
5902
6498
|
|
|
5903
|
-
// ../../packages/core/dist/chunk-GPM7PE44.js
|
|
5904
|
-
var bootstrapQuery;
|
|
5905
|
-
var init_chunk_GPM7PE44 = __esm({
|
|
5906
|
-
"../../packages/core/dist/chunk-GPM7PE44.js"() {
|
|
5907
|
-
"use strict";
|
|
5908
|
-
init_zod();
|
|
5909
|
-
bootstrapQuery = external_exports.object({
|
|
5910
|
-
active_hub_id: external_exports.string().uuid().optional(),
|
|
5911
|
-
active_conversation_id: external_exports.string().uuid().optional(),
|
|
5912
|
-
active_nav: external_exports.enum(["chat", "task", "support"]).optional(),
|
|
5913
|
-
/**
|
|
5914
|
-
* The people thread the URL names (`/chat/threads/:threadId`, gh #4661): the snapshot
|
|
5915
|
-
* preloads it as `people.active_thread` when the viewer is a member.
|
|
5916
|
-
*/
|
|
5917
|
-
active_thread_id: external_exports.string().uuid().optional()
|
|
5918
|
-
});
|
|
5919
|
-
}
|
|
5920
|
-
});
|
|
5921
|
-
|
|
5922
6499
|
// ../../packages/core/dist/chunk-YUZFHVB2.js
|
|
5923
6500
|
var invitePreviewQuery, inviteRolePreviewResponse, inviteContactPreviewResponse, invitePreviewResponse, inviteUnsubscribeBody;
|
|
5924
6501
|
var init_chunk_YUZFHVB2 = __esm({
|
|
@@ -7544,15 +8121,15 @@ var init_chunk_MH4O3YEX = __esm({
|
|
|
7544
8121
|
}
|
|
7545
8122
|
});
|
|
7546
8123
|
|
|
7547
|
-
// ../../packages/core/dist/chunk-
|
|
8124
|
+
// ../../packages/core/dist/chunk-HX2LNKHX.js
|
|
7548
8125
|
function catalogToolSideEffects(toolName) {
|
|
7549
8126
|
const sideEffects = CATALOG_SIDE_EFFECTS.get(toolName);
|
|
7550
8127
|
if (sideEffects === void 0) return "unknown";
|
|
7551
8128
|
return sideEffects ? "side_effects" : "none";
|
|
7552
8129
|
}
|
|
7553
8130
|
var NATIVE_TOOL_SCHEMAS, WAYAI_CONNECTOR, SANDBOX_AGENT_ROLES, BASE_NATIVE_TOOLS, NATIVE_TOOLS, CATALOG_SIDE_EFFECTS;
|
|
7554
|
-
var
|
|
7555
|
-
"../../packages/core/dist/chunk-
|
|
8131
|
+
var init_chunk_HX2LNKHX = __esm({
|
|
8132
|
+
"../../packages/core/dist/chunk-HX2LNKHX.js"() {
|
|
7556
8133
|
"use strict";
|
|
7557
8134
|
init_chunk_DHPUPI4Y();
|
|
7558
8135
|
NATIVE_TOOL_SCHEMAS = {
|
|
@@ -7900,14 +8477,14 @@ var init_chunk_QCQUZL4L = __esm({
|
|
|
7900
8477
|
"copy_to_sandbox": {
|
|
7901
8478
|
"tool_config": {
|
|
7902
8479
|
"name": "copy_to_sandbox",
|
|
7903
|
-
"description": `Copy one of this conversation's files into the code sandbox, so commands run with bash can read or edit it there. path names the file as read_file does: this conversation's attachments under "conversation/" (e.g. "conversation/report.xlsx"),
|
|
8480
|
+
"description": `Copy one of this conversation's files, or one of your documents, into the code sandbox, so commands run with bash can read or edit it there. path names the file as read_file does: this conversation's attachments under "conversation/" (e.g. "conversation/report.xlsx"), an earlier conversation's under "conversations/<conversation_id>/<name>", or a document under "data/<file-type>/" (e.g. "data/contracts/acme/msa.docx"), whose result gives the version you copied: pass it to copy_from_sandbox to copy the document back. Hub resource files cannot be copied. The copy lands at sandbox_path, replacing any file there; a file larger than 25 MiB is refused, and a document larger than 1 MiB. If no sandbox is running, the copy starts an empty one.`,
|
|
7904
8481
|
"strict": false,
|
|
7905
8482
|
"parameters": {
|
|
7906
8483
|
"type": "object",
|
|
7907
8484
|
"properties": {
|
|
7908
8485
|
"path": {
|
|
7909
8486
|
"type": "string",
|
|
7910
|
-
"description": `The file to copy, as read_file names it: "conversation/<name>" (a shorter unambiguous form or the file's id also works)
|
|
8487
|
+
"description": `The file to copy, as read_file names it: "conversation/<name>" (a shorter unambiguous form or the file's id also works), "conversations/<conversation_id>/<name>", or a document's full path "data/<file-type>/<path>".`
|
|
7911
8488
|
},
|
|
7912
8489
|
"sandbox_path": {
|
|
7913
8490
|
"type": "string",
|
|
@@ -7925,7 +8502,7 @@ var init_chunk_QCQUZL4L = __esm({
|
|
|
7925
8502
|
"copy_from_sandbox": {
|
|
7926
8503
|
"tool_config": {
|
|
7927
8504
|
"name": "copy_from_sandbox",
|
|
7928
|
-
"description":
|
|
8505
|
+
"description": `Copy a file out of the code sandbox into this conversation, so what your commands produced (a chart, a converted document) is kept after the sandbox ends. The file is stored for the support team on an internal note and is NOT sent to the customer: the result gives its path under "conversation/", and send_files with that path delivers it when it should reach them. A file larger than 25 MiB is refused. With path, the file is written instead to one of your documents under "data/<file-type>/", with the version you copied it in at: the write goes through only while that is still the document's current version, so a document changed since is refused, and only where you may write documents. A document holds at most 1 MiB.`,
|
|
7929
8506
|
"strict": false,
|
|
7930
8507
|
"parameters": {
|
|
7931
8508
|
"type": "object",
|
|
@@ -7936,7 +8513,15 @@ var init_chunk_QCQUZL4L = __esm({
|
|
|
7936
8513
|
},
|
|
7937
8514
|
"name": {
|
|
7938
8515
|
"type": "string",
|
|
7939
|
-
"description": `The stored file's name, with no folders (e.g. "q3-report.xlsx"). Defaults to the file's name in the sandbox.`
|
|
8516
|
+
"description": `The stored file's name, with no folders (e.g. "q3-report.xlsx"). Defaults to the file's name in the sandbox. Not with path.`
|
|
8517
|
+
},
|
|
8518
|
+
"path": {
|
|
8519
|
+
"type": "string",
|
|
8520
|
+
"description": 'A document to write the file to instead, by its full path, e.g. "data/contracts/acme/msa.docx", as copy_to_sandbox or list_files gives it. Leave it out to keep the file with this conversation.'
|
|
8521
|
+
},
|
|
8522
|
+
"version": {
|
|
8523
|
+
"type": "integer",
|
|
8524
|
+
"description": "With path: the version copy_to_sandbox (or read_file) gave for the document, to replace it. Leave it out to create a new document."
|
|
7940
8525
|
}
|
|
7941
8526
|
},
|
|
7942
8527
|
"required": [
|
|
@@ -8442,7 +9027,7 @@ var init_chunk_QCQUZL4L = __esm({
|
|
|
8442
9027
|
{ tool_native_id: "nt-008", tool_name: "get_state", tool_display_name: "Get State", tool_description: "Retrieve conversation or user state values", connector_id: WAYAI_CONNECTOR, operation: "get_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun" },
|
|
8443
9028
|
{ tool_native_id: "nt-009", tool_name: "reset_state", tool_display_name: "Reset State", tool_description: "Reset conversation or user state to initial values", connector_id: WAYAI_CONNECTOR, operation: "reset_state", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun" },
|
|
8444
9029
|
{ tool_native_id: "nt-010", tool_name: "set_state_path", tool_display_name: "Set State Path", tool_description: "Set a specific path within state object", connector_id: WAYAI_CONNECTOR, operation: "set_state_path", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun" },
|
|
8445
|
-
{ tool_native_id: "nt-011", tool_name: "consult_agent", tool_display_name: "Consult Agent", tool_description: "Consult an advisor agent for advice without transferring", connector_id: WAYAI_CONNECTOR, operation: "consult_agent", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "
|
|
9030
|
+
{ tool_native_id: "nt-011", tool_name: "consult_agent", tool_display_name: "Consult Agent", tool_description: "Consult an advisor agent for advice without transferring", connector_id: WAYAI_CONNECTOR, operation: "consult_agent", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "keyed" },
|
|
8446
9031
|
{ tool_native_id: "nt-013", tool_name: "read_skill", tool_display_name: "Read Skill", tool_description: "Read the full specification of a skill", connector_id: WAYAI_CONNECTOR, operation: "read_skill", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun" },
|
|
8447
9032
|
{ tool_native_id: "nt-014", tool_name: "read_skill_file", tool_display_name: "Read Skill File", tool_description: "Read a specific file within a skill", connector_id: WAYAI_CONNECTOR, operation: "read_skill_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun" },
|
|
8448
9033
|
{ tool_native_id: "nt-015", tool_name: "read_file", tool_display_name: "Read File", tool_description: "Read the contents of a resource file", connector_id: WAYAI_CONNECTOR, operation: "read_file", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun" },
|
|
@@ -8490,7 +9075,7 @@ var init_chunk_QCQUZL4L = __esm({
|
|
|
8490
9075
|
// collect (and, for a consultant target, the `allow_consultant_chain` opt-in). A
|
|
8491
9076
|
// live button that only ever errors is worse than no button. Assign via `wayai push`
|
|
8492
9077
|
// until the picker ships.
|
|
8493
|
-
{ tool_native_id: "nt-026", tool_name: "start_consult_thread", tool_display_name: "Start Consult Thread", tool_description: "Ask a configured consultant (or partner hub) a question in a visible consult thread", connector_id: WAYAI_CONNECTOR, operation: "start_consult_thread", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null, side_effects: true, replay: "
|
|
9078
|
+
{ tool_native_id: "nt-026", tool_name: "start_consult_thread", tool_display_name: "Start Consult Thread", tool_description: "Ask a configured consultant (or partner hub) a question in a visible consult thread", connector_id: WAYAI_CONNECTOR, operation: "start_consult_thread", tool_method: "internal", tool_group: "Native Tools", is_delegation: true, is_auto_set: false, execution_config: null, side_effects: true, replay: "keyed", ui_assignable: false },
|
|
8494
9079
|
// One drill-down listing for every file namespace, replacing `list_resource_files`
|
|
8495
9080
|
// (nt-016) and `list_resource_folders` (nt-017). Fully visible: it needs no
|
|
8496
9081
|
// configuration the Add dialog cannot collect — the mounts an agent may browse
|
|
@@ -8524,11 +9109,17 @@ var init_chunk_QCQUZL4L = __esm({
|
|
|
8524
9109
|
// setting is on, on any provider. Side effects: under an Allowlist or Web network setting, the
|
|
8525
9110
|
// code it runs can send data out of the conversation.
|
|
8526
9111
|
{ tool_native_id: "nt-030", tool_name: "bash", tool_display_name: "Run Code (Bash)", tool_description: "Run a Bash command in the conversation's code sandbox", connector_id: WAYAI_CONNECTOR, operation: "bash", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: true, replay: "reconcile", assignable_roles: SANDBOX_AGENT_ROLES },
|
|
8527
|
-
// Copies a conversation file
|
|
8528
|
-
// leaves the conversation.
|
|
9112
|
+
// Copies a conversation file, or a toolset document (`data/`, read through the toolset's `get`),
|
|
9113
|
+
// INTO the sandbox: the sandbox is conversation state, so nothing leaves the conversation. It is
|
|
9114
|
+
// shared by the conversation's agents that use it, so a document copied in is there for each of
|
|
9115
|
+
// them, as anything else an agent leaves in it.
|
|
8529
9116
|
{ tool_native_id: "nt-031", tool_name: "copy_to_sandbox", tool_display_name: "Copy File to Sandbox", tool_description: "Copy one of the conversation's files into its code sandbox", connector_id: WAYAI_CONNECTOR, operation: "copy_to_sandbox", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "rerun", assignable_roles: SANDBOX_AGENT_ROLES },
|
|
8530
9117
|
// Copies a sandbox file OUT into the conversation, as an internal note for the team: nothing
|
|
8531
|
-
// reaches the customer until `send_files` sends it.
|
|
9118
|
+
// reaches the customer until `send_files` sends it. With `path`, it writes a toolset document
|
|
9119
|
+
// instead, through the toolset's `put` with its version check: such a call is `reconcile`, not
|
|
9120
|
+
// `keyed`, as `write_file` is (`toolCallReplay` reads its arguments). Its side effects stay `false`:
|
|
9121
|
+
// only an agent holding a toolset `put` can write one, never a customer-facing role (premise 8),
|
|
9122
|
+
// and a voice call's turns, which read this flag, run the pilot track, which is customer-facing.
|
|
8532
9123
|
{ tool_native_id: "nt-032", tool_name: "copy_from_sandbox", tool_display_name: "Copy File from Sandbox", tool_description: "Copy a file out of the code sandbox into the conversation, for the team", connector_id: WAYAI_CONNECTOR, operation: "copy_from_sandbox", tool_method: "internal", tool_group: "Native Tools", is_delegation: false, is_auto_set: false, execution_config: null, side_effects: false, replay: "keyed", assignable_roles: SANDBOX_AGENT_ROLES },
|
|
8533
9124
|
// The `data/` mount's writes (#5142 step 2): a document of the agent's Base Toolset, written
|
|
8534
9125
|
// through the toolset's own `put` tool with its version check. `reconcile`, not `keyed`: a re-run
|
|
@@ -8764,7 +9355,7 @@ var init_chunk_T7KQKDET = __esm({
|
|
|
8764
9355
|
}
|
|
8765
9356
|
});
|
|
8766
9357
|
|
|
8767
|
-
// ../../packages/core/dist/chunk-
|
|
9358
|
+
// ../../packages/core/dist/chunk-BUXNYLB7.js
|
|
8768
9359
|
function resolveMonitorTrigger(monitorConfig) {
|
|
8769
9360
|
if (!monitorConfig || typeof monitorConfig !== "object") return "idle";
|
|
8770
9361
|
const raw = monitorConfig.trigger;
|
|
@@ -9070,10 +9661,10 @@ function refineMonitorRulesStructuredOutput(body, ctx) {
|
|
|
9070
9661
|
if (refusal2) ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["response_format"], message: refusal2 });
|
|
9071
9662
|
}
|
|
9072
9663
|
var NATIVE_TOOL_NAMES, previousConversationsCountField, summarizationThresholdField, flagConditionSchema, MONITOR_TRIGGERS, monitorTriggerSchema, AUTOMATION_GATE_TRIGGERS, NON_FIRING_TRIGGERS, MONITOR_FIRING_TRIGGERS, MONITOR_DELAY_SECONDS_MIN, MONITOR_IDLE_DELAY_REQUIRED_MESSAGE, MONITOR_HISTORY_MESSAGES_MAX, monitorHistoryMessagesSchema, monitorIncludeToolResultsSchema, MONITOR_INPUT_SHAPING_KEYS, MONITOR_INPUT_SHAPING_SCHEMAS, MONITOR_INPUT_SHAPING_IDLE_MESSAGE, monitorArgumentSourceSchema, MONITOR_NOTE_TEMPLATE_MAX, MONITOR_STEER_NOTE_MAX, MONITOR_GATE_AGENT_NAME_MAX, monitorActionSchema, monitorRuleSchema, MONITOR_RULE_KEYS, MONITOR_RULE_SCHEMAS, MONITOR_RULE_TRIGGERS, MONITOR_RULE_TRIGGER_MESSAGE, CALL_UTTERANCE_SPEAKERS, callUtteranceSpeakerSchema, MONITOR_CALL_KEYS, MONITOR_CALL_SCHEMAS, MONITOR_CALL_KEY_MESSAGE, CALL_STEERING_MIN_CONFIDENCE, CALL_UTTERANCE_RULE_NOT_CONFIDENT_MESSAGE, CALL_UTTERANCE_FALLBACK_MESSAGE, CALL_UTTERANCE_FLAG_CONDITIONS_MESSAGE, CALL_STEER_NOTE_BLANK_MESSAGE, MONITOR_USER_MESSAGE_ACTION_KINDS, MONITOR_ASSISTANT_REPLY_ACTION_KINDS, MONITOR_CALL_UTTERANCE_ACTION_KINDS, MONITOR_AUTOMATION_FIRE_ACTION_KINDS, MONITOR_AUTOMATION_ITEM_ACTION_KINDS, INSERT_NOTE_TOOL_NAME, RUN_MONITOR_TOOL_NAME, MONITOR_RULE_ALLOWED_NATIVE_TOOLS, MONITOR_RULE_REENTRY_TOOLS, MONITOR_RULE_REENTRY_TRACKS, AUTOMATION_FIRE_FLAG_CONDITIONS_MESSAGE, AUTOMATION_FIRE_INPUT_SHAPING_MESSAGE, AUTOMATION_ITEM_FLAG_CONDITIONS_MESSAGE, AUTOMATION_ITEM_INPUT_SHAPING_MESSAGE, AUTOMATION_GATE_KEY_MESSAGES, monitorConfigField, flagConditionsField, agentIdParam, listAgentsQuery, agentConnectionsQuery, createAgentBody, updateAgentBody, AGENT_PARAMETER_TYPES, AGENT_PARAMETER_NAME_REGEX, agentParameterCreateSchema, MAX_AGENT_PARAMETERS_PER_REQUEST, createAgentParametersBody;
|
|
9073
|
-
var
|
|
9074
|
-
"../../packages/core/dist/chunk-
|
|
9664
|
+
var init_chunk_BUXNYLB7 = __esm({
|
|
9665
|
+
"../../packages/core/dist/chunk-BUXNYLB7.js"() {
|
|
9075
9666
|
"use strict";
|
|
9076
|
-
|
|
9667
|
+
init_chunk_HX2LNKHX();
|
|
9077
9668
|
init_chunk_IBL7FANV();
|
|
9078
9669
|
init_chunk_PFDZEKDE();
|
|
9079
9670
|
init_chunk_HZ6CADSY();
|
|
@@ -9296,12 +9887,12 @@ var init_chunk_BQWVW6JO = __esm({
|
|
|
9296
9887
|
}
|
|
9297
9888
|
});
|
|
9298
9889
|
|
|
9299
|
-
// ../../packages/core/dist/chunk-
|
|
9890
|
+
// ../../packages/core/dist/chunk-7YJWGPAR.js
|
|
9300
9891
|
var conversationIdParam, conversationHubQuery, claimBody, releaseBody, transferBody, closeBody, additionalContextPayloadSchema, kanbanUpdateBody, annotateConversationBody, observabilityParams, observabilityListParams, deleteUserConversationsBody, deleteUserConversationsResponse, sendMessageBody, listConversationsQuery, flagSourceSchema, getConversationsResponse, getConversationDetailParams, getConversationDetailQuery, getConversationDetailResponse, listWhatsAppTemplatesQuery, sendWhatsAppTemplateBody;
|
|
9301
|
-
var
|
|
9302
|
-
"../../packages/core/dist/chunk-
|
|
9892
|
+
var init_chunk_7YJWGPAR = __esm({
|
|
9893
|
+
"../../packages/core/dist/chunk-7YJWGPAR.js"() {
|
|
9303
9894
|
"use strict";
|
|
9304
|
-
|
|
9895
|
+
init_chunk_BUXNYLB7();
|
|
9305
9896
|
init_chunk_HZ6CADSY();
|
|
9306
9897
|
init_chunk_DHPUPI4Y();
|
|
9307
9898
|
init_zod();
|
|
@@ -9757,7 +10348,7 @@ var init_chunk_BPCNB5RT = __esm({
|
|
|
9757
10348
|
}
|
|
9758
10349
|
});
|
|
9759
10350
|
|
|
9760
|
-
// ../../packages/core/dist/chunk-
|
|
10351
|
+
// ../../packages/core/dist/chunk-AR73HGPH.js
|
|
9761
10352
|
function idempotencyHeaderRefusal(value) {
|
|
9762
10353
|
if (typeof value !== "string" || !/^[A-Za-z0-9][A-Za-z0-9-]{0,63}$/.test(value)) {
|
|
9763
10354
|
return "idempotency_header must be a header name: letters, digits and dashes, at most 64";
|
|
@@ -9768,10 +10359,10 @@ function idempotencyHeaderRefusal(value) {
|
|
|
9768
10359
|
return null;
|
|
9769
10360
|
}
|
|
9770
10361
|
var CATALOG_REPLAY, REPLAY_OVERRIDES, RESERVED_HEADERS;
|
|
9771
|
-
var
|
|
9772
|
-
"../../packages/core/dist/chunk-
|
|
10362
|
+
var init_chunk_AR73HGPH = __esm({
|
|
10363
|
+
"../../packages/core/dist/chunk-AR73HGPH.js"() {
|
|
9773
10364
|
"use strict";
|
|
9774
|
-
|
|
10365
|
+
init_chunk_HX2LNKHX();
|
|
9775
10366
|
CATALOG_REPLAY = new Map(
|
|
9776
10367
|
NATIVE_TOOLS.map((tool) => [tool.tool_name, tool.replay])
|
|
9777
10368
|
);
|
|
@@ -10004,7 +10595,7 @@ var init_chunk_WEXVYJ5H = __esm({
|
|
|
10004
10595
|
}
|
|
10005
10596
|
});
|
|
10006
10597
|
|
|
10007
|
-
// ../../packages/core/dist/chunk-
|
|
10598
|
+
// ../../packages/core/dist/chunk-MK6D4I2Q.js
|
|
10008
10599
|
function refineToolConfig(value, ctx) {
|
|
10009
10600
|
const raw = value.tool_config;
|
|
10010
10601
|
if (raw == null) return;
|
|
@@ -10040,10 +10631,10 @@ function refineToolConfig(value, ctx) {
|
|
|
10040
10631
|
}
|
|
10041
10632
|
}
|
|
10042
10633
|
var toolIdParam, listToolsQuery, toolAgentQuery, deleteToolQuery, toolConnectionsQuery, nativeToolIdSchema, CONTEXT_BOUNDARIES, contextBoundarySchema, addNativeToolBody, addMcpToolBody, toolReplaySettingsFields, toolReplaySettingsSchema, createCustomToolBody, updateCustomToolBody, updateToolExecutionConfigBody;
|
|
10043
|
-
var
|
|
10044
|
-
"../../packages/core/dist/chunk-
|
|
10634
|
+
var init_chunk_MK6D4I2Q = __esm({
|
|
10635
|
+
"../../packages/core/dist/chunk-MK6D4I2Q.js"() {
|
|
10045
10636
|
"use strict";
|
|
10046
|
-
|
|
10637
|
+
init_chunk_AR73HGPH();
|
|
10047
10638
|
init_chunk_WEXVYJ5H();
|
|
10048
10639
|
init_chunk_HZ6CADSY();
|
|
10049
10640
|
init_zod();
|
|
@@ -10154,7 +10745,7 @@ var init_chunk_2ISUBJG7 = __esm({
|
|
|
10154
10745
|
}
|
|
10155
10746
|
});
|
|
10156
10747
|
|
|
10157
|
-
// ../../packages/core/dist/chunk-
|
|
10748
|
+
// ../../packages/core/dist/chunk-623SRYWZ.js
|
|
10158
10749
|
function describeRole(role) {
|
|
10159
10750
|
if (typeof role === "string") {
|
|
10160
10751
|
return JSON.stringify(role.length > ROLE_ECHO_MAX ? `${role.slice(0, ROLE_ECHO_MAX)}\u2026` : role);
|
|
@@ -10328,8 +10919,8 @@ function terminalRunCount(counts) {
|
|
|
10328
10919
|
return counts.successful_runs + counts.failed_runs;
|
|
10329
10920
|
}
|
|
10330
10921
|
var evalIdParam, sessionIdParam, scenarioSetIdParam, hubIdQuery, EVAL_FIXTURE_NAME_MAX_LENGTH, evalFixtureOverrideSchema, runSessionQuery, runJourneyQuery, EVAL_INPUT_ROLES, EVAL_INPUT_ROLE_LIST, ROLE_ECHO_MAX, EVAL_INITIAL_STATE_SCOPES, MAX_INITIAL_STATE_ENTRIES, evalInitialStateEntry, evalCustomerContactIdSchema, evalBody, EVAL_LIST_MAX_LIMIT, evalListLimit, getEvalsQuery, toggleEvalBody, bulkToggleEvalsBody, pacingConfigSchema, EVAL_MAX_SELECTED_SCENARIOS, evalScenarioSelectionEntrySchema, evalScenarioSelectionSchema, sessionConfigSchema, createSessionBody, getSessionsQuery, getSessionResultsQuery, getSessionRunsQuery, evalAnalyticsQuery, getSessionCostQuery, compareSessionsQuery, evalsSqlBody, evalsSqlSchemaQuery, exportResultsQuery, validateEvalBody, hubAgentsQuery, createScenarioSetBody, updateScenarioSetBody, scenarioSetsHubQuery, createScenarioFromConversationBody, createJourneyFromConversationBody, evalAttachmentRef, turnMayCarryAttachments, ATTACHMENTS_USER_ONLY_MESSAGE, journeyIdParam, journeyToolCallSchema, journeyTurnSchema, journeyStepOverrideSchema, fixtureField, createJourneyBody, updateJourneyBody, evalSessionConfigResponseSchema, evalSessionResponseSchema, evalSessionListConfigSchema, evalSessionListItemSchema, seedReleaseStatusSchema, EVAL_SESSION_NOT_QUIESCENT;
|
|
10331
|
-
var
|
|
10332
|
-
"../../packages/core/dist/chunk-
|
|
10922
|
+
var init_chunk_623SRYWZ = __esm({
|
|
10923
|
+
"../../packages/core/dist/chunk-623SRYWZ.js"() {
|
|
10333
10924
|
"use strict";
|
|
10334
10925
|
init_chunk_4LXDRTO4();
|
|
10335
10926
|
init_chunk_LZDVDC2S();
|
|
@@ -11415,7 +12006,7 @@ var init_chunk_6NI5RAM4 = __esm({
|
|
|
11415
12006
|
}
|
|
11416
12007
|
});
|
|
11417
12008
|
|
|
11418
|
-
// ../../packages/core/dist/chunk-
|
|
12009
|
+
// ../../packages/core/dist/chunk-EAT2SP4E.js
|
|
11419
12010
|
function refineHubAsCodeCustomTools(config, ctx) {
|
|
11420
12011
|
const agents = config.agents;
|
|
11421
12012
|
if (!Array.isArray(agents)) return;
|
|
@@ -11559,14 +12150,22 @@ function refineHubAsCodeEvals(config, ctx) {
|
|
|
11559
12150
|
message: `eval "${entry?.name ?? "<unnamed>"}": ${initialStateError}`
|
|
11560
12151
|
});
|
|
11561
12152
|
}
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
12153
|
+
}
|
|
12154
|
+
}
|
|
12155
|
+
function refineHubAsCodeCustomers(config, ctx) {
|
|
12156
|
+
for (const [key, kind] of [["evals", "eval"], ["journeys", "journey"]]) {
|
|
12157
|
+
const entries = config[key];
|
|
12158
|
+
if (!Array.isArray(entries)) continue;
|
|
12159
|
+
entries.forEach((entry, i) => {
|
|
12160
|
+
const error = evalCustomerContactIdError(entry);
|
|
12161
|
+
if (error) {
|
|
12162
|
+
ctx.addIssue({
|
|
12163
|
+
code: external_exports.ZodIssueCode.custom,
|
|
12164
|
+
path: [key, i, "customer_contact_id"],
|
|
12165
|
+
message: `${kind} "${entry?.name ?? "<unnamed>"}": ${error}`
|
|
12166
|
+
});
|
|
12167
|
+
}
|
|
12168
|
+
});
|
|
11570
12169
|
}
|
|
11571
12170
|
}
|
|
11572
12171
|
function refineHubAsCodeEvalAttachments(config, ctx) {
|
|
@@ -11943,12 +12542,12 @@ function refineHubAsCodeMonitorRulesOutput(config, ctx) {
|
|
|
11943
12542
|
});
|
|
11944
12543
|
}
|
|
11945
12544
|
var MAX_10MB_BASE64_ENCODED_BYTES, MAX_EVAL_ATTACHMENT_ENCODED_BYTES, MAX_EVAL_ATTACHMENT_CARRIERS, MAX_EVAL_ATTACHMENT_AGGREGATE_ENCODED_BYTES, MAX_RESOURCE_ENCODED_BYTES, MAX_HUB_AS_CODE_RESOURCES, MAX_HUB_AS_CODE_RESOURCE_FILES, MAX_RESOURCE_AGGREGATE_ENCODED_BYTES, MAX_CI_CONFIG_BODY_BYTES, MAX_CALL_OPENING_CARRIERS, MAX_CALL_OPENING_AGGREGATE_ENCODED_BYTES, hubAsCodeStateSchema, hubAsCodeResourceFileSchema, hubAsCodeResourceSchema, uuidPattern, ciUuidSchema, boundedHubAsCodeResourcesSchema, hubAsCodeConfigSchema, ciPullHubIdParam, ciBranchParam, ciPullQuery, ciHubsQuery, ciLookupQuery, ciBranchesQuery, ciSyncBody, ciPushBody, ciDiffBody, ciPublishBody, ciPublishPreviewBody, ciSyncMcpBody, ciOrgResourcesParam, orgResourcesConfigSchema, ciOrgResourcesPushBody, ciOrgResourcesDiffBody, CI_APPLY_ENTITY_KINDS, CI_APPLY_OPERATIONS, ciApplyErrorBaseSchema, ciApplyErrorSchema, ciSyncCountSchema, ciSyncChangesSchema, ciSyncWarningSchema, ciSyncResponseSchema;
|
|
11946
|
-
var
|
|
11947
|
-
"../../packages/core/dist/chunk-
|
|
12545
|
+
var init_chunk_EAT2SP4E = __esm({
|
|
12546
|
+
"../../packages/core/dist/chunk-EAT2SP4E.js"() {
|
|
11948
12547
|
"use strict";
|
|
11949
|
-
|
|
11950
|
-
|
|
11951
|
-
|
|
12548
|
+
init_chunk_MK6D4I2Q();
|
|
12549
|
+
init_chunk_623SRYWZ();
|
|
12550
|
+
init_chunk_BUXNYLB7();
|
|
11952
12551
|
init_chunk_LBDH7LCG();
|
|
11953
12552
|
init_chunk_4LXDRTO4();
|
|
11954
12553
|
init_chunk_T4Q5COKJ();
|
|
@@ -12052,6 +12651,7 @@ var init_chunk_54QBGDXF = __esm({
|
|
|
12052
12651
|
refineHubAsCodeLanes(config, ctx);
|
|
12053
12652
|
refineHubAsCodePresentation(config, ctx);
|
|
12054
12653
|
refineHubAsCodeEvals(config, ctx);
|
|
12654
|
+
refineHubAsCodeCustomers(config, ctx);
|
|
12055
12655
|
refineHubAsCodeEvalAttachments(config, ctx);
|
|
12056
12656
|
refineHubAsCodeCallOpenings(config, ctx);
|
|
12057
12657
|
refineHubAsCodeResources(config, ctx);
|
|
@@ -12788,10 +13388,10 @@ var init_chunk_YULHXJ5M = __esm({
|
|
|
12788
13388
|
}
|
|
12789
13389
|
});
|
|
12790
13390
|
|
|
12791
|
-
// ../../packages/core/dist/chunk-
|
|
13391
|
+
// ../../packages/core/dist/chunk-V4FRPRH5.js
|
|
12792
13392
|
var CONSULT_THREAD_STATUSES, consultThreadStatus, CONSULT_THREAD_TITLE_MAX, consultThreadTitle, consultThreadSummary, listConsultThreadsQuery, listConsultThreadsResponse, createConsultThreadBody, createConsultThreadResponse, consultThreadIdParam, updateConsultThreadBody, updateConsultThreadResponse, CONSULT_AWAIT_TIMEOUT_MS;
|
|
12793
|
-
var
|
|
12794
|
-
"../../packages/core/dist/chunk-
|
|
13393
|
+
var init_chunk_V4FRPRH5 = __esm({
|
|
13394
|
+
"../../packages/core/dist/chunk-V4FRPRH5.js"() {
|
|
12795
13395
|
"use strict";
|
|
12796
13396
|
init_zod();
|
|
12797
13397
|
CONSULT_THREAD_STATUSES = ["open", "awaiting", "resolved", "cancelled_at_close"];
|
|
@@ -12956,29 +13556,13 @@ var init_chunk_LQY2OCQL = __esm({
|
|
|
12956
13556
|
}
|
|
12957
13557
|
});
|
|
12958
13558
|
|
|
12959
|
-
// ../../packages/core/dist/chunk-
|
|
12960
|
-
var DATA_PROXY_MOUNT, DATA_PROXY_PREFIX, BASE_DESTRUCTION_MOUNT, BASE_DESTRUCTION_PREFIX, BASE_DESTRUCTION_CONFIRM_PATH, COMMUNITY_EMAIL_UNSUBSCRIBE_PATH, CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH, AUTOMATION_WEBHOOK_MOUNT;
|
|
12961
|
-
var init_chunk_F3JHI4Y4 = __esm({
|
|
12962
|
-
"../../packages/core/dist/chunk-F3JHI4Y4.js"() {
|
|
12963
|
-
"use strict";
|
|
12964
|
-
DATA_PROXY_MOUNT = "/data";
|
|
12965
|
-
DATA_PROXY_PREFIX = `/api${DATA_PROXY_MOUNT}`;
|
|
12966
|
-
BASE_DESTRUCTION_MOUNT = "/bases";
|
|
12967
|
-
BASE_DESTRUCTION_PREFIX = `/api${BASE_DESTRUCTION_MOUNT}`;
|
|
12968
|
-
BASE_DESTRUCTION_CONFIRM_PATH = `${BASE_DESTRUCTION_PREFIX}/destruction/confirm`;
|
|
12969
|
-
COMMUNITY_EMAIL_UNSUBSCRIBE_PATH = "/api/communities/email-unsubscribe";
|
|
12970
|
-
CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH = "/api/contact-book/email-unsubscribe";
|
|
12971
|
-
AUTOMATION_WEBHOOK_MOUNT = "/webhooks/automations";
|
|
12972
|
-
}
|
|
12973
|
-
});
|
|
12974
|
-
|
|
12975
|
-
// ../../packages/core/dist/chunk-MTQ6G7VB.js
|
|
13559
|
+
// ../../packages/core/dist/chunk-6N2QIHHQ.js
|
|
12976
13560
|
var AUTOMATION_TRIGGER_TYPES, AUTOMATION_EVENT_TYPES, AUTOMATION_TARGET_TYPES, AUTOMATION_ACTION_TYPES, AUTOMATION_CHANNEL_TYPES, AUTOMATION_PURPOSES, AUTOMATION_NAME_MAX, AUTOMATION_DESCRIPTION_MAX, AUTOMATION_INSTRUCTIONS_MAX, AUTOMATION_TEXT_MAX, AUTOMATION_CRON_MAX, AUTOMATION_WEBHOOK_AUTHS, AUTOMATION_WEBHOOK_HEADERS, AUTOMATION_WEBHOOK_BODY_MAX_BYTES, AUTOMATION_WEBHOOK_HEADER_NAME_MAX, WEBHOOK_RESERVED_HEADERS, automationTriggerSchema, automationTargetSchema, automationActionSchema, automationGateSchema, automationName, automationDescription, automationIdParam, automationHubQuery, automationWebhookParams, AUTOMATION_RUNS_PAGE_MAX, listAutomationRunsQuery, createAutomationBody, updateAutomationBody, AUTOMATION_ALERT_CATEGORIES, AUTOMATION_BLOCKED_ALERT_CATEGORY, automationEventSubscriberParam;
|
|
12977
|
-
var
|
|
12978
|
-
"../../packages/core/dist/chunk-
|
|
13561
|
+
var init_chunk_6N2QIHHQ = __esm({
|
|
13562
|
+
"../../packages/core/dist/chunk-6N2QIHHQ.js"() {
|
|
12979
13563
|
"use strict";
|
|
12980
13564
|
init_chunk_LQY2OCQL();
|
|
12981
|
-
|
|
13565
|
+
init_chunk_BUXNYLB7();
|
|
12982
13566
|
init_chunk_3HSB6J4J();
|
|
12983
13567
|
init_chunk_F3JHI4Y4();
|
|
12984
13568
|
init_chunk_DHPUPI4Y();
|
|
@@ -14223,6 +14807,7 @@ var init_chunk_TS4EYU3W = __esm({
|
|
|
14223
14807
|
var init_contracts = __esm({
|
|
14224
14808
|
"../../packages/core/dist/contracts/index.js"() {
|
|
14225
14809
|
"use strict";
|
|
14810
|
+
init_chunk_VXEAR36B();
|
|
14226
14811
|
init_chunk_TIZ57ZBA();
|
|
14227
14812
|
init_chunk_Q7KIQHAZ();
|
|
14228
14813
|
init_chunk_2U4BTOR7();
|
|
@@ -14230,16 +14815,16 @@ var init_contracts = __esm({
|
|
|
14230
14815
|
init_chunk_GK27ENTT();
|
|
14231
14816
|
init_chunk_SXDX4GHS();
|
|
14232
14817
|
init_chunk_FK6HBXM7();
|
|
14233
|
-
|
|
14818
|
+
init_chunk_GPM7PE44();
|
|
14234
14819
|
init_chunk_DGKMMFIR();
|
|
14235
14820
|
init_chunk_ZC6YGHNX();
|
|
14236
14821
|
init_chunk_ACKF2MVU();
|
|
14237
14822
|
init_chunk_Y7CFCYX4();
|
|
14238
14823
|
init_chunk_5DGHIY7L();
|
|
14239
|
-
|
|
14824
|
+
init_chunk_CGOLULK2();
|
|
14825
|
+
init_chunk_4MRBBR5L();
|
|
14240
14826
|
init_chunk_BEOHBREG();
|
|
14241
14827
|
init_chunk_IPDOHAZ5();
|
|
14242
|
-
init_chunk_GPM7PE44();
|
|
14243
14828
|
init_chunk_YUZFHVB2();
|
|
14244
14829
|
init_chunk_34LE6P7E();
|
|
14245
14830
|
init_chunk_FPSDNS2E();
|
|
@@ -14252,24 +14837,24 @@ var init_contracts = __esm({
|
|
|
14252
14837
|
init_chunk_LWMLR2WI();
|
|
14253
14838
|
init_chunk_ACGAI25T();
|
|
14254
14839
|
init_chunk_MH4O3YEX();
|
|
14255
|
-
|
|
14840
|
+
init_chunk_7YJWGPAR();
|
|
14256
14841
|
init_chunk_KSTYTQ7I();
|
|
14257
14842
|
init_chunk_LKBSHANA();
|
|
14258
14843
|
init_chunk_BPCNB5RT();
|
|
14259
|
-
|
|
14260
|
-
|
|
14261
|
-
|
|
14844
|
+
init_chunk_EAT2SP4E();
|
|
14845
|
+
init_chunk_MK6D4I2Q();
|
|
14846
|
+
init_chunk_623SRYWZ();
|
|
14262
14847
|
init_chunk_JDJ4EUOL();
|
|
14263
14848
|
init_chunk_3KYTITOR();
|
|
14264
14849
|
init_chunk_XCDUP42J();
|
|
14265
14850
|
init_chunk_6J742GLA();
|
|
14266
14851
|
init_chunk_ZVXO3XL6();
|
|
14267
14852
|
init_chunk_YULHXJ5M();
|
|
14268
|
-
|
|
14853
|
+
init_chunk_V4FRPRH5();
|
|
14269
14854
|
init_chunk_ZHJEQBXJ();
|
|
14270
|
-
|
|
14855
|
+
init_chunk_6N2QIHHQ();
|
|
14271
14856
|
init_chunk_LQY2OCQL();
|
|
14272
|
-
|
|
14857
|
+
init_chunk_BUXNYLB7();
|
|
14273
14858
|
init_chunk_3HSB6J4J();
|
|
14274
14859
|
init_chunk_IG6QBZMA();
|
|
14275
14860
|
init_chunk_ZJJ5ODOD();
|
|
@@ -14279,8 +14864,8 @@ var init_contracts = __esm({
|
|
|
14279
14864
|
init_chunk_E4DIIOOE();
|
|
14280
14865
|
init_chunk_7CYHPF35();
|
|
14281
14866
|
init_chunk_FMXFEZN4();
|
|
14282
|
-
|
|
14283
|
-
|
|
14867
|
+
init_chunk_AR73HGPH();
|
|
14868
|
+
init_chunk_HX2LNKHX();
|
|
14284
14869
|
init_chunk_NVKU5PPQ();
|
|
14285
14870
|
init_chunk_2FCLSQSQ();
|
|
14286
14871
|
init_chunk_FECLIZTY();
|
|
@@ -14293,6 +14878,7 @@ var init_contracts = __esm({
|
|
|
14293
14878
|
init_chunk_OMPT4ZHR();
|
|
14294
14879
|
init_chunk_2PZIF56U();
|
|
14295
14880
|
init_chunk_UEJOG2Q7();
|
|
14881
|
+
init_chunk_IEWJ5MZH();
|
|
14296
14882
|
init_chunk_SOJMWM63();
|
|
14297
14883
|
init_chunk_WEXVYJ5H();
|
|
14298
14884
|
init_chunk_LZDVDC2S();
|
|
@@ -15514,32 +16100,6 @@ var init_chunk_BF4WRSOQ = __esm({
|
|
|
15514
16100
|
});
|
|
15515
16101
|
|
|
15516
16102
|
// ../../packages/core/dist/index.js
|
|
15517
|
-
function matchPath(pattern, path42) {
|
|
15518
|
-
const normalizedPath = path42.replace(/\/$/, "");
|
|
15519
|
-
if (pattern.endsWith("/*")) {
|
|
15520
|
-
const prefix = pattern.slice(0, -2);
|
|
15521
|
-
return normalizedPath === prefix || normalizedPath.startsWith(prefix + "/");
|
|
15522
|
-
}
|
|
15523
|
-
return pattern === normalizedPath;
|
|
15524
|
-
}
|
|
15525
|
-
function getEndpointConfig(path42) {
|
|
15526
|
-
for (const excluded of EXCLUDED_PATHS) {
|
|
15527
|
-
if (matchPath(excluded, path42)) {
|
|
15528
|
-
return null;
|
|
15529
|
-
}
|
|
15530
|
-
}
|
|
15531
|
-
const normalizedPath = path42.replace(/\/$/, "");
|
|
15532
|
-
if (!normalizedPath.endsWith("/*") && Object.prototype.hasOwnProperty.call(ENDPOINT_CONFIGS, normalizedPath)) {
|
|
15533
|
-
return ENDPOINT_CONFIGS[normalizedPath];
|
|
15534
|
-
}
|
|
15535
|
-
const sortedPatterns = Object.keys(ENDPOINT_CONFIGS).sort((a, b) => b.length - a.length);
|
|
15536
|
-
for (const pattern of sortedPatterns) {
|
|
15537
|
-
if (matchPath(pattern, path42)) {
|
|
15538
|
-
return ENDPOINT_CONFIGS[pattern];
|
|
15539
|
-
}
|
|
15540
|
-
}
|
|
15541
|
-
return { tier: "STANDARD" };
|
|
15542
|
-
}
|
|
15543
16103
|
function bytesToUuid(bytes) {
|
|
15544
16104
|
const hex = [];
|
|
15545
16105
|
for (let i = 0; i < 16; i++) hex.push(bytes[i].toString(16).padStart(2, "0"));
|
|
@@ -15587,10 +16147,11 @@ function markdownToPlainText(text) {
|
|
|
15587
16147
|
result = result.replace(/^[-*_]{3,}$/gm, "");
|
|
15588
16148
|
return result;
|
|
15589
16149
|
}
|
|
15590
|
-
var
|
|
16150
|
+
var UUID_RE2, WAYAI_WORKSPACE_LAYOUT;
|
|
15591
16151
|
var init_dist = __esm({
|
|
15592
16152
|
"../../packages/core/dist/index.js"() {
|
|
15593
16153
|
"use strict";
|
|
16154
|
+
init_chunk_IEWJ5MZH();
|
|
15594
16155
|
init_chunk_SOJMWM63();
|
|
15595
16156
|
init_chunk_WEXVYJ5H();
|
|
15596
16157
|
init_chunk_LZDVDC2S();
|
|
@@ -15615,465 +16176,6 @@ var init_dist = __esm({
|
|
|
15615
16176
|
init_chunk_L5F7ZOUJ();
|
|
15616
16177
|
init_chunk_TS4EYU3W();
|
|
15617
16178
|
init_chunk_R7VSUKKX();
|
|
15618
|
-
ENDPOINT_CONFIGS = {
|
|
15619
|
-
// OAuth callbacks - strict, fail-closed (prevent brute force)
|
|
15620
|
-
"/oauth/callback/*": { tier: "STRICT", failClosed: true },
|
|
15621
|
-
// Auth endpoints - strict, fail-closed
|
|
15622
|
-
"/auth/phone": { tier: "STRICT", failClosed: true },
|
|
15623
|
-
"/auth/mcp/exchange": { tier: "MODERATE", failClosed: true },
|
|
15624
|
-
// Native magic-code login (pre-auth) — STRICT per-IP bounds a single host; WorkOS owns
|
|
15625
|
-
// the per-email lockout / TTL / single-use guarantees.
|
|
15626
|
-
"/api/auth/magic-code/*": { tier: "STRICT", failClosed: true },
|
|
15627
|
-
// Native password login (pre-auth) — same posture as magic-code above. STRICT per-IP bounds a
|
|
15628
|
-
// single host but FAILS OPEN on a binding error (like every IP limit); the `failClosed` flag
|
|
15629
|
-
// here is inert on a pre-auth route (it's only enforced on the per-user branch, which needs an
|
|
15630
|
-
// authenticated caller). The fail-CLOSED brute-force guarantee for the static credential comes
|
|
15631
|
-
// from the route's per-email caps (burst + cumulative hourly), not this flag.
|
|
15632
|
-
"/api/auth/password/*": { tier: "STRICT", failClosed: true },
|
|
15633
|
-
// Every `/api/*` group whose paths carry ids pins `userLimitKey` (gh #4756), held there by
|
|
15634
|
-
// `security/endpoint-invariants.test.ts`.
|
|
15635
|
-
// Billing - moderate, fail-closed with user limits
|
|
15636
|
-
"/api/billing/*": {
|
|
15637
|
-
tier: "MODERATE",
|
|
15638
|
-
userLimit: 100,
|
|
15639
|
-
failClosed: true,
|
|
15640
|
-
skipAuthenticatedUsers: true,
|
|
15641
|
-
userLimitKey: "/api/billing/*"
|
|
15642
|
-
},
|
|
15643
|
-
"/webhooks/stripe": { tier: "MODERATE", failClosed: true },
|
|
15644
|
-
"/webhooks/workos": { tier: "MODERATE", failClosed: true },
|
|
15645
|
-
// API endpoints - standard with user limits
|
|
15646
|
-
// 500 because the web's settings live updates refetch per hub in every open tab, whatever page
|
|
15647
|
-
// it shows (`settingsLiveUpdateStore.ts`): one batch of changes costs about 10 + 4 per agent
|
|
15648
|
-
// whose tools were opened + 2 per loaded resource, so a CLI push and publish (both hubs) with
|
|
15649
|
-
// two tabs open passes 150 in a minute. A multi-file resource upload adds 2–5 per file.
|
|
15650
|
-
"/api/setup/*": {
|
|
15651
|
-
tier: "STANDARD",
|
|
15652
|
-
userLimit: 500,
|
|
15653
|
-
skipAuthenticatedUsers: true,
|
|
15654
|
-
userLimitKey: "/api/setup/*"
|
|
15655
|
-
},
|
|
15656
|
-
// 500 because an eval run's drill-down (`SessionResults.tsx`) fetches its list and then every
|
|
15657
|
-
// LLM turn's record at once: up to 201 requests (`OBSERVABILITY_LIST_CAP`, 200). Sending keeps
|
|
15658
|
-
// the 150 it had when each path held its own budget, on the exact key below.
|
|
15659
|
-
"/api/conversations/*": {
|
|
15660
|
-
tier: "STANDARD",
|
|
15661
|
-
userLimit: 500,
|
|
15662
|
-
skipAuthenticatedUsers: true,
|
|
15663
|
-
userLimitKey: "/api/conversations/*"
|
|
15664
|
-
},
|
|
15665
|
-
"/api/conversations/send-message": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
15666
|
-
"/api/users/*": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
15667
|
-
"/api/analytics/*": {
|
|
15668
|
-
tier: "STANDARD",
|
|
15669
|
-
userLimit: 150,
|
|
15670
|
-
skipAuthenticatedUsers: true,
|
|
15671
|
-
userLimitKey: "/api/analytics/*"
|
|
15672
|
-
},
|
|
15673
|
-
// 500 because `wayai eval-results --eval` reads completed sessions one by one, newest first,
|
|
15674
|
-
// until one holds the eval (up to the 1000 the list returns). Tenant SQL keeps the 150 it had
|
|
15675
|
-
// when each path held its own budget: each query may run 30 s on shared-tenant ClickHouse
|
|
15676
|
-
// after its caller has gone (`sql-validator.ts`, stage 10b).
|
|
15677
|
-
"/api/evals/*": {
|
|
15678
|
-
tier: "STANDARD",
|
|
15679
|
-
userLimit: 500,
|
|
15680
|
-
skipAuthenticatedUsers: true,
|
|
15681
|
-
userLimitKey: "/api/evals/*"
|
|
15682
|
-
},
|
|
15683
|
-
"/api/evals/sql": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
15684
|
-
// An eval run's calls (gh #4418), kept off the end user's call budgets below. Create keeps the
|
|
15685
|
-
// 150 it had: it opens a provider session on the org's own key, billed per second. The routes
|
|
15686
|
-
// on one call take a budget of their own so no eval read can spend it: a refused hang-up
|
|
15687
|
-
// leaves the call running, and billing. 500 because a call suite polls each run's record
|
|
15688
|
-
// every second, about 64 requests a minute per suite.
|
|
15689
|
-
"/api/evals/calls": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
15690
|
-
"/api/evals/calls/*": {
|
|
15691
|
-
tier: "STANDARD",
|
|
15692
|
-
userLimit: 500,
|
|
15693
|
-
skipAuthenticatedUsers: true,
|
|
15694
|
-
userLimitKey: "/api/evals/calls/*"
|
|
15695
|
-
},
|
|
15696
|
-
"/api/files/*": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
15697
|
-
// The org contact book (wayai#4931 PR 2b). A CSV import sends one request per
|
|
15698
|
-
// `CONTACT_IMPORT_BATCH_MAX` rows, so 150 a minute imports 75,000 rows a minute.
|
|
15699
|
-
"/api/contact-book/*": {
|
|
15700
|
-
tier: "STANDARD",
|
|
15701
|
-
userLimit: 150,
|
|
15702
|
-
skipAuthenticatedUsers: true,
|
|
15703
|
-
userLimitKey: "/api/contact-book/*"
|
|
15704
|
-
},
|
|
15705
|
-
// An automation email's one-click unsubscribe (wayai#4931 PR 2d) — public: its token is its one
|
|
15706
|
-
// credential, so this per-IP limit is its one bound, and runs before the route reads anything.
|
|
15707
|
-
// MODERATE (30/min per IP), as the community email's unsubscribe, for the same reason: mail
|
|
15708
|
-
// clients' one-click unsubscribe POSTs it from their own servers, which many recipients' clicks
|
|
15709
|
-
// share. An exact key, so it wins over the wildcard above.
|
|
15710
|
-
[CONTACT_BOOK_EMAIL_UNSUBSCRIBE_PATH]: { tier: "MODERATE" },
|
|
15711
|
-
"/api/automations/*": {
|
|
15712
|
-
tier: "STANDARD",
|
|
15713
|
-
userLimit: 150,
|
|
15714
|
-
skipAuthenticatedUsers: true,
|
|
15715
|
-
userLimitKey: "/api/automations/*"
|
|
15716
|
-
},
|
|
15717
|
-
// A hub's own contact lists (wayai#4931 PR 2c): config edits, at the automations' rate.
|
|
15718
|
-
"/api/hub-contact-lists/*": {
|
|
15719
|
-
tier: "STANDARD",
|
|
15720
|
-
userLimit: 150,
|
|
15721
|
-
skipAuthenticatedUsers: true,
|
|
15722
|
-
userLimitKey: "/api/hub-contact-lists/*"
|
|
15723
|
-
},
|
|
15724
|
-
"/api/message-templates/*": {
|
|
15725
|
-
tier: "STANDARD",
|
|
15726
|
-
userLimit: 150,
|
|
15727
|
-
skipAuthenticatedUsers: true,
|
|
15728
|
-
userLimitKey: "/api/message-templates/*"
|
|
15729
|
-
},
|
|
15730
|
-
"/api/live-updates/*": { tier: "STANDARD", userLimit: 150, skipAuthenticatedUsers: true },
|
|
15731
|
-
"/api/hub-users/*": {
|
|
15732
|
-
tier: "STANDARD",
|
|
15733
|
-
userLimit: 150,
|
|
15734
|
-
skipAuthenticatedUsers: true,
|
|
15735
|
-
userLimitKey: "/api/hub-users/*"
|
|
15736
|
-
},
|
|
15737
|
-
"/api/conversation-state/*": {
|
|
15738
|
-
tier: "STANDARD",
|
|
15739
|
-
userLimit: 150,
|
|
15740
|
-
skipAuthenticatedUsers: true,
|
|
15741
|
-
userLimitKey: "/api/conversation-state/*"
|
|
15742
|
-
},
|
|
15743
|
-
// The authenticated byte route (`routes/files/r2.ts`), mounted after its `authMiddleware` so
|
|
15744
|
-
// the caller is known: each request reads the HubDO its key names, and a hub id never created
|
|
15745
|
-
// still wakes a Durable Object (gh #4747; since gh #4775 T5 one that writes nothing). The key is pinned
|
|
15746
|
-
// because the paths are file keys, which would otherwise each get a fresh budget (and one
|
|
15747
|
-
// over 256 characters would be refused outright, `checkUserLimitNative`).
|
|
15748
|
-
//
|
|
15749
|
-
// 500 a minute because the web chat fetches every attachment of every loaded message at
|
|
15750
|
-
// once, with no list virtualization: up to 50 per 50-message page, 200 per ended
|
|
15751
|
-
// conversation or history entry at one file per message, and 4 per message at most in its
|
|
15752
|
-
// image grid. Repeats come from the browser's HTTP cache (`private, max-age=3600`); mobile
|
|
15753
|
-
// renders through a disk cache and plays audio from `/files/signed`. The budget is shared by
|
|
15754
|
-
// a caller's tabs and devices. The other two byte routes, `/files/signed/*` and
|
|
15755
|
-
// `/files/public/*`, mount no limiter (the reason is at their entries in
|
|
15756
|
-
// `security/endpoint-invariants.test.ts`'s route registry).
|
|
15757
|
-
"/files/auth/*": {
|
|
15758
|
-
tier: "STANDARD",
|
|
15759
|
-
userLimit: 500,
|
|
15760
|
-
skipAuthenticatedUsers: true,
|
|
15761
|
-
userLimitKey: "/files/auth/*"
|
|
15762
|
-
},
|
|
15763
|
-
// Channel webhooks - high throughput (external systems, never authenticated)
|
|
15764
|
-
"/webhooks/whatsapp/*": { tier: "HIGH" },
|
|
15765
|
-
"/webhooks/instagram/*": { tier: "HIGH" },
|
|
15766
|
-
// An automation's webhook deliveries (wayai#4931 D13) — no bearer token: the automation's
|
|
15767
|
-
// secret is the credential, checked against one HubDO read. This per-IP limit runs first, before
|
|
15768
|
-
// anything is read. HIGH, as the channel webhooks: senders are servers, and an integration
|
|
15769
|
-
// platform's egress addresses carry many tenants' deliveries. The per-automation budget is
|
|
15770
|
-
// `AUTOMATION_WEBHOOK_PER_AUTOMATION_LIMIT`, counted after the delivery is verified.
|
|
15771
|
-
[`${AUTOMATION_WEBHOOK_MOUNT}/*`]: { tier: "HIGH" },
|
|
15772
|
-
// Reports (CLI → platform_report triage queue). One per-user budget covers intake
|
|
15773
|
-
// (POST), the content edit (PATCH /:id), the reporter read-back (GET, GET /:id), and the
|
|
15774
|
-
// verification loop (POST /:id/accept, /:id/contest). The limiter is path-based, so a
|
|
15775
|
-
// method-level split (loose reads, strict writes) isn't expressible — GET /:id and PATCH
|
|
15776
|
-
// /:id share a path. So the budget is sized for the read-heavy poll loop (a reporter agent
|
|
15777
|
-
// polling `report get`/`list` while it waits for the fix), at 60/hour. Intake-flooding stays
|
|
15778
|
-
// bounded regardless: identical reports collapse on `fingerprint` (ON CONFLICT DO NOTHING),
|
|
15779
|
-
// so a runaway create loop merges rather than floods. Still STRICT tier + failClosed so a
|
|
15780
|
-
// genuinely runaway distinct-report loop surfaces as 429. The `/*` is load-bearing — an
|
|
15781
|
-
// exact `/api/reports` key would leave the `/:id` paths unmatched (→ unthrottled).
|
|
15782
|
-
// `userLimitKey` is equally load-bearing: without it, middleware falls back to each request's
|
|
15783
|
-
// path and report IDs/actions receive independent counters instead of this shared budget.
|
|
15784
|
-
"/api/reports/*": {
|
|
15785
|
-
tier: "STRICT",
|
|
15786
|
-
userLimit: 60,
|
|
15787
|
-
userWindow: 3600,
|
|
15788
|
-
failClosed: true,
|
|
15789
|
-
skipAuthenticatedUsers: true,
|
|
15790
|
-
userLimitKey: "/api/reports/*"
|
|
15791
|
-
},
|
|
15792
|
-
// Admin report mutations (transition/group/ungroup/edit) — platform-admin
|
|
15793
|
-
// gated at the route, rate-limited here as defense-in-depth. Generous STANDARD
|
|
15794
|
-
// cap so legitimate triage bursts aren't throttled (grouping is one request
|
|
15795
|
-
// even for many members); deliberately NOT failClosed so a Redis blip never
|
|
15796
|
-
// blocks an admin's triage. The bare GET reads share this budget harmlessly.
|
|
15797
|
-
"/api/admin/reports/*": { tier: "STANDARD", userLimit: 150, userLimitKey: "/api/admin/reports/*" },
|
|
15798
|
-
// GitHub webhook (issues.closed → platform_report addressed). MODERATE +
|
|
15799
|
-
// fail-closed; GitHub retries on transient failures so a brief Redis blip
|
|
15800
|
-
// doesn't lose state.
|
|
15801
|
-
"/webhooks/github": { tier: "MODERATE", failClosed: true },
|
|
15802
|
-
// Account deletion — strict on the destructive POST + public undo. The
|
|
15803
|
-
// /api/users/* default tier above ALSO matches these, but `getEndpointConfig`
|
|
15804
|
-
// returns the longest-prefix match, so the more specific entries here win.
|
|
15805
|
-
// `skipAuthenticatedUsers` deliberately omitted: every caller is authenticated,
|
|
15806
|
-
// so including it would make `userLimit` a no-op — the per-user cap IS the gate.
|
|
15807
|
-
"/api/users/me/deletion": { tier: "STRICT", userLimit: 3, userWindow: 3600, failClosed: true },
|
|
15808
|
-
"/api/users/me/deletion/preflight": { tier: "STANDARD", userLimit: 30, failClosed: true },
|
|
15809
|
-
"/api/users/deletion/undo": { tier: "STRICT", failClosed: true },
|
|
15810
|
-
// Production-base destruction.
|
|
15811
|
-
//
|
|
15812
|
-
// Keyed off the CONTRACT constants, not literals: `BASE_DESTRUCTION_CONFIRM_PATH`'s own
|
|
15813
|
-
// docstring promises that the mount, the `AUTH_BYPASS_PATHS` entry, the rate-limit key and
|
|
15814
|
-
// the URL the email embeds move together, and a literal here is exactly how that promise
|
|
15815
|
-
// goes quietly false — leaving an unauthenticated HMAC endpoint on the default tier.
|
|
15816
|
-
//
|
|
15817
|
-
// The emailed GET is PUBLIC, so it gets the IP-tier bound the pre-auth routes get.
|
|
15818
|
-
// `failClosed` is deliberately NOT set: it is only consulted on the per-USER branch, which
|
|
15819
|
-
// needs an authenticated caller, so on a pre-auth route it is inert — the same thing
|
|
15820
|
-
// `/api/auth/magic-code/send` above says about itself. The IP limiter fails OPEN on a
|
|
15821
|
-
// limiter error by design (`packages/runtime/src/rate-limiter.ts`), and claiming otherwise
|
|
15822
|
-
// would tell the next auditor a protection exists that does not.
|
|
15823
|
-
[BASE_DESTRUCTION_CONFIRM_PATH]: { tier: "STRICT" },
|
|
15824
|
-
// The console's read, which every production base's settings pane issues on mount. Without
|
|
15825
|
-
// its own entry it inherits the mount-wide budget below, and an admin clicking through
|
|
15826
|
-
// thirty bases in an hour would be 429'd by a limit sized for irreversible writes.
|
|
15827
|
-
[`${BASE_DESTRUCTION_PREFIX}/destructions`]: { tier: "STANDARD", userLimit: 150 },
|
|
15828
|
-
// The mutating routes: initiate, the authenticated confirm, and cancel. Per user rather than
|
|
15829
|
-
// per IP — all three are authenticated and org-admin gated — and hourly, since a single human
|
|
15830
|
-
// touching thirty production-deletion operations in an hour is already far past normal.
|
|
15831
|
-
// Longest-prefix wins, so the two entries above are not widened by this one. The key is
|
|
15832
|
-
// pinned so those thirty are counted across every base the caller touches (gh #4756).
|
|
15833
|
-
[`${BASE_DESTRUCTION_PREFIX}/*`]: {
|
|
15834
|
-
tier: "STANDARD",
|
|
15835
|
-
userLimit: 30,
|
|
15836
|
-
userWindow: 3600,
|
|
15837
|
-
failClosed: true,
|
|
15838
|
-
userLimitKey: `${BASE_DESTRUCTION_PREFIX}/*`
|
|
15839
|
-
},
|
|
15840
|
-
// Email-invite preview — public, fail-closed. MODERATE (30/min IP) leaves
|
|
15841
|
-
// headroom for the signup-page server-fetch + reloads + prefetches; STRICT
|
|
15842
|
-
// would throttle legitimate users opening the link in multiple tabs.
|
|
15843
|
-
"/api/invites/preview": { tier: "MODERATE", failClosed: true },
|
|
15844
|
-
// "Don't email me again" (gh #4661) — public; one POST per click on its confirm page. STRICT
|
|
15845
|
-
// per IP. No `failClosed`: it is consulted only on the per-user branch, which a pre-auth route
|
|
15846
|
-
// never reaches (see `BASE_DESTRUCTION_CONFIRM_PATH` above).
|
|
15847
|
-
"/api/invites/unsubscribe": { tier: "STRICT" },
|
|
15848
|
-
// Admin DO debug reads — the ONE deliberate deviation from the Data Explorer
|
|
15849
|
-
// catch-all below, and the only reason a per-route key still exists here.
|
|
15850
|
-
// Platform-admin is enforced at the route; this is the independent
|
|
15851
|
-
// defense-in-depth signal so a runaway investigation script surfaces as 429.
|
|
15852
|
-
//
|
|
15853
|
-
// STRICT is kept ON PURPOSE even though the catch-all moved off it: per IP, at 5/min vs
|
|
15854
|
-
// STANDARD's 100/min, walking 10k ids takes ~33h instead of ~100min — a detection window
|
|
15855
|
-
// worth keeping on the routes that return RAW DO ROWS.
|
|
15856
|
-
//
|
|
15857
|
-
// The cost, stated rather than elided: STRICT is one bucket shared with
|
|
15858
|
-
// `/oauth/callback/*` and `/api/auth/magic-code/*`, so a debug session from a
|
|
15859
|
-
// NAT'd office IP can 429 a colleague's login for up to a minute. That trade is
|
|
15860
|
-
// accepted for a rare, deliberate investigation surface — and is exactly why the
|
|
15861
|
-
// browse routes below are NOT here.
|
|
15862
|
-
//
|
|
15863
|
-
// The key is pinned, so a sweep of `/debug/do/hub/<id>/tables` across ids spends one
|
|
15864
|
-
// per-admin budget (gh #4756). 120 an hour: an agent's investigation of a dozen
|
|
15865
|
-
// conversations made 37 debug reads in its busiest hour.
|
|
15866
|
-
"/api/admin/data-explorer/debug/*": {
|
|
15867
|
-
tier: "STRICT",
|
|
15868
|
-
userLimit: 120,
|
|
15869
|
-
userWindow: 3600,
|
|
15870
|
-
failClosed: true,
|
|
15871
|
-
userLimitKey: "/api/admin/data-explorer/debug/*"
|
|
15872
|
-
},
|
|
15873
|
-
// Catch-all for the rest of the Data Explorer (`/search`, `/entity/*`, `/do/*`,
|
|
15874
|
-
// `/kv/*`, the DELETE routes). Added as a catch-all rather than another per-route
|
|
15875
|
-
// key because this budget kept lagging behind routes joining the debug boundary:
|
|
15876
|
-
// with no match the path falls to the default, which sets no `userLimit`, so the
|
|
15877
|
-
// per-user layer never runs at all. `getEndpointConfig` takes the longest match,
|
|
15878
|
-
// so `debug/*` above still wins.
|
|
15879
|
-
//
|
|
15880
|
-
// `/entity/*` deliberately has NO key of its own: it needs precisely this config,
|
|
15881
|
-
// and a duplicate entry is how the two drift apart on the next edit.
|
|
15882
|
-
//
|
|
15883
|
-
// `tier` is deliberately STANDARD, not STRICT. `tier` is the per-IP knob and
|
|
15884
|
-
// STRICT is 5/min keyed on IP ALONE — one bucket shared across every STRICT
|
|
15885
|
-
// endpoint. On these browse routes that breaks the console (open Browse KV and
|
|
15886
|
-
// inspect four values = 6 requests; expand six org rows in the search results =
|
|
15887
|
-
// 6 `/entity/org/:id` calls) and would drain the same IP's budget for
|
|
15888
|
-
// `/oauth/callback/*` and `/api/auth/magic-code/*`, which on a NAT'd office IP
|
|
15889
|
-
// means all admins. The per-user hourly cap is the leg that matters here.
|
|
15890
|
-
//
|
|
15891
|
-
// The canonical key is deliberate: request paths contain DO ids and arbitrary
|
|
15892
|
-
// malformed requests can contain credential material. Keying on the request path
|
|
15893
|
-
// both stores that material in Redis/logs and gives every target a fresh counter.
|
|
15894
|
-
// One stable identity makes every browse request consume the same per-admin
|
|
15895
|
-
// budget without storing request targets.
|
|
15896
|
-
//
|
|
15897
|
-
// 600/hour preserves the original pagination requirement (about 30k rows at
|
|
15898
|
-
// 50/page) while now genuinely bounding id/key enumeration across the surface.
|
|
15899
|
-
"/api/admin/data-explorer/*": {
|
|
15900
|
-
tier: "STANDARD",
|
|
15901
|
-
userLimit: 600,
|
|
15902
|
-
userWindow: 3600,
|
|
15903
|
-
failClosed: true,
|
|
15904
|
-
userLimitKey: "/api/admin/data-explorer/*"
|
|
15905
|
-
},
|
|
15906
|
-
// Admin harness incident-response (harness-agents PR 2.9b) — mass-destroy reaps
|
|
15907
|
-
// EVERY live harness session/token/slot in scope, so it gets a strict budget
|
|
15908
|
-
// (STRICT + a low per-user hourly cap + fail-closed). Platform-admin is enforced
|
|
15909
|
-
// at the route; this is the independent defense-in-depth signal so a runaway
|
|
15910
|
-
// reap loop surfaces as 429. The break-glass disable/enable rides through
|
|
15911
|
-
// `/api/admin/config` (its own STANDARD budget), not this key.
|
|
15912
|
-
"/api/admin/harness/*": {
|
|
15913
|
-
tier: "STRICT",
|
|
15914
|
-
userLimit: 10,
|
|
15915
|
-
userWindow: 3600,
|
|
15916
|
-
failClosed: true,
|
|
15917
|
-
userLimitKey: "/api/admin/harness/*"
|
|
15918
|
-
},
|
|
15919
|
-
// Platform-wide team-orphan repair — a single call can WRITE across every hub of
|
|
15920
|
-
// every org (HubDO purge + UserDO mirror fan-out), so it gets the same strict,
|
|
15921
|
-
// fail-closed budget as the other cross-platform admin write. Exact path (not a
|
|
15922
|
-
// prefix): the sibling `/api/admin/hubs/:hubId/harness` flag flips stay on the
|
|
15923
|
-
// default admin budget. Platform-admin is enforced at the route; this is the
|
|
15924
|
-
// defense-in-depth signal that a repair loop surfaces as 429.
|
|
15925
|
-
"/api/admin/hubs/repair-team-orphans": { tier: "STRICT", userLimit: 10, userWindow: 3600, failClosed: true },
|
|
15926
|
-
// Branching-orphan repair (#4577) — deletes named production rows, and its report mode
|
|
15927
|
-
// fans out over every production hub of every org, so it takes the same strict,
|
|
15928
|
-
// fail-closed budget as the team-orphan repair. Exact path, for the same reason.
|
|
15929
|
-
"/api/admin/hubs/repair-branching-orphans": { tier: "STRICT", userLimit: 10, userWindow: 3600, failClosed: true },
|
|
15930
|
-
// CI/GitOps endpoints - standard with user limits. 500 because `wayai push` makes four requests
|
|
15931
|
-
// (pull, diff, push, pull) and a GitOps job may push every hub at once with one token; when
|
|
15932
|
-
// each path held its own 100, that ran to 100 hubs a minute.
|
|
15933
|
-
"/api/ci/*": { tier: "STANDARD", userLimit: 500, skipAuthenticatedUsers: true, userLimitKey: "/api/ci/*" },
|
|
15934
|
-
// Attested `/v1` proxy onto the Rekor Worker. `userLimitKey` is load-bearing here: the
|
|
15935
|
-
// per-user budget defaults to the full request path, and this surface's paths carry base,
|
|
15936
|
-
// record, and file ids — so without a pinned key every distinct resource would get its own
|
|
15937
|
-
// fresh budget and the per-user limit would bound nothing. One shared budget across the
|
|
15938
|
-
// whole data surface is the intent.
|
|
15939
|
-
[`${DATA_PROXY_PREFIX}/*`]: {
|
|
15940
|
-
tier: "STANDARD",
|
|
15941
|
-
userLimit: 150,
|
|
15942
|
-
skipAuthenticatedUsers: true,
|
|
15943
|
-
userLimitKey: `${DATA_PROXY_PREFIX}/*`
|
|
15944
|
-
},
|
|
15945
|
-
// Voice calls — the end user's call routes (`routes/api/calls`). Every caller is signed
|
|
15946
|
-
// in, so the per-IP tier is skipped for them: Brazilian mobile carriers put many callers
|
|
15947
|
-
// behind one IP, and a shared bucket would refuse one caller for another's traffic.
|
|
15948
|
-
//
|
|
15949
|
-
// Create is the only route that spends: it opens a provider session on the org's own
|
|
15950
|
-
// key, billed per second. So it takes its own exact key and an hourly per-user cap, which
|
|
15951
|
-
// lives on Redis and is fail-closed like the other hourly caps. The exact key wins over
|
|
15952
|
-
// the wildcard below, which also matches `/api/calls` (see `getEndpointConfig`).
|
|
15953
|
-
"/api/calls": {
|
|
15954
|
-
tier: "STANDARD",
|
|
15955
|
-
userLimit: 30,
|
|
15956
|
-
userWindow: 3600,
|
|
15957
|
-
failClosed: true,
|
|
15958
|
-
skipAuthenticatedUsers: true,
|
|
15959
|
-
userLimitKey: "/api/calls"
|
|
15960
|
-
},
|
|
15961
|
-
// The routes on one call: `ready`, hang-up, the delegation report and the status read; and
|
|
15962
|
-
// the opening read (`/api/calls/openings/:language`), one per call, before it exists.
|
|
15963
|
-
// NEVER fail-closed: a refused hang-up leaves the call running, and billing, until a
|
|
15964
|
-
// limit ends it. The 60 s budget runs on the native binding, which fails open. The key is
|
|
15965
|
-
// pinned because the paths carry the call id, which would otherwise give every call a
|
|
15966
|
-
// fresh budget.
|
|
15967
|
-
"/api/calls/*": {
|
|
15968
|
-
tier: "STANDARD",
|
|
15969
|
-
userLimit: 150,
|
|
15970
|
-
skipAuthenticatedUsers: true,
|
|
15971
|
-
userLimitKey: "/api/calls/*"
|
|
15972
|
-
},
|
|
15973
|
-
// Contacts between accounts (gh #4661, `routes/api/contacts`). Every caller is signed in, so
|
|
15974
|
-
// the per-IP tier is skipped. The keys are pinned because the paths carry user and invite
|
|
15975
|
-
// ids, which would otherwise give each id a fresh budget. Blocks take their own, lower
|
|
15976
|
-
// budget (#4661 Limits: blocks are rate-limited). The invites-per-day cap and every list
|
|
15977
|
-
// cap are counted in the caller's UserDO, in the same write they bound, not here.
|
|
15978
|
-
"/api/contacts/*": {
|
|
15979
|
-
tier: "STANDARD",
|
|
15980
|
-
userLimit: 60,
|
|
15981
|
-
skipAuthenticatedUsers: true,
|
|
15982
|
-
userLimitKey: "/api/contacts/*"
|
|
15983
|
-
},
|
|
15984
|
-
"/api/contacts/blocks/*": {
|
|
15985
|
-
tier: "STANDARD",
|
|
15986
|
-
userLimit: 30,
|
|
15987
|
-
skipAuthenticatedUsers: true,
|
|
15988
|
-
userLimitKey: "/api/contacts/blocks/*"
|
|
15989
|
-
},
|
|
15990
|
-
// Person-to-person threads (gh #4661, `routes/api/threads`): the per-user send limit, 60 a
|
|
15991
|
-
// minute on the native binding. Every thread route shares the one budget, reads included:
|
|
15992
|
-
// the paths carry thread and message ids, so the key is pinned, and a pattern here cannot
|
|
15993
|
-
// tell a send from a read of the same path. Like every 60 s budget it fails open on a
|
|
15994
|
-
// binding error, so it bounds sends in the steady state only.
|
|
15995
|
-
"/api/threads/*": {
|
|
15996
|
-
tier: "STANDARD",
|
|
15997
|
-
userLimit: 60,
|
|
15998
|
-
skipAuthenticatedUsers: true,
|
|
15999
|
-
userLimitKey: "/api/threads/*"
|
|
16000
|
-
},
|
|
16001
|
-
// An org admin's erasure of one person's thread messages (gh #4661 owner decision 7): any user
|
|
16002
|
-
// can own a free org, so each request arms that org's alarm, and this caps how fast one person
|
|
16003
|
-
// can ask. The org's own daily cap (`PEOPLE_ORG_ERASURES_PER_DAY`, counted in its OrgDO in the
|
|
16004
|
-
// write it bounds) is the fail-closed bound; this budget, on the native binding, fails open.
|
|
16005
|
-
"/api/threads/org-erasures": {
|
|
16006
|
-
tier: "STANDARD",
|
|
16007
|
-
userLimit: 10,
|
|
16008
|
-
skipAuthenticatedUsers: true,
|
|
16009
|
-
userLimitKey: "/api/threads/org-erasures"
|
|
16010
|
-
},
|
|
16011
|
-
// A person's report of a message, thread or person (`people-reports.ts`): each one written
|
|
16012
|
-
// arms the PlatformDO's alarm and emails the platform admins, so it takes its own, lower
|
|
16013
|
-
// budget. The reporter's daily cap (`PEOPLE_REPORTS_PER_DAY`, counted in the report store in
|
|
16014
|
-
// the write it bounds) is the fail-closed bound; this budget, on the native binding, fails open.
|
|
16015
|
-
"/api/threads/reports": {
|
|
16016
|
-
tier: "STANDARD",
|
|
16017
|
-
userLimit: 10,
|
|
16018
|
-
skipAuthenticatedUsers: true,
|
|
16019
|
-
userLimitKey: "/api/threads/reports"
|
|
16020
|
-
},
|
|
16021
|
-
// Communities (gh #4872, `routes/api/communities`): every signed-in route under the mount (the
|
|
16022
|
-
// preview below excepted) shares one per-user budget, which is also each member's send limit and
|
|
16023
|
-
// bounds the reads that scan a community's rows (a title search, the stats); a community's own
|
|
16024
|
-
// limits (its rate, the daily caps, slow mode) are counted in its CommunityDO, in the write they
|
|
16025
|
-
// bound. Every caller is signed in, so the per-IP tier is skipped; the key is pinned because the
|
|
16026
|
-
// paths carry community, room and message ids.
|
|
16027
|
-
"/api/communities/*": {
|
|
16028
|
-
tier: "STANDARD",
|
|
16029
|
-
userLimit: 60,
|
|
16030
|
-
skipAuthenticatedUsers: true,
|
|
16031
|
-
userLimitKey: "/api/communities/*"
|
|
16032
|
-
},
|
|
16033
|
-
// The join page's preview of a link — public, fetched from the visitor's browser, so this
|
|
16034
|
-
// per-IP limit is its one bound and runs before the route reads anything (#4872
|
|
16035
|
-
// owner-delegated decision 9). MODERATE (30/min per IP) leaves room for reloads and several
|
|
16036
|
-
// tabs. An exact key, so it wins over the wildcard above.
|
|
16037
|
-
"/api/communities/join-preview": { tier: "MODERATE" },
|
|
16038
|
-
// An announcement email's unsubscribe (#4872 PR 8a) — public: its token is its one credential, so
|
|
16039
|
-
// this per-IP limit is its one bound, and runs before the route reads anything. MODERATE (30/min
|
|
16040
|
-
// per IP), not STRICT: mail clients' one-click unsubscribe POSTs it from their own servers, which
|
|
16041
|
-
// many members' clicks share, and STRICT's 5/min bucket is shared with sign-in. An exact key, so
|
|
16042
|
-
// it wins over the wildcard above.
|
|
16043
|
-
[COMMUNITY_EMAIL_UNSUBSCRIBE_PATH]: { tier: "MODERATE" },
|
|
16044
|
-
// The WayAI Guide's help entry (wayai#4948, `routes/api/guide.ts`): one POST per click by a viewer
|
|
16045
|
-
// the snapshot does not show enrolled, which reads the person's row on the Guide hub and writes only
|
|
16046
|
-
// what is missing (an enrolled viewer's click opens the hub without a request). Every caller is
|
|
16047
|
-
// signed in, so the per-IP tier is skipped; 10 a minute per person on the native binding (fails
|
|
16048
|
-
// open, like every 60 s budget) bounds what a looping client costs the one Guide HubDO.
|
|
16049
|
-
"/api/guide/access": {
|
|
16050
|
-
tier: "STANDARD",
|
|
16051
|
-
userLimit: 10,
|
|
16052
|
-
skipAuthenticatedUsers: true,
|
|
16053
|
-
userLimitKey: "/api/guide/access"
|
|
16054
|
-
},
|
|
16055
|
-
// App channel - high throughput with user limits
|
|
16056
|
-
"/channels/app/*": { tier: "HIGH", userLimit: 500, skipAuthenticatedUsers: true },
|
|
16057
|
-
// API channel (api-channel-connector) - machine-to-machine, high throughput
|
|
16058
|
-
"/channels/api/*": { tier: "HIGH", userLimit: 500, skipAuthenticatedUsers: true },
|
|
16059
|
-
// System/scheduled - high throughput (internal)
|
|
16060
|
-
"/channels/system/*": { tier: "HIGH", skipAuthenticatedUsers: true },
|
|
16061
|
-
"/scheduled/*": { tier: "HIGH", skipAuthenticatedUsers: true },
|
|
16062
|
-
// WebSocket - standard with user limit to prevent connection spam
|
|
16063
|
-
"/live-updates/ws": { tier: "STANDARD", userLimit: 100, failClosed: true },
|
|
16064
|
-
// A community's live socket (gh #4872 PR 5, `GET /communities/:communityId/live`): outside
|
|
16065
|
-
// `/api/*`, with no bearer token, so its per-IP tier is its one bound before the CommunityDO
|
|
16066
|
-
// checks the ticket. STANDARD, as `/live-updates/ws`: a client opens one per community it shows
|
|
16067
|
-
// and reconnects within the hour.
|
|
16068
|
-
"/communities/*": { tier: "STANDARD" },
|
|
16069
|
-
// WS ticket mint - per-user 60/min accommodates aggressive reconnect patterns
|
|
16070
|
-
// without opening abuse vectors; fail-closed so Redis outages don't let
|
|
16071
|
-
// runaway clients mint unbounded tickets.
|
|
16072
|
-
"/api/auth/ws-ticket": { tier: "MODERATE", userLimit: 60, failClosed: true, skipAuthenticatedUsers: true },
|
|
16073
|
-
"/api/auth/session-check": { tier: "STANDARD", userLimit: 120, skipAuthenticatedUsers: true },
|
|
16074
|
-
"/api/auth/logout": { tier: "MODERATE", userLimit: 10, failClosed: true, skipAuthenticatedUsers: true }
|
|
16075
|
-
};
|
|
16076
|
-
EXCLUDED_PATHS = /* @__PURE__ */ new Set(["/health"]);
|
|
16077
16179
|
UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
16078
16180
|
WAYAI_WORKSPACE_LAYOUT = {
|
|
16079
16181
|
/** Product-namespaced root dir at the git root. */
|
|
@@ -20412,6 +20514,13 @@ function parseJourneyYaml(filePath2, relPath, hubFolder, bytesByHash) {
|
|
|
20412
20514
|
}
|
|
20413
20515
|
const variables = parseVariablesMap(data.variables, `Journey "${journeyName}" in ${relPath}`);
|
|
20414
20516
|
if (variables) journeyEntry.variables = variables;
|
|
20517
|
+
const customer = parseFixtureField(
|
|
20518
|
+
data.customer_contact_id,
|
|
20519
|
+
`Journey "${journeyName}" in ${relPath}`,
|
|
20520
|
+
"customer_contact_id",
|
|
20521
|
+
"the customer's contact id"
|
|
20522
|
+
);
|
|
20523
|
+
if (customer) journeyEntry.customer_contact_id = customer;
|
|
20415
20524
|
const fixture = parseFixtureField(data.fixture, `Journey "${journeyName}" in ${relPath}`, "fixture");
|
|
20416
20525
|
if (fixture) journeyEntry.fixture = fixture;
|
|
20417
20526
|
const targetBase = parseFixtureField(data.target_base, `Journey "${journeyName}" in ${relPath}`, "target_base");
|
|
@@ -20794,6 +20903,9 @@ function buildJourneyYamlObject(journeyEntry, slug) {
|
|
|
20794
20903
|
if (typeof journeyEntry.target_base === "string" && journeyEntry.target_base.trim()) {
|
|
20795
20904
|
out.target_base = journeyEntry.target_base.trim();
|
|
20796
20905
|
}
|
|
20906
|
+
if (typeof journeyEntry.customer_contact_id === "string" && journeyEntry.customer_contact_id) {
|
|
20907
|
+
out.customer_contact_id = journeyEntry.customer_contact_id;
|
|
20908
|
+
}
|
|
20797
20909
|
if (journeyEntry.variables && Object.keys(journeyEntry.variables).length > 0) {
|
|
20798
20910
|
out.variables = journeyEntry.variables;
|
|
20799
20911
|
}
|
|
@@ -33216,7 +33328,7 @@ token (\`wayai login --token\`, or WAYAI_TOKEN) is refused.
|
|
|
33216
33328
|
var init_rekor_toolset = __esm({
|
|
33217
33329
|
"../../packages/core/dist/contracts/rekor-toolset.js"() {
|
|
33218
33330
|
"use strict";
|
|
33219
|
-
|
|
33331
|
+
init_chunk_4MRBBR5L();
|
|
33220
33332
|
init_chunk_BEOHBREG();
|
|
33221
33333
|
init_chunk_DHPUPI4Y();
|
|
33222
33334
|
}
|
|
@@ -34181,7 +34293,10 @@ var init_plan = __esm({
|
|
|
34181
34293
|
|
|
34182
34294
|
// src/knowledge/migrate.ts
|
|
34183
34295
|
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
34184
|
-
|
|
34296
|
+
function refreshPreviewCommand(orgId, base) {
|
|
34297
|
+
return `wayai knowledge refresh-preview --org ${orgId} --base ${safe(base)}`;
|
|
34298
|
+
}
|
|
34299
|
+
async function resolveBases(kb, baseId2, orgId) {
|
|
34185
34300
|
const base = await kb.getBase(baseId2);
|
|
34186
34301
|
if (!base || base.deleted) throw expected(`Base "${safe(baseId2)}" not found in this organization.`);
|
|
34187
34302
|
if (base.environment === "preview") {
|
|
@@ -34201,7 +34316,7 @@ async function resolveBases(kb, baseId2) {
|
|
|
34201
34316
|
}
|
|
34202
34317
|
if (previews.length !== 1) {
|
|
34203
34318
|
throw expected(
|
|
34204
|
-
previews.length === 0 ? `Base "${safe(baseId2)}" has no preview
|
|
34319
|
+
previews.length === 0 ? `Base "${safe(baseId2)}" has no preview. Create one with \`wayai bases create-preview ${safe(baseId2)} --name <name>\`, then copy production's files into it with \`${refreshPreviewCommand(orgId, baseId2)}\`.` : `Base "${safe(baseId2)}" has ${previews.length} previews (${previews.map(safe).join(", ")}). Pass the one to use as --base.`
|
|
34205
34320
|
);
|
|
34206
34321
|
}
|
|
34207
34322
|
return { production: base.id, preview: previews[0] };
|
|
@@ -34307,7 +34422,7 @@ function printNotes(io, notes) {
|
|
|
34307
34422
|
}
|
|
34308
34423
|
}
|
|
34309
34424
|
async function migrateKnowledge(reader, kb, opts, io) {
|
|
34310
|
-
const { production: productionId, preview } = await resolveBases(kb, opts.base);
|
|
34425
|
+
const { production: productionId, preview } = await resolveBases(kb, opts.base, opts.orgId);
|
|
34311
34426
|
const hubs = opts.hubId === void 0 ? [] : await reader.listHubs();
|
|
34312
34427
|
const hub = opts.hubId === void 0 ? void 0 : productionHub(hubs, opts.hubId);
|
|
34313
34428
|
const production = await Production.read(kb, productionId);
|
|
@@ -34446,7 +34561,7 @@ Dry run: nothing written (${writes === 0 ? "nothing to write" : `${writes} write
|
|
|
34446
34561
|
for (const file of creates) await kb.putFile(productionId, file, await bodyBytes(reader, file));
|
|
34447
34562
|
io.log(
|
|
34448
34563
|
`
|
|
34449
|
-
Done: ${toWrite.length} file type(s) written, ${toPromote.length} promoted, ${creates.length} file(s) written. The preview holds none of these files
|
|
34564
|
+
Done: ${toWrite.length} file type(s) written, ${toPromote.length} promoted, ${creates.length} file(s) written. The preview holds none of these files: copy them with \`${refreshPreviewCommand(opts.orgId, opts.base)}\`.`
|
|
34450
34565
|
);
|
|
34451
34566
|
return { ...result, written: true };
|
|
34452
34567
|
}
|
|
@@ -34616,6 +34731,9 @@ function knowledgeBase(client) {
|
|
|
34616
34731
|
}
|
|
34617
34732
|
throw expected(`The listing of file type "${fileType}" on base "${base}" did not end after ${MAX_LIST_PAGES} pages.`);
|
|
34618
34733
|
},
|
|
34734
|
+
// `requestRaw`: the page is read whole, `{ data: … }`, as WayAI's copy of its answer declares it
|
|
34735
|
+
// (`@wayai/core/contracts` `rekorFileSnapshotAnswerSchema`).
|
|
34736
|
+
snapshotPage: (preview, cursor) => pacer.run(() => client.requestRaw("POST", `/v1/${baseSeg(preview)}/files/snapshot`, cursor === void 0 ? {} : { cursor })),
|
|
34619
34737
|
async putFile(base, file, bytes) {
|
|
34620
34738
|
await pacer.run(
|
|
34621
34739
|
() => client.upload(
|
|
@@ -34648,6 +34766,37 @@ var init_remote = __esm({
|
|
|
34648
34766
|
}
|
|
34649
34767
|
});
|
|
34650
34768
|
|
|
34769
|
+
// src/knowledge/refresh-preview.ts
|
|
34770
|
+
async function refreshPreviewKnowledge(kb, opts, io) {
|
|
34771
|
+
const { production, preview } = await resolveBases(kb, opts.base, opts.orgId);
|
|
34772
|
+
const question = `Make the files of preview "${sanitizeTerminalText(preview)}" equal production "${sanitizeTerminalText(production)}"'s? Files the preview changed are replaced, and files only it holds are removed.`;
|
|
34773
|
+
if (!opts.yes && !await io.confirm(question)) {
|
|
34774
|
+
io.log("Cancelled.");
|
|
34775
|
+
return null;
|
|
34776
|
+
}
|
|
34777
|
+
io.log(`Copying the files of production base "${sanitizeTerminalText(production)}" into its preview "${sanitizeTerminalText(preview)}".`);
|
|
34778
|
+
const run = await runFileSnapshot({
|
|
34779
|
+
sendPage: (cursor) => kb.snapshotPage(preview, cursor),
|
|
34780
|
+
// The data product refuses a page whose preview file changed while it was being copied, and
|
|
34781
|
+
// asks for the same cursor again.
|
|
34782
|
+
isConflict: (error) => error instanceof ApiError && error.status === FILE_SNAPSHOT_CONFLICT_STATUS,
|
|
34783
|
+
onPage: (page, n) => io.log(` Page ${n}: ${page.written} written, ${page.removed} removed, ${page.unchanged} unchanged.`)
|
|
34784
|
+
});
|
|
34785
|
+
io.log(
|
|
34786
|
+
!fileSnapshotChanged(run) ? `Nothing changed in ${run.pages} page(s): the preview's ${run.unchanged} file(s) already equal production's.` : `Done in ${run.pages} page(s): ${run.written} written, ${run.removed} removed, ${run.unchanged} unchanged. The preview's files now equal production's.`
|
|
34787
|
+
);
|
|
34788
|
+
return run;
|
|
34789
|
+
}
|
|
34790
|
+
var init_refresh_preview = __esm({
|
|
34791
|
+
"src/knowledge/refresh-preview.ts"() {
|
|
34792
|
+
"use strict";
|
|
34793
|
+
init_contracts();
|
|
34794
|
+
init_api_client();
|
|
34795
|
+
init_terminal_output();
|
|
34796
|
+
init_migrate2();
|
|
34797
|
+
}
|
|
34798
|
+
});
|
|
34799
|
+
|
|
34651
34800
|
// src/commands/knowledge.ts
|
|
34652
34801
|
var knowledge_exports = {};
|
|
34653
34802
|
__export(knowledge_exports, {
|
|
@@ -34669,15 +34818,23 @@ function readArgs(args2) {
|
|
|
34669
34818
|
function parseKnowledgeArgs(args2) {
|
|
34670
34819
|
const { values, positionals } = readArgs(args2);
|
|
34671
34820
|
const [verb, ...extra] = positionals;
|
|
34672
|
-
if (verb !== "migrate"
|
|
34821
|
+
if (verb !== "migrate" && verb !== "refresh-preview") {
|
|
34822
|
+
throw usageError3(verb === void 0 ? "No subcommand given." : `Unknown subcommand ${JSON.stringify(verb)}.`);
|
|
34823
|
+
}
|
|
34673
34824
|
if (extra.length > 0) throw usageError3(`Unexpected argument ${JSON.stringify(extra[0])}.`);
|
|
34674
34825
|
if (!values.org) throw usageError3("--org <org_id> is required.");
|
|
34675
34826
|
if (!UUID_RE3.test(values.org)) throw usageError3(`Invalid --org ${JSON.stringify(values.org)}: expected an organization id (a UUID).`);
|
|
34676
34827
|
if (!values.base) throw usageError3("--base <knowledge base> is required.");
|
|
34828
|
+
if (verb === "refresh-preview") {
|
|
34829
|
+
if (values.hub !== void 0) throw usageError3("--hub is migrate's: a refresh copies the whole knowledge base.");
|
|
34830
|
+
if (values["dry-run"] !== void 0) throw usageError3("--dry-run is migrate's: a refresh has none.");
|
|
34831
|
+
return { verb, orgId: values.org, base: values.base, yes: values.yes === true };
|
|
34832
|
+
}
|
|
34677
34833
|
if (values.hub !== void 0 && !UUID_RE3.test(values.hub)) {
|
|
34678
34834
|
throw usageError3(`Invalid --hub ${JSON.stringify(values.hub)}: expected a hub id (a UUID).`);
|
|
34679
34835
|
}
|
|
34680
34836
|
return {
|
|
34837
|
+
verb,
|
|
34681
34838
|
orgId: values.org,
|
|
34682
34839
|
base: values.base,
|
|
34683
34840
|
hubId: values.hub,
|
|
@@ -34691,6 +34848,12 @@ async function knowledgeCommand(args2) {
|
|
|
34691
34848
|
return;
|
|
34692
34849
|
}
|
|
34693
34850
|
const options = parseKnowledgeArgs(args2);
|
|
34851
|
+
if (options.verb === "refresh-preview") {
|
|
34852
|
+
if (!options.yes && !isInteractive()) throw usageError3("It asks before it copies; pass -y when not running in a terminal.");
|
|
34853
|
+
const data2 = await createDataClient(options.orgId, { refreshLogin: true });
|
|
34854
|
+
await refreshPreviewKnowledge(knowledgeBase(data2), options, { log: (line) => console.log(line), confirm });
|
|
34855
|
+
return;
|
|
34856
|
+
}
|
|
34694
34857
|
if (!options.dryRun && !options.yes && !isInteractive()) {
|
|
34695
34858
|
throw usageError3("It asks before it writes; pass -y when not running in a terminal, or --dry-run.");
|
|
34696
34859
|
}
|
|
@@ -34714,11 +34877,15 @@ var init_knowledge = __esm({
|
|
|
34714
34877
|
init_helpers();
|
|
34715
34878
|
init_migrate2();
|
|
34716
34879
|
init_remote();
|
|
34880
|
+
init_refresh_preview();
|
|
34717
34881
|
USAGE4 = `
|
|
34718
|
-
wayai knowledge
|
|
34882
|
+
wayai knowledge \u2014 the organization's knowledge base
|
|
34719
34883
|
|
|
34720
34884
|
Usage:
|
|
34721
34885
|
wayai knowledge migrate --org <org_id> --base <knowledge base> [--hub <hub_id>] [--dry-run] [-y]
|
|
34886
|
+
wayai knowledge refresh-preview --org <org_id> --base <knowledge base> [-y]
|
|
34887
|
+
|
|
34888
|
+
migrate: move knowledge resources into the organization's knowledge base.
|
|
34722
34889
|
|
|
34723
34890
|
Without --hub, it moves the organization's knowledge resources; with --hub, that hub's own
|
|
34724
34891
|
(read from its production hub), once the organization's have moved. Each resource becomes a
|
|
@@ -34729,6 +34896,15 @@ Usage:
|
|
|
34729
34896
|
written in production.
|
|
34730
34897
|
--dry-run List what it would write, and every reason it would stop, without writing.
|
|
34731
34898
|
-y, --yes Skip the question before writing.
|
|
34899
|
+
|
|
34900
|
+
refresh-preview: make the knowledge base's preview hold its production base's files \u2014 after
|
|
34901
|
+
creating the preview, and whenever it should be current again. Files the preview changed are
|
|
34902
|
+
replaced and files only it holds are removed; nothing else on it changes, and production is
|
|
34903
|
+
only read. It prints what each page copied, removed and left alone; a re-run of a current
|
|
34904
|
+
preview changes nothing.
|
|
34905
|
+
|
|
34906
|
+
--base The production knowledge base, or its preview.
|
|
34907
|
+
-y, --yes Skip the question before copying.
|
|
34732
34908
|
`;
|
|
34733
34909
|
OPTIONS = {
|
|
34734
34910
|
org: { type: "string" },
|
|
@@ -41259,6 +41435,7 @@ Commands:
|
|
|
41259
41435
|
communities An organization's communities: list, join link (get/rotate/off), stats, access list (list/add/remove) \u2014 \`wayai communities --help\`
|
|
41260
41436
|
contacts Link a contact to its customer record in a base, or unlink it (browser sign-in only) \u2014 \`wayai contacts --help\`
|
|
41261
41437
|
knowledge migrate Move an organization's knowledge resources, then a hub's (--hub), into its knowledge base as file types and files \u2014 \`wayai knowledge --help\`
|
|
41438
|
+
knowledge refresh-preview Make the knowledge base's preview hold its production base's files \u2014 \`wayai knowledge --help\`
|
|
41262
41439
|
report create Report a platform bug (submits to the triage queue)
|
|
41263
41440
|
report edit Amend your own pending report (title/description/error/steps/context)
|
|
41264
41441
|
update Update CLI to the latest version
|