switchroom 0.15.19 → 0.15.20
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/cli/switchroom.js +519 -336
- package/package.json +1 -1
- package/profiles/_shared/agent-self-service.md.hbs +24 -15
- package/telegram-plugin/dist/gateway/gateway.js +89 -7
- package/telegram-plugin/gateway/gateway.ts +118 -2
- package/telegram-plugin/gateway/grant-restart.ts +30 -0
- package/telegram-plugin/tests/grant-restart.test.ts +38 -0
- package/telegram-plugin/welcome-text.ts +2 -1
package/dist/cli/switchroom.js
CHANGED
|
@@ -15644,6 +15644,240 @@ function classifyTimezoneSource(config, resolvedAgent) {
|
|
|
15644
15644
|
}
|
|
15645
15645
|
var init_timezone = () => {};
|
|
15646
15646
|
|
|
15647
|
+
// src/config/google-workspace-acl.ts
|
|
15648
|
+
function shouldEmitGdriveMcp(agentName, agentGoogleAccount, googleAccounts) {
|
|
15649
|
+
if (!agentGoogleAccount)
|
|
15650
|
+
return false;
|
|
15651
|
+
const account = agentGoogleAccount.trim().toLowerCase();
|
|
15652
|
+
if (account.length === 0)
|
|
15653
|
+
return false;
|
|
15654
|
+
const acctEntry = googleAccounts?.[account];
|
|
15655
|
+
if (!acctEntry)
|
|
15656
|
+
return false;
|
|
15657
|
+
const enabledFor = acctEntry.enabled_for ?? [];
|
|
15658
|
+
return enabledFor.includes(agentName);
|
|
15659
|
+
}
|
|
15660
|
+
function vaultRefKey(value) {
|
|
15661
|
+
if (typeof value !== "string" || !value.startsWith("vault:"))
|
|
15662
|
+
return null;
|
|
15663
|
+
const key = value.slice("vault:".length).split("#")[0];
|
|
15664
|
+
return key.length > 0 ? key : null;
|
|
15665
|
+
}
|
|
15666
|
+
function isGoogleClientCredentialKeyForAgent(config, agentName, key) {
|
|
15667
|
+
if (!agentName || !key)
|
|
15668
|
+
return false;
|
|
15669
|
+
const agentConfig = config.agents?.[agentName];
|
|
15670
|
+
if (!agentConfig)
|
|
15671
|
+
return false;
|
|
15672
|
+
if (agentConfig.mcp_servers?.["gdrive"] === false) {
|
|
15673
|
+
return false;
|
|
15674
|
+
}
|
|
15675
|
+
const account = agentConfig.google_workspace?.account;
|
|
15676
|
+
if (!shouldEmitGdriveMcp(agentName, account, config.google_accounts)) {
|
|
15677
|
+
return false;
|
|
15678
|
+
}
|
|
15679
|
+
const gw = config.google_workspace;
|
|
15680
|
+
if (!gw)
|
|
15681
|
+
return false;
|
|
15682
|
+
for (const ref of [gw.google_client_id, gw.google_client_secret]) {
|
|
15683
|
+
if (vaultRefKey(ref) === key)
|
|
15684
|
+
return true;
|
|
15685
|
+
}
|
|
15686
|
+
return false;
|
|
15687
|
+
}
|
|
15688
|
+
|
|
15689
|
+
// src/vault/broker/acl.ts
|
|
15690
|
+
function isWebkiteCredentialKeyForAgent(agentConfig, key) {
|
|
15691
|
+
if (!WEBKITE_VAULT_KEYS.has(key))
|
|
15692
|
+
return false;
|
|
15693
|
+
if ((agentConfig?.mcp_servers ?? {})["webkite"] === false)
|
|
15694
|
+
return false;
|
|
15695
|
+
return true;
|
|
15696
|
+
}
|
|
15697
|
+
function parseCronUnit(unitName) {
|
|
15698
|
+
const m = unitName.match(/^switchroom-([a-zA-Z0-9_-]+)-cron-(\d+)\.service$/);
|
|
15699
|
+
if (!m)
|
|
15700
|
+
return null;
|
|
15701
|
+
const agentName = m[1];
|
|
15702
|
+
const index = parseInt(m[2], 10);
|
|
15703
|
+
if (!agentName)
|
|
15704
|
+
return null;
|
|
15705
|
+
return { agentName, index };
|
|
15706
|
+
}
|
|
15707
|
+
function agentSlugFromPeer(peer) {
|
|
15708
|
+
if (peer.systemdUnit === null)
|
|
15709
|
+
return null;
|
|
15710
|
+
const parsed = parseCronUnit(peer.systemdUnit);
|
|
15711
|
+
return parsed?.agentName ?? null;
|
|
15712
|
+
}
|
|
15713
|
+
function checkEntryScope(scope, agentSlug) {
|
|
15714
|
+
if (scope === undefined || scope === null) {
|
|
15715
|
+
return { allow: true };
|
|
15716
|
+
}
|
|
15717
|
+
const deny = scope.deny ?? [];
|
|
15718
|
+
const allow = scope.allow ?? [];
|
|
15719
|
+
if (agentSlug !== null && deny.includes(agentSlug)) {
|
|
15720
|
+
return {
|
|
15721
|
+
allow: false,
|
|
15722
|
+
reason: `agent '${agentSlug}' is in the entry's deny list (scope-deny)`
|
|
15723
|
+
};
|
|
15724
|
+
}
|
|
15725
|
+
if (allow.length > 0) {
|
|
15726
|
+
if (agentSlug === null || !allow.includes(agentSlug)) {
|
|
15727
|
+
return {
|
|
15728
|
+
allow: false,
|
|
15729
|
+
reason: agentSlug === null ? "caller agent slug could not be determined; entry has a non-empty allow list (scope-allow)" : `agent '${agentSlug}' is not in the entry's allow list (scope-allow)`
|
|
15730
|
+
};
|
|
15731
|
+
}
|
|
15732
|
+
}
|
|
15733
|
+
return { allow: true };
|
|
15734
|
+
}
|
|
15735
|
+
function checkAcl(peer, config, key) {
|
|
15736
|
+
if (peer.systemdUnit !== null) {
|
|
15737
|
+
const parsed = parseCronUnit(peer.systemdUnit);
|
|
15738
|
+
if (parsed === null) {
|
|
15739
|
+
return {
|
|
15740
|
+
allow: false,
|
|
15741
|
+
reason: `systemd unit '${peer.systemdUnit}' does not match switchroom cron unit naming convention`
|
|
15742
|
+
};
|
|
15743
|
+
}
|
|
15744
|
+
const { agentName, index } = parsed;
|
|
15745
|
+
const agentConfig = config.agents?.[agentName];
|
|
15746
|
+
if (!agentConfig) {
|
|
15747
|
+
return { allow: false, reason: `agent '${agentName}' not found in config` };
|
|
15748
|
+
}
|
|
15749
|
+
const schedule = agentConfig.schedule ?? [];
|
|
15750
|
+
if (index >= schedule.length || index < 0) {
|
|
15751
|
+
return {
|
|
15752
|
+
allow: false,
|
|
15753
|
+
reason: `schedule index ${index} out of range for agent '${agentName}' (${schedule.length} entries)`
|
|
15754
|
+
};
|
|
15755
|
+
}
|
|
15756
|
+
const entry = schedule[index];
|
|
15757
|
+
const allowedKeys = entry.secrets ?? [];
|
|
15758
|
+
if (!allowedKeys.includes(key)) {
|
|
15759
|
+
return {
|
|
15760
|
+
allow: false,
|
|
15761
|
+
reason: `key '${key}' not in ACL for ${agentName}/schedule[${index}]`
|
|
15762
|
+
};
|
|
15763
|
+
}
|
|
15764
|
+
return { allow: true };
|
|
15765
|
+
}
|
|
15766
|
+
return {
|
|
15767
|
+
allow: false,
|
|
15768
|
+
reason: "caller is not a switchroom cron unit; use 'switchroom vault get --no-broker' for interactive access"
|
|
15769
|
+
};
|
|
15770
|
+
}
|
|
15771
|
+
function checkAclByAgent(config, agentName, key) {
|
|
15772
|
+
if (!agentName) {
|
|
15773
|
+
return { allow: false, reason: "agent name unresolved" };
|
|
15774
|
+
}
|
|
15775
|
+
const agentConfig = config.agents?.[agentName];
|
|
15776
|
+
if (!agentConfig) {
|
|
15777
|
+
return { allow: false, reason: `agent '${agentName}' not found in config` };
|
|
15778
|
+
}
|
|
15779
|
+
const googleSlot = parseGoogleAccountSlotKey(key);
|
|
15780
|
+
if (googleSlot !== null) {
|
|
15781
|
+
return checkGoogleAccountAcl(config, agentName, googleSlot.account, key);
|
|
15782
|
+
}
|
|
15783
|
+
if (isGoogleClientCredentialKeyForAgent(config, agentName, key)) {
|
|
15784
|
+
return { allow: true };
|
|
15785
|
+
}
|
|
15786
|
+
if (isWebkiteCredentialKeyForAgent(agentConfig, key)) {
|
|
15787
|
+
return { allow: true };
|
|
15788
|
+
}
|
|
15789
|
+
const agentBot = agentConfig.bot_token;
|
|
15790
|
+
const botRef = agentBot && agentBot.length > 0 ? agentBot : config.telegram?.bot_token;
|
|
15791
|
+
if (typeof botRef === "string" && botRef.startsWith("vault:")) {
|
|
15792
|
+
const botKey = botRef.slice("vault:".length).split("#")[0];
|
|
15793
|
+
if (botKey.length > 0 && botKey === key) {
|
|
15794
|
+
return { allow: true };
|
|
15795
|
+
}
|
|
15796
|
+
}
|
|
15797
|
+
const cfgWithProfiles = config;
|
|
15798
|
+
const profileName = agentConfig.extends;
|
|
15799
|
+
const profileMcp = profileName != null && profileName.length > 0 ? cfgWithProfiles.profiles?.[profileName]?.mcp_servers ?? {} : {};
|
|
15800
|
+
const effectiveMcp = {
|
|
15801
|
+
...cfgWithProfiles.defaults?.mcp_servers ?? {},
|
|
15802
|
+
...profileMcp,
|
|
15803
|
+
...agentConfig.mcp_servers ?? {}
|
|
15804
|
+
};
|
|
15805
|
+
for (const mcpEntry of Object.values(effectiveMcp)) {
|
|
15806
|
+
if (!mcpEntry || typeof mcpEntry !== "object")
|
|
15807
|
+
continue;
|
|
15808
|
+
const declared = mcpEntry.secrets;
|
|
15809
|
+
if (Array.isArray(declared) && declared.includes(key)) {
|
|
15810
|
+
return { allow: true };
|
|
15811
|
+
}
|
|
15812
|
+
}
|
|
15813
|
+
const cfgSecrets = config;
|
|
15814
|
+
const profileSecrets = profileName != null && profileName.length > 0 ? cfgSecrets.profiles?.[profileName]?.secrets : undefined;
|
|
15815
|
+
const standingSecrets = [
|
|
15816
|
+
...Array.isArray(cfgSecrets.defaults?.secrets) ? cfgSecrets.defaults.secrets : [],
|
|
15817
|
+
...Array.isArray(profileSecrets) ? profileSecrets : [],
|
|
15818
|
+
...Array.isArray(agentConfig.secrets) ? agentConfig.secrets : []
|
|
15819
|
+
];
|
|
15820
|
+
if (standingSecrets.includes(key)) {
|
|
15821
|
+
return { allow: true };
|
|
15822
|
+
}
|
|
15823
|
+
const schedule = agentConfig.schedule ?? [];
|
|
15824
|
+
if (schedule.length === 0) {
|
|
15825
|
+
return {
|
|
15826
|
+
allow: false,
|
|
15827
|
+
reason: `agent '${agentName}' has no schedule entries declaring 'secrets', no mcp_servers.*.secrets[], and no agents.${agentName}.secrets[] standing grant declaring '${key}'; nothing is broker-accessible`
|
|
15828
|
+
};
|
|
15829
|
+
}
|
|
15830
|
+
for (const entry of schedule) {
|
|
15831
|
+
const allowed = entry?.secrets ?? [];
|
|
15832
|
+
if (allowed.includes(key)) {
|
|
15833
|
+
return { allow: true };
|
|
15834
|
+
}
|
|
15835
|
+
}
|
|
15836
|
+
return {
|
|
15837
|
+
allow: false,
|
|
15838
|
+
reason: `key '${key}' not in ACL for agent '${agentName}'`
|
|
15839
|
+
};
|
|
15840
|
+
}
|
|
15841
|
+
function parseGoogleAccountSlotKey(key) {
|
|
15842
|
+
const match = key.match(/^google:([^:]+):([a-z_]+)$/);
|
|
15843
|
+
if (!match)
|
|
15844
|
+
return null;
|
|
15845
|
+
return { account: match[1], field: match[2] };
|
|
15846
|
+
}
|
|
15847
|
+
function checkGoogleAccountAcl(config, agentName, account, key) {
|
|
15848
|
+
const accounts = config.google_accounts ?? {};
|
|
15849
|
+
const accountKey = account.toLowerCase();
|
|
15850
|
+
const accountEntry = accounts[accountKey] ?? accounts[account];
|
|
15851
|
+
if (!accountEntry) {
|
|
15852
|
+
return {
|
|
15853
|
+
allow: false,
|
|
15854
|
+
reason: `google_accounts['${account}'] not configured (key '${key}')`
|
|
15855
|
+
};
|
|
15856
|
+
}
|
|
15857
|
+
const enabled = accountEntry.enabled_for ?? [];
|
|
15858
|
+
if (enabled.length === 0) {
|
|
15859
|
+
return {
|
|
15860
|
+
allow: false,
|
|
15861
|
+
reason: `google_accounts['${account}'].enabled_for is empty (key '${key}')`
|
|
15862
|
+
};
|
|
15863
|
+
}
|
|
15864
|
+
if (!enabled.includes(agentName)) {
|
|
15865
|
+
return {
|
|
15866
|
+
allow: false,
|
|
15867
|
+
reason: `agent '${agentName}' not in google_accounts['${account}'].enabled_for (key '${key}')`
|
|
15868
|
+
};
|
|
15869
|
+
}
|
|
15870
|
+
return { allow: true };
|
|
15871
|
+
}
|
|
15872
|
+
var WEBKITE_VAULT_KEYS;
|
|
15873
|
+
var init_acl = __esm(() => {
|
|
15874
|
+
WEBKITE_VAULT_KEYS = new Set([
|
|
15875
|
+
"webkite/cloudflare-account-id",
|
|
15876
|
+
"webkite/cloudflare-api-token",
|
|
15877
|
+
"webkite/firecrawl-api-key"
|
|
15878
|
+
]);
|
|
15879
|
+
});
|
|
15880
|
+
|
|
15647
15881
|
// node_modules/.bun/handlebars@4.7.9/node_modules/handlebars/dist/cjs/handlebars/utils.js
|
|
15648
15882
|
var require_utils = __commonJS((exports) => {
|
|
15649
15883
|
exports.__esModule = true;
|
|
@@ -21292,48 +21526,6 @@ var init_scaffold_integration = __esm(() => {
|
|
|
21292
21526
|
init_hindsight();
|
|
21293
21527
|
});
|
|
21294
21528
|
|
|
21295
|
-
// src/config/google-workspace-acl.ts
|
|
21296
|
-
function shouldEmitGdriveMcp(agentName, agentGoogleAccount, googleAccounts) {
|
|
21297
|
-
if (!agentGoogleAccount)
|
|
21298
|
-
return false;
|
|
21299
|
-
const account = agentGoogleAccount.trim().toLowerCase();
|
|
21300
|
-
if (account.length === 0)
|
|
21301
|
-
return false;
|
|
21302
|
-
const acctEntry = googleAccounts?.[account];
|
|
21303
|
-
if (!acctEntry)
|
|
21304
|
-
return false;
|
|
21305
|
-
const enabledFor = acctEntry.enabled_for ?? [];
|
|
21306
|
-
return enabledFor.includes(agentName);
|
|
21307
|
-
}
|
|
21308
|
-
function vaultRefKey(value) {
|
|
21309
|
-
if (typeof value !== "string" || !value.startsWith("vault:"))
|
|
21310
|
-
return null;
|
|
21311
|
-
const key = value.slice("vault:".length).split("#")[0];
|
|
21312
|
-
return key.length > 0 ? key : null;
|
|
21313
|
-
}
|
|
21314
|
-
function isGoogleClientCredentialKeyForAgent(config, agentName, key) {
|
|
21315
|
-
if (!agentName || !key)
|
|
21316
|
-
return false;
|
|
21317
|
-
const agentConfig = config.agents?.[agentName];
|
|
21318
|
-
if (!agentConfig)
|
|
21319
|
-
return false;
|
|
21320
|
-
if (agentConfig.mcp_servers?.["gdrive"] === false) {
|
|
21321
|
-
return false;
|
|
21322
|
-
}
|
|
21323
|
-
const account = agentConfig.google_workspace?.account;
|
|
21324
|
-
if (!shouldEmitGdriveMcp(agentName, account, config.google_accounts)) {
|
|
21325
|
-
return false;
|
|
21326
|
-
}
|
|
21327
|
-
const gw = config.google_workspace;
|
|
21328
|
-
if (!gw)
|
|
21329
|
-
return false;
|
|
21330
|
-
for (const ref of [gw.google_client_id, gw.google_client_secret]) {
|
|
21331
|
-
if (vaultRefKey(ref) === key)
|
|
21332
|
-
return true;
|
|
21333
|
-
}
|
|
21334
|
-
return false;
|
|
21335
|
-
}
|
|
21336
|
-
|
|
21337
21529
|
// src/agents/reconcile-default-skills.ts
|
|
21338
21530
|
import { existsSync as existsSync6, lstatSync, mkdirSync as mkdirSync3, readdirSync as readdirSync3, readlinkSync as readlinkSync2, rmSync, symlinkSync } from "node:fs";
|
|
21339
21531
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -28406,198 +28598,6 @@ var init_via_claude = __esm(() => {
|
|
|
28406
28598
|
];
|
|
28407
28599
|
});
|
|
28408
28600
|
|
|
28409
|
-
// src/vault/broker/acl.ts
|
|
28410
|
-
function isWebkiteCredentialKeyForAgent(agentConfig, key) {
|
|
28411
|
-
if (!WEBKITE_VAULT_KEYS.has(key))
|
|
28412
|
-
return false;
|
|
28413
|
-
if ((agentConfig?.mcp_servers ?? {})["webkite"] === false)
|
|
28414
|
-
return false;
|
|
28415
|
-
return true;
|
|
28416
|
-
}
|
|
28417
|
-
function parseCronUnit(unitName) {
|
|
28418
|
-
const m = unitName.match(/^switchroom-([a-zA-Z0-9_-]+)-cron-(\d+)\.service$/);
|
|
28419
|
-
if (!m)
|
|
28420
|
-
return null;
|
|
28421
|
-
const agentName = m[1];
|
|
28422
|
-
const index = parseInt(m[2], 10);
|
|
28423
|
-
if (!agentName)
|
|
28424
|
-
return null;
|
|
28425
|
-
return { agentName, index };
|
|
28426
|
-
}
|
|
28427
|
-
function agentSlugFromPeer(peer) {
|
|
28428
|
-
if (peer.systemdUnit === null)
|
|
28429
|
-
return null;
|
|
28430
|
-
const parsed = parseCronUnit(peer.systemdUnit);
|
|
28431
|
-
return parsed?.agentName ?? null;
|
|
28432
|
-
}
|
|
28433
|
-
function checkEntryScope(scope, agentSlug) {
|
|
28434
|
-
if (scope === undefined || scope === null) {
|
|
28435
|
-
return { allow: true };
|
|
28436
|
-
}
|
|
28437
|
-
const deny = scope.deny ?? [];
|
|
28438
|
-
const allow = scope.allow ?? [];
|
|
28439
|
-
if (agentSlug !== null && deny.includes(agentSlug)) {
|
|
28440
|
-
return {
|
|
28441
|
-
allow: false,
|
|
28442
|
-
reason: `agent '${agentSlug}' is in the entry's deny list (scope-deny)`
|
|
28443
|
-
};
|
|
28444
|
-
}
|
|
28445
|
-
if (allow.length > 0) {
|
|
28446
|
-
if (agentSlug === null || !allow.includes(agentSlug)) {
|
|
28447
|
-
return {
|
|
28448
|
-
allow: false,
|
|
28449
|
-
reason: agentSlug === null ? "caller agent slug could not be determined; entry has a non-empty allow list (scope-allow)" : `agent '${agentSlug}' is not in the entry's allow list (scope-allow)`
|
|
28450
|
-
};
|
|
28451
|
-
}
|
|
28452
|
-
}
|
|
28453
|
-
return { allow: true };
|
|
28454
|
-
}
|
|
28455
|
-
function checkAcl(peer, config, key) {
|
|
28456
|
-
if (peer.systemdUnit !== null) {
|
|
28457
|
-
const parsed = parseCronUnit(peer.systemdUnit);
|
|
28458
|
-
if (parsed === null) {
|
|
28459
|
-
return {
|
|
28460
|
-
allow: false,
|
|
28461
|
-
reason: `systemd unit '${peer.systemdUnit}' does not match switchroom cron unit naming convention`
|
|
28462
|
-
};
|
|
28463
|
-
}
|
|
28464
|
-
const { agentName, index } = parsed;
|
|
28465
|
-
const agentConfig = config.agents?.[agentName];
|
|
28466
|
-
if (!agentConfig) {
|
|
28467
|
-
return { allow: false, reason: `agent '${agentName}' not found in config` };
|
|
28468
|
-
}
|
|
28469
|
-
const schedule = agentConfig.schedule ?? [];
|
|
28470
|
-
if (index >= schedule.length || index < 0) {
|
|
28471
|
-
return {
|
|
28472
|
-
allow: false,
|
|
28473
|
-
reason: `schedule index ${index} out of range for agent '${agentName}' (${schedule.length} entries)`
|
|
28474
|
-
};
|
|
28475
|
-
}
|
|
28476
|
-
const entry = schedule[index];
|
|
28477
|
-
const allowedKeys = entry.secrets ?? [];
|
|
28478
|
-
if (!allowedKeys.includes(key)) {
|
|
28479
|
-
return {
|
|
28480
|
-
allow: false,
|
|
28481
|
-
reason: `key '${key}' not in ACL for ${agentName}/schedule[${index}]`
|
|
28482
|
-
};
|
|
28483
|
-
}
|
|
28484
|
-
return { allow: true };
|
|
28485
|
-
}
|
|
28486
|
-
return {
|
|
28487
|
-
allow: false,
|
|
28488
|
-
reason: "caller is not a switchroom cron unit; use 'switchroom vault get --no-broker' for interactive access"
|
|
28489
|
-
};
|
|
28490
|
-
}
|
|
28491
|
-
function checkAclByAgent(config, agentName, key) {
|
|
28492
|
-
if (!agentName) {
|
|
28493
|
-
return { allow: false, reason: "agent name unresolved" };
|
|
28494
|
-
}
|
|
28495
|
-
const agentConfig = config.agents?.[agentName];
|
|
28496
|
-
if (!agentConfig) {
|
|
28497
|
-
return { allow: false, reason: `agent '${agentName}' not found in config` };
|
|
28498
|
-
}
|
|
28499
|
-
const googleSlot = parseGoogleAccountSlotKey(key);
|
|
28500
|
-
if (googleSlot !== null) {
|
|
28501
|
-
return checkGoogleAccountAcl(config, agentName, googleSlot.account, key);
|
|
28502
|
-
}
|
|
28503
|
-
if (isGoogleClientCredentialKeyForAgent(config, agentName, key)) {
|
|
28504
|
-
return { allow: true };
|
|
28505
|
-
}
|
|
28506
|
-
if (isWebkiteCredentialKeyForAgent(agentConfig, key)) {
|
|
28507
|
-
return { allow: true };
|
|
28508
|
-
}
|
|
28509
|
-
const agentBot = agentConfig.bot_token;
|
|
28510
|
-
const botRef = agentBot && agentBot.length > 0 ? agentBot : config.telegram?.bot_token;
|
|
28511
|
-
if (typeof botRef === "string" && botRef.startsWith("vault:")) {
|
|
28512
|
-
const botKey = botRef.slice("vault:".length).split("#")[0];
|
|
28513
|
-
if (botKey.length > 0 && botKey === key) {
|
|
28514
|
-
return { allow: true };
|
|
28515
|
-
}
|
|
28516
|
-
}
|
|
28517
|
-
const cfgWithProfiles = config;
|
|
28518
|
-
const profileName = agentConfig.extends;
|
|
28519
|
-
const profileMcp = profileName != null && profileName.length > 0 ? cfgWithProfiles.profiles?.[profileName]?.mcp_servers ?? {} : {};
|
|
28520
|
-
const effectiveMcp = {
|
|
28521
|
-
...cfgWithProfiles.defaults?.mcp_servers ?? {},
|
|
28522
|
-
...profileMcp,
|
|
28523
|
-
...agentConfig.mcp_servers ?? {}
|
|
28524
|
-
};
|
|
28525
|
-
for (const mcpEntry of Object.values(effectiveMcp)) {
|
|
28526
|
-
if (!mcpEntry || typeof mcpEntry !== "object")
|
|
28527
|
-
continue;
|
|
28528
|
-
const declared = mcpEntry.secrets;
|
|
28529
|
-
if (Array.isArray(declared) && declared.includes(key)) {
|
|
28530
|
-
return { allow: true };
|
|
28531
|
-
}
|
|
28532
|
-
}
|
|
28533
|
-
const cfgSecrets = config;
|
|
28534
|
-
const profileSecrets = profileName != null && profileName.length > 0 ? cfgSecrets.profiles?.[profileName]?.secrets : undefined;
|
|
28535
|
-
const standingSecrets = [
|
|
28536
|
-
...Array.isArray(cfgSecrets.defaults?.secrets) ? cfgSecrets.defaults.secrets : [],
|
|
28537
|
-
...Array.isArray(profileSecrets) ? profileSecrets : [],
|
|
28538
|
-
...Array.isArray(agentConfig.secrets) ? agentConfig.secrets : []
|
|
28539
|
-
];
|
|
28540
|
-
if (standingSecrets.includes(key)) {
|
|
28541
|
-
return { allow: true };
|
|
28542
|
-
}
|
|
28543
|
-
const schedule = agentConfig.schedule ?? [];
|
|
28544
|
-
if (schedule.length === 0) {
|
|
28545
|
-
return {
|
|
28546
|
-
allow: false,
|
|
28547
|
-
reason: `agent '${agentName}' has no schedule entries declaring 'secrets', no mcp_servers.*.secrets[], and no agents.${agentName}.secrets[] standing grant declaring '${key}'; nothing is broker-accessible`
|
|
28548
|
-
};
|
|
28549
|
-
}
|
|
28550
|
-
for (const entry of schedule) {
|
|
28551
|
-
const allowed = entry?.secrets ?? [];
|
|
28552
|
-
if (allowed.includes(key)) {
|
|
28553
|
-
return { allow: true };
|
|
28554
|
-
}
|
|
28555
|
-
}
|
|
28556
|
-
return {
|
|
28557
|
-
allow: false,
|
|
28558
|
-
reason: `key '${key}' not in ACL for agent '${agentName}'`
|
|
28559
|
-
};
|
|
28560
|
-
}
|
|
28561
|
-
function parseGoogleAccountSlotKey(key) {
|
|
28562
|
-
const match = key.match(/^google:([^:]+):([a-z_]+)$/);
|
|
28563
|
-
if (!match)
|
|
28564
|
-
return null;
|
|
28565
|
-
return { account: match[1], field: match[2] };
|
|
28566
|
-
}
|
|
28567
|
-
function checkGoogleAccountAcl(config, agentName, account, key) {
|
|
28568
|
-
const accounts = config.google_accounts ?? {};
|
|
28569
|
-
const accountKey = account.toLowerCase();
|
|
28570
|
-
const accountEntry = accounts[accountKey] ?? accounts[account];
|
|
28571
|
-
if (!accountEntry) {
|
|
28572
|
-
return {
|
|
28573
|
-
allow: false,
|
|
28574
|
-
reason: `google_accounts['${account}'] not configured (key '${key}')`
|
|
28575
|
-
};
|
|
28576
|
-
}
|
|
28577
|
-
const enabled = accountEntry.enabled_for ?? [];
|
|
28578
|
-
if (enabled.length === 0) {
|
|
28579
|
-
return {
|
|
28580
|
-
allow: false,
|
|
28581
|
-
reason: `google_accounts['${account}'].enabled_for is empty (key '${key}')`
|
|
28582
|
-
};
|
|
28583
|
-
}
|
|
28584
|
-
if (!enabled.includes(agentName)) {
|
|
28585
|
-
return {
|
|
28586
|
-
allow: false,
|
|
28587
|
-
reason: `agent '${agentName}' not in google_accounts['${account}'].enabled_for (key '${key}')`
|
|
28588
|
-
};
|
|
28589
|
-
}
|
|
28590
|
-
return { allow: true };
|
|
28591
|
-
}
|
|
28592
|
-
var WEBKITE_VAULT_KEYS;
|
|
28593
|
-
var init_acl = __esm(() => {
|
|
28594
|
-
WEBKITE_VAULT_KEYS = new Set([
|
|
28595
|
-
"webkite/cloudflare-account-id",
|
|
28596
|
-
"webkite/cloudflare-api-token",
|
|
28597
|
-
"webkite/firecrawl-api-key"
|
|
28598
|
-
]);
|
|
28599
|
-
});
|
|
28600
|
-
|
|
28601
28601
|
// src/util/audit-hashchain.ts
|
|
28602
28602
|
import { createHash as createHash7 } from "node:crypto";
|
|
28603
28603
|
import { openSync as openSync7, readSync as readSync2, fstatSync as fstatSync2, closeSync as closeSync7 } from "node:fs";
|
|
@@ -49612,6 +49612,10 @@ function dispatchTool(name, args) {
|
|
|
49612
49612
|
cliArgs = buildArgs(["config", "get"], args);
|
|
49613
49613
|
parseMode = "json";
|
|
49614
49614
|
break;
|
|
49615
|
+
case "whoami":
|
|
49616
|
+
cliArgs = buildArgs(["config", "whoami"], args);
|
|
49617
|
+
parseMode = "json";
|
|
49618
|
+
break;
|
|
49615
49619
|
case "cron_list":
|
|
49616
49620
|
cliArgs = buildArgs(["cron", "list"], args);
|
|
49617
49621
|
parseMode = "json";
|
|
@@ -49818,6 +49822,19 @@ var init_server3 = __esm(() => {
|
|
|
49818
49822
|
}
|
|
49819
49823
|
}
|
|
49820
49824
|
},
|
|
49825
|
+
{
|
|
49826
|
+
name: "whoami",
|
|
49827
|
+
description: "See your own sandbox: the tools, MCP servers, vault key-NAMES (never " + "values), skills, schedule and privileges (admin/root/config-edit) you " + "actually have, as JSON \u2014 computed from live enforcement. Call this " + "FIRST when you hit a permission wall, instead of guessing or asking. " + "Read-only; defaults to your own identity.",
|
|
49828
|
+
inputSchema: {
|
|
49829
|
+
type: "object",
|
|
49830
|
+
properties: {
|
|
49831
|
+
agent: {
|
|
49832
|
+
type: "string",
|
|
49833
|
+
description: "Target agent name. Optional \u2014 defaults to the env-pinned agent identity."
|
|
49834
|
+
}
|
|
49835
|
+
}
|
|
49836
|
+
}
|
|
49837
|
+
},
|
|
49821
49838
|
{
|
|
49822
49839
|
name: "cron_list",
|
|
49823
49840
|
description: "List the agent's scheduled cron entries (schedule array) as JSON.",
|
|
@@ -49840,7 +49857,7 @@ var init_server3 = __esm(() => {
|
|
|
49840
49857
|
},
|
|
49841
49858
|
{
|
|
49842
49859
|
name: "schedule_add",
|
|
49843
|
-
description: "Append a cron schedule entry to your overlay. Takes effect within ~30s \u2014 " + "the scheduler hot-reloads, no restart needed. Overlay entries with " + "non-empty `secrets:` are REJECTED (E_OVERLAY_SECRETS_REQUIRES_APPROVAL). " + "COST:
|
|
49860
|
+
description: "Append a cron schedule entry to your overlay. Takes effect within ~30s \u2014 " + "the scheduler hot-reloads, no restart needed. Overlay entries with " + "non-empty `secrets:` are REJECTED (E_OVERLAY_SECRETS_REQUIRES_APPROVAL). " + "COST: a FREQUENT cron (every <=60min) is already auto-routed to a cheap, " + "minimal-context cron session (Tier 1) by default; a DAILY/WEEKLY cron runs " + 'as a full turn in your live session (Tier 2). Set `model: "sonnet"` / ' + '`context: "fresh"` to force the cheap session for a self-contained ' + 'daily/weekly job, or `context: "agent"` to keep a fire on your full ' + "session when it needs your accumulated context. CHEAPER STILL (request " + "from the operator \u2014 you can't self-author these): a `kind: action` runs a " + "FIXED post/webhook MODEL-FREE (zero tokens, no session) for a set " + "reminder/ping; a `kind: poll` or reaction-dispatch handles 'only act when " + "X changes' without a wasted turn each tick.",
|
|
49844
49861
|
inputSchema: {
|
|
49845
49862
|
type: "object",
|
|
49846
49863
|
required: ["cron_expr", "prompt"],
|
|
@@ -49851,7 +49868,7 @@ var init_server3 = __esm(() => {
|
|
|
49851
49868
|
name: { type: "string", pattern: "^[a-z0-9-]{1,40}$" },
|
|
49852
49869
|
model: {
|
|
49853
49870
|
type: "string",
|
|
49854
|
-
description: "Optional cheap-cron tier hint. A known-cheap model ('sonnet'/'haiku') " + "routes this fire to a fresh, minimal-context cron session (Tier 1) " + "instead of your full live session (Tier 2) \u2014 cheaper per fire. Omit " + "for context-heavy work that needs your
|
|
49871
|
+
description: "Optional cheap-cron tier hint. A known-cheap model ('sonnet'/'haiku') " + "routes this fire to a fresh, minimal-context cron session (Tier 1) " + "instead of your full live session (Tier 2) \u2014 cheaper per fire. Omit " + "for context-heavy work that needs your accumulated conversation " + "context (a frequent cron is already Tier-1 by default; this is mainly " + "to make a daily/weekly self-contained job cheap too)."
|
|
49855
49872
|
},
|
|
49856
49873
|
context: {
|
|
49857
49874
|
type: "string",
|
|
@@ -50049,7 +50066,7 @@ __export(exports_server2, {
|
|
|
50049
50066
|
TOOLS: () => TOOLS2
|
|
50050
50067
|
});
|
|
50051
50068
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
50052
|
-
import { existsSync as existsSync83, readFileSync as
|
|
50069
|
+
import { existsSync as existsSync83, readFileSync as readFileSync71 } from "node:fs";
|
|
50053
50070
|
function selfSocketPath() {
|
|
50054
50071
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
50055
50072
|
}
|
|
@@ -50227,7 +50244,7 @@ function getLastUpdateApplyStatus() {
|
|
|
50227
50244
|
}
|
|
50228
50245
|
let raw;
|
|
50229
50246
|
try {
|
|
50230
|
-
raw =
|
|
50247
|
+
raw = readFileSync71(path8, "utf-8");
|
|
50231
50248
|
} catch (err2) {
|
|
50232
50249
|
return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
|
|
50233
50250
|
}
|
|
@@ -50460,8 +50477,8 @@ var {
|
|
|
50460
50477
|
} = import__.default;
|
|
50461
50478
|
|
|
50462
50479
|
// src/build-info.ts
|
|
50463
|
-
var VERSION = "0.15.
|
|
50464
|
-
var COMMIT_SHA = "
|
|
50480
|
+
var VERSION = "0.15.20";
|
|
50481
|
+
var COMMIT_SHA = "0b63ab9e";
|
|
50465
50482
|
|
|
50466
50483
|
// src/cli/agent.ts
|
|
50467
50484
|
init_source();
|
|
@@ -50578,6 +50595,8 @@ init_timezone();
|
|
|
50578
50595
|
|
|
50579
50596
|
// src/cli/agent-config.ts
|
|
50580
50597
|
init_helpers();
|
|
50598
|
+
init_merge();
|
|
50599
|
+
init_acl();
|
|
50581
50600
|
import { join as join2 } from "node:path";
|
|
50582
50601
|
import { homedir as homedir2 } from "node:os";
|
|
50583
50602
|
import {
|
|
@@ -50673,6 +50692,66 @@ function scheduleLiveNote(agent) {
|
|
|
50673
50692
|
const hotReload = (process.env.SWITCHROOM_SCHEDULER_HOT_RELOAD ?? "") !== "0";
|
|
50674
50693
|
return hotReload ? `Live within ~30s \u2014 the in-agent scheduler hot-reloads the overlay; no restart needed.` : `Not live yet \u2014 hot-reload is disabled (SWITCHROOM_SCHEDULER_HOT_RELOAD=0). ` + `Run \`switchroom agent restart ${agent}\` for this to take effect.`;
|
|
50675
50694
|
}
|
|
50695
|
+
function collectVaultKeys(value, out) {
|
|
50696
|
+
if (typeof value === "string") {
|
|
50697
|
+
if (value.startsWith("vault:"))
|
|
50698
|
+
out.add(value.slice("vault:".length));
|
|
50699
|
+
return;
|
|
50700
|
+
}
|
|
50701
|
+
if (Array.isArray(value)) {
|
|
50702
|
+
for (const v of value)
|
|
50703
|
+
collectVaultKeys(v, out);
|
|
50704
|
+
return;
|
|
50705
|
+
}
|
|
50706
|
+
if (value && typeof value === "object") {
|
|
50707
|
+
for (const [k, v] of Object.entries(value)) {
|
|
50708
|
+
if (k === "secrets" && Array.isArray(v)) {
|
|
50709
|
+
for (const s of v)
|
|
50710
|
+
if (typeof s === "string")
|
|
50711
|
+
out.add(s);
|
|
50712
|
+
} else {
|
|
50713
|
+
collectVaultKeys(v, out);
|
|
50714
|
+
}
|
|
50715
|
+
}
|
|
50716
|
+
}
|
|
50717
|
+
}
|
|
50718
|
+
function buildWhoami(config, agentName) {
|
|
50719
|
+
const slice = config.agents?.[agentName];
|
|
50720
|
+
if (!slice)
|
|
50721
|
+
throw new Error(`agent "${agentName}" not defined in switchroom.yaml`);
|
|
50722
|
+
const resolved = resolveAgentConfig(config.defaults, config.profiles, slice);
|
|
50723
|
+
const admin = resolved.admin === true || resolved.root === true;
|
|
50724
|
+
const root = resolved.root === true;
|
|
50725
|
+
const toolsBlock = resolved.tools ?? {};
|
|
50726
|
+
const allow = [
|
|
50727
|
+
...toolsBlock.allow ?? [],
|
|
50728
|
+
...resolved.allowed_tools ?? []
|
|
50729
|
+
];
|
|
50730
|
+
const deny = [
|
|
50731
|
+
...toolsBlock.deny ?? [],
|
|
50732
|
+
...resolved.disallowed_tools ?? []
|
|
50733
|
+
];
|
|
50734
|
+
const mcpServers = Object.keys(resolved.mcp_servers ?? {});
|
|
50735
|
+
const skills = (resolved.skills ?? []).slice();
|
|
50736
|
+
const vaultKeys = new Set;
|
|
50737
|
+
collectVaultKeys(resolved, vaultKeys);
|
|
50738
|
+
const vault = [...vaultKeys].sort().map((key) => ({ key, readable: checkAclByAgent(config, agentName, key).allow }));
|
|
50739
|
+
const configEdit = config.hostd?.config_edit_enabled === true;
|
|
50740
|
+
const schedule = resolved.schedule ?? [];
|
|
50741
|
+
return {
|
|
50742
|
+
name: agentName,
|
|
50743
|
+
persona: resolved.description ?? resolved.persona ?? null,
|
|
50744
|
+
model: resolved.model ?? null,
|
|
50745
|
+
tier: root ? "root" : admin ? "admin" : "standard",
|
|
50746
|
+
tools: { allow, deny },
|
|
50747
|
+
mcpServers,
|
|
50748
|
+
skills,
|
|
50749
|
+
vault,
|
|
50750
|
+
powers: { admin, root, configEdit, crossAgentHostVerbs: admin },
|
|
50751
|
+
scheduleCount: schedule.length,
|
|
50752
|
+
memoryBackend: config.memory?.backend ?? null
|
|
50753
|
+
};
|
|
50754
|
+
}
|
|
50676
50755
|
function getAgentSlice(config, agent) {
|
|
50677
50756
|
const slice = config.agents?.[agent];
|
|
50678
50757
|
if (!slice) {
|
|
@@ -50729,6 +50808,29 @@ function registerAgentConfigCommands(program2) {
|
|
|
50729
50808
|
process.exit(1);
|
|
50730
50809
|
}
|
|
50731
50810
|
}));
|
|
50811
|
+
config.command("whoami").description("Emit what this agent is allowed to do (tools, MCP, vault key-names, powers) as JSON \u2014 its own sandbox, computed from live enforcement").option("--agent <name>", "Target agent (defaults to $SWITCHROOM_AGENT_NAME)").action(withConfigError(async (opts) => {
|
|
50812
|
+
let agent;
|
|
50813
|
+
try {
|
|
50814
|
+
agent = resolveTargetAgent(opts.agent);
|
|
50815
|
+
} catch (err) {
|
|
50816
|
+
process.stderr.write(`${err.message}
|
|
50817
|
+
`);
|
|
50818
|
+
appendAudit(opts.agent ?? "<unknown>", "config.whoami", { ...opts }, 7);
|
|
50819
|
+
process.exit(7);
|
|
50820
|
+
}
|
|
50821
|
+
const cfg = getConfig(program2);
|
|
50822
|
+
try {
|
|
50823
|
+
const view = buildWhoami(cfg, agent);
|
|
50824
|
+
process.stdout.write(JSON.stringify(view) + `
|
|
50825
|
+
`);
|
|
50826
|
+
appendAudit(agent, "config.whoami", { ...opts }, 0);
|
|
50827
|
+
} catch (err) {
|
|
50828
|
+
process.stderr.write(`${err.message}
|
|
50829
|
+
`);
|
|
50830
|
+
appendAudit(agent, "config.whoami", { ...opts }, 1);
|
|
50831
|
+
process.exit(1);
|
|
50832
|
+
}
|
|
50833
|
+
}));
|
|
50732
50834
|
const cron = program2.command("cron").description("Read-only access to an agent's cron schedule");
|
|
50733
50835
|
cron.command("list").description("List the agent's scheduled cron entries as JSON").option("--agent <name>", "Target agent (defaults to $SWITCHROOM_AGENT_NAME)").action(withConfigError(async (opts) => {
|
|
50734
50836
|
let agent;
|
|
@@ -76329,10 +76431,48 @@ init_doctor();
|
|
|
76329
76431
|
init_source();
|
|
76330
76432
|
init_loader();
|
|
76331
76433
|
init_lifecycle();
|
|
76332
|
-
import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as readFileSync52, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync26 } from "node:fs";
|
|
76434
|
+
import { cpSync as cpSync2, existsSync as existsSync58, mkdirSync as mkdirSync32, readFileSync as readFileSync52, realpathSync as realpathSync6, rmSync as rmSync12, statSync as statSync26, chownSync as chownSync5 } from "node:fs";
|
|
76333
76435
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
76334
76436
|
import { join as join60, dirname as dirname14, resolve as resolve34 } from "node:path";
|
|
76335
76437
|
import { homedir as homedir36 } from "node:os";
|
|
76438
|
+
|
|
76439
|
+
// src/cli/release-yaml.ts
|
|
76440
|
+
var import_yaml18 = __toESM(require_dist(), 1);
|
|
76441
|
+
function setReleasePinInConfig(yamlText, pin, now = new Date().toISOString().slice(0, 10)) {
|
|
76442
|
+
const doc = import_yaml18.parseDocument(yamlText);
|
|
76443
|
+
const currentPin = doc.getIn(["release", "pin"]);
|
|
76444
|
+
const hasChannel = doc.hasIn(["release", "channel"]);
|
|
76445
|
+
if (currentPin === pin && !hasChannel)
|
|
76446
|
+
return yamlText;
|
|
76447
|
+
if (hasChannel)
|
|
76448
|
+
doc.deleteIn(["release", "channel"]);
|
|
76449
|
+
doc.setIn(["release", "pin"], pin);
|
|
76450
|
+
const node = doc.getIn(["release", "pin"], true);
|
|
76451
|
+
if (import_yaml18.isScalar(node)) {
|
|
76452
|
+
node.comment = ` ${now}: rolled by switchroom rollout`;
|
|
76453
|
+
}
|
|
76454
|
+
return String(doc);
|
|
76455
|
+
}
|
|
76456
|
+
|
|
76457
|
+
// src/cli/update.ts
|
|
76458
|
+
init_atomic();
|
|
76459
|
+
function defaultPersistPin(configPath) {
|
|
76460
|
+
return (pin) => {
|
|
76461
|
+
const path4 = configPath ?? findConfigFile();
|
|
76462
|
+
const before = readFileSync52(path4, "utf8");
|
|
76463
|
+
const after = setReleasePinInConfig(before, pin);
|
|
76464
|
+
if (after === before)
|
|
76465
|
+
return;
|
|
76466
|
+
atomicWriteFileSync2(path4, after, statSync26(path4).mode & 511);
|
|
76467
|
+
try {
|
|
76468
|
+
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
76469
|
+
const uid = resolveOperatorUid();
|
|
76470
|
+
if (uid !== undefined)
|
|
76471
|
+
chownSync5(path4, uid, uid);
|
|
76472
|
+
}
|
|
76473
|
+
} catch {}
|
|
76474
|
+
};
|
|
76475
|
+
}
|
|
76336
76476
|
var DEFAULT_COMPOSE_PATH = join60(homedir36(), ".switchroom", "compose", "docker-compose.yml");
|
|
76337
76477
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
76338
76478
|
let dir = dirname14(scriptPath);
|
|
@@ -76365,6 +76505,15 @@ function planUpdate(opts) {
|
|
|
76365
76505
|
const runner = opts.runner ?? defaultRunner;
|
|
76366
76506
|
const scriptPath = opts.scriptPath ?? process.argv[1] ?? "";
|
|
76367
76507
|
const steps = [];
|
|
76508
|
+
if (opts.pin) {
|
|
76509
|
+
const pin = opts.pin;
|
|
76510
|
+
const persist = opts.persistPinFn ?? defaultPersistPin(opts.configPath);
|
|
76511
|
+
steps.push({
|
|
76512
|
+
name: "persist-release-pin",
|
|
76513
|
+
description: `Persist release.pin=${pin} to switchroom.yaml (durable)`,
|
|
76514
|
+
run: () => persist(pin)
|
|
76515
|
+
});
|
|
76516
|
+
}
|
|
76368
76517
|
const releaseOverrideArgs = [];
|
|
76369
76518
|
if (opts.channel)
|
|
76370
76519
|
releaseOverrideArgs.push("--channel", opts.channel);
|
|
@@ -76814,6 +76963,8 @@ function registerUpdateCommand(program3) {
|
|
|
76814
76963
|
// src/cli/rollout.ts
|
|
76815
76964
|
init_helpers();
|
|
76816
76965
|
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
76966
|
+
import { readFileSync as readFileSync53, chownSync as chownSync6, statSync as statSync27 } from "node:fs";
|
|
76967
|
+
init_atomic();
|
|
76817
76968
|
function normalizeVersion(v) {
|
|
76818
76969
|
return v.trim().replace(/^v/, "");
|
|
76819
76970
|
}
|
|
@@ -76826,7 +76977,10 @@ function orderAgentsCanaryFirst(agents) {
|
|
|
76826
76977
|
return [...canary, ...rest];
|
|
76827
76978
|
}
|
|
76828
76979
|
function planRollout(agents, opts = {}) {
|
|
76829
|
-
const steps = [
|
|
76980
|
+
const steps = [];
|
|
76981
|
+
if (opts.pinToPersist)
|
|
76982
|
+
steps.push({ kind: "persist-pin", pin: opts.pinToPersist });
|
|
76983
|
+
steps.push({ kind: "apply" });
|
|
76830
76984
|
for (const agent of orderAgentsCanaryFirst(agents)) {
|
|
76831
76985
|
steps.push({ kind: "restart-agent", agent });
|
|
76832
76986
|
}
|
|
@@ -76843,6 +76997,9 @@ function formatRolloutPlan(steps, target) {
|
|
|
76843
76997
|
for (const s of steps) {
|
|
76844
76998
|
n += 1;
|
|
76845
76999
|
switch (s.kind) {
|
|
77000
|
+
case "persist-pin":
|
|
77001
|
+
lines.push(` ${n}. persist release.pin=${s.pin} to switchroom.yaml (durable)`);
|
|
77002
|
+
break;
|
|
76846
77003
|
case "apply":
|
|
76847
77004
|
lines.push(` ${n}. apply \u2014 regenerate compose with ${target} image refs`);
|
|
76848
77005
|
break;
|
|
@@ -76865,16 +77022,24 @@ function formatRolloutPlan(steps, target) {
|
|
|
76865
77022
|
return lines.join(`
|
|
76866
77023
|
`);
|
|
76867
77024
|
}
|
|
76868
|
-
function executeRollout(steps, target, deps
|
|
77025
|
+
function executeRollout(steps, target, deps) {
|
|
76869
77026
|
const targetNorm = normalizeVersion(target);
|
|
76870
77027
|
const rolled = [];
|
|
76871
77028
|
const warnings = [];
|
|
76872
77029
|
for (const step of steps) {
|
|
76873
77030
|
switch (step.kind) {
|
|
77031
|
+
case "persist-pin": {
|
|
77032
|
+
deps.log(`\u2192 persist release.pin=${step.pin} to switchroom.yaml`);
|
|
77033
|
+
if (deps.persistPin) {
|
|
77034
|
+
deps.persistPin(step.pin);
|
|
77035
|
+
} else {
|
|
77036
|
+
warnings.push(`persist-pin requested but no persist hook wired; pin NOT durable`);
|
|
77037
|
+
}
|
|
77038
|
+
break;
|
|
77039
|
+
}
|
|
76874
77040
|
case "apply": {
|
|
76875
77041
|
deps.log(`\u2192 apply \u2014 regenerating compose for ${target}`);
|
|
76876
|
-
const
|
|
76877
|
-
const r = deps.run(args);
|
|
77042
|
+
const r = deps.run(["apply"]);
|
|
76878
77043
|
if (r.status !== 0) {
|
|
76879
77044
|
return { ok: false, rolled, failedStep: "apply", warnings };
|
|
76880
77045
|
}
|
|
@@ -76956,12 +77121,16 @@ function registerRolloutCommand(program3) {
|
|
|
76956
77121
|
process.exitCode = 2;
|
|
76957
77122
|
return;
|
|
76958
77123
|
}
|
|
76959
|
-
const steps = planRollout(requested, {
|
|
77124
|
+
const steps = planRollout(requested, {
|
|
77125
|
+
skipWeb: opts.skipWeb,
|
|
77126
|
+
pinToPersist: opts.pin ?? undefined
|
|
77127
|
+
});
|
|
76960
77128
|
if (opts.dryRun) {
|
|
76961
77129
|
process.stdout.write(formatRolloutPlan(steps, target) + `
|
|
76962
77130
|
`);
|
|
76963
77131
|
return;
|
|
76964
77132
|
}
|
|
77133
|
+
const configPath = getConfigPath(program3);
|
|
76965
77134
|
const scriptPath = process.argv[1] ?? "switchroom";
|
|
76966
77135
|
const deps = {
|
|
76967
77136
|
run: (args) => {
|
|
@@ -76976,11 +77145,25 @@ function registerRolloutCommand(program3) {
|
|
|
76976
77145
|
`).pop()?.trim() ?? null;
|
|
76977
77146
|
},
|
|
76978
77147
|
log: (line) => process.stdout.write(line + `
|
|
76979
|
-
`)
|
|
77148
|
+
`),
|
|
77149
|
+
persistPin: (pin) => {
|
|
77150
|
+
const before = readFileSync53(configPath, "utf8");
|
|
77151
|
+
const after = setReleasePinInConfig(before, pin);
|
|
77152
|
+
if (after === before)
|
|
77153
|
+
return;
|
|
77154
|
+
atomicWriteFileSync2(configPath, after, statSync27(configPath).mode & 511);
|
|
77155
|
+
try {
|
|
77156
|
+
if (typeof process.geteuid === "function" && process.geteuid() === 0) {
|
|
77157
|
+
const uid = resolveOperatorUid();
|
|
77158
|
+
if (uid !== undefined)
|
|
77159
|
+
chownSync6(configPath, uid, uid);
|
|
77160
|
+
}
|
|
77161
|
+
} catch {}
|
|
77162
|
+
}
|
|
76980
77163
|
};
|
|
76981
77164
|
process.stdout.write(`Rolling ${requested.length} agent(s) to ${target}\u2026
|
|
76982
77165
|
`);
|
|
76983
|
-
const result = executeRollout(steps, target, deps
|
|
77166
|
+
const result = executeRollout(steps, target, deps);
|
|
76984
77167
|
for (const w of result.warnings)
|
|
76985
77168
|
process.stderr.write(`\u26a0\ufe0f ${w}
|
|
76986
77169
|
`);
|
|
@@ -77011,7 +77194,7 @@ init_source();
|
|
|
77011
77194
|
init_helpers();
|
|
77012
77195
|
init_lifecycle();
|
|
77013
77196
|
import { execSync as execSync4 } from "node:child_process";
|
|
77014
|
-
import { existsSync as existsSync59, readFileSync as
|
|
77197
|
+
import { existsSync as existsSync59, readFileSync as readFileSync54 } from "node:fs";
|
|
77015
77198
|
import { dirname as dirname15, join as join61 } from "node:path";
|
|
77016
77199
|
function getClaudeCodeVersion() {
|
|
77017
77200
|
try {
|
|
@@ -77065,7 +77248,7 @@ function locateSwitchroomInstallDir() {
|
|
|
77065
77248
|
const pkgPath = join61(dir, "package.json");
|
|
77066
77249
|
if (existsSync59(pkgPath)) {
|
|
77067
77250
|
try {
|
|
77068
|
-
const pkg = JSON.parse(
|
|
77251
|
+
const pkg = JSON.parse(readFileSync54(pkgPath, "utf-8"));
|
|
77069
77252
|
if (pkg.name === "switchroom" && existsSync59(join61(dir, ".git"))) {
|
|
77070
77253
|
return dir;
|
|
77071
77254
|
}
|
|
@@ -77296,9 +77479,9 @@ import {
|
|
|
77296
77479
|
mkdirSync as mkdirSync33,
|
|
77297
77480
|
openSync as openSync11,
|
|
77298
77481
|
readdirSync as readdirSync22,
|
|
77299
|
-
readFileSync as
|
|
77482
|
+
readFileSync as readFileSync55,
|
|
77300
77483
|
renameSync as renameSync12,
|
|
77301
|
-
statSync as
|
|
77484
|
+
statSync as statSync28,
|
|
77302
77485
|
unlinkSync as unlinkSync11,
|
|
77303
77486
|
writeFileSync as writeFileSync28,
|
|
77304
77487
|
writeSync as writeSync7
|
|
@@ -77706,7 +77889,7 @@ function readAll(stateDir) {
|
|
|
77706
77889
|
return [];
|
|
77707
77890
|
let raw;
|
|
77708
77891
|
try {
|
|
77709
|
-
raw =
|
|
77892
|
+
raw = readFileSync55(path4, "utf-8");
|
|
77710
77893
|
} catch {
|
|
77711
77894
|
return [];
|
|
77712
77895
|
}
|
|
@@ -77872,7 +78055,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
77872
78055
|
continue;
|
|
77873
78056
|
const tmpPath = join62(stateDir, entry);
|
|
77874
78057
|
try {
|
|
77875
|
-
const stat =
|
|
78058
|
+
const stat = statSync28(tmpPath);
|
|
77876
78059
|
if (stat.mtimeMs < cutoff) {
|
|
77877
78060
|
unlinkSync11(tmpPath);
|
|
77878
78061
|
}
|
|
@@ -77917,7 +78100,7 @@ function withLock(stateDir, fn) {
|
|
|
77917
78100
|
function tryStealStaleLock(lockPath) {
|
|
77918
78101
|
let pidStr;
|
|
77919
78102
|
try {
|
|
77920
|
-
pidStr =
|
|
78103
|
+
pidStr = readFileSync55(lockPath, "utf-8").trim();
|
|
77921
78104
|
} catch {
|
|
77922
78105
|
return true;
|
|
77923
78106
|
}
|
|
@@ -78174,7 +78357,7 @@ import { createHash as createHash11 } from "node:crypto";
|
|
|
78174
78357
|
import {
|
|
78175
78358
|
existsSync as existsSync61,
|
|
78176
78359
|
mkdirSync as mkdirSync34,
|
|
78177
|
-
readFileSync as
|
|
78360
|
+
readFileSync as readFileSync56,
|
|
78178
78361
|
rmSync as rmSync13,
|
|
78179
78362
|
writeFileSync as writeFileSync29
|
|
78180
78363
|
} from "node:fs";
|
|
@@ -78194,7 +78377,7 @@ function defaultPythonCacheRoot() {
|
|
|
78194
78377
|
return join63(homedir37(), ".switchroom", "deps", "python");
|
|
78195
78378
|
}
|
|
78196
78379
|
function hashFile(path4) {
|
|
78197
|
-
return createHash11("sha256").update(
|
|
78380
|
+
return createHash11("sha256").update(readFileSync56(path4)).digest("hex");
|
|
78198
78381
|
}
|
|
78199
78382
|
function ensurePythonEnv(opts) {
|
|
78200
78383
|
const { skillName, requirementsPath, force = false } = opts;
|
|
@@ -78210,7 +78393,7 @@ function ensurePythonEnv(opts) {
|
|
|
78210
78393
|
const pipBin = join63(binDir, "pip");
|
|
78211
78394
|
const targetHash = hashFile(requirementsPath);
|
|
78212
78395
|
if (!force && existsSync61(stampPath) && existsSync61(pythonBin)) {
|
|
78213
|
-
const existingHash =
|
|
78396
|
+
const existingHash = readFileSync56(stampPath, "utf8").trim();
|
|
78214
78397
|
if (existingHash === targetHash) {
|
|
78215
78398
|
return {
|
|
78216
78399
|
skillName,
|
|
@@ -78262,7 +78445,7 @@ import {
|
|
|
78262
78445
|
copyFileSync as copyFileSync9,
|
|
78263
78446
|
existsSync as existsSync62,
|
|
78264
78447
|
mkdirSync as mkdirSync35,
|
|
78265
|
-
readFileSync as
|
|
78448
|
+
readFileSync as readFileSync57,
|
|
78266
78449
|
rmSync as rmSync14,
|
|
78267
78450
|
writeFileSync as writeFileSync30
|
|
78268
78451
|
} from "node:fs";
|
|
@@ -78297,7 +78480,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
78297
78480
|
const hasher = createHash12("sha256");
|
|
78298
78481
|
hasher.update(`package.json
|
|
78299
78482
|
`);
|
|
78300
|
-
hasher.update(
|
|
78483
|
+
hasher.update(readFileSync57(packageJsonPath));
|
|
78301
78484
|
for (const lockName of ALL_LOCKFILES) {
|
|
78302
78485
|
const lockPath = join64(sourceDir, lockName);
|
|
78303
78486
|
if (existsSync62(lockPath)) {
|
|
@@ -78306,7 +78489,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
78306
78489
|
hasher.update(lockName);
|
|
78307
78490
|
hasher.update(`
|
|
78308
78491
|
`);
|
|
78309
|
-
hasher.update(
|
|
78492
|
+
hasher.update(readFileSync57(lockPath));
|
|
78310
78493
|
}
|
|
78311
78494
|
}
|
|
78312
78495
|
return hasher.digest("hex");
|
|
@@ -78325,7 +78508,7 @@ function ensureNodeEnv(opts) {
|
|
|
78325
78508
|
const binDir = join64(nodeModulesDir, ".bin");
|
|
78326
78509
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
78327
78510
|
if (!force && existsSync62(stampPath) && existsSync62(nodeModulesDir)) {
|
|
78328
|
-
const existingHash =
|
|
78511
|
+
const existingHash = readFileSync57(stampPath, "utf8").trim();
|
|
78329
78512
|
if (existingHash === targetHash) {
|
|
78330
78513
|
return {
|
|
78331
78514
|
skillName,
|
|
@@ -79344,7 +79527,7 @@ function safeParseInt(value, fallback) {
|
|
|
79344
79527
|
init_helpers();
|
|
79345
79528
|
init_loader();
|
|
79346
79529
|
init_merge();
|
|
79347
|
-
import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as
|
|
79530
|
+
import { copyFileSync as copyFileSync10, existsSync as existsSync65, readFileSync as readFileSync58, writeFileSync as writeFileSync31 } from "node:fs";
|
|
79348
79531
|
import { join as join66, resolve as resolve40 } from "node:path";
|
|
79349
79532
|
init_schema();
|
|
79350
79533
|
function resolveSoulTargetOrExit(program3, agentName) {
|
|
@@ -79390,7 +79573,7 @@ function registerSoulCommand(program3) {
|
|
|
79390
79573
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
79391
79574
|
process.exit(1);
|
|
79392
79575
|
}
|
|
79393
|
-
process.stdout.write(
|
|
79576
|
+
process.stdout.write(readFileSync58(t.soulPath, "utf-8"));
|
|
79394
79577
|
}));
|
|
79395
79578
|
cmd.command("reset <agent>").description("Re-seed SOUL.md from the agent's current profile " + "(backs the existing file up to SOUL.md.bak first)").option("-y, --yes", "Skip the confirmation prompt").action(withConfigError(async (agentName, opts) => {
|
|
79396
79579
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -79435,7 +79618,7 @@ function registerSoulCommand(program3) {
|
|
|
79435
79618
|
// src/cli/debug.ts
|
|
79436
79619
|
init_helpers();
|
|
79437
79620
|
init_loader();
|
|
79438
|
-
import { existsSync as existsSync66, readFileSync as
|
|
79621
|
+
import { existsSync as existsSync66, readFileSync as readFileSync59, readdirSync as readdirSync23, statSync as statSync29 } from "node:fs";
|
|
79439
79622
|
import { resolve as resolve41, join as join67 } from "node:path";
|
|
79440
79623
|
import { createHash as createHash13 } from "node:crypto";
|
|
79441
79624
|
init_merge();
|
|
@@ -79451,7 +79634,7 @@ function readMcpServerNames(agentDir) {
|
|
|
79451
79634
|
if (!existsSync66(mcpPath))
|
|
79452
79635
|
return [];
|
|
79453
79636
|
try {
|
|
79454
|
-
const parsed = JSON.parse(
|
|
79637
|
+
const parsed = JSON.parse(readFileSync59(mcpPath, "utf-8"));
|
|
79455
79638
|
return Object.keys(parsed.mcpServers ?? {});
|
|
79456
79639
|
} catch {
|
|
79457
79640
|
return null;
|
|
@@ -79474,7 +79657,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
79474
79657
|
const transcriptPath = join67(projectPath, "transcript.jsonl");
|
|
79475
79658
|
if (!existsSync66(transcriptPath))
|
|
79476
79659
|
continue;
|
|
79477
|
-
const stat3 =
|
|
79660
|
+
const stat3 = statSync29(transcriptPath);
|
|
79478
79661
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
79479
79662
|
latest = { path: transcriptPath, mtime: stat3.mtimeMs };
|
|
79480
79663
|
}
|
|
@@ -79486,7 +79669,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
79486
79669
|
}
|
|
79487
79670
|
function extractLatestUserMessage(transcriptPath) {
|
|
79488
79671
|
try {
|
|
79489
|
-
const content =
|
|
79672
|
+
const content = readFileSync59(transcriptPath, "utf-8");
|
|
79490
79673
|
const lines = content.trim().split(`
|
|
79491
79674
|
`).filter(Boolean);
|
|
79492
79675
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -79590,7 +79773,7 @@ function registerDebugCommand(program3) {
|
|
|
79590
79773
|
}
|
|
79591
79774
|
console.log(`=== Append System Prompt (per-session) ===
|
|
79592
79775
|
`);
|
|
79593
|
-
const handoffContent = existsSync66(handoffPath) ?
|
|
79776
|
+
const handoffContent = existsSync66(handoffPath) ? readFileSync59(handoffPath, "utf-8") : "";
|
|
79594
79777
|
if (handoffContent.trim().length > 0) {
|
|
79595
79778
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
79596
79779
|
console.log(handoffContent);
|
|
@@ -79601,7 +79784,7 @@ function registerDebugCommand(program3) {
|
|
|
79601
79784
|
}
|
|
79602
79785
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
79603
79786
|
`);
|
|
79604
|
-
const claudeMdContent = existsSync66(claudeMdPath) ?
|
|
79787
|
+
const claudeMdContent = existsSync66(claudeMdPath) ? readFileSync59(claudeMdPath, "utf-8") : "";
|
|
79605
79788
|
if (claudeMdContent.trim().length > 0) {
|
|
79606
79789
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
79607
79790
|
console.log(claudeMdContent);
|
|
@@ -79612,7 +79795,7 @@ function registerDebugCommand(program3) {
|
|
|
79612
79795
|
}
|
|
79613
79796
|
console.log(`=== Persona (SOUL.md) ===
|
|
79614
79797
|
`);
|
|
79615
|
-
const soulMdContent = existsSync66(soulMdPath) ?
|
|
79798
|
+
const soulMdContent = existsSync66(soulMdPath) ? readFileSync59(soulMdPath, "utf-8") : existsSync66(workspaceSoulMdPath) ? readFileSync59(workspaceSoulMdPath, "utf-8") : "";
|
|
79616
79799
|
if (soulMdContent.trim().length > 0) {
|
|
79617
79800
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
79618
79801
|
console.log(soulMdContent);
|
|
@@ -79676,8 +79859,8 @@ function registerDebugCommand(program3) {
|
|
|
79676
79859
|
const fleetDir = join67(agentsDir, "..", "fleet");
|
|
79677
79860
|
const fleetInvPath = join67(fleetDir, "switchroom-invariants.md");
|
|
79678
79861
|
const fleetClaudePath = join67(fleetDir, "CLAUDE.md");
|
|
79679
|
-
const fleetInvBytes = existsSync66(fleetInvPath) ?
|
|
79680
|
-
const fleetClaudeBytes = existsSync66(fleetClaudePath) ?
|
|
79862
|
+
const fleetInvBytes = existsSync66(fleetInvPath) ? readFileSync59(fleetInvPath, "utf-8").length : 0;
|
|
79863
|
+
const fleetClaudeBytes = existsSync66(fleetClaudePath) ? readFileSync59(fleetClaudePath, "utf-8").length : 0;
|
|
79681
79864
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
79682
79865
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
79683
79866
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -79719,7 +79902,7 @@ import { randomBytes as randomBytes13 } from "node:crypto";
|
|
|
79719
79902
|
import {
|
|
79720
79903
|
mkdirSync as mkdirSync36,
|
|
79721
79904
|
writeFileSync as writeFileSync32,
|
|
79722
|
-
readFileSync as
|
|
79905
|
+
readFileSync as readFileSync60,
|
|
79723
79906
|
readdirSync as readdirSync24,
|
|
79724
79907
|
unlinkSync as unlinkSync12,
|
|
79725
79908
|
existsSync as existsSync67,
|
|
@@ -79747,7 +79930,7 @@ function writeRecord(record2) {
|
|
|
79747
79930
|
function readRecord(id) {
|
|
79748
79931
|
const path7 = recordPath(id);
|
|
79749
79932
|
try {
|
|
79750
|
-
const raw =
|
|
79933
|
+
const raw = readFileSync60(path7, "utf8");
|
|
79751
79934
|
return JSON.parse(raw);
|
|
79752
79935
|
} catch {
|
|
79753
79936
|
return null;
|
|
@@ -80814,7 +80997,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
80814
80997
|
|
|
80815
80998
|
// src/cli/deliver-file.ts
|
|
80816
80999
|
init_client2();
|
|
80817
|
-
import { readFileSync as
|
|
81000
|
+
import { readFileSync as readFileSync61, statSync as statSync30 } from "node:fs";
|
|
80818
81001
|
import { basename as basename8 } from "node:path";
|
|
80819
81002
|
|
|
80820
81003
|
// src/delivery/onedrive.ts
|
|
@@ -81153,8 +81336,8 @@ async function defaultResolveProvider() {
|
|
|
81153
81336
|
}
|
|
81154
81337
|
async function runDeliverFile(localPath, deps = {}) {
|
|
81155
81338
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
81156
|
-
const sizeOf = deps.fileSize ?? ((p) =>
|
|
81157
|
-
const read = deps.readFile ?? ((p) => new Uint8Array(
|
|
81339
|
+
const sizeOf = deps.fileSize ?? ((p) => statSync30(p).size);
|
|
81340
|
+
const read = deps.readFile ?? ((p) => new Uint8Array(readFileSync61(p)));
|
|
81158
81341
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
81159
81342
|
let size;
|
|
81160
81343
|
try {
|
|
@@ -81444,7 +81627,7 @@ async function fetchToken(vaultKey) {
|
|
|
81444
81627
|
|
|
81445
81628
|
// src/cli/apply.ts
|
|
81446
81629
|
init_source();
|
|
81447
|
-
import { accessSync as accessSync3, chownSync as
|
|
81630
|
+
import { accessSync as accessSync3, chownSync as chownSync7, constants as fsConstants6, copyFileSync as copyFileSync11, existsSync as existsSync74, mkdirSync as mkdirSync42, readFileSync as readFileSync63, readdirSync as readdirSync26, renameSync as renameSync14, writeFileSync as writeFileSync37 } from "node:fs";
|
|
81448
81631
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
81449
81632
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
81450
81633
|
import readline from "node:readline";
|
|
@@ -81841,7 +82024,7 @@ init_loader();
|
|
|
81841
82024
|
init_loader();
|
|
81842
82025
|
|
|
81843
82026
|
// src/cli/update-prompt-hook.ts
|
|
81844
|
-
import { existsSync as existsSync72, readFileSync as
|
|
82027
|
+
import { existsSync as existsSync72, readFileSync as readFileSync62, writeFileSync as writeFileSync36, chmodSync as chmodSync10, mkdirSync as mkdirSync41 } from "node:fs";
|
|
81845
82028
|
import { join as join72 } from "node:path";
|
|
81846
82029
|
var HOOK_FILENAME = "update-card-on-prompt.sh";
|
|
81847
82030
|
function updatePromptHookScript() {
|
|
@@ -81913,7 +82096,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
81913
82096
|
const scriptPath = join72(hooksDir, HOOK_FILENAME);
|
|
81914
82097
|
const desired = updatePromptHookScript();
|
|
81915
82098
|
let installed = false;
|
|
81916
|
-
const existing = existsSync72(scriptPath) ?
|
|
82099
|
+
const existing = existsSync72(scriptPath) ? readFileSync62(scriptPath, "utf-8") : "";
|
|
81917
82100
|
if (existing !== desired) {
|
|
81918
82101
|
writeFileSync36(scriptPath, desired, { mode: 493 });
|
|
81919
82102
|
chmodSync10(scriptPath, 493);
|
|
@@ -81927,7 +82110,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
81927
82110
|
if (!existsSync72(settingsPath)) {
|
|
81928
82111
|
return { scriptPath, settingsPath, installed };
|
|
81929
82112
|
}
|
|
81930
|
-
const raw =
|
|
82113
|
+
const raw = readFileSync62(settingsPath, "utf-8");
|
|
81931
82114
|
let parsed;
|
|
81932
82115
|
try {
|
|
81933
82116
|
parsed = JSON.parse(raw);
|
|
@@ -82104,14 +82287,14 @@ async function ensureHostMountSources(config) {
|
|
|
82104
82287
|
}
|
|
82105
82288
|
try {
|
|
82106
82289
|
const uid = allocateAgentUid(name);
|
|
82107
|
-
|
|
82290
|
+
chownSync7(tokenPath, uid, uid);
|
|
82108
82291
|
} catch {}
|
|
82109
82292
|
}
|
|
82110
82293
|
const fleetDir = join74(home2, ".switchroom", "fleet");
|
|
82111
82294
|
await mkdir2(fleetDir, { recursive: true });
|
|
82112
82295
|
const invariantsPath = join74(fleetDir, "switchroom-invariants.md");
|
|
82113
82296
|
const invariantsCanonical = renderFleetInvariants();
|
|
82114
|
-
const invariantsCurrent = existsSync74(invariantsPath) ?
|
|
82297
|
+
const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync63(invariantsPath, "utf-8") : null;
|
|
82115
82298
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
82116
82299
|
writeFileSync37(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
82117
82300
|
}
|
|
@@ -82636,7 +82819,7 @@ function runRedactStdin() {
|
|
|
82636
82819
|
}
|
|
82637
82820
|
|
|
82638
82821
|
// src/cli/status-ask.ts
|
|
82639
|
-
import { readFileSync as
|
|
82822
|
+
import { readFileSync as readFileSync64, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
|
|
82640
82823
|
import { join as join75 } from "node:path";
|
|
82641
82824
|
import { homedir as homedir44 } from "node:os";
|
|
82642
82825
|
|
|
@@ -82912,7 +83095,7 @@ function runReport(opts) {
|
|
|
82912
83095
|
for (const src of sources) {
|
|
82913
83096
|
let content;
|
|
82914
83097
|
try {
|
|
82915
|
-
content =
|
|
83098
|
+
content = readFileSync64(src.path, "utf-8");
|
|
82916
83099
|
} catch (err) {
|
|
82917
83100
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
82918
83101
|
`);
|
|
@@ -83006,7 +83189,7 @@ function inferAgentFromPath(p) {
|
|
|
83006
83189
|
}
|
|
83007
83190
|
|
|
83008
83191
|
// src/cli/agent-config-write.ts
|
|
83009
|
-
var
|
|
83192
|
+
var import_yaml20 = __toESM(require_dist(), 1);
|
|
83010
83193
|
|
|
83011
83194
|
// src/config/overlay-writer.ts
|
|
83012
83195
|
init_paths();
|
|
@@ -83017,9 +83200,9 @@ import {
|
|
|
83017
83200
|
mkdirSync as mkdirSync43,
|
|
83018
83201
|
openSync as openSync13,
|
|
83019
83202
|
readdirSync as readdirSync28,
|
|
83020
|
-
readFileSync as
|
|
83203
|
+
readFileSync as readFileSync65,
|
|
83021
83204
|
renameSync as renameSync15,
|
|
83022
|
-
statSync as
|
|
83205
|
+
statSync as statSync31,
|
|
83023
83206
|
unlinkSync as unlinkSync14,
|
|
83024
83207
|
writeSync as writeSync8
|
|
83025
83208
|
} from "node:fs";
|
|
@@ -83063,7 +83246,7 @@ function withAgentLock(paths, fn) {
|
|
|
83063
83246
|
if (e.code !== "EEXIST")
|
|
83064
83247
|
throw err;
|
|
83065
83248
|
try {
|
|
83066
|
-
const age = Date.now() -
|
|
83249
|
+
const age = Date.now() - statSync31(paths.lockPath).mtimeMs;
|
|
83067
83250
|
if (age > 30000) {
|
|
83068
83251
|
unlinkSync14(paths.lockPath);
|
|
83069
83252
|
continue;
|
|
@@ -83141,7 +83324,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
83141
83324
|
continue;
|
|
83142
83325
|
const full = join76(paths.skillsDir, name);
|
|
83143
83326
|
try {
|
|
83144
|
-
const raw =
|
|
83327
|
+
const raw = readFileSync65(full, "utf-8");
|
|
83145
83328
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
83146
83329
|
out.push({ slug, path: full, raw });
|
|
83147
83330
|
} catch {}
|
|
@@ -83168,7 +83351,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
83168
83351
|
continue;
|
|
83169
83352
|
const full = join76(paths.scheduleDir, name);
|
|
83170
83353
|
try {
|
|
83171
|
-
const raw =
|
|
83354
|
+
const raw = readFileSync65(full, "utf-8");
|
|
83172
83355
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
83173
83356
|
out.push({ slug, path: full, raw });
|
|
83174
83357
|
} catch {}
|
|
@@ -83199,7 +83382,7 @@ function filterOverlaySecrets(doc, source) {
|
|
|
83199
83382
|
// src/agents/reconcile-dry-run.ts
|
|
83200
83383
|
init_overlay_schema();
|
|
83201
83384
|
init_schema();
|
|
83202
|
-
var
|
|
83385
|
+
var import_yaml19 = __toESM(require_dist(), 1);
|
|
83203
83386
|
init_lifecycle();
|
|
83204
83387
|
var MIN_CRON_INTERVAL_SECS = 5 * 60;
|
|
83205
83388
|
function violatesMinInterval(cron) {
|
|
@@ -83221,7 +83404,7 @@ function violatesMinInterval(cron) {
|
|
|
83221
83404
|
function dryRunReconcile(input) {
|
|
83222
83405
|
let parsed;
|
|
83223
83406
|
try {
|
|
83224
|
-
parsed =
|
|
83407
|
+
parsed = import_yaml19.parse(input.yamlText);
|
|
83225
83408
|
} catch (err) {
|
|
83226
83409
|
return {
|
|
83227
83410
|
ok: false,
|
|
@@ -83316,7 +83499,7 @@ import {
|
|
|
83316
83499
|
mkdirSync as mkdirSync44,
|
|
83317
83500
|
openSync as openSync14,
|
|
83318
83501
|
readdirSync as readdirSync29,
|
|
83319
|
-
readFileSync as
|
|
83502
|
+
readFileSync as readFileSync66,
|
|
83320
83503
|
renameSync as renameSync16,
|
|
83321
83504
|
unlinkSync as unlinkSync15,
|
|
83322
83505
|
writeFileSync as writeFileSync38,
|
|
@@ -83380,7 +83563,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
83380
83563
|
if (!existsSync77(yamlPath))
|
|
83381
83564
|
continue;
|
|
83382
83565
|
try {
|
|
83383
|
-
const meta = JSON.parse(
|
|
83566
|
+
const meta = JSON.parse(readFileSync66(metaPath, "utf-8"));
|
|
83384
83567
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
83385
83568
|
continue;
|
|
83386
83569
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -83418,7 +83601,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
83418
83601
|
}
|
|
83419
83602
|
|
|
83420
83603
|
// src/cli/agent-config-write.ts
|
|
83421
|
-
import { existsSync as existsSync78, readFileSync as
|
|
83604
|
+
import { existsSync as existsSync78, readFileSync as readFileSync67 } from "node:fs";
|
|
83422
83605
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
83423
83606
|
|
|
83424
83607
|
// src/scheduler/schedule-report.ts
|
|
@@ -83607,7 +83790,7 @@ function scheduleAdd(opts) {
|
|
|
83607
83790
|
]
|
|
83608
83791
|
};
|
|
83609
83792
|
const yamlText = (() => {
|
|
83610
|
-
const body =
|
|
83793
|
+
const body = import_yaml20.stringify(doc);
|
|
83611
83794
|
const header = opts.name ? `# name: ${opts.name}
|
|
83612
83795
|
` : "";
|
|
83613
83796
|
return header + body;
|
|
@@ -83721,7 +83904,7 @@ function scheduleAddOrStage(opts) {
|
|
|
83721
83904
|
]
|
|
83722
83905
|
};
|
|
83723
83906
|
const yamlText = (opts.name ? `# name: ${opts.name}
|
|
83724
|
-
` : "") +
|
|
83907
|
+
` : "") + import_yaml20.stringify(doc);
|
|
83725
83908
|
const summary = (() => {
|
|
83726
83909
|
const parts = [`cron=${opts.cronExpr}`];
|
|
83727
83910
|
if (opts.secrets?.length)
|
|
@@ -83771,7 +83954,7 @@ function scheduleRemove(opts) {
|
|
|
83771
83954
|
break;
|
|
83772
83955
|
}
|
|
83773
83956
|
try {
|
|
83774
|
-
const parsed =
|
|
83957
|
+
const parsed = import_yaml20.parse(e.raw);
|
|
83775
83958
|
if (parsed && parsed.name === opts.name) {
|
|
83776
83959
|
match = e;
|
|
83777
83960
|
break;
|
|
@@ -83790,7 +83973,7 @@ function scheduleRemove(opts) {
|
|
|
83790
83973
|
let priorContent = null;
|
|
83791
83974
|
try {
|
|
83792
83975
|
if (existsSync78(match.path))
|
|
83793
|
-
priorContent =
|
|
83976
|
+
priorContent = readFileSync67(match.path, "utf-8");
|
|
83794
83977
|
} catch {}
|
|
83795
83978
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
83796
83979
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -83993,7 +84176,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
83993
84176
|
}
|
|
83994
84177
|
let blob;
|
|
83995
84178
|
if (opts.jsonl) {
|
|
83996
|
-
blob = existsSync78(opts.jsonl) ?
|
|
84179
|
+
blob = existsSync78(opts.jsonl) ? readFileSync67(opts.jsonl, "utf-8") : "";
|
|
83997
84180
|
} else {
|
|
83998
84181
|
try {
|
|
83999
84182
|
blob = execFileSync25("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -84024,10 +84207,10 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
84024
84207
|
}
|
|
84025
84208
|
|
|
84026
84209
|
// src/cli/agent-config-skill-write.ts
|
|
84027
|
-
var
|
|
84210
|
+
var import_yaml21 = __toESM(require_dist(), 1);
|
|
84028
84211
|
import { existsSync as existsSync79 } from "node:fs";
|
|
84029
84212
|
init_reconcile_default_skills();
|
|
84030
|
-
var
|
|
84213
|
+
var import_yaml22 = __toESM(require_dist(), 1);
|
|
84031
84214
|
import { join as join78 } from "node:path";
|
|
84032
84215
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
84033
84216
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
@@ -84077,7 +84260,7 @@ function countCurrentSkills(agent, opts) {
|
|
|
84077
84260
|
let total = 0;
|
|
84078
84261
|
for (const e of entries) {
|
|
84079
84262
|
try {
|
|
84080
|
-
const doc =
|
|
84263
|
+
const doc = import_yaml22.parse(e.raw);
|
|
84081
84264
|
total += (doc?.skills ?? []).length;
|
|
84082
84265
|
} catch {}
|
|
84083
84266
|
}
|
|
@@ -84107,7 +84290,7 @@ function skillInstall(opts) {
|
|
|
84107
84290
|
if (!existsSync79(skillPath)) {
|
|
84108
84291
|
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.`);
|
|
84109
84292
|
}
|
|
84110
|
-
const yamlText =
|
|
84293
|
+
const yamlText = import_yaml21.stringify({ skills: [skillName] });
|
|
84111
84294
|
let path8;
|
|
84112
84295
|
try {
|
|
84113
84296
|
path8 = writeSkillsOverlayEntry(agent, slug, yamlText, { root: opts.root });
|
|
@@ -84273,12 +84456,12 @@ import {
|
|
|
84273
84456
|
mkdirSync as mkdirSync45,
|
|
84274
84457
|
mkdtempSync as mkdtempSync5,
|
|
84275
84458
|
openSync as openSync15,
|
|
84276
|
-
readFileSync as
|
|
84459
|
+
readFileSync as readFileSync68,
|
|
84277
84460
|
readdirSync as readdirSync30,
|
|
84278
84461
|
realpathSync as realpathSync7,
|
|
84279
84462
|
renameSync as renameSync17,
|
|
84280
84463
|
rmSync as rmSync16,
|
|
84281
|
-
statSync as
|
|
84464
|
+
statSync as statSync32,
|
|
84282
84465
|
writeFileSync as writeFileSync39
|
|
84283
84466
|
} from "node:fs";
|
|
84284
84467
|
import { tmpdir as tmpdir5, homedir as homedir45 } from "node:os";
|
|
@@ -84286,7 +84469,7 @@ import { dirname as dirname23, join as join79, relative as relative2, resolve as
|
|
|
84286
84469
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
84287
84470
|
|
|
84288
84471
|
// src/cli/skill-common.ts
|
|
84289
|
-
var
|
|
84472
|
+
var import_yaml23 = __toESM(require_dist(), 1);
|
|
84290
84473
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
84291
84474
|
var MAX_SKILL_BYTES = 2 * 1024 * 1024;
|
|
84292
84475
|
var MAX_FILES_PER_SKILL = 50;
|
|
@@ -84390,7 +84573,7 @@ function validateSkillMd(content, expectedName) {
|
|
|
84390
84573
|
}
|
|
84391
84574
|
let parsed;
|
|
84392
84575
|
try {
|
|
84393
|
-
parsed =
|
|
84576
|
+
parsed = import_yaml23.parse(fmText);
|
|
84394
84577
|
} catch (e) {
|
|
84395
84578
|
return authorErr("E_SKILL_INVALID_FRONTMATTER", `frontmatter is not valid YAML: ${e.message}`);
|
|
84396
84579
|
}
|
|
@@ -84508,7 +84691,7 @@ function isTarballPath(p) {
|
|
|
84508
84691
|
}
|
|
84509
84692
|
function loadFromDir(dir) {
|
|
84510
84693
|
const abs = realpathSync7(dir);
|
|
84511
|
-
if (!
|
|
84694
|
+
if (!statSync32(abs).isDirectory()) {
|
|
84512
84695
|
fail3(`--from path is not a directory: ${dir}`);
|
|
84513
84696
|
}
|
|
84514
84697
|
const files = {};
|
|
@@ -84525,7 +84708,7 @@ function loadFromDir(dir) {
|
|
|
84525
84708
|
continue;
|
|
84526
84709
|
}
|
|
84527
84710
|
if (ent.isFile()) {
|
|
84528
|
-
const buf =
|
|
84711
|
+
const buf = readFileSync68(full);
|
|
84529
84712
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
84530
84713
|
}
|
|
84531
84714
|
}
|
|
@@ -84574,7 +84757,7 @@ function loadFromTarball(tarPath) {
|
|
|
84574
84757
|
}
|
|
84575
84758
|
}
|
|
84576
84759
|
function loadSingleFile(filePath) {
|
|
84577
|
-
const content =
|
|
84760
|
+
const content = readFileSync68(filePath, "utf-8");
|
|
84578
84761
|
return { "SKILL.md": content };
|
|
84579
84762
|
}
|
|
84580
84763
|
function loadFromStdin() {
|
|
@@ -84665,7 +84848,7 @@ function diffSummary(currentDir, files) {
|
|
|
84665
84848
|
if (ent.isDirectory()) {
|
|
84666
84849
|
walk2(full);
|
|
84667
84850
|
} else if (ent.isFile()) {
|
|
84668
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
84851
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync68(full, "utf-8");
|
|
84669
84852
|
}
|
|
84670
84853
|
}
|
|
84671
84854
|
};
|
|
@@ -84768,7 +84951,7 @@ function registerSkillCommand(program3) {
|
|
|
84768
84951
|
if (!existsSync80(fromPath)) {
|
|
84769
84952
|
fail3(`--from path does not exist: ${opts.from}`);
|
|
84770
84953
|
}
|
|
84771
|
-
const st =
|
|
84954
|
+
const st = statSync32(fromPath);
|
|
84772
84955
|
if (st.isDirectory()) {
|
|
84773
84956
|
files = loadFromDir(fromPath);
|
|
84774
84957
|
} else if (isTarballPath(fromPath)) {
|
|
@@ -84825,11 +85008,11 @@ import {
|
|
|
84825
85008
|
mkdirSync as mkdirSync46,
|
|
84826
85009
|
mkdtempSync as mkdtempSync6,
|
|
84827
85010
|
openSync as openSync16,
|
|
84828
|
-
readFileSync as
|
|
85011
|
+
readFileSync as readFileSync69,
|
|
84829
85012
|
readdirSync as readdirSync31,
|
|
84830
85013
|
renameSync as renameSync18,
|
|
84831
85014
|
rmSync as rmSync17,
|
|
84832
|
-
statSync as
|
|
85015
|
+
statSync as statSync33,
|
|
84833
85016
|
utimesSync,
|
|
84834
85017
|
writeFileSync as writeFileSync40
|
|
84835
85018
|
} from "node:fs";
|
|
@@ -84907,7 +85090,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
84907
85090
|
if (ent.isDirectory())
|
|
84908
85091
|
walk2(s, d);
|
|
84909
85092
|
else if (ent.isFile()) {
|
|
84910
|
-
writeFileSync40(d,
|
|
85093
|
+
writeFileSync40(d, readFileSync69(s));
|
|
84911
85094
|
}
|
|
84912
85095
|
}
|
|
84913
85096
|
};
|
|
@@ -84970,7 +85153,7 @@ function readStdinSync2() {
|
|
|
84970
85153
|
}
|
|
84971
85154
|
function loadFromDir2(dir) {
|
|
84972
85155
|
const abs = resolve48(dir);
|
|
84973
|
-
if (!
|
|
85156
|
+
if (!statSync33(abs).isDirectory()) {
|
|
84974
85157
|
fail4(`--from path is not a directory: ${dir}`);
|
|
84975
85158
|
}
|
|
84976
85159
|
const files = {};
|
|
@@ -84986,7 +85169,7 @@ function loadFromDir2(dir) {
|
|
|
84986
85169
|
}
|
|
84987
85170
|
if (ent.isFile()) {
|
|
84988
85171
|
const rel = relative3(abs, full).replace(/\\/g, "/");
|
|
84989
|
-
files[rel] =
|
|
85172
|
+
files[rel] = readFileSync69(full, "utf-8");
|
|
84990
85173
|
}
|
|
84991
85174
|
}
|
|
84992
85175
|
};
|
|
@@ -85056,7 +85239,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
85056
85239
|
continue;
|
|
85057
85240
|
const entPath = join80(trash, ent.name);
|
|
85058
85241
|
try {
|
|
85059
|
-
const st =
|
|
85242
|
+
const st = statSync33(entPath);
|
|
85060
85243
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
85061
85244
|
rmSync17(entPath, { recursive: true, force: true });
|
|
85062
85245
|
}
|
|
@@ -85167,12 +85350,12 @@ function loadFiles(opts) {
|
|
|
85167
85350
|
if (!existsSync81(p)) {
|
|
85168
85351
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
85169
85352
|
}
|
|
85170
|
-
const st =
|
|
85353
|
+
const st = statSync33(p);
|
|
85171
85354
|
if (st.isDirectory()) {
|
|
85172
85355
|
return loadFromDir2(p);
|
|
85173
85356
|
}
|
|
85174
85357
|
if (p.endsWith(".md")) {
|
|
85175
|
-
return { "SKILL.md":
|
|
85358
|
+
return { "SKILL.md": readFileSync69(p, "utf-8") };
|
|
85176
85359
|
}
|
|
85177
85360
|
fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
85178
85361
|
}
|
|
@@ -85261,7 +85444,7 @@ function readSourceFiles(dir) {
|
|
|
85261
85444
|
fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
85262
85445
|
}
|
|
85263
85446
|
} catch {}
|
|
85264
|
-
files[rel] =
|
|
85447
|
+
files[rel] = readFileSync69(full, "utf-8");
|
|
85265
85448
|
}
|
|
85266
85449
|
}
|
|
85267
85450
|
};
|
|
@@ -85388,7 +85571,7 @@ function listPersonalAction(opts) {
|
|
|
85388
85571
|
if (e.isFile()) {
|
|
85389
85572
|
fileCount += 1;
|
|
85390
85573
|
try {
|
|
85391
|
-
totalBytes +=
|
|
85574
|
+
totalBytes += statSync33(join80(sub, e.name)).size;
|
|
85392
85575
|
} catch {}
|
|
85393
85576
|
} else if (e.isDirectory()) {
|
|
85394
85577
|
walk2(join80(sub, e.name));
|
|
@@ -85429,8 +85612,8 @@ function registerSkillPersonalCommands(program3) {
|
|
|
85429
85612
|
|
|
85430
85613
|
// src/cli/skill-search.ts
|
|
85431
85614
|
init_helpers();
|
|
85432
|
-
var
|
|
85433
|
-
import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as
|
|
85615
|
+
var import_yaml24 = __toESM(require_dist(), 1);
|
|
85616
|
+
import { existsSync as existsSync82, readdirSync as readdirSync32, readFileSync as readFileSync70, statSync as statSync34 } from "node:fs";
|
|
85434
85617
|
import { homedir as homedir47 } from "node:os";
|
|
85435
85618
|
import { join as join81, resolve as resolve49 } from "node:path";
|
|
85436
85619
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -85451,7 +85634,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
85451
85634
|
return null;
|
|
85452
85635
|
let content;
|
|
85453
85636
|
try {
|
|
85454
|
-
content =
|
|
85637
|
+
content = readFileSync70(mdPath, "utf-8");
|
|
85455
85638
|
} catch {
|
|
85456
85639
|
return null;
|
|
85457
85640
|
}
|
|
@@ -85469,7 +85652,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
85469
85652
|
const fmText = rest.slice(0, endIdx);
|
|
85470
85653
|
let parsed;
|
|
85471
85654
|
try {
|
|
85472
|
-
parsed =
|
|
85655
|
+
parsed = import_yaml24.parse(fmText);
|
|
85473
85656
|
} catch (e) {
|
|
85474
85657
|
return { error: `yaml parse: ${e.message}` };
|
|
85475
85658
|
}
|
|
@@ -85481,7 +85664,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
85481
85664
|
function statSkillMd(skillDir) {
|
|
85482
85665
|
const mdPath = join81(skillDir, "SKILL.md");
|
|
85483
85666
|
try {
|
|
85484
|
-
const st =
|
|
85667
|
+
const st = statSync34(mdPath);
|
|
85485
85668
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
85486
85669
|
} catch {
|
|
85487
85670
|
return null;
|
|
@@ -85505,7 +85688,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
85505
85688
|
continue;
|
|
85506
85689
|
const dirPath = join81(skillsDir, ent);
|
|
85507
85690
|
try {
|
|
85508
|
-
if (!
|
|
85691
|
+
if (!statSync34(dirPath).isDirectory())
|
|
85509
85692
|
continue;
|
|
85510
85693
|
} catch {
|
|
85511
85694
|
continue;
|
|
@@ -85545,7 +85728,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
85545
85728
|
continue;
|
|
85546
85729
|
const dirPath = join81(sharedRoot, ent);
|
|
85547
85730
|
try {
|
|
85548
|
-
if (!
|
|
85731
|
+
if (!statSync34(dirPath).isDirectory())
|
|
85549
85732
|
continue;
|
|
85550
85733
|
} catch {
|
|
85551
85734
|
continue;
|
|
@@ -85581,7 +85764,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
85581
85764
|
continue;
|
|
85582
85765
|
const dirPath = join81(bundledRoot, ent);
|
|
85583
85766
|
try {
|
|
85584
|
-
if (!
|
|
85767
|
+
if (!statSync34(dirPath).isDirectory())
|
|
85585
85768
|
continue;
|
|
85586
85769
|
} catch {
|
|
85587
85770
|
continue;
|
|
@@ -85723,7 +85906,7 @@ function registerHostdMcpCommand(program3) {
|
|
|
85723
85906
|
// src/cli/hostd.ts
|
|
85724
85907
|
init_source();
|
|
85725
85908
|
init_helpers();
|
|
85726
|
-
import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as
|
|
85909
|
+
import { existsSync as existsSync84, mkdirSync as mkdirSync47, readdirSync as readdirSync33, readFileSync as readFileSync72, writeFileSync as writeFileSync41, statSync as statSync35, copyFileSync as copyFileSync12 } from "node:fs";
|
|
85727
85910
|
import { homedir as homedir48 } from "node:os";
|
|
85728
85911
|
import { join as join82 } from "node:path";
|
|
85729
85912
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
@@ -85959,7 +86142,7 @@ function doStatus() {
|
|
|
85959
86142
|
continue;
|
|
85960
86143
|
const sockPath = join82(dir, name, "sock");
|
|
85961
86144
|
if (existsSync84(sockPath)) {
|
|
85962
|
-
const st =
|
|
86145
|
+
const st = statSync35(sockPath);
|
|
85963
86146
|
if ((st.mode & 61440) === 49152) {
|
|
85964
86147
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
85965
86148
|
}
|
|
@@ -86006,7 +86189,7 @@ function registerHostdCommand(program3) {
|
|
|
86006
86189
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
86007
86190
|
return;
|
|
86008
86191
|
}
|
|
86009
|
-
const raw =
|
|
86192
|
+
const raw = readFileSync72(logPath, "utf-8");
|
|
86010
86193
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
86011
86194
|
const filters = {
|
|
86012
86195
|
agent: opts.agent,
|