Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions scripts/telegram-bridge.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* Env:
* TELEGRAM_BOT_TOKEN — from @BotFather
* NVIDIA_API_KEY — for inference
* SANDBOX_NAME — sandbox name (default: nemoclaw)
* SANDBOX_NAME — sandbox name (default: from registry, or "my-assistant")
* ALLOWED_CHAT_IDS — comma-separated Telegram chat IDs to accept (optional, accepts all if unset)
*/

Expand All @@ -29,15 +29,32 @@ if (!OPENSHELL) {

const TOKEN = process.env.TELEGRAM_BOT_TOKEN;
const API_KEY = process.env.NVIDIA_API_KEY;
const SANDBOX = process.env.SANDBOX_NAME || "nemoclaw";
try { validateName(SANDBOX, "SANDBOX_NAME"); } catch (e) { console.error(e.message); process.exit(1); }
const ALLOWED_CHATS = process.env.ALLOWED_CHAT_IDS
? process.env.ALLOWED_CHAT_IDS.split(",").map((s) => s.trim())
: null;

if (!TOKEN) { console.error("TELEGRAM_BOT_TOKEN required"); process.exit(1); }
if (!API_KEY) { console.error("NVIDIA_API_KEY required"); process.exit(1); }

/**
* Resolve the sandbox name. Priority:
* 1. SANDBOX_NAME env var (explicit override)
* 2. The default sandbox from the NemoClaw registry
* 3. Fallback to "my-assistant" (matches onboard default)
*/
function resolveSandboxName() {
if (process.env.SANDBOX_NAME) return process.env.SANDBOX_NAME;
try {
const registry = require("../bin/lib/registry");
const def = registry.getDefault();
if (def) return def;
} catch {}
return "my-assistant";
}

const SANDBOX = resolveSandboxName();
try { validateName(SANDBOX, "SANDBOX_NAME"); } catch (e) { console.error(e.message); process.exit(1); }

let offset = 0;
const activeSessions = new Map(); // chatId → message history

Expand Down
85 changes: 85 additions & 0 deletions test/telegram-bridge-sandbox-name.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

const { describe, it } = require("node:test");
const assert = require("node:assert/strict");
const fs = require("node:fs");
const path = require("node:path");

const ROOT = path.resolve(__dirname, "..");
const SCRIPT = fs.readFileSync(path.join(ROOT, "scripts", "telegram-bridge.js"), "utf-8");

function extractFunctionSource(source, name) {
const signature = `function ${name}()`;
const start = source.indexOf(signature);
assert.notEqual(start, -1, `expected source to include ${signature}`);

const bodyStart = source.indexOf("{", start);
assert.notEqual(bodyStart, -1, `expected ${name} to have a function body`);

let depth = 0;
for (let i = bodyStart; i < source.length; i += 1) {
const char = source[i];
if (char === "{") {
depth += 1;
} else if (char === "}") {
depth -= 1;
if (depth === 0) {
return source.slice(start, i + 1);
}
}
}

assert.fail(`expected ${name}() to have balanced braces`);
}

const RESOLVE_SANDBOX_NAME_SOURCE = extractFunctionSource(SCRIPT, "resolveSandboxName");

function resolveSandboxNameWith({ env = {}, registryDefault, registryThrows = false } = {}) {
assert.ok(RESOLVE_SANDBOX_NAME_SOURCE, "expected telegram bridge to define resolveSandboxName()");

const processStub = { env: { ...env } };
const requireStub = (specifier) => {
if (specifier !== "../bin/lib/registry") {
throw new Error(`unexpected require: ${specifier}`);
}
if (registryThrows) {
throw new Error("registry unavailable");
}
return { getDefault: () => registryDefault };
};

const resolveSandboxName = new Function(
"process",
"require",
`${RESOLVE_SANDBOX_NAME_SOURCE}\nreturn resolveSandboxName;`,
)(processStub, requireStub);

return resolveSandboxName();
}

describe("telegram bridge sandbox resolution", () => {
it("prefers SANDBOX_NAME when explicitly set", () => {
assert.equal(
resolveSandboxNameWith({
env: { SANDBOX_NAME: "from-env" },
registryDefault: "from-registry",
}),
"from-env",
);
});

it("reads the default sandbox from the registry when env is unset", () => {
assert.equal(
resolveSandboxNameWith({ registryDefault: "from-registry" }),
"from-registry",
);
});

it("falls back to my-assistant when no explicit or registered sandbox exists", () => {
assert.equal(
resolveSandboxNameWith({ registryThrows: true }),
"my-assistant",
);
});
});