@sellable/install 0.1.579 → 0.1.581

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.
@@ -3,13 +3,13 @@ FROM ${HERMES_IMAGE}
3
3
 
4
4
  USER root
5
5
 
6
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.577
7
- ARG MCP_PACKAGE=@sellable/mcp@0.1.858
6
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.581
7
+ ARG MCP_PACKAGE=@sellable/mcp@0.1.859
8
8
  ARG INSTALLER_INTEGRITY
9
- ARG MCP_INTEGRITY=sha512-8yiUhwMbBk/KxZAXGXTsEHiN8XeB/qCqNgfApew0rhViJqiLlDN8oS67pZW5iB5gdmeuv4du0jx9Lhq/blr9hQ==
9
+ ARG MCP_INTEGRITY=sha512-U5uVeDThuUW/CdWghTXQqTAA58mPDly3XHGmfX6WVjczGQHRcDB+Q49vbrA1MOkGRCnzmjGZN1fIVfFtBlwMPw==
10
10
 
11
- RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.577" \
12
- && test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.858" \
11
+ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.581" \
12
+ && test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.859" \
13
13
  && test -n "${INSTALLER_INTEGRITY}" \
14
14
  && test "$(npm view "${INSTALLER_PACKAGE}" dist.integrity)" = "${INSTALLER_INTEGRITY}" \
15
15
  && test "$(npm view "${MCP_PACKAGE}" dist.integrity)" = "${MCP_INTEGRITY}" \
@@ -23,9 +23,9 @@ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.577" \
23
23
  && mkdir -p /opt/sellable-agent/install-root /opt/sellable-agent/mcp-root \
24
24
  && npm install --prefix /opt/sellable-agent/install-root --omit=dev --ignore-scripts --no-audit --no-fund "${INSTALLER_PACKAGE}" \
25
25
  && npm install --prefix /opt/sellable-agent/mcp-root --omit=dev --ignore-scripts --no-audit --no-fund "${MCP_PACKAGE}" \
26
- && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.577' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.858') throw new Error('package identity rejected');" \
26
+ && node --input-type=module -e "import { readFileSync } from 'node:fs'; const install = JSON.parse(readFileSync('/opt/sellable-agent/install-root/node_modules/@sellable/install/package.json')); const mcp = JSON.parse(readFileSync('/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp/package.json')); if (install.name !== '@sellable/install' || install.version !== '0.1.581' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.859') throw new Error('package identity rejected');" \
27
27
  && node --input-type=module -e "import { buildExternalRuntimeClosure } from '/opt/sellable-agent/install-root/node_modules/@sellable/install/lib/sellable-agent/external-runtime-builder.mjs'; const built = buildExternalRuntimeClosure({ mcpPackageRoot: '/opt/sellable-agent/mcp-root/node_modules/@sellable/mcp', mcpNodeModulesRoot: '/opt/sellable-agent/mcp-root/node_modules', hermesSourceRoot: '/opt/hermes', ownerUid: 0, ownerGid: 0 }); if (!built.closureDigest) throw new Error('runtime closure rejected');" \
28
- && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.577', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.858', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
28
+ && node --input-type=module -e "import { chmodSync, writeFileSync } from 'node:fs'; const release = { installerPackage: '@sellable/install@0.1.581', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.859', mcpIntegrity: process.argv[2] }; writeFileSync('/opt/sellable-agent/release.json', JSON.stringify(release) + '\\n', { mode: 0o444 }); chmodSync('/opt/sellable-agent/release.json', 0o444);" "${INSTALLER_INTEGRITY}" "${MCP_INTEGRITY}" \
29
29
  && npm cache clean --force
30
30
 
31
31
  COPY entrypoint.sh /usr/local/bin/sellable-agent-container-entrypoint
@@ -3,13 +3,13 @@
3
3
  This image is a dedicated Agent worker/runtime boundary beside an existing Hermes dashboard. It never replaces or reconfigures the dashboard container.
4
4
 
5
5
  Build from this directory after
6
- `@sellable/install@0.1.577` is
6
+ `@sellable/install@0.1.581` is
7
7
  published:
8
8
 
