sigild 0.0.1 → 0.0.2

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 (143) hide show
  1. package/README.md +107 -34
  2. package/THREAT_MODEL.md +32 -15
  3. package/dist/src/bin/sigil-hook-post.d.ts +3 -0
  4. package/dist/src/bin/sigil-hook-post.d.ts.map +1 -0
  5. package/dist/src/bin/sigil-hook-post.js +15 -0
  6. package/dist/src/bin/sigil-hook-post.js.map +1 -0
  7. package/dist/src/bin/sigil-hook-pre.d.ts +3 -0
  8. package/dist/src/bin/sigil-hook-pre.d.ts.map +1 -0
  9. package/dist/src/bin/sigil-hook-pre.js +18 -0
  10. package/dist/src/bin/sigil-hook-pre.js.map +1 -0
  11. package/dist/src/bin/sigil-mcp.d.ts +3 -0
  12. package/dist/src/bin/sigil-mcp.d.ts.map +1 -0
  13. package/dist/src/bin/sigil-mcp.js +87 -0
  14. package/dist/src/bin/sigil-mcp.js.map +1 -0
  15. package/dist/src/bin/sigil.d.ts +3 -0
  16. package/dist/src/bin/sigil.d.ts.map +1 -0
  17. package/dist/src/bin/sigil.js +9 -0
  18. package/dist/src/bin/sigil.js.map +1 -0
  19. package/dist/src/cli/args.d.ts +26 -0
  20. package/dist/src/cli/args.d.ts.map +1 -0
  21. package/dist/src/cli/args.js +36 -0
  22. package/dist/src/cli/args.js.map +1 -0
  23. package/dist/src/cli/index.d.ts +7 -0
  24. package/dist/src/cli/index.d.ts.map +1 -0
  25. package/dist/src/cli/index.js +7 -0
  26. package/dist/src/cli/index.js.map +1 -0
  27. package/dist/src/cli/main.d.ts +26 -0
  28. package/dist/src/cli/main.d.ts.map +1 -0
  29. package/dist/src/cli/main.js +152 -0
  30. package/dist/src/cli/main.js.map +1 -0
  31. package/dist/src/cli/paths.d.ts +17 -0
  32. package/dist/src/cli/paths.d.ts.map +1 -0
  33. package/dist/src/cli/paths.js +12 -0
  34. package/dist/src/cli/paths.js.map +1 -0
  35. package/dist/src/cli/portal.d.ts +50 -0
  36. package/dist/src/cli/portal.d.ts.map +1 -0
  37. package/dist/src/cli/portal.js +93 -0
  38. package/dist/src/cli/portal.js.map +1 -0
  39. package/dist/src/cli/status.d.ts +28 -0
  40. package/dist/src/cli/status.d.ts.map +1 -0
  41. package/dist/src/cli/status.js +59 -0
  42. package/dist/src/cli/status.js.map +1 -0
  43. package/dist/src/cli/unlock.d.ts +36 -0
  44. package/dist/src/cli/unlock.d.ts.map +1 -0
  45. package/dist/src/cli/unlock.js +77 -0
  46. package/dist/src/cli/unlock.js.map +1 -0
  47. package/dist/src/control/client.d.ts +26 -0
  48. package/dist/src/control/client.d.ts.map +1 -0
  49. package/dist/src/control/client.js +76 -0
  50. package/dist/src/control/client.js.map +1 -0
  51. package/dist/src/control/index.d.ts +4 -0
  52. package/dist/src/control/index.d.ts.map +1 -0
  53. package/dist/src/control/index.js +4 -0
  54. package/dist/src/control/index.js.map +1 -0
  55. package/dist/src/control/protocol.d.ts +54 -0
  56. package/dist/src/control/protocol.d.ts.map +1 -0
  57. package/dist/src/control/protocol.js +60 -0
  58. package/dist/src/control/protocol.js.map +1 -0
  59. package/dist/src/control/server.d.ts +52 -0
  60. package/dist/src/control/server.d.ts.map +1 -0
  61. package/dist/src/control/server.js +199 -0
  62. package/dist/src/control/server.js.map +1 -0
  63. package/dist/src/daemon/handles.d.ts +35 -6
  64. package/dist/src/daemon/handles.d.ts.map +1 -1
  65. package/dist/src/daemon/handles.js +83 -28
  66. package/dist/src/daemon/handles.js.map +1 -1
  67. package/dist/src/daemon/index.d.ts +2 -3
  68. package/dist/src/daemon/index.d.ts.map +1 -1
  69. package/dist/src/daemon/index.js +2 -3
  70. package/dist/src/daemon/index.js.map +1 -1
  71. package/dist/src/daemon/methods.d.ts +6 -0
  72. package/dist/src/daemon/methods.d.ts.map +1 -1
  73. package/dist/src/daemon/methods.js +13 -1
  74. package/dist/src/daemon/methods.js.map +1 -1
  75. package/dist/src/hooks/command-scanner.d.ts +5 -0
  76. package/dist/src/hooks/command-scanner.d.ts.map +1 -0
  77. package/dist/src/hooks/command-scanner.js +117 -0
  78. package/dist/src/hooks/command-scanner.js.map +1 -0
  79. package/dist/src/hooks/glob.d.ts +8 -0
  80. package/dist/src/hooks/glob.d.ts.map +1 -0
  81. package/dist/src/hooks/glob.js +98 -0
  82. package/dist/src/hooks/glob.js.map +1 -0
  83. package/dist/src/hooks/index.d.ts +9 -0
  84. package/dist/src/hooks/index.d.ts.map +1 -0
  85. package/dist/src/hooks/index.js +9 -0
  86. package/dist/src/hooks/index.js.map +1 -0
  87. package/dist/src/hooks/install.d.ts +29 -0
  88. package/dist/src/hooks/install.d.ts.map +1 -0
  89. package/dist/src/hooks/install.js +86 -0
  90. package/dist/src/hooks/install.js.map +1 -0
  91. package/dist/src/hooks/path-blocker.d.ts +29 -0
  92. package/dist/src/hooks/path-blocker.d.ts.map +1 -0
  93. package/dist/src/hooks/path-blocker.js +59 -0
  94. package/dist/src/hooks/path-blocker.js.map +1 -0
  95. package/dist/src/hooks/post-tool-use.d.ts +13 -0
  96. package/dist/src/hooks/post-tool-use.d.ts.map +1 -0
  97. package/dist/src/hooks/post-tool-use.js +45 -0
  98. package/dist/src/hooks/post-tool-use.js.map +1 -0
  99. package/dist/src/hooks/pre-tool-use.d.ts +8 -0
  100. package/dist/src/hooks/pre-tool-use.d.ts.map +1 -0
  101. package/dist/src/hooks/pre-tool-use.js +38 -0
  102. package/dist/src/hooks/pre-tool-use.js.map +1 -0
  103. package/dist/src/hooks/protocol.d.ts +41 -0
  104. package/dist/src/hooks/protocol.d.ts.map +1 -0
  105. package/dist/src/hooks/protocol.js +27 -0
  106. package/dist/src/hooks/protocol.js.map +1 -0
  107. package/dist/src/hooks/redactor.d.ts +19 -0
  108. package/dist/src/hooks/redactor.d.ts.map +1 -0
  109. package/dist/src/hooks/redactor.js +71 -0
  110. package/dist/src/hooks/redactor.js.map +1 -0
  111. package/dist/src/mcp/index.d.ts +4 -0
  112. package/dist/src/mcp/index.d.ts.map +1 -0
  113. package/dist/src/mcp/index.js +4 -0
  114. package/dist/src/mcp/index.js.map +1 -0
  115. package/dist/src/mcp/protocol.d.ts +98 -0
  116. package/dist/src/mcp/protocol.d.ts.map +1 -0
  117. package/dist/src/mcp/protocol.js +79 -0
  118. package/dist/src/mcp/protocol.js.map +1 -0
  119. package/dist/src/mcp/server.d.ts +46 -0
  120. package/dist/src/mcp/server.d.ts.map +1 -0
  121. package/dist/src/mcp/server.js +108 -0
  122. package/dist/src/mcp/server.js.map +1 -0
  123. package/dist/src/mcp/tools.d.ts +16 -0
  124. package/dist/src/mcp/tools.d.ts.map +1 -0
  125. package/dist/src/mcp/tools.js +117 -0
  126. package/dist/src/mcp/tools.js.map +1 -0
  127. package/package.json +8 -3
  128. package/dist/src/bin/sigild.d.ts +0 -3
  129. package/dist/src/bin/sigild.d.ts.map +0 -1
  130. package/dist/src/bin/sigild.js +0 -30
  131. package/dist/src/bin/sigild.js.map +0 -1
  132. package/dist/src/daemon/rpc.d.ts +0 -61
  133. package/dist/src/daemon/rpc.d.ts.map +0 -1
  134. package/dist/src/daemon/rpc.js +0 -76
  135. package/dist/src/daemon/rpc.js.map +0 -1
  136. package/dist/src/daemon/runtime.d.ts +0 -40
  137. package/dist/src/daemon/runtime.d.ts.map +0 -1
  138. package/dist/src/daemon/runtime.js +0 -61
  139. package/dist/src/daemon/runtime.js.map +0 -1
  140. package/dist/src/daemon/server.d.ts +0 -53
  141. package/dist/src/daemon/server.d.ts.map +0 -1
  142. package/dist/src/daemon/server.js +0 -103
  143. package/dist/src/daemon/server.js.map +0 -1
