switchroom 0.14.11 → 0.14.13
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/dist/agent-scheduler/index.js +4 -0
- package/dist/auth-broker/index.js +4 -0
- package/dist/cli/notion-write-pretool.mjs +4 -0
- package/dist/cli/switchroom.js +255 -12
- package/dist/host-control/main.js +84 -6
- package/dist/vault/approvals/kernel-server.js +5 -1
- package/dist/vault/broker/server.js +5 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/bridge/bridge.js +61 -8
- package/telegram-plugin/dist/gateway/gateway.js +287 -161
- package/telegram-plugin/dist/server.js +64 -9
- package/telegram-plugin/gateway/gateway.ts +78 -66
- package/telegram-plugin/gateway/ipc-protocol.ts +4 -2
- package/telegram-plugin/permission-rule.ts +200 -122
- package/telegram-plugin/permission-title.ts +209 -197
- package/telegram-plugin/tests/always-allow-grant.test.ts +86 -54
- package/telegram-plugin/tests/always-allow-persist.test.ts +35 -34
- package/telegram-plugin/tests/permission-rule.test.ts +185 -127
- package/telegram-plugin/tests/permission-title.test.ts +109 -195
|
@@ -11271,7 +11271,7 @@ var init_zod = __esm(() => {
|
|
|
11271
11271
|
});
|
|
11272
11272
|
|
|
11273
11273
|
// src/config/schema.ts
|
|
11274
|
-
var CodeRepoEntrySchema, AgentBindMountSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReleaseBlock, NetworkIsolationSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, HostdConfigSchema, SwitchroomConfigSchema;
|
|
11274
|
+
var CodeRepoEntrySchema, AgentBindMountSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReleaseBlock, NetworkIsolationSchema, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, HostdConfigSchema, SwitchroomConfigSchema;
|
|
11275
11275
|
var init_schema = __esm(() => {
|
|
11276
11276
|
init_zod();
|
|
11277
11277
|
CodeRepoEntrySchema = exports_external.object({
|
|
@@ -11721,6 +11721,9 @@ var init_schema = __esm(() => {
|
|
|
11721
11721
|
enabled: exports_external.boolean().default(true).describe("Whether the host-control daemon is in use. Default: true (since " + "RFC C Phase 2 default-flip — the gateway's /restart, /new, /reset, " + "and /update apply slash-commands all dispatch through hostd, and " + "without it those verbs fail on docker-mode installs because the " + "agent container has no docker binary/socket). " + "When true, the compose generator emits per-agent bind mounts " + "at `~/.switchroom/hostd/<name>/sock` for every admin-flagged " + "agent. Install the daemon with `switchroom hostd install` — " + "it runs as a docker container in its own compose project " + "(`switchroom-hostd`), separate from the agent fleet's compose " + "project so `up -d --remove-orphans` cycles of the fleet " + "can't recreate the daemon mid-RPC. See RFC C §5.1. " + "Set enabled: false only on legacy systemd-mode installs that " + "still rely on the in-container `spawnSwitchroomDetached` " + "shellout (removal is tracked as RFC C Phase 3)."),
|
|
11722
11722
|
auto_release_check: AutoReleaseCheckSchema.default({}).describe("Pull-based release-triggered fleet restart (#1743). hostd polls " + "the remote release tag on a fixed interval and applies + " + "restarts the fleet (graceful) when a new release is detected. " + "Opt-in: default enabled=false.")
|
|
11723
11723
|
});
|
|
11724
|
+
WebServiceConfigSchema = exports_external.object({
|
|
11725
|
+
managed: exports_external.boolean().default(false).describe("Whether `switchroom update` refreshes the web-service container " + "(dashboard + GitHub-webhook receiver) via `switchroom webd " + "install`. Default: false — existing installs run the web server " + "as the legacy `switchroom-web.service` systemd unit and must not " + "be surprised by a container takeover of host loopback 127.0.0.1:" + "8080 mid-update. Set true ONLY after cutting over to the " + "container (stop+disable the systemd unit, `switchroom webd " + "install`). The container runs in its own compose project " + "(`switchroom-web`), separate from the agent fleet, with " + "network_mode: host so it keeps owning loopback:8080 for the " + "cloudflared tunnel + tailscale serve consumers.")
|
|
11726
|
+
});
|
|
11724
11727
|
HostdConfigSchema = exports_external.object({
|
|
11725
11728
|
config_edit_enabled: exports_external.boolean().default(false).describe("Opt-in toggle for the `config_propose_edit` hostd verb (RFC " + "admin-agent-config-edit §3). Default false — the verb returns " + "`E_CONFIG_EDIT_DISABLED` until the operator explicitly flips " + "this to true. When true (and once PR 1c lands the apply path), " + "admin agents can propose unified-diff patches against " + "`/state/config/switchroom.yaml`, gated by an operator approval " + "card in the primary chat. Same trust posture as `update_apply` " + "and `agent_restart`: the human-in-the-loop tap is the security " + "boundary, not the agent's judgement."),
|
|
11726
11729
|
config_edit_rate_per_hour: exports_external.number().int().min(1).max(20).default(3).describe("Per-requesting-agent rate cap for `config_propose_edit` cards " + "(RFC admin-agent-config-edit §5). Default 3 cards/hour; min 1, " + "max 20. Implemented as a sqlite token bucket in PR 1c; the " + "field is wired here in PR 1a so operators can pin it before the " + "limiter is live. Above the cap, the verb returns " + "`E_RATE_LIMITED` without raising a card.")
|
|
@@ -11754,6 +11757,7 @@ var init_schema = __esm(() => {
|
|
|
11754
11757
|
quota: QuotaConfigSchema.optional().describe("Optional weekly/monthly USD spend budgets rendered in the session " + "greeting. Usage is read from ccusage at runtime; no network calls."),
|
|
11755
11758
|
host_control: HostControlConfigSchema.default({}).describe("Host-control daemon configuration. Defaults to enabled=true since " + "RFC C Phase 2 (docs/rfcs/host-control-daemon.md). Omit the block " + "to accept defaults; set `enabled: false` only on legacy systemd-" + "mode installs (removal tracked as RFC C Phase 3)."),
|
|
11756
11759
|
hostd: HostdConfigSchema.default({}).describe("hostd verb-level knobs (RFC admin-agent-config-edit). Distinct " + "from `host_control:` which governs whether the daemon runs at " + "all. Currently scopes the opt-in flag and rate cap for the new " + "`config_propose_edit` verb (PR 1a — disabled by default)."),
|
|
11760
|
+
web_service: WebServiceConfigSchema.default({}).describe("Web-service container (dashboard + GitHub-webhook receiver) config. " + "Defaults to managed=false so existing systemd-mode installs are " + "untouched. Set managed: true after cutting over to the " + "`switchroom-web` container — then `switchroom update` keeps it " + "refreshed. See `switchroom webd install`."),
|
|
11757
11761
|
google_accounts: exports_external.record(exports_external.string().regex(/^[^@\s:]+@[^@\s:]+\.[^@\s:]+$/, {
|
|
11758
11762
|
message: "Account key must be a Google account email like 'alice@example.com' (colons not allowed)"
|
|
11759
11763
|
}).transform((v) => v.trim().toLowerCase()), exports_external.object({
|
package/package.json
CHANGED
|
@@ -24446,6 +24446,40 @@ function createIpcClient(options) {
|
|
|
24446
24446
|
|
|
24447
24447
|
// permission-rule.ts
|
|
24448
24448
|
import { basename as basename2 } from "node:path";
|
|
24449
|
+
var FILE_TOOLS = new Set([
|
|
24450
|
+
"Edit",
|
|
24451
|
+
"Write",
|
|
24452
|
+
"MultiEdit",
|
|
24453
|
+
"NotebookEdit",
|
|
24454
|
+
"Read"
|
|
24455
|
+
]);
|
|
24456
|
+
var BROAD_ONLY_TOOLS = new Set([
|
|
24457
|
+
"Glob",
|
|
24458
|
+
"Grep",
|
|
24459
|
+
"WebFetch",
|
|
24460
|
+
"WebSearch",
|
|
24461
|
+
"Task",
|
|
24462
|
+
"Agent",
|
|
24463
|
+
"TodoWrite",
|
|
24464
|
+
"ExitPlanMode"
|
|
24465
|
+
]);
|
|
24466
|
+
function resolveSkillName(input) {
|
|
24467
|
+
return readString(input, "skill") ?? readString(input, "skill_name") ?? readString(input, "skillName") ?? readString(input, "name") ?? skillBasenameFromPath(input);
|
|
24468
|
+
}
|
|
24469
|
+
function filePathFrom(input) {
|
|
24470
|
+
if (!input)
|
|
24471
|
+
return null;
|
|
24472
|
+
return readString(input, "file_path") ?? readString(input, "notebook_path");
|
|
24473
|
+
}
|
|
24474
|
+
function bashFirstToken(command) {
|
|
24475
|
+
const m = /^\s*([^\s|&;<>()`$]+)/.exec(command);
|
|
24476
|
+
if (!m)
|
|
24477
|
+
return null;
|
|
24478
|
+
const tok = m[1];
|
|
24479
|
+
if (tok.includes(".."))
|
|
24480
|
+
return null;
|
|
24481
|
+
return /^[A-Za-z0-9._\-\/]+$/.test(tok) ? tok : null;
|
|
24482
|
+
}
|
|
24449
24483
|
function parseInput(raw) {
|
|
24450
24484
|
if (!raw || typeof raw !== "string")
|
|
24451
24485
|
return null;
|
|
@@ -24474,16 +24508,35 @@ function skillBasenameFromPath(input) {
|
|
|
24474
24508
|
function matchesAllowRule(rule, toolName, inputPreview) {
|
|
24475
24509
|
if (!rule || !toolName)
|
|
24476
24510
|
return false;
|
|
24477
|
-
|
|
24478
|
-
|
|
24479
|
-
|
|
24511
|
+
if (rule.endsWith("__*") && rule.startsWith("mcp__")) {
|
|
24512
|
+
const prefix = rule.slice(0, -1);
|
|
24513
|
+
return toolName.startsWith(prefix);
|
|
24514
|
+
}
|
|
24515
|
+
const scoped = /^([A-Za-z]+)\((.+)\)$/.exec(rule);
|
|
24516
|
+
if (scoped) {
|
|
24517
|
+
const ruleTool = scoped[1];
|
|
24518
|
+
const arg = scoped[2];
|
|
24519
|
+
if (ruleTool !== toolName)
|
|
24480
24520
|
return false;
|
|
24481
|
-
const ruleSkill = skillMatch[1];
|
|
24482
24521
|
const input = parseInput(inputPreview);
|
|
24483
|
-
if (
|
|
24484
|
-
|
|
24485
|
-
|
|
24486
|
-
|
|
24522
|
+
if (ruleTool === "Skill") {
|
|
24523
|
+
if (!input)
|
|
24524
|
+
return false;
|
|
24525
|
+
return resolveSkillName(input) === arg;
|
|
24526
|
+
}
|
|
24527
|
+
if (ruleTool === "Bash") {
|
|
24528
|
+
const cmd = input ? readString(input, "command") : null;
|
|
24529
|
+
if (!cmd)
|
|
24530
|
+
return false;
|
|
24531
|
+
const m = /^([^:]+):\*$/.exec(arg);
|
|
24532
|
+
if (!m)
|
|
24533
|
+
return false;
|
|
24534
|
+
return bashFirstToken(cmd) === m[1];
|
|
24535
|
+
}
|
|
24536
|
+
if (FILE_TOOLS.has(ruleTool)) {
|
|
24537
|
+
return filePathFrom(input) === arg;
|
|
24538
|
+
}
|
|
24539
|
+
return false;
|
|
24487
24540
|
}
|
|
24488
24541
|
return rule === toolName;
|
|
24489
24542
|
}
|