@tangle-network/hub-sdk 0.30.6 → 0.30.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -111,32 +111,6 @@ var HubAppsClient = class {
111
111
  }
112
112
  };
113
113
  //#endregion
114
- //#region src/inbound-webhooks.ts
115
- var HubInboundWebhooksClient = class {
116
- constructor(request) {
117
- this.request = request;
118
- }
119
- async create(connectionId, options = {}) {
120
- return this.request(this.path(connectionId), jsonInit("POST", options));
121
- }
122
- async get(connectionId) {
123
- return this.request(this.path(connectionId), { method: "GET" });
124
- }
125
- /** Add one owned Linq WhatsApp number without rotating the webhook secret. */
126
- async ensurePhoneNumber(connectionId, phoneNumberId) {
127
- if (!phoneNumberId || phoneNumberId.length > 128 || !/^[\x21-\x7e]+$/.test(phoneNumberId)) throw new Error("A bounded phone-number id is required");
128
- return this.request(`${this.path(connectionId)}/phone-numbers/ensure`, jsonInit("POST", { phoneNumberId }));
129
- }
130
- /** Remove event subscriptions first. This does not silently rotate a live URL. */
131
- async delete(connectionId) {
132
- return this.request(this.path(connectionId), { method: "DELETE" });
133
- }
134
- path(connectionId) {
135
- if (!connectionId || connectionId.length > 256) throw new Error("A bounded connection id is required");
136
- return `/v1/hub/connections/${encodeURIComponent(connectionId)}/inbound-webhook`;
137
- }
138
- };
139
- //#endregion
140
114
  //#region src/knowledge-management.ts