@@ -0,0 +1,152 @@
1
+ import { readPassphrase } from '../daemon/passphrase.js';
2
+ import { installInto } from '../hooks/install.js';
3
+ import { ArgsError, parseSubcommand } from './args.js';
4
+ import { resolvePaths } from './paths.js';
5
+ import { portalAdd, portalListFromDisk, portalRemove } from './portal.js';
6
+ import { status } from './status.js';
7
+ import { formatResult, lock, unlock } from './unlock.js';
8
+ const USAGE = `sigil — local signing control for Claude Code
9
+
10
+ Usage:
11
+ sigil init [--user]
12
+ sigil status
13
+ sigil portal add <handle> --key-file <path> [--no-remove-source]
14
+ sigil portal list
15
+ sigil portal remove <handle>
16
+ sigil unlock
17
+ sigil lock
18
+
19
+ "sigil init" writes the MCP server registration + tool hooks into
20
+ .claude/settings.json (or ~/.claude/settings.json with --user).
21
+ "sigil unlock" prompts for the passphrase and pushes it to the running
22
+ sigil-mcp process (spawned by Claude Code) over the control socket.
23
+ Set SIGIL_HOME to override ~/.sigil.
24
+ `;
25
+ /**
26
+ * Pure dispatcher: takes argv (without the node binary or script path),
27
+ * returns an exit code. Side effects route through the injected streams
28
+ * and passphrase reader so tests don't poke at process.stdout etc.
29
+ */
30
+ export async function runCli(opts) {
31
+ const out = opts.stdout ?? process.stdout;
32
+ const err = opts.stderr ?? process.stderr;
33
+ const paths = resolvePaths(opts.env ?? process.env);
34
+ const askPassphrase = opts.passphrase ?? (() => readPassphrase('sigil passphrase: '));
35
+ if (opts.argv.length === 0 || opts.argv[0] === '--help' || opts.argv[0] === '-h') {
36
+ out.write(USAGE);
37
+ return { code: 0 };
38
+ }
39
+ try {
40
+ const [head, ...rest] = opts.argv;
41
+ if (head === 'init') {
42
+ const sub = parseSubcommand(['init', ...rest], {
43
+ init: { options: { user: { type: 'boolean' } } },
44
+ });
45
+ const scope = sub.options['user'] === true ? 'user' : 'project';
46
+ const result = installInto({ scope });
47
+ if (result.changed)
48
+ out.write(`updated ${result.settingsPath}\n`);
49
+ else
50
+ out.write(`${result.settingsPath} is already up to date\n`);
51
+ return { code: 0 };
52
+ }
53
+ if (head === 'status') {
54
+ const report = await status(paths);
55
+ out.write(JSON.stringify(report, null, 2) + '\n');
56
+ return { code: 0 };
57
+ }
58
+ if (head === 'unlock') {
59
+ const passphrase = await askPassphrase();
60
+ try {
61
+ const result = await unlock({ paths, passphrase });
62
+ const { message, code } = formatResult('unlock', result);
63
+ (code === 0 ? out : err).write(message + '\n');
64
+ return { code };
65
+ }
66
+ finally {
67
+ passphrase.fill(0);
68
+ }
69
+ }
70
+ if (head === 'lock') {
71
+ const result = await lock({ paths });
72
+ const { message, code } = formatResult('lock', result);
73
+ (code === 0 ? out : err).write(message + '\n');
74
+ return { code };
75
+ }
76
+ if (head === 'portal') {
77
+ const sub = parseSubcommand(rest, {
78
+ add: {
79
+ options: {
80
+ 'key-file': { type: 'string' },
81
+ 'no-remove-source': { type: 'boolean' },
82
+ },
83
+ },
84
+ list: { options: {} },
85
+ remove: { options: {} },
86
+ });
87
+ if (sub.command === 'add') {
88
+ const handle = sub.positionals[0];
89
+ const keyFile = sub.options['key-file'];
90
+ if (!handle)
91
+ throw new ArgsError('portal add: missing handle');
92
+ if (typeof keyFile !== 'string' || keyFile.length === 0) {
93
+ throw new ArgsError('portal add: --key-file is required');
94
+ }
95
+ const passphrase = await askPassphrase();
96
+ try {
97
+ const { address, keyfilePath } = portalAdd(paths, {
98
+ handle,
99
+ keyFile,
100
+ passphrase,
101
+ ...(sub.options['no-remove-source'] === true ? { removeSource: false } : {}),
102
+ ...(opts.kdfParams ? { kdfParams: opts.kdfParams } : {}),
103
+ });
104
+ out.write(`added ${handle} (${address}) → ${keyfilePath}\n`);
105
+ }
106
+ finally {
107
+ passphrase.fill(0);
108
+ }
109
+ return { code: 0 };
110
+ }
111
+ if (sub.command === 'list') {
112
+ const passphrase = await askPassphrase();
113
+ try {
114
+ const portals = portalListFromDisk(paths, passphrase);
115
+ if (portals.length === 0) {
116
+ out.write('(no portals)\n');
117
+ }
118
+ else {
119
+ for (const p of portals)
120
+ out.write(`${p.handle}\t${p.address}\n`);
121
+ }
122
+ }
123
+ finally {
124
+ passphrase.fill(0);
125
+ }
126
+ return { code: 0 };
127
+ }
128
+ if (sub.command === 'remove') {
129
+ const handle = sub.positionals[0];
130
+ if (!handle)
131
+ throw new ArgsError('portal remove: missing handle');
132
+ const result = portalRemove(paths, handle);
133
+ if (result.removed)
134
+ out.write(`removed ${handle} (${result.path})\n`);
135
+ else
136
+ out.write(`portal "${handle}" not found at ${result.path}\n`);
137
+ return { code: result.removed ? 0 : 1 };
138
+ }
139
+ }
140
+ throw new ArgsError(`unknown subcommand "${head}"`);
141
+ }
142
+ catch (e) {
143
+ if (e instanceof ArgsError) {
144
+ err.write(`sigil: ${e.message}\n`);
145
+ err.write('\n' + USAGE);
146
+ return { code: 2 };
147
+ }
148
+ err.write(`sigil: ${e.message}\n`);
149
+ return { code: 1 };
150
+ }
151
+ }
152
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../../src/cli/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Resolves the locations sigil cares about, honouring SIGIL_HOME env override.
3
+ * One source of truth used by the CLI, daemon entrypoint, and tests.
4
+ *
5
+ * The control socket is where the running sigil-mcp listens for unlock/lock/
6
+ * status commands from the `sigil` CLI. It lives inside ~/.sigil so it
7
+ * inherits the 0o700 directory permission; the socket file itself is also
8
+ * chmod'd to 0o600 by the server on bind.
9
+ */
10
+ export interface SigilPaths {
11
+ readonly home: string;
12
+ readonly keysDir: string;
13
+ readonly controlSocket: string;
14
+ readonly auditLog: string;
15
+ }
16
+ export declare function resolvePaths(env?: NodeJS.ProcessEnv): SigilPaths;
17
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/cli/paths.ts"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CAQ7E"}
@@ -0,0 +1,12 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ export function resolvePaths(env = process.env) {
4
+ const home = env['SIGIL_HOME'] ?? join(homedir(), '.sigil');
5
+ return {
6
+ home,
7
+ keysDir: join(home, 'keys'),
8
+ controlSocket: env['SIGIL_CONTROL_SOCK'] ?? join(home, 'control.sock'),
9
+ auditLog: join(home, 'audit.log'),
10
+ };
11
+ }
12
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../../src/cli/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAkBjC,MAAM,UAAU,YAAY,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC/D,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC5D,OAAO;QACL,IAAI;QACJ,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,GAAG,CAAC,oBAAoB,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC;QACtE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC;KAClC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { type KdfParams, SecretBuffer } from '../crypto/index.js';
2
+ import type { SigilPaths } from './paths.js';
3
+ export interface PortalAddOpts {
4
+ handle: string;
5
+ keyFile: string;
6
+ passphrase: Buffer;
7
+ /**
8
+ * If true, the source key file is deleted after successful encryption.
9
+ * Defaults to true — leaving plaintext keys lying around is the whole
10
+ * thing sigil is trying to prevent.
11
+ */
12
+ removeSource?: boolean;
13
+ /**
14
+ * Override KDF parameters. Production code path uses DEFAULT_KDF_PARAMS
15
+ * (64MiB / 3 iters / 4 parallelism). Tests pass weaker params to keep
16
+ * the suite fast. The CLI binary never sets this.
17
+ */
18
+ kdfParams?: KdfParams;
19
+ }
20
+ /**
21
+ * Reads a private key from disk, encrypts it with the passphrase, writes it
22
+ * to the keys directory under <handle>.sigil, and (by default) deletes the
23
+ * source file. Returns the derived address.
24
+ *
25
+ * Accepts the key file as either:
26
+ * - 32 raw bytes (binary)
27
+ * - 64 hex characters (optionally 0x-prefixed, optionally with trailing whitespace)
28
+ */
29
+ export declare function portalAdd(paths: SigilPaths, opts: PortalAddOpts): {
30
+ address: string;
31
+ keyfilePath: string;
32
+ };
33
+ export interface PortalInfo {
34
+ handle: string;
35
+ kind: 'eth';
36
+ address: string;
37
+ }
38
+ /**
39
+ * Lists portals by reading the keys directory and decrypting each keyfile
40
+ * just long enough to derive the address. Requires the passphrase.
41
+ * Returns an empty list if the directory doesn't exist.
42
+ */
43
+ export declare function portalListFromDisk(paths: SigilPaths, passphrase: Buffer): PortalInfo[];
44
+ export interface PortalRemoveResult {
45
+ removed: boolean;
46
+ path: string;
47
+ }
48
+ export declare function portalRemove(paths: SigilPaths, handle: string): PortalRemoveResult;
49
+ export { SecretBuffer };
50
+ //# sourceMappingURL=portal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../src/cli/portal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAW,YAAY,EAAa,MAAM,oBAAoB,CAAC;AAGtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,aAAa,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CA4B1G;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,UAAU,EAAE,CAgBtF;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAMlF;AAqBD,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,93 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { sealKey, SecretBuffer, unsealKey } from '../crypto/index.js';
4
+ import { addressFromPrivateKey } from '../eth/index.js';
5
+ import { HandleTable } from '../daemon/handles.js';
6
+ /**
7
+ * Reads a private key from disk, encrypts it with the passphrase, writes it
8
+ * to the keys directory under <handle>.sigil, and (by default) deletes the
9
+ * source file. Returns the derived address.
10
+ *
11
+ * Accepts the key file as either:
12
+ * - 32 raw bytes (binary)
13
+ * - 64 hex characters (optionally 0x-prefixed, optionally with trailing whitespace)
14
+ */
15
+ export function portalAdd(paths, opts) {
16
+ HandleTable.parseHandle(opts.handle); // validates format
17
+ mkdirSync(paths.keysDir, { recursive: true, mode: 0o700 });
18
+ const destPath = join(paths.keysDir, `${opts.handle}.sigil`);
19
+ if (existsSync(destPath)) {
20
+ throw new Error(`portal "${opts.handle}" already exists at ${destPath}; remove it first`);
21
+ }
22
+ const raw = readFileSync(opts.keyFile);
23
+ const priv = normalizePrivateKey(raw);
24
+ let address;
25
+ try {
26
+ address = addressFromPrivateKey(priv);
27
+ const sealed = opts.kdfParams
28
+ ? sealKey(priv, opts.passphrase, opts.kdfParams)
29
+ : sealKey(priv, opts.passphrase);
30
+ writeFileSync(destPath, sealed, { mode: 0o600 });
31
+ }
32
+ finally {
33
+ priv.fill(0);
34
+ }
35
+ if (opts.removeSource !== false) {
36
+ try {
37
+ unlinkSync(opts.keyFile);
38
+ }
39
+ catch { /* best-effort cleanup; file may already be gone */ }
40
+ }
41
+ return { address, keyfilePath: destPath };
42
+ }
43
+ /**
44
+ * Lists portals by reading the keys directory and decrypting each keyfile
45
+ * just long enough to derive the address. Requires the passphrase.
46
+ * Returns an empty list if the directory doesn't exist.
47
+ */
48
+ export function portalListFromDisk(paths, passphrase) {
49
+ if (!existsSync(paths.keysDir))
50
+ return [];
51
+ const entries = readdirSync(paths.keysDir).sort();
52
+ const out = [];
53
+ for (const filename of entries) {
54
+ const handle = HandleTable.handleFromFilename(filename);
55
+ if (handle === null)
56
+ continue;
57
+ const blob = readFileSync(join(paths.keysDir, filename));
58
+ const sb = unsealKey(blob, passphrase);
59
+ try {
60
+ out.push({ handle, kind: 'eth', address: addressFromPrivateKey(sb.bytes()) });
61
+ }
62
+ finally {
63
+ sb.dispose();
64
+ }
65
+ }
66
+ return out;
67
+ }
68
+ export function portalRemove(paths, handle) {
69
+ HandleTable.parseHandle(handle); // validates format
70
+ const destPath = join(paths.keysDir, `${handle}.sigil`);
71
+ if (!existsSync(destPath))
72
+ return { removed: false, path: destPath };
73
+ unlinkSync(destPath);
74
+ return { removed: true, path: destPath };
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Helpers
78
+ // ---------------------------------------------------------------------------
79
+ function normalizePrivateKey(raw) {
80
+ if (raw.length === 32)
81
+ return Buffer.from(raw); // raw 32 bytes
82
+ // Otherwise: expect ASCII hex, possibly with 0x prefix + trailing whitespace.
83
+ const text = raw.toString('utf8').trim();
84
+ const stripped = text.startsWith('0x') ? text.slice(2) : text;
85
+ if (!/^[0-9a-fA-F]{64}$/.test(stripped)) {
86
+ throw new Error('key file must be either 32 raw bytes or 64 hex chars (with optional 0x prefix)');
87
+ }
88
+ return Buffer.from(stripped, 'hex');
89
+ }
90
+ // Re-export SecretBuffer so tests can assert it disposes correctly without
91
+ // reaching into the crypto module directly.
92
+ export { SecretBuffer };
93
+ //# sourceMappingURL=portal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portal.js","sourceRoot":"","sources":["../../../src/cli/portal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { type PortalSummary } from '../control/index.js';
2
+ import type { SigilPaths } from './paths.js';
3
+ export interface StatusReport {
4
+ /** Number of *.sigil files in the keys directory. */
5
+ keyfilesOnDisk: number;
6
+ /** Path the audit log lives at. Useful for `tail`-ing during incidents. */
7
+ auditLog: string;
8
+ /** True if the control socket responded — i.e. sigil-mcp is alive. */
9
+ mcpRunning: boolean;
10
+ /** Process ID of the running sigil-mcp; null when not running. */
11
+ mcpPid: number | null;
12
+ /** True if the HandleTable has been unlocked. False when mcp is down or locked. */
13
+ unlocked: boolean;
14
+ /** Portals currently loaded in the running sigil-mcp. Empty when locked or down. */
15
+ portals: PortalSummary[];
16
+ }
17
+ /**
18
+ * Reports the on-disk + live state of sigil. Does NOT require the passphrase.
19
+ *
20
+ * Disk side: counts encrypted keyfiles.
21
+ * Live side: probes the control socket. If the server is unreachable, marks
22
+ * mcpRunning=false; this is the common case when no Claude Code session is
23
+ * open. Any other client error is also treated as not-running — the user
24
+ * can re-run "sigil unlock" or check the socket file directly for more
25
+ * detailed diagnosis.
26
+ */
27
+ export declare function status(paths: SigilPaths): Promise<StatusReport>;
28
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/cli/status.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,cAAc,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAC;IACjB,sEAAsE;IACtE,UAAU,EAAE,OAAO,CAAC;IACpB,kEAAkE;IAClE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,mFAAmF;IACnF,QAAQ,EAAE,OAAO,CAAC;IAClB,oFAAoF;IACpF,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CA8BrE"}
@@ -0,0 +1,59 @@
1
+ import { existsSync, readdirSync, statSync } from 'node:fs';
2
+ import { ControlClientError, controlRequest, isControlError, } from '../control/index.js';
3
+ /**
4
+ * Reports the on-disk + live state of sigil. Does NOT require the passphrase.
5
+ *
6
+ * Disk side: counts encrypted keyfiles.
7
+ * Live side: probes the control socket. If the server is unreachable, marks
8
+ * mcpRunning=false; this is the common case when no Claude Code session is
9
+ * open. Any other client error is also treated as not-running — the user
10
+ * can re-run "sigil unlock" or check the socket file directly for more
11
+ * detailed diagnosis.
12
+ */
13
+ export async function status(paths) {
14
+ const keyfilesOnDisk = countKeyfiles(paths.keysDir);
15
+ let mcpRunning = false;
16
+ let mcpPid = null;
17
+ let unlocked = false;
18
+ let portals = [];
19
+ try {
20
+ const resp = await controlRequest({
21
+ socketPath: paths.controlSocket,
22
+ request: { method: 'status' },
23
+ timeoutMs: 1_000,
24
+ });
25
+ if (!isControlError(resp)) {
26
+ mcpRunning = true;
27
+ mcpPid = resp.pid;
28
+ unlocked = resp.unlocked;
29
+ portals = resp.portals;
30
+ }
31
+ }
32
+ catch (err) {
33
+ if (!(err instanceof ControlClientError))
34
+ throw err;
35
+ // SERVER_DOWN / CONNECT_FAILED / TIMEOUT all → mcpRunning=false.
36
+ }
37
+ return {
38
+ keyfilesOnDisk,
39
+ auditLog: paths.auditLog,
40
+ mcpRunning,
41
+ mcpPid,
42
+ unlocked,
43
+ portals,
44
+ };
45
+ }
46
+ function countKeyfiles(dir) {
47
+ if (!existsSync(dir))
48
+ return 0;
49
+ try {
50
+ const stat = statSync(dir);
51
+ if (!stat.isDirectory())
52
+ return 0;
53
+ }
54
+ catch {
55
+ return 0;
56
+ }
57
+ return readdirSync(dir).filter((f) => f.endsWith('.sigil')).length;
58
+ }
59
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5D,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,cAAc,GAEf,MAAM,qBAAqB,CAAC;AAkB7B;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,KAAiB;IAC5C,MAAM,cAAc,GAAG,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,OAAO,GAAoB,EAAE,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC;YAChC,UAAU,EAAE,KAAK,CAAC,aAAa;YAC/B,OAAO,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE;YAC7B,SAAS,EAAE,KAAK;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,UAAU,GAAG,IAAI,CAAC;YAClB,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC;YAClB,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YACzB,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QACzB,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC,CAAC,GAAG,YAAY,kBAAkB,CAAC;YAAE,MAAM,GAAG,CAAC;QACpD,iEAAiE;IACnE,CAAC;IACD,OAAO;QACL,cAAc;QACd,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU;QACV,MAAM;QACN,QAAQ;QACR,OAAO;KACR,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE;YAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;AACrE,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { ControlClientError, type ControlResponse } from '../control/index.js';
2
+ import type { SigilPaths } from './paths.js';
3
+ export interface UnlockOpts {
4
+ paths: SigilPaths;
5
+ /** Buffer holding the passphrase. Caller is responsible for zeroizing it. */
6
+ passphrase: Buffer;
7
+ /** Optional connect timeout in ms (default 5000). */
8
+ timeoutMs?: number;
9
+ }
10
+ export interface UnlockResult {
11
+ /** Server-side response, or null if the server is unreachable. */
12
+ response: ControlResponse | null;
13
+ /** Set when the control socket couldn't be reached. */
14
+ clientError?: ControlClientError;
15
+ }
16
+ /**
17
+ * Send an unlock request to the running sigil-mcp. Encodes the passphrase as
18
+ * base64 for transport; does NOT clear the caller's buffer (the caller owns
19
+ * lifetime). Returns the parsed response, or a client error if the server
20
+ * was unreachable.
21
+ */
22
+ export declare function unlock(opts: UnlockOpts): Promise<UnlockResult>;
23
+ export interface LockOpts {
24
+ paths: SigilPaths;
25
+ timeoutMs?: number;
26
+ }
27
+ export declare function lock(opts: LockOpts): Promise<UnlockResult>;
28
+ /**
29
+ * Format a result for human-readable CLI output. Returns the message and the
30
+ * exit code the CLI should use.
31
+ */
32
+ export declare function formatResult(action: 'unlock' | 'lock', result: UnlockResult): {
33
+ message: string;
34
+ code: number;
35
+ };
36
+ //# sourceMappingURL=unlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlock.d.ts","sourceRoot":"","sources":["../../../src/cli/unlock.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAGlB,KAAK,eAAe,EACrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,uDAAuD;IACvD,WAAW,CAAC,EAAE,kBAAkB,CAAC;CAClC;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAapE;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,YAAY,CAAC,CAYhE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,CAkCA"}
@@ -0,0 +1,77 @@
1
+ import { ControlClientError, controlRequest, isControlError, } from '../control/index.js';
2
+ /**
3
+ * Send an unlock request to the running sigil-mcp. Encodes the passphrase as
4
+ * base64 for transport; does NOT clear the caller's buffer (the caller owns
5
+ * lifetime). Returns the parsed response, or a client error if the server
6
+ * was unreachable.
7
+ */
8
+ export async function unlock(opts) {
9
+ const passphraseB64 = opts.passphrase.toString('base64');
10
+ try {
11
+ const response = await controlRequest({
12
+ socketPath: opts.paths.controlSocket,
13
+ request: { method: 'unlock', passphraseB64 },
14
+ ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
15
+ });
16
+ return { response };
17
+ }
18
+ catch (err) {
19
+ if (err instanceof ControlClientError)
20
+ return { response: null, clientError: err };
21
+ throw err;
22
+ }
23
+ }
24
+ export async function lock(opts) {
25
+ try {
26
+ const response = await controlRequest({
27
+ socketPath: opts.paths.controlSocket,
28
+ request: { method: 'lock' },
29
+ ...(opts.timeoutMs !== undefined ? { timeoutMs: opts.timeoutMs } : {}),
30
+ });
31
+ return { response };
32
+ }
33
+ catch (err) {
34
+ if (err instanceof ControlClientError)
35
+ return { response: null, clientError: err };
36
+ throw err;
37
+ }
38
+ }
39
+ /**
40
+ * Format a result for human-readable CLI output. Returns the message and the
41
+ * exit code the CLI should use.
42
+ */
43
+ export function formatResult(action, result) {
44
+ if (result.clientError) {
45
+ if (result.clientError.code === 'SERVER_DOWN') {
46
+ return {
47
+ message: `sigil-mcp is not running. Start a Claude Code session (which spawns it via your MCP config) and try again.`,
48
+ code: 1,
49
+ };
50
+ }
51
+ return { message: `sigil ${action}: ${result.clientError.message}`, code: 1 };
52
+ }
53
+ const resp = result.response;
54
+ if (isControlError(resp)) {
55
+ if (resp.code === 'WRONG_PASSPHRASE') {
56
+ return { message: `sigil ${action}: wrong passphrase`, code: 2 };
57
+ }
58
+ if (resp.code === 'ALREADY_UNLOCKED') {
59
+ return {
60
+ message: `sigil ${action}: already unlocked; run "sigil lock" first if you want to re-unlock`,
61
+ code: 1,
62
+ };
63
+ }
64
+ return { message: `sigil ${action}: ${resp.error} (${resp.code})`, code: 1 };
65
+ }
66
+ if (action === 'unlock') {
67
+ const n = resp.portals.length;
68
+ return {
69
+ message: n === 0
70
+ ? 'unlocked (no portals on disk yet — add one with "sigil portal add")'
71
+ : `unlocked ${n} portal${n === 1 ? '' : 's'}: ${resp.portals.map((p) => p.handle).join(', ')}`,
72
+ code: 0,
73
+ };
74
+ }
75
+ return { message: 'locked', code: 0 };
76
+ }
77
+ //# sourceMappingURL=unlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unlock.js","sourceRoot":"","sources":["../../../src/cli/unlock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { ControlRequest, ControlResponse } from './protocol.js';
2
+ export interface ControlRequestOpts {
3
+ /** Path of the control socket (defaults provided by callers). */
4
+ socketPath: string;
5
+ /** The request to send. */
6
+ request: ControlRequest;
7
+ /** Connect + I/O timeout (ms). Defaults to 5s. */
8
+ timeoutMs?: number;
9
+ }
10
+ /**
11
+ * Sends a single request to the running sigil-mcp control socket and returns
12
+ * the parsed response. Throws ControlClientError if:
13
+ * - the socket file is missing or refused (server not running)
14
+ * - the connection times out
15
+ * - the response is not valid JSON
16
+ *
17
+ * Note: a server-side denial (wrong passphrase, etc.) is NOT thrown — it
18
+ * comes back as a ControlResponse with `ok: false`. Callers branch on that.
19
+ */
20
+ export declare function controlRequest(opts: ControlRequestOpts): Promise<ControlResponse>;
21
+ export type ControlClientErrorCode = 'SERVER_DOWN' | 'CONNECT_FAILED' | 'TIMEOUT' | 'BAD_RESPONSE' | 'NO_RESPONSE';
22
+ export declare class ControlClientError extends Error {
23
+ readonly code: ControlClientErrorCode;
24
+ constructor(message: string, code: ControlClientErrorCode);
25
+ }
26
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/control/client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAIrE,MAAM,WAAW,kBAAkB;IACjC,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,OAAO,EAAE,cAAc,CAAC;IACxB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,eAAe,CAAC,CA8DvF;AAED,MAAM,MAAM,sBAAsB,GAC9B,aAAa,GACb,gBAAgB,GAChB,SAAS,GACT,cAAc,GACd,aAAa,CAAC;AAElB,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;gBAC1B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB;CAK1D"}