@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.
- package/container/Dockerfile +7 -7
- package/container/README.md +2 -2
- package/lib/sellable-agent/external-runtime-builder.mjs +1 -1
- package/lib/sellable-agent/fly-admin-image/Dockerfile +13 -0
- package/lib/sellable-agent/fly-admin-image/admin-runtime.mjs +82 -1
- package/lib/sellable-agent/fly-customer-image/Dockerfile +13 -4
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +2 -2
- package/lib/sellable-agent/fly-skills-bridge-exec.mjs +45 -0
- package/lib/sellable-agent/fly-skills-bridge.mjs +977 -0
- package/lib/sellable-agent/hermes-bridge.mjs +126 -3
- package/lib/sellable-agent/host-bootstrap.mjs +2 -2
- package/lib/sellable-agent/host-worker.mjs +12 -5
- package/lib/sellable-agent/profile-materializer.mjs +169 -14
- package/lib/sellable-agent/provisioning-adapter.mjs +5 -8
- package/package.json +1 -1
package/container/Dockerfile
CHANGED
|
@@ -3,13 +3,13 @@ FROM ${HERMES_IMAGE}
|
|
|
3
3
|
|
|
4
4
|
USER root
|
|
5
5
|
|
|
6
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
7
|
-
ARG MCP_PACKAGE=@sellable/mcp@0.1.
|
|
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-
|
|
9
|
+
ARG MCP_INTEGRITY=sha512-U5uVeDThuUW/CdWghTXQqTAA58mPDly3XHGmfX6WVjczGQHRcDB+Q49vbrA1MOkGRCnzmjGZN1fIVfFtBlwMPw==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.
|
|
12
|
-
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.
|
|
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.
|
|
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.
|
|
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
|
package/container/README.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
8
|
-
ARG MCP_INTEGRITY=sha512-
|
|
9
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
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.
|
|
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.
|
|
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
|
+
}
|