@agilemonster/mcp-server 1.0.22 → 1.0.24
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 +33 -0
- package/dist/agent-engine.d.ts +132 -0
- package/dist/agent-engine.js +517 -0
- package/dist/client.d.ts +15 -2
- package/dist/client.js +23 -2
- package/dist/deploy-profile.d.ts +26 -1
- package/dist/deploy-profile.js +69 -1
- package/dist/deploy-runner.js +3 -1
- package/dist/server.d.ts +24 -2
- package/dist/server.js +679 -286
- package/dist/types.d.ts +19 -0
- package/package.json +1 -1
package/dist/deploy-profile.js
CHANGED
|
@@ -1,6 +1,34 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
+
/**
|
|
5
|
+
* Second check on this side of the wire: the server only sends a binding's route as confirmed to the account
|
|
6
|
+
* that set it, but what runs in a shell on this computer is decided HERE. If the confirmed fields name a
|
|
7
|
+
* setter and it is not this machine's account, the route is demoted to a proposal.
|
|
8
|
+
* `ownerId` null = this machine's account could not be determined; the fields are left as the server sent them.
|
|
9
|
+
*/
|
|
10
|
+
export function trustBoundRoute(bound, ownerId) {
|
|
11
|
+
if (!bound)
|
|
12
|
+
return null;
|
|
13
|
+
const setBy = (bound.deploy_set_by || '').trim();
|
|
14
|
+
const hasRoute = Boolean((bound.deploy_method || '').trim() || (bound.deploy_command || '').trim());
|
|
15
|
+
if (!hasRoute || !setBy || !ownerId || setBy === ownerId)
|
|
16
|
+
return bound;
|
|
17
|
+
return {
|
|
18
|
+
...bound,
|
|
19
|
+
deploy_method: null,
|
|
20
|
+
deploy_command: null,
|
|
21
|
+
deploy_source: null,
|
|
22
|
+
proposal: bound.proposal || {
|
|
23
|
+
method: bound.deploy_method,
|
|
24
|
+
command: bound.deploy_command,
|
|
25
|
+
live_url: bound.live_url,
|
|
26
|
+
source: bound.deploy_source || 'auto',
|
|
27
|
+
set_by: setBy,
|
|
28
|
+
set_by_name: null,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
4
32
|
// ---------------------------------------------------------------------------
|
|
5
33
|
// Small helpers
|
|
6
34
|
// ---------------------------------------------------------------------------
|
|
@@ -898,8 +926,43 @@ export function resolveDeployProfile(projectPath, options = {}) {
|
|
|
898
926
|
}
|
|
899
927
|
if (bound && boundSource === 'user') {
|
|
900
928
|
const method = normalizeMethod(bound.deploy_method);
|
|
929
|
+
// The server only sends source "user" to the account that set the route (see trustBoundRoute)
|
|
901
930
|
if (method)
|
|
902
|
-
return pinned(method, bound.deploy_command || null, 'user', 'set by
|
|
931
|
+
return pinned(method, bound.deploy_command || null, 'user', 'set by you in Agile Monster');
|
|
932
|
+
}
|
|
933
|
+
// A route someone else set on the board. It may steer this machine towards something it could have run
|
|
934
|
+
// unasked anyway (push and let CI deploy, refresh the detected Compose stack). Anything that puts THEIR
|
|
935
|
+
// command line, a deploy script, a cloud CLI or a manual dispatch on this computer is asked about once;
|
|
936
|
+
// only the machine owner's answer on the card confirms it, and it is then remembered locally.
|
|
937
|
+
const proposal = bound?.proposal || null;
|
|
938
|
+
const proposalSource = (proposal?.source || '').toLowerCase();
|
|
939
|
+
const fromProposal = () => {
|
|
940
|
+
const method = normalizeMethod(proposal?.method);
|
|
941
|
+
if (!method)
|
|
942
|
+
return null;
|
|
943
|
+
const match = candidateFor(method);
|
|
944
|
+
const theirCommand = isRunnableCommand(proposal?.command) ? (proposal?.command).trim() : null;
|
|
945
|
+
const custom = theirCommand !== null && theirCommand !== (match?.command || null);
|
|
946
|
+
const sensitive = requiresConfirmation({ method, dispatchOnly: Boolean(match?.dispatchOnly) });
|
|
947
|
+
if (!custom && sensitive && !match?.command)
|
|
948
|
+
return null; // nothing concrete to run or to confirm
|
|
949
|
+
if (!custom && !match && method !== 'manual' && method !== 'none')
|
|
950
|
+
return null; // does not apply to this checkout
|
|
951
|
+
const last = memory?.last_success || null;
|
|
952
|
+
const ranHereBefore = Boolean(last) && normalizeMethod(last?.method) === method && ((last?.command || '').trim() || null) === (theirCommand || match?.command || null);
|
|
953
|
+
const needsConfirmation = !ranHereBefore && (custom || sensitive);
|
|
954
|
+
const who = (proposal?.set_by_name || '').trim() || 'a teammate';
|
|
955
|
+
const what = proposalSource === 'verified' ? `verified by a deployment on ${who}'s machine` : `set by ${who} in Agile Monster`;
|
|
956
|
+
return {
|
|
957
|
+
...pinned(method, theirCommand, 'proposed', needsConfirmation ? `${what}; not confirmed on this computer yet` : what),
|
|
958
|
+
confidence: needsConfirmation ? 'medium' : 'high',
|
|
959
|
+
needsConfirmation,
|
|
960
|
+
};
|
|
961
|
+
};
|
|
962
|
+
if (proposal && proposalSource === 'user') {
|
|
963
|
+
const proposed = fromProposal();
|
|
964
|
+
if (proposed)
|
|
965
|
+
return proposed;
|
|
903
966
|
}
|
|
904
967
|
// What worked last time, as long as it has not started failing since
|
|
905
968
|
const last = memory?.last_success || null;
|
|
@@ -917,6 +980,11 @@ export function resolveDeployProfile(projectPath, options = {}) {
|
|
|
917
980
|
return pinned(method, bound.deploy_command || null, 'verified', 'verified by an earlier deployment recorded in Agile Monster');
|
|
918
981
|
}
|
|
919
982
|
}
|
|
983
|
+
if (proposal && proposalSource === 'verified') {
|
|
984
|
+
const proposed = fromProposal();
|
|
985
|
+
if (proposed)
|
|
986
|
+
return proposed;
|
|
987
|
+
}
|
|
920
988
|
const top = candidates[0];
|
|
921
989
|
if (!top) {
|
|
922
990
|
return {
|
package/dist/deploy-runner.js
CHANGED
|
@@ -973,7 +973,9 @@ export async function runDeployment(req) {
|
|
|
973
973
|
log.info('deploy.needs_confirmation', { card: req.cardId, project: req.projectPath, method: profile.method, command: profile.command });
|
|
974
974
|
return result({
|
|
975
975
|
errorKind: 'needs_confirmation',
|
|
976
|
-
errorDetail:
|
|
976
|
+
errorDetail: profile.source === 'proposed'
|
|
977
|
+
? `On the board this project is set to deploy with ${methodLabel(profile.method)}${profile.command ? ` (\`${profile.command}\`)` : ''}, but that was not set from this computer's account and has never been confirmed here.`
|
|
978
|
+
: `This project looks like it deploys with ${methodLabel(profile.method)}${profile.command ? ` (\`${profile.command}\`)` : ''}, but I have never run that here and nobody has confirmed it.`,
|
|
977
979
|
});
|
|
978
980
|
}
|
|
979
981
|
const git = await commitAndPush(req, profile, pushPolicy);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
2
|
import { AgileMonsterClient } from './client.js';
|
|
3
|
-
import { Card } from './types.js';
|
|
3
|
+
import { ActiveClaim, Card } from './types.js';
|
|
4
|
+
import { BoundProfile } from './deploy-profile.js';
|
|
4
5
|
export declare function createServer(client?: AgileMonsterClient): Server<{
|
|
5
6
|
method: string;
|
|
6
7
|
params?: {
|
|
@@ -35,7 +36,24 @@ export declare function createServer(client?: AgileMonsterClient): Server<{
|
|
|
35
36
|
} | undefined;
|
|
36
37
|
} | undefined;
|
|
37
38
|
}>;
|
|
38
|
-
export declare function collectProjectSourceFiles(projectPath: string, keywords: string[]): string[];
|
|
39
|
+
export declare function collectProjectSourceFiles(projectPath: string, keywords: string[], phrases?: string[]): string[];
|
|
40
|
+
/**
|
|
41
|
+
* The board binding's deploy profile for a card, as this machine may use it. A route that another account
|
|
42
|
+
* set arrives as a proposal (or is demoted to one here): see trustBoundRoute and resolveDeployProfile.
|
|
43
|
+
*/
|
|
44
|
+
export declare function boundProfileOf(card: Partial<Card>, ownerId: string | null): BoundProfile;
|
|
45
|
+
/**
|
|
46
|
+
* The id under which this process claims cards. The server keeps a claim for the session that made it, so
|
|
47
|
+
* the id is stable for the life of the process. Background runs start with `auto-pulse-`: only those are
|
|
48
|
+
* re-queued automatically when they stop responding.
|
|
49
|
+
*/
|
|
50
|
+
export declare function sessionRunRef(kind: 'background' | 'interactive', provider?: string): string;
|
|
51
|
+
export declare function holdClaim(cardId: string, runRef: string): void;
|
|
52
|
+
/** The agent did something with the card: its claim is still in use. */
|
|
53
|
+
export declare function touchClaim(cardId: string): void;
|
|
54
|
+
export declare function releaseClaim(cardId: string): void;
|
|
55
|
+
/** What the next pulse reports as held by this process. */
|
|
56
|
+
export declare function activeClaims(now?: number): ActiveClaim[];
|
|
39
57
|
/**
|
|
40
58
|
* Roll back edits left behind by a worker that died mid-card. A file is only restored if it still holds
|
|
41
59
|
* exactly what that worker wrote — anything the developer touched since is left alone.
|
|
@@ -46,6 +64,10 @@ export declare function recoverEditJournals(): number;
|
|
|
46
64
|
* Anchored to the start of the title so "Add a restart button to the server page" is not one.
|
|
47
65
|
*/
|
|
48
66
|
export declare function isRedeployOnlyCard(card: Pick<Card, 'title'>): boolean;
|
|
67
|
+
/** The part of a build log that names the problem, without colour codes. Falls back to the last lines. */
|
|
68
|
+
export declare function buildErrorExcerpt(output: string, maxLines?: number, maxChars?: number): string;
|
|
69
|
+
/** Exact phrases the requester put in quotes ("Recently active projects"): the strongest pointer to the right file. */
|
|
70
|
+
export declare function quotedPhrases(text: string): string[];
|
|
49
71
|
export declare function executeQueuedCardAutonomously(client: AgileMonsterClient, card: Card, projectPath: string): Promise<boolean>;
|
|
50
72
|
/**
|
|
51
73
|
* Finish a card the human approved into Done (or flagged as `deploying`).
|