specguard-mcp 0.1.12 → 0.1.13
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 +113 -58
- package/dist/src/config.d.ts +100 -18
- package/dist/src/config.js +125 -5
- package/dist/src/config.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +7 -6
- package/dist/src/index.js.map +1 -1
- package/dist/src/support/specguard-api.d.ts +2 -2
- package/dist/src/support/specguard-api.js +11 -10
- package/dist/src/support/specguard-api.js.map +1 -1
- package/dist/src/tools/add-repository.js +3 -2
- package/dist/src/tools/add-repository.js.map +1 -1
- package/dist/src/tools/index.d.ts +4 -3
- package/dist/src/tools/index.js +4 -3
- package/dist/src/tools/index.js.map +1 -1
- package/dist/src/tools/list-repositories.d.ts +47 -25
- package/dist/src/tools/list-repositories.js +79 -40
- package/dist/src/tools/list-repositories.js.map +1 -1
- package/dist/src/tools/list-repository-members.js +3 -2
- package/dist/src/tools/list-repository-members.js.map +1 -1
- package/dist/src/tools/near-duplicate-clusters.d.ts +20 -8
- package/dist/src/tools/near-duplicate-clusters.js +70 -23
- package/dist/src/tools/near-duplicate-clusters.js.map +1 -1
- package/dist/src/tools/registrable-repositories.js +2 -2
- package/dist/src/tools/registrable-repositories.js.map +1 -1
- package/dist/src/tools/repository-overview.d.ts +41 -1
- package/dist/src/tools/repository-overview.js +107 -6
- package/dist/src/tools/repository-overview.js.map +1 -1
- package/package.json +1 -1
package/dist/src/config.js
CHANGED
|
@@ -25,6 +25,7 @@ export function loadConfig(env = process.env) {
|
|
|
25
25
|
endpointVariable,
|
|
26
26
|
apiKey: presence(env["SPECGUARD_API_KEY"]),
|
|
27
27
|
userApiKey: presence(env["SPECGUARD_USER_API_KEY"]),
|
|
28
|
+
agentApiKey: presence(env["SPECGUARD_AGENT_API_KEY"]),
|
|
28
29
|
lintCommand: lintCommand.length > 0 ? lintCommand : DEFAULT_LINT_COMMAND,
|
|
29
30
|
requestTimeoutMs: positiveInteger(env["SPECGUARD_TIMEOUT_MS"]) ?? DEFAULT_REQUEST_TIMEOUT_MS,
|
|
30
31
|
};
|
|
@@ -59,6 +60,34 @@ export const USER_CREDENTIAL = {
|
|
|
59
60
|
"that person may open, an sgk_… repository key is refused here without a lookup, " +
|
|
60
61
|
"and a revoked key reads the same as a wrong one",
|
|
61
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* The `sga_` key: an AGENT, and a credential that speaks for nobody.
|
|
65
|
+
*
|
|
66
|
+
* Like the `sgu_` key it is minted from the account page, but the two are not
|
|
67
|
+
* the same gesture and the message must not blur them: a person key speaks with
|
|
68
|
+
* the person's whole grantable surface, while an agent key reaches exactly the
|
|
69
|
+
* repository set and permission set granted onto it at mint time — which is the
|
|
70
|
+
* whole reason an automated agent holds this one rather than a person's. The
|
|
71
|
+
* `issuedFrom` fragment names the panel, because an operator sent to "the
|
|
72
|
+
* account page" for an `sga_` key lands on the person-key panel one scroll
|
|
73
|
+
* earlier and mints the wrong kind.
|
|
74
|
+
*
|
|
75
|
+
* The `rejection` fragment names the two refusals specific to this kind: the
|
|
76
|
+
* mutual prefix refusal (both other key kinds are refused here without a
|
|
77
|
+
* lookup), and the fact that a key whose grant was too narrow does NOT read as
|
|
78
|
+
* a 401 — a repository outside the key's set answers 404, deliberately
|
|
79
|
+
* indistinguishable from a nonexistent one, so a 401 here is about the KEY
|
|
80
|
+
* itself (wrong kind, revoked, or the owner archived) and never about which
|
|
81
|
+
* repositories it names.
|
|
82
|
+
*/
|
|
83
|
+
export const AGENT_CREDENTIAL = {
|
|
84
|
+
variable: "SPECGUARD_AGENT_API_KEY",
|
|
85
|
+
prefix: "sga_",
|
|
86
|
+
issuedFrom: "issued from your account page's Agent keys panel",
|
|
87
|
+
rejection: "for this agent — an agent key speaks for nobody and reaches only the repository set " +
|
|
88
|
+
"granted onto it at mint time, an sgk_… repository key or an sgu_… user key is refused " +
|
|
89
|
+
"here without a lookup, and a revoked key reads the same as a wrong one",
|
|
90
|
+
};
|
|
62
91
|
/**
|
|
63
92
|
* The name to speak when no variable was set at all — the message is telling
|
|
64
93
|
* someone to set one, and this is the spelling the rest of the toolchain reads.
|
|
@@ -90,6 +119,70 @@ export function requireApiConfig(config) {
|
|
|
90
119
|
export function requireUserApiConfig(config) {
|
|
91
120
|
return requireCredentialledApiConfig(config, config.userApiKey, USER_CREDENTIAL);
|
|
92
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* What a tool needing the `sga_` AGENT key requires — the endpoint and
|
|
124
|
+
* `SPECGUARD_AGENT_API_KEY`.
|
|
125
|
+
*
|
|
126
|
+
* The third sibling over the one shared body, for the reason the file's own
|
|
127
|
+
* header anticipated: a tool asks for what IT needs, startup still validates
|
|
128
|
+
* nothing, and a fix to the diagnostics reaches all three because they cannot
|
|
129
|
+
* drift into describing the same situation differently. A repository-scoped
|
|
130
|
+
* tool asked about a repository its `sgk_` slot does not name calls this, and
|
|
131
|
+
* reads the plural endpoints the agent credential is served by.
|
|
132
|
+
*/
|
|
133
|
+
export function requireAgentApiConfig(config) {
|
|
134
|
+
return requireCredentialledApiConfig(config, config.agentApiKey, AGENT_CREDENTIAL);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* What a tool EITHER credential serves requires — the endpoint and at least one
|
|
138
|
+
* of `SPECGUARD_AGENT_API_KEY` / `SPECGUARD_USER_API_KEY`.
|
|
139
|
+
*
|
|
140
|
+
* The plural endpoints accept both key kinds and bound the answer by whichever
|
|
141
|
+
* was presented — the person's `accessible_by` set for an `sgu_` key, the key's
|
|
142
|
+
* own granted set for an `sga_` one — so the tool's question ("which
|
|
143
|
+
* repositories may I ask about") has a true answer under either credential, and
|
|
144
|
+
* refusing until the operator picks one would be inventing a requirement the
|
|
145
|
+
* deployment does not have.
|
|
146
|
+
*
|
|
147
|
+
* When BOTH are set, the agent key wins, and the reason is consistency of
|
|
148
|
+
* scope rather than preference: every other agent-keyed tool answers inside the
|
|
149
|
+
* key's granted set, so a listing served from the person's wider set would
|
|
150
|
+
* advertise repositories the agent then cannot open — discovery promising more
|
|
151
|
+
* than the tools that follow it can deliver. The agent set is a subset of its
|
|
152
|
+
* owner's by mint-time validation, so this never widens what the answer names,
|
|
153
|
+
* only keeps it to what the agent can actually act on. The variable is new, so
|
|
154
|
+
* no existing operator's behavior changes: today nobody sets it, and setting it
|
|
155
|
+
* is a deliberate act this precedence honours.
|
|
156
|
+
*
|
|
157
|
+
* When NEITHER is set, the one-message rule applies at double width: both
|
|
158
|
+
* variables are named, because telling an operator who is expected to choose
|
|
159
|
+
* between two spellings about only one of them would have them fix a variable,
|
|
160
|
+
* re-call, and be told about the other — the round-trip waste the shared body's
|
|
161
|
+
* note above exists to prevent. That sentence is NOT written here: the helper
|
|
162
|
+
* falls through to the shared body with the credentials it should name, so the
|
|
163
|
+
* wording is produced once, by the same code the other two entry points use,
|
|
164
|
+
* and the one invariant this file asserts about its diagnostics — a fix reaches
|
|
165
|
+
* every caller because no caller describes the situation itself — holds for
|
|
166
|
+
* this helper too. The endpoint joins the same sentence when it is missing too.
|
|
167
|
+
*/
|
|
168
|
+
export function requireUserOrAgentApiConfig(config) {
|
|
169
|
+
if (config.agentApiKey !== undefined) {
|
|
170
|
+
return requireCredentialledApiConfig(config, config.agentApiKey, AGENT_CREDENTIAL);
|
|
171
|
+
}
|
|
172
|
+
if (config.userApiKey !== undefined) {
|
|
173
|
+
return requireCredentialledApiConfig(config, config.userApiKey, USER_CREDENTIAL);
|
|
174
|
+
}
|
|
175
|
+
// Neither admissible key is set, so the choice is the operator's — and the
|
|
176
|
+
// message has to say so by naming BOTH spellings. That is what the fourth
|
|
177
|
+
// argument is for: USER_CREDENTIAL leads (the variable an existing operator
|
|
178
|
+
// is more likely to hold, and the one every other user-scoped tool reads)
|
|
179
|
+
// and AGENT_CREDENTIAL rides as its alternative, so `${USER}.variable or
|
|
180
|
+
// ${AGENT}.variable` is what the shared joiner produces rather than a string
|
|
181
|
+
// this helper hand-assembled. Nothing here re-states the sentence, its
|
|
182
|
+
// pluralisation or its tail — a fix to any of those lands here too, which is
|
|
183
|
+
// the whole point of routing through the body this file documents twice.
|
|
184
|
+
return requireCredentialledApiConfig(config, undefined, USER_CREDENTIAL, [AGENT_CREDENTIAL]);
|
|
185
|
+
}
|
|
93
186
|
/**
|
|
94
187
|
* Both halves or a legible failure — never one half and a surprise later.
|
|
95
188
|
*
|
|
@@ -118,20 +211,47 @@ export function requireUserApiConfig(config) {
|
|
|
118
211
|
* The prefix is carried so a MESSAGE can name it, not so this file can enforce
|
|
119
212
|
* it; the 401 branch of `describeFailure` is where a wrong-kind key is
|
|
120
213
|
* diagnosed, with the deployment's own verdict in hand.
|
|
214
|
+
*
|
|
215
|
+
* `alternatives` names the OTHER credentials that would equally answer the
|
|
216
|
+
* calling tool's question, for the missing-key message only: when the key IS
|
|
217
|
+
* present the parameter is dead weight and is never consulted, because the
|
|
218
|
+
* caller has already bound the value it found to the credential it came from.
|
|
219
|
+
* When the key is missing, every admissible variable is named — one credential
|
|
220
|
+
* in the common case, two for the one tool that serves either — so an operator
|
|
221
|
+
* choosing between spellings learns the whole choice in one round trip, and the
|
|
222
|
+
* sentence is written HERE, once, rather than re-authored per helper.
|
|
121
223
|
*/
|
|
122
|
-
function requireCredentialledApiConfig(config, apiKey, credential) {
|
|
224
|
+
function requireCredentialledApiConfig(config, apiKey, credential, alternatives = []) {
|
|
123
225
|
const endpointVariable = config.endpointVariable ?? DEFAULT_ENDPOINT_VARIABLE;
|
|
226
|
+
// Every credential that would answer the calling tool's question — the one
|
|
227
|
+
// the caller binds its PRESENT key to first, then any alternates it also
|
|
228
|
+
// accepts. The list does two jobs, and both only while the key is missing:
|
|
229
|
+
// the missing-variable slot reads `${first.variable} or ${alt.variable}`, and
|
|
230
|
+
// the "Set them in" parenthetical reads one prefix-and-minting clause per
|
|
231
|
+
// entry, joined the same way. With no alternates — the common case, all three
|
|
232
|
+
// single-credential helpers — both reductions collapse to exactly what they
|
|
233
|
+
// were before this parameter existed, byte for byte. With two, the operator
|
|
234
|
+
// expected to choose between spellings learns about all of them in one
|
|
235
|
+
// round trip, which is the property the one-sentence rule exists to protect
|
|
236
|
+
// — and it is the SHARED BODY that says it, so the sentence cannot fork into
|
|
237
|
+
// per-helper dialects the way a hand-rolled copy did before this parameter
|
|
238
|
+
// did.
|
|
239
|
+
const admissible = [credential, ...alternatives];
|
|
124
240
|
const missing = [];
|
|
125
241
|
if (config.endpoint === undefined)
|
|
126
242
|
missing.push(endpointVariable);
|
|
127
|
-
if (apiKey === undefined)
|
|
128
|
-
missing.push(
|
|
243
|
+
if (apiKey === undefined) {
|
|
244
|
+
missing.push(admissible.map((entry) => entry.variable).join(" or "));
|
|
245
|
+
}
|
|
129
246
|
if (missing.length > 0) {
|
|
130
247
|
throw new ConfigError(`This tool talks to a SpecGuard deployment, and ${missing.join(" and ")} ` +
|
|
131
248
|
`${missing.length === 1 ? "is" : "are"} not set in the MCP server's environment. ` +
|
|
132
249
|
"Set them in your MCP client's server config " +
|
|
133
|
-
`(${endpointVariable} is your deployment's root URL,
|
|
134
|
-
|
|
250
|
+
`(${endpointVariable} is your deployment's root URL, ` +
|
|
251
|
+
admissible
|
|
252
|
+
.map((entry) => `${entry.variable} an ${entry.prefix}… key ${entry.issuedFrom}`)
|
|
253
|
+
.join(", or ") +
|
|
254
|
+
"). " +
|
|
135
255
|
"Tools that do not reach the deployment are unaffected.");
|
|
136
256
|
}
|
|
137
257
|
return {
|
package/dist/src/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createServer, SERVER_NAME, SERVER_VERSION, type CreateServerOptions } from "./server.js";
|
|
2
|
-
export { loadConfig, requireApiConfig, requireUserApiConfig, tokenise, type ApiConfig, type ApiKeyVariable, type Config, type Credential, } from "./config.js";
|
|
2
|
+
export { loadConfig, requireApiConfig, requireUserApiConfig, requireAgentApiConfig, requireUserOrAgentApiConfig, tokenise, type ApiConfig, type ApiKeyVariable, type Config, type Credential, } from "./config.js";
|
|
3
3
|
export { ApiError, ArgumentError, CommandError, ConfigError, SpecGuardMcpError } from "./errors.js";
|
|
4
4
|
export { TOOLS } from "./tools/index.js";
|
|
5
5
|
export type { ToolContext, ToolDefinition, ToolResult } from "./tools/types.js";
|
package/dist/src/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export { createServer, SERVER_NAME, SERVER_VERSION } from "./server.js";
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
// can name only
|
|
5
|
-
// reach the
|
|
6
|
-
// exists to keep out of the
|
|
7
|
-
|
|
2
|
+
// The require* helpers are re-exported together rather than left behind each
|
|
3
|
+
// other: they are one seam over one credential machinery, now with three
|
|
4
|
+
// variables, and a consumer that can name only part of it would have to
|
|
5
|
+
// deep-import past this entrypoint to reach the rest — the same
|
|
6
|
+
// unnameable-but-typed state `test/index.test.ts` exists to keep out of the
|
|
7
|
+
// error taxonomy.
|
|
8
|
+
export { loadConfig, requireApiConfig, requireUserApiConfig, requireAgentApiConfig, requireUserOrAgentApiConfig, tokenise, } from "./config.js";
|
|
8
9
|
export { ApiError, ArgumentError, CommandError, ConfigError, SpecGuardMcpError } from "./errors.js";
|
|
9
10
|
export { TOOLS } from "./tools/index.js";
|
|
10
11
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAA4B,MAAM,aAAa,CAAC;AAClG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAA4B,MAAM,aAAa,CAAC;AAClG,6EAA6E;AAC7E,yEAAyE;AACzE,wEAAwE;AACxE,gEAAgE;AAChE,4EAA4E;AAC5E,kBAAkB;AAClB,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,2BAA2B,EAC3B,QAAQ,GAKT,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { requireApiConfig, requireUserApiConfig, type ApiConfig } from "../config.js";
|
|
1
|
+
import { requireAgentApiConfig, requireApiConfig, requireUserApiConfig, requireUserOrAgentApiConfig, type ApiConfig } from "../config.js";
|
|
2
2
|
/**
|
|
3
3
|
* The SpecGuard HTTP client — a Bearer key and a path, and nothing else.
|
|
4
4
|
*
|
|
@@ -112,4 +112,4 @@ export declare function patchJsonObject(api: ApiConfig, path: string, body: Reco
|
|
|
112
112
|
* are the only legal body, it is that no tool re-types this guard.
|
|
113
113
|
*/
|
|
114
114
|
export declare function getJsonObject(api: ApiConfig, path: string, query: Record<string, string | undefined>, fetchImpl: typeof globalThis.fetch): Promise<Record<string, unknown>>;
|
|
115
|
-
export { requireApiConfig, requireUserApiConfig };
|
|
115
|
+
export { requireApiConfig, requireUserApiConfig, requireAgentApiConfig, requireUserOrAgentApiConfig };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { requireApiConfig, requireUserApiConfig } from "../config.js";
|
|
1
|
+
import { requireAgentApiConfig, requireApiConfig, requireUserApiConfig, requireUserOrAgentApiConfig, } from "../config.js";
|
|
2
2
|
import { ApiError } from "../errors.js";
|
|
3
3
|
/**
|
|
4
4
|
* The SpecGuard HTTP client — a Bearer key and a path, and nothing else.
|
|
@@ -293,14 +293,15 @@ function timedOut(api) {
|
|
|
293
293
|
* diagnosis has to be supplied from this side.
|
|
294
294
|
*
|
|
295
295
|
* WHICH VARIABLE AND WHICH PREFIX ARE READ OFF `api.credential`, never spelled
|
|
296
|
-
* out here. SpecGuard has
|
|
297
|
-
* before any table is read, so this one branch is reached by tools
|
|
298
|
-
* different variables — and the sentence it used to hardcode
|
|
299
|
-
* must be an sgk_… key … keys are per-repository") is false
|
|
300
|
-
* claims for a user-scoped tool, naming a
|
|
301
|
-
* touched. That is the same defect
|
|
302
|
-
* it gets the same remedy rather
|
|
303
|
-
*
|
|
296
|
+
* out here. SpecGuard has three credential kinds that refuse each other's
|
|
297
|
+
* tokens before any table is read, so this one branch is reached by tools
|
|
298
|
+
* reading three different variables — and the sentence it used to hardcode
|
|
299
|
+
* ("SPECGUARD_API_KEY must be an sgk_… key … keys are per-repository") is false
|
|
300
|
+
* in all three of its claims for a user- or agent-scoped tool, naming a
|
|
301
|
+
* variable its operator may never have touched. That is the same defect
|
|
302
|
+
* `endpointVariable` fixes one branch down, and it gets the same remedy rather
|
|
303
|
+
* than a second hardcoded string: a tool added later inherits correct naming
|
|
304
|
+
* from the `require*` helper it already calls.
|
|
304
305
|
*/
|
|
305
306
|
function describeFailure(status, body, api) {
|
|
306
307
|
if (status === 401) {
|
|
@@ -372,5 +373,5 @@ function refusalMessage(body, status) {
|
|
|
372
373
|
return undefined;
|
|
373
374
|
return `SpecGuard refused the request (${status}): ${message.trim()}`;
|
|
374
375
|
}
|
|
375
|
-
export { requireApiConfig, requireUserApiConfig };
|
|
376
|
+
export { requireApiConfig, requireUserApiConfig, requireAgentApiConfig, requireUserOrAgentApiConfig };
|
|
376
377
|
//# sourceMappingURL=specguard-api.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specguard-api.js","sourceRoot":"","sources":["../../../src/support/specguard-api.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"specguard-api.js","sourceRoot":"","sources":["../../../src/support/specguard-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -73,8 +73,9 @@ const addRepository = {
|
|
|
73
73
|
"with a message saying exactly that, and the fix is theirs to perform in a browser — no " +
|
|
74
74
|
"argument to this tool can substitute for it. The repository must also be one the SpecGuard " +
|
|
75
75
|
"GitHub App is installed on and that this person administers. " +
|
|
76
|
-
"Needs SPECGUARD_USER_API_KEY (an sgu_… key)
|
|
77
|
-
"and a DIFFERENT one from the sgk_… repository key
|
|
76
|
+
"Needs SPECGUARD_USER_API_KEY (an sgu_… key) — a PERSON key, which `list_repositories` also " +
|
|
77
|
+
"accepts but does not require — and a DIFFERENT one from the sgk_… repository key " +
|
|
78
|
+
"`get_repository_overview` uses.",
|
|
78
79
|
inputSchema: {
|
|
79
80
|
type: "object",
|
|
80
81
|
properties: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"add-repository.js","sourceRoot":"","sources":["../../../src/tools/add-repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,aAAa,GAAmB;IACpC,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,gBAAgB;IACvB,WAAW,EACT,4FAA4F;QAC5F,mEAAmE;QACnE,gGAAgG;QAChG,2CAA2C;QAC3C,mFAAmF;QACnF,mFAAmF;QACnF,gGAAgG;QAChG,2FAA2F;QAC3F,SAAS;QACT,6FAA6F;QAC7F,8FAA8F;QAC9F,yFAAyF;QACzF,sFAAsF;QACtF,6FAA6F;QAC7F,gGAAgG;QAChG,yFAAyF;QACzF,6FAA6F;QAC7F,+DAA+D;QAC/D,6FAA6F;QAC7F,mFAAmF;
|
|
1
|
+
{"version":3,"file":"add-repository.js","sourceRoot":"","sources":["../../../src/tools/add-repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,MAAM,aAAa,GAAmB;IACpC,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,gBAAgB;IACvB,WAAW,EACT,4FAA4F;QAC5F,mEAAmE;QACnE,gGAAgG;QAChG,2CAA2C;QAC3C,mFAAmF;QACnF,mFAAmF;QACnF,gGAAgG;QAChG,2FAA2F;QAC3F,SAAS;QACT,6FAA6F;QAC7F,8FAA8F;QAC9F,yFAAyF;QACzF,sFAAsF;QACtF,6FAA6F;QAC7F,gGAAgG;QAChG,yFAAyF;QACzF,6FAA6F;QAC7F,+DAA+D;QAC/D,6FAA6F;QAC7F,mFAAmF;QACnF,iCAAiC;IACnC,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,SAAS,EAAE;gBACT,IAAI,EAAE,QAAQ;gBACd,WAAW,EACT,oFAAoF;oBACpF,gFAAgF;oBAChF,8EAA8E;aACjF;SACF;QACD,QAAQ,EAAE,CAAC,WAAW,CAAC;QACvB,yEAAyE;QACzE,6EAA6E;QAC7E,4EAA4E;QAC5E,qEAAqE;QACrE,iEAAiE;QACjE,oBAAoB,EAAE,KAAK;KAC5B;IAED,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO;QACrB,6EAA6E;QAC7E,2EAA2E;QAC3E,gDAAgD;QAChD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,CAAC;QAE/D,MAAM,GAAG,GAAG,oBAAoB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEjD,MAAM,YAAY,GAAG,MAAM,cAAc,CACvC,GAAG,EACH,sBAAsB,EACtB,EAAE,gBAAgB,EAAE,QAAQ,EAAE,EAC9B,OAAO,CAAC,KAAK,CACd,CAAC;QAEF,4EAA4E;QAC5E,8EAA8E;QAC9E,2EAA2E;QAC3E,qBAAqB;QACrB,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,UAAU,EAAE,YAAY;SACzB,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,eAAe,aAAa,CAAC"}
|
|
@@ -25,9 +25,10 @@ import type { ToolDefinition } from "./types.js";
|
|
|
25
25
|
* - `list_repositories` wraps `GET /api/v1/repositories` (shipped:
|
|
26
26
|
* `specguard/config/routes.rb`, `Api::V1::UserRepositoriesController`).
|
|
27
27
|
*
|
|
28
|
-
* It is the only tool here that answers to an `
|
|
29
|
-
* `
|
|
30
|
-
*
|
|
28
|
+
* It is the only tool here that answers to an `sga_` AGENT key first, falling
|
|
29
|
+
* back to the `sgu_` USER key, rather than an `sgk_` repository key, and
|
|
30
|
+
* SpecGuard refuses each credential in the other's place before it reads a
|
|
31
|
+
* table. So this entry is also what proves the second
|
|
31
32
|
* variable, the second `require*` helper and the credential-aware diagnostics
|
|
32
33
|
* work end to end — one tool, over a real endpoint, rather than a seam nothing
|
|
33
34
|
* exercises.
|
package/dist/src/tools/index.js
CHANGED
|
@@ -41,9 +41,10 @@ import updateRepositoryMemberPermissions from "./update-repository-member-permis
|
|
|
41
41
|
* - `list_repositories` wraps `GET /api/v1/repositories` (shipped:
|
|
42
42
|
* `specguard/config/routes.rb`, `Api::V1::UserRepositoriesController`).
|
|
43
43
|
*
|
|
44
|
-
* It is the only tool here that answers to an `
|
|
45
|
-
* `
|
|
46
|
-
*
|
|
44
|
+
* It is the only tool here that answers to an `sga_` AGENT key first, falling
|
|
45
|
+
* back to the `sgu_` USER key, rather than an `sgk_` repository key, and
|
|
46
|
+
* SpecGuard refuses each credential in the other's place before it reads a
|
|
47
|
+
* table. So this entry is also what proves the second
|
|
47
48
|
* variable, the second `require*` helper and the credential-aware diagnostics
|
|
48
49
|
* work end to end — one tool, over a real endpoint, rather than a seam nothing
|
|
49
50
|
* exercises.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,qBAAqB,CAAC;AAChD,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAC7D,OAAO,sBAAsB,MAAM,gCAAgC,CAAC;AACpE,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,uBAAuB,MAAM,iCAAiC,CAAC;AACtE,OAAO,uCAAuC,MAAM,mDAAmD,CAAC;AACxG,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,qBAAqB,MAAM,0BAA0B,CAAC;AAC7D,OAAO,uBAAuB,MAAM,+BAA+B,CAAC;AACpE,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,sBAAsB,MAAM,+BAA+B,CAAC;AACnE,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,wBAAwB,MAAM,kCAAkC,CAAC;AACxE,OAAO,sBAAsB,MAAM,gCAAgC,CAAC;AACpE,OAAO,iCAAiC,MAAM,2CAA2C,CAAC;AAG1F
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,qBAAqB,CAAC;AAChD,OAAO,mBAAmB,MAAM,4BAA4B,CAAC;AAC7D,OAAO,sBAAsB,MAAM,gCAAgC,CAAC;AACpE,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,uBAAuB,MAAM,iCAAiC,CAAC;AACtE,OAAO,uCAAuC,MAAM,mDAAmD,CAAC;AACxG,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,qBAAqB,MAAM,8BAA8B,CAAC;AACjE,OAAO,qBAAqB,MAAM,0BAA0B,CAAC;AAC7D,OAAO,uBAAuB,MAAM,+BAA+B,CAAC;AACpE,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,sBAAsB,MAAM,+BAA+B,CAAC;AACnE,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,wBAAwB,MAAM,kCAAkC,CAAC;AACxE,OAAO,sBAAsB,MAAM,gCAAgC,CAAC;AACpE,OAAO,iCAAiC,MAAM,2CAA2C,CAAC;AAG1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4KG;AACH,MAAM,CAAC,MAAM,KAAK,GAA8B;IAC9C,qBAAqB;IACrB,qBAAqB;IACrB,gBAAgB;IAChB,aAAa;IACb,uBAAuB;IACvB,gBAAgB;IAChB,sBAAsB;IACtB,sBAAsB;IACtB,qBAAqB;IACrB,qBAAqB;IACrB,mBAAmB;IACnB,iCAAiC;IACjC,sBAAsB;IACtB,gBAAgB;IAChB,uBAAuB;IACvB,uCAAuC;IACvC,wBAAwB;CACzB,CAAC"}
|
|
@@ -8,9 +8,9 @@ import type { ToolDefinition } from "./types.js";
|
|
|
8
8
|
* Every other tool here answers about ONE repository the caller has already
|
|
9
9
|
* named — and a bridge that can only answer about a repository you can already
|
|
10
10
|
* name cannot tell an agent which repositories there ARE. `get_repository_overview`
|
|
11
|
-
* does not take a repository: its `sgk_` key IS the repository,
|
|
12
|
-
* "what may I ask about" has no answer anywhere in this server. This
|
|
13
|
-
* answer, and it is the whole of what this tool does.
|
|
11
|
+
* without `repository` does not take a repository: its `sgk_` key IS the repository,
|
|
12
|
+
* so the question "what may I ask about" has no answer anywhere in this server. This
|
|
13
|
+
* is that answer, and it is the whole of what this tool does.
|
|
14
14
|
*
|
|
15
15
|
* It is also the tool that proves the second credential slot works end to end,
|
|
16
16
|
* which is why it shipped alone. It no longer IS alone: `add_repository`
|
|
@@ -33,17 +33,24 @@ import type { ToolDefinition } from "./types.js";
|
|
|
33
33
|
* `Api::BaseController` decides which credential table to consult from the
|
|
34
34
|
* token's PREFIX, before any table is read, and answers 401 on a mismatch
|
|
35
35
|
* without a lookup — so this endpoint refuses the `sgk_` key
|
|
36
|
-
* `get_repository_overview` uses, and that one refuses this key. Hence
|
|
37
|
-
* `
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
36
|
+
* `get_repository_overview` uses, and that one refuses this key. Hence a
|
|
37
|
+
* `require*` helper rather than a raw key: the `Credential` carried on the
|
|
38
|
+
* resolved config is what makes a 401 or an unset variable name the one the
|
|
39
|
+
* OPERATOR of this tool has to go and fix. See `config.ts`.
|
|
40
|
+
*
|
|
41
|
+
* Since SPGD-953 it is `requireUserOrAgentApiConfig`, because SPGD-952 made the
|
|
42
|
+
* endpoint answer to BOTH key kinds — the person's `accessible_by` set for an
|
|
43
|
+
* `sgu_` key, the agent key's own granted set for an `sga_` one — and an agent
|
|
44
|
+
* holding only the agent credential must still be able to ask "what may I ask
|
|
45
|
+
* about". When both variables are set the agent key wins, so the listing names
|
|
46
|
+
* the same set every other agent-keyed tool answers inside; the precedence and
|
|
47
|
+
* its reasoning live on the helper.
|
|
41
48
|
*
|
|
42
49
|
* == The credential is the SCOPE, and the arguments narrow WITHIN it
|
|
43
50
|
*
|
|
44
51
|
* This file first shipped argument-less on a premise that has since rotted: it
|
|
45
|
-
* said "the endpoint takes no parameters", because the
|
|
46
|
-
*
|
|
52
|
+
* said "the endpoint takes no parameters", because the credential behind the
|
|
53
|
+
* key was the entire scope of the answer. That stopped being true on
|
|
47
54
|
* 2026-09-05, when specguard `ef6236d` (SPGD-940, #941) grew
|
|
48
55
|
* `GET /api/v1/repositories` three narrowing asks through the shared
|
|
49
56
|
* `RepositoryNarrowing` concern — `?q=` (case-insensitive substring on
|
|
@@ -56,23 +63,29 @@ import type { ToolDefinition } from "./types.js";
|
|
|
56
63
|
* a machine" — and this bridge IS that machine, so they are forwarded here
|
|
57
64
|
* rather than re-invented.
|
|
58
65
|
*
|
|
59
|
-
* What has NOT changed is which side of the boundary the asks sit on.
|
|
60
|
-
* `Repository.accessible_by`
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
+
* What has NOT changed is which side of the boundary the asks sit on. WHICH
|
|
67
|
+
* boundary answered follows the credential — `Repository.accessible_by` (owned
|
|
68
|
+
* UNION shared-through-a-membership) for an `sgu_` key, `AgentApiKey#repositories`
|
|
69
|
+
* (the mint-time granted set) for an `sga_` one — and either way it is the
|
|
70
|
+
* platform's read-side rule, not a filter this bridge could widen or narrow:
|
|
71
|
+
* the controller chains every ask onto the relation that boundary already
|
|
72
|
+
* admitted (`narrow_repositories(authorized_repositories, …)`), so a
|
|
73
|
+
* repository the credential does not admit never ENTERS the relation and no
|
|
74
|
+
* argument here widens the answer — it can only narrow, or re-order, what the
|
|
66
75
|
* credential already admits. Out-of-vocabulary values are the server's to
|
|
67
76
|
* clamp, not ours: an unknown `role` or `sort` settles to the no-ask, never a
|
|
68
77
|
* 400, which is also why a value that reaches the wire is passed through
|
|
69
|
-
* verbatim rather than validated against a second vocabulary here.
|
|
78
|
+
* verbatim rather than validated against a second vocabulary here. Under the
|
|
79
|
+
* agent key the `?role=` ask clamps to the no-ask outright — ownership is a
|
|
80
|
+
* person fact and the key speaks for nobody, so there is no owned/shared line
|
|
81
|
+
* to draw (`UserRepositoriesController#requested_role` is where the server
|
|
82
|
+
* writes that rule, beside `#credential_role`'s `role: "agent"`).
|
|
70
83
|
*
|
|
71
84
|
* Blank is no ask, and so is undefined: `optionalString` returns `undefined`
|
|
72
85
|
* for a blank value and `getJson` omits an `undefined` query entry, so
|
|
73
86
|
* declining an ask and omitting the argument are the same wire request — the
|
|
74
87
|
* established spelling in this codebase (`repository-overview.ts` states it
|
|
75
|
-
* as build-don't-stringify), and the reason the no-
|
|
88
|
+
* as build-don't-stringify), and the reason the no-ask request below is
|
|
76
89
|
* byte-identical to the one this tool made before it had arguments at all.
|
|
77
90
|
*
|
|
78
91
|
* == The response is passed through, not re-modelled
|
|
@@ -85,12 +98,21 @@ import type { ToolDefinition } from "./types.js";
|
|
|
85
98
|
* `repository` block, so a client that has read one knows how to read the other.
|
|
86
99
|
* Renaming or flattening anything here would spend that parity on the last hop.
|
|
87
100
|
*
|
|
88
|
-
* `role` is the field this surface adds
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* register keys or change settings needs to know which of these
|
|
92
|
-
*
|
|
93
|
-
*
|
|
101
|
+
* `role` is the field this surface adds, and its value depends on WHICH
|
|
102
|
+
* credential answered — three values, one per credential kind, and all three
|
|
103
|
+
* are named rather than left to be discovered from the data, because an agent
|
|
104
|
+
* that will later register keys or change settings needs to know which of these
|
|
105
|
+
* entries it may expect to administer. Under the `sgu_` PERSON key it is
|
|
106
|
+
* `"owner"` or `"member"`: the list MIXES repositories the person owns with
|
|
107
|
+
* repositories somebody shared with them, and no other field separates the two.
|
|
108
|
+
* Under the `sga_` AGENT key every entry is `"agent"` — the key is NOBODY, so
|
|
109
|
+
* the owner/member question does not apply, and a client branching on
|
|
110
|
+
* owner/member reads false for both, which is the correct reading rather than a
|
|
111
|
+
* gap (`Api::V1::UserRepositoriesController#credential_role` is where the
|
|
112
|
+
* server writes that rule down, beside the `?role=` clamp that exists for the
|
|
113
|
+
* same reason). One value per kind also means the field identifies WHICH
|
|
114
|
+
* credential served the list — worth knowing when both variables are set and
|
|
115
|
+
* the agent key wins.
|
|
94
116
|
*
|
|
95
117
|
* The order is `full_name` ascending, which the controller picks as the only
|
|
96
118
|
* column a client can page or diff against without SpecGuard promising an id
|