@rapidthoughtlabs/heku 0.2.0 → 0.3.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.
- package/CHANGELOG.md +14 -1
- package/README.md +2 -2
- package/dist/cli.js +330 -157
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-06-11
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`heku update`** now updates registry-installed configs to their latest versions (instead of updating the heku binary). Accepts an optional target: `heku update github-http`, `heku update linear:graphql`, or `heku update @ns/linear` (all connector variants). Local credentials (`connector.env`) and overlays are preserved on update.
|
|
14
|
+
- **`one.registry_update`** MCP tool — same update logic callable by an LLM agent. Updates one config by `config_id` or all installed configs when no argument is passed.
|
|
15
|
+
- **`one.list_configs`** now returns `[{ id, name, description }]` objects instead of a flat array of IDs, so agents can identify the right config without an extra round-trip.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Publish modal always shows the version field. New (unpublished) configs show an empty field with a "not published yet" hint; existing configs are pre-filled with the next patch version and show the current published version as a hint.
|
|
19
|
+
- Flat manifest (`search`, `list_configs`, `list_tools`, `invoke`) no longer prefixes tool descriptions with `[one]` — it was noise when only four tools are shown. Namespaced mode is unchanged.
|
|
20
|
+
- Discovery tool descriptions updated with explicit workflow steps and call examples for users without custom system prompts.
|
|
21
|
+
|
|
10
22
|
## [0.2.0] - 2026-06-09
|
|
11
23
|
|
|
12
24
|
### Changed
|
|
@@ -35,7 +47,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
35
47
|
- Initial public release on npm as `@rapidthoughtlabs/mcpone`.
|
|
36
48
|
- Single dynamic MCP server that turns JSON configs into working API tools.
|
|
37
49
|
|
|
38
|
-
[Unreleased]: https://github.com/RapidThoughtLabs/heku/compare/v0.
|
|
50
|
+
[Unreleased]: https://github.com/RapidThoughtLabs/heku/compare/v0.3.0...HEAD
|
|
51
|
+
[0.3.0]: https://github.com/RapidThoughtLabs/heku/compare/v0.2.0...v0.3.0
|
|
39
52
|
[0.2.0]: https://github.com/RapidThoughtLabs/heku/compare/v0.1.2...v0.2.0
|
|
40
53
|
[0.1.2]: https://github.com/RapidThoughtLabs/heku/compare/v0.1.0...v0.1.2
|
|
41
54
|
[0.1.0]: https://github.com/RapidThoughtLabs/heku/releases/tag/v0.1.0
|
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Stop building one MCP server per API. Build one config.
|
|
|
14
14
|
- **Hot-reload** — add or edit a config, tools update live without restart
|
|
15
15
|
- **Auto-discovery** — gRPC reflection, GraphQL introspection, and child MCP tool listing fill in tools automatically
|
|
16
16
|
- **Built-in console UI** — React dashboard for chat, config editing, and registry browsing
|
|
17
|
-
- **Registry** — publish and install community configs from [
|
|
17
|
+
- **Registry** — publish and install community configs from [app.rapidthoughtlabs.space](https://app.rapidthoughtlabs.space)
|
|
18
18
|
- **Auth handled** — bearer, basic, API key, and OAuth2 with `.env`-based credential management
|
|
19
19
|
- **Self-managing** — the server can create and edit its own configs via internal tools
|
|
20
20
|
|
|
@@ -186,7 +186,7 @@ Built with React 19, TailwindCSS v4, Zustand, and the MCP SDK.
|
|
|
186
186
|
|
|
187
187
|
## Registry
|
|
188
188
|
|
|
189
|
-
[**
|
|
189
|
+
[**app.rapidthoughtlabs.space**](https://app.rapidthoughtlabs.space) is the default registry for sharing configs.
|
|
190
190
|
|
|
191
191
|
```bash
|
|
192
192
|
heku install @rtl/github
|
package/dist/cli.js
CHANGED
|
@@ -4601,7 +4601,7 @@ var require_schemes = __commonJS({
|
|
|
4601
4601
|
serialize: httpSerialize
|
|
4602
4602
|
}
|
|
4603
4603
|
);
|
|
4604
|
-
var
|
|
4604
|
+
var https = (
|
|
4605
4605
|
/** @type {SchemeHandler} */
|
|
4606
4606
|
{
|
|
4607
4607
|
scheme: "https",
|
|
@@ -4650,7 +4650,7 @@ var require_schemes = __commonJS({
|
|
|
4650
4650
|
/** @type {Record<SchemeName, SchemeHandler>} */
|
|
4651
4651
|
{
|
|
4652
4652
|
http: http2,
|
|
4653
|
-
https
|
|
4653
|
+
https,
|
|
4654
4654
|
ws,
|
|
4655
4655
|
wss,
|
|
4656
4656
|
urn,
|
|
@@ -8442,7 +8442,7 @@ var require_supports_color = __commonJS({
|
|
|
8442
8442
|
"node_modules/supports-color/index.js"(exports2, module2) {
|
|
8443
8443
|
"use strict";
|
|
8444
8444
|
init_esm_shims();
|
|
8445
|
-
var
|
|
8445
|
+
var os6 = __require("os");
|
|
8446
8446
|
var tty = __require("tty");
|
|
8447
8447
|
var hasFlag = require_has_flag();
|
|
8448
8448
|
var { env } = process;
|
|
@@ -8499,7 +8499,7 @@ var require_supports_color = __commonJS({
|
|
|
8499
8499
|
return min;
|
|
8500
8500
|
}
|
|
8501
8501
|
if (process.platform === "win32") {
|
|
8502
|
-
const osRelease =
|
|
8502
|
+
const osRelease = os6.release().split(".");
|
|
8503
8503
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
8504
8504
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
8505
8505
|
}
|
|
@@ -32320,7 +32320,7 @@ var require_cross_spawn = __commonJS({
|
|
|
32320
32320
|
enoent.hookChildProcess(spawned, parsed);
|
|
32321
32321
|
return spawned;
|
|
32322
32322
|
}
|
|
32323
|
-
function
|
|
32323
|
+
function spawnSync(command2, args2, options) {
|
|
32324
32324
|
const parsed = parse3(command2, args2, options);
|
|
32325
32325
|
const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
|
|
32326
32326
|
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
|
|
@@ -32328,7 +32328,7 @@ var require_cross_spawn = __commonJS({
|
|
|
32328
32328
|
}
|
|
32329
32329
|
module2.exports = spawn4;
|
|
32330
32330
|
module2.exports.spawn = spawn4;
|
|
32331
|
-
module2.exports.sync =
|
|
32331
|
+
module2.exports.sync = spawnSync;
|
|
32332
32332
|
module2.exports._parse = parse3;
|
|
32333
32333
|
module2.exports._enoent = enoent;
|
|
32334
32334
|
}
|
|
@@ -33933,7 +33933,7 @@ var require_service_config = __commonJS({
|
|
|
33933
33933
|
exports2.validateRetryThrottling = validateRetryThrottling;
|
|
33934
33934
|
exports2.validateServiceConfig = validateServiceConfig;
|
|
33935
33935
|
exports2.extractAndSelectServiceConfig = extractAndSelectServiceConfig;
|
|
33936
|
-
var
|
|
33936
|
+
var os6 = __require("os");
|
|
33937
33937
|
var constants_1 = require_constants();
|
|
33938
33938
|
var DURATION_REGEX = /^\d+(\.\d{1,9})?s$/;
|
|
33939
33939
|
var CLIENT_LANGUAGE_STRING = "node";
|
|
@@ -34232,7 +34232,7 @@ var require_service_config = __commonJS({
|
|
|
34232
34232
|
if (Array.isArray(validatedConfig.clientHostname)) {
|
|
34233
34233
|
let hostnameMatched = false;
|
|
34234
34234
|
for (const hostname3 of validatedConfig.clientHostname) {
|
|
34235
|
-
if (hostname3 ===
|
|
34235
|
+
if (hostname3 === os6.hostname()) {
|
|
34236
34236
|
hostnameMatched = true;
|
|
34237
34237
|
}
|
|
34238
34238
|
}
|
|
@@ -48781,7 +48781,7 @@ var require_subchannel_call = __commonJS({
|
|
|
48781
48781
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
48782
48782
|
exports2.Http2SubchannelCall = void 0;
|
|
48783
48783
|
var http2 = __require("http2");
|
|
48784
|
-
var
|
|
48784
|
+
var os6 = __require("os");
|
|
48785
48785
|
var constants_1 = require_constants();
|
|
48786
48786
|
var metadata_1 = require_metadata2();
|
|
48787
48787
|
var stream_decoder_1 = require_stream_decoder();
|
|
@@ -48789,7 +48789,7 @@ var require_subchannel_call = __commonJS({
|
|
|
48789
48789
|
var constants_2 = require_constants();
|
|
48790
48790
|
var TRACER_NAME = "subchannel_call";
|
|
48791
48791
|
function getSystemErrorName(errno) {
|
|
48792
|
-
for (const [name, num] of Object.entries(
|
|
48792
|
+
for (const [name, num] of Object.entries(os6.constants.errno)) {
|
|
48793
48793
|
if (num === errno) {
|
|
48794
48794
|
return name;
|
|
48795
48795
|
}
|
|
@@ -93824,7 +93824,7 @@ var require_client_metadata = __commonJS({
|
|
|
93824
93824
|
exports2.makeClientMetadata = makeClientMetadata;
|
|
93825
93825
|
exports2.addContainerMetadata = addContainerMetadata;
|
|
93826
93826
|
exports2.getFAASEnv = getFAASEnv;
|
|
93827
|
-
var
|
|
93827
|
+
var os6 = __require("os");
|
|
93828
93828
|
var process5 = __require("process");
|
|
93829
93829
|
var bson_1 = require_bson2();
|
|
93830
93830
|
var error_1 = require_error2();
|
|
@@ -93894,7 +93894,7 @@ var require_client_metadata = __commonJS({
|
|
|
93894
93894
|
if (!metadataDocument.ifItFitsItSits("platform", runtimeInfo)) {
|
|
93895
93895
|
throw new error_1.MongoInvalidArgumentError("Unable to include driverInfo platform, metadata cannot exceed 512 bytes");
|
|
93896
93896
|
}
|
|
93897
|
-
const osInfo = (/* @__PURE__ */ new Map()).set("name", process5.platform).set("architecture", process5.arch).set("version",
|
|
93897
|
+
const osInfo = (/* @__PURE__ */ new Map()).set("name", process5.platform).set("architecture", process5.arch).set("version", os6.release()).set("type", os6.type());
|
|
93898
93898
|
if (!metadataDocument.ifItFitsItSits("os", osInfo)) {
|
|
93899
93899
|
for (const key of osInfo.keys()) {
|
|
93900
93900
|
osInfo.delete(key);
|
|
@@ -93997,13 +93997,13 @@ var require_client_metadata = __commonJS({
|
|
|
93997
93997
|
function getRuntimeInfo() {
|
|
93998
93998
|
if ("Deno" in globalThis) {
|
|
93999
93999
|
const version2 = typeof Deno?.version?.deno === "string" ? Deno?.version?.deno : "0.0.0-unknown";
|
|
94000
|
-
return `Deno v${version2}, ${
|
|
94000
|
+
return `Deno v${version2}, ${os6.endianness()}`;
|
|
94001
94001
|
}
|
|
94002
94002
|
if ("Bun" in globalThis) {
|
|
94003
94003
|
const version2 = typeof Bun?.version === "string" ? Bun?.version : "0.0.0-unknown";
|
|
94004
|
-
return `Bun v${version2}, ${
|
|
94004
|
+
return `Bun v${version2}, ${os6.endianness()}`;
|
|
94005
94005
|
}
|
|
94006
|
-
return `Node.js ${process5.version}, ${
|
|
94006
|
+
return `Node.js ${process5.version}, ${os6.endianness()}`;
|
|
94007
94007
|
}
|
|
94008
94008
|
}
|
|
94009
94009
|
});
|
|
@@ -131059,8 +131059,7 @@ function getInstalledEntry(slug, registry2 = "default") {
|
|
|
131059
131059
|
|
|
131060
131060
|
// src/lib/version.ts
|
|
131061
131061
|
init_esm_shims();
|
|
131062
|
-
var VERSION = true ? "0.
|
|
131063
|
-
var PKG_NAME = true ? "@rapidthoughtlabs/heku" : "@rapidthoughtlabs/heku";
|
|
131062
|
+
var VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
131064
131063
|
|
|
131065
131064
|
// src/connectors/mcp.ts
|
|
131066
131065
|
init_esm_shims();
|
|
@@ -134326,6 +134325,8 @@ var ToolRegistry = class {
|
|
|
134326
134325
|
}
|
|
134327
134326
|
this.tools.set(qualifiedName, {
|
|
134328
134327
|
configId: config2.id,
|
|
134328
|
+
configName: config2.name,
|
|
134329
|
+
configDescription: config2.description,
|
|
134329
134330
|
connectorConfig: config2.connector,
|
|
134330
134331
|
tool
|
|
134331
134332
|
});
|
|
@@ -134373,6 +134374,7 @@ var BLOCKED_WHEN_LOCKED = /* @__PURE__ */ new Set([
|
|
|
134373
134374
|
"one.remove_tool",
|
|
134374
134375
|
"one.update_tool",
|
|
134375
134376
|
"one.registry_install",
|
|
134377
|
+
"one.registry_update",
|
|
134376
134378
|
"one.auth_set"
|
|
134377
134379
|
]);
|
|
134378
134380
|
function blockedTool(qualifiedName, args2) {
|
|
@@ -134393,7 +134395,7 @@ function makeServer(registry2, transportCtx) {
|
|
|
134393
134395
|
const { visible, rewrite } = buildManifestView(manifestStyle, registry2);
|
|
134394
134396
|
const tools = visible.map((rt) => ({
|
|
134395
134397
|
name: rewrite(`${rt.configId}.${rt.tool.name}`),
|
|
134396
|
-
description: `[${rt.configId}] ${rt.tool.description}`,
|
|
134398
|
+
description: manifestStyle === "flat" ? rt.tool.description : `[${rt.configId}] ${rt.tool.description}`,
|
|
134397
134399
|
inputSchema: buildInputSchema(rt.tool.params)
|
|
134398
134400
|
}));
|
|
134399
134401
|
return { tools };
|
|
@@ -137822,6 +137824,9 @@ async function getConfigMeta(namespace, slug, connectorType, registryName = "def
|
|
|
137822
137824
|
const slugWithConnector = connectorType ? `${slug}:${connectorType}` : slug;
|
|
137823
137825
|
return jsonFetch(`/configs/${namespace}/${slugWithConnector}`, { registryName });
|
|
137824
137826
|
}
|
|
137827
|
+
async function listVersions(namespace, slug, registryName = "default") {
|
|
137828
|
+
return jsonFetch(`/configs/${namespace}/${slug}/versions`, { registryName });
|
|
137829
|
+
}
|
|
137825
137830
|
async function fetchVersionPayload(namespace, slug, connectorType, version2, registryName = "default") {
|
|
137826
137831
|
const slugWithConnector = connectorType ? `${slug}:${connectorType}` : slug;
|
|
137827
137832
|
const vPath = version2 ?? "latest";
|
|
@@ -137982,7 +137987,7 @@ function createRegistryRouter() {
|
|
|
137982
137987
|
const slug = colonIdx !== -1 ? rest.slice(0, colonIdx) : rest;
|
|
137983
137988
|
const connectorType = colonIdx !== -1 ? rest.slice(colonIdx + 1) : void 0;
|
|
137984
137989
|
const meta3 = await getConfigMeta(namespace, slug, connectorType, registry2);
|
|
137985
|
-
res.json({ category: meta3.category, tags: meta3.tags });
|
|
137990
|
+
res.json({ category: meta3.category, tags: meta3.tags, latest_version: meta3.latest_version ?? null });
|
|
137986
137991
|
}));
|
|
137987
137992
|
router.post("/check-updates", wrap(async (req, res) => {
|
|
137988
137993
|
const { installed, registry: registry2 = "default" } = req.body;
|
|
@@ -138094,6 +138099,12 @@ function createRegistryRouter() {
|
|
|
138094
138099
|
const { payload } = await fetchVersionPayload(namespace, slug, connector_type, void 0, registry2);
|
|
138095
138100
|
res.json(payload);
|
|
138096
138101
|
}));
|
|
138102
|
+
router.get("/versions/:namespace/:slug", wrap(async (req, res) => {
|
|
138103
|
+
const { namespace, slug } = req.params;
|
|
138104
|
+
const registry2 = req.query["registry"] ?? "default";
|
|
138105
|
+
const versions = await listVersions(namespace, slug, registry2);
|
|
138106
|
+
res.json(versions);
|
|
138107
|
+
}));
|
|
138097
138108
|
router.delete("/uninstall/:id", wrap(async (req, res) => {
|
|
138098
138109
|
const id = req.params["id"];
|
|
138099
138110
|
const registry2 = req.query["registry"] ?? "default";
|
|
@@ -138206,12 +138217,12 @@ var INTERNAL_CONFIG = {
|
|
|
138206
138217
|
},
|
|
138207
138218
|
{
|
|
138208
138219
|
name: "list_configs",
|
|
138209
|
-
description: "**Start here.** Returns
|
|
138220
|
+
description: "**Start here.** Returns all active configs as an array of objects with `id`, `name`, and `description`. Config IDs follow the pattern `<base>-<connector>` (e.g. `github-http`, `linear-graphql`). Use the description to pick the right config, then pass its `id` to `search` or `list_tools`.",
|
|
138210
138221
|
params: []
|
|
138211
138222
|
},
|
|
138212
138223
|
{
|
|
138213
138224
|
name: "search",
|
|
138214
|
-
description: "Find tools by name or intent across all configs (or within a specific one). Returns matching tools with full schemas grouped by
|
|
138225
|
+
description: "Find tools by name or intent across all configs (or within a specific one). Returns matching tools with full schemas grouped by quality: exact \u2192 partial \u2192 description \u2192 related. Covers both native `one.*` self-management tools and all loaded service tools. Narrow by config: `github-http` returns only github-http tools; `github` matches github-http and github-cli. Once you have a tool name, call `invoke` with `config_id.tool_name` to execute it.",
|
|
138215
138226
|
params: [
|
|
138216
138227
|
{
|
|
138217
138228
|
name: "query",
|
|
@@ -138229,7 +138240,7 @@ var INTERNAL_CONFIG = {
|
|
|
138229
138240
|
},
|
|
138230
138241
|
{
|
|
138231
138242
|
name: "invoke",
|
|
138232
|
-
description: "Execute any registered tool by its qualified name (config_id.tool_name).
|
|
138243
|
+
description: "Execute any registered tool by its qualified name (`config_id.tool_name`). Workflow: (1) list_configs \u2192 pick a config id, (2) search or list_tools \u2192 get the tool name and required args, (3) invoke \u2192 run it. Examples: `github-http.create_issue`, `linear-graphql.create_issue`, `one.server_status`.",
|
|
138233
138244
|
params: [
|
|
138234
138245
|
{
|
|
138235
138246
|
name: "tool",
|
|
@@ -138345,6 +138356,14 @@ var INTERNAL_CONFIG = {
|
|
|
138345
138356
|
{ name: "registry", type: "string", required: false, description: "Registry source name (default: 'default')" }
|
|
138346
138357
|
]
|
|
138347
138358
|
},
|
|
138359
|
+
{
|
|
138360
|
+
name: "registry_update",
|
|
138361
|
+
description: "Update registry-installed configs to the latest available version. Preserves local credentials (connector.env) and overlays. Pass config_id to update a single config, or omit to update all installed configs.",
|
|
138362
|
+
params: [
|
|
138363
|
+
{ name: "config_id", type: "string", required: false, description: "ID of the config to update (e.g. 'github-http'). Omit to update all installed configs." },
|
|
138364
|
+
{ name: "registry", type: "string", required: false, description: "Registry source name (default: 'default')" }
|
|
138365
|
+
]
|
|
138366
|
+
},
|
|
138348
138367
|
{
|
|
138349
138368
|
name: "auth_status",
|
|
138350
138369
|
description: "Check which configs have missing or misconfigured credentials. Returns per-config auth status and the specific env vars that are missing.",
|
|
@@ -141195,7 +141214,17 @@ async function handleGetConfig(ctx, args2) {
|
|
|
141195
141214
|
return { success: true, data: raw };
|
|
141196
141215
|
}
|
|
141197
141216
|
async function handleListConfigs(ctx, _args) {
|
|
141198
|
-
const
|
|
141217
|
+
const seen = /* @__PURE__ */ new Map();
|
|
141218
|
+
for (const rt of ctx.registry.list()) {
|
|
141219
|
+
if (!seen.has(rt.configId)) {
|
|
141220
|
+
seen.set(rt.configId, {
|
|
141221
|
+
id: rt.configId,
|
|
141222
|
+
...rt.configName && { name: rt.configName },
|
|
141223
|
+
...rt.configDescription && { description: rt.configDescription }
|
|
141224
|
+
});
|
|
141225
|
+
}
|
|
141226
|
+
}
|
|
141227
|
+
const configs = [...seen.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
141199
141228
|
return { success: true, data: { configs } };
|
|
141200
141229
|
}
|
|
141201
141230
|
async function handleUpdateConfig(ctx, args2) {
|
|
@@ -141633,6 +141662,112 @@ async function handleRegistryInstall(ctx, args2) {
|
|
|
141633
141662
|
return { success: false, data: { error: err.message } };
|
|
141634
141663
|
}
|
|
141635
141664
|
}
|
|
141665
|
+
async function handleRegistryUpdate(ctx, args2) {
|
|
141666
|
+
const configId = args2.config_id;
|
|
141667
|
+
const registry2 = args2.registry ?? "default";
|
|
141668
|
+
const { installed } = loadManifest();
|
|
141669
|
+
const forRegistry = installed.filter((e) => e.registry === registry2);
|
|
141670
|
+
let toCheck;
|
|
141671
|
+
if (configId) {
|
|
141672
|
+
const entry = forRegistry.find((e) => {
|
|
141673
|
+
const withoutNs = e.slug.replace(/^@[^/]+\//, "");
|
|
141674
|
+
const colonIdx = withoutNs.indexOf(":");
|
|
141675
|
+
if (colonIdx === -1) return false;
|
|
141676
|
+
const base2 = withoutNs.slice(0, colonIdx);
|
|
141677
|
+
const ct = withoutNs.slice(colonIdx + 1);
|
|
141678
|
+
return `${base2}-${ct}` === configId;
|
|
141679
|
+
});
|
|
141680
|
+
if (!entry) {
|
|
141681
|
+
return {
|
|
141682
|
+
success: false,
|
|
141683
|
+
data: { error: `"${configId}" is not a registry-installed config or was not found in the manifest. Only configs installed from the registry can be updated this way.` }
|
|
141684
|
+
};
|
|
141685
|
+
}
|
|
141686
|
+
toCheck = [entry];
|
|
141687
|
+
} else {
|
|
141688
|
+
if (forRegistry.length === 0) {
|
|
141689
|
+
return { success: true, data: { message: "No registry-installed configs to update", updated: [], up_to_date: [] } };
|
|
141690
|
+
}
|
|
141691
|
+
toCheck = forRegistry;
|
|
141692
|
+
}
|
|
141693
|
+
let updateCheck;
|
|
141694
|
+
try {
|
|
141695
|
+
updateCheck = await checkUpdates(
|
|
141696
|
+
toCheck.map((e) => ({ slug: e.slug, version: e.version })),
|
|
141697
|
+
registry2
|
|
141698
|
+
);
|
|
141699
|
+
} catch (err) {
|
|
141700
|
+
if (err instanceof RegistryError) {
|
|
141701
|
+
return { success: false, data: { error: err.message, status: err.status } };
|
|
141702
|
+
}
|
|
141703
|
+
return { success: false, data: { error: err.message } };
|
|
141704
|
+
}
|
|
141705
|
+
if (updateCheck.updates.length === 0) {
|
|
141706
|
+
return {
|
|
141707
|
+
success: true,
|
|
141708
|
+
data: { message: "All configs are up to date", up_to_date: updateCheck.up_to_date }
|
|
141709
|
+
};
|
|
141710
|
+
}
|
|
141711
|
+
const updated = [];
|
|
141712
|
+
const errors2 = [];
|
|
141713
|
+
for (const info of updateCheck.updates) {
|
|
141714
|
+
const withoutAt = info.slug.startsWith("@") ? info.slug.slice(1) : info.slug;
|
|
141715
|
+
const slashIdx = withoutAt.indexOf("/");
|
|
141716
|
+
if (slashIdx === -1) continue;
|
|
141717
|
+
const namespace = withoutAt.slice(0, slashIdx);
|
|
141718
|
+
const rest = withoutAt.slice(slashIdx + 1);
|
|
141719
|
+
const colonIdx = rest.indexOf(":");
|
|
141720
|
+
const rawSlug = colonIdx !== -1 ? rest.slice(0, colonIdx) : rest;
|
|
141721
|
+
const connType = colonIdx !== -1 ? rest.slice(colonIdx + 1) : void 0;
|
|
141722
|
+
const installedId = `${rawSlug}-${connType ?? ""}`;
|
|
141723
|
+
const outFile = path17.join(ctx.configDir, `mcp.${installedId}.json`);
|
|
141724
|
+
try {
|
|
141725
|
+
const { payload, version: resolvedVersion } = await fetchVersionPayload(
|
|
141726
|
+
namespace,
|
|
141727
|
+
rawSlug,
|
|
141728
|
+
connType,
|
|
141729
|
+
info.latest_version,
|
|
141730
|
+
registry2
|
|
141731
|
+
);
|
|
141732
|
+
const payloadObj = payload;
|
|
141733
|
+
payloadObj.id = installedId;
|
|
141734
|
+
if (fs15.existsSync(outFile)) {
|
|
141735
|
+
try {
|
|
141736
|
+
const existing = JSON.parse(fs15.readFileSync(outFile, "utf-8"));
|
|
141737
|
+
const existingConnector = existing.connector;
|
|
141738
|
+
if (existingConnector?.env) {
|
|
141739
|
+
const newConnector = payloadObj.connector ?? {};
|
|
141740
|
+
newConnector.env = existingConnector.env;
|
|
141741
|
+
payloadObj.connector = newConnector;
|
|
141742
|
+
}
|
|
141743
|
+
const existingOverlays = existing.overlays ?? {};
|
|
141744
|
+
const newOverlays = payloadObj.overlays ?? {};
|
|
141745
|
+
payloadObj.overlays = { ...existingOverlays, ...newOverlays };
|
|
141746
|
+
} catch {
|
|
141747
|
+
}
|
|
141748
|
+
}
|
|
141749
|
+
fs15.mkdirSync(ctx.configDir, { recursive: true });
|
|
141750
|
+
fs15.writeFileSync(outFile, JSON.stringify(payloadObj, null, 2) + "\n", "utf-8");
|
|
141751
|
+
addToManifest(info.slug, resolvedVersion, connType ?? "", registry2);
|
|
141752
|
+
updated.push({ slug: info.slug, id: installedId, from: info.installed_version, to: resolvedVersion });
|
|
141753
|
+
} catch (err) {
|
|
141754
|
+
if (err instanceof RegistryError) {
|
|
141755
|
+
errors2.push({ slug: info.slug, error: err.message });
|
|
141756
|
+
} else {
|
|
141757
|
+
errors2.push({ slug: info.slug, error: err.message });
|
|
141758
|
+
}
|
|
141759
|
+
}
|
|
141760
|
+
}
|
|
141761
|
+
return {
|
|
141762
|
+
success: errors2.length === 0,
|
|
141763
|
+
data: {
|
|
141764
|
+
message: updated.length > 0 ? `Updated ${updated.length} config(s).${errors2.length > 0 ? ` ${errors2.length} failed.` : ""} Server will hot-reload automatically.` : `All configs are up to date.`,
|
|
141765
|
+
updated,
|
|
141766
|
+
up_to_date: updateCheck.up_to_date,
|
|
141767
|
+
...errors2.length > 0 && { errors: errors2 }
|
|
141768
|
+
}
|
|
141769
|
+
};
|
|
141770
|
+
}
|
|
141636
141771
|
async function handleRegistryCheckUpdates(_ctx, args2) {
|
|
141637
141772
|
const registry2 = args2.registry ?? "default";
|
|
141638
141773
|
try {
|
|
@@ -141927,6 +142062,7 @@ var HANDLERS = {
|
|
|
141927
142062
|
registry_browse: handleRegistryBrowse,
|
|
141928
142063
|
registry_install: handleRegistryInstall,
|
|
141929
142064
|
registry_check_updates: handleRegistryCheckUpdates,
|
|
142065
|
+
registry_update: handleRegistryUpdate,
|
|
141930
142066
|
// Auth
|
|
141931
142067
|
auth_status: handleAuthStatus,
|
|
141932
142068
|
auth_set: handleAuthSet,
|
|
@@ -144088,159 +144224,195 @@ async function run8(args2) {
|
|
|
144088
144224
|
|
|
144089
144225
|
// src/commands/update.ts
|
|
144090
144226
|
init_esm_shims();
|
|
144091
|
-
import { spawnSync } from "child_process";
|
|
144092
|
-
|
|
144093
|
-
// src/lib/update-check.ts
|
|
144094
|
-
init_esm_shims();
|
|
144095
|
-
import https from "https";
|
|
144096
144227
|
import fs22 from "fs";
|
|
144097
144228
|
import path23 from "path";
|
|
144098
|
-
|
|
144099
|
-
|
|
144100
|
-
|
|
144101
|
-
|
|
144102
|
-
|
|
144103
|
-
|
|
144104
|
-
|
|
144105
|
-
|
|
144106
|
-
|
|
144107
|
-
}
|
|
144108
|
-
|
|
144109
|
-
|
|
144110
|
-
|
|
144111
|
-
|
|
144112
|
-
|
|
144113
|
-
|
|
144114
|
-
|
|
144115
|
-
|
|
144116
|
-
|
|
144117
|
-
}
|
|
144118
|
-
}
|
|
144119
|
-
function fetchLatestVersion() {
|
|
144120
|
-
return new Promise((resolve3, reject) => {
|
|
144121
|
-
const req = https.get(
|
|
144122
|
-
`https://registry.npmjs.org/${PKG_NAME}/latest`,
|
|
144123
|
-
{ headers: { Accept: "application/json" } },
|
|
144124
|
-
(res) => {
|
|
144125
|
-
let data = "";
|
|
144126
|
-
res.on("data", (chunk) => {
|
|
144127
|
-
data += chunk;
|
|
144128
|
-
});
|
|
144129
|
-
res.on("end", () => {
|
|
144130
|
-
try {
|
|
144131
|
-
if (res.statusCode !== 200) {
|
|
144132
|
-
reject(new Error(`npm registry returned HTTP ${res.statusCode}`));
|
|
144133
|
-
return;
|
|
144134
|
-
}
|
|
144135
|
-
const json2 = JSON.parse(data);
|
|
144136
|
-
if (!json2.version) {
|
|
144137
|
-
reject(new Error("npm registry response missing version field"));
|
|
144138
|
-
return;
|
|
144139
|
-
}
|
|
144140
|
-
resolve3(json2.version);
|
|
144141
|
-
} catch (e) {
|
|
144142
|
-
reject(e);
|
|
144143
|
-
}
|
|
144144
|
-
});
|
|
144145
|
-
}
|
|
144146
|
-
);
|
|
144147
|
-
req.on("error", reject);
|
|
144148
|
-
req.setTimeout(5e3, () => {
|
|
144149
|
-
req.destroy();
|
|
144150
|
-
reject(new Error("timeout"));
|
|
144151
|
-
});
|
|
144152
|
-
});
|
|
144153
|
-
}
|
|
144154
|
-
function semverGt(a, b) {
|
|
144155
|
-
const parse3 = (v) => v.replace(/[^0-9.]/g, "").split(".").map(Number);
|
|
144156
|
-
const pa = parse3(a);
|
|
144157
|
-
const pb = parse3(b);
|
|
144158
|
-
for (let i = 0; i < 3; i++) {
|
|
144159
|
-
const na = pa[i] ?? 0;
|
|
144160
|
-
const nb = pb[i] ?? 0;
|
|
144161
|
-
if (na > nb) return true;
|
|
144162
|
-
if (na < nb) return false;
|
|
144163
|
-
}
|
|
144229
|
+
function compoundId2(entry) {
|
|
144230
|
+
const withoutNs = entry.slug.replace(/^@[^/]+\//, "");
|
|
144231
|
+
const colonIdx = withoutNs.indexOf(":");
|
|
144232
|
+
const rawSlug = colonIdx !== -1 ? withoutNs.slice(0, colonIdx) : withoutNs;
|
|
144233
|
+
const ct = colonIdx !== -1 ? withoutNs.slice(colonIdx + 1) : entry.connector_type;
|
|
144234
|
+
return `${rawSlug}-${ct}`;
|
|
144235
|
+
}
|
|
144236
|
+
function matchesTarget(entry, target) {
|
|
144237
|
+
const slug = entry.slug;
|
|
144238
|
+
if (slug === target || slug === `@${target}`) return true;
|
|
144239
|
+
const withoutNs = slug.replace(/^@[^/]+\//, "");
|
|
144240
|
+
const colonIdx = withoutNs.indexOf(":");
|
|
144241
|
+
const rawSlug = colonIdx !== -1 ? withoutNs.slice(0, colonIdx) : withoutNs;
|
|
144242
|
+
const ct = colonIdx !== -1 ? withoutNs.slice(colonIdx + 1) : "";
|
|
144243
|
+
if (`${rawSlug}-${ct}` === target) return true;
|
|
144244
|
+
if (withoutNs === target) return true;
|
|
144245
|
+
const withoutConnector = slug.replace(/:.*$/, "");
|
|
144246
|
+
if (withoutConnector === target || withoutConnector === `@${target}`) return true;
|
|
144247
|
+
if (!target.includes(":") && !target.includes("-") && rawSlug === target) return true;
|
|
144164
144248
|
return false;
|
|
144165
144249
|
}
|
|
144166
|
-
function
|
|
144167
|
-
const
|
|
144168
|
-
const
|
|
144169
|
-
const
|
|
144170
|
-
const
|
|
144171
|
-
|
|
144172
|
-
|
|
144173
|
-
|
|
144174
|
-
|
|
144175
|
-
|
|
144176
|
-
|
|
144177
|
-
|
|
144178
|
-
|
|
144250
|
+
async function downloadAndInstall(entry, latestVersion, configDir) {
|
|
144251
|
+
const withoutAt = entry.slug.startsWith("@") ? entry.slug.slice(1) : entry.slug;
|
|
144252
|
+
const slashIdx = withoutAt.indexOf("/");
|
|
144253
|
+
const namespace = withoutAt.slice(0, slashIdx);
|
|
144254
|
+
const rest = withoutAt.slice(slashIdx + 1);
|
|
144255
|
+
const colonIdx = rest.indexOf(":");
|
|
144256
|
+
const rawSlug = colonIdx !== -1 ? rest.slice(0, colonIdx) : rest;
|
|
144257
|
+
const connType = colonIdx !== -1 ? rest.slice(colonIdx + 1) : entry.connector_type;
|
|
144258
|
+
const id = `${rawSlug}-${connType}`;
|
|
144259
|
+
const outFile = path23.join(configDir, `mcp.${id}.json`);
|
|
144260
|
+
const { payload, version: resolvedVersion } = await fetchVersionPayload(
|
|
144261
|
+
namespace,
|
|
144262
|
+
rawSlug,
|
|
144263
|
+
connType,
|
|
144264
|
+
latestVersion,
|
|
144265
|
+
entry.registry
|
|
144179
144266
|
);
|
|
144180
|
-
|
|
144181
|
-
|
|
144182
|
-
|
|
144183
|
-
|
|
144184
|
-
|
|
144185
|
-
|
|
144267
|
+
const payloadObj = payload;
|
|
144268
|
+
payloadObj.id = id;
|
|
144269
|
+
if (fs22.existsSync(outFile)) {
|
|
144270
|
+
try {
|
|
144271
|
+
const existing = JSON.parse(fs22.readFileSync(outFile, "utf-8"));
|
|
144272
|
+
const existingConn = existing.connector;
|
|
144273
|
+
if (existingConn?.env) {
|
|
144274
|
+
const newConn = payloadObj.connector ?? {};
|
|
144275
|
+
newConn.env = existingConn.env;
|
|
144276
|
+
payloadObj.connector = newConn;
|
|
144277
|
+
}
|
|
144278
|
+
const existingOverlays = existing.overlays ?? {};
|
|
144279
|
+
const newOverlays = payloadObj.overlays ?? {};
|
|
144280
|
+
payloadObj.overlays = { ...existingOverlays, ...newOverlays };
|
|
144281
|
+
} catch {
|
|
144186
144282
|
}
|
|
144283
|
+
}
|
|
144284
|
+
fs22.mkdirSync(configDir, { recursive: true });
|
|
144285
|
+
fs22.writeFileSync(outFile, JSON.stringify(payloadObj, null, 2) + "\n", "utf-8");
|
|
144286
|
+
addToManifest(entry.slug, resolvedVersion, connType, entry.registry, entry.forked_from ?? null);
|
|
144287
|
+
return { id, from: entry.version, to: resolvedVersion };
|
|
144288
|
+
}
|
|
144289
|
+
async function run9(args2) {
|
|
144290
|
+
const registryIdx = args2.indexOf("--registry");
|
|
144291
|
+
const registry2 = registryIdx !== -1 ? args2[registryIdx + 1] ?? "default" : "default";
|
|
144292
|
+
const skipIndices = /* @__PURE__ */ new Set();
|
|
144293
|
+
if (registryIdx !== -1) {
|
|
144294
|
+
skipIndices.add(registryIdx);
|
|
144295
|
+
skipIndices.add(registryIdx + 1);
|
|
144296
|
+
}
|
|
144297
|
+
const target = args2.find((a, i) => !skipIndices.has(i) && !a.startsWith("--"));
|
|
144298
|
+
const systemConfig = loadSystemConfig(process.cwd());
|
|
144299
|
+
const configDir = resolveConfigDir(void 0, systemConfig);
|
|
144300
|
+
const { installed } = loadManifest();
|
|
144301
|
+
const forRegistry = installed.filter((e) => e.registry === registry2);
|
|
144302
|
+
if (forRegistry.length === 0) {
|
|
144303
|
+
console.log();
|
|
144304
|
+
console.log(dim(" No registry-installed configs found."));
|
|
144305
|
+
console.log(dim(` Install one with: ${bold("heku install @ns/slug")}`));
|
|
144306
|
+
console.log();
|
|
144187
144307
|
return;
|
|
144188
144308
|
}
|
|
144189
|
-
|
|
144190
|
-
|
|
144191
|
-
|
|
144192
|
-
|
|
144309
|
+
let toCheck;
|
|
144310
|
+
if (target) {
|
|
144311
|
+
toCheck = forRegistry.filter((e) => matchesTarget(e, target));
|
|
144312
|
+
if (toCheck.length === 0) {
|
|
144313
|
+
console.log();
|
|
144314
|
+
console.error(red("\u2717") + ` No installed config matches "${bold(target)}".`);
|
|
144315
|
+
console.error(dim(` Run ${bold("heku list")} to see installed configs.`));
|
|
144316
|
+
console.log();
|
|
144317
|
+
process.exit(1);
|
|
144193
144318
|
}
|
|
144194
|
-
}
|
|
144195
|
-
|
|
144196
|
-
}
|
|
144197
|
-
|
|
144198
|
-
|
|
144199
|
-
|
|
144200
|
-
|
|
144201
|
-
|
|
144202
|
-
|
|
144319
|
+
} else {
|
|
144320
|
+
toCheck = forRegistry;
|
|
144321
|
+
}
|
|
144322
|
+
console.log();
|
|
144323
|
+
if (target) {
|
|
144324
|
+
console.log(bold(` Checking ${cyan(target)}...`));
|
|
144325
|
+
} else {
|
|
144326
|
+
console.log(bold(" Checking for updates..."));
|
|
144327
|
+
}
|
|
144328
|
+
console.log();
|
|
144329
|
+
let updateResult;
|
|
144203
144330
|
try {
|
|
144204
|
-
|
|
144205
|
-
|
|
144206
|
-
|
|
144207
|
-
|
|
144331
|
+
updateResult = await checkUpdates(
|
|
144332
|
+
toCheck.map((e) => ({ slug: e.slug, version: e.version })),
|
|
144333
|
+
registry2
|
|
144334
|
+
);
|
|
144335
|
+
} catch (err) {
|
|
144336
|
+
console.error(
|
|
144337
|
+
red("\u2717") + ` ${err instanceof RegistryError ? err.message : err.message}`
|
|
144338
|
+
);
|
|
144208
144339
|
process.exit(1);
|
|
144209
144340
|
}
|
|
144210
|
-
|
|
144211
|
-
|
|
144212
|
-
|
|
144341
|
+
const { updates, up_to_date, deprecated } = updateResult;
|
|
144342
|
+
const colWidth = Math.max(
|
|
144343
|
+
...[...updates, ...up_to_date, ...deprecated].map((u) => {
|
|
144344
|
+
const entry = toCheck.find((e) => e.slug === u.slug);
|
|
144345
|
+
return entry ? compoundId2(entry).length : 0;
|
|
144346
|
+
}),
|
|
144347
|
+
12
|
|
144348
|
+
);
|
|
144349
|
+
for (const u of up_to_date) {
|
|
144350
|
+
const entry = toCheck.find((e) => e.slug === u.slug);
|
|
144351
|
+
const id = entry ? compoundId2(entry) : u.slug;
|
|
144352
|
+
console.log(` ${dim(id.padEnd(colWidth))} ${dim(`v${u.version}`)} ${dim("up to date")}`);
|
|
144353
|
+
}
|
|
144354
|
+
for (const u of updates) {
|
|
144355
|
+
const entry = toCheck.find((e) => e.slug === u.slug);
|
|
144356
|
+
const id = entry ? compoundId2(entry) : u.slug;
|
|
144357
|
+
const badge = u.breaking ? yellow(`${u.severity} \xB7 breaking`) : cyan(u.severity);
|
|
144358
|
+
console.log(
|
|
144359
|
+
` ${bold(id.padEnd(colWidth))} ${dim(`v${u.installed_version}`)} \u2192 ${green(`v${u.latest_version}`)} [${badge}]`
|
|
144360
|
+
);
|
|
144361
|
+
}
|
|
144362
|
+
for (const d of deprecated) {
|
|
144363
|
+
const entry = toCheck.find((e) => e.slug === d.slug);
|
|
144364
|
+
const id = entry ? compoundId2(entry) : d.slug;
|
|
144365
|
+
const note = d.replacement ? ` \u2192 use ${cyan(d.replacement)}` : "";
|
|
144366
|
+
console.log(` ${yellow(id.padEnd(colWidth))} ${dim(`v${d.installed_version}`)} ${yellow("deprecated")}${note}`);
|
|
144367
|
+
}
|
|
144368
|
+
console.log();
|
|
144369
|
+
if (updates.length === 0) {
|
|
144370
|
+
console.log(green("\u2713") + (target ? ` ${cyan(target)} is up to date.` : " All configs are up to date."));
|
|
144371
|
+
if (deprecated.length > 0) {
|
|
144372
|
+
console.log(yellow("\u26A0") + ` ${deprecated.length} deprecated \u2014 consider replacing them.`);
|
|
144373
|
+
}
|
|
144374
|
+
console.log();
|
|
144213
144375
|
return;
|
|
144214
144376
|
}
|
|
144215
|
-
|
|
144216
|
-
`);
|
|
144217
|
-
|
|
144218
|
-
|
|
144219
|
-
|
|
144220
|
-
|
|
144221
|
-
|
|
144222
|
-
|
|
144223
|
-
|
|
144224
|
-
|
|
144225
|
-
process.
|
|
144226
|
-
|
|
144227
|
-
|
|
144228
|
-
|
|
144229
|
-
|
|
144230
|
-
)
|
|
144231
|
-
|
|
144377
|
+
const noun = updates.length === 1 ? "config" : "configs";
|
|
144378
|
+
console.log(bold(` Updating ${updates.length} ${noun}...`));
|
|
144379
|
+
console.log();
|
|
144380
|
+
const entryBySlug = new Map(toCheck.map((e) => [e.slug, e]));
|
|
144381
|
+
let successCount = 0;
|
|
144382
|
+
let failCount = 0;
|
|
144383
|
+
for (const u of updates) {
|
|
144384
|
+
const entry = entryBySlug.get(u.slug);
|
|
144385
|
+
if (!entry) continue;
|
|
144386
|
+
const id = compoundId2(entry);
|
|
144387
|
+
process.stdout.write(` ${bold(id.padEnd(colWidth))} Downloading... `);
|
|
144388
|
+
try {
|
|
144389
|
+
const result = await downloadAndInstall(entry, u.latest_version, configDir);
|
|
144390
|
+
console.log(green("done") + ` ${dim(`${result.from} \u2192 ${result.to}`)}`);
|
|
144391
|
+
successCount++;
|
|
144392
|
+
} catch (err) {
|
|
144393
|
+
console.log(red("failed"));
|
|
144394
|
+
const msg = err instanceof RegistryError ? err.message : err.message;
|
|
144395
|
+
console.error(` ${"".padEnd(colWidth)} ${red("\u2717")} ${msg}`);
|
|
144396
|
+
failCount++;
|
|
144397
|
+
}
|
|
144232
144398
|
}
|
|
144233
|
-
|
|
144234
|
-
|
|
144235
|
-
`);
|
|
144399
|
+
console.log();
|
|
144400
|
+
if (failCount === 0) {
|
|
144401
|
+
const upToDateNote = up_to_date.length > 0 ? ` ${dim(`${up_to_date.length} already up to date.`)}` : "";
|
|
144402
|
+
console.log(green("\u2713") + ` ${successCount} ${noun} updated.${upToDateNote}`);
|
|
144403
|
+
} else if (successCount > 0) {
|
|
144404
|
+
console.log(yellow("\u26A0") + ` ${successCount} updated, ${failCount} failed.`);
|
|
144405
|
+
} else {
|
|
144406
|
+
console.log(red("\u2717") + ` All updates failed.`);
|
|
144407
|
+
process.exit(1);
|
|
144408
|
+
}
|
|
144409
|
+
console.log(dim(" Restart or reload heku to apply changes."));
|
|
144410
|
+
console.log();
|
|
144236
144411
|
}
|
|
144237
144412
|
|
|
144238
144413
|
// src/cli.ts
|
|
144239
144414
|
var args = process.argv.slice(2);
|
|
144240
144415
|
var command = args[0] ?? "start";
|
|
144241
|
-
if (command !== "update") {
|
|
144242
|
-
checkForUpdate();
|
|
144243
|
-
}
|
|
144244
144416
|
switch (command) {
|
|
144245
144417
|
case "start":
|
|
144246
144418
|
await run(args.slice(1));
|
|
@@ -144296,11 +144468,12 @@ function printUsage() {
|
|
|
144296
144468
|
heku auth setup [service] Interactive credential setup wizard
|
|
144297
144469
|
heku install <target> Install a config from the registry
|
|
144298
144470
|
heku uninstall <target> Remove an installed registry config
|
|
144471
|
+
heku update Update all installed configs to latest versions
|
|
144472
|
+
heku update <config> Update a specific config (e.g. github-http or @ns/github:http)
|
|
144299
144473
|
heku login Log in to the mcp.rtl.space registry
|
|
144300
144474
|
heku logout Log out of the registry
|
|
144301
144475
|
heku publish [file] Publish a config to the registry
|
|
144302
144476
|
heku fork <namespace/slug> Fork a registry config into your namespace
|
|
144303
|
-
heku update Update heku to the latest version
|
|
144304
144477
|
heku help Show this message
|
|
144305
144478
|
|
|
144306
144479
|
HTTP mode:
|