@gaberoo/kalshitools 1.0.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 (57) hide show
  1. package/README.md +666 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/config/init.d.ts +13 -0
  7. package/dist/commands/config/init.js +89 -0
  8. package/dist/commands/config/show.d.ts +10 -0
  9. package/dist/commands/config/show.js +77 -0
  10. package/dist/commands/markets/list.d.ts +11 -0
  11. package/dist/commands/markets/list.js +64 -0
  12. package/dist/commands/markets/show.d.ts +13 -0
  13. package/dist/commands/markets/show.js +79 -0
  14. package/dist/commands/orders/cancel.d.ts +14 -0
  15. package/dist/commands/orders/cancel.js +129 -0
  16. package/dist/commands/orders/create.d.ts +19 -0
  17. package/dist/commands/orders/create.js +211 -0
  18. package/dist/commands/orders/list.d.ts +13 -0
  19. package/dist/commands/orders/list.js +92 -0
  20. package/dist/commands/portfolio/balance.d.ts +9 -0
  21. package/dist/commands/portfolio/balance.js +36 -0
  22. package/dist/commands/portfolio/fills.d.ts +11 -0
  23. package/dist/commands/portfolio/fills.js +80 -0
  24. package/dist/commands/portfolio/positions.d.ts +9 -0
  25. package/dist/commands/portfolio/positions.js +58 -0
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +1 -0
  28. package/dist/lib/base-command.d.ts +13 -0
  29. package/dist/lib/base-command.js +38 -0
  30. package/dist/lib/config/manager.d.ts +71 -0
  31. package/dist/lib/config/manager.js +137 -0
  32. package/dist/lib/config/schema.d.ts +175 -0
  33. package/dist/lib/config/schema.js +59 -0
  34. package/dist/lib/errors/base.d.ts +84 -0
  35. package/dist/lib/errors/base.js +106 -0
  36. package/dist/lib/kalshi/auth.d.ts +17 -0
  37. package/dist/lib/kalshi/auth.js +71 -0
  38. package/dist/lib/kalshi/client.d.ts +86 -0
  39. package/dist/lib/kalshi/client.js +228 -0
  40. package/dist/lib/kalshi/index.d.ts +8 -0
  41. package/dist/lib/kalshi/index.js +19 -0
  42. package/dist/lib/kalshi/types.d.ts +155 -0
  43. package/dist/lib/kalshi/types.js +4 -0
  44. package/dist/lib/logger.d.ts +9 -0
  45. package/dist/lib/logger.js +41 -0
  46. package/dist/lib/output/formatter.d.ts +69 -0
  47. package/dist/lib/output/formatter.js +111 -0
  48. package/dist/lib/retry.d.ts +18 -0
  49. package/dist/lib/retry.js +81 -0
  50. package/dist/lib/sanitize.d.ts +28 -0
  51. package/dist/lib/sanitize.js +124 -0
  52. package/dist/lib/shutdown.d.ts +43 -0
  53. package/dist/lib/shutdown.js +106 -0
  54. package/dist/lib/validation.d.ts +37 -0
  55. package/dist/lib/validation.js +120 -0
  56. package/oclif.manifest.json +520 -0
  57. package/package.json +98 -0
