@cogenta/plugins 0.1.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.
Files changed (86) hide show
  1. package/dist/guest/sandbox-entry.mjs +155 -0
  2. package/dist/host/capabilities.d.ts +46 -0
  3. package/dist/host/capabilities.d.ts.map +1 -0
  4. package/dist/host/capabilities.js +105 -0
  5. package/dist/host/capabilities.js.map +1 -0
  6. package/dist/host/protocol.d.ts +58 -0
  7. package/dist/host/protocol.d.ts.map +1 -0
  8. package/dist/host/protocol.js +10 -0
  9. package/dist/host/protocol.js.map +1 -0
  10. package/dist/host/worker-runner.d.ts +92 -0
  11. package/dist/host/worker-runner.d.ts.map +1 -0
  12. package/dist/host/worker-runner.js +196 -0
  13. package/dist/host/worker-runner.js.map +1 -0
  14. package/dist/index.d.ts +34 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +20 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/loader.d.ts +81 -0
  19. package/dist/loader.d.ts.map +1 -0
  20. package/dist/loader.js +190 -0
  21. package/dist/loader.js.map +1 -0
  22. package/dist/manifest.d.ts +93 -0
  23. package/dist/manifest.d.ts.map +1 -0
  24. package/dist/manifest.js +197 -0
  25. package/dist/manifest.js.map +1 -0
  26. package/dist/permissions/describe.d.ts +22 -0
  27. package/dist/permissions/describe.d.ts.map +1 -0
  28. package/dist/permissions/describe.js +158 -0
  29. package/dist/permissions/describe.js.map +1 -0
  30. package/dist/permissions/disabled.d.ts +25 -0
  31. package/dist/permissions/disabled.d.ts.map +1 -0
  32. package/dist/permissions/disabled.js +29 -0
  33. package/dist/permissions/disabled.js.map +1 -0
  34. package/dist/permissions/grants.d.ts +22 -0
  35. package/dist/permissions/grants.d.ts.map +1 -0
  36. package/dist/permissions/grants.js +31 -0
  37. package/dist/permissions/grants.js.map +1 -0
  38. package/dist/permissions/resolve.d.ts +47 -0
  39. package/dist/permissions/resolve.d.ts.map +1 -0
  40. package/dist/permissions/resolve.js +51 -0
  41. package/dist/permissions/resolve.js.map +1 -0
  42. package/dist/permissions/review.d.ts +50 -0
  43. package/dist/permissions/review.d.ts.map +1 -0
  44. package/dist/permissions/review.js +46 -0
  45. package/dist/permissions/review.js.map +1 -0
  46. package/dist/permissions/tables.d.ts +18 -0
  47. package/dist/permissions/tables.d.ts.map +1 -0
  48. package/dist/permissions/tables.js +48 -0
  49. package/dist/permissions/tables.js.map +1 -0
  50. package/dist/registries/plugins.d.ts +74 -0
  51. package/dist/registries/plugins.d.ts.map +1 -0
  52. package/dist/registries/plugins.js +127 -0
  53. package/dist/registries/plugins.js.map +1 -0
  54. package/dist/registries/skills.d.ts +54 -0
  55. package/dist/registries/skills.d.ts.map +1 -0
  56. package/dist/registries/skills.js +113 -0
  57. package/dist/registries/skills.js.map +1 -0
  58. package/dist/registries/skins.d.ts +33 -0
  59. package/dist/registries/skins.d.ts.map +1 -0
  60. package/dist/registries/skins.js +65 -0
  61. package/dist/registries/skins.js.map +1 -0
  62. package/dist/registries/tables.d.ts +44 -0
  63. package/dist/registries/tables.d.ts.map +1 -0
  64. package/dist/registries/tables.js +118 -0
  65. package/dist/registries/tables.js.map +1 -0
  66. package/dist/registries/themes.d.ts +59 -0
  67. package/dist/registries/themes.d.ts.map +1 -0
  68. package/dist/registries/themes.js +82 -0
  69. package/dist/registries/themes.js.map +1 -0
  70. package/dist/semver.d.ts +24 -0
  71. package/dist/semver.d.ts.map +1 -0
  72. package/dist/semver.js +93 -0
  73. package/dist/semver.js.map +1 -0
  74. package/dist/signing/keys.d.ts +18 -0
  75. package/dist/signing/keys.d.ts.map +1 -0
  76. package/dist/signing/keys.js +15 -0
  77. package/dist/signing/keys.js.map +1 -0
  78. package/dist/signing/sign.d.ts +35 -0
  79. package/dist/signing/sign.d.ts.map +1 -0
  80. package/dist/signing/sign.js +63 -0
  81. package/dist/signing/sign.js.map +1 -0
  82. package/dist/signing/verify.d.ts +40 -0
  83. package/dist/signing/verify.d.ts.map +1 -0
  84. package/dist/signing/verify.js +73 -0
  85. package/dist/signing/verify.js.map +1 -0
  86. package/package.json +46 -0
