@capybara-hq/kernel 0.1.0-rc.17 → 0.1.0-rc.19

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.
@@ -77,6 +77,8 @@ export interface ComponentView {
77
77
  en: string;
78
78
  } | null;
79
79
  description: string | null;
80
+ /** The description's Chinese text (null: show `description` in every locale). */
81
+ descriptionZh: string | null;
80
82
  /** The plugin it came from (null: none). */
81
83
  plugin: string | null;
82
84
  itemId: string | null;
@@ -151,6 +153,8 @@ export interface OrgOfferedComponentView extends OrgComponentView {
151
153
  en: string;
152
154
  } | null;
153
155
  description: string | null;
156
+ /** The description's Chinese text (null: show `description` in every locale). */
157
+ descriptionZh: string | null;
154
158
  itemId: string | null;
155
159
  version: string;
156
160
  scopeMode: "follow" | "own";
@@ -211,6 +211,7 @@ let ComponentScopeService = class ComponentScopeService {
211
211
  name: c.name,
212
212
  displayName: c.displayName,
213
213
  description: c.description,
214
+ descriptionZh: c.descriptionZh,
214
215
  plugin: c.plugin,
215
216
  itemId: c.itemId,
216
217
  owner: ownerOf(c.ownerOrgId, owners),
@@ -269,6 +270,7 @@ let ComponentScopeService = class ComponentScopeService {
269
270
  name: c.name,
270
271
  displayName: c.displayName,
271
272
  description: c.description,
273
+ descriptionZh: c.descriptionZh,
272
274
  plugin: c.plugin,
273
275
  itemId: c.itemId,
274
276
  version: c.version,
@@ -373,6 +375,7 @@ let ComponentScopeService = class ComponentScopeService {
373
375
  name: r.name,
374
376
  displayName: null,
375
377
  description: r.description,
378
+ descriptionZh: null,
376
379
  plugin: r.plugin,
377
380
  itemId: r.catalog_item_id,
378
381
  owner: ownerOf(r.org_id, owners),
@@ -1097,6 +1100,7 @@ const virtualOf = (o) => ({
1097
1100
  plugin: o.plugin,
1098
1101
  version: String(o.version),
1099
1102
  description: null,
1103
+ descriptionZh: null,
1100
1104
  displayName: null,
1101
1105
  itemId: null,
1102
1106
  scopeMode: "own",
@@ -68,6 +68,8 @@ export interface ComponentRow extends ComponentScopeFacts {
68
68
  /** The current version (a connector template: the catalog version that last changed it). */
69
69
  version: string;
70
70
  description: string | null;
71
+ /** The description's Chinese text (a theme's or skill's `description.zh`); null without one. */
72
+ descriptionZh: string | null;
71
73
  /**
72
74
  * A connector template's localized display name (its file's `displayName`); null for skills and
73
75
  * agent templates, whose name is what they are shown by.
@@ -119,6 +119,7 @@ export async function loadComponents(db, kind, filter = {}) {
119
119
  plugin: r.item_name,
120
120
  version: r.current_version,
121
121
  description: typeof file?.description === "string" ? file.description : null,
122
+ descriptionZh: null,
122
123
  displayName: localizedName(file?.displayName),
123
124
  originOrgId: null,
124
125
  };
@@ -137,6 +138,7 @@ export async function loadComponents(db, kind, filter = {}) {
137
138
  "p.plugin_name",
138
139
  "p.name",
139
140
  "p.description",
141
+ "p.description_zh",
140
142
  "p.current_version",
141
143
  "p.scope_mode",
142
144
  "p.scope",
@@ -161,6 +163,7 @@ export async function loadComponents(db, kind, filter = {}) {
161
163
  plugin: r.plugin_name,
162
164
  version: String(r.current_version),
163
165
  description: r.description,
166
+ descriptionZh: r.description_zh,
164
167
  displayName: null,
165
168
  originOrgId: r.origin_org_id,
166
169
  }));
@@ -188,7 +188,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
188
188
  /** The host versions (an entry's `minKernel` is judged against them). */
189
189
  export declare const HOST_VERSIONS: {
190
190
  readonly kernel: string;
191
- readonly sdk: "0.1.0-rc.13";
191
+ readonly sdk: "0.1.0-rc.14";
192
192
  };
193
193
  /** Whether this deployment satisfies the entry's `minKernel`. */
194
194
  export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
@@ -90,12 +90,13 @@ export declare class ClientAccess {
90
90
  * build ({@link listed}: one per extension), in name order, minus the stopped ones; `components`
91
91
  * are those whose slot the role may use (`canUseSlot`: its `minRole`, never `platform.page`). An
92
92
  * extension with no component left is still listed (its stylesheets and entry still load).
93
+ * `signInId` gives each item the viewer's sign-in id for that plugin (`ClientSignIn`).
93
94
  */
94
- orgSet(orgId: string, role: OrgRole): Promise<ClientExtensionsResponse>;
95
+ orgSet(orgId: string, role: OrgRole, signInId: (plugin: string) => string): Promise<ClientExtensionsResponse>;
95
96
  /**
96
97
  * The admin console's set (spec §9.6): the extensions with a served build (whatever their org
97
98
  * enablement) with at least one `platform.page` component and an entry, only those components
98
99
  * and no stylesheets (the admin console runs no org client code); with `plugin`, at most that one.
99
100
  */
100
- platformSet(plugin?: string): Promise<ClientExtensionsResponse>;
101
+ platformSet(signInId: (plugin: string) => string, plugin?: string): Promise<ClientExtensionsResponse>;
101
102
  }
@@ -234,8 +234,9 @@ let ClientAccess = class ClientAccess {
234
234
  * build ({@link listed}: one per extension), in name order, minus the stopped ones; `components`
235
235
  * are those whose slot the role may use (`canUseSlot`: its `minRole`, never `platform.page`). An
236
236
  * extension with no component left is still listed (its stylesheets and entry still load).
237
+ * `signInId` gives each item the viewer's sign-in id for that plugin (`ClientSignIn`).
237
238
  */
238
- async orgSet(orgId, role) {
239
+ async orgSet(orgId, role, signInId) {
239
240
  const db = this.db.db;
240
241
  const [kill, rows] = await Promise.all([
241
242
  readClientExtensions(db, this.log),
@@ -253,6 +254,7 @@ let ClientAccess = class ClientAccess {
253
254
  components: componentsOf(r.section)
254
255
  .filter((c) => canUseSlot(c.slot, role))
255
256
  .map((c) => ({ id: c.id, slot: c.slot.id })),
257
+ signInId: signInId(r.plugin),
256
258
  }));
257
259
  return { enabled: true, apiVersion: CLIENT_API_VERSION, items };
258
260
  }
@@ -261,7 +263,7 @@ let ClientAccess = class ClientAccess {
261
263
  * enablement) with at least one `platform.page` component and an entry, only those components
262
264
  * and no stylesheets (the admin console runs no org client code); with `plugin`, at most that one.
263
265
  */
264
- async platformSet(plugin) {
266
+ async platformSet(signInId, plugin) {
265
267
  const db = this.db.db;
266
268
  let q = this.listed(db);
267
269
  if (plugin !== undefined)
@@ -278,7 +280,14 @@ let ClientAccess = class ClientAccess {
278
280
  if (components.length === 0)
279
281
  return [];
280
282
  return [
281
- { plugin: r.plugin, build: r.build, entry: fileRef(r, r.entry), styles: [], components },
283
+ {
284
+ plugin: r.plugin,
285
+ build: r.build,
286
+ entry: fileRef(r, r.entry),
287
+ styles: [],
288
+ components,
289
+ signInId: signInId(r.plugin),
290
+ },
282
291
  ];
283
292
  });
284
293
  return { enabled: true, apiVersion: CLIENT_API_VERSION, items };
@@ -0,0 +1,21 @@
1
+ import type { Keyring } from "../ctx/secrets.js";
2
+ import type { UserPrincipal } from "../identity/principal.js";
3
+ /**
4
+ * The sign-in ids of the shell reads (client extensions spec §4.2, `ClientApi.signIn`): an opaque
5
+ * id per plugin that stays the same for one sign-in (one web session, across token refreshes and
6
+ * every tab of it) and changes on the next sign-in, so client code can do something once per
7
+ * sign-in. It is `HMAC-SHA256(key, "<sign-in>\0<plugin>")` truncated to 128 bits, the key derived
8
+ * from the master keyring: not a credential and not reversible to the session id. The plugin name
9
+ * is an input only so that the id is not the same string in every plugin; that is no isolation
10
+ * boundary: client code runs with full trust in the page (spec D2) and can read the whole shell
11
+ * read (or the query cache), which lists every plugin's id. Derived per request from the
12
+ * principal, no state. A personal token (no session) is its own sign-in. Rotating the master key
13
+ * changes every id once (client code then treats the session as a new sign-in); during a rolling
14
+ * restart old and new replicas answer different ids, so it may look like more than one.
15
+ */
16
+ export declare class ClientSignIn {
17
+ private readonly key;
18
+ constructor(keyring: Keyring);
19
+ /** The id function of the caller's sign-in: plugin name → that plugin's sign-in id. */
20
+ of(principal: UserPrincipal): (plugin: string) => string;
21
+ }
@@ -0,0 +1,49 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
+ return function (target, key) { decorator(target, key, paramIndex); }
9
+ };
10
+ import { createHmac } from "node:crypto";
11
+ import { Inject, Injectable } from "@nestjs/common";
12
+ import { KEYRING } from "../infra/tokens.js";
13
+ /** The HKDF purpose of the sign-in id key (`capybara:client-sign-in/v1`). */
14
+ const SIGN_IN_KEY_INFO = "client-sign-in/v1";
15
+ /** A sign-in id is 128 bits of the HMAC, base64url (22 characters). */
16
+ const SIGN_IN_ID_BYTES = 16;
17
+ /**
18
+ * The sign-in ids of the shell reads (client extensions spec §4.2, `ClientApi.signIn`): an opaque
19
+ * id per plugin that stays the same for one sign-in (one web session, across token refreshes and
20
+ * every tab of it) and changes on the next sign-in, so client code can do something once per
21
+ * sign-in. It is `HMAC-SHA256(key, "<sign-in>\0<plugin>")` truncated to 128 bits, the key derived
22
+ * from the master keyring: not a credential and not reversible to the session id. The plugin name
23
+ * is an input only so that the id is not the same string in every plugin; that is no isolation
24
+ * boundary: client code runs with full trust in the page (spec D2) and can read the whole shell
25
+ * read (or the query cache), which lists every plugin's id. Derived per request from the
26
+ * principal, no state. A personal token (no session) is its own sign-in. Rotating the master key
27
+ * changes every id once (client code then treats the session as a new sign-in); during a rolling
28
+ * restart old and new replicas answer different ids, so it may look like more than one.
29
+ */
30
+ let ClientSignIn = class ClientSignIn {
31
+ key;
32
+ constructor(keyring) {
33
+ this.key = keyring.deriveKey(SIGN_IN_KEY_INFO).current;
34
+ }
35
+ /** The id function of the caller's sign-in: plugin name → that plugin's sign-in id. */
36
+ of(principal) {
37
+ const signIn = principal.via === "session" ? `session:${principal.sessionId}` : `pat:${principal.tokenId}`;
38
+ return (plugin) => createHmac("sha256", this.key)
39
+ .update(`${signIn}\0${plugin}`)
40
+ .digest()
41
+ .subarray(0, SIGN_IN_ID_BYTES)
42
+ .toString("base64url");
43
+ }
44
+ };
45
+ ClientSignIn = __decorate([
46
+ Injectable(),
47
+ __param(0, Inject(KEYRING))
48
+ ], ClientSignIn);
49
+ export { ClientSignIn };
@@ -4,18 +4,21 @@ import { type OrgContext } from "../orgs/org.guard.js";
4
4
  import { AdminClientQuery, type ClientExtensionsResponse, ReportBody } from "./client.schemas.js";
5
5
  import { ClientAccess } from "./client-access.service.js";
6
6
  import { ClientReports } from "./client-reports.service.js";
7
+ import { ClientSignIn } from "./client-sign-in.service.js";
7
8
  /**
8
9
  * The workspace shell read of client extensions (client extensions spec §9.2), `ui.use` (guest and
9
10
  * up; a person, not a service token): `GET /api/v1/orgs/:orgId/ui/client` →
10
11
  * {@link ClientExtensionsResponse}: the extensions enabled in the org that run with an approved
11
- * client build, their file URLs and integrity, and the components the caller's role may use;
12
+ * client build, their file URLs and integrity, the components the caller's role may use and the
13
+ * caller's sign-in id per plugin (`ClientSignIn`);
12
14
  * `enabled: false` with no items under the kill switch. `no-store`: the set changes with an org
13
15
  * switch, an upgrade or the kill switch.
14
16
  */
15
17
  export declare class OrgClientController {
16
18
  private readonly access;
17
19
  private readonly reports;
18
- constructor(access: ClientAccess, reports: ClientReports);
20
+ private readonly signIn;
21
+ constructor(access: ClientAccess, reports: ClientReports, signIn: ClientSignIn);
19
22
  list(principal: Principal, org: OrgContext): Promise<ClientExtensionsResponse>;
20
23
  /**
21
24
  * `POST /api/v1/orgs/:orgId/ui/client/reports` (spec §10.4), `ui.use`, a person, 20 per minute
@@ -34,7 +37,8 @@ export declare class OrgClientController {
34
37
  export declare class AdminClientController {
35
38
  private readonly access;
36
39
  private readonly reports;
37
- constructor(access: ClientAccess, reports: ClientReports);
40
+ private readonly signIn;
41
+ constructor(access: ClientAccess, reports: ClientReports, signIn: ClientSignIn);
38
42
  list(principal: Principal, q: z.infer<typeof AdminClientQuery>): Promise<ClientExtensionsResponse>;
39
43
  /**
40
44
  * `POST /api/v1/admin/ui/client/reports` (spec §10.4), super admins, 20 per minute per account:
@@ -16,6 +16,7 @@ import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
16
16
  import { AdminClientQuery, ReportBody } from "./client.schemas.js";
17
17
  import { ClientAccess } from "./client-access.service.js";
18
18
  import { ClientReports } from "./client-reports.service.js";
19
+ import { ClientSignIn } from "./client-sign-in.service.js";
19
20
  class AdminClientQueryDto extends createZodDto(AdminClientQuery, "AdminClientQuery") {
20
21
  }
21
22
  class ReportBodyDto extends createZodDto(ReportBody, "ClientReportBody") {
@@ -24,20 +25,23 @@ class ReportBodyDto extends createZodDto(ReportBody, "ClientReportBody") {
24
25
  * The workspace shell read of client extensions (client extensions spec §9.2), `ui.use` (guest and
25
26
  * up; a person, not a service token): `GET /api/v1/orgs/:orgId/ui/client` →
26
27
  * {@link ClientExtensionsResponse}: the extensions enabled in the org that run with an approved
27
- * client build, their file URLs and integrity, and the components the caller's role may use;
28
+ * client build, their file URLs and integrity, the components the caller's role may use and the
29
+ * caller's sign-in id per plugin (`ClientSignIn`);
28
30
  * `enabled: false` with no items under the kill switch. `no-store`: the set changes with an org
29
31
  * switch, an upgrade or the kill switch.
30
32
  */
31
33
  let OrgClientController = class OrgClientController {
32
34
  access;
33
35
  reports;
34
- constructor(access, reports) {
36
+ signIn;
37
+ constructor(access, reports, signIn) {
35
38
  this.access = access;
36
39
  this.reports = reports;
40
+ this.signIn = signIn;
37
41
  }
38
42
  async list(principal, org) {
39
- requireUser(principal);
40
- return this.access.orgSet(org.orgId, org.role);
43
+ const user = requireUser(principal);
44
+ return this.access.orgSet(org.orgId, org.role, this.signIn.of(user));
41
45
  }
42
46
  /**
43
47
  * `POST /api/v1/orgs/:orgId/ui/client/reports` (spec §10.4), `ui.use`, a person, 20 per minute
@@ -69,7 +73,8 @@ __decorate([
69
73
  OrgClientController = __decorate([
70
74
  Controller("api/v1/orgs/:orgId/ui/client"),
71
75
  __param(0, Inject(ClientAccess)),
72
- __param(1, Inject(ClientReports))
76
+ __param(1, Inject(ClientReports)),
77
+ __param(2, Inject(ClientSignIn))
73
78
  ], OrgClientController);
74
79
  export { OrgClientController };
75
80
  /**
@@ -81,13 +86,15 @@ export { OrgClientController };
81
86
  let AdminClientController = class AdminClientController {
82
87
  access;
83
88
  reports;
84
- constructor(access, reports) {
89
+ signIn;
90
+ constructor(access, reports, signIn) {
85
91
  this.access = access;
86
92
  this.reports = reports;
93
+ this.signIn = signIn;
87
94
  }
88
95
  async list(principal, q) {
89
- requireUser(principal);
90
- return this.access.platformSet(q.plugin);
96
+ const user = requireUser(principal);
97
+ return this.access.platformSet(this.signIn.of(user), q.plugin);
91
98
  }
92
99
  /**
93
100
  * `POST /api/v1/admin/ui/client/reports` (spec §10.4), super admins, 20 per minute per account:
@@ -116,6 +123,7 @@ AdminClientController = __decorate([
116
123
  Controller("api/v1/admin/ui/client"),
117
124
  SuperAdmin(),
118
125
  __param(0, Inject(ClientAccess)),
119
- __param(1, Inject(ClientReports))
126
+ __param(1, Inject(ClientReports)),
127
+ __param(2, Inject(ClientSignIn))
120
128
  ], AdminClientController);
121
129
  export { AdminClientController };
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Client extensions (spec 2026-11-09-client-extensions-design.md): the client builds stored with
3
3
  * extension packages ({@link ClientBuilds}) and who may load them ({@link ClientAccess}: the
4
- * delivery route and the shell reads, whose controllers the api role lists).
4
+ * delivery route and the shell reads, whose controllers the api role lists; {@link ClientSignIn}:
5
+ * the per-plugin sign-in ids of those reads).
5
6
  */
6
7
  export declare class ClientModule {
7
8
  }
@@ -9,18 +9,20 @@ import { StorageModule } from "../storage/storage.module.js";
9
9
  import { ClientAccess } from "./client-access.service.js";
10
10
  import { ClientBuilds } from "./client-builds.service.js";
11
11
  import { ClientReports } from "./client-reports.service.js";
12
+ import { ClientSignIn } from "./client-sign-in.service.js";
12
13
  /**
13
14
  * Client extensions (spec 2026-11-09-client-extensions-design.md): the client builds stored with
14
15
  * extension packages ({@link ClientBuilds}) and who may load them ({@link ClientAccess}: the
15
- * delivery route and the shell reads, whose controllers the api role lists).
16
+ * delivery route and the shell reads, whose controllers the api role lists; {@link ClientSignIn}:
17
+ * the per-plugin sign-in ids of those reads).
16
18
  */
17
19
  let ClientModule = class ClientModule {
18
20
  };
19
21
  ClientModule = __decorate([
20
22
  Module({
21
23
  imports: [StorageModule],
22
- providers: [ClientBuilds, ClientAccess, ClientReports],
23
- exports: [ClientBuilds, ClientAccess, ClientReports],
24
+ providers: [ClientBuilds, ClientAccess, ClientReports, ClientSignIn],
25
+ exports: [ClientBuilds, ClientAccess, ClientReports, ClientSignIn],
24
26
  })
25
27
  ], ClientModule);
26
28
  export { ClientModule };
@@ -22,6 +22,12 @@ export interface ClientExtensionItem {
22
22
  id: string;
23
23
  slot: UiSlotId;
24
24
  }[];
25
+ /**
26
+ * The viewer's sign-in id for this plugin (`ClientApi.signIn.id`, spec §4.2): opaque, the same
27
+ * for every read of one sign-in and different after the next one; a different string per plugin,
28
+ * not a secret between plugins (client code is full trust and reads the whole set).
29
+ */
30
+ signInId: string;
25
31
  }
26
32
  /** The shell read (spec §9.2). */
27
33
  export interface ClientExtensionsResponse {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capybara-hq/kernel",
3
- "version": "0.1.0-rc.17",
3
+ "version": "0.1.0-rc.19",
4
4
  "description": "Capybara kernel: config, database, plugin host and HTTP API of the Capybara agent platform",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -65,7 +65,7 @@
65
65
  "yaml": "^2.9.1",
66
66
  "yauzl": "3.4.0",
67
67
  "zod": "^4.6.7",
68
- "@capybara-hq/plugin-sdk": "^0.1.0-rc.13"
68
+ "@capybara-hq/plugin-sdk": "^0.1.0-rc.14"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@langchain/langgraph-checkpoint-validation": "^1.2.0",