@candledottv/agent-sdk 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.
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/authorization-signature.d.ts +87 -0
- package/dist/authorization-signature.d.ts.map +1 -0
- package/dist/client.d.ts +1192 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/errors.d.ts +58 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/evm-tx.d.ts +100 -0
- package/dist/evm-tx.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1186 -0
- package/dist/internal/encoding.d.ts +19 -0
- package/dist/internal/encoding.d.ts.map +1 -0
- package/dist/keychain-secret-store.d.ts +51 -0
- package/dist/keychain-secret-store.d.ts.map +1 -0
- package/dist/secret-store.d.ts +82 -0
- package/dist/secret-store.d.ts.map +1 -0
- package/dist/wallet-import.d.ts +93 -0
- package/dist/wallet-import.d.ts.map +1 -0
- package/dist/webhooks.d.ts +20 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared base64/ArrayBuffer/Uint8Array conversion helpers for this SDK's crypto modules
|
|
3
|
+
* (wallet-import.ts, authorization-signature.ts, secret-store.ts). Internal only -- not
|
|
4
|
+
* re-exported from index.ts -- since these are plumbing for WebCrypto/HPKE call sites, not part
|
|
5
|
+
* of the SDK's public surface.
|
|
6
|
+
*
|
|
7
|
+
* These used to be copied near-identically in all three call sites. Consolidated here with
|
|
8
|
+
* byte-identical behavior to the originals: same `Buffer.from`/`.slice` calls, just given one
|
|
9
|
+
* home. Each call site keeps using whichever pair of functions matches the value shape it already
|
|
10
|
+
* works with (ArrayBuffer for WebCrypto/HPKE APIs, Uint8Array for everything else).
|
|
11
|
+
*/
|
|
12
|
+
/** A `Uint8Array` sliced down to exactly its own bytes, safe to hand to WebCrypto/HPKE as an `ArrayBuffer`. */
|
|
13
|
+
export declare function toArrayBuffer(view: Uint8Array): ArrayBuffer;
|
|
14
|
+
export declare function arrayBufferToBase64(data: ArrayBuffer): string;
|
|
15
|
+
/** Decodes a base64 string to an `ArrayBuffer` containing exactly its bytes (no pooled-buffer slack). */
|
|
16
|
+
export declare function base64ToArrayBuffer(base64: string): ArrayBuffer;
|
|
17
|
+
export declare function toBase64(bytes: Uint8Array): string;
|
|
18
|
+
export declare function fromBase64(base64: string): Uint8Array;
|
|
19
|
+
//# sourceMappingURL=encoding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.d.ts","sourceRoot":"","sources":["../../src/internal/encoding.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,+GAA+G;AAC/G,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,CAE3D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAE7D;AAED,yGAAyG;AACzG,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAG/D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAElD;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAErD"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* keychain-secret-store: the OS-keychain `SecretStore` secret-store.ts's module doc deferred to
|
|
3
|
+
* "Phase 4", closing the loop with the Candle CLI's wallet import.
|
|
4
|
+
*
|
|
5
|
+
* `candle wallets import` stores each imported wallet's signer private key in the OS keychain:
|
|
6
|
+
* service `tv.candle.cli`, account `wallet_signer_<linkedWalletId>`, value = the PEM's base64
|
|
7
|
+
* body on a single line (the CLI's `pemToStoredSigner` form; a raw PEM contains newlines, which
|
|
8
|
+
* the CLI's macOS write path refuses by design). This store reads that exact contract, so an SDK
|
|
9
|
+
* agent on the same machine picks up CLI-imported wallets with zero manual key handling:
|
|
10
|
+
*
|
|
11
|
+
* const store = KeychainSecretStore.detect()
|
|
12
|
+
* const candle = new CandleClient({ apiKey, privyAppId, secretStore: store ?? undefined })
|
|
13
|
+
* await candle.trade({ ..., from: { linkedWalletId, privyWalletId } })
|
|
14
|
+
*
|
|
15
|
+
* `get()` re-armors the stored single-line value into standard PEM, which is what the signing
|
|
16
|
+
* path (`buildPrivyAuthorizationSignature`) consumes. `set()` accepts a PEM and stores the
|
|
17
|
+
* single-line form, so writes from either side stay format-compatible. Node-only (spawns
|
|
18
|
+
* `security` on macOS, `secret-tool` on Linux), like `EncryptedFileSecretStore`; browser and
|
|
19
|
+
* ephemeral runs keep using `InMemorySecretStore`.
|
|
20
|
+
*
|
|
21
|
+
* Secrets travel exclusively via the child's STDIN or its stdout, never argv, mirroring the
|
|
22
|
+
* CLI's own stores: macOS `security` only takes secrets as a `-w` argv flag in its normal form,
|
|
23
|
+
* so writes go through `security -i` (command-on-stdin mode) instead. The stored value is
|
|
24
|
+
* base64 by construction, so embedding it in the quoted `-w "<value>"` token cannot break out
|
|
25
|
+
* of the quoting; `assertStorable` makes that invariant checked rather than assumed.
|
|
26
|
+
*/
|
|
27
|
+
import type { SecretStore } from "./secret-store";
|
|
28
|
+
export interface ExecResult {
|
|
29
|
+
status: number;
|
|
30
|
+
stdout: string;
|
|
31
|
+
}
|
|
32
|
+
export type ExecFn = (binary: string, args: string[], stdin?: string) => Promise<ExecResult>;
|
|
33
|
+
export declare class KeychainSecretStore implements SecretStore {
|
|
34
|
+
private readonly backend;
|
|
35
|
+
private readonly exec;
|
|
36
|
+
constructor(opts?: {
|
|
37
|
+
backend?: "security" | "secret-tool";
|
|
38
|
+
exec?: ExecFn;
|
|
39
|
+
});
|
|
40
|
+
/**
|
|
41
|
+
* The store for this machine, or `null` when no OS keychain tool is on `PATH` (fall back to
|
|
42
|
+
* `EncryptedFileSecretStore` or `InMemorySecretStore` then). Presence-only: a resolvable
|
|
43
|
+
* binary whose daemon is down surfaces later as `get() === null`, which every caller already
|
|
44
|
+
* handles as "no signer stored".
|
|
45
|
+
*/
|
|
46
|
+
static detect(): KeychainSecretStore | null;
|
|
47
|
+
get(walletRef: string): Promise<string | null>;
|
|
48
|
+
set(walletRef: string, privateKeyPem: string): Promise<void>;
|
|
49
|
+
delete(walletRef: string): Promise<void>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=keychain-secret-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain-secret-store.d.ts","sourceRoot":"","sources":["../src/keychain-secret-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AA4BjD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;AAiC5F,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAQ;gBAEjB,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO;IAK9E;;;;;OAKG;IACH,MAAM,CAAC,MAAM,IAAI,mBAAmB,GAAG,IAAI;IAMrC,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAa9C,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB5D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAS/C"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* secret-store: pluggable storage for an agent's P-256 signer private key (Agent Pilot Phase 3).
|
|
3
|
+
*
|
|
4
|
+
* Phase 3 lets a headless agent sign for its own linked wallets by holding the P-256 signer key
|
|
5
|
+
* (`generateSignerKeypair`'s `privateKeyPem`, wallet-import.ts) client-side, not on Candle's
|
|
6
|
+
* servers. Candle never sees this key; the SDK is where it has to live instead, so this module
|
|
7
|
+
* defines where and how. A later task (one-shot trade / selfLaunch) loads the key from a
|
|
8
|
+
* `SecretStore` by `walletRef` to compute the signature at call time.
|
|
9
|
+
*
|
|
10
|
+
* Two implementations ship here:
|
|
11
|
+
* - `InMemorySecretStore`: a plain `Map`, universal (browser or Node), for tests and ephemeral
|
|
12
|
+
* agent runs that re-import or re-generate the key each process start.
|
|
13
|
+
* - `EncryptedFileSecretStore`: AES-GCM-at-rest via WebCrypto, keyed from a passphrase through
|
|
14
|
+
* PBKDF2, Node-only (it touches the filesystem via `node:fs/promises`).
|
|
15
|
+
*
|
|
16
|
+
* An OS-keychain-backed store (macOS Keychain / libsecret / Windows Credential Manager) is
|
|
17
|
+
* deliberately NOT included here -- it is Node/CLI-specific and belongs to a later phase (Phase
|
|
18
|
+
* 4). Implement `SecretStore` for it then.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Storage for an agent's signer private-key PEM, keyed by `walletRef` (the caller's stable
|
|
22
|
+
* handle for a linked wallet: its `linkedWalletId` or its address). Implementations decide the
|
|
23
|
+
* medium (memory, an encrypted file, later an OS keychain) but must agree on this shape.
|
|
24
|
+
*
|
|
25
|
+
* Deliberately excluded from this module: an OS-keychain implementation. That store is
|
|
26
|
+
* Node/CLI-specific (macOS Keychain, libsecret, Windows Credential Manager) and is planned for
|
|
27
|
+
* Phase 4, not Phase 3 -- implement this interface against it there rather than here.
|
|
28
|
+
*/
|
|
29
|
+
export interface SecretStore {
|
|
30
|
+
/** Returns the stored PEM for `walletRef`, or `null` if nothing is stored under that ref. */
|
|
31
|
+
get(walletRef: string): Promise<string | null>;
|
|
32
|
+
/** Stores `privateKeyPem` under `walletRef`, replacing any previous value for that ref. */
|
|
33
|
+
set(walletRef: string, privateKeyPem: string): Promise<void>;
|
|
34
|
+
/** Removes any stored value for `walletRef`. A no-op if nothing was stored under it. */
|
|
35
|
+
delete(walletRef: string): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* A plain in-memory `SecretStore`. Works identically in a browser or Node (no filesystem, no
|
|
39
|
+
* platform APIs), which makes it the right default for tests and for ephemeral agent runs that
|
|
40
|
+
* are fine re-deriving or re-importing the key on every process start. Nothing here survives
|
|
41
|
+
* process exit.
|
|
42
|
+
*/
|
|
43
|
+
export declare class InMemorySecretStore implements SecretStore {
|
|
44
|
+
private readonly entries;
|
|
45
|
+
get(walletRef: string): Promise<string | null>;
|
|
46
|
+
set(walletRef: string, privateKeyPem: string): Promise<void>;
|
|
47
|
+
delete(walletRef: string): Promise<void>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A `SecretStore` backed by a single encrypted JSON file, keyed from a passphrase. Node-only (it
|
|
51
|
+
* reads and writes the filesystem via `node:fs/promises`, imported lazily inside each method so
|
|
52
|
+
* that importing `InMemorySecretStore` from this module never drags a `node:fs` dependency into
|
|
53
|
+
* a browser bundle).
|
|
54
|
+
*
|
|
55
|
+
* Crypto construction (WebCrypto, matching wallet-import.ts's use of `crypto.subtle`):
|
|
56
|
+
* - Key derivation: PBKDF2-HMAC-SHA256, {@link PBKDF2_ITERATIONS} iterations at the time of
|
|
57
|
+
* writing, a random {@link SALT_LENGTH_BYTES}-byte salt generated fresh for every `set` call.
|
|
58
|
+
* Both the salt and the iteration count actually used are stored alongside that entry's
|
|
59
|
+
* ciphertext (not read back from the module constant), so a correct passphrase can always
|
|
60
|
+
* re-derive the key even after {@link PBKDF2_ITERATIONS} is raised for new entries later.
|
|
61
|
+
* - Encryption: AES-GCM-256, a random {@link IV_LENGTH_BYTES}-byte IV generated fresh for every
|
|
62
|
+
* `set` call (never reused across entries or across overwrites of the same entry).
|
|
63
|
+
* - At rest: one JSON object per file, one salt/iv/iterations/ciphertext record per `walletRef`.
|
|
64
|
+
* The ciphertext is AES-GCM's own output (ciphertext + auth tag), so a wrong passphrase
|
|
65
|
+
* derives the wrong key and `crypto.subtle.decrypt` throws on the auth-tag check -- it can
|
|
66
|
+
* never silently return garbage plaintext instead of the real PEM.
|
|
67
|
+
*
|
|
68
|
+
* Not safe for concurrent writers against the same file: `set` and `delete` both do a
|
|
69
|
+
* read-modify-write of the whole file, so two concurrent calls (same process or different
|
|
70
|
+
* processes) against the same `path` can race and lose one write.
|
|
71
|
+
*/
|
|
72
|
+
export declare class EncryptedFileSecretStore implements SecretStore {
|
|
73
|
+
private readonly path;
|
|
74
|
+
private readonly passphrase;
|
|
75
|
+
constructor(path: string, passphrase: string);
|
|
76
|
+
get(walletRef: string): Promise<string | null>;
|
|
77
|
+
set(walletRef: string, privateKeyPem: string): Promise<void>;
|
|
78
|
+
delete(walletRef: string): Promise<void>;
|
|
79
|
+
private readFile;
|
|
80
|
+
private writeFile;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=secret-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-store.d.ts","sourceRoot":"","sources":["../src/secret-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC1B,6FAA6F;IAC7F,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;IAC9C,2FAA2F;IAC3F,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC5D,wFAAwF;IACxF,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IAE9C,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9C,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C;AA6CD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,wBAAyB,YAAW,WAAW;IAExD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM;IAG/B,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAoB9C,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB5D,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAOhC,QAAQ;YAWR,SAAS;CAMxB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side HPKE for Candle's ciphertext-only wallet import (PR3, Task 8).
|
|
3
|
+
*
|
|
4
|
+
* `POST /api/v1/agent/wallets/import/init` proxies Privy's HPKE receiver public key so an agent
|
|
5
|
+
* can encrypt a wallet's private key locally; `POST /api/v1/agent/wallets/import/submit` accepts
|
|
6
|
+
* only the resulting ciphertext. Candle -- and the network in between -- never sees plaintext.
|
|
7
|
+
* `CandleClient.importWallet` (client.ts) drives both requests and is the only place in this SDK
|
|
8
|
+
* that ever holds the plaintext key; this module supplies the pure crypto it calls.
|
|
9
|
+
*
|
|
10
|
+
* HPKE parameters match Privy's import contract exactly (RFC 9180, mode Base):
|
|
11
|
+
* - KEM: DHKEM(P-256, HKDF-SHA256) -- `DhkemP256HkdfSha256`
|
|
12
|
+
* - KDF: HKDF-SHA256 -- `HkdfSha256`
|
|
13
|
+
* - AEAD: ChaCha20-Poly1305 -- `Chacha20Poly1305`
|
|
14
|
+
* All three wire values -- the receiver's `encryptionPublicKey` (input) and the `ciphertext` /
|
|
15
|
+
* `encapsulatedKey` this module produces (output) -- are base64-encoded raw KEM bytes (an
|
|
16
|
+
* uncompressed SEC1 point for the P-256 public key), never DER. DER only shows up below for the
|
|
17
|
+
* UNRELATED signer keypair, which is a separate WebCrypto ECDSA key, not part of the HPKE
|
|
18
|
+
* exchange.
|
|
19
|
+
*
|
|
20
|
+
* `@hpke/core`, `@hpke/chacha20poly1305`, and `@scure/base` are this SDK's first three runtime
|
|
21
|
+
* dependencies (previously zero -- see client.ts's module doc). All three already resolve in
|
|
22
|
+
* this monorepo as transitive hoists of apps/api's `@privy-io/server-auth`, but a phantom hoist
|
|
23
|
+
* is invisible to a package's own manifest: publishing the SDK without declaring them would
|
|
24
|
+
* break any consumer that doesn't happen to also depend on `@privy-io/server-auth`.
|
|
25
|
+
*
|
|
26
|
+
* CRITICAL: what gets HPKE-sealed is not the `privateKey` string's own UTF-8 bytes -- it is that
|
|
27
|
+
* string DECODED to the wallet's raw private-key bytes first, exactly like Privy's own reference
|
|
28
|
+
* import flow (node_modules/@privy-io/server-auth/dist/cjs/wallet-api/import.js,
|
|
29
|
+
* `handleWalletImport`): "evm" keys are hex (an optional leading "0x" is stripped, then
|
|
30
|
+
* hex-decoded), "solana" keys are base58 (decoded with `@scure/base`'s `base58.decode`, the same
|
|
31
|
+
* codec Privy's own SDK uses). Sealing the string's UTF-8 text instead -- what an earlier version
|
|
32
|
+
* of this module did -- produces a ciphertext that either fails to decrypt correctly on Privy's
|
|
33
|
+
* side or, worse, silently decrypts to the WRONG bytes and provisions a garbage-keyed wallet.
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* Chain for the linked-wallets import flow (PR3). "evm" covers every EVM chain (Privy's own
|
|
37
|
+
* `chain_type: "ethereum"`), not just Ethereum mainnet; "solana" is Solana. Deliberately NOT the
|
|
38
|
+
* launch-surface `Chain` in client.ts ("solana" | "hood"): import targets Privy's wallet chain
|
|
39
|
+
* types, which distinguish "solana" from EVM rather than from Candle's own Hood/base-mainnet
|
|
40
|
+
* split.
|
|
41
|
+
*/
|
|
42
|
+
export type WalletChain = "solana" | "evm";
|
|
43
|
+
/**
|
|
44
|
+
* Decodes a Solana private key given as either a base58 string (`solana-keygen`'s
|
|
45
|
+
* "phantom-paste" form) or the JSON byte-array contents of an `id.json` keyfile (the form
|
|
46
|
+
* `solana-keygen new` actually writes to disk). Branches on whether the trimmed input starts
|
|
47
|
+
* with `[`: no -> base58 decode; yes -> parse as a 64-int 0..255 JSON array. That branch (rather
|
|
48
|
+
* than "try base58, then try JSON") is what lets a malformed array get a keyfile-specific error
|
|
49
|
+
* ("this looks like an id.json but...") instead of the generic base58 one.
|
|
50
|
+
*/
|
|
51
|
+
export declare function parseSolanaSecret(input: string): Uint8Array;
|
|
52
|
+
export interface EncryptWalletKeyParams {
|
|
53
|
+
/** Which chain `privateKey` belongs to; selects how it is decoded to raw bytes before sealing. */
|
|
54
|
+
chain: WalletChain;
|
|
55
|
+
/**
|
|
56
|
+
* The wallet's private key exactly as the caller holds it: a hex string for "evm" (an optional
|
|
57
|
+
* leading "0x" is accepted and stripped), or a base58 string for "solana". This is decoded to
|
|
58
|
+
* raw bytes locally -- matching Privy's own wallet-import reference decode -- and THOSE bytes
|
|
59
|
+
* are what gets HPKE-sealed, never the input string's own UTF-8 text.
|
|
60
|
+
*/
|
|
61
|
+
privateKey: string;
|
|
62
|
+
/** Privy's HPKE receiver public key from `/wallets/import/init`'s `encryptionPublicKey`, base64-encoded. */
|
|
63
|
+
encryptionPublicKey: string;
|
|
64
|
+
}
|
|
65
|
+
export interface EncryptWalletKeyResult {
|
|
66
|
+
/** Base64-encoded HPKE seal output; posted as `/wallets/import/submit`'s `ciphertext`. */
|
|
67
|
+
ciphertext: string;
|
|
68
|
+
/** Base64-encoded HPKE encapsulated key; posted as `/wallets/import/submit`'s `encapsulatedKey`. */
|
|
69
|
+
encapsulatedKey: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Encrypts `privateKey` (decoded to raw bytes per `chain`, see `decodeWalletPrivateKey`) to
|
|
73
|
+
* `encryptionPublicKey` under the HPKE suite above (RFC 9180 single-shot Base-mode seal with a
|
|
74
|
+
* fresh ephemeral sender keypair per call). Neither the input string nor its decoded bytes leave
|
|
75
|
+
* this function call; only the returned ciphertext and encapsulated key are meant to travel
|
|
76
|
+
* further.
|
|
77
|
+
*/
|
|
78
|
+
export declare function encryptWalletKeyForImport(params: EncryptWalletKeyParams): Promise<EncryptWalletKeyResult>;
|
|
79
|
+
export interface SignerKeypair {
|
|
80
|
+
/** PEM-encoded PKCS8 private key. The caller keeps this; the SDK never transmits it anywhere. */
|
|
81
|
+
privateKeyPem: string;
|
|
82
|
+
/** Base64-encoded SPKI (DER) public key -- the value `CandleClient.importWallet`'s `signerPublicKey` expects. */
|
|
83
|
+
publicKeyDerBase64: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Generates a fresh P-256 (ECDSA) keypair via WebCrypto for use as an agent's wallet signer.
|
|
87
|
+
* Only `publicKeyDerBase64` is meant to leave the process (it becomes `signerPublicKey` in
|
|
88
|
+
* `/wallets/import/submit`, which Privy registers as a 1-of-1 key quorum authorized to sign for
|
|
89
|
+
* the imported wallet); `privateKeyPem` is the caller's to store and use for that later signing,
|
|
90
|
+
* never sent to Candle or Privy by this SDK.
|
|
91
|
+
*/
|
|
92
|
+
export declare function generateSignerKeypair(): Promise<SignerKeypair>;
|
|
93
|
+
//# sourceMappingURL=wallet-import.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet-import.d.ts","sourceRoot":"","sources":["../src/wallet-import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAeH;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,KAAK,CAAA;AAE1C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA0B3D;AAkBD,MAAM,WAAW,sBAAsB;IACrC,kGAAkG;IAClG,KAAK,EAAE,WAAW,CAAA;IAClB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,4GAA4G;IAC5G,mBAAmB,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,0FAA0F;IAC1F,UAAU,EAAE,MAAM,CAAA;IAClB,oGAAoG;IACpG,eAAe,EAAE,MAAM,CAAA;CACxB;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAY/G;AAED,MAAM,WAAW,aAAa;IAC5B,iGAAiG;IACjG,aAAa,EAAE,MAAM,CAAA;IACrB,iHAAiH;IACjH,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,aAAa,CAAC,CAUpE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook signature verification for Candle webhook deliveries (Phase 2, R9).
|
|
3
|
+
*
|
|
4
|
+
* Every delivery carries `x-candle-signature: t=<unix seconds>,v1=<hex hmac-sha256(secret,
|
|
5
|
+
* "<t>.<body>")>` where `<body>` is the EXACT raw request body string (re-serializing the
|
|
6
|
+
* parsed JSON can reorder keys and break the signature; always verify the raw bytes as text).
|
|
7
|
+
*
|
|
8
|
+
* Implemented with `node:crypto` (`createHmac` + `timingSafeEqual`), which both Node 18+ and
|
|
9
|
+
* Bun provide, so the SDK stays zero-dependency. Edge runtimes without `node:crypto` would
|
|
10
|
+
* need a Web Crypto (`crypto.subtle`) implementation; that is a deliberate later concern, not
|
|
11
|
+
* covered here.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Returns true only when `header` carries a well-formed `t=...,v1=...` signature whose
|
|
15
|
+
* timestamp is within `toleranceSec` (default 300) of `nowSec` and whose `v1` digest matches
|
|
16
|
+
* HMAC-SHA256(secret, `${t}.${body}`). Never throws on malformed input; every failure mode
|
|
17
|
+
* (missing header, bad format, stale timestamp, wrong secret, tampered body) is `false`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function verifyWebhookSignature(secret: string, header: string | null | undefined, body: string, nowSec: number, toleranceSec?: number): boolean;
|
|
20
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,YAAY,SAAM,GACjB,OAAO,CA0BT"}
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@candledottv/agent-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "TypeScript SDK for the Candle agent rail: typed headless launches, market reads, and webhook verification",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/candledottv/agentic.git",
|
|
18
|
+
"directory": "packages/sdk"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://docs.candle.tv/developers/agent-sdk",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/candledottv/agentic/issues"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"candle",
|
|
26
|
+
"solana",
|
|
27
|
+
"agent",
|
|
28
|
+
"sdk",
|
|
29
|
+
"token-launch",
|
|
30
|
+
"ai-agent"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build:js": "bun build src/index.ts --target=node --format=esm --packages=external --outfile=dist/index.js",
|
|
42
|
+
"build:types": "tsc -p tsconfig.build.json",
|
|
43
|
+
"build": "bun run build:js && bun run build:types",
|
|
44
|
+
"clean": "rm -rf dist",
|
|
45
|
+
"test": "bun test",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"lint": "biome check ."
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@hpke/chacha20poly1305": "^1.6.2",
|
|
51
|
+
"@hpke/core": "^1.7.2",
|
|
52
|
+
"@scure/base": "^1.2.6",
|
|
53
|
+
"canonicalize": "^2.1.0"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@privy-io/server-auth": "^1.32.5",
|
|
57
|
+
"@types/bun": "^1.1.14",
|
|
58
|
+
"typescript": "^5.7.2"
|
|
59
|
+
}
|
|
60
|
+
}
|