@@ -0,0 +1,113 @@
1
+ import { parseSkillFile } from '@cogenta/agents';
2
+ import { CogentaError, identifier, newId, sql } from '@cogenta/core';
3
+ import { REGISTRY_TABLES } from './tables.js';
4
+ function toEntry(row) {
5
+ const status = row.status === 'accepted' ? 'accepted' : row.status === 'pending' ? 'pending' : 'rejected';
6
+ return {
7
+ id: row.id,
8
+ submitterId: row.submitter_id,
9
+ displayName: row.display_name,
10
+ description: row.description,
11
+ status,
12
+ skillName: row.skill_name,
13
+ skillVersion: row.skill_version,
14
+ rejectionCode: row.rejection_code,
15
+ rejectionReason: row.rejection_reason,
16
+ reviewedBy: row.reviewed_by,
17
+ reviewedAt: row.reviewed_at,
18
+ submittedAt: row.submitted_at,
19
+ };
20
+ }
21
+ export function createSkillRegistry(db, now = Date.now) {
22
+ const skills = identifier(REGISTRY_TABLES.skills, db.dialect);
23
+ return {
24
+ async submit(input) {
25
+ const id = newId(now);
26
+ const submittedAt = new Date(now()).toISOString();
27
+ let status = 'pending';
28
+ let skillName = null;
29
+ let skillVersion = null;
30
+ let rejectionCode = null;
31
+ let rejectionReason = null;
32
+ try {
33
+ const { metadata } = parseSkillFile(input.displayName, input.rawContent);
34
+ skillName = metadata.name;
35
+ skillVersion = metadata.version;
36
+ }
37
+ catch (error) {
38
+ status = 'rejected';
39
+ rejectionCode = error instanceof CogentaError ? error.code : 'SKILL_DEFINITION_INVALID';
40
+ rejectionReason = error instanceof Error ? error.message : String(error);
41
+ }
42
+ const description = input.description ?? null;
43
+ await db.query(sql `
44
+ insert into ${skills}
45
+ (id, submitter_id, display_name, description, raw_content, skill_name, skill_version,
46
+ status, rejection_code, rejection_reason, reviewed_by, reviewed_at, submitted_at)
47
+ values
48
+ (${id}, ${input.submitterId}, ${input.displayName}, ${description}, ${input.rawContent},
49
+ ${skillName}, ${skillVersion}, ${status}, ${rejectionCode}, ${rejectionReason},
50
+ ${null}, ${null}, ${submittedAt})`);
51
+ return {
52
+ id,
53
+ submitterId: input.submitterId,
54
+ displayName: input.displayName,
55
+ description,
56
+ status,
57
+ skillName,
58
+ skillVersion,
59
+ rejectionCode,
60
+ rejectionReason,
61
+ reviewedBy: null,
62
+ reviewedAt: null,
63
+ submittedAt,
64
+ };
65
+ },
66
+ async review(id, decision, reviewerUserId, notes) {
67
+ const result = await db.query(sql `
68
+ select id, submitter_id, display_name, description, skill_name, skill_version, status,
69
+ rejection_code, rejection_reason, reviewed_by, reviewed_at, submitted_at
70
+ from ${skills} where id = ${id}`);
71
+ const row = result.rows[0];
72
+ if (row === undefined)
73
+ return { ok: false, reason: 'not_found' };
74
+ const current = toEntry(row);
75
+ if (current.status !== 'pending')
76
+ return { ok: false, reason: 'already_decided', entry: current };
77
+ const reviewedAt = new Date(now()).toISOString();
78
+ const nextStatus = decision === 'accept' ? 'accepted' : 'rejected';
79
+ const rejectionReason = decision === 'reject' ? (notes ?? null) : null;
80
+ await db.query(sql `
81
+ update ${skills}
82
+ set status = ${nextStatus}, rejection_reason = ${rejectionReason},
83
+ reviewed_by = ${reviewerUserId}, reviewed_at = ${reviewedAt}
84
+ where id = ${id}`);
85
+ return {
86
+ ok: true,
87
+ entry: {
88
+ ...current,
89
+ status: nextStatus,
90
+ rejectionReason,
91
+ reviewedBy: reviewerUserId,
92
+ reviewedAt,
93
+ },
94
+ };
95
+ },
96
+ async listAccepted() {
97
+ const result = await db.query(sql `
98
+ select id, submitter_id, display_name, description, skill_name, skill_version, status,
99
+ rejection_code, rejection_reason, reviewed_by, reviewed_at, submitted_at
100
+ from ${skills} where status = 'accepted' order by submitted_at asc`);
101
+ return result.rows.map(toEntry);
102
+ },
103
+ async get(id) {
104
+ const result = await db.query(sql `
105
+ select id, submitter_id, display_name, description, skill_name, skill_version, status,
106
+ rejection_code, rejection_reason, reviewed_by, reviewed_at, submitted_at
107
+ from ${skills} where id = ${id}`);
108
+ const row = result.rows[0];
109
+ return row === undefined ? null : toEntry(row);
110
+ },
111
+ };
112
+ }
113
+ //# sourceMappingURL=skills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/registries/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAuB,UAAU,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AACzF,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAwE7C,SAAS,OAAO,CAAC,GAAa;IAC5B,MAAM,MAAM,GACV,GAAG,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAA;IAC5F,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,WAAW,EAAE,GAAG,CAAC,YAAY;QAC7B,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,MAAM;QACN,SAAS,EAAE,GAAG,CAAC,UAAU;QACzB,YAAY,EAAE,GAAG,CAAC,aAAa;QAC/B,aAAa,EAAE,GAAG,CAAC,cAAc;QACjC,eAAe,EAAE,GAAG,CAAC,gBAAgB;QACrC,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,UAAU,EAAE,GAAG,CAAC,WAAW;QAC3B,WAAW,EAAE,GAAG,CAAC,YAAY;KAC9B,CAAA;AACH,CAAC;AAED,MAAM,UAAU,mBAAmB,CACjC,EAAkB,EAClB,GAAG,GAAiB,IAAI,CAAC,GAAG;IAE5B,MAAM,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,CAAA;IAE7D,OAAO;QACL,KAAK,CAAC,MAAM,CAAC,KAAK;YAChB,MAAM,EAAE,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;YACrB,MAAM,WAAW,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;YAEjD,IAAI,MAAM,GAA0B,SAAS,CAAA;YAC7C,IAAI,SAAS,GAAkB,IAAI,CAAA;YACnC,IAAI,YAAY,GAAkB,IAAI,CAAA;YACtC,IAAI,aAAa,GAAkB,IAAI,CAAA;YACvC,IAAI,eAAe,GAAkB,IAAI,CAAA;YACzC,IAAI,CAAC;gBACH,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,UAAU,CAAC,CAAA;gBACxE,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAA;gBACzB,YAAY,GAAG,QAAQ,CAAC,OAAO,CAAA;YACjC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,UAAU,CAAA;gBACnB,aAAa,GAAG,KAAK,YAAY,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,0BAA0B,CAAA;gBACvF,eAAe,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAC1E,CAAC;YAED,MAAM,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAA;YAC7C,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;sBACF,MAAM;;;;aAIf,EAAE,KAAK,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC,WAAW,KAAK,WAAW,KAAK,KAAK,CAAC,UAAU;aACnF,SAAS,KAAK,YAAY,KAAK,MAAM,KAAK,aAAa,KAAK,eAAe;aAC3E,IAAI,KAAK,IAAI,KAAK,WAAW,GAAG,CAAC,CAAA;YAExC,OAAO;gBACL,EAAE;gBACF,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,WAAW;gBACX,MAAM;gBACN,SAAS;gBACT,YAAY;gBACZ,aAAa;gBACb,eAAe;gBACf,UAAU,EAAE,IAAI;gBAChB,UAAU,EAAE,IAAI;gBAChB,WAAW;aACZ,CAAA;QACH,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,KAAK;YAC9C,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAW,GAAG,CAAA;;;eAGlC,MAAM,eAAe,EAAE,EAAE,CAAC,CAAA;YACnC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC1B,IAAI,GAAG,KAAK,SAAS;gBAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAA;YAEhE,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAA;YAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS;gBAC9B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,CAAA;YAEjE,MAAM,UAAU,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;YAChD,MAAM,UAAU,GAA0B,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAA;YACzF,MAAM,eAAe,GAAG,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;YAEtE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,CAAA;iBACP,MAAM;uBACA,UAAU,wBAAwB,eAAe;4BAC5C,cAAc,mBAAmB,UAAU;qBAClD,EAAE,EAAE,CAAC,CAAA;YAEpB,OAAO;gBACL,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACL,GAAG,OAAO;oBACV,MAAM,EAAE,UAAU;oBAClB,eAAe;oBACf,UAAU,EAAE,cAAc;oBAC1B,UAAU;iBACX;aACF,CAAA;QACH,CAAC;QAED,KAAK,CAAC,YAAY;YAChB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAW,GAAG,CAAA;;;eAGlC,MAAM,sDAAsD,CAAC,CAAA;YACtE,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACjC,CAAC;QAED,KAAK,CAAC,GAAG,CAAC,EAAE;YACV,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,KAAK,CAAW,GAAG,CAAA;;;eAGlC,MAAM,eAAe,EAAE,EAAE,CAAC,CAAA;YACnC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC1B,OAAO,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAChD,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,33 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ export type SkinSubmissionStatus = 'accepted' | 'rejected';
3
+ export interface SkinSubmissionInput {
4
+ readonly submitterId: string;
5
+ readonly displayName: string;
6
+ readonly description?: string;
7
+ /** A skin's raw candidate token JSON, unvalidated until `submit()` runs it through `validateSkin`. */
8
+ readonly tokens: unknown;
9
+ }
10
+ export interface SkinGalleryEntry {
11
+ readonly id: string;
12
+ readonly submitterId: string;
13
+ readonly displayName: string;
14
+ readonly description: string | null;
15
+ readonly status: SkinSubmissionStatus;
16
+ /** `validateSkin`'s real failure code (e.g. `SKIN_CONTRAST_INSUFFICIENT`) — `null` when accepted. */
17
+ readonly rejectionCode: string | null;
18
+ readonly rejectionReason: string | null;
19
+ readonly submittedAt: string;
20
+ }
21
+ export interface SkinGallery {
22
+ /**
23
+ * Runs the candidate through the real `validateSkin` (`@cogenta/render`)
24
+ * gate and stores the outcome either way — "sans revue humaine" (the lot's
25
+ * own words): there is no pending/reviewed state a human could act on,
26
+ * only `accepted`/`rejected`, decided once, at submission time.
27
+ */
28
+ submit(input: SkinSubmissionInput): Promise<SkinGalleryEntry>;
29
+ listAccepted(): Promise<readonly SkinGalleryEntry[]>;
30
+ get(id: string): Promise<SkinGalleryEntry | null>;
31
+ }
32
+ export declare function createSkinGallery(db: DatabaseHandle, now?: () => number): SkinGallery;
33
+ //# sourceMappingURL=skins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skins.d.ts","sourceRoot":"","sources":["../../src/registries/skins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAIzF,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,UAAU,CAAA;AAE1D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;IACrC,qGAAqG;IACrG,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAC7D,YAAY,IAAI,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAAA;IACpD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAA;CAClD;AA0BD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,GAAE,MAAM,MAAiB,GAAG,WAAW,CAqD/F"}
@@ -0,0 +1,65 @@
1
+ import { CogentaError, identifier, newId, sql } from '@cogenta/core';
2
+ import { validateSkin } from '@cogenta/render';
3
+ import { REGISTRY_TABLES } from './tables.js';
4
+ function toEntry(row) {
5
+ return {
6
+ id: row.id,
7
+ submitterId: row.submitter_id,
8
+ displayName: row.display_name,
9
+ description: row.description,
10
+ status: row.status === 'accepted' ? 'accepted' : 'rejected',
11
+ rejectionCode: row.rejection_code,
12
+ rejectionReason: row.rejection_reason,
13
+ submittedAt: row.submitted_at,
14
+ };
15
+ }
16
+ export function createSkinGallery(db, now = Date.now) {
17
+ const skins = identifier(REGISTRY_TABLES.skins, db.dialect);
18
+ return {
19
+ async submit(input) {
20
+ const id = newId(now);
21
+ const submittedAt = new Date(now()).toISOString();
22
+ let status = 'accepted';
23
+ let rejectionCode = null;
24
+ let rejectionReason = null;
25
+ try {
26
+ validateSkin(input.tokens);
27
+ }
28
+ catch (error) {
29
+ status = 'rejected';
30
+ rejectionCode = error instanceof CogentaError ? error.code : 'SKIN_VALIDATION_FAILED';
31
+ rejectionReason = error instanceof Error ? error.message : String(error);
32
+ }
33
+ const description = input.description ?? null;
34
+ await db.query(sql `
35
+ insert into ${skins}
36
+ (id, submitter_id, display_name, description, tokens_json, status, rejection_code, rejection_reason, submitted_at)
37
+ values
38
+ (${id}, ${input.submitterId}, ${input.displayName}, ${description}, ${JSON.stringify(input.tokens)}, ${status}, ${rejectionCode}, ${rejectionReason}, ${submittedAt})`);
39
+ return {
40
+ id,
41
+ submitterId: input.submitterId,
42
+ displayName: input.displayName,
43
+ description,
44
+ status,
45
+ rejectionCode,
46
+ rejectionReason,
47
+ submittedAt,
48
+ };
49
+ },
50
+ async listAccepted() {
51
+ const result = await db.query(sql `
52
+ select id, submitter_id, display_name, description, status, rejection_code, rejection_reason, submitted_at
53
+ from ${skins} where status = 'accepted' order by submitted_at asc`);
54
+ return result.rows.map(toEntry);
55
+ },
56
+ async get(id) {
57
+ const result = await db.query(sql `
58
+ select id, submitter_id, display_name, description, status, rejection_code, rejection_reason, submitted_at
59
+ from ${skins} where id = ${id}`);
60
+ const row = result.rows[0];
61
+ return row === undefined ? null : toEntry(row);
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=skins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skins.js","sourceRoot":"","sources":["../../src/registries/skins.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ export declare const REGISTRY_TABLES: {
3
+ readonly skins: 'cogenta_skin_gallery';
4
+ readonly skills: 'cogenta_skill_registry';
5
+ readonly themes: 'cogenta_theme_registry';
6
+ readonly plugins: 'cogenta_plugin_registry';
7
+ };
8
+ /**
9
+ * Owned by `@cogenta/plugins`, following `permissions/tables.ts`'s
10
+ * `ensurePluginTables` pattern exactly: `create table if not exists`, run
11
+ * once at startup, no separate migration file.
12
+ *
13
+ * A skin submission never executes code (contract D: a skin is a closed JSON
14
+ * token set) — the "Skins" registry's only real gate is automatic validation
15
+ * against `@cogenta/render`'s `validateSkin`, so there is no `reviewed_by`/
16
+ * `reviewed_at` column the way a code-executing registry (plugins, themes)
17
+ * would need: `status` is written once, at submission time, by the gate
18
+ * itself, never by a human afterward.
19
+ *
20
+ * A skill submission is the opposite case in the lot's own "## Registres"
21
+ * table: "Revue de contenu", not automatic validation. `skill_name`/
22
+ * `skill_version`/`reviewed_by`/`reviewed_at` exist here — absent from the
23
+ * skins table above — because a skill's gate is a real two-step state
24
+ * machine (automatic parse pre-check, then a human decision), not a single
25
+ * automatic verdict.
26
+ *
27
+ * A theme submission (task 12) is automatic-only again, like skins — no
28
+ * `reviewed_by`/`reviewed_at` — so its base columns end up identical to the
29
+ * skins table's, minus `tokens_json` (a theme's real content lives on disk
30
+ * at submission time, verified in place, not persisted as an inline blob).
31
+ * With three real registries now built, this is the natural point to weigh a
32
+ * shared `Registry<T>` helper (task 11 deferred that exact question here) —
33
+ * the honest call: the three literal `create table` blocks below stay
34
+ * because Zig-zagging column sets (skins/themes share a shape skills does
35
+ * not; skills' own two-step state machine has no equivalent in the other
36
+ * two) make a generic helper either leaky (an interface wide enough for all
37
+ * three degenerates into passing raw column lists, which is what this
38
+ * function already does) or a false abstraction over accidentally-similar
39
+ * shapes. `identifier`/`sql`/`createIndexIfMissing` below already are the
40
+ * real shared primitives; duplicating eight lines of column declarations
41
+ * three times costs less than a parameterised table builder would.
42
+ */
43
+ export declare function ensureRegistryTables(db: DatabaseHandle): Promise<void>;
44
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/registries/tables.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EAKpB,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,eAAe;aAC1B,KAAK,EAAE,sBAAsB;aAC7B,MAAM,EAAE,wBAAwB;aAChC,MAAM,EAAE,wBAAwB;aAChC,OAAO,EAAE,yBAAyB;CAC1B,CAAA;AAMV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAyF5E"}
@@ -0,0 +1,118 @@
1
+ import { identifier, sql, unsafeRaw, } from '@cogenta/core';
2
+ export const REGISTRY_TABLES = {
3
+ skins: 'cogenta_skin_gallery',
4
+ skills: 'cogenta_skill_registry',
5
+ themes: 'cogenta_theme_registry',
6
+ plugins: 'cogenta_plugin_registry',
7
+ };
8
+ function textColumn(dialect, length) {
9
+ return unsafeRaw(dialect === 'sqlite' ? 'text' : `varchar(${length})`);
10
+ }
11
+ /**
12
+ * Owned by `@cogenta/plugins`, following `permissions/tables.ts`'s
13
+ * `ensurePluginTables` pattern exactly: `create table if not exists`, run
14
+ * once at startup, no separate migration file.
15
+ *
16
+ * A skin submission never executes code (contract D: a skin is a closed JSON
17
+ * token set) — the "Skins" registry's only real gate is automatic validation
18
+ * against `@cogenta/render`'s `validateSkin`, so there is no `reviewed_by`/
19
+ * `reviewed_at` column the way a code-executing registry (plugins, themes)
20
+ * would need: `status` is written once, at submission time, by the gate
21
+ * itself, never by a human afterward.
22
+ *
23
+ * A skill submission is the opposite case in the lot's own "## Registres"
24
+ * table: "Revue de contenu", not automatic validation. `skill_name`/
25
+ * `skill_version`/`reviewed_by`/`reviewed_at` exist here — absent from the
26
+ * skins table above — because a skill's gate is a real two-step state
27
+ * machine (automatic parse pre-check, then a human decision), not a single
28
+ * automatic verdict.
29
+ *
30
+ * A theme submission (task 12) is automatic-only again, like skins — no
31
+ * `reviewed_by`/`reviewed_at` — so its base columns end up identical to the
32
+ * skins table's, minus `tokens_json` (a theme's real content lives on disk
33
+ * at submission time, verified in place, not persisted as an inline blob).
34
+ * With three real registries now built, this is the natural point to weigh a
35
+ * shared `Registry<T>` helper (task 11 deferred that exact question here) —
36
+ * the honest call: the three literal `create table` blocks below stay
37
+ * because Zig-zagging column sets (skins/themes share a shape skills does
38
+ * not; skills' own two-step state machine has no equivalent in the other
39
+ * two) make a generic helper either leaky (an interface wide enough for all
40
+ * three degenerates into passing raw column lists, which is what this
41
+ * function already does) or a false abstraction over accidentally-similar
42
+ * shapes. `identifier`/`sql`/`createIndexIfMissing` below already are the
43
+ * real shared primitives; duplicating eight lines of column declarations
44
+ * three times costs less than a parameterised table builder would.
45
+ */
46
+ export async function ensureRegistryTables(db) {
47
+ const d = db.dialect;
48
+ const skins = identifier(REGISTRY_TABLES.skins, d);
49
+ const t255 = textColumn(d, 255);
50
+ const tLong = unsafeRaw(d === 'sqlite' ? 'text' : 'text');
51
+ await db.query(sql `
52
+ create table if not exists ${skins} (
53
+ id ${t255} not null primary key,
54
+ submitter_id ${t255} not null,
55
+ display_name ${t255} not null,
56
+ description ${tLong},
57
+ tokens_json ${tLong} not null,
58
+ status ${t255} not null,
59
+ rejection_code ${t255},
60
+ rejection_reason ${tLong},
61
+ submitted_at ${t255} not null
62
+ )`);
63
+ await createIndexIfMissing(db, 'cogenta_skin_gallery_status', skins, sql `(status, submitted_at)`);
64
+ const skills = identifier(REGISTRY_TABLES.skills, d);
65
+ await db.query(sql `
66
+ create table if not exists ${skills} (
67
+ id ${t255} not null primary key,
68
+ submitter_id ${t255} not null,
69
+ display_name ${t255} not null,
70
+ description ${tLong},
71
+ raw_content ${tLong} not null,
72
+ skill_name ${t255},
73
+ skill_version ${t255},
74
+ status ${t255} not null,
75
+ rejection_code ${t255},
76
+ rejection_reason ${tLong},
77
+ reviewed_by ${t255},
78
+ reviewed_at ${t255},
79
+ submitted_at ${t255} not null
80
+ )`);
81
+ await createIndexIfMissing(db, 'cogenta_skill_registry_status', skills, sql `(status, submitted_at)`);
82
+ const themes = identifier(REGISTRY_TABLES.themes, d);
83
+ await db.query(sql `
84
+ create table if not exists ${themes} (
85
+ id ${t255} not null primary key,
86
+ submitter_id ${t255} not null,
87
+ display_name ${t255} not null,
88
+ description ${tLong},
89
+ status ${t255} not null,
90
+ rejection_code ${t255},
91
+ rejection_reason ${tLong},
92
+ submitted_at ${t255} not null
93
+ )`);
94
+ await createIndexIfMissing(db, 'cogenta_theme_registry_status', themes, sql `(status, submitted_at)`);
95
+ const plugins = identifier(REGISTRY_TABLES.plugins, d);
96
+ await db.query(sql `
97
+ create table if not exists ${plugins} (
98
+ id ${t255} not null primary key,
99
+ submitter_id ${t255} not null,
100
+ display_name ${t255} not null,
101
+ description ${tLong},
102
+ plugin_name ${t255},
103
+ plugin_version ${t255},
104
+ status ${t255} not null,
105
+ rejection_code ${t255},
106
+ rejection_reason ${tLong},
107
+ reviewed_by ${t255},
108
+ reviewed_at ${t255},
109
+ submitted_at ${t255} not null
110
+ )`);
111
+ await createIndexIfMissing(db, 'cogenta_plugin_registry_status', plugins, sql `(status, submitted_at)`);
112
+ }
113
+ async function createIndexIfMissing(db, name, table, columns) {
114
+ await db
115
+ .query(sql `create index ${identifier(name, db.dialect)} on ${table} ${columns}`)
116
+ .catch(() => undefined); // already there — no portable "if not exists" for indexes
117
+ }
118
+ //# sourceMappingURL=tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.js","sourceRoot":"","sources":["../../src/registries/tables.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { type DatabaseHandle } from '@cogenta/core';
2
+ export type ThemeSubmissionStatus = 'accepted' | 'rejected';
3
+ export interface ThemeSubmissionInput {
4
+ readonly submitterId: string;
5
+ readonly displayName: string;
6
+ readonly description?: string;
7
+ /** Raw manifest input, unvalidated until `submit()` runs it through `parseThemeManifest`. */
8
+ readonly manifest: unknown;
9
+ /**
10
+ * Real filesystem path to the theme's extracted source tree — `verifyTheme`
11
+ * (contract D's install-time check) scans real files for the forbidden-import
12
+ * and `implements`-coverage rules, so a submission needs real content on
13
+ * disk, not just an inline JSON blob the way a skin submission does.
14
+ */
15
+ readonly themeRoot: string;
16
+ /** Base64 Ed25519 signature over the manifest's canonical content, or `null` for none. */
17
+ readonly signatureBase64: string | null;
18
+ }
19
+ export interface ThemeRegistryEntry {
20
+ readonly id: string;
21
+ readonly submitterId: string;
22
+ readonly displayName: string;
23
+ readonly description: string | null;
24
+ readonly status: ThemeSubmissionStatus;
25
+ /** Which real gate failed: `signature`, or a contract-D `CogentaError` code — `null` when accepted. */
26
+ readonly rejectionCode: string | null;
27
+ readonly rejectionReason: string | null;
28
+ readonly submittedAt: string;
29
+ }
30
+ export interface ThemeRegistryOptions {
31
+ readonly trustedPublicKeys?: readonly string[];
32
+ }
33
+ export interface ThemeRegistry {
34
+ /**
35
+ * Runs the submission through the lot's own two named gates, in order:
36
+ * (1) **Signature** — the manifest's canonical content must carry a valid
37
+ * Ed25519 signature from a trusted registry key (task 9's real primitive,
38
+ * generalized to arbitrary content). Checked first because it is the more
39
+ * fundamental trust question — an unsigned theme is refused before any
40
+ * contract inspection runs, the same way an unlinked channel identity is
41
+ * refused before a command is even looked up (L6 task 3's precedent).
42
+ * (2) **Contrat vérifié** — contract D's real install-time check
43
+ * (`verifyTheme`, `@cogenta/render`): every vocabulary block is declared in
44
+ * `implements`, no forbidden import (`node:fs`, `@cogenta/core`, ...)
45
+ * appears anywhere in the theme's real sources, and its default skin
46
+ * (`tokens.json`) passes `validateSkin` — contract D's token rules, reused
47
+ * exactly as the Skins gallery (task 10) reused them, not reimplemented.
48
+ *
49
+ * No human-review step exists for themes ("Signature, contrat vérifié" is
50
+ * the lot's whole named requirement — no "revue" column value, unlike
51
+ * Skills) — a single automatic decision at submission time, same shape as
52
+ * the fully-automatic Skins gallery, just with more real checks.
53
+ */
54
+ submit(input: ThemeSubmissionInput): Promise<ThemeRegistryEntry>;
55
+ listAccepted(): Promise<readonly ThemeRegistryEntry[]>;
56
+ get(id: string): Promise<ThemeRegistryEntry | null>;
57
+ }
58
+ export declare function createThemeRegistry(db: DatabaseHandle, options?: ThemeRegistryOptions, now?: () => number): ThemeRegistry;
59
+ //# sourceMappingURL=themes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.d.ts","sourceRoot":"","sources":["../../src/registries/themes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,cAAc,EAA0B,MAAM,eAAe,CAAA;AAKzF,MAAM,MAAM,qBAAqB,GAAG,UAAU,GAAG,UAAU,CAAA;AAE3D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6FAA6F;IAC7F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAA;IACtC,uGAAuG;IACvG,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;;;;;;;;;;OAmBG;IACH,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAChE,YAAY,IAAI,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAA;IACtD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAA;CACpD;AA0BD,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,cAAc,EAClB,OAAO,GAAE,oBAAyB,EAClC,GAAG,GAAE,MAAM,MAAiB,GAC3B,aAAa,CA2Ef"}
@@ -0,0 +1,82 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { CogentaError, identifier, newId, sql } from '@cogenta/core';
4
+ import { parseThemeManifest, validateSkin, verifyTheme } from '@cogenta/render';
5
+ import { TRUSTED_REGISTRY_PUBLIC_KEYS, verifyContentAgainstTrustedKeys } from '../signing/verify.js';
6
+ import { REGISTRY_TABLES } from './tables.js';
7
+ function toEntry(row) {
8
+ return {
9
+ id: row.id,
10
+ submitterId: row.submitter_id,
11
+ displayName: row.display_name,
12
+ description: row.description,
13
+ status: row.status === 'accepted' ? 'accepted' : 'rejected',
14
+ rejectionCode: row.rejection_code,
15
+ rejectionReason: row.rejection_reason,
16
+ submittedAt: row.submitted_at,
17
+ };
18
+ }
19
+ export function createThemeRegistry(db, options = {}, now = Date.now) {
20
+ const themes = identifier(REGISTRY_TABLES.themes, db.dialect);
21
+ const trustedPublicKeys = options.trustedPublicKeys ?? TRUSTED_REGISTRY_PUBLIC_KEYS;
22
+ return {
23
+ async submit(input) {
24
+ const id = newId(now);
25
+ const submittedAt = new Date(now()).toISOString();
26
+ let status = 'accepted';
27
+ let rejectionCode = null;
28
+ let rejectionReason = null;
29
+ try {
30
+ const manifest = parseThemeManifest(input.manifest, input.displayName);
31
+ const signed = verifyContentAgainstTrustedKeys(manifest, input.signatureBase64, trustedPublicKeys);
32
+ if (!signed) {
33
+ throw new CogentaError({
34
+ code: 'THEME_SIGNATURE_INVALID',
35
+ message: `The theme "${manifest.name}" has no valid signature from a trusted registry key.`,
36
+ hint: 'Sign the theme manifest with a registered registry key before submitting, or trust the signing key explicitly.',
37
+ details: { theme: manifest.name },
38
+ });
39
+ }
40
+ await verifyTheme({ root: input.themeRoot, manifest });
41
+ const tokensPath = join(input.themeRoot, manifest.tokens);
42
+ const tokensRaw = await readFile(tokensPath, 'utf8');
43
+ validateSkin(JSON.parse(tokensRaw));
44
+ }
45
+ catch (error) {
46
+ status = 'rejected';
47
+ rejectionCode = error instanceof CogentaError ? error.code : 'THEME_SUBMISSION_INVALID';
48
+ rejectionReason = error instanceof Error ? error.message : String(error);
49
+ }
50
+ const description = input.description ?? null;
51
+ await db.query(sql `
52
+ insert into ${themes}
53
+ (id, submitter_id, display_name, description, status, rejection_code, rejection_reason, submitted_at)
54
+ values
55
+ (${id}, ${input.submitterId}, ${input.displayName}, ${description}, ${status}, ${rejectionCode}, ${rejectionReason}, ${submittedAt})`);
56
+ return {
57
+ id,
58
+ submitterId: input.submitterId,
59
+ displayName: input.displayName,
60
+ description,
61
+ status,
62
+ rejectionCode,
63
+ rejectionReason,
64
+ submittedAt,
65
+ };
66
+ },
67
+ async listAccepted() {
68
+ const result = await db.query(sql `
69
+ select id, submitter_id, display_name, description, status, rejection_code, rejection_reason, submitted_at
70
+ from ${themes} where status = 'accepted' order by submitted_at asc`);
71
+ return result.rows.map(toEntry);
72
+ },
73
+ async get(id) {
74
+ const result = await db.query(sql `
75
+ select id, submitter_id, display_name, description, status, rejection_code, rejection_reason, submitted_at
76
+ from ${themes} where id = ${id}`);
77
+ const row = result.rows[0];
78
+ return row === undefined ? null : toEntry(row);
79
+ },
80
+ };
81
+ }
82
+ //# sourceMappingURL=themes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"themes.js","sourceRoot":"","sources":["../../src/registries/themes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A small, real semver-range matcher scoped to exactly the range shapes
3
+ * `manifest.ts`'s `SEMVER_RANGE_PATTERN` already accepts: `^1.2.3`,
4
+ * `~1.2.3`, a bare exact `1.2.3`, and a space-separated list of comparators
5
+ * (`>=1.0.0 <2.0.0`). No `semver` dependency (R9) — this is the whole
6
+ * matcher a plugin's `engine` field realistically needs, not a general
7
+ * semver library.
8
+ */
9
+ export interface SemverVersion {
10
+ readonly major: number;
11
+ readonly minor: number;
12
+ readonly patch: number;
13
+ }
14
+ export declare function parseVersion(input: string): SemverVersion | null;
15
+ /** -1 if `a < b`, 0 if equal, 1 if `a > b`. */
16
+ export declare function compareVersions(a: SemverVersion, b: SemverVersion): -1 | 0 | 1;
17
+ /**
18
+ * Whether `versionString` satisfies `range`. Returns `false` (never throws)
19
+ * for a range or version this matcher cannot parse — an unparseable engine
20
+ * range already fails `manifest.ts`'s own validation before this is ever
21
+ * called, so this is a defensive fallback, not the primary guard.
22
+ */
23
+ export declare function satisfiesRange(versionString: string, range: string): boolean;
24
+ //# sourceMappingURL=semver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"semver.d.ts","sourceRoot":"","sources":["../src/semver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAID,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAKhE;AAED,+CAA+C;AAC/C,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAK9E;AA0CD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAiB5E"}