switchroom 0.15.18 → 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 +521 -336
- package/package.json +1 -1
- package/profiles/_shared/agent-self-service.md.hbs +24 -15
- package/telegram-plugin/dist/gateway/gateway.js +304 -6
- package/telegram-plugin/gateway/effort-command.ts +272 -0
- package/telegram-plugin/gateway/gateway.ts +199 -2
- package/telegram-plugin/gateway/grant-restart.ts +30 -0
- package/telegram-plugin/tests/effort-command.test.ts +191 -0
- package/telegram-plugin/tests/grant-restart.test.ts +38 -0
- package/telegram-plugin/welcome-text.ts +7 -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";
|
|
@@ -25739,6 +25931,7 @@ var init_inject = __esm(() => {
|
|
|
25739
25931
|
["/hooks", { description: "List configured hooks", expectsOutput: true }],
|
|
25740
25932
|
["/memory", { description: "Open memory picker", expectsOutput: true }],
|
|
25741
25933
|
["/model", { description: "Open model picker", expectsOutput: true }],
|
|
25934
|
+
["/effort", { description: "Set reasoning effort", expectsOutput: true }],
|
|
25742
25935
|
[
|
|
25743
25936
|
"/clear",
|
|
25744
25937
|
{ description: "Clear session screen", expectsOutput: false }
|
|
@@ -28405,198 +28598,6 @@ var init_via_claude = __esm(() => {
|
|
|
28405
28598
|
];
|
|
28406
28599
|
});
|
|
28407
28600
|
|
|
28408
|
-
// src/vault/broker/acl.ts
|
|
28409
|
-
function isWebkiteCredentialKeyForAgent(agentConfig, key) {
|
|
28410
|
-
if (!WEBKITE_VAULT_KEYS.has(key))
|
|
28411
|
-
return false;
|
|
28412
|
-
if ((agentConfig?.mcp_servers ?? {})["webkite"] === false)
|
|
28413
|
-
return false;
|
|
28414
|
-
return true;
|
|
28415
|
-
}
|
|
28416
|
-
function parseCronUnit(unitName) {
|
|
28417
|
-
const m = unitName.match(/^switchroom-([a-zA-Z0-9_-]+)-cron-(\d+)\.service$/);
|
|
28418
|
-
if (!m)
|
|
28419
|
-
return null;
|
|
28420
|
-
const agentName = m[1];
|
|
28421
|
-
const index = parseInt(m[2], 10);
|
|
28422
|
-
if (!agentName)
|
|
28423
|
-
return null;
|
|
28424
|
-
return { agentName, index };
|
|
28425
|
-
}
|
|
28426
|
-
function agentSlugFromPeer(peer) {
|
|
28427
|
-
if (peer.systemdUnit === null)
|
|
28428
|
-
return null;
|
|
28429
|
-
const parsed = parseCronUnit(peer.systemdUnit);
|
|
28430
|
-
return parsed?.agentName ?? null;
|
|
28431
|
-
}
|
|
28432
|
-
function checkEntryScope(scope, agentSlug) {
|
|
28433
|
-
if (scope === undefined || scope === null) {
|
|
28434
|
-
return { allow: true };
|
|
28435
|
-
}
|
|
28436
|
-
const deny = scope.deny ?? [];
|
|
28437
|
-
const allow = scope.allow ?? [];
|
|
28438
|
-
if (agentSlug !== null && deny.includes(agentSlug)) {
|
|
28439
|
-
return {
|
|
28440
|
-
allow: false,
|
|
28441
|
-
reason: `agent '${agentSlug}' is in the entry's deny list (scope-deny)`
|
|
28442
|
-
};
|
|
28443
|
-
}
|
|
28444
|
-
if (allow.length > 0) {
|
|
28445
|
-
if (agentSlug === null || !allow.includes(agentSlug)) {
|
|
28446
|
-
return {
|
|
28447
|
-
allow: false,
|
|
28448
|
-
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)`
|
|
28449
|
-
};
|
|
28450
|
-
}
|
|
28451
|
-
}
|
|
28452
|
-
return { allow: true };
|
|
28453
|
-
}
|
|
28454
|
-
function checkAcl(peer, config, key) {
|
|
28455
|
-
if (peer.systemdUnit !== null) {
|
|
28456
|
-
const parsed = parseCronUnit(peer.systemdUnit);
|
|
28457
|
-
if (parsed === null) {
|
|
28458
|
-
return {
|
|
28459
|
-
allow: false,
|
|
28460
|
-
reason: `systemd unit '${peer.systemdUnit}' does not match switchroom cron unit naming convention`
|
|
28461
|
-
};
|
|
28462
|
-
}
|
|
28463
|
-
const { agentName, index } = parsed;
|
|
28464
|
-
const agentConfig = config.agents?.[agentName];
|
|
28465
|
-
if (!agentConfig) {
|
|
28466
|
-
return { allow: false, reason: `agent '${agentName}' not found in config` };
|
|
28467
|
-
}
|
|
28468
|
-
const schedule = agentConfig.schedule ?? [];
|
|
28469
|
-
if (index >= schedule.length || index < 0) {
|
|
28470
|
-
return {
|
|
28471
|
-
allow: false,
|
|
28472
|
-
reason: `schedule index ${index} out of range for agent '${agentName}' (${schedule.length} entries)`
|
|
28473
|
-
};
|
|
28474
|
-
}
|
|
28475
|
-
const entry = schedule[index];
|
|
28476
|
-
const allowedKeys = entry.secrets ?? [];
|
|
28477
|
-
if (!allowedKeys.includes(key)) {
|
|
28478
|
-
return {
|
|
28479
|
-
allow: false,
|
|
28480
|
-
reason: `key '${key}' not in ACL for ${agentName}/schedule[${index}]`
|
|
28481
|
-
};
|
|
28482
|
-
}
|
|
28483
|
-
return { allow: true };
|
|
28484
|
-
}
|
|
28485
|
-
return {
|
|
28486
|
-
allow: false,
|
|
28487
|
-
reason: "caller is not a switchroom cron unit; use 'switchroom vault get --no-broker' for interactive access"
|
|
28488
|
-
};
|
|
28489
|
-
}
|
|
28490
|
-
function checkAclByAgent(config, agentName, key) {
|
|
28491
|
-
if (!agentName) {
|
|
28492
|
-
return { allow: false, reason: "agent name unresolved" };
|
|
28493
|
-
}
|
|
28494
|
-
const agentConfig = config.agents?.[agentName];
|
|
28495
|
-
if (!agentConfig) {
|
|
28496
|
-
return { allow: false, reason: `agent '${agentName}' not found in config` };
|
|
28497
|
-
}
|
|
28498
|
-
const googleSlot = parseGoogleAccountSlotKey(key);
|
|
28499
|
-
if (googleSlot !== null) {
|
|
28500
|
-
return checkGoogleAccountAcl(config, agentName, googleSlot.account, key);
|
|
28501
|
-
}
|
|
28502
|
-
if (isGoogleClientCredentialKeyForAgent(config, agentName, key)) {
|
|
28503
|
-
return { allow: true };
|
|
28504
|
-
}
|
|
28505
|
-
if (isWebkiteCredentialKeyForAgent(agentConfig, key)) {
|
|
28506
|
-
return { allow: true };
|
|
28507
|
-
}
|
|
28508
|
-
const agentBot = agentConfig.bot_token;
|
|
28509
|
-
const botRef = agentBot && agentBot.length > 0 ? agentBot : config.telegram?.bot_token;
|
|
28510
|
-
if (typeof botRef === "string" && botRef.startsWith("vault:")) {
|
|
28511
|
-
const botKey = botRef.slice("vault:".length).split("#")[0];
|
|
28512
|
-
if (botKey.length > 0 && botKey === key) {
|
|
28513
|
-
return { allow: true };
|
|
28514
|
-
}
|
|
28515
|
-
}
|
|
28516
|
-
const cfgWithProfiles = config;
|
|
28517
|
-
const profileName = agentConfig.extends;
|
|
28518
|
-
const profileMcp = profileName != null && profileName.length > 0 ? cfgWithProfiles.profiles?.[profileName]?.mcp_servers ?? {} : {};
|
|
28519
|
-
const effectiveMcp = {
|
|
28520
|
-
...cfgWithProfiles.defaults?.mcp_servers ?? {},
|
|
28521
|
-
...profileMcp,
|
|
28522
|
-
...agentConfig.mcp_servers ?? {}
|
|
28523
|
-
};
|
|
28524
|
-
for (const mcpEntry of Object.values(effectiveMcp)) {
|
|
28525
|
-
if (!mcpEntry || typeof mcpEntry !== "object")
|
|
28526
|
-
continue;
|
|
28527
|
-
const declared = mcpEntry.secrets;
|
|
28528
|
-
if (Array.isArray(declared) && declared.includes(key)) {
|
|
28529
|
-
return { allow: true };
|
|
28530
|
-
}
|
|
28531
|
-
}
|
|
28532
|
-
const cfgSecrets = config;
|
|
28533
|
-
const profileSecrets = profileName != null && profileName.length > 0 ? cfgSecrets.profiles?.[profileName]?.secrets : undefined;
|
|
28534
|
-
const standingSecrets = [
|
|
28535
|
-
...Array.isArray(cfgSecrets.defaults?.secrets) ? cfgSecrets.defaults.secrets : [],
|
|
28536
|
-
...Array.isArray(profileSecrets) ? profileSecrets : [],
|
|
28537
|
-
...Array.isArray(agentConfig.secrets) ? agentConfig.secrets : []
|
|
28538
|
-
];
|
|
28539
|
-
if (standingSecrets.includes(key)) {
|
|
28540
|
-
return { allow: true };
|
|
28541
|
-
}
|
|
28542
|
-
const schedule = agentConfig.schedule ?? [];
|
|
28543
|
-
if (schedule.length === 0) {
|
|
28544
|
-
return {
|
|
28545
|
-
allow: false,
|
|
28546
|
-
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`
|
|
28547
|
-
};
|
|
28548
|
-
}
|
|
28549
|
-
for (const entry of schedule) {
|
|
28550
|
-
const allowed = entry?.secrets ?? [];
|
|
28551
|
-
if (allowed.includes(key)) {
|
|
28552
|
-
return { allow: true };
|
|
28553
|
-
}
|
|
28554
|
-
}
|
|
28555
|
-
return {
|
|
28556
|
-
allow: false,
|
|
28557
|
-
reason: `key '${key}' not in ACL for agent '${agentName}'`
|
|
28558
|
-
};
|
|
28559
|
-
}
|
|
28560
|
-
function parseGoogleAccountSlotKey(key) {
|
|
28561
|
-
const match = key.match(/^google:([^:]+):([a-z_]+)$/);
|
|
28562
|
-
if (!match)
|
|
28563
|
-
return null;
|
|
28564
|
-
return { account: match[1], field: match[2] };
|
|
28565
|
-
}
|
|
28566
|
-
function checkGoogleAccountAcl(config, agentName, account, key) {
|
|
28567
|
-
const accounts = config.google_accounts ?? {};
|
|
28568
|
-
const accountKey = account.toLowerCase();
|
|
28569
|
-
const accountEntry = accounts[accountKey] ?? accounts[account];
|
|
28570
|
-
if (!accountEntry) {
|
|
28571
|
-
return {
|
|
28572
|
-
allow: false,
|
|
28573
|
-
reason: `google_accounts['${account}'] not configured (key '${key}')`
|
|
28574
|
-
};
|
|
28575
|
-
}
|
|
28576
|
-
const enabled = accountEntry.enabled_for ?? [];
|
|
28577
|
-
if (enabled.length === 0) {
|
|
28578
|
-
return {
|
|
28579
|
-
allow: false,
|
|
28580
|
-
reason: `google_accounts['${account}'].enabled_for is empty (key '${key}')`
|
|
28581
|
-
};
|
|
28582
|
-
}
|
|
28583
|
-
if (!enabled.includes(agentName)) {
|
|
28584
|
-
return {
|
|
28585
|
-
allow: false,
|
|
28586
|
-
reason: `agent '${agentName}' not in google_accounts['${account}'].enabled_for (key '${key}')`
|
|
28587
|
-
};
|
|
28588
|
-
}
|
|
28589
|
-
return { allow: true };
|
|
28590
|
-
}
|
|
28591
|
-
var WEBKITE_VAULT_KEYS;
|
|
28592
|
-
var init_acl = __esm(() => {
|
|
28593
|
-
WEBKITE_VAULT_KEYS = new Set([
|
|
28594
|
-
"webkite/cloudflare-account-id",
|
|
28595
|
-
"webkite/cloudflare-api-token",
|
|
28596
|
-
"webkite/firecrawl-api-key"
|
|
28597
|
-
]);
|
|
28598
|
-
});
|
|
28599
|
-
|
|
28600
28601
|
// src/util/audit-hashchain.ts
|
|
28601
28602
|
import { createHash as createHash7 } from "node:crypto";
|
|
28602
28603
|
import { openSync as openSync7, readSync as readSync2, fstatSync as fstatSync2, closeSync as closeSync7 } from "node:fs";
|
|
@@ -49611,6 +49612,10 @@ function dispatchTool(name, args) {
|
|
|
49611
49612
|
cliArgs = buildArgs(["config", "get"], args);
|
|
49612
49613
|
parseMode = "json";
|
|
49613
49614
|
break;
|
|
49615
|
+
case "whoami":
|
|
49616
|
+
cliArgs = buildArgs(["config", "whoami"], args);
|
|
49617
|
+
parseMode = "json";
|
|
49618
|
+
break;
|
|
49614
49619
|
case "cron_list":
|
|
49615
49620
|
cliArgs = buildArgs(["cron", "list"], args);
|
|
49616
49621
|
parseMode = "json";
|
|
@@ -49817,6 +49822,19 @@ var init_server3 = __esm(() => {
|
|
|
49817
49822
|
}
|
|
49818
49823
|
}
|
|
49819
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
|
+
},
|
|
49820
49838
|
{
|
|
49821
49839
|
name: "cron_list",
|
|
49822
49840
|
description: "List the agent's scheduled cron entries (schedule array) as JSON.",
|
|
@@ -49839,7 +49857,7 @@ var init_server3 = __esm(() => {
|
|
|
49839
49857
|
},
|
|
49840
49858
|
{
|
|
49841
49859
|
name: "schedule_add",
|
|
49842
|
-
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.",
|
|
49843
49861
|
inputSchema: {
|
|
49844
49862
|
type: "object",
|
|
49845
49863
|
required: ["cron_expr", "prompt"],
|
|
@@ -49850,7 +49868,7 @@ var init_server3 = __esm(() => {
|
|
|
49850
49868
|
name: { type: "string", pattern: "^[a-z0-9-]{1,40}$" },
|
|
49851
49869
|
model: {
|
|
49852
49870
|
type: "string",
|
|
49853
|
-
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)."
|
|
49854
49872
|
},
|
|
49855
49873
|
context: {
|
|
49856
49874
|
type: "string",
|
|
@@ -50048,7 +50066,7 @@ __export(exports_server2, {
|
|
|
50048
50066
|
TOOLS: () => TOOLS2
|
|
50049
50067
|
});
|
|
50050
50068
|
import { randomBytes as randomBytes15 } from "node:crypto";
|
|
50051
|
-
import { existsSync as existsSync83, readFileSync as
|
|
50069
|
+
import { existsSync as existsSync83, readFileSync as readFileSync71 } from "node:fs";
|
|
50052
50070
|
function selfSocketPath() {
|
|
50053
50071
|
return `/run/switchroom/hostd/${SELF_AGENT}/sock`;
|
|
50054
50072
|
}
|
|
@@ -50226,7 +50244,7 @@ function getLastUpdateApplyStatus() {
|
|
|
50226
50244
|
}
|
|
50227
50245
|
let raw;
|
|
50228
50246
|
try {
|
|
50229
|
-
raw =
|
|
50247
|
+
raw = readFileSync71(path8, "utf-8");
|
|
50230
50248
|
} catch (err2) {
|
|
50231
50249
|
return errorText2(`get_status: failed to read audit log at ${path8}: ${err2.message}`);
|
|
50232
50250
|
}
|
|
@@ -50459,8 +50477,8 @@ var {
|
|
|
50459
50477
|
} = import__.default;
|
|
50460
50478
|
|
|
50461
50479
|
// src/build-info.ts
|
|
50462
|
-
var VERSION = "0.15.
|
|
50463
|
-
var COMMIT_SHA = "
|
|
50480
|
+
var VERSION = "0.15.20";
|
|
50481
|
+
var COMMIT_SHA = "0b63ab9e";
|
|
50464
50482
|
|
|
50465
50483
|
// src/cli/agent.ts
|
|
50466
50484
|
init_source();
|
|
@@ -50577,6 +50595,8 @@ init_timezone();
|
|
|
50577
50595
|
|
|
50578
50596
|
// src/cli/agent-config.ts
|
|
50579
50597
|
init_helpers();
|
|
50598
|
+
init_merge();
|
|
50599
|
+
init_acl();
|
|
50580
50600
|
import { join as join2 } from "node:path";
|
|
50581
50601
|
import { homedir as homedir2 } from "node:os";
|
|
50582
50602
|
import {
|
|
@@ -50672,6 +50692,66 @@ function scheduleLiveNote(agent) {
|
|
|
50672
50692
|
const hotReload = (process.env.SWITCHROOM_SCHEDULER_HOT_RELOAD ?? "") !== "0";
|
|
50673
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.`;
|
|
50674
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
|
+
}
|
|
50675
50755
|
function getAgentSlice(config, agent) {
|
|
50676
50756
|
const slice = config.agents?.[agent];
|
|
50677
50757
|
if (!slice) {
|
|
@@ -50728,6 +50808,29 @@ function registerAgentConfigCommands(program2) {
|
|
|
50728
50808
|
process.exit(1);
|
|
50729
50809
|
}
|
|
50730
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
|
+
}));
|
|
50731
50834
|
const cron = program2.command("cron").description("Read-only access to an agent's cron schedule");
|
|
50732
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) => {
|
|
50733
50836
|
let agent;
|
|
@@ -57061,6 +57164,7 @@ function registerAgentCommand(program3) {
|
|
|
57061
57164
|
status: status?.active ?? "unknown",
|
|
57062
57165
|
uptime: formatUptime2(status?.uptime ?? null),
|
|
57063
57166
|
model: resolved.model ?? SWITCHROOM_DEFAULT_MAIN_MODEL,
|
|
57167
|
+
thinking_effort: resolved.thinking_effort ?? SWITCHROOM_DEFAULT_THINKING_EFFORT,
|
|
57064
57168
|
extends: agentConfig.extends ?? "default",
|
|
57065
57169
|
topic_name: agentConfig.topic_name,
|
|
57066
57170
|
topic_emoji: agentConfig.topic_emoji,
|
|
@@ -76327,10 +76431,48 @@ init_doctor();
|
|
|
76327
76431
|
init_source();
|
|
76328
76432
|
init_loader();
|
|
76329
76433
|
init_lifecycle();
|
|
76330
|
-
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";
|
|
76331
76435
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
76332
76436
|
import { join as join60, dirname as dirname14, resolve as resolve34 } from "node:path";
|
|
76333
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
|
+
}
|
|
76334
76476
|
var DEFAULT_COMPOSE_PATH = join60(homedir36(), ".switchroom", "compose", "docker-compose.yml");
|
|
76335
76477
|
function runningFromSwitchroomCheckout(scriptPath) {
|
|
76336
76478
|
let dir = dirname14(scriptPath);
|
|
@@ -76363,6 +76505,15 @@ function planUpdate(opts) {
|
|
|
76363
76505
|
const runner = opts.runner ?? defaultRunner;
|
|
76364
76506
|
const scriptPath = opts.scriptPath ?? process.argv[1] ?? "";
|
|
76365
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
|
+
}
|
|
76366
76517
|
const releaseOverrideArgs = [];
|
|
76367
76518
|
if (opts.channel)
|
|
76368
76519
|
releaseOverrideArgs.push("--channel", opts.channel);
|
|
@@ -76812,6 +76963,8 @@ function registerUpdateCommand(program3) {
|
|
|
76812
76963
|
// src/cli/rollout.ts
|
|
76813
76964
|
init_helpers();
|
|
76814
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();
|
|
76815
76968
|
function normalizeVersion(v) {
|
|
76816
76969
|
return v.trim().replace(/^v/, "");
|
|
76817
76970
|
}
|
|
@@ -76824,7 +76977,10 @@ function orderAgentsCanaryFirst(agents) {
|
|
|
76824
76977
|
return [...canary, ...rest];
|
|
76825
76978
|
}
|
|
76826
76979
|
function planRollout(agents, opts = {}) {
|
|
76827
|
-
const steps = [
|
|
76980
|
+
const steps = [];
|
|
76981
|
+
if (opts.pinToPersist)
|
|
76982
|
+
steps.push({ kind: "persist-pin", pin: opts.pinToPersist });
|
|
76983
|
+
steps.push({ kind: "apply" });
|
|
76828
76984
|
for (const agent of orderAgentsCanaryFirst(agents)) {
|
|
76829
76985
|
steps.push({ kind: "restart-agent", agent });
|
|
76830
76986
|
}
|
|
@@ -76841,6 +76997,9 @@ function formatRolloutPlan(steps, target) {
|
|
|
76841
76997
|
for (const s of steps) {
|
|
76842
76998
|
n += 1;
|
|
76843
76999
|
switch (s.kind) {
|
|
77000
|
+
case "persist-pin":
|
|
77001
|
+
lines.push(` ${n}. persist release.pin=${s.pin} to switchroom.yaml (durable)`);
|
|
77002
|
+
break;
|
|
76844
77003
|
case "apply":
|
|
76845
77004
|
lines.push(` ${n}. apply \u2014 regenerate compose with ${target} image refs`);
|
|
76846
77005
|
break;
|
|
@@ -76863,16 +77022,24 @@ function formatRolloutPlan(steps, target) {
|
|
|
76863
77022
|
return lines.join(`
|
|
76864
77023
|
`);
|
|
76865
77024
|
}
|
|
76866
|
-
function executeRollout(steps, target, deps
|
|
77025
|
+
function executeRollout(steps, target, deps) {
|
|
76867
77026
|
const targetNorm = normalizeVersion(target);
|
|
76868
77027
|
const rolled = [];
|
|
76869
77028
|
const warnings = [];
|
|
76870
77029
|
for (const step of steps) {
|
|
76871
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
|
+
}
|
|
76872
77040
|
case "apply": {
|
|
76873
77041
|
deps.log(`\u2192 apply \u2014 regenerating compose for ${target}`);
|
|
76874
|
-
const
|
|
76875
|
-
const r = deps.run(args);
|
|
77042
|
+
const r = deps.run(["apply"]);
|
|
76876
77043
|
if (r.status !== 0) {
|
|
76877
77044
|
return { ok: false, rolled, failedStep: "apply", warnings };
|
|
76878
77045
|
}
|
|
@@ -76954,12 +77121,16 @@ function registerRolloutCommand(program3) {
|
|
|
76954
77121
|
process.exitCode = 2;
|
|
76955
77122
|
return;
|
|
76956
77123
|
}
|
|
76957
|
-
const steps = planRollout(requested, {
|
|
77124
|
+
const steps = planRollout(requested, {
|
|
77125
|
+
skipWeb: opts.skipWeb,
|
|
77126
|
+
pinToPersist: opts.pin ?? undefined
|
|
77127
|
+
});
|
|
76958
77128
|
if (opts.dryRun) {
|
|
76959
77129
|
process.stdout.write(formatRolloutPlan(steps, target) + `
|
|
76960
77130
|
`);
|
|
76961
77131
|
return;
|
|
76962
77132
|
}
|
|
77133
|
+
const configPath = getConfigPath(program3);
|
|
76963
77134
|
const scriptPath = process.argv[1] ?? "switchroom";
|
|
76964
77135
|
const deps = {
|
|
76965
77136
|
run: (args) => {
|
|
@@ -76974,11 +77145,25 @@ function registerRolloutCommand(program3) {
|
|
|
76974
77145
|
`).pop()?.trim() ?? null;
|
|
76975
77146
|
},
|
|
76976
77147
|
log: (line) => process.stdout.write(line + `
|
|
76977
|
-
`)
|
|
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
|
+
}
|
|
76978
77163
|
};
|
|
76979
77164
|
process.stdout.write(`Rolling ${requested.length} agent(s) to ${target}\u2026
|
|
76980
77165
|
`);
|
|
76981
|
-
const result = executeRollout(steps, target, deps
|
|
77166
|
+
const result = executeRollout(steps, target, deps);
|
|
76982
77167
|
for (const w of result.warnings)
|
|
76983
77168
|
process.stderr.write(`\u26a0\ufe0f ${w}
|
|
76984
77169
|
`);
|
|
@@ -77009,7 +77194,7 @@ init_source();
|
|
|
77009
77194
|
init_helpers();
|
|
77010
77195
|
init_lifecycle();
|
|
77011
77196
|
import { execSync as execSync4 } from "node:child_process";
|
|
77012
|
-
import { existsSync as existsSync59, readFileSync as
|
|
77197
|
+
import { existsSync as existsSync59, readFileSync as readFileSync54 } from "node:fs";
|
|
77013
77198
|
import { dirname as dirname15, join as join61 } from "node:path";
|
|
77014
77199
|
function getClaudeCodeVersion() {
|
|
77015
77200
|
try {
|
|
@@ -77063,7 +77248,7 @@ function locateSwitchroomInstallDir() {
|
|
|
77063
77248
|
const pkgPath = join61(dir, "package.json");
|
|
77064
77249
|
if (existsSync59(pkgPath)) {
|
|
77065
77250
|
try {
|
|
77066
|
-
const pkg = JSON.parse(
|
|
77251
|
+
const pkg = JSON.parse(readFileSync54(pkgPath, "utf-8"));
|
|
77067
77252
|
if (pkg.name === "switchroom" && existsSync59(join61(dir, ".git"))) {
|
|
77068
77253
|
return dir;
|
|
77069
77254
|
}
|
|
@@ -77294,9 +77479,9 @@ import {
|
|
|
77294
77479
|
mkdirSync as mkdirSync33,
|
|
77295
77480
|
openSync as openSync11,
|
|
77296
77481
|
readdirSync as readdirSync22,
|
|
77297
|
-
readFileSync as
|
|
77482
|
+
readFileSync as readFileSync55,
|
|
77298
77483
|
renameSync as renameSync12,
|
|
77299
|
-
statSync as
|
|
77484
|
+
statSync as statSync28,
|
|
77300
77485
|
unlinkSync as unlinkSync11,
|
|
77301
77486
|
writeFileSync as writeFileSync28,
|
|
77302
77487
|
writeSync as writeSync7
|
|
@@ -77704,7 +77889,7 @@ function readAll(stateDir) {
|
|
|
77704
77889
|
return [];
|
|
77705
77890
|
let raw;
|
|
77706
77891
|
try {
|
|
77707
|
-
raw =
|
|
77892
|
+
raw = readFileSync55(path4, "utf-8");
|
|
77708
77893
|
} catch {
|
|
77709
77894
|
return [];
|
|
77710
77895
|
}
|
|
@@ -77870,7 +78055,7 @@ function sweepOrphanTmpFiles(stateDir) {
|
|
|
77870
78055
|
continue;
|
|
77871
78056
|
const tmpPath = join62(stateDir, entry);
|
|
77872
78057
|
try {
|
|
77873
|
-
const stat =
|
|
78058
|
+
const stat = statSync28(tmpPath);
|
|
77874
78059
|
if (stat.mtimeMs < cutoff) {
|
|
77875
78060
|
unlinkSync11(tmpPath);
|
|
77876
78061
|
}
|
|
@@ -77915,7 +78100,7 @@ function withLock(stateDir, fn) {
|
|
|
77915
78100
|
function tryStealStaleLock(lockPath) {
|
|
77916
78101
|
let pidStr;
|
|
77917
78102
|
try {
|
|
77918
|
-
pidStr =
|
|
78103
|
+
pidStr = readFileSync55(lockPath, "utf-8").trim();
|
|
77919
78104
|
} catch {
|
|
77920
78105
|
return true;
|
|
77921
78106
|
}
|
|
@@ -78172,7 +78357,7 @@ import { createHash as createHash11 } from "node:crypto";
|
|
|
78172
78357
|
import {
|
|
78173
78358
|
existsSync as existsSync61,
|
|
78174
78359
|
mkdirSync as mkdirSync34,
|
|
78175
|
-
readFileSync as
|
|
78360
|
+
readFileSync as readFileSync56,
|
|
78176
78361
|
rmSync as rmSync13,
|
|
78177
78362
|
writeFileSync as writeFileSync29
|
|
78178
78363
|
} from "node:fs";
|
|
@@ -78192,7 +78377,7 @@ function defaultPythonCacheRoot() {
|
|
|
78192
78377
|
return join63(homedir37(), ".switchroom", "deps", "python");
|
|
78193
78378
|
}
|
|
78194
78379
|
function hashFile(path4) {
|
|
78195
|
-
return createHash11("sha256").update(
|
|
78380
|
+
return createHash11("sha256").update(readFileSync56(path4)).digest("hex");
|
|
78196
78381
|
}
|
|
78197
78382
|
function ensurePythonEnv(opts) {
|
|
78198
78383
|
const { skillName, requirementsPath, force = false } = opts;
|
|
@@ -78208,7 +78393,7 @@ function ensurePythonEnv(opts) {
|
|
|
78208
78393
|
const pipBin = join63(binDir, "pip");
|
|
78209
78394
|
const targetHash = hashFile(requirementsPath);
|
|
78210
78395
|
if (!force && existsSync61(stampPath) && existsSync61(pythonBin)) {
|
|
78211
|
-
const existingHash =
|
|
78396
|
+
const existingHash = readFileSync56(stampPath, "utf8").trim();
|
|
78212
78397
|
if (existingHash === targetHash) {
|
|
78213
78398
|
return {
|
|
78214
78399
|
skillName,
|
|
@@ -78260,7 +78445,7 @@ import {
|
|
|
78260
78445
|
copyFileSync as copyFileSync9,
|
|
78261
78446
|
existsSync as existsSync62,
|
|
78262
78447
|
mkdirSync as mkdirSync35,
|
|
78263
|
-
readFileSync as
|
|
78448
|
+
readFileSync as readFileSync57,
|
|
78264
78449
|
rmSync as rmSync14,
|
|
78265
78450
|
writeFileSync as writeFileSync30
|
|
78266
78451
|
} from "node:fs";
|
|
@@ -78295,7 +78480,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
78295
78480
|
const hasher = createHash12("sha256");
|
|
78296
78481
|
hasher.update(`package.json
|
|
78297
78482
|
`);
|
|
78298
|
-
hasher.update(
|
|
78483
|
+
hasher.update(readFileSync57(packageJsonPath));
|
|
78299
78484
|
for (const lockName of ALL_LOCKFILES) {
|
|
78300
78485
|
const lockPath = join64(sourceDir, lockName);
|
|
78301
78486
|
if (existsSync62(lockPath)) {
|
|
@@ -78304,7 +78489,7 @@ function hashDepInputs(packageJsonPath) {
|
|
|
78304
78489
|
hasher.update(lockName);
|
|
78305
78490
|
hasher.update(`
|
|
78306
78491
|
`);
|
|
78307
|
-
hasher.update(
|
|
78492
|
+
hasher.update(readFileSync57(lockPath));
|
|
78308
78493
|
}
|
|
78309
78494
|
}
|
|
78310
78495
|
return hasher.digest("hex");
|
|
@@ -78323,7 +78508,7 @@ function ensureNodeEnv(opts) {
|
|
|
78323
78508
|
const binDir = join64(nodeModulesDir, ".bin");
|
|
78324
78509
|
const targetHash = hashDepInputs(packageJsonPath);
|
|
78325
78510
|
if (!force && existsSync62(stampPath) && existsSync62(nodeModulesDir)) {
|
|
78326
|
-
const existingHash =
|
|
78511
|
+
const existingHash = readFileSync57(stampPath, "utf8").trim();
|
|
78327
78512
|
if (existingHash === targetHash) {
|
|
78328
78513
|
return {
|
|
78329
78514
|
skillName,
|
|
@@ -79342,7 +79527,7 @@ function safeParseInt(value, fallback) {
|
|
|
79342
79527
|
init_helpers();
|
|
79343
79528
|
init_loader();
|
|
79344
79529
|
init_merge();
|
|
79345
|
-
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";
|
|
79346
79531
|
import { join as join66, resolve as resolve40 } from "node:path";
|
|
79347
79532
|
init_schema();
|
|
79348
79533
|
function resolveSoulTargetOrExit(program3, agentName) {
|
|
@@ -79388,7 +79573,7 @@ function registerSoulCommand(program3) {
|
|
|
79388
79573
|
console.error(`soul: ${t.soulPath} does not exist yet \u2014 run ` + `\`switchroom soul reset ${agentName}\` to seed it.`);
|
|
79389
79574
|
process.exit(1);
|
|
79390
79575
|
}
|
|
79391
|
-
process.stdout.write(
|
|
79576
|
+
process.stdout.write(readFileSync58(t.soulPath, "utf-8"));
|
|
79392
79577
|
}));
|
|
79393
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) => {
|
|
79394
79579
|
const t = resolveSoulTargetOrExit(program3, agentName);
|
|
@@ -79433,7 +79618,7 @@ function registerSoulCommand(program3) {
|
|
|
79433
79618
|
// src/cli/debug.ts
|
|
79434
79619
|
init_helpers();
|
|
79435
79620
|
init_loader();
|
|
79436
|
-
import { existsSync as existsSync66, readFileSync as
|
|
79621
|
+
import { existsSync as existsSync66, readFileSync as readFileSync59, readdirSync as readdirSync23, statSync as statSync29 } from "node:fs";
|
|
79437
79622
|
import { resolve as resolve41, join as join67 } from "node:path";
|
|
79438
79623
|
import { createHash as createHash13 } from "node:crypto";
|
|
79439
79624
|
init_merge();
|
|
@@ -79449,7 +79634,7 @@ function readMcpServerNames(agentDir) {
|
|
|
79449
79634
|
if (!existsSync66(mcpPath))
|
|
79450
79635
|
return [];
|
|
79451
79636
|
try {
|
|
79452
|
-
const parsed = JSON.parse(
|
|
79637
|
+
const parsed = JSON.parse(readFileSync59(mcpPath, "utf-8"));
|
|
79453
79638
|
return Object.keys(parsed.mcpServers ?? {});
|
|
79454
79639
|
} catch {
|
|
79455
79640
|
return null;
|
|
@@ -79472,7 +79657,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
79472
79657
|
const transcriptPath = join67(projectPath, "transcript.jsonl");
|
|
79473
79658
|
if (!existsSync66(transcriptPath))
|
|
79474
79659
|
continue;
|
|
79475
|
-
const stat3 =
|
|
79660
|
+
const stat3 = statSync29(transcriptPath);
|
|
79476
79661
|
if (!latest || stat3.mtimeMs > latest.mtime) {
|
|
79477
79662
|
latest = { path: transcriptPath, mtime: stat3.mtimeMs };
|
|
79478
79663
|
}
|
|
@@ -79484,7 +79669,7 @@ function findLatestTranscriptJsonl(claudeConfigDir) {
|
|
|
79484
79669
|
}
|
|
79485
79670
|
function extractLatestUserMessage(transcriptPath) {
|
|
79486
79671
|
try {
|
|
79487
|
-
const content =
|
|
79672
|
+
const content = readFileSync59(transcriptPath, "utf-8");
|
|
79488
79673
|
const lines = content.trim().split(`
|
|
79489
79674
|
`).filter(Boolean);
|
|
79490
79675
|
for (let i = lines.length - 1;i >= 0; i--) {
|
|
@@ -79588,7 +79773,7 @@ function registerDebugCommand(program3) {
|
|
|
79588
79773
|
}
|
|
79589
79774
|
console.log(`=== Append System Prompt (per-session) ===
|
|
79590
79775
|
`);
|
|
79591
|
-
const handoffContent = existsSync66(handoffPath) ?
|
|
79776
|
+
const handoffContent = existsSync66(handoffPath) ? readFileSync59(handoffPath, "utf-8") : "";
|
|
79592
79777
|
if (handoffContent.trim().length > 0) {
|
|
79593
79778
|
console.log(`-- Handoff Briefing (${formatBytes(handoffContent.length)}) --`);
|
|
79594
79779
|
console.log(handoffContent);
|
|
@@ -79599,7 +79784,7 @@ function registerDebugCommand(program3) {
|
|
|
79599
79784
|
}
|
|
79600
79785
|
console.log(`=== CLAUDE.md (auto-loaded by Claude Code) ===
|
|
79601
79786
|
`);
|
|
79602
|
-
const claudeMdContent = existsSync66(claudeMdPath) ?
|
|
79787
|
+
const claudeMdContent = existsSync66(claudeMdPath) ? readFileSync59(claudeMdPath, "utf-8") : "";
|
|
79603
79788
|
if (claudeMdContent.trim().length > 0) {
|
|
79604
79789
|
console.log(`(${formatBytes(claudeMdContent.length)})`);
|
|
79605
79790
|
console.log(claudeMdContent);
|
|
@@ -79610,7 +79795,7 @@ function registerDebugCommand(program3) {
|
|
|
79610
79795
|
}
|
|
79611
79796
|
console.log(`=== Persona (SOUL.md) ===
|
|
79612
79797
|
`);
|
|
79613
|
-
const soulMdContent = existsSync66(soulMdPath) ?
|
|
79798
|
+
const soulMdContent = existsSync66(soulMdPath) ? readFileSync59(soulMdPath, "utf-8") : existsSync66(workspaceSoulMdPath) ? readFileSync59(workspaceSoulMdPath, "utf-8") : "";
|
|
79614
79799
|
if (soulMdContent.trim().length > 0) {
|
|
79615
79800
|
console.log(`(${formatBytes(soulMdContent.length)})`);
|
|
79616
79801
|
console.log(soulMdContent);
|
|
@@ -79674,8 +79859,8 @@ function registerDebugCommand(program3) {
|
|
|
79674
79859
|
const fleetDir = join67(agentsDir, "..", "fleet");
|
|
79675
79860
|
const fleetInvPath = join67(fleetDir, "switchroom-invariants.md");
|
|
79676
79861
|
const fleetClaudePath = join67(fleetDir, "CLAUDE.md");
|
|
79677
|
-
const fleetInvBytes = existsSync66(fleetInvPath) ?
|
|
79678
|
-
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;
|
|
79679
79864
|
const fleetBytes = fleetInvBytes + fleetClaudeBytes;
|
|
79680
79865
|
const totalBytes = stableBytes + perSessionBytes + claudeMdBytes + fleetBytes + perTurnBytes + userBytes;
|
|
79681
79866
|
console.log(`Stable prefix: ${formatBytes(stableBytes).padEnd(20)} (cache-hot; includes SOUL.md ${soulMdBytes.toLocaleString()}B)`);
|
|
@@ -79717,7 +79902,7 @@ import { randomBytes as randomBytes13 } from "node:crypto";
|
|
|
79717
79902
|
import {
|
|
79718
79903
|
mkdirSync as mkdirSync36,
|
|
79719
79904
|
writeFileSync as writeFileSync32,
|
|
79720
|
-
readFileSync as
|
|
79905
|
+
readFileSync as readFileSync60,
|
|
79721
79906
|
readdirSync as readdirSync24,
|
|
79722
79907
|
unlinkSync as unlinkSync12,
|
|
79723
79908
|
existsSync as existsSync67,
|
|
@@ -79745,7 +79930,7 @@ function writeRecord(record2) {
|
|
|
79745
79930
|
function readRecord(id) {
|
|
79746
79931
|
const path7 = recordPath(id);
|
|
79747
79932
|
try {
|
|
79748
|
-
const raw =
|
|
79933
|
+
const raw = readFileSync60(path7, "utf8");
|
|
79749
79934
|
return JSON.parse(raw);
|
|
79750
79935
|
} catch {
|
|
79751
79936
|
return null;
|
|
@@ -80812,7 +80997,7 @@ function registerNotionMcpLauncherCommand(program3) {
|
|
|
80812
80997
|
|
|
80813
80998
|
// src/cli/deliver-file.ts
|
|
80814
80999
|
init_client2();
|
|
80815
|
-
import { readFileSync as
|
|
81000
|
+
import { readFileSync as readFileSync61, statSync as statSync30 } from "node:fs";
|
|
80816
81001
|
import { basename as basename8 } from "node:path";
|
|
80817
81002
|
|
|
80818
81003
|
// src/delivery/onedrive.ts
|
|
@@ -81151,8 +81336,8 @@ async function defaultResolveProvider() {
|
|
|
81151
81336
|
}
|
|
81152
81337
|
async function runDeliverFile(localPath, deps = {}) {
|
|
81153
81338
|
const agentName = safeAgentName(deps.agentName ?? process.env.SWITCHROOM_AGENT_NAME);
|
|
81154
|
-
const sizeOf = deps.fileSize ?? ((p) =>
|
|
81155
|
-
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)));
|
|
81156
81341
|
const resolveProvider = deps.resolveProvider ?? defaultResolveProvider;
|
|
81157
81342
|
let size;
|
|
81158
81343
|
try {
|
|
@@ -81442,7 +81627,7 @@ async function fetchToken(vaultKey) {
|
|
|
81442
81627
|
|
|
81443
81628
|
// src/cli/apply.ts
|
|
81444
81629
|
init_source();
|
|
81445
|
-
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";
|
|
81446
81631
|
import { mkdir as mkdir2 } from "node:fs/promises";
|
|
81447
81632
|
import { spawnSync as childSpawnSync } from "node:child_process";
|
|
81448
81633
|
import readline from "node:readline";
|
|
@@ -81839,7 +82024,7 @@ init_loader();
|
|
|
81839
82024
|
init_loader();
|
|
81840
82025
|
|
|
81841
82026
|
// src/cli/update-prompt-hook.ts
|
|
81842
|
-
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";
|
|
81843
82028
|
import { join as join72 } from "node:path";
|
|
81844
82029
|
var HOOK_FILENAME = "update-card-on-prompt.sh";
|
|
81845
82030
|
function updatePromptHookScript() {
|
|
@@ -81911,7 +82096,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
81911
82096
|
const scriptPath = join72(hooksDir, HOOK_FILENAME);
|
|
81912
82097
|
const desired = updatePromptHookScript();
|
|
81913
82098
|
let installed = false;
|
|
81914
|
-
const existing = existsSync72(scriptPath) ?
|
|
82099
|
+
const existing = existsSync72(scriptPath) ? readFileSync62(scriptPath, "utf-8") : "";
|
|
81915
82100
|
if (existing !== desired) {
|
|
81916
82101
|
writeFileSync36(scriptPath, desired, { mode: 493 });
|
|
81917
82102
|
chmodSync10(scriptPath, 493);
|
|
@@ -81925,7 +82110,7 @@ function installUpdatePromptHook(agentDir) {
|
|
|
81925
82110
|
if (!existsSync72(settingsPath)) {
|
|
81926
82111
|
return { scriptPath, settingsPath, installed };
|
|
81927
82112
|
}
|
|
81928
|
-
const raw =
|
|
82113
|
+
const raw = readFileSync62(settingsPath, "utf-8");
|
|
81929
82114
|
let parsed;
|
|
81930
82115
|
try {
|
|
81931
82116
|
parsed = JSON.parse(raw);
|
|
@@ -82102,14 +82287,14 @@ async function ensureHostMountSources(config) {
|
|
|
82102
82287
|
}
|
|
82103
82288
|
try {
|
|
82104
82289
|
const uid = allocateAgentUid(name);
|
|
82105
|
-
|
|
82290
|
+
chownSync7(tokenPath, uid, uid);
|
|
82106
82291
|
} catch {}
|
|
82107
82292
|
}
|
|
82108
82293
|
const fleetDir = join74(home2, ".switchroom", "fleet");
|
|
82109
82294
|
await mkdir2(fleetDir, { recursive: true });
|
|
82110
82295
|
const invariantsPath = join74(fleetDir, "switchroom-invariants.md");
|
|
82111
82296
|
const invariantsCanonical = renderFleetInvariants();
|
|
82112
|
-
const invariantsCurrent = existsSync74(invariantsPath) ?
|
|
82297
|
+
const invariantsCurrent = existsSync74(invariantsPath) ? readFileSync63(invariantsPath, "utf-8") : null;
|
|
82113
82298
|
if (invariantsCurrent !== invariantsCanonical) {
|
|
82114
82299
|
writeFileSync37(invariantsPath, invariantsCanonical, { mode: 420 });
|
|
82115
82300
|
}
|
|
@@ -82634,7 +82819,7 @@ function runRedactStdin() {
|
|
|
82634
82819
|
}
|
|
82635
82820
|
|
|
82636
82821
|
// src/cli/status-ask.ts
|
|
82637
|
-
import { readFileSync as
|
|
82822
|
+
import { readFileSync as readFileSync64, existsSync as existsSync75, readdirSync as readdirSync27 } from "node:fs";
|
|
82638
82823
|
import { join as join75 } from "node:path";
|
|
82639
82824
|
import { homedir as homedir44 } from "node:os";
|
|
82640
82825
|
|
|
@@ -82910,7 +83095,7 @@ function runReport(opts) {
|
|
|
82910
83095
|
for (const src of sources) {
|
|
82911
83096
|
let content;
|
|
82912
83097
|
try {
|
|
82913
|
-
content =
|
|
83098
|
+
content = readFileSync64(src.path, "utf-8");
|
|
82914
83099
|
} catch (err) {
|
|
82915
83100
|
process.stderr.write(`status-ask report: cannot read ${src.path}: ${err instanceof Error ? err.message : String(err)}
|
|
82916
83101
|
`);
|
|
@@ -83004,7 +83189,7 @@ function inferAgentFromPath(p) {
|
|
|
83004
83189
|
}
|
|
83005
83190
|
|
|
83006
83191
|
// src/cli/agent-config-write.ts
|
|
83007
|
-
var
|
|
83192
|
+
var import_yaml20 = __toESM(require_dist(), 1);
|
|
83008
83193
|
|
|
83009
83194
|
// src/config/overlay-writer.ts
|
|
83010
83195
|
init_paths();
|
|
@@ -83015,9 +83200,9 @@ import {
|
|
|
83015
83200
|
mkdirSync as mkdirSync43,
|
|
83016
83201
|
openSync as openSync13,
|
|
83017
83202
|
readdirSync as readdirSync28,
|
|
83018
|
-
readFileSync as
|
|
83203
|
+
readFileSync as readFileSync65,
|
|
83019
83204
|
renameSync as renameSync15,
|
|
83020
|
-
statSync as
|
|
83205
|
+
statSync as statSync31,
|
|
83021
83206
|
unlinkSync as unlinkSync14,
|
|
83022
83207
|
writeSync as writeSync8
|
|
83023
83208
|
} from "node:fs";
|
|
@@ -83061,7 +83246,7 @@ function withAgentLock(paths, fn) {
|
|
|
83061
83246
|
if (e.code !== "EEXIST")
|
|
83062
83247
|
throw err;
|
|
83063
83248
|
try {
|
|
83064
|
-
const age = Date.now() -
|
|
83249
|
+
const age = Date.now() - statSync31(paths.lockPath).mtimeMs;
|
|
83065
83250
|
if (age > 30000) {
|
|
83066
83251
|
unlinkSync14(paths.lockPath);
|
|
83067
83252
|
continue;
|
|
@@ -83139,7 +83324,7 @@ function listSkillsOverlayEntries(agent, opts = {}) {
|
|
|
83139
83324
|
continue;
|
|
83140
83325
|
const full = join76(paths.skillsDir, name);
|
|
83141
83326
|
try {
|
|
83142
|
-
const raw =
|
|
83327
|
+
const raw = readFileSync65(full, "utf-8");
|
|
83143
83328
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
83144
83329
|
out.push({ slug, path: full, raw });
|
|
83145
83330
|
} catch {}
|
|
@@ -83166,7 +83351,7 @@ function listOverlayEntries(agent, opts = {}) {
|
|
|
83166
83351
|
continue;
|
|
83167
83352
|
const full = join76(paths.scheduleDir, name);
|
|
83168
83353
|
try {
|
|
83169
|
-
const raw =
|
|
83354
|
+
const raw = readFileSync65(full, "utf-8");
|
|
83170
83355
|
const slug = name.replace(/\.ya?ml$/i, "");
|
|
83171
83356
|
out.push({ slug, path: full, raw });
|
|
83172
83357
|
} catch {}
|
|
@@ -83197,7 +83382,7 @@ function filterOverlaySecrets(doc, source) {
|
|
|
83197
83382
|
// src/agents/reconcile-dry-run.ts
|
|
83198
83383
|
init_overlay_schema();
|
|
83199
83384
|
init_schema();
|
|
83200
|
-
var
|
|
83385
|
+
var import_yaml19 = __toESM(require_dist(), 1);
|
|
83201
83386
|
init_lifecycle();
|
|
83202
83387
|
var MIN_CRON_INTERVAL_SECS = 5 * 60;
|
|
83203
83388
|
function violatesMinInterval(cron) {
|
|
@@ -83219,7 +83404,7 @@ function violatesMinInterval(cron) {
|
|
|
83219
83404
|
function dryRunReconcile(input) {
|
|
83220
83405
|
let parsed;
|
|
83221
83406
|
try {
|
|
83222
|
-
parsed =
|
|
83407
|
+
parsed = import_yaml19.parse(input.yamlText);
|
|
83223
83408
|
} catch (err) {
|
|
83224
83409
|
return {
|
|
83225
83410
|
ok: false,
|
|
@@ -83314,7 +83499,7 @@ import {
|
|
|
83314
83499
|
mkdirSync as mkdirSync44,
|
|
83315
83500
|
openSync as openSync14,
|
|
83316
83501
|
readdirSync as readdirSync29,
|
|
83317
|
-
readFileSync as
|
|
83502
|
+
readFileSync as readFileSync66,
|
|
83318
83503
|
renameSync as renameSync16,
|
|
83319
83504
|
unlinkSync as unlinkSync15,
|
|
83320
83505
|
writeFileSync as writeFileSync38,
|
|
@@ -83378,7 +83563,7 @@ function listPendingScheduleEntries(agent, opts = {}) {
|
|
|
83378
83563
|
if (!existsSync77(yamlPath))
|
|
83379
83564
|
continue;
|
|
83380
83565
|
try {
|
|
83381
|
-
const meta = JSON.parse(
|
|
83566
|
+
const meta = JSON.parse(readFileSync66(metaPath, "utf-8"));
|
|
83382
83567
|
if (meta?.v !== 1 || typeof meta.stage_id !== "string")
|
|
83383
83568
|
continue;
|
|
83384
83569
|
out.push({ stageId: meta.stage_id, agent: meta.agent, yamlPath, metaPath, meta });
|
|
@@ -83416,7 +83601,7 @@ function denyPendingScheduleEntry(opts) {
|
|
|
83416
83601
|
}
|
|
83417
83602
|
|
|
83418
83603
|
// src/cli/agent-config-write.ts
|
|
83419
|
-
import { existsSync as existsSync78, readFileSync as
|
|
83604
|
+
import { existsSync as existsSync78, readFileSync as readFileSync67 } from "node:fs";
|
|
83420
83605
|
import { execFileSync as execFileSync25 } from "node:child_process";
|
|
83421
83606
|
|
|
83422
83607
|
// src/scheduler/schedule-report.ts
|
|
@@ -83605,7 +83790,7 @@ function scheduleAdd(opts) {
|
|
|
83605
83790
|
]
|
|
83606
83791
|
};
|
|
83607
83792
|
const yamlText = (() => {
|
|
83608
|
-
const body =
|
|
83793
|
+
const body = import_yaml20.stringify(doc);
|
|
83609
83794
|
const header = opts.name ? `# name: ${opts.name}
|
|
83610
83795
|
` : "";
|
|
83611
83796
|
return header + body;
|
|
@@ -83719,7 +83904,7 @@ function scheduleAddOrStage(opts) {
|
|
|
83719
83904
|
]
|
|
83720
83905
|
};
|
|
83721
83906
|
const yamlText = (opts.name ? `# name: ${opts.name}
|
|
83722
|
-
` : "") +
|
|
83907
|
+
` : "") + import_yaml20.stringify(doc);
|
|
83723
83908
|
const summary = (() => {
|
|
83724
83909
|
const parts = [`cron=${opts.cronExpr}`];
|
|
83725
83910
|
if (opts.secrets?.length)
|
|
@@ -83769,7 +83954,7 @@ function scheduleRemove(opts) {
|
|
|
83769
83954
|
break;
|
|
83770
83955
|
}
|
|
83771
83956
|
try {
|
|
83772
|
-
const parsed =
|
|
83957
|
+
const parsed = import_yaml20.parse(e.raw);
|
|
83773
83958
|
if (parsed && parsed.name === opts.name) {
|
|
83774
83959
|
match = e;
|
|
83775
83960
|
break;
|
|
@@ -83788,7 +83973,7 @@ function scheduleRemove(opts) {
|
|
|
83788
83973
|
let priorContent = null;
|
|
83789
83974
|
try {
|
|
83790
83975
|
if (existsSync78(match.path))
|
|
83791
|
-
priorContent =
|
|
83976
|
+
priorContent = readFileSync67(match.path, "utf-8");
|
|
83792
83977
|
} catch {}
|
|
83793
83978
|
deleteOverlayEntry(agent, match.slug, { root: opts.root });
|
|
83794
83979
|
const reconcileFn = opts.reconcile === undefined ? opts.root ? null : reconcileAgentCronOnly : opts.reconcile;
|
|
@@ -83991,7 +84176,7 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
83991
84176
|
}
|
|
83992
84177
|
let blob;
|
|
83993
84178
|
if (opts.jsonl) {
|
|
83994
|
-
blob = existsSync78(opts.jsonl) ?
|
|
84179
|
+
blob = existsSync78(opts.jsonl) ? readFileSync67(opts.jsonl, "utf-8") : "";
|
|
83995
84180
|
} else {
|
|
83996
84181
|
try {
|
|
83997
84182
|
blob = execFileSync25("docker", ["exec", `switchroom-${agent}`, "cat", "/state/agent/scheduler.jsonl"], {
|
|
@@ -84022,10 +84207,10 @@ function registerAgentConfigWriteCommands(program3) {
|
|
|
84022
84207
|
}
|
|
84023
84208
|
|
|
84024
84209
|
// src/cli/agent-config-skill-write.ts
|
|
84025
|
-
var
|
|
84210
|
+
var import_yaml21 = __toESM(require_dist(), 1);
|
|
84026
84211
|
import { existsSync as existsSync79 } from "node:fs";
|
|
84027
84212
|
init_reconcile_default_skills();
|
|
84028
|
-
var
|
|
84213
|
+
var import_yaml22 = __toESM(require_dist(), 1);
|
|
84029
84214
|
import { join as join78 } from "node:path";
|
|
84030
84215
|
var MAX_SKILLS_PER_AGENT = 20;
|
|
84031
84216
|
var V1_ALLOWED_SOURCE_PREFIX = "bundled:";
|
|
@@ -84075,7 +84260,7 @@ function countCurrentSkills(agent, opts) {
|
|
|
84075
84260
|
let total = 0;
|
|
84076
84261
|
for (const e of entries) {
|
|
84077
84262
|
try {
|
|
84078
|
-
const doc =
|
|
84263
|
+
const doc = import_yaml22.parse(e.raw);
|
|
84079
84264
|
total += (doc?.skills ?? []).length;
|
|
84080
84265
|
} catch {}
|
|
84081
84266
|
}
|
|
@@ -84105,7 +84290,7 @@ function skillInstall(opts) {
|
|
|
84105
84290
|
if (!existsSync79(skillPath)) {
|
|
84106
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.`);
|
|
84107
84292
|
}
|
|
84108
|
-
const yamlText =
|
|
84293
|
+
const yamlText = import_yaml21.stringify({ skills: [skillName] });
|
|
84109
84294
|
let path8;
|
|
84110
84295
|
try {
|
|
84111
84296
|
path8 = writeSkillsOverlayEntry(agent, slug, yamlText, { root: opts.root });
|
|
@@ -84271,12 +84456,12 @@ import {
|
|
|
84271
84456
|
mkdirSync as mkdirSync45,
|
|
84272
84457
|
mkdtempSync as mkdtempSync5,
|
|
84273
84458
|
openSync as openSync15,
|
|
84274
|
-
readFileSync as
|
|
84459
|
+
readFileSync as readFileSync68,
|
|
84275
84460
|
readdirSync as readdirSync30,
|
|
84276
84461
|
realpathSync as realpathSync7,
|
|
84277
84462
|
renameSync as renameSync17,
|
|
84278
84463
|
rmSync as rmSync16,
|
|
84279
|
-
statSync as
|
|
84464
|
+
statSync as statSync32,
|
|
84280
84465
|
writeFileSync as writeFileSync39
|
|
84281
84466
|
} from "node:fs";
|
|
84282
84467
|
import { tmpdir as tmpdir5, homedir as homedir45 } from "node:os";
|
|
@@ -84284,7 +84469,7 @@ import { dirname as dirname23, join as join79, relative as relative2, resolve as
|
|
|
84284
84469
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
84285
84470
|
|
|
84286
84471
|
// src/cli/skill-common.ts
|
|
84287
|
-
var
|
|
84472
|
+
var import_yaml23 = __toESM(require_dist(), 1);
|
|
84288
84473
|
var MAX_FILE_BYTES = 256 * 1024;
|
|
84289
84474
|
var MAX_SKILL_BYTES = 2 * 1024 * 1024;
|
|
84290
84475
|
var MAX_FILES_PER_SKILL = 50;
|
|
@@ -84388,7 +84573,7 @@ function validateSkillMd(content, expectedName) {
|
|
|
84388
84573
|
}
|
|
84389
84574
|
let parsed;
|
|
84390
84575
|
try {
|
|
84391
|
-
parsed =
|
|
84576
|
+
parsed = import_yaml23.parse(fmText);
|
|
84392
84577
|
} catch (e) {
|
|
84393
84578
|
return authorErr("E_SKILL_INVALID_FRONTMATTER", `frontmatter is not valid YAML: ${e.message}`);
|
|
84394
84579
|
}
|
|
@@ -84506,7 +84691,7 @@ function isTarballPath(p) {
|
|
|
84506
84691
|
}
|
|
84507
84692
|
function loadFromDir(dir) {
|
|
84508
84693
|
const abs = realpathSync7(dir);
|
|
84509
|
-
if (!
|
|
84694
|
+
if (!statSync32(abs).isDirectory()) {
|
|
84510
84695
|
fail3(`--from path is not a directory: ${dir}`);
|
|
84511
84696
|
}
|
|
84512
84697
|
const files = {};
|
|
@@ -84523,7 +84708,7 @@ function loadFromDir(dir) {
|
|
|
84523
84708
|
continue;
|
|
84524
84709
|
}
|
|
84525
84710
|
if (ent.isFile()) {
|
|
84526
|
-
const buf =
|
|
84711
|
+
const buf = readFileSync68(full);
|
|
84527
84712
|
files[rel.replace(/\\/g, "/")] = buf.toString("utf-8");
|
|
84528
84713
|
}
|
|
84529
84714
|
}
|
|
@@ -84572,7 +84757,7 @@ function loadFromTarball(tarPath) {
|
|
|
84572
84757
|
}
|
|
84573
84758
|
}
|
|
84574
84759
|
function loadSingleFile(filePath) {
|
|
84575
|
-
const content =
|
|
84760
|
+
const content = readFileSync68(filePath, "utf-8");
|
|
84576
84761
|
return { "SKILL.md": content };
|
|
84577
84762
|
}
|
|
84578
84763
|
function loadFromStdin() {
|
|
@@ -84663,7 +84848,7 @@ function diffSummary(currentDir, files) {
|
|
|
84663
84848
|
if (ent.isDirectory()) {
|
|
84664
84849
|
walk2(full);
|
|
84665
84850
|
} else if (ent.isFile()) {
|
|
84666
|
-
currentFiles[rel.replace(/\\/g, "/")] =
|
|
84851
|
+
currentFiles[rel.replace(/\\/g, "/")] = readFileSync68(full, "utf-8");
|
|
84667
84852
|
}
|
|
84668
84853
|
}
|
|
84669
84854
|
};
|
|
@@ -84766,7 +84951,7 @@ function registerSkillCommand(program3) {
|
|
|
84766
84951
|
if (!existsSync80(fromPath)) {
|
|
84767
84952
|
fail3(`--from path does not exist: ${opts.from}`);
|
|
84768
84953
|
}
|
|
84769
|
-
const st =
|
|
84954
|
+
const st = statSync32(fromPath);
|
|
84770
84955
|
if (st.isDirectory()) {
|
|
84771
84956
|
files = loadFromDir(fromPath);
|
|
84772
84957
|
} else if (isTarballPath(fromPath)) {
|
|
@@ -84823,11 +85008,11 @@ import {
|
|
|
84823
85008
|
mkdirSync as mkdirSync46,
|
|
84824
85009
|
mkdtempSync as mkdtempSync6,
|
|
84825
85010
|
openSync as openSync16,
|
|
84826
|
-
readFileSync as
|
|
85011
|
+
readFileSync as readFileSync69,
|
|
84827
85012
|
readdirSync as readdirSync31,
|
|
84828
85013
|
renameSync as renameSync18,
|
|
84829
85014
|
rmSync as rmSync17,
|
|
84830
|
-
statSync as
|
|
85015
|
+
statSync as statSync33,
|
|
84831
85016
|
utimesSync,
|
|
84832
85017
|
writeFileSync as writeFileSync40
|
|
84833
85018
|
} from "node:fs";
|
|
@@ -84905,7 +85090,7 @@ function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
|
84905
85090
|
if (ent.isDirectory())
|
|
84906
85091
|
walk2(s, d);
|
|
84907
85092
|
else if (ent.isFile()) {
|
|
84908
|
-
writeFileSync40(d,
|
|
85093
|
+
writeFileSync40(d, readFileSync69(s));
|
|
84909
85094
|
}
|
|
84910
85095
|
}
|
|
84911
85096
|
};
|
|
@@ -84968,7 +85153,7 @@ function readStdinSync2() {
|
|
|
84968
85153
|
}
|
|
84969
85154
|
function loadFromDir2(dir) {
|
|
84970
85155
|
const abs = resolve48(dir);
|
|
84971
|
-
if (!
|
|
85156
|
+
if (!statSync33(abs).isDirectory()) {
|
|
84972
85157
|
fail4(`--from path is not a directory: ${dir}`);
|
|
84973
85158
|
}
|
|
84974
85159
|
const files = {};
|
|
@@ -84984,7 +85169,7 @@ function loadFromDir2(dir) {
|
|
|
84984
85169
|
}
|
|
84985
85170
|
if (ent.isFile()) {
|
|
84986
85171
|
const rel = relative3(abs, full).replace(/\\/g, "/");
|
|
84987
|
-
files[rel] =
|
|
85172
|
+
files[rel] = readFileSync69(full, "utf-8");
|
|
84988
85173
|
}
|
|
84989
85174
|
}
|
|
84990
85175
|
};
|
|
@@ -85054,7 +85239,7 @@ function sweepTrash(agentsRoot, agent) {
|
|
|
85054
85239
|
continue;
|
|
85055
85240
|
const entPath = join80(trash, ent.name);
|
|
85056
85241
|
try {
|
|
85057
|
-
const st =
|
|
85242
|
+
const st = statSync33(entPath);
|
|
85058
85243
|
if (now - st.mtimeMs > TRASH_TTL_MS) {
|
|
85059
85244
|
rmSync17(entPath, { recursive: true, force: true });
|
|
85060
85245
|
}
|
|
@@ -85165,12 +85350,12 @@ function loadFiles(opts) {
|
|
|
85165
85350
|
if (!existsSync81(p)) {
|
|
85166
85351
|
fail4(`--from path does not exist: ${opts.from}`);
|
|
85167
85352
|
}
|
|
85168
|
-
const st =
|
|
85353
|
+
const st = statSync33(p);
|
|
85169
85354
|
if (st.isDirectory()) {
|
|
85170
85355
|
return loadFromDir2(p);
|
|
85171
85356
|
}
|
|
85172
85357
|
if (p.endsWith(".md")) {
|
|
85173
|
-
return { "SKILL.md":
|
|
85358
|
+
return { "SKILL.md": readFileSync69(p, "utf-8") };
|
|
85174
85359
|
}
|
|
85175
85360
|
fail4(`--from must be a directory or a .md file. Got: ${opts.from}`);
|
|
85176
85361
|
}
|
|
@@ -85259,7 +85444,7 @@ function readSourceFiles(dir) {
|
|
|
85259
85444
|
fail4(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
85260
85445
|
}
|
|
85261
85446
|
} catch {}
|
|
85262
|
-
files[rel] =
|
|
85447
|
+
files[rel] = readFileSync69(full, "utf-8");
|
|
85263
85448
|
}
|
|
85264
85449
|
}
|
|
85265
85450
|
};
|
|
@@ -85386,7 +85571,7 @@ function listPersonalAction(opts) {
|
|
|
85386
85571
|
if (e.isFile()) {
|
|
85387
85572
|
fileCount += 1;
|
|
85388
85573
|
try {
|
|
85389
|
-
totalBytes +=
|
|
85574
|
+
totalBytes += statSync33(join80(sub, e.name)).size;
|
|
85390
85575
|
} catch {}
|
|
85391
85576
|
} else if (e.isDirectory()) {
|
|
85392
85577
|
walk2(join80(sub, e.name));
|
|
@@ -85427,8 +85612,8 @@ function registerSkillPersonalCommands(program3) {
|
|
|
85427
85612
|
|
|
85428
85613
|
// src/cli/skill-search.ts
|
|
85429
85614
|
init_helpers();
|
|
85430
|
-
var
|
|
85431
|
-
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";
|
|
85432
85617
|
import { homedir as homedir47 } from "node:os";
|
|
85433
85618
|
import { join as join81, resolve as resolve49 } from "node:path";
|
|
85434
85619
|
var PERSONAL_PREFIX2 = "personal-";
|
|
@@ -85449,7 +85634,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
85449
85634
|
return null;
|
|
85450
85635
|
let content;
|
|
85451
85636
|
try {
|
|
85452
|
-
content =
|
|
85637
|
+
content = readFileSync70(mdPath, "utf-8");
|
|
85453
85638
|
} catch {
|
|
85454
85639
|
return null;
|
|
85455
85640
|
}
|
|
@@ -85467,7 +85652,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
85467
85652
|
const fmText = rest.slice(0, endIdx);
|
|
85468
85653
|
let parsed;
|
|
85469
85654
|
try {
|
|
85470
|
-
parsed =
|
|
85655
|
+
parsed = import_yaml24.parse(fmText);
|
|
85471
85656
|
} catch (e) {
|
|
85472
85657
|
return { error: `yaml parse: ${e.message}` };
|
|
85473
85658
|
}
|
|
@@ -85479,7 +85664,7 @@ function readSkillFrontmatter(skillDir) {
|
|
|
85479
85664
|
function statSkillMd(skillDir) {
|
|
85480
85665
|
const mdPath = join81(skillDir, "SKILL.md");
|
|
85481
85666
|
try {
|
|
85482
|
-
const st =
|
|
85667
|
+
const st = statSync34(mdPath);
|
|
85483
85668
|
return { size: st.size, mtime: st.mtime.toISOString() };
|
|
85484
85669
|
} catch {
|
|
85485
85670
|
return null;
|
|
@@ -85503,7 +85688,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
85503
85688
|
continue;
|
|
85504
85689
|
const dirPath = join81(skillsDir, ent);
|
|
85505
85690
|
try {
|
|
85506
|
-
if (!
|
|
85691
|
+
if (!statSync34(dirPath).isDirectory())
|
|
85507
85692
|
continue;
|
|
85508
85693
|
} catch {
|
|
85509
85694
|
continue;
|
|
@@ -85543,7 +85728,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
|
85543
85728
|
continue;
|
|
85544
85729
|
const dirPath = join81(sharedRoot, ent);
|
|
85545
85730
|
try {
|
|
85546
|
-
if (!
|
|
85731
|
+
if (!statSync34(dirPath).isDirectory())
|
|
85547
85732
|
continue;
|
|
85548
85733
|
} catch {
|
|
85549
85734
|
continue;
|
|
@@ -85579,7 +85764,7 @@ function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
|
85579
85764
|
continue;
|
|
85580
85765
|
const dirPath = join81(bundledRoot, ent);
|
|
85581
85766
|
try {
|
|
85582
|
-
if (!
|
|
85767
|
+
if (!statSync34(dirPath).isDirectory())
|
|
85583
85768
|
continue;
|
|
85584
85769
|
} catch {
|
|
85585
85770
|
continue;
|
|
@@ -85721,7 +85906,7 @@ function registerHostdMcpCommand(program3) {
|
|
|
85721
85906
|
// src/cli/hostd.ts
|
|
85722
85907
|
init_source();
|
|
85723
85908
|
init_helpers();
|
|
85724
|
-
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";
|
|
85725
85910
|
import { homedir as homedir48 } from "node:os";
|
|
85726
85911
|
import { join as join82 } from "node:path";
|
|
85727
85912
|
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
@@ -85957,7 +86142,7 @@ function doStatus() {
|
|
|
85957
86142
|
continue;
|
|
85958
86143
|
const sockPath = join82(dir, name, "sock");
|
|
85959
86144
|
if (existsSync84(sockPath)) {
|
|
85960
|
-
const st =
|
|
86145
|
+
const st = statSync35(sockPath);
|
|
85961
86146
|
if ((st.mode & 61440) === 49152) {
|
|
85962
86147
|
entries.push(`${name} \u2192 ${sockPath}`);
|
|
85963
86148
|
}
|
|
@@ -86004,7 +86189,7 @@ function registerHostdCommand(program3) {
|
|
|
86004
86189
|
The log is created when hostd handles its first privileged-verb request.`));
|
|
86005
86190
|
return;
|
|
86006
86191
|
}
|
|
86007
|
-
const raw =
|
|
86192
|
+
const raw = readFileSync72(logPath, "utf-8");
|
|
86008
86193
|
const limit = Math.max(1, parseInt(opts.tail ?? "50", 10) || 50);
|
|
86009
86194
|
const filters = {
|
|
86010
86195
|
agent: opts.agent,
|