switchroom 0.12.13 → 0.12.14
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 +107 -371
- package/dist/cli/switchroom.js +397 -245
- package/dist/vault/approvals/kernel-server.js +20 -0
- package/dist/vault/broker/server.js +111 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +208 -26
- package/telegram-plugin/gateway/approval-callback.ts +24 -13
- package/telegram-plugin/gateway/gateway.ts +149 -17
- package/telegram-plugin/gateway/pending-permission-decisions.ts +112 -0
- package/telegram-plugin/gateway/vault-grant-inbound-builders.ts +117 -0
- package/telegram-plugin/tests/pending-permission-decisions.test.ts +73 -0
- package/telegram-plugin/tests/vault-save-inbound-builders.test.ts +96 -0
package/dist/cli/switchroom.js
CHANGED
|
@@ -20645,18 +20645,6 @@ function getGdriveMcpSettingsEntry(switchroomCliPath, options = {}) {
|
|
|
20645
20645
|
}
|
|
20646
20646
|
};
|
|
20647
20647
|
}
|
|
20648
|
-
function shouldEmitGdriveMcp(agentName, agentGoogleAccount, googleAccounts) {
|
|
20649
|
-
if (!agentGoogleAccount)
|
|
20650
|
-
return false;
|
|
20651
|
-
const account = agentGoogleAccount.trim().toLowerCase();
|
|
20652
|
-
if (account.length === 0)
|
|
20653
|
-
return false;
|
|
20654
|
-
const acctEntry = googleAccounts?.[account];
|
|
20655
|
-
if (!acctEntry)
|
|
20656
|
-
return false;
|
|
20657
|
-
const enabledFor = acctEntry.enabled_for ?? [];
|
|
20658
|
-
return enabledFor.includes(agentName);
|
|
20659
|
-
}
|
|
20660
20648
|
function getBuiltinDefaultMcpEntries() {
|
|
20661
20649
|
const playwright = getPlaywrightMcpSettingsEntry();
|
|
20662
20650
|
return [
|
|
@@ -20689,6 +20677,48 @@ var init_scaffold_integration = __esm(() => {
|
|
|
20689
20677
|
init_hindsight();
|
|
20690
20678
|
});
|
|
20691
20679
|
|
|
20680
|
+
// src/config/google-workspace-acl.ts
|
|
20681
|
+
function shouldEmitGdriveMcp(agentName, agentGoogleAccount, googleAccounts) {
|
|
20682
|
+
if (!agentGoogleAccount)
|
|
20683
|
+
return false;
|
|
20684
|
+
const account = agentGoogleAccount.trim().toLowerCase();
|
|
20685
|
+
if (account.length === 0)
|
|
20686
|
+
return false;
|
|
20687
|
+
const acctEntry = googleAccounts?.[account];
|
|
20688
|
+
if (!acctEntry)
|
|
20689
|
+
return false;
|
|
20690
|
+
const enabledFor = acctEntry.enabled_for ?? [];
|
|
20691
|
+
return enabledFor.includes(agentName);
|
|
20692
|
+
}
|
|
20693
|
+
function vaultRefKey(value) {
|
|
20694
|
+
if (typeof value !== "string" || !value.startsWith("vault:"))
|
|
20695
|
+
return null;
|
|
20696
|
+
const key = value.slice("vault:".length).split("#")[0];
|
|
20697
|
+
return key.length > 0 ? key : null;
|
|
20698
|
+
}
|
|
20699
|
+
function isGoogleClientCredentialKeyForAgent(config, agentName, key) {
|
|
20700
|
+
if (!agentName || !key)
|
|
20701
|
+
return false;
|
|
20702
|
+
const agentConfig = config.agents?.[agentName];
|
|
20703
|
+
if (!agentConfig)
|
|
20704
|
+
return false;
|
|
20705
|
+
if (agentConfig.mcp_servers?.["gdrive"] === false) {
|
|
20706
|
+
return false;
|
|
20707
|
+
}
|
|
20708
|
+
const account = agentConfig.google_workspace?.account;
|
|
20709
|
+
if (!shouldEmitGdriveMcp(agentName, account, config.google_accounts)) {
|
|
20710
|
+
return false;
|
|
20711
|
+
}
|
|
20712
|
+
const gw = config.google_workspace;
|
|
20713
|
+
if (!gw)
|
|
20714
|
+
return false;
|
|
20715
|
+
for (const ref of [gw.google_client_id, gw.google_client_secret]) {
|
|
20716
|
+
if (vaultRefKey(ref) === key)
|
|
20717
|
+
return true;
|
|
20718
|
+
}
|
|
20719
|
+
return false;
|
|
20720
|
+
}
|
|
20721
|
+
|
|
20692
20722
|
// src/agents/reconcile-default-skills.ts
|
|
20693
20723
|
import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, readlinkSync as readlinkSync2, rmSync, symlinkSync } from "node:fs";
|
|
20694
20724
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -21379,7 +21409,7 @@ function stripWireFields(entry) {
|
|
|
21379
21409
|
files: entry.files
|
|
21380
21410
|
};
|
|
21381
21411
|
}
|
|
21382
|
-
var MAX_FRAME_BYTES, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
|
|
21412
|
+
var MAX_FRAME_BYTES, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, PreflightAccessRequestSchema, OkPreflightAccessResponseSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, RequestSchema, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, ErrorResponseSchema, ResponseSchema;
|
|
21383
21413
|
var init_protocol = __esm(() => {
|
|
21384
21414
|
init_zod();
|
|
21385
21415
|
MAX_FRAME_BYTES = 64 * 1024;
|
|
@@ -21438,6 +21468,24 @@ var init_protocol = __esm(() => {
|
|
|
21438
21468
|
v: exports_external.literal(1),
|
|
21439
21469
|
op: exports_external.literal("lock")
|
|
21440
21470
|
});
|
|
21471
|
+
PreflightAccessRequestSchema = exports_external.object({
|
|
21472
|
+
v: exports_external.literal(1),
|
|
21473
|
+
op: exports_external.literal("preflight_access"),
|
|
21474
|
+
agent: exports_external.string().min(1),
|
|
21475
|
+
keys: exports_external.array(exports_external.string().min(1)).min(1).max(128)
|
|
21476
|
+
});
|
|
21477
|
+
OkPreflightAccessResponseSchema = exports_external.object({
|
|
21478
|
+
ok: exports_external.literal(true),
|
|
21479
|
+
op: exports_external.literal("preflight_access"),
|
|
21480
|
+
results: exports_external.array(exports_external.object({
|
|
21481
|
+
key: exports_external.string(),
|
|
21482
|
+
exists: exports_external.boolean(),
|
|
21483
|
+
acl_ok: exports_external.boolean(),
|
|
21484
|
+
acl_reason: exports_external.string().optional(),
|
|
21485
|
+
scope_ok: exports_external.boolean(),
|
|
21486
|
+
scope_reason: exports_external.string().optional()
|
|
21487
|
+
}))
|
|
21488
|
+
});
|
|
21441
21489
|
ApprovalRequestRequestSchema = exports_external.object({
|
|
21442
21490
|
v: exports_external.literal(1),
|
|
21443
21491
|
op: exports_external.literal("approval_request"),
|
|
@@ -21495,6 +21543,7 @@ var init_protocol = __esm(() => {
|
|
|
21495
21543
|
ListRequestSchema,
|
|
21496
21544
|
StatusRequestSchema,
|
|
21497
21545
|
LockRequestSchema,
|
|
21546
|
+
PreflightAccessRequestSchema,
|
|
21498
21547
|
MintGrantRequestSchema,
|
|
21499
21548
|
ListGrantsRequestSchema,
|
|
21500
21549
|
RevokeGrantRequestSchema,
|
|
@@ -21635,6 +21684,7 @@ var init_protocol = __esm(() => {
|
|
|
21635
21684
|
OkKeysResponseSchema,
|
|
21636
21685
|
OkStatusResponseSchema,
|
|
21637
21686
|
OkLockResponseSchema,
|
|
21687
|
+
OkPreflightAccessResponseSchema,
|
|
21638
21688
|
OkPutResponseSchema,
|
|
21639
21689
|
OkMintGrantResponseSchema,
|
|
21640
21690
|
OkListGrantsResponseSchema,
|
|
@@ -27169,6 +27219,9 @@ function checkAclByAgent(config, agentName, key) {
|
|
|
27169
27219
|
if (googleSlot !== null) {
|
|
27170
27220
|
return checkGoogleAccountAcl(config, agentName, googleSlot.account, key);
|
|
27171
27221
|
}
|
|
27222
|
+
if (isGoogleClientCredentialKeyForAgent(config, agentName, key)) {
|
|
27223
|
+
return { allow: true };
|
|
27224
|
+
}
|
|
27172
27225
|
const agentBot = agentConfig.bot_token;
|
|
27173
27226
|
const botRef = agentBot && agentBot.length > 0 ? agentBot : config.telegram?.bot_token;
|
|
27174
27227
|
if (typeof botRef === "string" && botRef.startsWith("vault:")) {
|
|
@@ -27226,6 +27279,7 @@ function checkGoogleAccountAcl(config, agentName, account, key) {
|
|
|
27226
27279
|
}
|
|
27227
27280
|
return { allow: true };
|
|
27228
27281
|
}
|
|
27282
|
+
var init_acl = () => {};
|
|
27229
27283
|
|
|
27230
27284
|
// src/util/audit-hashchain.ts
|
|
27231
27285
|
import { createHash as createHash6 } from "node:crypto";
|
|
@@ -28525,7 +28579,8 @@ import {
|
|
|
28525
28579
|
realpathSync as realpathSync4,
|
|
28526
28580
|
statSync as statSync20
|
|
28527
28581
|
} from "node:fs";
|
|
28528
|
-
import { userInfo } from "node:os";
|
|
28582
|
+
import { userInfo, homedir as homedir21 } from "node:os";
|
|
28583
|
+
import { join as join40 } from "node:path";
|
|
28529
28584
|
function resolveVaultPath2(config) {
|
|
28530
28585
|
return config.vault?.path ? config.vault.path.replace(/^~/, process.env.HOME ?? "") : resolveStatePath("vault.enc");
|
|
28531
28586
|
}
|
|
@@ -28575,7 +28630,47 @@ function collectVaultRefs2(value, out) {
|
|
|
28575
28630
|
}
|
|
28576
28631
|
}
|
|
28577
28632
|
}
|
|
28578
|
-
function
|
|
28633
|
+
function collectNeeds(resolved) {
|
|
28634
|
+
const cronKeys = new Set;
|
|
28635
|
+
for (const entry of resolved.schedule ?? []) {
|
|
28636
|
+
for (const s of entry.secrets ?? [])
|
|
28637
|
+
cronKeys.add(s);
|
|
28638
|
+
}
|
|
28639
|
+
const refKeys = new Set;
|
|
28640
|
+
collectVaultRefs2(resolved, refKeys);
|
|
28641
|
+
return { needed: new Set([...cronKeys, ...refKeys]), cronKeys };
|
|
28642
|
+
}
|
|
28643
|
+
function keyGap(key, isCron, exists, acl, scope) {
|
|
28644
|
+
const isGoogleSlot = key.startsWith("google:");
|
|
28645
|
+
if (!isGoogleSlot && !exists)
|
|
28646
|
+
return `'${key}' missing from the vault`;
|
|
28647
|
+
if (isCron && !acl.allow) {
|
|
28648
|
+
return `'${key}' (cron) \u2014 no static ACL grants read (${acl.reason})`;
|
|
28649
|
+
}
|
|
28650
|
+
if (!scope.allow) {
|
|
28651
|
+
return `'${key}' \u2014 per-key scope denies read (${scope.reason})`;
|
|
28652
|
+
}
|
|
28653
|
+
return null;
|
|
28654
|
+
}
|
|
28655
|
+
async function defaultPreflight(socketPath, agent, keys) {
|
|
28656
|
+
const r = await rpcRaw({ v: 1, op: "preflight_access", agent, keys }, { socket: socketPath, timeoutMs: 5000 });
|
|
28657
|
+
if (r.kind === "unreachable")
|
|
28658
|
+
return { kind: "unreachable", msg: r.msg };
|
|
28659
|
+
const resp = r.resp;
|
|
28660
|
+
if (resp.ok === true && resp.op === "preflight_access") {
|
|
28661
|
+
return {
|
|
28662
|
+
kind: "ok",
|
|
28663
|
+
results: resp.results
|
|
28664
|
+
};
|
|
28665
|
+
}
|
|
28666
|
+
if (resp.ok === false && resp.code === "LOCKED")
|
|
28667
|
+
return { kind: "locked" };
|
|
28668
|
+
return {
|
|
28669
|
+
kind: "unreachable",
|
|
28670
|
+
msg: resp.ok === false ? `broker error ${resp.code}: ${resp.msg}` : "unexpected broker response"
|
|
28671
|
+
};
|
|
28672
|
+
}
|
|
28673
|
+
async function runSecretAccessChecks(config, deps = {}) {
|
|
28579
28674
|
const results = [];
|
|
28580
28675
|
const vaultPath = deps.vaultPath ?? resolveVaultPath2(config);
|
|
28581
28676
|
const statVault = deps.statVault ?? defaultStatVault;
|
|
@@ -28609,14 +28704,57 @@ function runSecretAccessChecks(config, deps = {}) {
|
|
|
28609
28704
|
detail: `operator can read ${vf.realPath}`
|
|
28610
28705
|
});
|
|
28611
28706
|
}
|
|
28707
|
+
const pushAgentResult = (name, total, gaps) => {
|
|
28708
|
+
results.push(gaps.length === 0 ? {
|
|
28709
|
+
name: `secret access: ${name}`,
|
|
28710
|
+
status: "ok",
|
|
28711
|
+
detail: `${total} secret(s): all present + ACL ok`
|
|
28712
|
+
} : {
|
|
28713
|
+
name: `secret access: ${name}`,
|
|
28714
|
+
status: "fail",
|
|
28715
|
+
detail: `${gaps.length}/${total} unreachable \u2014 ${gaps.join("; ")}`,
|
|
28716
|
+
fix: "`switchroom vault set <key>` for missing keys; " + "`switchroom vault set <key> --allow " + name + "` to grant this agent read access"
|
|
28717
|
+
});
|
|
28718
|
+
};
|
|
28612
28719
|
const passphrase = deps.passphrase ?? process.env.SWITCHROOM_VAULT_PASSPHRASE;
|
|
28613
28720
|
if (!passphrase) {
|
|
28614
|
-
|
|
28615
|
-
|
|
28616
|
-
|
|
28617
|
-
|
|
28618
|
-
|
|
28619
|
-
|
|
28721
|
+
const sock = deps.brokerOperatorSocket ?? join40(homedir21(), ".switchroom", "broker-operator", "sock");
|
|
28722
|
+
const preflight = deps.preflight ?? ((a, k) => defaultPreflight(sock, a, k));
|
|
28723
|
+
for (const name of Object.keys(config.agents ?? {})) {
|
|
28724
|
+
const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[name]);
|
|
28725
|
+
const { needed, cronKeys } = collectNeeds(resolved);
|
|
28726
|
+
if (needed.size === 0) {
|
|
28727
|
+
results.push({
|
|
28728
|
+
name: `secret access: ${name}`,
|
|
28729
|
+
status: "ok",
|
|
28730
|
+
detail: "no declared vault secrets"
|
|
28731
|
+
});
|
|
28732
|
+
continue;
|
|
28733
|
+
}
|
|
28734
|
+
const out = await preflight(name, [...needed].sort());
|
|
28735
|
+
if (out.kind === "unreachable" || out.kind === "locked") {
|
|
28736
|
+
results.push({
|
|
28737
|
+
name: "agent secret access",
|
|
28738
|
+
status: "skip",
|
|
28739
|
+
detail: out.kind === "locked" ? "vault-broker is locked \u2014 cron-secret existence/ACL unverified (re-run after it unlocks; it auto-unlocks on boot)" : `vault-broker operator socket unreachable (${out.msg}) and SWITCHROOM_VAULT_PASSPHRASE unset \u2014 cron-secret existence/ACL unverified`,
|
|
28740
|
+
fix: "Ensure the broker operator socket (~/.switchroom/broker-operator/sock) is bound, or export SWITCHROOM_VAULT_PASSPHRASE and re-run `switchroom doctor`"
|
|
28741
|
+
});
|
|
28742
|
+
return results;
|
|
28743
|
+
}
|
|
28744
|
+
const byKey = new Map(out.results.map((r) => [r.key, r]));
|
|
28745
|
+
const gaps = [];
|
|
28746
|
+
for (const key of [...needed].sort()) {
|
|
28747
|
+
const r = byKey.get(key);
|
|
28748
|
+
if (r === undefined) {
|
|
28749
|
+
gaps.push(`'${key}' \u2014 broker returned no result`);
|
|
28750
|
+
continue;
|
|
28751
|
+
}
|
|
28752
|
+
const g = keyGap(key, cronKeys.has(key), r.exists, { allow: r.acl_ok, reason: r.acl_reason }, { allow: r.scope_ok, reason: r.scope_reason });
|
|
28753
|
+
if (g)
|
|
28754
|
+
gaps.push(g);
|
|
28755
|
+
}
|
|
28756
|
+
pushAgentResult(name, needed.size, gaps);
|
|
28757
|
+
}
|
|
28620
28758
|
return results;
|
|
28621
28759
|
}
|
|
28622
28760
|
let entries;
|
|
@@ -28631,17 +28769,9 @@ function runSecretAccessChecks(config, deps = {}) {
|
|
|
28631
28769
|
});
|
|
28632
28770
|
return results;
|
|
28633
28771
|
}
|
|
28634
|
-
const
|
|
28635
|
-
for (const name of agents) {
|
|
28772
|
+
for (const name of Object.keys(config.agents ?? {})) {
|
|
28636
28773
|
const resolved = resolveAgentConfig(config.defaults, config.profiles, config.agents[name]);
|
|
28637
|
-
const cronKeys =
|
|
28638
|
-
for (const entry of resolved.schedule ?? []) {
|
|
28639
|
-
for (const s of entry.secrets ?? [])
|
|
28640
|
-
cronKeys.add(s);
|
|
28641
|
-
}
|
|
28642
|
-
const refKeys = new Set;
|
|
28643
|
-
collectVaultRefs2(resolved, refKeys);
|
|
28644
|
-
const needed = new Set([...cronKeys, ...refKeys]);
|
|
28774
|
+
const { needed, cronKeys } = collectNeeds(resolved);
|
|
28645
28775
|
if (needed.size === 0) {
|
|
28646
28776
|
results.push({
|
|
28647
28777
|
name: `secret access: ${name}`,
|
|
@@ -28652,37 +28782,11 @@ function runSecretAccessChecks(config, deps = {}) {
|
|
|
28652
28782
|
}
|
|
28653
28783
|
const gaps = [];
|
|
28654
28784
|
for (const key of [...needed].sort()) {
|
|
28655
|
-
const
|
|
28656
|
-
if (
|
|
28657
|
-
gaps.push(
|
|
28658
|
-
continue;
|
|
28659
|
-
}
|
|
28660
|
-
const byScope = checkEntryScope(entries[key]?.scope, name);
|
|
28661
|
-
if (cronKeys.has(key)) {
|
|
28662
|
-
const byAgent = checkAclByAgent(config, name, key);
|
|
28663
|
-
if (!byAgent.allow) {
|
|
28664
|
-
gaps.push(`'${key}' (cron) \u2014 no static ACL grants read (${byAgent.reason})`);
|
|
28665
|
-
continue;
|
|
28666
|
-
}
|
|
28667
|
-
}
|
|
28668
|
-
if (!byScope.allow) {
|
|
28669
|
-
gaps.push(`'${key}' \u2014 per-key scope denies read (${byScope.reason})`);
|
|
28670
|
-
}
|
|
28671
|
-
}
|
|
28672
|
-
if (gaps.length === 0) {
|
|
28673
|
-
results.push({
|
|
28674
|
-
name: `secret access: ${name}`,
|
|
28675
|
-
status: "ok",
|
|
28676
|
-
detail: `${needed.size} secret(s): all present + ACL ok`
|
|
28677
|
-
});
|
|
28678
|
-
} else {
|
|
28679
|
-
results.push({
|
|
28680
|
-
name: `secret access: ${name}`,
|
|
28681
|
-
status: "fail",
|
|
28682
|
-
detail: `${gaps.length}/${needed.size} unreachable \u2014 ${gaps.join("; ")}`,
|
|
28683
|
-
fix: "`switchroom vault set <key>` for missing keys; " + "`switchroom vault set <key> --allow " + name + "` to grant this agent read access"
|
|
28684
|
-
});
|
|
28785
|
+
const g = keyGap(key, cronKeys.has(key), key in entries, checkAclByAgent(config, name, key), checkEntryScope(entries[key]?.scope, name));
|
|
28786
|
+
if (g)
|
|
28787
|
+
gaps.push(g);
|
|
28685
28788
|
}
|
|
28789
|
+
pushAgentResult(name, needed.size, gaps);
|
|
28686
28790
|
}
|
|
28687
28791
|
return results;
|
|
28688
28792
|
}
|
|
@@ -28690,6 +28794,8 @@ var init_doctor_secret_access = __esm(() => {
|
|
|
28690
28794
|
init_paths();
|
|
28691
28795
|
init_merge();
|
|
28692
28796
|
init_vault();
|
|
28797
|
+
init_acl();
|
|
28798
|
+
init_client();
|
|
28693
28799
|
});
|
|
28694
28800
|
|
|
28695
28801
|
// src/cli/doctor-inlined-secrets.ts
|
|
@@ -28788,19 +28894,19 @@ var init_doctor_inlined_secrets = __esm(() => {
|
|
|
28788
28894
|
|
|
28789
28895
|
// src/cli/doctor-audit-integrity.ts
|
|
28790
28896
|
import { readFileSync as fsReadFileSync2 } from "node:fs";
|
|
28791
|
-
import { homedir as
|
|
28792
|
-
import { join as
|
|
28897
|
+
import { homedir as homedir22 } from "node:os";
|
|
28898
|
+
import { join as join41 } from "node:path";
|
|
28793
28899
|
function rootWrittenLogs(home2) {
|
|
28794
28900
|
return [
|
|
28795
|
-
{ label: "vault-broker", path:
|
|
28901
|
+
{ label: "vault-broker", path: join41(home2, ".switchroom", "vault-audit.log") },
|
|
28796
28902
|
{
|
|
28797
28903
|
label: "hostd",
|
|
28798
|
-
path:
|
|
28904
|
+
path: join41(home2, ".switchroom", "host-control-audit.log")
|
|
28799
28905
|
}
|
|
28800
28906
|
];
|
|
28801
28907
|
}
|
|
28802
28908
|
function runAuditIntegrityChecks(deps = {}) {
|
|
28803
|
-
const home2 = deps.homeDir ??
|
|
28909
|
+
const home2 = deps.homeDir ?? homedir22();
|
|
28804
28910
|
const read = deps.readFileSync ?? ((p) => fsReadFileSync2(p, "utf8"));
|
|
28805
28911
|
const results = [];
|
|
28806
28912
|
for (const { label, path: path4 } of rootWrittenLogs(home2)) {
|
|
@@ -29077,14 +29183,14 @@ var init_client4 = __esm(() => {
|
|
|
29077
29183
|
|
|
29078
29184
|
// src/cli/doctor-agent-smoke.ts
|
|
29079
29185
|
import { existsSync as existsSync47 } from "node:fs";
|
|
29080
|
-
import { homedir as
|
|
29081
|
-
import { join as
|
|
29186
|
+
import { homedir as homedir23 } from "node:os";
|
|
29187
|
+
import { join as join42 } from "node:path";
|
|
29082
29188
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
29083
29189
|
async function runAgentSmokeChecks(config, deps = {}) {
|
|
29084
29190
|
if (deps.fast)
|
|
29085
29191
|
return [];
|
|
29086
|
-
const home2 = deps.homeDir ??
|
|
29087
|
-
const sock = deps.operatorSockPath ??
|
|
29192
|
+
const home2 = deps.homeDir ?? homedir23();
|
|
29193
|
+
const sock = deps.operatorSockPath ?? join42(home2, ".switchroom", "hostd", "operator", "sock");
|
|
29088
29194
|
if (!deps.hostdRequestImpl && !existsSync47(sock)) {
|
|
29089
29195
|
return [
|
|
29090
29196
|
{
|
|
@@ -29211,16 +29317,16 @@ import {
|
|
|
29211
29317
|
readdirSync as readdirSync17,
|
|
29212
29318
|
statSync as statSync21
|
|
29213
29319
|
} from "node:fs";
|
|
29214
|
-
import { dirname as dirname12, join as
|
|
29320
|
+
import { dirname as dirname12, join as join43, resolve as resolve29 } from "node:path";
|
|
29215
29321
|
import { createPublicKey, createPrivateKey } from "node:crypto";
|
|
29216
29322
|
function findInNvm(bin) {
|
|
29217
|
-
const nvmRoot =
|
|
29323
|
+
const nvmRoot = join43(process.env.HOME ?? "", ".nvm", "versions", "node");
|
|
29218
29324
|
if (!existsSync48(nvmRoot))
|
|
29219
29325
|
return null;
|
|
29220
29326
|
try {
|
|
29221
29327
|
const versions = readdirSync17(nvmRoot).sort().reverse();
|
|
29222
29328
|
for (const v of versions) {
|
|
29223
|
-
const candidate =
|
|
29329
|
+
const candidate = join43(nvmRoot, v, "bin", bin);
|
|
29224
29330
|
try {
|
|
29225
29331
|
const s = statSync21(candidate);
|
|
29226
29332
|
if (s.isFile() || s.isSymbolicLink()) {
|
|
@@ -29385,7 +29491,7 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
|
|
|
29385
29491
|
if (envBrowsersPath && envBrowsersPath.length > 0) {
|
|
29386
29492
|
cacheLocations.push(envBrowsersPath);
|
|
29387
29493
|
}
|
|
29388
|
-
cacheLocations.push(
|
|
29494
|
+
cacheLocations.push(join43(homeDir, ".cache", "ms-playwright"));
|
|
29389
29495
|
for (const cacheDir of cacheLocations) {
|
|
29390
29496
|
if (!existsSync48(cacheDir))
|
|
29391
29497
|
continue;
|
|
@@ -29393,10 +29499,10 @@ function findChromium(homeDir = process.env.HOME ?? "", envBrowsersPath = proces
|
|
|
29393
29499
|
const entries = readdirSync17(cacheDir).filter((e) => e.startsWith("chromium"));
|
|
29394
29500
|
for (const entry of entries) {
|
|
29395
29501
|
const candidates2 = [
|
|
29396
|
-
|
|
29397
|
-
|
|
29398
|
-
|
|
29399
|
-
|
|
29502
|
+
join43(cacheDir, entry, "chrome-linux64", "chrome"),
|
|
29503
|
+
join43(cacheDir, entry, "chrome-linux", "chrome"),
|
|
29504
|
+
join43(cacheDir, entry, "chrome-linux64", "headless_shell"),
|
|
29505
|
+
join43(cacheDir, entry, "chrome-linux", "headless_shell")
|
|
29400
29506
|
];
|
|
29401
29507
|
for (const path4 of candidates2) {
|
|
29402
29508
|
if (existsSync48(path4))
|
|
@@ -29479,7 +29585,7 @@ function checkConfig(config, configPath) {
|
|
|
29479
29585
|
function checkLegacyState() {
|
|
29480
29586
|
const results = [];
|
|
29481
29587
|
const h = process.env.HOME ?? "/root";
|
|
29482
|
-
const clerkDir =
|
|
29588
|
+
const clerkDir = join43(h, LEGACY_STATE_DIR);
|
|
29483
29589
|
const clerkPresent = existsSync48(clerkDir);
|
|
29484
29590
|
results.push({
|
|
29485
29591
|
name: "legacy ~/.clerk state",
|
|
@@ -29489,7 +29595,7 @@ function checkLegacyState() {
|
|
|
29489
29595
|
fix: "Legacy state detected. Run `mv ~/.clerk ~/.switchroom` and rename " + "any top-level `clerk:` key in switchroom.yaml to `switchroom:`. " + "This back-compat shim is REMOVED in v0.13.0 \u2014 no automatic " + "migration exists."
|
|
29490
29596
|
} : {}
|
|
29491
29597
|
});
|
|
29492
|
-
const legacySock =
|
|
29598
|
+
const legacySock = join43(h, ".switchroom", "vault-broker.sock");
|
|
29493
29599
|
let sockStat = null;
|
|
29494
29600
|
try {
|
|
29495
29601
|
sockStat = lstatSync5(legacySock);
|
|
@@ -29693,7 +29799,7 @@ async function checkHindsight(config) {
|
|
|
29693
29799
|
}
|
|
29694
29800
|
function checkPendingRetainsQueue(dir) {
|
|
29695
29801
|
const home2 = process.env.HOME ?? "";
|
|
29696
|
-
const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ??
|
|
29802
|
+
const pendingDir = dir ?? process.env.HINDSIGHT_PENDING_DIR ?? join43(home2, ".hindsight", "pending-retains");
|
|
29697
29803
|
if (!existsSync48(pendingDir)) {
|
|
29698
29804
|
return {
|
|
29699
29805
|
name: "pending-retains queue",
|
|
@@ -29824,7 +29930,7 @@ async function checkTelegram(config) {
|
|
|
29824
29930
|
const plugin = agentConfig.channels?.telegram?.plugin ?? "switchroom";
|
|
29825
29931
|
if (plugin !== "switchroom")
|
|
29826
29932
|
continue;
|
|
29827
|
-
const envPath =
|
|
29933
|
+
const envPath = join43(agentsDir, name, "telegram", ".env");
|
|
29828
29934
|
const read = tryReadHostFile(envPath);
|
|
29829
29935
|
if (read.kind === "eacces") {
|
|
29830
29936
|
results.push({
|
|
@@ -29907,7 +30013,7 @@ function checkStartShStale(agentName, startShPath) {
|
|
|
29907
30013
|
}
|
|
29908
30014
|
function checkLeakedHomeSwitchroom(agentName, agentDir) {
|
|
29909
30015
|
const label = `${agentName}: $HOME/.switchroom symlink (#910)`;
|
|
29910
|
-
const path4 =
|
|
30016
|
+
const path4 = join43(agentDir, "home", ".switchroom");
|
|
29911
30017
|
let stats;
|
|
29912
30018
|
try {
|
|
29913
30019
|
stats = lstatSync5(path4);
|
|
@@ -29944,7 +30050,7 @@ function checkLeakedHomeSwitchroom(agentName, agentDir) {
|
|
|
29944
30050
|
}
|
|
29945
30051
|
function checkRepoHygiene(repoRoot) {
|
|
29946
30052
|
const results = [];
|
|
29947
|
-
const exportDir =
|
|
30053
|
+
const exportDir = join43(repoRoot, "clerk-export");
|
|
29948
30054
|
if (existsSync48(exportDir)) {
|
|
29949
30055
|
results.push({
|
|
29950
30056
|
name: "repo hygiene: clerk-export/ on disk (#1072)",
|
|
@@ -29953,7 +30059,7 @@ function checkRepoHygiene(repoRoot) {
|
|
|
29953
30059
|
fix: `Run scripts/migrate-clerk-export-to-vault.sh to move the bundle ` + `into the vault, then delete the on-disk copy.`
|
|
29954
30060
|
});
|
|
29955
30061
|
}
|
|
29956
|
-
const knownTarball =
|
|
30062
|
+
const knownTarball = join43(repoRoot, "clerk-export-with-secrets.tar.gz");
|
|
29957
30063
|
if (existsSync48(knownTarball)) {
|
|
29958
30064
|
results.push({
|
|
29959
30065
|
name: "repo hygiene: clerk-export-with-secrets.tar.gz on disk (#1072)",
|
|
@@ -29971,7 +30077,7 @@ function checkRepoHygiene(repoRoot) {
|
|
|
29971
30077
|
results.push({
|
|
29972
30078
|
name: `repo hygiene: ${name} on disk (#1072)`,
|
|
29973
30079
|
status: "warn",
|
|
29974
|
-
detail: `${
|
|
30080
|
+
detail: `${join43(repoRoot, name)} matches the *-with-secrets*.tar.gz ` + `pattern. Likely contains real credentials.`,
|
|
29975
30081
|
fix: `Inspect, migrate any secrets into the vault, then delete the ` + `archive.`
|
|
29976
30082
|
});
|
|
29977
30083
|
}
|
|
@@ -29994,9 +30100,9 @@ function checkRepoHygiene(repoRoot) {
|
|
|
29994
30100
|
}
|
|
29995
30101
|
function isSwitchroomCheckout(dir) {
|
|
29996
30102
|
try {
|
|
29997
|
-
if (!existsSync48(
|
|
30103
|
+
if (!existsSync48(join43(dir, ".git")))
|
|
29998
30104
|
return false;
|
|
29999
|
-
const pkgPath =
|
|
30105
|
+
const pkgPath = join43(dir, "package.json");
|
|
30000
30106
|
if (!existsSync48(pkgPath))
|
|
30001
30107
|
return false;
|
|
30002
30108
|
const pkg = JSON.parse(readFileSync44(pkgPath, "utf-8"));
|
|
@@ -30033,7 +30139,7 @@ function checkAgents(config, configPath) {
|
|
|
30033
30139
|
fix: `Rotate the bot token (e.g. via \`switchroom vault\`), then run ` + `\`switchroom agent unquarantine ${name}\` and \`switchroom agent restart ${name}\``
|
|
30034
30140
|
});
|
|
30035
30141
|
}
|
|
30036
|
-
results.push(checkStartShStale(name,
|
|
30142
|
+
results.push(checkStartShStale(name, join43(agentDir, "start.sh")));
|
|
30037
30143
|
results.push(checkLeakedHomeSwitchroom(name, agentDir));
|
|
30038
30144
|
const status = statuses[name];
|
|
30039
30145
|
const active = status?.active ?? "unknown";
|
|
@@ -30110,7 +30216,7 @@ function checkAgents(config, configPath) {
|
|
|
30110
30216
|
}
|
|
30111
30217
|
}
|
|
30112
30218
|
if (agentConfig.channels?.telegram?.plugin === "switchroom") {
|
|
30113
|
-
const mcpJsonPath =
|
|
30219
|
+
const mcpJsonPath = join43(agentDir, ".mcp.json");
|
|
30114
30220
|
if (!existsSync48(mcpJsonPath)) {
|
|
30115
30221
|
results.push({
|
|
30116
30222
|
name: `${name}: .mcp.json`,
|
|
@@ -30411,7 +30517,7 @@ async function checkMffAuthFlow(envPath = mffEnvPath(), timeoutMs = 8000) {
|
|
|
30411
30517
|
};
|
|
30412
30518
|
}
|
|
30413
30519
|
const credDir = dirname12(envPath);
|
|
30414
|
-
const authScript =
|
|
30520
|
+
const authScript = join43(credDir, "claude-auth.py");
|
|
30415
30521
|
if (!existsSync48(authScript)) {
|
|
30416
30522
|
return {
|
|
30417
30523
|
name: "mff: auth flow",
|
|
@@ -30690,7 +30796,7 @@ function registerDoctorCommand(program3) {
|
|
|
30690
30796
|
},
|
|
30691
30797
|
{ title: "Legacy State", results: checkLegacyState() },
|
|
30692
30798
|
{ title: "Vault", results: checkVault(config) },
|
|
30693
|
-
{ title: "Vault access", results: runSecretAccessChecks(config) },
|
|
30799
|
+
{ title: "Vault access", results: await runSecretAccessChecks(config) },
|
|
30694
30800
|
{ title: "Memory (Hindsight)", results: await checkHindsight(config) },
|
|
30695
30801
|
{ title: "Telegram", results: await checkTelegram(config) },
|
|
30696
30802
|
{ title: "Agents", results: checkAgents(config, configPath) },
|
|
@@ -47047,8 +47153,8 @@ var {
|
|
|
47047
47153
|
} = import__.default;
|
|
47048
47154
|
|
|
47049
47155
|
// src/build-info.ts
|
|
47050
|
-
var VERSION = "0.12.
|
|
47051
|
-
var COMMIT_SHA = "
|
|
47156
|
+
var VERSION = "0.12.14";
|
|
47157
|
+
var COMMIT_SHA = "db6d87d6";
|
|
47052
47158
|
|
|
47053
47159
|
// src/cli/agent.ts
|
|
47054
47160
|
init_source();
|
|
@@ -55869,6 +55975,7 @@ var DEFAULT_AUTO_UNLOCK_PATH = "~/.switchroom/vault-auto-unlock";
|
|
|
55869
55975
|
|
|
55870
55976
|
// src/vault/broker/server.ts
|
|
55871
55977
|
init_peercred();
|
|
55978
|
+
init_acl();
|
|
55872
55979
|
init_protocol();
|
|
55873
55980
|
|
|
55874
55981
|
// src/vault/broker/audit-log.ts
|
|
@@ -58199,7 +58306,9 @@ class VaultBroker {
|
|
|
58199
58306
|
this._writePidFile();
|
|
58200
58307
|
this._sdNotify(`READY=1
|
|
58201
58308
|
`);
|
|
58202
|
-
this.
|
|
58309
|
+
if (this.testOpts._testSecrets === undefined) {
|
|
58310
|
+
this._tryAutoUnlock();
|
|
58311
|
+
}
|
|
58203
58312
|
if (process.platform !== "linux") {
|
|
58204
58313
|
process.stderr.write(`[vault-broker] WARNING: running on ${process.platform} with ` + `SWITCHROOM_BROKER_ALLOW_NON_LINUX=1 \u2014 peercred ACL is disabled. ` + `Access control is socket file mode 0600 ONLY. Do not use this configuration for production secrets.
|
|
58205
58314
|
`);
|
|
@@ -58510,6 +58619,49 @@ class VaultBroker {
|
|
|
58510
58619
|
socket.write(encodeResponse({ ok: true, locked: true }));
|
|
58511
58620
|
return;
|
|
58512
58621
|
}
|
|
58622
|
+
if (req.op === "preflight_access") {
|
|
58623
|
+
if (!isOperator) {
|
|
58624
|
+
writeAudit({
|
|
58625
|
+
ts: new Date().toISOString(),
|
|
58626
|
+
op: "preflight_access",
|
|
58627
|
+
caller: auditCaller,
|
|
58628
|
+
pid: auditPid,
|
|
58629
|
+
cgroup: auditCgroup,
|
|
58630
|
+
result: "denied:operator-only"
|
|
58631
|
+
});
|
|
58632
|
+
socket.write(encodeResponse(errorResponse("DENIED", "preflight_access is operator-only")));
|
|
58633
|
+
return;
|
|
58634
|
+
}
|
|
58635
|
+
if (this.secrets === null) {
|
|
58636
|
+
socket.write(encodeResponse(errorResponse("LOCKED", "Vault is locked")));
|
|
58637
|
+
return;
|
|
58638
|
+
}
|
|
58639
|
+
const pfSecrets = this.secrets;
|
|
58640
|
+
const pfConfig = this.config;
|
|
58641
|
+
const results = req.keys.map((key) => {
|
|
58642
|
+
const exists = Object.prototype.hasOwnProperty.call(pfSecrets, key);
|
|
58643
|
+
const acl = pfConfig !== null ? checkAclByAgent(pfConfig, req.agent, key) : { allow: false, reason: "broker has no config loaded" };
|
|
58644
|
+
const scope = checkEntryScope(pfSecrets[key]?.scope, req.agent);
|
|
58645
|
+
return {
|
|
58646
|
+
key,
|
|
58647
|
+
exists,
|
|
58648
|
+
acl_ok: acl.allow,
|
|
58649
|
+
...acl.allow ? {} : { acl_reason: acl.reason },
|
|
58650
|
+
scope_ok: scope.allow,
|
|
58651
|
+
...scope.allow ? {} : { scope_reason: scope.reason }
|
|
58652
|
+
};
|
|
58653
|
+
});
|
|
58654
|
+
writeAudit({
|
|
58655
|
+
ts: new Date().toISOString(),
|
|
58656
|
+
op: "preflight_access",
|
|
58657
|
+
caller: auditCaller,
|
|
58658
|
+
pid: auditPid,
|
|
58659
|
+
cgroup: auditCgroup,
|
|
58660
|
+
result: `allowed:agent=${req.agent},keys=${req.keys.length}`
|
|
58661
|
+
});
|
|
58662
|
+
socket.write(encodeResponse({ ok: true, op: "preflight_access", results }));
|
|
58663
|
+
return;
|
|
58664
|
+
}
|
|
58513
58665
|
if (req.op === "list") {
|
|
58514
58666
|
if (this.secrets === null) {
|
|
58515
58667
|
socket.write(encodeResponse(errorResponse("LOCKED", "Vault is locked")));
|
|
@@ -68972,15 +69124,15 @@ init_loader();
|
|
|
68972
69124
|
init_lifecycle();
|
|
68973
69125
|
import { cpSync as cpSync2, existsSync as existsSync49, mkdirSync as mkdirSync27, readFileSync as readFileSync45, realpathSync as realpathSync5, rmSync as rmSync12, statSync as statSync22 } from "node:fs";
|
|
68974
69126
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
68975
|
-
import { join as
|
|
68976
|
-
import { homedir as
|
|
68977
|
-
var DEFAULT_COMPOSE_PATH =
|
|
69127
|
+
import { join as join44, dirname as dirname13, resolve as resolve30 } from "node:path";
|
|
69128
|
+
import { homedir as homedir24 } from "node:os";
|
|
69129
|
+
var DEFAULT_COMPOSE_PATH = join44(homedir24(), ".switchroom", "compose", "docker-compose.yml");
|
|
68978
69130
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
68979
69131
|
let dir = dirname13(scriptPath);
|
|
68980
69132
|
for (let i = 0;i < 12; i++) {
|
|
68981
|
-
if (existsSync49(
|
|
69133
|
+
if (existsSync49(join44(dir, ".git"))) {
|
|
68982
69134
|
try {
|
|
68983
|
-
const pkg = JSON.parse(readFileSync45(
|
|
69135
|
+
const pkg = JSON.parse(readFileSync45(join44(dir, "package.json"), "utf-8"));
|
|
68984
69136
|
if (pkg.name === "switchroom")
|
|
68985
69137
|
return true;
|
|
68986
69138
|
} catch {}
|
|
@@ -69111,7 +69263,7 @@ function planUpdate(opts) {
|
|
|
69111
69263
|
return;
|
|
69112
69264
|
}
|
|
69113
69265
|
const source = resolve30(import.meta.dirname, "../../skills");
|
|
69114
|
-
const dest =
|
|
69266
|
+
const dest = join44(homedir24(), ".switchroom", "skills", "_bundled");
|
|
69115
69267
|
if (!existsSync49(source)) {
|
|
69116
69268
|
process.stderr.write(`switchroom update: sync-bundled-skills \u2014 CLI bundle has no adjacent skills/ at ${source}; skipping.
|
|
69117
69269
|
`);
|
|
@@ -69225,7 +69377,7 @@ function defaultStatusProbe(composePath) {
|
|
|
69225
69377
|
} catch {}
|
|
69226
69378
|
let dir = dirname13(scriptPath);
|
|
69227
69379
|
for (let i = 0;i < 8; i++) {
|
|
69228
|
-
const pkgPath =
|
|
69380
|
+
const pkgPath = join44(dir, "package.json");
|
|
69229
69381
|
if (existsSync49(pkgPath)) {
|
|
69230
69382
|
try {
|
|
69231
69383
|
const pkg = JSON.parse(readFileSync45(pkgPath, "utf-8"));
|
|
@@ -69445,7 +69597,7 @@ init_helpers();
|
|
|
69445
69597
|
init_lifecycle();
|
|
69446
69598
|
import { execSync as execSync4 } from "node:child_process";
|
|
69447
69599
|
import { existsSync as existsSync50, readFileSync as readFileSync46 } from "node:fs";
|
|
69448
|
-
import { dirname as dirname14, join as
|
|
69600
|
+
import { dirname as dirname14, join as join45 } from "node:path";
|
|
69449
69601
|
function getClaudeCodeVersion() {
|
|
69450
69602
|
try {
|
|
69451
69603
|
const out = execSync4("claude --version 2>/dev/null", {
|
|
@@ -69495,11 +69647,11 @@ function formatUptime3(timestamp) {
|
|
|
69495
69647
|
function locateSwitchroomInstallDir() {
|
|
69496
69648
|
let dir = import.meta.dirname;
|
|
69497
69649
|
for (let i = 0;i < 10 && dir && dir !== "/"; i++) {
|
|
69498
|
-
const pkgPath =
|
|
69650
|
+
const pkgPath = join45(dir, "package.json");
|
|
69499
69651
|
if (existsSync50(pkgPath)) {
|
|
69500
69652
|
try {
|
|
69501
69653
|
const pkg = JSON.parse(readFileSync46(pkgPath, "utf-8"));
|
|
69502
|
-
if (pkg.name === "switchroom" && existsSync50(
|
|
69654
|
+
if (pkg.name === "switchroom" && existsSync50(join45(dir, ".git"))) {
|
|
69503
69655
|
return dir;
|
|
69504
69656
|
}
|
|
69505
69657
|
} catch {}
|
|
@@ -69729,7 +69881,7 @@ import {
|
|
|
69729
69881
|
writeFileSync as writeFileSync25,
|
|
69730
69882
|
writeSync as writeSync6
|
|
69731
69883
|
} from "node:fs";
|
|
69732
|
-
import { join as
|
|
69884
|
+
import { join as join46 } from "node:path";
|
|
69733
69885
|
import { randomBytes as randomBytes10 } from "node:crypto";
|
|
69734
69886
|
import { execSync as execSync5 } from "node:child_process";
|
|
69735
69887
|
|
|
@@ -70049,7 +70201,7 @@ function redactedMarker(ruleId) {
|
|
|
70049
70201
|
var ISSUES_FILE = "issues.jsonl";
|
|
70050
70202
|
var ISSUES_LOCK = "issues.lock";
|
|
70051
70203
|
function readAll(stateDir) {
|
|
70052
|
-
const path4 =
|
|
70204
|
+
const path4 = join46(stateDir, ISSUES_FILE);
|
|
70053
70205
|
if (!existsSync51(path4))
|
|
70054
70206
|
return [];
|
|
70055
70207
|
let raw;
|
|
@@ -70127,7 +70279,7 @@ function record(stateDir, input, nowFn = Date.now) {
|
|
|
70127
70279
|
});
|
|
70128
70280
|
}
|
|
70129
70281
|
function resolve33(stateDir, fingerprint, nowFn = Date.now) {
|
|
70130
|
-
if (!existsSync51(
|
|
70282
|
+
if (!existsSync51(join46(stateDir, ISSUES_FILE)))
|
|
70131
70283
|
return 0;
|
|
70132
70284
|
return withLock(stateDir, () => {
|
|
70133
70285
|
const all = readAll(stateDir);
|
|
@@ -70145,7 +70297,7 @@ function resolve33(stateDir, fingerprint, nowFn = Date.now) {
|
|
|
70145
70297
|
});
|
|
70146
70298
|
}
|
|
70147
70299
|
function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
70148
|
-
if (!existsSync51(
|
|
70300
|
+
if (!existsSync51(join46(stateDir, ISSUES_FILE)))
|
|
70149
70301
|
return 0;
|
|
70150
70302
|
return withLock(stateDir, () => {
|
|
70151
70303
|
const all = readAll(stateDir);
|
|
@@ -70163,7 +70315,7 @@ function resolveAllBySource(stateDir, source, nowFn = Date.now) {
|
|
|
70163
70315
|
});
|
|
70164
70316
|
}
|
|
70165
70317
|
function prune(stateDir, opts = {}) {
|
|
70166
|
-
if (!existsSync51(
|
|
70318
|
+
if (!existsSync51(join46(stateDir, ISSUES_FILE)))
|
|
70167
70319
|
return 0;
|
|
70168
70320
|
return withLock(stateDir, () => {
|
|
70169
70321
|
const all = readAll(stateDir);
|
|
@@ -70196,7 +70348,7 @@ function ensureDir(stateDir) {
|
|
|
70196
70348
|
mkdirSync28(stateDir, { recursive: true });
|
|
70197
70349
|
}
|
|
70198
70350
|
function writeAll(stateDir, events) {
|
|
70199
|
-
const path4 =
|
|
70351
|
+
const path4 = join46(stateDir, ISSUES_FILE);
|
|
70200
70352
|
sweepOrphanTmpFiles(stateDir);
|
|
70201
70353
|
const tmp = `${path4}.tmp-${process.pid}-${randomBytes10(4).toString("hex")}`;
|
|
70202
70354
|
const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
|
|
@@ -70218,7 +70370,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
70218
70370
|
for (const entry of entries) {
|
|
70219
70371
|
if (!entry.startsWith(TMP_PREFIX))
|
|
70220
70372
|
continue;
|
|
70221
|
-
const tmpPath =
|
|
70373
|
+
const tmpPath = join46(stateDir, entry);
|
|
70222
70374
|
try {
|
|
70223
70375
|
const stat = statSync23(tmpPath);
|
|
70224
70376
|
if (stat.mtimeMs < cutoff) {
|
|
@@ -70230,7 +70382,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
70230
70382
|
var LOCK_RETRY_MS = 25;
|
|
70231
70383
|
var LOCK_TIMEOUT_MS = 1e4;
|
|
70232
70384
|
function withLock(stateDir, fn) {
|
|
70233
|
-
const lockPath =
|
|
70385
|
+
const lockPath = join46(stateDir, ISSUES_LOCK);
|
|
70234
70386
|
const startedAt = Date.now();
|
|
70235
70387
|
let fd = null;
|
|
70236
70388
|
while (fd === null) {
|
|
@@ -70514,8 +70666,8 @@ function relTime(deltaMs) {
|
|
|
70514
70666
|
// src/cli/deps.ts
|
|
70515
70667
|
init_source();
|
|
70516
70668
|
import { existsSync as existsSync54 } from "node:fs";
|
|
70517
|
-
import { homedir as
|
|
70518
|
-
import { join as
|
|
70669
|
+
import { homedir as homedir27 } from "node:os";
|
|
70670
|
+
import { join as join49, resolve as resolve34 } from "node:path";
|
|
70519
70671
|
|
|
70520
70672
|
// src/deps/python.ts
|
|
70521
70673
|
import { createHash as createHash9 } from "node:crypto";
|
|
@@ -70526,8 +70678,8 @@ import {
|
|
|
70526
70678
|
rmSync as rmSync13,
|
|
70527
70679
|
writeFileSync as writeFileSync26
|
|
70528
70680
|
} from "node:fs";
|
|
70529
|
-
import { dirname as dirname15, join as
|
|
70530
|
-
import { homedir as
|
|
70681
|
+
import { dirname as dirname15, join as join47 } from "node:path";
|
|
70682
|
+
import { homedir as homedir25 } from "node:os";
|
|
70531
70683
|
import { execFileSync as execFileSync14 } from "node:child_process";
|
|
70532
70684
|
|
|
70533
70685
|
class PythonEnvError extends Error {
|
|
@@ -70539,7 +70691,7 @@ class PythonEnvError extends Error {
|
|
|
70539
70691
|
}
|
|
70540
70692
|
}
|
|
70541
70693
|
function defaultPythonCacheRoot() {
|
|
70542
|
-
return
|
|
70694
|
+
return join47(homedir25(), ".switchroom", "deps", "python");
|
|
70543
70695
|
}
|
|
70544
70696
|
function hashFile(path4) {
|
|
70545
70697
|
return createHash9("sha256").update(readFileSync48(path4)).digest("hex");
|
|
@@ -70551,11 +70703,11 @@ function ensurePythonEnv(opts) {
|
|
|
70551
70703
|
if (!existsSync52(requirementsPath)) {
|
|
70552
70704
|
throw new PythonEnvError(`requirements file not found: ${requirementsPath}`);
|
|
70553
70705
|
}
|
|
70554
|
-
const venvDir =
|
|
70555
|
-
const stampPath =
|
|
70556
|
-
const binDir =
|
|
70557
|
-
const pythonBin =
|
|
70558
|
-
const pipBin =
|
|
70706
|
+
const venvDir = join47(cacheRoot, skillName);
|
|
70707
|
+
const stampPath = join47(venvDir, ".requirements.sha256");
|
|
70708
|
+
const binDir = join47(venvDir, "bin");
|
|
70709
|
+
const pythonBin = join47(binDir, "python");
|
|
70710
|
+
const pipBin = join47(binDir, "pip");
|
|
70559
70711
|
const targetHash = hashFile(requirementsPath);
|
|
70560
70712
|
if (!force && existsSync52(stampPath) && existsSync52(pythonBin)) {
|
|
70561
70713
|
const existingHash = readFileSync48(stampPath, "utf8").trim();
|
|
@@ -70614,8 +70766,8 @@ import {
|
|
|
70614
70766
|
rmSync as rmSync14,
|
|
70615
70767
|
writeFileSync as writeFileSync27
|
|
70616
70768
|
} from "node:fs";
|
|
70617
|
-
import { dirname as dirname16, join as
|
|
70618
|
-
import { homedir as
|
|
70769
|
+
import { dirname as dirname16, join as join48 } from "node:path";
|
|
70770
|
+
import { homedir as homedir26 } from "node:os";
|
|
70619
70771
|
import { execFileSync as execFileSync15 } from "node:child_process";
|
|
70620
70772
|
|
|
70621
70773
|
class NodeEnvError extends Error {
|
|
@@ -70638,7 +70790,7 @@ var LOCKFILES_FOR = {
|
|
|
70638
70790
|
npm: ["package-lock.json"]
|
|
70639
70791
|
};
|
|
70640
70792
|
function defaultNodeCacheRoot() {
|
|
70641
|
-
return
|
|
70793
|
+
return join48(homedir26(), ".switchroom", "deps", "node");
|
|
70642
70794
|
}
|
|
70643
70795
|
function hashDepInputs(packageJsonPath) {
|
|
70644
70796
|
const sourceDir = dirname16(packageJsonPath);
|
|
@@ -70647,7 +70799,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
70647
70799
|
`);
|
|
70648
70800
|
hasher.update(readFileSync49(packageJsonPath));
|
|
70649
70801
|
for (const lockName of ALL_LOCKFILES) {
|
|
70650
|
-
const lockPath =
|
|
70802
|
+
const lockPath = join48(sourceDir, lockName);
|
|
70651
70803
|
if (existsSync53(lockPath)) {
|
|
70652
70804
|
hasher.update(`
|
|
70653
70805
|
`);
|
|
@@ -70667,10 +70819,10 @@ function ensureNodeEnv(opts) {
|
|
|
70667
70819
|
throw new NodeEnvError(`package.json not found: ${packageJsonPath}`);
|
|
70668
70820
|
}
|
|
70669
70821
|
const sourceDir = dirname16(packageJsonPath);
|
|
70670
|
-
const envDir =
|
|
70671
|
-
const stampPath =
|
|
70672
|
-
const nodeModulesDir =
|
|
70673
|
-
const binDir =
|
|
70822
|
+
const envDir = join48(cacheRoot, skillName);
|
|
70823
|
+
const stampPath = join48(envDir, ".package.sha256");
|
|
70824
|
+
const nodeModulesDir = join48(envDir, "node_modules");
|
|
70825
|
+
const binDir = join48(nodeModulesDir, ".bin");
|
|
70674
70826
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
70675
70827
|
if (!force && existsSync53(stampPath) && existsSync53(nodeModulesDir)) {
|
|
70676
70828
|
const existingHash = readFileSync49(stampPath, "utf8").trim();
|
|
@@ -70688,12 +70840,12 @@ function ensureNodeEnv(opts) {
|
|
|
70688
70840
|
rmSync14(envDir, { recursive: true, force: true });
|
|
70689
70841
|
}
|
|
70690
70842
|
mkdirSync30(envDir, { recursive: true });
|
|
70691
|
-
copyFileSync9(packageJsonPath,
|
|
70843
|
+
copyFileSync9(packageJsonPath, join48(envDir, "package.json"));
|
|
70692
70844
|
let copiedLockfile = false;
|
|
70693
70845
|
for (const lockName of LOCKFILES_FOR[installer]) {
|
|
70694
|
-
const lockPath =
|
|
70846
|
+
const lockPath = join48(sourceDir, lockName);
|
|
70695
70847
|
if (existsSync53(lockPath)) {
|
|
70696
|
-
copyFileSync9(lockPath,
|
|
70848
|
+
copyFileSync9(lockPath, join48(envDir, lockName));
|
|
70697
70849
|
copiedLockfile = true;
|
|
70698
70850
|
}
|
|
70699
70851
|
}
|
|
@@ -70722,7 +70874,7 @@ function ensureNodeEnv(opts) {
|
|
|
70722
70874
|
|
|
70723
70875
|
// src/cli/deps.ts
|
|
70724
70876
|
function builtinSkillsRoot() {
|
|
70725
|
-
return resolve34(
|
|
70877
|
+
return resolve34(homedir27(), ".switchroom/skills/_bundled");
|
|
70726
70878
|
}
|
|
70727
70879
|
function registerDepsCommand(program3) {
|
|
70728
70880
|
const deps = program3.command("deps").description("Manage cached per-skill dependency environments");
|
|
@@ -70732,13 +70884,13 @@ function registerDepsCommand(program3) {
|
|
|
70732
70884
|
console.error(source_default.red(`Bundled skills pool dir not found at ${skillsRoot} \u2014 run \`switchroom update\` to install it.`));
|
|
70733
70885
|
process.exit(1);
|
|
70734
70886
|
}
|
|
70735
|
-
const skillDir =
|
|
70887
|
+
const skillDir = join49(skillsRoot, skill);
|
|
70736
70888
|
if (!existsSync54(skillDir)) {
|
|
70737
70889
|
console.error(source_default.red(`Unknown skill: ${skill} (no dir at ${skillDir})`));
|
|
70738
70890
|
process.exit(1);
|
|
70739
70891
|
}
|
|
70740
|
-
const requirementsPath =
|
|
70741
|
-
const packageJsonPath =
|
|
70892
|
+
const requirementsPath = join49(skillDir, "requirements.txt");
|
|
70893
|
+
const packageJsonPath = join49(skillDir, "package.json");
|
|
70742
70894
|
const wantPython = opts.python ?? (!opts.python && !opts.node && existsSync54(requirementsPath));
|
|
70743
70895
|
const wantNode = opts.node ?? (!opts.python && !opts.node && existsSync54(packageJsonPath));
|
|
70744
70896
|
let did = 0;
|
|
@@ -71693,7 +71845,7 @@ init_helpers();
|
|
|
71693
71845
|
init_loader();
|
|
71694
71846
|
init_merge();
|
|
71695
71847
|
import { copyFileSync as copyFileSync10, existsSync as existsSync56, readFileSync as readFileSync50, writeFileSync as writeFileSync28 } from "node:fs";
|
|
71696
|
-
import { join as
|
|
71848
|
+
import { join as join50, resolve as resolve36 } from "node:path";
|
|
71697
71849
|
init_schema();
|
|
71698
71850
|
function resolveSoulTargetOrExit(program3, agentName) {
|
|
71699
71851
|
const config = getConfig(program3);
|
|
@@ -71717,7 +71869,7 @@ function resolveSoulTargetOrExit(program3, agentName) {
|
|
|
71717
71869
|
profileName,
|
|
71718
71870
|
profilePath,
|
|
71719
71871
|
workspaceDir,
|
|
71720
|
-
soulPath:
|
|
71872
|
+
soulPath: join50(workspaceDir, "SOUL.md"),
|
|
71721
71873
|
soul: merged.soul
|
|
71722
71874
|
};
|
|
71723
71875
|
}
|
|
@@ -71784,7 +71936,7 @@ function registerSoulCommand(program3) {
|
|
|
71784
71936
|
init_helpers();
|
|
71785
71937
|
init_loader();
|
|
71786
71938
|
import { existsSync as existsSync57, readFileSync as readFileSync51, readdirSync as readdirSync19, statSync as statSync24 } from "node:fs";
|
|
71787
|
-
import { resolve as resolve37, join as
|
|
71939
|
+
import { resolve as resolve37, join as join51 } from "node:path";
|
|
71788
71940
|
import { createHash as createHash11 } from "node:crypto";
|
|
71789
71941
|
init_merge();
|
|
71790
71942
|
init_hindsight();
|
|
@@ -71798,7 +71950,7 @@ function sha256(content) {
|
|
|
71798
71950
|
return createHash11("sha256").update(content).digest("hex").slice(0, 16);
|
|
71799
71951
|
}
|
|
71800
71952
|
function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
71801
|
-
const projectsDir =
|
|
71953
|
+
const projectsDir = join51(claudeConfigDir, "projects");
|
|
71802
71954
|
if (!existsSync57(projectsDir))
|
|
71803
71955
|
return;
|
|
71804
71956
|
try {
|
|
@@ -71807,8 +71959,8 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
71807
71959
|
for (const entry of entries) {
|
|
71808
71960
|
if (!entry.isDirectory())
|
|
71809
71961
|
continue;
|
|
71810
|
-
const projectPath =
|
|
71811
|
-
const transcriptPath =
|
|
71962
|
+
const projectPath = join51(projectsDir, entry.name);
|
|
71963
|
+
const transcriptPath = join51(projectPath, "transcript.jsonl");
|
|
71812
71964
|
if (!existsSync57(transcriptPath))
|
|
71813
71965
|
continue;
|
|
71814
71966
|
const stat3 = statSync24(transcriptPath);
|
|
@@ -71877,11 +72029,11 @@ function registerDebugCommand(program3) {
|
|
|
71877
72029
|
process.exit(1);
|
|
71878
72030
|
}
|
|
71879
72031
|
const workspaceDir = resolveAgentWorkspaceDir(agentDir);
|
|
71880
|
-
const claudeConfigDir =
|
|
71881
|
-
const claudeMdPath =
|
|
71882
|
-
const soulMdPath =
|
|
71883
|
-
const workspaceSoulMdPath =
|
|
71884
|
-
const handoffPath =
|
|
72032
|
+
const claudeConfigDir = join51(agentDir, ".claude");
|
|
72033
|
+
const claudeMdPath = join51(agentDir, "CLAUDE.md");
|
|
72034
|
+
const soulMdPath = join51(agentDir, "SOUL.md");
|
|
72035
|
+
const workspaceSoulMdPath = join51(workspaceDir, "SOUL.md");
|
|
72036
|
+
const handoffPath = join51(agentDir, ".handoff.md");
|
|
71885
72037
|
const lastN = parseInt(opts.last, 10);
|
|
71886
72038
|
if (isNaN(lastN) || lastN < 1) {
|
|
71887
72039
|
console.error("--last must be a positive integer");
|
|
@@ -72031,8 +72183,8 @@ init_source();
|
|
|
72031
72183
|
// src/worktree/claim.ts
|
|
72032
72184
|
import { execFileSync as execFileSync16 } from "node:child_process";
|
|
72033
72185
|
import { closeSync as closeSync11, mkdirSync as mkdirSync32, openSync as openSync11, existsSync as existsSync59, unlinkSync as unlinkSync12 } from "node:fs";
|
|
72034
|
-
import { join as
|
|
72035
|
-
import { homedir as
|
|
72186
|
+
import { join as join53, resolve as resolve39 } from "node:path";
|
|
72187
|
+
import { homedir as homedir29 } from "node:os";
|
|
72036
72188
|
import { randomBytes as randomBytes11 } from "node:crypto";
|
|
72037
72189
|
|
|
72038
72190
|
// src/worktree/registry.ts
|
|
@@ -72045,13 +72197,13 @@ import {
|
|
|
72045
72197
|
existsSync as existsSync58,
|
|
72046
72198
|
renameSync as renameSync11
|
|
72047
72199
|
} from "node:fs";
|
|
72048
|
-
import { join as
|
|
72049
|
-
import { homedir as
|
|
72200
|
+
import { join as join52, resolve as resolve38 } from "node:path";
|
|
72201
|
+
import { homedir as homedir28 } from "node:os";
|
|
72050
72202
|
function registryDir() {
|
|
72051
|
-
return resolve38(process.env.SWITCHROOM_WORKTREE_DIR ??
|
|
72203
|
+
return resolve38(process.env.SWITCHROOM_WORKTREE_DIR ?? join52(homedir28(), ".switchroom", "worktrees"));
|
|
72052
72204
|
}
|
|
72053
72205
|
function recordPath(id) {
|
|
72054
|
-
return
|
|
72206
|
+
return join52(registryDir(), `${id}.json`);
|
|
72055
72207
|
}
|
|
72056
72208
|
function ensureDir2() {
|
|
72057
72209
|
mkdirSync31(registryDir(), { recursive: true });
|
|
@@ -72102,7 +72254,7 @@ function acquireRepoLock(repoPath) {
|
|
|
72102
72254
|
const lockDir = registryDir();
|
|
72103
72255
|
mkdirSync32(lockDir, { recursive: true });
|
|
72104
72256
|
const lockName = repoPath.replace(/[^A-Za-z0-9]/g, "_");
|
|
72105
|
-
const lockPath =
|
|
72257
|
+
const lockPath = join53(lockDir, `.lock-${lockName}`);
|
|
72106
72258
|
const deadline = Date.now() + 5000;
|
|
72107
72259
|
let fd = null;
|
|
72108
72260
|
while (fd === null) {
|
|
@@ -72129,7 +72281,7 @@ function acquireRepoLock(repoPath) {
|
|
|
72129
72281
|
}
|
|
72130
72282
|
var DEFAULT_CONCURRENCY = 5;
|
|
72131
72283
|
function worktreesBaseDir() {
|
|
72132
|
-
return resolve39(process.env.SWITCHROOM_WORKTREE_BASE ??
|
|
72284
|
+
return resolve39(process.env.SWITCHROOM_WORKTREE_BASE ?? join53(homedir29(), ".switchroom", "worktree-checkouts"));
|
|
72133
72285
|
}
|
|
72134
72286
|
function shortId() {
|
|
72135
72287
|
return randomBytes11(4).toString("hex");
|
|
@@ -72151,7 +72303,7 @@ function resolveRepoPath(repo, codeRepos) {
|
|
|
72151
72303
|
}
|
|
72152
72304
|
function expandHome(p) {
|
|
72153
72305
|
if (p.startsWith("~/"))
|
|
72154
|
-
return
|
|
72306
|
+
return join53(homedir29(), p.slice(2));
|
|
72155
72307
|
return p;
|
|
72156
72308
|
}
|
|
72157
72309
|
async function claimWorktree(input, codeRepos) {
|
|
@@ -72179,7 +72331,7 @@ async function claimWorktree(input, codeRepos) {
|
|
|
72179
72331
|
branch = `task/${taskSuffix}-${id}`;
|
|
72180
72332
|
const baseDir = worktreesBaseDir();
|
|
72181
72333
|
mkdirSync32(baseDir, { recursive: true });
|
|
72182
|
-
worktreePath =
|
|
72334
|
+
worktreePath = join53(baseDir, `${id}-${taskSuffix}`);
|
|
72183
72335
|
const now = new Date().toISOString();
|
|
72184
72336
|
const record2 = {
|
|
72185
72337
|
id,
|
|
@@ -72434,7 +72586,7 @@ import {
|
|
|
72434
72586
|
rmSync as rmSync15,
|
|
72435
72587
|
writeFileSync as writeFileSync30
|
|
72436
72588
|
} from "node:fs";
|
|
72437
|
-
import { join as
|
|
72589
|
+
import { join as join54 } from "node:path";
|
|
72438
72590
|
function encodeCredentialsFilename(email) {
|
|
72439
72591
|
const SAFE = new Set([
|
|
72440
72592
|
..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
|
|
@@ -72598,16 +72750,16 @@ function resolveCredentialsDir(env2) {
|
|
|
72598
72750
|
if (explicit && explicit.length > 0)
|
|
72599
72751
|
return explicit;
|
|
72600
72752
|
const stateBase = env2.SWITCHROOM_CONTAINER === "1" ? "/state/agent" : env2.HOME ?? ".";
|
|
72601
|
-
return
|
|
72753
|
+
return join54(stateBase, "google-workspace-mcp", "credentials");
|
|
72602
72754
|
}
|
|
72603
72755
|
function writeSeedFile(dir, email, seed) {
|
|
72604
72756
|
mkdirSync33(dir, { recursive: true, mode: 448 });
|
|
72605
72757
|
chmodSync9(dir, 448);
|
|
72606
72758
|
for (const name of readdirSync21(dir)) {
|
|
72607
|
-
rmSync15(
|
|
72759
|
+
rmSync15(join54(dir, name), { force: true, recursive: true });
|
|
72608
72760
|
}
|
|
72609
72761
|
const filename = encodeCredentialsFilename(email);
|
|
72610
|
-
const filePath =
|
|
72762
|
+
const filePath = join54(dir, filename);
|
|
72611
72763
|
writeFileSync30(filePath, JSON.stringify(seed), { mode: 384 });
|
|
72612
72764
|
chmodSync9(filePath, 384);
|
|
72613
72765
|
return filePath;
|
|
@@ -73105,8 +73257,8 @@ agents:
|
|
|
73105
73257
|
|
|
73106
73258
|
// src/cli/apply.ts
|
|
73107
73259
|
init_resolver();
|
|
73108
|
-
import { dirname as dirname19, join as
|
|
73109
|
-
import { homedir as
|
|
73260
|
+
import { dirname as dirname19, join as join58, resolve as resolve41 } from "node:path";
|
|
73261
|
+
import { homedir as homedir31 } from "node:os";
|
|
73110
73262
|
import { execFileSync as execFileSync19 } from "node:child_process";
|
|
73111
73263
|
init_vault();
|
|
73112
73264
|
init_loader();
|
|
@@ -73114,7 +73266,7 @@ init_loader();
|
|
|
73114
73266
|
|
|
73115
73267
|
// src/cli/update-prompt-hook.ts
|
|
73116
73268
|
import { existsSync as existsSync62, readFileSync as readFileSync53, writeFileSync as writeFileSync31, chmodSync as chmodSync10, mkdirSync as mkdirSync34 } from "node:fs";
|
|
73117
|
-
import { join as
|
|
73269
|
+
import { join as join55 } from "node:path";
|
|
73118
73270
|
var HOOK_FILENAME = "update-card-on-prompt.sh";
|
|
73119
73271
|
function updatePromptHookScript() {
|
|
73120
73272
|
return `#!/bin/bash
|
|
@@ -73180,9 +73332,9 @@ exit 0
|
|
|
73180
73332
|
`;
|
|
73181
73333
|
}
|
|
73182
73334
|
function installUpdatePromptHook(agentDir) {
|
|
73183
|
-
const hooksDir =
|
|
73335
|
+
const hooksDir = join55(agentDir, ".claude", "hooks");
|
|
73184
73336
|
mkdirSync34(hooksDir, { recursive: true });
|
|
73185
|
-
const scriptPath =
|
|
73337
|
+
const scriptPath = join55(hooksDir, HOOK_FILENAME);
|
|
73186
73338
|
const desired = updatePromptHookScript();
|
|
73187
73339
|
let installed = false;
|
|
73188
73340
|
const existing = existsSync62(scriptPath) ? readFileSync53(scriptPath, "utf-8") : "";
|
|
@@ -73195,7 +73347,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
73195
73347
|
chmodSync10(scriptPath, 493);
|
|
73196
73348
|
} catch {}
|
|
73197
73349
|
}
|
|
73198
|
-
const settingsPath =
|
|
73350
|
+
const settingsPath = join55(agentDir, ".claude", "settings.json");
|
|
73199
73351
|
if (!existsSync62(settingsPath)) {
|
|
73200
73352
|
return { scriptPath, settingsPath, installed };
|
|
73201
73353
|
}
|
|
@@ -73321,7 +73473,7 @@ import {
|
|
|
73321
73473
|
realpathSync as realpathSync6,
|
|
73322
73474
|
statSync as statSync25
|
|
73323
73475
|
} from "node:fs";
|
|
73324
|
-
import { join as
|
|
73476
|
+
import { join as join57 } from "node:path";
|
|
73325
73477
|
function resolveOperatorUid() {
|
|
73326
73478
|
const sudoUid = process.env.SUDO_UID;
|
|
73327
73479
|
if (sudoUid !== undefined) {
|
|
@@ -73337,14 +73489,14 @@ function resolveOperatorUid() {
|
|
|
73337
73489
|
return;
|
|
73338
73490
|
}
|
|
73339
73491
|
function operatorOwnedPaths(home2) {
|
|
73340
|
-
const root =
|
|
73492
|
+
const root = join57(home2, ".switchroom");
|
|
73341
73493
|
return [
|
|
73342
|
-
|
|
73343
|
-
|
|
73344
|
-
|
|
73345
|
-
|
|
73346
|
-
|
|
73347
|
-
|
|
73494
|
+
join57(root, "vault"),
|
|
73495
|
+
join57(root, "vault-auto-unlock"),
|
|
73496
|
+
join57(root, "vault-audit.log"),
|
|
73497
|
+
join57(root, "host-control-audit.log"),
|
|
73498
|
+
join57(root, "accounts"),
|
|
73499
|
+
join57(root, "compose")
|
|
73348
73500
|
];
|
|
73349
73501
|
}
|
|
73350
73502
|
function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
|
|
@@ -73393,7 +73545,7 @@ function restoreOperatorOwnership(home2, operatorUid, deps = {}) {
|
|
|
73393
73545
|
} catch {}
|
|
73394
73546
|
if (isDir(target)) {
|
|
73395
73547
|
for (const entry of readdir2(target)) {
|
|
73396
|
-
visit(
|
|
73548
|
+
visit(join57(target, entry));
|
|
73397
73549
|
}
|
|
73398
73550
|
}
|
|
73399
73551
|
};
|
|
@@ -73407,14 +73559,14 @@ var EMBEDDED_EXAMPLES = {
|
|
|
73407
73559
|
switchroom: switchroom_default,
|
|
73408
73560
|
minimal: minimal_default
|
|
73409
73561
|
};
|
|
73410
|
-
var DEFAULT_COMPOSE_PATH2 =
|
|
73562
|
+
var DEFAULT_COMPOSE_PATH2 = join58(homedir31(), ".switchroom", "compose", "docker-compose.yml");
|
|
73411
73563
|
var COMPOSE_PROJECT2 = "switchroom";
|
|
73412
73564
|
function resolveVaultBindMountDir(homeDir, ctx) {
|
|
73413
73565
|
const isCustomPath = ctx.migrationKind === "custom-path-skipped";
|
|
73414
73566
|
if (isCustomPath && ctx.customVaultPath) {
|
|
73415
73567
|
return dirname19(ctx.customVaultPath);
|
|
73416
73568
|
}
|
|
73417
|
-
return
|
|
73569
|
+
return join58(homeDir, ".switchroom", "vault");
|
|
73418
73570
|
}
|
|
73419
73571
|
function inspectVaultBindMountDir(vaultDir) {
|
|
73420
73572
|
if (!existsSync65(vaultDir))
|
|
@@ -73443,31 +73595,31 @@ function hasVaultRefs(value) {
|
|
|
73443
73595
|
return false;
|
|
73444
73596
|
}
|
|
73445
73597
|
async function ensureHostMountSources(config) {
|
|
73446
|
-
const home2 =
|
|
73598
|
+
const home2 = homedir31();
|
|
73447
73599
|
const dirs = [
|
|
73448
|
-
|
|
73449
|
-
|
|
73450
|
-
|
|
73451
|
-
|
|
73452
|
-
|
|
73600
|
+
join58(home2, ".switchroom", "approvals"),
|
|
73601
|
+
join58(home2, ".switchroom", "scheduler"),
|
|
73602
|
+
join58(home2, ".switchroom", "logs"),
|
|
73603
|
+
join58(home2, ".switchroom", "compose"),
|
|
73604
|
+
join58(home2, ".switchroom", "broker-operator")
|
|
73453
73605
|
];
|
|
73454
73606
|
for (const name of Object.keys(config.agents)) {
|
|
73455
|
-
dirs.push(
|
|
73456
|
-
dirs.push(
|
|
73457
|
-
dirs.push(
|
|
73607
|
+
dirs.push(join58(home2, ".switchroom", "agents", name));
|
|
73608
|
+
dirs.push(join58(home2, ".switchroom", "logs", name));
|
|
73609
|
+
dirs.push(join58(home2, ".claude", "projects", name));
|
|
73458
73610
|
}
|
|
73459
73611
|
for (const dir of dirs) {
|
|
73460
73612
|
await mkdir(dir, { recursive: true });
|
|
73461
73613
|
}
|
|
73462
|
-
const autoUnlockPath =
|
|
73614
|
+
const autoUnlockPath = join58(home2, ".switchroom", "vault-auto-unlock");
|
|
73463
73615
|
if (!existsSync65(autoUnlockPath)) {
|
|
73464
73616
|
writeFileSync32(autoUnlockPath, "", { mode: 384 });
|
|
73465
73617
|
}
|
|
73466
|
-
const auditLogPath =
|
|
73618
|
+
const auditLogPath = join58(home2, ".switchroom", "vault-audit.log");
|
|
73467
73619
|
if (!existsSync65(auditLogPath)) {
|
|
73468
73620
|
writeFileSync32(auditLogPath, "", { mode: 420 });
|
|
73469
73621
|
}
|
|
73470
|
-
const hostdAuditLogPath =
|
|
73622
|
+
const hostdAuditLogPath = join58(home2, ".switchroom", "host-control-audit.log");
|
|
73471
73623
|
if (!existsSync65(hostdAuditLogPath)) {
|
|
73472
73624
|
writeFileSync32(hostdAuditLogPath, "", { mode: 420 });
|
|
73473
73625
|
}
|
|
@@ -73539,10 +73691,10 @@ function detectAndReportLegacyGdriveSlots(vaultPath) {
|
|
|
73539
73691
|
`));
|
|
73540
73692
|
}
|
|
73541
73693
|
}
|
|
73542
|
-
function writeInstallTypeCache(homeDir =
|
|
73694
|
+
function writeInstallTypeCache(homeDir = homedir31()) {
|
|
73543
73695
|
const ctx = detectInstallType();
|
|
73544
|
-
const dir =
|
|
73545
|
-
const out =
|
|
73696
|
+
const dir = join58(homeDir, ".switchroom");
|
|
73697
|
+
const out = join58(dir, "install-type.json");
|
|
73546
73698
|
const tmp = `${out}.tmp`;
|
|
73547
73699
|
mkdirSync35(dir, { recursive: true });
|
|
73548
73700
|
const payload = {
|
|
@@ -73591,14 +73743,14 @@ Applying switchroom config...
|
|
|
73591
73743
|
writeOut(source_default.green(` + ${name}`) + source_default.gray(` (${agentConfig.extends ?? "default"}) \u2014 ${detail}
|
|
73592
73744
|
`));
|
|
73593
73745
|
try {
|
|
73594
|
-
installUpdatePromptHook(
|
|
73746
|
+
installUpdatePromptHook(join58(agentsDir, name));
|
|
73595
73747
|
} catch (hookErr) {
|
|
73596
73748
|
writeOut(source_default.gray(` (update-prompt hook install failed for ${name}: ${hookErr.message})
|
|
73597
73749
|
`));
|
|
73598
73750
|
}
|
|
73599
73751
|
try {
|
|
73600
73752
|
const uid = allocateAgentUid(name);
|
|
73601
|
-
alignAgentUid(name,
|
|
73753
|
+
alignAgentUid(name, join58(agentsDir, name), uid, {
|
|
73602
73754
|
confirm: !options.nonInteractive,
|
|
73603
73755
|
writeOut
|
|
73604
73756
|
});
|
|
@@ -73635,7 +73787,7 @@ Applying switchroom config...
|
|
|
73635
73787
|
for (const name of agentNames) {
|
|
73636
73788
|
try {
|
|
73637
73789
|
const uid = allocateAgentUid(name);
|
|
73638
|
-
alignAgentUid(name,
|
|
73790
|
+
alignAgentUid(name, join58(agentsDir, name), uid, {
|
|
73639
73791
|
confirm: !options.nonInteractive,
|
|
73640
73792
|
writeOut
|
|
73641
73793
|
});
|
|
@@ -73649,7 +73801,7 @@ Applying switchroom config...
|
|
|
73649
73801
|
}
|
|
73650
73802
|
const vaultPathConfigured = config.vault?.path;
|
|
73651
73803
|
const customVaultPath = vaultPathConfigured ? resolvePath(vaultPathConfigured) : undefined;
|
|
73652
|
-
const migrationResult = migrateVaultLayout(
|
|
73804
|
+
const migrationResult = migrateVaultLayout(homedir31(), {
|
|
73653
73805
|
customVaultPath
|
|
73654
73806
|
});
|
|
73655
73807
|
switch (migrationResult.kind) {
|
|
@@ -73675,7 +73827,7 @@ Applying switchroom config...
|
|
|
73675
73827
|
writeErr(formatDivergentRecoveryMessage(migrationResult.details));
|
|
73676
73828
|
process.exit(4);
|
|
73677
73829
|
}
|
|
73678
|
-
const postMigrationInspect = inspectVaultLayout(
|
|
73830
|
+
const postMigrationInspect = inspectVaultLayout(homedir31());
|
|
73679
73831
|
const acceptable = [
|
|
73680
73832
|
"no-vault",
|
|
73681
73833
|
"already-migrated",
|
|
@@ -73690,7 +73842,7 @@ Applying switchroom config...
|
|
|
73690
73842
|
`));
|
|
73691
73843
|
process.exit(5);
|
|
73692
73844
|
}
|
|
73693
|
-
const vaultDir = resolveVaultBindMountDir(
|
|
73845
|
+
const vaultDir = resolveVaultBindMountDir(homedir31(), {
|
|
73694
73846
|
migrationKind: migrationResult.kind,
|
|
73695
73847
|
customVaultPath
|
|
73696
73848
|
});
|
|
@@ -73720,7 +73872,7 @@ Applying switchroom config...
|
|
|
73720
73872
|
imageTag: composeImageTag,
|
|
73721
73873
|
buildMode: options.buildLocal ? "local" : "pull",
|
|
73722
73874
|
buildContext: options.buildContext,
|
|
73723
|
-
homeDir:
|
|
73875
|
+
homeDir: homedir31(),
|
|
73724
73876
|
switchroomConfigPath,
|
|
73725
73877
|
operatorUid
|
|
73726
73878
|
});
|
|
@@ -73740,7 +73892,7 @@ Wrote `) + composePath + source_default.gray(` (${composeBytes} bytes)
|
|
|
73740
73892
|
writeOut(source_default.gray(` (If pull returns 401, login to ghcr.io first: see docs/operators/install.md#ghcr-auth)
|
|
73741
73893
|
`));
|
|
73742
73894
|
if (process.geteuid?.() === 0 && operatorUid !== undefined) {
|
|
73743
|
-
const restored = restoreOperatorOwnership(
|
|
73895
|
+
const restored = restoreOperatorOwnership(homedir31(), operatorUid);
|
|
73744
73896
|
if (restored.length > 0) {
|
|
73745
73897
|
writeOut(source_default.gray(` Restored operator ownership of ${restored.length} ~/.switchroom path(s)
|
|
73746
73898
|
`));
|
|
@@ -73810,7 +73962,7 @@ function findUnwritableAgentDirs(config, opts) {
|
|
|
73810
73962
|
const targets = opts.only ? [opts.only] : Object.keys(config.agents ?? {});
|
|
73811
73963
|
const unwritable = [];
|
|
73812
73964
|
for (const name of targets) {
|
|
73813
|
-
const startSh =
|
|
73965
|
+
const startSh = join58(agentsDir, name, "start.sh");
|
|
73814
73966
|
if (!existsSync65(startSh))
|
|
73815
73967
|
continue;
|
|
73816
73968
|
try {
|
|
@@ -73990,8 +74142,8 @@ function runRedactStdin() {
|
|
|
73990
74142
|
|
|
73991
74143
|
// src/cli/status-ask.ts
|
|
73992
74144
|
import { readFileSync as readFileSync54, existsSync as existsSync66, readdirSync as readdirSync24 } from "node:fs";
|
|
73993
|
-
import { join as
|
|
73994
|
-
import { homedir as
|
|
74145
|
+
import { join as join59 } from "node:path";
|
|
74146
|
+
import { homedir as homedir32 } from "node:os";
|
|
73995
74147
|
|
|
73996
74148
|
// src/status-ask/report.ts
|
|
73997
74149
|
function parseJsonl(content) {
|
|
@@ -74326,7 +74478,7 @@ function resolveSources(explicitPath) {
|
|
|
74326
74478
|
const config = loadConfig();
|
|
74327
74479
|
agentsDir = resolveAgentsDir(config);
|
|
74328
74480
|
} catch {
|
|
74329
|
-
agentsDir =
|
|
74481
|
+
agentsDir = join59(homedir32(), ".switchroom", "agents");
|
|
74330
74482
|
}
|
|
74331
74483
|
if (!existsSync66(agentsDir))
|
|
74332
74484
|
return [];
|
|
@@ -74338,7 +74490,7 @@ function resolveSources(explicitPath) {
|
|
|
74338
74490
|
return [];
|
|
74339
74491
|
}
|
|
74340
74492
|
for (const name of entries) {
|
|
74341
|
-
const path8 =
|
|
74493
|
+
const path8 = join59(agentsDir, name, "runtime-metrics.jsonl");
|
|
74342
74494
|
if (existsSync66(path8)) {
|
|
74343
74495
|
sources.push({ path: path8, agent: name });
|
|
74344
74496
|
}
|
|
@@ -74360,17 +74512,17 @@ function inferAgentFromPath(p) {
|
|
|
74360
74512
|
|
|
74361
74513
|
// src/cli/agent-config.ts
|
|
74362
74514
|
init_helpers();
|
|
74363
|
-
import { join as
|
|
74364
|
-
import { homedir as
|
|
74515
|
+
import { join as join60 } from "node:path";
|
|
74516
|
+
import { homedir as homedir33 } from "node:os";
|
|
74365
74517
|
import {
|
|
74366
74518
|
existsSync as existsSync67,
|
|
74367
74519
|
mkdirSync as mkdirSync36,
|
|
74368
74520
|
appendFileSync as appendFileSync3,
|
|
74369
74521
|
readFileSync as readFileSync55
|
|
74370
74522
|
} from "node:fs";
|
|
74371
|
-
var AUDIT_ROOT =
|
|
74523
|
+
var AUDIT_ROOT = join60(homedir33(), ".switchroom", "audit");
|
|
74372
74524
|
function auditPathFor(agent) {
|
|
74373
|
-
return
|
|
74525
|
+
return join60(AUDIT_ROOT, agent, "agent-config.jsonl");
|
|
74374
74526
|
}
|
|
74375
74527
|
function appendAudit(agent, cmd, args, exit, opts = {}) {
|
|
74376
74528
|
const row = {
|
|
@@ -74628,21 +74780,21 @@ import {
|
|
|
74628
74780
|
unlinkSync as unlinkSync13,
|
|
74629
74781
|
writeSync as writeSync7
|
|
74630
74782
|
} from "node:fs";
|
|
74631
|
-
import { join as
|
|
74783
|
+
import { join as join61, resolve as resolve42 } from "node:path";
|
|
74632
74784
|
var STAGING_SUBDIR = ".staging";
|
|
74633
74785
|
function overlayPathsFor(agent, opts = {}) {
|
|
74634
74786
|
const base = opts.root ? resolve42(opts.root, agent) : resolve42(resolveDualPath(`~/.switchroom/agents/${agent}`));
|
|
74635
|
-
const scheduleDir =
|
|
74636
|
-
const scheduleStagingDir =
|
|
74637
|
-
const skillsDir =
|
|
74638
|
-
const skillsStagingDir =
|
|
74787
|
+
const scheduleDir = join61(base, "schedule.d");
|
|
74788
|
+
const scheduleStagingDir = join61(scheduleDir, STAGING_SUBDIR);
|
|
74789
|
+
const skillsDir = join61(base, "skills.d");
|
|
74790
|
+
const skillsStagingDir = join61(skillsDir, STAGING_SUBDIR);
|
|
74639
74791
|
return {
|
|
74640
74792
|
agentRoot: base,
|
|
74641
74793
|
scheduleDir,
|
|
74642
74794
|
scheduleStagingDir,
|
|
74643
74795
|
skillsDir,
|
|
74644
74796
|
skillsStagingDir,
|
|
74645
|
-
lockPath:
|
|
74797
|
+
lockPath: join61(base, ".lock"),
|
|
74646
74798
|
stagingDir: scheduleStagingDir
|
|
74647
74799
|
};
|
|
74648
74800
|
}
|
|
@@ -74696,8 +74848,8 @@ function writeOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
74696
74848
|
const paths = overlayPathsFor(agent, opts);
|
|
74697
74849
|
return withAgentLock(paths, () => {
|
|
74698
74850
|
ensureDirs(paths);
|
|
74699
|
-
const stagingPath =
|
|
74700
|
-
const finalPath =
|
|
74851
|
+
const stagingPath = join61(paths.scheduleStagingDir, `${slug}.yaml`);
|
|
74852
|
+
const finalPath = join61(paths.scheduleDir, `${slug}.yaml`);
|
|
74701
74853
|
const fd = openSync12(stagingPath, "w", 384);
|
|
74702
74854
|
try {
|
|
74703
74855
|
writeSync7(fd, yamlText);
|
|
@@ -74713,8 +74865,8 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
74713
74865
|
const paths = overlayPathsFor(agent, opts);
|
|
74714
74866
|
return withAgentLock(paths, () => {
|
|
74715
74867
|
ensureSkillsDirs(paths);
|
|
74716
|
-
const stagingPath =
|
|
74717
|
-
const finalPath =
|
|
74868
|
+
const stagingPath = join61(paths.skillsStagingDir, `${slug}.yaml`);
|
|
74869
|
+
const finalPath = join61(paths.skillsDir, `${slug}.yaml`);
|
|
74718
74870
|
const fd = openSync12(stagingPath, "w", 384);
|
|
74719
74871
|
try {
|
|
74720
74872
|
writeSync7(fd, yamlText);
|
|
@@ -74729,7 +74881,7 @@ function writeSkillsOverlayEntry(agent, slug, yamlText, opts = {}) {
|
|
|
74729
74881
|
function deleteSkillsOverlayEntry(agent, slug, opts = {}) {
|
|
74730
74882
|
const paths = overlayPathsFor(agent, opts);
|
|
74731
74883
|
return withAgentLock(paths, () => {
|
|
74732
|
-
const finalPath =
|
|
74884
|
+
const finalPath = join61(paths.skillsDir, `${slug}.yaml`);
|
|
74733
74885
|
if (!existsSync68(finalPath))
|
|
74734
74886
|
return false;
|
|
74735
74887
|
unlinkSync13(finalPath);
|
|
@@ -74744,7 +74896,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
74744
74896
|
for (const name of readdirSync25(paths.skillsDir)) {
|
|
74745
74897
|
if (!/\.ya?ml$/i.test(name))
|
|
74746
74898
|
continue;
|
|
74747
|
-
const full =
|
|
74899
|
+
const full = join61(paths.skillsDir, name);
|
|
74748
74900
|
try {
|
|
74749
74901
|
const raw = readFileSync56(full, "utf-8");
|
|
74750
74902
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -74756,7 +74908,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
74756
74908
|
function deleteOverlayEntry(agent, slug, opts = {}) {
|
|
74757
74909
|
const paths = overlayPathsFor(agent, opts);
|
|
74758
74910
|
return withAgentLock(paths, () => {
|
|
74759
|
-
const finalPath =
|
|
74911
|
+
const finalPath = join61(paths.scheduleDir, `${slug}.yaml`);
|
|
74760
74912
|
if (!existsSync68(finalPath))
|
|
74761
74913
|
return false;
|
|
74762
74914
|
unlinkSync13(finalPath);
|
|
@@ -74771,7 +74923,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
74771
74923
|
for (const name of readdirSync25(paths.scheduleDir)) {
|
|
74772
74924
|
if (!/\.ya?ml$/i.test(name))
|
|
74773
74925
|
continue;
|
|
74774
|
-
const full =
|
|
74926
|
+
const full = join61(paths.scheduleDir, name);
|
|
74775
74927
|
try {
|
|
74776
74928
|
const raw = readFileSync56(full, "utf-8");
|
|
74777
74929
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
@@ -74927,12 +75079,12 @@ import {
|
|
|
74927
75079
|
writeFileSync as writeFileSync33,
|
|
74928
75080
|
writeSync as writeSync8
|
|
74929
75081
|
} from "node:fs";
|
|
74930
|
-
import { join as
|
|
75082
|
+
import { join as join62 } from "node:path";
|
|
74931
75083
|
import { randomBytes as randomBytes12 } from "node:crypto";
|
|
74932
75084
|
var STAGE_ID_PREFIX = "cap_";
|
|
74933
75085
|
function pendingDir(agent, opts = {}) {
|
|
74934
75086
|
const paths = overlayPathsFor(agent, opts);
|
|
74935
|
-
return
|
|
75087
|
+
return join62(paths.scheduleDir, ".pending");
|
|
74936
75088
|
}
|
|
74937
75089
|
function ensurePendingDir(agent, opts = {}) {
|
|
74938
75090
|
const dir = pendingDir(agent, opts);
|
|
@@ -74945,8 +75097,8 @@ function newStageId() {
|
|
|
74945
75097
|
function stagePendingScheduleEntry(opts) {
|
|
74946
75098
|
const dir = ensurePendingDir(opts.agent, { root: opts.root });
|
|
74947
75099
|
const stageId = opts.stageId ?? newStageId();
|
|
74948
|
-
const yamlPath =
|
|
74949
|
-
const metaPath =
|
|
75100
|
+
const yamlPath = join62(dir, `${stageId}.yaml`);
|
|
75101
|
+
const metaPath = join62(dir, `${stageId}.meta.json`);
|
|
74950
75102
|
const meta = {
|
|
74951
75103
|
v: 1,
|
|
74952
75104
|
stage_id: stageId,
|
|
@@ -74980,8 +75132,8 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
74980
75132
|
if (!name.endsWith(".meta.json"))
|
|
74981
75133
|
continue;
|
|
74982
75134
|
const stageId = name.slice(0, -".meta.json".length);
|
|
74983
|
-
const metaPath =
|
|
74984
|
-
const yamlPath =
|
|
75135
|
+
const metaPath = join62(dir, name);
|
|
75136
|
+
const yamlPath = join62(dir, `${stageId}.yaml`);
|
|
74985
75137
|
if (!existsSync69(yamlPath))
|
|
74986
75138
|
continue;
|
|
74987
75139
|
try {
|
|
@@ -75000,7 +75152,7 @@ function commitPendingScheduleEntry(opts) {
|
|
|
75000
75152
|
return { committed: false, reason: "not_found" };
|
|
75001
75153
|
const slug = match.meta.entry.name ?? match.stageId;
|
|
75002
75154
|
const paths = overlayPathsFor(opts.agent, { root: opts.root });
|
|
75003
|
-
const finalPath =
|
|
75155
|
+
const finalPath = join62(paths.scheduleDir, `${slug}.yaml`);
|
|
75004
75156
|
if (existsSync69(finalPath)) {
|
|
75005
75157
|
return { committed: false, reason: "slug_collision" };
|
|
75006
75158
|
}
|
|
@@ -75453,7 +75605,7 @@ var import_yaml15 = __toESM(require_dist(), 1);
|
|
|
75453
75605
|
import { existsSync as existsSync71 } from "node:fs";
|
|
75454
75606
|
init_reconcile_default_skills();
|
|
75455
75607
|
var import_yaml16 = __toESM(require_dist(), 1);
|
|
75456
|
-
import { join as
|
|
75608
|
+
import { join as join63 } from "node:path";
|
|
75457
75609
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
75458
75610
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
75459
75611
|
function exitCodeFor2(code) {
|
|
@@ -75528,7 +75680,7 @@ function skillInstall(opts) {
|
|
|
75528
75680
|
return err("E_SKILL_QUOTA_EXCEEDED", `agent ${agent} already has ${used} overlay-installed skills (cap ${MAX_SKILLS_PER_AGENT})`);
|
|
75529
75681
|
}
|
|
75530
75682
|
const poolDir = opts.bundledSkillsPoolDir ?? getBundledSkillsPoolDir();
|
|
75531
|
-
const skillPath =
|
|
75683
|
+
const skillPath = join63(poolDir, skillName);
|
|
75532
75684
|
if (!existsSync71(skillPath)) {
|
|
75533
75685
|
return err("E_SKILL_NOT_FOUND", `bundled skill not found at ${skillPath}. The operator needs to ` + `place the skill at this path before the agent can opt in.`);
|
|
75534
75686
|
}
|
|
@@ -75721,14 +75873,14 @@ import {
|
|
|
75721
75873
|
unlinkSync as unlinkSync15
|
|
75722
75874
|
} from "node:fs";
|
|
75723
75875
|
import { createHash as createHash12 } from "node:crypto";
|
|
75724
|
-
import { join as
|
|
75876
|
+
import { join as join64 } from "node:path";
|
|
75725
75877
|
function planCronUnitRenames(agentsDir, agents) {
|
|
75726
75878
|
const plans = [];
|
|
75727
75879
|
for (const [agentName, agentConfig] of Object.entries(agents)) {
|
|
75728
75880
|
const schedule = agentConfig.schedule ?? [];
|
|
75729
75881
|
if (schedule.length === 0)
|
|
75730
75882
|
continue;
|
|
75731
|
-
const telegramDir =
|
|
75883
|
+
const telegramDir = join64(agentsDir, agentName, "telegram");
|
|
75732
75884
|
if (!existsSync73(telegramDir))
|
|
75733
75885
|
continue;
|
|
75734
75886
|
let entries;
|
|
@@ -75750,8 +75902,8 @@ function planCronUnitRenames(agentsDir, agents) {
|
|
|
75750
75902
|
continue;
|
|
75751
75903
|
plans.push({
|
|
75752
75904
|
agent: agentName,
|
|
75753
|
-
from:
|
|
75754
|
-
to:
|
|
75905
|
+
from: join64(telegramDir, file),
|
|
75906
|
+
to: join64(telegramDir, canonical),
|
|
75755
75907
|
scheduleIdx: idx,
|
|
75756
75908
|
entry
|
|
75757
75909
|
});
|
|
@@ -75889,8 +76041,8 @@ function registerMigrateCommand(program3) {
|
|
|
75889
76041
|
init_source();
|
|
75890
76042
|
init_helpers();
|
|
75891
76043
|
import { existsSync as existsSync74, mkdirSync as mkdirSync39, readdirSync as readdirSync28, readFileSync as readFileSync61, writeFileSync as writeFileSync34, statSync as statSync28, copyFileSync as copyFileSync12 } from "node:fs";
|
|
75892
|
-
import { homedir as
|
|
75893
|
-
import { join as
|
|
76044
|
+
import { homedir as homedir34 } from "node:os";
|
|
76045
|
+
import { join as join65 } from "node:path";
|
|
75894
76046
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
75895
76047
|
init_audit_reader();
|
|
75896
76048
|
var DEFAULT_IMAGE_TAG = "latest";
|
|
@@ -75981,10 +76133,10 @@ networks:
|
|
|
75981
76133
|
`;
|
|
75982
76134
|
}
|
|
75983
76135
|
function hostdDir() {
|
|
75984
|
-
return
|
|
76136
|
+
return join65(homedir34(), ".switchroom", "hostd");
|
|
75985
76137
|
}
|
|
75986
76138
|
function hostdComposePath() {
|
|
75987
|
-
return
|
|
76139
|
+
return join65(hostdDir(), "docker-compose.yml");
|
|
75988
76140
|
}
|
|
75989
76141
|
function backupExistingCompose() {
|
|
75990
76142
|
const p = hostdComposePath();
|
|
@@ -76023,7 +76175,7 @@ async function doInstall(opts, program3) {
|
|
|
76023
76175
|
const composePath = hostdComposePath();
|
|
76024
76176
|
mkdirSync39(dir, { recursive: true });
|
|
76025
76177
|
const yaml = renderHostdComposeFile({
|
|
76026
|
-
hostHome:
|
|
76178
|
+
hostHome: homedir34(),
|
|
76027
76179
|
imageTag: opts.tag ?? DEFAULT_IMAGE_TAG,
|
|
76028
76180
|
operatorUid: resolveOperatorUid()
|
|
76029
76181
|
});
|
|
@@ -76092,7 +76244,7 @@ function doStatus() {
|
|
|
76092
76244
|
for (const name of readdirSync28(dir)) {
|
|
76093
76245
|
if (name === "docker-compose.yml" || name.startsWith("docker-compose.yml."))
|
|
76094
76246
|
continue;
|
|
76095
|
-
const sockPath =
|
|
76247
|
+
const sockPath = join65(dir, name, "sock");
|
|
76096
76248
|
if (existsSync74(sockPath)) {
|
|
76097
76249
|
const st = statSync28(sockPath);
|
|
76098
76250
|
if ((st.mode & 61440) === 49152) {
|