@app-vitals/agent-admin 1.4.0 → 1.6.0
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/openapi.json +254 -0
- package/openapi.platform.json +254 -0
- package/package.json +2 -2
- package/src/admin-boot.ts +5 -1
- package/src/agent-marketplaces-api.integration.test.ts +263 -0
- package/src/agent-marketplaces.ts +120 -0
- package/src/agent-marketplaces.unit.test.ts +72 -0
- package/src/agents-api.ts +117 -1
- package/src/api.ts +29 -5
- package/src/openapi-schemas.ts +56 -0
- package/src/openapi-schemas.unit.test.ts +29 -0
package/src/api.ts
CHANGED
|
@@ -44,6 +44,12 @@ export interface AgentConfigResponse {
|
|
|
44
44
|
env: Record<string, string>;
|
|
45
45
|
allowedTools: string[];
|
|
46
46
|
plugins: AgentPlugin[];
|
|
47
|
+
/**
|
|
48
|
+
* PMK-1.2: remote plugin marketplaces to register before plugin install.
|
|
49
|
+
* Optional so existing fixtures/doubles built against this interface keep
|
|
50
|
+
* compiling; the live handler always populates it (empty when none).
|
|
51
|
+
*/
|
|
52
|
+
marketplaces?: Array<{ name: string; source: string; ref: string | null }>;
|
|
47
53
|
repos: string[];
|
|
48
54
|
reviewAuthorAllowlist: string[];
|
|
49
55
|
patchAuthorAllowlist: string[];
|
|
@@ -155,6 +161,12 @@ interface AgentPluginServiceLike {
|
|
|
155
161
|
listEnabled(agentId: string): Promise<Array<{ name: string }>>;
|
|
156
162
|
}
|
|
157
163
|
|
|
164
|
+
interface AgentMarketplaceServiceLike {
|
|
165
|
+
list(
|
|
166
|
+
agentId: string,
|
|
167
|
+
): Promise<Array<{ name: string; source: string; ref: string | null }>>;
|
|
168
|
+
}
|
|
169
|
+
|
|
158
170
|
export interface AgentPhaseMethodologyServiceLike {
|
|
159
171
|
list(
|
|
160
172
|
agentId: string,
|
|
@@ -166,6 +178,11 @@ export interface AgentRuntimeDeps {
|
|
|
166
178
|
agentCronJobService: AgentCronJobServiceLike;
|
|
167
179
|
agentService: AgentServiceLike;
|
|
168
180
|
agentPluginService: AgentPluginServiceLike;
|
|
181
|
+
/**
|
|
182
|
+
* PMK-1.2: optional. Absent → GET /agents/:id/config reports
|
|
183
|
+
* `marketplaces: []`.
|
|
184
|
+
*/
|
|
185
|
+
agentMarketplaceService?: AgentMarketplaceServiceLike;
|
|
169
186
|
/**
|
|
170
187
|
* Optional: supplied by an AdminExtension (the coding extension). Absent →
|
|
171
188
|
* GET /agents/:id/config omits `phaseMethodology`.
|
|
@@ -290,11 +307,13 @@ export function createAgentRuntimeApp(deps: AgentRuntimeDeps): OpenAPIHono {
|
|
|
290
307
|
}
|
|
291
308
|
|
|
292
309
|
// Fetch env bundle, plugins, and phase-methodology overrides in parallel
|
|
293
|
-
const [bundle, plugins, phaseMethodologyRows] =
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
310
|
+
const [bundle, plugins, marketplaceRows, phaseMethodologyRows] =
|
|
311
|
+
await Promise.all([
|
|
312
|
+
agentEnvService.getConfigBundle(id),
|
|
313
|
+
agentPluginService.listEnabled(id),
|
|
314
|
+
deps.agentMarketplaceService?.list(id),
|
|
315
|
+
agentPhaseMethodologyService?.list(id),
|
|
316
|
+
]);
|
|
298
317
|
|
|
299
318
|
// WIF-2.1: with federation enabled, a non-self-hosted agent must never be
|
|
300
319
|
// handed Anthropic credentials, even ones already stored in its bundle
|
|
@@ -341,6 +360,11 @@ export function createAgentRuntimeApp(deps: AgentRuntimeDeps): OpenAPIHono {
|
|
|
341
360
|
? { marketplace: "shipwright", plugin: p.name }
|
|
342
361
|
: { plugin: p.name.slice(0, at), marketplace: p.name.slice(at + 1) };
|
|
343
362
|
}),
|
|
363
|
+
marketplaces: (marketplaceRows ?? []).map((m) => ({
|
|
364
|
+
name: m.name,
|
|
365
|
+
source: m.source,
|
|
366
|
+
ref: m.ref,
|
|
367
|
+
})),
|
|
344
368
|
repos: agent.repos,
|
|
345
369
|
reviewAuthorAllowlist: agent.reviewAuthorAllowlist,
|
|
346
370
|
patchAuthorAllowlist: agent.patchAuthorAllowlist,
|
package/src/openapi-schemas.ts
CHANGED
|
@@ -895,6 +895,43 @@ export const PatchAgentPluginBodySchema = z
|
|
|
895
895
|
})
|
|
896
896
|
.openapi("PatchAgentPluginBody");
|
|
897
897
|
|
|
898
|
+
// ─── AgentMarketplace ─────────────────────────────────────────────────────────
|
|
899
|
+
|
|
900
|
+
export const AgentMarketplaceSchema = z
|
|
901
|
+
.object({
|
|
902
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
903
|
+
agentId: z.string().openapi({ example: "clx1234567890" }),
|
|
904
|
+
name: z.string().openapi({ example: "shipwright" }),
|
|
905
|
+
source: z.string().openapi({ example: "app-vitals/shipwright" }),
|
|
906
|
+
ref: z.string().nullable().openapi({ example: "main" }),
|
|
907
|
+
createdAt: z
|
|
908
|
+
.string()
|
|
909
|
+
.datetime()
|
|
910
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
911
|
+
updatedAt: z
|
|
912
|
+
.string()
|
|
913
|
+
.datetime()
|
|
914
|
+
.openapi({ example: "2026-01-01T00:00:00.000Z" }),
|
|
915
|
+
})
|
|
916
|
+
.openapi("AgentMarketplace");
|
|
917
|
+
|
|
918
|
+
export type AgentMarketplace = z.infer<typeof AgentMarketplaceSchema>;
|
|
919
|
+
|
|
920
|
+
/**
|
|
921
|
+
* `source` / `name` / `ref` are validated by AgentMarketplaceService (422), not
|
|
922
|
+
* here — the Zod layer answers 400.
|
|
923
|
+
*/
|
|
924
|
+
export const UpsertAgentMarketplaceBodySchema = z
|
|
925
|
+
.object({
|
|
926
|
+
name: z.string().openapi({ example: "shipwright" }),
|
|
927
|
+
source: z.string().openapi({
|
|
928
|
+
description: "GitHub `owner/repo` or an https git URL",
|
|
929
|
+
example: "app-vitals/shipwright",
|
|
930
|
+
}),
|
|
931
|
+
ref: z.string().nullable().optional().openapi({ example: "main" }),
|
|
932
|
+
})
|
|
933
|
+
.openapi("UpsertAgentMarketplaceBody");
|
|
934
|
+
|
|
898
935
|
// ─── Pipeline phases ────────────────────────────────────────────────────────────
|
|
899
936
|
//
|
|
900
937
|
// The six pipeline phases a methodology override can target — the keys of the
|
|
@@ -1084,6 +1121,11 @@ export const TokenIdParamSchema = z.object({
|
|
|
1084
1121
|
tokenId: z.string().openapi({ example: "clx0987654321" }),
|
|
1085
1122
|
});
|
|
1086
1123
|
|
|
1124
|
+
export const MarketplaceIdParamSchema = z.object({
|
|
1125
|
+
id: z.string().openapi({ example: "clx1234567890" }),
|
|
1126
|
+
marketplaceId: z.string().openapi({ example: "clx0987654321" }),
|
|
1127
|
+
});
|
|
1128
|
+
|
|
1087
1129
|
export const EnvKeyParamSchema = z.object({
|
|
1088
1130
|
id: z.string().openapi({ example: "clx1234567890" }),
|
|
1089
1131
|
key: z.string().openapi({ example: "MY_VAR" }),
|
|
@@ -1298,6 +1340,14 @@ const AgentConfigPluginSchema = z
|
|
|
1298
1340
|
})
|
|
1299
1341
|
.openapi("AgentConfigPlugin");
|
|
1300
1342
|
|
|
1343
|
+
const AgentConfigMarketplaceSchema = z
|
|
1344
|
+
.object({
|
|
1345
|
+
name: z.string().openapi({ example: "shipwright" }),
|
|
1346
|
+
source: z.string().openapi({ example: "app-vitals/shipwright" }),
|
|
1347
|
+
ref: z.string().nullable().openapi({ example: "main" }),
|
|
1348
|
+
})
|
|
1349
|
+
.openapi("AgentConfigMarketplace");
|
|
1350
|
+
|
|
1301
1351
|
export const AgentConfigResponseSchema = z
|
|
1302
1352
|
.object({
|
|
1303
1353
|
env: z
|
|
@@ -1305,6 +1355,12 @@ export const AgentConfigResponseSchema = z
|
|
|
1305
1355
|
.openapi({ example: { SLACK_BOT_TOKEN: "xoxb-..." } }),
|
|
1306
1356
|
allowedTools: z.array(z.string()).openapi({ example: ["Read", "Write"] }),
|
|
1307
1357
|
plugins: z.array(AgentConfigPluginSchema),
|
|
1358
|
+
/**
|
|
1359
|
+
* PMK-1.2: remote plugin marketplaces to register before plugin install.
|
|
1360
|
+
* Optional so existing fixtures/doubles keep compiling; the handler
|
|
1361
|
+
* always populates it (empty array when the agent has none).
|
|
1362
|
+
*/
|
|
1363
|
+
marketplaces: z.array(AgentConfigMarketplaceSchema).optional(),
|
|
1308
1364
|
repos: z.array(z.string()).openapi({ example: ["org/repo1", "org/repo2"] }),
|
|
1309
1365
|
reviewAuthorAllowlist: z
|
|
1310
1366
|
.array(z.string())
|
|
@@ -10,6 +10,7 @@ import { type AgentRuntimeDeps, createAgentRuntimeApp } from "./api.ts";
|
|
|
10
10
|
import {
|
|
11
11
|
type AgentCronJob,
|
|
12
12
|
AgentCronJobSchema,
|
|
13
|
+
AgentMarketplaceSchema,
|
|
13
14
|
type AgentPlugin,
|
|
14
15
|
AgentPluginSchema,
|
|
15
16
|
type AgentToken,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
AgentToolSchema,
|
|
19
20
|
ErrorSchema,
|
|
20
21
|
PatchAgentBodySchema,
|
|
22
|
+
UpsertAgentMarketplaceBodySchema,
|
|
21
23
|
} from "./openapi-schemas.ts";
|
|
22
24
|
|
|
23
25
|
// ─── Fixtures ─────────────────────────────────────────────────────────────────
|
|
@@ -381,3 +383,30 @@ describe("PatchAgentBodySchema.podServiceAccountName (COS-49.1)", () => {
|
|
|
381
383
|
}
|
|
382
384
|
});
|
|
383
385
|
});
|
|
386
|
+
|
|
387
|
+
// ─── AgentMarketplace (PMK-1.2) ───────────────────────────────────────────────
|
|
388
|
+
|
|
389
|
+
describe("AgentMarketplace schemas", () => {
|
|
390
|
+
test("AgentMarketplaceSchema accepts a row with a null ref", () => {
|
|
391
|
+
const result = AgentMarketplaceSchema.safeParse({
|
|
392
|
+
id: "m1",
|
|
393
|
+
agentId: "a1",
|
|
394
|
+
name: "shipwright",
|
|
395
|
+
source: "app-vitals/shipwright",
|
|
396
|
+
ref: null,
|
|
397
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
398
|
+
updatedAt: "2026-01-01T00:00:00.000Z",
|
|
399
|
+
});
|
|
400
|
+
expect(result.success).toBe(true);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("UpsertAgentMarketplaceBodySchema leaves source validation to the service (422)", () => {
|
|
404
|
+
expect(
|
|
405
|
+
UpsertAgentMarketplaceBodySchema.safeParse({ name: "m", source: "bad" })
|
|
406
|
+
.success,
|
|
407
|
+
).toBe(true);
|
|
408
|
+
expect(
|
|
409
|
+
UpsertAgentMarketplaceBodySchema.safeParse({ name: "m" }).success,
|
|
410
|
+
).toBe(false);
|
|
411
|
+
});
|
|
412
|
+
});
|