@switchboard-mcp/core 0.1.0

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.
Files changed (74) hide show
  1. package/README.md +9 -0
  2. package/dist/approvals/approval-requests.d.ts +151 -0
  3. package/dist/approvals/approval-requests.d.ts.map +1 -0
  4. package/dist/approvals/approval-requests.js +393 -0
  5. package/dist/approvals/approval-requests.js.map +1 -0
  6. package/dist/audit/audit-log.d.ts +45 -0
  7. package/dist/audit/audit-log.d.ts.map +1 -0
  8. package/dist/audit/audit-log.js +88 -0
  9. package/dist/audit/audit-log.js.map +1 -0
  10. package/dist/config/gitignore.d.ts +9 -0
  11. package/dist/config/gitignore.d.ts.map +1 -0
  12. package/dist/config/gitignore.js +90 -0
  13. package/dist/config/gitignore.js.map +1 -0
  14. package/dist/config/load-config.d.ts +28 -0
  15. package/dist/config/load-config.d.ts.map +1 -0
  16. package/dist/config/load-config.js +138 -0
  17. package/dist/config/load-config.js.map +1 -0
  18. package/dist/config/paths.d.ts +13 -0
  19. package/dist/config/paths.d.ts.map +1 -0
  20. package/dist/config/paths.js +38 -0
  21. package/dist/config/paths.js.map +1 -0
  22. package/dist/daemon/daemon-state.d.ts +44 -0
  23. package/dist/daemon/daemon-state.d.ts.map +1 -0
  24. package/dist/daemon/daemon-state.js +110 -0
  25. package/dist/daemon/daemon-state.js.map +1 -0
  26. package/dist/import/import-plan.d.ts +92 -0
  27. package/dist/import/import-plan.d.ts.map +1 -0
  28. package/dist/import/import-plan.js +677 -0
  29. package/dist/import/import-plan.js.map +1 -0
  30. package/dist/index.d.ts +18 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +18 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/install/client-config.d.ts +73 -0
  35. package/dist/install/client-config.d.ts.map +1 -0
  36. package/dist/install/client-config.js +514 -0
  37. package/dist/install/client-config.js.map +1 -0
  38. package/dist/mandates/mandates.d.ts +244 -0
  39. package/dist/mandates/mandates.d.ts.map +1 -0
  40. package/dist/mandates/mandates.js +690 -0
  41. package/dist/mandates/mandates.js.map +1 -0
  42. package/dist/namespaces/namespaces.d.ts +16 -0
  43. package/dist/namespaces/namespaces.d.ts.map +1 -0
  44. package/dist/namespaces/namespaces.js +49 -0
  45. package/dist/namespaces/namespaces.js.map +1 -0
  46. package/dist/onboarding/init-config.d.ts +19 -0
  47. package/dist/onboarding/init-config.d.ts.map +1 -0
  48. package/dist/onboarding/init-config.js +85 -0
  49. package/dist/onboarding/init-config.js.map +1 -0
  50. package/dist/providers/provider-add.d.ts +25 -0
  51. package/dist/providers/provider-add.d.ts.map +1 -0
  52. package/dist/providers/provider-add.js +179 -0
  53. package/dist/providers/provider-add.js.map +1 -0
  54. package/dist/providers/provider-templates.d.ts +96 -0
  55. package/dist/providers/provider-templates.d.ts.map +1 -0
  56. package/dist/providers/provider-templates.js +556 -0
  57. package/dist/providers/provider-templates.js.map +1 -0
  58. package/dist/scan/scan.d.ts +67 -0
  59. package/dist/scan/scan.d.ts.map +1 -0
  60. package/dist/scan/scan.js +389 -0
  61. package/dist/scan/scan.js.map +1 -0
  62. package/dist/schemas/config.d.ts +197 -0
  63. package/dist/schemas/config.d.ts.map +1 -0
  64. package/dist/schemas/config.js +133 -0
  65. package/dist/schemas/config.js.map +1 -0
  66. package/dist/secrets/secret-refs.d.ts +10 -0
  67. package/dist/secrets/secret-refs.d.ts.map +1 -0
  68. package/dist/secrets/secret-refs.js +32 -0
  69. package/dist/secrets/secret-refs.js.map +1 -0
  70. package/dist/secrets/secrets.d.ts +63 -0
  71. package/dist/secrets/secrets.d.ts.map +1 -0
  72. package/dist/secrets/secrets.js +301 -0
  73. package/dist/secrets/secrets.js.map +1 -0
  74. package/package.json +42 -0
