@sellable/install 0.1.688 → 0.1.690
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 +4 -4
- package/container/README.md +1 -1
- package/lib/sellable-agent/admin-endpoint-runtime.mjs +53 -0
- package/lib/sellable-agent/fly-admin-image/Dockerfile +15 -0
- package/lib/sellable-agent/fly-admin-image/admin-runtime.mjs +55 -10
- package/lib/sellable-agent/fly-customer-image/Dockerfile +1 -1
- package/lib/sellable-agent/fly-customer-image/customer-runtime.mjs +11 -13
- package/lib/sellable-agent/host-bootstrap.mjs +1 -1
- package/lib/sellable-agent/host-worker.mjs +1 -1
- package/lib/sellable-agent/mcp-context-proxy.mjs +13 -15
- package/lib/sellable-agent/profile-materializer.mjs +1 -1
- package/lib/sellable-agent/provisioning-adapter.mjs +1 -1
- package/package.json +1 -1
package/container/Dockerfile
CHANGED
|
@@ -3,12 +3,12 @@ FROM ${HERMES_IMAGE}
|
|
|
3
3
|
|
|
4
4
|
USER root
|
|
5
5
|
|
|
6
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
6
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.690
|
|
7
7
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.901
|
|
8
8
|
ARG INSTALLER_INTEGRITY
|
|
9
9
|
ARG MCP_INTEGRITY=sha512-0YBT2d3YBgZFSgEcmoDCNLz/PWMiBbqMWeZoEFArWuk63/vrYATEp/qBNzYhpy7SKClLapsb1RRagdXrvo/p9g==
|
|
10
10
|
|
|
11
|
-
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.
|
|
11
|
+
RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.690" \
|
|
12
12
|
&& test "${MCP_PACKAGE}" = "@sellable/mcp@0.1.901" \
|
|
13
13
|
&& test -n "${INSTALLER_INTEGRITY}" \
|
|
14
14
|
&& test "$(npm view "${INSTALLER_PACKAGE}" dist.integrity)" = "${INSTALLER_INTEGRITY}" \
|
|
@@ -23,9 +23,9 @@ RUN test "${INSTALLER_PACKAGE}" = "@sellable/install@0.1.688" \
|
|
|
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.690' || mcp.name !== '@sellable/mcp' || mcp.version !== '0.1.901') 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.690', installerIntegrity: process.argv[1], mcpPackage: '@sellable/mcp@0.1.901', 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,7 +3,7 @@
|
|
|
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.690` is
|
|
7
7
|
published:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isAbsolute } from "node:path";
|
|
2
|
+
import { nativeGatewayRunning } from "./admin-native-gateway-supervisor.mjs";
|
|
3
|
+
|
|
4
|
+
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
|
|
5
|
+
|
|
6
|
+
export function projectAdminEndpointRuntime({
|
|
7
|
+
activeLaunch,
|
|
8
|
+
activeGateway,
|
|
9
|
+
runtimeGeneration,
|
|
10
|
+
trustRootPem,
|
|
11
|
+
turnDescriptorPath,
|
|
12
|
+
}) {
|
|
13
|
+
const claim = activeLaunch?.claim;
|
|
14
|
+
const operation = claim?.operation;
|
|
15
|
+
const authority = claim?.runtimeAuthority;
|
|
16
|
+
const service = claim?.runtimeContract?.service;
|
|
17
|
+
const profileId = activeLaunch?.profile?.profile;
|
|
18
|
+
if (
|
|
19
|
+
!nativeGatewayRunning(activeGateway) ||
|
|
20
|
+
authority?.scope !== "ASSIGNED" ||
|
|
21
|
+
authority.runtimeArtifactId !== authority.assignedRuntimeArtifactId ||
|
|
22
|
+
!SAFE_ID.test(authority.runtimeId ?? "") ||
|
|
23
|
+
!SAFE_ID.test(operation?.workspaceId ?? "") ||
|
|
24
|
+
!SAFE_ID.test(operation?.agentId ?? "") ||
|
|
25
|
+
!SAFE_ID.test(profileId ?? "") ||
|
|
26
|
+
profileId !== operation?.profileId ||
|
|
27
|
+
!Number.isSafeInteger(runtimeGeneration) ||
|
|
28
|
+
runtimeGeneration < 1 ||
|
|
29
|
+
!Number.isSafeInteger(service?.credentialGeneration) ||
|
|
30
|
+
service.credentialGeneration < 1 ||
|
|
31
|
+
typeof trustRootPem !== "string" ||
|
|
32
|
+
!trustRootPem.includes("-----BEGIN PUBLIC KEY-----") ||
|
|
33
|
+
!trustRootPem.includes("-----END PUBLIC KEY-----") ||
|
|
34
|
+
!isAbsolute(turnDescriptorPath ?? "")
|
|
35
|
+
) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return Object.freeze({
|
|
39
|
+
desired: Object.freeze({
|
|
40
|
+
workspaceId: operation.workspaceId,
|
|
41
|
+
agentId: operation.agentId,
|
|
42
|
+
runtimeId: authority.runtimeId,
|
|
43
|
+
profileId,
|
|
44
|
+
runtimeGeneration,
|
|
45
|
+
serviceCredentialGeneration: service.credentialGeneration,
|
|
46
|
+
}),
|
|
47
|
+
runtimeConfig: Object.freeze({ turnDescriptorPath }),
|
|
48
|
+
trustRootPem,
|
|
49
|
+
gateway: Object.freeze({
|
|
50
|
+
readback: () => ({ running: nativeGatewayRunning(activeGateway) }),
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -155,8 +155,15 @@ COPY fly-admin-image/admin-runtime.mjs /usr/local/lib/sellable-agent/admin-runti
|
|
|
155
155
|
COPY fly-admin-image/admin-runtime-phase-telemetry.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-runtime-phase-telemetry.mjs
|
|
156
156
|
COPY fly-admin-image/admin-state-generation.mjs /usr/local/lib/sellable-agent/admin-runtime/admin-state-generation.mjs
|
|
157
157
|
COPY fly-admin-image/start-admin-runtime /usr/local/lib/sellable-agent/admin-runtime/start-admin-runtime
|
|
158
|
+
COPY fly-customer-image/customer-runtime.mjs /usr/local/lib/sellable-agent/fly-customer-image/customer-runtime.mjs
|
|
159
|
+
COPY fly-customer-image/hermes-runs-client.mjs /usr/local/lib/sellable-agent/fly-customer-image/hermes-runs-client.mjs
|
|
160
|
+
COPY fly-customer-image/endpoint-uat-completion-gate.mjs /usr/local/lib/sellable-agent/fly-customer-image/endpoint-uat-completion-gate.mjs
|
|
158
161
|
COPY admin-native-gateway-supervisor.mjs /usr/local/lib/sellable-agent/admin-native-gateway-supervisor.mjs
|
|
162
|
+
COPY admin-endpoint-runtime.mjs /usr/local/lib/sellable-agent/admin-endpoint-runtime.mjs
|
|
159
163
|
COPY abandoned-runtime-scratch.mjs /usr/local/lib/sellable-agent/abandoned-runtime-scratch.mjs
|
|
164
|
+
COPY fly-customer-model.mjs /usr/local/lib/sellable-agent/fly-customer-model.mjs
|
|
165
|
+
COPY fly-customer-worker.mjs /usr/local/lib/sellable-agent/fly-customer-worker.mjs
|
|
166
|
+
COPY profile-materializer.mjs /usr/local/lib/sellable-agent/profile-materializer.mjs
|
|
160
167
|
COPY mcp-context-proxy.mjs /usr/local/lib/sellable-agent/mcp-context-proxy.mjs
|
|
161
168
|
COPY mcp-runtime-binding.mjs /usr/local/lib/sellable-agent/mcp-runtime-binding.mjs
|
|
162
169
|
COPY fly-runtime-identity.mjs /usr/local/lib/sellable-agent/fly-runtime-identity.mjs
|
|
@@ -243,8 +250,15 @@ RUN set -eux; \
|
|
|
243
250
|
/usr/local/lib/sellable-agent/admin-runtime/admin-runtime.mjs \
|
|
244
251
|
/usr/local/lib/sellable-agent/admin-runtime/admin-state-generation.mjs \
|
|
245
252
|
/usr/local/lib/sellable-agent/admin-runtime/start-admin-runtime \
|
|
253
|
+
/usr/local/lib/sellable-agent/fly-customer-image/customer-runtime.mjs \
|
|
254
|
+
/usr/local/lib/sellable-agent/fly-customer-image/hermes-runs-client.mjs \
|
|
255
|
+
/usr/local/lib/sellable-agent/fly-customer-image/endpoint-uat-completion-gate.mjs \
|
|
246
256
|
/usr/local/lib/sellable-agent/admin-native-gateway-supervisor.mjs \
|
|
257
|
+
/usr/local/lib/sellable-agent/admin-endpoint-runtime.mjs \
|
|
247
258
|
/usr/local/lib/sellable-agent/abandoned-runtime-scratch.mjs \
|
|
259
|
+
/usr/local/lib/sellable-agent/fly-customer-model.mjs \
|
|
260
|
+
/usr/local/lib/sellable-agent/fly-customer-worker.mjs \
|
|
261
|
+
/usr/local/lib/sellable-agent/profile-materializer.mjs \
|
|
248
262
|
/usr/local/lib/sellable-agent/mcp-context-proxy.mjs \
|
|
249
263
|
/usr/local/lib/sellable-agent/mcp-runtime-binding.mjs \
|
|
250
264
|
/usr/local/lib/sellable-agent/fly-runtime-identity.mjs \
|
|
@@ -270,6 +284,7 @@ RUN set -eux; \
|
|
|
270
284
|
|
|
271
285
|
LABEL io.sellable.runtime.kind="admin" \
|
|
272
286
|
io.sellable.runtime.listener="native-dedicated-slack" \
|
|
287
|
+
io.sellable.runtime.api-server="authenticated-loopback-127.0.0.1:8642" \
|
|
273
288
|
io.sellable.runtime.installers="build-only" \
|
|
274
289
|
io.sellable.runtime.default-profile-bundle-version="${DEFAULT_PROFILE_BUNDLE_VERSION}" \
|
|
275
290
|
io.sellable.runtime.default-profile-bundle-digest="${DEFAULT_PROFILE_BUNDLE_DIGEST}"
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
isOperationBoundBackupClaim,
|
|
47
47
|
} from "../admin-artifact/node_modules/@sellable/install/lib/sellable-agent/fly-customer-worker.mjs";
|
|
48
48
|
import sodium from "../admin-artifact/node_modules/libsodium-wrappers/dist/modules-esm/libsodium-wrappers.mjs";
|
|
49
|
+
import { projectAdminEndpointRuntime } from "../admin-endpoint-runtime.mjs";
|
|
49
50
|
import {
|
|
50
51
|
nativeGatewayRunning,
|
|
51
52
|
nextAdminNativeGatewayRecovery,
|
|
@@ -60,6 +61,7 @@ import {
|
|
|
60
61
|
SELLABLE_BROWSER_TOOLSET_DEFAULT_MARKER,
|
|
61
62
|
sellableSlackToolsetsForMaterialization,
|
|
62
63
|
} from "../fly-capabilities-bridge.mjs";
|
|
64
|
+
import { createCustomerEndpointRelay } from "../fly-customer-image/customer-runtime.mjs";
|
|
63
65
|
import {
|
|
64
66
|
claimWithSoulObservation,
|
|
65
67
|
createFlyCustomerControlClient,
|
|
@@ -131,9 +133,9 @@ const PROFILES_ROOT = join(DATA_ROOT, "profiles");
|
|
|
131
133
|
const RUNTIME_ROOT = join(DATA_ROOT, ".sellable-agent", "admin-runtime");
|
|
132
134
|
const DELIVERY_ROOT = join(RUNTIME_ROOT, "delivery");
|
|
133
135
|
const SECRET_ROOT = "/run/sellable-agent/admin-runtime";
|
|
134
|
-
// Turn descriptor + consumed roots for the Product MCP context proxy.
|
|
135
|
-
//
|
|
136
|
-
//
|
|
136
|
+
// Turn descriptor + consumed roots for the Product MCP context proxy. They are
|
|
137
|
+
// normally empty and receive one signed descriptor only while the fenced
|
|
138
|
+
// endpoint relay owns a turn; the proxy fails closed if either root is absent.
|
|
137
139
|
const MCP_TURN_CONTEXT_ROOT = join(SECRET_ROOT, "mcp-turn-context");
|
|
138
140
|
const HERMES = "/opt/hermes/.venv/bin/hermes";
|
|
139
141
|
const HERMES_PYTHON = "/opt/hermes/.venv/bin/python";
|
|
@@ -693,7 +695,7 @@ function ensureProductMcpContextProxyRoots() {
|
|
|
693
695
|
privateDirectory(join(MCP_TURN_CONTEXT_ROOT, "consumed"));
|
|
694
696
|
}
|
|
695
697
|
|
|
696
|
-
function
|
|
698
|
+
function readMcpTurnTrustRoot() {
|
|
697
699
|
let trustRootPem;
|
|
698
700
|
try {
|
|
699
701
|
const entry = lstatSync(MCP_TURN_TRUST_ROOT);
|
|
@@ -708,6 +710,11 @@ function productMcpContextProxyEnvironment(layout) {
|
|
|
708
710
|
} catch {
|
|
709
711
|
throw new Error("admin_runtime_trust_root_rejected");
|
|
710
712
|
}
|
|
713
|
+
return trustRootPem;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function productMcpContextProxyEnvironment(layout) {
|
|
717
|
+
const trustRootPem = readMcpTurnTrustRoot();
|
|
711
718
|
ensureProductMcpContextProxyRoots();
|
|
712
719
|
return {
|
|
713
720
|
SELLABLE_AGENT_TURN_DESCRIPTOR_PATH: join(
|
|
@@ -810,6 +817,10 @@ function installAdminModelConfig(profileRoot, modelConfig) {
|
|
|
810
817
|
vision: { provider: "openrouter", model: "google/gemini-3.5-flash" },
|
|
811
818
|
});
|
|
812
819
|
}
|
|
820
|
+
document.setIn(["platforms", "api_server"], {
|
|
821
|
+
enabled: true,
|
|
822
|
+
extra: { host: "127.0.0.1", port: 8642, cors_origins: [] },
|
|
823
|
+
});
|
|
813
824
|
atomicPrivateFile(path, document.toString());
|
|
814
825
|
const readback = parseDocument(readFileSync(path, "utf8")).toJS();
|
|
815
826
|
if (
|
|
@@ -820,7 +831,12 @@ function installAdminModelConfig(profileRoot, modelConfig) {
|
|
|
820
831
|
canonicalJson(readback.credential_pool_strategies) !==
|
|
821
832
|
canonicalJson(modelConfig.credential_pool_strategies) ||
|
|
822
833
|
canonicalJson(readback.fallback_providers) !==
|
|
823
|
-
canonicalJson(modelConfig.fallback_providers)
|
|
834
|
+
canonicalJson(modelConfig.fallback_providers) ||
|
|
835
|
+
canonicalJson(readback.platforms?.api_server) !==
|
|
836
|
+
canonicalJson({
|
|
837
|
+
enabled: true,
|
|
838
|
+
extra: { host: "127.0.0.1", port: 8642, cors_origins: [] },
|
|
839
|
+
})
|
|
824
840
|
) {
|
|
825
841
|
throw new Error("admin_model_config_rejected");
|
|
826
842
|
}
|
|
@@ -1826,6 +1842,10 @@ function installRuntimeSecrets({ claim, secrets, desiredLifecycle }) {
|
|
|
1826
1842
|
);
|
|
1827
1843
|
atomicPrivateFile(join(SECRET_ROOT, "slack-app"), `${secrets.slackApp}\n`);
|
|
1828
1844
|
atomicPrivateFile(join(SECRET_ROOT, "slack-bot"), `${secrets.slackBot}\n`);
|
|
1845
|
+
atomicPrivateFile(
|
|
1846
|
+
join(SECRET_ROOT, "api-server-key"),
|
|
1847
|
+
sha256(`admin-endpoint-api\0${randomUUID()}\0${Date.now()}`)
|
|
1848
|
+
);
|
|
1829
1849
|
const slackSecretFile = join(SECRET_ROOT, "profile-slack.env");
|
|
1830
1850
|
atomicPrivateFile(
|
|
1831
1851
|
slackSecretFile,
|
|
@@ -2032,6 +2052,7 @@ function clearRuntimeSecrets() {
|
|
|
2032
2052
|
for (const name of [
|
|
2033
2053
|
"slack-app",
|
|
2034
2054
|
"slack-bot",
|
|
2055
|
+
"api-server-key",
|
|
2035
2056
|
"profile-slack.env",
|
|
2036
2057
|
"sellable-config.json",
|
|
2037
2058
|
]) {
|
|
@@ -2869,10 +2890,9 @@ async function startNativeGateway({ profile, claim }) {
|
|
|
2869
2890
|
profileRoot: profile.profileHome,
|
|
2870
2891
|
secretsRoot: SECRET_ROOT,
|
|
2871
2892
|
profileId: profile.profile,
|
|
2872
|
-
//
|
|
2873
|
-
//
|
|
2874
|
-
|
|
2875
|
-
apiServerKeyRequired: false,
|
|
2893
|
+
// The endpoint relay is runtime-local and uses a fresh tmpfs key. The key
|
|
2894
|
+
// never crosses the control plane and Hermes still binds only loopback.
|
|
2895
|
+
apiServerKeyRequired: true,
|
|
2876
2896
|
env: {
|
|
2877
2897
|
...adminToolEnv(adminHomeLayout(profile.profileHome)),
|
|
2878
2898
|
...adminAgentServiceEnvironment(claim),
|
|
@@ -2896,11 +2916,17 @@ async function startNativeGateway({ profile, claim }) {
|
|
|
2896
2916
|
launch.inheritedFileDescriptors.find(({ fd }) => fd === 9).path,
|
|
2897
2917
|
"r"
|
|
2898
2918
|
);
|
|
2919
|
+
const apiServerKey = readFileSync(launch.apiServerKeyFile, "utf8");
|
|
2920
|
+
if (!/^[a-f0-9]{64}$/.test(apiServerKey)) {
|
|
2921
|
+
closeSync(appFd);
|
|
2922
|
+
closeSync(botFd);
|
|
2923
|
+
throw new Error("admin_endpoint_api_key_rejected");
|
|
2924
|
+
}
|
|
2899
2925
|
let child;
|
|
2900
2926
|
try {
|
|
2901
2927
|
child = spawn(HERMES, launch.args, {
|
|
2902
2928
|
cwd: launch.cwd,
|
|
2903
|
-
env: launch.env,
|
|
2929
|
+
env: { ...launch.env, API_SERVER_KEY: apiServerKey },
|
|
2904
2930
|
stdio: [
|
|
2905
2931
|
"ignore",
|
|
2906
2932
|
"ignore",
|
|
@@ -3566,12 +3592,28 @@ async function runAdminProduction() {
|
|
|
3566
3592
|
let activeLaunch = null;
|
|
3567
3593
|
let activeModelHealthReporter = null;
|
|
3568
3594
|
let shutdownMemorySnapshot = null;
|
|
3595
|
+
const endpointRelay = createCustomerEndpointRelay({
|
|
3596
|
+
getControlClient: () => client,
|
|
3597
|
+
getActiveRuntime: () =>
|
|
3598
|
+
projectAdminEndpointRuntime({
|
|
3599
|
+
activeLaunch,
|
|
3600
|
+
activeGateway,
|
|
3601
|
+
runtimeGeneration: RUNTIME_GENERATION,
|
|
3602
|
+
trustRootPem: readMcpTurnTrustRoot(),
|
|
3603
|
+
turnDescriptorPath: join(MCP_TURN_CONTEXT_ROOT, "turn-context.json"),
|
|
3604
|
+
}),
|
|
3605
|
+
runtimeGeneration: RUNTIME_GENERATION,
|
|
3606
|
+
getApiKeyFile: () => join(SECRET_ROOT, "api-server-key"),
|
|
3607
|
+
diagnostic: (value) => process.stderr.write(`${JSON.stringify(value)}\n`),
|
|
3608
|
+
});
|
|
3609
|
+
const endpointLoop = endpointRelay.run(controller.signal);
|
|
3569
3610
|
const beginShutdownSnapshot = () => {
|
|
3570
3611
|
if (!activeLaunch || shutdownMemorySnapshot) return;
|
|
3571
3612
|
const launch = activeLaunch;
|
|
3572
3613
|
const gateway = activeGateway;
|
|
3573
3614
|
shutdownMemorySnapshot = (async () => {
|
|
3574
3615
|
try {
|
|
3616
|
+
await endpointRelay.fenceActive("RUNTIME_SHUTDOWN");
|
|
3575
3617
|
await stopNativeGateway(gateway);
|
|
3576
3618
|
if (activeGateway === gateway) activeGateway = null;
|
|
3577
3619
|
return await flushHermesSnapshot({
|
|
@@ -3677,6 +3719,7 @@ async function runAdminProduction() {
|
|
|
3677
3719
|
!nativeGatewayRunning(activeGateway) &&
|
|
3678
3720
|
!gatewayRestartExhaustionReported
|
|
3679
3721
|
) {
|
|
3722
|
+
await endpointRelay.fenceActive("GATEWAY_RESTART");
|
|
3680
3723
|
const recovery = await recoverUnexpectedNativeGateway({
|
|
3681
3724
|
activeLaunch,
|
|
3682
3725
|
activeGateway,
|
|
@@ -3768,6 +3811,7 @@ async function runAdminProduction() {
|
|
|
3768
3811
|
});
|
|
3769
3812
|
await activeModelHealthReporter?.stop();
|
|
3770
3813
|
activeModelHealthReporter = null;
|
|
3814
|
+
await endpointRelay.fenceActive("LIFECYCLE_REFRESH");
|
|
3771
3815
|
await stopNativeGateway(activeGateway);
|
|
3772
3816
|
activeGateway = null;
|
|
3773
3817
|
activeLaunch = null;
|
|
@@ -4163,6 +4207,7 @@ async function runAdminProduction() {
|
|
|
4163
4207
|
})}\n`
|
|
4164
4208
|
);
|
|
4165
4209
|
}
|
|
4210
|
+
await endpointLoop;
|
|
4166
4211
|
await activeModelHealthReporter?.stop();
|
|
4167
4212
|
await stopNativeGateway(activeGateway);
|
|
4168
4213
|
clearRuntimeSecrets();
|
|
@@ -8,7 +8,7 @@ ENV PLAYWRIGHT_BROWSERS_PATH=/opt/hermes/.playwright
|
|
|
8
8
|
|
|
9
9
|
ARG MCP_PACKAGE=@sellable/mcp@0.1.901
|
|
10
10
|
ARG MCP_INTEGRITY=sha512-0YBT2d3YBgZFSgEcmoDCNLz/PWMiBbqMWeZoEFArWuk63/vrYATEp/qBNzYhpy7SKClLapsb1RRagdXrvo/p9g==
|
|
11
|
-
ARG INSTALLER_PACKAGE=@sellable/install@0.1.
|
|
11
|
+
ARG INSTALLER_PACKAGE=@sellable/install@0.1.690
|
|
12
12
|
ARG ADMIN_MCP_PACKAGE=@sellable/admin-mcp@0.1.186-wip.phase158.20260802034727
|
|
13
13
|
ARG ADMIN_MCP_INTEGRITY=sha512-qYNew5wd2IfgVcp1UYOG9QvpF/8Mx19YJ5HQtMKzvSfUf3evePjtaKsoXLf2C/GUJ5QlLlHJo84VWOI3V01+9A==
|
|
14
14
|
ARG TUS_JS_CLIENT_VERSION=4.3.1
|
|
@@ -18,6 +18,11 @@ import { loadavg } from "node:os";
|
|
|
18
18
|
import { dirname, join } from "node:path";
|
|
19
19
|
import { pathToFileURL } from "node:url";
|
|
20
20
|
import { pruneAbandonedRuntimeScratch } from "../abandoned-runtime-scratch.mjs";
|
|
21
|
+
import {
|
|
22
|
+
bindDefaultProfileProbeCron,
|
|
23
|
+
buildDefaultProfileProjection,
|
|
24
|
+
reconcileDefaultProfileBundle,
|
|
25
|
+
} from "../default-profile-reconciler.mjs";
|
|
21
26
|
import {
|
|
22
27
|
buildNativeHermesLaunch,
|
|
23
28
|
buildRuntimeServingObservation,
|
|
@@ -39,17 +44,12 @@ import {
|
|
|
39
44
|
renewFlyCustomerControlSession,
|
|
40
45
|
signedWorkerAuthorization,
|
|
41
46
|
} from "../fly-runtime-identity.mjs";
|
|
47
|
+
import { proveDefaultProfileNativeSkills } from "../fly-skills-bridge.mjs";
|
|
42
48
|
import {
|
|
43
49
|
executeExternalSoulBridgeOperation,
|
|
44
50
|
observeProfileSoul,
|
|
45
51
|
prepareSoulLockRoot,
|
|
46
52
|
} from "../fly-soul-bridge.mjs";
|
|
47
|
-
import { proveDefaultProfileNativeSkills } from "../fly-skills-bridge.mjs";
|
|
48
|
-
import {
|
|
49
|
-
bindDefaultProfileProbeCron,
|
|
50
|
-
buildDefaultProfileProjection,
|
|
51
|
-
reconcileDefaultProfileBundle,
|
|
52
|
-
} from "../default-profile-reconciler.mjs";
|
|
53
53
|
import {
|
|
54
54
|
ensureHermesSnapshotCron,
|
|
55
55
|
markHermesSnapshotDirty,
|
|
@@ -105,8 +105,7 @@ const RUNTIME_GENERATION = Number(
|
|
|
105
105
|
const BOOT_SESSION_ID = randomUUID();
|
|
106
106
|
const HERMES_VERSION = process.env.SELLABLE_AGENT_HERMES_VERSION ?? "0.20.4";
|
|
107
107
|
const INSTALLER_PACKAGE =
|
|
108
|
-
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ??
|
|
109
|
-
"@sellable/install@0.1.688";
|
|
108
|
+
process.env.SELLABLE_AGENT_INSTALLER_PACKAGE ?? "@sellable/install@0.1.690";
|
|
110
109
|
const MCP_PACKAGE =
|
|
111
110
|
process.env.SELLABLE_AGENT_MCP_PACKAGE ?? "@sellable/mcp@0.1.901";
|
|
112
111
|
const DEFAULT_PROFILE_BUNDLE_ROOT =
|
|
@@ -1051,6 +1050,8 @@ export function createCustomerEndpointRelay(options = {}) {
|
|
|
1051
1050
|
getControlClient,
|
|
1052
1051
|
getActiveRuntime,
|
|
1053
1052
|
runtimeGeneration,
|
|
1053
|
+
getApiKeyFile = ({ claim }) =>
|
|
1054
|
+
join(SECRET_ROOT, claim.runtime.profileId, "api-server-key"),
|
|
1054
1055
|
createRunsClient = ({ apiKeyFile }) =>
|
|
1055
1056
|
createHermesRunsClient({ apiKeyFile }),
|
|
1056
1057
|
now = () => new Date(),
|
|
@@ -1062,6 +1063,7 @@ export function createCustomerEndpointRelay(options = {}) {
|
|
|
1062
1063
|
if (
|
|
1063
1064
|
typeof getControlClient !== "function" ||
|
|
1064
1065
|
typeof getActiveRuntime !== "function" ||
|
|
1066
|
+
typeof getApiKeyFile !== "function" ||
|
|
1065
1067
|
typeof createRunsClient !== "function" ||
|
|
1066
1068
|
typeof now !== "function" ||
|
|
1067
1069
|
typeof sleepImpl !== "function" ||
|
|
@@ -1364,11 +1366,7 @@ export function createCustomerEndpointRelay(options = {}) {
|
|
|
1364
1366
|
if (installedDescriptor.ok !== true) {
|
|
1365
1367
|
throw new Error("customer_endpoint_mcp_descriptor_rejected");
|
|
1366
1368
|
}
|
|
1367
|
-
const apiKeyFile =
|
|
1368
|
-
SECRET_ROOT,
|
|
1369
|
-
claim.runtime.profileId,
|
|
1370
|
-
"api-server-key"
|
|
1371
|
-
);
|
|
1369
|
+
const apiKeyFile = getApiKeyFile({ claim, activeRuntime });
|
|
1372
1370
|
const runs = createRunsClient({ apiKeyFile, claim, activeRuntime });
|
|
1373
1371
|
const controller = new AbortController();
|
|
1374
1372
|
const abortFromOuter = () => controller.abort(outerSignal.reason);
|
|
@@ -38,7 +38,7 @@ import { installAgentHostServices } from "./service-installer.mjs";
|
|
|
38
38
|
const PACKAGE_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
|
39
39
|
const VERSION = "sellable-agent-host-bootstrap/v1";
|
|
40
40
|
const RECEIPT_VERSION = "sellable-agent-host-registration/v1";
|
|
41
|
-
const INSTALLER_PACKAGE = "@sellable/install@0.1.
|
|
41
|
+
const INSTALLER_PACKAGE = "@sellable/install@0.1.690";
|
|
42
42
|
const MCP_PACKAGE = "@sellable/mcp@0.1.901";
|
|
43
43
|
const HERMES_VERSION = "0.18.0";
|
|
44
44
|
const SHA256 = /^[a-f0-9]{64}$/;
|
|
@@ -1547,7 +1547,7 @@ export function compileClaimToProfileDesired(activeClaim, config) {
|
|
|
1547
1547
|
pinned.hermesCli !== "hermes" ||
|
|
1548
1548
|
pinned.hermesVersion !== "0.18.0" ||
|
|
1549
1549
|
pinned.installerPackage !==
|
|
1550
|
-
"@sellable/install@0.1.
|
|
1550
|
+
"@sellable/install@0.1.690" ||
|
|
1551
1551
|
pinned.mcpPackage !== "@sellable/mcp@0.1.901" ||
|
|
1552
1552
|
!Array.isArray(policy.toolInclude) ||
|
|
1553
1553
|
policy.toolInclude.length === 0 ||
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
constants,
|
|
7
7
|
existsSync,
|
|
8
8
|
fsyncSync,
|
|
9
|
-
linkSync,
|
|
10
9
|
lstatSync,
|
|
11
10
|
openSync,
|
|
12
11
|
readFileSync,
|
|
@@ -712,21 +711,20 @@ function activateOrRefreshEndpointSession({
|
|
|
712
711
|
return { ok: false, code: "endpoint_session_not_applicable" };
|
|
713
712
|
}
|
|
714
713
|
if (state.body && !endpointSessionBodyMatches(state.body, valid.body)) {
|
|
715
|
-
// Hermes
|
|
716
|
-
// endpoint runs.
|
|
717
|
-
//
|
|
718
|
-
//
|
|
719
|
-
//
|
|
720
|
-
//
|
|
721
|
-
//
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
if (error?.code !== "EEXIST") {
|
|
726
|
-
return { ok: false, code: "endpoint_descriptor_restore_failed" };
|
|
727
|
-
}
|
|
714
|
+
// Hermes deliberately reuses one stdio MCP transport across sequential
|
|
715
|
+
// endpoint runs. Rotate that transport in place after proving the next
|
|
716
|
+
// server-signed descriptor against the runtime binding. Returning a
|
|
717
|
+
// synthetic transport-expired error here made Hermes mark the Sellable
|
|
718
|
+
// MCP unavailable mid-conversation, so the following reviewed Calendar
|
|
719
|
+
// read never reached the backend. The nonce is burned before the session
|
|
720
|
+
// pointer moves, preserving the exact same single-use fence as first
|
|
721
|
+
// activation; a stale or replayed descriptor still fails closed.
|
|
722
|
+
if (!burnNonce(consumedRoot, valid.body.nonce)) {
|
|
723
|
+
return { ok: false, code: "descriptor_replayed" };
|
|
728
724
|
}
|
|
729
|
-
|
|
725
|
+
state.body = Object.freeze({ ...valid.body });
|
|
726
|
+
state.activated = true;
|
|
727
|
+
return { ok: true, body: state.body };
|
|
730
728
|
}
|
|
731
729
|
if (!burnNonce(consumedRoot, valid.body.nonce)) {
|
|
732
730
|
return { ok: false, code: "descriptor_replayed" };
|
|
@@ -283,7 +283,7 @@ function validateDesired(desired) {
|
|
|
283
283
|
desired.hermesCli !== "hermes" ||
|
|
284
284
|
desired.hermesVersion !== "0.18.0" ||
|
|
285
285
|
desired.installerPackage !==
|
|
286
|
-
"@sellable/install@0.1.
|
|
286
|
+
"@sellable/install@0.1.690" ||
|
|
287
287
|
desired.mcpPackage !== "@sellable/mcp@0.1.901" ||
|
|
288
288
|
!Array.isArray(desired.toolInclude) ||
|
|
289
289
|
desired.toolInclude.length === 0 ||
|
|
@@ -26,7 +26,7 @@ import { deriveContainedProfileId } from "./profile-materializer.mjs";
|
|
|
26
26
|
|
|
27
27
|
export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE";
|
|
28
28
|
export const PINNED_INSTALL_PACKAGE =
|
|
29
|
-
"@sellable/install@0.1.
|
|
29
|
+
"@sellable/install@0.1.690";
|
|
30
30
|
export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.901";
|
|
31
31
|
const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice(
|
|
32
32
|
PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1
|