@remixhq/mcp 0.1.12 → 0.1.14
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/cli.js +351 -65
- package/dist/cli.js.map +1 -1
- package/dist/index.js +336 -61
- package/dist/index.js.map +1 -1
- package/dist/server.js +336 -61
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -352,14 +352,6 @@ function assertConfirm(confirm, operation) {
|
|
|
352
352
|
if (confirm) return;
|
|
353
353
|
throw createPolicyError(`${operation} requires explicit confirmation.`, "Pass confirm=true to run this tool.");
|
|
354
354
|
}
|
|
355
|
-
function assertDiffWithinLimit(policy, diff) {
|
|
356
|
-
const sizeBytes = Buffer.byteLength(diff, "utf8");
|
|
357
|
-
if (sizeBytes <= policy.maxDiffBytes) return;
|
|
358
|
-
throw createPolicyError(
|
|
359
|
-
"Diff exceeds the configured maximum size for Remix MCP.",
|
|
360
|
-
`Configured limit=${policy.maxDiffBytes} bytes actual=${sizeBytes} bytes.`
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
355
|
|
|
364
356
|
// src/bootstrap/context.ts
|
|
365
357
|
function createServerContext(params) {
|
|
@@ -507,8 +499,6 @@ var finalizeTurnInputSchema = {
|
|
|
507
499
|
...commonRequestFieldsSchema,
|
|
508
500
|
prompt: z2.string().trim().min(1),
|
|
509
501
|
assistantResponse: z2.string().trim().min(1),
|
|
510
|
-
diffSource: z2.enum(["worktree", "external"]).optional(),
|
|
511
|
-
externalDiff: z2.string().optional(),
|
|
512
502
|
sync: z2.boolean().optional(),
|
|
513
503
|
allowBranchMismatch: z2.boolean().optional(),
|
|
514
504
|
idempotencyKey: z2.string().trim().min(1).optional()
|
|
@@ -516,11 +506,17 @@ var finalizeTurnInputSchema = {
|
|
|
516
506
|
var previewInputSchema = {
|
|
517
507
|
...commonRequestFieldsSchema
|
|
518
508
|
};
|
|
509
|
+
var drainFinalizeQueueInputSchema = {
|
|
510
|
+
...commonRequestFieldsSchema
|
|
511
|
+
};
|
|
519
512
|
var applyInputSchema = {
|
|
520
513
|
...commonRequestFieldsSchema,
|
|
521
514
|
confirm: z2.boolean(),
|
|
522
515
|
allowBranchMismatch: z2.boolean().optional()
|
|
523
516
|
};
|
|
517
|
+
var reAnchorInputSchema = {
|
|
518
|
+
...applyInputSchema
|
|
519
|
+
};
|
|
524
520
|
var requestMergeInputSchema = {
|
|
525
521
|
...commonRequestFieldsSchema
|
|
526
522
|
};
|
|
@@ -623,7 +619,7 @@ var statusDataSchema = z2.object({
|
|
|
623
619
|
status: genericRecordSchema,
|
|
624
620
|
riskLevel: z2.enum(["low", "medium", "high"])
|
|
625
621
|
});
|
|
626
|
-
var
|
|
622
|
+
var initSyncDataSchema = z2.object({
|
|
627
623
|
reused: z2.boolean(),
|
|
628
624
|
projectId: z2.string(),
|
|
629
625
|
appId: z2.string(),
|
|
@@ -632,8 +628,26 @@ var initDataSchema = z2.object({
|
|
|
632
628
|
bindingPath: z2.string(),
|
|
633
629
|
repoRoot: z2.string(),
|
|
634
630
|
bindingMode: z2.enum(["legacy", "lane", "explicit_root"]).optional(),
|
|
635
|
-
createdCanonicalFamily: z2.boolean().optional()
|
|
631
|
+
createdCanonicalFamily: z2.boolean().optional(),
|
|
632
|
+
baselineStatus: z2.enum(["seeded", "existing", "requires_re_anchor", "requires_sync"]).optional()
|
|
633
|
+
});
|
|
634
|
+
var initQueuedDataSchema = z2.object({
|
|
635
|
+
queued: z2.literal(true),
|
|
636
|
+
status: z2.literal("ready_to_record"),
|
|
637
|
+
doNotRetry: z2.literal(true),
|
|
638
|
+
jobId: z2.string(),
|
|
639
|
+
projectId: z2.string(),
|
|
640
|
+
appId: z2.string(),
|
|
641
|
+
dashboardUrl: z2.string(),
|
|
642
|
+
upstreamAppId: z2.string(),
|
|
643
|
+
bindingPath: z2.string(),
|
|
644
|
+
repoRoot: z2.string(),
|
|
645
|
+
bindingMode: z2.enum(["legacy", "lane", "explicit_root"]),
|
|
646
|
+
createdCanonicalFamily: z2.boolean(),
|
|
647
|
+
remoteUrl: z2.string().nullable(),
|
|
648
|
+
defaultBranch: z2.string().nullable()
|
|
636
649
|
});
|
|
650
|
+
var initDataSchema = initSyncDataSchema;
|
|
637
651
|
var listDataSchema = z2.object({
|
|
638
652
|
apps: genericArraySchema,
|
|
639
653
|
pagination: paginationSchema
|
|
@@ -655,19 +669,26 @@ var checkoutDataSchema = z2.object({
|
|
|
655
669
|
repoRoot: z2.string()
|
|
656
670
|
});
|
|
657
671
|
var addDataSchema = z2.object({
|
|
658
|
-
changeStep: genericRecordSchema
|
|
659
|
-
autoSync: genericRecordSchema
|
|
672
|
+
changeStep: genericRecordSchema
|
|
660
673
|
});
|
|
661
674
|
var recordTurnDataSchema = genericRecordSchema;
|
|
662
675
|
var finalizeTurnDataSchema = z2.object({
|
|
663
676
|
mode: z2.enum(["changed_turn", "no_diff_turn"]),
|
|
664
677
|
idempotencyKey: z2.string().min(1),
|
|
678
|
+
queued: z2.boolean(),
|
|
679
|
+
jobId: z2.string().nullable(),
|
|
680
|
+
repoState: z2.string().nullable(),
|
|
665
681
|
changeStep: genericRecordSchema.nullable(),
|
|
666
682
|
collabTurn: genericRecordSchema.nullable(),
|
|
667
683
|
autoSync: genericRecordSchema.nullable(),
|
|
668
684
|
warnings: z2.array(z2.string())
|
|
669
685
|
});
|
|
686
|
+
var drainFinalizeQueueDataSchema = z2.object({
|
|
687
|
+
processed: z2.number().int().nonnegative(),
|
|
688
|
+
results: z2.array(genericRecordSchema)
|
|
689
|
+
});
|
|
670
690
|
var syncDataSchema = genericRecordSchema;
|
|
691
|
+
var reAnchorDataSchema = genericRecordSchema;
|
|
671
692
|
var requestMergeDataSchema = genericRecordSchema;
|
|
672
693
|
var mergeRequestQueueDataSchema = z2.object({
|
|
673
694
|
queue: mergeRequestQueueSchema,
|
|
@@ -742,7 +763,9 @@ var checkoutSuccessSchema = makeSuccessSchema(checkoutDataSchema);
|
|
|
742
763
|
var addSuccessSchema = makeSuccessSchema(addDataSchema);
|
|
743
764
|
var recordTurnSuccessSchema = makeSuccessSchema(recordTurnDataSchema);
|
|
744
765
|
var finalizeTurnSuccessSchema = makeSuccessSchema(finalizeTurnDataSchema);
|
|
766
|
+
var drainFinalizeQueueSuccessSchema = makeSuccessSchema(drainFinalizeQueueDataSchema);
|
|
745
767
|
var syncSuccessSchema = makeSuccessSchema(syncDataSchema);
|
|
768
|
+
var reAnchorSuccessSchema = makeSuccessSchema(reAnchorDataSchema);
|
|
746
769
|
var requestMergeSuccessSchema = makeSuccessSchema(requestMergeDataSchema);
|
|
747
770
|
var mergeRequestQueueSuccessSchema = makeSuccessSchema(mergeRequestQueueDataSchema);
|
|
748
771
|
var viewMergeRequestSuccessSchema = makeSuccessSchema(viewMergeRequestDataSchema);
|
|
@@ -760,17 +783,18 @@ var accessDebugSuccessSchema = makeSuccessSchema(accessDebugDataSchema);
|
|
|
760
783
|
var updateMemberRoleSuccessSchema = makeSuccessSchema(updateMemberRoleDataSchema);
|
|
761
784
|
|
|
762
785
|
// src/domain/coreAdapter.ts
|
|
786
|
+
import { spawn } from "child_process";
|
|
763
787
|
import {
|
|
764
788
|
collabList as coreCollabList,
|
|
765
789
|
collabListMembers as coreCollabListMembers,
|
|
766
790
|
collabUpdateMemberRole as coreCollabUpdateMemberRole,
|
|
767
|
-
|
|
791
|
+
drainPendingFinalizeQueue as coreDrainPendingFinalizeQueue,
|
|
768
792
|
collabFinalizeTurn as coreCollabFinalizeTurn,
|
|
769
|
-
collabRecordTurn as coreCollabRecordTurn,
|
|
770
793
|
collabApprove as coreCollabApprove,
|
|
771
794
|
collabCheckout as coreCollabCheckout,
|
|
772
795
|
collabListMergeRequests as coreCollabListMergeRequests,
|
|
773
796
|
collabInit as coreCollabInit,
|
|
797
|
+
collabReAnchor as coreCollabReAnchor,
|
|
774
798
|
collabInvite as coreCollabInvite,
|
|
775
799
|
collabReconcile as coreCollabReconcile,
|
|
776
800
|
collabReject as coreCollabReject,
|
|
@@ -781,11 +805,13 @@ import {
|
|
|
781
805
|
collabSyncUpstream as coreCollabSyncUpstream,
|
|
782
806
|
collabView as coreCollabView
|
|
783
807
|
} from "@remixhq/core/collab";
|
|
784
|
-
import { findGitRoot
|
|
808
|
+
import { findGitRoot } from "@remixhq/core/repo";
|
|
785
809
|
function getRiskLevel(status) {
|
|
786
810
|
if (status.recommendedAction === "reconcile") return "high";
|
|
787
|
-
if (status.recommendedAction === "choose_family") return "medium";
|
|
788
|
-
if (status.recommendedAction === "
|
|
811
|
+
if (status.recommendedAction === "choose_family" || status.recommendedAction === "await_finalize") return "medium";
|
|
812
|
+
if (status.recommendedAction === "pull" || status.recommendedAction === "re_anchor" || status.remote.incomingOpenMergeRequestCount) {
|
|
813
|
+
return "medium";
|
|
814
|
+
}
|
|
789
815
|
if (status.repo.branchMismatch || !status.repo.isGitRepo || !status.binding.isBound || !status.repo.worktree.isClean) return "medium";
|
|
790
816
|
return "low";
|
|
791
817
|
}
|
|
@@ -798,10 +824,22 @@ function getRecommendedNextActions(status) {
|
|
|
798
824
|
switch (status.recommendedAction) {
|
|
799
825
|
case "init":
|
|
800
826
|
return ["Run remix_collab_init to bind the repository to Remix before using any Remix collaboration mutation flow."];
|
|
801
|
-
case "
|
|
802
|
-
return ["Run remix_collab_sync_preview, then remix_collab_sync_apply if the preview is acceptable.
|
|
827
|
+
case "pull":
|
|
828
|
+
return ["Run remix_collab_sync_preview, then remix_collab_sync_apply if the preview is acceptable. This pulls the server delta into the local working tree without rewriting local git history."];
|
|
829
|
+
case "re_anchor":
|
|
830
|
+
return [
|
|
831
|
+
"Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply. This seeds a local Remix baseline. It is required because no local baseline exists for this lane yet (fresh clone, deleted .remix/ state, or first init didn't seed) \u2014 not because of any specific git operation. After it succeeds, normal recording (remix_collab_finalize_turn) becomes available."
|
|
832
|
+
];
|
|
833
|
+
case "record":
|
|
834
|
+
return [
|
|
835
|
+
"Run remix_collab_finalize_turn to capture the local boundary delta. This is the catch-all for any local content change since the last recorded turn, regardless of whether the change came from agent edits, manual user edits, git commit, git pull, git merge, git rebase, or git reset."
|
|
836
|
+
];
|
|
803
837
|
case "reconcile":
|
|
804
|
-
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile
|
|
838
|
+
return ["Run remix_collab_reconcile_preview before attempting remix_collab_reconcile_apply. Reconcile applies only when both the local workspace and the server lane changed since the last agreed baseline."];
|
|
839
|
+
case "await_finalize":
|
|
840
|
+
return [
|
|
841
|
+
"Run remix_collab_drain_finalize_queue before merge-related or recovery flows. finalize_turn is queued only until the local finalize queue is drained."
|
|
842
|
+
];
|
|
805
843
|
case "review_queue":
|
|
806
844
|
return ["Run remix_collab_review_queue to inspect reviewable merge requests instead of using local git merge flows."];
|
|
807
845
|
case "choose_family":
|
|
@@ -835,6 +873,21 @@ function truncateText(value, maxChars) {
|
|
|
835
873
|
originalChars: value.length
|
|
836
874
|
};
|
|
837
875
|
}
|
|
876
|
+
function spawnFinalizeQueueDrainer() {
|
|
877
|
+
const entrypoint = process.argv[1];
|
|
878
|
+
if (!entrypoint) return false;
|
|
879
|
+
const child = spawn(process.execPath, [...process.execArgv, entrypoint, "--drain-finalize-queue"], {
|
|
880
|
+
detached: true,
|
|
881
|
+
stdio: "ignore",
|
|
882
|
+
env: process.env
|
|
883
|
+
});
|
|
884
|
+
child.unref();
|
|
885
|
+
return true;
|
|
886
|
+
}
|
|
887
|
+
async function drainBeforeMutation(api) {
|
|
888
|
+
const results = await coreDrainPendingFinalizeQueue({ api });
|
|
889
|
+
return results.flatMap((result) => collectResultWarnings(result));
|
|
890
|
+
}
|
|
838
891
|
async function getStatus(params) {
|
|
839
892
|
const api = params.includeRemote ? await createCollabApiClient() : null;
|
|
840
893
|
const status = await coreCollabStatus({
|
|
@@ -860,15 +913,26 @@ async function initCollab(params) {
|
|
|
860
913
|
api,
|
|
861
914
|
cwd: params.cwd,
|
|
862
915
|
appName: params.appName ?? null,
|
|
863
|
-
forceNew: params.forceNew ?? false
|
|
916
|
+
forceNew: params.forceNew ?? false,
|
|
917
|
+
asyncSubmit: false
|
|
864
918
|
});
|
|
919
|
+
if ("queued" in result && result.queued) {
|
|
920
|
+
throw new Error(
|
|
921
|
+
"Unexpected queued init result on the MCP path. Async init is currently disabled for agent callers; if you intended to re-enable it, also restore the queued shape in initDataSchema (see remix-mcp/src/contracts/collab.ts)."
|
|
922
|
+
);
|
|
923
|
+
}
|
|
924
|
+
const syncResult = result;
|
|
865
925
|
return {
|
|
866
|
-
data:
|
|
926
|
+
data: syncResult,
|
|
867
927
|
warnings: collectResultWarnings(result),
|
|
868
|
-
recommendedNextActions:
|
|
928
|
+
recommendedNextActions: syncResult.baselineStatus === "requires_re_anchor" ? [
|
|
929
|
+
"This checkout has no local Remix baseline yet. Run remix_collab_re_anchor_preview, then remix_collab_re_anchor_apply to seed one. After it succeeds, normal recording (remix_collab_finalize_turn) becomes available."
|
|
930
|
+
] : syncResult.baselineStatus === "requires_sync" ? [
|
|
931
|
+
"Run remix_collab_sync_preview, then remix_collab_sync_apply to pull the server delta and create the first local baseline for this checkout."
|
|
932
|
+
] : ["Run remix_collab_status to inspect sync, reconcile, and merge-request readiness before mutating bound-repo state."],
|
|
869
933
|
logContext: {
|
|
870
|
-
repoRoot:
|
|
871
|
-
appId:
|
|
934
|
+
repoRoot: syncResult.repoRoot,
|
|
935
|
+
appId: syncResult.appId
|
|
872
936
|
}
|
|
873
937
|
};
|
|
874
938
|
}
|
|
@@ -935,23 +999,46 @@ async function finalizeCollabTurn(params) {
|
|
|
935
999
|
cwd: params.cwd,
|
|
936
1000
|
prompt: params.prompt,
|
|
937
1001
|
assistantResponse: params.assistantResponse,
|
|
938
|
-
diff: params.externalDiff ?? null,
|
|
939
|
-
diffSource: params.diffSource,
|
|
940
1002
|
sync: params.sync,
|
|
941
1003
|
allowBranchMismatch: params.allowBranchMismatch ?? false,
|
|
942
1004
|
idempotencyKey: params.idempotencyKey ?? null,
|
|
943
|
-
actor: params.agent
|
|
1005
|
+
actor: params.agent,
|
|
1006
|
+
awaitingUsageDeadlineMs: params.awaitingUsageDeadlineMs ?? null
|
|
944
1007
|
});
|
|
1008
|
+
const hasAwaitingDeadline = typeof params.awaitingUsageDeadlineMs === "number" && params.awaitingUsageDeadlineMs > 0;
|
|
1009
|
+
if (result.queued && !hasAwaitingDeadline) {
|
|
1010
|
+
if (!spawnFinalizeQueueDrainer()) {
|
|
1011
|
+
await coreDrainPendingFinalizeQueue({ api });
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
945
1014
|
return {
|
|
946
1015
|
data: result,
|
|
947
1016
|
warnings: result.warnings,
|
|
948
|
-
recommendedNextActions: [],
|
|
1017
|
+
recommendedNextActions: result.queued ? ["Run remix_collab_drain_finalize_queue before merge-related flows if you need this queued turn recorded immediately."] : [],
|
|
949
1018
|
logContext: {
|
|
950
1019
|
repoRoot,
|
|
951
1020
|
appId: result.changeStep?.appId ?? result.collabTurn?.appId ?? null
|
|
952
1021
|
}
|
|
953
1022
|
};
|
|
954
1023
|
}
|
|
1024
|
+
async function drainFinalizeQueue(params) {
|
|
1025
|
+
const api = await createCollabApiClient();
|
|
1026
|
+
const repoRoot = await findGitRoot(params.cwd);
|
|
1027
|
+
const results = await coreDrainPendingFinalizeQueue({ api });
|
|
1028
|
+
const warnings = results.flatMap((result) => collectResultWarnings(result));
|
|
1029
|
+
return {
|
|
1030
|
+
data: {
|
|
1031
|
+
processed: results.length,
|
|
1032
|
+
results
|
|
1033
|
+
},
|
|
1034
|
+
warnings,
|
|
1035
|
+
recommendedNextActions: [],
|
|
1036
|
+
logContext: {
|
|
1037
|
+
repoRoot,
|
|
1038
|
+
appId: null
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
955
1042
|
async function syncCollab(params) {
|
|
956
1043
|
const api = await createCollabApiClient();
|
|
957
1044
|
const result = await coreCollabSync({
|
|
@@ -963,21 +1050,45 @@ async function syncCollab(params) {
|
|
|
963
1050
|
return {
|
|
964
1051
|
data: result,
|
|
965
1052
|
warnings: collectResultWarnings(result),
|
|
966
|
-
recommendedNextActions: params.dryRun ? ["Run remix_collab_sync_apply with confirm=true to apply this
|
|
1053
|
+
recommendedNextActions: params.dryRun ? result.status === "delta_ready" ? ["Run remix_collab_sync_apply with confirm=true to apply this server delta into the local working tree."] : result.status === "base_unknown" ? [
|
|
1054
|
+
"Direct pull is unavailable because Remix cannot diff from the last acknowledged server head.",
|
|
1055
|
+
"Run remix_collab_reconcile_preview next to inspect recovery options before applying any recovery flow."
|
|
1056
|
+
] : [] : [],
|
|
967
1057
|
logContext: {
|
|
968
1058
|
repoRoot: result.repoRoot
|
|
969
1059
|
}
|
|
970
1060
|
};
|
|
971
1061
|
}
|
|
1062
|
+
async function reAnchor(params) {
|
|
1063
|
+
const api = await createCollabApiClient();
|
|
1064
|
+
const result = await coreCollabReAnchor({
|
|
1065
|
+
api,
|
|
1066
|
+
cwd: params.cwd,
|
|
1067
|
+
dryRun: params.dryRun,
|
|
1068
|
+
allowBranchMismatch: params.allowBranchMismatch ?? false
|
|
1069
|
+
});
|
|
1070
|
+
return {
|
|
1071
|
+
data: result,
|
|
1072
|
+
warnings: collectWarnings(result.warnings),
|
|
1073
|
+
recommendedNextActions: params.dryRun ? [
|
|
1074
|
+
"Run remix_collab_re_anchor_apply with confirm=true to seed a local Remix baseline for this checkout. Re-anchor is for missing-baseline cases only and does not replace remix_collab_finalize_turn for ordinary local content changes."
|
|
1075
|
+
] : [],
|
|
1076
|
+
logContext: {
|
|
1077
|
+
repoRoot: result.repoRoot,
|
|
1078
|
+
appId: result.currentAppId
|
|
1079
|
+
}
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
972
1082
|
async function requestMerge(params) {
|
|
973
1083
|
const api = await createCollabApiClient();
|
|
1084
|
+
const drainWarnings = await drainBeforeMutation(api);
|
|
974
1085
|
const result = await coreCollabRequestMerge({
|
|
975
1086
|
api,
|
|
976
1087
|
cwd: params.cwd
|
|
977
1088
|
});
|
|
978
1089
|
return {
|
|
979
1090
|
data: result,
|
|
980
|
-
warnings:
|
|
1091
|
+
warnings: drainWarnings,
|
|
981
1092
|
recommendedNextActions: result.id ? [`Run remix_collab_view_merge_request with mrId=${String(result.id)} to inspect the request before deciding whether to approve or reject it.`] : [],
|
|
982
1093
|
logContext: {
|
|
983
1094
|
mrId: typeof result.id === "string" ? result.id : null
|
|
@@ -1133,6 +1244,7 @@ async function syncUpstream(params) {
|
|
|
1133
1244
|
}
|
|
1134
1245
|
async function reconcile(params) {
|
|
1135
1246
|
const api = await createCollabApiClient();
|
|
1247
|
+
const drainWarnings = params.dryRun ? [] : await drainBeforeMutation(api);
|
|
1136
1248
|
const result = await coreCollabReconcile({
|
|
1137
1249
|
api,
|
|
1138
1250
|
cwd: params.cwd,
|
|
@@ -1141,9 +1253,9 @@ async function reconcile(params) {
|
|
|
1141
1253
|
});
|
|
1142
1254
|
return {
|
|
1143
1255
|
data: result,
|
|
1144
|
-
warnings: collectWarnings(result.warnings),
|
|
1145
|
-
recommendedNextActions: params.dryRun ? ["Run remix_collab_reconcile_apply with confirm=true only if the preview is acceptable.
|
|
1146
|
-
risks: params.dryRun ? ["Reconcile
|
|
1256
|
+
warnings: [...drainWarnings, ...collectWarnings(result.warnings)],
|
|
1257
|
+
recommendedNextActions: params.dryRun ? ["Run remix_collab_reconcile_apply with confirm=true only if the preview is acceptable. This is the explicit Remix recovery flow for diverged state."] : [],
|
|
1258
|
+
risks: params.dryRun ? ["Reconcile may upload local history to recover the server lane onto the latest agreed state before future recording continues."] : [],
|
|
1147
1259
|
logContext: {
|
|
1148
1260
|
repoRoot: result.repoRoot ?? null
|
|
1149
1261
|
}
|
|
@@ -1690,6 +1802,50 @@ async function accessDebug(params) {
|
|
|
1690
1802
|
};
|
|
1691
1803
|
}
|
|
1692
1804
|
|
|
1805
|
+
// src/tools/collab/autoSpawnHistoryImport.ts
|
|
1806
|
+
import { spawn as spawn2 } from "child_process";
|
|
1807
|
+
import { existsSync, mkdirSync, openSync } from "fs";
|
|
1808
|
+
import path2 from "path";
|
|
1809
|
+
var MARKER_REL_PATH = path2.join(".remix", ".history-imported");
|
|
1810
|
+
var LOG_REL_PATH = path2.join(".remix", "history-import.log");
|
|
1811
|
+
function shouldAutoSpawnHistoryImport(repoRoot) {
|
|
1812
|
+
try {
|
|
1813
|
+
return !existsSync(path2.join(repoRoot, MARKER_REL_PATH));
|
|
1814
|
+
} catch {
|
|
1815
|
+
return false;
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
function spawnHistoryImportDetached(repoRoot) {
|
|
1819
|
+
const remixDir = path2.join(repoRoot, ".remix");
|
|
1820
|
+
try {
|
|
1821
|
+
mkdirSync(remixDir, { recursive: true });
|
|
1822
|
+
} catch {
|
|
1823
|
+
}
|
|
1824
|
+
const logPath = path2.join(repoRoot, LOG_REL_PATH);
|
|
1825
|
+
const out = openSync(logPath, "a");
|
|
1826
|
+
const err = openSync(logPath, "a");
|
|
1827
|
+
const child = spawn2(
|
|
1828
|
+
"remix",
|
|
1829
|
+
[
|
|
1830
|
+
"history",
|
|
1831
|
+
"import",
|
|
1832
|
+
"--repo",
|
|
1833
|
+
repoRoot,
|
|
1834
|
+
// Include prompt text for parity with the CLI auto-spawn path:
|
|
1835
|
+
// first-time UX is a lot worse if the dashboard renders every
|
|
1836
|
+
// historical row as "(prompt not uploaded)".
|
|
1837
|
+
"--include-prompt-text"
|
|
1838
|
+
],
|
|
1839
|
+
{
|
|
1840
|
+
detached: true,
|
|
1841
|
+
stdio: ["ignore", out, err],
|
|
1842
|
+
env: { ...process.env, REMIX_HISTORY_AUTO_SPAWN: "1" }
|
|
1843
|
+
}
|
|
1844
|
+
);
|
|
1845
|
+
child.unref();
|
|
1846
|
+
return { pid: child.pid, logPath };
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1693
1849
|
// src/tools/collab/register.ts
|
|
1694
1850
|
function getAnnotations(access, options) {
|
|
1695
1851
|
if (access === "read") {
|
|
@@ -1719,9 +1875,6 @@ function buildSuccessEnvelope(tool, requestId, result) {
|
|
|
1719
1875
|
};
|
|
1720
1876
|
}
|
|
1721
1877
|
function deriveErrorRisks(tool, normalized) {
|
|
1722
|
-
if (isFinalizeTurnLocalSyncFailure(tool, normalized)) {
|
|
1723
|
-
return ["The change step succeeded remotely, but the local repository may need manual recovery or a follow-up sync."];
|
|
1724
|
-
}
|
|
1725
1878
|
if (normalized.code === "DESTRUCTIVE_OPERATION_BLOCKED") {
|
|
1726
1879
|
return ["A policy guard blocked a potentially destructive or state-mutating operation."];
|
|
1727
1880
|
}
|
|
@@ -1733,16 +1886,9 @@ function deriveErrorRisks(tool, normalized) {
|
|
|
1733
1886
|
}
|
|
1734
1887
|
return [];
|
|
1735
1888
|
}
|
|
1736
|
-
function isFinalizeTurnLocalSyncFailure(tool, normalized) {
|
|
1737
|
-
return tool === "remix_collab_finalize_turn" && normalized.message === "Change step succeeded remotely, but automatic local sync failed.";
|
|
1738
|
-
}
|
|
1739
1889
|
function buildErrorEnvelope(tool, requestId, error) {
|
|
1740
1890
|
const normalized = normalizeToolError(error);
|
|
1741
|
-
const recommendedNextActions =
|
|
1742
|
-
"Run `remix_collab_status` to confirm the bound repo state before attempting recovery.",
|
|
1743
|
-
"Run `remix_collab_sync_preview` next, then `remix_collab_sync_apply` with `confirm=true` if the preview looks correct.",
|
|
1744
|
-
"Inspect `error.hint` for any preserved diff backup path before retrying local recovery, and do not rerun `remix_collab_finalize_turn` immediately."
|
|
1745
|
-
] : normalized.code === "AUTH_REQUIRED" ? ["Set COMERGE_ACCESS_TOKEN, then retry the tool call."] : normalized.code === "REPO_LOCK_TIMEOUT" ? ["Wait for the active Remix mutation to finish, then retry the tool call."] : normalized.code === "REPO_STATE_CHANGED_DURING_OPERATION" ? ["Review local repository changes, then rerun the tool once the worktree is stable."] : normalized.code === "PREFERRED_BRANCH_MISMATCH" ? ["Switch to the repository's preferred Remix branch, or rerun with allowBranchMismatch=true if intentional."] : [];
|
|
1891
|
+
const recommendedNextActions = normalized.code === "AUTH_REQUIRED" ? ["Set COMERGE_ACCESS_TOKEN, then retry the tool call."] : normalized.code === "REPO_LOCK_TIMEOUT" ? ["Wait for the active Remix mutation to finish, then retry the tool call."] : normalized.code === "REPO_STATE_CHANGED_DURING_OPERATION" ? ["Review local repository changes, then rerun the tool once the worktree is stable."] : normalized.code === "PREFERRED_BRANCH_MISMATCH" ? ["Switch to the repository's preferred Remix branch, or rerun with allowBranchMismatch=true if intentional."] : [];
|
|
1746
1892
|
return {
|
|
1747
1893
|
schemaVersion: SCHEMA_VERSION,
|
|
1748
1894
|
ok: false,
|
|
@@ -1821,18 +1967,37 @@ function registerCollabTools(server, context) {
|
|
|
1821
1967
|
});
|
|
1822
1968
|
registerTool(server, context, {
|
|
1823
1969
|
name: "remix_collab_init",
|
|
1824
|
-
description: "Import the current repository into Remix and write the local binding file.",
|
|
1970
|
+
description: "Import the current repository into Remix and write the local binding file. Synchronous: by the time this tool resolves, the local binding file AND the local Remix baseline are both on disk, so the very next call to remix_collab_finalize_turn will succeed. Brand-new init on the default branch typically takes ~10s; non-default-branch init can take 30-90s while the server provisions a feature lane. The result includes `reused: boolean` (false for a brand-new app, true if a binding already existed) plus the canonical app/project identifiers and the dashboard URL. Use forceNew=true only when intentionally creating a new canonical family from scratch in a previously-bound repo; do NOT use forceNew as a retry mechanism for a failed init \u2014 it creates orphan backend apps and triggers canonical-family ambiguity errors on subsequent inits in this directory.",
|
|
1825
1971
|
access: "remote_write",
|
|
1826
1972
|
inputSchema: initInputSchema,
|
|
1827
1973
|
outputSchema: initSuccessSchema,
|
|
1828
1974
|
run: async (args) => {
|
|
1829
1975
|
const input = z3.object(initInputSchema).parse(args);
|
|
1830
1976
|
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
1831
|
-
|
|
1977
|
+
const result = await initCollab({
|
|
1832
1978
|
cwd,
|
|
1833
1979
|
appName: input.appName,
|
|
1834
1980
|
forceNew: input.forceNew
|
|
1835
1981
|
});
|
|
1982
|
+
try {
|
|
1983
|
+
const repoRoot = result && typeof result === "object" && "data" in result && result.data && typeof result.data.repoRoot === "string" ? result.data.repoRoot : null;
|
|
1984
|
+
if (repoRoot && shouldAutoSpawnHistoryImport(repoRoot)) {
|
|
1985
|
+
const spawned = spawnHistoryImportDetached(repoRoot);
|
|
1986
|
+
context.logger.log({
|
|
1987
|
+
level: "info",
|
|
1988
|
+
message: `history_import_auto_spawned pid=${spawned.pid ?? "?"} log=${spawned.logPath}`,
|
|
1989
|
+
tool: "remix_collab_init",
|
|
1990
|
+
repoRoot
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
} catch (spawnError) {
|
|
1994
|
+
context.logger.log({
|
|
1995
|
+
level: "error",
|
|
1996
|
+
message: `history_import_auto_spawn_failed: ${spawnError instanceof Error ? spawnError.message : String(spawnError)}`,
|
|
1997
|
+
tool: "remix_collab_init"
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
return result;
|
|
1836
2001
|
}
|
|
1837
2002
|
});
|
|
1838
2003
|
registerTool(server, context, {
|
|
@@ -1888,7 +2053,7 @@ function registerCollabTools(server, context) {
|
|
|
1888
2053
|
});
|
|
1889
2054
|
registerTool(server, context, {
|
|
1890
2055
|
name: "remix_collab_finalize_turn",
|
|
1891
|
-
description: "Primary turn recorder for the current bound repository.
|
|
2056
|
+
description: "Primary turn recorder for the current bound repository. Required: call this exactly once before the final response on every turn that touched a Remix-bound repo. Captures the current boundary locally and queues remote processing. Queued only: no remote change step exists yet until the finalize queue drains. Runtime exception: if this turn's context contains a [Remix runtime status] system note from the Remix UserPromptSubmit hook stating that the Stop hook will record this turn automatically, do NOT call this tool \u2014 the runtime is recording it on your behalf and a manual call would create a duplicate record. The runtime note is the only sanctioned override; trivial prompts, error states, and ambiguity all still require this call.",
|
|
1892
2057
|
access: "local_write",
|
|
1893
2058
|
inputSchema: finalizeTurnInputSchema,
|
|
1894
2059
|
outputSchema: finalizeTurnSuccessSchema,
|
|
@@ -1896,25 +2061,34 @@ function registerCollabTools(server, context) {
|
|
|
1896
2061
|
run: async (args) => {
|
|
1897
2062
|
const input = z3.object(finalizeTurnInputSchema).parse(args);
|
|
1898
2063
|
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
1899
|
-
if ((input.diffSource ?? "worktree") === "external" || typeof input.externalDiff === "string") {
|
|
1900
|
-
assertDiffWithinLimit(context.policy, input.externalDiff ?? "");
|
|
1901
|
-
}
|
|
1902
2064
|
return finalizeCollabTurn({
|
|
1903
2065
|
cwd,
|
|
1904
2066
|
prompt: input.prompt,
|
|
1905
2067
|
assistantResponse: input.assistantResponse,
|
|
1906
|
-
diffSource: input.diffSource,
|
|
1907
|
-
externalDiff: input.externalDiff,
|
|
1908
2068
|
sync: input.sync,
|
|
1909
2069
|
allowBranchMismatch: input.allowBranchMismatch ?? false,
|
|
1910
2070
|
idempotencyKey: input.idempotencyKey,
|
|
1911
|
-
agent: context.agentMetadata
|
|
2071
|
+
agent: context.agentMetadata,
|
|
2072
|
+
awaitingUsageDeadlineMs: 3e4
|
|
1912
2073
|
});
|
|
1913
2074
|
}
|
|
1914
2075
|
});
|
|
2076
|
+
registerTool(server, context, {
|
|
2077
|
+
name: "remix_collab_drain_finalize_queue",
|
|
2078
|
+
description: "Drain the local finalize queue and record queued finalize_turn jobs immediately. NOT required as a precondition for `remix_collab_request_merge` or `remix_collab_reconcile_apply` \u2014 those tools drain the queue internally before they run. Useful only for explicit recovery flows (e.g. status reports `await_finalize` and you want to flush before re-checking). Runtime exception: if this turn's context contains a [Remix runtime status] system note from the Remix UserPromptSubmit hook, the runtime drains the queue automatically in the background; do NOT call this tool unless an explicit recovery flow requires it.",
|
|
2079
|
+
access: "local_write",
|
|
2080
|
+
inputSchema: drainFinalizeQueueInputSchema,
|
|
2081
|
+
outputSchema: drainFinalizeQueueSuccessSchema,
|
|
2082
|
+
annotations: getAnnotations("local_write", { idempotent: true }),
|
|
2083
|
+
run: async (args) => {
|
|
2084
|
+
const input = z3.object(drainFinalizeQueueInputSchema).parse(args);
|
|
2085
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2086
|
+
return drainFinalizeQueue({ cwd });
|
|
2087
|
+
}
|
|
2088
|
+
});
|
|
1915
2089
|
registerTool(server, context, {
|
|
1916
2090
|
name: "remix_collab_sync_preview",
|
|
1917
|
-
description: "Preview whether the current bound repository can
|
|
2091
|
+
description: "Preview whether the current bound repository can pull the server delta into the working tree. Use this instead of raw git pull or rebase for bound-repo alignment.",
|
|
1918
2092
|
access: "read",
|
|
1919
2093
|
inputSchema: previewInputSchema,
|
|
1920
2094
|
outputSchema: syncSuccessSchema,
|
|
@@ -1926,7 +2100,7 @@ function registerCollabTools(server, context) {
|
|
|
1926
2100
|
});
|
|
1927
2101
|
registerTool(server, context, {
|
|
1928
2102
|
name: "remix_collab_sync_apply",
|
|
1929
|
-
description: "
|
|
2103
|
+
description: "Pull the server delta into the local working tree without moving local git history. This is the Remix-native replacement for raw git pull or rebase in a bound repo.",
|
|
1930
2104
|
access: "local_write",
|
|
1931
2105
|
inputSchema: applyInputSchema,
|
|
1932
2106
|
outputSchema: syncSuccessSchema,
|
|
@@ -1937,6 +2111,31 @@ function registerCollabTools(server, context) {
|
|
|
1937
2111
|
return syncCollab({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
|
|
1938
2112
|
}
|
|
1939
2113
|
});
|
|
2114
|
+
registerTool(server, context, {
|
|
2115
|
+
name: "remix_collab_re_anchor_preview",
|
|
2116
|
+
description: "Preview whether this checkout needs a fresh local Remix baseline. Use only when status reports `re_anchor` (no local baseline exists for this lane yet \u2014 fresh clone, deleted `.remix/` state, or first init didn't seed). Re-anchor does not replace `remix_collab_finalize_turn`; ordinary local content changes (including merges, pulls, and rebases) are recorded by `finalize-turn`, not by re-anchor.",
|
|
2117
|
+
access: "read",
|
|
2118
|
+
inputSchema: previewInputSchema,
|
|
2119
|
+
outputSchema: reAnchorSuccessSchema,
|
|
2120
|
+
run: async (args) => {
|
|
2121
|
+
const input = z3.object(previewInputSchema).parse(args);
|
|
2122
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2123
|
+
return reAnchor({ cwd, dryRun: true });
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
registerTool(server, context, {
|
|
2127
|
+
name: "remix_collab_re_anchor_apply",
|
|
2128
|
+
description: "Establish a local Remix baseline for the current checkout against the existing app head, without rewriting the local checkout afterward. Required only when status reports `re_anchor` (missing local baseline). It does not replace `remix_collab_finalize_turn` \u2014 local commits, pulls, merges, and rebases must still be recorded with `finalize-turn`.",
|
|
2129
|
+
access: "local_write",
|
|
2130
|
+
inputSchema: reAnchorInputSchema,
|
|
2131
|
+
outputSchema: reAnchorSuccessSchema,
|
|
2132
|
+
run: async (args) => {
|
|
2133
|
+
const input = z3.object(reAnchorInputSchema).parse(args);
|
|
2134
|
+
assertConfirm(input.confirm, "remix_collab_re_anchor_apply");
|
|
2135
|
+
const cwd = resolvePolicyCwd(context.policy, input.cwd);
|
|
2136
|
+
return reAnchor({ cwd, dryRun: false, allowBranchMismatch: input.allowBranchMismatch ?? false });
|
|
2137
|
+
}
|
|
2138
|
+
});
|
|
1940
2139
|
registerTool(server, context, {
|
|
1941
2140
|
name: "remix_collab_request_merge",
|
|
1942
2141
|
description: "Open a prompt-backed Remix merge request from the current bound repository to its upstream app instead of merging locally with raw git.",
|
|
@@ -2077,7 +2276,7 @@ function registerCollabTools(server, context) {
|
|
|
2077
2276
|
});
|
|
2078
2277
|
registerTool(server, context, {
|
|
2079
2278
|
name: "remix_collab_reconcile_preview",
|
|
2080
|
-
description: "Preview
|
|
2279
|
+
description: "Preview the explicit Remix recovery flow when local and server state diverged beyond a simple pull.",
|
|
2081
2280
|
access: "read",
|
|
2082
2281
|
inputSchema: previewInputSchema,
|
|
2083
2282
|
outputSchema: reconcileSuccessSchema,
|
|
@@ -2089,7 +2288,7 @@ function registerCollabTools(server, context) {
|
|
|
2089
2288
|
});
|
|
2090
2289
|
registerTool(server, context, {
|
|
2091
2290
|
name: "remix_collab_reconcile_apply",
|
|
2092
|
-
description: "
|
|
2291
|
+
description: "Run the explicit Remix recovery flow for diverged local/server state.",
|
|
2093
2292
|
access: "local_write",
|
|
2094
2293
|
inputSchema: applyInputSchema,
|
|
2095
2294
|
outputSchema: reconcileSuccessSchema,
|
|
@@ -3214,6 +3413,19 @@ import { z as z9 } from "zod";
|
|
|
3214
3413
|
// src/contracts/ops.ts
|
|
3215
3414
|
import { z as z8 } from "zod";
|
|
3216
3415
|
var genericRecordSchema4 = z8.record(z8.string(), z8.unknown());
|
|
3416
|
+
var appJobKindSchema = z8.enum([
|
|
3417
|
+
"fork",
|
|
3418
|
+
"edit",
|
|
3419
|
+
"bundle",
|
|
3420
|
+
"import_github",
|
|
3421
|
+
"import_upload",
|
|
3422
|
+
"merge",
|
|
3423
|
+
"revert",
|
|
3424
|
+
"change_step",
|
|
3425
|
+
"reconcile",
|
|
3426
|
+
"change_step_replay"
|
|
3427
|
+
]);
|
|
3428
|
+
var appJobStatusSchema = z8.enum(["pending", "enqueued", "processing", "succeeded", "failed", "cancelled"]);
|
|
3217
3429
|
var appScopedInputSchema = {
|
|
3218
3430
|
...commonRequestFieldsSchema,
|
|
3219
3431
|
appId: z8.string().trim().min(1).optional()
|
|
@@ -3223,6 +3435,13 @@ var editQueueInputSchema = {
|
|
|
3223
3435
|
limit: z8.number().int().positive().max(100).optional(),
|
|
3224
3436
|
offset: z8.number().int().nonnegative().optional()
|
|
3225
3437
|
};
|
|
3438
|
+
var appJobQueueInputSchema = {
|
|
3439
|
+
...appScopedInputSchema,
|
|
3440
|
+
limit: z8.number().int().positive().max(100).optional(),
|
|
3441
|
+
offset: z8.number().int().nonnegative().optional(),
|
|
3442
|
+
kind: z8.array(appJobKindSchema).min(1).optional(),
|
|
3443
|
+
status: z8.array(appJobStatusSchema).min(1).optional()
|
|
3444
|
+
};
|
|
3226
3445
|
var bundleInputSchema = {
|
|
3227
3446
|
...appScopedInputSchema,
|
|
3228
3447
|
bundleId: z8.string().trim().min(1)
|
|
@@ -3255,6 +3474,7 @@ var agentRunEventsInputSchema = {
|
|
|
3255
3474
|
};
|
|
3256
3475
|
var appOverviewSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3257
3476
|
var editQueueSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3477
|
+
var appJobQueueSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3258
3478
|
var bundleSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3259
3479
|
var timelineSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
3260
3480
|
var agentRunsSuccessSchema = makeSuccessSchema(genericRecordSchema4);
|
|
@@ -3288,7 +3508,7 @@ async function getAppOverview(params) {
|
|
|
3288
3508
|
data,
|
|
3289
3509
|
warnings: [],
|
|
3290
3510
|
recommendedNextActions: [
|
|
3291
|
-
"Use `remix_ops_list_timeline`, `remix_ops_list_agent_runs`, `remix_ops_get_edit_queue`, or `remix_ops_get_sandbox_status` for the next operational drill-down on this app."
|
|
3511
|
+
"Use `remix_ops_list_timeline`, `remix_ops_list_agent_runs`, `remix_ops_get_edit_queue`, `remix_ops_list_app_job_queue`, or `remix_ops_get_sandbox_status` for the next operational drill-down on this app."
|
|
3292
3512
|
],
|
|
3293
3513
|
logContext: target
|
|
3294
3514
|
};
|
|
@@ -3311,6 +3531,42 @@ async function getEditQueue(params) {
|
|
|
3311
3531
|
logContext: target
|
|
3312
3532
|
};
|
|
3313
3533
|
}
|
|
3534
|
+
async function listAppJobQueue(params) {
|
|
3535
|
+
const api = await createApiClient();
|
|
3536
|
+
const target = await resolveAppTarget(api, params);
|
|
3537
|
+
const data = unwrapResponseObject(
|
|
3538
|
+
await api.listAppJobQueue(target.appId, {
|
|
3539
|
+
limit: params.limit,
|
|
3540
|
+
offset: params.offset,
|
|
3541
|
+
kind: params.kind,
|
|
3542
|
+
status: params.status
|
|
3543
|
+
}),
|
|
3544
|
+
"app job queue"
|
|
3545
|
+
);
|
|
3546
|
+
const pageInfo = typeof data.pageInfo === "object" && data.pageInfo ? data.pageInfo : null;
|
|
3547
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
3548
|
+
const activeBlockingKinds = /* @__PURE__ */ new Set(["merge", "change_step", "change_step_replay", "reconcile", "revert"]);
|
|
3549
|
+
const hasBlockingJobs = items.some((item) => {
|
|
3550
|
+
if (!item || typeof item !== "object") return false;
|
|
3551
|
+
const record = item;
|
|
3552
|
+
return typeof record.kind === "string" && activeBlockingKinds.has(record.kind) && (typeof record.status !== "string" || ["pending", "enqueued", "processing"].includes(record.status));
|
|
3553
|
+
});
|
|
3554
|
+
const recommendedNextActions = [];
|
|
3555
|
+
if (hasBlockingJobs) {
|
|
3556
|
+
recommendedNextActions.push(
|
|
3557
|
+
"Inspect the returned active workflow jobs before concluding that merge, replay, reconcile, or revert work is stuck. This surface reflects backend app-scoped queue state, not local finalize state."
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3560
|
+
if (pageInfo?.hasMore === true && typeof pageInfo.limit === "number" && typeof pageInfo.offset === "number") {
|
|
3561
|
+
recommendedNextActions.push(`Pass offset=${pageInfo.offset + pageInfo.limit} to load the next app job queue page.`);
|
|
3562
|
+
}
|
|
3563
|
+
return {
|
|
3564
|
+
data,
|
|
3565
|
+
warnings: [],
|
|
3566
|
+
recommendedNextActions,
|
|
3567
|
+
logContext: target
|
|
3568
|
+
};
|
|
3569
|
+
}
|
|
3314
3570
|
async function getBundle(params) {
|
|
3315
3571
|
const api = await createApiClient();
|
|
3316
3572
|
const target = await resolveAppTarget(api, params);
|
|
@@ -3537,6 +3793,25 @@ function registerOpsTools(server, context) {
|
|
|
3537
3793
|
});
|
|
3538
3794
|
}
|
|
3539
3795
|
});
|
|
3796
|
+
registerTool4(server, context, {
|
|
3797
|
+
name: "remix_ops_list_app_job_queue",
|
|
3798
|
+
description: "Inspect bounded app-scoped workflow queue jobs for one app, including active backend work such as merge, replay, reconcile, revert, bundle, or import processing.",
|
|
3799
|
+
access: "read",
|
|
3800
|
+
inputSchema: appJobQueueInputSchema,
|
|
3801
|
+
outputSchema: appJobQueueSuccessSchema,
|
|
3802
|
+
run: async (args) => {
|
|
3803
|
+
const input = z9.object(appJobQueueInputSchema).parse(args);
|
|
3804
|
+
const cwd = input.cwd ? resolvePolicyCwd(context.policy, input.cwd) : void 0;
|
|
3805
|
+
return listAppJobQueue({
|
|
3806
|
+
appId: input.appId,
|
|
3807
|
+
cwd,
|
|
3808
|
+
limit: input.limit,
|
|
3809
|
+
offset: input.offset,
|
|
3810
|
+
kind: input.kind,
|
|
3811
|
+
status: input.status
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
});
|
|
3540
3815
|
registerTool4(server, context, {
|
|
3541
3816
|
name: "remix_ops_get_bundle",
|
|
3542
3817
|
description: "Inspect one app bundle by id without downloading the artifact payload.",
|