@quantiya/codevibe-claude-plugin 1.0.10 → 1.0.12
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/.claude-plugin/plugin.json +1 -1
- package/README.md +75 -245
- package/dist/server.js +16 -1162
- package/node_modules/@quantiya/codevibe-core/README.md +15 -6
- package/node_modules/@quantiya/codevibe-core/bin/codevibe.js +1 -1
- package/node_modules/@quantiya/codevibe-core/dist/index.js +216 -67
- package/node_modules/@quantiya/codevibe-core/package.json +12 -9
- package/node_modules/node-abi/abi_registry.json +7 -0
- package/node_modules/node-abi/package.json +1 -1
- package/package.json +11 -21
- package/dist/appsync-client.d.ts +0 -67
- package/dist/appsync-client.d.ts.map +0 -1
- package/dist/appsync-client.js +0 -858
- package/dist/appsync-client.js.map +0 -1
- package/dist/auth-cli.d.ts +0 -18
- package/dist/auth-cli.d.ts.map +0 -1
- package/dist/auth-cli.js +0 -472
- package/dist/auth-cli.js.map +0 -1
- package/dist/command-executor.d.ts +0 -20
- package/dist/command-executor.d.ts.map +0 -1
- package/dist/command-executor.js +0 -127
- package/dist/command-executor.js.map +0 -1
- package/dist/config.d.ts +0 -25
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -106
- package/dist/config.js.map +0 -1
- package/dist/crypto-service.d.ts +0 -115
- package/dist/crypto-service.d.ts.map +0 -1
- package/dist/crypto-service.js +0 -278
- package/dist/crypto-service.js.map +0 -1
- package/dist/http-api.d.ts +0 -35
- package/dist/http-api.d.ts.map +0 -1
- package/dist/http-api.js +0 -334
- package/dist/http-api.js.map +0 -1
- package/dist/key-manager.d.ts +0 -87
- package/dist/key-manager.d.ts.map +0 -1
- package/dist/key-manager.js +0 -287
- package/dist/key-manager.js.map +0 -1
- package/dist/logger.d.ts +0 -2
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -18
- package/dist/logger.js.map +0 -1
- package/dist/prompt-responder.d.ts +0 -22
- package/dist/prompt-responder.d.ts.map +0 -1
- package/dist/prompt-responder.js +0 -132
- package/dist/prompt-responder.js.map +0 -1
- package/dist/server.d.ts +0 -9
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/token-storage.d.ts +0 -39
- package/dist/token-storage.d.ts.map +0 -1
- package/dist/token-storage.js +0 -169
- package/dist/token-storage.js.map +0 -1
- package/dist/types.d.ts +0 -110
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -17
- package/dist/types.js.map +0 -1
- package/node_modules/@quantiya/codevibe-core/dist/appsync/appsync-client.js +0 -576
- package/node_modules/@quantiya/codevibe-core/dist/appsync/index.js +0 -10
- package/node_modules/@quantiya/codevibe-core/dist/appsync/queries.js +0 -189
- package/node_modules/@quantiya/codevibe-core/dist/auth/auth-cli.js +0 -217
- package/node_modules/@quantiya/codevibe-core/dist/auth/auth-service.js +0 -464
- package/node_modules/@quantiya/codevibe-core/dist/auth/fetch-helpers.js +0 -165
- package/node_modules/@quantiya/codevibe-core/dist/auth/index.js +0 -9
- package/node_modules/@quantiya/codevibe-core/dist/config/config.js +0 -123
- package/node_modules/@quantiya/codevibe-core/dist/config/index.js +0 -8
- package/node_modules/@quantiya/codevibe-core/dist/crypto/crypto-service.js +0 -284
- package/node_modules/@quantiya/codevibe-core/dist/crypto/index.js +0 -9
- package/node_modules/@quantiya/codevibe-core/dist/keychain/index.js +0 -8
- package/node_modules/@quantiya/codevibe-core/dist/keychain/keychain-manager.js +0 -375
- package/node_modules/@quantiya/codevibe-core/dist/logger/index.js +0 -8
- package/node_modules/@quantiya/codevibe-core/dist/logger/logger.js +0 -142
- package/node_modules/@quantiya/codevibe-core/dist/prompt-parser.js +0 -236
- package/node_modules/@quantiya/codevibe-core/dist/session/index.js +0 -7
- package/node_modules/@quantiya/codevibe-core/dist/session/session-resume.js +0 -151
- package/node_modules/@quantiya/codevibe-core/dist/types/auth.js +0 -3
- package/node_modules/@quantiya/codevibe-core/dist/types/encryption.js +0 -3
- package/node_modules/@quantiya/codevibe-core/dist/types/events.js +0 -28
- package/node_modules/@quantiya/codevibe-core/dist/types/index.js +0 -22
- package/node_modules/@quantiya/codevibe-core/dist/types/session.js +0 -22
package/dist/appsync-client.js
DELETED
|
@@ -1,858 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AppSyncClient = void 0;
|
|
7
|
-
const ws_1 = __importDefault(require("ws"));
|
|
8
|
-
const uuid_1 = require("uuid");
|
|
9
|
-
const config_1 = require("./config");
|
|
10
|
-
const logger_1 = require("./logger");
|
|
11
|
-
const token_storage_1 = require("./token-storage");
|
|
12
|
-
const types_1 = require("./types");
|
|
13
|
-
// GraphQL queries and mutations
|
|
14
|
-
const createSessionMutation = /* GraphQL */ `
|
|
15
|
-
mutation CreateSession($input: CreateSessionInput!) {
|
|
16
|
-
createSession(input: $input) {
|
|
17
|
-
sessionId
|
|
18
|
-
userId
|
|
19
|
-
agentType
|
|
20
|
-
projectPath
|
|
21
|
-
status
|
|
22
|
-
createdAt
|
|
23
|
-
updatedAt
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
`;
|
|
27
|
-
const updateSessionMutation = /* GraphQL */ `
|
|
28
|
-
mutation UpdateSession($input: UpdateSessionInput!) {
|
|
29
|
-
updateSession(input: $input) {
|
|
30
|
-
sessionId
|
|
31
|
-
status
|
|
32
|
-
updatedAt
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
`;
|
|
36
|
-
const createEventMutation = /* GraphQL */ `
|
|
37
|
-
mutation CreateEvent($input: CreateEventInput!) {
|
|
38
|
-
createEvent(input: $input) {
|
|
39
|
-
eventId
|
|
40
|
-
sessionId
|
|
41
|
-
type
|
|
42
|
-
source
|
|
43
|
-
content
|
|
44
|
-
timestamp
|
|
45
|
-
metadata
|
|
46
|
-
promptId
|
|
47
|
-
deliveryStatus
|
|
48
|
-
deliveredAt
|
|
49
|
-
executedAt
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
`;
|
|
53
|
-
const updateEventStatusMutation = /* GraphQL */ `
|
|
54
|
-
mutation UpdateEventStatus($input: UpdateEventStatusInput!) {
|
|
55
|
-
updateEventStatus(input: $input) {
|
|
56
|
-
eventId
|
|
57
|
-
sessionId
|
|
58
|
-
type
|
|
59
|
-
source
|
|
60
|
-
content
|
|
61
|
-
timestamp
|
|
62
|
-
metadata
|
|
63
|
-
promptId
|
|
64
|
-
deliveryStatus
|
|
65
|
-
deliveredAt
|
|
66
|
-
executedAt
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
`;
|
|
70
|
-
const createFileChangeMutation = /* GraphQL */ `
|
|
71
|
-
mutation CreateFileChange($input: CreateFileChangeInput!) {
|
|
72
|
-
createFileChange(input: $input) {
|
|
73
|
-
changeId
|
|
74
|
-
sessionId
|
|
75
|
-
filePath
|
|
76
|
-
action
|
|
77
|
-
diff
|
|
78
|
-
timestamp
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
`;
|
|
82
|
-
const getSessionQuery = /* GraphQL */ `
|
|
83
|
-
query GetSession($sessionId: ID!) {
|
|
84
|
-
getSession(sessionId: $sessionId) {
|
|
85
|
-
sessionId
|
|
86
|
-
userId
|
|
87
|
-
projectPath
|
|
88
|
-
status
|
|
89
|
-
createdAt
|
|
90
|
-
updatedAt
|
|
91
|
-
metadata
|
|
92
|
-
isEncrypted
|
|
93
|
-
encryptedKeys {
|
|
94
|
-
deviceId
|
|
95
|
-
encryptedKey
|
|
96
|
-
ephemeralPublicKey
|
|
97
|
-
}
|
|
98
|
-
creatorDeviceId
|
|
99
|
-
encryptionVersion
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
`;
|
|
103
|
-
const listEventsQuery = /* GraphQL */ `
|
|
104
|
-
query ListEvents($sessionId: ID!, $source: EventSource) {
|
|
105
|
-
listEvents(sessionId: $sessionId, source: $source) {
|
|
106
|
-
items {
|
|
107
|
-
eventId
|
|
108
|
-
sessionId
|
|
109
|
-
type
|
|
110
|
-
source
|
|
111
|
-
content
|
|
112
|
-
timestamp
|
|
113
|
-
metadata
|
|
114
|
-
promptId
|
|
115
|
-
}
|
|
116
|
-
nextToken
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
`;
|
|
120
|
-
const onEventCreatedSubscription = /* GraphQL */ `
|
|
121
|
-
subscription OnEventCreated($sessionId: ID!) {
|
|
122
|
-
onEventCreated(sessionId: $sessionId) {
|
|
123
|
-
eventId
|
|
124
|
-
sessionId
|
|
125
|
-
type
|
|
126
|
-
source
|
|
127
|
-
content
|
|
128
|
-
timestamp
|
|
129
|
-
metadata
|
|
130
|
-
promptId
|
|
131
|
-
attachments {
|
|
132
|
-
id
|
|
133
|
-
type
|
|
134
|
-
filename
|
|
135
|
-
s3Key
|
|
136
|
-
size
|
|
137
|
-
width
|
|
138
|
-
height
|
|
139
|
-
isEncrypted
|
|
140
|
-
}
|
|
141
|
-
deliveryStatus
|
|
142
|
-
deliveredAt
|
|
143
|
-
executedAt
|
|
144
|
-
isEncrypted
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
`;
|
|
148
|
-
const getAttachmentDownloadUrlMutation = /* GraphQL */ `
|
|
149
|
-
mutation GetAttachmentDownloadUrl($s3Key: String!) {
|
|
150
|
-
getAttachmentDownloadUrl(s3Key: $s3Key) {
|
|
151
|
-
downloadUrl
|
|
152
|
-
expiresAt
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
`;
|
|
156
|
-
const listUserDeviceKeysQuery = /* GraphQL */ `
|
|
157
|
-
query ListUserDeviceKeys {
|
|
158
|
-
listUserDeviceKeys {
|
|
159
|
-
userId
|
|
160
|
-
deviceId
|
|
161
|
-
publicKey
|
|
162
|
-
platform
|
|
163
|
-
deviceName
|
|
164
|
-
createdAt
|
|
165
|
-
lastUsedAt
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
`;
|
|
169
|
-
const registerDeviceKeyMutation = /* GraphQL */ `
|
|
170
|
-
mutation RegisterDeviceKey($input: RegisterDeviceKeyInput!) {
|
|
171
|
-
registerDeviceKey(input: $input) {
|
|
172
|
-
userId
|
|
173
|
-
deviceId
|
|
174
|
-
publicKey
|
|
175
|
-
platform
|
|
176
|
-
deviceName
|
|
177
|
-
createdAt
|
|
178
|
-
lastUsedAt
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
`;
|
|
182
|
-
// Reconnection configuration
|
|
183
|
-
const RECONNECT_CONFIG = {
|
|
184
|
-
maxAttempts: 10,
|
|
185
|
-
baseDelayMs: 1000, // Start with 1 second
|
|
186
|
-
maxDelayMs: 60000, // Max 1 minute between attempts
|
|
187
|
-
backoffMultiplier: 2, // Double delay each attempt
|
|
188
|
-
};
|
|
189
|
-
class AppSyncClient {
|
|
190
|
-
constructor() {
|
|
191
|
-
this.authenticated = false;
|
|
192
|
-
this.currentUserId = null;
|
|
193
|
-
this.currentEmail = null;
|
|
194
|
-
this.storedTokens = null;
|
|
195
|
-
this.activeSubscriptions = new Map();
|
|
196
|
-
logger_1.logger.info('AppSync client initialized (Cognito User Pool auth via OAuth)');
|
|
197
|
-
}
|
|
198
|
-
// Get the current authenticated user ID
|
|
199
|
-
getCurrentUserId() {
|
|
200
|
-
return this.currentUserId || 'default-user';
|
|
201
|
-
}
|
|
202
|
-
// Get the current authenticated user email
|
|
203
|
-
getCurrentUserEmail() {
|
|
204
|
-
return this.currentEmail;
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Authenticate using stored OAuth tokens from 'codevibe-claude login'
|
|
208
|
-
* Returns true if successfully authenticated, false otherwise
|
|
209
|
-
*/
|
|
210
|
-
async authenticateWithStoredTokens() {
|
|
211
|
-
try {
|
|
212
|
-
// Load tokens from storage
|
|
213
|
-
const tokens = (0, token_storage_1.loadTokens)();
|
|
214
|
-
if (!tokens) {
|
|
215
|
-
logger_1.logger.debug('No stored tokens found');
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
logger_1.logger.info('Found stored OAuth tokens', {
|
|
219
|
-
userId: tokens.userId,
|
|
220
|
-
email: tokens.email,
|
|
221
|
-
expired: (0, token_storage_1.isTokenExpired)(tokens),
|
|
222
|
-
});
|
|
223
|
-
// If tokens are expired, try to refresh them
|
|
224
|
-
if ((0, token_storage_1.isTokenExpired)(tokens)) {
|
|
225
|
-
logger_1.logger.info('Stored tokens are expired, attempting refresh...');
|
|
226
|
-
const refreshed = await this.refreshStoredTokens(tokens);
|
|
227
|
-
if (!refreshed) {
|
|
228
|
-
logger_1.logger.warn('Token refresh failed - user needs to re-authenticate');
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
// Use the stored tokens
|
|
233
|
-
this.storedTokens = tokens;
|
|
234
|
-
this.currentUserId = tokens.userId;
|
|
235
|
-
this.currentEmail = tokens.email;
|
|
236
|
-
this.authenticated = true;
|
|
237
|
-
logger_1.logger.info('Authenticated with stored OAuth tokens', {
|
|
238
|
-
userId: this.currentUserId,
|
|
239
|
-
email: this.currentEmail,
|
|
240
|
-
});
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
catch (error) {
|
|
244
|
-
logger_1.logger.error('Failed to authenticate with stored tokens:', error);
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Refresh expired tokens using the refresh token
|
|
250
|
-
*/
|
|
251
|
-
async refreshStoredTokens(tokens) {
|
|
252
|
-
try {
|
|
253
|
-
const tokenUrl = `https://${config_1.config.aws.cognitoDomain}/oauth2/token`;
|
|
254
|
-
const params = new URLSearchParams({
|
|
255
|
-
grant_type: 'refresh_token',
|
|
256
|
-
client_id: config_1.config.aws.cognitoClientId,
|
|
257
|
-
refresh_token: tokens.refreshToken,
|
|
258
|
-
});
|
|
259
|
-
const response = await fetch(tokenUrl, {
|
|
260
|
-
method: 'POST',
|
|
261
|
-
headers: {
|
|
262
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
263
|
-
},
|
|
264
|
-
body: params.toString(),
|
|
265
|
-
});
|
|
266
|
-
if (!response.ok) {
|
|
267
|
-
const errorText = await response.text();
|
|
268
|
-
logger_1.logger.error('Token refresh failed:', { status: response.status, error: errorText });
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
const data = await response.json();
|
|
272
|
-
// Update stored tokens
|
|
273
|
-
const updatedTokens = {
|
|
274
|
-
...tokens,
|
|
275
|
-
accessToken: data.access_token,
|
|
276
|
-
idToken: data.id_token,
|
|
277
|
-
expiresAt: Date.now() + (data.expires_in * 1000),
|
|
278
|
-
};
|
|
279
|
-
(0, token_storage_1.saveTokens)(updatedTokens);
|
|
280
|
-
this.storedTokens = updatedTokens;
|
|
281
|
-
logger_1.logger.info('Tokens refreshed successfully', {
|
|
282
|
-
expiresAt: new Date(updatedTokens.expiresAt).toISOString(),
|
|
283
|
-
});
|
|
284
|
-
return true;
|
|
285
|
-
}
|
|
286
|
-
catch (error) {
|
|
287
|
-
logger_1.logger.error('Token refresh error:', error);
|
|
288
|
-
return false;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Check if stored tokens exist and are valid
|
|
293
|
-
*/
|
|
294
|
-
hasValidStoredTokens() {
|
|
295
|
-
const tokens = (0, token_storage_1.loadTokens)();
|
|
296
|
-
return tokens !== null && !(0, token_storage_1.isTokenExpired)(tokens);
|
|
297
|
-
}
|
|
298
|
-
// Sign out - clears local state (token removal handled by auth-cli)
|
|
299
|
-
signOutUser() {
|
|
300
|
-
this.authenticated = false;
|
|
301
|
-
this.storedTokens = null;
|
|
302
|
-
this.currentUserId = null;
|
|
303
|
-
this.currentEmail = null;
|
|
304
|
-
logger_1.logger.info('Signed out successfully');
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Make a direct GraphQL request to AppSync using fetch
|
|
308
|
-
* Uses stored OAuth tokens for Cognito User Pool authentication
|
|
309
|
-
* Automatically refreshes token and retries on 401 errors
|
|
310
|
-
*/
|
|
311
|
-
async graphqlRequest(query, variables, isRetry = false) {
|
|
312
|
-
const headers = {
|
|
313
|
-
'Content-Type': 'application/json',
|
|
314
|
-
};
|
|
315
|
-
// Add authorization header using stored OAuth tokens
|
|
316
|
-
if (this.storedTokens?.idToken) {
|
|
317
|
-
// Cognito User Pool expects raw JWT in Authorization header (no Bearer prefix)
|
|
318
|
-
headers['Authorization'] = this.storedTokens.idToken;
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
throw new Error('No valid auth tokens available. Run "codevibe-claude login" first.');
|
|
322
|
-
}
|
|
323
|
-
logger_1.logger.debug('Making GraphQL request', {
|
|
324
|
-
hasAuth: !!headers['Authorization'],
|
|
325
|
-
isRetry,
|
|
326
|
-
});
|
|
327
|
-
const response = await fetch(config_1.config.aws.appsyncUrl, {
|
|
328
|
-
method: 'POST',
|
|
329
|
-
headers,
|
|
330
|
-
body: JSON.stringify({ query, variables }),
|
|
331
|
-
});
|
|
332
|
-
const result = await response.json();
|
|
333
|
-
// Check for 401 Unauthorized - try to refresh token and retry once
|
|
334
|
-
if (response.status === 401 && !isRetry && this.storedTokens) {
|
|
335
|
-
logger_1.logger.info('Received 401 Unauthorized, attempting token refresh...');
|
|
336
|
-
const refreshed = await this.refreshStoredTokens(this.storedTokens);
|
|
337
|
-
if (refreshed) {
|
|
338
|
-
logger_1.logger.info('Token refreshed successfully, retrying request...');
|
|
339
|
-
return this.graphqlRequest(query, variables, true);
|
|
340
|
-
}
|
|
341
|
-
else {
|
|
342
|
-
logger_1.logger.error('Token refresh failed - user needs to re-authenticate with "codevibe-claude login"');
|
|
343
|
-
throw new Error('Token expired and refresh failed. Run "codevibe-claude login" to re-authenticate.');
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
if (!response.ok) {
|
|
347
|
-
logger_1.logger.error('GraphQL request failed', { status: response.status, errors: result.errors });
|
|
348
|
-
throw new Error(`GraphQL request failed: ${response.status}`);
|
|
349
|
-
}
|
|
350
|
-
if (result.errors && result.errors.length > 0) {
|
|
351
|
-
logger_1.logger.error('GraphQL errors', { errors: result.errors });
|
|
352
|
-
throw new Error(`GraphQL error: ${result.errors[0].message}`);
|
|
353
|
-
}
|
|
354
|
-
return result;
|
|
355
|
-
}
|
|
356
|
-
// Create a new session
|
|
357
|
-
async createSession(input) {
|
|
358
|
-
try {
|
|
359
|
-
logger_1.logger.debug('Creating session', input);
|
|
360
|
-
// Stringify metadata for AWSJSON type
|
|
361
|
-
const preparedInput = {
|
|
362
|
-
...input,
|
|
363
|
-
metadata: input.metadata ? JSON.stringify(input.metadata) : undefined,
|
|
364
|
-
};
|
|
365
|
-
const response = await this.graphqlRequest(createSessionMutation, { input: preparedInput });
|
|
366
|
-
const session = response.data.createSession;
|
|
367
|
-
logger_1.logger.info('Session created', { sessionId: session.sessionId });
|
|
368
|
-
return session;
|
|
369
|
-
}
|
|
370
|
-
catch (error) {
|
|
371
|
-
logger_1.logger.error('Failed to create session:', error);
|
|
372
|
-
throw error;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// Update an existing session
|
|
376
|
-
async updateSession(input) {
|
|
377
|
-
try {
|
|
378
|
-
logger_1.logger.debug('Updating session', input);
|
|
379
|
-
// Stringify metadata for AWSJSON type
|
|
380
|
-
const preparedInput = {
|
|
381
|
-
...input,
|
|
382
|
-
metadata: input.metadata ? JSON.stringify(input.metadata) : undefined,
|
|
383
|
-
};
|
|
384
|
-
const response = await this.graphqlRequest(updateSessionMutation, { input: preparedInput });
|
|
385
|
-
const session = response.data.updateSession;
|
|
386
|
-
logger_1.logger.info('Session updated', { sessionId: session.sessionId });
|
|
387
|
-
return session;
|
|
388
|
-
}
|
|
389
|
-
catch (error) {
|
|
390
|
-
logger_1.logger.error('Failed to update session:', error);
|
|
391
|
-
throw error;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
// Create an event
|
|
395
|
-
async createEvent(input) {
|
|
396
|
-
try {
|
|
397
|
-
logger_1.logger.debug('Creating event', {
|
|
398
|
-
sessionId: input.sessionId,
|
|
399
|
-
type: input.type,
|
|
400
|
-
source: input.source,
|
|
401
|
-
});
|
|
402
|
-
// Stringify metadata for AWSJSON type
|
|
403
|
-
const preparedInput = {
|
|
404
|
-
...input,
|
|
405
|
-
metadata: input.metadata ? JSON.stringify(input.metadata) : undefined,
|
|
406
|
-
};
|
|
407
|
-
const response = await this.graphqlRequest(createEventMutation, { input: preparedInput });
|
|
408
|
-
const event = response.data.createEvent;
|
|
409
|
-
logger_1.logger.info('Event created', {
|
|
410
|
-
eventId: event.eventId,
|
|
411
|
-
sessionId: event.sessionId,
|
|
412
|
-
type: event.type,
|
|
413
|
-
});
|
|
414
|
-
return event;
|
|
415
|
-
}
|
|
416
|
-
catch (error) {
|
|
417
|
-
logger_1.logger.error('Failed to create event:', error);
|
|
418
|
-
throw error;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
// Update event delivery status (for double checkmark feature)
|
|
422
|
-
async updateEventStatus(input) {
|
|
423
|
-
try {
|
|
424
|
-
logger_1.logger.debug('Updating event status', {
|
|
425
|
-
eventId: input.eventId,
|
|
426
|
-
sessionId: input.sessionId,
|
|
427
|
-
deliveryStatus: input.deliveryStatus,
|
|
428
|
-
});
|
|
429
|
-
const response = await this.graphqlRequest(updateEventStatusMutation, { input });
|
|
430
|
-
const event = response.data.updateEventStatus;
|
|
431
|
-
logger_1.logger.info('Event status updated', {
|
|
432
|
-
eventId: event.eventId,
|
|
433
|
-
deliveryStatus: event.deliveryStatus,
|
|
434
|
-
});
|
|
435
|
-
return event;
|
|
436
|
-
}
|
|
437
|
-
catch (error) {
|
|
438
|
-
logger_1.logger.error('Failed to update event status:', error);
|
|
439
|
-
throw error;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
// Create a file change
|
|
443
|
-
async createFileChange(input) {
|
|
444
|
-
try {
|
|
445
|
-
logger_1.logger.debug('Creating file change', {
|
|
446
|
-
sessionId: input.sessionId,
|
|
447
|
-
filePath: input.filePath,
|
|
448
|
-
action: input.action,
|
|
449
|
-
});
|
|
450
|
-
// Stringify metadata for AWSJSON type
|
|
451
|
-
const preparedInput = {
|
|
452
|
-
...input,
|
|
453
|
-
metadata: input.metadata ? JSON.stringify(input.metadata) : undefined,
|
|
454
|
-
};
|
|
455
|
-
const response = await this.graphqlRequest(createFileChangeMutation, { input: preparedInput });
|
|
456
|
-
const fileChange = response.data.createFileChange;
|
|
457
|
-
logger_1.logger.info('File change created', {
|
|
458
|
-
changeId: fileChange.changeId,
|
|
459
|
-
filePath: fileChange.filePath,
|
|
460
|
-
});
|
|
461
|
-
return fileChange;
|
|
462
|
-
}
|
|
463
|
-
catch (error) {
|
|
464
|
-
logger_1.logger.error('Failed to create file change:', error);
|
|
465
|
-
throw error;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
// Get a session
|
|
469
|
-
async getSession(sessionId) {
|
|
470
|
-
try {
|
|
471
|
-
logger_1.logger.debug('Getting session', { sessionId });
|
|
472
|
-
const response = await this.graphqlRequest(getSessionQuery, { sessionId });
|
|
473
|
-
return response.data.getSession;
|
|
474
|
-
}
|
|
475
|
-
catch (error) {
|
|
476
|
-
logger_1.logger.error('Failed to get session:', error);
|
|
477
|
-
throw error;
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
// List events for a session
|
|
481
|
-
async listEvents(sessionId, source) {
|
|
482
|
-
try {
|
|
483
|
-
logger_1.logger.debug('Listing events', { sessionId, source });
|
|
484
|
-
const response = await this.graphqlRequest(listEventsQuery, { sessionId, source });
|
|
485
|
-
return response.data.listEvents.items;
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
logger_1.logger.error('Failed to list events:', error);
|
|
489
|
-
throw error;
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
// Subscribe to events for a session with automatic reconnection
|
|
493
|
-
subscribeToEvents(sessionId, onEvent, onError) {
|
|
494
|
-
logger_1.logger.info('Subscribing to events', { sessionId });
|
|
495
|
-
// Clean up existing subscription for this sessionId if any
|
|
496
|
-
const existingState = this.activeSubscriptions.get(sessionId);
|
|
497
|
-
if (existingState) {
|
|
498
|
-
logger_1.logger.info('Cleaning up existing subscription before creating new one', { sessionId });
|
|
499
|
-
this.cleanupSubscriptionState(existingState);
|
|
500
|
-
this.activeSubscriptions.delete(sessionId);
|
|
501
|
-
}
|
|
502
|
-
// Create subscription state
|
|
503
|
-
const state = {
|
|
504
|
-
ws: null,
|
|
505
|
-
subscriptionId: (0, uuid_1.v4)(),
|
|
506
|
-
sessionId,
|
|
507
|
-
onEvent,
|
|
508
|
-
onError,
|
|
509
|
-
reconnectAttempts: 0,
|
|
510
|
-
isReconnecting: false,
|
|
511
|
-
};
|
|
512
|
-
// Store state before creating subscription
|
|
513
|
-
this.activeSubscriptions.set(sessionId, state);
|
|
514
|
-
// Create the actual subscription
|
|
515
|
-
this.createSubscription(state);
|
|
516
|
-
// Return unsubscribe function
|
|
517
|
-
return () => {
|
|
518
|
-
logger_1.logger.info('Unsubscribing from events', { sessionId });
|
|
519
|
-
this.cleanupSubscriptionState(state);
|
|
520
|
-
this.activeSubscriptions.delete(sessionId);
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
/**
|
|
524
|
-
* Build the AppSync real-time WebSocket URL with authorization
|
|
525
|
-
*/
|
|
526
|
-
buildRealtimeUrl() {
|
|
527
|
-
// Convert AppSync URL to real-time URL
|
|
528
|
-
// https://xxx.appsync-api.region.amazonaws.com/graphql -> wss://xxx.appsync-realtime-api.region.amazonaws.com/graphql
|
|
529
|
-
const realtimeUrl = config_1.config.aws.appsyncUrl
|
|
530
|
-
.replace('https://', 'wss://')
|
|
531
|
-
.replace('appsync-api', 'appsync-realtime-api');
|
|
532
|
-
// Build authorization header using stored OAuth token
|
|
533
|
-
const authHeader = {
|
|
534
|
-
host: new URL(config_1.config.aws.appsyncUrl).host,
|
|
535
|
-
};
|
|
536
|
-
if (this.storedTokens?.idToken) {
|
|
537
|
-
authHeader['Authorization'] = this.storedTokens.idToken;
|
|
538
|
-
}
|
|
539
|
-
// Encode headers as base64 for URL
|
|
540
|
-
const headerBase64 = Buffer.from(JSON.stringify(authHeader)).toString('base64');
|
|
541
|
-
const payloadBase64 = Buffer.from(JSON.stringify({})).toString('base64');
|
|
542
|
-
return `${realtimeUrl}?header=${headerBase64}&payload=${payloadBase64}`;
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Create a custom WebSocket subscription to AppSync
|
|
546
|
-
* This bypasses Amplify which doesn't work with externally stored tokens
|
|
547
|
-
*/
|
|
548
|
-
createSubscription(state) {
|
|
549
|
-
const { sessionId, subscriptionId, onEvent, onError } = state;
|
|
550
|
-
try {
|
|
551
|
-
const wsUrl = this.buildRealtimeUrl();
|
|
552
|
-
logger_1.logger.info('Creating WebSocket subscription', { sessionId, subscriptionId });
|
|
553
|
-
const ws = new ws_1.default(wsUrl, ['graphql-ws']);
|
|
554
|
-
ws.on('open', () => {
|
|
555
|
-
logger_1.logger.info('WebSocket connected, sending connection_init', { sessionId });
|
|
556
|
-
// Send connection_init
|
|
557
|
-
ws.send(JSON.stringify({
|
|
558
|
-
type: 'connection_init',
|
|
559
|
-
}));
|
|
560
|
-
});
|
|
561
|
-
ws.on('message', (data) => {
|
|
562
|
-
try {
|
|
563
|
-
const message = JSON.parse(data.toString());
|
|
564
|
-
switch (message.type) {
|
|
565
|
-
case 'connection_ack':
|
|
566
|
-
logger_1.logger.info('WebSocket connection acknowledged', {
|
|
567
|
-
sessionId,
|
|
568
|
-
connectionTimeout: message.payload?.connectionTimeoutMs
|
|
569
|
-
});
|
|
570
|
-
// Start the subscription
|
|
571
|
-
this.sendSubscriptionStart(ws, state);
|
|
572
|
-
break;
|
|
573
|
-
case 'start_ack':
|
|
574
|
-
logger_1.logger.info('Subscription started successfully', { sessionId, subscriptionId });
|
|
575
|
-
state.isReconnecting = false;
|
|
576
|
-
if (state.reconnectAttempts > 0) {
|
|
577
|
-
logger_1.logger.info('Subscription reconnected successfully', {
|
|
578
|
-
sessionId,
|
|
579
|
-
afterAttempts: state.reconnectAttempts
|
|
580
|
-
});
|
|
581
|
-
state.reconnectAttempts = 0;
|
|
582
|
-
}
|
|
583
|
-
break;
|
|
584
|
-
case 'data':
|
|
585
|
-
// Reset keep-alive timer on data
|
|
586
|
-
this.resetKeepAliveTimer(state);
|
|
587
|
-
const event = message.payload?.data?.onEventCreated;
|
|
588
|
-
if (event) {
|
|
589
|
-
// DEBUG: Log full event to diagnose attachment.isEncrypted issue
|
|
590
|
-
if (event.attachments && event.attachments.length > 0) {
|
|
591
|
-
logger_1.logger.info('DEBUG: Raw subscription payload for event with attachments', {
|
|
592
|
-
eventId: event.eventId,
|
|
593
|
-
fullPayload: JSON.stringify(message.payload?.data?.onEventCreated),
|
|
594
|
-
});
|
|
595
|
-
}
|
|
596
|
-
logger_1.logger.debug('Event received from subscription', {
|
|
597
|
-
eventId: event.eventId,
|
|
598
|
-
type: event.type,
|
|
599
|
-
source: event.source,
|
|
600
|
-
});
|
|
601
|
-
// Filter out desktop events (we only want mobile events)
|
|
602
|
-
if (event.source === types_1.EventSource.MOBILE) {
|
|
603
|
-
onEvent(event);
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
break;
|
|
607
|
-
case 'ka':
|
|
608
|
-
// Keep-alive message - reset timer
|
|
609
|
-
this.resetKeepAliveTimer(state);
|
|
610
|
-
logger_1.logger.debug('Keep-alive received', { sessionId });
|
|
611
|
-
break;
|
|
612
|
-
case 'error':
|
|
613
|
-
logger_1.logger.error('Subscription error from server', {
|
|
614
|
-
sessionId,
|
|
615
|
-
errors: message.payload?.errors
|
|
616
|
-
});
|
|
617
|
-
const errorMsg = message.payload?.errors?.[0]?.message || 'Unknown subscription error';
|
|
618
|
-
this.handleSubscriptionError(state, new Error(errorMsg));
|
|
619
|
-
break;
|
|
620
|
-
case 'complete':
|
|
621
|
-
logger_1.logger.info('Subscription completed by server', { sessionId });
|
|
622
|
-
break;
|
|
623
|
-
default:
|
|
624
|
-
logger_1.logger.debug('Unknown WebSocket message type', { type: message.type, sessionId });
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
catch (parseError) {
|
|
628
|
-
logger_1.logger.error('Failed to parse WebSocket message', { error: parseError, data: data.toString() });
|
|
629
|
-
}
|
|
630
|
-
});
|
|
631
|
-
ws.on('error', (error) => {
|
|
632
|
-
logger_1.logger.error('WebSocket error:', { sessionId, error: error.message });
|
|
633
|
-
this.handleSubscriptionError(state, error);
|
|
634
|
-
});
|
|
635
|
-
ws.on('close', (code, reason) => {
|
|
636
|
-
logger_1.logger.info('WebSocket closed', {
|
|
637
|
-
sessionId,
|
|
638
|
-
code,
|
|
639
|
-
reason: reason.toString()
|
|
640
|
-
});
|
|
641
|
-
// Clear keep-alive timer
|
|
642
|
-
if (state.keepAliveTimer) {
|
|
643
|
-
clearTimeout(state.keepAliveTimer);
|
|
644
|
-
state.keepAliveTimer = undefined;
|
|
645
|
-
}
|
|
646
|
-
// Attempt reconnection if not intentionally closed
|
|
647
|
-
if (code !== 1000 && this.activeSubscriptions.has(sessionId)) {
|
|
648
|
-
this.handleSubscriptionError(state, new Error(`WebSocket closed: ${code} ${reason.toString()}`));
|
|
649
|
-
}
|
|
650
|
-
});
|
|
651
|
-
state.ws = ws;
|
|
652
|
-
// Set initial keep-alive timer (AppSync sends ka every ~240 seconds)
|
|
653
|
-
this.resetKeepAliveTimer(state);
|
|
654
|
-
}
|
|
655
|
-
catch (error) {
|
|
656
|
-
logger_1.logger.error('Failed to create WebSocket subscription:', { sessionId, error });
|
|
657
|
-
this.handleSubscriptionError(state, error);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* Send subscription start message
|
|
662
|
-
*/
|
|
663
|
-
sendSubscriptionStart(ws, state) {
|
|
664
|
-
const { sessionId, subscriptionId } = state;
|
|
665
|
-
// Build authorization for the subscription using stored OAuth token
|
|
666
|
-
const authHeader = {
|
|
667
|
-
host: new URL(config_1.config.aws.appsyncUrl).host,
|
|
668
|
-
};
|
|
669
|
-
if (this.storedTokens?.idToken) {
|
|
670
|
-
authHeader['Authorization'] = this.storedTokens.idToken;
|
|
671
|
-
}
|
|
672
|
-
const startMessage = {
|
|
673
|
-
id: subscriptionId,
|
|
674
|
-
type: 'start',
|
|
675
|
-
payload: {
|
|
676
|
-
data: JSON.stringify({
|
|
677
|
-
query: onEventCreatedSubscription,
|
|
678
|
-
variables: { sessionId },
|
|
679
|
-
}),
|
|
680
|
-
extensions: {
|
|
681
|
-
authorization: authHeader,
|
|
682
|
-
},
|
|
683
|
-
},
|
|
684
|
-
};
|
|
685
|
-
logger_1.logger.debug('Sending subscription start', { sessionId, subscriptionId });
|
|
686
|
-
ws.send(JSON.stringify(startMessage));
|
|
687
|
-
}
|
|
688
|
-
/**
|
|
689
|
-
* Reset the keep-alive timer - if no message received in 5 minutes, reconnect
|
|
690
|
-
*/
|
|
691
|
-
resetKeepAliveTimer(state) {
|
|
692
|
-
if (state.keepAliveTimer) {
|
|
693
|
-
clearTimeout(state.keepAliveTimer);
|
|
694
|
-
}
|
|
695
|
-
// AppSync sends keep-alive every ~240 seconds, timeout after 5 minutes
|
|
696
|
-
state.keepAliveTimer = setTimeout(() => {
|
|
697
|
-
logger_1.logger.warn('Keep-alive timeout, reconnecting subscription', { sessionId: state.sessionId });
|
|
698
|
-
this.handleSubscriptionError(state, new Error('Keep-alive timeout'));
|
|
699
|
-
}, 5 * 60 * 1000);
|
|
700
|
-
}
|
|
701
|
-
// Handle subscription errors with reconnection logic
|
|
702
|
-
handleSubscriptionError(state, error) {
|
|
703
|
-
const { sessionId, onError } = state;
|
|
704
|
-
// Don't reconnect if we're already trying or if session was removed
|
|
705
|
-
if (state.isReconnecting || !this.activeSubscriptions.has(sessionId)) {
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
// Check if we've exceeded max attempts
|
|
709
|
-
if (state.reconnectAttempts >= RECONNECT_CONFIG.maxAttempts) {
|
|
710
|
-
logger_1.logger.error('Max reconnection attempts reached, giving up', {
|
|
711
|
-
sessionId,
|
|
712
|
-
attempts: state.reconnectAttempts
|
|
713
|
-
});
|
|
714
|
-
if (onError) {
|
|
715
|
-
onError(new Error(`Subscription failed after ${state.reconnectAttempts} reconnection attempts: ${error.message}`));
|
|
716
|
-
}
|
|
717
|
-
return;
|
|
718
|
-
}
|
|
719
|
-
state.isReconnecting = true;
|
|
720
|
-
state.reconnectAttempts++;
|
|
721
|
-
// Calculate delay with exponential backoff
|
|
722
|
-
const delay = Math.min(RECONNECT_CONFIG.baseDelayMs * Math.pow(RECONNECT_CONFIG.backoffMultiplier, state.reconnectAttempts - 1), RECONNECT_CONFIG.maxDelayMs);
|
|
723
|
-
logger_1.logger.info('Scheduling subscription reconnection', {
|
|
724
|
-
sessionId,
|
|
725
|
-
attempt: state.reconnectAttempts,
|
|
726
|
-
maxAttempts: RECONNECT_CONFIG.maxAttempts,
|
|
727
|
-
delayMs: delay
|
|
728
|
-
});
|
|
729
|
-
// Clean up old WebSocket if it exists
|
|
730
|
-
if (state.ws) {
|
|
731
|
-
try {
|
|
732
|
-
state.ws.close(1000, 'Reconnecting');
|
|
733
|
-
}
|
|
734
|
-
catch (e) {
|
|
735
|
-
// Ignore errors when closing failed WebSocket
|
|
736
|
-
}
|
|
737
|
-
state.ws = null;
|
|
738
|
-
}
|
|
739
|
-
// Clear keep-alive timer
|
|
740
|
-
if (state.keepAliveTimer) {
|
|
741
|
-
clearTimeout(state.keepAliveTimer);
|
|
742
|
-
state.keepAliveTimer = undefined;
|
|
743
|
-
}
|
|
744
|
-
// Schedule reconnection
|
|
745
|
-
state.reconnectTimer = setTimeout(() => {
|
|
746
|
-
// Verify session still exists before reconnecting
|
|
747
|
-
if (this.activeSubscriptions.has(sessionId)) {
|
|
748
|
-
logger_1.logger.info('Attempting subscription reconnection', {
|
|
749
|
-
sessionId,
|
|
750
|
-
attempt: state.reconnectAttempts
|
|
751
|
-
});
|
|
752
|
-
// Generate new subscription ID for reconnection
|
|
753
|
-
state.subscriptionId = (0, uuid_1.v4)();
|
|
754
|
-
this.createSubscription(state);
|
|
755
|
-
}
|
|
756
|
-
}, delay);
|
|
757
|
-
}
|
|
758
|
-
// Clean up subscription state
|
|
759
|
-
cleanupSubscriptionState(state) {
|
|
760
|
-
// Clear any pending reconnection timer
|
|
761
|
-
if (state.reconnectTimer) {
|
|
762
|
-
clearTimeout(state.reconnectTimer);
|
|
763
|
-
state.reconnectTimer = undefined;
|
|
764
|
-
}
|
|
765
|
-
// Clear keep-alive timer
|
|
766
|
-
if (state.keepAliveTimer) {
|
|
767
|
-
clearTimeout(state.keepAliveTimer);
|
|
768
|
-
state.keepAliveTimer = undefined;
|
|
769
|
-
}
|
|
770
|
-
// Close WebSocket if active
|
|
771
|
-
if (state.ws) {
|
|
772
|
-
try {
|
|
773
|
-
// Send stop message for the subscription before closing
|
|
774
|
-
if (state.ws.readyState === ws_1.default.OPEN) {
|
|
775
|
-
state.ws.send(JSON.stringify({
|
|
776
|
-
id: state.subscriptionId,
|
|
777
|
-
type: 'stop',
|
|
778
|
-
}));
|
|
779
|
-
state.ws.close(1000, 'Unsubscribing');
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
catch (e) {
|
|
783
|
-
// Ignore errors
|
|
784
|
-
}
|
|
785
|
-
state.ws = null;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
// Cleanup all subscriptions
|
|
789
|
-
cleanupSubscriptions() {
|
|
790
|
-
logger_1.logger.info('Cleaning up all subscriptions', {
|
|
791
|
-
count: this.activeSubscriptions.size,
|
|
792
|
-
});
|
|
793
|
-
this.activeSubscriptions.forEach((state, sessionId) => {
|
|
794
|
-
logger_1.logger.debug('Unsubscribing from session', { sessionId });
|
|
795
|
-
this.cleanupSubscriptionState(state);
|
|
796
|
-
});
|
|
797
|
-
this.activeSubscriptions.clear();
|
|
798
|
-
}
|
|
799
|
-
// Get pre-signed download URL for an attachment
|
|
800
|
-
async getAttachmentDownloadUrl(s3Key) {
|
|
801
|
-
try {
|
|
802
|
-
logger_1.logger.debug('Getting attachment download URL', { s3Key });
|
|
803
|
-
const response = await this.graphqlRequest(getAttachmentDownloadUrlMutation, { s3Key });
|
|
804
|
-
const result = response.data.getAttachmentDownloadUrl;
|
|
805
|
-
logger_1.logger.info('Got attachment download URL', {
|
|
806
|
-
s3Key,
|
|
807
|
-
expiresAt: result.expiresAt,
|
|
808
|
-
});
|
|
809
|
-
return result;
|
|
810
|
-
}
|
|
811
|
-
catch (error) {
|
|
812
|
-
logger_1.logger.error('Failed to get attachment download URL:', error);
|
|
813
|
-
throw error;
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
// List all device keys for the current user (for E2E encryption)
|
|
817
|
-
async listUserDeviceKeys() {
|
|
818
|
-
try {
|
|
819
|
-
logger_1.logger.debug('Listing user device keys');
|
|
820
|
-
const response = await this.graphqlRequest(listUserDeviceKeysQuery, {});
|
|
821
|
-
// listUserDeviceKeys returns [DeviceKey!]! directly, not a connection type
|
|
822
|
-
const items = response.data.listUserDeviceKeys || [];
|
|
823
|
-
logger_1.logger.info('Listed user device keys', { count: items.length });
|
|
824
|
-
return items.map((item) => ({
|
|
825
|
-
deviceId: item.deviceId,
|
|
826
|
-
publicKey: item.publicKey,
|
|
827
|
-
}));
|
|
828
|
-
}
|
|
829
|
-
catch (error) {
|
|
830
|
-
logger_1.logger.error('Failed to list user device keys:', error);
|
|
831
|
-
throw error;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
// Register device key for E2E encryption
|
|
835
|
-
async registerDeviceKey(deviceId, publicKey, platform, deviceName) {
|
|
836
|
-
try {
|
|
837
|
-
logger_1.logger.debug('Registering device key', { deviceId, platform, deviceName });
|
|
838
|
-
const input = {
|
|
839
|
-
deviceId,
|
|
840
|
-
publicKey,
|
|
841
|
-
platform,
|
|
842
|
-
deviceName,
|
|
843
|
-
};
|
|
844
|
-
await this.graphqlRequest(registerDeviceKeyMutation, { input });
|
|
845
|
-
logger_1.logger.info('Device key registered', { deviceId, platform, deviceName });
|
|
846
|
-
}
|
|
847
|
-
catch (error) {
|
|
848
|
-
logger_1.logger.error('Failed to register device key:', error);
|
|
849
|
-
throw error;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
// Check if authenticated
|
|
853
|
-
isAuthenticated() {
|
|
854
|
-
return this.authenticated;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
exports.AppSyncClient = AppSyncClient;
|
|
858
|
-
//# sourceMappingURL=appsync-client.js.map
|