141
115
  var HubKnowledgeManagementClient = class {
142
116
  constructor(request) {
@@ -345,7 +319,7 @@ function redactHubValue(value) {
345
319
  return value;
346
320
  }
347
321
  //#endregion
348
- //#region src/client.ts
322
+ //#region src/transport.ts
349
323
  var HubSdkError = class extends Error {
350
324
  code;
351
325
  details;
@@ -390,179 +364,6 @@ function isHubSuccessEnvelope(value) {
390
364
  function isRecord$1(value) {
391
365
  return typeof value === "object" && value !== null;
392
366
  }
393
- function isDispatchCurrentData(value) {
394
- return isRecord$1(value) && typeof value.current === "boolean";
395
- }
396
- /** Read a wire field as a string, or "" when the server sent anything else.
397
- * `raw` is that field straight off the parsed payload, before any check. */
398
- function optString(raw) {
399
- return typeof raw === "string" ? raw : "";
400
- }
401
- /** An agent iteration's terminal status (`iteration.ended`) — only ever
402
- * succeeded/failed; an iteration is never guard-skipped. */
403
- function isIterationStatus(value) {
404
- return value === "succeeded" || value === "failed";
405
- }
406
- /** An action's terminal status on `action.finished`. Beyond succeeded/failed a
407
- * guarded action can end `skipped` (its `if` resolved false, so it never ran) —
408
- * accepted here so a skipped event is surfaced, not dropped as malformed. */
409
- function isActionFinishedStatus(value) {
410
- return value === "succeeded" || value === "failed" || value === "skipped";
411
- }
412
- /** Terminal status of a whole run's `run.done` event. A run can end `cancelled`
413
- * (a user cancel) — a value neither the action nor iteration status checkers
414
- * accept — so `run.done` uses this rather than them, which would drop a
415
- * cancelled terminal event and hang the stream. */
416
- function isRunDoneStatus(value) {
417
- return value === "succeeded" || value === "failed" || value === "cancelled";
418
- }
419
- /**
420
- * Map one raw SSE event (its `event:` name + parsed JSON `data:`) to a typed
421
- * run-stream event, validating the fields each variant declares rather than
422
- * blindly casting the wire payload. A frame that doesn't match its event's
423
- * shape (or an unknown event name) returns null so the consumer skips it — a
424
- * malformed `token` can never reach a consumer as `{ delta: undefined }`, and
425
- * new server event kinds don't break older clients.
426
- */
427
- function toRunStreamEvent(eventType, raw) {
428
- if (eventType === "ping") return { type: "ping" };
429
- if (!isRecord$1(raw)) return null;
430
- switch (eventType) {
431
- case "snapshot": return typeof raw.id === "string" && typeof raw.workflowId === "string" && typeof raw.status === "string" && Array.isArray(raw.actionResults) ? {
432
- type: "snapshot",
433
- run: raw
434
- } : null;
435
- case "token": return typeof raw.delta === "string" && typeof raw.actionIndex === "number" ? {
436
- type: "token",
437
- actionIndex: raw.actionIndex,
438
- delta: raw.delta,
439
- at: optString(raw.at)
440
- } : null;
441
- case "run.done": return isRunDoneStatus(raw.status) ? {
442
- type: "run.done",
443
- status: raw.status,
444
- error: typeof raw.error === "string" ? raw.error : null,
445
- ...typeof raw.at === "string" ? { at: raw.at } : {}
446
- } : null;
447
- case "run.waiting": return typeof raw.decisionId === "string" && raw.decisionId.length > 0 ? {
448
- type: "run.waiting",
449
- decisionId: raw.decisionId,
450
- ...typeof raw.at === "string" ? { at: raw.at } : {}
451
- } : null;
452
- case "action.started": return typeof raw.index === "number" && typeof raw.kind === "string" && typeof raw.at === "string" ? {
453
- type: "action.started",
454
- index: raw.index,
455
- kind: raw.kind,
456
- at: raw.at
457
- } : null;
458
- case "action.finished": return typeof raw.index === "number" && typeof raw.kind === "string" && isActionFinishedStatus(raw.status) && typeof raw.at === "string" ? {
459
- type: "action.finished",
460
- index: raw.index,
461
- kind: raw.kind,
462
- status: raw.status,
463
- at: raw.at,
464
- ...typeof raw.costUsd === "number" ? { costUsd: raw.costUsd } : {},
465
- ...typeof raw.error === "string" ? { error: raw.error } : {}
466
- } : null;
467
- case "iteration.started": return typeof raw.actionIndex === "number" && typeof raw.iterationIndex === "number" && typeof raw.at === "string" ? {
468
- type: "iteration.started",
469
- actionIndex: raw.actionIndex,
470
- iterationIndex: raw.iterationIndex,
471
- at: raw.at,
472
- ...typeof raw.model === "string" ? { model: raw.model } : {}
473
- } : null;
474
- case "iteration.ended": return typeof raw.actionIndex === "number" && typeof raw.iterationIndex === "number" && isIterationStatus(raw.status) && typeof raw.at === "string" ? {
475
- type: "iteration.ended",
476
- actionIndex: raw.actionIndex,
477
- iterationIndex: raw.iterationIndex,
478
- status: raw.status,
479
- at: raw.at,
480
- ...typeof raw.outputPreview === "string" ? { outputPreview: raw.outputPreview } : {},
481
- ...typeof raw.inputTokens === "number" ? { inputTokens: raw.inputTokens } : {},
482
- ...typeof raw.outputTokens === "number" ? { outputTokens: raw.outputTokens } : {},
483
- ...typeof raw.costUsd === "number" ? { costUsd: raw.costUsd } : {}
484
- } : null;
485
- default: return null;
486
- }
487
- }
488
- /** True for an aborted/timed-out fetch (or any abort). fetch rejects with the
489
- * signal's reason — a DOMException named `AbortError` (caller) or `TimeoutError`
490
- * (AbortSignal.timeout). */
491
- function isAbortError(cause) {
492
- return cause instanceof Error && (cause.name === "AbortError" || cause.name === "TimeoutError");
493
- }
494
- /** A signal that aborts when the caller's signal aborts OR `timeoutMs` elapses.
495
- * Returns undefined when neither bound applies (no per-request abort needed). */
496
- function combineAbortSignals(caller, timeoutMs) {
497
- const signals = [];
498
- if (caller) signals.push(caller);
499
- if (timeoutMs !== null) signals.push(AbortSignal.timeout(timeoutMs));
500
- if (signals.length === 0) return void 0;
501
- return signals.length === 1 ? signals[0] : AbortSignal.any(signals);
502
- }
503
- /** Resolve after `ms`, or reject if `signal` aborts first. Clears its timer on
504
- * abort so a cancelled wait leaves nothing pending. */
505
- function delay(ms, signal) {
506
- return new Promise((resolve, reject) => {
507
- if (signal?.aborted) {
508
- reject(signal.reason ?? /* @__PURE__ */ new Error("The operation was aborted"));
509
- return;
510
- }
511
- const onAbort = () => {
512
- clearTimeout(timer);
513
- reject(signal?.reason ?? /* @__PURE__ */ new Error("The operation was aborted"));
514
- };
515
- const timer = setTimeout(() => {
516
- signal?.removeEventListener("abort", onAbort);
517
- resolve();
518
- }, ms);
519
- signal?.addEventListener("abort", onAbort, { once: true });
520
- });
521
- }
522
- const HUB_URL_ENV_VAR = "TANGLE_HUB_URL";
523
- const HUB_API_KEY_ENV_VAR = "TANGLE_API_KEY";
524
- const HUB_CAPABILITY_TOKEN_ENV_VAR = "TANGLE_HUB_CAPABILITY_TOKEN";
525
- function resolveHubBaseUrl(env = readProcessEnv()) {
526
- const raw = env[HUB_URL_ENV_VAR];
527
- if (raw === void 0 || raw.trim() === "") throw new HubSdkError({
528
- code: "HUB_CONFIG_MISSING",
529
- message: `${HUB_URL_ENV_VAR} is required`,
530
- details: { envVar: HUB_URL_ENV_VAR }
531
- });
532
- const trimmed = raw.trim();
533
- try {
534
- new URL(trimmed);
535
- } catch {
536
- throw new HubSdkError({
537
- code: "HUB_CONFIG_INVALID",
538
- message: `${HUB_URL_ENV_VAR} is not a valid URL`,
539
- details: { envVar: HUB_URL_ENV_VAR }
540
- });
541
- }
542
- return trimmed.replace(/\/+$/, "").replace(/\/v1\/hub$/, "");
543
- }
544
- function resolveHubAuth(env = readProcessEnv()) {
545
- const present = [nonEmpty(env[HUB_API_KEY_ENV_VAR]), nonEmpty(env[HUB_CAPABILITY_TOKEN_ENV_VAR])].filter((value) => value !== void 0);
546
- if (present.length === 0) throw new HubSdkError({
547
- code: "HUB_CONFIG_MISSING",
548
- message: `One of ${HUB_API_KEY_ENV_VAR} or ${HUB_CAPABILITY_TOKEN_ENV_VAR} is required`,
549
- details: { envVars: [HUB_API_KEY_ENV_VAR, HUB_CAPABILITY_TOKEN_ENV_VAR] }
550
- });
551
- if (present.length > 1) throw new HubSdkError({
552
- code: "HUB_CONFIG_INVALID",
553
- message: `Set exactly one of ${HUB_API_KEY_ENV_VAR} or ${HUB_CAPABILITY_TOKEN_ENV_VAR}, not both`,
554
- details: { envVars: [HUB_API_KEY_ENV_VAR, HUB_CAPABILITY_TOKEN_ENV_VAR] }
555
- });
556
- return present[0];
557
- }
558
- function nonEmpty(value) {
559
- if (value === void 0) return void 0;
560
- const trimmed = value.trim();
561
- return trimmed.length === 0 ? void 0 : trimmed;
562
- }
563
- function readProcessEnv() {
564
- return globalThis.process?.env ?? {};
565
- }
566
367
  var HubTransport = class {
567
368
  baseUrl;
568
369
  fetch;
@@ -623,135 +424,68 @@ var HubTransport = class {
623
424
  return mergedHeaders;
624
425
  }
625
426
  };
626
- var HubClient = class HubClient extends HubTransport {
627
- knowledge;
628
- allowances;
629
- apps;
630
- connections;
631
- channels;
632
- numbers;
633
- eventSubscriptions;
634
- permissions;
635
- tokens;
636
- tools;
637
- approvals;
638
- audit;
639
- githubApp;
640
- workflows;
641
- lines;
642
- constructor(options) {
643
- super(options);
644
- this.knowledge = new HubKnowledgeClient((path, init) => this.request(path, init));
645
- this.allowances = new HubAllowancesClient((path, init) => this.request(path, init));
646
- this.apps = new HubAppsClient((path, init) => this.request(path, init));
647
- this.numbers = new HubManagedNumbersClient((path, init) => this.request(path, init));
648
- this.connections = new HubConnectionsClient((path, init) => this.request(path, init));
649
- this.channels = new HubChannelsClient((path, init) => this.request(path, init));
650
- this.eventSubscriptions = new HubEventSubscriptionsClient((path, init) => this.request(path, init));
651
- this.permissions = new HubPermissionsClient((path, init) => this.request(path, init));
652
- this.tokens = new HubTokensClient((path, init) => this.request(path, init));
653
- this.tools = new HubToolsClient((path, init) => this.request(path, init));
654
- this.approvals = new HubApprovalsClient((path, init) => this.request(path, init));
655
- this.audit = new HubAuditClient((path, init) => this.request(path, init));
656
- this.githubApp = new HubGithubAppClient((path, init) => this.request(path, init));
657
- this.workflows = new HubWorkflowsClient((path, init) => this.request(path, init), (path, init) => this.stream(path, init), (path, init) => this.requestBytes(path, init));
658
- this.lines = new HubLinesClient((path, init) => this.request(path, init), (yaml) => this.workflows.create(yaml), (path, init, callbackBearer) => {
659
- if (typeof callbackBearer !== "string" || !/^[\x21-\x7e]{16,512}$/.test(callbackBearer)) throw new HubSdkError({
660
- code: "HUB_INVALID_INPUT",
661
- message: "Application callback bearer is invalid"
662
- });
663
- const headers = new Headers(init.headers);
664
- headers.set("Authorization", `Bearer ${callbackBearer}`);
665
- return this.request(path, {
666
- ...init,
667
- headers,
668
- credentials: "omit",
669
- redirect: "manual"
670
- }, {
671
- auth: "caller",
672
- validateData: isDispatchCurrentData
673
- });
674
- });
675
- }
676
- static fromEnv(options = {}) {
677
- const env = options.env ?? readProcessEnv();
678
- return new HubClient({
679
- baseUrl: resolveHubBaseUrl(env),
680
- apiKey: options.apiKey ?? (options.authHeaders ? void 0 : resolveHubAuth(env)),
681
- authHeaders: options.authHeaders,
682
- fetch: options.fetch
683
- });
684
- }
685
- async status() {
686
- return this.request("/v1/hub/status", { method: "GET" });
687
- }
688
- /**
689
- * Open a streaming (Server-Sent Events) response and return its raw body.
690
- * Shares auth-header building + `fetch` with {@link request}, but does NOT
691
- * buffer or JSON-parse the body — the caller consumes the stream.
692
- *
693
- * A successful stream comes back as `text/event-stream`. Anything else is an
694
- * error envelope (404/429/401 JSON) or a transport failure (gateway/HTML
695
- * page); it is surfaced the same way {@link request} surfaces errors — a
696
- * typed `HubSdkError` from a `{success:false}` envelope, otherwise an
697
- * `HUB_HTTP_<status>` transport error — rather than handed back as bogus SSE.
698
- */
699
- async stream(path, init) {
700
- const response = await this.fetch(`${this.baseUrl}${path}`, {
701
- ...init,
702
- headers: await this.buildHeaders(init.headers)
703
- });
704
- const status = response.status;
705
- const contentType = response.headers.get("content-type") ?? "";
706
- if (!response.ok || !contentType.includes("text/event-stream")) {
707
- const bodyText = await response.text().catch(() => "");
708
- if (contentType.includes("application/json")) {
709
- let parsed;
710
- try {
711
- parsed = JSON.parse(bodyText);
712
- } catch {
713
- parsed = void 0;
714
- }
715
- if (isHubErrorEnvelope(parsed)) throw new HubSdkError(parsed.error, { status });
716
- }
717
- throw httpError(path, status, response.statusText, bodyText, contentType);
427
+ /** Decode a failed non-envelope response using the same redacted SDK errors. */
428
+ async function throwHubResponseError(path, response, status, contentType) {
429
+ const bodyText = await response.text().catch(() => "");
430
+ if (contentType.includes("application/json")) {
431
+ let parsed;
432
+ try {
433
+ parsed = JSON.parse(bodyText);
434
+ } catch {
435
+ parsed = void 0;
718
436
  }
719
- if (!response.body) throw httpError(path, status, response.statusText, "event-stream response had no body", contentType);
720
- return response.body;
437
+ if (isHubErrorEnvelope(parsed)) throw new HubSdkError(parsed.error, { status });
721
438
  }
722
- /**
723
- * Fetch raw (non-envelope) bytes — the artifact download route, whose 200 is
724
- * the artifact's own content type, not JSON. Error responses on such routes
725
- * are still the standard envelope, so a failure surfaces exactly like
726
- * {@link request}: a typed `HubSdkError` from a `{success:false}` body,
727
- * otherwise an `HUB_HTTP_<status>` transport error.
728
- */
729
- async requestBytes(path, init) {
730
- const response = await this.fetch(`${this.baseUrl}${path}`, {
731
- ...init,
732
- headers: await this.buildHeaders(init.headers)
439
+ throw httpError(path, status, response.statusText, bodyText, contentType);
440
+ }
441
+ //#endregion
442
+ //#region src/environment.ts
443
+ const HUB_URL_ENV_VAR = "TANGLE_HUB_URL";
444
+ const HUB_API_KEY_ENV_VAR = "TANGLE_API_KEY";
445
+ const HUB_CAPABILITY_TOKEN_ENV_VAR = "TANGLE_HUB_CAPABILITY_TOKEN";
446
+ function resolveHubBaseUrl(env = readProcessEnv()) {
447
+ const raw = env[HUB_URL_ENV_VAR];
448
+ if (raw === void 0 || raw.trim() === "") throw new HubSdkError({
449
+ code: "HUB_CONFIG_MISSING",
450
+ message: `${HUB_URL_ENV_VAR} is required`,
451
+ details: { envVar: HUB_URL_ENV_VAR }
452
+ });
453
+ const trimmed = raw.trim();
454
+ try {
455
+ new URL(trimmed);
456
+ } catch {
457
+ throw new HubSdkError({
458
+ code: "HUB_CONFIG_INVALID",
459
+ message: `${HUB_URL_ENV_VAR} is not a valid URL`,
460
+ details: { envVar: HUB_URL_ENV_VAR }
733
461
  });
734
- const status = response.status;
735
- const contentType = response.headers.get("content-type") ?? "";
736
- if (!response.ok) {
737
- const bodyText = await response.text().catch(() => "");
738
- if (contentType.includes("application/json")) {
739
- let parsed;
740
- try {
741
- parsed = JSON.parse(bodyText);
742
- } catch {
743
- parsed = void 0;
744
- }
745
- if (isHubErrorEnvelope(parsed)) throw new HubSdkError(parsed.error, { status });
746
- }
747
- throw httpError(path, status, response.statusText, bodyText, contentType);
748
- }
749
- return {
750
- bytes: new Uint8Array(await response.arrayBuffer()),
751
- contentType
752
- };
753
462
  }
754
- };
463
+ return trimmed.replace(/\/+$/, "").replace(/\/v1\/hub$/, "");
464
+ }
465
+ function resolveHubAuth(env = readProcessEnv()) {
466
+ const present = [nonEmpty(env[HUB_API_KEY_ENV_VAR]), nonEmpty(env[HUB_CAPABILITY_TOKEN_ENV_VAR])].filter((value) => value !== void 0);
467
+ if (present.length === 0) throw new HubSdkError({
468
+ code: "HUB_CONFIG_MISSING",
469
+ message: `One of ${HUB_API_KEY_ENV_VAR} or ${HUB_CAPABILITY_TOKEN_ENV_VAR} is required`,
470
+ details: { envVars: [HUB_API_KEY_ENV_VAR, HUB_CAPABILITY_TOKEN_ENV_VAR] }
471
+ });
472
+ if (present.length > 1) throw new HubSdkError({
473
+ code: "HUB_CONFIG_INVALID",
474
+ message: `Set exactly one of ${HUB_API_KEY_ENV_VAR} or ${HUB_CAPABILITY_TOKEN_ENV_VAR}, not both`,
475
+ details: { envVars: [HUB_API_KEY_ENV_VAR, HUB_CAPABILITY_TOKEN_ENV_VAR] }
476
+ });
477
+ return present[0];
478
+ }
479
+ function nonEmpty(value) {
480
+ if (value === void 0) return void 0;
481
+ const trimmed = value.trim();
482
+ return trimmed.length === 0 ? void 0 : trimmed;
483
+ }
484
+ function readProcessEnv() {
485
+ return globalThis.process?.env ?? {};
486
+ }
487
+ //#endregion
488
+ //#region src/authorization.ts
755
489
  var HubTokensClient = class {
756
490
  constructor(request) {
757
491
  this.request = request;
@@ -809,6 +543,22 @@ var HubPermissionsClient = class {
809
543
  return this.request("/v1/hub/policies/revert-writes", jsonInit("POST", { connectionId }));
810
544
  }
811
545
  };
546
+ var HubApprovalsClient = class {
547
+ constructor(request) {
548
+ this.request = request;
549
+ }
550
+ async list() {
551
+ return this.request("/v1/hub/approvals", { method: "GET" });
552
+ }
553
+ async approve(approvalId) {
554
+ return this.request(`/v1/hub/approvals/${encodeURIComponent(approvalId)}/approve`, { method: "POST" });
555
+ }
556
+ async deny(approvalId) {
557
+ return this.request(`/v1/hub/approvals/${encodeURIComponent(approvalId)}/deny`, { method: "POST" });
558
+ }
559
+ };
560
+ //#endregion
561
+ //#region src/tools.ts
812
562
  var HubToolsClient = class {
813
563
  constructor(request) {
814
564
  this.request = request;
@@ -906,6 +656,34 @@ function validateApprovedExecution(approved, path, connectionId) {
906
656
  }
907
657
  });
908
658
  }
659
+ //#endregion
660
+ //#region src/inbound-webhooks.ts
661
+ var HubInboundWebhooksClient = class {
662
+ constructor(request) {
663
+ this.request = request;
664
+ }
665
+ async create(connectionId, options = {}) {
666
+ return this.request(this.path(connectionId), jsonInit("POST", options));
667
+ }
668
+ async get(connectionId) {
669
+ return this.request(this.path(connectionId), { method: "GET" });
670
+ }
671
+ /** Add one owned Linq WhatsApp number without rotating the webhook secret. */
672
+ async ensurePhoneNumber(connectionId, phoneNumberId) {
673
+ if (!phoneNumberId || phoneNumberId.length > 128 || !/^[\x21-\x7e]+$/.test(phoneNumberId)) throw new Error("A bounded phone-number id is required");
674
+ return this.request(`${this.path(connectionId)}/phone-numbers/ensure`, jsonInit("POST", { phoneNumberId }));
675
+ }
676
+ /** Remove event subscriptions first. This does not silently rotate a live URL. */
677
+ async delete(connectionId) {
678
+ return this.request(this.path(connectionId), { method: "DELETE" });
679
+ }
680
+ path(connectionId) {
681
+ if (!connectionId || connectionId.length > 256) throw new Error("A bounded connection id is required");
682
+ return `/v1/hub/connections/${encodeURIComponent(connectionId)}/inbound-webhook`;
683
+ }
684
+ };
685
+ //#endregion
686
+ //#region src/connections.ts
909
687
  const GITHUB_EXACT_CONNECT_SCOPES = [
910
688
  "read:user",
911
689
  "repo",
@@ -917,34 +695,6 @@ function sameOAuthScopeSet(left, right) {
917
695
  const rightSet = new Set(right);
918
696
  return leftSet.size === left.length && rightSet.size === right.length && leftSet.size === rightSet.size && [...leftSet].every((scope) => rightSet.has(scope));
919
697
  }
920
- var HubChannelsClient = class {
921
- constructor(request) {
922
- this.request = request;
923
- }
924
- async list() {
925
- return this.request("/v1/hub/channels", { method: "GET" });
926
- }
927
- async createEmail(input = {}) {
928
- return this.request("/v1/hub/channels/email", jsonInit("POST", input));
929
- }
930
- async delete(channelId) {
931
- return this.request(`/v1/hub/channels/${encodeURIComponent(channelId)}`, { method: "DELETE" });
932
- }
933
- };
934
- var HubEventSubscriptionsClient = class {
935
- constructor(request) {
936
- this.request = request;
937
- }
938
- async list() {
939
- return this.request("/v1/hub/event-subscriptions", { method: "GET" });
940
- }
941
- async create(input) {
942
- return this.request("/v1/hub/event-subscriptions", jsonInit("POST", input));
943
- }
944
- async delete(subscriptionId) {
945
- return this.request(`/v1/hub/event-subscriptions/${encodeURIComponent(subscriptionId)}`, { method: "DELETE" });
946
- }
947
- };
948
698
  var HubConnectionsClient = class {
949
699
  inboundWebhook;
950
700
  constructor(request) {
@@ -997,24 +747,42 @@ var HubConnectionsClient = class {
997
747
  async revoke(connectionId) {
998
748
  return this.request(`/v1/hub/connections/${encodeURIComponent(connectionId)}`, { method: "DELETE" });
999
749
  }
1000
- async health(connectionId) {
1001
- return this.request(`/v1/hub/connections/${encodeURIComponent(connectionId)}/health`, { method: "POST" });
750
+ async health(connectionId) {
751
+ return this.request(`/v1/hub/connections/${encodeURIComponent(connectionId)}/health`, { method: "POST" });
752
+ }
753
+ };
754
+ //#endregion
755
+ //#region src/channels.ts
756
+ var HubChannelsClient = class {
757
+ constructor(request) {
758
+ this.request = request;
759
+ }
760
+ async list() {
761
+ return this.request("/v1/hub/channels", { method: "GET" });
762
+ }
763
+ async createEmail(input = {}) {
764
+ return this.request("/v1/hub/channels/email", jsonInit("POST", input));
765
+ }
766
+ async delete(channelId) {
767
+ return this.request(`/v1/hub/channels/${encodeURIComponent(channelId)}`, { method: "DELETE" });
1002
768
  }
1003
769
  };
1004
- var HubApprovalsClient = class {
770
+ var HubEventSubscriptionsClient = class {
1005
771
  constructor(request) {
1006
772
  this.request = request;
1007
773
  }
1008
774
  async list() {
1009
- return this.request("/v1/hub/approvals", { method: "GET" });
775
+ return this.request("/v1/hub/event-subscriptions", { method: "GET" });
1010
776
  }
1011
- async approve(approvalId) {
1012
- return this.request(`/v1/hub/approvals/${encodeURIComponent(approvalId)}/approve`, { method: "POST" });
777
+ async create(input) {
778
+ return this.request("/v1/hub/event-subscriptions", jsonInit("POST", input));
1013
779
  }
1014
- async deny(approvalId) {
1015
- return this.request(`/v1/hub/approvals/${encodeURIComponent(approvalId)}/deny`, { method: "POST" });
780
+ async delete(subscriptionId) {
781
+ return this.request(`/v1/hub/event-subscriptions/${encodeURIComponent(subscriptionId)}`, { method: "DELETE" });
1016
782
  }
1017
783
  };
784
+ //#endregion
785
+ //#region src/github-app.ts
1018
786
  var HubGithubAppClient = class {
1019
787
  constructor(request) {
1020
788
  this.request = request;
@@ -1055,6 +823,8 @@ var HubGithubAppClient = class {
1055
823
  return this.request(`/v1/hub/github-app/is-repo-installed?${params.toString()}`, { method: "GET" });
1056
824
  }
1057
825
  };
826
+ //#endregion
827
+ //#region src/audit.ts
1058
828
  var HubAuditClient = class {
1059
829
  constructor(request) {
1060
830
  this.request = request;
@@ -1072,6 +842,136 @@ var HubAuditClient = class {
1072
842
  return this.request(`/v1/hub/audit${query ? `?${query}` : ""}`, { method: "GET" });
1073
843
  }
1074
844
  };
845
+ //#endregion
846
+ //#region src/workflow-run-events.ts
847
+ /** Read a wire field as a string, or "" when the server sent anything else.
848
+ * `raw` is that field straight off the parsed payload, before any check. */
849
+ function optString(raw) {
850
+ return typeof raw === "string" ? raw : "";
851
+ }
852
+ /** An agent iteration's terminal status (`iteration.ended`) — only ever
853
+ * succeeded/failed; an iteration is never guard-skipped. */
854
+ function isIterationStatus(value) {
855
+ return value === "succeeded" || value === "failed";
856
+ }
857
+ /** An action's terminal status on `action.finished`. Beyond succeeded/failed a
858
+ * guarded action can end `skipped` (its `if` resolved false, so it never ran) —
859
+ * accepted here so a skipped event is surfaced, not dropped as malformed. */
860
+ function isActionFinishedStatus(value) {
861
+ return value === "succeeded" || value === "failed" || value === "skipped";
862
+ }
863
+ /** Terminal status of a whole run's `run.done` event. A run can end `cancelled`
864
+ * (a user cancel) — a value neither the action nor iteration status checkers
865
+ * accept — so `run.done` uses this rather than them, which would drop a
866
+ * cancelled terminal event and hang the stream. */
867
+ function isRunDoneStatus(value) {
868
+ return value === "succeeded" || value === "failed" || value === "cancelled";
869
+ }
870
+ /**
871
+ * Map one raw SSE event (its `event:` name + parsed JSON `data:`) to a typed
872
+ * run-stream event, validating the fields each variant declares rather than
873
+ * blindly casting the wire payload. A frame that doesn't match its event's
874
+ * shape (or an unknown event name) returns null so the consumer skips it — a
875
+ * malformed `token` can never reach a consumer as `{ delta: undefined }`, and
876
+ * new server event kinds don't break older clients.
877
+ */
878
+ function toRunStreamEvent(eventType, raw) {
879
+ if (eventType === "ping") return { type: "ping" };
880
+ if (!isRecord$1(raw)) return null;
881
+ switch (eventType) {
882
+ case "snapshot": return typeof raw.id === "string" && typeof raw.workflowId === "string" && typeof raw.status === "string" && Array.isArray(raw.actionResults) ? {
883
+ type: "snapshot",
884
+ run: raw
885
+ } : null;
886
+ case "token": return typeof raw.delta === "string" && typeof raw.actionIndex === "number" ? {
887
+ type: "token",
888
+ actionIndex: raw.actionIndex,
889
+ delta: raw.delta,
890
+ at: optString(raw.at)
891
+ } : null;
892
+ case "run.done": return isRunDoneStatus(raw.status) ? {
893
+ type: "run.done",
894
+ status: raw.status,
895
+ error: typeof raw.error === "string" ? raw.error : null,
896
+ ...typeof raw.at === "string" ? { at: raw.at } : {}
897
+ } : null;
898
+ case "run.waiting": return typeof raw.decisionId === "string" && raw.decisionId.length > 0 ? {
899
+ type: "run.waiting",
900
+ decisionId: raw.decisionId,
901
+ ...typeof raw.at === "string" ? { at: raw.at } : {}
902
+ } : null;
903
+ case "action.started": return typeof raw.index === "number" && typeof raw.kind === "string" && typeof raw.at === "string" ? {
904
+ type: "action.started",
905
+ index: raw.index,
906
+ kind: raw.kind,
907
+ at: raw.at
908
+ } : null;
909
+ case "action.finished": return typeof raw.index === "number" && typeof raw.kind === "string" && isActionFinishedStatus(raw.status) && typeof raw.at === "string" ? {
910
+ type: "action.finished",
911
+ index: raw.index,
912
+ kind: raw.kind,
913
+ status: raw.status,
914
+ at: raw.at,
915
+ ...typeof raw.costUsd === "number" ? { costUsd: raw.costUsd } : {},
916
+ ...typeof raw.error === "string" ? { error: raw.error } : {}
917
+ } : null;
918
+ case "iteration.started": return typeof raw.actionIndex === "number" && typeof raw.iterationIndex === "number" && typeof raw.at === "string" ? {
919
+ type: "iteration.started",
920
+ actionIndex: raw.actionIndex,
921
+ iterationIndex: raw.iterationIndex,
922
+ at: raw.at,
923
+ ...typeof raw.model === "string" ? { model: raw.model } : {}
924
+ } : null;
925
+ case "iteration.ended": return typeof raw.actionIndex === "number" && typeof raw.iterationIndex === "number" && isIterationStatus(raw.status) && typeof raw.at === "string" ? {
926
+ type: "iteration.ended",
927
+ actionIndex: raw.actionIndex,
928
+ iterationIndex: raw.iterationIndex,
929
+ status: raw.status,
930
+ at: raw.at,
931
+ ...typeof raw.outputPreview === "string" ? { outputPreview: raw.outputPreview } : {},
932
+ ...typeof raw.inputTokens === "number" ? { inputTokens: raw.inputTokens } : {},
933
+ ...typeof raw.outputTokens === "number" ? { outputTokens: raw.outputTokens } : {},
934
+ ...typeof raw.costUsd === "number" ? { costUsd: raw.costUsd } : {}
935
+ } : null;
936
+ default: return null;
937
+ }
938
+ }
939
+ //#endregion
940
+ //#region src/workflows.ts
941
+ /** True for an aborted/timed-out fetch (or any abort). fetch rejects with the
942
+ * signal's reason — a DOMException named `AbortError` (caller) or `TimeoutError`
943
+ * (AbortSignal.timeout). */
944
+ function isAbortError(cause) {
945
+ return cause instanceof Error && (cause.name === "AbortError" || cause.name === "TimeoutError");
946
+ }
947
+ /** A signal that aborts when the caller's signal aborts OR `timeoutMs` elapses.
948
+ * Returns undefined when neither bound applies (no per-request abort needed). */
949
+ function combineAbortSignals(caller, timeoutMs) {
950
+ const signals = [];
951
+ if (caller) signals.push(caller);
952
+ if (timeoutMs !== null) signals.push(AbortSignal.timeout(timeoutMs));
953
+ if (signals.length === 0) return void 0;
954
+ return signals.length === 1 ? signals[0] : AbortSignal.any(signals);
955
+ }
956
+ /** Resolve after `ms`, or reject if `signal` aborts first. Clears its timer on
957
+ * abort so a cancelled wait leaves nothing pending. */
958
+ function delay(ms, signal) {
959
+ return new Promise((resolve, reject) => {
960
+ if (signal?.aborted) {
961
+ reject(signal.reason ?? /* @__PURE__ */ new Error("The operation was aborted"));
962
+ return;
963
+ }
964
+ const onAbort = () => {
965
+ clearTimeout(timer);
966
+ reject(signal?.reason ?? /* @__PURE__ */ new Error("The operation was aborted"));
967
+ };
968
+ const timer = setTimeout(() => {
969
+ signal?.removeEventListener("abort", onAbort);
970
+ resolve();
971
+ }, ms);
972
+ signal?.addEventListener("abort", onAbort, { once: true });
973
+ });
974
+ }
1075
975
  var HubWorkflowsClient = class {
1076
976
  constructor(request, stream, download) {
1077
977
  this.request = request;
@@ -1142,8 +1042,9 @@ var HubWorkflowsClient = class {
1142
1042
  * `{ "pull_request.number": "123" }`. Omit it for a one-click run of a
1143
1043
  * workflow that reads no trigger fields. Throws `HubSdkError`:
1144
1044
  * `MISSING_RUN_INPUTS` when a required field is absent (`details.missing`
1145
- * names them), `WORKFLOW_DISABLED` when the workflow is paused, `NOT_FOUND`
1146
- * for an unknown/foreign id. Pass `opts.signal` to abort a slow request.
1045
+ * names them), `NOT_FOUND` for an unknown/foreign id. A paused workflow
1046
+ * still runs once; pausing only stops its triggers. Pass `opts.signal` to
1047
+ * abort a slow request.
1147
1048
  */
1148
1049
  async run(id, inputs, opts = {}) {
1149
1050
  const hasInputs = inputs !== void 0 && Object.keys(inputs).length > 0;
@@ -1188,8 +1089,8 @@ var HubWorkflowsClient = class {
1188
1089
  * follow it with {@link getRun}/{@link watchRun}/{@link waitForRun} like any
1189
1090
  * other run. Throws `HubSdkError`: `RUN_NOT_RETRYABLE` when the run is not
1190
1091
  * terminal-failed/cancelled (queued/running/waiting, or succeeded),
1191
- * `WORKFLOW_DISABLED` when the workflow has since been disabled, `NOT_FOUND`
1192
- * for an unknown/foreign run id. Pass `opts.signal` to abort a slow request.
1092
+ * `NOT_FOUND` for an unknown/foreign run id; a workflow paused since the
1093
+ * failure still retries. Pass `opts.signal` to abort a slow request.
1193
1094
  */
1194
1095
  async retryRun(id, runId, opts = {}) {
1195
1096
  return this.request(`/v1/workflows/${encodeURIComponent(id)}/runs/${encodeURIComponent(runId)}/retry`, {
@@ -1538,6 +1439,116 @@ var HubWorkflowsClient = class {
1538
1439
  }
1539
1440
  };
1540
1441
  //#endregion
1442
+ //#region src/client.ts
1443
+ function isDispatchCurrentData(value) {
1444
+ return isRecord$1(value) && typeof value.current === "boolean";
1445
+ }
1446
+ var HubClient = class HubClient extends HubTransport {
1447
+ knowledge;
1448
+ allowances;
1449
+ apps;
1450
+ connections;
1451
+ channels;
1452
+ numbers;
1453
+ eventSubscriptions;
1454
+ permissions;
1455
+ tokens;
1456
+ tools;
1457
+ approvals;
1458
+ audit;
1459
+ githubApp;
1460
+ workflows;
1461
+ lines;
1462
+ constructor(options) {
1463
+ super(options);
1464
+ this.knowledge = new HubKnowledgeClient((path, init) => this.request(path, init));
1465
+ this.allowances = new HubAllowancesClient((path, init) => this.request(path, init));
1466
+ this.apps = new HubAppsClient((path, init) => this.request(path, init));
1467
+ this.numbers = new HubManagedNumbersClient((path, init) => this.request(path, init));
1468
+ this.connections = new HubConnectionsClient((path, init) => this.request(path, init));
1469
+ this.channels = new HubChannelsClient((path, init) => this.request(path, init));
1470
+ this.eventSubscriptions = new HubEventSubscriptionsClient((path, init) => this.request(path, init));
1471
+ this.permissions = new HubPermissionsClient((path, init) => this.request(path, init));
1472
+ this.tokens = new HubTokensClient((path, init) => this.request(path, init));
1473
+ this.tools = new HubToolsClient((path, init) => this.request(path, init));
1474
+ this.approvals = new HubApprovalsClient((path, init) => this.request(path, init));
1475
+ this.audit = new HubAuditClient((path, init) => this.request(path, init));
1476
+ this.githubApp = new HubGithubAppClient((path, init) => this.request(path, init));
1477
+ this.workflows = new HubWorkflowsClient((path, init) => this.request(path, init), (path, init) => this.stream(path, init), (path, init) => this.requestBytes(path, init));
1478
+ this.lines = new HubLinesClient((path, init) => this.request(path, init), (yaml) => this.workflows.create(yaml), (path, init, callbackBearer) => {
1479
+ if (typeof callbackBearer !== "string" || !/^[\x21-\x7e]{16,512}$/.test(callbackBearer)) throw new HubSdkError({
1480
+ code: "HUB_INVALID_INPUT",
1481
+ message: "Application callback bearer is invalid"
1482
+ });
1483
+ const headers = new Headers(init.headers);
1484
+ headers.set("Authorization", `Bearer ${callbackBearer}`);
1485
+ return this.request(path, {
1486
+ ...init,
1487
+ headers,
1488
+ credentials: "omit",
1489
+ redirect: "manual"
1490
+ }, {
1491
+ auth: "caller",
1492
+ validateData: isDispatchCurrentData
1493
+ });
1494
+ });
1495
+ }
1496
+ static fromEnv(options = {}) {
1497
+ const env = options.env ?? readProcessEnv();
1498
+ return new HubClient({
1499
+ baseUrl: resolveHubBaseUrl(env),
1500
+ apiKey: options.apiKey ?? (options.authHeaders ? void 0 : resolveHubAuth(env)),
1501
+ authHeaders: options.authHeaders,
1502
+ fetch: options.fetch
1503
+ });
1504
+ }
1505
+ async status() {
1506
+ return this.request("/v1/hub/status", { method: "GET" });
1507
+ }
1508
+ /**
1509
+ * Open a streaming (Server-Sent Events) response and return its raw body.
1510
+ * Shares auth-header building + `fetch` with {@link request}, but does NOT
1511
+ * buffer or JSON-parse the body — the caller consumes the stream.
1512
+ *
1513
+ * A successful stream comes back as `text/event-stream`. Anything else is an
1514
+ * error envelope (404/429/401 JSON) or a transport failure (gateway/HTML
1515
+ * page); it is surfaced the same way {@link request} surfaces errors — a
1516
+ * typed `HubSdkError` from a `{success:false}` envelope, otherwise an
1517
+ * `HUB_HTTP_<status>` transport error — rather than handed back as bogus SSE.
1518
+ */
1519
+ async stream(path, init) {
1520
+ const response = await this.fetch(`${this.baseUrl}${path}`, {
1521
+ ...init,
1522
+ headers: await this.buildHeaders(init.headers)
1523
+ });
1524
+ const status = response.status;
1525
+ const contentType = response.headers.get("content-type") ?? "";
1526
+ if (!response.ok || !contentType.includes("text/event-stream")) await throwHubResponseError(path, response, status, contentType);
1527
+ if (!response.body) throw httpError(path, status, response.statusText, "event-stream response had no body", contentType);
1528
+ return response.body;
1529
+ }
1530
+ /**
1531
+ * Fetch raw (non-envelope) bytes — the artifact download route, whose 200 is
1532
+ * the artifact's own content type, not JSON. Error responses on such routes
1533
+ * are still the standard envelope, so a failure surfaces exactly like
1534
+ * {@link request}: a typed `HubSdkError` from a `{success:false}` body,
1535
+ * otherwise an `HUB_HTTP_<status>` transport error.
1536
+ */
1537
+ async requestBytes(path, init) {
1538
+ const response = await this.fetch(`${this.baseUrl}${path}`, {
1539
+ ...init,
1540
+ headers: await this.buildHeaders(init.headers)
1541
+ });
1542
+ const status = response.status;
1543
+ const contentType = response.headers.get("content-type") ?? "";
1544
+ if (!response.ok) await throwHubResponseError(path, response, status, contentType);
1545
+ return {
1546
+ bytes: new Uint8Array(await response.arrayBuffer()),
1547
+ contentType
1548
+ };
1549
+ }
1550
+ };
1551
+ //#endregion
1541
1552
  //#region src/broker.ts
1542
1553
  /** Server-only client. App secrets never become a user's general API key.
1543
1554
  * Every invocation mints and consumes one short-lived, exact-grant token. */