aui-agent-builder 0.3.102 → 0.3.104
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/api-client/index.d.ts +304 -19
- package/dist/api-client/index.d.ts.map +1 -1
- package/dist/api-client/index.js +337 -69
- package/dist/api-client/index.js.map +1 -1
- package/dist/api-client/kb-view-client.d.ts +26 -12
- package/dist/api-client/kb-view-client.d.ts.map +1 -1
- package/dist/api-client/kb-view-client.js.map +1 -1
- package/dist/commands/agents.d.ts +55 -1
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +193 -50
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/import-agent.d.ts +24 -0
- package/dist/commands/import-agent.d.ts.map +1 -1
- package/dist/commands/import-agent.js +807 -153
- package/dist/commands/import-agent.js.map +1 -1
- package/dist/commands/legacy/push-records-mode.d.ts +166 -0
- package/dist/commands/legacy/push-records-mode.d.ts.map +1 -0
- package/dist/commands/legacy/push-records-mode.js +2536 -0
- package/dist/commands/legacy/push-records-mode.js.map +1 -0
- package/dist/commands/pull-agent.d.ts +9 -0
- package/dist/commands/pull-agent.d.ts.map +1 -1
- package/dist/commands/pull-agent.js +571 -127
- package/dist/commands/pull-agent.js.map +1 -1
- package/dist/commands/push.d.ts +78 -107
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +1014 -1807
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/rag.js +1 -1
- package/dist/commands/rag.js.map +1 -1
- package/dist/commands/util/agent-mode.d.ts +69 -0
- package/dist/commands/util/agent-mode.d.ts.map +1 -0
- package/dist/commands/util/agent-mode.js +101 -0
- package/dist/commands/util/agent-mode.js.map +1 -0
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +23 -7
- package/dist/commands/validate.js.map +1 -1
- package/dist/commands/version-snapshot.d.ts.map +1 -1
- package/dist/commands/version-snapshot.js +253 -49
- package/dist/commands/version-snapshot.js.map +1 -1
- package/dist/commands/version.d.ts +15 -1
- package/dist/commands/version.d.ts.map +1 -1
- package/dist/commands/version.js +102 -7
- package/dist/commands/version.js.map +1 -1
- package/dist/config/index.d.ts +16 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/index.js +22 -5
- package/dist/index.js.map +1 -1
- package/dist/services/kb-view.service.d.ts +12 -27
- package/dist/services/kb-view.service.d.ts.map +1 -1
- package/dist/services/kb-view.service.js +134 -106
- package/dist/services/kb-view.service.js.map +1 -1
- package/dist/telemetry.d.ts +2 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js.map +1 -1
- package/dist/ui/views/ImportAgentView.d.ts +15 -0
- package/dist/ui/views/ImportAgentView.d.ts.map +1 -1
- package/dist/ui/views/ImportAgentView.js +8 -3
- package/dist/ui/views/ImportAgentView.js.map +1 -1
- package/dist/utils/index.d.ts +80 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +330 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
package/dist/api-client/index.js
CHANGED
|
@@ -345,6 +345,12 @@ export class AUIClient {
|
|
|
345
345
|
});
|
|
346
346
|
if (filters?.network_id)
|
|
347
347
|
qs.set("network_id", filters.network_id);
|
|
348
|
+
if (filters?.scope_type)
|
|
349
|
+
qs.set("scope_type", filters.scope_type);
|
|
350
|
+
if (filters?.kind)
|
|
351
|
+
qs.set("kind", filters.kind);
|
|
352
|
+
if (filters?.network_category_id)
|
|
353
|
+
qs.set("network_category_id", filters.network_category_id);
|
|
348
354
|
const url = `${getAgentManagementBaseUrl()}/v1/agents?${qs}`;
|
|
349
355
|
const resp = await this.agentManagementFetch(url, {
|
|
350
356
|
method: "GET",
|
|
@@ -410,22 +416,84 @@ export class AUIClient {
|
|
|
410
416
|
return (await resp.json());
|
|
411
417
|
},
|
|
412
418
|
getAgent: async (agentId) => {
|
|
419
|
+
// `GET /v1/agents/{id}` is the dispatch oracle for the whole CLI —
|
|
420
|
+
// every `aui import-agent`, `aui pull`, and `aui push` hits it
|
|
421
|
+
// before touching the per-entity / bundle endpoints (see
|
|
422
|
+
// `commands/util/agent-mode.ts → detectAgentBundleMode`). When
|
|
423
|
+
// a user reports "the CLI hit the wrong push endpoint" or
|
|
424
|
+
// "import keeps failing on this agent", the FIRST question is
|
|
425
|
+
// "what did the get-agent response say (especially bundle_mode)?".
|
|
426
|
+
//
|
|
427
|
+
// To answer that straight from Logfire — without re-running the
|
|
428
|
+
// CLI with `AUI_DEBUG=1` — we attach an event on the active span
|
|
429
|
+
// (`aui.import` / `aui.pull` / `aui.push`) carrying the full
|
|
430
|
+
// curl repro (auth redacted), HTTP method/url/status, duration,
|
|
431
|
+
// and the response body. Using `addEvent` (vs `setAttribute`)
|
|
432
|
+
// preserves multiple per-command calls: push's
|
|
433
|
+
// `lookupAgentManagementInfoForPush` + `resolveVersionDraft` can
|
|
434
|
+
// call `getAgent` more than once, and we want each call visible
|
|
435
|
+
// as its own log entry under the parent span.
|
|
413
436
|
const url = `${getAgentManagementBaseUrl()}/v1/agents/${agentId}`;
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
437
|
+
const method = "GET";
|
|
438
|
+
const headers = this.agentManagementHeaders();
|
|
439
|
+
const startedAt = Date.now();
|
|
440
|
+
let resp;
|
|
441
|
+
try {
|
|
442
|
+
resp = await this.agentManagementFetch(url, { method, headers }, "get agent");
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
// Network-level failure (DNS, ECONNREFUSED, timeout, abort, ...).
|
|
446
|
+
// Attach the request-side telemetry before re-throwing so the
|
|
447
|
+
// span tells the full story even when there's no HTTP response.
|
|
448
|
+
const networkErr = err instanceof Error ? err : new Error(String(err));
|
|
449
|
+
this.attachGetAgentTelemetry({
|
|
450
|
+
method,
|
|
451
|
+
url,
|
|
452
|
+
headers,
|
|
453
|
+
agentId,
|
|
454
|
+
responseText: networkErr.message,
|
|
455
|
+
status: 0,
|
|
456
|
+
success: false,
|
|
457
|
+
durationMs: Date.now() - startedAt,
|
|
458
|
+
});
|
|
459
|
+
throw networkErr;
|
|
460
|
+
}
|
|
461
|
+
// Read the body ONCE — Response bodies are single-shot and we
|
|
462
|
+
// need the same string for telemetry, error envelopes, and the
|
|
463
|
+
// success JSON parse below. Mirrors the validate-call path.
|
|
464
|
+
let responseText;
|
|
465
|
+
try {
|
|
466
|
+
responseText = await resp.text();
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
responseText = "";
|
|
470
|
+
}
|
|
471
|
+
this.attachGetAgentTelemetry({
|
|
472
|
+
method,
|
|
473
|
+
url,
|
|
474
|
+
headers,
|
|
475
|
+
agentId,
|
|
476
|
+
responseText,
|
|
477
|
+
status: resp.status,
|
|
478
|
+
success: resp.ok,
|
|
479
|
+
durationMs: Date.now() - startedAt,
|
|
480
|
+
});
|
|
418
481
|
if (!resp.ok) {
|
|
419
482
|
let errorBody;
|
|
420
483
|
try {
|
|
421
|
-
errorBody =
|
|
484
|
+
errorBody = JSON.parse(responseText);
|
|
422
485
|
}
|
|
423
486
|
catch {
|
|
424
|
-
errorBody =
|
|
487
|
+
errorBody = responseText || null;
|
|
425
488
|
}
|
|
426
489
|
throw new AUIAPIError(resp.status, `get agent: ${resp.statusText}`, errorBody);
|
|
427
490
|
}
|
|
428
|
-
|
|
491
|
+
try {
|
|
492
|
+
return JSON.parse(responseText);
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
throw new AUIAPIError(resp.status, `get agent: invalid JSON response`, responseText);
|
|
496
|
+
}
|
|
429
497
|
},
|
|
430
498
|
activateVersion: async (agentId, versionId) => {
|
|
431
499
|
const url = `${getAgentManagementBaseUrl()}/v1/agents/${agentId}/active-version`;
|
|
@@ -650,34 +718,89 @@ export class AUIClient {
|
|
|
650
718
|
return resp.json();
|
|
651
719
|
},
|
|
652
720
|
/**
|
|
653
|
-
* POST /v1/agents/{agentId}/versions/{versionId}/
|
|
654
|
-
*
|
|
655
|
-
*
|
|
721
|
+
* POST /v1/agents/{agentId}/versions/{versionId}/push
|
|
722
|
+
*
|
|
723
|
+
* JSON-only upload (multipart was retired on 2026-05-20 — see
|
|
724
|
+
* Notion "Agent Settings Push/Pull" and the OpenAPI under
|
|
725
|
+
* `PushRequestSchema`).
|
|
726
|
+
*
|
|
727
|
+
* Server flow:
|
|
728
|
+
* 1. Validates the assembled `bundle` through the v1 transcoder
|
|
729
|
+
* BEFORE acquiring the per-version Redis lock — fail-fast 422
|
|
730
|
+
* with a structured `BundleValidationResult` and no GCS /
|
|
731
|
+
* Mongo writes.
|
|
732
|
+
* 2. Canonicalizes the bundle bytes (deterministic key order,
|
|
733
|
+
* no extra whitespace), hashes with SHA-256, and writes a
|
|
734
|
+
* single `bundle.json` blob to GCS under
|
|
735
|
+
* `agent_id={id}/version_id={id}/version_tag={tag}/bundle.json`.
|
|
736
|
+
* 3. Bumps `version_revision_number` by exactly 1 and returns the
|
|
737
|
+
* new `version_tag`, `gcs_key`, `sha256`, and `size` so
|
|
738
|
+
* clients can verify integrity locally.
|
|
739
|
+
*
|
|
740
|
+
* Request body (`PushRequestSchema`):
|
|
741
|
+
* - `caller` : "agent_builder" | "ui" | "cli" (required)
|
|
742
|
+
* - `commit_message` : optional save note (≤ 4000 chars after trim)
|
|
743
|
+
* - `bundle` : `AgentSettingsBundle` — the CLI assembles
|
|
744
|
+
* this from local `.aui.json` files (see
|
|
745
|
+
* `assembleBundleFromFiles` in `utils/`).
|
|
746
|
+
*
|
|
747
|
+
* Auth: Bearer JWT + `X-Organization-ID` (same as other v1 routes).
|
|
748
|
+
* The legacy `x-api-key` fallback is NOT honoured here — Bearer only.
|
|
749
|
+
* Push requires `update` on `agent_settings` for the target agent.
|
|
656
750
|
*/
|
|
657
|
-
|
|
658
|
-
const url = `${getAgentManagementBaseUrl()}/v1/agents/${agentId}/versions/${versionId}/
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
751
|
+
pushVersionBlobs: async (agentId, versionId, body) => {
|
|
752
|
+
const url = `${getAgentManagementBaseUrl()}/v1/agents/${agentId}/versions/${versionId}/push`;
|
|
753
|
+
// Defensive normalization of the request body:
|
|
754
|
+
// - `caller` always set (TS already enforces it, but be belt+suspenders)
|
|
755
|
+
// - `commit_message` trimmed + capped (server caps too, but the
|
|
756
|
+
// CLI shouldn't make the server do work on bad input)
|
|
757
|
+
// - `pushed_by` forwarded as-is when the caller supplies it
|
|
758
|
+
// (added 2026-05-25 per backend team — recorded on the
|
|
759
|
+
// blob revision row so revision provenance ties back to a
|
|
760
|
+
// real user, not just a surface). Only emitted when set so
|
|
761
|
+
// older server versions that don't read the field still
|
|
762
|
+
// accept the request body unchanged.
|
|
763
|
+
// - `bundle.schema_version` defaults to 1 if the caller forgot
|
|
764
|
+
const reqBody = {
|
|
765
|
+
caller: body.caller,
|
|
766
|
+
...(body.commit_message && body.commit_message.trim()
|
|
767
|
+
? { commit_message: body.commit_message.trim().slice(0, 4000) }
|
|
768
|
+
: {}),
|
|
769
|
+
...(body.pushed_by ? { pushed_by: body.pushed_by } : {}),
|
|
770
|
+
bundle: {
|
|
771
|
+
schema_version: body.bundle.schema_version ?? 1,
|
|
772
|
+
...body.bundle,
|
|
773
|
+
},
|
|
774
|
+
};
|
|
663
775
|
if (process.env.AUI_DEBUG) {
|
|
664
|
-
|
|
665
|
-
|
|
776
|
+
const b = reqBody.bundle;
|
|
777
|
+
const summary = {
|
|
778
|
+
schema_version: b.schema_version,
|
|
779
|
+
general_settings: b.general_settings ? "<object>" : null,
|
|
780
|
+
parameters: b.parameters ? b.parameters.length : null,
|
|
781
|
+
entities: b.entities ? b.entities.length : null,
|
|
782
|
+
integrations: b.integrations ? b.integrations.length : null,
|
|
783
|
+
rules: b.rules ? b.rules.length : null,
|
|
784
|
+
tools: b.tools ? b.tools.length : null,
|
|
785
|
+
};
|
|
786
|
+
console.log(`[debug] POST ${url} as caller=${reqBody.caller} pushed_by=${reqBody.pushed_by ?? "(absent)"}; bundle:`, JSON.stringify(summary));
|
|
666
787
|
}
|
|
667
788
|
const headers = {
|
|
668
789
|
Accept: "application/json",
|
|
790
|
+
"Content-Type": "application/json",
|
|
669
791
|
Authorization: `Bearer ${this.authToken}`,
|
|
670
792
|
"X-Organization-ID": this.organizationId,
|
|
671
793
|
};
|
|
672
|
-
//
|
|
673
|
-
//
|
|
674
|
-
//
|
|
675
|
-
// truly hung request surfaces before the BFF's own
|
|
676
|
-
// (BFF caps `aui push` at 5 min in
|
|
677
|
-
// `sandbox-command-client.ts:auiPush`).
|
|
678
|
-
// `
|
|
679
|
-
|
|
680
|
-
|
|
794
|
+
// Bundle uploads can be moderately large (every parameter +
|
|
795
|
+
// entity + tool encoded as JSON). Cap the request at 5 minutes —
|
|
796
|
+
// long enough for a slow E2B sandbox cold-start, short enough
|
|
797
|
+
// that a truly hung request surfaces before the BFF's own
|
|
798
|
+
// pipeline timeout (BFF caps `aui push` at 5 min in
|
|
799
|
+
// `agent-builder-bff`'s `sandbox-command-client.ts:auiPush`).
|
|
800
|
+
// Override via `AUI_PUSH_TIMEOUT_MS` env var if needed (legacy
|
|
801
|
+
// `AUI_SNAPSHOT_TIMEOUT_MS` honoured too for back-compat).
|
|
802
|
+
const pushTimeoutMs = (() => {
|
|
803
|
+
const fromEnv = process.env.AUI_PUSH_TIMEOUT_MS ?? process.env.AUI_SNAPSHOT_TIMEOUT_MS;
|
|
681
804
|
if (fromEnv) {
|
|
682
805
|
const parsed = parseInt(fromEnv, 10);
|
|
683
806
|
if (!Number.isNaN(parsed) && parsed >= 0)
|
|
@@ -685,56 +808,65 @@ export class AUIClient {
|
|
|
685
808
|
}
|
|
686
809
|
return 300_000;
|
|
687
810
|
})();
|
|
688
|
-
const
|
|
689
|
-
const
|
|
690
|
-
? setTimeout(() =>
|
|
811
|
+
const pushController = new AbortController();
|
|
812
|
+
const pushTimer = pushTimeoutMs > 0
|
|
813
|
+
? setTimeout(() => pushController.abort(), pushTimeoutMs)
|
|
691
814
|
: null;
|
|
692
|
-
const
|
|
815
|
+
const pushStart = Date.now();
|
|
816
|
+
const serialized = JSON.stringify(reqBody);
|
|
693
817
|
let resp;
|
|
694
818
|
try {
|
|
695
819
|
resp = await globalThis.fetch(url, {
|
|
696
820
|
method: "POST",
|
|
697
821
|
headers,
|
|
698
|
-
body:
|
|
699
|
-
signal:
|
|
822
|
+
body: serialized,
|
|
823
|
+
signal: pushController.signal,
|
|
700
824
|
});
|
|
701
825
|
}
|
|
702
826
|
catch (err) {
|
|
703
|
-
if (
|
|
704
|
-
clearTimeout(
|
|
705
|
-
const elapsedMs = Date.now() -
|
|
827
|
+
if (pushTimer)
|
|
828
|
+
clearTimeout(pushTimer);
|
|
829
|
+
const elapsedMs = Date.now() - pushStart;
|
|
706
830
|
const isAbort = err instanceof Error &&
|
|
707
|
-
(err.name === "AbortError" ||
|
|
708
|
-
if (isAbort &&
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
const msg = `Snapshot upload timed out after ${elapsedMs}ms (limit ${snapshotTimeoutMs}ms): POST ${url}. ` +
|
|
712
|
-
`Override with AUI_SNAPSHOT_TIMEOUT_MS=<ms> if your agent is unusually large.`;
|
|
831
|
+
(err.name === "AbortError" || pushController.signal.aborted);
|
|
832
|
+
if (isAbort && pushTimeoutMs > 0 && elapsedMs >= pushTimeoutMs - 50) {
|
|
833
|
+
const msg = `Push blob upload timed out after ${elapsedMs}ms (limit ${pushTimeoutMs}ms): POST ${url}. ` +
|
|
834
|
+
`Override with AUI_PUSH_TIMEOUT_MS=<ms> if your agent is unusually large.`;
|
|
713
835
|
captureRequest({
|
|
714
836
|
timestamp: new Date().toISOString(),
|
|
715
837
|
method: "POST",
|
|
716
838
|
url,
|
|
717
839
|
headers,
|
|
718
|
-
body: {
|
|
840
|
+
body: {
|
|
841
|
+
caller: reqBody.caller,
|
|
842
|
+
commit_message: reqBody.commit_message,
|
|
843
|
+
pushed_by: reqBody.pushed_by,
|
|
844
|
+
bundle_size_bytes: serialized.length,
|
|
845
|
+
},
|
|
719
846
|
status: 0,
|
|
720
847
|
responseBody: msg,
|
|
721
|
-
label: "push
|
|
848
|
+
label: "push version blobs",
|
|
722
849
|
success: false,
|
|
723
850
|
});
|
|
724
|
-
return { success: false,
|
|
851
|
+
return { success: false, error: msg };
|
|
725
852
|
}
|
|
726
853
|
throw err;
|
|
727
854
|
}
|
|
728
|
-
if (
|
|
729
|
-
clearTimeout(
|
|
855
|
+
if (pushTimer)
|
|
856
|
+
clearTimeout(pushTimer);
|
|
730
857
|
captureRequest({
|
|
731
858
|
timestamp: new Date().toISOString(),
|
|
732
859
|
method: "POST",
|
|
733
860
|
url,
|
|
734
861
|
headers,
|
|
735
|
-
body: {
|
|
862
|
+
body: {
|
|
863
|
+
caller: reqBody.caller,
|
|
864
|
+
commit_message: reqBody.commit_message,
|
|
865
|
+
pushed_by: reqBody.pushed_by,
|
|
866
|
+
bundle_size_bytes: serialized.length,
|
|
867
|
+
},
|
|
736
868
|
status: resp.status,
|
|
737
|
-
label: "push
|
|
869
|
+
label: "push version blobs",
|
|
738
870
|
success: resp.ok,
|
|
739
871
|
});
|
|
740
872
|
if (!resp.ok) {
|
|
@@ -746,37 +878,67 @@ export class AUIClient {
|
|
|
746
878
|
errorBody = await resp.text();
|
|
747
879
|
}
|
|
748
880
|
if (process.env.AUI_DEBUG) {
|
|
749
|
-
console.log(`[debug]
|
|
881
|
+
console.log(`[debug] push version blobs failed: ${resp.status} ${JSON.stringify(errorBody)}`);
|
|
750
882
|
}
|
|
883
|
+
// 422 has two flavours per the doc:
|
|
884
|
+
// - request validation: { detail: "string" } or { detail: [{ loc, msg, type }] }
|
|
885
|
+
// - bundle validation : BundleValidationResult JSON
|
|
886
|
+
// Surface whichever is present without losing the structured body.
|
|
751
887
|
const detail = errorBody?.detail;
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
888
|
+
let errorMsg;
|
|
889
|
+
if (Array.isArray(detail)) {
|
|
890
|
+
errorMsg = detail
|
|
891
|
+
.map((d) => `${d.msg ?? d.message ?? ""} (${(d.loc ?? []).join(".")})`)
|
|
892
|
+
.join("; ");
|
|
893
|
+
}
|
|
894
|
+
else if (typeof detail === "string") {
|
|
895
|
+
errorMsg = detail;
|
|
896
|
+
}
|
|
897
|
+
else if (typeof errorBody === "string") {
|
|
898
|
+
errorMsg = errorBody;
|
|
899
|
+
}
|
|
900
|
+
else {
|
|
901
|
+
errorMsg = JSON.stringify(errorBody);
|
|
902
|
+
}
|
|
903
|
+
return {
|
|
904
|
+
success: false,
|
|
905
|
+
error: `${resp.status}: ${errorMsg}`,
|
|
906
|
+
data: errorBody,
|
|
907
|
+
};
|
|
757
908
|
}
|
|
758
909
|
let responseData;
|
|
759
910
|
try {
|
|
760
|
-
responseData = await resp.json();
|
|
911
|
+
responseData = (await resp.json());
|
|
761
912
|
}
|
|
762
913
|
catch {
|
|
763
|
-
responseData =
|
|
914
|
+
responseData = undefined;
|
|
764
915
|
}
|
|
765
916
|
if (process.env.AUI_DEBUG) {
|
|
766
|
-
console.log(`[debug]
|
|
917
|
+
console.log(`[debug] push version blobs succeeded: ${JSON.stringify(responseData)}`);
|
|
767
918
|
}
|
|
768
|
-
return { success: true,
|
|
919
|
+
return { success: true, data: responseData };
|
|
769
920
|
},
|
|
770
921
|
/**
|
|
771
|
-
* GET /v1/agents/{agentId}/versions/{versionId}/
|
|
772
|
-
*
|
|
922
|
+
* GET /v1/agents/{agentId}/versions/{versionId}/pull
|
|
923
|
+
*
|
|
924
|
+
* JSON-only read (signed-URL + `return_type=json` modes were
|
|
925
|
+
* retired on 2026-05-20 — the new server returns the assembled
|
|
926
|
+
* bundle inline as `PullReadResponse`).
|
|
927
|
+
*
|
|
928
|
+
* Caching: every `version_tag` is an immutable snapshot — Pull
|
|
929
|
+
* tries Redis first. `expires_at` on the response is the snapshot
|
|
930
|
+
* row's TTL, not a per-request expiry.
|
|
931
|
+
*
|
|
932
|
+
* Auth: Bearer JWT + `X-Organization-ID`. The legacy `x-api-key`
|
|
933
|
+
* fallback is NOT honoured here — Bearer only. Pull requires
|
|
934
|
+
* `read` on `agent_settings`.
|
|
773
935
|
*/
|
|
774
|
-
|
|
936
|
+
pullVersionBlobs: async (agentId, versionId, options = {}) => {
|
|
775
937
|
const qs = new URLSearchParams();
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const url = `${getAgentManagementBaseUrl()}/v1/agents/${agentId}/versions/${versionId}/
|
|
938
|
+
if (options.versionTag)
|
|
939
|
+
qs.set("version_tag", options.versionTag);
|
|
940
|
+
const search = qs.toString();
|
|
941
|
+
const url = `${getAgentManagementBaseUrl()}/v1/agents/${agentId}/versions/${versionId}/pull${search ? `?${search}` : ""}`;
|
|
780
942
|
if (process.env.AUI_DEBUG) {
|
|
781
943
|
console.log(`[debug] GET ${url}`);
|
|
782
944
|
}
|
|
@@ -787,7 +949,7 @@ export class AUIClient {
|
|
|
787
949
|
Authorization: `Bearer ${this.authToken}`,
|
|
788
950
|
"X-Organization-ID": this.organizationId,
|
|
789
951
|
},
|
|
790
|
-
}, "
|
|
952
|
+
}, "pull version blobs");
|
|
791
953
|
if (!resp.ok) {
|
|
792
954
|
let errorBody;
|
|
793
955
|
try {
|
|
@@ -796,9 +958,29 @@ export class AUIClient {
|
|
|
796
958
|
catch {
|
|
797
959
|
errorBody = await resp.text();
|
|
798
960
|
}
|
|
799
|
-
throw new AUIAPIError(resp.status, `
|
|
961
|
+
throw new AUIAPIError(resp.status, `pull version blobs: ${resp.statusText}`, errorBody);
|
|
800
962
|
}
|
|
801
|
-
return resp.json();
|
|
963
|
+
return (await resp.json());
|
|
964
|
+
},
|
|
965
|
+
/**
|
|
966
|
+
* @deprecated Multipart-files upload was removed on 2026-05-20. The
|
|
967
|
+
* new server only accepts the JSON `PushBundleRequest` shape via
|
|
968
|
+
* {@link pushVersionBlobs}. Callers must assemble the bundle from
|
|
969
|
+
* local files (see `utils/assembleBundleFromFiles`) and pass it
|
|
970
|
+
* in directly.
|
|
971
|
+
*/
|
|
972
|
+
pushSnapshot: async () => {
|
|
973
|
+
throw new AUIAPIError(410, "pushSnapshot is gone. The multipart /push endpoint was retired on 2026-05-20; use pushVersionBlobs with an assembled AgentSettingsBundle.");
|
|
974
|
+
},
|
|
975
|
+
/**
|
|
976
|
+
* @deprecated Signed-URL pull mode was removed on 2026-05-20. The
|
|
977
|
+
* new server returns the assembled bundle inline as
|
|
978
|
+
* {@link PullReadResponse}; consumers should call
|
|
979
|
+
* {@link pullVersionBlobs} directly and read `response.bundle`
|
|
980
|
+
* instead of paging through per-file signed URLs.
|
|
981
|
+
*/
|
|
982
|
+
getSnapshot: async () => {
|
|
983
|
+
throw new AUIAPIError(410, "getSnapshot is gone. Signed-URL pull mode was retired on 2026-05-20; use pullVersionBlobs and read response.bundle.");
|
|
802
984
|
},
|
|
803
985
|
/**
|
|
804
986
|
* POST /v1/agents/{agentId}/validate
|
|
@@ -989,6 +1171,92 @@ export class AUIClient {
|
|
|
989
1171
|
// Telemetry must never break the call path.
|
|
990
1172
|
}
|
|
991
1173
|
}
|
|
1174
|
+
/**
|
|
1175
|
+
* Attach `GET /v1/agents/{id}` telemetry to the currently-active span
|
|
1176
|
+
* (typically `aui.import` / `aui.pull` / `aui.push`). The CLI calls
|
|
1177
|
+
* this endpoint before every import/pull/push to read `bundle_mode`
|
|
1178
|
+
* and decide which surface to use — so making every call self-
|
|
1179
|
+
* describing in Logfire (curl repro, status, response body, duration)
|
|
1180
|
+
* answers "did the CLI even know the right mode?" without re-running
|
|
1181
|
+
* locally. See the comment block in `getAgent` for the rationale.
|
|
1182
|
+
*
|
|
1183
|
+
* Best-effort throughout: telemetry must NEVER break the call path.
|
|
1184
|
+
*/
|
|
1185
|
+
attachGetAgentTelemetry(input) {
|
|
1186
|
+
try {
|
|
1187
|
+
const span = trace.getActiveSpan();
|
|
1188
|
+
if (!span)
|
|
1189
|
+
return;
|
|
1190
|
+
const MAX = 32 * 1024;
|
|
1191
|
+
const truncatedResponse = input.responseText.length > MAX
|
|
1192
|
+
? input.responseText.slice(0, MAX) +
|
|
1193
|
+
`... [truncated ${input.responseText.length - MAX} bytes]`
|
|
1194
|
+
: input.responseText;
|
|
1195
|
+
// Best-effort: surface `bundle_mode` as its own event attribute
|
|
1196
|
+
// so dashboards can filter "show me every push where the agent
|
|
1197
|
+
// reported bundle_mode=false" without grepping response bodies.
|
|
1198
|
+
let bundleMode;
|
|
1199
|
+
let agentName;
|
|
1200
|
+
let activeVersionId;
|
|
1201
|
+
try {
|
|
1202
|
+
const parsed = JSON.parse(input.responseText);
|
|
1203
|
+
if (typeof parsed.bundle_mode === "boolean") {
|
|
1204
|
+
bundleMode = parsed.bundle_mode;
|
|
1205
|
+
}
|
|
1206
|
+
if (typeof parsed.name === "string")
|
|
1207
|
+
agentName = parsed.name;
|
|
1208
|
+
if (typeof parsed.active_version_id === "string") {
|
|
1209
|
+
activeVersionId = parsed.active_version_id;
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
catch {
|
|
1213
|
+
// Response wasn't JSON (network failure stringified error,
|
|
1214
|
+
// 5xx HTML page, etc.) — fall through with bundleMode unset.
|
|
1215
|
+
}
|
|
1216
|
+
const eventAttrs = {
|
|
1217
|
+
"http.method": input.method,
|
|
1218
|
+
"http.url": input.url,
|
|
1219
|
+
"get_agent.agent_id": input.agentId,
|
|
1220
|
+
"get_agent.duration_ms": input.durationMs,
|
|
1221
|
+
"get_agent.success": input.success,
|
|
1222
|
+
"get_agent.response_body": truncatedResponse,
|
|
1223
|
+
"aui.curl_repro": formatAsCurlSafe({
|
|
1224
|
+
timestamp: new Date().toISOString(),
|
|
1225
|
+
method: input.method,
|
|
1226
|
+
url: input.url,
|
|
1227
|
+
headers: input.headers,
|
|
1228
|
+
status: input.status,
|
|
1229
|
+
responseBody: input.responseText,
|
|
1230
|
+
label: "get agent",
|
|
1231
|
+
success: input.success,
|
|
1232
|
+
}, { maxBodyBytes: MAX }),
|
|
1233
|
+
};
|
|
1234
|
+
// status === 0 ⇒ no HTTP response (network failure). Mirror the
|
|
1235
|
+
// validate-call convention and omit `http.status_code` so
|
|
1236
|
+
// dashboards filtering on status don't conflate "couldn't reach
|
|
1237
|
+
// backend" with a real status.
|
|
1238
|
+
if (input.status > 0) {
|
|
1239
|
+
eventAttrs["http.status_code"] = input.status;
|
|
1240
|
+
}
|
|
1241
|
+
if (typeof bundleMode === "boolean") {
|
|
1242
|
+
eventAttrs["get_agent.bundle_mode"] = bundleMode;
|
|
1243
|
+
// Also pin the most-recent observed mode at the span level so
|
|
1244
|
+
// it's filterable without expanding the event list. Multiple
|
|
1245
|
+
// calls per command overwrite — the last call wins, which is
|
|
1246
|
+
// the one whose decision actually drove the dispatch.
|
|
1247
|
+
span.setAttribute("agent.bundle_mode", bundleMode);
|
|
1248
|
+
}
|
|
1249
|
+
if (agentName)
|
|
1250
|
+
eventAttrs["get_agent.agent_name"] = agentName;
|
|
1251
|
+
if (activeVersionId) {
|
|
1252
|
+
eventAttrs["get_agent.active_version_id"] = activeVersionId;
|
|
1253
|
+
}
|
|
1254
|
+
span.addEvent("get_agent", eventAttrs);
|
|
1255
|
+
}
|
|
1256
|
+
catch {
|
|
1257
|
+
// Telemetry must never break the call path.
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
992
1260
|
agentManagementHeaders() {
|
|
993
1261
|
return {
|
|
994
1262
|
Accept: "application/json",
|