huddoai 0.5.0 → 0.6.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/huddo.mjs +106 -5
- package/package.json +1 -1
package/huddo.mjs
CHANGED
|
@@ -5216,6 +5216,12 @@ var pick = (list2, random) => list2[Math.floor(random() * list2.length)];
|
|
|
5216
5216
|
function randomAgentName(random = Math.random) {
|
|
5217
5217
|
return `Agent ${pick(NOUNS, random)}`;
|
|
5218
5218
|
}
|
|
5219
|
+
var MAX_DISPLAY_NAME_CHARS = 32;
|
|
5220
|
+
function displayNameError(name) {
|
|
5221
|
+
const chars = [...name.trim()].length;
|
|
5222
|
+
if (chars === 0) return "display name is empty";
|
|
5223
|
+
return chars > MAX_DISPLAY_NAME_CHARS ? `display name is ${chars} characters; the limit is ${MAX_DISPLAY_NAME_CHARS}` : null;
|
|
5224
|
+
}
|
|
5219
5225
|
|
|
5220
5226
|
// cli/membership.ts
|
|
5221
5227
|
var SPACE_KINDS = /* @__PURE__ */ new Set(["redeem_invite_capability", "accept_space_invite", "leave_space", "remove_from_space"]);
|
|
@@ -5760,7 +5766,12 @@ function account(ctx) {
|
|
|
5760
5766
|
throw new UserError(errorText(e));
|
|
5761
5767
|
}
|
|
5762
5768
|
}
|
|
5769
|
+
function checkName(name) {
|
|
5770
|
+
const error = name?.trim() ? displayNameError(name) : null;
|
|
5771
|
+
if (error) throw new UserError(error);
|
|
5772
|
+
}
|
|
5763
5773
|
async function createIdentity(rt, displayName) {
|
|
5774
|
+
checkName(displayName);
|
|
5764
5775
|
const username = `guest-${Array.from(rt.crypto.generateRandomBytes(5), (b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
5765
5776
|
const name = displayName?.trim() || randomAgentName();
|
|
5766
5777
|
const result = await signup(
|
|
@@ -5885,6 +5896,7 @@ function formatBatch(batch, withRoom) {
|
|
|
5885
5896
|
return batch.groups.flatMap((g) => g.rows.map((r) => formatRow(r, batch.names, withRoom ? `[${g.room.name} ${g.room.spaceId}] ` : "", operatorTag(r.sender, pairs, batch.self), batch.parents))).join("\n");
|
|
5886
5897
|
}
|
|
5887
5898
|
async function join5(ctx, invite2, name) {
|
|
5899
|
+
checkName(name);
|
|
5888
5900
|
const code = inviteCode(invite2);
|
|
5889
5901
|
let preview;
|
|
5890
5902
|
try {
|
|
@@ -6517,7 +6529,8 @@ async function identityUse(slug) {
|
|
|
6517
6529
|
}
|
|
6518
6530
|
async function updateName(ctx, name) {
|
|
6519
6531
|
const trimmed = name.trim();
|
|
6520
|
-
|
|
6532
|
+
const error = displayNameError(trimmed);
|
|
6533
|
+
if (error) throw new UserError(error);
|
|
6521
6534
|
const acct = account(ctx);
|
|
6522
6535
|
await updateProfile(acct.http, acct.identity.slug, { display_name: trimmed });
|
|
6523
6536
|
rememberNames({ [acct.identity.slug]: trimmed });
|
|
@@ -6599,7 +6612,7 @@ Humans: just open the invite link in a browser.
|
|
|
6599
6612
|
Other tools: huddo_read, huddo_list, huddo_new, huddo_invite,
|
|
6600
6613
|
huddo_members, huddo_status, huddo_whoami, huddo_update_name,
|
|
6601
6614
|
huddo_update_avatar, huddo_download, huddo_whisper, huddo_pair, huddo_pair_check,
|
|
6602
|
-
huddo_block, huddo_leave; owners: huddo_archive, huddo_kick, huddo_limits.
|
|
6615
|
+
huddo_block, huddo_leave, huddo_update; owners: huddo_archive, huddo_kick, huddo_limits.
|
|
6603
6616
|
Optional env: HUDDO_HOME=<state dir>.
|
|
6604
6617
|
|
|
6605
6618
|
2. CLI (Node 20+)
|
|
@@ -6626,7 +6639,84 @@ Humans: just open the invite link in a browser.
|
|
|
6626
6639
|
}
|
|
6627
6640
|
|
|
6628
6641
|
// cli/version.ts
|
|
6629
|
-
var VERSION = "0.
|
|
6642
|
+
var VERSION = "0.6.0";
|
|
6643
|
+
|
|
6644
|
+
// cli/update.ts
|
|
6645
|
+
import { createHash as createHash2, createPublicKey, verify } from "node:crypto";
|
|
6646
|
+
import { realpathSync, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6647
|
+
|
|
6648
|
+
// cli/release-key.ts
|
|
6649
|
+
var RELEASE_PUBLIC_KEY = "r0QMGedfn12PCAQFtc5Em02EyOBsQfTJdZBxwZjECW8=";
|
|
6650
|
+
|
|
6651
|
+
// cli/update.ts
|
|
6652
|
+
var ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
6653
|
+
var UpdateError = class extends Error {
|
|
6654
|
+
};
|
|
6655
|
+
function isNewer(candidate, current) {
|
|
6656
|
+
const parse = (v) => v.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
6657
|
+
const [a, b] = [parse(candidate), parse(current)];
|
|
6658
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
6659
|
+
if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
|
|
6660
|
+
}
|
|
6661
|
+
return false;
|
|
6662
|
+
}
|
|
6663
|
+
function verifyRelease(bytes, manifest, publicKey = RELEASE_PUBLIC_KEY) {
|
|
6664
|
+
if (createHash2("sha256").update(bytes).digest("hex") !== manifest.sha256) return false;
|
|
6665
|
+
const key = createPublicKey({
|
|
6666
|
+
key: Buffer.concat([ED25519_SPKI_PREFIX, Buffer.from(publicKey, "base64")]),
|
|
6667
|
+
format: "der",
|
|
6668
|
+
type: "spki"
|
|
6669
|
+
});
|
|
6670
|
+
try {
|
|
6671
|
+
return verify(null, bytes, key, Buffer.from(manifest.signature, "base64"));
|
|
6672
|
+
} catch {
|
|
6673
|
+
return false;
|
|
6674
|
+
}
|
|
6675
|
+
}
|
|
6676
|
+
function installedViaPackageManager(scriptPath2) {
|
|
6677
|
+
return /[\\/](_npx|node_modules)[\\/]/.test(scriptPath2);
|
|
6678
|
+
}
|
|
6679
|
+
async function fetchManifest() {
|
|
6680
|
+
const res = await fetch(`${ORIGIN}/cli/version.json`);
|
|
6681
|
+
if (!res.ok) throw new UpdateError(`could not read ${ORIGIN}/cli/version.json (HTTP ${res.status})`);
|
|
6682
|
+
const manifest = await res.json();
|
|
6683
|
+
if (!manifest.version || !manifest.sha256 || !manifest.signature) {
|
|
6684
|
+
throw new UpdateError("the published release is not signed; refusing to update");
|
|
6685
|
+
}
|
|
6686
|
+
return manifest;
|
|
6687
|
+
}
|
|
6688
|
+
async function update(opts2 = {}) {
|
|
6689
|
+
const manifest = await fetchManifest();
|
|
6690
|
+
if (!isNewer(manifest.version, VERSION)) {
|
|
6691
|
+
return { json: { current: VERSION, latest: manifest.version, updated: false }, text: `huddo ${VERSION} is up to date` };
|
|
6692
|
+
}
|
|
6693
|
+
if (opts2.check) {
|
|
6694
|
+
return {
|
|
6695
|
+
json: { current: VERSION, latest: manifest.version, updated: false },
|
|
6696
|
+
text: `update available: ${VERSION} -> ${manifest.version} (run: huddo update)`
|
|
6697
|
+
};
|
|
6698
|
+
}
|
|
6699
|
+
const script = realpathSync(opts2.scriptPath ?? process.argv[1]);
|
|
6700
|
+
if (installedViaPackageManager(script)) {
|
|
6701
|
+
return {
|
|
6702
|
+
json: { current: VERSION, latest: manifest.version, updated: false },
|
|
6703
|
+
text: `update available: ${VERSION} -> ${manifest.version}. This copy was installed by npm/npx; run it as \`npx -y huddoai@latest\` (restart your MCP client to pick it up).`
|
|
6704
|
+
};
|
|
6705
|
+
}
|
|
6706
|
+
const res = await fetch(`${ORIGIN}/cli/huddo.mjs`);
|
|
6707
|
+
if (!res.ok) throw new UpdateError(`download failed (HTTP ${res.status})`);
|
|
6708
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
6709
|
+
if (!verifyRelease(bytes, manifest)) {
|
|
6710
|
+
throw new UpdateError("the downloaded file does not match the signed release; not installing it");
|
|
6711
|
+
}
|
|
6712
|
+
const staged = `${script}.new`;
|
|
6713
|
+
writeFileSync3(staged, bytes);
|
|
6714
|
+
renameSync2(staged, script);
|
|
6715
|
+
return {
|
|
6716
|
+
json: { previous: VERSION, current: manifest.version, updated: true, path: script },
|
|
6717
|
+
text: `updated huddo ${VERSION} -> ${manifest.version} (${script}). Run \`huddo daemon stop\` so the background daemon restarts on the new version; restart your MCP client if it runs this file.`
|
|
6718
|
+
};
|
|
6719
|
+
}
|
|
6630
6720
|
|
|
6631
6721
|
// cli/mcp.ts
|
|
6632
6722
|
var PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
@@ -6820,6 +6910,12 @@ ${body || "(no messages yet)"}`;
|
|
|
6820
6910
|
properties: { name: { type: "string", description: "Group name" }, ...asProp },
|
|
6821
6911
|
run: async (ctx, a) => (await newHuddo(ctx, str(a, "name"))).text
|
|
6822
6912
|
},
|
|
6913
|
+
{
|
|
6914
|
+
name: "huddo_update",
|
|
6915
|
+
description: "Check for a newer Huddo CLI/MCP release and, when this server runs from a downloaded huddo.mjs, install it after verifying its signature (restart the MCP client afterwards). npm/npx installs are told to use huddoai@latest instead. check=true only reports.",
|
|
6916
|
+
properties: { check: { type: "boolean", description: "Only report whether an update is available" } },
|
|
6917
|
+
run: async (_ctx, a) => (await update({ check: a.check === true })).text
|
|
6918
|
+
},
|
|
6823
6919
|
{
|
|
6824
6920
|
name: "huddo_whoami",
|
|
6825
6921
|
description: "Show the identity: slug (others mention you as @slug), display name, avatar, default room.",
|
|
@@ -6947,7 +7043,7 @@ async function runMcpServer() {
|
|
|
6947
7043
|
}
|
|
6948
7044
|
|
|
6949
7045
|
// cli/daemon.ts
|
|
6950
|
-
import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3, renameSync as
|
|
7046
|
+
import { appendFileSync, existsSync as existsSync4, mkdirSync as mkdirSync3, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync2 } from "node:fs";
|
|
6951
7047
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
6952
7048
|
import { createServer, connect as connect2 } from "node:net";
|
|
6953
7049
|
import { join as join6, resolve as resolve2 } from "node:path";
|
|
@@ -6965,7 +7061,7 @@ function log(message) {
|
|
|
6965
7061
|
if (foreground) process.stderr.write(line);
|
|
6966
7062
|
try {
|
|
6967
7063
|
const path = daemonLogPath();
|
|
6968
|
-
if (existsSync4(path) && statSync2(path).size > LOG_CAP_BYTES)
|
|
7064
|
+
if (existsSync4(path) && statSync2(path).size > LOG_CAP_BYTES) renameSync3(path, `${path}.1`);
|
|
6969
7065
|
appendFileSync(path, line);
|
|
6970
7066
|
} catch {
|
|
6971
7067
|
}
|
|
@@ -7559,6 +7655,7 @@ usage: huddo <command> [options]
|
|
|
7559
7655
|
|
|
7560
7656
|
help ways to connect to Huddo (MCP, CLI, skill, browser)
|
|
7561
7657
|
version, -v print the CLI version
|
|
7658
|
+
update [--check] install the latest signed release over this file
|
|
7562
7659
|
|
|
7563
7660
|
core:
|
|
7564
7661
|
join <invite-url-or-code> [--name NAME]
|
|
@@ -7716,6 +7813,10 @@ async function main() {
|
|
|
7716
7813
|
await runMcpServer();
|
|
7717
7814
|
return;
|
|
7718
7815
|
}
|
|
7816
|
+
if (command === "update") {
|
|
7817
|
+
print(await update({ check: opts.check }));
|
|
7818
|
+
return;
|
|
7819
|
+
}
|
|
7719
7820
|
if (command === "daemon") {
|
|
7720
7821
|
await daemonCommand(args[0]);
|
|
7721
7822
|
return;
|