@@ -0,0 +1,137 @@
1
+ import Conf from 'conf';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { readFileSync } from 'node:fs';
5
+ import { defaultConfig } from './schema.js';
6
+ import { ConfigError } from '../errors/base.js';
7
+ import { logger } from '../logger.js';
8
+ /**
9
+ * Configuration manager for kalshitools
10
+ */
11
+ export class ConfigManager {
12
+ store;
13
+ static instance = null;
14
+ constructor() {
15
+ this.store = new Conf({
16
+ projectName: 'kalshitools',
17
+ defaults: defaultConfig,
18
+ });
19
+ }
20
+ /**
21
+ * Get singleton instance
22
+ */
23
+ static getInstance() {
24
+ if (!ConfigManager.instance) {
25
+ ConfigManager.instance = new ConfigManager();
26
+ }
27
+ return ConfigManager.instance;
28
+ }
29
+ /**
30
+ * Get the full configuration
31
+ */
32
+ getConfig() {
33
+ const config = this.store.store;
34
+ return config;
35
+ }
36
+ /**
37
+ * Get the current environment (demo or production)
38
+ */
39
+ getEnvironment() {
40
+ return process.env.KALSHI_ENV === 'production' ? 'production' : this.store.get('environment', 'demo');
41
+ }
42
+ /**
43
+ * Set the environment
44
+ */
45
+ setEnvironment(env) {
46
+ this.store.set('environment', env);
47
+ }
48
+ /**
49
+ * Get API configuration for the current environment
50
+ */
51
+ getApiConfig() {
52
+ const env = this.getEnvironment();
53
+ const config = this.store.get(`api.${env}`);
54
+ // Override with environment variables if present
55
+ const keyId = process.env.KALSHI_API_KEY_ID || config.keyId;
56
+ const privateKeyPath = process.env.KALSHI_PRIVATE_KEY_PATH || config.privateKeyPath;
57
+ if (!keyId || !privateKeyPath) {
58
+ throw new ConfigError(`API credentials not configured for ${env} environment. Run 'kalshitools config init' to set up.`, { environment: env });
59
+ }
60
+ return {
61
+ baseUrl: config.baseUrl,
62
+ keyId,
63
+ privateKeyPath,
64
+ };
65
+ }
66
+ /**
67
+ * Read private key from file
68
+ */
69
+ readPrivateKey() {
70
+ const apiConfig = this.getApiConfig();
71
+ const keyPath = apiConfig.privateKeyPath;
72
+ // Expand ~ to home directory
73
+ const expandedPath = keyPath.startsWith('~') ? join(homedir(), keyPath.slice(1)) : keyPath;
74
+ try {
75
+ const privateKey = readFileSync(expandedPath, 'utf-8');
76
+ logger.debug({ path: expandedPath }, 'Read private key from file');
77
+ return privateKey;
78
+ }
79
+ catch (error) {
80
+ throw new ConfigError(`Failed to read private key from ${expandedPath}`, {
81
+ path: expandedPath,
82
+ error: error instanceof Error ? error.message : String(error),
83
+ });
84
+ }
85
+ }
86
+ /**
87
+ * Set API configuration for an environment
88
+ */
89
+ setApiConfig(env, config) {
90
+ const current = this.store.get(`api.${env}`);
91
+ this.store.set(`api.${env}`, { ...current, ...config });
92
+ }
93
+ /**
94
+ * Get output configuration
95
+ */
96
+ getOutputConfig() {
97
+ return this.store.get('output');
98
+ }
99
+ /**
100
+ * Get trading configuration
101
+ */
102
+ getTradingConfig() {
103
+ return this.store.get('trading');
104
+ }
105
+ /**
106
+ * Set a configuration value
107
+ */
108
+ set(key, value) {
109
+ this.store.set(key, value);
110
+ }
111
+ /**
112
+ * Get the configuration file path
113
+ */
114
+ getConfigPath() {
115
+ return this.store.path;
116
+ }
117
+ /**
118
+ * Reset configuration to defaults
119
+ */
120
+ reset() {
121
+ this.store.clear();
122
+ }
123
+ /**
124
+ * Check if configuration is initialized
125
+ */
126
+ isConfigured() {
127
+ const env = this.getEnvironment();
128
+ const config = this.store.get(`api.${env}`);
129
+ return Boolean(config.keyId && config.privateKeyPath);
130
+ }
131
+ }
132
+ /**
133
+ * Get the global configuration manager instance
134
+ */
135
+ export function getConfig() {
136
+ return ConfigManager.getInstance();
137
+ }
@@ -0,0 +1,175 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * API environment configuration schema
4
+ */
5
+ declare const apiEnvSchema: z.ZodObject<{
6
+ baseUrl: z.ZodString;
7
+ keyId: z.ZodOptional<z.ZodString>;
8
+ privateKeyPath: z.ZodOptional<z.ZodString>;
9
+ }, "strip", z.ZodTypeAny, {
10
+ baseUrl: string;
11
+ keyId?: string | undefined;
12
+ privateKeyPath?: string | undefined;
13
+ }, {
14
+ baseUrl: string;
15
+ keyId?: string | undefined;
16
+ privateKeyPath?: string | undefined;
17
+ }>;
18
+ /**
19
+ * Output configuration schema
20
+ */
21
+ declare const outputSchema: z.ZodObject<{
22
+ defaultFormat: z.ZodDefault<z.ZodEnum<["table", "json"]>>;
23
+ colors: z.ZodDefault<z.ZodBoolean>;
24
+ }, "strip", z.ZodTypeAny, {
25
+ defaultFormat: "json" | "table";
26
+ colors: boolean;
27
+ }, {
28
+ defaultFormat?: "json" | "table" | undefined;
29
+ colors?: boolean | undefined;
30
+ }>;
31
+ /**
32
+ * Trading safety configuration schema
33
+ */
34
+ declare const tradingSchema: z.ZodObject<{
35
+ confirmations: z.ZodDefault<z.ZodBoolean>;
36
+ maxOrderSize: z.ZodDefault<z.ZodNumber>;
37
+ }, "strip", z.ZodTypeAny, {
38
+ confirmations: boolean;
39
+ maxOrderSize: number;
40
+ }, {
41
+ confirmations?: boolean | undefined;
42
+ maxOrderSize?: number | undefined;
43
+ }>;
44
+ /**
45
+ * Main configuration schema
46
+ */
47
+ export declare const configSchema: z.ZodObject<{
48
+ version: z.ZodLiteral<"1">;
49
+ environment: z.ZodDefault<z.ZodEnum<["demo", "production"]>>;
50
+ api: z.ZodObject<{
51
+ demo: z.ZodObject<{
52
+ baseUrl: z.ZodString;
53
+ keyId: z.ZodOptional<z.ZodString>;
54
+ privateKeyPath: z.ZodOptional<z.ZodString>;
55
+ }, "strip", z.ZodTypeAny, {
56
+ baseUrl: string;
57
+ keyId?: string | undefined;
58
+ privateKeyPath?: string | undefined;
59
+ }, {
60
+ baseUrl: string;
61
+ keyId?: string | undefined;
62
+ privateKeyPath?: string | undefined;
63
+ }>;
64
+ production: z.ZodObject<{
65
+ baseUrl: z.ZodString;
66
+ keyId: z.ZodOptional<z.ZodString>;
67
+ privateKeyPath: z.ZodOptional<z.ZodString>;
68
+ }, "strip", z.ZodTypeAny, {
69
+ baseUrl: string;
70
+ keyId?: string | undefined;
71
+ privateKeyPath?: string | undefined;
72
+ }, {
73
+ baseUrl: string;
74
+ keyId?: string | undefined;
75
+ privateKeyPath?: string | undefined;
76
+ }>;
77
+ }, "strip", z.ZodTypeAny, {
78
+ demo: {
79
+ baseUrl: string;
80
+ keyId?: string | undefined;
81
+ privateKeyPath?: string | undefined;
82
+ };
83
+ production: {
84
+ baseUrl: string;
85
+ keyId?: string | undefined;
86
+ privateKeyPath?: string | undefined;
87
+ };
88
+ }, {
89
+ demo: {
90
+ baseUrl: string;
91
+ keyId?: string | undefined;
92
+ privateKeyPath?: string | undefined;
93
+ };
94
+ production: {
95
+ baseUrl: string;
96
+ keyId?: string | undefined;
97
+ privateKeyPath?: string | undefined;
98
+ };
99
+ }>;
100
+ output: z.ZodDefault<z.ZodObject<{
101
+ defaultFormat: z.ZodDefault<z.ZodEnum<["table", "json"]>>;
102
+ colors: z.ZodDefault<z.ZodBoolean>;
103
+ }, "strip", z.ZodTypeAny, {
104
+ defaultFormat: "json" | "table";
105
+ colors: boolean;
106
+ }, {
107
+ defaultFormat?: "json" | "table" | undefined;
108
+ colors?: boolean | undefined;
109
+ }>>;
110
+ trading: z.ZodDefault<z.ZodObject<{
111
+ confirmations: z.ZodDefault<z.ZodBoolean>;
112
+ maxOrderSize: z.ZodDefault<z.ZodNumber>;
113
+ }, "strip", z.ZodTypeAny, {
114
+ confirmations: boolean;
115
+ maxOrderSize: number;
116
+ }, {
117
+ confirmations?: boolean | undefined;
118
+ maxOrderSize?: number | undefined;
119
+ }>>;
120
+ }, "strip", z.ZodTypeAny, {
121
+ version: "1";
122
+ output: {
123
+ defaultFormat: "json" | "table";
124
+ colors: boolean;
125
+ };
126
+ environment: "demo" | "production";
127
+ api: {
128
+ demo: {
129
+ baseUrl: string;
130
+ keyId?: string | undefined;
131
+ privateKeyPath?: string | undefined;
132
+ };
133
+ production: {
134
+ baseUrl: string;
135
+ keyId?: string | undefined;
136
+ privateKeyPath?: string | undefined;
137
+ };
138
+ };
139
+ trading: {
140
+ confirmations: boolean;
141
+ maxOrderSize: number;
142
+ };
143
+ }, {
144
+ version: "1";
145
+ api: {
146
+ demo: {
147
+ baseUrl: string;
148
+ keyId?: string | undefined;
149
+ privateKeyPath?: string | undefined;
150
+ };
151
+ production: {
152
+ baseUrl: string;
153
+ keyId?: string | undefined;
154
+ privateKeyPath?: string | undefined;
155
+ };
156
+ };
157
+ output?: {
158
+ defaultFormat?: "json" | "table" | undefined;
159
+ colors?: boolean | undefined;
160
+ } | undefined;
161
+ environment?: "demo" | "production" | undefined;
162
+ trading?: {
163
+ confirmations?: boolean | undefined;
164
+ maxOrderSize?: number | undefined;
165
+ } | undefined;
166
+ }>;
167
+ export type Config = z.infer<typeof configSchema>;
168
+ export type ApiEnvConfig = z.infer<typeof apiEnvSchema>;
169
+ export type OutputConfig = z.infer<typeof outputSchema>;
170
+ export type TradingConfig = z.infer<typeof tradingSchema>;
171
+ /**
172
+ * Default configuration
173
+ */
174
+ export declare const defaultConfig: Config;
175
+ export {};
@@ -0,0 +1,59 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * API environment configuration schema
4
+ */
5
+ const apiEnvSchema = z.object({
6
+ baseUrl: z.string().url(),
7
+ keyId: z.string().optional(),
8
+ privateKeyPath: z.string().optional(),
9
+ });
10
+ /**
11
+ * Output configuration schema
12
+ */
13
+ const outputSchema = z.object({
14
+ defaultFormat: z.enum(['table', 'json']).default('table'),
15
+ colors: z.boolean().default(true),
16
+ });
17
+ /**
18
+ * Trading safety configuration schema
19
+ */
20
+ const tradingSchema = z.object({
21
+ confirmations: z.boolean().default(true),
22
+ maxOrderSize: z.number().positive().default(1000),
23
+ });
24
+ /**
25
+ * Main configuration schema
26
+ */
27
+ export const configSchema = z.object({
28
+ version: z.literal('1'),
29
+ environment: z.enum(['demo', 'production']).default('demo'),
30
+ api: z.object({
31
+ demo: apiEnvSchema,
32
+ production: apiEnvSchema,
33
+ }),
34
+ output: outputSchema.default({}),
35
+ trading: tradingSchema.default({}),
36
+ });
37
+ /**
38
+ * Default configuration
39
+ */
40
+ export const defaultConfig = {
41
+ version: '1',
42
+ environment: 'demo',
43
+ api: {
44
+ demo: {
45
+ baseUrl: 'https://demo-api.kalshi.co/trade-api/v2',
46
+ },
47
+ production: {
48
+ baseUrl: 'https://trading-api.kalshi.com/trade-api/v2',
49
+ },
50
+ },
51
+ output: {
52
+ defaultFormat: 'table',
53
+ colors: true,
54
+ },
55
+ trading: {
56
+ confirmations: true,
57
+ maxOrderSize: 1000,
58
+ },
59
+ };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Exit codes for the CLI
3
+ */
4
+ export declare enum ExitCode {
5
+ Success = 0,
6
+ GeneralError = 1,
7
+ ConfigError = 2,
8
+ AuthError = 3,
9
+ APIError = 4,
10
+ ValidationError = 5,
11
+ RateLimitError = 6
12
+ }
13
+ /**
14
+ * Error code type for structured error responses
15
+ */
16
+ export type ErrorCode = 'GENERAL_ERROR' | 'CONFIG_ERROR' | 'AUTH_ERROR' | 'API_ERROR' | 'VALIDATION_ERROR' | 'RATE_LIMIT_ERROR' | 'NETWORK_ERROR' | 'NOT_FOUND' | 'TIMEOUT';
17
+ /**
18
+ * Base error class for all kalshitools errors
19
+ */
20
+ export declare class KalshiToolsError extends Error {
21
+ readonly code: ErrorCode;
22
+ readonly exitCode: ExitCode;
23
+ readonly details?: Record<string, unknown>;
24
+ constructor(message: string, code?: ErrorCode, exitCode?: ExitCode, details?: Record<string, unknown>);
25
+ /**
26
+ * Convert error to JSON format for structured output
27
+ */
28
+ toJSON(): {
29
+ success: false;
30
+ error: {
31
+ code: ErrorCode;
32
+ message: string;
33
+ details?: Record<string, unknown>;
34
+ };
35
+ };
36
+ }
37
+ /**
38
+ * Configuration-related errors
39
+ */
40
+ export declare class ConfigError extends KalshiToolsError {
41
+ constructor(message: string, details?: Record<string, unknown>);
42
+ }
43
+ /**
44
+ * Authentication-related errors
45
+ */
46
+ export declare class AuthError extends KalshiToolsError {
47
+ constructor(message: string, details?: Record<string, unknown>);
48
+ }
49
+ /**
50
+ * API-related errors
51
+ */
52
+ export declare class APIError extends KalshiToolsError {
53
+ constructor(message: string, details?: Record<string, unknown>);
54
+ }
55
+ /**
56
+ * Validation errors
57
+ */
58
+ export declare class ValidationError extends KalshiToolsError {
59
+ constructor(message: string, details?: Record<string, unknown>);
60
+ }
61
+ /**
62
+ * Rate limit errors
63
+ */
64
+ export declare class RateLimitError extends KalshiToolsError {
65
+ constructor(message: string, details?: Record<string, unknown>);
66
+ }
67
+ /**
68
+ * Network-related errors
69
+ */
70
+ export declare class NetworkError extends KalshiToolsError {
71
+ constructor(message: string, details?: Record<string, unknown>);
72
+ }
73
+ /**
74
+ * Resource not found errors
75
+ */
76
+ export declare class NotFoundError extends KalshiToolsError {
77
+ constructor(message: string, details?: Record<string, unknown>);
78
+ }
79
+ /**
80
+ * Timeout errors
81
+ */
82
+ export declare class TimeoutError extends KalshiToolsError {
83
+ constructor(message: string, details?: Record<string, unknown>);
84
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Exit codes for the CLI
3
+ */
4
+ export var ExitCode;
5
+ (function (ExitCode) {
6
+ ExitCode[ExitCode["Success"] = 0] = "Success";
7
+ ExitCode[ExitCode["GeneralError"] = 1] = "GeneralError";
8
+ ExitCode[ExitCode["ConfigError"] = 2] = "ConfigError";
9
+ ExitCode[ExitCode["AuthError"] = 3] = "AuthError";
10
+ ExitCode[ExitCode["APIError"] = 4] = "APIError";
11
+ ExitCode[ExitCode["ValidationError"] = 5] = "ValidationError";
12
+ ExitCode[ExitCode["RateLimitError"] = 6] = "RateLimitError";
13
+ })(ExitCode || (ExitCode = {}));
14
+ /**
15
+ * Base error class for all kalshitools errors
16
+ */
17
+ export class KalshiToolsError extends Error {
18
+ code;
19
+ exitCode;
20
+ details;
21
+ constructor(message, code = 'GENERAL_ERROR', exitCode = ExitCode.GeneralError, details) {
22
+ super(message);
23
+ this.name = this.constructor.name;
24
+ this.code = code;
25
+ this.exitCode = exitCode;
26
+ this.details = details;
27
+ Error.captureStackTrace(this, this.constructor);
28
+ }
29
+ /**
30
+ * Convert error to JSON format for structured output
31
+ */
32
+ toJSON() {
33
+ return {
34
+ success: false,
35
+ error: {
36
+ code: this.code,
37
+ message: this.message,
38
+ ...(this.details && { details: this.details }),
39
+ },
40
+ };
41
+ }
42
+ }
43
+ /**
44
+ * Configuration-related errors
45
+ */
46
+ export class ConfigError extends KalshiToolsError {
47
+ constructor(message, details) {
48
+ super(message, 'CONFIG_ERROR', ExitCode.ConfigError, details);
49
+ }
50
+ }
51
+ /**
52
+ * Authentication-related errors
53
+ */
54
+ export class AuthError extends KalshiToolsError {
55
+ constructor(message, details) {
56
+ super(message, 'AUTH_ERROR', ExitCode.AuthError, details);
57
+ }
58
+ }
59
+ /**
60
+ * API-related errors
61
+ */
62
+ export class APIError extends KalshiToolsError {
63
+ constructor(message, details) {
64
+ super(message, 'API_ERROR', ExitCode.APIError, details);
65
+ }
66
+ }
67
+ /**
68
+ * Validation errors
69
+ */
70
+ export class ValidationError extends KalshiToolsError {
71
+ constructor(message, details) {
72
+ super(message, 'VALIDATION_ERROR', ExitCode.ValidationError, details);
73
+ }
74
+ }
75
+ /**
76
+ * Rate limit errors
77
+ */
78
+ export class RateLimitError extends KalshiToolsError {
79
+ constructor(message, details) {
80
+ super(message, 'RATE_LIMIT_ERROR', ExitCode.RateLimitError, details);
81
+ }
82
+ }
83
+ /**
84
+ * Network-related errors
85
+ */
86
+ export class NetworkError extends KalshiToolsError {
87
+ constructor(message, details) {
88
+ super(message, 'NETWORK_ERROR', ExitCode.GeneralError, details);
89
+ }
90
+ }
91
+ /**
92
+ * Resource not found errors
93
+ */
94
+ export class NotFoundError extends KalshiToolsError {
95
+ constructor(message, details) {
96
+ super(message, 'NOT_FOUND', ExitCode.GeneralError, details);
97
+ }
98
+ }
99
+ /**
100
+ * Timeout errors
101
+ */
102
+ export class TimeoutError extends KalshiToolsError {
103
+ constructor(message, details) {
104
+ super(message, 'TIMEOUT', ExitCode.GeneralError, details);
105
+ }
106
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Generate HTTP signature for Kalshi API authentication
3
+ * Uses RSA-PSS signing as required by Kalshi API
4
+ */
5
+ export declare class KalshiAuth {
6
+ private keyId;
7
+ private privateKey;
8
+ constructor(keyId: string, privateKey: string);
9
+ /**
10
+ * Generate authentication headers for a request
11
+ */
12
+ generateAuthHeaders(method: string, path: string, timestamp?: number): Record<string, string>;
13
+ /**
14
+ * Validate private key format
15
+ */
16
+ static validatePrivateKey(privateKey: string): boolean;
17
+ }
@@ -0,0 +1,71 @@
1
+ import crypto, { createSign } from 'node:crypto';
2
+ import { AuthError } from '../errors/base.js';
3
+ import { logger } from '../logger.js';
4
+ /**
5
+ * Generate HTTP signature for Kalshi API authentication
6
+ * Uses RSA-PSS signing as required by Kalshi API
7
+ */
8
+ export class KalshiAuth {
9
+ keyId;
10
+ privateKey;
11
+ constructor(keyId, privateKey) {
12
+ this.keyId = keyId;
13
+ this.privateKey = privateKey;
14
+ }
15
+ /**
16
+ * Generate authentication headers for a request
17
+ */
18
+ generateAuthHeaders(method, path, timestamp) {
19
+ const ts = timestamp || Date.now();
20
+ const timestampStr = Math.floor(ts / 1000).toString();
21
+ // Create signature string: METHOD + timestamp + path
22
+ const signaturePayload = `${timestampStr}${method}${path}`;
23
+ logger.debug({ method, path, timestamp: timestampStr }, 'Generating signature');
24
+ try {
25
+ // Sign using RSA-PSS
26
+ const sign = createSign('RSA-SHA256');
27
+ sign.update(signaturePayload);
28
+ sign.end();
29
+ const signature = sign.sign({
30
+ key: this.privateKey,
31
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
32
+ saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST,
33
+ }, 'base64');
34
+ return {
35
+ 'KALSHI-ACCESS-KEY': this.keyId,
36
+ 'KALSHI-ACCESS-SIGNATURE': signature,
37
+ 'KALSHI-ACCESS-TIMESTAMP': timestampStr,
38
+ };
39
+ }
40
+ catch (error) {
41
+ logger.error({ error }, 'Failed to generate signature');
42
+ throw new AuthError('Failed to generate authentication signature', {
43
+ error: error instanceof Error ? error.message : String(error),
44
+ });
45
+ }
46
+ }
47
+ /**
48
+ * Validate private key format
49
+ */
50
+ static validatePrivateKey(privateKey) {
51
+ try {
52
+ // Check if it's PEM format
53
+ if (!privateKey.includes('-----BEGIN') || !privateKey.includes('-----END')) {
54
+ return false;
55
+ }
56
+ // Try to create a sign instance to validate the key
57
+ const sign = createSign('RSA-SHA256');
58
+ sign.update('test');
59
+ sign.end();
60
+ sign.sign({
61
+ key: privateKey,
62
+ padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
63
+ saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST,
64
+ }, 'base64');
65
+ return true;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ }