@octonodes/cli 0.1.13 → 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.
@@ -346,10 +346,10 @@ var require_croner = __commonJS({
346
346
  if (t && typeof t == "object" || typeof t == "function") for (let s of E(t)) !M.call(n, s) && s !== e && D(n, s, { get: () => t[s], enumerable: !(r = x(t, s)) || r.enumerable });
347
347
  return n;
348
348
  };
349
- var z2 = (n) => A(D({}, "__esModule", { value: true }), n);
349
+ var z3 = (n) => A(D({}, "__esModule", { value: true }), n);
350
350
  var W = {};
351
351
  U(W, { Cron: () => N, CronDate: () => h, CronPattern: () => g, scheduledJobs: () => C });
352
- module2.exports = z2(W);
352
+ module2.exports = z3(W);
353
353
  function f(n, t, e, r, s, i, a, l) {
354
354
  return f.fromTZ(f.tp(n, t, e, r, s, i, a), l);
355
355
  }
@@ -830,7 +830,7 @@ var require_constants = __commonJS({
830
830
  "packages/schema/dist/constants.js"(exports2) {
831
831
  "use strict";
832
832
  Object.defineProperty(exports2, "__esModule", { value: true });
833
- exports2.CAPABILITY_UNAVAILABLE_REASONS = exports2.CAPABILITY_MODES = exports2.PLATFORM_CAPABILITIES = exports2.SOURCE_CONTROL_REGION_KINDS = exports2.TASK_RELEASE_STATES = exports2.TASK_SPRINT_STATES = exports2.TASK_LINK_TYPES = exports2.TASK_VIEW_SORTS = exports2.TASK_VIEW_GROUPS = exports2.TASK_VIEW_LAYOUTS = exports2.TASK_FIELD_TYPES = exports2.WORK_ITEM_PRIORITIES = exports2.BUILTIN_WORK_ITEM_TYPES = exports2.WORK_ITEM_LEVELS = exports2.TASK_STATUS_CATEGORIES = exports2.TASK_SPACE_TEMPLATES = exports2.COLLABORATION_ACTIONS = exports2.AGENT_TOOL_RISKS = exports2.AGENT_ERROR_CODES = exports2.AGENT_RUN_STATUSES = exports2.WORKSPACE_ACTIONS = exports2.SETTINGS_SECTIONS = exports2.SETTINGS_API_VERSION = exports2.MARKETPLACE_SCOPES = exports2.PLUGIN_DEFINITION_FILENAMES = exports2.PLUGIN_MANIFEST_FILENAME = exports2.COMMUNITY_MEDIA_TYPES = exports2.COMMUNITY_MEDIA_BODY_BYTES = exports2.COMMUNITY_MEDIA_VIDEO_BYTES = exports2.COMMUNITY_MEDIA_IMAGE_BYTES = exports2.PROJECT_JOB_PROTOCOL = exports2.PLUGIN_SCHEMA_VERSION = void 0;
833
+ exports2.PLUGIN_PUBLISH_MAX_BYTES = exports2.PLUGIN_CONFIG_MAX_BYTES = exports2.PLUGIN_PUBLISH_AUDIENCE = exports2.PLUGIN_RELEASE_FILES = exports2.CAPABILITY_UNAVAILABLE_REASONS = exports2.CAPABILITY_MODES = exports2.PLATFORM_CAPABILITIES = exports2.SOURCE_CONTROL_REGION_KINDS = exports2.TASK_RELEASE_STATES = exports2.TASK_SPRINT_STATES = exports2.TASK_LINK_TYPES = exports2.TASK_VIEW_SORTS = exports2.TASK_VIEW_GROUPS = exports2.TASK_VIEW_LAYOUTS = exports2.TASK_FIELD_TYPES = exports2.WORK_ITEM_PRIORITIES = exports2.BUILTIN_WORK_ITEM_TYPES = exports2.WORK_ITEM_LEVELS = exports2.TASK_STATUS_CATEGORIES = exports2.TASK_SPACE_TEMPLATES = exports2.COLLABORATION_ACTIONS = exports2.AGENT_TOOL_RISKS = exports2.AGENT_ERROR_CODES = exports2.AGENT_RUN_STATUSES = exports2.WORKSPACE_ACTIONS = exports2.SETTINGS_SECTIONS = exports2.SETTINGS_API_VERSION = exports2.MARKETPLACE_SCOPES = exports2.PLUGIN_DEFINITION_FILENAMES = exports2.PLUGIN_MANIFEST_FILENAME = exports2.COMMUNITY_MEDIA_TYPES = exports2.COMMUNITY_MEDIA_BODY_BYTES = exports2.COMMUNITY_MEDIA_VIDEO_BYTES = exports2.COMMUNITY_MEDIA_IMAGE_BYTES = exports2.PROJECT_JOB_PROTOCOL = exports2.PLUGIN_SCHEMA_VERSION = void 0;
834
834
  exports2.PLUGIN_SCHEMA_VERSION = "1";
835
835
  exports2.PROJECT_JOB_PROTOCOL = "octonode.project-job.v1";
836
836
  exports2.COMMUNITY_MEDIA_IMAGE_BYTES = 8 * 1024 * 1024;
@@ -1034,6 +1034,10 @@ var require_constants = __commonJS({
1034
1034
  "missing_entitlement",
1035
1035
  "missing_permission"
1036
1036
  ];
1037
+ exports2.PLUGIN_RELEASE_FILES = ["plugin.octonode.json", "plugin.octonode.yml", "plugin.octonode.yaml"];
1038
+ exports2.PLUGIN_PUBLISH_AUDIENCE = "https://plugins.octonodes.com";
1039
+ exports2.PLUGIN_CONFIG_MAX_BYTES = 65536;
1040
+ exports2.PLUGIN_PUBLISH_MAX_BYTES = 32 * 1024 * 1024;
1037
1041
  }
1038
1042
  });
1039
1043
 
@@ -1316,6 +1320,8 @@ var require_plugin = __commonJS({
1316
1320
  }).strict().optional(),
1317
1321
  icon: icons_1.IconName.optional(),
1318
1322
  author: zod_1.z.string().optional(),
1323
+ contributors: zod_1.z.array(zod_1.z.string().trim().min(1).max(240)).max(100).optional(),
1324
+ repository: zod_1.z.string().url().regex(/^https:\/\/github\.com\/[^/]+\/[^/]+\/?$/).optional(),
1319
1325
  homepage: zod_1.z.string().optional(),
1320
1326
  license: zod_1.z.string().optional(),
1321
1327
  /** Distribution tiers this plugin may be discovered/installed from. Private by default. */
@@ -2639,8 +2645,8 @@ var require_planning = __commonJS({
2639
2645
  version: collaboration_1.revisionSchema,
2640
2646
  createdAt: common_1.taskTimestampSchema,
2641
2647
  updatedAt: common_1.taskTimestampSchema
2642
- }).strict().superRefine((release, context) => {
2643
- if (release.state === "released" && release.releasedAt === null) {
2648
+ }).strict().superRefine((release2, context) => {
2649
+ if (release2.state === "released" && release2.releasedAt === null) {
2644
2650
  context.addIssue({
2645
2651
  code: zod_1.z.ZodIssueCode.custom,
2646
2652
  path: ["releasedAt"],
@@ -7719,10 +7725,10 @@ var require_build = __commonJS({
7719
7725
  else out += sub.appendTo();
7720
7726
  break;
7721
7727
  case Regexp2.Op.ALTERNATE: {
7722
- let sep = "";
7728
+ let sep2 = "";
7723
7729
  for (let sub of this.subs) {
7724
- out += sep;
7725
- sep = "|";
7730
+ out += sep2;
7731
+ sep2 = "|";
7726
7732
  out += sub.appendTo();
7727
7733
  }
7728
7734
  break;
@@ -11423,6 +11429,7 @@ var require_plugin_version = __commonJS({
11423
11429
  Object.defineProperty(exports2, "__esModule", { value: true });
11424
11430
  exports2.PluginVersion = void 0;
11425
11431
  exports2.comparePluginVersions = comparePluginVersions;
11432
+ exports2.bumpPluginVersion = bumpPluginVersion;
11426
11433
  var zod_1 = require("zod");
11427
11434
  exports2.PluginVersion = zod_1.z.string().max(200).regex(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*)?$/, "Use a semantic version such as 1.2.3 or 2.0.0-beta.1");
11428
11435
  function comparePluginVersions(left, right) {
@@ -11453,6 +11460,120 @@ var require_plugin_version = __commonJS({
11453
11460
  }
11454
11461
  return ap.length - bp.length;
11455
11462
  }
11463
+ function bumpPluginVersion(version, release2) {
11464
+ exports2.PluginVersion.parse(version);
11465
+ const core = version.split("+")[0];
11466
+ const prerelease = core.includes("-");
11467
+ let [major, minor, patch] = core.split("-")[0].split(".").map(BigInt);
11468
+ if (release2 === "major") {
11469
+ major += !prerelease || minor !== 0n || patch !== 0n ? 1n : 0n;
11470
+ minor = 0n;
11471
+ patch = 0n;
11472
+ } else if (release2 === "minor") {
11473
+ minor += !prerelease || patch !== 0n ? 1n : 0n;
11474
+ patch = 0n;
11475
+ } else
11476
+ patch += prerelease ? 0n : 1n;
11477
+ return exports2.PluginVersion.parse(`${major}.${minor}.${patch}`);
11478
+ }
11479
+ }
11480
+ });
11481
+
11482
+ // packages/schema/dist/plugin/execution.constants.js
11483
+ var require_execution_constants = __commonJS({
11484
+ "packages/schema/dist/plugin/execution.constants.js"(exports2) {
11485
+ "use strict";
11486
+ Object.defineProperty(exports2, "__esModule", { value: true });
11487
+ exports2.PREPARED_RUNTIME_ARCHIVE = exports2.PREPARED_RUNTIME_MAX_FILES = exports2.PREPARED_RUNTIME_MAX_BYTES = exports2.PLUGIN_EXECUTION_TIMEOUT_MS = exports2.PLUGIN_EXECUTION_MAX_BYTES = exports2.PLUGIN_RUNNER_PORT = exports2.PLUGIN_RUNNER_ORIGIN = void 0;
11488
+ exports2.PLUGIN_RUNNER_ORIGIN = "http://plugin-runner.octonode.internal";
11489
+ exports2.PLUGIN_RUNNER_PORT = 4001;
11490
+ exports2.PLUGIN_EXECUTION_MAX_BYTES = 1024 * 1024;
11491
+ exports2.PLUGIN_EXECUTION_TIMEOUT_MS = 5 * 6e4;
11492
+ exports2.PREPARED_RUNTIME_MAX_BYTES = 256 * 1024 * 1024;
11493
+ exports2.PREPARED_RUNTIME_MAX_FILES = 5e4;
11494
+ exports2.PREPARED_RUNTIME_ARCHIVE = "octonode-runtime.tgz";
11495
+ }
11496
+ });
11497
+
11498
+ // packages/schema/dist/plugin/execution.js
11499
+ var require_execution = __commonJS({
11500
+ "packages/schema/dist/plugin/execution.js"(exports2) {
11501
+ "use strict";
11502
+ Object.defineProperty(exports2, "__esModule", { value: true });
11503
+ exports2.AuthorizedPluginRelease = exports2.PluginExecutionRequest = exports2.PluginExecutionReference = exports2.PreparedPluginRuntime = void 0;
11504
+ var zod_1 = require("zod");
11505
+ var ipc_envelope_1 = require_ipc_envelope();
11506
+ var plugin_1 = require_plugin();
11507
+ var execution_constants_1 = require_execution_constants();
11508
+ exports2.PreparedPluginRuntime = zod_1.z.object({
11509
+ format: zod_1.z.literal(1),
11510
+ nodeMajor: zod_1.z.literal(24),
11511
+ platform: zod_1.z.enum(["portable", "linux", "darwin"]),
11512
+ arch: zod_1.z.string().max(32).optional(),
11513
+ libc: zod_1.z.string().max(64).optional(),
11514
+ dependencies: zod_1.z.literal(execution_constants_1.PREPARED_RUNTIME_ARCHIVE).optional()
11515
+ }).strict();
11516
+ exports2.PluginExecutionReference = zod_1.z.object({
11517
+ installId: zod_1.z.string().uuid(),
11518
+ sha256: zod_1.z.string().regex(/^sha256:[a-f0-9]{64}$/),
11519
+ nodeId: zod_1.z.string().regex(/^[a-z0-9][a-z0-9_-]*$/i)
11520
+ }).strict();
11521
+ exports2.PluginExecutionRequest = zod_1.z.object({
11522
+ plugin: exports2.PluginExecutionReference,
11523
+ request: ipc_envelope_1.InvokeRequest,
11524
+ environment: zod_1.z.record(zod_1.z.string().regex(/^[a-z_][a-z0-9_]*$/i), zod_1.z.string().max(65536).refine((value) => !value.includes("\0"))).default({}),
11525
+ timeoutMs: zod_1.z.number().int().positive().max(execution_constants_1.PLUGIN_EXECUTION_TIMEOUT_MS).default(execution_constants_1.PLUGIN_EXECUTION_TIMEOUT_MS)
11526
+ }).strict();
11527
+ exports2.AuthorizedPluginRelease = zod_1.z.object({
11528
+ manifest: plugin_1.PluginManifest,
11529
+ archiveSha256: zod_1.z.string().regex(/^[a-f0-9]{64}$/)
11530
+ });
11531
+ }
11532
+ });
11533
+
11534
+ // packages/schema/dist/plugin-publishing.js
11535
+ var require_plugin_publishing = __commonJS({
11536
+ "packages/schema/dist/plugin-publishing.js"(exports2) {
11537
+ "use strict";
11538
+ Object.defineProperty(exports2, "__esModule", { value: true });
11539
+ exports2.PluginPublisherConnections = exports2.PluginPublisherConnection = exports2.PluginPublisherPreview = exports2.PluginPublisherConsent = exports2.PluginPublisherInput = exports2.pluginReleaseScope = exports2.PluginReleaseConfig = exports2.PluginConfigPath = void 0;
11540
+ var zod_1 = require("zod");
11541
+ var plugin_version_1 = require_plugin_version();
11542
+ exports2.PluginConfigPath = zod_1.z.string().max(512).refine((path) => /^(?:[a-zA-Z0-9_.-]+\/)*plugin\.octonode\.(json|ya?ml)$/.test(path) && !path.split("/").some((part) => [".", "..", ".git", "node_modules"].includes(part)), "Select a repository-relative plugin.octonode.json or .yml file");
11543
+ exports2.PluginReleaseConfig = zod_1.z.object({
11544
+ apiVersion: zod_1.z.literal("octonode.plugin/v1"),
11545
+ id: zod_1.z.string().max(128).regex(/^[a-z0-9][a-z0-9-]*$/),
11546
+ version: plugin_version_1.PluginVersion,
11547
+ scope: zod_1.z.enum(["user", "team", "organization", "public"]),
11548
+ teamId: zod_1.z.string().min(1).max(128).optional(),
11549
+ orgId: zod_1.z.string().min(1).max(128).optional(),
11550
+ contributors: zod_1.z.array(zod_1.z.string().trim().min(1).max(240)).max(100).optional(),
11551
+ workflow: zod_1.z.string().regex(/^\.github\/workflows\/[a-zA-Z0-9_-]+\.ya?ml$/).default(".github/workflows/octonode-publish.yml")
11552
+ }).strict().superRefine((config, ctx) => {
11553
+ if (config.scope === "team" !== Boolean(config.teamId))
11554
+ ctx.addIssue({ code: "custom", path: ["teamId"], message: "teamId is required only for team scope" });
11555
+ if (config.scope === "organization" !== Boolean(config.orgId))
11556
+ ctx.addIssue({ code: "custom", path: ["orgId"], message: "orgId is required only for organization scope" });
11557
+ });
11558
+ var pluginReleaseScope = (config) => config.scope === "team" ? "group" : config.scope === "organization" ? "org" : config.scope;
11559
+ exports2.pluginReleaseScope = pluginReleaseScope;
11560
+ exports2.PluginPublisherInput = zod_1.z.object({ repositoryId: zod_1.z.number().int().positive(), configPath: exports2.PluginConfigPath }).strict();
11561
+ exports2.PluginPublisherConsent = exports2.PluginPublisherInput.extend({ sha256: zod_1.z.string().regex(/^[a-f0-9]{64}$/) });
11562
+ exports2.PluginPublisherPreview = zod_1.z.object({
11563
+ config: exports2.PluginReleaseConfig,
11564
+ sha256: zod_1.z.string(),
11565
+ repository: zod_1.z.string(),
11566
+ branch: zod_1.z.string()
11567
+ });
11568
+ exports2.PluginPublisherConnection = exports2.PluginPublisherPreview.extend({
11569
+ id: zod_1.z.string().uuid(),
11570
+ repositoryId: zod_1.z.number(),
11571
+ configPath: exports2.PluginConfigPath,
11572
+ lastVersion: zod_1.z.string().nullable(),
11573
+ lastSha: zod_1.z.string().nullable(),
11574
+ lastPublishedAt: zod_1.z.number().nullable()
11575
+ });
11576
+ exports2.PluginPublisherConnections = zod_1.z.object({ items: zod_1.z.array(exports2.PluginPublisherConnection) });
11456
11577
  }
11457
11578
  });
11458
11579
 
@@ -11518,6 +11639,9 @@ var require_dist = __commonJS({
11518
11639
  __exportStar(require_project_lifecycle(), exports2);
11519
11640
  __exportStar(require_project_jobs(), exports2);
11520
11641
  __exportStar(require_plugin_version(), exports2);
11642
+ __exportStar(require_execution(), exports2);
11643
+ __exportStar(require_execution_constants(), exports2);
11644
+ __exportStar(require_plugin_publishing(), exports2);
11521
11645
  }
11522
11646
  });
11523
11647
 
@@ -11845,7 +11969,7 @@ var require_runner = __commonJS({
11845
11969
  let draining = false;
11846
11970
  let ended = false;
11847
11971
  let done;
11848
- const finished = new Promise((resolve5) => done = resolve5);
11972
+ const finished = new Promise((resolve7) => done = resolve7);
11849
11973
  const drain = async () => {
11850
11974
  if (draining)
11851
11975
  return;
@@ -11895,190 +12019,13 @@ var require_runner = __commonJS({
11895
12019
  }
11896
12020
  });
11897
12021
 
11898
- // packages/plugin-runtime/dist/plugin.js
11899
- var require_plugin2 = __commonJS({
11900
- "packages/plugin-runtime/dist/plugin.js"(exports2) {
11901
- "use strict";
11902
- Object.defineProperty(exports2, "__esModule", { value: true });
11903
- exports2.definePlugin = definePlugin;
11904
- exports2.startPlugin = startPlugin;
11905
- var schema_1 = require_dist();
11906
- var define_node_1 = require_define_node();
11907
- var runner_1 = require_runner();
11908
- function definePlugin(definition, handlers) {
11909
- const options = definition;
11910
- if (!handlers && (!options || !Array.isArray(options.nodes)))
11911
- throw new Error("plugin needs a nodes array");
11912
- if (!handlers) {
11913
- for (const node of options.nodes) {
11914
- if (typeof node.run !== "function")
11915
- throw new Error(`plugin node "${node.id}" needs a handler`);
11916
- if (node.kind && node.kind !== "function")
11917
- throw new Error("plugin nodes must be functions");
11918
- }
11919
- }
11920
- const manifest2 = schema_1.PluginManifest.parse(handlers ? definition : {
11921
- ...options,
11922
- nodes: options.nodes.map(({ run: _run, ...node }) => ({
11923
- ...node,
11924
- command: `node dist/index.js ${node.id}`,
11925
- language: "typescript"
11926
- }))
11927
- });
11928
- const nodeHandlers = handlers ?? Object.fromEntries(options.nodes.map((node) => [node.id, node.run]));
11929
- for (const node of manifest2.nodes) {
11930
- for (const field of ["inputs", "outputs", "defaults"]) {
11931
- const error = node[field] === void 0 ? void 0 : (0, runner_1.jsonSafetyError)(node[field]);
11932
- if (error)
11933
- throw new Error(`plugin node "${node.id}" ${field} must be JSON-serializable: ${error}`);
11934
- }
11935
- }
11936
- for (const id of Object.keys(nodeHandlers)) {
11937
- if (!manifest2.nodes.some((node) => node.id === id))
11938
- throw new Error(`handler "${id}" has no plugin node`);
11939
- }
11940
- return {
11941
- manifest: manifest2,
11942
- ...!handlers && options.assets ? { assets: options.assets } : {},
11943
- nodes: Object.fromEntries(manifest2.nodes.map((node) => {
11944
- if (!Object.hasOwn(nodeHandlers, node.id))
11945
- throw new Error(`plugin node "${node.id}" needs a handler`);
11946
- const fields = (node.connections ?? []).flatMap((id) => Object.entries(manifest2.connections?.[id]?.fields ?? {}));
11947
- return [
11948
- node.id,
11949
- (0, define_node_1.defineNode)({
11950
- ...node,
11951
- run: (inputs, context) => {
11952
- const missing = fields.filter(([name, field]) => field.required && !process.env[name]).map(([name]) => name);
11953
- if (missing.length)
11954
- throw new runner_1.NodeError(`Missing connection credentials: ${missing.join(", ")}`);
11955
- return nodeHandlers[node.id](inputs, context);
11956
- }
11957
- })
11958
- ];
11959
- }))
11960
- };
11961
- }
11962
- function startPlugin(plugin, nodeId = process.argv[2]) {
11963
- const id = nodeId ?? (plugin.manifest.nodes.length === 1 ? plugin.manifest.nodes[0].id : void 0);
11964
- if (!id || !Object.hasOwn(plugin.nodes, id))
11965
- throw new Error(`Choose a plugin node: ${Object.keys(plugin.nodes).join(", ")}`);
11966
- (0, runner_1.start)(plugin.nodes[id]);
11967
- }
11968
- }
11969
- });
11970
-
11971
- // packages/plugin-runtime/dist/plugin-file.js
11972
- var require_plugin_file = __commonJS({
11973
- "packages/plugin-runtime/dist/plugin-file.js"(exports2) {
11974
- "use strict";
11975
- Object.defineProperty(exports2, "__esModule", { value: true });
11976
- exports2.pluginDefinitionPath = pluginDefinitionPath;
11977
- exports2.loadPluginDefinition = loadPluginDefinition3;
11978
- var node_fs_1 = require("node:fs");
11979
- var node_path_1 = require("node:path");
11980
- var yaml_1 = require("yaml");
11981
- var schema_1 = require_dist();
11982
- function pluginDefinitionPath(dir) {
11983
- const files = schema_1.PLUGIN_DEFINITION_FILENAMES.map((name) => (0, node_path_1.join)(dir, name)).filter(node_fs_1.existsSync);
11984
- if (files.length !== 1)
11985
- throw new Error(files.length ? `Multiple plugin definitions in ${dir}; keep exactly one` : `No plugin definition in ${dir}; create octonode.yml or octonode.json`);
11986
- const file = files[0];
11987
- if ((0, node_path_1.dirname)((0, node_fs_1.realpathSync)(file)) !== (0, node_fs_1.realpathSync)(dir))
11988
- throw new Error("Plugin definition must stay inside its folder");
11989
- if (!(0, node_fs_1.statSync)(file).isFile() || (0, node_fs_1.statSync)(file).size > 1048576)
11990
- throw new Error("Plugin definition must be a file smaller than 1 MiB");
11991
- return file;
11992
- }
11993
- function loadPluginDefinition3(dir) {
11994
- const file = pluginDefinitionPath(dir);
11995
- const source = (0, node_fs_1.readFileSync)(file, "utf8");
11996
- const document = (0, yaml_1.parseDocument)(source, { version: "1.2", uniqueKeys: true });
11997
- (0, yaml_1.visit)(document, {
11998
- Alias() {
11999
- throw new Error("Plugin definitions cannot contain YAML aliases");
12000
- }
12001
- });
12002
- if (document.errors.length || document.warnings.length)
12003
- throw new Error(`${file}: ${[...document.errors, ...document.warnings].map((error) => error.message).join("; ")}`);
12004
- const raw = file.endsWith(".json") ? JSON.parse(source) : document.toJS({ maxAliasCount: 0 });
12005
- if (file.endsWith(schema_1.PLUGIN_MANIFEST_FILENAME))
12006
- return schema_1.PluginManifest.parse(raw);
12007
- const settings = schema_1.OctonodeSettingsDocument.parse(raw);
12008
- if (!settings.plugin)
12009
- throw new Error(`${file} has no plugin section`);
12010
- return settings.plugin;
12011
- }
12012
- }
12013
- });
12014
-
12015
- // packages/plugin-runtime/dist/index.js
12016
- var require_dist2 = __commonJS({
12017
- "packages/plugin-runtime/dist/index.js"(exports2) {
12018
- "use strict";
12019
- Object.defineProperty(exports2, "__esModule", { value: true });
12020
- exports2.PluginConnection = exports2.PluginNode = exports2.PluginManifest = exports2.pluginDefinitionPath = exports2.loadPluginDefinition = exports2.startPlugin = exports2.definePlugin = exports2.validate = exports2.NodeError = exports2.start = exports2.runNode = exports2.defineConst = exports2.defineClass = exports2.defineService = exports2.defineNode = void 0;
12021
- var define_node_1 = require_define_node();
12022
- Object.defineProperty(exports2, "defineNode", { enumerable: true, get: function() {
12023
- return define_node_1.defineNode;
12024
- } });
12025
- Object.defineProperty(exports2, "defineService", { enumerable: true, get: function() {
12026
- return define_node_1.defineService;
12027
- } });
12028
- Object.defineProperty(exports2, "defineClass", { enumerable: true, get: function() {
12029
- return define_node_1.defineClass;
12030
- } });
12031
- Object.defineProperty(exports2, "defineConst", { enumerable: true, get: function() {
12032
- return define_node_1.defineConst;
12033
- } });
12034
- var runner_1 = require_runner();
12035
- Object.defineProperty(exports2, "runNode", { enumerable: true, get: function() {
12036
- return runner_1.runNode;
12037
- } });
12038
- Object.defineProperty(exports2, "start", { enumerable: true, get: function() {
12039
- return runner_1.start;
12040
- } });
12041
- Object.defineProperty(exports2, "NodeError", { enumerable: true, get: function() {
12042
- return runner_1.NodeError;
12043
- } });
12044
- var json_schema_1 = require_json_schema2();
12045
- Object.defineProperty(exports2, "validate", { enumerable: true, get: function() {
12046
- return json_schema_1.validate;
12047
- } });
12048
- var plugin_1 = require_plugin2();
12049
- Object.defineProperty(exports2, "definePlugin", { enumerable: true, get: function() {
12050
- return plugin_1.definePlugin;
12051
- } });
12052
- Object.defineProperty(exports2, "startPlugin", { enumerable: true, get: function() {
12053
- return plugin_1.startPlugin;
12054
- } });
12055
- var plugin_file_1 = require_plugin_file();
12056
- Object.defineProperty(exports2, "loadPluginDefinition", { enumerable: true, get: function() {
12057
- return plugin_file_1.loadPluginDefinition;
12058
- } });
12059
- Object.defineProperty(exports2, "pluginDefinitionPath", { enumerable: true, get: function() {
12060
- return plugin_file_1.pluginDefinitionPath;
12061
- } });
12062
- var schema_1 = require_dist();
12063
- Object.defineProperty(exports2, "PluginManifest", { enumerable: true, get: function() {
12064
- return schema_1.PluginManifest;
12065
- } });
12066
- Object.defineProperty(exports2, "PluginNode", { enumerable: true, get: function() {
12067
- return schema_1.PluginNode;
12068
- } });
12069
- Object.defineProperty(exports2, "PluginConnection", { enumerable: true, get: function() {
12070
- return schema_1.PluginConnection;
12071
- } });
12072
- }
12073
- });
12074
-
12075
12022
  // packages/common/dist/constants.js
12076
12023
  var require_constants2 = __commonJS({
12077
12024
  "packages/common/dist/constants.js"(exports2) {
12078
12025
  "use strict";
12079
12026
  Object.defineProperty(exports2, "__esModule", { value: true });
12080
- exports2.UI_DIR = exports2.PYTHONPATH = exports2.PORT = exports2.PATH = exports2.OPENROUTER_API_KEY = exports2.CLOUDFLARE_ACCOUNT_ID = exports2.CLOUDFLARE_API_KEY = exports2.OPENAI_API_KEY = exports2.OTEL_SERVICE_NAME = exports2.OTEL_SDK_DISABLED = exports2.OTEL_EXPORTER_OTLP_ENDPOINT = exports2.OCTONODE_WORKSPACE = exports2.OCTONODE_VISUAL_PORT = exports2.OCTONODE_USER_EMAIL = exports2.OCTONODE_USER = exports2.OCTONODE_STUDIO_URL = exports2.OCTONODE_STORE_DIR = exports2.OCTONODE_SOURCE_WATCH_CONCURRENCY = exports2.OCTONODE_SHUTDOWN_TIMEOUT_MS = exports2.OCTONODE_SERVER_PROFILE = exports2.OCTONODE_RUNTIME = exports2.OCTONODE_REPOSITORY_REGISTRY_V2 = exports2.OCTONODE_RUN_RETENTION_COUNT = exports2.OCTONODE_RUN_RETENTION_BYTES = exports2.OCTONODE_RUN_RETENTION_AGE_MS = exports2.OCTONODE_STATE_NAMESPACE = exports2.OCTONODE_RUN_QUEUE = exports2.OCTONODE_RUN_CONCURRENCY = exports2.OCTONODE_MARKETPLACE_URL = exports2.OCTONODE_MARKETPLACE_TOKEN = exports2.OCTONODE_PACKAGE_CACHE = exports2.OCTONODE_PROJECT = exports2.OCTONODE_CWD = exports2.OCTONODE_COMMUNITY_ROLLOUT = exports2.OCTONODE_CHAT_SERVICE_URL = exports2.OCTONODE_CHAT_SERVICE_TOKEN = exports2.OCTONODE_CHAT_SERVICE_AVAILABLE = exports2.OCTONODE_CHAT_MODEL = exports2.OCTONODE_CLOUD_INTERNAL_TOKEN = exports2.OCTONODE_CORS_ORIGINS = exports2.OCTONODE_API_TOKEN = exports2.OCTONODE_API_URL = exports2.OCTONODE_AUTH_TOKEN = exports2.OCTONODE_AUTH_PRINCIPAL = exports2.HOST = exports2.CONFIG_PATH = exports2.setEnvironmentVariable = exports2.childProcessEnvironment = exports2.workflowEnvironment = exports2.PROCESS_ENV = void 0;
12081
- exports2.VISUAL_UPDATE = exports2.VISUAL_STORY = exports2.VISUAL_REVIEW_DIR = exports2.VISUAL_FULL = exports2.VISUAL_BASE = exports2.USERNAME = exports2.USER = void 0;
12027
+ exports2.PYTHONPATH = exports2.PORT = exports2.PATH = exports2.OPENROUTER_API_KEY = exports2.CLOUDFLARE_ACCOUNT_ID = exports2.CLOUDFLARE_API_KEY = exports2.OPENAI_API_KEY = exports2.OTEL_SERVICE_NAME = exports2.OTEL_SDK_DISABLED = exports2.OTEL_EXPORTER_OTLP_ENDPOINT = exports2.OCTONODE_WORKSPACE = exports2.OCTONODE_VISUAL_PORT = exports2.OCTONODE_USER_EMAIL = exports2.OCTONODE_USER = exports2.OCTONODE_STUDIO_URL = exports2.OCTONODE_STORE_DIR = exports2.OCTONODE_SOURCE_WATCH_CONCURRENCY = exports2.OCTONODE_SHUTDOWN_TIMEOUT_MS = exports2.OCTONODE_SERVER_PROFILE = exports2.OCTONODE_RUNTIME = exports2.OCTONODE_REPOSITORY_REGISTRY_V2 = exports2.OCTONODE_RUN_RETENTION_COUNT = exports2.OCTONODE_RUN_RETENTION_BYTES = exports2.OCTONODE_RUN_RETENTION_AGE_MS = exports2.OCTONODE_STATE_NAMESPACE = exports2.OCTONODE_RUN_QUEUE = exports2.OCTONODE_RUN_CONCURRENCY = exports2.DEFAULT_MARKETPLACE_URL = exports2.OCTONODE_MARKETPLACE_URL = exports2.OCTONODE_MARKETPLACE_TOKEN = exports2.OCTONODE_PACKAGE_CACHE = exports2.OCTONODE_PROJECT = exports2.OCTONODE_CWD = exports2.OCTONODE_COMMUNITY_ROLLOUT = exports2.OCTONODE_CHAT_SERVICE_URL = exports2.OCTONODE_CHAT_SERVICE_TOKEN = exports2.OCTONODE_CHAT_SERVICE_AVAILABLE = exports2.OCTONODE_CHAT_MODEL = exports2.OCTONODE_CLOUD_INTERNAL_TOKEN = exports2.OCTONODE_CORS_ORIGINS = exports2.OCTONODE_API_TOKEN = exports2.OCTONODE_API_URL = exports2.OCTONODE_AUTH_TOKEN = exports2.OCTONODE_AUTH_PRINCIPAL = exports2.HOST = exports2.CONFIG_PATH = exports2.setEnvironmentVariable = exports2.childProcessEnvironment = exports2.workflowEnvironment = exports2.PROCESS_ENV = void 0;
12028
+ exports2.VISUAL_UPDATE = exports2.VISUAL_STORY = exports2.VISUAL_REVIEW_DIR = exports2.VISUAL_FULL = exports2.VISUAL_BASE = exports2.USERNAME = exports2.USER = exports2.UI_DIR = void 0;
12082
12029
  exports2.PROCESS_ENV = typeof process === "undefined" ? {} : process.env;
12083
12030
  var workflowEnvironment = (variables = {}) => {
12084
12031
  const environment = Object.fromEntries(Object.entries(variables).filter((entry) => typeof entry[1] === "string"));
@@ -12089,8 +12036,8 @@ var require_constants2 = __commonJS({
12089
12036
  return environment;
12090
12037
  };
12091
12038
  exports2.workflowEnvironment = workflowEnvironment;
12092
- var childProcessEnvironment3 = (overrides = {}) => (0, exports2.workflowEnvironment)({ ...exports2.PROCESS_ENV, ...overrides });
12093
- exports2.childProcessEnvironment = childProcessEnvironment3;
12039
+ var childProcessEnvironment4 = (overrides = {}) => (0, exports2.workflowEnvironment)({ ...exports2.PROCESS_ENV, ...overrides });
12040
+ exports2.childProcessEnvironment = childProcessEnvironment4;
12094
12041
  var setEnvironmentVariable = (name, value) => {
12095
12042
  if (value === void 0)
12096
12043
  delete exports2.PROCESS_ENV[name];
@@ -12132,8 +12079,9 @@ var require_constants2 = __commonJS({
12132
12079
  exports2.OCTONODE_PACKAGE_CACHE = OCTONODE_PACKAGE_CACHE2;
12133
12080
  var OCTONODE_MARKETPLACE_TOKEN2 = () => exports2.PROCESS_ENV.OCTONODE_MARKETPLACE_TOKEN ?? exports2.PROCESS_ENV.VITE_OCTONODE_MARKETPLACE_TOKEN ?? exports2.PROCESS_ENV.VITE_PUBLIC_OCTONODE_MARKETPLACE_TOKEN;
12134
12081
  exports2.OCTONODE_MARKETPLACE_TOKEN = OCTONODE_MARKETPLACE_TOKEN2;
12135
- var OCTONODE_MARKETPLACE_URL2 = () => exports2.PROCESS_ENV.OCTONODE_MARKETPLACE_URL ?? exports2.PROCESS_ENV.VITE_OCTONODE_MARKETPLACE_URL ?? exports2.PROCESS_ENV.VITE_PUBLIC_OCTONODE_MARKETPLACE_URL;
12136
- exports2.OCTONODE_MARKETPLACE_URL = OCTONODE_MARKETPLACE_URL2;
12082
+ var OCTONODE_MARKETPLACE_URL3 = () => exports2.PROCESS_ENV.OCTONODE_MARKETPLACE_URL ?? exports2.PROCESS_ENV.VITE_OCTONODE_MARKETPLACE_URL ?? exports2.PROCESS_ENV.VITE_PUBLIC_OCTONODE_MARKETPLACE_URL;
12083
+ exports2.OCTONODE_MARKETPLACE_URL = OCTONODE_MARKETPLACE_URL3;
12084
+ exports2.DEFAULT_MARKETPLACE_URL = "https://plugins.octonodes.com";
12137
12085
  var OCTONODE_RUN_CONCURRENCY = () => exports2.PROCESS_ENV.OCTONODE_RUN_CONCURRENCY;
12138
12086
  exports2.OCTONODE_RUN_CONCURRENCY = OCTONODE_RUN_CONCURRENCY;
12139
12087
  var OCTONODE_RUN_QUEUE = () => exports2.PROCESS_ENV.OCTONODE_RUN_QUEUE;
@@ -12391,38 +12339,248 @@ var require_graph_layout = __commonJS({
12391
12339
  }
12392
12340
  });
12393
12341
 
12394
- // packages/common/dist/index.js
12342
+ // packages/common/dist/plugin-nodes.js
12343
+ var require_plugin_nodes = __commonJS({
12344
+ "packages/common/dist/plugin-nodes.js"(exports2) {
12345
+ "use strict";
12346
+ Object.defineProperty(exports2, "__esModule", { value: true });
12347
+ exports2.installedPluginNodesPage = installedPluginNodesPage;
12348
+ function isCatalogPlugin(value) {
12349
+ if (!value || typeof value !== "object")
12350
+ return false;
12351
+ const plugin = value;
12352
+ return typeof plugin.id === "string" && typeof plugin.name === "string" && typeof plugin.version === "string" && Array.isArray(plugin.nodes) && plugin.nodes.every((node) => {
12353
+ if (!node || typeof node !== "object")
12354
+ return false;
12355
+ const item = node;
12356
+ return typeof item.id === "string" && (item.icon === void 0 || typeof item.icon === "string") && (item.description === void 0 || typeof item.description === "string");
12357
+ });
12358
+ }
12359
+ function installedPluginNodesPage(inventory, query) {
12360
+ if (!inventory.every(isCatalogPlugin))
12361
+ throw new Error("Plugin node inventory is incomplete");
12362
+ const q = query.q?.trim().toLowerCase();
12363
+ const nodes = inventory.flatMap((plugin) => plugin.nodes.filter((node) => !q || [node.id, node.description ?? "", plugin.id, plugin.name].join(" ").toLowerCase().includes(q)).map((node) => ({ ...node, pluginId: plugin.id, pluginName: plugin.name, pluginVersion: plugin.version }))).sort((a, b) => a.pluginName.localeCompare(b.pluginName) || a.id.localeCompare(b.id));
12364
+ const offset = Math.min(query.offset, nodes.length);
12365
+ return { items: nodes.slice(offset, offset + query.limit), total: nodes.length, offset, limit: query.limit };
12366
+ }
12367
+ }
12368
+ });
12369
+
12370
+ // packages/common/dist/index.js
12371
+ var require_dist2 = __commonJS({
12372
+ "packages/common/dist/index.js"(exports2) {
12373
+ "use strict";
12374
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
12375
+ if (k2 === void 0) k2 = k;
12376
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12377
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12378
+ desc = { enumerable: true, get: function() {
12379
+ return m[k];
12380
+ } };
12381
+ }
12382
+ Object.defineProperty(o, k2, desc);
12383
+ }) : (function(o, m, k, k2) {
12384
+ if (k2 === void 0) k2 = k;
12385
+ o[k2] = m[k];
12386
+ }));
12387
+ var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
12388
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
12389
+ };
12390
+ Object.defineProperty(exports2, "__esModule", { value: true });
12391
+ exports2.installedPluginNodesPage = exports2.layeredLayout = exports2.packageCacheEnvironment = void 0;
12392
+ __exportStar(require_constants2(), exports2);
12393
+ __exportStar(require_legal_constants(), exports2);
12394
+ var package_cache_1 = require_package_cache();
12395
+ Object.defineProperty(exports2, "packageCacheEnvironment", { enumerable: true, get: function() {
12396
+ return package_cache_1.packageCacheEnvironment;
12397
+ } });
12398
+ __exportStar(require_api_exposure(), exports2);
12399
+ var graph_layout_1 = require_graph_layout();
12400
+ Object.defineProperty(exports2, "layeredLayout", { enumerable: true, get: function() {
12401
+ return graph_layout_1.layeredLayout;
12402
+ } });
12403
+ var plugin_nodes_1 = require_plugin_nodes();
12404
+ Object.defineProperty(exports2, "installedPluginNodesPage", { enumerable: true, get: function() {
12405
+ return plugin_nodes_1.installedPluginNodesPage;
12406
+ } });
12407
+ }
12408
+ });
12409
+
12410
+ // packages/plugin-runtime/dist/plugin.js
12411
+ var require_plugin2 = __commonJS({
12412
+ "packages/plugin-runtime/dist/plugin.js"(exports2) {
12413
+ "use strict";
12414
+ Object.defineProperty(exports2, "__esModule", { value: true });
12415
+ exports2.definePlugin = definePlugin;
12416
+ exports2.startPlugin = startPlugin;
12417
+ var common_1 = require_dist2();
12418
+ var schema_1 = require_dist();
12419
+ var define_node_1 = require_define_node();
12420
+ var runner_1 = require_runner();
12421
+ function definePlugin(definition, handlers) {
12422
+ const options = definition;
12423
+ if (!handlers && (!options || !Array.isArray(options.nodes)))
12424
+ throw new Error("plugin needs a nodes array");
12425
+ if (!handlers) {
12426
+ for (const node of options.nodes) {
12427
+ if (typeof node.run !== "function")
12428
+ throw new Error(`plugin node "${node.id}" needs a handler`);
12429
+ if (node.kind && node.kind !== "function")
12430
+ throw new Error("plugin nodes must be functions");
12431
+ }
12432
+ }
12433
+ const manifest2 = schema_1.PluginManifest.parse(handlers ? definition : {
12434
+ ...options,
12435
+ nodes: options.nodes.map(({ run: _run, ...node }) => ({
12436
+ ...node,
12437
+ command: `node dist/index.js ${node.id}`,
12438
+ language: "typescript"
12439
+ }))
12440
+ });
12441
+ const nodeHandlers = handlers ?? Object.fromEntries(options.nodes.map((node) => [node.id, node.run]));
12442
+ for (const node of manifest2.nodes) {
12443
+ for (const field of ["inputs", "outputs", "defaults"]) {
12444
+ const error = node[field] === void 0 ? void 0 : (0, runner_1.jsonSafetyError)(node[field]);
12445
+ if (error)
12446
+ throw new Error(`plugin node "${node.id}" ${field} must be JSON-serializable: ${error}`);
12447
+ }
12448
+ }
12449
+ for (const id of Object.keys(nodeHandlers)) {
12450
+ if (!manifest2.nodes.some((node) => node.id === id))
12451
+ throw new Error(`handler "${id}" has no plugin node`);
12452
+ }
12453
+ return {
12454
+ manifest: manifest2,
12455
+ ...!handlers && options.assets ? { assets: options.assets } : {},
12456
+ nodes: Object.fromEntries(manifest2.nodes.map((node) => {
12457
+ if (!Object.hasOwn(nodeHandlers, node.id))
12458
+ throw new Error(`plugin node "${node.id}" needs a handler`);
12459
+ const fields = (node.connections ?? []).flatMap((id) => Object.entries(manifest2.connections?.[id]?.fields ?? {}));
12460
+ return [
12461
+ node.id,
12462
+ (0, define_node_1.defineNode)({
12463
+ ...node,
12464
+ run: (inputs, context) => {
12465
+ const missing = fields.filter(([name, field]) => field.required && !common_1.PROCESS_ENV[name]).map(([name]) => name);
12466
+ if (missing.length)
12467
+ throw new runner_1.NodeError(`Missing connection credentials: ${missing.join(", ")}`);
12468
+ return nodeHandlers[node.id](inputs, context);
12469
+ }
12470
+ })
12471
+ ];
12472
+ }))
12473
+ };
12474
+ }
12475
+ function startPlugin(plugin, nodeId = process.argv[2]) {
12476
+ const id = nodeId ?? (plugin.manifest.nodes.length === 1 ? plugin.manifest.nodes[0].id : void 0);
12477
+ if (!id || !Object.hasOwn(plugin.nodes, id))
12478
+ throw new Error(`Choose a plugin node: ${Object.keys(plugin.nodes).join(", ")}`);
12479
+ (0, runner_1.start)(plugin.nodes[id]);
12480
+ }
12481
+ }
12482
+ });
12483
+
12484
+ // packages/plugin-runtime/dist/plugin-file.js
12485
+ var require_plugin_file = __commonJS({
12486
+ "packages/plugin-runtime/dist/plugin-file.js"(exports2) {
12487
+ "use strict";
12488
+ Object.defineProperty(exports2, "__esModule", { value: true });
12489
+ exports2.pluginDefinitionPath = pluginDefinitionPath2;
12490
+ exports2.loadPluginDefinition = loadPluginDefinition3;
12491
+ var node_fs_1 = require("node:fs");
12492
+ var node_path_1 = require("node:path");
12493
+ var yaml_1 = require("yaml");
12494
+ var schema_1 = require_dist();
12495
+ function pluginDefinitionPath2(dir) {
12496
+ const files = schema_1.PLUGIN_DEFINITION_FILENAMES.map((name) => (0, node_path_1.join)(dir, name)).filter(node_fs_1.existsSync);
12497
+ if (files.length !== 1)
12498
+ throw new Error(files.length ? `Multiple plugin definitions in ${dir}; keep exactly one` : `No plugin definition in ${dir}; create octonode.yml or octonode.json`);
12499
+ const file = files[0];
12500
+ if ((0, node_path_1.dirname)((0, node_fs_1.realpathSync)(file)) !== (0, node_fs_1.realpathSync)(dir))
12501
+ throw new Error("Plugin definition must stay inside its folder");
12502
+ if (!(0, node_fs_1.statSync)(file).isFile() || (0, node_fs_1.statSync)(file).size > 1048576)
12503
+ throw new Error("Plugin definition must be a file smaller than 1 MiB");
12504
+ return file;
12505
+ }
12506
+ function loadPluginDefinition3(dir) {
12507
+ const file = pluginDefinitionPath2(dir);
12508
+ const source = (0, node_fs_1.readFileSync)(file, "utf8");
12509
+ const document = (0, yaml_1.parseDocument)(source, { version: "1.2", uniqueKeys: true });
12510
+ (0, yaml_1.visit)(document, {
12511
+ Alias() {
12512
+ throw new Error("Plugin definitions cannot contain YAML aliases");
12513
+ }
12514
+ });
12515
+ if (document.errors.length || document.warnings.length)
12516
+ throw new Error(`${file}: ${[...document.errors, ...document.warnings].map((error) => error.message).join("; ")}`);
12517
+ const raw = file.endsWith(".json") ? JSON.parse(source) : document.toJS({ maxAliasCount: 0 });
12518
+ if (file.endsWith(schema_1.PLUGIN_MANIFEST_FILENAME))
12519
+ return schema_1.PluginManifest.parse(raw);
12520
+ const settings = schema_1.OctonodeSettingsDocument.parse(raw);
12521
+ if (!settings.plugin)
12522
+ throw new Error(`${file} has no plugin section`);
12523
+ return settings.plugin;
12524
+ }
12525
+ }
12526
+ });
12527
+
12528
+ // packages/plugin-runtime/dist/index.js
12395
12529
  var require_dist3 = __commonJS({
12396
- "packages/common/dist/index.js"(exports2) {
12530
+ "packages/plugin-runtime/dist/index.js"(exports2) {
12397
12531
  "use strict";
12398
- var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
12399
- if (k2 === void 0) k2 = k;
12400
- var desc = Object.getOwnPropertyDescriptor(m, k);
12401
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12402
- desc = { enumerable: true, get: function() {
12403
- return m[k];
12404
- } };
12405
- }
12406
- Object.defineProperty(o, k2, desc);
12407
- }) : (function(o, m, k, k2) {
12408
- if (k2 === void 0) k2 = k;
12409
- o[k2] = m[k];
12410
- }));
12411
- var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
12412
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
12413
- };
12414
12532
  Object.defineProperty(exports2, "__esModule", { value: true });
12415
- exports2.layeredLayout = exports2.packageCacheEnvironment = void 0;
12416
- __exportStar(require_constants2(), exports2);
12417
- __exportStar(require_legal_constants(), exports2);
12418
- var package_cache_1 = require_package_cache();
12419
- Object.defineProperty(exports2, "packageCacheEnvironment", { enumerable: true, get: function() {
12420
- return package_cache_1.packageCacheEnvironment;
12533
+ exports2.PluginConnection = exports2.PluginNode = exports2.PluginManifest = exports2.pluginDefinitionPath = exports2.loadPluginDefinition = exports2.startPlugin = exports2.definePlugin = exports2.validate = exports2.NodeError = exports2.start = exports2.runNode = exports2.defineConst = exports2.defineClass = exports2.defineService = exports2.defineNode = void 0;
12534
+ var define_node_1 = require_define_node();
12535
+ Object.defineProperty(exports2, "defineNode", { enumerable: true, get: function() {
12536
+ return define_node_1.defineNode;
12421
12537
  } });
12422
- __exportStar(require_api_exposure(), exports2);
12423
- var graph_layout_1 = require_graph_layout();
12424
- Object.defineProperty(exports2, "layeredLayout", { enumerable: true, get: function() {
12425
- return graph_layout_1.layeredLayout;
12538
+ Object.defineProperty(exports2, "defineService", { enumerable: true, get: function() {
12539
+ return define_node_1.defineService;
12540
+ } });
12541
+ Object.defineProperty(exports2, "defineClass", { enumerable: true, get: function() {
12542
+ return define_node_1.defineClass;
12543
+ } });
12544
+ Object.defineProperty(exports2, "defineConst", { enumerable: true, get: function() {
12545
+ return define_node_1.defineConst;
12546
+ } });
12547
+ var runner_1 = require_runner();
12548
+ Object.defineProperty(exports2, "runNode", { enumerable: true, get: function() {
12549
+ return runner_1.runNode;
12550
+ } });
12551
+ Object.defineProperty(exports2, "start", { enumerable: true, get: function() {
12552
+ return runner_1.start;
12553
+ } });
12554
+ Object.defineProperty(exports2, "NodeError", { enumerable: true, get: function() {
12555
+ return runner_1.NodeError;
12556
+ } });
12557
+ var json_schema_1 = require_json_schema2();
12558
+ Object.defineProperty(exports2, "validate", { enumerable: true, get: function() {
12559
+ return json_schema_1.validate;
12560
+ } });
12561
+ var plugin_1 = require_plugin2();
12562
+ Object.defineProperty(exports2, "definePlugin", { enumerable: true, get: function() {
12563
+ return plugin_1.definePlugin;
12564
+ } });
12565
+ Object.defineProperty(exports2, "startPlugin", { enumerable: true, get: function() {
12566
+ return plugin_1.startPlugin;
12567
+ } });
12568
+ var plugin_file_1 = require_plugin_file();
12569
+ Object.defineProperty(exports2, "loadPluginDefinition", { enumerable: true, get: function() {
12570
+ return plugin_file_1.loadPluginDefinition;
12571
+ } });
12572
+ Object.defineProperty(exports2, "pluginDefinitionPath", { enumerable: true, get: function() {
12573
+ return plugin_file_1.pluginDefinitionPath;
12574
+ } });
12575
+ var schema_1 = require_dist();
12576
+ Object.defineProperty(exports2, "PluginManifest", { enumerable: true, get: function() {
12577
+ return schema_1.PluginManifest;
12578
+ } });
12579
+ Object.defineProperty(exports2, "PluginNode", { enumerable: true, get: function() {
12580
+ return schema_1.PluginNode;
12581
+ } });
12582
+ Object.defineProperty(exports2, "PluginConnection", { enumerable: true, get: function() {
12583
+ return schema_1.PluginConnection;
12426
12584
  } });
12427
12585
  }
12428
12586
  });
@@ -12435,18 +12593,18 @@ __export(index_exports, {
12435
12593
  module.exports = __toCommonJS(index_exports);
12436
12594
 
12437
12595
  // packages/plugin/src/consumer/lifecycle.ts
12438
- var import_node_fs13 = require("node:fs");
12439
- var import_node_path13 = require("node:path");
12596
+ var import_node_fs15 = require("node:fs");
12597
+ var import_node_path15 = require("node:path");
12440
12598
 
12441
12599
  // packages/plugin/src/lifecycle.ts
12442
- var import_node_fs8 = require("node:fs");
12443
- var import_node_path8 = require("node:path");
12600
+ var import_node_fs10 = require("node:fs");
12601
+ var import_node_path10 = require("node:path");
12444
12602
 
12445
12603
  // packages/plugin/src/store.ts
12446
12604
  var import_node_fs2 = require("node:fs");
12447
12605
  var import_node_os = require("node:os");
12448
12606
  var import_node_path2 = require("node:path");
12449
- var import_plugin_runtime = __toESM(require_dist2());
12607
+ var import_plugin_runtime = __toESM(require_dist3());
12450
12608
 
12451
12609
  // packages/plugin/src/integrity.ts
12452
12610
  var import_node_crypto = require("node:crypto");
@@ -12538,7 +12696,7 @@ function addToStore(srcDir, opts = {}) {
12538
12696
  var import_node_fs4 = require("node:fs");
12539
12697
  var import_node_os2 = require("node:os");
12540
12698
  var import_node_path4 = require("node:path");
12541
- var import_plugin_runtime2 = __toESM(require_dist2());
12699
+ var import_plugin_runtime2 = __toESM(require_dist3());
12542
12700
  var import_schema2 = __toESM(require_dist());
12543
12701
 
12544
12702
  // packages/plugin/src/lock.ts
@@ -12551,6 +12709,8 @@ var LockEntry = import_zod.z.object({
12551
12709
  version: import_zod.z.string(),
12552
12710
  /** `sha256:<hex>` dir-content hash (hashPluginDir) — the store address + integrity check. */
12553
12711
  sha256: import_zod.z.string(),
12712
+ /** Registry archive checksum, distinct from the unpacked file-tree identity. */
12713
+ archiveSha256: import_zod.z.string().regex(/^[a-f0-9]{64}$/).optional(),
12554
12714
  /** Marketplace base URL the bundle came from (absent for local installs). */
12555
12715
  registry: import_zod.z.string().optional(),
12556
12716
  /** Marketplace tier it was resolved from. */
@@ -12625,12 +12785,12 @@ function isPluginDir(dir) {
12625
12785
 
12626
12786
  // packages/plugin/src/dependencies.ts
12627
12787
  var import_node_child_process = require("node:child_process");
12628
- var import_node_fs5 = require("node:fs");
12629
- var import_node_os3 = require("node:os");
12630
- var import_node_path5 = require("node:path");
12788
+ var import_node_fs6 = require("node:fs");
12789
+ var import_node_os4 = require("node:os");
12790
+ var import_node_path6 = require("node:path");
12631
12791
  var import_node_util = require("node:util");
12632
12792
  var import_yaml = require("yaml");
12633
- var import_common = __toESM(require_dist3());
12793
+ var import_common = __toESM(require_dist2());
12634
12794
 
12635
12795
  // packages/plugin/src/dependencies.constants.ts
12636
12796
  var DEFAULT_PACKAGE_MANAGER = "pnpm@12.3.4";
@@ -12644,43 +12804,291 @@ var PACKAGE_MANAGER_LOCKS = {
12644
12804
  bun: ["bun.lock", "bun.lockb"]
12645
12805
  };
12646
12806
 
12807
+ // packages/plugin/src/dependency/snapshot.ts
12808
+ var import_node_crypto2 = require("node:crypto");
12809
+ var import_node_fs5 = require("node:fs");
12810
+ var import_node_path5 = require("node:path");
12811
+ var import_node_os3 = require("node:os");
12812
+ var import_zod2 = require("zod");
12813
+
12814
+ // packages/plugin/src/dependency/snapshot.constants.ts
12815
+ var DEPENDENCY_SNAPSHOT_FORMAT = 1;
12816
+ var DEPENDENCY_SNAPSHOT_MAX_BYTES = 256 * 1024 * 1024;
12817
+ var DEPENDENCY_SNAPSHOT_MAX_FILES = 5e4;
12818
+ var DEPENDENCY_SNAPSHOT_OUTPUTS = [
12819
+ "node_modules",
12820
+ ".pnp.cjs",
12821
+ ".pnp.loader.mjs",
12822
+ ".pnp.data.json",
12823
+ ".yarn/cache",
12824
+ ".yarn/unplugged",
12825
+ ".yarn/install-state.gz"
12826
+ ];
12827
+
12828
+ // packages/plugin/src/dependency/snapshot.ts
12829
+ function inside(root, path) {
12830
+ const local = (0, import_node_path5.relative)(root, path);
12831
+ return local === "" || !(0, import_node_path5.isAbsolute)(local) && local !== ".." && !local.startsWith(`..${import_node_path5.sep}`);
12832
+ }
12833
+ function digestTree(root, paths, projectRoot) {
12834
+ const hash = (0, import_node_crypto2.createHash)("sha256");
12835
+ let bytes = 0;
12836
+ let count = 0;
12837
+ const visit2 = (path) => {
12838
+ if (++count > DEPENDENCY_SNAPSHOT_MAX_FILES) throw new Error("Dependency snapshot file limit");
12839
+ const file = (0, import_node_path5.join)(root, path);
12840
+ for (let parent = (0, import_node_path5.dirname)(file); inside(root, parent); parent = (0, import_node_path5.dirname)(parent)) {
12841
+ if ((0, import_node_fs5.lstatSync)(parent).isSymbolicLink()) throw new Error("Dependency snapshot ancestor is a link");
12842
+ if (parent === root) break;
12843
+ }
12844
+ const stat = (0, import_node_fs5.lstatSync)(file);
12845
+ hash.update(
12846
+ JSON.stringify([
12847
+ path,
12848
+ stat.mode & 511,
12849
+ stat.isDirectory() ? "directory" : stat.isSymbolicLink() ? "link" : "file"
12850
+ ])
12851
+ );
12852
+ if (stat.isSymbolicLink()) {
12853
+ const link = (0, import_node_fs5.readlinkSync)(file);
12854
+ if (!inside(projectRoot, (0, import_node_path5.resolve)((0, import_node_path5.dirname)((0, import_node_path5.join)(projectRoot, path)), link)))
12855
+ throw new Error("Dependency snapshot link leaves project");
12856
+ hash.update(JSON.stringify(link));
12857
+ } else if (stat.isDirectory()) {
12858
+ for (const name of (0, import_node_fs5.readdirSync)(file).sort()) visit2((0, import_node_path5.join)(path, name));
12859
+ } else if (stat.isFile()) {
12860
+ bytes += stat.size;
12861
+ if (bytes > DEPENDENCY_SNAPSHOT_MAX_BYTES) throw new Error("Dependency snapshot size limit");
12862
+ hash.update(String(stat.size)).update("\0").update((0, import_node_fs5.readFileSync)(file));
12863
+ } else throw new Error("Unsupported dependency snapshot file");
12864
+ };
12865
+ for (const path of [...paths].sort()) visit2(path);
12866
+ return hash.digest("hex");
12867
+ }
12868
+ function dependencySnapshot(project, cacheRoot, managerVersion, environment) {
12869
+ try {
12870
+ if (inside(cacheRoot, project.root)) return;
12871
+ if (!PACKAGE_MANAGER_LOCKS[project.manager].some((name) => (0, import_node_fs5.existsSync)((0, import_node_path5.join)(project.root, name)))) return;
12872
+ const outputs = new Set(DEPENDENCY_SNAPSHOT_OUTPUTS);
12873
+ let inputCount = 0;
12874
+ if (/--(?:require|import|loader|experimental-loader)(?:[=\s]|$)/.test(environment.NODE_OPTIONS ?? "")) return;
12875
+ const walk = (directory) => (0, import_node_fs5.readdirSync)(directory).sort().flatMap((name) => {
12876
+ const file = (0, import_node_path5.join)(directory, name);
12877
+ const local = (0, import_node_path5.relative)(project.root, file);
12878
+ if (inside(cacheRoot, file) || outputs.has(local) || name === "node_modules" || name === ".git" || name === ".octonode" || name === DEPENDENCY_MUTEX || name.startsWith(".octonode-recovery-"))
12879
+ return [];
12880
+ if (++inputCount > DEPENDENCY_SNAPSHOT_MAX_FILES) throw new Error("Dependency input file limit");
12881
+ const stat = (0, import_node_fs5.lstatSync)(file);
12882
+ if (stat.isSymbolicLink()) throw new Error("Linked source is not snapshot eligible");
12883
+ if (/^\.?pnpmfile\.[cm]?js$/.test(name))
12884
+ throw new Error("Dynamic package-manager hooks require installation");
12885
+ if (name === ".yarnrc.yml" && /^(?:plugins|yarnPath):/m.test((0, import_node_fs5.readFileSync)(file, "utf8")))
12886
+ throw new Error("Custom Yarn code requires installation");
12887
+ if (stat.isDirectory()) return walk(file);
12888
+ else {
12889
+ if (name === "package.json") {
12890
+ const manifest2 = JSON.parse((0, import_node_fs5.readFileSync)(file, "utf8"));
12891
+ for (const dependencies of [
12892
+ manifest2.dependencies,
12893
+ manifest2.devDependencies,
12894
+ manifest2.optionalDependencies
12895
+ ]) {
12896
+ for (const spec of Object.values(dependencies ?? {})) {
12897
+ if (typeof spec !== "string") throw new Error("Invalid dependency");
12898
+ const localSpec = /^(?:file:|link:|portal:)(.*)$/.exec(spec);
12899
+ if (localSpec) {
12900
+ const target = (0, import_node_path5.resolve)(directory, localSpec[1]);
12901
+ if (!inside(project.root, target) || inside(cacheRoot, target) || (0, import_node_path5.relative)(project.root, target).split(import_node_path5.sep).some((part) => ["node_modules", ".git", ".octonode"].includes(part)))
12902
+ throw new Error("External or excluded dependency is not snapshot eligible");
12903
+ }
12904
+ }
12905
+ }
12906
+ outputs.add((0, import_node_path5.relative)(project.root, (0, import_node_path5.join)(directory, "node_modules")));
12907
+ }
12908
+ return [local];
12909
+ }
12910
+ });
12911
+ const inputs = walk(project.root);
12912
+ const inputHash = digestTree(project.root, inputs, project.root);
12913
+ const userRoot = environment.HOME ?? (0, import_node_os3.homedir)();
12914
+ const configuration = new Set(
12915
+ [
12916
+ (0, import_node_path5.join)(userRoot, ".npmrc"),
12917
+ (0, import_node_path5.join)(userRoot, ".yarnrc"),
12918
+ (0, import_node_path5.join)(userRoot, ".yarnrc.yml"),
12919
+ (0, import_node_path5.join)(userRoot, ".config/pnpm/rc"),
12920
+ "/etc/npmrc",
12921
+ "/usr/local/etc/npmrc",
12922
+ environment.npm_config_userconfig,
12923
+ environment.NPM_CONFIG_USERCONFIG,
12924
+ environment.npm_config_globalconfig,
12925
+ environment.NPM_CONFIG_GLOBALCONFIG
12926
+ ].filter((path) => !!path)
12927
+ );
12928
+ for (let cursor = (0, import_node_path5.dirname)(project.root); ; cursor = (0, import_node_path5.dirname)(cursor)) {
12929
+ for (const name of [".npmrc", ".yarnrc", ".yarnrc.yml"]) configuration.add((0, import_node_path5.join)(cursor, name));
12930
+ if (cursor === (0, import_node_path5.dirname)(cursor)) break;
12931
+ }
12932
+ const configHash = [...configuration].sort().map((file) => {
12933
+ if (!(0, import_node_fs5.existsSync)(file)) return [file, null];
12934
+ if (/(?:yarnPath|plugins|pnpmfile)/.test((0, import_node_fs5.readFileSync)(file, "utf8")))
12935
+ throw new Error("Custom package-manager configuration requires installation");
12936
+ return [file, digestTree((0, import_node_path5.dirname)(file), [(0, import_node_path5.relative)((0, import_node_path5.dirname)(file), file)], (0, import_node_path5.dirname)(file))];
12937
+ });
12938
+ const yarnCache = (0, import_node_path5.join)(cacheRoot, "yarn");
12939
+ const nativeCache = project.manager === "yarn" && (0, import_node_fs5.existsSync)(yarnCache) ? digestTree(yarnCache, (0, import_node_fs5.readdirSync)(yarnCache), yarnCache) : null;
12940
+ const key = (0, import_node_crypto2.createHash)("sha256").update(
12941
+ JSON.stringify({
12942
+ format: DEPENDENCY_SNAPSHOT_FORMAT,
12943
+ root: project.root,
12944
+ cacheRoot,
12945
+ manager: project.manager,
12946
+ managerVersion,
12947
+ declared: project.declared,
12948
+ platform: process.platform,
12949
+ arch: process.arch,
12950
+ kernel: (0, import_node_os3.release)(),
12951
+ runtime: process.versions,
12952
+ environment: Object.entries(environment).sort(([a], [b]) => a.localeCompare(b)),
12953
+ inputHash,
12954
+ configHash,
12955
+ nativeCache
12956
+ })
12957
+ ).digest("hex");
12958
+ const projectKey = (0, import_node_crypto2.createHash)("sha256").update(project.root).digest("hex");
12959
+ return {
12960
+ root: project.root,
12961
+ cacheRoot,
12962
+ directory: (0, import_node_path5.join)(cacheRoot, "prepared-v1", projectKey),
12963
+ key,
12964
+ outputs: [...outputs].sort()
12965
+ };
12966
+ } catch {
12967
+ return void 0;
12968
+ }
12969
+ }
12970
+ function copy(source, destination) {
12971
+ (0, import_node_fs5.cpSync)(source, destination, {
12972
+ recursive: true,
12973
+ dereference: false,
12974
+ verbatimSymlinks: true,
12975
+ mode: import_node_fs5.constants.COPYFILE_FICLONE
12976
+ });
12977
+ }
12978
+ function restoreDependencySnapshot(snapshot) {
12979
+ let staging;
12980
+ let activated = [];
12981
+ let previous = [];
12982
+ try {
12983
+ if ((0, import_node_fs5.lstatSync)(snapshot.directory, { throwIfNoEntry: false })?.isSymbolicLink()) return false;
12984
+ const record = import_zod2.z.object({
12985
+ format: import_zod2.z.literal(DEPENDENCY_SNAPSHOT_FORMAT),
12986
+ key: import_zod2.z.literal(snapshot.key),
12987
+ sha256: import_zod2.z.string().regex(/^[a-f0-9]{64}$/),
12988
+ outputs: import_zod2.z.array(import_zod2.z.string()).refine(
12989
+ (paths) => new Set(paths).size === paths.length && paths.every((path) => snapshot.outputs.includes(path))
12990
+ )
12991
+ }).parse(JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path5.join)(snapshot.directory, "snapshot.json"), "utf8")));
12992
+ const stored = (0, import_node_path5.join)(snapshot.directory, "files");
12993
+ if (!record.outputs.length || digestTree(stored, record.outputs, snapshot.root) !== record.sha256) return false;
12994
+ for (const path of snapshot.outputs) {
12995
+ for (let cursor = (0, import_node_path5.dirname)((0, import_node_path5.join)(snapshot.root, path)); inside(snapshot.root, cursor); cursor = (0, import_node_path5.dirname)(cursor)) {
12996
+ if ((0, import_node_fs5.lstatSync)(cursor, { throwIfNoEntry: false })?.isSymbolicLink()) return false;
12997
+ if (cursor === snapshot.root) break;
12998
+ }
12999
+ }
13000
+ staging = (0, import_node_fs5.mkdtempSync)((0, import_node_path5.join)(snapshot.root, DEPENDENCY_MUTEX, "snapshot-"));
13001
+ for (const path of record.outputs) copy((0, import_node_path5.join)(stored, path), (0, import_node_path5.join)(staging, "next", path));
13002
+ if (digestTree((0, import_node_path5.join)(staging, "next"), record.outputs, snapshot.root) !== record.sha256) return false;
13003
+ for (const path of snapshot.outputs) {
13004
+ const target = (0, import_node_path5.join)(snapshot.root, path);
13005
+ if ((0, import_node_fs5.lstatSync)(target, { throwIfNoEntry: false })) {
13006
+ const backup = (0, import_node_path5.join)(staging, "previous", path);
13007
+ (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(backup), { recursive: true });
13008
+ (0, import_node_fs5.renameSync)(target, backup);
13009
+ previous = [...previous, path];
13010
+ }
13011
+ if (record.outputs.includes(path)) {
13012
+ (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(target), { recursive: true });
13013
+ (0, import_node_fs5.renameSync)((0, import_node_path5.join)(staging, "next", path), target);
13014
+ activated = [...activated, path];
13015
+ }
13016
+ }
13017
+ return true;
13018
+ } catch {
13019
+ try {
13020
+ for (const path of [...activated].reverse()) (0, import_node_fs5.rmSync)((0, import_node_path5.join)(snapshot.root, path), { recursive: true, force: true });
13021
+ for (const path of [...previous].reverse())
13022
+ (0, import_node_fs5.renameSync)((0, import_node_path5.join)(staging, "previous", path), (0, import_node_path5.join)(snapshot.root, path));
13023
+ } catch {
13024
+ const retained = staging;
13025
+ staging = void 0;
13026
+ throw new Error(`Dependency snapshot activation failed; previous files retained in ${retained}`);
13027
+ }
13028
+ return false;
13029
+ } finally {
13030
+ if (staging) (0, import_node_fs5.rmSync)(staging, { recursive: true, force: true });
13031
+ }
13032
+ }
13033
+ function saveDependencySnapshot(snapshot) {
13034
+ let staging;
13035
+ try {
13036
+ const outputs = snapshot.outputs.filter((path) => (0, import_node_fs5.lstatSync)((0, import_node_path5.join)(snapshot.root, path), { throwIfNoEntry: false }));
13037
+ if (!outputs.length) return;
13038
+ const sha256 = digestTree(snapshot.root, outputs, snapshot.root);
13039
+ (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(snapshot.directory), { recursive: true });
13040
+ staging = (0, import_node_fs5.mkdtempSync)((0, import_node_path5.join)((0, import_node_path5.dirname)(snapshot.directory), ".prepared-"));
13041
+ for (const path of outputs) copy((0, import_node_path5.join)(snapshot.root, path), (0, import_node_path5.join)(staging, "files", path));
13042
+ if (digestTree((0, import_node_path5.join)(staging, "files"), outputs, snapshot.root) !== sha256) return;
13043
+ (0, import_node_fs5.writeFileSync)(
13044
+ (0, import_node_path5.join)(staging, "snapshot.json"),
13045
+ JSON.stringify({ format: DEPENDENCY_SNAPSHOT_FORMAT, key: snapshot.key, sha256, outputs })
13046
+ );
13047
+ (0, import_node_fs5.rmSync)(snapshot.directory, { recursive: true, force: true });
13048
+ (0, import_node_fs5.renameSync)(staging, snapshot.directory);
13049
+ } catch {
13050
+ } finally {
13051
+ if (staging) (0, import_node_fs5.rmSync)(staging, { recursive: true, force: true });
13052
+ }
13053
+ }
13054
+
12647
13055
  // packages/plugin/src/dependencies.ts
12648
13056
  var execute = (0, import_node_util.promisify)(import_node_child_process.execFile);
12649
13057
  var installations = /* @__PURE__ */ new Map();
12650
13058
  function checkTransactionPath(root, file) {
12651
- const local = (0, import_node_path5.relative)(root, file);
12652
- if (!local || local.startsWith("../") || (0, import_node_path5.resolve)(root, local) !== file)
13059
+ const local = (0, import_node_path6.relative)(root, file);
13060
+ if (!local || local.startsWith("../") || (0, import_node_path6.resolve)(root, local) !== file)
12653
13061
  throw new Error("Transaction path must stay inside its project");
12654
- for (let cursor = file; cursor !== root; cursor = (0, import_node_path5.dirname)(cursor)) {
12655
- const stat = (0, import_node_fs5.lstatSync)(cursor, { throwIfNoEntry: false });
13062
+ for (let cursor = file; cursor !== root; cursor = (0, import_node_path6.dirname)(cursor)) {
13063
+ const stat = (0, import_node_fs6.lstatSync)(cursor, { throwIfNoEntry: false });
12656
13064
  if (stat?.isSymbolicLink()) throw new Error("Transaction path contains a symbolic link");
12657
13065
  }
12658
13066
  }
12659
13067
  function manifest(root) {
12660
- const file = (0, import_node_path5.join)(root, "package.json");
12661
- return (0, import_node_fs5.existsSync)(file) ? JSON.parse((0, import_node_fs5.readFileSync)(file, "utf8")) : {};
13068
+ const file = (0, import_node_path6.join)(root, "package.json");
13069
+ return (0, import_node_fs6.existsSync)(file) ? JSON.parse((0, import_node_fs6.readFileSync)(file, "utf8")) : {};
12662
13070
  }
12663
13071
  function dependencyCacheRoot(root) {
12664
- return (0, import_node_path5.resolve)(root ?? (0, import_common.OCTONODE_PACKAGE_CACHE)() ?? (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".octonode", "package-cache"));
13072
+ return (0, import_node_path6.resolve)(root ?? (0, import_common.OCTONODE_PACKAGE_CACHE)() ?? (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".octonode", "package-cache"));
12665
13073
  }
12666
13074
  function dependencyPaths(cwd) {
12667
- const start = (0, import_node_fs5.realpathSync)(cwd);
13075
+ const start = (0, import_node_fs6.realpathSync)(cwd);
12668
13076
  const ancestors = [];
12669
- for (let path = start; ; path = (0, import_node_path5.dirname)(path)) {
13077
+ for (let path = start; ; path = (0, import_node_path6.dirname)(path)) {
12670
13078
  ancestors.push(path);
12671
- if ((0, import_node_fs5.existsSync)((0, import_node_path5.join)(path, ".git")) || (0, import_node_path5.dirname)(path) === path) break;
13079
+ if ((0, import_node_fs6.existsSync)((0, import_node_path6.join)(path, ".git")) || (0, import_node_path6.dirname)(path) === path) break;
12672
13080
  }
12673
- const repository = ancestors.find((path) => (0, import_node_fs5.existsSync)((0, import_node_path5.join)(path, ".git")));
12674
- const target = (0, import_node_fs5.existsSync)((0, import_node_path5.join)(start, "package.json")) ? start : repository ? ancestors.find((path) => (0, import_node_fs5.existsSync)((0, import_node_path5.join)(path, "package.json"))) ?? start : start;
13081
+ const repository = ancestors.find((path) => (0, import_node_fs6.existsSync)((0, import_node_path6.join)(path, ".git")));
13082
+ const target = (0, import_node_fs6.existsSync)((0, import_node_path6.join)(start, "package.json")) ? start : repository ? ancestors.find((path) => (0, import_node_fs6.existsSync)((0, import_node_path6.join)(path, "package.json"))) ?? start : start;
12675
13083
  const root = ancestors.find((path) => {
12676
- if (path === target || !(0, import_node_path5.relative)(path, target) || (0, import_node_path5.relative)(path, target).startsWith("..")) return false;
13084
+ if (path === target || !(0, import_node_path6.relative)(path, target) || (0, import_node_path6.relative)(path, target).startsWith("..")) return false;
12677
13085
  const workspaces = manifest(path).workspaces;
12678
- const pnpmWorkspace = (0, import_node_path5.join)(path, "pnpm-workspace.yaml");
12679
- const patterns = (0, import_node_fs5.existsSync)(pnpmWorkspace) ? (0, import_yaml.parse)((0, import_node_fs5.readFileSync)(pnpmWorkspace, "utf8"))?.packages : Array.isArray(workspaces) ? workspaces : workspaces?.packages;
13086
+ const pnpmWorkspace = (0, import_node_path6.join)(path, "pnpm-workspace.yaml");
13087
+ const patterns = (0, import_node_fs6.existsSync)(pnpmWorkspace) ? (0, import_yaml.parse)((0, import_node_fs6.readFileSync)(pnpmWorkspace, "utf8"))?.packages : Array.isArray(workspaces) ? workspaces : workspaces?.packages;
12680
13088
  if (!Array.isArray(patterns)) return false;
12681
- const local = (0, import_node_path5.relative)(path, target).split("\\").join("/");
13089
+ const local = (0, import_node_path6.relative)(path, target).split("\\").join("/");
12682
13090
  const globs = patterns.filter((value) => typeof value === "string");
12683
- return globs.some((pattern) => !pattern.startsWith("!") && (0, import_node_path5.matchesGlob)(local, pattern.replace(/\/$/, ""))) && !globs.some((pattern) => pattern.startsWith("!") && (0, import_node_path5.matchesGlob)(local, pattern.slice(1).replace(/\/$/, "")));
13091
+ return globs.some((pattern) => !pattern.startsWith("!") && (0, import_node_path6.matchesGlob)(local, pattern.replace(/\/$/, ""))) && !globs.some((pattern) => pattern.startsWith("!") && (0, import_node_path6.matchesGlob)(local, pattern.slice(1).replace(/\/$/, "")));
12684
13092
  }) ?? target;
12685
13093
  return { root, target };
12686
13094
  }
@@ -12688,7 +13096,7 @@ function resolveDependencyProject(cwd) {
12688
13096
  const { root, target } = dependencyPaths(cwd);
12689
13097
  const declared = manifest(root).packageManager;
12690
13098
  const locked = Object.keys(PACKAGE_MANAGER_LOCKS).filter(
12691
- (manager2) => PACKAGE_MANAGER_LOCKS[manager2].some((file) => (0, import_node_fs5.existsSync)((0, import_node_path5.join)(root, file)))
13099
+ (manager2) => PACKAGE_MANAGER_LOCKS[manager2].some((file) => (0, import_node_fs6.existsSync)((0, import_node_path6.join)(root, file)))
12692
13100
  );
12693
13101
  const manager = declared?.split("@")[0];
12694
13102
  if (manager && !(manager in PACKAGE_MANAGER_LOCKS)) throw new Error(`unsupported package manager: ${declared}`);
@@ -12698,7 +13106,7 @@ function resolveDependencyProject(cwd) {
12698
13106
  return {
12699
13107
  target,
12700
13108
  root,
12701
- manager: manager ?? locked[0] ?? ((0, import_node_fs5.existsSync)((0, import_node_path5.join)(root, "pnpm-workspace.yaml")) ? "pnpm" : (0, import_node_fs5.existsSync)((0, import_node_path5.join)(target, "package.json")) ? "npm" : "pnpm"),
13109
+ manager: manager ?? locked[0] ?? ((0, import_node_fs6.existsSync)((0, import_node_path6.join)(root, "pnpm-workspace.yaml")) ? "pnpm" : (0, import_node_fs6.existsSync)((0, import_node_path6.join)(target, "package.json")) ? "npm" : "pnpm"),
12702
13110
  declared
12703
13111
  };
12704
13112
  }
@@ -12726,42 +13134,48 @@ function installCommand(project, frozen) {
12726
13134
  return ["npm", [frozen ? "ci" : "install", "--ignore-scripts", "--no-audit", "--no-fund"]];
12727
13135
  }
12728
13136
  }
13137
+ function installEnvironment(cacheRoot) {
13138
+ return (0, import_common.childProcessEnvironment)({
13139
+ ...(0, import_common.packageCacheEnvironment)(dependencyCacheRoot(cacheRoot)),
13140
+ YARN_ENABLE_SCRIPTS: "false",
13141
+ COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
13142
+ COREPACK_ENABLE_AUTO_PIN: "0"
13143
+ });
13144
+ }
13145
+ function isClassicYarn(project) {
13146
+ return project.manager === "yarn" && (project.declared?.startsWith("yarn@1.") || !project.declared && (0, import_node_fs6.existsSync)((0, import_node_path6.join)(project.root, "yarn.lock")) && (0, import_node_fs6.readFileSync)((0, import_node_path6.join)(project.root, "yarn.lock"), "utf8").includes("# yarn lockfile v1"));
13147
+ }
12729
13148
  async function run(project, command, cwd, cacheRoot) {
12730
13149
  const [file, args] = command;
12731
- const yarnClassic = project.manager === "yarn" && (project.declared?.startsWith("yarn@1.") || !project.declared && (0, import_node_fs5.existsSync)((0, import_node_path5.join)(project.root, "yarn.lock")) && (0, import_node_fs5.readFileSync)((0, import_node_path5.join)(project.root, "yarn.lock"), "utf8").includes("# yarn lockfile v1"));
13150
+ const yarnClassic = isClassicYarn(project);
12732
13151
  const flags = yarnClassic ? [
12733
13152
  ...args.filter((arg) => arg !== "--mode=skip-build").map((arg) => arg === "--immutable" ? "--frozen-lockfile" : arg),
12734
13153
  "--ignore-scripts"
12735
- ] : project.manager === "pnpm" && args[0] === "add" && project.root === cwd && (0, import_node_fs5.existsSync)((0, import_node_path5.join)(cwd, "pnpm-workspace.yaml")) ? [...args, "--workspace-root"] : args;
13154
+ ] : project.manager === "pnpm" && args[0] === "add" && project.root === cwd && (0, import_node_fs6.existsSync)((0, import_node_path6.join)(cwd, "pnpm-workspace.yaml")) ? [...args, "--workspace-root"] : args;
12736
13155
  const pinned = project.declared && file !== "bun" || yarnClassic;
12737
13156
  const executable = yarnClassic && !project.declared ? CLASSIC_YARN : file;
12738
13157
  await execute(pinned ? "corepack" : file, pinned ? [executable, ...flags] : flags, {
12739
13158
  cwd,
12740
- env: (0, import_common.childProcessEnvironment)({
12741
- ...(0, import_common.packageCacheEnvironment)(dependencyCacheRoot(cacheRoot)),
12742
- YARN_ENABLE_SCRIPTS: "false",
12743
- COREPACK_ENABLE_DOWNLOAD_PROMPT: "0",
12744
- COREPACK_ENABLE_AUTO_PIN: "0"
12745
- }),
13159
+ env: installEnvironment(cacheRoot),
12746
13160
  timeout: DEPENDENCY_INSTALL_TIMEOUT_MS,
12747
13161
  maxBuffer: 4 * 1024 * 1024
12748
13162
  });
12749
13163
  }
12750
13164
  async function verifyProjectDependencies(project, names) {
12751
13165
  if (!names.length) return;
12752
- const pnp = (0, import_node_path5.join)(project.root, ".pnp.cjs");
12753
- const loader = (0, import_node_path5.join)(project.root, ".pnp.loader.mjs");
13166
+ const pnp = (0, import_node_path6.join)(project.root, ".pnp.cjs");
13167
+ const loader = (0, import_node_path6.join)(project.root, ".pnp.loader.mjs");
12754
13168
  await execute(
12755
13169
  process.execPath,
12756
13170
  [
12757
- ...(0, import_node_fs5.existsSync)(pnp) ? ["--require", pnp] : [],
12758
- ...(0, import_node_fs5.existsSync)(loader) ? ["--loader", loader] : [],
13171
+ ...(0, import_node_fs6.existsSync)(pnp) ? ["--require", pnp] : [],
13172
+ ...(0, import_node_fs6.existsSync)(loader) ? ["--loader", loader] : [],
12759
13173
  "--input-type=module",
12760
13174
  "-e",
12761
13175
  `import { createRequire } from 'node:module';
12762
13176
  import { existsSync } from 'node:fs';
12763
13177
  import { fileURLToPath } from 'node:url';
12764
- const require = createRequire(${JSON.stringify((0, import_node_path5.join)(project.target, "package.json"))});
13178
+ const require = createRequire(${JSON.stringify((0, import_node_path6.join)(project.target, "package.json"))});
12765
13179
  for (const name of ${JSON.stringify(names)}) {
12766
13180
  try { require.resolve(name); } catch {
12767
13181
  const entry = import.meta.resolve(name);
@@ -12779,19 +13193,19 @@ async function withDependencyInstall(cwd, action) {
12779
13193
  const previous = installations.get(root) ?? Promise.resolve();
12780
13194
  const next = previous.catch(() => {
12781
13195
  }).then(async () => {
12782
- const mutex = (0, import_node_path5.join)(root, DEPENDENCY_MUTEX);
13196
+ const mutex = (0, import_node_path6.join)(root, DEPENDENCY_MUTEX);
12783
13197
  try {
12784
- (0, import_node_fs5.mkdirSync)(mutex);
13198
+ (0, import_node_fs6.mkdirSync)(mutex);
12785
13199
  } catch {
12786
13200
  throw new Error(
12787
13201
  "Another dependency installation is running or needs recovery; use octonodes plugin recover after it exits"
12788
13202
  );
12789
13203
  }
12790
- (0, import_node_fs5.writeFileSync)((0, import_node_path5.join)(mutex, "owner.json"), JSON.stringify({ pid: process.pid }));
13204
+ (0, import_node_fs6.writeFileSync)((0, import_node_path6.join)(mutex, "owner.json"), JSON.stringify({ pid: process.pid }));
12791
13205
  try {
12792
13206
  return await action();
12793
13207
  } finally {
12794
- if (!(0, import_node_fs5.existsSync)((0, import_node_path5.join)(mutex, "journal.json"))) (0, import_node_fs5.rmSync)(mutex, { recursive: true, force: true });
13208
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path6.join)(mutex, "journal.json"))) (0, import_node_fs6.rmSync)(mutex, { recursive: true, force: true });
12795
13209
  }
12796
13210
  });
12797
13211
  installations.set(root, next);
@@ -12802,6 +13216,8 @@ async function withDependencyInstall(cwd, action) {
12802
13216
  }
12803
13217
  }
12804
13218
  async function reconcileDependencies(cwd, dependencies, options = {}) {
13219
+ if (options.metadataOnly && (dependencies.length || options.remove?.length || options.frozen))
13220
+ throw new Error("Metadata-only transactions cannot change or restore dependencies");
12805
13221
  if (options.frozen && (dependencies.length || options.remove?.length))
12806
13222
  throw new Error("Frozen installation cannot add or remove dependencies");
12807
13223
  for (const { name, spec, subpaths } of dependencies) {
@@ -12815,64 +13231,82 @@ async function reconcileDependencies(cwd, dependencies, options = {}) {
12815
13231
  }
12816
13232
  }
12817
13233
  const project = resolveDependencyProject(cwd);
12818
- const packageJson = (0, import_node_path5.join)(project.target, "package.json");
12819
- if (!dependencies.length && !(0, import_node_fs5.existsSync)(packageJson) && !options.commit) return;
12820
- const hasLock = PACKAGE_MANAGER_LOCKS[project.manager].some((file) => (0, import_node_fs5.existsSync)((0, import_node_path5.join)(project.root, file)));
13234
+ const packageJson = (0, import_node_path6.join)(project.target, "package.json");
13235
+ if (!dependencies.length && !(0, import_node_fs6.existsSync)(packageJson) && !options.commit) return;
13236
+ const hasLock = PACKAGE_MANAGER_LOCKS[project.manager].some((file) => (0, import_node_fs6.existsSync)((0, import_node_path6.join)(project.root, file)));
12821
13237
  if (options.frozen && !hasLock)
12822
13238
  throw new Error("commit the native package-manager lockfile before preparing this worktree");
12823
13239
  const files = [
12824
13240
  .../* @__PURE__ */ new Set([
12825
13241
  packageJson,
12826
- (0, import_node_path5.join)(project.root, "package.json"),
12827
- ...Object.values(PACKAGE_MANAGER_LOCKS).flat().map((file) => (0, import_node_path5.join)(project.root, file)),
12828
- (0, import_node_path5.join)(project.root, "pnpm-workspace.yaml"),
13242
+ (0, import_node_path6.join)(project.root, "package.json"),
13243
+ ...Object.values(PACKAGE_MANAGER_LOCKS).flat().map((file) => (0, import_node_path6.join)(project.root, file)),
13244
+ (0, import_node_path6.join)(project.root, "pnpm-workspace.yaml"),
12829
13245
  ...options.transactionFiles ?? []
12830
13246
  ])
12831
13247
  ];
12832
13248
  for (const path of files) checkTransactionPath(project.root, path);
12833
- const before = new Map(files.map((path) => [path, (0, import_node_fs5.existsSync)(path) ? (0, import_node_fs5.readFileSync)(path) : void 0]));
12834
- const journal = (0, import_node_path5.join)(project.root, DEPENDENCY_MUTEX, "journal.json");
12835
- const hadModules = (0, import_node_fs5.existsSync)((0, import_node_path5.join)(project.root, "node_modules"));
13249
+ const before = new Map(files.map((path) => [path, (0, import_node_fs6.existsSync)(path) ? (0, import_node_fs6.readFileSync)(path) : void 0]));
13250
+ const journal = (0, import_node_path6.join)(project.root, DEPENDENCY_MUTEX, "journal.json");
13251
+ const hadModules = (0, import_node_fs6.existsSync)((0, import_node_path6.join)(project.root, "node_modules"));
12836
13252
  const pnpFiles = [".pnp.cjs", ".pnp.loader.mjs", ".pnp.data.json"];
12837
13253
  const pnpBefore = new Map(
12838
13254
  pnpFiles.map((file) => {
12839
- const path = (0, import_node_path5.join)(project.root, file);
12840
- return [path, (0, import_node_fs5.existsSync)(path) ? (0, import_node_fs5.readFileSync)(path) : void 0];
13255
+ const path = (0, import_node_path6.join)(project.root, file);
13256
+ return [path, (0, import_node_fs6.existsSync)(path) ? (0, import_node_fs6.readFileSync)(path) : void 0];
12841
13257
  })
12842
13258
  );
12843
- const hadInstall = hadModules || (0, import_node_fs5.existsSync)((0, import_node_path5.join)(project.root, ".pnp.cjs"));
13259
+ const hadInstall = hadModules || (0, import_node_fs6.existsSync)((0, import_node_path6.join)(project.root, ".pnp.cjs"));
12844
13260
  let metadataTouched = false;
12845
13261
  const metadata = new Set(options.transactionFiles ?? []);
12846
13262
  const restoreBefore = () => {
12847
13263
  for (const [path, content] of before) {
12848
13264
  if (metadata.has(path) && !metadataTouched) continue;
12849
- if (content === void 0) (0, import_node_fs5.rmSync)(path, { force: true });
12850
- else (0, import_node_fs5.writeFileSync)(path, content);
13265
+ if (content === void 0) (0, import_node_fs6.rmSync)(path, { force: true });
13266
+ else (0, import_node_fs6.writeFileSync)(path, content);
12851
13267
  }
12852
13268
  };
12853
- if (hadInstall && !hasLock)
13269
+ if (hadInstall && !hasLock && !options.metadataOnly)
12854
13270
  throw new Error(
12855
13271
  "create a native lockfile before updating existing dependencies so failed installations can be recovered"
12856
13272
  );
12857
13273
  try {
12858
- if ((0, import_node_fs5.existsSync)((0, import_node_path5.dirname)(journal)))
12859
- (0, import_node_fs5.writeFileSync)(
13274
+ if (options.metadataOnly && (0, import_node_fs6.existsSync)((0, import_node_path6.dirname)(journal)))
13275
+ (0, import_node_fs6.writeFileSync)((0, import_node_path6.join)((0, import_node_path6.dirname)(journal), "metadata-only"), "1");
13276
+ if ((0, import_node_fs6.existsSync)((0, import_node_path6.dirname)(journal)))
13277
+ (0, import_node_fs6.writeFileSync)(
12860
13278
  journal,
12861
13279
  JSON.stringify(
12862
13280
  [...before].map(([path, content]) => ({
12863
- path: (0, import_node_path5.relative)(project.root, path).replaceAll("\\", "/"),
13281
+ path: (0, import_node_path6.relative)(project.root, path).replaceAll("\\", "/"),
12864
13282
  content: content?.toString("base64") ?? null
12865
13283
  }))
12866
13284
  )
12867
13285
  );
12868
- if (!(0, import_node_fs5.existsSync)(packageJson)) {
12869
- (0, import_node_fs5.mkdirSync)(project.target, { recursive: true });
12870
- (0, import_node_fs5.writeFileSync)(
13286
+ if (options.metadataOnly) {
13287
+ metadataTouched = true;
13288
+ options.commit?.();
13289
+ (0, import_node_fs6.rmSync)(journal, { force: true });
13290
+ (0, import_node_fs6.rmSync)((0, import_node_path6.join)((0, import_node_path6.dirname)(journal), "metadata-only"), { force: true });
13291
+ return;
13292
+ }
13293
+ if (!(0, import_node_fs6.existsSync)(packageJson)) {
13294
+ (0, import_node_fs6.mkdirSync)(project.target, { recursive: true });
13295
+ (0, import_node_fs6.writeFileSync)(
12871
13296
  packageJson,
12872
13297
  JSON.stringify({ private: true, packageManager: DEFAULT_PACKAGE_MANAGER }, null, 2) + "\n"
12873
13298
  );
12874
13299
  }
12875
13300
  const current = resolveDependencyProject(cwd);
13301
+ const snapshotEnvironment = installEnvironment(options.cacheRoot);
13302
+ const pinnedManager = current.declared && current.manager !== "bun" || isClassicYarn(current);
13303
+ const versionCommand = isClassicYarn(current) && !current.declared ? CLASSIC_YARN : current.manager;
13304
+ const managerVersion = options.frozen && (options.cacheRoot || (0, import_common.OCTONODE_PACKAGE_CACHE)()) ? await execute(
13305
+ pinnedManager ? "corepack" : current.manager,
13306
+ pinnedManager ? [versionCommand, "--version"] : ["--version"],
13307
+ { cwd: current.root, env: snapshotEnvironment, timeout: 3e4 }
13308
+ ).then(({ stdout }) => stdout.trim()).catch(() => void 0) : void 0;
13309
+ const snapshot = managerVersion ? dependencySnapshot(current, dependencyCacheRoot(options.cacheRoot), managerVersion, snapshotEnvironment) : void 0;
12876
13310
  if (options.remove?.length) {
12877
13311
  for (const name of options.remove)
12878
13312
  if (!/^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/i.test(name))
@@ -12880,81 +13314,92 @@ async function reconcileDependencies(cwd, dependencies, options = {}) {
12880
13314
  const args = current.manager === "npm" ? ["uninstall", "--ignore-scripts", "--no-audit", "--no-fund"] : current.manager === "yarn" ? ["remove", "--mode=skip-build"] : ["remove", "--ignore-scripts"];
12881
13315
  await run(current, [current.manager, [...args, ...options.remove]], current.target, options.cacheRoot);
12882
13316
  }
12883
- await run(
12884
- current,
12885
- dependencies.length ? packageManagerAdd(
12886
- current.manager,
12887
- ...dependencies.map(({ name, spec }) => spec.startsWith(`${name}@`) ? spec : `${name}@${spec}`)
12888
- ) : installCommand(current, options.frozen ?? false),
12889
- dependencies.length ? current.target : current.root,
12890
- options.cacheRoot
12891
- );
13317
+ const restored = snapshot ? restoreDependencySnapshot(snapshot) : false;
13318
+ if (!restored)
13319
+ await run(
13320
+ current,
13321
+ dependencies.length ? packageManagerAdd(
13322
+ current.manager,
13323
+ ...dependencies.map(({ name, spec }) => spec.startsWith(`${name}@`) ? spec : `${name}@${spec}`)
13324
+ ) : installCommand(current, options.frozen ?? false),
13325
+ dependencies.length ? current.target : current.root,
13326
+ options.cacheRoot
13327
+ );
12892
13328
  await verifyProjectDependencies(
12893
13329
  current,
12894
13330
  dependencies.flatMap(({ name, subpaths }) => subpaths ? subpaths.map((path) => name + path.slice(1)) : [name])
12895
13331
  );
12896
13332
  if (options.frozen) {
12897
13333
  for (const [path, content] of before) {
12898
- const after = (0, import_node_fs5.existsSync)(path) ? (0, import_node_fs5.readFileSync)(path) : void 0;
13334
+ const after = (0, import_node_fs6.existsSync)(path) ? (0, import_node_fs6.readFileSync)(path) : void 0;
12899
13335
  if (content?.equals(after ?? Buffer.alloc(0)) || !content && !after) continue;
12900
13336
  if (path.endsWith("package.json") && content && after && (0, import_node_util.isDeepStrictEqual)(JSON.parse(content.toString()), JSON.parse(after.toString()))) {
12901
- (0, import_node_fs5.writeFileSync)(path, content);
13337
+ (0, import_node_fs6.writeFileSync)(path, content);
12902
13338
  } else {
12903
- throw new Error(`frozen installation changed ${(0, import_node_path5.relative)(project.root, path)}`);
13339
+ throw new Error(`frozen installation changed ${(0, import_node_path6.relative)(project.root, path)}`);
12904
13340
  }
12905
13341
  }
12906
13342
  }
12907
13343
  for (const path of metadata) {
12908
13344
  const content = before.get(path);
12909
- const current2 = (0, import_node_fs5.existsSync)(path) ? (0, import_node_fs5.readFileSync)(path) : void 0;
13345
+ const current2 = (0, import_node_fs6.existsSync)(path) ? (0, import_node_fs6.readFileSync)(path) : void 0;
12910
13346
  if (!(content?.equals(current2 ?? Buffer.alloc(0)) || !content && !current2))
12911
13347
  throw new Error(
12912
- `Owner metadata changed during installation: ${(0, import_node_path5.relative)(project.root, path)}; refresh and retry`
13348
+ `Owner metadata changed during installation: ${(0, import_node_path6.relative)(project.root, path)}; refresh and retry`
12913
13349
  );
12914
13350
  }
12915
13351
  metadataTouched = true;
12916
13352
  options.commit?.();
12917
- (0, import_node_fs5.rmSync)(journal, { force: true });
13353
+ (0, import_node_fs6.rmSync)(journal, { force: true });
13354
+ if (snapshot && !restored) {
13355
+ const after = dependencySnapshot(
13356
+ current,
13357
+ dependencyCacheRoot(options.cacheRoot),
13358
+ managerVersion,
13359
+ snapshotEnvironment
13360
+ );
13361
+ if (after?.key === snapshot.key) saveDependencySnapshot(after);
13362
+ }
12918
13363
  } catch (error) {
12919
- const changed = [...before].filter(([path, content]) => (0, import_node_fs5.existsSync)(path) && !content?.equals((0, import_node_fs5.readFileSync)(path)));
13364
+ const changed = [...before].filter(([path, content]) => (0, import_node_fs6.existsSync)(path) && !content?.equals((0, import_node_fs6.readFileSync)(path)));
12920
13365
  if (changed.length) {
12921
- const backup = (0, import_node_path5.join)(project.root, `.octonode-recovery-${Date.now()}`);
13366
+ const backup = (0, import_node_path6.join)(project.root, `.octonode-recovery-${Date.now()}`);
12922
13367
  for (const [path] of changed) {
12923
13368
  checkTransactionPath(project.root, path);
12924
- const target = (0, import_node_path5.join)(backup, (0, import_node_path5.relative)(project.root, path));
12925
- (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(target), { recursive: true });
12926
- (0, import_node_fs5.writeFileSync)(target, (0, import_node_fs5.readFileSync)(path));
13369
+ const target = (0, import_node_path6.join)(backup, (0, import_node_path6.relative)(project.root, path));
13370
+ (0, import_node_fs6.mkdirSync)((0, import_node_path6.dirname)(target), { recursive: true });
13371
+ (0, import_node_fs6.writeFileSync)(target, (0, import_node_fs6.readFileSync)(path));
12927
13372
  }
12928
13373
  process.stderr.write(`Pre-rollback files preserved in ${backup}
12929
13374
  `);
12930
13375
  }
12931
13376
  restoreBefore();
12932
13377
  let recovery = "";
12933
- if (hadInstall && hasLock) {
13378
+ if (!options.metadataOnly && hadInstall && hasLock) {
12934
13379
  try {
12935
13380
  await run(project, installCommand(project, true), project.root, options.cacheRoot);
12936
13381
  } catch {
12937
13382
  recovery = "; manifests restored, but dependency recovery failed \u2014 run a frozen install before executing workflows";
12938
13383
  }
12939
13384
  restoreBefore();
12940
- } else if (!hadInstall) {
12941
- (0, import_node_fs5.rmSync)((0, import_node_path5.join)(project.root, "node_modules"), { recursive: true, force: true });
13385
+ } else if (!options.metadataOnly && !hadInstall) {
13386
+ (0, import_node_fs6.rmSync)((0, import_node_path6.join)(project.root, "node_modules"), { recursive: true, force: true });
12942
13387
  }
12943
13388
  for (const [path, content] of pnpBefore) {
12944
- if (content === void 0) (0, import_node_fs5.rmSync)(path, { force: true });
12945
- else (0, import_node_fs5.writeFileSync)(path, content);
13389
+ if (content === void 0) (0, import_node_fs6.rmSync)(path, { force: true });
13390
+ else (0, import_node_fs6.writeFileSync)(path, content);
12946
13391
  }
12947
13392
  const failure = error;
12948
- if (!recovery) (0, import_node_fs5.rmSync)(journal, { force: true });
13393
+ if (!recovery) (0, import_node_fs6.rmSync)(journal, { force: true });
12949
13394
  throw new Error(`package installation failed: ${failure.stderr?.trim() || failure.message}${recovery}`);
12950
13395
  }
12951
13396
  }
12952
13397
  async function recoverDependencyInstall(cwd) {
12953
13398
  const project = resolveDependencyProject(cwd);
12954
- const mutex = (0, import_node_path5.join)(project.root, DEPENDENCY_MUTEX);
12955
- if (!(0, import_node_fs5.existsSync)(mutex)) return;
12956
- checkTransactionPath(project.root, (0, import_node_path5.join)(mutex, "owner.json"));
12957
- const owner = JSON.parse((0, import_node_fs5.readFileSync)((0, import_node_path5.join)(mutex, "owner.json"), "utf8"));
13399
+ const mutex = (0, import_node_path6.join)(project.root, DEPENDENCY_MUTEX);
13400
+ if (!(0, import_node_fs6.existsSync)(mutex)) return;
13401
+ checkTransactionPath(project.root, (0, import_node_path6.join)(mutex, "owner.json"));
13402
+ const owner = JSON.parse((0, import_node_fs6.readFileSync)((0, import_node_path6.join)(mutex, "owner.json"), "utf8"));
12958
13403
  if (!Number.isInteger(owner.pid) || owner.pid < 1)
12959
13404
  throw new Error("Invalid installation owner; inspect the transaction manually");
12960
13405
  try {
@@ -12963,37 +13408,37 @@ async function recoverDependencyInstall(cwd) {
12963
13408
  } catch (error) {
12964
13409
  if (error.code !== "ESRCH") throw error;
12965
13410
  }
12966
- const journal = (0, import_node_path5.join)(mutex, "journal.json");
13411
+ const journal = (0, import_node_path6.join)(mutex, "journal.json");
12967
13412
  checkTransactionPath(project.root, journal);
12968
- if ((0, import_node_fs5.existsSync)(journal)) {
12969
- const records = JSON.parse((0, import_node_fs5.readFileSync)(journal, "utf8"));
13413
+ if ((0, import_node_fs6.existsSync)(journal)) {
13414
+ const records = JSON.parse((0, import_node_fs6.readFileSync)(journal, "utf8"));
12970
13415
  if (!Array.isArray(records)) throw new Error("Invalid recovery journal");
12971
13416
  for (const record of records) {
12972
13417
  if (!record || typeof record.path !== "string" || !record.path || record.path.includes("\\") || record.path.startsWith("/") || record.path.split("/").some((part) => part === ".." || part === "." || part.includes(":")) || record.content !== null && typeof record.content !== "string")
12973
13418
  throw new Error("Invalid recovery journal path");
12974
- checkTransactionPath(project.root, (0, import_node_path5.join)(project.root, record.path));
12975
- checkTransactionPath(project.root, (0, import_node_path5.join)(mutex, "recovery-backup", record.path));
13419
+ checkTransactionPath(project.root, (0, import_node_path6.join)(project.root, record.path));
13420
+ checkTransactionPath(project.root, (0, import_node_path6.join)(mutex, "recovery-backup", record.path));
12976
13421
  }
12977
13422
  for (const record of records) {
12978
- const path = (0, import_node_path5.join)(project.root, record.path);
12979
- if ((0, import_node_fs5.existsSync)(path)) {
12980
- const backup = (0, import_node_path5.join)(mutex, "recovery-backup", record.path);
12981
- (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(backup), { recursive: true });
12982
- if (!(0, import_node_fs5.existsSync)(backup)) (0, import_node_fs5.writeFileSync)(backup, (0, import_node_fs5.readFileSync)(path));
12983
- }
12984
- if (record.content === null) (0, import_node_fs5.rmSync)(path, { force: true });
12985
- else (0, import_node_fs5.writeFileSync)(path, Buffer.from(record.content, "base64"));
13423
+ const path = (0, import_node_path6.join)(project.root, record.path);
13424
+ if ((0, import_node_fs6.existsSync)(path)) {
13425
+ const backup = (0, import_node_path6.join)(mutex, "recovery-backup", record.path);
13426
+ (0, import_node_fs6.mkdirSync)((0, import_node_path6.dirname)(backup), { recursive: true });
13427
+ if (!(0, import_node_fs6.existsSync)(backup)) (0, import_node_fs6.writeFileSync)(backup, (0, import_node_fs6.readFileSync)(path));
13428
+ }
13429
+ if (record.content === null) (0, import_node_fs6.rmSync)(path, { force: true });
13430
+ else (0, import_node_fs6.writeFileSync)(path, Buffer.from(record.content, "base64"));
12986
13431
  }
12987
- if (PACKAGE_MANAGER_LOCKS[project.manager].some((file) => (0, import_node_fs5.existsSync)((0, import_node_path5.join)(project.root, file))))
13432
+ if (!(0, import_node_fs6.existsSync)((0, import_node_path6.join)(mutex, "metadata-only")) && PACKAGE_MANAGER_LOCKS[project.manager].some((file) => (0, import_node_fs6.existsSync)((0, import_node_path6.join)(project.root, file))))
12988
13433
  await run(project, installCommand(project, true), project.root);
12989
13434
  }
12990
- if ((0, import_node_fs5.existsSync)((0, import_node_path5.join)(mutex, "recovery-backup"))) {
12991
- const backup = (0, import_node_path5.join)(project.root, `.octonode-recovery-${Date.now()}`);
12992
- (0, import_node_fs5.renameSync)((0, import_node_path5.join)(mutex, "recovery-backup"), backup);
13435
+ if ((0, import_node_fs6.existsSync)((0, import_node_path6.join)(mutex, "recovery-backup"))) {
13436
+ const backup = (0, import_node_path6.join)(project.root, `.octonode-recovery-${Date.now()}`);
13437
+ (0, import_node_fs6.renameSync)((0, import_node_path6.join)(mutex, "recovery-backup"), backup);
12993
13438
  process.stderr.write(`Pre-recovery files preserved in ${backup}
12994
13439
  `);
12995
13440
  }
12996
- (0, import_node_fs5.rmSync)(mutex, { recursive: true, force: true });
13441
+ (0, import_node_fs6.rmSync)(mutex, { recursive: true, force: true });
12997
13442
  }
12998
13443
  function installProjectDependencies(cwd, dependencies = [], options = {}) {
12999
13444
  return withDependencyInstall(cwd, () => reconcileDependencies(cwd, dependencies, options));
@@ -13002,15 +13447,15 @@ function installProjectDependencies(cwd, dependencies = [], options = {}) {
13002
13447
  // packages/plugin/src/generated-package.ts
13003
13448
  var import_tar = require("tar");
13004
13449
  var import_node_zlib = require("node:zlib");
13450
+ var import_node_crypto4 = require("node:crypto");
13451
+ var import_node_fs8 = require("node:fs");
13452
+ var import_node_os5 = require("node:os");
13453
+ var import_node_path8 = require("node:path");
13454
+
13455
+ // packages/plugin/src/authoring/artifact.ts
13005
13456
  var import_node_crypto3 = require("node:crypto");
13006
13457
  var import_node_fs7 = require("node:fs");
13007
- var import_node_os4 = require("node:os");
13008
13458
  var import_node_path7 = require("node:path");
13009
-
13010
- // packages/plugin/src/authoring/artifact.ts
13011
- var import_node_crypto2 = require("node:crypto");
13012
- var import_node_fs6 = require("node:fs");
13013
- var import_node_path6 = require("node:path");
13014
13459
  var import_schema3 = __toESM(require_dist());
13015
13460
  var import_yaml2 = require("yaml");
13016
13461
 
@@ -13030,18 +13475,18 @@ var RESERVED_ASSETS = /* @__PURE__ */ new Set([
13030
13475
  ]);
13031
13476
 
13032
13477
  // packages/plugin/src/authoring/artifact.ts
13033
- var fileHash = (path) => (0, import_node_crypto2.createHash)("sha256").update((0, import_node_fs6.readFileSync)(path)).digest("hex");
13478
+ var fileHash = (path) => (0, import_node_crypto3.createHash)("sha256").update((0, import_node_fs7.readFileSync)(path)).digest("hex");
13034
13479
  function safePath(path) {
13035
- if (!path || (0, import_node_path6.isAbsolute)(path) || path.includes("\\") || path.split("/").some((part) => !part || part === "." || part === ".." || part.includes(":") || FORBIDDEN_PART.test(part))) {
13480
+ if (!path || (0, import_node_path7.isAbsolute)(path) || path.includes("\\") || path.split("/").some((part) => !part || part === "." || part === ".." || part.includes(":") || FORBIDDEN_PART.test(part))) {
13036
13481
  throw new Error(`Unsafe plugin file: ${path}`);
13037
13482
  }
13038
13483
  }
13039
13484
  function pluginFiles(root, prefix = "") {
13040
- if (!(0, import_node_fs6.lstatSync)((0, import_node_path6.join)(root, prefix)).isDirectory()) throw new Error(`Not a plugin directory: ${root}`);
13041
- return (0, import_node_fs6.readdirSync)((0, import_node_path6.join)(root, prefix)).sort().flatMap((name) => {
13485
+ if (!(0, import_node_fs7.lstatSync)((0, import_node_path7.join)(root, prefix)).isDirectory()) throw new Error(`Not a plugin directory: ${root}`);
13486
+ return (0, import_node_fs7.readdirSync)((0, import_node_path7.join)(root, prefix)).sort().flatMap((name) => {
13042
13487
  const path = prefix ? `${prefix}/${name}` : name;
13043
13488
  safePath(path);
13044
- const stat = (0, import_node_fs6.lstatSync)((0, import_node_path6.join)(root, path));
13489
+ const stat = (0, import_node_fs7.lstatSync)((0, import_node_path7.join)(root, path));
13045
13490
  if (stat.isSymbolicLink()) throw new Error(`Plugin files cannot be symbolic links: ${path}`);
13046
13491
  if (stat.isDirectory()) return pluginFiles(root, path);
13047
13492
  if (!stat.isFile()) throw new Error(`Plugin asset must be a regular file: ${path}`);
@@ -13049,14 +13494,14 @@ function pluginFiles(root, prefix = "") {
13049
13494
  });
13050
13495
  }
13051
13496
  function loadManifest(directory) {
13052
- if ((0, import_node_fs6.lstatSync)(directory).isSymbolicLink()) throw new Error("Plugin directory cannot be a symbolic link");
13053
- const definitions = DEFINITION_FILES.filter((name) => (0, import_node_fs6.existsSync)((0, import_node_path6.join)(directory, name)));
13497
+ if ((0, import_node_fs7.lstatSync)(directory).isSymbolicLink()) throw new Error("Plugin directory cannot be a symbolic link");
13498
+ const definitions = DEFINITION_FILES.filter((name) => (0, import_node_fs7.existsSync)((0, import_node_path7.join)(directory, name)));
13054
13499
  if (definitions.length !== 1) throw new Error("Keep exactly one plugin definition in the package");
13055
- const path = (0, import_node_path6.join)(directory, definitions[0]);
13056
- const stat = (0, import_node_fs6.lstatSync)(path);
13500
+ const path = (0, import_node_path7.join)(directory, definitions[0]);
13501
+ const stat = (0, import_node_fs7.lstatSync)(path);
13057
13502
  if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 1048576)
13058
13503
  throw new Error("Invalid plugin definition file");
13059
- const document = (0, import_yaml2.parseDocument)((0, import_node_fs6.readFileSync)(path, "utf8"), { uniqueKeys: true, version: "1.2" });
13504
+ const document = (0, import_yaml2.parseDocument)((0, import_node_fs7.readFileSync)(path, "utf8"), { uniqueKeys: true, version: "1.2" });
13060
13505
  (0, import_yaml2.visit)(document, {
13061
13506
  Alias() {
13062
13507
  throw new Error("Plugin definitions cannot contain YAML aliases");
@@ -13072,7 +13517,16 @@ function verifyBuild(directory) {
13072
13517
  const manifest2 = loadManifest(directory);
13073
13518
  const files = pluginFiles(directory);
13074
13519
  if (!files.includes(BUILD_RECORD)) throw new Error("Missing build record; run octonodes plugin build first");
13075
- const record = JSON.parse((0, import_node_fs6.readFileSync)((0, import_node_path6.join)(directory, BUILD_RECORD), "utf8"));
13520
+ const record = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_path7.join)(directory, BUILD_RECORD), "utf8"));
13521
+ if (record.runtime) {
13522
+ const runtime = import_schema3.PreparedPluginRuntime.parse(record.runtime);
13523
+ if ((manifest2.integration?.npm || manifest2.integration?.npmDependencies?.length) && !runtime.dependencies)
13524
+ throw new Error("Prepared npm plugins must include their dependency runtime");
13525
+ if (runtime.dependencies && !files.includes(runtime.dependencies))
13526
+ throw new Error("Missing prepared dependency archive");
13527
+ if (runtime.dependencies && (runtime.platform === "portable" || !runtime.arch || runtime.platform === "linux" && !runtime.libc))
13528
+ throw new Error("Prepared dependencies require a platform, architecture and libc identity");
13529
+ }
13076
13530
  if (record.format !== 1 || !record.files || typeof record.files !== "object" || Array.isArray(record.files)) {
13077
13531
  throw new Error("Invalid plugin build record");
13078
13532
  }
@@ -13081,7 +13535,7 @@ function verifyBuild(directory) {
13081
13535
  throw new Error("Plugin files changed since build; rebuild before publishing");
13082
13536
  }
13083
13537
  for (const file of expected) {
13084
- if (record.files[file] !== fileHash((0, import_node_path6.join)(directory, file)))
13538
+ if (record.files[file] !== fileHash((0, import_node_path7.join)(directory, file)))
13085
13539
  throw new Error(`Plugin file changed since build: ${file}`);
13086
13540
  }
13087
13541
  if (!expected.includes("dist/index.js") || !manifest2.nodes.length || manifest2.nodes.some(
@@ -13102,14 +13556,14 @@ function verifyBuild(directory) {
13102
13556
  )
13103
13557
  );
13104
13558
  for (const entry of new Set(ui.map(({ path }) => path))) {
13105
- if (!expected.includes(entry) || (0, import_node_fs6.lstatSync)((0, import_node_path6.join)(directory, entry)).size > import_schema3.PLUGIN_UI_BUNDLE_MAX_BYTES)
13559
+ if (!expected.includes(entry) || (0, import_node_fs7.lstatSync)((0, import_node_path7.join)(directory, entry)).size > import_schema3.PLUGIN_UI_BUNDLE_MAX_BYTES)
13106
13560
  throw new Error(`Invalid plugin UI bundle: ${entry}`);
13107
13561
  }
13108
13562
  if (JSON.stringify(record.ui ?? []) !== JSON.stringify(
13109
13563
  ui.map((declaration) => ({
13110
13564
  ...declaration,
13111
- size: (0, import_node_fs6.lstatSync)((0, import_node_path6.join)(directory, declaration.path)).size,
13112
- sha256: fileHash((0, import_node_path6.join)(directory, declaration.path))
13565
+ size: (0, import_node_fs7.lstatSync)((0, import_node_path7.join)(directory, declaration.path)).size,
13566
+ sha256: fileHash((0, import_node_path7.join)(directory, declaration.path))
13113
13567
  }))
13114
13568
  ))
13115
13569
  throw new Error("Invalid plugin UI build record");
@@ -13134,41 +13588,41 @@ async function generatePluginPackage(cwd, selections, expectedPacker) {
13134
13588
  const packer = "tar@7.5.22/portable-v1";
13135
13589
  if (expectedPacker && expectedPacker !== packer)
13136
13590
  throw new Error(`Plugin package restore requires ${expectedPacker}; found ${packer}`);
13137
- const digest = (0, import_node_crypto3.createHash)("sha256").update(
13591
+ const digest = (0, import_node_crypto4.createHash)("sha256").update(
13138
13592
  JSON.stringify({
13139
13593
  format: GENERATED_PLUGIN_FORMAT,
13140
13594
  packer,
13141
13595
  selections: selected.map(({ alias, sha256 }) => ({ alias, sha256 }))
13142
13596
  })
13143
13597
  ).digest("hex");
13144
- const stage = (0, import_node_fs7.mkdtempSync)((0, import_node_path7.join)((0, import_node_os4.tmpdir)(), "octonode-package-"));
13598
+ const stage = (0, import_node_fs8.mkdtempSync)((0, import_node_path8.join)((0, import_node_os5.tmpdir)(), "octonode-package-"));
13145
13599
  try {
13146
- const packageRoot = (0, import_node_path7.join)(stage, "package");
13147
- (0, import_node_fs7.mkdirSync)(packageRoot);
13600
+ const packageRoot = (0, import_node_path8.join)(stage, "package");
13601
+ (0, import_node_fs8.mkdirSync)(packageRoot);
13148
13602
  for (const { alias, directory, sha256 } of selected) {
13149
- if ((0, import_node_fs7.lstatSync)(directory).isSymbolicLink())
13603
+ if ((0, import_node_fs8.lstatSync)(directory).isSymbolicLink())
13150
13604
  throw new Error(`Plugin directory cannot be a symbolic link: ${alias}`);
13151
- const snapshot = (0, import_node_path7.join)(stage, "snapshots", alias);
13152
- (0, import_node_fs7.mkdirSync)(snapshot, { recursive: true });
13605
+ const snapshot = (0, import_node_path8.join)(stage, "snapshots", alias);
13606
+ (0, import_node_fs8.mkdirSync)(snapshot, { recursive: true });
13153
13607
  for (const file of pluginBundleFiles(directory)) {
13154
13608
  safePath(file);
13155
- (0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)((0, import_node_path7.join)(snapshot, file)), { recursive: true });
13156
- (0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(snapshot, file), (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(directory, file)));
13609
+ (0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)((0, import_node_path8.join)(snapshot, file)), { recursive: true });
13610
+ (0, import_node_fs8.writeFileSync)((0, import_node_path8.join)(snapshot, file), (0, import_node_fs8.readFileSync)((0, import_node_path8.join)(directory, file)));
13157
13611
  }
13158
13612
  if (hashPluginDir(snapshot) !== sha256) throw new Error(`Plugin integrity mismatch: ${alias}`);
13159
- const library = (0, import_node_path7.join)(snapshot, "library");
13160
- if (!(0, import_node_fs7.existsSync)((0, import_node_path7.join)(library, "index.js")) || !(0, import_node_fs7.existsSync)((0, import_node_path7.join)(library, "index.d.ts")))
13613
+ const library = (0, import_node_path8.join)(snapshot, "library");
13614
+ if (!(0, import_node_fs8.existsSync)((0, import_node_path8.join)(library, "index.js")) || !(0, import_node_fs8.existsSync)((0, import_node_path8.join)(library, "index.d.ts")))
13161
13615
  throw new Error(`Plugin ${alias} has no importable library; rebuild with a compatible CLI`);
13162
13616
  for (const file of pluginFiles(library)) {
13163
- const target = (0, import_node_path7.join)(packageRoot, alias, file);
13164
- (0, import_node_fs7.mkdirSync)((0, import_node_path7.dirname)(target), { recursive: true });
13165
- (0, import_node_fs7.writeFileSync)(target, (0, import_node_fs7.readFileSync)((0, import_node_path7.join)(library, file)));
13166
- (0, import_node_fs7.chmodSync)(target, 420);
13617
+ const target = (0, import_node_path8.join)(packageRoot, alias, file);
13618
+ (0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(target), { recursive: true });
13619
+ (0, import_node_fs8.writeFileSync)(target, (0, import_node_fs8.readFileSync)((0, import_node_path8.join)(library, file)));
13620
+ (0, import_node_fs8.chmodSync)(target, 420);
13167
13621
  }
13168
13622
  }
13169
13623
  const subpaths = selected.map(({ alias }) => `./${alias}`);
13170
- (0, import_node_fs7.writeFileSync)(
13171
- (0, import_node_path7.join)(packageRoot, "package.json"),
13624
+ (0, import_node_fs8.writeFileSync)(
13625
+ (0, import_node_path8.join)(packageRoot, "package.json"),
13172
13626
  JSON.stringify(
13173
13627
  {
13174
13628
  name: GENERATED_PLUGIN_PACKAGE,
@@ -13183,8 +13637,8 @@ async function generatePluginPackage(cwd, selections, expectedPacker) {
13183
13637
  2
13184
13638
  ) + "\n"
13185
13639
  );
13186
- (0, import_node_fs7.chmodSync)((0, import_node_path7.join)(packageRoot, "package.json"), 420);
13187
- const archive = (0, import_node_path7.join)(stage, "package.tar");
13640
+ (0, import_node_fs8.chmodSync)((0, import_node_path8.join)(packageRoot, "package.json"), 420);
13641
+ const archive = (0, import_node_path8.join)(stage, "package.tar");
13188
13642
  (0, import_tar.create)(
13189
13643
  {
13190
13644
  file: archive,
@@ -13197,24 +13651,24 @@ async function generatePluginPackage(cwd, selections, expectedPacker) {
13197
13651
  },
13198
13652
  pluginFiles(packageRoot)
13199
13653
  );
13200
- const bytes = (0, import_node_zlib.gzipSync)((0, import_node_fs7.readFileSync)(archive), { level: 0 });
13654
+ const bytes = (0, import_node_zlib.gzipSync)((0, import_node_fs8.readFileSync)(archive), { level: 0 });
13201
13655
  bytes[9] = 255;
13202
- const output = (0, import_node_path7.join)(cwd, GENERATED_PLUGIN_DIRECTORY);
13203
- (0, import_node_fs7.mkdirSync)(output, { recursive: true });
13204
- if ((0, import_node_fs7.lstatSync)(output).isSymbolicLink()) throw new Error("Generated plugin directory cannot be a symbolic link");
13205
- const ignore = (0, import_node_path7.join)(output, ".gitignore");
13206
- if (!(0, import_node_fs7.existsSync)(ignore)) (0, import_node_fs7.writeFileSync)(ignore, "*\n", { flag: "wx" });
13207
- const destination = (0, import_node_path7.join)(output, `plugin-${digest}.tgz`);
13208
- if ((0, import_node_fs7.existsSync)(destination)) {
13209
- if ((0, import_node_fs7.lstatSync)(destination).isSymbolicLink() || !(0, import_node_fs7.readFileSync)(destination).equals(bytes))
13656
+ const output = (0, import_node_path8.join)(cwd, GENERATED_PLUGIN_DIRECTORY);
13657
+ (0, import_node_fs8.mkdirSync)(output, { recursive: true });
13658
+ if ((0, import_node_fs8.lstatSync)(output).isSymbolicLink()) throw new Error("Generated plugin directory cannot be a symbolic link");
13659
+ const ignore = (0, import_node_path8.join)(output, ".gitignore");
13660
+ if (!(0, import_node_fs8.existsSync)(ignore)) (0, import_node_fs8.writeFileSync)(ignore, "*\n", { flag: "wx" });
13661
+ const destination = (0, import_node_path8.join)(output, `plugin-${digest}.tgz`);
13662
+ if ((0, import_node_fs8.existsSync)(destination)) {
13663
+ if ((0, import_node_fs8.lstatSync)(destination).isSymbolicLink() || !(0, import_node_fs8.readFileSync)(destination).equals(bytes))
13210
13664
  throw new Error("Generated plugin package changed; remove the corrupt generated file and restore again");
13211
13665
  } else {
13212
- const temporary = (0, import_node_fs7.mkdtempSync)((0, import_node_path7.join)(output, ".pack-"));
13666
+ const temporary = (0, import_node_fs8.mkdtempSync)((0, import_node_path8.join)(output, ".pack-"));
13213
13667
  try {
13214
- (0, import_node_fs7.writeFileSync)((0, import_node_path7.join)(temporary, "package.tgz"), bytes);
13215
- (0, import_node_fs7.renameSync)((0, import_node_path7.join)(temporary, "package.tgz"), destination);
13668
+ (0, import_node_fs8.writeFileSync)((0, import_node_path8.join)(temporary, "package.tgz"), bytes);
13669
+ (0, import_node_fs8.renameSync)((0, import_node_path8.join)(temporary, "package.tgz"), destination);
13216
13670
  } finally {
13217
- (0, import_node_fs7.rmSync)(temporary, { recursive: true, force: true });
13671
+ (0, import_node_fs8.rmSync)(temporary, { recursive: true, force: true });
13218
13672
  }
13219
13673
  }
13220
13674
  return {
@@ -13222,10 +13676,10 @@ async function generatePluginPackage(cwd, selections, expectedPacker) {
13222
13676
  spec: `file:./${GENERATED_PLUGIN_DIRECTORY}/plugin-${digest}.tgz`,
13223
13677
  subpaths,
13224
13678
  packer,
13225
- sha256: `sha256:${(0, import_node_crypto3.createHash)("sha256").update(bytes).digest("hex")}`
13679
+ sha256: `sha256:${(0, import_node_crypto4.createHash)("sha256").update(bytes).digest("hex")}`
13226
13680
  };
13227
13681
  } finally {
13228
- (0, import_node_fs7.rmSync)(stage, { recursive: true, force: true });
13682
+ (0, import_node_fs8.rmSync)(stage, { recursive: true, force: true });
13229
13683
  }
13230
13684
  }
13231
13685
 
@@ -13234,6 +13688,42 @@ function pluginNpmDependencies(manifest2) {
13234
13688
  return manifest2.integration?.npmDependencies ?? (manifest2.integration?.npm ? [manifest2.integration.npm] : []);
13235
13689
  }
13236
13690
 
13691
+ // packages/plugin/src/prepared/invocation.ts
13692
+ var import_common3 = __toESM(require_dist2());
13693
+ var import_schema5 = __toESM(require_dist());
13694
+
13695
+ // packages/plugin/src/prepared/runtime.ts
13696
+ var import_node_fs9 = require("node:fs");
13697
+ var import_node_path9 = require("node:path");
13698
+ var import_tar2 = require("tar");
13699
+ var import_common2 = __toESM(require_dist2());
13700
+ var import_schema4 = __toESM(require_dist());
13701
+ function preparedRuntime(directory) {
13702
+ const path = (0, import_node_path9.join)(directory, BUILD_RECORD);
13703
+ if (!(0, import_node_fs9.existsSync)(path)) return;
13704
+ const record = JSON.parse((0, import_node_fs9.readFileSync)(path, "utf8"));
13705
+ if (record.runtime === void 0) return;
13706
+ return import_schema4.PreparedPluginRuntime.parse(record.runtime);
13707
+ }
13708
+ function assertPreparedRuntime(runtime) {
13709
+ const libc = process.platform === "linux" && process.report.getReport().header?.glibcVersionRuntime ? "glibc" : "musl";
13710
+ if (Number(process.versions.node.split(".")[0]) !== runtime.nodeMajor || runtime.platform !== "portable" && (runtime.platform !== process.platform || runtime.arch !== process.arch || runtime.platform === "linux" && runtime.libc !== libc))
13711
+ throw new Error("Prepared plugin runtime is incompatible; rebuild for this Node/platform/architecture/libc");
13712
+ }
13713
+
13714
+ // packages/plugin/src/prepared/invocation.ts
13715
+ function usesPreparedRunner(directory) {
13716
+ if (import_common3.PROCESS_ENV.OCTONODE_PLUGIN_RUNNER !== "true") return false;
13717
+ const runtime = preparedRuntime(directory);
13718
+ if (!runtime) return false;
13719
+ assertPreparedRuntime(runtime);
13720
+ const { manifest: manifest2 } = verifyBuild(directory);
13721
+ return !manifest2.permissions.some((permission) => permission.resource === "project_data");
13722
+ }
13723
+ function preparedPluginPin(directory, pin) {
13724
+ return !!pin?.installId && !!pin.registry && pin.registry.replace(/\/$/, "") === ((0, import_common3.OCTONODE_MARKETPLACE_URL)() ?? import_common3.DEFAULT_MARKETPLACE_URL).replace(/\/$/, "") && usesPreparedRunner(directory);
13725
+ }
13726
+
13237
13727
  // packages/plugin/src/lifecycle.ts
13238
13728
  async function reconcilePluginLock(cwd, next, options = {}) {
13239
13729
  const project = resolveDependencyProject(cwd);
@@ -13243,21 +13733,21 @@ async function reconcilePluginLock(cwd, next, options = {}) {
13243
13733
  const manifest2 = loadPluginManifest(directory);
13244
13734
  if (manifest2.id !== (pin.pluginId ?? alias) || manifest2.version !== pin.version || hashPluginDir(directory) !== pin.sha256)
13245
13735
  throw new Error(`Plugin identity or integrity mismatch: ${alias}`);
13246
- return { alias, directory, sha256: pin.sha256, manifest: manifest2 };
13736
+ return { alias, directory, sha256: pin.sha256, manifest: manifest2, remote: preparedPluginPin(directory, pin) };
13247
13737
  });
13248
13738
  const libraries = loaded.filter((item) => item.manifest.library);
13249
13739
  const generated = libraries.length ? await generatePluginPackage(project.target, libraries) : void 0;
13250
- const packageFile = (0, import_node_path8.join)(project.target, "package.json");
13251
- const packageJson = (0, import_node_fs8.existsSync)(packageFile) ? JSON.parse((0, import_node_fs8.readFileSync)(packageFile, "utf8")) : {};
13740
+ const packageFile = (0, import_node_path10.join)(project.target, "package.json");
13741
+ const packageJson = (0, import_node_fs10.existsSync)(packageFile) ? JSON.parse((0, import_node_fs10.readFileSync)(packageFile, "utf8")) : {};
13252
13742
  const declared = { ...packageJson.devDependencies, ...packageJson.optionalDependencies, ...packageJson.dependencies };
13253
13743
  if (generated && declared[generated.name] && declared[generated.name].replace(/^file:\.\//, "file:") !== before.generated?.spec.replace(/^file:\.\//, "file:") && !options.migrate)
13254
13744
  throw new Error(
13255
13745
  "Existing @octonodes/plugin dependency is not managed by this lock; pass --migrate to replace it explicitly"
13256
13746
  );
13257
- const dependencies = loaded.flatMap(
13747
+ const dependencies = loaded.filter((item) => !item.remote || item.manifest.library).flatMap(
13258
13748
  ({ manifest: manifest2 }) => pluginNpmDependencies(manifest2).map((npm) => {
13259
13749
  const spec = npm.spec.startsWith(`${npm.package}@`) ? npm.spec.slice(npm.package.length + 1) : npm.spec;
13260
- const sameLocal = typeof declared[npm.package] === "string" && declared[npm.package].startsWith("file:") && spec.startsWith("file:") && (0, import_node_path8.resolve)(project.target, declared[npm.package].slice(5)) === (0, import_node_path8.resolve)(project.target, spec.slice(5));
13750
+ const sameLocal = typeof declared[npm.package] === "string" && declared[npm.package].startsWith("file:") && spec.startsWith("file:") && (0, import_node_path10.resolve)(project.target, declared[npm.package].slice(5)) === (0, import_node_path10.resolve)(project.target, spec.slice(5));
13261
13751
  if (declared[npm.package] && declared[npm.package] !== spec && !sameLocal)
13262
13752
  throw new Error(
13263
13753
  `Dependency conflict: ${npm.package} is ${declared[npm.package]}, plugin requires ${spec}; reconcile it explicitly first`
@@ -13276,6 +13766,7 @@ async function reconcilePluginLock(cwd, next, options = {}) {
13276
13766
  if (generated || before.generated || unique.length || options.commit) {
13277
13767
  await reconcileDependencies(project.target, [...unique, ...generated ? [generated] : []], {
13278
13768
  cacheRoot: options.cacheRoot,
13769
+ metadataOnly: !generated && !before.generated && !unique.length,
13279
13770
  remove: !generated && before.generated ? [before.generated.name] : void 0,
13280
13771
  transactionFiles: [lockfilePath(cwd), ...options.transactionFiles ?? []],
13281
13772
  commit: () => {
@@ -13318,20 +13809,20 @@ async function removePluginPin(alias, options = {}) {
13318
13809
  }
13319
13810
 
13320
13811
  // packages/plugin/src/restore.ts
13321
- var import_node_fs12 = require("node:fs");
13322
- var import_node_path12 = require("node:path");
13812
+ var import_node_fs14 = require("node:fs");
13813
+ var import_node_path14 = require("node:path");
13323
13814
 
13324
- // packages/plugin/src/remote-registry.ts
13325
- var import_node_fs11 = require("node:fs");
13326
- var import_node_os5 = require("node:os");
13327
- var import_node_path11 = require("node:path");
13815
+ // packages/plugin/src/remote/registry.ts
13816
+ var import_node_fs13 = require("node:fs");
13817
+ var import_node_os6 = require("node:os");
13818
+ var import_node_path13 = require("node:path");
13328
13819
 
13329
13820
  // packages/plugin/src/install.ts
13330
- var import_node_fs9 = require("node:fs");
13331
- var import_node_path9 = require("node:path");
13821
+ var import_node_fs11 = require("node:fs");
13822
+ var import_node_path11 = require("node:path");
13332
13823
  var import_node_child_process2 = require("node:child_process");
13333
13824
  var import_node_util2 = require("node:util");
13334
- var import_common2 = __toESM(require_dist3());
13825
+ var import_common4 = __toESM(require_dist2());
13335
13826
  function commandError(err) {
13336
13827
  const error = err;
13337
13828
  return [error.stderr, error.stdout].map((output) => output?.toString().trim()).find(Boolean) ?? error.message;
@@ -13347,7 +13838,7 @@ async function installNodeDeps(dir, opts = {}) {
13347
13838
  if (opts.cacheOnly) {
13348
13839
  for (const npm of dependencies)
13349
13840
  await (0, import_node_util2.promisify)(import_node_child_process2.execFile)("npm", ["cache", "add", "--ignore-scripts", "--", npm.spec], {
13350
- env: (0, import_common2.childProcessEnvironment)((0, import_common2.packageCacheEnvironment)(dependencyCacheRoot(opts.cacheRoot))),
13841
+ env: (0, import_common4.childProcessEnvironment)((0, import_common4.packageCacheEnvironment)(dependencyCacheRoot(opts.cacheRoot))),
13351
13842
  timeout: 10 * 6e4
13352
13843
  });
13353
13844
  return {};
@@ -13364,9 +13855,9 @@ async function installNodeDeps(dir, opts = {}) {
13364
13855
  return { depsInstalled: false, depsError: commandError(err) };
13365
13856
  }
13366
13857
  }
13367
- const pjPath = (0, import_node_path9.join)(dir, "package.json");
13368
- if (!(0, import_node_fs9.existsSync)(pjPath)) return {};
13369
- const pj = JSON.parse((0, import_node_fs9.readFileSync)(pjPath, "utf8"));
13858
+ const pjPath = (0, import_node_path11.join)(dir, "package.json");
13859
+ if (!(0, import_node_fs11.existsSync)(pjPath)) return {};
13860
+ const pj = JSON.parse((0, import_node_fs11.readFileSync)(pjPath, "utf8"));
13370
13861
  if (!pj.dependencies && !pj.optionalDependencies) return {};
13371
13862
  try {
13372
13863
  await (0, import_node_util2.promisify)(import_node_child_process2.execFile)(
@@ -13382,7 +13873,7 @@ async function installNodeDeps(dir, opts = {}) {
13382
13873
  ],
13383
13874
  {
13384
13875
  cwd: dir,
13385
- env: (0, import_common2.childProcessEnvironment)((0, import_common2.packageCacheEnvironment)(dependencyCacheRoot(opts.cacheRoot))),
13876
+ env: (0, import_common4.childProcessEnvironment)((0, import_common4.packageCacheEnvironment)(dependencyCacheRoot(opts.cacheRoot))),
13386
13877
  timeout: 10 * 6e4
13387
13878
  }
13388
13879
  );
@@ -13392,7 +13883,7 @@ async function installNodeDeps(dir, opts = {}) {
13392
13883
  }
13393
13884
  }
13394
13885
  async function installPlugin(src, opts = {}) {
13395
- if (!(0, import_node_fs9.existsSync)(src) || !(0, import_node_fs9.statSync)(src).isDirectory()) {
13886
+ if (!(0, import_node_fs11.existsSync)(src) || !(0, import_node_fs11.statSync)(src).isDirectory()) {
13396
13887
  throw new Error(`source "${src}" is not a directory`);
13397
13888
  }
13398
13889
  if (!isPluginDir(src)) {
@@ -13400,17 +13891,17 @@ async function installPlugin(src, opts = {}) {
13400
13891
  }
13401
13892
  const { manifest: manifest2 } = loadPlugin(src);
13402
13893
  const { root, source } = installRoot(opts);
13403
- const dest = (0, import_node_path9.join)(root, manifest2.id);
13404
- if ((0, import_node_fs9.existsSync)(dest)) {
13894
+ const dest = (0, import_node_path11.join)(root, manifest2.id);
13895
+ if ((0, import_node_fs11.existsSync)(dest)) {
13405
13896
  if (!opts.force) {
13406
13897
  throw new Error(`plugin "${manifest2.id}" is already installed at ${dest} (use --force to overwrite)`);
13407
13898
  }
13408
13899
  }
13409
13900
  const deps = pluginNpmDependencies(manifest2).length ? await installNodeDeps(src, { ...opts, cacheOnly: opts.cacheOnly ?? !opts.project }) : {};
13410
13901
  if (deps.depsError) return { manifest: manifest2, dir: dest, source, ...deps };
13411
- if ((0, import_node_fs9.existsSync)(dest)) (0, import_node_fs9.rmSync)(dest, { recursive: true, force: true });
13412
- (0, import_node_fs9.mkdirSync)(root, { recursive: true });
13413
- (0, import_node_fs9.cpSync)(src, dest, { recursive: true });
13902
+ if ((0, import_node_fs11.existsSync)(dest)) (0, import_node_fs11.rmSync)(dest, { recursive: true, force: true });
13903
+ (0, import_node_fs11.mkdirSync)(root, { recursive: true });
13904
+ (0, import_node_fs11.cpSync)(src, dest, { recursive: true });
13414
13905
  return {
13415
13906
  manifest: manifest2,
13416
13907
  dir: dest,
@@ -13420,14 +13911,14 @@ async function installPlugin(src, opts = {}) {
13420
13911
  };
13421
13912
  }
13422
13913
 
13423
- // packages/plugin/src/remote-registry.ts
13424
- var import_common3 = __toESM(require_dist3());
13914
+ // packages/plugin/src/remote/registry.ts
13915
+ var import_common5 = __toESM(require_dist2());
13425
13916
 
13426
13917
  // packages/plugin/src/archive.ts
13427
13918
  var import_node_zlib2 = require("node:zlib");
13428
- var import_node_fs10 = require("node:fs");
13429
- var import_node_path10 = require("node:path");
13430
- var import_tar2 = require("tar");
13919
+ var import_node_fs12 = require("node:fs");
13920
+ var import_node_path12 = require("node:path");
13921
+ var import_tar3 = require("tar");
13431
13922
 
13432
13923
  // packages/plugin/src/archive.constants.ts
13433
13924
  var PLUGIN_ARCHIVE_MAX_BYTES = 64 * 1024 * 1024;
@@ -13437,11 +13928,11 @@ var PLUGIN_ARCHIVE_MAX_FILES = 1e4;
13437
13928
  function extractPluginArchive(bytes, stage) {
13438
13929
  if (bytes.length > PLUGIN_ARCHIVE_MAX_BYTES) throw new Error("Plugin archive exceeds download limit");
13439
13930
  const decoded = (0, import_node_zlib2.gunzipSync)(bytes, { maxOutputLength: PLUGIN_ARCHIVE_MAX_BYTES });
13440
- const archive = (0, import_node_path10.join)(stage, "bundle.tar");
13441
- (0, import_node_fs10.writeFileSync)(archive, decoded);
13931
+ const archive = (0, import_node_path12.join)(stage, "bundle.tar");
13932
+ (0, import_node_fs12.writeFileSync)(archive, decoded);
13442
13933
  const seen = /* @__PURE__ */ new Set();
13443
13934
  let total = 0;
13444
- (0, import_tar2.list)({
13935
+ (0, import_tar3.list)({
13445
13936
  file: archive,
13446
13937
  sync: true,
13447
13938
  strict: true,
@@ -13458,9 +13949,9 @@ function extractPluginArchive(bytes, stage) {
13458
13949
  throw new Error("Plugin archive exceeds extraction limits");
13459
13950
  }
13460
13951
  });
13461
- const output = (0, import_node_path10.join)(stage, "plugin");
13462
- (0, import_node_fs10.mkdirSync)(output);
13463
- (0, import_tar2.extract)({
13952
+ const output = (0, import_node_path12.join)(stage, "plugin");
13953
+ (0, import_node_fs12.mkdirSync)(output);
13954
+ (0, import_tar3.extract)({
13464
13955
  file: archive,
13465
13956
  cwd: output,
13466
13957
  sync: true,
@@ -13473,11 +13964,12 @@ function extractPluginArchive(bytes, stage) {
13473
13964
  return output;
13474
13965
  }
13475
13966
 
13476
- // packages/plugin/src/remote-registry.ts
13477
- var import_tar3 = require("tar");
13967
+ // packages/plugin/src/remote/registry.ts
13968
+ var import_tar4 = require("tar");
13969
+ var import_node_crypto5 = require("node:crypto");
13478
13970
  var RemoteRegistry = class {
13479
13971
  constructor(opts = {}) {
13480
- const baseUrl = opts.baseUrl ?? (0, import_common3.OCTONODE_MARKETPLACE_URL)();
13972
+ const baseUrl = opts.baseUrl ?? (0, import_common5.OCTONODE_MARKETPLACE_URL)() ?? import_common5.DEFAULT_MARKETPLACE_URL;
13481
13973
  if (!baseUrl) {
13482
13974
  throw new Error("marketplace URL is not configured (set OCTONODE_MARKETPLACE_URL or pass baseUrl)");
13483
13975
  }
@@ -13487,7 +13979,7 @@ var RemoteRegistry = class {
13487
13979
  );
13488
13980
  }
13489
13981
  this.baseUrl = baseUrl.replace(/\/$/, "");
13490
- this.token = opts.token ?? (0, import_common3.OCTONODE_MARKETPLACE_TOKEN)();
13982
+ this.token = opts.token ?? (0, import_common5.OCTONODE_MARKETPLACE_TOKEN)();
13491
13983
  }
13492
13984
  async request(path, init = {}) {
13493
13985
  const headers = new Headers(init.headers);
@@ -13504,19 +13996,49 @@ var RemoteRegistry = class {
13504
13996
  }
13505
13997
  return res;
13506
13998
  }
13999
+ async searchPage(filter = {}, offset = 0, limit = 24) {
14000
+ const params = new URLSearchParams({ offset: String(offset), limit: String(limit) });
14001
+ for (const [key, value] of Object.entries(filter)) if (value !== void 0) params.set(key, value);
14002
+ const res = await this.request(`/marketplace/plugins?${params}`);
14003
+ return await res.json();
14004
+ }
13507
14005
  async search(filter = {}) {
13508
- const params = new URLSearchParams();
13509
- for (const [key, value] of Object.entries(filter)) if (value) params.set(key, value);
13510
- const query = params.size > 0 ? `?${params}` : "";
13511
- const res = await this.request(`/marketplace/plugins${query}`);
13512
- const body = await res.json();
13513
- return body.entries;
14006
+ let entries = [];
14007
+ for (; ; ) {
14008
+ const page = await this.searchPage(filter, entries.length, 100);
14009
+ entries = [...entries, ...page.entries];
14010
+ if (!page.entries.length || page.total === void 0 || entries.length >= page.total) return entries;
14011
+ }
13514
14012
  }
13515
14013
  async detail(id, scope) {
13516
14014
  const query = scope ? `?scope=${encodeURIComponent(scope)}` : "";
13517
14015
  const res = await this.request(`/marketplace/plugins/${encodeURIComponent(id)}${query}`);
13518
14016
  return await res.json();
13519
14017
  }
14018
+ /** Reauthorize an immutable pin, including older locks that only recorded the extracted tree hash. */
14019
+ async matchesLockedRelease(id, pin) {
14020
+ const remoteId = pin.remoteId ?? id;
14021
+ const detail = await this.detail(remoteId, pin.scope);
14022
+ const release2 = detail.versions.find((entry) => entry.version === pin.version);
14023
+ if (!release2 || pin.publisher && release2.publishedBy !== pin.publisher) return false;
14024
+ const checksum = release2.archiveSha256 ?? release2.sha256;
14025
+ if (pin.archiveSha256 || checksum?.startsWith("sha256:")) return checksum === (pin.archiveSha256 ?? pin.sha256);
14026
+ if (!/^[a-f0-9]{64}$/.test(checksum)) return false;
14027
+ const directory = await this.downloadBundle(remoteId, pin.version, pin.scope, id, checksum);
14028
+ try {
14029
+ return hashPluginDir(directory) === pin.sha256;
14030
+ } finally {
14031
+ (0, import_node_fs13.rmSync)((0, import_node_path13.dirname)(directory), { recursive: true, force: true });
14032
+ }
14033
+ }
14034
+ async registerInstall(id, version, scope, opts) {
14035
+ const response = await this.request(`/marketplace/plugins/${encodeURIComponent(id)}/install`, {
14036
+ method: "POST",
14037
+ headers: { "content-type": "application/json" },
14038
+ body: JSON.stringify({ version, scope, ownerId: opts.ownerId, environment: opts.environment ?? "self_hosted" })
14039
+ });
14040
+ return (await response.json()).installId;
14041
+ }
13520
14042
  /**
13521
14043
  * Pack a plugin folder into a tarball and publish it (metadata + bundle).
13522
14044
  * `orgId` names the org an org/group-scoped version belongs to; `teamId`
@@ -13526,6 +14048,13 @@ var RemoteRegistry = class {
13526
14048
  */
13527
14049
  async publish(pluginDir, opts = {}) {
13528
14050
  const manifest2 = loadPluginManifest(pluginDir);
14051
+ const packageFile = (0, import_node_path13.join)(pluginDir, "package.json");
14052
+ if ((0, import_node_fs13.existsSync)(packageFile)) {
14053
+ const metadata = JSON.parse((0, import_node_fs13.readFileSync)(packageFile, "utf8"));
14054
+ if (metadata.version && metadata.version !== manifest2.version) {
14055
+ throw new Error("package.json version must match the plugin version before publishing");
14056
+ }
14057
+ }
13529
14058
  const bundle = packPluginDir(pluginDir);
13530
14059
  const form = new FormData();
13531
14060
  form.set("manifest", JSON.stringify(manifest2));
@@ -13536,10 +14065,14 @@ var RemoteRegistry = class {
13536
14065
  method: "POST",
13537
14066
  body: form
13538
14067
  });
13539
- return await res.json();
14068
+ const release2 = await res.json();
14069
+ const digest = (0, import_node_crypto5.createHash)("sha256").update(bundle).digest("hex");
14070
+ if (release2.sha256 !== digest || release2.archiveSha256 !== void 0 && release2.archiveSha256 !== digest)
14071
+ throw new Error("Marketplace published archive checksum mismatch; check the release before publishing again");
14072
+ return release2;
13540
14073
  }
13541
14074
  /** Download a version's bundle and extract it to a fresh temp folder. */
13542
- async downloadBundle(id, version, scope, expectedId = id) {
14075
+ async downloadBundle(id, version, scope, expectedId = id, expectedChecksum) {
13543
14076
  const query = scope ? `?scope=${encodeURIComponent(scope)}` : "";
13544
14077
  const res = await this.request(
13545
14078
  `/marketplace/plugins/${encodeURIComponent(id)}/versions/${encodeURIComponent(version)}/bundle${query}`
@@ -13560,17 +14093,27 @@ var RemoteRegistry = class {
13560
14093
  await reader.cancel();
13561
14094
  }
13562
14095
  const bytes = Buffer.concat(chunks);
13563
- const dir = (0, import_node_fs11.mkdtempSync)((0, import_node_path11.join)((0, import_node_os5.tmpdir)(), "octo-bundle-"));
14096
+ const archiveChecksum = res.headers.get("x-octonode-archive-sha256");
14097
+ if (archiveChecksum !== null && !/^[a-f0-9]{64}$/.test(archiveChecksum))
14098
+ throw new Error("Plugin registry archive checksum is invalid");
14099
+ const checksums = [archiveChecksum, res.headers.get("x-octonode-sha256"), expectedChecksum].filter(
14100
+ (checksum) => !!checksum
14101
+ );
14102
+ const digest = (0, import_node_crypto5.createHash)("sha256").update(bytes).digest("hex");
14103
+ for (const expected of checksums)
14104
+ if (/^[a-f0-9]{64}$/.test(expected) && expected !== digest) throw new Error("Plugin registry checksum mismatch");
14105
+ const dir = (0, import_node_fs13.mkdtempSync)((0, import_node_path13.join)((0, import_node_os6.tmpdir)(), "octo-bundle-"));
13564
14106
  try {
13565
14107
  const extracted = extractPluginArchive(bytes, dir);
13566
14108
  const manifest2 = loadPluginManifest(extracted);
13567
14109
  if (manifest2.id !== expectedId || manifest2.version !== version)
13568
14110
  throw new Error(`Downloaded plugin does not match requested ${id}@${version}`);
13569
- const checksum = res.headers.get("x-octonode-sha256");
13570
- if (checksum && hashPluginDir(extracted) !== checksum) throw new Error("Plugin registry checksum mismatch");
14111
+ for (const expected of checksums)
14112
+ if (!/^[a-f0-9]{64}$/.test(expected) && hashPluginDir(extracted) !== expected)
14113
+ throw new Error("Plugin registry checksum mismatch");
13571
14114
  return extracted;
13572
14115
  } catch (error) {
13573
- (0, import_node_fs11.rmSync)(dir, { recursive: true, force: true });
14116
+ (0, import_node_fs13.rmSync)(dir, { recursive: true, force: true });
13574
14117
  throw error;
13575
14118
  }
13576
14119
  }
@@ -13582,7 +14125,14 @@ var RemoteRegistry = class {
13582
14125
  async install(id, opts = {}) {
13583
14126
  const detail = await this.detail(id, opts.scope);
13584
14127
  const version = opts.version ?? detail.version;
13585
- const dir = await this.downloadBundle(id, version, opts.scope);
14128
+ const release2 = detail.versions?.find((entry) => entry.version === version);
14129
+ const dir = await this.downloadBundle(
14130
+ id,
14131
+ version,
14132
+ opts.scope,
14133
+ id,
14134
+ release2?.archiveSha256 ?? release2?.sha256
14135
+ );
13586
14136
  const plugin = await installPlugin(dir, opts);
13587
14137
  const res = await this.request(`/marketplace/plugins/${encodeURIComponent(id)}/install`, {
13588
14138
  method: "POST",
@@ -13608,19 +14158,21 @@ var RemoteRegistry = class {
13608
14158
  const detail = await this.detail(id, opts.scope);
13609
14159
  const version = opts.version ?? detail.version;
13610
14160
  const versionMeta = detail.versions?.find((v) => v.version === version);
13611
- if (!opts.cacheOnly && !opts.noDeps && !versionMeta?.sha256)
14161
+ const releaseChecksum = versionMeta?.archiveSha256 ?? versionMeta?.sha256;
14162
+ if (!opts.cacheOnly && !opts.noDeps && !releaseChecksum)
13612
14163
  throw new Error("Marketplace release is missing its immutable checksum");
13613
14164
  const availableScopes = versionMeta?.scope ?? detail.scope;
13614
14165
  const scope = opts.scope && availableScopes.includes(opts.scope) ? opts.scope : availableScopes[0];
13615
- const dir = await this.downloadBundle(id, version, scope);
14166
+ const dir = await this.downloadBundle(id, version, scope, id, releaseChecksum);
13616
14167
  const entry = (() => {
13617
14168
  try {
13618
14169
  return addToStore(dir, { storeRoot: opts.storeRoot });
13619
14170
  } finally {
13620
- (0, import_node_fs11.rmSync)((0, import_node_path11.dirname)(dir), { recursive: true, force: true });
14171
+ (0, import_node_fs13.rmSync)((0, import_node_path13.dirname)(dir), { recursive: true, force: true });
13621
14172
  }
13622
14173
  })();
13623
- if (versionMeta?.sha256 && entry.sha256 !== versionMeta.sha256) throw new Error("Plugin release checksum mismatch");
14174
+ const archiveSha256 = releaseChecksum && /^[a-f0-9]{64}$/.test(releaseChecksum) ? releaseChecksum : void 0;
14175
+ const prepared = this.baseUrl === ((0, import_common5.OCTONODE_MARKETPLACE_URL)() ?? import_common5.DEFAULT_MARKETPLACE_URL).replace(/\/$/, "") && usesPreparedRunner(entry.dir);
13624
14176
  if (entry.manifest.library && opts.noDeps && !opts.cacheOnly)
13625
14177
  throw new Error(
13626
14178
  "Library installation requires native dependency activation; use frozen --artifacts-only for CI restore"
@@ -13632,26 +14184,29 @@ var RemoteRegistry = class {
13632
14184
  remoteId: id,
13633
14185
  version: entry.version,
13634
14186
  sha256: entry.sha256,
14187
+ archiveSha256,
13635
14188
  registry: this.baseUrl,
13636
14189
  scope,
13637
14190
  permissions: versionMeta?.permissions ?? detail.permissions,
13638
14191
  publisher: versionMeta?.publishedBy
13639
14192
  };
13640
- await activatePluginPin(alias2, pin2, opts);
13641
14193
  let installId2 = "";
13642
- if (!opts.dryRun) {
14194
+ if (prepared && !opts.dryRun) {
14195
+ installId2 = await this.registerInstall(id, version, scope, { ...opts, environment: "cloud" });
14196
+ if (!installId2 || !/^[a-f0-9-]{36}$/.test(installId2))
14197
+ throw new Error("Marketplace did not authorize the prepared installation");
14198
+ pin2.installId = installId2;
14199
+ }
14200
+ try {
14201
+ await activatePluginPin(alias2, pin2, opts);
14202
+ } catch (error) {
14203
+ if (installId2) await this.postEvent(installId2, "uninstall").catch(() => {
14204
+ });
14205
+ throw error;
14206
+ }
14207
+ if (!opts.dryRun && !prepared) {
13643
14208
  try {
13644
- const response = await this.request(`/marketplace/plugins/${encodeURIComponent(id)}/install`, {
13645
- method: "POST",
13646
- headers: { "content-type": "application/json" },
13647
- body: JSON.stringify({
13648
- version,
13649
- scope,
13650
- ownerId: opts.ownerId,
13651
- environment: opts.environment ?? "self_hosted"
13652
- })
13653
- });
13654
- installId2 = (await response.json()).installId;
14209
+ installId2 = await this.registerInstall(id, version, scope, opts);
13655
14210
  if (installId2)
13656
14211
  await withDependencyInstall(opts.cwd ?? process.cwd(), async () => {
13657
14212
  const current = readLockfile(opts.cwd).plugins[alias2];
@@ -13665,10 +14220,10 @@ var RemoteRegistry = class {
13665
14220
  plugin: { manifest: entry.manifest, dir: entry.dir, source: "store" },
13666
14221
  entry,
13667
14222
  installId: installId2,
13668
- depsInstalled: !opts.dryRun
14223
+ depsInstalled: !opts.dryRun && (!prepared || !!entry.manifest.library)
13669
14224
  };
13670
14225
  }
13671
- const deps = entry.created || entry.manifest.integration?.npm ? await installNodeDeps(entry.dir, {
14226
+ const deps = !prepared && (entry.created || entry.manifest.integration?.npm) ? await installNodeDeps(entry.dir, {
13672
14227
  noDeps: opts.noDeps,
13673
14228
  cwd: opts.cwd,
13674
14229
  cacheRoot: opts.cacheRoot,
@@ -13681,25 +14236,36 @@ var RemoteRegistry = class {
13681
14236
  remoteId: id,
13682
14237
  version: entry.version,
13683
14238
  sha256: entry.sha256,
14239
+ archiveSha256,
13684
14240
  registry: this.baseUrl,
13685
14241
  scope,
13686
14242
  publisher: versionMeta?.publishedBy,
13687
14243
  permissions: versionMeta?.permissions ?? detail.permissions
13688
14244
  };
13689
- await withDependencyInstall(opts.cwd ?? process.cwd(), async () => {
13690
- const previous = readLockfile(opts.cwd).plugins[alias];
13691
- if (previous && ((previous.pluginId ?? alias) !== entry.id || previous.registry !== this.baseUrl || previous.publisher !== pin.publisher))
13692
- throw new Error("Plugin alias belongs to another publisher or registry");
13693
- upsertLockEntry(opts.cwd ?? process.cwd(), alias, pin);
13694
- });
14245
+ if (opts.dryRun)
14246
+ return { plugin: { manifest: entry.manifest, dir: entry.dir, source: "store" }, entry, installId: "" };
13695
14247
  let installId = "";
14248
+ if (prepared) {
14249
+ installId = await this.registerInstall(id, version, scope, { ...opts, environment: "cloud" });
14250
+ if (!installId || !/^[a-f0-9-]{36}$/.test(installId))
14251
+ throw new Error("Marketplace did not authorize the prepared installation");
14252
+ pin.installId = installId;
14253
+ }
13696
14254
  try {
13697
- const response = await this.request(`/marketplace/plugins/${encodeURIComponent(id)}/install`, {
13698
- method: "POST",
13699
- headers: { "content-type": "application/json" },
13700
- body: JSON.stringify({ version, scope, ownerId: opts.ownerId, environment: opts.environment ?? "self_hosted" })
14255
+ await withDependencyInstall(opts.cwd ?? process.cwd(), async () => {
14256
+ const previous = readLockfile(opts.cwd).plugins[alias];
14257
+ if (previous && ((previous.pluginId ?? alias) !== entry.id || previous.registry !== this.baseUrl || previous.publisher !== pin.publisher))
14258
+ throw new Error("Plugin alias belongs to another publisher or registry");
14259
+ upsertLockEntry(opts.cwd ?? process.cwd(), alias, pin);
14260
+ });
14261
+ } catch (error) {
14262
+ if (installId) await this.postEvent(installId, "uninstall").catch(() => {
13701
14263
  });
13702
- installId = (await response.json()).installId;
14264
+ throw error;
14265
+ }
14266
+ if (prepared) return { plugin: { manifest: entry.manifest, dir: entry.dir, source: "store" }, entry, installId };
14267
+ try {
14268
+ installId = await this.registerInstall(id, version, scope, opts);
13703
14269
  await withDependencyInstall(opts.cwd ?? process.cwd(), async () => {
13704
14270
  const current = readLockfile(opts.cwd).plugins[alias];
13705
14271
  if (current?.sha256 === pin.sha256 && current.registry === pin.registry)
@@ -13725,13 +14291,13 @@ var RemoteRegistry = class {
13725
14291
  };
13726
14292
  function packPluginDir(pluginDir) {
13727
14293
  pluginBundleFiles(pluginDir);
13728
- const staging = (0, import_node_fs11.mkdtempSync)((0, import_node_path11.join)((0, import_node_os5.tmpdir)(), "octo-pack-"));
13729
- const out = (0, import_node_path11.join)(staging, "bundle.tar.gz");
14294
+ const staging = (0, import_node_fs13.mkdtempSync)((0, import_node_path13.join)((0, import_node_os6.tmpdir)(), "octo-pack-"));
14295
+ const out = (0, import_node_path13.join)(staging, "bundle.tar.gz");
13730
14296
  try {
13731
- (0, import_tar3.create)({ file: out, cwd: pluginDir, sync: true, portable: true, gzip: true }, pluginBundleFiles(pluginDir));
13732
- return (0, import_node_fs11.readFileSync)(out);
14297
+ (0, import_tar4.create)({ file: out, cwd: pluginDir, sync: true, portable: true, gzip: true }, pluginBundleFiles(pluginDir));
14298
+ return (0, import_node_fs13.readFileSync)(out);
13733
14299
  } finally {
13734
- (0, import_node_fs11.rmSync)(staging, { recursive: true, force: true });
14300
+ (0, import_node_fs13.rmSync)(staging, { recursive: true, force: true });
13735
14301
  }
13736
14302
  }
13737
14303
 
@@ -13739,7 +14305,7 @@ function packPluginDir(pluginDir) {
13739
14305
  async function installFromLock(opts = {}) {
13740
14306
  const cwd = resolveDependencyProject(opts.cwd ?? process.cwd()).target;
13741
14307
  if (opts.frozen && opts.force) throw new Error("Frozen restore cannot accept changed artifact bytes");
13742
- if (opts.frozen && !(0, import_node_fs12.existsSync)((0, import_node_path12.join)(cwd, "octonode.lock"))) throw new Error("Frozen restore requires octonode.lock");
14308
+ if (opts.frozen && !(0, import_node_fs14.existsSync)((0, import_node_path14.join)(cwd, "octonode.lock"))) throw new Error("Frozen restore requires octonode.lock");
13743
14309
  return withDependencyInstall(cwd, async () => {
13744
14310
  const lock = readLockfile(cwd);
13745
14311
  if (lock.generated && opts.force)
@@ -13756,9 +14322,8 @@ async function installFromLock(opts = {}) {
13756
14322
  throw new Error("Frozen restore cannot change the registry");
13757
14323
  if (pin.scope && pin.scope !== "public" && registry) {
13758
14324
  if (opts.offline) throw new Error("Private plugin access must be reauthorized online before restore");
13759
- const detail = await new RemoteRegistry({ baseUrl: registry, token: opts.token }).detail(remoteId, pin.scope);
13760
- const version = detail.versions.find((item) => item.version === pin.version);
13761
- if (!version || version.sha256 !== pin.sha256 || pin.publisher && version.publishedBy !== pin.publisher)
14325
+ const remote = new RemoteRegistry({ baseUrl: registry, token: opts.token });
14326
+ if (!await remote.matchesLockedRelease(id, pin))
13762
14327
  throw new Error("Locked private release is not authorized or changed");
13763
14328
  }
13764
14329
  let directory = storeEntryDir(id, pin.version, pin.sha256, opts.storeRoot);
@@ -13772,16 +14337,18 @@ async function installFromLock(opts = {}) {
13772
14337
  remoteId,
13773
14338
  pin.version,
13774
14339
  pin.scope,
13775
- id
14340
+ id,
14341
+ opts.force ? void 0 : pin.archiveSha256
13776
14342
  );
13777
14343
  try {
13778
14344
  const actual = hashPluginDir(downloaded);
13779
14345
  if (actual !== pin.sha256 && !opts.force) throw new Error("integrity mismatch for " + alias);
13780
14346
  const entry = addToStore(downloaded, { storeRoot: opts.storeRoot });
13781
14347
  directory = entry.dir;
13782
- if (actual !== pin.sha256) upsertLockEntry(cwd, alias, { ...pin, sha256: actual });
14348
+ if (actual !== pin.sha256)
14349
+ upsertLockEntry(cwd, alias, { ...pin, sha256: actual, archiveSha256: void 0 });
13783
14350
  } finally {
13784
- (0, import_node_fs12.rmSync)((0, import_node_path12.dirname)(downloaded), { recursive: true, force: true });
14351
+ (0, import_node_fs14.rmSync)((0, import_node_path14.dirname)(downloaded), { recursive: true, force: true });
13785
14352
  }
13786
14353
  result.restored.push(alias);
13787
14354
  }
@@ -13789,9 +14356,10 @@ async function installFromLock(opts = {}) {
13789
14356
  if (manifest2.id !== id || manifest2.version !== pin.version)
13790
14357
  throw new Error("Plugin identity mismatch: " + alias);
13791
14358
  if (manifest2.library) libraries.push({ alias, directory, sha256: pin.sha256 });
14359
+ if (!manifest2.library && preparedPluginPin(directory, pin)) continue;
13792
14360
  if (!manifest2.library && !pluginNpmDependencies(manifest2).length && !opts.artifactsOnly && !opts.noDeps) {
13793
- const packagePath = (0, import_node_path12.join)(directory, "package.json");
13794
- const legacy = (0, import_node_fs12.existsSync)(packagePath) ? JSON.parse((0, import_node_fs12.readFileSync)(packagePath, "utf8")) : {};
14361
+ const packagePath = (0, import_node_path14.join)(directory, "package.json");
14362
+ const legacy = (0, import_node_fs14.existsSync)(packagePath) ? JSON.parse((0, import_node_fs14.readFileSync)(packagePath, "utf8")) : {};
13795
14363
  if (legacy.dependencies || legacy.optionalDependencies) {
13796
14364
  if (opts.frozen || opts.offline)
13797
14365
  throw new Error(
@@ -13818,7 +14386,7 @@ async function installFromLock(opts = {}) {
13818
14386
  const generated = await generatePluginPackage(cwd, libraries, lock.generated.packer);
13819
14387
  if (generated.spec !== lock.generated.spec || generated.sha256 !== lock.generated.sha256 || JSON.stringify(generated.subpaths) !== JSON.stringify(lock.generated.subpaths))
13820
14388
  throw new Error("Generated package does not match octonode.lock");
13821
- const manifest2 = JSON.parse((0, import_node_fs12.readFileSync)((0, import_node_path12.join)(cwd, "package.json"), "utf8"));
14389
+ const manifest2 = JSON.parse((0, import_node_fs14.readFileSync)((0, import_node_path14.join)(cwd, "package.json"), "utf8"));
13822
14390
  if (manifest2.dependencies?.[generated.name]?.replace(/^file:\.\//, "file:") !== generated.spec.replace(/^file:\.\//, "file:"))
13823
14391
  throw new Error("package.json disagrees with octonode.lock");
13824
14392
  }
@@ -13838,11 +14406,11 @@ async function installFromLock(opts = {}) {
13838
14406
  }
13839
14407
 
13840
14408
  // packages/plugin/src/consumer/lifecycle.ts
13841
- var import_schema4 = __toESM(require_dist());
14409
+ var import_schema6 = __toESM(require_dist());
13842
14410
  async function runPluginLifecycle(command, target, flags) {
13843
- const cwd = resolveDependencyProject(typeof flags.cwd === "string" ? (0, import_node_path13.resolve)(flags.cwd) : process.cwd()).target;
14411
+ const cwd = resolveDependencyProject(typeof flags.cwd === "string" ? (0, import_node_path15.resolve)(flags.cwd) : process.cwd()).target;
13844
14412
  const scope = typeof flags.scope === "string" ? flags.scope : void 0;
13845
- if (scope && !import_schema4.MARKETPLACE_SCOPES.includes(scope)) throw new Error("Invalid marketplace scope");
14413
+ if (scope && !import_schema6.MARKETPLACE_SCOPES.includes(scope)) throw new Error("Invalid marketplace scope");
13846
14414
  const options = {
13847
14415
  cwd,
13848
14416
  alias: typeof flags.alias === "string" ? flags.alias : void 0,
@@ -13870,8 +14438,8 @@ async function runPluginLifecycle(command, target, flags) {
13870
14438
  await removePluginPin(target, options);
13871
14439
  return { removed: target, dryRun: options.dryRun };
13872
14440
  }
13873
- if (command === "install" && (0, import_node_fs13.existsSync)((0, import_node_path13.resolve)(cwd, target))) {
13874
- const entry = await installLocalArtifact((0, import_node_path13.resolve)(cwd, target), options);
14441
+ if (command === "install" && (0, import_node_fs15.existsSync)((0, import_node_path15.resolve)(cwd, target))) {
14442
+ const entry = await installLocalArtifact((0, import_node_path15.resolve)(cwd, target), options);
13875
14443
  return { id: options.alias ?? entry.id, version: entry.version, sha256: entry.sha256, dryRun: options.dryRun };
13876
14444
  }
13877
14445
  if (command !== "install" && command !== "update") throw new Error(`Unknown plugin lifecycle command: ${command}`);