@sellable/install 0.1.309 → 0.1.310
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/README.md +14 -0
- package/bin/sellable-install.mjs +89 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,6 +119,7 @@ It creates:
|
|
|
119
119
|
/srv/hermes/profiles/acme/sellable/config.json
|
|
120
120
|
/srv/hermes/profiles/acme/sellable/configs/
|
|
121
121
|
/srv/hermes/profiles/acme/skills/sellable/
|
|
122
|
+
/srv/hermes/bin/entrypoint-acme.sh
|
|
122
123
|
/srv/hermes/profiles/acme/.env # only when Slack token inputs are supplied
|
|
123
124
|
```
|
|
124
125
|
|
|
@@ -129,6 +130,19 @@ For profile paths under a gateway root such as
|
|
|
129
130
|
Hermes Desktop command autocomplete scans the gateway/root skill config, not
|
|
130
131
|
only the selected profile's local skill tree.
|
|
131
132
|
|
|
133
|
+
Bootstrap also writes `/srv/hermes/bin/entrypoint-acme.sh`. Point the Hermes
|
|
134
|
+
Docker service at that script when this profile should be the Desktop/dashboard
|
|
135
|
+
default:
|
|
136
|
+
|
|
137
|
+
```yaml
|
|
138
|
+
entrypoint: ["/opt/data/bin/entrypoint-acme.sh"]
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The wrapper starts `hermes gateway run` when needed, then intentionally runs
|
|
142
|
+
`hermes -p acme dashboard --isolated`. Without `--isolated`, Hermes OS chat can
|
|
143
|
+
see `/sellable-*` commands while Hermes Desktop autocomplete misses them because
|
|
144
|
+
the dashboard backend is not scoped to the selected profile.
|
|
145
|
+
|
|
132
146
|
If no token is supplied, bootstrap still creates a pending
|
|
133
147
|
`sellable/config.json` so the profile path is concrete from the first MCP
|
|
134
148
|
launch. Finish auth with:
|
package/bin/sellable-install.mjs
CHANGED
|
@@ -3676,6 +3676,74 @@ function hermesGatewayConfigPath(opts = {}) {
|
|
|
3676
3676
|
return join(root, "config.yaml");
|
|
3677
3677
|
}
|
|
3678
3678
|
|
|
3679
|
+
function hermesDashboardEntrypointPath(opts = {}, profile = "") {
|
|
3680
|
+
const root = hermesGatewayRoot(opts);
|
|
3681
|
+
if (!root) return null;
|
|
3682
|
+
const safeProfile = String(profile || basenameSafe(hermesHome(opts))).replace(
|
|
3683
|
+
/[^A-Za-z0-9_.-]/g,
|
|
3684
|
+
"_"
|
|
3685
|
+
);
|
|
3686
|
+
return join(root, "bin", `entrypoint-${safeProfile}.sh`);
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
function hermesDashboardCommand(profile) {
|
|
3690
|
+
return `hermes -p ${profile} dashboard --isolated --host 0.0.0.0 --port 4860 --no-open --skip-build`;
|
|
3691
|
+
}
|
|
3692
|
+
|
|
3693
|
+
function hermesDashboardEntrypointContent(opts, profile) {
|
|
3694
|
+
const quotedProfile = shellQuote(profile);
|
|
3695
|
+
const quotedProfileRoot = shellQuote(hermesHome(opts));
|
|
3696
|
+
const gatewayRoot = hermesGatewayRoot(opts);
|
|
3697
|
+
const quotedGatewayRoot = shellQuote(gatewayRoot || dirname(hermesHome(opts)));
|
|
3698
|
+
const quotedDashboardLog = shellQuote(join(hermesHome(opts), "logs", "dashboard.log"));
|
|
3699
|
+
return `#!/usr/bin/env bash
|
|
3700
|
+
set -euo pipefail
|
|
3701
|
+
|
|
3702
|
+
# This file is generated by sellable hermes profile bootstrap.
|
|
3703
|
+
# It preserves the Hostinger image startup shape, but launches the dashboard in
|
|
3704
|
+
# an isolated Sellable profile. Without --isolated, Hermes OS chat can see
|
|
3705
|
+
# profile skills while Desktop slash autocomplete stays on the default profile.
|
|
3706
|
+
profile=${quotedProfile}
|
|
3707
|
+
profile_root=${quotedProfileRoot}
|
|
3708
|
+
gateway_root=${quotedGatewayRoot}
|
|
3709
|
+
dashboard_log=${quotedDashboardLog}
|
|
3710
|
+
dashboard_port="\${HERMES_DASHBOARD_PORT:-4860}"
|
|
3711
|
+
run_as=()
|
|
3712
|
+
if command -v gosu >/dev/null 2>&1; then
|
|
3713
|
+
run_as=(gosu hermes)
|
|
3714
|
+
fi
|
|
3715
|
+
|
|
3716
|
+
chown -R hermes:hermes "$gateway_root" 2>/dev/null || true
|
|
3717
|
+
mkdir -p "$gateway_root/logs" "$profile_root/logs"
|
|
3718
|
+
chown -R hermes:hermes "$gateway_root/logs" "$profile_root/logs" 2>/dev/null || true
|
|
3719
|
+
|
|
3720
|
+
if [[ -f "$gateway_root/config.yaml" ]]; then
|
|
3721
|
+
if ! pgrep -f "hermes gateway run" >/dev/null 2>&1; then
|
|
3722
|
+
"\${run_as[@]}" nohup hermes gateway run >>"$gateway_root/logs/gateway.log" 2>&1 </dev/null &
|
|
3723
|
+
fi
|
|
3724
|
+
fi
|
|
3725
|
+
|
|
3726
|
+
for v in ADMIN_USERNAME ADMIN_PASSWORD; do
|
|
3727
|
+
if [[ -z "\${!v:-}" ]]; then
|
|
3728
|
+
echo "missing required environment variable $v" >&2
|
|
3729
|
+
exit 1
|
|
3730
|
+
fi
|
|
3731
|
+
done
|
|
3732
|
+
|
|
3733
|
+
export HERMES_DASHBOARD_BASIC_AUTH_USERNAME="$ADMIN_USERNAME"
|
|
3734
|
+
export HERMES_DASHBOARD_BASIC_AUTH_PASSWORD="$ADMIN_PASSWORD"
|
|
3735
|
+
|
|
3736
|
+
exec "\${run_as[@]}" hermes -p "$profile" dashboard --isolated --host 0.0.0.0 --port "$dashboard_port" --no-open --skip-build >>"$dashboard_log" 2>&1
|
|
3737
|
+
`;
|
|
3738
|
+
}
|
|
3739
|
+
|
|
3740
|
+
function writeHermesDashboardEntrypoint(opts, profile) {
|
|
3741
|
+
const path = hermesDashboardEntrypointPath(opts, profile);
|
|
3742
|
+
if (!path) return null;
|
|
3743
|
+
writeFile(path, hermesDashboardEntrypointContent(opts, profile), opts, 0o755);
|
|
3744
|
+
return path;
|
|
3745
|
+
}
|
|
3746
|
+
|
|
3679
3747
|
function hermesLikelyInstalled() {
|
|
3680
3748
|
return (
|
|
3681
3749
|
Boolean(process.env.HERMES_HOME?.trim()) ||
|
|
@@ -4054,6 +4122,7 @@ function runHermesProfileBootstrap(argv) {
|
|
|
4054
4122
|
sellableConfigsDir,
|
|
4055
4123
|
skillsRoot: hermesSkillsRoot(opts),
|
|
4056
4124
|
gatewayConfigPath: hermesGatewayConfigPath(opts),
|
|
4125
|
+
hermesDashboardEntrypointPath: hermesDashboardEntrypointPath(opts, profile),
|
|
4057
4126
|
},
|
|
4058
4127
|
auth: {
|
|
4059
4128
|
apiUrl: opts.apiUrl,
|
|
@@ -4073,6 +4142,12 @@ function runHermesProfileBootstrap(argv) {
|
|
|
4073
4142
|
.map(([key, value]) => [key, tokenFingerprint(value)])
|
|
4074
4143
|
),
|
|
4075
4144
|
},
|
|
4145
|
+
dashboard: {
|
|
4146
|
+
command: hermesDashboardCommand(profile),
|
|
4147
|
+
composeEntrypoint: hermesDashboardEntrypointPath(opts, profile)
|
|
4148
|
+
? `entrypoint: ["${hermesDashboardEntrypointPath(opts, profile)}"]`
|
|
4149
|
+
: null,
|
|
4150
|
+
},
|
|
4076
4151
|
created: [],
|
|
4077
4152
|
updated: [],
|
|
4078
4153
|
preserved: [],
|
|
@@ -4140,6 +4215,10 @@ function runHermesProfileBootstrap(argv) {
|
|
|
4140
4215
|
const hermesConfigExisted = existsSync(hermesConfigPath(opts));
|
|
4141
4216
|
const gatewayConfigPath = hermesGatewayConfigPath(opts);
|
|
4142
4217
|
const gatewayConfigExisted = gatewayConfigPath ? existsSync(gatewayConfigPath) : false;
|
|
4218
|
+
const dashboardEntrypointPath = hermesDashboardEntrypointPath(opts, profile);
|
|
4219
|
+
const dashboardEntrypointExisted = dashboardEntrypointPath
|
|
4220
|
+
? existsSync(dashboardEntrypointPath)
|
|
4221
|
+
: false;
|
|
4143
4222
|
const skillsExisted = existsSync(hermesSkillsRoot(opts));
|
|
4144
4223
|
const authExisted = existsSync(sellableConfigPath);
|
|
4145
4224
|
const configsDirExisted = existsSync(sellableConfigsDir);
|
|
@@ -4178,6 +4257,7 @@ function runHermesProfileBootstrap(argv) {
|
|
|
4178
4257
|
mkdirSync(sellableConfigsDir, { recursive: true, mode: 0o700 });
|
|
4179
4258
|
writeHermesProfileSellableConfig(sellableConfigPath, nextAuth, opts);
|
|
4180
4259
|
installHermes(opts);
|
|
4260
|
+
writeHermesDashboardEntrypoint(opts, profile);
|
|
4181
4261
|
if (Object.values(slackTokens).some(Boolean)) {
|
|
4182
4262
|
for (const [key, value] of Object.entries(slackTokens)) {
|
|
4183
4263
|
if (value) envState.entries.set(key, value);
|
|
@@ -4198,6 +4278,15 @@ function runHermesProfileBootstrap(argv) {
|
|
|
4198
4278
|
gatewayConfigPath
|
|
4199
4279
|
);
|
|
4200
4280
|
}
|
|
4281
|
+
if (dashboardEntrypointPath) {
|
|
4282
|
+
(dashboardEntrypointExisted ? summary.updated : summary.created).push(
|
|
4283
|
+
dashboardEntrypointPath
|
|
4284
|
+
);
|
|
4285
|
+
} else {
|
|
4286
|
+
summary.skipped.push(
|
|
4287
|
+
"Hermes dashboard entrypoint: profile root is not under a gateway profiles directory"
|
|
4288
|
+
);
|
|
4289
|
+
}
|
|
4201
4290
|
(skillsExisted ? summary.updated : summary.created).push(hermesSkillsRoot(opts));
|
|
4202
4291
|
if (Object.values(slackTokens).some(Boolean)) {
|
|
4203
4292
|
(envExisted ? summary.updated : summary.created).push(envPath);
|