@stoneforge/quarry 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 (330) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +160 -0
  3. package/dist/api/index.d.ts +8 -0
  4. package/dist/api/index.d.ts.map +1 -0
  5. package/dist/api/index.js +8 -0
  6. package/dist/api/index.js.map +1 -0
  7. package/dist/api/quarry-api.d.ts +268 -0
  8. package/dist/api/quarry-api.d.ts.map +1 -0
  9. package/dist/api/quarry-api.js +3905 -0
  10. package/dist/api/quarry-api.js.map +1 -0
  11. package/dist/api/types.d.ts +1359 -0
  12. package/dist/api/types.d.ts.map +1 -0
  13. package/dist/api/types.js +204 -0
  14. package/dist/api/types.js.map +1 -0
  15. package/dist/bin/sf.d.ts +3 -0
  16. package/dist/bin/sf.d.ts.map +1 -0
  17. package/dist/bin/sf.js +9 -0
  18. package/dist/bin/sf.js.map +1 -0
  19. package/dist/cli/commands/admin.d.ts +11 -0
  20. package/dist/cli/commands/admin.d.ts.map +1 -0
  21. package/dist/cli/commands/admin.js +465 -0
  22. package/dist/cli/commands/admin.js.map +1 -0
  23. package/dist/cli/commands/alias.d.ts +8 -0
  24. package/dist/cli/commands/alias.d.ts.map +1 -0
  25. package/dist/cli/commands/alias.js +70 -0
  26. package/dist/cli/commands/alias.js.map +1 -0
  27. package/dist/cli/commands/channel.d.ts +13 -0
  28. package/dist/cli/commands/channel.d.ts.map +1 -0
  29. package/dist/cli/commands/channel.js +680 -0
  30. package/dist/cli/commands/channel.js.map +1 -0
  31. package/dist/cli/commands/completion.d.ts +8 -0
  32. package/dist/cli/commands/completion.d.ts.map +1 -0
  33. package/dist/cli/commands/completion.js +87 -0
  34. package/dist/cli/commands/completion.js.map +1 -0
  35. package/dist/cli/commands/config.d.ts +12 -0
  36. package/dist/cli/commands/config.d.ts.map +1 -0
  37. package/dist/cli/commands/config.js +242 -0
  38. package/dist/cli/commands/config.js.map +1 -0
  39. package/dist/cli/commands/crud.d.ts +64 -0
  40. package/dist/cli/commands/crud.d.ts.map +1 -0
  41. package/dist/cli/commands/crud.js +805 -0
  42. package/dist/cli/commands/crud.js.map +1 -0
  43. package/dist/cli/commands/dep.d.ts +16 -0
  44. package/dist/cli/commands/dep.d.ts.map +1 -0
  45. package/dist/cli/commands/dep.js +499 -0
  46. package/dist/cli/commands/dep.js.map +1 -0
  47. package/dist/cli/commands/document.d.ts +12 -0
  48. package/dist/cli/commands/document.d.ts.map +1 -0
  49. package/dist/cli/commands/document.js +1039 -0
  50. package/dist/cli/commands/document.js.map +1 -0
  51. package/dist/cli/commands/embeddings.d.ts +12 -0
  52. package/dist/cli/commands/embeddings.d.ts.map +1 -0
  53. package/dist/cli/commands/embeddings.js +273 -0
  54. package/dist/cli/commands/embeddings.js.map +1 -0
  55. package/dist/cli/commands/entity.d.ts +16 -0
  56. package/dist/cli/commands/entity.d.ts.map +1 -0
  57. package/dist/cli/commands/entity.js +522 -0
  58. package/dist/cli/commands/entity.js.map +1 -0
  59. package/dist/cli/commands/gc.d.ts +10 -0
  60. package/dist/cli/commands/gc.d.ts.map +1 -0
  61. package/dist/cli/commands/gc.js +257 -0
  62. package/dist/cli/commands/gc.js.map +1 -0
  63. package/dist/cli/commands/help.d.ts +11 -0
  64. package/dist/cli/commands/help.d.ts.map +1 -0
  65. package/dist/cli/commands/help.js +169 -0
  66. package/dist/cli/commands/help.js.map +1 -0
  67. package/dist/cli/commands/history.d.ts +9 -0
  68. package/dist/cli/commands/history.d.ts.map +1 -0
  69. package/dist/cli/commands/history.js +160 -0
  70. package/dist/cli/commands/history.js.map +1 -0
  71. package/dist/cli/commands/identity.d.ts +18 -0
  72. package/dist/cli/commands/identity.d.ts.map +1 -0
  73. package/dist/cli/commands/identity.js +698 -0
  74. package/dist/cli/commands/identity.js.map +1 -0
  75. package/dist/cli/commands/inbox.d.ts +20 -0
  76. package/dist/cli/commands/inbox.d.ts.map +1 -0
  77. package/dist/cli/commands/inbox.js +493 -0
  78. package/dist/cli/commands/inbox.js.map +1 -0
  79. package/dist/cli/commands/init.d.ts +20 -0
  80. package/dist/cli/commands/init.d.ts.map +1 -0
  81. package/dist/cli/commands/init.js +144 -0
  82. package/dist/cli/commands/init.js.map +1 -0
  83. package/dist/cli/commands/install.d.ts +9 -0
  84. package/dist/cli/commands/install.d.ts.map +1 -0
  85. package/dist/cli/commands/install.js +200 -0
  86. package/dist/cli/commands/install.js.map +1 -0
  87. package/dist/cli/commands/library.d.ts +12 -0
  88. package/dist/cli/commands/library.d.ts.map +1 -0
  89. package/dist/cli/commands/library.js +665 -0
  90. package/dist/cli/commands/library.js.map +1 -0
  91. package/dist/cli/commands/message.d.ts +11 -0
  92. package/dist/cli/commands/message.d.ts.map +1 -0
  93. package/dist/cli/commands/message.js +608 -0
  94. package/dist/cli/commands/message.js.map +1 -0
  95. package/dist/cli/commands/plan.d.ts +17 -0
  96. package/dist/cli/commands/plan.d.ts.map +1 -0
  97. package/dist/cli/commands/plan.js +698 -0
  98. package/dist/cli/commands/plan.js.map +1 -0
  99. package/dist/cli/commands/playbook.d.ts +12 -0
  100. package/dist/cli/commands/playbook.d.ts.map +1 -0
  101. package/dist/cli/commands/playbook.js +730 -0
  102. package/dist/cli/commands/playbook.js.map +1 -0
  103. package/dist/cli/commands/reset.d.ts +12 -0
  104. package/dist/cli/commands/reset.d.ts.map +1 -0
  105. package/dist/cli/commands/reset.js +306 -0
  106. package/dist/cli/commands/reset.js.map +1 -0
  107. package/dist/cli/commands/serve.d.ts +11 -0
  108. package/dist/cli/commands/serve.d.ts.map +1 -0
  109. package/dist/cli/commands/serve.js +106 -0
  110. package/dist/cli/commands/serve.js.map +1 -0
  111. package/dist/cli/commands/stats.d.ts +8 -0
  112. package/dist/cli/commands/stats.d.ts.map +1 -0
  113. package/dist/cli/commands/stats.js +82 -0
  114. package/dist/cli/commands/stats.js.map +1 -0
  115. package/dist/cli/commands/sync.d.ts +14 -0
  116. package/dist/cli/commands/sync.d.ts.map +1 -0
  117. package/dist/cli/commands/sync.js +370 -0
  118. package/dist/cli/commands/sync.js.map +1 -0
  119. package/dist/cli/commands/task.d.ts +25 -0
  120. package/dist/cli/commands/task.d.ts.map +1 -0
  121. package/dist/cli/commands/task.js +1153 -0
  122. package/dist/cli/commands/task.js.map +1 -0
  123. package/dist/cli/commands/team.d.ts +13 -0
  124. package/dist/cli/commands/team.d.ts.map +1 -0
  125. package/dist/cli/commands/team.js +471 -0
  126. package/dist/cli/commands/team.js.map +1 -0
  127. package/dist/cli/commands/workflow.d.ts +16 -0
  128. package/dist/cli/commands/workflow.d.ts.map +1 -0
  129. package/dist/cli/commands/workflow.js +753 -0
  130. package/dist/cli/commands/workflow.js.map +1 -0
  131. package/dist/cli/completion.d.ts +28 -0
  132. package/dist/cli/completion.d.ts.map +1 -0
  133. package/dist/cli/completion.js +295 -0
  134. package/dist/cli/completion.js.map +1 -0
  135. package/dist/cli/db.d.ts +38 -0
  136. package/dist/cli/db.d.ts.map +1 -0
  137. package/dist/cli/db.js +90 -0
  138. package/dist/cli/db.js.map +1 -0
  139. package/dist/cli/formatter.d.ts +87 -0
  140. package/dist/cli/formatter.d.ts.map +1 -0
  141. package/dist/cli/formatter.js +464 -0
  142. package/dist/cli/formatter.js.map +1 -0
  143. package/dist/cli/index.d.ts +33 -0
  144. package/dist/cli/index.d.ts.map +1 -0
  145. package/dist/cli/index.js +38 -0
  146. package/dist/cli/index.js.map +1 -0
  147. package/dist/cli/parser.d.ts +45 -0
  148. package/dist/cli/parser.d.ts.map +1 -0
  149. package/dist/cli/parser.js +256 -0
  150. package/dist/cli/parser.js.map +1 -0
  151. package/dist/cli/plugin-loader.d.ts +39 -0
  152. package/dist/cli/plugin-loader.d.ts.map +1 -0
  153. package/dist/cli/plugin-loader.js +165 -0
  154. package/dist/cli/plugin-loader.js.map +1 -0
  155. package/dist/cli/plugin-registry.d.ts +50 -0
  156. package/dist/cli/plugin-registry.d.ts.map +1 -0
  157. package/dist/cli/plugin-registry.js +206 -0
  158. package/dist/cli/plugin-registry.js.map +1 -0
  159. package/dist/cli/plugin-types.d.ts +106 -0
  160. package/dist/cli/plugin-types.d.ts.map +1 -0
  161. package/dist/cli/plugin-types.js +103 -0
  162. package/dist/cli/plugin-types.js.map +1 -0
  163. package/dist/cli/runner.d.ts +35 -0
  164. package/dist/cli/runner.d.ts.map +1 -0
  165. package/dist/cli/runner.js +340 -0
  166. package/dist/cli/runner.js.map +1 -0
  167. package/dist/cli/suggest.d.ts +15 -0
  168. package/dist/cli/suggest.d.ts.map +1 -0
  169. package/dist/cli/suggest.js +49 -0
  170. package/dist/cli/suggest.js.map +1 -0
  171. package/dist/cli/types.d.ts +138 -0
  172. package/dist/cli/types.d.ts.map +1 -0
  173. package/dist/cli/types.js +63 -0
  174. package/dist/cli/types.js.map +1 -0
  175. package/dist/config/config.d.ts +86 -0
  176. package/dist/config/config.d.ts.map +1 -0
  177. package/dist/config/config.js +348 -0
  178. package/dist/config/config.js.map +1 -0
  179. package/dist/config/defaults.d.ts +66 -0
  180. package/dist/config/defaults.d.ts.map +1 -0
  181. package/dist/config/defaults.js +114 -0
  182. package/dist/config/defaults.js.map +1 -0
  183. package/dist/config/duration.d.ts +75 -0
  184. package/dist/config/duration.d.ts.map +1 -0
  185. package/dist/config/duration.js +190 -0
  186. package/dist/config/duration.js.map +1 -0
  187. package/dist/config/env.d.ts +67 -0
  188. package/dist/config/env.d.ts.map +1 -0
  189. package/dist/config/env.js +207 -0
  190. package/dist/config/env.js.map +1 -0
  191. package/dist/config/file.d.ts +97 -0
  192. package/dist/config/file.d.ts.map +1 -0
  193. package/dist/config/file.js +365 -0
  194. package/dist/config/file.js.map +1 -0
  195. package/dist/config/index.d.ts +35 -0
  196. package/dist/config/index.d.ts.map +1 -0
  197. package/dist/config/index.js +41 -0
  198. package/dist/config/index.js.map +1 -0
  199. package/dist/config/merge.d.ts +53 -0
  200. package/dist/config/merge.d.ts.map +1 -0
  201. package/dist/config/merge.js +226 -0
  202. package/dist/config/merge.js.map +1 -0
  203. package/dist/config/types.d.ts +257 -0
  204. package/dist/config/types.d.ts.map +1 -0
  205. package/dist/config/types.js +72 -0
  206. package/dist/config/types.js.map +1 -0
  207. package/dist/config/validation.d.ts +55 -0
  208. package/dist/config/validation.d.ts.map +1 -0
  209. package/dist/config/validation.js +251 -0
  210. package/dist/config/validation.js.map +1 -0
  211. package/dist/http/index.d.ts +8 -0
  212. package/dist/http/index.d.ts.map +1 -0
  213. package/dist/http/index.js +12 -0
  214. package/dist/http/index.js.map +1 -0
  215. package/dist/http/sync-handlers.d.ts +162 -0
  216. package/dist/http/sync-handlers.d.ts.map +1 -0
  217. package/dist/http/sync-handlers.js +271 -0
  218. package/dist/http/sync-handlers.js.map +1 -0
  219. package/dist/index.d.ts +25 -0
  220. package/dist/index.d.ts.map +1 -0
  221. package/dist/index.js +69 -0
  222. package/dist/index.js.map +1 -0
  223. package/dist/server/index.d.ts +34 -0
  224. package/dist/server/index.d.ts.map +1 -0
  225. package/dist/server/index.js +3329 -0
  226. package/dist/server/index.js.map +1 -0
  227. package/dist/server/static.d.ts +18 -0
  228. package/dist/server/static.d.ts.map +1 -0
  229. package/dist/server/static.js +71 -0
  230. package/dist/server/static.js.map +1 -0
  231. package/dist/server/ws/broadcaster.d.ts +8 -0
  232. package/dist/server/ws/broadcaster.d.ts.map +1 -0
  233. package/dist/server/ws/broadcaster.js +7 -0
  234. package/dist/server/ws/broadcaster.js.map +1 -0
  235. package/dist/server/ws/handler.d.ts +55 -0
  236. package/dist/server/ws/handler.d.ts.map +1 -0
  237. package/dist/server/ws/handler.js +160 -0
  238. package/dist/server/ws/handler.js.map +1 -0
  239. package/dist/services/blocked-cache.d.ts +297 -0
  240. package/dist/services/blocked-cache.d.ts.map +1 -0
  241. package/dist/services/blocked-cache.js +755 -0
  242. package/dist/services/blocked-cache.js.map +1 -0
  243. package/dist/services/dependency.d.ts +205 -0
  244. package/dist/services/dependency.d.ts.map +1 -0
  245. package/dist/services/dependency.js +566 -0
  246. package/dist/services/dependency.js.map +1 -0
  247. package/dist/services/embeddings/fusion.d.ts +33 -0
  248. package/dist/services/embeddings/fusion.d.ts.map +1 -0
  249. package/dist/services/embeddings/fusion.js +34 -0
  250. package/dist/services/embeddings/fusion.js.map +1 -0
  251. package/dist/services/embeddings/index.d.ts +12 -0
  252. package/dist/services/embeddings/index.d.ts.map +1 -0
  253. package/dist/services/embeddings/index.js +10 -0
  254. package/dist/services/embeddings/index.js.map +1 -0
  255. package/dist/services/embeddings/local-provider.d.ts +31 -0
  256. package/dist/services/embeddings/local-provider.d.ts.map +1 -0
  257. package/dist/services/embeddings/local-provider.js +80 -0
  258. package/dist/services/embeddings/local-provider.js.map +1 -0
  259. package/dist/services/embeddings/service.d.ts +76 -0
  260. package/dist/services/embeddings/service.d.ts.map +1 -0
  261. package/dist/services/embeddings/service.js +153 -0
  262. package/dist/services/embeddings/service.js.map +1 -0
  263. package/dist/services/embeddings/types.d.ts +70 -0
  264. package/dist/services/embeddings/types.d.ts.map +1 -0
  265. package/dist/services/embeddings/types.js +8 -0
  266. package/dist/services/embeddings/types.js.map +1 -0
  267. package/dist/services/id-length-cache.d.ts +156 -0
  268. package/dist/services/id-length-cache.d.ts.map +1 -0
  269. package/dist/services/id-length-cache.js +197 -0
  270. package/dist/services/id-length-cache.js.map +1 -0
  271. package/dist/services/inbox.d.ts +147 -0
  272. package/dist/services/inbox.d.ts.map +1 -0
  273. package/dist/services/inbox.js +428 -0
  274. package/dist/services/inbox.js.map +1 -0
  275. package/dist/services/index.d.ts +10 -0
  276. package/dist/services/index.d.ts.map +1 -0
  277. package/dist/services/index.js +10 -0
  278. package/dist/services/index.js.map +1 -0
  279. package/dist/services/priority-service.d.ts +145 -0
  280. package/dist/services/priority-service.d.ts.map +1 -0
  281. package/dist/services/priority-service.js +272 -0
  282. package/dist/services/priority-service.js.map +1 -0
  283. package/dist/services/search-utils.d.ts +47 -0
  284. package/dist/services/search-utils.d.ts.map +1 -0
  285. package/dist/services/search-utils.js +83 -0
  286. package/dist/services/search-utils.js.map +1 -0
  287. package/dist/sync/hash.d.ts +48 -0
  288. package/dist/sync/hash.d.ts.map +1 -0
  289. package/dist/sync/hash.js +136 -0
  290. package/dist/sync/hash.js.map +1 -0
  291. package/dist/sync/index.d.ts +11 -0
  292. package/dist/sync/index.d.ts.map +1 -0
  293. package/dist/sync/index.js +16 -0
  294. package/dist/sync/index.js.map +1 -0
  295. package/dist/sync/merge.d.ts +80 -0
  296. package/dist/sync/merge.d.ts.map +1 -0
  297. package/dist/sync/merge.js +310 -0
  298. package/dist/sync/merge.js.map +1 -0
  299. package/dist/sync/serialization.d.ts +132 -0
  300. package/dist/sync/serialization.d.ts.map +1 -0
  301. package/dist/sync/serialization.js +306 -0
  302. package/dist/sync/serialization.js.map +1 -0
  303. package/dist/sync/service.d.ts +102 -0
  304. package/dist/sync/service.d.ts.map +1 -0
  305. package/dist/sync/service.js +493 -0
  306. package/dist/sync/service.js.map +1 -0
  307. package/dist/sync/types.d.ts +275 -0
  308. package/dist/sync/types.d.ts.map +1 -0
  309. package/dist/sync/types.js +76 -0
  310. package/dist/sync/types.js.map +1 -0
  311. package/dist/systems/identity.d.ts +479 -0
  312. package/dist/systems/identity.d.ts.map +1 -0
  313. package/dist/systems/identity.js +817 -0
  314. package/dist/systems/identity.js.map +1 -0
  315. package/dist/systems/index.d.ts +8 -0
  316. package/dist/systems/index.d.ts.map +1 -0
  317. package/dist/systems/index.js +29 -0
  318. package/dist/systems/index.js.map +1 -0
  319. package/package.json +121 -0
  320. package/web/assets/charts-vendor-D1YcbGux.js +55 -0
  321. package/web/assets/dnd-vendor-DmxE-_ZH.js +5 -0
  322. package/web/assets/editor-vendor-BxraAWts.js +279 -0
  323. package/web/assets/index-B77vv208.js +341 -0
  324. package/web/assets/index-CF_XnVLh.css +1 -0
  325. package/web/assets/router-vendor-BCKpRBrB.js +41 -0
  326. package/web/assets/ui-vendor-DUahGnbT.js +45 -0
  327. package/web/assets/utils-vendor-CfYKiENT.js +813 -0
  328. package/web/favicon.ico +0 -0
  329. package/web/index.html +23 -0
  330. package/web/logo.png +0 -0