9
9
  ```sh
10
10
  docker build --platform linux/amd64 \
11
11
  --build-arg INSTALLER_INTEGRITY='sha512-…' \
12
- --tag sellable-agent-host:0.1.577 .
12
+ --tag sellable-agent-host:0.1.581 .
13
13
  ```
14
14
 
15
15
  Create a private `bootstrap/` directory owned by root with mode `0700`. It must contain `bootstrap.json`, `provider-auth`, and `slack-app`, each owned by root with mode `0600`. `bootstrap.json` contains only non-secret identities, hashes, package integrities, and the control-plane URL. The other two files contain the provider JSON and global Slack app token whose hashes are pinned by `bootstrap.json`.
@@ -28,7 +28,7 @@ import {
28
28
 
29
29
  const MANIFEST_VERSION = "sellable-agent-external-runtime-closure/v1";
30
30
  const MCP_PACKAGE = "@sellable/mcp";
31
- const MCP_VERSION = "0.1.858";
31
+ const MCP_VERSION = "0.1.859";
32
32
  const SAFE_COMPONENT = /^[A-Za-z0-9@._+-]+$/;
33
33
  const HERMES_EXCLUDES = new Set([".git", ".playwright", "node_modules"]);
34
34
 
@@ -86,6 +86,8 @@ COPY hermes-bridge.mjs /usr/local/lib/sellable-agent/hermes-bridge.mjs
86
86
  COPY hermes-memory-snapshot.mjs /usr/local/lib/sellable-agent/hermes-memory-snapshot.mjs
87
87
  COPY fly-soul-bridge.mjs /usr/local/lib/sellable-agent/fly-soul-bridge.mjs
88
88
  COPY fly-soul-bridge-exec.mjs /usr/local/lib/sellable-agent/fly-soul-bridge-exec.mjs
89
+ COPY fly-skills-bridge.mjs /usr/local/lib/sellable-agent/fly-skills-bridge.mjs
90
+ COPY fly-skills-bridge-exec.mjs /usr/local/lib/sellable-agent/fly-skills-bridge-exec.mjs
89
91
  COPY soul-artifact-validator.mjs /usr/local/lib/sellable-agent/soul-artifact-validator.mjs
90
92
  COPY hermes-memory-dirty.mjs /opt/sellable/bin/hermes-memory-dirty
91
93
  COPY hermes-memory-reconcile.sh /opt/sellable/share/hermes-memory-reconcile.sh
@@ -107,9 +109,18 @@ RUN node --input-type=module -e \
107
109
  'import("file:///usr/local/lib/sellable-agent/fly-soul-bridge-exec.mjs").then(({ runFlySoulBridgeExecCli }) => runFlySoulBridgeExecCli());' \
108
110
  > /usr/local/bin/sellable-soul-bridge-exec \
109
111
  && chmod 0555 /usr/local/bin/sellable-soul-bridge-exec \
112
+ && printf '%s\n' \
113
+ '#!/usr/bin/env node' \
114
+ 'import("file:///usr/local/lib/sellable-agent/fly-skills-bridge-exec.mjs").then(({ runFlySkillsBridgeExecCli }) => runFlySkillsBridgeExecCli());' \
115
+ > /usr/local/bin/sellable-skills-bridge-exec \
116
+ && chmod 0555 /usr/local/bin/sellable-skills-bridge-exec \
110
117
  && sha256sum /usr/local/lib/sellable-agent/fly-soul-bridge.mjs \
111
118
  > /usr/local/share/sellable-agent/soul-bridge-source.sha256 \
119
+ && sha256sum /usr/local/lib/sellable-agent/fly-skills-bridge.mjs \
120
+ /usr/local/lib/sellable-agent/fly-skills-bridge-exec.mjs \
121
+ > /usr/local/share/sellable-agent/skills-bridge-source.sha256 \
112
122
  && chmod 0444 /usr/local/share/sellable-agent/soul-bridge-source.sha256 \
123
+ /usr/local/share/sellable-agent/skills-bridge-source.sha256 \
113
124
  && chmod 0755 /etc \
114
125
  && chmod 0555 \
115
126
  /usr/local/lib/sellable-agent/admin-runtime/admin-runtime.mjs \
@@ -122,6 +133,8 @@ RUN node --input-type=module -e \
122
133
  /usr/local/lib/sellable-agent/hermes-memory-snapshot.mjs \
123
134
  /usr/local/lib/sellable-agent/fly-soul-bridge.mjs \
124
135
  /usr/local/lib/sellable-agent/fly-soul-bridge-exec.mjs \
136
+ /usr/local/lib/sellable-agent/fly-skills-bridge.mjs \
137
+ /usr/local/lib/sellable-agent/fly-skills-bridge-exec.mjs \
125
138
  /usr/local/lib/sellable-agent/soul-artifact-validator.mjs \
126
139
  /opt/sellable/bin/hermes-memory-dirty \
127
140
  /opt/sellable/bin/hermes-endpoint-cron \
@@ -637,6 +637,78 @@ function installAdminModelConfig(profileRoot, modelConfig) {
637
637
  }
638
638
  }