@@ -0,0 +1,88 @@
1
+ import { appendFile, chmod, mkdir, readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ export function resolveAuditLogPath(options = {}) {
5
+ const env = options.env ?? process.env;
6
+ const home = options.homeDir ?? homedir();
7
+ const stateRoot = env.XDG_STATE_HOME
8
+ ? resolve(env.XDG_STATE_HOME)
9
+ : join(home, ".local", "state");
10
+ return join(stateRoot, "switchboard", "logs", "switchboard.jsonl");
11
+ }
12
+ export function createJsonlAuditLogger(options = {}) {
13
+ const path = options.path ?? resolveAuditLogPath(options);
14
+ const now = options.now ?? (() => new Date());
15
+ return {
16
+ async log(entry) {
17
+ await mkdir(dirname(path), { recursive: true, mode: 0o700 });
18
+ const completeEntry = {
19
+ version: 1,
20
+ timestamp: now().toISOString(),
21
+ ...redactAuditEntry(entry)
22
+ };
23
+ await appendFile(path, `${JSON.stringify(completeEntry)}\n`, {
24
+ mode: 0o600
25
+ });
26
+ await chmod(path, 0o600);
27
+ }
28
+ };
29
+ }
30
+ export const noopAuditLogger = {
31
+ async log() {
32
+ // Intentionally empty for tests and embeddings that do not want disk writes.
33
+ }
34
+ };
35
+ export async function safeAuditLog(logger, entry, options = {}) {
36
+ try {
37
+ await logger.log(entry);
38
+ }
39
+ catch (error) {
40
+ options.onError?.(error);
41
+ }
42
+ }
43
+ export async function readAuditLogEntries(options = {}) {
44
+ const path = options.path ?? resolveAuditLogPath();
45
+ const raw = await readFile(path, "utf8").catch((error) => {
46
+ if (typeof error === "object" &&
47
+ error !== null &&
48
+ "code" in error &&
49
+ error.code === "ENOENT") {
50
+ return "";
51
+ }
52
+ throw error;
53
+ });
54
+ const entries = raw
55
+ .split("\n")
56
+ .filter(Boolean)
57
+ .flatMap((line) => parseAuditLogLine(line))
58
+ .filter((entry) => options.mandateId ? entry.mandateId === options.mandateId : true);
59
+ if (options.limit === undefined) {
60
+ return entries;
61
+ }
62
+ return entries.slice(Math.max(entries.length - options.limit, 0));
63
+ }
64
+ function redactAuditEntry(entry) {
65
+ if (!entry.error) {
66
+ return entry;
67
+ }
68
+ return { ...entry, error: redactSecretLikeText(entry.error) };
69
+ }
70
+ function redactSecretLikeText(value) {
71
+ return value
72
+ .replace(/https?:\/\/([^/\s:@]+):([^/\s@]+)@/gi, "https://[redacted]@")
73
+ .replace(/\b(sk-[A-Za-z0-9_-]{8,})\b/g, "[redacted]")
74
+ .replace(/\b(gh[pousr]_[A-Za-z0-9_]{8,})\b/g, "[redacted]")
75
+ .replace(/\b(xox[baprs]-[A-Za-z0-9-]{8,})\b/g, "[redacted]")
76
+ .replace(/\b(authorization\s*:\s*bearer)\s+[A-Za-z0-9._~+/=-]+/gi, "$1 [redacted]")
77
+ .replace(/"((?:api[_-]?key|token|secret|password))"\s*:\s*"[^"]+"/gi, '"$1":"[redacted]"')
78
+ .replace(/\b(token|secret|password|api[_-]?key)=\S+/gi, "$1=[redacted]");
79
+ }
80
+ function parseAuditLogLine(line) {
81
+ try {
82
+ return [JSON.parse(line)];
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
88
+ //# sourceMappingURL=audit-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-log.js","sourceRoot":"","sources":["../../src/audit/audit-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export interface LocalConfigIgnoreCheck {
2
+ ok: boolean;
3
+ method: "git-check-ignore" | "gitignore-file" | "not-found";
4
+ message: string;
5
+ localConfigPresent: boolean;
6
+ localConfigPath?: string;
7
+ }
8
+ export declare function checkLocalConfigIgnored(cwd?: string): LocalConfigIgnoreCheck;
9
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/config/gitignore.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,kBAAkB,GAAG,gBAAgB,GAAG,WAAW,CAAC;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,uBAAuB,CAAC,GAAG,SAAgB,GAAG,sBAAsB,CAmEnF"}
@@ -0,0 +1,90 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { dirname, join, parse, resolve } from "node:path";
4
+ export function checkLocalConfigIgnored(cwd = process.cwd()) {
5
+ const resolvedCwd = resolve(cwd);
6
+ const localConfigPath = findNearestLocalConfig(resolvedCwd);
7
+ const localConfigPresent = localConfigPath !== undefined;
8
+ const ignoreProbePath = localConfigPath ?? join(resolvedCwd, ".switchboard.local.yaml");
9
+ try {
10
+ execFileSync("git", ["check-ignore", ignoreProbePath], {
11
+ cwd: resolvedCwd,
12
+ stdio: "ignore"
13
+ });
14
+ return {
15
+ ok: true,
16
+ method: "git-check-ignore",
17
+ message: ".switchboard.local.yaml is ignored by git.",
18
+ localConfigPresent,
19
+ ...(localConfigPath ? { localConfigPath } : {})
20
+ };
21
+ }
22
+ catch {
23
+ // Fall through to a text check for fresh folders or environments without git metadata.
24
+ }
25
+ const gitignorePath = findNearestGitignore(localConfigPath ? dirname(localConfigPath) : resolvedCwd);
26
+ if (!gitignorePath) {
27
+ if (!localConfigPresent) {
28
+ return {
29
+ ok: true,
30
+ method: "not-found",
31
+ message: "No .switchboard.local.yaml found. Add it to .gitignore before storing local overrides.",
32
+ localConfigPresent
33
+ };
34
+ }
35
+ return {
36
+ ok: false,
37
+ method: "not-found",
38
+ message: "No .gitignore found. Add .switchboard.local.yaml before storing local overrides.",
39
+ localConfigPresent,
40
+ ...(localConfigPath ? { localConfigPath } : {})
41
+ };
42
+ }
43
+ const lines = readFileSync(gitignorePath, "utf8")
44
+ .split(/\r?\n/)
45
+ .map((line) => line.trim())
46
+ .filter((line) => line.length > 0 && !line.startsWith("#"));
47
+ const ignored = lines.includes(".switchboard.local.yaml") ||
48
+ lines.includes("/.switchboard.local.yaml");
49
+ const ok = ignored || !localConfigPresent;
50
+ return {
51
+ ok,
52
+ method: "gitignore-file",
53
+ message: ok
54
+ ? ignored
55
+ ? ".switchboard.local.yaml is listed in .gitignore."
56
+ : "No .switchboard.local.yaml found. Add it to .gitignore before storing local overrides."
57
+ : "Add .switchboard.local.yaml to .gitignore before storing local overrides.",
58
+ localConfigPresent,
59
+ ...(localConfigPath ? { localConfigPath } : {})
60
+ };
61
+ }
62
+ function findNearestGitignore(cwd) {
63
+ let current = cwd;
64
+ const root = parse(current).root;
65
+ while (true) {
66
+ const candidate = join(current, ".gitignore");
67
+ if (existsSync(candidate)) {
68
+ return candidate;
69
+ }
70
+ if (current === root) {
71
+ return undefined;
72
+ }
73
+ current = dirname(current);
74
+ }
75
+ }
76
+ function findNearestLocalConfig(cwd) {
77
+ let current = cwd;
78
+ const root = parse(current).root;
79
+ while (true) {
80
+ const candidate = join(current, ".switchboard.local.yaml");
81
+ if (existsSync(candidate)) {
82
+ return candidate;
83
+ }
84
+ if (current === root) {
85
+ return undefined;
86
+ }
87
+ current = dirname(current);
88
+ }
89
+ }
90
+ //# sourceMappingURL=gitignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.js","sourceRoot":"","sources":["../../src/config/gitignore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { detectNamespaceCollisions } from "../namespaces/namespaces.js";
2
+ import { type SwitchboardConfig } from "../schemas/config.js";
3
+ import { type PathResolutionOptions } from "./paths.js";
4
+ export type ConfigSourceKind = "built-in" | "global" | "repo" | "repo-local" | "environment" | "cli";
5
+ export interface ConfigSource {
6
+ kind: ConfigSourceKind;
7
+ path?: string;
8
+ loaded: boolean;
9
+ }
10
+ export interface ConfigDiagnostic {
11
+ level: "error" | "warning" | "info";
12
+ message: string;
13
+ source?: ConfigSourceKind;
14
+ path?: string;
15
+ }
16
+ export interface LoadConfigOptions extends PathResolutionOptions {
17
+ cliOverrides?: Record<string, unknown>;
18
+ }
19
+ export interface LoadedConfig {
20
+ config: SwitchboardConfig;
21
+ sources: ConfigSource[];
22
+ diagnostics: ConfigDiagnostic[];
23
+ namespaceCollisions: ReturnType<typeof detectNamespaceCollisions>;
24
+ }
25
+ export declare function loadSwitchboardConfig(options?: LoadConfigOptions): LoadedConfig;
26
+ export declare function configFromEnvironment(env: NodeJS.ProcessEnv): Record<string, unknown>;
27
+ export declare function deepMerge(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown>;
28
+ //# sourceMappingURL=load-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-config.d.ts","sourceRoot":"","sources":["../../src/config/load-config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,KAAK,qBAAqB,EAG3B,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,gBAAgB,GACxB,UAAU,GACV,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,aAAa,GACb,KAAK,CAAC;AAEV,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAkB,SAAQ,qBAAqB;IAC9D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,EAAE,gBAAgB,EAAE,CAAC;IAChC,mBAAmB,EAAE,UAAU,CAAC,OAAO,yBAAyB,CAAC,CAAC;CACnE;AAeD,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,iBAAsB,GAC9B,YAAY,CAuFd;AAED,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAUzB;AAED,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB"}
@@ -0,0 +1,138 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { parse as parseYaml } from "yaml";
3
+ import { detectNamespaceCollisions } from "../namespaces/namespaces.js";
4
+ import { switchboardConfigSchema } from "../schemas/config.js";
5
+ import { resolveGlobalConfigPath, resolveRepoConfigPaths } from "./paths.js";
6
+ const builtInConfig = {
7
+ version: 1,
8
+ defaults: {
9
+ auditLog: true,
10
+ confirmDestructive: true,
11
+ hideDisabledProfiles: true,
12
+ toolNameFormat: "{namespace}_{tool}"
13
+ },
14
+ profiles: {},
15
+ workspaces: {},
16
+ policies: {}
17
+ };
18
+ export function loadSwitchboardConfig(options = {}) {
19
+ const env = options.env ?? process.env;
20
+ const diagnostics = [];
21
+ const sources = [{ kind: "built-in", loaded: true }];
22
+ const rawConfigs = [builtInConfig];
23
+ const globalPath = resolveGlobalConfigPath(options);
24
+ const repoPaths = resolveRepoConfigPaths(options);
25
+ pushFileConfig("global", globalPath);
26
+ if (repoPaths.repoConfigPath) {
27
+ pushFileConfig("repo", repoPaths.repoConfigPath);
28
+ }
29
+ else {
30
+ sources.push({ kind: "repo", loaded: false });
31
+ }
32
+ if (repoPaths.repoLocalConfigPath) {
33
+ pushFileConfig("repo-local", repoPaths.repoLocalConfigPath);
34
+ }
35
+ else {
36
+ sources.push({ kind: "repo-local", loaded: false });
37
+ }
38
+ const envConfig = configFromEnvironment(env);
39
+ sources.push({ kind: "environment", loaded: Object.keys(envConfig).length > 0 });
40
+ rawConfigs.push(envConfig);
41
+ const cliConfig = options.cliOverrides ?? {};
42
+ sources.push({ kind: "cli", loaded: Object.keys(cliConfig).length > 0 });
43
+ rawConfigs.push(cliConfig);
44
+ const merged = rawConfigs.reduce((current, next) => deepMerge(current, next), {});
45
+ const parsed = switchboardConfigSchema.safeParse(merged);
46
+ if (!parsed.success) {
47
+ for (const issue of parsed.error.issues) {
48
+ diagnostics.push({
49
+ level: "error",
50
+ message: `${issue.path.join(".") || "config"}: ${issue.message}`
51
+ });
52
+ }
53
+ return {
54
+ config: builtInConfig,
55
+ sources,
56
+ diagnostics,
57
+ namespaceCollisions: []
58
+ };
59
+ }
60
+ const namespaceCollisions = detectNamespaceCollisions(parsed.data.profiles);
61
+ for (const collision of namespaceCollisions) {
62
+ diagnostics.push({
63
+ level: "error",
64
+ message: `Namespace "${collision.namespace}" is used by profiles: ${collision.profiles.join(", ")}`
65
+ });
66
+ }
67
+ return {
68
+ config: parsed.data,
69
+ sources,
70
+ diagnostics,
71
+ namespaceCollisions
72
+ };
73
+ function pushFileConfig(kind, path) {
74
+ if (!existsSync(path)) {
75
+ sources.push({ kind, path, loaded: false });
76
+ return;
77
+ }
78
+ try {
79
+ const raw = readFileSync(path, "utf8");
80
+ const parsedYaml = raw.trim().length === 0 ? {} : parseYaml(raw);
81
+ rawConfigs.push(asConfigObject(parsedYaml, path, kind, diagnostics));
82
+ sources.push({ kind, path, loaded: true });
83
+ }
84
+ catch (error) {
85
+ diagnostics.push({
86
+ level: "error",
87
+ source: kind,
88
+ path,
89
+ message: error instanceof Error ? error.message : String(error)
90
+ });
91
+ sources.push({ kind, path, loaded: false });
92
+ }
93
+ }
94
+ }
95
+ export function configFromEnvironment(env) {
96
+ const defaults = {};
97
+ if (env.SWITCHBOARD_DEFAULT_ENVIRONMENT) {
98
+ defaults.defaultEnvironment = env.SWITCHBOARD_DEFAULT_ENVIRONMENT;
99
+ }
100
+ if (env.SWITCHBOARD_ACTIVE_PROFILE) {
101
+ defaults.activeProfile = env.SWITCHBOARD_ACTIVE_PROFILE;
102
+ }
103
+ return Object.keys(defaults).length > 0 ? { defaults } : {};
104
+ }
105
+ export function deepMerge(base, override) {
106
+ const result = { ...base };
107
+ for (const [key, value] of Object.entries(override)) {
108
+ const baseValue = result[key];
109
+ if (isPlainObject(baseValue) && isPlainObject(value)) {
110
+ result[key] = deepMerge(baseValue, value);
111
+ }
112
+ else {
113
+ result[key] = value;
114
+ }
115
+ }
116
+ return result;
117
+ }
118
+ function asConfigObject(value, path, kind, diagnostics) {
119
+ if (value === null || value === undefined) {
120
+ return {};
121
+ }
122
+ if (!isPlainObject(value)) {
123
+ diagnostics.push({
124
+ level: "error",
125
+ source: kind,
126
+ path,
127
+ message: "Config root must be a YAML mapping."
128
+ });
129
+ return {};
130
+ }
131
+ return value;
132
+ }
133
+ function isPlainObject(value) {
134
+ return (typeof value === "object" &&
135
+ value !== null &&
136
+ !Array.isArray(value));
137
+ }
138
+ //# sourceMappingURL=load-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-config.js","sourceRoot":"","sources":["../../src/config/load-config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface PathResolutionOptions {
2
+ cwd?: string;
3
+ env?: NodeJS.ProcessEnv;
4
+ homeDir?: string;
5
+ }
6
+ export interface RepoConfigPaths {
7
+ repoConfigPath?: string;
8
+ repoLocalConfigPath?: string;
9
+ }
10
+ export declare function resolveGlobalConfigPath(options?: PathResolutionOptions): string;
11
+ export declare function findNearestFile(fileName: string, options?: PathResolutionOptions): string | undefined;
12
+ export declare function resolveRepoConfigPaths(options?: PathResolutionOptions): RepoConfigPaths;
13
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/config/paths.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,qBAA0B,GAClC,MAAM,CAQR;AAED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,qBAA0B,GAClC,MAAM,GAAG,SAAS,CAgBpB;AAED,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,qBAA0B,GAClC,eAAe,CAiBjB"}
@@ -0,0 +1,38 @@
1
+ import { existsSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, parse, resolve } from "node:path";
4
+ export function resolveGlobalConfigPath(options = {}) {
5
+ const env = options.env ?? process.env;
6
+ const home = options.homeDir ?? homedir();
7
+ const configRoot = env.XDG_CONFIG_HOME
8
+ ? resolve(env.XDG_CONFIG_HOME)
9
+ : join(home, ".config");
10
+ return join(configRoot, "switchboard", "config.yaml");
11
+ }
12
+ export function findNearestFile(fileName, options = {}) {
13
+ let current = resolve(options.cwd ?? process.cwd());
14
+ const root = parse(current).root;
15
+ while (true) {
16
+ const candidate = join(current, fileName);
17
+ if (existsSync(candidate)) {
18
+ return candidate;
19
+ }
20
+ if (current === root) {
21
+ return undefined;
22
+ }
23
+ current = dirname(current);
24
+ }
25
+ }
26
+ export function resolveRepoConfigPaths(options = {}) {
27
+ const repoConfigPath = findNearestFile(".switchboard.yaml", options);
28
+ const repoLocalConfigPath = findNearestFile(".switchboard.local.yaml", options);
29
+ const paths = {};
30
+ if (repoConfigPath) {
31
+ paths.repoConfigPath = repoConfigPath;
32
+ }
33
+ if (repoLocalConfigPath) {
34
+ paths.repoLocalConfigPath = repoLocalConfigPath;
35
+ }
36
+ return paths;
37
+ }
38
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/config/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAa1D,MAAM,UAAU,uBAAuB,CACrC,UAAiC,EAAE;IAEnC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;IAC1C,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe;QACpC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;QAC9B,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAE1B,OAAO,IAAI,CAAC,UAAU,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,UAAiC,EAAE;IAEnC,IAAI,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC;IAEjC,OAAO,IAAI,EAAE,CAAC;QACZ,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC1B,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,sBAAsB,CACpC,UAAiC,EAAE;IAEnC,MAAM,cAAc,GAAG,eAAe,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;IACrE,MAAM,mBAAmB,GAAG,eAAe,CACzC,yBAAyB,EACzB,OAAO,CACR,CAAC;IACF,MAAM,KAAK,GAAoB,EAAE,CAAC;IAElC,IAAI,cAAc,EAAE,CAAC;QACnB,KAAK,CAAC,cAAc,GAAG,cAAc,CAAC;IACxC,CAAC;IAED,IAAI,mBAAmB,EAAE,CAAC;QACxB,KAAK,CAAC,mBAAmB,GAAG,mBAAmB,CAAC;IAClD,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,44 @@
1
+ import type { PathResolutionOptions } from "../config/paths.js";
2
+ export interface DaemonPaths {
3
+ runtimeDir: string;
4
+ socketPath: string;
5
+ statePath: string;
6
+ }
7
+ export interface DaemonState {
8
+ version: 1;
9
+ pid: number;
10
+ startedAt: string;
11
+ socketPath: string;
12
+ cwd?: string;
13
+ }
14
+ export type DaemonStatus = {
15
+ state: "not-running";
16
+ paths: DaemonPaths;
17
+ } | {
18
+ state: "running";
19
+ paths: DaemonPaths;
20
+ daemon: DaemonState;
21
+ } | {
22
+ state: "stale";
23
+ paths: DaemonPaths;
24
+ daemon: DaemonState;
25
+ } | {
26
+ state: "invalid";
27
+ paths: DaemonPaths;
28
+ error: string;
29
+ };
30
+ export interface ResolveDaemonPathsOptions extends PathResolutionOptions {
31
+ runtimeDir?: string;
32
+ }
33
+ export declare function resolveDaemonPaths(options?: ResolveDaemonPathsOptions): DaemonPaths;
34
+ export declare function writeDaemonState(state: DaemonState, paths: DaemonPaths): Promise<void>;
35
+ export declare function removeDaemonState(paths: DaemonPaths): Promise<void>;
36
+ export declare function getDaemonStatus(paths: DaemonPaths): DaemonStatus;
37
+ export declare function readDaemonState(paths: DaemonPaths): Promise<DaemonState | undefined>;
38
+ export declare function createDaemonState(options: {
39
+ pid: number;
40
+ socketPath: string;
41
+ startedAt?: Date;
42
+ cwd?: string;
43
+ }): DaemonState;
44
+ //# sourceMappingURL=daemon-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-state.d.ts","sourceRoot":"","sources":["../../src/daemon/daemon-state.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAEhE,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,CAAC,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,YAAY,GACpB;IACE,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,WAAW,CAAC;CACpB,GACD;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB,GACD;IACE,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,MAAM,EAAE,WAAW,CAAC;CACrB,GACD;IACE,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEN,MAAM,WAAW,yBAA0B,SAAQ,qBAAqB;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,WAAW,CAeb;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,WAAW,EAClB,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzE;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,CAqBhE;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAelC;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,WAAW,CAYd"}
@@ -0,0 +1,110 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join, resolve } from "node:path";
5
+ export function resolveDaemonPaths(options = {}) {
6
+ const env = options.env ?? process.env;
7
+ const runtimeRoot = options.runtimeDir
8
+ ? resolve(options.runtimeDir)
9
+ : env.SWITCHBOARD_RUNTIME_DIR
10
+ ? resolve(env.SWITCHBOARD_RUNTIME_DIR)
11
+ : env.XDG_RUNTIME_DIR
12
+ ? join(resolve(env.XDG_RUNTIME_DIR), "switchboard")
13
+ : join(tmpdir(), `switchboard-${currentUid()}`);
14
+ return {
15
+ runtimeDir: runtimeRoot,
16
+ socketPath: join(runtimeRoot, "daemon.sock"),
17
+ statePath: join(runtimeRoot, "daemon.json")
18
+ };
19
+ }
20
+ export async function writeDaemonState(state, paths) {
21
+ await mkdir(paths.runtimeDir, { recursive: true, mode: 0o700 });
22
+ await writeFile(paths.statePath, `${JSON.stringify(state, null, 2)}\n`, {
23
+ mode: 0o600
24
+ });
25
+ }
26
+ export async function removeDaemonState(paths) {
27
+ await Promise.all([
28
+ rm(paths.statePath, { force: true }),
29
+ rm(paths.socketPath, { force: true })
30
+ ]);
31
+ }
32
+ export function getDaemonStatus(paths) {
33
+ if (!existsSync(paths.statePath)) {
34
+ return { state: "not-running", paths };
35
+ }
36
+ let daemon;
37
+ try {
38
+ daemon = parseDaemonState(readFileSync(paths.statePath, "utf8"));
39
+ }
40
+ catch (error) {
41
+ return {
42
+ state: "invalid",
43
+ paths,
44
+ error: error instanceof Error ? error.message : String(error)
45
+ };
46
+ }
47
+ if (!isProcessRunning(daemon.pid) || !existsSync(daemon.socketPath)) {
48
+ return { state: "stale", paths, daemon };
49
+ }
50
+ return { state: "running", paths, daemon };
51
+ }
52
+ export async function readDaemonState(paths) {
53
+ const raw = await readFile(paths.statePath, "utf8").catch((error) => {
54
+ if (typeof error === "object" &&
55
+ error !== null &&
56
+ "code" in error &&
57
+ error.code === "ENOENT") {
58
+ return undefined;
59
+ }
60
+ throw error;
61
+ });
62
+ return raw === undefined ? undefined : parseDaemonState(raw);
63
+ }
64
+ export function createDaemonState(options) {
65
+ const state = {
66
+ version: 1,
67
+ pid: options.pid,
68
+ socketPath: options.socketPath,
69
+ startedAt: (options.startedAt ?? new Date()).toISOString()
70
+ };
71
+ if (options.cwd) {
72
+ state.cwd = resolve(options.cwd);
73
+ }
74
+ return state;
75
+ }
76
+ function parseDaemonState(raw) {
77
+ const parsed = JSON.parse(raw);
78
+ if (parsed.version !== 1) {
79
+ throw new Error("Daemon state file is invalid.");
80
+ }
81
+ if (typeof parsed.pid !== "number" ||
82
+ !Number.isSafeInteger(parsed.pid) ||
83
+ parsed.pid <= 0) {
84
+ throw new Error("Daemon state file has an invalid pid.");
85
+ }
86
+ if (typeof parsed.startedAt !== "string" ||
87
+ typeof parsed.socketPath !== "string") {
88
+ throw new Error("Daemon state file is invalid.");
89
+ }
90
+ if (parsed.cwd !== undefined && typeof parsed.cwd !== "string") {
91
+ throw new Error("Daemon state file is invalid.");
92
+ }
93
+ return parsed;
94
+ }
95
+ function isProcessRunning(pid) {
96
+ try {
97
+ process.kill(pid, 0);
98
+ return true;
99
+ }
100
+ catch (error) {
101
+ return (typeof error === "object" &&
102
+ error !== null &&
103
+ "code" in error &&
104
+ error.code === "EPERM");
105
+ }
106
+ }
107
+ function currentUid() {
108
+ return typeof process.getuid === "function" ? String(process.getuid()) : "user";
109
+ }
110
+ //# sourceMappingURL=daemon-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-state.js","sourceRoot":"","sources":["../../src/daemon/daemon-state.ts"],"names":[],"mappings":"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"}