@abinnovision/payloadcms-mcpx 2.0.0 → 2.1.0-beta.1
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/endpoint/confirmations.mjs +5 -5
- package/dist/endpoint/handler.mjs +2 -2
- package/dist/endpoint/http.mjs +5 -5
- package/dist/endpoint/upload.mjs +3 -3
- package/dist/{grants → leases}/confirmation.mjs +15 -16
- package/dist/leases/grant.mjs +29 -0
- package/dist/{grants/grant.mjs → leases/lease.mjs} +21 -43
- package/dist/tools/run-confirmed.mjs +3 -3
- package/dist/upload/file.mjs +4 -3
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { isPlainObject } from "../guards.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { leaseContext, leaseHandle, leaseKvSlug } from "../leases/lease.mjs";
|
|
3
3
|
import { respond } from "./http.mjs";
|
|
4
4
|
import { CONFIRMATIONS_PATH, DECISIONS } from "../api-keys/confirmation-view.mjs";
|
|
5
5
|
import { relationId } from "../auth/resolve.mjs";
|
|
6
|
-
import { decideConfirmations, issueConfirmation, listConfirmations } from "../
|
|
6
|
+
import { decideConfirmations, issueConfirmation, listConfirmations } from "../leases/confirmation.mjs";
|
|
7
7
|
import { publicOrigin } from "../request.mjs";
|
|
8
8
|
import { jsonResult } from "../result.mjs";
|
|
9
9
|
import { BUILTIN_TOOLS } from "../tools/builtin.mjs";
|
|
@@ -25,7 +25,7 @@ const keyViewUrl = (req, options, apiKeyId) => {
|
|
|
25
25
|
confirmed: false,
|
|
26
26
|
request: async (args) => {
|
|
27
27
|
const { req } = scope;
|
|
28
|
-
const { slug, apiKeyId } =
|
|
28
|
+
const { slug, apiKeyId } = leaseContext(req);
|
|
29
29
|
const view = keyViewUrl(req, options, apiKeyId);
|
|
30
30
|
const { id, exp } = await issueConfirmation(req.payload, slug, {
|
|
31
31
|
apiKeyId,
|
|
@@ -42,7 +42,7 @@ const keyViewUrl = (req, options, apiKeyId) => {
|
|
|
42
42
|
}
|
|
43
43
|
});
|
|
44
44
|
const authorize = async (req, options, keyId) => {
|
|
45
|
-
const slug =
|
|
45
|
+
const slug = leaseKvSlug(req.payload.config);
|
|
46
46
|
if (slug === void 0) return respond(404, { error: "Confirmations are not available." });
|
|
47
47
|
if (req.user?.collection !== options.userCollection) return respond(401, { error: "Sign in as the user of this key." });
|
|
48
48
|
const key = typeof keyId === "string" || typeof keyId === "number" ? await req.payload.findByID({
|
|
@@ -88,7 +88,7 @@ const viewOf = async (req, entry, highlight) => {
|
|
|
88
88
|
if (allowed instanceof Response) return allowed;
|
|
89
89
|
const { payload } = req;
|
|
90
90
|
const highlighted = req.searchParams.get("confirmation");
|
|
91
|
-
const highlight = highlighted === null ? void 0 :
|
|
91
|
+
const highlight = highlighted === null ? void 0 : leaseHandle(payload, highlighted);
|
|
92
92
|
const entries = (await listConfirmations(payload, allowed.slug, allowed.apiKeyId)).filter((entry) => entry.confirmation.state === "pending");
|
|
93
93
|
const confirmations = [];
|
|
94
94
|
for (const entry of entries) confirmations.push(await viewOf(req, entry, highlight));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveCapabilities, scopeSlugs } from "../capabilities.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { leasesAvailable, readCapped } from "./http.mjs";
|
|
3
3
|
import { isValidAuthResult, resolveApiKeyAuth } from "../auth/resolve.mjs";
|
|
4
4
|
import { jsonRpcError } from "./errors.mjs";
|
|
5
5
|
import { createMcpServer } from "./server.mjs";
|
|
@@ -22,7 +22,7 @@ const BATCH_MESSAGES_LIMIT = 10;
|
|
|
22
22
|
defaultLocale: localization.defaultLocale
|
|
23
23
|
} : null,
|
|
24
24
|
limits: options.limits,
|
|
25
|
-
uploads:
|
|
25
|
+
uploads: leasesAvailable(req, options) !== void 0,
|
|
26
26
|
diagnostics: options.diagnostics ? options.serverInfo : null,
|
|
27
27
|
exposure: {
|
|
28
28
|
collections: options.collections,
|
package/dist/endpoint/http.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { leaseKvSlug } from "../leases/lease.mjs";
|
|
2
2
|
//#region src/endpoint/http.ts
|
|
3
3
|
const respond = (status, body) => Response.json(body, { status });
|
|
4
4
|
/**
|
|
@@ -19,9 +19,9 @@ const respond = (status, body) => Response.json(body, { status });
|
|
|
19
19
|
return Buffer.concat(chunks);
|
|
20
20
|
};
|
|
21
21
|
/**
|
|
22
|
-
* The
|
|
22
|
+
* The lease KV slug where uploads, downloads and confirmations are possible:
|
|
23
23
|
* the KV is database-backed and no custom `auth.resolve` is set, since the
|
|
24
|
-
*
|
|
25
|
-
*/ const
|
|
24
|
+
* lease endpoints cannot replay what such a resolver authenticated by.
|
|
25
|
+
*/ const leasesAvailable = (req, options) => options.auth?.resolve === void 0 ? leaseKvSlug(req.payload.config) : void 0;
|
|
26
26
|
//#endregion
|
|
27
|
-
export {
|
|
27
|
+
export { leasesAvailable, readCapped, respond };
|
package/dist/endpoint/upload.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { isToolEnabled, toolInputSchema } from "../define-tool.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { grantsAvailable, readCapped, respond } from "./http.mjs";
|
|
2
|
+
import { leasesAvailable, readCapped, respond } from "./http.mjs";
|
|
4
3
|
import { resolveApiKeyById } from "../auth/resolve.mjs";
|
|
5
4
|
import { parseResult } from "../result.mjs";
|
|
5
|
+
import { claimGrant } from "../leases/grant.mjs";
|
|
6
6
|
import { downloadSlugs, setUploadedFile, uploadMaxBytes } from "../upload/file.mjs";
|
|
7
7
|
import { BUILTIN_TOOLS } from "../tools/builtin.mjs";
|
|
8
8
|
import { runTool } from "./server.mjs";
|
|
@@ -23,7 +23,7 @@ const NO_SESSION = {
|
|
|
23
23
|
sendRequest: () => Promise.reject(/* @__PURE__ */ new Error("No MCP session."))
|
|
24
24
|
};
|
|
25
25
|
const claimAs = async (req, options, kind) => {
|
|
26
|
-
const slug =
|
|
26
|
+
const slug = leasesAvailable(req, options);
|
|
27
27
|
const grantId = req.headers.get("x-mcpx-grant");
|
|
28
28
|
if (slug === void 0 || grantId === null) return;
|
|
29
29
|
const grant = await claimGrant(req.payload, slug, grantId);
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/
|
|
1
|
+
import { LEASE_ID, NO_REQ, claimLease, issueLease, leaseHandle, leasePrefixes, leasesWith, unexpired } from "./lease.mjs";
|
|
2
|
+
//#region src/leases/confirmation.ts
|
|
3
3
|
const CONFIRMATION_TTL_MS = 9e5;
|
|
4
4
|
const HANDLE = /^[\da-f]{64}$/;
|
|
5
|
-
const asConfirmation = (row, now) => row && expOf(row.data) > now ? row.data : void 0;
|
|
6
5
|
/**
|
|
7
6
|
* Stores a pending call and returns its id. A key may hold at most
|
|
8
7
|
* {@link CONFIRMATIONS_PER_KEY} calls.
|
|
@@ -11,8 +10,8 @@ const asConfirmation = (row, now) => row && expOf(row.data) > now ? row.data : v
|
|
|
11
10
|
...call,
|
|
12
11
|
state: "pending"
|
|
13
12
|
};
|
|
14
|
-
return await
|
|
15
|
-
...
|
|
13
|
+
return await issueLease(payload, slug, {
|
|
14
|
+
...leasePrefixes("confirmation", call.apiKeyId),
|
|
16
15
|
cap: 50,
|
|
17
16
|
capMessage: `This key already has ${String(50)} calls waiting for approval. Run or abandon them first; each expires 15 minutes after it was requested.`,
|
|
18
17
|
ttlMs: CONFIRMATION_TTL_MS,
|
|
@@ -23,22 +22,22 @@ const asConfirmation = (row, now) => row && expOf(row.data) > now ? row.data : v
|
|
|
23
22
|
* The unexpired call of `apiKeyId` with `id`, without claiming it. A call of
|
|
24
23
|
* another key is `undefined`, as is a malformed id, before any database read.
|
|
25
24
|
*/ const readConfirmation = async (payload, slug, apiKeyId, id) => {
|
|
26
|
-
if (!
|
|
27
|
-
const key = `${
|
|
28
|
-
const rows = await
|
|
29
|
-
return
|
|
25
|
+
if (!LEASE_ID.test(id)) return;
|
|
26
|
+
const key = `${leasePrefixes("confirmation", apiKeyId).prefix}${leaseHandle(payload, id)}`;
|
|
27
|
+
const rows = await leasesWith(payload, slug, [key]);
|
|
28
|
+
return unexpired(rows.find((row) => row.key === key), Date.now());
|
|
30
29
|
};
|
|
31
30
|
/**
|
|
32
31
|
* Marks the call used and removes it. Of two concurrent claims exactly one
|
|
33
32
|
* returns `true`.
|
|
34
|
-
*/ const claimConfirmation = async (payload, slug, apiKeyId, id, exp) => await
|
|
33
|
+
*/ const claimConfirmation = async (payload, slug, apiKeyId, id, exp) => await claimLease(payload, slug, leasePrefixes("confirmation", apiKeyId), leaseHandle(payload, id), exp);
|
|
35
34
|
/**
|
|
36
35
|
* Every unexpired call of `apiKeyId` with its handle, oldest first.
|
|
37
36
|
*/ const listConfirmations = async (payload, slug, apiKeyId) => {
|
|
38
|
-
const { prefix } =
|
|
37
|
+
const { prefix } = leasePrefixes("confirmation", apiKeyId);
|
|
39
38
|
const now = Date.now();
|
|
40
|
-
return (await
|
|
41
|
-
const confirmation =
|
|
39
|
+
return (await leasesWith(payload, slug, [prefix])).flatMap((row) => {
|
|
40
|
+
const confirmation = unexpired(row, now);
|
|
42
41
|
return confirmation ? [{
|
|
43
42
|
handle: row.key.slice(prefix.length),
|
|
44
43
|
confirmation
|
|
@@ -50,13 +49,13 @@ const asConfirmation = (row, now) => row && expOf(row.data) > now ? row.data : v
|
|
|
50
49
|
* returns the handles it changed. A call that was decided, run or expired is
|
|
51
50
|
* left alone and not returned.
|
|
52
51
|
*/ const decideConfirmations = async (payload, slug, apiKeyId, handles, state) => {
|
|
53
|
-
const { prefix } =
|
|
52
|
+
const { prefix } = leasePrefixes("confirmation", apiKeyId);
|
|
54
53
|
const keys = new Set(handles.filter((handle) => HANDLE.test(handle)).map((handle) => `${prefix}${handle}`));
|
|
55
54
|
if (keys.size === 0) return [];
|
|
56
55
|
const now = Date.now();
|
|
57
56
|
const decided = [];
|
|
58
|
-
for (const row of await
|
|
59
|
-
const confirmation =
|
|
57
|
+
for (const row of await leasesWith(payload, slug, [prefix])) {
|
|
58
|
+
const confirmation = unexpired(row, now);
|
|
60
59
|
if (!keys.has(row.key) || confirmation?.state !== "pending") continue;
|
|
61
60
|
const updated = await payload.db.updateMany({
|
|
62
61
|
collection: slug,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { LEASE_ID, claimLease, issueLease, leaseHandle, leasePrefixes, leasesMatching, unexpired } from "./lease.mjs";
|
|
2
|
+
//#region src/leases/grant.ts
|
|
3
|
+
const GRANT_TTL_MS = 3e5;
|
|
4
|
+
const GRANTS_PER_KEY = 10;
|
|
5
|
+
/**
|
|
6
|
+
* Stores a grant and returns its id. A key may hold at most
|
|
7
|
+
* {@link GRANTS_PER_KEY} grants.
|
|
8
|
+
*/ const issueGrant = async (payload, slug, grant) => await issueLease(payload, slug, {
|
|
9
|
+
...leasePrefixes("grant", grant.apiKeyId),
|
|
10
|
+
cap: GRANTS_PER_KEY,
|
|
11
|
+
capMessage: `This key already has ${String(GRANTS_PER_KEY)} uploads or downloads waiting. Use or abandon them first; each expires 5 minutes after it was issued.`,
|
|
12
|
+
ttlMs: GRANT_TTL_MS,
|
|
13
|
+
data: grant
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Loads the grant for `grantId` and marks it used, or returns `undefined` for
|
|
17
|
+
* a malformed, unknown, expired or used one. A malformed id is refused before
|
|
18
|
+
* any database read.
|
|
19
|
+
*/ const claimGrant = async (payload, slug, grantId) => {
|
|
20
|
+
if (!LEASE_ID.test(grantId)) return;
|
|
21
|
+
const handle = leaseHandle(payload, grantId);
|
|
22
|
+
const row = (await leasesMatching(payload, slug, [handle])).find((candidate) => candidate.key.startsWith("mcpx-grant:") && candidate.key.endsWith(`:${handle}`));
|
|
23
|
+
const grant = unexpired(row, Date.now());
|
|
24
|
+
if (!row || !grant) return;
|
|
25
|
+
const prefixes = leasePrefixes("grant", grant.apiKeyId);
|
|
26
|
+
return row.key === `${prefixes.prefix}${handle}` && await claimLease(payload, slug, prefixes, handle, grant.exp) ? grant : void 0;
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
29
|
+
export { claimGrant, issueGrant };
|
|
@@ -1,33 +1,31 @@
|
|
|
1
1
|
import { isPlainObject } from "../guards.mjs";
|
|
2
2
|
import { generateApiKey, hashApiKey } from "../api-keys/key.mjs";
|
|
3
3
|
import { APIError, ValidationError } from "payload";
|
|
4
|
-
//#region src/
|
|
5
|
-
const
|
|
6
|
-
const GRANTS_PER_KEY = 10;
|
|
7
|
-
const ROW_ID = /^[\w-]{43}$/;
|
|
4
|
+
//#region src/leases/lease.ts
|
|
5
|
+
const LEASE_ID = /^[\w-]{43}$/;
|
|
8
6
|
const NO_REQ = {};
|
|
9
7
|
/**
|
|
10
|
-
* The key prefixes of a kind of
|
|
8
|
+
* The key prefixes of a kind of lease: waiting leases and the markers of used
|
|
11
9
|
* ones.
|
|
12
|
-
*/ const
|
|
10
|
+
*/ const leasePrefixes = (kind, apiKeyId) => ({
|
|
13
11
|
prefix: `mcpx-${kind}:${String(apiKeyId)}:`,
|
|
14
12
|
usedPrefix: `mcpx-${kind}-used:${String(apiKeyId)}:`
|
|
15
13
|
});
|
|
16
14
|
/**
|
|
17
|
-
* What a
|
|
18
|
-
*/ const
|
|
15
|
+
* What a lease is addressed by: the HMAC of its id, which the row key ends in.
|
|
16
|
+
*/ const leaseHandle = (payload, id) => hashApiKey(payload.secret, id);
|
|
19
17
|
const expOf = (data) => isPlainObject(data) && typeof data["exp"] === "number" ? data["exp"] : 0;
|
|
20
18
|
/**
|
|
21
19
|
* The KV collection slug, when Payload's database KV adapter backs it with a
|
|
22
20
|
* unique `key`, which the single-use claim relies on. `undefined` otherwise.
|
|
23
|
-
*/ const
|
|
21
|
+
*/ const leaseKvSlug = (config) => {
|
|
24
22
|
const kv = config.kv;
|
|
25
23
|
const key = kv?.kvCollection?.fields.find((field) => "name" in field && field.name === "key");
|
|
26
24
|
return key && "unique" in key && key.unique ? kv?.kvCollection?.slug : void 0;
|
|
27
25
|
};
|
|
28
26
|
/**
|
|
29
27
|
* KV rows whose key contains one of `fragments`.
|
|
30
|
-
*/ const
|
|
28
|
+
*/ const leasesMatching = async (payload, slug, fragments) => {
|
|
31
29
|
const { docs } = await payload.db.find({
|
|
32
30
|
collection: slug,
|
|
33
31
|
where: { or: fragments.map((fragment) => ({ key: { like: fragment } })) },
|
|
@@ -40,15 +38,15 @@ const expOf = (data) => isPlainObject(data) && typeof data["exp"] === "number" ?
|
|
|
40
38
|
/**
|
|
41
39
|
* KV rows whose key starts with one of `prefixes`. `like` matches a
|
|
42
40
|
* substring, so the prefix is checked again here.
|
|
43
|
-
*/ const
|
|
41
|
+
*/ const leasesWith = async (payload, slug, prefixes) => (await leasesMatching(payload, slug, prefixes)).filter((row) => prefixes.some((prefix) => row.key.startsWith(prefix)));
|
|
44
42
|
/**
|
|
45
43
|
* Stores `data` under `prefix` and the HMAC of a fresh id, and returns the id,
|
|
46
44
|
* the only copy of which goes to the client. Expired rows under `prefix` and
|
|
47
45
|
* `usedPrefix` are purged first, and at most `cap` unexpired rows may wait
|
|
48
46
|
* under `prefix`; past that, `capMessage` is thrown with 429.
|
|
49
|
-
*/ const
|
|
47
|
+
*/ const issueLease = async (payload, slug, args) => {
|
|
50
48
|
const now = Date.now();
|
|
51
|
-
const rows = await
|
|
49
|
+
const rows = await leasesWith(payload, slug, [args.prefix, args.usedPrefix]);
|
|
52
50
|
const expired = rows.filter((row) => expOf(row.data) <= now);
|
|
53
51
|
if (expired.length > 0) await payload.db.deleteMany({
|
|
54
52
|
collection: slug,
|
|
@@ -61,7 +59,7 @@ const expOf = (data) => isPlainObject(data) && typeof data["exp"] === "number" ?
|
|
|
61
59
|
await payload.db.create({
|
|
62
60
|
collection: slug,
|
|
63
61
|
data: {
|
|
64
|
-
key: `${args.prefix}${
|
|
62
|
+
key: `${args.prefix}${leaseHandle(payload, id)}`,
|
|
65
63
|
data: {
|
|
66
64
|
...args.data,
|
|
67
65
|
exp
|
|
@@ -77,7 +75,7 @@ const expOf = (data) => isPlainObject(data) && typeof data["exp"] === "number" ?
|
|
|
77
75
|
/**
|
|
78
76
|
* Marks the row under `handle` used and removes it. The marker is a
|
|
79
77
|
* unique-key insert, so of two concurrent claims exactly one returns `true`.
|
|
80
|
-
*/ const
|
|
78
|
+
*/ const claimLease = async (payload, slug, prefixes, handle, exp) => {
|
|
81
79
|
try {
|
|
82
80
|
await payload.db.create({
|
|
83
81
|
collection: slug,
|
|
@@ -99,39 +97,19 @@ const expOf = (data) => isPlainObject(data) && typeof data["exp"] === "number" ?
|
|
|
99
97
|
return true;
|
|
100
98
|
};
|
|
101
99
|
/**
|
|
102
|
-
* The KV slug and key id of the request, which a call that stores a
|
|
103
|
-
*
|
|
104
|
-
*/ const
|
|
105
|
-
const slug =
|
|
100
|
+
* The KV slug and key id of the request, which a call that stores a lease
|
|
101
|
+
* needs. Throws where the endpoint would not offer one.
|
|
102
|
+
*/ const leaseContext = (req) => {
|
|
103
|
+
const slug = leaseKvSlug(req.payload.config);
|
|
106
104
|
const apiKeyId = req.context.mcpx?.apiKeyId;
|
|
107
|
-
if (slug === void 0 || apiKeyId === void 0) throw new Error("A
|
|
105
|
+
if (slug === void 0 || apiKeyId === void 0) throw new Error("A lease needs a database KV and a key.");
|
|
108
106
|
return {
|
|
109
107
|
slug,
|
|
110
108
|
apiKeyId
|
|
111
109
|
};
|
|
112
110
|
};
|
|
113
111
|
/**
|
|
114
|
-
*
|
|
115
|
-
|
|
116
|
-
*/ const issueGrant = async (payload, slug, grant) => await issueRow(payload, slug, {
|
|
117
|
-
...rowPrefixes("grant", grant.apiKeyId),
|
|
118
|
-
cap: GRANTS_PER_KEY,
|
|
119
|
-
capMessage: `This key already has ${String(GRANTS_PER_KEY)} uploads or downloads waiting. Use or abandon them first; each expires 5 minutes after it was issued.`,
|
|
120
|
-
ttlMs: GRANT_TTL_MS,
|
|
121
|
-
data: grant
|
|
122
|
-
});
|
|
123
|
-
/**
|
|
124
|
-
* Loads the grant for `grantId` and marks it used, or returns `undefined` for
|
|
125
|
-
* a malformed, unknown, expired or used one. A malformed id is refused before
|
|
126
|
-
* any database read.
|
|
127
|
-
*/ const claimGrant = async (payload, slug, grantId) => {
|
|
128
|
-
if (!ROW_ID.test(grantId)) return;
|
|
129
|
-
const handle = rowHandle(payload, grantId);
|
|
130
|
-
const row = (await findRows(payload, slug, [handle])).find((candidate) => candidate.key.startsWith("mcpx-grant:") && candidate.key.endsWith(`:${handle}`));
|
|
131
|
-
const grant = row?.data;
|
|
132
|
-
if (!row || !isPlainObject(grant) || expOf(grant) <= Date.now()) return;
|
|
133
|
-
const prefixes = rowPrefixes("grant", grant.apiKeyId);
|
|
134
|
-
return row.key === `${prefixes.prefix}${handle}` && await claimRow(payload, slug, prefixes, handle, grant.exp) ? grant : void 0;
|
|
135
|
-
};
|
|
112
|
+
* The data of `row` when it has not expired at `now`, otherwise `undefined`.
|
|
113
|
+
*/ const unexpired = (row, now) => row && expOf(row.data) > now ? row.data : void 0;
|
|
136
114
|
//#endregion
|
|
137
|
-
export {
|
|
115
|
+
export { LEASE_ID, NO_REQ, claimLease, issueLease, leaseContext, leaseHandle, leaseKvSlug, leasePrefixes, leasesMatching, leasesWith, unexpired };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defineMcpxTool, isToolEnabled, toolInputSchema } from "../define-tool.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { claimConfirmation, readConfirmation } from "../
|
|
2
|
+
import { leaseContext } from "../leases/lease.mjs";
|
|
3
|
+
import { claimConfirmation, readConfirmation } from "../leases/confirmation.mjs";
|
|
4
4
|
import { jsonResult, parseResult } from "../result.mjs";
|
|
5
5
|
import { withRequestLocale } from "./document.mjs";
|
|
6
6
|
import { z } from "zod";
|
|
@@ -86,7 +86,7 @@ const claimApproved = async (req, slug, apiKeyId, id) => {
|
|
|
86
86
|
inputSchema: { ids: z.array(z.string().max(64)).min(1).max(50).describe("\"id\" of each \"confirmation\".") },
|
|
87
87
|
handler: async ({ args, scope, extra }) => {
|
|
88
88
|
const { req } = scope;
|
|
89
|
-
const { slug, apiKeyId } =
|
|
89
|
+
const { slug, apiKeyId } = leaseContext(req);
|
|
90
90
|
const outcomes = [];
|
|
91
91
|
for (const id of args.ids) {
|
|
92
92
|
const confirmation = await claimApproved(req, slug, apiKeyId, id);
|
package/dist/upload/file.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { leaseContext } from "../leases/lease.mjs";
|
|
2
2
|
import { publicOrigin } from "../request.mjs";
|
|
3
3
|
import { errorResult, jsonResult } from "../result.mjs";
|
|
4
|
+
import { issueGrant } from "../leases/grant.mjs";
|
|
4
5
|
import { SchemaError } from "../schema/errors.mjs";
|
|
5
6
|
import { resolveDataPointer } from "../schema/pointer.mjs";
|
|
6
7
|
import "../schema/index.mjs";
|
|
@@ -126,7 +127,7 @@ const handoffUrl = (req, path) => {
|
|
|
126
127
|
*/ const uploadHandoff = async (scope, call) => {
|
|
127
128
|
const { req } = scope;
|
|
128
129
|
const { payload } = req;
|
|
129
|
-
const { slug, apiKeyId } =
|
|
130
|
+
const { slug, apiKeyId } = leaseContext(req);
|
|
130
131
|
const url = handoffUrl(req, "upload");
|
|
131
132
|
const { id, exp } = await issueGrant(payload, slug, {
|
|
132
133
|
kind: "upload",
|
|
@@ -158,7 +159,7 @@ const handoffUrl = (req, path) => {
|
|
|
158
159
|
* GET it.
|
|
159
160
|
*/ const downloadHandoff = async (scope, target) => {
|
|
160
161
|
const { req } = scope;
|
|
161
|
-
const { slug, apiKeyId } =
|
|
162
|
+
const { slug, apiKeyId } = leaseContext(req);
|
|
162
163
|
const url = handoffUrl(req, "file");
|
|
163
164
|
const { id, exp } = await issueGrant(req.payload, slug, {
|
|
164
165
|
kind: "download",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@abinnovision/payloadcms-mcpx",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.1.0-beta.1",
|
|
5
5
|
"description": "Payload CMS plugin exposing a fixed, schema-aware MCP tool surface with draft-only writes and per-API-key capabilities.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"payload",
|