aui-agent-builder 0.3.76 → 0.3.78
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 +157 -26
- package/dist/api-client/index.d.ts +41 -0
- package/dist/api-client/index.d.ts.map +1 -1
- package/dist/api-client/index.js +98 -0
- package/dist/api-client/index.js.map +1 -1
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +6 -8
- package/dist/commands/agents.js.map +1 -1
- package/dist/commands/import-agent.js +8 -8
- package/dist/commands/import-agent.js.map +1 -1
- package/dist/commands/integration.js +1 -1
- package/dist/commands/integration.js.map +1 -1
- package/dist/commands/login.js +1 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/pull-agent.js +1 -1
- package/dist/commands/pull-agent.js.map +1 -1
- package/dist/commands/push.js +228 -85
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +1 -4
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/version-snapshot.d.ts +21 -0
- package/dist/commands/version-snapshot.d.ts.map +1 -0
- package/dist/commands/version-snapshot.js +669 -0
- package/dist/commands/version-snapshot.js.map +1 -0
- package/dist/commands/version.d.ts +2 -1
- package/dist/commands/version.d.ts.map +1 -1
- package/dist/commands/version.js +111 -42
- package/dist/commands/version.js.map +1 -1
- package/dist/index.js +71 -9
- package/dist/index.js.map +1 -1
- package/dist/ui/views/PushView.d.ts +3 -1
- package/dist/ui/views/PushView.d.ts.map +1 -1
- package/dist/ui/views/PushView.js +8 -2
- package/dist/ui/views/PushView.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/push.js
CHANGED
|
@@ -11,7 +11,7 @@ import { findAuiFiles, parseAuiFile } from "../utils/index.js";
|
|
|
11
11
|
import { validate } from "./validate.js";
|
|
12
12
|
import { getTracer, SpanStatusCode, setUserContext } from "../telemetry.js";
|
|
13
13
|
import { getItemLevelDiff } from "../utils/git.js";
|
|
14
|
-
import { AuthenticationError, ConfigError, ValidationError } from "../errors/index.js";
|
|
14
|
+
import { AuthenticationError, CLIError, ConfigError, ValidationError } from "../errors/index.js";
|
|
15
15
|
import { StatusLine, Spinner, ErrorDisplay, Hint, } from "../ui/components/index.js";
|
|
16
16
|
import { colors, icons } from "../ui/theme.js";
|
|
17
17
|
import { PushFileSummary, PushChangesView, PushTaskLine, PushFinalSummary, } from "../ui/views/PushView.js";
|
|
@@ -459,15 +459,18 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
459
459
|
if (savedApiKey && !options.apiKey) {
|
|
460
460
|
client.setAgentSettingsApiKey(savedApiKey);
|
|
461
461
|
}
|
|
462
|
-
//
|
|
463
|
-
//
|
|
462
|
+
// Version management: push is only allowed to draft versions.
|
|
463
|
+
// If the project has version_id in .auirc or --version-id is passed,
|
|
464
|
+
// we validate it's a draft. If no version context exists, we auto-detect
|
|
465
|
+
// available drafts. Push is rejected if no draft is found.
|
|
464
466
|
let prePushDraft = null;
|
|
465
467
|
if (projectConfig.version_id || options.versionId) {
|
|
466
468
|
prePushDraft = await resolveVersionDraft(config, projectConfig, session, options.versionId);
|
|
467
|
-
if (prePushDraft
|
|
468
|
-
|
|
469
|
-
log(_jsx(StatusLine, { kind: "info", label: `Pushing into version draft: ${prePushDraft.label}` }));
|
|
469
|
+
if (!prePushDraft) {
|
|
470
|
+
return;
|
|
470
471
|
}
|
|
472
|
+
agentSettingsParams.version_id = prePushDraft.versionId;
|
|
473
|
+
log(_jsx(StatusLine, { kind: "info", label: `Pushing into draft version: ${prePushDraft.label}` }));
|
|
471
474
|
}
|
|
472
475
|
const pushTasks = buildPushTasks(diff, fileData, projectRoot, getFileDiff);
|
|
473
476
|
pushSpan.setAttribute("push.task_count", pushTasks.length);
|
|
@@ -667,33 +670,150 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
667
670
|
if (pushFailures.length > 0) {
|
|
668
671
|
pushSpan.setAttribute("push.failures", JSON.stringify(pushFailures));
|
|
669
672
|
}
|
|
673
|
+
// ─── Push Snapshot (last step — after entity push) ───
|
|
674
|
+
// The snapshot captures the local file state and uploads it as the
|
|
675
|
+
// source of truth for this push. Runs regardless of entity-push outcomes
|
|
676
|
+
// so the file history is preserved even on partial DB failures.
|
|
677
|
+
// Files = source of truth; DB updates are best-effort.
|
|
678
|
+
//
|
|
679
|
+
// IMPORTANT: snapshot failures must be loud — they are NOT counted in
|
|
680
|
+
// the entity `failed` counter, so without explicit surfacing here and
|
|
681
|
+
// a thrown CLIError at the end of `_push`, a snapshot failure would
|
|
682
|
+
// be hidden behind the "All N change(s) pushed successfully" summary
|
|
683
|
+
// and the process would exit 0.
|
|
684
|
+
// Retry policy: snapshot upload is the source-of-truth step; transient
|
|
685
|
+
// failures (network blips, 5xx, timeouts) are common with multipart
|
|
686
|
+
// uploads, so we retry up to 3 times with exponential backoff (1s, 2s,
|
|
687
|
+
// 4s) before giving up. 4xx responses are still retried — keeping the
|
|
688
|
+
// policy dumb here mirrors the single retry already in the api-client
|
|
689
|
+
// layer for entity pushes.
|
|
690
|
+
let snapshotSucceeded = false;
|
|
691
|
+
let snapshotError;
|
|
692
|
+
let snapshotAttempts = 0;
|
|
693
|
+
if (prePushDraft) {
|
|
694
|
+
const SNAPSHOT_MAX_ATTEMPTS = 4;
|
|
695
|
+
const SNAPSHOT_RETRY_BASE_MS = 1000;
|
|
696
|
+
for (let attempt = 1; attempt <= SNAPSHOT_MAX_ATTEMPTS; attempt++) {
|
|
697
|
+
snapshotAttempts = attempt;
|
|
698
|
+
const label = attempt === 1
|
|
699
|
+
? "Pushing snapshot (file state)..."
|
|
700
|
+
: `Retrying snapshot push (attempt ${attempt}/${SNAPSHOT_MAX_ATTEMPTS})...`;
|
|
701
|
+
if (json)
|
|
702
|
+
stderrLog(label);
|
|
703
|
+
const snapshotSpinner = json ? null : startSpinner(label);
|
|
704
|
+
let attemptError;
|
|
705
|
+
try {
|
|
706
|
+
const snapshotResult = await pushSnapshot(client, prePushDraft.agentId, prePushDraft.versionId, projectRoot, fileData);
|
|
707
|
+
if (snapshotResult.success) {
|
|
708
|
+
const okMsg = attempt === 1
|
|
709
|
+
? `Snapshot pushed (${fileData.length} file(s))`
|
|
710
|
+
: `Snapshot pushed (${fileData.length} file(s), attempt ${attempt}/${SNAPSHOT_MAX_ATTEMPTS})`;
|
|
711
|
+
if (snapshotSpinner)
|
|
712
|
+
snapshotSpinner.succeed(okMsg);
|
|
713
|
+
else
|
|
714
|
+
stderrLog(okMsg);
|
|
715
|
+
snapshotSucceeded = true;
|
|
716
|
+
snapshotError = undefined;
|
|
717
|
+
break;
|
|
718
|
+
}
|
|
719
|
+
attemptError = snapshotResult.error || "Unknown snapshot error";
|
|
720
|
+
}
|
|
721
|
+
catch (error) {
|
|
722
|
+
attemptError = error instanceof Error ? error.message : String(error);
|
|
723
|
+
}
|
|
724
|
+
snapshotError = attemptError;
|
|
725
|
+
const isLast = attempt === SNAPSHOT_MAX_ATTEMPTS;
|
|
726
|
+
const failMsg = isLast
|
|
727
|
+
? `Snapshot push failed after ${attempt} attempt(s): ${attemptError}`
|
|
728
|
+
: `Snapshot push failed (attempt ${attempt}/${SNAPSHOT_MAX_ATTEMPTS}): ${attemptError}`;
|
|
729
|
+
if (snapshotSpinner)
|
|
730
|
+
snapshotSpinner.fail(failMsg);
|
|
731
|
+
else
|
|
732
|
+
stderrLog(failMsg);
|
|
733
|
+
if (isLast)
|
|
734
|
+
break;
|
|
735
|
+
const delayMs = SNAPSHOT_RETRY_BASE_MS * Math.pow(2, attempt - 1);
|
|
736
|
+
if (json)
|
|
737
|
+
stderrLog(`Waiting ${delayMs}ms before retry...`);
|
|
738
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
739
|
+
}
|
|
740
|
+
pushSpan.setAttribute("push.snapshot.success", snapshotSucceeded);
|
|
741
|
+
pushSpan.setAttribute("push.snapshot.attempts", snapshotAttempts);
|
|
742
|
+
if (!snapshotSucceeded && snapshotError) {
|
|
743
|
+
pushSpan.setAttribute("push.snapshot.error", snapshotError);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
const snapshotStatus = prePushDraft
|
|
747
|
+
? snapshotSucceeded
|
|
748
|
+
? "succeeded"
|
|
749
|
+
: "failed"
|
|
750
|
+
: undefined;
|
|
670
751
|
const memoryPath = writePushMemory(projectRoot, agentCodeStr, agentIdStr, pushTasks, succeededFiles, pushFailures);
|
|
752
|
+
// ─── Baseline Update ───
|
|
753
|
+
// Only commit baseline if snapshot succeeded (or no draft = legacy mode).
|
|
754
|
+
// This ensures: if snapshot fails, user re-runs `aui push` to retry both
|
|
755
|
+
// failed entity pushes AND the snapshot. Local files remain the source
|
|
756
|
+
// of truth until the server has captured them.
|
|
671
757
|
let baselineUpdated = false;
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
758
|
+
const canCommitBaseline = !prePushDraft || snapshotSucceeded;
|
|
759
|
+
if (canCommitBaseline) {
|
|
760
|
+
if (failed > 0 && succeeded > 0) {
|
|
761
|
+
if (succeededFiles.length > 0) {
|
|
762
|
+
commitBaselineFiles(projectRoot, succeededFiles, `pushed ${succeeded} change(s)`);
|
|
763
|
+
baselineUpdated = true;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
else if (failed === 0) {
|
|
767
|
+
commitBaseline(projectRoot, "pushed changes");
|
|
675
768
|
baselineUpdated = true;
|
|
676
769
|
}
|
|
677
770
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
771
|
+
log(_jsx(PushFinalSummary, { succeeded: succeeded, failed: failed, baselineUpdated: baselineUpdated, logDir: logRelPath, memoryPath: memoryPath, snapshotStatus: snapshotStatus, snapshotError: snapshotError }));
|
|
772
|
+
if (failed > 0) {
|
|
773
|
+
log(_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(StatusLine, { kind: "warning", label: `${failed} entity change(s) failed to push to DB.` }), pushFailures.map((f) => (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { color: "red", children: [" ", icons.error, " ", f.label] }), _jsxs(Text, { color: colors.muted, children: [" Error: ", f.error] }), f.file && _jsxs(Text, { color: colors.muted, children: [" File: ", f.file] })] }, f.label))), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.info, bold: true, children: "What to do next: " }), _jsxs(Text, { color: colors.muted, children: ["Fix the issues above and re-run ", _jsx(Text, { bold: true, children: "aui push" }), " to retry the failed changes."] })] })] }));
|
|
681
774
|
}
|
|
682
|
-
|
|
775
|
+
// ─── Snapshot result message ───
|
|
683
776
|
if (prePushDraft) {
|
|
684
|
-
|
|
777
|
+
if (!snapshotSucceeded) {
|
|
778
|
+
log(_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(StatusLine, { kind: "error", label: `Snapshot upload failed${snapshotError ? `: ${snapshotError}` : ""}` }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: colors.info, bold: true, children: "What to do next: " }), _jsxs(Text, { color: colors.muted, children: ["Re-run ", _jsx(Text, { bold: true, children: "aui push" }), " to retry the snapshot. Your local files are the source of truth \u2014 they remain unchanged."] })] })] }));
|
|
779
|
+
}
|
|
780
|
+
else if (failed === 0) {
|
|
781
|
+
log(_jsx(StatusLine, { kind: "info", label: `All changes pushed into draft ${prePushDraft.label}. Run: aui version publish → aui version activate` }));
|
|
782
|
+
}
|
|
783
|
+
else {
|
|
784
|
+
log(_jsx(StatusLine, { kind: "warning", label: `Snapshot saved for draft ${prePushDraft.label}, but ${failed} DB update(s) failed. Re-run "aui push" to retry the DB updates before publishing.` }));
|
|
785
|
+
}
|
|
685
786
|
}
|
|
787
|
+
const snapshotFailed = snapshotStatus === "failed";
|
|
686
788
|
pushSpan.setAttribute("push.exit_reason", failed > 0
|
|
687
789
|
? succeeded > 0
|
|
688
790
|
? "partial_failure"
|
|
689
791
|
: "failed"
|
|
690
|
-
:
|
|
792
|
+
: snapshotFailed
|
|
793
|
+
? "snapshot_failed"
|
|
794
|
+
: "completed");
|
|
691
795
|
pushSpan.setAttribute("push.succeeded_count", succeeded);
|
|
692
796
|
pushSpan.setAttribute("push.failed_count", failed);
|
|
797
|
+
// Snapshot failure must propagate as a non-zero exit code. Without this
|
|
798
|
+
// throw, `failed === 0` would let the command exit 0 and CI/CD would
|
|
799
|
+
// never notice the snapshot upload failed. handleError() also produces
|
|
800
|
+
// a structured JSON envelope when --json is set.
|
|
801
|
+
if (snapshotFailed) {
|
|
802
|
+
throw new CLIError(`Snapshot upload failed${snapshotError ? `: ${snapshotError}` : ""}`, {
|
|
803
|
+
suggestion: "Re-run `aui push` to retry the snapshot. Your local files are the source of truth — they remain unchanged.",
|
|
804
|
+
});
|
|
805
|
+
}
|
|
693
806
|
}
|
|
694
807
|
catch (error) {
|
|
695
808
|
if (spinner)
|
|
696
809
|
spinner.fail("Push failed");
|
|
810
|
+
// CLIErrors carry actionable info and a meaningful exit code — let
|
|
811
|
+
// handleError() format them and exit non-zero. Without this re-throw
|
|
812
|
+
// a snapshot-failure CLIError thrown above would be swallowed in TUI
|
|
813
|
+
// mode and the process would exit 0.
|
|
814
|
+
if (error instanceof CLIError) {
|
|
815
|
+
throw error;
|
|
816
|
+
}
|
|
697
817
|
if (!json)
|
|
698
818
|
log(_jsx(ErrorDisplay, { error: error }));
|
|
699
819
|
else
|
|
@@ -711,110 +831,133 @@ async function resolveVersionDraft(config, projectConfig, session, explicitVersi
|
|
|
711
831
|
const key = loadAgentSettingsApiKey();
|
|
712
832
|
if (key)
|
|
713
833
|
client.setAgentSettingsApiKey(key);
|
|
714
|
-
|
|
715
|
-
|
|
834
|
+
let agentInfo;
|
|
835
|
+
const agentMgmtId = session.agent_management_id;
|
|
836
|
+
// Project's network_id (from .auirc) takes priority over session — when
|
|
837
|
+
// you're inside a project, that's the agent you mean. Session agent may
|
|
838
|
+
// point at a different agent (e.g. last `aui agents --switch`).
|
|
839
|
+
const projectNetworkId = projectConfig.agent_id;
|
|
840
|
+
const fallbackNetworkId = session.network_id;
|
|
841
|
+
if (projectNetworkId) {
|
|
716
842
|
try {
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
const ver = await client.agentManagement.getVersion(agentMgmtId, explicitVersionId);
|
|
720
|
-
const label = `v${ver.version_number}.${ver.version_revision_number}`;
|
|
721
|
-
return { versionId: ver.id, label };
|
|
722
|
-
}
|
|
843
|
+
const resp = await client.agentManagement.listAgents(client.getOrganizationId(), 1, 50, { network_id: projectNetworkId });
|
|
844
|
+
agentInfo = resp.items.find((a) => a.scope.network_id === projectNetworkId || a.id === projectNetworkId);
|
|
723
845
|
}
|
|
724
846
|
catch {
|
|
725
|
-
// fall through
|
|
847
|
+
// listing failed, fall through
|
|
726
848
|
}
|
|
727
|
-
return { versionId: explicitVersionId, label: explicitVersionId.slice(0, 10) };
|
|
728
849
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
const networkId = projectConfig.agent_id || session.network_id;
|
|
732
|
-
if (agentMgmtId) {
|
|
850
|
+
// Fall back to session's agent_management_id only when not inside a project
|
|
851
|
+
if (!agentInfo && !projectNetworkId && agentMgmtId) {
|
|
733
852
|
try {
|
|
734
853
|
agentInfo = await client.agentManagement.getAgent(agentMgmtId);
|
|
735
854
|
}
|
|
736
855
|
catch {
|
|
737
|
-
// stale ID
|
|
856
|
+
// stale ID, fall through
|
|
738
857
|
}
|
|
739
858
|
}
|
|
740
|
-
|
|
859
|
+
// Last resort: session's network_id
|
|
860
|
+
if (!agentInfo && fallbackNetworkId) {
|
|
741
861
|
try {
|
|
742
|
-
const
|
|
743
|
-
|
|
744
|
-
let hasMore = true;
|
|
745
|
-
while (hasMore) {
|
|
746
|
-
const resp = await client.agentManagement.listAgents(client.getOrganizationId(), page, 50);
|
|
747
|
-
allAgents.push(...resp.items);
|
|
748
|
-
hasMore = page < resp.pages;
|
|
749
|
-
page++;
|
|
750
|
-
}
|
|
751
|
-
agentInfo = allAgents.find((a) => a.scope.network_id === networkId || a.id === networkId);
|
|
862
|
+
const resp = await client.agentManagement.listAgents(client.getOrganizationId(), 1, 50, { network_id: fallbackNetworkId });
|
|
863
|
+
agentInfo = resp.items.find((a) => a.scope.network_id === fallbackNetworkId || a.id === fallbackNetworkId);
|
|
752
864
|
}
|
|
753
865
|
catch {
|
|
754
866
|
// no agent-management available
|
|
755
867
|
}
|
|
756
868
|
}
|
|
757
|
-
if (!agentInfo)
|
|
869
|
+
if (!agentInfo) {
|
|
870
|
+
log(_jsx(ErrorDisplay, { message: "Could not resolve agent for version management.", suggestion: "Run `aui import-agent` to link an agent, or check your session with `aui status`." }));
|
|
758
871
|
return null;
|
|
872
|
+
}
|
|
873
|
+
// If user passed --version-id, validate it's a draft
|
|
874
|
+
if (explicitVersionId) {
|
|
875
|
+
try {
|
|
876
|
+
const ver = await client.agentManagement.getVersion(agentInfo.id, explicitVersionId);
|
|
877
|
+
if (ver.status !== "draft") {
|
|
878
|
+
log(_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(StatusLine, { kind: "error", label: `Version v${ver.version_number} is "${ver.status}" — you can only push to a draft version.` }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.info, bold: true, children: "What to do next:" }), _jsxs(Text, { color: colors.muted, children: [" 1. Create a new draft version: ", _jsx(Text, { bold: true, children: "aui version create" })] }), _jsxs(Text, { color: colors.muted, children: [" 2. Then push with: ", _jsxs(Text, { bold: true, children: ["aui push --version-id ", '<new-draft-id>'] })] })] })] }));
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
const label = `v${ver.version_number}`;
|
|
882
|
+
return { versionId: ver.id, label, agentId: agentInfo.id };
|
|
883
|
+
}
|
|
884
|
+
catch (error) {
|
|
885
|
+
log(_jsx(ErrorDisplay, { message: `Could not fetch version "${explicitVersionId}": ${error instanceof Error ? error.message : String(error)}`, suggestion: "Check the version ID with `aui version list` and try again." }));
|
|
886
|
+
return null;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
// Resolve from .auirc version_id or auto-detect drafts
|
|
759
890
|
let allVersions = [];
|
|
760
891
|
try {
|
|
761
892
|
const versionsResp = await client.agentManagement.listVersions(agentInfo.id, 1, 50);
|
|
762
893
|
allVersions = versionsResp.items;
|
|
763
894
|
}
|
|
764
895
|
catch {
|
|
896
|
+
log(_jsx(ErrorDisplay, { message: "Could not fetch versions for this agent.", suggestion: "Check your connection and try again. Use `aui version list` to debug." }));
|
|
765
897
|
return null;
|
|
766
898
|
}
|
|
899
|
+
// If .auirc has a version_id, validate it's still a draft
|
|
900
|
+
if (projectConfig.version_id) {
|
|
901
|
+
const configVersion = allVersions.find((v) => v.id === projectConfig.version_id);
|
|
902
|
+
if (configVersion) {
|
|
903
|
+
if (configVersion.status !== "draft") {
|
|
904
|
+
log(_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(StatusLine, { kind: "error", label: `The version in your .auirc (v${configVersion.version_number}) is "${configVersion.status}" — you can only push to a draft version.` }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.info, bold: true, children: "What to do next:" }), _jsxs(Text, { color: colors.muted, children: [" 1. Create a new draft version: ", _jsx(Text, { bold: true, children: "aui version create" })] }), _jsxs(Text, { color: colors.muted, children: [" 2. Import the new draft: ", _jsxs(Text, { bold: true, children: ["aui import-agent --version ", '<new-draft-id>'] })] }), _jsxs(Text, { color: colors.muted, children: [" 3. Then push your changes: ", _jsx(Text, { bold: true, children: "aui push" })] })] })] }));
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
const label = `v${configVersion.version_number}`;
|
|
908
|
+
return { versionId: configVersion.id, label, agentId: agentInfo.id };
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
// Auto-detect drafts
|
|
767
912
|
const drafts = allVersions.filter((v) => v.status === "draft");
|
|
768
|
-
|
|
913
|
+
if (drafts.length === 0) {
|
|
914
|
+
log(_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(StatusLine, { kind: "error", label: "No draft version found \u2014 you can only push to a draft version." }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: colors.info, bold: true, children: "What to do next:" }), _jsxs(Text, { color: colors.muted, children: [" 1. Create a new draft version: ", _jsx(Text, { bold: true, children: "aui version create" })] }), _jsxs(Text, { color: colors.muted, children: [" 2. Import the draft: ", _jsxs(Text, { bold: true, children: ["aui import-agent --version ", '<draft-id>'] })] }), _jsxs(Text, { color: colors.muted, children: [" 3. Then push your changes: ", _jsx(Text, { bold: true, children: "aui push" })] })] })] }));
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
769
917
|
if (drafts.length === 1) {
|
|
770
918
|
const draft = drafts[0];
|
|
771
|
-
const label = `v${draft.version_number}
|
|
772
|
-
return { versionId: draft.id, label };
|
|
919
|
+
const label = `v${draft.version_number}`;
|
|
920
|
+
return { versionId: draft.id, label, agentId: agentInfo.id };
|
|
773
921
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
}
|
|
781
|
-
const { selected } = await inquirer.prompt([
|
|
782
|
-
{
|
|
783
|
-
type: "list",
|
|
784
|
-
name: "selected",
|
|
785
|
-
message: `${drafts.length} drafts found. Push into which draft?`,
|
|
786
|
-
choices: drafts.map((d) => {
|
|
787
|
-
const dl = `v${d.version_number}.${d.version_revision_number}`;
|
|
788
|
-
return {
|
|
789
|
-
name: `${dl} — ${d.stats?.total ?? "?"} entities ${d.label || ""}`,
|
|
790
|
-
value: d,
|
|
791
|
-
};
|
|
792
|
-
}),
|
|
793
|
-
pageSize: 10,
|
|
794
|
-
},
|
|
795
|
-
]);
|
|
796
|
-
const chosen = selected;
|
|
797
|
-
const label = `v${chosen.version_number}.${chosen.version_revision_number}`;
|
|
798
|
-
return { versionId: chosen.id, label };
|
|
922
|
+
// Multiple drafts — auto-pick the latest (drafts are sorted DESC by created_at)
|
|
923
|
+
// No interactive prompt: keeps `aui push` non-blocking. Use --version-id to
|
|
924
|
+
// explicitly target a different draft.
|
|
925
|
+
const latest = drafts[0];
|
|
926
|
+
const latestLabel = `v${latest.version_number}`;
|
|
927
|
+
if (!isJsonMode()) {
|
|
928
|
+
log(_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(StatusLine, { kind: "info", label: `${drafts.length} drafts found — auto-selecting latest: ${latestLabel}` }), _jsxs(Text, { color: colors.muted, children: [" Tip: pass ", _jsxs(Text, { bold: true, children: ["--version-id ", '<id>'] }), " to target a specific draft."] })] }));
|
|
799
929
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
930
|
+
return { versionId: latest.id, label: latestLabel, agentId: agentInfo.id };
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Push a snapshot of all local agent files to the server via multipart upload.
|
|
934
|
+
* This captures the current state of the files to maintain version history.
|
|
935
|
+
* Version bump happens automatically on the server when snapshot succeeds.
|
|
936
|
+
*
|
|
937
|
+
* If any files fail, the user must re-run `aui push` to retry.
|
|
938
|
+
*/
|
|
939
|
+
async function pushSnapshot(client, agentId, versionId, projectRoot, fileData) {
|
|
940
|
+
const filesToUpload = [];
|
|
941
|
+
for (const fd of fileData) {
|
|
942
|
+
const fullPath = path.join(projectRoot, fd.file);
|
|
943
|
+
if (!fs.existsSync(fullPath))
|
|
944
|
+
continue;
|
|
945
|
+
const content = fs.readFileSync(fullPath);
|
|
946
|
+
filesToUpload.push({
|
|
947
|
+
filePath: fullPath,
|
|
948
|
+
fileName: path.basename(fd.file),
|
|
949
|
+
content,
|
|
809
950
|
});
|
|
810
|
-
const label = `v${draft.version_number}.${draft.version_revision_number}`;
|
|
811
|
-
versionSpinner.succeed(`Version draft created: ${label}`);
|
|
812
|
-
return { versionId: draft.id, label };
|
|
813
951
|
}
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
return null;
|
|
952
|
+
if (filesToUpload.length === 0) {
|
|
953
|
+
return { success: true, failed: [] };
|
|
817
954
|
}
|
|
955
|
+
const result = await client.agentManagement.pushSnapshot(agentId, versionId, filesToUpload);
|
|
956
|
+
return {
|
|
957
|
+
success: result.success,
|
|
958
|
+
failed: result.failed,
|
|
959
|
+
error: result.error,
|
|
960
|
+
};
|
|
818
961
|
}
|
|
819
962
|
// ─── Agent Settings Params Resolution ───
|
|
820
963
|
async function resolveAgentSettingsParams(config, projectConfig, session, projectRoot, scopeLevel) {
|