@@ -0,0 +1,698 @@
1
+ /**
2
+ * Identity Commands - Identity management and whoami
3
+ *
4
+ * Provides CLI commands for identity operations:
5
+ * - whoami: Show current actor context
6
+ * - identity: Parent command for identity operations
7
+ * - sign: Sign data using a private key
8
+ * - verify: Verify a signature against data
9
+ * - keygen: Generate a new Ed25519 keypair
10
+ */
11
+ import { readFileSync } from 'node:fs';
12
+ import { success, failure, ExitCode } from '../types.js';
13
+ import { getOutputMode } from '../formatter.js';
14
+ import { getValue, getValueSource, loadConfig } from '../../config/index.js';
15
+ import { IdentityMode, ActorSource, signEd25519, verifyEd25519Signature, generateEd25519Keypair, constructSignedData, hashRequestBody, isValidPublicKey, isValidSignature, isValidRequestHash, } from '../../systems/identity.js';
16
+ /**
17
+ * Resolves the current actor from various sources
18
+ *
19
+ * Priority order (highest to lowest):
20
+ * 1. CLI --actor flag
21
+ * 2. STONEFORGE_ACTOR environment variable
22
+ * 3. Config file actor setting
23
+ * 4. Default fallback
24
+ */
25
+ function resolveCurrentActor(options) {
26
+ // Load config with CLI overrides
27
+ const cliOverrides = options.actor ? { actor: options.actor } : undefined;
28
+ loadConfig({ cliOverrides });
29
+ // Get identity mode
30
+ const mode = getValue('identity.mode');
31
+ // Check CLI flag first
32
+ if (options.actor) {
33
+ return {
34
+ actor: options.actor,
35
+ source: ActorSource.CLI_FLAG,
36
+ verified: false,
37
+ mode,
38
+ };
39
+ }
40
+ // Check configured actor
41
+ const configuredActor = getValue('actor');
42
+ if (configuredActor) {
43
+ const source = getValueSource('actor');
44
+ return {
45
+ actor: configuredActor,
46
+ source,
47
+ verified: false,
48
+ mode,
49
+ details: source === 'environment' ? { envVar: 'STONEFORGE_ACTOR' } : undefined,
50
+ };
51
+ }
52
+ // No actor configured - return indication
53
+ return {
54
+ actor: '',
55
+ source: ActorSource.SYSTEM,
56
+ verified: false,
57
+ mode,
58
+ };
59
+ }
60
+ // ============================================================================
61
+ // Whoami Command
62
+ // ============================================================================
63
+ async function whoamiHandler(_args, options) {
64
+ const resolution = resolveCurrentActor(options);
65
+ const mode = getOutputMode(options);
66
+ // Build data object for JSON output
67
+ const data = {
68
+ actor: resolution.actor || null,
69
+ source: resolution.source,
70
+ verified: resolution.verified,
71
+ identityMode: resolution.mode,
72
+ ...(resolution.details && { details: resolution.details }),
73
+ };
74
+ if (mode === 'json') {
75
+ return success(data);
76
+ }
77
+ if (mode === 'quiet') {
78
+ if (!resolution.actor) {
79
+ return failure('No actor configured', ExitCode.NOT_FOUND);
80
+ }
81
+ return success(resolution.actor);
82
+ }
83
+ // Human-readable output
84
+ if (!resolution.actor) {
85
+ const lines = [
86
+ 'No actor configured.',
87
+ '',
88
+ 'Set an actor using one of:',
89
+ ' --actor <name> CLI flag (highest priority)',
90
+ ' STONEFORGE_ACTOR=<name> Environment variable',
91
+ ' sf config set actor <name> Configuration file',
92
+ ];
93
+ return success(data, lines.join('\n'));
94
+ }
95
+ // Build formatted output
96
+ const lines = [];
97
+ lines.push(`Actor: ${resolution.actor}`);
98
+ lines.push(`Source: ${formatSource(resolution.source)}`);
99
+ lines.push(`Identity Mode: ${resolution.mode}`);
100
+ lines.push(`Verified: ${resolution.verified ? 'yes' : 'no'}`);
101
+ if (resolution.details?.envVar) {
102
+ lines.push(`Environment Variable: ${resolution.details.envVar}`);
103
+ }
104
+ return success(data, lines.join('\n'));
105
+ }
106
+ /**
107
+ * Formats a source value for human display
108
+ */
109
+ function formatSource(source) {
110
+ switch (source) {
111
+ case ActorSource.CLI_FLAG:
112
+ case 'cli':
113
+ return 'CLI --actor flag';
114
+ case ActorSource.CONFIG:
115
+ case 'file':
116
+ return 'configuration file';
117
+ case 'environment':
118
+ return 'environment variable';
119
+ case ActorSource.EXPLICIT:
120
+ return 'explicit';
121
+ case ActorSource.ELEMENT:
122
+ return 'element';
123
+ case ActorSource.SYSTEM:
124
+ return 'system';
125
+ case 'default':
126
+ return 'default';
127
+ default:
128
+ return String(source);
129
+ }
130
+ }
131
+ export const whoamiCommand = {
132
+ name: 'whoami',
133
+ description: 'Show current actor identity',
134
+ usage: 'sf whoami',
135
+ help: `Display the current actor identity and how it was determined.
136
+
137
+ The actor is resolved from multiple sources in priority order:
138
+ 1. CLI --actor flag (highest priority)
139
+ 2. STONEFORGE_ACTOR environment variable
140
+ 3. Configuration file (actor setting)
141
+ 4. No actor (operations will require explicit actor)
142
+
143
+ Output includes:
144
+ - Actor name
145
+ - Source of the actor identity
146
+ - Identity mode (soft, cryptographic, hybrid)
147
+ - Verification status
148
+
149
+ Examples:
150
+ sf whoami
151
+ sf whoami --json
152
+ sf --actor myagent whoami`,
153
+ options: [],
154
+ handler: whoamiHandler,
155
+ };
156
+ // ============================================================================
157
+ // Identity Parent Command
158
+ // ============================================================================
159
+ async function identityHandler(args, options) {
160
+ // If no subcommand, show current identity (same as whoami)
161
+ return whoamiHandler(args, options);
162
+ }
163
+ // ============================================================================
164
+ // Private Key Resolution Helper
165
+ // ============================================================================
166
+ /**
167
+ * Resolves the private key from CLI options or environment
168
+ *
169
+ * Priority order:
170
+ * 1. --sign-key flag (direct key)
171
+ * 2. --sign-key-file flag (path to key file)
172
+ * 3. STONEFORGE_SIGN_KEY environment variable
173
+ * 4. STONEFORGE_SIGN_KEY_FILE environment variable
174
+ */
175
+ function resolvePrivateKey(options) {
176
+ // Check direct key from CLI
177
+ if (options.signKey) {
178
+ return { key: options.signKey, source: 'cli_flag' };
179
+ }
180
+ // Check key file from CLI
181
+ if (options.signKeyFile) {
182
+ try {
183
+ const key = readFileSync(options.signKeyFile, 'utf8').trim();
184
+ return { key, source: 'cli_file' };
185
+ }
186
+ catch (err) {
187
+ const message = err instanceof Error ? err.message : String(err);
188
+ throw new Error(`Failed to read key file: ${message}`);
189
+ }
190
+ }
191
+ // Check environment variable for direct key
192
+ const envKey = process.env.STONEFORGE_SIGN_KEY;
193
+ if (envKey) {
194
+ return { key: envKey, source: 'environment' };
195
+ }
196
+ // Check environment variable for key file
197
+ const envKeyFile = process.env.STONEFORGE_SIGN_KEY_FILE;
198
+ if (envKeyFile) {
199
+ try {
200
+ const key = readFileSync(envKeyFile, 'utf8').trim();
201
+ return { key, source: 'environment_file' };
202
+ }
203
+ catch (err) {
204
+ const message = err instanceof Error ? err.message : String(err);
205
+ throw new Error(`Failed to read key file from STONEFORGE_SIGN_KEY_FILE: ${message}`);
206
+ }
207
+ }
208
+ return { key: null, source: 'none' };
209
+ }
210
+ const signOptions = [
211
+ {
212
+ name: 'data',
213
+ short: 'd',
214
+ description: 'Data to sign (string)',
215
+ hasValue: true,
216
+ },
217
+ {
218
+ name: 'file',
219
+ short: 'f',
220
+ description: 'Path to file containing data to sign',
221
+ hasValue: true,
222
+ },
223
+ {
224
+ name: 'hash',
225
+ description: 'Pre-computed hash to sign (for request signing)',
226
+ hasValue: true,
227
+ },
228
+ ];
229
+ async function signHandler(_args, options) {
230
+ const mode = getOutputMode(options);
231
+ // Resolve actor
232
+ const resolution = resolveCurrentActor(options);
233
+ if (!resolution.actor) {
234
+ return failure('Actor is required for signing. Use --actor <name>', ExitCode.VALIDATION);
235
+ }
236
+ // Resolve private key
237
+ let keyInfo;
238
+ try {
239
+ keyInfo = resolvePrivateKey(options);
240
+ }
241
+ catch (err) {
242
+ const message = err instanceof Error ? err.message : String(err);
243
+ return failure(message, ExitCode.GENERAL_ERROR);
244
+ }
245
+ if (!keyInfo.key) {
246
+ return failure('Private key is required for signing. Use --sign-key <key>, --sign-key-file <path>, or set STONEFORGE_SIGN_KEY', ExitCode.VALIDATION);
247
+ }
248
+ // Get data to sign
249
+ let dataToSign;
250
+ let requestHash;
251
+ if (options.hash) {
252
+ // Validate pre-computed hash format
253
+ if (!isValidRequestHash(options.hash)) {
254
+ return failure('Invalid hash format. Expected 64-character hex-encoded SHA256 hash', ExitCode.VALIDATION);
255
+ }
256
+ requestHash = options.hash;
257
+ }
258
+ else if (options.data) {
259
+ // Hash the provided data
260
+ requestHash = await hashRequestBody(options.data);
261
+ }
262
+ else if (options.file) {
263
+ // Read and hash file contents
264
+ try {
265
+ const fileData = readFileSync(options.file, 'utf8');
266
+ requestHash = await hashRequestBody(fileData);
267
+ }
268
+ catch (err) {
269
+ const message = err instanceof Error ? err.message : String(err);
270
+ return failure(`Failed to read file: ${message}`, ExitCode.GENERAL_ERROR);
271
+ }
272
+ }
273
+ else {
274
+ return failure('No data to sign. Use --data <string>, --file <path>, or --hash <hash>', ExitCode.VALIDATION);
275
+ }
276
+ // Create signed data string
277
+ const signedAt = new Date().toISOString();
278
+ dataToSign = constructSignedData({
279
+ actor: resolution.actor,
280
+ signedAt,
281
+ requestHash,
282
+ });
283
+ // Sign the data
284
+ try {
285
+ const signature = await signEd25519(keyInfo.key, dataToSign);
286
+ const data = {
287
+ signature,
288
+ signedAt,
289
+ actor: resolution.actor,
290
+ requestHash,
291
+ keySource: keyInfo.source,
292
+ };
293
+ if (mode === 'json') {
294
+ return success(data);
295
+ }
296
+ if (mode === 'quiet') {
297
+ return success(signature);
298
+ }
299
+ const lines = [];
300
+ lines.push(`Signature: ${signature}`);
301
+ lines.push(`Signed At: ${signedAt}`);
302
+ lines.push(`Actor: ${resolution.actor}`);
303
+ lines.push(`Request Hash: ${requestHash}`);
304
+ lines.push(`Key Source: ${keyInfo.source}`);
305
+ return success(data, lines.join('\n'));
306
+ }
307
+ catch (err) {
308
+ const message = err instanceof Error ? err.message : String(err);
309
+ return failure(`Failed to sign data: ${message}`, ExitCode.GENERAL_ERROR);
310
+ }
311
+ }
312
+ export const signCommand = {
313
+ name: 'sign',
314
+ description: 'Sign data using a private key',
315
+ usage: 'sf identity sign [options]',
316
+ help: `Sign data using an Ed25519 private key.
317
+
318
+ The signature is computed over: actor|signedAt|requestHash
319
+
320
+ Options:
321
+ -d, --data <string> Data to sign (will be hashed)
322
+ -f, --file <path> File containing data to sign
323
+ --hash <hash> Pre-computed SHA256 hash (hex)
324
+ --sign-key <key> Private key (base64 PKCS8)
325
+ --sign-key-file <path> Path to private key file
326
+
327
+ The private key can also be set via environment variables:
328
+ STONEFORGE_SIGN_KEY Direct base64-encoded private key
329
+ STONEFORGE_SIGN_KEY_FILE Path to file containing private key
330
+
331
+ Examples:
332
+ sf identity sign --data "hello world" --sign-key <key> --actor alice
333
+ sf identity sign --file request.json --sign-key-file ~/.stoneforge/private.key
334
+ sf identity sign --hash abc123... --actor alice`,
335
+ options: signOptions,
336
+ handler: signHandler,
337
+ };
338
+ const verifyOptions = [
339
+ {
340
+ name: 'signature',
341
+ short: 's',
342
+ description: 'Signature to verify (base64)',
343
+ hasValue: true,
344
+ required: true,
345
+ },
346
+ {
347
+ name: 'data',
348
+ short: 'd',
349
+ description: 'Original data that was signed',
350
+ hasValue: true,
351
+ },
352
+ {
353
+ name: 'file',
354
+ short: 'f',
355
+ description: 'Path to file containing original data',
356
+ hasValue: true,
357
+ },
358
+ {
359
+ name: 'hash',
360
+ description: 'Request hash that was signed',
361
+ hasValue: true,
362
+ },
363
+ {
364
+ name: 'public-key',
365
+ short: 'k',
366
+ description: 'Public key to verify against (base64)',
367
+ hasValue: true,
368
+ required: true,
369
+ },
370
+ {
371
+ name: 'signed-at',
372
+ description: 'Timestamp when signature was created (ISO 8601)',
373
+ hasValue: true,
374
+ required: true,
375
+ },
376
+ ];
377
+ async function verifyHandler(_args, options) {
378
+ const mode = getOutputMode(options);
379
+ // Validate required options
380
+ if (!options.signature) {
381
+ return failure('--signature is required', ExitCode.VALIDATION);
382
+ }
383
+ if (!options['public-key']) {
384
+ return failure('--public-key is required', ExitCode.VALIDATION);
385
+ }
386
+ if (!options['signed-at']) {
387
+ return failure('--signed-at is required', ExitCode.VALIDATION);
388
+ }
389
+ // Resolve actor
390
+ const resolution = resolveCurrentActor(options);
391
+ if (!resolution.actor) {
392
+ return failure('Actor is required for verification. Use --actor <name>', ExitCode.VALIDATION);
393
+ }
394
+ // Validate signature format
395
+ if (!isValidSignature(options.signature)) {
396
+ return failure('Invalid signature format. Expected 88-character base64 string', ExitCode.VALIDATION);
397
+ }
398
+ // Validate public key format
399
+ if (!isValidPublicKey(options['public-key'])) {
400
+ return failure('Invalid public key format. Expected 44-character base64 string', ExitCode.VALIDATION);
401
+ }
402
+ // Get request hash
403
+ let requestHash;
404
+ if (options.hash) {
405
+ // Validate pre-computed hash format
406
+ if (!isValidRequestHash(options.hash)) {
407
+ return failure('Invalid hash format. Expected 64-character hex-encoded SHA256 hash', ExitCode.VALIDATION);
408
+ }
409
+ requestHash = options.hash;
410
+ }
411
+ else if (options.data) {
412
+ requestHash = await hashRequestBody(options.data);
413
+ }
414
+ else if (options.file) {
415
+ try {
416
+ const fileData = readFileSync(options.file, 'utf8');
417
+ requestHash = await hashRequestBody(fileData);
418
+ }
419
+ catch (err) {
420
+ const message = err instanceof Error ? err.message : String(err);
421
+ return failure(`Failed to read file: ${message}`, ExitCode.GENERAL_ERROR);
422
+ }
423
+ }
424
+ else {
425
+ return failure('Must provide --data, --file, or --hash', ExitCode.VALIDATION);
426
+ }
427
+ // Construct signed data
428
+ const signedData = constructSignedData({
429
+ actor: resolution.actor,
430
+ signedAt: options['signed-at'],
431
+ requestHash,
432
+ });
433
+ // Verify the signature
434
+ try {
435
+ const valid = await verifyEd25519Signature(options['public-key'], options.signature, signedData);
436
+ const data = {
437
+ valid,
438
+ actor: resolution.actor,
439
+ signedAt: options['signed-at'],
440
+ requestHash,
441
+ };
442
+ if (mode === 'json') {
443
+ return success(data);
444
+ }
445
+ if (mode === 'quiet') {
446
+ return success(valid ? 'valid' : 'invalid');
447
+ }
448
+ if (valid) {
449
+ return success(data, 'Signature is VALID');
450
+ }
451
+ else {
452
+ return success(data, 'Signature is INVALID');
453
+ }
454
+ }
455
+ catch (err) {
456
+ const message = err instanceof Error ? err.message : String(err);
457
+ return failure(`Failed to verify signature: ${message}`, ExitCode.GENERAL_ERROR);
458
+ }
459
+ }
460
+ export const verifyCommand = {
461
+ name: 'verify',
462
+ description: 'Verify a signature against data',
463
+ usage: 'sf identity verify [options]',
464
+ help: `Verify an Ed25519 signature against data.
465
+
466
+ The signature must have been computed over: actor|signedAt|requestHash
467
+
468
+ Required options:
469
+ -s, --signature <sig> Signature to verify (base64)
470
+ -k, --public-key <key> Public key (base64)
471
+ --signed-at <time> Timestamp when signed (ISO 8601)
472
+
473
+ Data options (one required):
474
+ -d, --data <string> Original data that was signed
475
+ -f, --file <path> File containing original data
476
+ --hash <hash> Request hash that was signed
477
+
478
+ Examples:
479
+ sf identity verify --signature <sig> --public-key <key> --signed-at 2024-01-01T00:00:00Z --data "hello" --actor alice
480
+ sf identity verify -s <sig> -k <key> --signed-at <time> --hash abc123... --actor alice`,
481
+ options: verifyOptions,
482
+ handler: verifyHandler,
483
+ };
484
+ // ============================================================================
485
+ // Keygen Command
486
+ // ============================================================================
487
+ async function keygenHandler(_args, options) {
488
+ const mode = getOutputMode(options);
489
+ try {
490
+ const keypair = await generateEd25519Keypair();
491
+ const data = {
492
+ publicKey: keypair.publicKey,
493
+ privateKey: keypair.privateKey,
494
+ };
495
+ if (mode === 'json') {
496
+ return success(data);
497
+ }
498
+ if (mode === 'quiet') {
499
+ // In quiet mode, return just the public key (safest for scripts)
500
+ return success(keypair.publicKey);
501
+ }
502
+ const lines = [];
503
+ lines.push('Generated Ed25519 keypair:');
504
+ lines.push('');
505
+ lines.push(`Public Key: ${keypair.publicKey}`);
506
+ lines.push(`Private Key: ${keypair.privateKey}`);
507
+ lines.push('');
508
+ lines.push('IMPORTANT: Store the private key securely. It cannot be recovered.');
509
+ lines.push('');
510
+ lines.push('Register this entity with:');
511
+ lines.push(` sf entity register <name> --public-key ${keypair.publicKey}`);
512
+ lines.push('');
513
+ lines.push('Sign requests with:');
514
+ lines.push(' sf --sign-key <private-key> --actor <name> <command>');
515
+ return success(data, lines.join('\n'));
516
+ }
517
+ catch (err) {
518
+ const message = err instanceof Error ? err.message : String(err);
519
+ return failure(`Failed to generate keypair: ${message}`, ExitCode.GENERAL_ERROR);
520
+ }
521
+ }
522
+ export const keygenCommand = {
523
+ name: 'keygen',
524
+ description: 'Generate a new Ed25519 keypair',
525
+ usage: 'sf identity keygen',
526
+ help: `Generate a new Ed25519 keypair for cryptographic identity.
527
+
528
+ The keypair can be used for:
529
+ - Registering an entity with a public key
530
+ - Signing requests in cryptographic mode
531
+
532
+ Output:
533
+ - Public Key: Register with 'sf entity register --public-key <key>'
534
+ - Private Key: Use with --sign-key to sign requests
535
+
536
+ SECURITY: The private key should be stored securely and never shared.
537
+
538
+ Examples:
539
+ sf identity keygen
540
+ sf identity keygen --json
541
+ sf identity keygen --quiet # Returns just the public key`,
542
+ options: [],
543
+ handler: keygenHandler,
544
+ };
545
+ const hashOptions = [
546
+ {
547
+ name: 'data',
548
+ short: 'd',
549
+ description: 'Data to hash',
550
+ hasValue: true,
551
+ },
552
+ {
553
+ name: 'file',
554
+ short: 'f',
555
+ description: 'Path to file to hash',
556
+ hasValue: true,
557
+ },
558
+ ];
559
+ async function hashHandler(_args, options) {
560
+ const mode = getOutputMode(options);
561
+ let dataToHash;
562
+ if (options.data) {
563
+ dataToHash = options.data;
564
+ }
565
+ else if (options.file) {
566
+ try {
567
+ dataToHash = readFileSync(options.file, 'utf8');
568
+ }
569
+ catch (err) {
570
+ const message = err instanceof Error ? err.message : String(err);
571
+ return failure(`Failed to read file: ${message}`, ExitCode.GENERAL_ERROR);
572
+ }
573
+ }
574
+ else {
575
+ return failure('Must provide --data or --file', ExitCode.VALIDATION);
576
+ }
577
+ try {
578
+ const hash = await hashRequestBody(dataToHash);
579
+ const data = { hash, length: dataToHash.length };
580
+ if (mode === 'json') {
581
+ return success(data);
582
+ }
583
+ if (mode === 'quiet') {
584
+ return success(hash);
585
+ }
586
+ return success(data, `SHA256: ${hash}`);
587
+ }
588
+ catch (err) {
589
+ const message = err instanceof Error ? err.message : String(err);
590
+ return failure(`Failed to hash data: ${message}`, ExitCode.GENERAL_ERROR);
591
+ }
592
+ }
593
+ export const hashCommand = {
594
+ name: 'hash',
595
+ description: 'Compute SHA256 hash of data',
596
+ usage: 'sf identity hash [options]',
597
+ help: `Compute the SHA256 hash of data for use in signing.
598
+
599
+ Options:
600
+ -d, --data <string> Data to hash
601
+ -f, --file <path> File to hash
602
+
603
+ Examples:
604
+ sf identity hash --data "hello world"
605
+ sf identity hash --file request.json`,
606
+ options: hashOptions,
607
+ handler: hashHandler,
608
+ };
609
+ export const identityCommand = {
610
+ name: 'identity',
611
+ description: 'Manage identity settings',
612
+ usage: 'sf identity [subcommand]',
613
+ help: `Manage identity settings and view current actor.
614
+
615
+ Without a subcommand, shows the current actor identity (same as 'sf whoami').
616
+
617
+ Subcommands:
618
+ whoami Show current actor identity
619
+ mode Show or set identity mode
620
+ sign Sign data using a private key
621
+ verify Verify a signature against data
622
+ keygen Generate a new Ed25519 keypair
623
+ hash Compute SHA256 hash of data
624
+
625
+ Examples:
626
+ sf identity Show current identity
627
+ sf identity whoami Same as above
628
+ sf identity mode Show current identity mode
629
+ sf identity mode soft Set identity mode to soft
630
+ sf identity keygen Generate a new keypair
631
+ sf identity sign --data "hello" --sign-key <key>
632
+ sf identity verify --signature <sig> --public-key <key> --data "hello"`,
633
+ handler: identityHandler,
634
+ subcommands: {
635
+ whoami: whoamiCommand,
636
+ sign: signCommand,
637
+ verify: verifyCommand,
638
+ keygen: keygenCommand,
639
+ hash: hashCommand,
640
+ mode: {
641
+ name: 'mode',
642
+ description: 'Show or set identity mode',
643
+ usage: 'sf identity mode [mode]',
644
+ help: `Show or set the identity verification mode.
645
+
646
+ Available modes:
647
+ soft Name-based identity without verification (default)
648
+ cryptographic Key-based identity with signature verification
649
+ hybrid Accepts both verified and unverified actors
650
+
651
+ Examples:
652
+ sf identity mode Show current mode
653
+ sf identity mode soft Set to soft mode
654
+ sf identity mode cryptographic Set to cryptographic mode`,
655
+ options: [],
656
+ handler: async (args, options) => {
657
+ const mode = getOutputMode(options);
658
+ if (args.length === 0) {
659
+ // Show current mode
660
+ loadConfig();
661
+ const currentMode = getValue('identity.mode');
662
+ const source = getValueSource('identity.mode');
663
+ const data = { mode: currentMode, source };
664
+ if (mode === 'json') {
665
+ return success(data);
666
+ }
667
+ if (mode === 'quiet') {
668
+ return success(currentMode);
669
+ }
670
+ return success(data, `Identity mode: ${currentMode} (from ${source})`);
671
+ }
672
+ // Set mode
673
+ const newMode = args[0].toLowerCase();
674
+ const validModes = Object.values(IdentityMode);
675
+ if (!validModes.includes(newMode)) {
676
+ return failure(`Invalid identity mode: ${newMode}. Must be one of: ${validModes.join(', ')}`, ExitCode.VALIDATION);
677
+ }
678
+ try {
679
+ const { setValue } = await import('../../config/index.js');
680
+ setValue('identity.mode', newMode);
681
+ const data = { mode: newMode, previous: getValue('identity.mode') };
682
+ if (mode === 'json') {
683
+ return success(data);
684
+ }
685
+ if (mode === 'quiet') {
686
+ return success(newMode);
687
+ }
688
+ return success(data, `Identity mode set to: ${newMode}`);
689
+ }
690
+ catch (err) {
691
+ const message = err instanceof Error ? err.message : String(err);
692
+ return failure(`Failed to set identity mode: ${message}`, ExitCode.GENERAL_ERROR);
693
+ }
694
+ },
695
+ },
696
+ },
697
+ };
698
+ //# sourceMappingURL=identity.js.map