@sundaeswap/sprinkles 0.6.1 → 0.8.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 (197) hide show
  1. package/README.md +178 -181
  2. package/dist/cjs/Sprinkle/__tests__/action-integration.test.js +590 -0
  3. package/dist/cjs/Sprinkle/__tests__/action-integration.test.js.map +1 -0
  4. package/dist/cjs/Sprinkle/__tests__/action-registry.test.js +193 -0
  5. package/dist/cjs/Sprinkle/__tests__/action-registry.test.js.map +1 -0
  6. package/dist/cjs/Sprinkle/__tests__/action-runner.test.js +304 -0
  7. package/dist/cjs/Sprinkle/__tests__/action-runner.test.js.map +1 -0
  8. package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js +1110 -0
  9. package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js.map +1 -0
  10. package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js +744 -0
  11. package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js.map +1 -0
  12. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +15 -1
  13. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
  14. package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js +711 -0
  15. package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -0
  16. package/dist/cjs/Sprinkle/__tests__/native-script.test.js +390 -0
  17. package/dist/cjs/Sprinkle/__tests__/native-script.test.js.map +1 -0
  18. package/dist/cjs/Sprinkle/__tests__/tui-helpers.test.js +334 -0
  19. package/dist/cjs/Sprinkle/__tests__/tui-helpers.test.js.map +1 -0
  20. package/dist/cjs/Sprinkle/__tests__/utility-actions.test.js +367 -0
  21. package/dist/cjs/Sprinkle/__tests__/utility-actions.test.js.map +1 -0
  22. package/dist/cjs/Sprinkle/__tests__/wallet-transaction-actions.test.js +749 -0
  23. package/dist/cjs/Sprinkle/__tests__/wallet-transaction-actions.test.js.map +1 -0
  24. package/dist/cjs/Sprinkle/actions/builtin/addressbook-actions.js +164 -0
  25. package/dist/cjs/Sprinkle/actions/builtin/addressbook-actions.js.map +1 -0
  26. package/dist/cjs/Sprinkle/actions/builtin/blaze-helper.js +61 -0
  27. package/dist/cjs/Sprinkle/actions/builtin/blaze-helper.js.map +1 -0
  28. package/dist/cjs/Sprinkle/actions/builtin/index.js +174 -0
  29. package/dist/cjs/Sprinkle/actions/builtin/index.js.map +1 -0
  30. package/dist/cjs/Sprinkle/actions/builtin/native-script.js +139 -0
  31. package/dist/cjs/Sprinkle/actions/builtin/native-script.js.map +1 -0
  32. package/dist/cjs/Sprinkle/actions/builtin/profile-actions.js +202 -0
  33. package/dist/cjs/Sprinkle/actions/builtin/profile-actions.js.map +1 -0
  34. package/dist/cjs/Sprinkle/actions/builtin/settings-actions.js +87 -0
  35. package/dist/cjs/Sprinkle/actions/builtin/settings-actions.js.map +1 -0
  36. package/dist/cjs/Sprinkle/actions/builtin/transaction-actions.js +345 -0
  37. package/dist/cjs/Sprinkle/actions/builtin/transaction-actions.js.map +1 -0
  38. package/dist/cjs/Sprinkle/actions/builtin/utility-actions.js +218 -0
  39. package/dist/cjs/Sprinkle/actions/builtin/utility-actions.js.map +1 -0
  40. package/dist/cjs/Sprinkle/actions/builtin/wallet-actions.js +212 -0
  41. package/dist/cjs/Sprinkle/actions/builtin/wallet-actions.js.map +1 -0
  42. package/dist/cjs/Sprinkle/actions/cli-adapter.js +390 -0
  43. package/dist/cjs/Sprinkle/actions/cli-adapter.js.map +1 -0
  44. package/dist/cjs/Sprinkle/actions/index.js +139 -0
  45. package/dist/cjs/Sprinkle/actions/index.js.map +1 -0
  46. package/dist/cjs/Sprinkle/actions/mcp-adapter.js +557 -0
  47. package/dist/cjs/Sprinkle/actions/mcp-adapter.js.map +1 -0
  48. package/dist/cjs/Sprinkle/actions/registry.js +92 -0
  49. package/dist/cjs/Sprinkle/actions/registry.js.map +1 -0
  50. package/dist/cjs/Sprinkle/actions/runner.js +190 -0
  51. package/dist/cjs/Sprinkle/actions/runner.js.map +1 -0
  52. package/dist/cjs/Sprinkle/actions/tui-helpers.js +96 -0
  53. package/dist/cjs/Sprinkle/actions/tui-helpers.js.map +1 -0
  54. package/dist/cjs/Sprinkle/actions/types.js +68 -0
  55. package/dist/cjs/Sprinkle/actions/types.js.map +1 -0
  56. package/dist/cjs/Sprinkle/index.js +678 -5
  57. package/dist/cjs/Sprinkle/index.js.map +1 -1
  58. package/dist/cjs/Sprinkle/prompts.js +12 -7
  59. package/dist/cjs/Sprinkle/prompts.js.map +1 -1
  60. package/dist/cjs/Sprinkle/schemas.js +17 -1
  61. package/dist/cjs/Sprinkle/schemas.js.map +1 -1
  62. package/dist/cjs/Sprinkle/type-guards.js +7 -1
  63. package/dist/cjs/Sprinkle/type-guards.js.map +1 -1
  64. package/dist/esm/Sprinkle/__tests__/action-integration.test.js +588 -0
  65. package/dist/esm/Sprinkle/__tests__/action-integration.test.js.map +1 -0
  66. package/dist/esm/Sprinkle/__tests__/action-registry.test.js +192 -0
  67. package/dist/esm/Sprinkle/__tests__/action-registry.test.js.map +1 -0
  68. package/dist/esm/Sprinkle/__tests__/action-runner.test.js +302 -0
  69. package/dist/esm/Sprinkle/__tests__/action-runner.test.js.map +1 -0
  70. package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js +1107 -0
  71. package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js.map +1 -0
  72. package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js +742 -0
  73. package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js.map +1 -0
  74. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +15 -1
  75. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
  76. package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js +710 -0
  77. package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -0
  78. package/dist/esm/Sprinkle/__tests__/native-script.test.js +388 -0
  79. package/dist/esm/Sprinkle/__tests__/native-script.test.js.map +1 -0
  80. package/dist/esm/Sprinkle/__tests__/tui-helpers.test.js +332 -0
  81. package/dist/esm/Sprinkle/__tests__/tui-helpers.test.js.map +1 -0
  82. package/dist/esm/Sprinkle/__tests__/utility-actions.test.js +365 -0
  83. package/dist/esm/Sprinkle/__tests__/utility-actions.test.js.map +1 -0
  84. package/dist/esm/Sprinkle/__tests__/wallet-transaction-actions.test.js +747 -0
  85. package/dist/esm/Sprinkle/__tests__/wallet-transaction-actions.test.js.map +1 -0
  86. package/dist/esm/Sprinkle/actions/builtin/addressbook-actions.js +159 -0
  87. package/dist/esm/Sprinkle/actions/builtin/addressbook-actions.js.map +1 -0
  88. package/dist/esm/Sprinkle/actions/builtin/blaze-helper.js +55 -0
  89. package/dist/esm/Sprinkle/actions/builtin/blaze-helper.js.map +1 -0
  90. package/dist/esm/Sprinkle/actions/builtin/index.js +37 -0
  91. package/dist/esm/Sprinkle/actions/builtin/index.js.map +1 -0
  92. package/dist/esm/Sprinkle/actions/builtin/native-script.js +133 -0
  93. package/dist/esm/Sprinkle/actions/builtin/native-script.js.map +1 -0
  94. package/dist/esm/Sprinkle/actions/builtin/profile-actions.js +197 -0
  95. package/dist/esm/Sprinkle/actions/builtin/profile-actions.js.map +1 -0
  96. package/dist/esm/Sprinkle/actions/builtin/settings-actions.js +81 -0
  97. package/dist/esm/Sprinkle/actions/builtin/settings-actions.js.map +1 -0
  98. package/dist/esm/Sprinkle/actions/builtin/transaction-actions.js +340 -0
  99. package/dist/esm/Sprinkle/actions/builtin/transaction-actions.js.map +1 -0
  100. package/dist/esm/Sprinkle/actions/builtin/utility-actions.js +213 -0
  101. package/dist/esm/Sprinkle/actions/builtin/utility-actions.js.map +1 -0
  102. package/dist/esm/Sprinkle/actions/builtin/wallet-actions.js +207 -0
  103. package/dist/esm/Sprinkle/actions/builtin/wallet-actions.js.map +1 -0
  104. package/dist/esm/Sprinkle/actions/cli-adapter.js +379 -0
  105. package/dist/esm/Sprinkle/actions/cli-adapter.js.map +1 -0
  106. package/dist/esm/Sprinkle/actions/index.js +12 -0
  107. package/dist/esm/Sprinkle/actions/index.js.map +1 -0
  108. package/dist/esm/Sprinkle/actions/mcp-adapter.js +547 -0
  109. package/dist/esm/Sprinkle/actions/mcp-adapter.js.map +1 -0
  110. package/dist/esm/Sprinkle/actions/registry.js +85 -0
  111. package/dist/esm/Sprinkle/actions/registry.js.map +1 -0
  112. package/dist/esm/Sprinkle/actions/runner.js +182 -0
  113. package/dist/esm/Sprinkle/actions/runner.js.map +1 -0
  114. package/dist/esm/Sprinkle/actions/tui-helpers.js +91 -0
  115. package/dist/esm/Sprinkle/actions/tui-helpers.js.map +1 -0
  116. package/dist/esm/Sprinkle/actions/types.js +61 -0
  117. package/dist/esm/Sprinkle/actions/types.js.map +1 -0
  118. package/dist/esm/Sprinkle/index.js +517 -7
  119. package/dist/esm/Sprinkle/index.js.map +1 -1
  120. package/dist/esm/Sprinkle/prompts.js +12 -7
  121. package/dist/esm/Sprinkle/prompts.js.map +1 -1
  122. package/dist/esm/Sprinkle/schemas.js +16 -0
  123. package/dist/esm/Sprinkle/schemas.js.map +1 -1
  124. package/dist/esm/Sprinkle/type-guards.js +3 -0
  125. package/dist/esm/Sprinkle/type-guards.js.map +1 -1
  126. package/dist/types/Sprinkle/actions/builtin/addressbook-actions.d.ts +50 -0
  127. package/dist/types/Sprinkle/actions/builtin/addressbook-actions.d.ts.map +1 -0
  128. package/dist/types/Sprinkle/actions/builtin/blaze-helper.d.ts +39 -0
  129. package/dist/types/Sprinkle/actions/builtin/blaze-helper.d.ts.map +1 -0
  130. package/dist/types/Sprinkle/actions/builtin/index.d.ts +30 -0
  131. package/dist/types/Sprinkle/actions/builtin/index.d.ts.map +1 -0
  132. package/dist/types/Sprinkle/actions/builtin/native-script.d.ts +27 -0
  133. package/dist/types/Sprinkle/actions/builtin/native-script.d.ts.map +1 -0
  134. package/dist/types/Sprinkle/actions/builtin/profile-actions.d.ts +55 -0
  135. package/dist/types/Sprinkle/actions/builtin/profile-actions.d.ts.map +1 -0
  136. package/dist/types/Sprinkle/actions/builtin/settings-actions.d.ts +32 -0
  137. package/dist/types/Sprinkle/actions/builtin/settings-actions.d.ts.map +1 -0
  138. package/dist/types/Sprinkle/actions/builtin/transaction-actions.d.ts +70 -0
  139. package/dist/types/Sprinkle/actions/builtin/transaction-actions.d.ts.map +1 -0
  140. package/dist/types/Sprinkle/actions/builtin/utility-actions.d.ts +48 -0
  141. package/dist/types/Sprinkle/actions/builtin/utility-actions.d.ts.map +1 -0
  142. package/dist/types/Sprinkle/actions/builtin/wallet-actions.d.ts +50 -0
  143. package/dist/types/Sprinkle/actions/builtin/wallet-actions.d.ts.map +1 -0
  144. package/dist/types/Sprinkle/actions/cli-adapter.d.ts +104 -0
  145. package/dist/types/Sprinkle/actions/cli-adapter.d.ts.map +1 -0
  146. package/dist/types/Sprinkle/actions/index.d.ts +13 -0
  147. package/dist/types/Sprinkle/actions/index.d.ts.map +1 -0
  148. package/dist/types/Sprinkle/actions/mcp-adapter.d.ts +116 -0
  149. package/dist/types/Sprinkle/actions/mcp-adapter.d.ts.map +1 -0
  150. package/dist/types/Sprinkle/actions/registry.d.ts +42 -0
  151. package/dist/types/Sprinkle/actions/registry.d.ts.map +1 -0
  152. package/dist/types/Sprinkle/actions/runner.d.ts +45 -0
  153. package/dist/types/Sprinkle/actions/runner.d.ts.map +1 -0
  154. package/dist/types/Sprinkle/actions/tui-helpers.d.ts +53 -0
  155. package/dist/types/Sprinkle/actions/tui-helpers.d.ts.map +1 -0
  156. package/dist/types/Sprinkle/actions/types.d.ts +76 -0
  157. package/dist/types/Sprinkle/actions/types.d.ts.map +1 -0
  158. package/dist/types/Sprinkle/index.d.ts +84 -2
  159. package/dist/types/Sprinkle/index.d.ts.map +1 -1
  160. package/dist/types/Sprinkle/prompts.d.ts.map +1 -1
  161. package/dist/types/Sprinkle/schemas.d.ts +72 -0
  162. package/dist/types/Sprinkle/schemas.d.ts.map +1 -1
  163. package/dist/types/Sprinkle/type-guards.d.ts +4 -1
  164. package/dist/types/Sprinkle/type-guards.d.ts.map +1 -1
  165. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  166. package/package.json +9 -2
  167. package/src/Sprinkle/__tests__/action-integration.test.ts +558 -0
  168. package/src/Sprinkle/__tests__/action-registry.test.ts +187 -0
  169. package/src/Sprinkle/__tests__/action-runner.test.ts +324 -0
  170. package/src/Sprinkle/__tests__/builtin-actions.test.ts +1022 -0
  171. package/src/Sprinkle/__tests__/cli-adapter.test.ts +736 -0
  172. package/src/Sprinkle/__tests__/fill-in-struct.test.ts +23 -1
  173. package/src/Sprinkle/__tests__/mcp-adapter.test.ts +720 -0
  174. package/src/Sprinkle/__tests__/native-script.test.ts +341 -0
  175. package/src/Sprinkle/__tests__/tui-helpers.test.ts +325 -0
  176. package/src/Sprinkle/__tests__/utility-actions.test.ts +348 -0
  177. package/src/Sprinkle/__tests__/wallet-transaction-actions.test.ts +695 -0
  178. package/src/Sprinkle/actions/builtin/addressbook-actions.ts +168 -0
  179. package/src/Sprinkle/actions/builtin/blaze-helper.ts +89 -0
  180. package/src/Sprinkle/actions/builtin/index.ts +125 -0
  181. package/src/Sprinkle/actions/builtin/native-script.ts +165 -0
  182. package/src/Sprinkle/actions/builtin/profile-actions.ts +229 -0
  183. package/src/Sprinkle/actions/builtin/settings-actions.ts +99 -0
  184. package/src/Sprinkle/actions/builtin/transaction-actions.ts +381 -0
  185. package/src/Sprinkle/actions/builtin/utility-actions.ts +285 -0
  186. package/src/Sprinkle/actions/builtin/wallet-actions.ts +233 -0
  187. package/src/Sprinkle/actions/cli-adapter.ts +446 -0
  188. package/src/Sprinkle/actions/index.ts +33 -0
  189. package/src/Sprinkle/actions/mcp-adapter.ts +638 -0
  190. package/src/Sprinkle/actions/registry.ts +97 -0
  191. package/src/Sprinkle/actions/runner.ts +200 -0
  192. package/src/Sprinkle/actions/tui-helpers.ts +114 -0
  193. package/src/Sprinkle/actions/types.ts +91 -0
  194. package/src/Sprinkle/index.ts +612 -3
  195. package/src/Sprinkle/prompts.ts +118 -72
  196. package/src/Sprinkle/schemas.ts +20 -0
  197. package/src/Sprinkle/type-guards.ts +9 -0
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Built-in profile management actions for the Sprinkle action system.
3
+ * These actions expose profile CRUD operations as non-interactive actions
4
+ * available in CLI and MCP modes.
5
+ *
6
+ * All actions use category "sprinkles".
7
+ */
8
+
9
+ import { Type } from "@sinclair/typebox";
10
+ import { ActionError } from "../types.js";
11
+ /** Shape of a profile entry as returned by profile actions */
12
+ const ProfileEntrySchema = Type.Object({
13
+ id: Type.String(),
14
+ name: Type.String(),
15
+ description: Type.Optional(Type.String()),
16
+ createdAt: Type.String(),
17
+ updatedAt: Type.String()
18
+ });
19
+
20
+ /**
21
+ * `list-profiles` -- Returns all profiles and the active profile ID.
22
+ */
23
+ export const listProfiles = {
24
+ name: "list-profiles",
25
+ description: "List all profiles and indicate which one is currently active.",
26
+ category: "sprinkles",
27
+ inputSchema: Type.Object({}),
28
+ outputSchema: Type.Object({
29
+ profiles: Type.Array(ProfileEntrySchema),
30
+ activeProfileId: Type.Optional(Type.String())
31
+ }),
32
+ execute: async (_input, context) => {
33
+ const entries = context.sprinkle.scanProfiles();
34
+ const profiles = entries.map(e => ({
35
+ id: e.id,
36
+ name: e.meta.name,
37
+ description: e.meta.description,
38
+ createdAt: e.meta.createdAt,
39
+ updatedAt: e.meta.updatedAt
40
+ }));
41
+ return {
42
+ profiles,
43
+ activeProfileId: context.sprinkle.currentProfile?.id
44
+ };
45
+ }
46
+ };
47
+
48
+ /**
49
+ * `get-profile` -- Returns metadata and settings for a specific profile.
50
+ * Settings are masked by default; pass `includeSensitive: true` to get raw values.
51
+ */
52
+ export const getProfile = {
53
+ name: "get-profile",
54
+ description: "Get metadata and settings for a profile. Sensitive fields are masked by default.",
55
+ category: "sprinkles",
56
+ inputSchema: Type.Object({
57
+ id: Type.String(),
58
+ includeSensitive: Type.Optional(Type.Boolean({
59
+ default: false
60
+ }))
61
+ }),
62
+ outputSchema: Type.Object({
63
+ id: Type.String(),
64
+ name: Type.String(),
65
+ description: Type.Optional(Type.String()),
66
+ createdAt: Type.String(),
67
+ updatedAt: Type.String(),
68
+ settings: Type.Any()
69
+ }),
70
+ execute: async (input, context) => {
71
+ // Validate ID to prevent path traversal (defense in depth)
72
+ if (input.id.includes("/") || input.id.includes("\\") || input.id.includes("..")) {
73
+ throw new ActionError(`Invalid profile ID "${input.id}".`, "INVALID_PROFILE_ID", {
74
+ id: input.id
75
+ });
76
+ }
77
+ const profile = context.sprinkle.getProfileById(input.id);
78
+ if (!profile) {
79
+ throw new ActionError(`Profile "${input.id}" not found.`, "PROFILE_NOT_FOUND", {
80
+ id: input.id
81
+ });
82
+ }
83
+
84
+ // Read the profile file to get settings
85
+ const fs = await import("fs");
86
+ const path = await import("path");
87
+ const storagePath = context.sprinkle.storagePath;
88
+ const profilePath = path.join(storagePath, "profiles", `${input.id}.json`);
89
+ let settings = {};
90
+ try {
91
+ const content = fs.readFileSync(profilePath, "utf-8");
92
+ const parsed = JSON.parse(content);
93
+ settings = parsed.settings ?? {};
94
+ } catch (err) {
95
+ // Throw an error so the caller knows the profile file couldn't be read
96
+ throw new ActionError(`Failed to read profile "${input.id}": ${err instanceof Error ? err.message : String(err)}`, "PROFILE_READ_ERROR", {
97
+ id: input.id,
98
+ error: err instanceof Error ? err.message : String(err)
99
+ });
100
+ }
101
+
102
+ // Mask sensitive fields unless includeSensitive is true
103
+ if (!input.includeSensitive) {
104
+ const {
105
+ maskSensitiveFields
106
+ } = await import("../../encryption.js");
107
+ settings = maskSensitiveFields(settings, context.sprinkle.type);
108
+ }
109
+ return {
110
+ id: profile.id,
111
+ name: profile.meta.name,
112
+ description: profile.meta.description,
113
+ createdAt: profile.meta.createdAt,
114
+ updatedAt: profile.meta.updatedAt,
115
+ settings
116
+ };
117
+ }
118
+ };
119
+
120
+ /**
121
+ * `set-profile` -- Switch the active profile.
122
+ */
123
+ export const setProfile = {
124
+ name: "set-profile",
125
+ description: "Switch the active profile.",
126
+ category: "sprinkles",
127
+ inputSchema: Type.Object({
128
+ id: Type.String()
129
+ }),
130
+ outputSchema: Type.Object({
131
+ id: Type.String(),
132
+ name: Type.String()
133
+ }),
134
+ execute: async (input, context) => {
135
+ const profile = context.sprinkle.getProfileById(input.id);
136
+ if (!profile) {
137
+ throw new ActionError(`Profile "${input.id}" not found.`, "PROFILE_NOT_FOUND", {
138
+ id: input.id
139
+ });
140
+ }
141
+ await context.sprinkle.loadProfile(input.id);
142
+ return {
143
+ id: input.id,
144
+ name: profile.meta.name
145
+ };
146
+ }
147
+ };
148
+
149
+ /**
150
+ * `create-profile` -- Create a new profile without prompts.
151
+ */
152
+ export const createProfile = {
153
+ name: "create-profile",
154
+ description: "Create a new profile.",
155
+ category: "sprinkles",
156
+ inputSchema: Type.Object({
157
+ name: Type.String(),
158
+ description: Type.Optional(Type.String()),
159
+ settings: Type.Optional(Type.Any())
160
+ }),
161
+ outputSchema: Type.Object({
162
+ id: Type.String(),
163
+ name: Type.String()
164
+ }),
165
+ execute: async (input, context) => {
166
+ const entry = await context.sprinkle.createProfileNonInteractive(input.name, input.description, input.settings);
167
+ return {
168
+ id: entry.id,
169
+ name: entry.meta.name
170
+ };
171
+ }
172
+ };
173
+
174
+ /**
175
+ * `delete-profile` -- Delete a profile by ID.
176
+ * Cannot delete the only profile or the currently active profile.
177
+ */
178
+ export const deleteProfile = {
179
+ name: "delete-profile",
180
+ description: "Delete a profile. Cannot delete the only profile or the active profile.",
181
+ category: "sprinkles",
182
+ inputSchema: Type.Object({
183
+ id: Type.String()
184
+ }),
185
+ outputSchema: Type.Object({
186
+ deleted: Type.Boolean(),
187
+ id: Type.String()
188
+ }),
189
+ execute: async (input, context) => {
190
+ context.sprinkle.deleteProfileById(input.id);
191
+ return {
192
+ deleted: true,
193
+ id: input.id
194
+ };
195
+ }
196
+ };
197
+ //# sourceMappingURL=profile-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-actions.js","names":["Type","ActionError","ProfileEntrySchema","Object","id","String","name","description","Optional","createdAt","updatedAt","listProfiles","category","inputSchema","outputSchema","profiles","Array","activeProfileId","execute","_input","context","entries","sprinkle","scanProfiles","map","e","meta","currentProfile","getProfile","includeSensitive","Boolean","default","settings","Any","input","includes","profile","getProfileById","fs","path","storagePath","profilePath","join","content","readFileSync","parsed","JSON","parse","err","Error","message","error","maskSensitiveFields","type","setProfile","loadProfile","createProfile","entry","createProfileNonInteractive","deleteProfile","deleted","deleteProfileById"],"sources":["../../../../../src/Sprinkle/actions/builtin/profile-actions.ts"],"sourcesContent":["/**\n * Built-in profile management actions for the Sprinkle action system.\n * These actions expose profile CRUD operations as non-interactive actions\n * available in CLI and MCP modes.\n *\n * All actions use category \"sprinkles\".\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport { ActionError } from \"../types.js\";\nimport type { IAction } from \"../types.js\";\n\n/** Shape of a profile entry as returned by profile actions */\nconst ProfileEntrySchema = Type.Object({\n id: Type.String(),\n name: Type.String(),\n description: Type.Optional(Type.String()),\n createdAt: Type.String(),\n updatedAt: Type.String(),\n});\n\n/**\n * `list-profiles` -- Returns all profiles and the active profile ID.\n */\nexport const listProfiles: IAction<\n Record<string, never>,\n { profiles: unknown[]; activeProfileId: string | undefined },\n TSchema\n> = {\n name: \"list-profiles\",\n description: \"List all profiles and indicate which one is currently active.\",\n category: \"sprinkles\",\n inputSchema: Type.Object({}),\n outputSchema: Type.Object({\n profiles: Type.Array(ProfileEntrySchema),\n activeProfileId: Type.Optional(Type.String()),\n }),\n execute: async (_input, context) => {\n const entries = context.sprinkle.scanProfiles();\n const profiles = entries.map((e) => ({\n id: e.id,\n name: e.meta.name,\n description: e.meta.description,\n createdAt: e.meta.createdAt,\n updatedAt: e.meta.updatedAt,\n }));\n return {\n profiles,\n activeProfileId: context.sprinkle.currentProfile?.id,\n };\n },\n};\n\n/**\n * `get-profile` -- Returns metadata and settings for a specific profile.\n * Settings are masked by default; pass `includeSensitive: true` to get raw values.\n */\nexport const getProfile: IAction<\n { id: string; includeSensitive?: boolean },\n unknown,\n TSchema\n> = {\n name: \"get-profile\",\n description:\n \"Get metadata and settings for a profile. Sensitive fields are masked by default.\",\n category: \"sprinkles\",\n inputSchema: Type.Object({\n id: Type.String(),\n includeSensitive: Type.Optional(Type.Boolean({ default: false })),\n }),\n outputSchema: Type.Object({\n id: Type.String(),\n name: Type.String(),\n description: Type.Optional(Type.String()),\n createdAt: Type.String(),\n updatedAt: Type.String(),\n settings: Type.Any(),\n }),\n execute: async (input, context) => {\n // Validate ID to prevent path traversal (defense in depth)\n if (input.id.includes(\"/\") || input.id.includes(\"\\\\\") || input.id.includes(\"..\")) {\n throw new ActionError(\n `Invalid profile ID \"${input.id}\".`,\n \"INVALID_PROFILE_ID\",\n { id: input.id },\n );\n }\n\n const profile = context.sprinkle.getProfileById(input.id);\n if (!profile) {\n throw new ActionError(\n `Profile \"${input.id}\" not found.`,\n \"PROFILE_NOT_FOUND\",\n { id: input.id },\n );\n }\n\n // Read the profile file to get settings\n const fs = await import(\"fs\");\n const path = await import(\"path\");\n const storagePath = context.sprinkle.storagePath;\n\n const profilePath = path.join(storagePath, \"profiles\", `${input.id}.json`);\n let settings: unknown = {};\n try {\n const content = fs.readFileSync(profilePath, \"utf-8\");\n const parsed = JSON.parse(content);\n settings = parsed.settings ?? {};\n } catch (err) {\n // Throw an error so the caller knows the profile file couldn't be read\n throw new ActionError(\n `Failed to read profile \"${input.id}\": ${err instanceof Error ? err.message : String(err)}`,\n \"PROFILE_READ_ERROR\",\n { id: input.id, error: err instanceof Error ? err.message : String(err) },\n );\n }\n\n // Mask sensitive fields unless includeSensitive is true\n if (!input.includeSensitive) {\n const { maskSensitiveFields } = await import(\"../../encryption.js\");\n settings = maskSensitiveFields(settings, context.sprinkle.type);\n }\n\n return {\n id: profile.id,\n name: profile.meta.name,\n description: profile.meta.description,\n createdAt: profile.meta.createdAt,\n updatedAt: profile.meta.updatedAt,\n settings,\n };\n },\n};\n\n/**\n * `set-profile` -- Switch the active profile.\n */\nexport const setProfile: IAction<\n { id: string },\n { id: string; name: string },\n TSchema\n> = {\n name: \"set-profile\",\n description: \"Switch the active profile.\",\n category: \"sprinkles\",\n inputSchema: Type.Object({\n id: Type.String(),\n }),\n outputSchema: Type.Object({\n id: Type.String(),\n name: Type.String(),\n }),\n execute: async (input, context) => {\n const profile = context.sprinkle.getProfileById(input.id);\n if (!profile) {\n throw new ActionError(\n `Profile \"${input.id}\" not found.`,\n \"PROFILE_NOT_FOUND\",\n { id: input.id },\n );\n }\n\n await context.sprinkle.loadProfile(input.id);\n return {\n id: input.id,\n name: profile.meta.name,\n };\n },\n};\n\n/**\n * `create-profile` -- Create a new profile without prompts.\n */\nexport const createProfile: IAction<\n { name: string; description?: string; settings?: unknown },\n { id: string; name: string },\n TSchema\n> = {\n name: \"create-profile\",\n description: \"Create a new profile.\",\n category: \"sprinkles\",\n inputSchema: Type.Object({\n name: Type.String(),\n description: Type.Optional(Type.String()),\n settings: Type.Optional(Type.Any()),\n }),\n outputSchema: Type.Object({\n id: Type.String(),\n name: Type.String(),\n }),\n execute: async (input, context) => {\n const entry = await context.sprinkle.createProfileNonInteractive(\n input.name,\n input.description,\n input.settings as never,\n );\n return {\n id: entry.id,\n name: entry.meta.name,\n };\n },\n};\n\n/**\n * `delete-profile` -- Delete a profile by ID.\n * Cannot delete the only profile or the currently active profile.\n */\nexport const deleteProfile: IAction<\n { id: string },\n { deleted: boolean; id: string },\n TSchema\n> = {\n name: \"delete-profile\",\n description:\n \"Delete a profile. Cannot delete the only profile or the active profile.\",\n category: \"sprinkles\",\n inputSchema: Type.Object({\n id: Type.String(),\n }),\n outputSchema: Type.Object({\n deleted: Type.Boolean(),\n id: Type.String(),\n }),\n execute: async (input, context) => {\n context.sprinkle.deleteProfileById(input.id);\n return { deleted: true, id: input.id };\n },\n};\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Built-in settings actions for the Sprinkle action system.
3
+ * These actions expose settings read and update operations as non-interactive
4
+ * actions available in CLI and MCP modes.
5
+ *
6
+ * All actions use category "sprinkles".
7
+ */
8
+
9
+ import { Type } from "@sinclair/typebox";
10
+ import { Value } from "@sinclair/typebox/value";
11
+ import { ActionError } from "../types.js";
12
+ /**
13
+ * `get-settings` -- Returns the current profile settings.
14
+ * Sensitive fields are masked by default; pass `includeSensitive: true` for raw values.
15
+ */
16
+ export const getSettings = {
17
+ name: "get-settings",
18
+ description: "Get the current profile settings. Sensitive fields are masked by default.",
19
+ category: "sprinkles",
20
+ inputSchema: Type.Object({
21
+ includeSensitive: Type.Optional(Type.Boolean({
22
+ default: false
23
+ }))
24
+ }),
25
+ outputSchema: Type.Object({
26
+ settings: Type.Any(),
27
+ profileId: Type.String(),
28
+ profileName: Type.String()
29
+ }),
30
+ execute: async (input, context) => {
31
+ const settings = input.includeSensitive ? context.sprinkle.settings : context.sprinkle.getDisplaySettings();
32
+ return {
33
+ settings,
34
+ profileId: context.sprinkle.profileId,
35
+ profileName: context.sprinkle.profileMeta.name
36
+ };
37
+ }
38
+ };
39
+
40
+ /**
41
+ * `update-settings` -- Shallow-merges settings onto the current profile and persists.
42
+ * Validates the merged result against the Sprinkle schema before saving.
43
+ * Returns masked settings.
44
+ */
45
+ export const updateSettings = {
46
+ name: "update-settings",
47
+ description: "Merge partial settings onto the current profile and save. Returns masked settings.",
48
+ category: "sprinkles",
49
+ inputSchema: Type.Object({
50
+ settings: Type.Record(Type.String(), Type.Any())
51
+ }),
52
+ outputSchema: Type.Object({
53
+ settings: Type.Any(),
54
+ profileId: Type.String()
55
+ }),
56
+ execute: async (input, context) => {
57
+ // Shallow merge the incoming settings onto the current settings
58
+ const merged = {
59
+ ...context.sprinkle.settings,
60
+ ...input.settings
61
+ };
62
+
63
+ // Validate the merged result against the Sprinkle schema
64
+ if (!Value.Check(context.sprinkle.type, merged)) {
65
+ const errors = [...Value.Errors(context.sprinkle.type, merged)];
66
+ const message = errors.map(e => `${e.path}: ${e.message}`).join("; ");
67
+ throw new ActionError(`Settings validation failed: ${message}`, "VALIDATION_ERROR", {
68
+ errors
69
+ });
70
+ }
71
+
72
+ // Apply and persist
73
+ context.sprinkle.settings = merged;
74
+ context.sprinkle.saveSettings();
75
+ return {
76
+ settings: context.sprinkle.getDisplaySettings(),
77
+ profileId: context.sprinkle.profileId
78
+ };
79
+ }
80
+ };
81
+ //# sourceMappingURL=settings-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-actions.js","names":["Type","Value","ActionError","getSettings","name","description","category","inputSchema","Object","includeSensitive","Optional","Boolean","default","outputSchema","settings","Any","profileId","String","profileName","execute","input","context","sprinkle","getDisplaySettings","profileMeta","updateSettings","Record","merged","Check","type","errors","Errors","message","map","e","path","join","saveSettings"],"sources":["../../../../../src/Sprinkle/actions/builtin/settings-actions.ts"],"sourcesContent":["/**\n * Built-in settings actions for the Sprinkle action system.\n * These actions expose settings read and update operations as non-interactive\n * actions available in CLI and MCP modes.\n *\n * All actions use category \"sprinkles\".\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport { Value } from \"@sinclair/typebox/value\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport { ActionError } from \"../types.js\";\nimport type { IAction } from \"../types.js\";\n\n/**\n * `get-settings` -- Returns the current profile settings.\n * Sensitive fields are masked by default; pass `includeSensitive: true` for raw values.\n */\nexport const getSettings: IAction<\n { includeSensitive?: boolean },\n { settings: unknown; profileId: string; profileName: string },\n TSchema\n> = {\n name: \"get-settings\",\n description:\n \"Get the current profile settings. Sensitive fields are masked by default.\",\n category: \"sprinkles\",\n inputSchema: Type.Object({\n includeSensitive: Type.Optional(Type.Boolean({ default: false })),\n }),\n outputSchema: Type.Object({\n settings: Type.Any(),\n profileId: Type.String(),\n profileName: Type.String(),\n }),\n execute: async (input, context) => {\n const settings = input.includeSensitive\n ? context.sprinkle.settings\n : context.sprinkle.getDisplaySettings();\n\n return {\n settings,\n profileId: context.sprinkle.profileId,\n profileName: context.sprinkle.profileMeta.name,\n };\n },\n};\n\n/**\n * `update-settings` -- Shallow-merges settings onto the current profile and persists.\n * Validates the merged result against the Sprinkle schema before saving.\n * Returns masked settings.\n */\nexport const updateSettings: IAction<\n { settings: Record<string, unknown> },\n { settings: unknown; profileId: string },\n TSchema\n> = {\n name: \"update-settings\",\n description:\n \"Merge partial settings onto the current profile and save. Returns masked settings.\",\n category: \"sprinkles\",\n inputSchema: Type.Object({\n settings: Type.Record(Type.String(), Type.Any()),\n }),\n outputSchema: Type.Object({\n settings: Type.Any(),\n profileId: Type.String(),\n }),\n execute: async (input, context) => {\n // Shallow merge the incoming settings onto the current settings\n const merged = {\n ...(context.sprinkle.settings as Record<string, unknown>),\n ...input.settings,\n };\n\n // Validate the merged result against the Sprinkle schema\n if (!Value.Check(context.sprinkle.type, merged)) {\n const errors = [...Value.Errors(context.sprinkle.type, merged)];\n const message = errors\n .map((e) => `${e.path}: ${e.message}`)\n .join(\"; \");\n throw new ActionError(\n `Settings validation failed: ${message}`,\n \"VALIDATION_ERROR\",\n { errors },\n );\n }\n\n // Apply and persist\n context.sprinkle.settings = merged as typeof context.sprinkle.settings;\n context.sprinkle.saveSettings();\n\n return {\n settings: context.sprinkle.getDisplaySettings(),\n profileId: context.sprinkle.profileId,\n };\n },\n};\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,340 @@
1
+ /**
2
+ * Built-in transaction actions for the Sprinkle action system.
3
+ * These actions expose transaction signing, submission, and decoding
4
+ * as non-interactive actions available in CLI and MCP modes.
5
+ *
6
+ * Sign/submit actions use category "wallet".
7
+ * The decode action uses category "transaction" (no wallet required).
8
+ */
9
+
10
+ import { Type } from "@sinclair/typebox";
11
+ import { Core } from "@blaze-cardano/sdk";
12
+ import { ActionError } from "../types.js";
13
+ import { getBlazeFromContext, isHotWallet } from "./blaze-helper.js";
14
+ import { countSignatures, getRequiredSigners } from "../../tx-dialog.js";
15
+
16
+ /** Shared token entry schema (also used in wallet-actions) */
17
+ const TokenEntrySchema = Type.Object({
18
+ policyId: Type.String({
19
+ description: "Policy ID of the token"
20
+ }),
21
+ assetName: Type.String({
22
+ description: "Asset name of the token (hex)"
23
+ }),
24
+ quantity: Type.String({
25
+ description: "Token quantity as string (BigInt-safe)"
26
+ })
27
+ });
28
+
29
+ /**
30
+ * Parses a hex CBOR string into a `Core.Transaction`.
31
+ * Throws `ActionError("INVALID_CBOR")` if the string is not valid transaction CBOR.
32
+ */
33
+ function parseTxCbor(txCbor) {
34
+ try {
35
+ return Core.Transaction.fromCbor(Core.TxCBOR(txCbor));
36
+ } catch (err) {
37
+ throw new ActionError(`Invalid transaction CBOR: ${err instanceof Error ? err.message : String(err)}`, "INVALID_CBOR", {
38
+ error: err instanceof Error ? err.message : String(err)
39
+ });
40
+ }
41
+ }
42
+
43
+ /**
44
+ * `sign-transaction` -- Signs a transaction with the configured hot wallet.
45
+ * Cold wallets cannot sign; use an external signing tool and submit the result.
46
+ */
47
+ export const signTransaction = {
48
+ name: "sign-transaction",
49
+ description: "Sign a transaction with the configured hot wallet. Requires a hot wallet; cold wallets cannot sign.",
50
+ category: "wallet",
51
+ inputSchema: Type.Object({
52
+ txCbor: Type.String({
53
+ description: "Transaction CBOR hex string to sign"
54
+ })
55
+ }),
56
+ outputSchema: Type.Object({
57
+ signedTxCbor: Type.String({
58
+ description: "Signed transaction CBOR hex"
59
+ }),
60
+ txHash: Type.String({
61
+ description: "Transaction hash"
62
+ }),
63
+ signatureCount: Type.Number({
64
+ description: "Number of VKey witnesses after signing"
65
+ })
66
+ }),
67
+ execute: async (input, context) => {
68
+ const blaze = await getBlazeFromContext(context);
69
+ if (!isHotWallet(blaze)) {
70
+ throw new ActionError("Cold wallets cannot sign transactions. Export the CBOR and sign externally, then use submit-transaction.", "COLD_WALLET");
71
+ }
72
+ const tx = parseTxCbor(input.txCbor);
73
+ let signedTx;
74
+ try {
75
+ signedTx = await blaze.signTransaction(tx);
76
+ } catch (err) {
77
+ throw new ActionError(`Failed to sign transaction: ${err instanceof Error ? err.message : String(err)}`, "SIGN_ERROR", {
78
+ error: err instanceof Error ? err.message : String(err)
79
+ });
80
+ }
81
+ return {
82
+ signedTxCbor: signedTx.toCbor(),
83
+ txHash: signedTx.body().hash().toString(),
84
+ signatureCount: countSignatures(signedTx)
85
+ };
86
+ }
87
+ };
88
+
89
+ /**
90
+ * `submit-transaction` -- Submits a signed transaction to the blockchain.
91
+ */
92
+ export const submitTransaction = {
93
+ name: "submit-transaction",
94
+ description: "Submit a signed transaction to the blockchain.",
95
+ category: "wallet",
96
+ inputSchema: Type.Object({
97
+ txCbor: Type.String({
98
+ description: "Signed transaction CBOR hex string to submit"
99
+ })
100
+ }),
101
+ outputSchema: Type.Object({
102
+ txHash: Type.String({
103
+ description: "Submitted transaction hash"
104
+ }),
105
+ submitted: Type.Boolean()
106
+ }),
107
+ execute: async (input, context) => {
108
+ const blaze = await getBlazeFromContext(context);
109
+ const tx = parseTxCbor(input.txCbor);
110
+ let txHash;
111
+ try {
112
+ txHash = await blaze.submitTransaction(tx);
113
+ } catch (err) {
114
+ throw new ActionError(`Transaction submission failed: ${err instanceof Error ? err.message : String(err)}`, "SUBMISSION_ERROR", {
115
+ error: err instanceof Error ? err.message : String(err)
116
+ });
117
+ }
118
+ return {
119
+ txHash: txHash.toString(),
120
+ submitted: true
121
+ };
122
+ }
123
+ };
124
+
125
+ /**
126
+ * `sign-and-submit` -- Signs a transaction and immediately submits it.
127
+ * Optionally waits for on-chain confirmation with a configurable timeout.
128
+ */
129
+ export const signAndSubmit = {
130
+ name: "sign-and-submit",
131
+ description: "Sign and submit a transaction. Optionally wait for on-chain confirmation.",
132
+ category: "wallet",
133
+ inputSchema: Type.Object({
134
+ txCbor: Type.String({
135
+ description: "Transaction CBOR hex string to sign and submit"
136
+ }),
137
+ waitForConfirmation: Type.Optional(Type.Boolean({
138
+ default: false,
139
+ description: "Whether to wait for on-chain confirmation before returning"
140
+ })),
141
+ confirmationTimeout: Type.Optional(Type.Number({
142
+ default: 60,
143
+ description: "Timeout in seconds to wait for confirmation"
144
+ }))
145
+ }),
146
+ outputSchema: Type.Object({
147
+ txHash: Type.String({
148
+ description: "Transaction hash"
149
+ }),
150
+ submitted: Type.Boolean(),
151
+ confirmed: Type.Optional(Type.Boolean({
152
+ description: "Whether tx was confirmed on-chain"
153
+ })),
154
+ signedTxCbor: Type.String({
155
+ description: "Signed transaction CBOR hex"
156
+ })
157
+ }),
158
+ execute: async (input, context) => {
159
+ const blaze = await getBlazeFromContext(context);
160
+ if (!isHotWallet(blaze)) {
161
+ throw new ActionError("Cold wallets cannot sign transactions. Export the CBOR and sign externally, then use submit-transaction.", "COLD_WALLET");
162
+ }
163
+ const tx = parseTxCbor(input.txCbor);
164
+
165
+ // Sign
166
+ let signedTx;
167
+ try {
168
+ signedTx = await blaze.signTransaction(tx);
169
+ } catch (err) {
170
+ throw new ActionError(`Failed to sign transaction: ${err instanceof Error ? err.message : String(err)}`, "SIGN_ERROR", {
171
+ error: err instanceof Error ? err.message : String(err)
172
+ });
173
+ }
174
+ const signedTxCbor = signedTx.toCbor();
175
+
176
+ // Submit
177
+ let txHash;
178
+ try {
179
+ txHash = await blaze.submitTransaction(signedTx);
180
+ } catch (err) {
181
+ throw new ActionError(`Transaction submission failed: ${err instanceof Error ? err.message : String(err)}`, "SUBMISSION_ERROR", {
182
+ error: err instanceof Error ? err.message : String(err),
183
+ signedTxCbor
184
+ });
185
+ }
186
+ const txHashStr = txHash.toString();
187
+
188
+ // If confirmation not requested, return immediately
189
+ if (!input.waitForConfirmation) {
190
+ return {
191
+ txHash: txHashStr,
192
+ submitted: true,
193
+ signedTxCbor
194
+ };
195
+ }
196
+
197
+ // Poll for confirmation
198
+ const timeoutSeconds = input.confirmationTimeout ?? 60;
199
+ const deadline = Date.now() + timeoutSeconds * 1000;
200
+ const pollIntervalMs = 5000; // 5 seconds between polls
201
+
202
+ let confirmed = false;
203
+ while (Date.now() < deadline) {
204
+ await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
205
+ try {
206
+ // Query UTxOs and check if any reference this tx hash
207
+ // This is a lightweight confirmation check using the provider
208
+ const utxos = await blaze.provider.getUnspentOutputs(await blaze.wallet.getChangeAddress());
209
+ // If provider returns without error and tx hash appears in a UTxO input or
210
+ // the query succeeds after submission, assume confirmed.
211
+ // A more precise check would be to look up the tx directly.
212
+ // Use resolveUnspentOutputs if available, otherwise accept the provider response.
213
+ const anyMatch = utxos.some(u => u.input().transactionId().toString() === txHashStr);
214
+ // If we get a fresh UTxO referencing our tx, confirmed
215
+ if (anyMatch) {
216
+ confirmed = true;
217
+ break;
218
+ }
219
+ // Check if we got back any UTxOs at all - for a send transaction the
220
+ // change output should be visible once confirmed
221
+ // Fall through and keep polling
222
+ } catch {
223
+ // Provider error during poll -- keep trying until timeout
224
+ }
225
+ }
226
+ if (!confirmed && input.waitForConfirmation) {
227
+ // Return partial success rather than throwing -- tx was submitted successfully
228
+ // but we couldn't confirm within the timeout
229
+ return {
230
+ txHash: txHashStr,
231
+ submitted: true,
232
+ confirmed: false,
233
+ signedTxCbor
234
+ };
235
+ }
236
+ return {
237
+ txHash: txHashStr,
238
+ submitted: true,
239
+ confirmed,
240
+ signedTxCbor
241
+ };
242
+ }
243
+ };
244
+
245
+ /**
246
+ * `decode-transaction` -- Decodes a transaction CBOR hex without requiring a wallet.
247
+ * Extracts inputs, outputs, fee, witness count, and required signers from the tx body.
248
+ */
249
+ export const decodeTransaction = {
250
+ name: "decode-transaction",
251
+ description: "Decode a transaction CBOR hex and return its inputs, outputs, fee, and signers. Does not require a wallet.",
252
+ category: "transaction",
253
+ inputSchema: Type.Object({
254
+ txCbor: Type.String({
255
+ description: "Transaction CBOR hex string to decode"
256
+ })
257
+ }),
258
+ outputSchema: Type.Object({
259
+ txHash: Type.String({
260
+ description: "Transaction body hash"
261
+ }),
262
+ inputs: Type.Array(Type.Object({
263
+ txHash: Type.String({
264
+ description: "Input transaction hash"
265
+ }),
266
+ outputIndex: Type.Number({
267
+ description: "Input output index"
268
+ })
269
+ })),
270
+ outputs: Type.Array(Type.Object({
271
+ address: Type.String({
272
+ description: "Output address (bech32 or hex)"
273
+ }),
274
+ lovelace: Type.String({
275
+ description: "Output lovelace amount as string"
276
+ }),
277
+ tokens: Type.Array(TokenEntrySchema)
278
+ })),
279
+ fee: Type.String({
280
+ description: "Transaction fee in lovelace as string"
281
+ }),
282
+ signatureCount: Type.Number({
283
+ description: "Number of VKey witnesses"
284
+ }),
285
+ requiredSigners: Type.Array(Type.String({
286
+ description: "Required signer key hashes"
287
+ }))
288
+ }),
289
+ execute: async (input, _context) => {
290
+ // No Blaze instance needed -- pure CBOR parsing
291
+ const tx = parseTxCbor(input.txCbor);
292
+ const body = tx.body();
293
+ const txHash = body.hash().toString();
294
+
295
+ // Extract inputs
296
+ const inputSet = body.inputs();
297
+ const inputs = Array.from(inputSet.values()).map(txInput => ({
298
+ txHash: txInput.transactionId().toString(),
299
+ outputIndex: Number(txInput.index())
300
+ }));
301
+
302
+ // Extract outputs
303
+ const outputs = body.outputs().map(txOutput => {
304
+ const value = txOutput.amount();
305
+ const tokens = [];
306
+ const multiasset = value.multiasset();
307
+ if (multiasset) {
308
+ for (const [assetId, quantity] of multiasset.entries()) {
309
+ tokens.push({
310
+ policyId: Core.AssetId.getPolicyId(assetId),
311
+ assetName: Core.AssetId.getAssetName(assetId),
312
+ quantity: quantity.toString()
313
+ });
314
+ }
315
+ }
316
+
317
+ // Attempt bech32 address, fall back to hex if conversion fails
318
+ let address;
319
+ try {
320
+ address = txOutput.address().toBech32();
321
+ } catch {
322
+ address = txOutput.address().toBytes();
323
+ }
324
+ return {
325
+ address,
326
+ lovelace: value.coin().toString(),
327
+ tokens
328
+ };
329
+ });
330
+ return {
331
+ txHash,
332
+ inputs,
333
+ outputs,
334
+ fee: body.fee().toString(),
335
+ signatureCount: countSignatures(tx),
336
+ requiredSigners: getRequiredSigners(tx)
337
+ };
338
+ }
339
+ };
340
+ //# sourceMappingURL=transaction-actions.js.map