639
639
 
640
+ const ADMIN_LIVE_SKILLS_CONFIG_KEYS = Object.freeze([
641
+ "disabled",
642
+ "platform_disabled",
643
+ ]);
644
+
645
+ function adminPreservedSkillsIdentity(profileRoot) {
646
+ if (!existsSync(profileRoot)) return null;
647
+ const entries = [];
648
+ const skillsRoot = join(profileRoot, "skills");
649
+ const walk = (directory, relativePath = "") => {
650
+ for (const name of readdirSync(directory).sort()) {
651
+ const nextRelative = relativePath ? `${relativePath}/${name}` : name;
652
+ if (nextRelative === "sellable") continue;
653
+ const path = join(directory, name);
654
+ const stat = lstatSync(path);
655
+ if (stat.isSymbolicLink()) {
656
+ throw new Error("admin_runtime_live_skills_symlink_rejected");
657
+ }
658
+ if (stat.isDirectory()) walk(path, nextRelative);
659
+ else if (stat.isFile()) {
660
+ entries.push({
661
+ path: nextRelative,
662
+ sha256: sha256(readFileSync(path)),
663
+ });
664
+ } else throw new Error("admin_runtime_live_skills_entry_rejected");
665
+ }
666
+ };
667
+ if (existsSync(skillsRoot)) walk(skillsRoot);
668
+ const configPath = join(profileRoot, "config.yaml");
669
+ let disabled = {};
670
+ if (existsSync(configPath)) {
671
+ const document = parseDocument(readFileSync(configPath, "utf8"));
672
+ if (document.errors.length > 0) {
673
+ throw new Error("admin_runtime_live_skills_config_rejected");
674
+ }
675
+ const skills = document.toJS()?.skills;
676
+ if (skills && typeof skills === "object" && !Array.isArray(skills)) {
677
+ disabled = Object.fromEntries(
678
+ ADMIN_LIVE_SKILLS_CONFIG_KEYS.filter((key) =>
679
+ Object.prototype.hasOwnProperty.call(skills, key)
680
+ ).map((key) => [key, skills[key]])
681
+ );
682
+ }
683
+ }
684
+ return sha256(canonicalJson({ disabled, entries }));
685
+ }
686
+
687
+ function mergeAdminLiveSkillsConfig(sourceRoot, targetRoot) {
688
+ const sourcePath = join(sourceRoot, "config.yaml");
689
+ const targetPath = join(targetRoot, "config.yaml");
690
+ if (!existsSync(sourcePath) || !existsSync(targetPath)) return;
691
+ const sourceDocument = parseDocument(readFileSync(sourcePath, "utf8"));
692
+ const targetDocument = parseDocument(readFileSync(targetPath, "utf8"));
693
+ if (sourceDocument.errors.length > 0 || targetDocument.errors.length > 0) {
694
+ throw new Error("admin_runtime_live_skills_config_rejected");
695
+ }
696
+ const sourceSkills = sourceDocument.toJS()?.skills;
697
+ if (
698
+ !sourceSkills ||
699
+ typeof sourceSkills !== "object" ||
700
+ Array.isArray(sourceSkills)
701
+ ) {
702
+ return;
703
+ }
704
+ for (const key of ADMIN_LIVE_SKILLS_CONFIG_KEYS) {
705
+ if (Object.prototype.hasOwnProperty.call(sourceSkills, key)) {
706
+ targetDocument.setIn(["skills", key], structuredClone(sourceSkills[key]));
707
+ }
708
+ }
709
+ atomicPrivateFile(targetPath, targetDocument.toString());
710
+ }
711
+
640
712
  async function materializeAdminProfile({
641
713
  profileContract,
642
714
  settingsRoot,
@@ -742,7 +814,6 @@ async function materializeAdminProfile({
742
814
  },
743
815
  });
744
816
  installAdminModelConfig(stageRoot, modelConfig);
745
- mergeHermesState(profileHome, stageRoot);
746
817
  installAdminHermesMemoryHook(stageRoot);
747
818
  stageAdminRuntimeTree(stageRoot);
748
819
  return stage;
@@ -766,6 +837,16 @@ async function materializeAdminProfile({
766
837
  rmSync(stageRoot, { recursive: true, force: true });
767
838
  return { status: "RESTAGE", stagedAuthority: finalAuthority };
768
839
  }
840
+ const liveSkillsIdentity = adminPreservedSkillsIdentity(profileHome);
841
+ mergeHermesState(profileHome, stageRoot);
842
+ mergeAdminLiveSkillsConfig(profileHome, stageRoot);
843
+ if (
844
+ liveSkillsIdentity !== null &&
845
+ adminPreservedSkillsIdentity(stageRoot) !== liveSkillsIdentity
846
+ ) {
847
+ rmSync(stageRoot, { recursive: true, force: true });
848
+ return { status: "RESTAGE", stagedAuthority: finalAuthority };
849
+ }
769
850
  const stagedSoulPath = join(stageRoot, "SOUL.md");
770
851
  const stagedSoul = lstatSync(stagedSoulPath);
771
852
  const stagedSoulBytes = readFileSync(stagedSoulPath);
@@ -4,9 +4,9 @@ FROM ${HERMES_IMAGE}
4
4
 
5
5
  USER root
6
6
 
7
- ARG MCP_PACKAGE=@sellable/mcp@0.1.858
8
- ARG MCP_INTEGRITY=sha512-8yiUhwMbBk/KxZAXGXTsEHiN8XeB/qCqNgfApew0rhViJqiLlDN8oS67pZW5iB5gdmeuv4du0jx9Lhq/blr9hQ==
9
- ARG INSTALLER_PACKAGE=@sellable/install@0.1.577
7
+ ARG MCP_PACKAGE=@sellable/mcp@0.1.859
8
+ ARG MCP_INTEGRITY=sha512-U5uVeDThuUW/CdWghTXQqTAA58mPDly3XHGmfX6WVjczGQHRcDB+Q49vbrA1MOkGRCnzmjGZN1fIVfFtBlwMPw==
9
+ ARG INSTALLER_PACKAGE=@sellable/install@0.1.581
10
10
  ARG TUS_JS_CLIENT_VERSION=4.3.1
11
11
  ARG HERMES_VERSION=0.20.0
12
12
  ARG TIRITH_VERSION=0.3.3
@@ -59,7 +59,7 @@ RUN --mount=type=secret,id=mcp_trust_root,required=true \
59
59
  /etc/cont-init.d/02-reconcile-profiles \
60
60
  && npm cache clean --force
61
61
 
62
- COPY mcp-context-proxy.mjs mcp-runtime-binding.mjs fly-customer-model.mjs fly-customer-worker.mjs profile-materializer.mjs hermes-bridge.mjs hermes-memory-snapshot.mjs fly-runtime-identity.mjs fly-soul-bridge.mjs fly-soul-bridge-exec.mjs soul-artifact-validator.mjs model-auth-reconciler.mjs model-auth-canary-reseed.mjs model-health-reporter.mjs \
62
+ COPY mcp-context-proxy.mjs mcp-runtime-binding.mjs fly-customer-model.mjs fly-customer-worker.mjs profile-materializer.mjs hermes-bridge.mjs hermes-memory-snapshot.mjs fly-runtime-identity.mjs fly-soul-bridge.mjs fly-soul-bridge-exec.mjs fly-skills-bridge.mjs fly-skills-bridge-exec.mjs soul-artifact-validator.mjs model-auth-reconciler.mjs model-auth-canary-reseed.mjs model-health-reporter.mjs \
63
63
  /usr/local/lib/sellable-agent/
64
64
  COPY hermes-memory-dirty.mjs /opt/sellable/bin/hermes-memory-dirty
65
65
  COPY hermes-memory-reconcile.sh /opt/sellable/share/hermes-memory-reconcile.sh
@@ -86,9 +86,18 @@ RUN node --input-type=module -e \
86
86
  'import("file:///usr/local/lib/sellable-agent/fly-soul-bridge-exec.mjs").then(({ runFlySoulBridgeExecCli }) => runFlySoulBridgeExecCli());' \
87
87
  > /usr/local/bin/sellable-soul-bridge-exec \
88
88
  && chmod 0555 /usr/local/bin/sellable-soul-bridge-exec \
89
+ && printf '%s\n' \
90
+ '#!/usr/bin/env node' \
91
+ 'import("file:///usr/local/lib/sellable-agent/fly-skills-bridge-exec.mjs").then(({ runFlySkillsBridgeExecCli }) => runFlySkillsBridgeExecCli());' \
92
+ > /usr/local/bin/sellable-skills-bridge-exec \
93
+ && chmod 0555 /usr/local/bin/sellable-skills-bridge-exec \
89
94
  && sha256sum /usr/local/lib/sellable-agent/fly-soul-bridge.mjs \
90
95
  > /usr/local/share/sellable-agent/soul-bridge-source.sha256 \
96
+ && sha256sum /usr/local/lib/sellable-agent/fly-skills-bridge.mjs \
97
+ /usr/local/lib/sellable-agent/fly-skills-bridge-exec.mjs \
98
+ > /usr/local/share/sellable-agent/skills-bridge-source.sha256 \
91
99
  && chmod 0444 /usr/local/share/sellable-agent/soul-bridge-source.sha256 \
100
+ /usr/local/share/sellable-agent/skills-bridge-source.sha256 \
92
101
  && chmod 0755 /etc \
93
102
  && chmod 0555 /usr/local/lib/sellable-agent/*.mjs \
94
103
  && chmod 0555 /opt/sellable/bin/hermes-memory-dirty /opt/sellable/bin/hermes-endpoint-cron \
@@ -93,9 +93,9 @@ const RUNTIME_GENERATION = Number(
93
93
  const BOOT_SESSION_ID = randomUUID();
94
94
  const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.0";
95
95
  const INSTALLER_PACKAGE =
96
- process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.577";
96
+ process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.581";
97
97
  const MCP_PACKAGE =
98
- process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.858";
98
+ process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.859";
99
99
 
100
100
  const sha256 = (value) => createHash("sha256").update(value).digest("hex");
101
101
  const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ import {
7
+ FlySkillsBridgeError,
8
+ SKILLS_REQUEST_MAX_BYTES,
9
+ executeExternalSkillsBridgeOperation,
10
+ } from "./fly-skills-bridge.mjs";
11
+
12
+ export async function runFlySkillsBridgeExecCli() {
13
+ try {
14
+ if (process.argv.length !== 2)
15
+ throw new FlySkillsBridgeError("skills_request_rejected");
16
+ const chunks = [];
17
+ let total = 0;
18
+ for await (const chunk of process.stdin) {
19
+ total += chunk.length;
20
+ if (total > SKILLS_REQUEST_MAX_BYTES)
21
+ throw new FlySkillsBridgeError("skills_request_too_large");
22
+ chunks.push(chunk);
23
+ }
24
+ let request;
25
+ try {
26
+ request = JSON.parse(Buffer.concat(chunks).toString("utf8"));
27
+ } catch {
28
+ throw new FlySkillsBridgeError("skills_request_rejected");
29
+ }
30
+ const result = executeExternalSkillsBridgeOperation(request);
31
+ process.stdout.write(`${JSON.stringify(result)}\n`);
32
+ process.exitCode = result.ok ? 0 : 2;
33
+ } catch (error) {
34
+ process.stdout.write(
35
+ `${JSON.stringify({ ok: false, code: error instanceof FlySkillsBridgeError ? error.code : "skills_bridge_failed" })}\n`
36
+ );
37
+ process.exitCode = 2;
38
+ }
39
+ }
40
+
41
+ if (
42
+ resolve(process.argv[1] ?? "") === resolve(fileURLToPath(import.meta.url))
43
+ ) {
44
+ await runFlySkillsBridgeExecCli();
45
+ }