@stigmer/plugin-package 3.18.0 → 3.19.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/client/builtin.d.ts +20 -23
- package/client/builtin.d.ts.map +1 -1
- package/client/builtin.js +19 -36
- package/client/builtin.js.map +1 -1
- package/client/presentation.d.ts +19 -0
- package/client/presentation.d.ts.map +1 -0
- package/client/presentation.js +44 -0
- package/client/presentation.js.map +1 -0
- package/client.d.ts +3 -0
- package/client.d.ts.map +1 -1
- package/client.js +3 -0
- package/client.js.map +1 -1
- package/detect.d.ts +7 -0
- package/detect.d.ts.map +1 -1
- package/detect.js +7 -0
- package/detect.js.map +1 -1
- package/dialects/manifest.d.ts +23 -5
- package/dialects/manifest.d.ts.map +1 -1
- package/dialects/manifest.js +38 -6
- package/dialects/manifest.js.map +1 -1
- package/dialects/open.d.ts +3 -2
- package/dialects/open.d.ts.map +1 -1
- package/dialects/open.js +3 -2
- package/dialects/open.js.map +1 -1
- package/files.d.ts +7 -0
- package/files.d.ts.map +1 -1
- package/files.js +7 -0
- package/files.js.map +1 -1
- package/index.d.ts +3 -1
- package/index.d.ts.map +1 -1
- package/index.js +3 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/presentation.d.ts +53 -0
- package/presentation.d.ts.map +1 -0
- package/presentation.js +156 -0
- package/presentation.js.map +1 -0
- package/src/__tests__/client-prepare.test.ts +3 -7
- package/src/__tests__/client-select-archive.test.ts +14 -12
- package/src/__tests__/detect.test.ts +11 -0
- package/src/__tests__/presentation.test.ts +186 -0
- package/src/client/builtin.ts +20 -39
- package/src/client/presentation.ts +47 -0
- package/src/client.ts +3 -0
- package/src/detect.ts +8 -0
- package/src/dialects/manifest.ts +37 -6
- package/src/dialects/open.ts +3 -2
- package/src/files.ts +7 -0
- package/src/index.ts +3 -1
- package/src/presentation.ts +170 -0
package/files.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAkBH;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,8EAA8E;IAC9E,QAAQ,EAAE,GAAG,GAAG,IAAI;IACpB,iCAAiC;IACjC,SAAS,EAAE,GAAG,GAAG,IAAI;IACrB;;;;OAIG;IACH,OAAO,EAAE,IAAI,GAAG,IAAI;IACpB,yEAAyE;IACzE,QAAQ,EAAE,IAAI,GAAG,IAAI;IACrB,uDAAuD;IACvD,OAAO,EAAE,IAAI,GAAG,IAAI;IACpB;;;;OAIG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI;CAChB,CAAC;AAIX,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;AAClC,4EAA4E;AAC5E,4EAA4E;AAC5E,wDAAwD;AACxD,MAAM,OAAO,GAAG,IAAI,WAAW,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAE3D,sFAAsF;AACtF,MAAM,UAAU,mBAAmB,CAAC,KAA+C;IACjF,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;QACpC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;SAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChD,OAAO;QACL,OAAO;QACP,IAAI,CAAC,IAAI;YACP,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,iBAAiB,CAAC,CAAC;YACzD,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,CAAS,EAAE,CAAS;IAC/C,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,0HAA0H;AAC1H,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7E,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC;AACnG,CAAC;AAMD,MAAM,eAAe,GAAG,WAAW,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;IACrF,IAAI,KAAK,KAAK,GAAG,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACnE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAC7E,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACnD,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IAClD,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAC/E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;AACrC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,IAAY;IAChD,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACpC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,eAAe;IAGL;IAFJ,MAAM,CAAuC;IAE9D,YAAqB,KAAkB;QAAlB,UAAK,GAAL,KAAK,CAAa;QACrC,IAAI,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,IAAY;QAChB,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,wGAAwG;IACxG,UAAU,CAAC,GAAW;QACpB,IAAI,GAAG,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrE,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC;QACzB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxG,CAAC;IAED,qDAAqD;IACrD,WAAW,CAAC,GAAW;QACrB,IAAI,GAAG,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,CAAC;QACzB,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED,6EAA6E;IAC7E,gBAAgB,CAAC,GAAW;QAC1B,MAAM,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,SAAS;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAChC,IAAI,KAAK,KAAK,CAAC,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACvC,CAAC;IAED,0DAA0D;IAC1D,UAAU,CAAC,GAAW;QACpB,MAAM,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC;QAC3C,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACvC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,SAAS;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAClC,CAAC;CACF"}
|
|
1
|
+
{"version":3,"file":"files.js","sourceRoot":"","sources":["../src/files.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* `@stigmer/plugin-package`: the public surface.
|
|
3
3
|
*
|
|
4
4
|
* `readPluginPackage` reads one plugin; `readMarketplace` reads a catalogue
|
|
5
|
-
* of them
|
|
5
|
+
* of them; `readPluginPresentation` reads what a card shows for one entry
|
|
6
|
+
* without opening the rest of it. Everything else here is what a consumer needs to supply its
|
|
6
7
|
* input (`PluginFiles`, the caps), route a directory (`MANIFEST_LOCATIONS`,
|
|
7
8
|
* `hasPluginManifest`, `MARKETPLACE_LOCATIONS`, `hasMarketplaceFile`), or
|
|
8
9
|
* render an outcome (the finding kinds and `isErrorKind`). The dialect
|
|
@@ -20,5 +21,6 @@ export { MARKETPLACE_LOCATIONS } from "./marketplace/messages.js";
|
|
|
20
21
|
export type { Marketplace, MarketplaceDialect, MarketplaceEntry, MarketplaceErrorKind, MarketplaceFinding, MarketplaceFindingKind, MarketplaceOwner, MarketplaceReadOutcome, MarketplaceWarningKind, } from "./marketplace/outcome.js";
|
|
21
22
|
export { hasMarketplaceFile, readMarketplace, readMarketplaceFile } from "./marketplace/read-marketplace.js";
|
|
22
23
|
export { readPluginPackage } from "./read-plugin-package.js";
|
|
24
|
+
export { type PluginPresentation, readPluginPresentation } from "./presentation.js";
|
|
23
25
|
export type { IgnoredComponent, IgnoredComponentKind, ModelAlias, ModelHint, OverlayDocument, OverlayNamedDocument, OverlayServerDocument, PluginAuthor, PluginDialect, PluginMcpServer, PluginPackage, PluginSkill, PluginSubAgent, PluginVariable, StigmerOverlay, } from "./types.js";
|
|
24
26
|
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,sBAAsB,EACtB,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,KAAK,eAAe,EACpB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,YAAY,EACV,OAAO,EACP,cAAc,EACd,eAAe,EACf,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,sBAAsB,EACtB,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,KAAK,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACpF,YAAY,EACV,gBAAgB,EAChB,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,eAAe,EACf,oBAAoB,EACpB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,eAAe,EACf,aAAa,EACb,WAAW,EACX,cAAc,EACd,cAAc,EACd,cAAc,GACf,MAAM,YAAY,CAAC"}
|
package/index.js
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* `@stigmer/plugin-package`: the public surface.
|
|
3
3
|
*
|
|
4
4
|
* `readPluginPackage` reads one plugin; `readMarketplace` reads a catalogue
|
|
5
|
-
* of them
|
|
5
|
+
* of them; `readPluginPresentation` reads what a card shows for one entry
|
|
6
|
+
* without opening the rest of it. Everything else here is what a consumer needs to supply its
|
|
6
7
|
* input (`PluginFiles`, the caps), route a directory (`MANIFEST_LOCATIONS`,
|
|
7
8
|
* `hasPluginManifest`, `MARKETPLACE_LOCATIONS`, `hasMarketplaceFile`), or
|
|
8
9
|
* render an outcome (the finding kinds and `isErrorKind`). The dialect
|
|
@@ -18,4 +19,5 @@ export { PLACEHOLDER_PATTERN, VARIABLE_NAME_PATTERN } from "./placeholders.js";
|
|
|
18
19
|
export { MARKETPLACE_LOCATIONS } from "./marketplace/messages.js";
|
|
19
20
|
export { hasMarketplaceFile, readMarketplace, readMarketplaceFile } from "./marketplace/read-marketplace.js";
|
|
20
21
|
export { readPluginPackage } from "./read-plugin-package.js";
|
|
22
|
+
export { readPluginPresentation } from "./presentation.js";
|
|
21
23
|
//# sourceMappingURL=index.js.map
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,sBAAsB,EACtB,mBAAmB,GAKpB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EACL,6BAA6B,EAC7B,wBAAwB,EACxB,YAAY,EACZ,WAAW,EACX,kBAAkB,EAClB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,0BAA0B,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAU/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAYlE,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC7G,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAA2B,sBAAsB,EAAE,MAAM,mBAAmB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stigmer/plugin-package",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.0",
|
|
4
4
|
"description": "Reads an Agent Plugins package — the open 1.0.0 format and the Cursor, Claude Code and Codex dialects — into one normalised description of what Stigmer would install, refusing with one sentence per problem what Stigmer cannot carry; pure over an in-memory file reader, shared by the CLI's offline validation and the server's install. The ./client entry is what every installing client does identically: file selection, the deterministic archive and its digest, the marketplace grammars",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a storefront shows for a plugin before anyone installs it: a display
|
|
3
|
+
* name, a logo, an author, a version, a description, a category.
|
|
4
|
+
*
|
|
5
|
+
* This is a read apart from `readPluginPackage` on purpose. The full read
|
|
6
|
+
* decides what Stigmer INSTALLS and refuses what it cannot; a card must
|
|
7
|
+
* render for every entry a catalogue lists, refused or not, and must cost
|
|
8
|
+
* one manifest, not the plugin's whole tree. So this reader opens only the
|
|
9
|
+
* manifests present, takes what they say about appearance, and never
|
|
10
|
+
* reports a finding: an unreadable manifest contributes nothing, a wrong
|
|
11
|
+
* type is treated as absent, and the caller draws a fallback face.
|
|
12
|
+
*
|
|
13
|
+
* Which manifest speaks is the full reader's rule (`MANIFEST_PRECEDENCE`:
|
|
14
|
+
* the root manifest, then Claude, Cursor, Codex), so a plugin shipping two
|
|
15
|
+
* dialects is named the same way on a card and in an install. Identity
|
|
16
|
+
* fields (`name`, `version`, `description`, `author`) come from the first
|
|
17
|
+
* manifest that carries each; the presentation fields come from the
|
|
18
|
+
* dialects that define them: Cursor's top-level `displayName`, `logo` and
|
|
19
|
+
* `category`; the legacy Codex manifest's `interface.displayName`,
|
|
20
|
+
* `interface.logo` and `interface.category`; the open format's
|
|
21
|
+
* `extensions["ai.stigmer"]` with the same three names, the one place that
|
|
22
|
+
* format leaves a client for them (Stigmer's authored catalogue plugins are
|
|
23
|
+
* its first users). Claude Code's manifest defines none.
|
|
24
|
+
*
|
|
25
|
+
* A logo is a path inside the plugin, and the card that shows it fetches it
|
|
26
|
+
* by URL from wherever the tree lives. So the path is kept only when it is
|
|
27
|
+
* contained, names an image by extension, is LISTED in the tree, and its
|
|
28
|
+
* declared size is under `PLUGIN_DOCUMENT_LIMITS.logo`; the library's rule
|
|
29
|
+
* that a cap is checked from declared sizes before a byte moves holds here
|
|
30
|
+
* too. Both spellings the vendors use (`assets/logo.png`, `./assets/logo.png`)
|
|
31
|
+
* are accepted, as the marketplace reader accepts both for a source.
|
|
32
|
+
*
|
|
33
|
+
* `logo` stays an ignored component in the full read: nothing in an
|
|
34
|
+
* Organization carries the image after an install, and the preview's "Not
|
|
35
|
+
* installed" line is true until the stored plugin does.
|
|
36
|
+
*/
|
|
37
|
+
import { type PluginFiles } from "./files.js";
|
|
38
|
+
import type { PluginAuthor } from "./types.js";
|
|
39
|
+
/** What a card shows for a plugin; every field optional, because every dialect may omit it. */
|
|
40
|
+
export interface PluginPresentation {
|
|
41
|
+
/** The name for people (`Thermos`); the install name stays `name`. */
|
|
42
|
+
readonly displayName?: string;
|
|
43
|
+
/** A plugin-relative path to an image the tree lists, verified as described in the module header. */
|
|
44
|
+
readonly logo?: string;
|
|
45
|
+
readonly author?: PluginAuthor;
|
|
46
|
+
readonly version?: string;
|
|
47
|
+
readonly description?: string;
|
|
48
|
+
/** The catalogue's own grouping word (`developer-tools`, `Productivity`), as written. */
|
|
49
|
+
readonly category?: string;
|
|
50
|
+
}
|
|
51
|
+
/** The appearance fields of every manifest present, merged in precedence order. */
|
|
52
|
+
export declare function readPluginPresentation(files: PluginFiles): PluginPresentation;
|
|
53
|
+
//# sourceMappingURL=presentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation.d.ts","sourceRoot":"","sources":["../src/presentation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAIH,OAAO,EAAwE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC;AAGpH,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAE9D,+FAA+F;AAC/F,MAAM,WAAW,kBAAkB;IACjC,sEAAsE;IACtE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,qGAAqG;IACrG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAKD,mFAAmF;AACnF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,kBAAkB,CAc7E"}
|
package/presentation.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a storefront shows for a plugin before anyone installs it: a display
|
|
3
|
+
* name, a logo, an author, a version, a description, a category.
|
|
4
|
+
*
|
|
5
|
+
* This is a read apart from `readPluginPackage` on purpose. The full read
|
|
6
|
+
* decides what Stigmer INSTALLS and refuses what it cannot; a card must
|
|
7
|
+
* render for every entry a catalogue lists, refused or not, and must cost
|
|
8
|
+
* one manifest, not the plugin's whole tree. So this reader opens only the
|
|
9
|
+
* manifests present, takes what they say about appearance, and never
|
|
10
|
+
* reports a finding: an unreadable manifest contributes nothing, a wrong
|
|
11
|
+
* type is treated as absent, and the caller draws a fallback face.
|
|
12
|
+
*
|
|
13
|
+
* Which manifest speaks is the full reader's rule (`MANIFEST_PRECEDENCE`:
|
|
14
|
+
* the root manifest, then Claude, Cursor, Codex), so a plugin shipping two
|
|
15
|
+
* dialects is named the same way on a card and in an install. Identity
|
|
16
|
+
* fields (`name`, `version`, `description`, `author`) come from the first
|
|
17
|
+
* manifest that carries each; the presentation fields come from the
|
|
18
|
+
* dialects that define them: Cursor's top-level `displayName`, `logo` and
|
|
19
|
+
* `category`; the legacy Codex manifest's `interface.displayName`,
|
|
20
|
+
* `interface.logo` and `interface.category`; the open format's
|
|
21
|
+
* `extensions["ai.stigmer"]` with the same three names, the one place that
|
|
22
|
+
* format leaves a client for them (Stigmer's authored catalogue plugins are
|
|
23
|
+
* its first users). Claude Code's manifest defines none.
|
|
24
|
+
*
|
|
25
|
+
* A logo is a path inside the plugin, and the card that shows it fetches it
|
|
26
|
+
* by URL from wherever the tree lives. So the path is kept only when it is
|
|
27
|
+
* contained, names an image by extension, is LISTED in the tree, and its
|
|
28
|
+
* declared size is under `PLUGIN_DOCUMENT_LIMITS.logo`; the library's rule
|
|
29
|
+
* that a cap is checked from declared sizes before a byte moves holds here
|
|
30
|
+
* too. Both spellings the vendors use (`assets/logo.png`, `./assets/logo.png`)
|
|
31
|
+
* are accepted, as the marketplace reader accepts both for a source.
|
|
32
|
+
*
|
|
33
|
+
* `logo` stays an ignored component in the full read: nothing in an
|
|
34
|
+
* Organization carries the image after an install, and the preview's "Not
|
|
35
|
+
* installed" line is true until the stored plugin does.
|
|
36
|
+
*/
|
|
37
|
+
import { MANIFEST_PRECEDENCE } from "./detect.js";
|
|
38
|
+
import { stigmerExtensionOf } from "./dialects/manifest.js";
|
|
39
|
+
import { decodeUtf8, isContainedPath, PLUGIN_DOCUMENT_LIMITS, PluginFileIndex } from "./files.js";
|
|
40
|
+
import { isJsonObject } from "./documents.js";
|
|
41
|
+
import { MANIFEST_LOCATIONS } from "./messages.js";
|
|
42
|
+
/** The image extensions a browser renders in an `<img>`; a logo named otherwise is not shown. */
|
|
43
|
+
const LOGO_EXTENSIONS = new Set(["png", "svg", "jpg", "jpeg", "webp", "gif"]);
|
|
44
|
+
/** The appearance fields of every manifest present, merged in precedence order. */
|
|
45
|
+
export function readPluginPresentation(files) {
|
|
46
|
+
const index = new PluginFileIndex(files);
|
|
47
|
+
const presentation = {};
|
|
48
|
+
for (const dialect of MANIFEST_PRECEDENCE) {
|
|
49
|
+
const object = readManifest(index, MANIFEST_LOCATIONS[dialect]);
|
|
50
|
+
if (object === undefined)
|
|
51
|
+
continue;
|
|
52
|
+
const contribution = contributionOf(dialect, object, index);
|
|
53
|
+
for (const key of Object.keys(contribution)) {
|
|
54
|
+
if (presentation[key] === undefined)
|
|
55
|
+
Object.assign(presentation, { [key]: contribution[key] });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return presentation;
|
|
59
|
+
}
|
|
60
|
+
/** The manifest at `path` as an object, or `undefined` for anything a card should not stumble on. */
|
|
61
|
+
function readManifest(index, path) {
|
|
62
|
+
const entry = index.entry(path);
|
|
63
|
+
if (entry === undefined || entry.size > PLUGIN_DOCUMENT_LIMITS.manifest)
|
|
64
|
+
return undefined;
|
|
65
|
+
let bytes;
|
|
66
|
+
try {
|
|
67
|
+
bytes = index.files.read(path);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
if (bytes.length > PLUGIN_DOCUMENT_LIMITS.manifest)
|
|
73
|
+
return undefined;
|
|
74
|
+
let value;
|
|
75
|
+
try {
|
|
76
|
+
value = JSON.parse(decodeUtf8(bytes));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
return isJsonObject(value) ? value : undefined;
|
|
82
|
+
}
|
|
83
|
+
function contributionOf(dialect, object, index) {
|
|
84
|
+
const identity = identityOf(object);
|
|
85
|
+
switch (dialect) {
|
|
86
|
+
case "cursor":
|
|
87
|
+
return { ...identity, ...appearanceOf(object, index) };
|
|
88
|
+
case "codex": {
|
|
89
|
+
const surface = object["interface"];
|
|
90
|
+
return isJsonObject(surface) ? { ...identity, ...appearanceOf(surface, index) } : identity;
|
|
91
|
+
}
|
|
92
|
+
case "agent-plugins": {
|
|
93
|
+
const extension = stigmerExtensionOf(object);
|
|
94
|
+
return extension === undefined ? identity : { ...identity, ...appearanceOf(extension, index) };
|
|
95
|
+
}
|
|
96
|
+
case "claude":
|
|
97
|
+
return identity;
|
|
98
|
+
default: {
|
|
99
|
+
const exhaustive = dialect;
|
|
100
|
+
return exhaustive;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** The identity fields every dialect shares, typed loosely: a wrong type is absent, never a finding. */
|
|
105
|
+
function identityOf(object) {
|
|
106
|
+
const author = authorOf(object["author"]);
|
|
107
|
+
return {
|
|
108
|
+
...optional("version", object["version"]),
|
|
109
|
+
...optional("description", object["description"]),
|
|
110
|
+
...(author !== undefined && { author }),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/** `displayName`, `logo` and `category` from an object that carries them at its top level (a Cursor manifest, a Codex `interface`, an open manifest's `extensions["ai.stigmer"]`). */
|
|
114
|
+
function appearanceOf(object, index) {
|
|
115
|
+
const logo = logoPath(object["logo"], index);
|
|
116
|
+
return {
|
|
117
|
+
...optional("displayName", object["displayName"]),
|
|
118
|
+
...optional("category", object["category"]),
|
|
119
|
+
...(logo !== undefined && { logo }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function authorOf(value) {
|
|
123
|
+
if (!isJsonObject(value))
|
|
124
|
+
return undefined;
|
|
125
|
+
const author = {
|
|
126
|
+
...optional("name", value["name"]),
|
|
127
|
+
...optional("email", value["email"]),
|
|
128
|
+
...optional("url", value["url"]),
|
|
129
|
+
};
|
|
130
|
+
return Object.keys(author).length === 0 ? undefined : author;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* A declared logo as a plugin-relative path the tree lists, or nothing.
|
|
134
|
+
* `./` is stripped (Codex's spelling); an absolute path, a `..` segment, a
|
|
135
|
+
* URL, a non-image extension, an unlisted file or an over-cap file all
|
|
136
|
+
* mean "no logo", never a refusal.
|
|
137
|
+
*/
|
|
138
|
+
function logoPath(value, index) {
|
|
139
|
+
if (typeof value !== "string")
|
|
140
|
+
return undefined;
|
|
141
|
+
const path = value.startsWith("./") ? value.slice(2) : value;
|
|
142
|
+
if (!isContainedPath(path))
|
|
143
|
+
return undefined;
|
|
144
|
+
const dot = path.lastIndexOf(".");
|
|
145
|
+
if (dot === -1 || !LOGO_EXTENSIONS.has(path.slice(dot + 1).toLowerCase()))
|
|
146
|
+
return undefined;
|
|
147
|
+
const entry = index.entry(path);
|
|
148
|
+
if (entry === undefined || entry.size > PLUGIN_DOCUMENT_LIMITS.logo)
|
|
149
|
+
return undefined;
|
|
150
|
+
return path;
|
|
151
|
+
}
|
|
152
|
+
/** `{ [key]: value }` when `value` is a non-empty string; `{}` otherwise. */
|
|
153
|
+
function optional(key, value) {
|
|
154
|
+
return typeof value === "string" && value !== "" ? { [key]: value } : {};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=presentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentation.js","sourceRoot":"","sources":["../src/presentation.ts"],"names":[],"mappings":"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"}
|
|
@@ -153,15 +153,11 @@ describe("rerootSingleDirectory", () => {
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
describe("BUILT_IN_MARKETPLACES", () => {
|
|
156
|
-
it("lists the official catalogue
|
|
157
|
-
expect(BUILT_IN_MARKETPLACES.map((entry) => entry.name)).toEqual([
|
|
158
|
-
OFFICIAL_MARKETPLACE_NAME,
|
|
159
|
-
"cursor-plugins",
|
|
160
|
-
"claude-code-plugins",
|
|
161
|
-
"codex-plugins",
|
|
162
|
-
]);
|
|
156
|
+
it("lists the official catalogue alone, a reserved name; a vendor's repository is a source the user adds", () => {
|
|
157
|
+
expect(BUILT_IN_MARKETPLACES.map((entry) => entry.name)).toEqual([OFFICIAL_MARKETPLACE_NAME]);
|
|
163
158
|
expect(BUILT_IN_MARKETPLACES[0]?.source).toEqual({ type: "official" });
|
|
164
159
|
for (const entry of BUILT_IN_MARKETPLACES) expect(isBuiltInMarketplaceName(entry.name)).toBe(true);
|
|
160
|
+
expect(isBuiltInMarketplaceName("cursor-plugins")).toBe(false);
|
|
165
161
|
expect(isBuiltInMarketplaceName("acme-plugins")).toBe(false);
|
|
166
162
|
});
|
|
167
163
|
});
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
* list (the console's edge) yields one digest, and that digest is the one
|
|
5
5
|
* the CLI produced before this module existed.
|
|
6
6
|
*
|
|
7
|
-
* The
|
|
8
|
-
* `d75b0724a` (`preparePluginPush` over `
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the
|
|
7
|
+
* The frozen constant below was computed with the unmodified CLI at
|
|
8
|
+
* `d75b0724a` (`preparePluginPush` over the vendored `thermos` fixture,
|
|
9
|
+
* whose bytes never change). If it changes, every installed plugin would
|
|
10
|
+
* look like an upgrade; this test is the tripwire. The catalogue's own
|
|
11
|
+
* `plugins/assistant` is read too, but its content is allowed to evolve (a
|
|
12
|
+
* manifest edit is an upgrade by design), so its cases pin the identity
|
|
13
|
+
* discipline (the directory edge and the in-memory edge agree on one
|
|
14
|
+
* digest, and the selection is exactly its files) rather than a constant.
|
|
13
15
|
*
|
|
14
16
|
* The selection cases pin the walk order (component-wise, directories and
|
|
15
17
|
* files interleaved by name), the whole-subtree skip of an ignored
|
|
@@ -29,7 +31,6 @@ const FIXTURES = fileURLToPath(new URL("./fixtures/cursor-plugins/", import.meta
|
|
|
29
31
|
const REPO_PLUGINS = fileURLToPath(new URL("../../../../../../plugins/", import.meta.url));
|
|
30
32
|
|
|
31
33
|
const BASELINE = {
|
|
32
|
-
assistant: "8d1000b4a48136a8530509f603805693fc2478d279962d4fade40716435064ce",
|
|
33
34
|
thermos: "51bc4e5450e24762bb8515765c76bfe262f65c57f9afeda015c5818038396d5a",
|
|
34
35
|
} as const;
|
|
35
36
|
|
|
@@ -67,7 +68,7 @@ function inMemory(files: Record<string, string>): { candidates: CandidateFile[];
|
|
|
67
68
|
const RESPECT = { respectGitignore: true };
|
|
68
69
|
|
|
69
70
|
describe("parity with the CLI's original walk", () => {
|
|
70
|
-
it("plugins/assistant
|
|
71
|
+
it("plugins/assistant selects exactly its files, in walk order", () => {
|
|
71
72
|
const { candidates, read } = listDirectory(`${REPO_PLUGINS}assistant`);
|
|
72
73
|
const selection = selectPluginFiles(candidates, read, RESPECT);
|
|
73
74
|
expect(selection.files.entries.map((entry) => entry.path)).toEqual([
|
|
@@ -75,7 +76,6 @@ describe("parity with the CLI's original walk", () => {
|
|
|
75
76
|
"icon.svg",
|
|
76
77
|
"plugin.json",
|
|
77
78
|
]);
|
|
78
|
-
expect(await digestArchive(archivePlugin(selection.files))).toBe(BASELINE.assistant);
|
|
79
79
|
});
|
|
80
80
|
|
|
81
81
|
it("the thermos fixture archives to the CLI's digest", async () => {
|
|
@@ -85,13 +85,15 @@ describe("parity with the CLI's original walk", () => {
|
|
|
85
85
|
expect(await digestArchive(archivePlugin(selection.files))).toBe(BASELINE.thermos);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
it("the same bytes from an in-memory listing yield
|
|
88
|
+
it("the same bytes from a directory and from an in-memory listing yield one digest", async () => {
|
|
89
89
|
const dir = listDirectory(`${REPO_PLUGINS}assistant`);
|
|
90
|
+
const fromDirectory = await digestArchive(archivePlugin(selectPluginFiles(dir.candidates, dir.read, RESPECT).files));
|
|
90
91
|
const memory = inMemory(
|
|
91
92
|
Object.fromEntries(dir.candidates.map((c) => [c.path, new TextDecoder().decode(dir.read(c.path))])),
|
|
92
93
|
);
|
|
93
|
-
const
|
|
94
|
-
expect(
|
|
94
|
+
const fromMemory = await digestArchive(archivePlugin(selectPluginFiles(memory.candidates, memory.read, RESPECT).files));
|
|
95
|
+
expect(fromMemory).toBe(fromDirectory);
|
|
96
|
+
expect(fromDirectory).toMatch(/^[0-9a-f]{64}$/);
|
|
95
97
|
});
|
|
96
98
|
});
|
|
97
99
|
|
|
@@ -126,6 +126,17 @@ describe("unknown and mistyped fields", () => {
|
|
|
126
126
|
expect(plugin.ignored).toEqual([{ kind: "extension", path: "plugin.json#extensions.com.openai" }]);
|
|
127
127
|
});
|
|
128
128
|
|
|
129
|
+
it("reads ai.stigmer's appearance fields the way Cursor's are read: the logo an ignored component, the rest silent", () => {
|
|
130
|
+
const plugin = accepted(read(openPlugin({ manifest: { extensions: { "ai.stigmer": { displayName: "Linear", logo: "assets/logo.png", category: "integrations" } } } })));
|
|
131
|
+
expect(plugin.ignored).toEqual([{ kind: "logo", path: "plugin.json#extensions.ai.stigmer.logo" }]);
|
|
132
|
+
expect(kindsOf(read(openPlugin({ manifest: { extensions: { "ai.stigmer": { displayName: "Linear" } } } })))).toEqual({ errors: [], warnings: [] });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("a field ai.stigmer does not define makes the namespace an ignored extension, so a misspelling stays visible", () => {
|
|
136
|
+
const plugin = accepted(read(openPlugin({ manifest: { extensions: { "ai.stigmer": { displayname: "Linear" } } } })));
|
|
137
|
+
expect(plugin.ignored).toEqual([{ kind: "extension", path: "plugin.json#extensions.ai.stigmer" }]);
|
|
138
|
+
});
|
|
139
|
+
|
|
129
140
|
it("warns on a non-object extensions field and keeps reading", () => {
|
|
130
141
|
const outcome = read(openPlugin({ manifest: { extensions: "nope" } }));
|
|
131
142
|
expect(kindsOf(outcome)).toEqual({ errors: [], warnings: ["manifest-extensions-invalid"] });
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The presentation read, the one a storefront card runs.
|
|
3
|
+
*
|
|
4
|
+
* Pins: the vendored Cursor fixtures yield their `displayName`, `logo`,
|
|
5
|
+
* `category` and author (thermos, a PNG; playwright, an SVG); a Codex
|
|
6
|
+
* manifest yields the same fields from `interface`, with the `./` spelling
|
|
7
|
+
* stripped; a Claude manifest yields identity only, and so does an open
|
|
8
|
+
* manifest until it carries `extensions["ai.stigmer"]`, which speaks the
|
|
9
|
+
* same three fields under the same logo rules; a
|
|
10
|
+
* root manifest names the plugin ahead of a vendor manifest beside it,
|
|
11
|
+
* while the vendor manifest still lends its appearance; a logo that is
|
|
12
|
+
* absent from the listing, over the cap, not an image, a URL or an escape
|
|
13
|
+
* is dropped without a finding; an unparseable manifest contributes nothing
|
|
14
|
+
* and the read still answers. The lazy twin reads exactly the manifests
|
|
15
|
+
* listed and no other file, and treats a fetch that rejects as a manifest
|
|
16
|
+
* that is not there.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { fileURLToPath } from "node:url";
|
|
20
|
+
|
|
21
|
+
import { describe, expect, it } from "vitest";
|
|
22
|
+
|
|
23
|
+
import { type LazyCandidate } from "../client/prepare.js";
|
|
24
|
+
import { readPluginPresentationFromTree } from "../client/presentation.js";
|
|
25
|
+
import { inMemoryPluginFiles, PLUGIN_DOCUMENT_LIMITS } from "../files.js";
|
|
26
|
+
import { MANIFEST_LOCATIONS } from "../messages.js";
|
|
27
|
+
import { readPluginPresentation } from "../presentation.js";
|
|
28
|
+
import { claudePlugin, codexPlugin, cursorPlugin, openPlugin, withFile, type PluginFixture } from "../testing.js";
|
|
29
|
+
import { directoryPluginFiles } from "../__test-utils__/directory-files.js";
|
|
30
|
+
|
|
31
|
+
const FIXTURES = fileURLToPath(new URL("./fixtures/cursor-plugins/", import.meta.url));
|
|
32
|
+
const PNG_BYTES = new Uint8Array([0x89, 0x50, 0x4e, 0x47]);
|
|
33
|
+
|
|
34
|
+
function present(files: PluginFixture) {
|
|
35
|
+
return readPluginPresentation(inMemoryPluginFiles(files));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe("readPluginPresentation over the vendored Cursor fixtures", () => {
|
|
39
|
+
it("thermos: display name, PNG logo, category, author", () => {
|
|
40
|
+
const presentation = readPluginPresentation(directoryPluginFiles(`${FIXTURES}thermos`));
|
|
41
|
+
expect(presentation).toEqual({
|
|
42
|
+
displayName: "Thermos",
|
|
43
|
+
logo: "assets/logo.png",
|
|
44
|
+
category: "developer-tools",
|
|
45
|
+
version: "1.0.0",
|
|
46
|
+
description: expect.stringContaining("Thermo-nuclear branch review"),
|
|
47
|
+
author: { name: "Cursor", email: "plugins@cursor.com" },
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("playwright: an SVG logo is an image too", () => {
|
|
52
|
+
const presentation = readPluginPresentation(directoryPluginFiles(`${FIXTURES}playwright`));
|
|
53
|
+
expect(presentation.displayName).toBe("Playwright");
|
|
54
|
+
expect(presentation.logo).toBe("assets/logo.svg");
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("the dialects that carry appearance", () => {
|
|
59
|
+
it("a Cursor manifest speaks at its top level", () => {
|
|
60
|
+
const files = withFile(
|
|
61
|
+
cursorPlugin({ manifest: { displayName: "GitHub", logo: "assets/logo.png", category: "integrations", author: { name: "Cursor" } } }),
|
|
62
|
+
"assets/logo.png",
|
|
63
|
+
PNG_BYTES,
|
|
64
|
+
);
|
|
65
|
+
expect(present(files)).toMatchObject({ displayName: "GitHub", logo: "assets/logo.png", category: "integrations", author: { name: "Cursor" } });
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("a legacy Codex manifest speaks through `interface`, and `./` is stripped from its logo", () => {
|
|
69
|
+
const files = withFile(
|
|
70
|
+
codexPlugin({
|
|
71
|
+
version: "6.0.1",
|
|
72
|
+
manifest: { interface: { displayName: "Airtable", logo: "./assets/logo.png", category: "Productivity", developerName: "Airtable" } },
|
|
73
|
+
}),
|
|
74
|
+
"assets/logo.png",
|
|
75
|
+
PNG_BYTES,
|
|
76
|
+
);
|
|
77
|
+
expect(present(files)).toEqual({ displayName: "Airtable", logo: "assets/logo.png", category: "Productivity", version: "6.0.1" });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("a Claude manifest and an open manifest without the extension yield identity only", () => {
|
|
81
|
+
expect(present(claudePlugin({ version: "1.0.0", description: "Reviews pull requests.", manifest: { author: { name: "Anthropic" } } }))).toEqual({
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
description: "Reviews pull requests.",
|
|
84
|
+
author: { name: "Anthropic" },
|
|
85
|
+
});
|
|
86
|
+
const open = present(openPlugin({ version: "2.0.0", description: "The assistant." }));
|
|
87
|
+
expect(open.displayName).toBeUndefined();
|
|
88
|
+
expect(open.logo).toBeUndefined();
|
|
89
|
+
expect(open.version).toBe("2.0.0");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("an open manifest speaks through extensions[\"ai.stigmer\"], under the same logo rules as a vendor manifest", () => {
|
|
93
|
+
const files = withFile(
|
|
94
|
+
openPlugin({
|
|
95
|
+
version: "1.0.0",
|
|
96
|
+
description: "Linear's hosted MCP server.",
|
|
97
|
+
manifest: { author: { name: "Stigmer" }, extensions: { "ai.stigmer": { displayName: "Linear", logo: "assets/logo.png", category: "integrations" } } },
|
|
98
|
+
}),
|
|
99
|
+
"assets/logo.png",
|
|
100
|
+
PNG_BYTES,
|
|
101
|
+
);
|
|
102
|
+
expect(present(files)).toEqual({
|
|
103
|
+
displayName: "Linear",
|
|
104
|
+
logo: "assets/logo.png",
|
|
105
|
+
category: "integrations",
|
|
106
|
+
version: "1.0.0",
|
|
107
|
+
description: "Linear's hosted MCP server.",
|
|
108
|
+
author: { name: "Stigmer" },
|
|
109
|
+
});
|
|
110
|
+
const unlisted = present(openPlugin({ manifest: { extensions: { "ai.stigmer": { displayName: "Linear", logo: "assets/missing.png" } } } }));
|
|
111
|
+
expect(unlisted).toEqual({ displayName: "Linear" });
|
|
112
|
+
expect(present(openPlugin({ manifest: { extensions: { "ai.stigmer": "nope" } } }))).toEqual({});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("a root manifest names the plugin first; a vendor manifest beside it still lends its appearance", () => {
|
|
116
|
+
let files = openPlugin({ version: "3.0.0", description: "From the root manifest." });
|
|
117
|
+
files = withFile(
|
|
118
|
+
files,
|
|
119
|
+
MANIFEST_LOCATIONS.cursor,
|
|
120
|
+
JSON.stringify({ name: "plugin", version: "1.0.0", description: "From the Cursor manifest.", displayName: "Plugin", logo: "assets/logo.png" }),
|
|
121
|
+
);
|
|
122
|
+
files = withFile(files, "assets/logo.png", PNG_BYTES);
|
|
123
|
+
expect(present(files)).toMatchObject({ version: "3.0.0", description: "From the root manifest.", displayName: "Plugin", logo: "assets/logo.png" });
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("a logo that cannot be shown is no logo, never a finding", () => {
|
|
128
|
+
const cases: readonly [string, string, PluginFixture | undefined][] = [
|
|
129
|
+
["not listed", "assets/missing.png", undefined],
|
|
130
|
+
["a URL", "https://example.com/logo.png", undefined],
|
|
131
|
+
["an escape", "../logo.png", undefined],
|
|
132
|
+
["not an image", "assets/logo.txt", withFile(cursorPlugin(), "assets/logo.txt", "text")],
|
|
133
|
+
["over the cap", "assets/huge.png", withFile(cursorPlugin(), "assets/huge.png", new Uint8Array(PLUGIN_DOCUMENT_LIMITS.logo + 1))],
|
|
134
|
+
];
|
|
135
|
+
for (const [label, logo, base] of cases) {
|
|
136
|
+
it(label, () => {
|
|
137
|
+
const files = withFile(base ?? cursorPlugin(), MANIFEST_LOCATIONS.cursor, JSON.stringify({ name: "plugin", displayName: "Plugin", logo }));
|
|
138
|
+
const presentation = present(files);
|
|
139
|
+
expect(presentation.displayName).toBe("Plugin");
|
|
140
|
+
expect(presentation.logo).toBeUndefined();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
it("a wrong type is absent", () => {
|
|
145
|
+
const files = withFile(cursorPlugin(), MANIFEST_LOCATIONS.cursor, JSON.stringify({ name: "plugin", displayName: 7, logo: ["assets/logo.png"], author: "Cursor" }));
|
|
146
|
+
expect(present(files)).toEqual({});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("an unparseable manifest contributes nothing and the read answers", () => {
|
|
150
|
+
const files = withFile(cursorPlugin({ version: "1.0.0" }), MANIFEST_LOCATIONS.cursor, "{ not json");
|
|
151
|
+
expect(present(files)).toEqual({});
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
describe("readPluginPresentationFromTree", () => {
|
|
156
|
+
function lazy(files: PluginFixture, reads: string[], failing: ReadonlySet<string> = new Set()): LazyCandidate[] {
|
|
157
|
+
const encoder = new TextEncoder();
|
|
158
|
+
return [...files.entries()].map(([path, content]) => {
|
|
159
|
+
const bytes = typeof content === "string" ? encoder.encode(content) : content;
|
|
160
|
+
return {
|
|
161
|
+
path,
|
|
162
|
+
size: bytes.length,
|
|
163
|
+
read: async () => {
|
|
164
|
+
reads.push(path);
|
|
165
|
+
if (failing.has(path)) throw new Error(`cannot read ${path}`);
|
|
166
|
+
return bytes;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
it("reads exactly the manifests listed and no other file", async () => {
|
|
173
|
+
const reads: string[] = [];
|
|
174
|
+
const files = withFile(cursorPlugin({ manifest: { displayName: "Thermos", logo: "assets/logo.png" }, skills: [{ name: "review" }] }), "assets/logo.png", PNG_BYTES);
|
|
175
|
+
const presentation = await readPluginPresentationFromTree(lazy(files, reads));
|
|
176
|
+
expect(presentation).toMatchObject({ displayName: "Thermos", logo: "assets/logo.png" });
|
|
177
|
+
expect(reads).toEqual([MANIFEST_LOCATIONS.cursor]);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("a manifest whose fetch rejects is a manifest that is not there", async () => {
|
|
181
|
+
const reads: string[] = [];
|
|
182
|
+
const files = cursorPlugin({ manifest: { displayName: "Thermos" } });
|
|
183
|
+
const presentation = await readPluginPresentationFromTree(lazy(files, reads, new Set([MANIFEST_LOCATIONS.cursor])));
|
|
184
|
+
expect(presentation).toEqual({});
|
|
185
|
+
});
|
|
186
|
+
});
|