@webboxes/plugin-hub 0.0.0-stage → 0.2.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 (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +8 -2
  3. package/dist/contract.d.ts +63 -0
  4. package/dist/contract.d.ts.map +1 -0
  5. package/dist/contract.js +310 -0
  6. package/dist/contract.js.map +1 -0
  7. package/dist/folders.d.ts +28 -0
  8. package/dist/folders.d.ts.map +1 -0
  9. package/dist/folders.js +88 -0
  10. package/dist/folders.js.map +1 -0
  11. package/dist/github-cli.d.ts +18 -0
  12. package/dist/github-cli.d.ts.map +1 -0
  13. package/dist/github-cli.js +140 -0
  14. package/dist/github-cli.js.map +1 -0
  15. package/dist/http.d.ts +17 -0
  16. package/dist/http.d.ts.map +1 -0
  17. package/dist/http.js +59 -0
  18. package/dist/http.js.map +1 -0
  19. package/dist/hub.d.ts +129 -0
  20. package/dist/hub.d.ts.map +1 -0
  21. package/dist/hub.js +148 -0
  22. package/dist/hub.js.map +1 -0
  23. package/dist/index.d.ts +17 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +20 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/name.d.ts +31 -0
  28. package/dist/name.d.ts.map +1 -0
  29. package/dist/name.js +57 -0
  30. package/dist/name.js.map +1 -0
  31. package/dist/npm-cli.d.ts +11 -0
  32. package/dist/npm-cli.d.ts.map +1 -0
  33. package/dist/npm-cli.js +102 -0
  34. package/dist/npm-cli.js.map +1 -0
  35. package/dist/npm-publish.d.ts +36 -0
  36. package/dist/npm-publish.d.ts.map +1 -0
  37. package/dist/npm-publish.js +63 -0
  38. package/dist/npm-publish.js.map +1 -0
  39. package/dist/pack.d.ts +32 -0
  40. package/dist/pack.d.ts.map +1 -0
  41. package/dist/pack.js +137 -0
  42. package/dist/pack.js.map +1 -0
  43. package/dist/pull.d.ts +55 -0
  44. package/dist/pull.d.ts.map +1 -0
  45. package/dist/pull.js +127 -0
  46. package/dist/pull.js.map +1 -0
  47. package/dist/save.d.ts +10 -0
  48. package/dist/save.d.ts.map +1 -0
  49. package/dist/save.js +15 -0
  50. package/dist/save.js.map +1 -0
  51. package/dist/share.d.ts +72 -0
  52. package/dist/share.d.ts.map +1 -0
  53. package/dist/share.js +136 -0
  54. package/dist/share.js.map +1 -0
  55. package/dist/source.d.ts +38 -0
  56. package/dist/source.d.ts.map +1 -0
  57. package/dist/source.js +122 -0
  58. package/dist/source.js.map +1 -0
  59. package/dist/store.d.ts +43 -0
  60. package/dist/store.d.ts.map +1 -0
  61. package/dist/store.js +112 -0
  62. package/dist/store.js.map +1 -0
  63. package/dist/tarball.d.ts +16 -0
  64. package/dist/tarball.d.ts.map +1 -0
  65. package/dist/tarball.js +138 -0
  66. package/dist/tarball.js.map +1 -0
  67. package/dist/version.d.ts +16 -0
  68. package/dist/version.d.ts.map +1 -0
  69. package/dist/version.js +69 -0
  70. package/dist/version.js.map +1 -0
  71. package/package.json +44 -3
package/dist/name.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ export interface PluginName {
2
+ /** The full name as npm and the profile's package.json spell it. */
3
+ readonly full: string;
4
+ /** The scope without `@` (the GitHub owner or npm scope), or undefined for an unscoped name. */
5
+ readonly owner?: string;
6
+ /** The name after the scope. */
7
+ readonly local: string;
8
+ /**
9
+ * The levels of the author's tree. Undefined when a level is not a plain segment (an older name such as `socket.io-client` or one with `_`):
10
+ * such a name is valid and installable, it just has no tree.
11
+ */
12
+ readonly path?: readonly string[];
13
+ }
14
+ export declare class InvalidPluginNameError extends Error {
15
+ readonly input: string;
16
+ readonly reason: string;
17
+ constructor(input: string, reason: string);
18
+ }
19
+ /** Read a plugin name. Throws for anything npm or DSH's installer would refuse. */
20
+ export declare function parsePluginName(input: string): PluginName;
21
+ /**
22
+ * Build a name from an owner and the levels of a tree, refusing a level that would make the dots ambiguous.
23
+ * `pluginNameOf('inboxxobni', ['accounting-app-aurora', 'sidebar', 'date-filter'])` is `@inboxxobni/accounting-app-aurora.sidebar.date-filter`.
24
+ */
25
+ export declare function pluginNameOf(owner: string | undefined, path: readonly string[]): PluginName;
26
+ /**
27
+ * The directory a name is kept under in a store: `@owner/a.b` becomes `@owner/a.b`, an unscoped `a.b` stays `a.b`. Both parts are already
28
+ * valid file names on every system (lowercase, no reserved characters).
29
+ */
30
+ export declare function storeDirectoryOf(name: PluginName): readonly string[];
31
+ //# sourceMappingURL=name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name.d.ts","sourceRoot":"","sources":["../src/name.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,gGAAgG;IAChG,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,gCAAgC;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAClC;AAED,qBAAa,sBAAuB,SAAQ,KAAK;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAtC,KAAK,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM;CAI5D;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAYzD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAM3F;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS,MAAM,EAAE,CAEpE"}
package/dist/name.js ADDED
@@ -0,0 +1,57 @@
1
+ // A plugin's name (spec 044 lifecycle.md, D9): an npm package name with exactly one level of scope, whose dots are levels of the author's own tree.
2
+ // `@inboxxobni/accounting-app-aurora.sidebar.date-filter` is owner `inboxxobni`, path `accounting-app-aurora` > `sidebar` > `date-filter`.
3
+ // npm, pnpm, DSH's installer and GitHub all accept this form, so the dotted name is the one form everywhere: typed, shown, published.
4
+ /** One level of the tree: lowercase letters, digits and hyphens, starting with a letter or digit. A dot is never part of a segment. */
5
+ const SEGMENT = /^[a-z0-9][a-z0-9-]*$/u;
6
+ /** What npm and DSH's installer accept (`PACKAGE_NAME` in `@deepseek-ai/dsh-plugin-manager`'s install-spec.ts). */
7
+ const NPM_NAME = /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/u;
8
+ const NPM_NAME_MAX_LENGTH = 214;
9
+ export class InvalidPluginNameError extends Error {
10
+ input;
11
+ reason;
12
+ constructor(input, reason) {
13
+ super(`${JSON.stringify(input)} is not a plugin name: ${reason}`);
14
+ this.input = input;
15
+ this.reason = reason;
16
+ this.name = 'InvalidPluginNameError';
17
+ }
18
+ }
19
+ /** Read a plugin name. Throws for anything npm or DSH's installer would refuse. */
20
+ export function parsePluginName(input) {
21
+ const full = input.trim();
22
+ if (full === '')
23
+ throw new InvalidPluginNameError(input, 'it is empty');
24
+ if (full.length > NPM_NAME_MAX_LENGTH)
25
+ throw new InvalidPluginNameError(input, `it is longer than ${NPM_NAME_MAX_LENGTH} characters`);
26
+ if (full !== full.toLowerCase())
27
+ throw new InvalidPluginNameError(input, 'npm accepts only lowercase names; write date-filter, not dateFilter');
28
+ if (!NPM_NAME.test(full))
29
+ throw new InvalidPluginNameError(input, 'use @owner/name with letters, digits, hyphens and dots, one "/" at most');
30
+ const slash = full.indexOf('/');
31
+ const owner = full.startsWith('@') ? full.slice(1, slash) : undefined;
32
+ const local = owner === undefined ? full : full.slice(slash + 1);
33
+ const levels = local.split('.');
34
+ const path = levels.every(level => SEGMENT.test(level)) ? levels : undefined;
35
+ return { full, local, ...(owner === undefined ? {} : { owner }), ...(path === undefined ? {} : { path }) };
36
+ }
37
+ /**
38
+ * Build a name from an owner and the levels of a tree, refusing a level that would make the dots ambiguous.
39
+ * `pluginNameOf('inboxxobni', ['accounting-app-aurora', 'sidebar', 'date-filter'])` is `@inboxxobni/accounting-app-aurora.sidebar.date-filter`.
40
+ */
41
+ export function pluginNameOf(owner, path) {
42
+ if (path.length === 0)
43
+ throw new InvalidPluginNameError('', 'it needs at least one level');
44
+ for (const level of path) {
45
+ if (!SEGMENT.test(level))
46
+ throw new InvalidPluginNameError(level, 'a level is lowercase letters, digits and hyphens, with no dot');
47
+ }
48
+ return parsePluginName(`${owner === undefined ? '' : `@${owner}/`}${path.join('.')}`);
49
+ }
50
+ /**
51
+ * The directory a name is kept under in a store: `@owner/a.b` becomes `@owner/a.b`, an unscoped `a.b` stays `a.b`. Both parts are already
52
+ * valid file names on every system (lowercase, no reserved characters).
53
+ */
54
+ export function storeDirectoryOf(name) {
55
+ return name.owner === undefined ? [name.local] : [`@${name.owner}`, name.local];
56
+ }
57
+ //# sourceMappingURL=name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name.js","sourceRoot":"","sources":["../src/name.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { NpmPublishPort } from './npm-publish.js';
2
+ import type { RegistryPort } from './pull.js';
3
+ export declare function npmCli(): NpmPublishPort;
4
+ /**
5
+ * A registry read through the user's npm, for a private registry or scope an anonymous request cannot see: `npm view` for the version and
6
+ * integrity, `npm pack` for the bytes. `registry` overrides the user's configuration; without it npm's own (scoped) configuration decides.
7
+ */
8
+ export declare function npmRegistryViaCli(registry?: string): RegistryPort;
9
+ /** Resolve from the first registry port that knows the package (anonymous https, then the user's npm), and download through that one. */
10
+ export declare function firstRegistryPort(...ports: readonly RegistryPort[]): RegistryPort;
11
+ //# sourceMappingURL=npm-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm-cli.d.ts","sourceRoot":"","sources":["../src/npm-cli.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAoB7C,wBAAgB,MAAM,IAAI,cAAc,CAavC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,YAAY,CA2BjE;AAED,yIAAyI;AACzI,wBAAgB,iBAAiB,CAAC,GAAG,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,CAgBjF"}
@@ -0,0 +1,102 @@
1
+ // npm through the user's own `npm` and its configuration (`.npmrc`: logins, scoped registries). Nothing here reads or stores a token; a missing
2
+ // npm or login is reported with what to do.
3
+ import { spawnSync } from 'node:child_process';
4
+ import { mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { ToolError } from './github-cli.js';
8
+ const shell = process.platform === 'win32';
9
+ function npm(args, cwd) {
10
+ // On Windows npm is a .cmd file, which Node runs only through a shell; the arguments here are ours, never user text.
11
+ const result = spawnSync('npm', [...args], { cwd, encoding: 'utf8', shell, env: { ...process.env, npm_config_yes: 'true', npm_config_update_notifier: 'false' }, maxBuffer: 64 * 1024 * 1024 });
12
+ if (result.error !== undefined)
13
+ throw new ToolError('npm is not available here: install Node.js (https://nodejs.org), which includes npm, and sign in with: npm login');
14
+ return { status: result.status ?? 1, stdout: result.stdout ?? '', stderr: result.stderr ?? '' };
15
+ }
16
+ function signInHint(registry) {
17
+ return `sign in with: npm login${registry === undefined ? '' : ` --registry ${registry}`}`;
18
+ }
19
+ function failure(what, stderr, registry) {
20
+ const last = stderr.trim().split('\n').filter(line => !/^npm (notice|warn)/iu.test(line)).at(-1) ?? '';
21
+ return new ToolError(`npm ${what} failed: ${last}${/E401|E403|ENEEDAUTH|need auth|unauthorized|login/iu.test(stderr) ? ` (${signInHint(registry)})` : ''}`);
22
+ }
23
+ export function npmCli() {
24
+ return {
25
+ hasVersion(name, version, registry) {
26
+ const result = npm(['view', `${name}@${version}`, 'version', '--registry', registry]);
27
+ if (result.status === 0)
28
+ return result.stdout.trim() !== '';
29
+ if (/E404|404 Not Found|is not in this registry/iu.test(result.stderr))
30
+ return false;
31
+ throw failure('view', result.stderr, registry);
32
+ },
33
+ publish(tarballPath, registry, access) {
34
+ const result = npm(['publish', tarballPath, '--registry', registry, '--access', access, '--ignore-scripts']);
35
+ if (result.status !== 0)
36
+ throw failure('publish', result.stderr, registry);
37
+ },
38
+ };
39
+ }
40
+ /**
41
+ * A registry read through the user's npm, for a private registry or scope an anonymous request cannot see: `npm view` for the version and
42
+ * integrity, `npm pack` for the bytes. `registry` overrides the user's configuration; without it npm's own (scoped) configuration decides.
43
+ */
44
+ export function npmRegistryViaCli(registry) {
45
+ const flags = registry === undefined ? [] : ['--registry', registry];
46
+ return {
47
+ async resolve(name, version) {
48
+ const result = npm(['view', version === undefined ? name : `${name}@${version}`, 'version', 'dist.tarball', 'dist.integrity', '--json', ...flags]);
49
+ if (result.status !== 0) {
50
+ if (/E404|is not in this registry/iu.test(result.stderr))
51
+ return undefined;
52
+ throw failure('view', result.stderr, registry);
53
+ }
54
+ const parsed = JSON.parse(result.stdout || 'null');
55
+ const view = Array.isArray(parsed) ? parsed.at(-1) : parsed;
56
+ if (view === null || view === undefined || typeof view['version'] !== 'string' || typeof view['dist.tarball'] !== 'string')
57
+ return undefined;
58
+ return { version: view['version'], tarball: `npm:${name}@${view['version']}`, ...(typeof view['dist.integrity'] === 'string' ? { integrity: view['dist.integrity'] } : {}) };
59
+ },
60
+ async download(spec) {
61
+ const work = mkdtempSync(join(tmpdir(), 'plugin-hub-npm-pack-'));
62
+ try {
63
+ const result = npm(['pack', spec.replace(/^npm:/u, ''), '--pack-destination', work, '--ignore-scripts', ...flags], work);
64
+ if (result.status !== 0)
65
+ throw failure('pack', result.stderr, registry);
66
+ const file = readdirSync(work).find(entry => entry.endsWith('.tgz'));
67
+ if (file === undefined)
68
+ throw new ToolError(`npm pack ${spec} wrote no tarball`);
69
+ return new Uint8Array(readFileSync(join(work, file)));
70
+ }
71
+ finally {
72
+ rmSync(work, { recursive: true, force: true });
73
+ }
74
+ },
75
+ };
76
+ }
77
+ /** Resolve from the first registry port that knows the package (anonymous https, then the user's npm), and download through that one. */
78
+ export function firstRegistryPort(...ports) {
79
+ const answered = new Map();
80
+ return {
81
+ async resolve(name, version) {
82
+ let lastError;
83
+ for (const port of ports) {
84
+ try {
85
+ const found = await port.resolve(name, version);
86
+ if (found !== undefined) {
87
+ answered.set(found.tarball, port);
88
+ return found;
89
+ }
90
+ }
91
+ catch (cause) {
92
+ lastError = cause;
93
+ }
94
+ }
95
+ if (lastError !== undefined)
96
+ throw lastError;
97
+ return undefined;
98
+ },
99
+ download: async (url) => (answered.get(url) ?? ports[0]).download(url),
100
+ };
101
+ }
102
+ //# sourceMappingURL=npm-cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm-cli.js","sourceRoot":"","sources":["../src/npm-cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import type { SecretFinding } from '@webboxes/app-persistence';
2
+ import { type Visibility } from './share.js';
3
+ import type { PluginStore } from './store.js';
4
+ /** The registry, through the user's own `npm` (an adapter in npm-cli.ts; a test fake otherwise). */
5
+ export interface NpmPublishPort {
6
+ /** Whether `name@version` is already on the registry. */
7
+ hasVersion(name: string, version: string, registry: string): boolean;
8
+ /** Publish a tarball file; `access` is `public` or `restricted` (a private scoped package). */
9
+ publish(tarballPath: string, registry: string, access: 'public' | 'restricted'): void;
10
+ }
11
+ export interface NpmPublishOptions {
12
+ /** Default: the public npm registry. */
13
+ readonly registry?: string;
14
+ /** Default: the plugin's `acryl.visibility`, else private. */
15
+ readonly visibility?: Visibility;
16
+ }
17
+ export type NpmPublishResult = {
18
+ readonly status: 'published';
19
+ readonly name: string;
20
+ readonly version: string;
21
+ readonly registry: string;
22
+ readonly access: 'public' | 'restricted';
23
+ readonly integrity: string;
24
+ readonly files: readonly string[];
25
+ readonly updatedManifest: readonly string[];
26
+ } | {
27
+ readonly status: 'refused';
28
+ readonly reason: string;
29
+ readonly secrets?: readonly SecretFinding[];
30
+ };
31
+ export declare const NPM_REGISTRY = "https://registry.npmjs.org/";
32
+ export declare function publishToNpm(folder: string, options: NpmPublishOptions, ports: {
33
+ readonly npm: NpmPublishPort;
34
+ readonly store: PluginStore;
35
+ }): Promise<NpmPublishResult>;
36
+ //# sourceMappingURL=npm-publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm-publish.d.ts","sourceRoot":"","sources":["../src/npm-publish.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AAI9D,OAAO,EAA4B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,oGAAoG;AACpG,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACpE,+FAA+F;IAC/F,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,YAAY,GAAG,IAAI,CAAA;CACtF;AAED,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CACjC;AAED,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,YAAY,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAClQ;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;CAAE,CAAA;AAExG,eAAO,MAAM,YAAY,gCAAgC,CAAA;AAEzD,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4C9K"}
@@ -0,0 +1,63 @@
1
+ // Publishing a plugin to an npm registry (spec 044 lifecycle.md, "Publish: the methods", npm; build step 6): the public npm registry or a private
2
+ // one (GitHub Packages, Verdaccio, a company registry). The same guards as sharing to git, then the same deterministic tarball, published with
3
+ // the user's own npm login. A pull of `npm:<name>` checks the registry's own integrity against what was published.
4
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { syncServiceContract } from './contract.js';
8
+ import { parsePluginName } from './name.js';
9
+ import { bundleProblem, packFolder } from './pack.js';
10
+ import { PLUGIN_LABELS, secretsIn } from './share.js';
11
+ export const NPM_REGISTRY = 'https://registry.npmjs.org/';
12
+ export async function publishToNpm(folder, options, ports) {
13
+ const refuse = (reason, secrets) => ({ status: 'refused', reason, ...(secrets === undefined ? {} : { secrets }) });
14
+ const registry = options.registry ?? NPM_REGISTRY;
15
+ const before = JSON.parse(readFileSync(join(folder, 'package.json'), 'utf8'));
16
+ const name = parsePluginName(String(before['name']));
17
+ const acryl = before['acryl'];
18
+ const visibility = options.visibility ?? (acryl?.visibility === 'public' ? 'public' : 'private');
19
+ if (visibility === 'public' && typeof before['license'] !== 'string')
20
+ return refuse('a public plugin needs a "license" in its package.json (for example "MIT")');
21
+ // An unscoped npm package is always public; a private plugin needs a scope (`@owner/name`) to be published with restricted access.
22
+ if (visibility === 'private' && name.owner === undefined)
23
+ return refuse(`${name.full} is private, and npm can keep only a scoped package (@owner/name) private; rename it with a scope or publish it publicly`);
24
+ if (before['private'] === true)
25
+ return refuse('package.json says "private": true, which npm refuses to publish; remove it (ACRYL keeps visibility in acryl.visibility)');
26
+ const contract = await syncServiceContract(folder);
27
+ if (!contract.ok)
28
+ return refuse(`its service contract cannot be read: ${contract.errors.join('; ')}`);
29
+ const manifest = JSON.parse(readFileSync(join(folder, 'package.json'), 'utf8'));
30
+ const updatedManifest = contract.written ? ['service contract and slots'] : [];
31
+ const keywords = Array.isArray(manifest['keywords']) ? manifest['keywords'].filter((word) => typeof word === 'string') : [];
32
+ const missing = PLUGIN_LABELS.filter(label => !keywords.includes(label));
33
+ if (missing.length > 0) {
34
+ manifest['keywords'] = [...keywords, ...missing];
35
+ writeFileSync(join(folder, 'package.json'), `${JSON.stringify(manifest, null, 2)}\n`);
36
+ updatedManifest.push(`keywords +${missing.join(', +')}`);
37
+ }
38
+ const packed = packFolder(folder);
39
+ const notBundle = bundleProblem(packed);
40
+ if (notBundle !== undefined)
41
+ return refuse(notBundle);
42
+ const secrets = secretsIn(packed);
43
+ if (secrets.length > 0)
44
+ return refuse(`the secret check found ${String(secrets.length)} possible secret(s) in the files that would be published; nothing was sent`, secrets);
45
+ if (ports.npm.hasVersion(packed.name.full, packed.version, registry))
46
+ return refuse(`${packed.name.full}@${packed.version} is already on ${registry}; a version is published once, so give this change a new version (acryl plugin version patch)`);
47
+ const kept = ports.store.list().find(entry => entry.name === packed.name.full && entry.version === packed.version);
48
+ if (kept !== undefined && kept.integrity !== packed.integrity)
49
+ return refuse(`${packed.name.full} ${packed.version} was already saved here with different files; give this change a new version (acryl plugin version patch)`);
50
+ const access = visibility === 'public' ? 'public' : 'restricted';
51
+ const work = mkdtempSync(join(tmpdir(), 'plugin-hub-npm-'));
52
+ try {
53
+ const file = join(work, packed.fileName);
54
+ writeFileSync(file, packed.tarball);
55
+ ports.npm.publish(file, registry, access);
56
+ }
57
+ finally {
58
+ rmSync(work, { recursive: true, force: true });
59
+ }
60
+ ports.store.put(packed.name.full, packed.version, packed.tarball, { from: folder });
61
+ return { status: 'published', name: packed.name.full, version: packed.version, registry, access, integrity: packed.integrity, files: packed.files, updatedManifest };
62
+ }
63
+ //# sourceMappingURL=npm-publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"npm-publish.js","sourceRoot":"","sources":["../src/npm-publish.ts"],"names":[],"mappings":"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"}
package/dist/pack.d.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { type PluginName } from './name.js';
2
+ import { type TarballFile } from './tarball.js';
3
+ export interface PackedPlugin {
4
+ readonly name: PluginName;
5
+ readonly version: string;
6
+ /** The package.json as packed. */
7
+ readonly manifest: Readonly<Record<string, unknown>>;
8
+ /** Paths inside the package, sorted. */
9
+ readonly files: readonly string[];
10
+ readonly tarball: Buffer;
11
+ readonly integrity: string;
12
+ /** `<local name with dots and @ removed>-<version>.tgz`, as `npm pack` names it (`@a/b.c` 1.0.0 is `a-b.c-1.0.0.tgz`). */
13
+ readonly fileName: string;
14
+ }
15
+ /** The files `npm pack` would put in the package, as `/`-separated paths. */
16
+ export declare function packageFiles(dir: string, manifest: Readonly<Record<string, unknown>>): string[];
17
+ export declare class PackError extends Error {
18
+ readonly dir: string;
19
+ readonly reason: string;
20
+ constructor(dir: string, reason: string);
21
+ }
22
+ /** Pack a source folder. Refuses a folder without a valid name and version, which no target could hold. */
23
+ export declare function packFolder(dir: string): PackedPlugin;
24
+ /**
25
+ * Why DeepSeek Harness's installer would refuse this package as "not a bundle", or undefined when it would not: package.json names its
26
+ * Loader patch in `dsh.bundle.patch`, and that file is among the packed ones. Checked before anything is sent, so no target ever holds a
27
+ * plugin that no app can install.
28
+ */
29
+ export declare function bundleProblem(packed: Pick<PackedPlugin, 'manifest' | 'files'>): string | undefined;
30
+ /** The package.json inside a tarball, which every pull reads to know what it got before installing it. */
31
+ export declare function manifestOfTarball(files: readonly TarballFile[]): Record<string, unknown>;
32
+ //# sourceMappingURL=pack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.d.ts","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AAC5D,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,cAAc,CAAA;AAG1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,kCAAkC;IAClC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACpD,wCAAwC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,0HAA0H;IAC1H,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAuCD,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,EAAE,CAY/F;AAiBD,qBAAa,SAAU,SAAQ,KAAK;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAApC,GAAG,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM;CAI1D;AAED,2GAA2G;AAC3G,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAkBpD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,MAAM,GAAG,SAAS,CAQlG;AAED,0GAA0G;AAC1G,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMxF"}
package/dist/pack.js ADDED
@@ -0,0 +1,137 @@
1
+ // Packing a plugin's source folder into its tarball. Which files go in follows npm's rules closely enough that a plugin packs the same way
2
+ // here as with `npm pack`: the package.json `files` list when there is one, otherwise everything not ignored; package.json, README and
3
+ // LICENSE always; never node_modules, .git or the other files npm never packs. The file list is returned too: it is exactly what a share
4
+ // checks for secrets and sends, so nothing leaves the machine that was not checked.
5
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
6
+ import { join, relative, sep } from 'node:path';
7
+ import { parsePluginName } from './name.js';
8
+ import { integrityOf, writeTarball } from './tarball.js';
9
+ import { parseVersion } from './version.js';
10
+ /** Never packed, wherever they are (npm's own list plus ACRYL's staging folder). */
11
+ const NEVER = new Set(['node_modules', '.git', '.svn', '.hg', 'CVS', '.DS_Store', '.npmrc', 'npm-debug.log', '.lock-wscript', '.wafpickle-N', 'config.gypi', 'package-lock.json', '.acryl-staged', '.acryl-plugin-hub']);
12
+ const NEVER_PATTERN = [/^\._/u, /\.swp$/u, /\.orig$/u, /^\.#/u, /\.tgz$/u];
13
+ /** Always packed from the folder's top level, case-insensitively, as npm does. */
14
+ const ALWAYS = [/^package\.json$/iu, /^readme(?:\..*)?$/iu, /^licen[cs]e(?:\..*)?$/iu, /^changelog(?:\..*)?$/iu];
15
+ function never(name) {
16
+ return NEVER.has(name) || NEVER_PATTERN.some(pattern => pattern.test(name));
17
+ }
18
+ /** A gitignore-style pattern as a test on a `/`-separated path: `*` within a level, `**` across levels, a trailing `/` for folders. */
19
+ function patternTest(pattern) {
20
+ const directoryOnly = pattern.endsWith('/');
21
+ const body = pattern.replace(/^\//u, '').replace(/\/$/u, '');
22
+ const anchored = pattern.startsWith('/') || body.includes('/');
23
+ const source = body.split('**').map(part => part.split('*').map(text => text.replace(/[.+?^${}()|[\]\\]/gu, '\\$&')).join('[^/]*')).join('.*');
24
+ const regex = new RegExp(anchored ? `^${source}(?:/.*)?$` : `(?:^|/)${source}(?:/.*)?$`, 'u');
25
+ return (path, isDirectory) => (!directoryOnly || isDirectory || path.includes('/')) && regex.test(path);
26
+ }
27
+ function ignoreRules(dir) {
28
+ const file = existsSync(join(dir, '.npmignore')) ? '.npmignore' : existsSync(join(dir, '.gitignore')) ? '.gitignore' : undefined;
29
+ if (file === undefined)
30
+ return [];
31
+ return readFileSync(join(dir, file), 'utf8').split(/\r?\n/u).map(line => line.trim()).filter(line => line !== '' && !line.startsWith('#') && !line.startsWith('!')).map(patternTest);
32
+ }
33
+ function walk(root, current, out, skip) {
34
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
35
+ if (never(entry.name) || entry.isSymbolicLink())
36
+ continue;
37
+ const full = join(current, entry.name);
38
+ const path = relative(root, full).split(sep).join('/');
39
+ if (skip(path, entry.isDirectory()))
40
+ continue;
41
+ if (entry.isDirectory())
42
+ walk(root, full, out, skip);
43
+ else if (entry.isFile())
44
+ out.push(path);
45
+ }
46
+ }
47
+ /** The files `npm pack` would put in the package, as `/`-separated paths. */
48
+ export function packageFiles(dir, manifest) {
49
+ const all = [];
50
+ const declared = manifest['files'];
51
+ if (Array.isArray(declared)) {
52
+ walk(dir, dir, all, () => false);
53
+ const tests = declared.filter((entry) => typeof entry === 'string').map(entry => patternTest(`/${entry.replace(/^\.\//u, '')}`));
54
+ const main = typeof manifest['main'] === 'string' ? manifest['main'].replace(/^\.\//u, '') : undefined;
55
+ return all.filter(path => tests.some(test => test(path, false)) || path === main || (!path.includes('/') && ALWAYS.some(pattern => pattern.test(path)))).sort();
56
+ }
57
+ const rules = ignoreRules(dir);
58
+ walk(dir, dir, all, (path, isDirectory) => rules.some(rule => rule(path, isDirectory)));
59
+ return all.filter(path => path !== '.npmignore' && path !== '.gitignore').sort();
60
+ }
61
+ /** The files package.json's `bin` names, as package paths. */
62
+ function binTargets(manifest) {
63
+ const bin = manifest['bin'];
64
+ const targets = typeof bin === 'string' ? [bin] : typeof bin === 'object' && bin !== null ? Object.values(bin) : [];
65
+ return new Set(targets.filter((target) => typeof target === 'string').map(target => target.replace(/^\.\//u, '')));
66
+ }
67
+ /**
68
+ * Whether a file starts with `#!`. Executable files are decided from the content and `bin`, never from the file mode: Windows has no
69
+ * executable bit, so the mode would make the same files pack to different bytes on different systems.
70
+ */
71
+ function startsWithShebang(content) {
72
+ return content[0] === 0x23 && content[1] === 0x21;
73
+ }
74
+ export class PackError extends Error {
75
+ dir;
76
+ reason;
77
+ constructor(dir, reason) {
78
+ super(`cannot pack ${dir}: ${reason}`);
79
+ this.dir = dir;
80
+ this.reason = reason;
81
+ this.name = 'PackError';
82
+ }
83
+ }
84
+ /** Pack a source folder. Refuses a folder without a valid name and version, which no target could hold. */
85
+ export function packFolder(dir) {
86
+ let manifest;
87
+ try {
88
+ manifest = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
89
+ }
90
+ catch {
91
+ throw new PackError(dir, 'package.json is missing or not valid JSON');
92
+ }
93
+ if (typeof manifest['name'] !== 'string')
94
+ throw new PackError(dir, 'package.json has no "name"');
95
+ const name = parsePluginName(manifest['name']);
96
+ const version = manifest['version'];
97
+ if (typeof version !== 'string' || parseVersion(version) === undefined)
98
+ throw new PackError(dir, 'package.json has no semantic "version" such as 0.1.0');
99
+ const files = packageFiles(dir, manifest);
100
+ const bins = binTargets(manifest);
101
+ const entries = files.map(path => {
102
+ const content = readFileSync(join(dir, ...path.split('/')));
103
+ return { path, content, executable: bins.has(path) || startsWithShebang(content) };
104
+ });
105
+ const tarball = writeTarball(entries);
106
+ const fileName = `${name.full.replace(/^@/u, '').replace('/', '-')}-${version}.tgz`;
107
+ return { name, version, manifest, files, tarball, integrity: integrityOf(tarball), fileName };
108
+ }
109
+ /**
110
+ * Why DeepSeek Harness's installer would refuse this package as "not a bundle", or undefined when it would not: package.json names its
111
+ * Loader patch in `dsh.bundle.patch`, and that file is among the packed ones. Checked before anything is sent, so no target ever holds a
112
+ * plugin that no app can install.
113
+ */
114
+ export function bundleProblem(packed) {
115
+ const dsh = packed.manifest['dsh'];
116
+ const patch = dsh?.bundle?.patch;
117
+ const example = '"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }';
118
+ if (typeof patch !== 'string')
119
+ return `package.json does not name its Loader patch, so no app can install it; add ${example} and a cordis.patch.yml that inserts its row`;
120
+ const path = patch.replace(/^\.\//u, '');
121
+ if (!packed.files.includes(path))
122
+ return `its Loader patch ${path} is not among the files that would be sent; create it, or add it to package.json "files"`;
123
+ return undefined;
124
+ }
125
+ /** The package.json inside a tarball, which every pull reads to know what it got before installing it. */
126
+ export function manifestOfTarball(files) {
127
+ const file = files.find(entry => entry.path === 'package.json');
128
+ if (file === undefined)
129
+ throw new Error('the tarball has no package.json');
130
+ try {
131
+ return JSON.parse(Buffer.from(file.content).toString('utf8'));
132
+ }
133
+ catch {
134
+ throw new Error('the tarball\'s package.json is not valid JSON');
135
+ }
136
+ }
137
+ //# sourceMappingURL=pack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../src/pack.ts"],"names":[],"mappings":"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"}
package/dist/pull.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { type PluginSource } from './source.js';
2
+ import { PluginStore, type StoredVersion } from './store.js';
3
+ /** A release as a git host reports it: its tag and its attached files. */
4
+ export interface ReleaseInfo {
5
+ readonly tag: string;
6
+ /** `digest` is what the host publishes for the asset (`sha256:<hex>`), when it does. */
7
+ readonly assets: readonly {
8
+ readonly name: string;
9
+ readonly url: string;
10
+ readonly digest?: string;
11
+ }[];
12
+ }
13
+ /** The git host a `github:` source reads releases from (an HTTP adapter for public repositories, the user's `gh` for private ones). */
14
+ export interface ReleasePort {
15
+ /** The release with this tag, or the latest release when `tag` is undefined; undefined when there is none. */
16
+ release(owner: string, repo: string, tag?: string): Promise<ReleaseInfo | undefined>;
17
+ /** A release asset's bytes. */
18
+ download(url: string): Promise<Uint8Array>;
19
+ }
20
+ /** The npm registry: a version's tarball address and the integrity the registry records for it. */
21
+ export interface RegistryPort {
22
+ resolve(name: string, version?: string): Promise<{
23
+ readonly version: string;
24
+ readonly tarball: string;
25
+ readonly integrity?: string;
26
+ } | undefined>;
27
+ download(url: string): Promise<Uint8Array>;
28
+ }
29
+ export interface PullPorts {
30
+ readonly store: PluginStore;
31
+ readonly releases?: ReleasePort;
32
+ readonly registry?: RegistryPort;
33
+ /** Bytes at an https URL, for a `tarball-url` source. */
34
+ readonly download?: (url: string) => Promise<Uint8Array>;
35
+ }
36
+ export interface PullOptions {
37
+ /** The sha512 a catalog lists for this tarball; a different one is refused. */
38
+ readonly expectIntegrity?: string;
39
+ }
40
+ export interface PulledPlugin {
41
+ readonly stored: StoredVersion;
42
+ /** False when the store already held these exact bytes. */
43
+ readonly added: boolean;
44
+ readonly manifest: Readonly<Record<string, unknown>>;
45
+ /** Where the bytes came from: the release asset or registry URL, or the file. */
46
+ readonly fetchedFrom: string;
47
+ }
48
+ export declare class PullError extends Error {
49
+ readonly source: string;
50
+ readonly reason: string;
51
+ constructor(source: string, reason: string);
52
+ }
53
+ /** Pull a plugin into the store. Every check runs before anything is stored; a failed pull leaves the store as it was. */
54
+ export declare function pullPlugin(source: PluginSource, ports: PullPorts, options?: PullOptions): Promise<PulledPlugin>;
55
+ //# sourceMappingURL=pull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../src/pull.ts"],"names":[],"mappings":"AAOA,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA;AACnE,OAAO,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAA;AAI5D,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CACtG;AAED,uIAAuI;AACvI,MAAM,WAAW,WAAW;IAC1B,8GAA8G;IAC9G,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAA;IACpF,+BAA+B;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CAC3C;AAED,mGAAmG;AACnG,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;IACjJ,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAA;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAA;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;IAChC,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CACzD;AAED,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;IACpD,iFAAiF;IACjF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,qBAAa,SAAU,SAAQ,KAAK;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM;gBAAvC,MAAM,EAAE,MAAM,EAAW,MAAM,EAAE,MAAM;CAI7D;AA6DD,0HAA0H;AAC1H,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAmCzH"}