@kairoguard/sdk 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/backend.d.ts CHANGED
@@ -232,6 +232,16 @@ export interface PolicyDetailsResponse {
232
232
  policy?: Record<string, unknown>;
233
233
  error?: string;
234
234
  }
235
+ export interface ReaffirmPolicyBindingParams {
236
+ bindingObjectId: string;
237
+ registryObjectId?: string;
238
+ }
239
+ export interface ReaffirmPolicyBindingResponse {
240
+ success: boolean;
241
+ digest: string;
242
+ activeVersionObjectId?: string;
243
+ error?: string;
244
+ }
235
245
  export interface DWalletFullResponse {
236
246
  success: boolean;
237
247
  dWallet?: unknown;
@@ -273,6 +283,7 @@ export declare class BackendClient {
273
283
  getGovernance(governanceId: string): Promise<GovernanceGetResponse>;
274
284
  getGovernanceProposal(proposalId: string): Promise<GovernanceProposalGetResponse>;
275
285
  getPolicy(policyObjectId: string): Promise<PolicyDetailsResponse>;
286
+ reaffirmPolicyBinding(params: ReaffirmPolicyBindingParams): Promise<ReaffirmPolicyBindingResponse>;
276
287
  getDWalletFull(dWalletId: string): Promise<DWalletFullResponse>;
277
288
  getSuiObject(objectId: string): Promise<SuiObjectResponse>;
278
289
  policySign(params: Record<string, unknown>): Promise<Record<string, unknown>>;
package/dist/backend.js CHANGED
@@ -89,6 +89,9 @@ export class BackendClient {
89
89
  async getPolicy(policyObjectId) {
90
90
  return this.request("GET", `/api/policies/${policyObjectId}`);
91
91
  }
92
+ async reaffirmPolicyBinding(params) {
93
+ return this.request("POST", "/api/policy/binding/reaffirm", params);
94
+ }
92
95
  async getDWalletFull(dWalletId) {
93
96
  return this.request("GET", `/api/dwallet/full/${dWalletId}`);
94
97
  }
package/dist/cli.js CHANGED
@@ -2,9 +2,12 @@
2
2
  import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { homedir } from "node:os";
5
+ import { createInterface } from "node:readline/promises";
6
+ import { stdin as input, stdout as output } from "node:process";
5
7
  import { verifyAuditBundle } from "./auditBundle.js";
6
8
  import { BackendClient, DEFAULT_BACKEND_URL } from "./backend.js";
7
9
  import { KairoClient } from "./client.js";
10
+ import { POLICY_TEMPLATE_PRESETS, buildPolicyTemplatePayload } from "./policy-templates.js";
8
11
  import { SKILL_MD, API_REFERENCE_MD, SDK_REFERENCE_MD } from "./skill-templates.js";
9
12
  const CONFIG_DIR = join(homedir(), ".kairo");
10
13
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
@@ -39,6 +42,9 @@ function flag(args, name) {
39
42
  return undefined;
40
43
  return args[idx + 1];
41
44
  }
45
+ function hasFlag(args, name) {
46
+ return args.includes(name);
47
+ }
42
48
  function requireFlag(args, name, label) {
43
49
  const v = flag(args, name);
44
50
  if (!v) {
@@ -47,6 +53,24 @@ function requireFlag(args, name, label) {
47
53
  }
48
54
  return v;
49
55
  }
56
+ async function promptPolicyTemplateId() {
57
+ const rl = createInterface({ input, output });
58
+ try {
59
+ console.log("Choose a default policy template:");
60
+ console.log(` 1) ${POLICY_TEMPLATE_PRESETS["tpl-1"].label}`);
61
+ console.log(` 2) ${POLICY_TEMPLATE_PRESETS["tpl-2"].label}`);
62
+ console.log(` 3) ${POLICY_TEMPLATE_PRESETS["tpl-3"].label}`);
63
+ const answer = (await rl.question("Template [1/2/3] (default 2): ")).trim();
64
+ if (answer === "1")
65
+ return "tpl-1";
66
+ if (answer === "3")
67
+ return "tpl-3";
68
+ return "tpl-2";
69
+ }
70
+ finally {
71
+ rl.close();
72
+ }
73
+ }
50
74
  // ── Commands ────────────────────────────────────────────────────────────────
51
75
  async function cmdInit(args) {
52
76
  const apiKey = args[0];
@@ -88,17 +112,41 @@ async function cmdWalletCreate(args) {
88
112
  }
89
113
  const policyId = flag(args, "--policy-id");
90
114
  const stableId = flag(args, "--stable-id");
115
+ const autoProvision = hasFlag(args, "--auto-provision");
91
116
  const cfg = requireConfig();
117
+ let resolvedPolicyId = policyId;
118
+ if (autoProvision && !resolvedPolicyId) {
119
+ const templateId = await promptPolicyTemplateId();
120
+ const templatePayload = buildPolicyTemplatePayload(templateId);
121
+ const stable = stableId ?? `agent-policy-${Date.now()}`;
122
+ const client = getClient();
123
+ const created = await client.createPolicyV4({
124
+ stableId: stable,
125
+ version: "1.0.0",
126
+ allowNamespaces: templatePayload.allowNamespaces,
127
+ rules: templatePayload.rules,
128
+ });
129
+ if (!created.success || !created.policyObjectId?.startsWith("0x")) {
130
+ throw new Error(created.error ?? "Failed to create default policy from template");
131
+ }
132
+ await client.registerPolicyVersionFromPolicy({ policyObjectId: created.policyObjectId });
133
+ resolvedPolicyId = created.policyObjectId;
134
+ console.log(`Created + registered policy ${resolvedPolicyId} using ${templatePayload.template.id}.`);
135
+ }
92
136
  const kairo = new KairoClient({
93
137
  apiKey: cfg.apiKey,
94
138
  backendUrl: cfg.backendUrl,
95
139
  });
96
140
  const wallet = await kairo.createWallet({
97
141
  curve: curveRaw,
98
- policyObjectId: policyId,
142
+ policyObjectId: resolvedPolicyId,
99
143
  stableId,
100
144
  });
101
145
  console.log(JSON.stringify(wallet, null, 2));
146
+ if (!resolvedPolicyId) {
147
+ console.log("Wallet created but not provisioned. To show it in dashboard policies, run: " +
148
+ `kairo vault-provision --wallet-id ${wallet.walletId} --policy-id <policyObjectId> [--stable-id <id>]`);
149
+ }
102
150
  }
103
151
  async function cmdRegister(args) {
104
152
  const label = requireFlag(args, "--label", "name");
@@ -141,9 +189,13 @@ async function cmdVaultStatus(args) {
141
189
  async function cmdVaultProvision(args) {
142
190
  const walletId = requireFlag(args, "--wallet-id", "dwalletId");
143
191
  const policyId = requireFlag(args, "--policy-id", "objectId");
144
- const stableId = requireFlag(args, "--stable-id", "id");
145
- const client = getClient();
146
- const res = await client.provision({ dwalletObjectId: walletId, policyObjectId: policyId, stableId });
192
+ const stableId = flag(args, "--stable-id");
193
+ const cfg = requireConfig();
194
+ const kairo = new KairoClient({
195
+ apiKey: cfg.apiKey,
196
+ backendUrl: cfg.backendUrl,
197
+ });
198
+ const res = await kairo.provision(walletId, policyId, stableId);
147
199
  console.log(JSON.stringify(res, null, 2));
148
200
  }
149
201
  async function cmdReceiptMint(args) {
@@ -204,14 +256,14 @@ Setup:
204
256
 
205
257
  Wallet & Policy:
206
258
  health Server health check
207
- wallet-create [--curve secp256k1|ed25519] [--policy-id <id>] [--stable-id <id>]
259
+ wallet-create [--curve secp256k1|ed25519] [--policy-id <id>] [--stable-id <id>] [--auto-provision]
208
260
  Create a new dWallet via SDK DKG flow
209
261
  register --label <name> Register new API key
210
262
  policy-create --stable-id <id> --allow <addrs> Create policy
211
263
  policy-register --policy-id <id> Register policy version
212
264
  policy-details --policy-id <id> Get policy details
213
265
  vault-status --wallet-id <id> Check vault registration
214
- vault-provision --wallet-id <id> --policy-id <id> --stable-id <id>
266
+ vault-provision --wallet-id <id> --policy-id <id> [--stable-id <id>]
215
267
  receipt-mint --policy-id <id> --binding-id <id> --destination <hex> --intent-hash <hex>
216
268
 
217
269
  Utility:
package/dist/client.d.ts CHANGED
@@ -153,6 +153,18 @@ export declare class KairoClient {
153
153
  listWallets(): WalletInfo[];
154
154
  /** Get a wallet from local key store by ID. */
155
155
  getWallet(walletId: string): WalletInfo | null;
156
+ /**
157
+ * Provision an existing local wallet into the policy vault.
158
+ * Persists binding/policy metadata to local keystore.
159
+ */
160
+ provision(walletId: string, policyObjectId: string, stableId?: string): Promise<{
161
+ bindingObjectId: string;
162
+ digest: string;
163
+ }>;
164
+ reaffirmBinding(walletId: string): Promise<{
165
+ digest: string;
166
+ activeVersionObjectId?: string;
167
+ }>;
156
168
  /**
157
169
  * Governance-first policy update: creates a new policy + version, then proposes
158
170
  * a change for approvers. This method does NOT execute/reaffirm directly.
@@ -182,6 +194,9 @@ export declare class KairoClient {
182
194
  private pollPresignStatus;
183
195
  private pollSignStatus;
184
196
  private mintPolicyReceipt;
197
+ private resolvePolicyVersion;
198
+ private isReaffirmRequiredError;
199
+ private requestSignWithReaffirmRetry;
185
200
  private computeUserSignMessageWithExtensionFallback;
186
201
  private rebuildSigningMaterialFromChain;
187
202
  private resolveEvmRpcUrl;
package/dist/client.js CHANGED
@@ -173,13 +173,8 @@ export class KairoClient {
173
173
  // 10. Provision into vault (binding + registration) if policy is provided
174
174
  let bindingObjectId;
175
175
  if (opts?.policyObjectId) {
176
- const provisionResult = await this.backend.provision({
177
- dwalletObjectId: walletId,
178
- policyObjectId: opts.policyObjectId,
179
- stableId: opts.stableId ?? `agent-wallet-${walletId.slice(0, 8)}`,
180
- });
181
- bindingObjectId = provisionResult.bindingObjectId ?? undefined;
182
- this.store.save({ ...record, bindingObjectId });
176
+ const provisionResult = await this.provision(walletId, opts.policyObjectId, opts.stableId);
177
+ bindingObjectId = provisionResult.bindingObjectId;
183
178
  }
184
179
  return {
185
180
  walletId,
@@ -212,6 +207,44 @@ export class KairoClient {
212
207
  createdAt: r.createdAt,
213
208
  };
214
209
  }
210
+ /**
211
+ * Provision an existing local wallet into the policy vault.
212
+ * Persists binding/policy metadata to local keystore.
213
+ */
214
+ async provision(walletId, policyObjectId, stableId) {
215
+ const wallet = this.requireWalletRecord(walletId);
216
+ if (!policyObjectId?.startsWith("0x")) {
217
+ throw new Error("policyObjectId must be a valid 0x object id");
218
+ }
219
+ const result = await this.backend.provision({
220
+ dwalletObjectId: walletId,
221
+ policyObjectId,
222
+ stableId: stableId ?? `agent-wallet-${walletId.slice(0, 8)}`,
223
+ });
224
+ const bindingObjectId = String(result.bindingObjectId ?? "");
225
+ if (!bindingObjectId.startsWith("0x")) {
226
+ throw new Error("Provision succeeded but no bindingObjectId was returned");
227
+ }
228
+ this.store.save({
229
+ ...wallet,
230
+ bindingObjectId,
231
+ policyObjectId,
232
+ });
233
+ return { bindingObjectId, digest: result.digest };
234
+ }
235
+ async reaffirmBinding(walletId) {
236
+ const wallet = this.requireWalletRecord(walletId);
237
+ if (!wallet.bindingObjectId?.startsWith("0x")) {
238
+ throw new Error("Wallet is missing bindingObjectId. Provision the wallet before reaffirming.");
239
+ }
240
+ const result = await this.backend.reaffirmPolicyBinding({
241
+ bindingObjectId: wallet.bindingObjectId,
242
+ });
243
+ return {
244
+ digest: result.digest,
245
+ activeVersionObjectId: result.activeVersionObjectId,
246
+ };
247
+ }
215
248
  /**
216
249
  * Governance-first policy update: creates a new policy + version, then proposes
217
250
  * a change for approvers. This method does NOT execute/reaffirm directly.
@@ -403,7 +436,8 @@ export class KairoClient {
403
436
  if (!dWalletCapId) {
404
437
  throw new Error("Wallet record is missing dWalletCapId. Recreate/provision this wallet before signing.");
405
438
  }
406
- const req = await this.backend.requestSign({
439
+ const resolvedPolicyVersion = await this.resolvePolicyVersion(wallet, opts?.policyVersion);
440
+ const req = await this.requestSignWithReaffirmRetry(wallet, {
407
441
  dWalletId: wallet.walletId,
408
442
  dWalletCapId,
409
443
  encryptedUserSecretKeyShareId: wallet.encryptedUserSecretKeyShareId ?? "",
@@ -414,7 +448,7 @@ export class KairoClient {
414
448
  policyReceiptId,
415
449
  policyBindingObjectId: wallet.bindingObjectId,
416
450
  policyObjectId: wallet.policyObjectId,
417
- policyVersion: opts?.policyVersion ?? "1.0.0",
451
+ policyVersion: resolvedPolicyVersion,
418
452
  ethTx: opts?.ethTx,
419
453
  });
420
454
  if (!req.success) {
@@ -627,7 +661,7 @@ export class KairoClient {
627
661
  if (!wallet.policyObjectId || !wallet.bindingObjectId) {
628
662
  throw new Error("Wallet is missing policy binding metadata. Ensure it is provisioned with policyObjectId and bindingObjectId.");
629
663
  }
630
- const response = await this.backend.mintReceipt({
664
+ const mintOnce = () => this.backend.mintReceipt({
631
665
  policyObjectId: wallet.policyObjectId,
632
666
  bindingObjectId: wallet.bindingObjectId,
633
667
  namespace: ctx.namespace,
@@ -638,6 +672,12 @@ export class KairoClient {
638
672
  nativeValueHex: ctx.nativeValue.toString(16).padStart(64, "0"),
639
673
  contextDataHex: ctx.contextDataHex ? stripHexPrefix(ctx.contextDataHex) : undefined,
640
674
  });
675
+ let response = await mintOnce();
676
+ const initialError = String(response?.error ?? "");
677
+ if (response.success === false && this.isReaffirmRequiredError(initialError)) {
678
+ await this.reaffirmBinding(wallet.walletId);
679
+ response = await mintOnce();
680
+ }
641
681
  if (response.success === false) {
642
682
  throw new Error(String(response.error ?? "Failed to mint policy receipt"));
643
683
  }
@@ -650,6 +690,38 @@ export class KairoClient {
650
690
  }
651
691
  return receiptId;
652
692
  }
693
+ async resolvePolicyVersion(wallet, override) {
694
+ const explicit = String(override ?? "").trim();
695
+ if (explicit)
696
+ return explicit;
697
+ if (!wallet.policyObjectId?.startsWith("0x")) {
698
+ throw new Error("Wallet is missing policyObjectId. Provision the wallet before signing.");
699
+ }
700
+ const response = await this.backend.getPolicy(wallet.policyObjectId);
701
+ if (!response.success || !response.policy) {
702
+ throw new Error(response.error ?? "Failed to resolve policy details for signing");
703
+ }
704
+ const version = decodeMoveString(response.policy.version).trim();
705
+ if (!version) {
706
+ throw new Error("Policy version is missing on-chain. Pass policyVersion explicitly.");
707
+ }
708
+ return version;
709
+ }
710
+ isReaffirmRequiredError(err) {
711
+ const message = err instanceof Error ? err.message : String(err);
712
+ return /requires confirmation/i.test(message) || /reaffirm/i.test(message);
713
+ }
714
+ async requestSignWithReaffirmRetry(wallet, payload) {
715
+ try {
716
+ return await this.backend.requestSign(payload);
717
+ }
718
+ catch (error) {
719
+ if (!this.isReaffirmRequiredError(error))
720
+ throw error;
721
+ await this.reaffirmBinding(wallet.walletId);
722
+ return this.backend.requestSign(payload);
723
+ }
724
+ }
653
725
  async computeUserSignMessageWithExtensionFallback(wallet, protocolParams, presignBytes, messageBytes) {
654
726
  try {
655
727
  return await createUserSignMessageWithPublicOutput(protocolParams, new Uint8Array(wallet.userPublicOutput), new Uint8Array(wallet.userSecretKeyShare), presignBytes, messageBytes, Hash.KECCAK256, SignatureAlgorithm.ECDSASecp256k1, Curve.SECP256K1);
@@ -733,6 +805,26 @@ function toBigInt(value) {
733
805
  return BigInt(value);
734
806
  return value.startsWith("0x") ? BigInt(value) : BigInt(value);
735
807
  }
808
+ function decodeMoveString(value) {
809
+ if (typeof value === "string")
810
+ return value;
811
+ if (Array.isArray(value) && value.every((x) => Number.isInteger(x) && x >= 0 && x <= 255)) {
812
+ try {
813
+ return new TextDecoder().decode(Uint8Array.from(value));
814
+ }
815
+ catch {
816
+ return "";
817
+ }
818
+ }
819
+ if (value && typeof value === "object") {
820
+ const obj = value;
821
+ return (decodeMoveString(obj.bytes) ||
822
+ decodeMoveString(obj.data) ||
823
+ decodeMoveString(obj.value) ||
824
+ decodeMoveString(obj.fields));
825
+ }
826
+ return "";
827
+ }
736
828
  function buildNormalizedEncryptedShare(fields) {
737
829
  const { state } = normalizeMoveEnumState(fields.state);
738
830
  const candidate = {
@@ -0,0 +1,16 @@
1
+ export interface PolicyTemplatePreset {
2
+ id: "tpl-1" | "tpl-2" | "tpl-3";
3
+ label: string;
4
+ singleTxUsd: number;
5
+ dailyLimitUsd: number;
6
+ }
7
+ export declare const POLICY_TEMPLATE_PRESETS: Record<PolicyTemplatePreset["id"], PolicyTemplatePreset>;
8
+ export declare function buildPolicyTemplatePayload(templateId: string): {
9
+ template: PolicyTemplatePreset;
10
+ allowNamespaces: number[];
11
+ rules: Array<{
12
+ ruleType: number;
13
+ namespace?: number;
14
+ params: string;
15
+ }>;
16
+ };
@@ -0,0 +1,56 @@
1
+ export const POLICY_TEMPLATE_PRESETS = {
2
+ "tpl-1": {
3
+ id: "tpl-1",
4
+ label: "Conservative ($200/tx, $1k/day)",
5
+ singleTxUsd: 200,
6
+ dailyLimitUsd: 1_000,
7
+ },
8
+ "tpl-2": {
9
+ id: "tpl-2",
10
+ label: "Standard ($2k/tx, $10k/day)",
11
+ singleTxUsd: 2_000,
12
+ dailyLimitUsd: 10_000,
13
+ },
14
+ "tpl-3": {
15
+ id: "tpl-3",
16
+ label: "High-limit ($10k/tx, $50k/day)",
17
+ singleTxUsd: 10_000,
18
+ dailyLimitUsd: 50_000,
19
+ },
20
+ };
21
+ function stripHexPrefix(value) {
22
+ return value.startsWith("0x") ? value.slice(2) : value;
23
+ }
24
+ function encodeU256(value) {
25
+ const big = typeof value === "bigint" ? value : BigInt(value);
26
+ return `0x${big.toString(16).padStart(64, "0")}`;
27
+ }
28
+ function encodeMaxNativeRule(maxNativeBaseUnits) {
29
+ return encodeU256(maxNativeBaseUnits);
30
+ }
31
+ function encodeDailyPeriodLimitRule(maxDailyBaseUnits) {
32
+ const periodTypeDaily = 1;
33
+ return `0x${periodTypeDaily.toString(16).padStart(2, "0")}${stripHexPrefix(encodeU256(maxDailyBaseUnits))}`;
34
+ }
35
+ export function buildPolicyTemplatePayload(templateId) {
36
+ const template = POLICY_TEMPLATE_PRESETS[templateId] ?? POLICY_TEMPLATE_PRESETS["tpl-2"];
37
+ const maxNativeBaseUnits = BigInt(Math.round(template.singleTxUsd * 1_000_000));
38
+ const dailyLimitBaseUnits = BigInt(Math.round(template.dailyLimitUsd * 1_000_000));
39
+ return {
40
+ template,
41
+ // Default to EVM namespace so newly onboarded users can sign EVM txs immediately.
42
+ allowNamespaces: [1],
43
+ rules: [
44
+ {
45
+ ruleType: 1,
46
+ namespace: 0,
47
+ params: encodeMaxNativeRule(maxNativeBaseUnits),
48
+ },
49
+ {
50
+ ruleType: 10,
51
+ namespace: 0,
52
+ params: encodeDailyPeriodLimitRule(dailyLimitBaseUnits),
53
+ },
54
+ ],
55
+ };
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kairoguard/sdk",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",