@telora/mcp-products 0.22.159 → 0.22.165
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/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +1 -0
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/migrationClaimHandlers.d.ts +17 -0
- package/dist/handlers/migrationClaimHandlers.js +217 -0
- package/dist/handlers/migrationClaimHandlers.js.map +1 -0
- package/dist/handlers/migrationObservation.d.ts +181 -0
- package/dist/handlers/migrationObservation.js +446 -0
- package/dist/handlers/migrationObservation.js.map +1 -0
- package/dist/handlers/organizationHandlers.js +1 -1
- package/dist/handlers/organizationHandlers.js.map +1 -1
- package/dist/index.js +10 -475
- package/dist/index.js.map +1 -1
- package/dist/shared.js +29 -0
- package/dist/shared.js.map +1 -1
- package/dist/tool-registration.d.ts +26 -0
- package/dist/tool-registration.js +554 -0
- package/dist/tool-registration.js.map +1 -0
- package/package.json +1 -1
package/dist/handlers/index.d.ts
CHANGED
|
@@ -30,3 +30,4 @@ export { registerEndpointTools, handleEndpointTool } from "./endpointHandlers.js
|
|
|
30
30
|
export { registerServiceDefinitionTools, handleServiceDefinitionTool } from "./serviceDefinitionHandlers.js";
|
|
31
31
|
export { registerStarterRepoTools, handleStarterRepoTool } from "./starterRepoHandlers.js";
|
|
32
32
|
export { registerCodeObservationTools, handleCodeObservationCreate } from "./codeObservationHandlers.js";
|
|
33
|
+
export { registerMigrationClaimTools, handleMigrationClaim } from "./migrationClaimHandlers.js";
|
package/dist/handlers/index.js
CHANGED
|
@@ -33,4 +33,5 @@ export { registerEndpointTools, handleEndpointTool } from "./endpointHandlers.js
|
|
|
33
33
|
export { registerServiceDefinitionTools, handleServiceDefinitionTool } from "./serviceDefinitionHandlers.js";
|
|
34
34
|
export { registerStarterRepoTools, handleStarterRepoTool } from "./starterRepoHandlers.js";
|
|
35
35
|
export { registerCodeObservationTools, handleCodeObservationCreate } from "./codeObservationHandlers.js";
|
|
36
|
+
export { registerMigrationClaimTools, handleMigrationClaim } from "./migrationClaimHandlers.js";
|
|
36
37
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,uDAAuD;AACvD,8EAA8E;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AACnH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,sCAAsC,EACtC,gCAAgC,GACjC,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7G,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC3F,OAAO,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,uDAAuD;AACvD,8EAA8E;AAE9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,+BAA+B,EAAE,MAAM,iCAAiC,CAAC;AAClF,OAAO,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAChF,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,8BAA8B,EAAE,MAAM,mBAAmB,CAAC;AACnH,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,EACL,sCAAsC,EACtC,gCAAgC,GACjC,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,MAAM,6BAA6B,CAAC;AACtG,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAClF,OAAO,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7G,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAC3F,OAAO,EAAE,4BAA4B,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AACzG,OAAO,EAAE,2BAA2B,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { type ObservationOutcome } from "./migrationObservation.js";
|
|
3
|
+
import { successResult, validationError, type GetCreds, type ToolProfile } from "../shared.js";
|
|
4
|
+
export interface MigrationClaimParams {
|
|
5
|
+
productId?: string;
|
|
6
|
+
focusId?: string;
|
|
7
|
+
tag?: string;
|
|
8
|
+
agentSessionId?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function handleMigrationClaim(params: MigrationClaimParams, callApi: (body: Record<string, unknown>) => Promise<unknown>,
|
|
11
|
+
/**
|
|
12
|
+
* What THIS repository holds right now. Sent with the claim so the grant is
|
|
13
|
+
* made against a view taken at grant time, not only against the snapshot the
|
|
14
|
+
* daemon published earlier -- see migrationObservation.ts. Injected for tests.
|
|
15
|
+
*/
|
|
16
|
+
observe?: () => ObservationOutcome): Promise<ReturnType<typeof successResult> | ReturnType<typeof validationError>>;
|
|
17
|
+
export declare function registerMigrationClaimTools(server: McpServer, getCreds: GetCreds, _profile?: ToolProfile): void;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Migration-number claim tool (consolidated):
|
|
3
|
+
// telora_product_migration (claim | list)
|
|
4
|
+
//
|
|
5
|
+
// THE RULE THIS TOOL EXISTS TO MAKE POSSIBLE: a migration number is claimed
|
|
6
|
+
// once, through Telora, BEFORE the file is named. Never read from your own
|
|
7
|
+
// worktree.
|
|
8
|
+
//
|
|
9
|
+
// Why: two focuses armed concurrently on one product each work in their own
|
|
10
|
+
// worktree, cut from the same base. A team that picks the next number by
|
|
11
|
+
// reading the highest one it can see picks the same number its sibling just
|
|
12
|
+
// picked. Measured 2026-09-22 on Tinker Tailor: from base 0351, two armed
|
|
13
|
+
// focuses both authored 0352, 0353, 0354 and 0355. Git sees no conflict (the
|
|
14
|
+
// files have different names), the merge admits it, and the breakage appears
|
|
15
|
+
// only when the migrations are applied in order -- after which repairing it is
|
|
16
|
+
// a renumber of every file plus the journal tail plus every reference.
|
|
17
|
+
//
|
|
18
|
+
// The number is granted from the union of what integration holds, what every
|
|
19
|
+
// live focus branch holds, and every outstanding claim -- none of which a
|
|
20
|
+
// single worktree can see. The grant is atomic at the database, so two teams
|
|
21
|
+
// claiming in the same instant cannot receive one number.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import { observeLocalMigrations } from "./migrationObservation.js";
|
|
25
|
+
import { callProductApi, successResult, validationError, wrapHandler, LIST_INDEX_NOTE, } from "../shared.js";
|
|
26
|
+
export async function handleMigrationClaim(params, callApi,
|
|
27
|
+
/**
|
|
28
|
+
* What THIS repository holds right now. Sent with the claim so the grant is
|
|
29
|
+
* made against a view taken at grant time, not only against the snapshot the
|
|
30
|
+
* daemon published earlier -- see migrationObservation.ts. Injected for tests.
|
|
31
|
+
*/
|
|
32
|
+
observe = observeLocalMigrations) {
|
|
33
|
+
if (!params.productId)
|
|
34
|
+
return validationError("productId required for claim");
|
|
35
|
+
const contested = [];
|
|
36
|
+
for (let attempt = 1; attempt <= MAX_CLAIM_ATTEMPTS; attempt += 1) {
|
|
37
|
+
// WHAT THIS REPOSITORY HOLDS -- and, when it cannot say, saying so instead
|
|
38
|
+
// of saying nothing.
|
|
39
|
+
//
|
|
40
|
+
// Ordinals are additive: they only ever join the taken set, so they can
|
|
41
|
+
// raise the granted number and can never make two teams share one. The
|
|
42
|
+
// report of what could NOT be read is not additive evidence at all -- it
|
|
43
|
+
// tells the allocator the view is partial -- so it is sent whole, even when
|
|
44
|
+
// the refs that WERE read happened to hold nothing.
|
|
45
|
+
//
|
|
46
|
+
// An INCOMPLETE reading is refused HERE rather than sent. There is nothing
|
|
47
|
+
// useful to transmit: the fields would describe a subset of the repository
|
|
48
|
+
// with no way for the handler to know what is missing, and the handler's
|
|
49
|
+
// own fallback -- grant against the daemon's older leased snapshot -- is
|
|
50
|
+
// exactly the window this reader exists to close. A refusal is retryable; a
|
|
51
|
+
// number granted twice is not.
|
|
52
|
+
const observed = observe();
|
|
53
|
+
if (observed.kind === "incomplete")
|
|
54
|
+
return refuseIncomplete(observed.reason);
|
|
55
|
+
const body = {
|
|
56
|
+
action: "migration_claim_create",
|
|
57
|
+
productId: params.productId,
|
|
58
|
+
};
|
|
59
|
+
if (observed.kind === "observed") {
|
|
60
|
+
const { observation } = observed;
|
|
61
|
+
body.observedHeld = observation.observedHeld;
|
|
62
|
+
body.observedRefs = observation.observedRefs;
|
|
63
|
+
body.observedAt = observation.observedAt;
|
|
64
|
+
body.observedUnreadable = observation.observedUnreadable;
|
|
65
|
+
body.observedTips = observation.observedTips;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
// GENUINELY NOTHING TO OBSERVE -- no repository, or a product that
|
|
69
|
+
// declares no numbering. Declared explicitly so the handler can tell a
|
|
70
|
+
// deliberate omission from a claimant that simply sent no fields; the two
|
|
71
|
+
// used to be the same wire message.
|
|
72
|
+
body.observationScope = observed.reason;
|
|
73
|
+
}
|
|
74
|
+
if (params.focusId !== undefined)
|
|
75
|
+
body.focusId = params.focusId;
|
|
76
|
+
if (params.tag !== undefined)
|
|
77
|
+
body.tag = params.tag;
|
|
78
|
+
if (params.agentSessionId !== undefined)
|
|
79
|
+
body.agentSessionId = params.agentSessionId;
|
|
80
|
+
const result = await callApi(body);
|
|
81
|
+
const ordinal = String(result.claim.ordinal);
|
|
82
|
+
// ─── THE BARRIER ─────────────────────────────────────────────────────────
|
|
83
|
+
//
|
|
84
|
+
// THE WINDOW THIS CLOSES. Everything above bounds the reading; nothing
|
|
85
|
+
// above makes it hold THROUGH the insert. The scan ends, the request
|
|
86
|
+
// crosses the network, the row is inserted -- and in those hundreds of
|
|
87
|
+
// milliseconds a sibling worktree can commit a migration it never claimed.
|
|
88
|
+
// The number just granted is then one a branch already holds, and no party
|
|
89
|
+
// to the grant could have known: the handler has no git, and this process
|
|
90
|
+
// had already stopped looking.
|
|
91
|
+
//
|
|
92
|
+
// So it looks AGAIN, after the grant, and asks the one question that
|
|
93
|
+
// matters: is the number I was given still free on every branch? A grant
|
|
94
|
+
// that fails that check is not returned. The next attempt re-reads, and
|
|
95
|
+
// that reading now CONTAINS the sibling's ordinal, so the allocator is
|
|
96
|
+
// asked for a number above it.
|
|
97
|
+
//
|
|
98
|
+
// This does not make the grant atomic against the repository -- nothing on
|
|
99
|
+
// either side of the boundary can be, because a commit that does not exist
|
|
100
|
+
// yet is unobservable. What it does is make the unobservable interval
|
|
101
|
+
// bounded and CHECKED: a number that lost the race is detected and
|
|
102
|
+
// discarded rather than handed to a team that writes a file at it.
|
|
103
|
+
//
|
|
104
|
+
// THE LOSING CLAIM IS LEFT STANDING, deliberately. Its ordinal really is
|
|
105
|
+
// taken -- by the sibling's unclaimed file -- so releasing it would offer
|
|
106
|
+
// that number to a third team. It stays until the lifecycle releases it
|
|
107
|
+
// with its focus, and the file that took it is named to the team that wrote
|
|
108
|
+
// it by the unclaimed-migration record.
|
|
109
|
+
if (observed.kind !== "observed")
|
|
110
|
+
return granted(result, params);
|
|
111
|
+
const after = observe();
|
|
112
|
+
if (after.kind !== "observed") {
|
|
113
|
+
// LOSS OF VERIFICATION, NOT PROOF OF FREEDOM.
|
|
114
|
+
//
|
|
115
|
+
// The pre-grant reading was complete, so this process WAS in a declared
|
|
116
|
+
// repository moments ago. A second reading that now answers `incomplete`
|
|
117
|
+
// -- or `not_applicable`, which after a complete one means a transient
|
|
118
|
+
// `rev-parse` failure or a declaration that just disappeared -- has not
|
|
119
|
+
// established that the number still is free. It has established that
|
|
120
|
+
// nothing can currently say.
|
|
121
|
+
//
|
|
122
|
+
// Returning the ordinal there is the barrier declaring success on the
|
|
123
|
+
// strength of its own failure. The claim ROW stays (its ordinal may well
|
|
124
|
+
// be taken, and releasing it would offer that number to a third team),
|
|
125
|
+
// but the caller is refused and retries.
|
|
126
|
+
const reason = after.kind === "incomplete"
|
|
127
|
+
? after.reason
|
|
128
|
+
: `the repository stopped being readable mid-claim (${after.reason})`;
|
|
129
|
+
return refuseIncomplete(`${reason} -- a number was granted and could NOT be confirmed still free`);
|
|
130
|
+
}
|
|
131
|
+
if (!after.observation.observedHeld.includes(ordinal)) {
|
|
132
|
+
return granted(result, params);
|
|
133
|
+
}
|
|
134
|
+
contested.push(ordinal);
|
|
135
|
+
}
|
|
136
|
+
return validationError(`Cannot claim a migration number: ${contested.length} grant(s) `
|
|
137
|
+
+ `(${contested.join(", ")}) were each taken by a branch between the reading they were `
|
|
138
|
+
+ `made against and the moment they were granted. That is a sibling writing migrations `
|
|
139
|
+
+ `without claiming them -- the rule this tool exists to enforce -- rather than ordinary `
|
|
140
|
+
+ `contention. Find the unclaimed files on the other branches before retrying.`);
|
|
141
|
+
}
|
|
142
|
+
/** How many times a grant may lose the scan-to-insert race before refusing. */
|
|
143
|
+
const MAX_CLAIM_ATTEMPTS = 3;
|
|
144
|
+
/** The refusal for a reading that could not be completed. */
|
|
145
|
+
function refuseIncomplete(reason) {
|
|
146
|
+
return validationError(`Cannot claim a migration number: this repository could not be read completely `
|
|
147
|
+
+ `(${reason}). The claim is not attempted, because granting against a partial view of `
|
|
148
|
+
+ `the branches is how two teams receive one number. Retry; if it persists, check the `
|
|
149
|
+
+ `repository rather than numbering by hand.`);
|
|
150
|
+
}
|
|
151
|
+
/** A grant that survived the barrier. */
|
|
152
|
+
function granted(result, params) {
|
|
153
|
+
return successResult({
|
|
154
|
+
claim: result.claim,
|
|
155
|
+
// Stated back rather than assumed: the number is the one to write, and the
|
|
156
|
+
// tag recorded here is the INTENT -- the file may carry a different one,
|
|
157
|
+
// and the confirm pass records what the file actually carries.
|
|
158
|
+
use: `${String(result.claim.ordinal)}${params.tag ? `_${params.tag}` : ""}`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export function registerMigrationClaimTools(server, getCreds, _profile = 'full') {
|
|
162
|
+
server.tool("telora_product_migration", "CLAIM A MIGRATION NUMBER BEFORE YOU NAME THE FILE. Never pick a migration " +
|
|
163
|
+
"number by reading your own worktree: two focuses armed at once on one " +
|
|
164
|
+
"product are cut from the same base, so 'the next free number' computed " +
|
|
165
|
+
"locally is the same number your sibling just computed. Git sees no conflict " +
|
|
166
|
+
"(the filenames differ), the merge admits it, and it surfaces only when the " +
|
|
167
|
+
"migrations run in order. This tool grants a number free across integration, " +
|
|
168
|
+
"every live focus branch, and every outstanding claim -- atomically, so two " +
|
|
169
|
+
"teams claiming in the same instant never receive one number. Pass the tag " +
|
|
170
|
+
"you intend to use; the file may end up named differently and that is fine " +
|
|
171
|
+
"(the daemon confirms the claim with the tag the file actually carries). " +
|
|
172
|
+
"REFUSALS are never a guessed number: migration_numbering_unavailable means " +
|
|
173
|
+
"the product declares no numbering (telora.migrations.json) or its " +
|
|
174
|
+
"declaration is broken; migration_held_set_unavailable means no daemon has " +
|
|
175
|
+
"reported what the branches hold, or a branch could not be read, so granting " +
|
|
176
|
+
"would be guessing. Fix the cause -- do not fall back to numbering by hand. " +
|
|
177
|
+
"Actions: claim a number (productId, usually with focusId + tag), or list " +
|
|
178
|
+
"what a product or focus holds." +
|
|
179
|
+
LIST_INDEX_NOTE, {
|
|
180
|
+
action: z.enum(["claim", "list"]).describe("Action to perform"),
|
|
181
|
+
productId: z.string().uuid().optional().describe("Product UUID the migration belongs to (required for claim and list)"),
|
|
182
|
+
migrationClaimId: z.string().uuid().optional().describe("Migration-claim UUID (self id; reserved for future get/by-id reads)"),
|
|
183
|
+
focusId: z.string().uuid().optional().describe("Focus UUID the claim is made for -- the claim follows this branch, and is released if the focus ends without the file. Omit only for a claim made outside a focus."),
|
|
184
|
+
tag: z.string().max(200).optional().describe("The migration name you intend to use (the part after the number). Recorded as intent; the file may carry a different tag and the daemon confirms with the real one."),
|
|
185
|
+
agentSessionId: z.string().uuid().optional().describe("Provenance: the session making the claim"),
|
|
186
|
+
includeReleased: z.boolean().optional().describe("List: include released claims (the history). Default: live claims only."),
|
|
187
|
+
limit: z.number().int().min(1).max(500).optional().describe("Max results (1-500, default 50)"),
|
|
188
|
+
offset: z.number().int().min(0).optional().describe("Number of results to skip (default 0)"),
|
|
189
|
+
}, wrapHandler(async (params) => {
|
|
190
|
+
switch (params.action) {
|
|
191
|
+
case "claim": {
|
|
192
|
+
return handleMigrationClaim(params, (body) => callProductApi(getCreds(), body));
|
|
193
|
+
}
|
|
194
|
+
case "list": {
|
|
195
|
+
if (!params.productId)
|
|
196
|
+
return validationError("productId required for list");
|
|
197
|
+
const body = {
|
|
198
|
+
action: "migration_claim_list",
|
|
199
|
+
productId: params.productId,
|
|
200
|
+
};
|
|
201
|
+
if (params.focusId !== undefined)
|
|
202
|
+
body.focusId = params.focusId;
|
|
203
|
+
if (params.includeReleased !== undefined)
|
|
204
|
+
body.includeReleased = params.includeReleased;
|
|
205
|
+
if (params.limit !== undefined)
|
|
206
|
+
body.limit = params.limit;
|
|
207
|
+
if (params.offset !== undefined)
|
|
208
|
+
body.offset = params.offset;
|
|
209
|
+
const result = await callProductApi(getCreds(), body);
|
|
210
|
+
return successResult({ items: result.claims, totalCount: result.totalCount });
|
|
211
|
+
}
|
|
212
|
+
default:
|
|
213
|
+
return validationError(`Unknown action: ${params.action}`);
|
|
214
|
+
}
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=migrationClaimHandlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrationClaimHandlers.js","sourceRoot":"","sources":["../../src/handlers/migrationClaimHandlers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/** What the claimant's own repository holds, right now. */
|
|
2
|
+
export interface MigrationObservation {
|
|
3
|
+
/** Every ordinal any local branch holds, deduplicated. */
|
|
4
|
+
observedHeld: string[];
|
|
5
|
+
/** How many refs were read -- evidence the observation is not vacuous. */
|
|
6
|
+
observedRefs: number;
|
|
7
|
+
/** When it was taken (epoch ms). */
|
|
8
|
+
observedAt: number;
|
|
9
|
+
/**
|
|
10
|
+
* Refs this observation could NOT read.
|
|
11
|
+
*
|
|
12
|
+
* NAMED RATHER THAN DROPPED. A ref that could not be read holds an unknown
|
|
13
|
+
* set of ordinals, and "unknown" silently folded in as "none" is the exact
|
|
14
|
+
* shape of fail-open the daemon's own reader refuses. The handler treats a
|
|
15
|
+
* non-empty list as a partial view and refuses the grant, retryably --
|
|
16
|
+
* symmetric with `migration_held_set_unavailable` on the daemon's side. A
|
|
17
|
+
* caller with NO repository at all reports no observation and is unaffected;
|
|
18
|
+
* this is only about a repository that exists and could not be fully read.
|
|
19
|
+
*/
|
|
20
|
+
observedUnreadable: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Every ref that was read, with the commit it pointed at. Recorded on the
|
|
23
|
+
* granted row, so "which commits did the allocator look at?" is answered by
|
|
24
|
+
* the claim rather than reconstructed afterwards.
|
|
25
|
+
*/
|
|
26
|
+
observedTips: Record<string, string>;
|
|
27
|
+
}
|
|
28
|
+
/** A repo's migration declaration, as much of it as this reader needs. */
|
|
29
|
+
interface Declaration {
|
|
30
|
+
dir: string;
|
|
31
|
+
filePattern: string;
|
|
32
|
+
scheme: MigrationScheme;
|
|
33
|
+
journal?: {
|
|
34
|
+
path?: string;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/** The two numbering schemes; an unrecognised value is malformed, never defaulted. */
|
|
38
|
+
type MigrationScheme = "sequential" | "timestamp";
|
|
39
|
+
/** The ordinals a declaration's pattern reads out of a list of paths. */
|
|
40
|
+
export declare function ordinalsFromPaths(paths: readonly string[], declaration: Declaration): string[];
|
|
41
|
+
/**
|
|
42
|
+
* The width the declaration's own ordinal group fixes, when it fixes one.
|
|
43
|
+
*
|
|
44
|
+
* WHY THE DECLARATION AND NOT THE FILES. A journal entry is a bare index --
|
|
45
|
+
* `2`, not `0002` -- so placing it needs a width, and reading that width from
|
|
46
|
+
* the matching migration FILES fails in exactly the case that matters: a
|
|
47
|
+
* sibling branch that has advanced its journal but has no file this pattern
|
|
48
|
+
* matches, or a ref carrying journal entries and no migration file at all.
|
|
49
|
+
* The idx was then dropped, and against a stale heartbeat the allocator could
|
|
50
|
+
* grant the very number the journal already holds. The declaration fixes the
|
|
51
|
+
* width independently of what any ref happens to contain.
|
|
52
|
+
*
|
|
53
|
+
* Only a pattern that PINS the width answers -- `\d{4}` or `[0-9]{4}`. A
|
|
54
|
+
* variable-width group (`\d+`) fixes nothing and returns null, which is the
|
|
55
|
+
* honest answer rather than a guess.
|
|
56
|
+
*/
|
|
57
|
+
export declare function declaredOrdinalWidth(filePattern: string): number | null;
|
|
58
|
+
/**
|
|
59
|
+
* What reading a declared journal produced.
|
|
60
|
+
*
|
|
61
|
+
* A JOURNAL THAT CANNOT BE READ IS NOT AN EMPTY JOURNAL. This used to answer
|
|
62
|
+
* `[]` for unparseable text, a missing `entries` array, and an index it could
|
|
63
|
+
* not place -- indistinguishable from a journal that genuinely registers
|
|
64
|
+
* nothing. Against a heartbeat taken before a journal-only advance, that drops
|
|
65
|
+
* the held idx and lets the allocator grant the number again: the exact
|
|
66
|
+
* fail-open the observation exists to close, arriving through the one input
|
|
67
|
+
* that has no file to corroborate it.
|
|
68
|
+
*/
|
|
69
|
+
export type JournalReading = {
|
|
70
|
+
ok: true;
|
|
71
|
+
ordinals: string[];
|
|
72
|
+
} | {
|
|
73
|
+
ok: false;
|
|
74
|
+
reason: "unparseable" | "no_entries" | "unplaceable_entry";
|
|
75
|
+
};
|
|
76
|
+
/** The idx values a journal registers, normalised to the width in use. */
|
|
77
|
+
export declare function readJournalOrdinals(text: string, width: number | null): JournalReading;
|
|
78
|
+
/**
|
|
79
|
+
* What reading a file produced.
|
|
80
|
+
*
|
|
81
|
+
* ABSENT AND UNREADABLE ARE DIFFERENT ANSWERS, and collapsing them to `null` is
|
|
82
|
+
* how a transient failure came to mean "this product opted out". A missing
|
|
83
|
+
* declaration is a product that does not use the allocator; a declaration that
|
|
84
|
+
* exists and could not be read is a repository this process cannot currently
|
|
85
|
+
* describe, and granting against the daemon's older snapshot alone is exactly
|
|
86
|
+
* what the claimant's reading exists to prevent.
|
|
87
|
+
*/
|
|
88
|
+
export type FileReading = {
|
|
89
|
+
kind: "read";
|
|
90
|
+
text: string;
|
|
91
|
+
} | {
|
|
92
|
+
kind: "absent";
|
|
93
|
+
} | {
|
|
94
|
+
kind: "unreadable";
|
|
95
|
+
reason: string;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* VALIDATE TO THE CANONICAL CONTRACT.
|
|
99
|
+
*
|
|
100
|
+
* The rules here are the daemon's, transcribed:
|
|
101
|
+
* `packages/daemon/src/migrations/migration-manifest.ts` is the single
|
|
102
|
+
* definition, and `migration-manifest-parity.test.ts` pins that this reader
|
|
103
|
+
* accepts and rejects the same declarations it does.
|
|
104
|
+
*
|
|
105
|
+
* WHY TRANSCRIBED RATHER THAN IMPORTED. This package is published and installed
|
|
106
|
+
* on daemon hosts by itself; it cannot depend on `@telora/daemon`. The parity
|
|
107
|
+
* test is what keeps a transcription from drifting into a second, laxer
|
|
108
|
+
* contract.
|
|
109
|
+
*
|
|
110
|
+
* WHY IT MATTERS THAT THEY AGREE. A claimant that accepts a declaration the
|
|
111
|
+
* daemon calls unreadable reports a COMPLETE observation of a repository it
|
|
112
|
+
* cannot actually read -- an empty one, because a pattern that does not compile
|
|
113
|
+
* matches nothing -- and a focus-scoped claim then grants against the daemon's
|
|
114
|
+
* older published snapshot with an empty "current" reading vouching for it.
|
|
115
|
+
* Every present-but-invalid declaration must reach the same verdict on both
|
|
116
|
+
* sides: not usable.
|
|
117
|
+
*/
|
|
118
|
+
export declare function validateDeclaration(parsed: unknown): {
|
|
119
|
+
ok: true;
|
|
120
|
+
declaration: Declaration;
|
|
121
|
+
} | {
|
|
122
|
+
ok: false;
|
|
123
|
+
problem: string;
|
|
124
|
+
};
|
|
125
|
+
/** Injected seams, so the reader is testable over a real temp repo or a fake. */
|
|
126
|
+
export interface ObservationDeps {
|
|
127
|
+
cwd: () => string;
|
|
128
|
+
runGit: (args: string[], cwd: string) => string | null;
|
|
129
|
+
readFile: (path: string) => FileReading;
|
|
130
|
+
now: () => number;
|
|
131
|
+
}
|
|
132
|
+
export declare const defaultObservationDeps: ObservationDeps;
|
|
133
|
+
/**
|
|
134
|
+
* What observing this process's repository produced.
|
|
135
|
+
*
|
|
136
|
+
* THE THREE OUTCOMES ARE NOT INTERCHANGEABLE, and collapsing them to
|
|
137
|
+
* "observation or null" is what let a failed read be served as though the
|
|
138
|
+
* caller simply had no repository. The edge then treated the absence as an
|
|
139
|
+
* empty VALID report and granted against the daemon's older leased snapshot
|
|
140
|
+
* alone -- which is precisely the window the observation exists to close.
|
|
141
|
+
*
|
|
142
|
+
* - `not_applicable` -- there is genuinely nothing to observe: no repository,
|
|
143
|
+
* or a product that declares no numbering. The grant proceeds on the
|
|
144
|
+
* daemon's reading exactly as it did before this reader existed. This is
|
|
145
|
+
* the ONLY silent path, and it is silent on purpose: refusing here would
|
|
146
|
+
* block claiming for every caller outside a checkout.
|
|
147
|
+
* - `incomplete` -- there IS a declared repository and it could not be read
|
|
148
|
+
* whole. Nothing is sent and the claim is not attempted: a partial view
|
|
149
|
+
* folded in as "nothing" is how a held number reads as free.
|
|
150
|
+
* - `observed` -- a complete reading, taken over a ref set that did not move
|
|
151
|
+
* while it was being taken.
|
|
152
|
+
*/
|
|
153
|
+
export type ObservationOutcome = {
|
|
154
|
+
kind: "observed";
|
|
155
|
+
observation: MigrationObservation;
|
|
156
|
+
} | {
|
|
157
|
+
kind: "not_applicable";
|
|
158
|
+
reason: "no_repository" | "no_declaration";
|
|
159
|
+
} | {
|
|
160
|
+
kind: "incomplete";
|
|
161
|
+
reason: string;
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Observe every local branch's migration ordinals, right now.
|
|
165
|
+
*
|
|
166
|
+
* READ BY COMMIT, NOT BY NAME. The tips are captured once, up front, and every
|
|
167
|
+
* tree and journal below is read through the captured SHA. Reading through the
|
|
168
|
+
* mutable branch NAME meant a branch that moved mid-scan was reported at its
|
|
169
|
+
* OLD tip while its NEW tree had been folded in -- `granted_against_tips` then
|
|
170
|
+
* recorded provenance that was simply false, naming commits the allocator had
|
|
171
|
+
* not looked at.
|
|
172
|
+
*
|
|
173
|
+
* AND THE SET IS RE-CHECKED AFTERWARDS. Reading each ref by SHA makes the
|
|
174
|
+
* individual reads coherent; it does not make the SET of them a single moment.
|
|
175
|
+
* A sibling that commits, or a branch created, between the first ref and the
|
|
176
|
+
* last is invisible to a scan that never looks again -- so the enumeration is
|
|
177
|
+
* repeated at the end, and any difference makes the whole reading `incomplete`
|
|
178
|
+
* rather than a snapshot of no particular instant.
|
|
179
|
+
*/
|
|
180
|
+
export declare function observeLocalMigrations(deps?: ObservationDeps): ObservationOutcome;
|
|
181
|
+
export {};
|