@paytaca/opencode-plugin 0.1.13 → 0.1.15

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.
@@ -45,37 +45,6 @@ function log(message) {
45
45
  logStream.write(timestamp + ' [Proxy] ' + message + '\\n');
46
46
  }
47
47
 
48
- // Heartbeat monitoring - proxy exits if heartbeat is stale
49
- const HEARTBEAT_FILE = path.join(LOG_DIR, 'heartbeat');
50
- const HEARTBEAT_TIMEOUT = 300000; // 5 minutes
51
-
52
- function checkHeartbeat() {
53
- try {
54
- if (!fs.existsSync(HEARTBEAT_FILE)) {
55
- // No heartbeat file yet, wait a bit
56
- return true;
57
- }
58
- const heartbeat = parseInt(fs.readFileSync(HEARTBEAT_FILE, 'utf8'));
59
- if (heartbeat === 0) {
60
- // Special value: plugin is stopping
61
- log('Heartbeat = 0, shutting down...');
62
- return false;
63
- }
64
- const elapsed = Date.now() - heartbeat;
65
- if (elapsed > HEARTBEAT_TIMEOUT) {
66
- log('Heartbeat stale (' + elapsed + 'ms), shutting down...');
67
- return false;
68
- }
69
- return true;
70
- } catch (err) {
71
- // If we can't read heartbeat, keep running (graceful degradation)
72
- return true;
73
- }
74
- }
75
-
76
- // Heartbeat checker reference (will be started after server creation)
77
- let heartbeatChecker = null;
78
-
79
48
  // Store pending payment requests per wallet hash
80
49
  // Each entry: { body, modelId, displayName, durationMinutes, tiers[], step }
81
50
  // step: 'tier_select' (user must pick a tier) or 'approval' (yes/no)
@@ -172,22 +141,19 @@ function sseDone(res) {
172
141
  res.write('data: [DONE]\\n\\n');
173
142
  }
174
143
 
175
- // Build and stream SSE tier selection prompt
176
- async function streamTierSelectionPrompt(res, walletHash, modelName, tiers) {
177
- res.writeHead(200, {
178
- 'Content-Type': 'text/event-stream',
179
- 'Cache-Control': 'no-cache',
180
- 'X-Payment-Required': 'true',
181
- 'Connection': 'keep-alive',
182
- });
183
-
184
- sseLine(res, {
185
- id: 'tier-1',
186
- object: 'chat.completion.chunk',
187
- created: Math.floor(Date.now() / 1000),
188
- model: modelName,
189
- choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }],
190
- });
144
+ // Stream the tier-selection prompt body (SSE lines) into an in-progress response.
145
+ // When includeRole is false the leading role delta is skipped, so the body can be
146
+ // appended to a stream that already emitted content (e.g. after a payment failure).
147
+ async function streamTierSelectionBody(res, walletHash, modelName, tiers, includeRole) {
148
+ if (includeRole !== false) {
149
+ sseLine(res, {
150
+ id: 'tier-1',
151
+ object: 'chat.completion.chunk',
152
+ created: Math.floor(Date.now() / 1000),
153
+ model: modelName,
154
+ choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }],
155
+ });
156
+ }
191
157
 
192
158
  // Loading sequence
193
159
  sseLine(res, {
@@ -275,7 +241,19 @@ async function streamTierSelectionPrompt(res, walletHash, modelName, tiers) {
275
241
  choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
276
242
  usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
277
243
  });
244
+ }
278
245
 
246
+ // Build and stream a full tier-selection prompt (headers + body + [DONE]) to the client.
247
+ async function streamTierSelectionPrompt(res, walletHash, modelName, tiers) {
248
+ if (!res.headersSent) {
249
+ res.writeHead(200, {
250
+ 'Content-Type': 'text/event-stream',
251
+ 'Cache-Control': 'no-cache',
252
+ 'X-Payment-Required': 'true',
253
+ 'Connection': 'keep-alive',
254
+ });
255
+ }
256
+ await streamTierSelectionBody(res, walletHash, modelName, tiers, true);
279
257
  sseDone(res);
280
258
  res.end();
281
259
  }
@@ -567,8 +545,15 @@ function forceNonStreaming(body) {
567
545
  }
568
546
 
569
547
  // Convert a chat.completion JSON object to SSE format
570
- function jsonToSse(res, chatCompletion) {
571
- const content = chatCompletion.choices?.[0]?.message?.content || '';
548
+ function jsonToSse(res, chatCompletion, opts) {
549
+ opts = opts || {};
550
+ if (res.destroyed || res.writableEnded) {
551
+ log('jsonToSse: response already destroyed/ended, cannot send SSE');
552
+ return;
553
+ }
554
+ const message = chatCompletion.choices?.[0]?.message || {};
555
+ const content = message.content || '';
556
+ const toolCalls = Array.isArray(message.tool_calls) ? message.tool_calls : null;
572
557
  const model = chatCompletion.model || chatCompletion.model_id || 'deepseek/deepseek-v4-flash';
573
558
  const created = chatCompletion.created || Math.floor(Date.now() / 1000);
574
559
 
@@ -580,6 +565,7 @@ function jsonToSse(res, chatCompletion) {
580
565
  'Connection': 'keep-alive',
581
566
  });
582
567
  } catch (e) {
568
+ log('jsonToSse writeHead failed: ' + e.message);
583
569
  return;
584
570
  }
585
571
  }
@@ -593,45 +579,115 @@ function jsonToSse(res, chatCompletion) {
593
579
  choices: [{ index: 0, delta: { role: 'assistant' }, finish_reason: null }],
594
580
  });
595
581
  } catch (e) {
582
+ log('jsonToSse: failed to write role delta: ' + e.message);
596
583
  }
597
584
 
585
+ const allContent = (opts.prependContent || '') + content;
598
586
  const chunkSize = 20;
599
587
  let chunksWritten = 0;
600
- for (let i = 0; i < content.length; i += chunkSize) {
588
+ for (let i = 0; i < allContent.length; i += chunkSize) {
601
589
  try {
602
590
  sseLine(res, {
603
591
  id: 'chatcmpl-' + (i + 2),
604
592
  object: 'chat.completion.chunk',
605
593
  created,
606
594
  model,
607
- choices: [{ index: 0, delta: { content: content.slice(i, i + chunkSize) }, finish_reason: null }],
595
+ choices: [{ index: 0, delta: { content: allContent.slice(i, i + chunkSize) }, finish_reason: null }],
608
596
  });
609
597
  chunksWritten++;
610
598
  } catch (e) {
599
+ log('jsonToSse: failed to write content chunk ' + (i / chunkSize) + ': ' + e.message);
611
600
  break;
612
601
  }
613
602
  }
614
603
 
604
+ let finishReason = 'stop';
605
+ if (toolCalls && toolCalls.length > 0) {
606
+ const toolCallDeltas = [];
607
+ for (let i = 0; i < toolCalls.length; i++) {
608
+ const tc = toolCalls[i] || {};
609
+ const fn = tc.function || {};
610
+ let args = fn.arguments;
611
+ if (args !== undefined && typeof args !== 'string') {
612
+ try { args = JSON.stringify(args); } catch (e) { args = String(args); }
613
+ }
614
+ toolCallDeltas.push({
615
+ index: i,
616
+ id: tc.id || ('call_' + i),
617
+ type: 'function',
618
+ function: {
619
+ name: fn.name || '',
620
+ arguments: args === undefined || args === null ? '' : String(args),
621
+ },
622
+ });
623
+ }
624
+ try {
625
+ sseLine(res, {
626
+ id: 'chatcmpl-tools',
627
+ object: 'chat.completion.chunk',
628
+ created,
629
+ model,
630
+ choices: [{ index: 0, delta: { tool_calls: toolCallDeltas }, finish_reason: null }],
631
+ });
632
+ } catch (e) {
633
+ log('jsonToSse: failed to write tool_calls: ' + e.message);
634
+ }
635
+ finishReason = 'tool_calls';
636
+ }
637
+
615
638
  try {
616
639
  sseLine(res, {
617
640
  id: 'chatcmpl-done',
618
641
  object: 'chat.completion.chunk',
619
642
  created,
620
643
  model,
621
- choices: [{ index: 0, delta: {}, finish_reason: 'stop' }],
644
+ choices: [{ index: 0, delta: {}, finish_reason: finishReason }],
622
645
  usage: chatCompletion.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 },
623
646
  });
624
647
  } catch (e) {
648
+ log('jsonToSse: failed to write final delta: ' + e.message);
625
649
  }
626
650
 
627
651
  try {
628
652
  sseDone(res);
629
653
  } catch (e) {
654
+ log('jsonToSse: failed to write [DONE]: ' + e.message);
655
+ }
656
+
657
+ try {
658
+ res.end();
659
+ } catch (e) {
660
+ log('jsonToSse: res.end() failed: ' + e.message);
630
661
  }
662
+ }
631
663
 
664
+ // Stream a payment-failure message, then re-show the tier-selection prompt so the
665
+ // user can retry the same or a different plan without sending another message.
666
+ // The pending payment is restored to the tier-select step so the next tier pick is
667
+ // handled by the proxy instead of being forwarded fresh to Django.
668
+ async function streamPaymentFailureAndRetry(res, walletHash, pendingPayload, message) {
669
+ try {
670
+ sseLine(res, {
671
+ id: 'pay-err',
672
+ object: 'chat.completion.chunk',
673
+ created: Math.floor(Date.now() / 1000),
674
+ model: 'deepseek/deepseek-v4-flash',
675
+ choices: [{ index: 0, delta: { content: message }, finish_reason: 'stop' }],
676
+ });
677
+ } catch (e) {
678
+ }
679
+ pendingPayload.step = 'tier_select';
680
+ pendingPayload.durationMinutes = null;
681
+ pendingPayments.set(walletHash, pendingPayload);
632
682
  try {
683
+ const tiers = Array.isArray(pendingPayload.tiers) ? pendingPayload.tiers : [];
684
+ if (tiers.length > 0) {
685
+ await streamTierSelectionBody(res, walletHash, pendingPayload.displayName || pendingPayload.modelId || 'AI Model', tiers, false);
686
+ }
687
+ sseDone(res);
633
688
  res.end();
634
689
  } catch (e) {
690
+ try { res.end(); } catch (e2) {}
635
691
  }
636
692
  }
637
693
 
@@ -696,7 +752,17 @@ function runPaytacaPay(djangoUrl, body, walletHash, extraHeaders, callback) {
696
752
  callback(new Error('Could not parse paytaca pay response: ' + err.message));
697
753
  }
698
754
  } else {
699
- callback(new Error(stderr.trim() || 'paytaca pay wrapper exited with code ' + code));
755
+ // Try to extract error from stdout (wrapper writes JSON errors to stdout, not stderr)
756
+ let wrapperErr = stderr.trim();
757
+ if (!wrapperErr) {
758
+ try {
759
+ const parsed = JSON.parse(stdout.trim());
760
+ wrapperErr = parsed.error || 'Unknown error';
761
+ } catch {
762
+ wrapperErr = stdout.trim() || 'paytaca pay wrapper exited with code ' + code;
763
+ }
764
+ }
765
+ callback(new Error(wrapperErr));
700
766
  }
701
767
  });
702
768
 
@@ -1062,42 +1128,49 @@ const server = http.createServer(async (req, res) => {
1062
1128
  res.write(': keepalive\\n\\n');
1063
1129
  }, 2000);
1064
1130
 
1065
- runPaytacaPay(BACKEND_URL + '/v1', pendingPayload.body, walletHash, extraHeaders, (err, responseJson) => {
1131
+ runPaytacaPay(BACKEND_URL + '/v1', pendingPayload.body, walletHash, extraHeaders, async (err, responseJson) => {
1066
1132
  pendingPayments.delete(walletHash);
1067
1133
  clearInterval(keepalive);
1068
1134
 
1069
1135
  if (err) {
1070
1136
  log('paytaca pay failed: ' + err.message);
1071
- if (res.headersSent) {
1137
+ if (res.headersSent && !res.destroyed && !res.writableEnded) {
1138
+ await streamPaymentFailureAndRetry(res, walletHash, pendingPayload, '\\n\\n❌ Payment failed: ' + err.message + '\\n\\n');
1139
+ } else if (!res.headersSent) {
1072
1140
  try {
1073
- sseLine(res, { id: 'pay-err', object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model: 'deepseek/deepseek-v4-flash', choices: [{ index: 0, delta: { content: '\\n\\n❌ Payment failed: ' + err.message }, finish_reason: 'stop' }] });
1074
- sseLine(res, { id: 'pay-err-done', object: 'chat.completion.chunk', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] });
1075
- sseDone(res);
1076
- res.end();
1077
- } catch (e) {}
1141
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1142
+ res.end(JSON.stringify({ error: 'Payment failed', message: err.message, details: 'Please check your wallet balance and try again.' }));
1143
+ } catch (e) { log('Failed to send payment error response: ' + e.message); }
1078
1144
  } else {
1079
- res.writeHead(500, { 'Content-Type': 'application/json' });
1080
- res.end(JSON.stringify({ error: 'Payment failed', message: err.message, details: 'Please check your wallet balance and try again.' }));
1145
+ log('Cannot send payment failure — response already ended or destroyed');
1081
1146
  }
1082
1147
  return;
1083
1148
  }
1084
1149
 
1085
1150
  if (!responseJson.success) {
1086
1151
  const isTimeout = responseJson.timeout;
1087
- const sseContent = isTimeout ? '\\n\\n⏱️ Response timed out. Your payment was processed \\u2014 check credits with \\'credits\\' and try again' : '\\n\\n❌ Payment failed: ' + (responseJson.error || 'Unknown error');
1152
+ const sseContent = isTimeout ? '\\n\\n⏱️ Response timed out. Your payment was processed \\u2014 check credits with \\'credits\\' and try again' : '\\n\\n❌ Payment failed: ' + (responseJson.error || 'Unknown error') + '\\n\\n';
1088
1153
  const errLabel = isTimeout ? 'Response timeout' : 'Payment failed';
1089
1154
  const errMsg = isTimeout ? 'Response timed out. Payment was processed.' : responseJson.error;
1090
1155
  const errDetails = isTimeout ? 'Try again or check credits with \\'credits\\'.' : 'Please check your balance and try again.';
1091
- if (res.headersSent) {
1156
+ if (res.headersSent && !res.destroyed && !res.writableEnded) {
1157
+ if (isTimeout) {
1158
+ try {
1159
+ sseLine(res, { id: 'pay-err', object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model: 'deepseek/deepseek-v4-flash', choices: [{ index: 0, delta: { content: sseContent }, finish_reason: 'stop' }] });
1160
+ sseLine(res, { id: 'pay-err-done', object: 'chat.completion.chunk', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] });
1161
+ sseDone(res);
1162
+ res.end();
1163
+ } catch (e) { log('Failed to send timeout error via SSE: ' + e.message); }
1164
+ } else {
1165
+ await streamPaymentFailureAndRetry(res, walletHash, pendingPayload, sseContent);
1166
+ }
1167
+ } else if (!res.headersSent) {
1092
1168
  try {
1093
- sseLine(res, { id: 'pay-err', object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model: 'deepseek/deepseek-v4-flash', choices: [{ index: 0, delta: { content: sseContent }, finish_reason: 'stop' }] });
1094
- sseLine(res, { id: 'pay-err-done', object: 'chat.completion.chunk', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] });
1095
- sseDone(res);
1096
- res.end();
1097
- } catch (e) {}
1169
+ res.writeHead(responseJson.status || 500, { 'Content-Type': 'application/json' });
1170
+ res.end(JSON.stringify({ error: errLabel, message: errMsg, details: errDetails }));
1171
+ } catch (e) { log('Failed to send payment error JSON: ' + e.message); }
1098
1172
  } else {
1099
- res.writeHead(responseJson.status || 500, { 'Content-Type': 'application/json' });
1100
- res.end(JSON.stringify({ error: errLabel, message: errMsg, details: errDetails }));
1173
+ log('Cannot send payment error response already ended or destroyed');
1101
1174
  }
1102
1175
  return;
1103
1176
  }
@@ -1111,15 +1184,22 @@ const server = http.createServer(async (req, res) => {
1111
1184
 
1112
1185
  log('paytaca pay succeeded. Returning chat response.');
1113
1186
 
1187
+ if (res.destroyed || res.writableEnded) {
1188
+ log('Payment succeeded but response connection is gone — cannot deliver chat response');
1189
+ return;
1190
+ }
1191
+
1114
1192
  if (wasStreaming) {
1115
1193
  try {
1116
- jsonToSse(res, chatCompletion);
1117
- } catch (e) {}
1194
+ jsonToSse(res, chatCompletion, { prependContent: '\\n💳 Payment successful — generating your response...\\n\\n' });
1195
+ } catch (e) { log('jsonToSse threw: ' + e.message); }
1118
1196
  } else {
1119
1197
  try {
1120
- res.writeHead(200, { 'Content-Type': 'application/json' });
1198
+ if (!res.headersSent) {
1199
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1200
+ }
1121
1201
  res.end(JSON.stringify(chatCompletion));
1122
- } catch (e) {}
1202
+ } catch (e) { log('Failed to send non-streaming response: ' + e.message); }
1123
1203
  }
1124
1204
  });
1125
1205
  return;
@@ -1161,16 +1241,20 @@ const server = http.createServer(async (req, res) => {
1161
1241
  clearInterval(keepalive);
1162
1242
  if (err) {
1163
1243
  log('paytaca pay failed: ' + err.message);
1164
- if (res.headersSent) {
1244
+ if (res.headersSent && !res.destroyed && !res.writableEnded) {
1165
1245
  try {
1166
1246
  sseLine(res, { id: 'pay-err', object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model: 'deepseek/deepseek-v4-flash', choices: [{ index: 0, delta: { content: '\\n\\n❌ Payment failed: ' + err.message }, finish_reason: 'stop' }] });
1167
1247
  sseLine(res, { id: 'pay-err-done', object: 'chat.completion.chunk', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] });
1168
1248
  sseDone(res);
1169
1249
  res.end();
1170
- } catch (e) {}
1250
+ } catch (e) { log('Failed to send payment error via SSE: ' + e.message); }
1251
+ } else if (!res.headersSent) {
1252
+ try {
1253
+ res.writeHead(500, { 'Content-Type': 'application/json' });
1254
+ res.end(JSON.stringify({ error: 'Payment failed', message: err.message, details: 'Please check your wallet balance and try again.' }));
1255
+ } catch (e) { log('Failed to send payment error JSON: ' + e.message); }
1171
1256
  } else {
1172
- res.writeHead(500, { 'Content-Type': 'application/json' });
1173
- res.end(JSON.stringify({ error: 'Payment failed', message: err.message, details: 'Please check your wallet balance and try again.' }));
1257
+ log('Cannot send payment failure — response already ended or destroyed');
1174
1258
  }
1175
1259
  return;
1176
1260
  }
@@ -1181,16 +1265,20 @@ const server = http.createServer(async (req, res) => {
1181
1265
  const errLabel = isTimeout ? 'Response timeout' : 'Payment failed';
1182
1266
  const errMsg = isTimeout ? 'Response timed out. Payment was processed.' : responseJson.error;
1183
1267
  const errDetails = isTimeout ? 'Try again or check credits with \\'credits\\'.' : 'Please check your balance and try again.';
1184
- if (res.headersSent) {
1268
+ if (res.headersSent && !res.destroyed && !res.writableEnded) {
1185
1269
  try {
1186
1270
  sseLine(res, { id: 'pay-err', object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model: 'deepseek/deepseek-v4-flash', choices: [{ index: 0, delta: { content: sseContent }, finish_reason: 'stop' }] });
1187
1271
  sseLine(res, { id: 'pay-err-done', object: 'chat.completion.chunk', choices: [{ index: 0, delta: {}, finish_reason: 'stop' }] });
1188
1272
  sseDone(res);
1189
1273
  res.end();
1190
- } catch (e) {}
1274
+ } catch (e) { log('Failed to send error via SSE: ' + e.message); }
1275
+ } else if (!res.headersSent) {
1276
+ try {
1277
+ res.writeHead(responseJson.status || 500, { 'Content-Type': 'application/json' });
1278
+ res.end(JSON.stringify({ error: errLabel, message: errMsg, details: errDetails }));
1279
+ } catch (e) { log('Failed to send error JSON: ' + e.message); }
1191
1280
  } else {
1192
- res.writeHead(responseJson.status || 500, { 'Content-Type': 'application/json' });
1193
- res.end(JSON.stringify({ error: errLabel, message: errMsg, details: errDetails }));
1281
+ log('Cannot send payment error response already ended or destroyed');
1194
1282
  }
1195
1283
  return;
1196
1284
  }
@@ -1203,16 +1291,23 @@ const server = http.createServer(async (req, res) => {
1203
1291
  } catch {}
1204
1292
 
1205
1293
  log('paytaca pay succeeded. Returning chat response.');
1294
+
1295
+ if (res.destroyed || res.writableEnded) {
1296
+ log('Payment succeeded but response connection is gone — cannot deliver chat response');
1297
+ return;
1298
+ }
1206
1299
 
1207
1300
  if (wasStreaming) {
1208
1301
  try {
1209
- jsonToSse(res, chatCompletion);
1210
- } catch (e) {}
1302
+ jsonToSse(res, chatCompletion, { prependContent: '\\n💳 Payment successful — generating your response...\\n\\n' });
1303
+ } catch (e) { log('jsonToSse threw: ' + e.message); }
1211
1304
  } else {
1212
1305
  try {
1213
- res.writeHead(200, { 'Content-Type': 'application/json' });
1306
+ if (!res.headersSent) {
1307
+ res.writeHead(200, { 'Content-Type': 'application/json' });
1308
+ }
1214
1309
  res.end(JSON.stringify(chatCompletion));
1215
- } catch (e) {}
1310
+ } catch (e) { log('Failed to send non-streaming response: ' + e.message); }
1216
1311
  }
1217
1312
  });
1218
1313
  return;
@@ -1311,6 +1406,7 @@ const server = http.createServer(async (req, res) => {
1311
1406
  pendingPayments.set(walletHash, {
1312
1407
  body: body,
1313
1408
  modelId: modelId,
1409
+ displayName: displayName,
1314
1410
  durationMinutes: null,
1315
1411
  tiers: tiers,
1316
1412
  step: tiers ? 'tier_select' : 'approval'
@@ -1425,24 +1521,11 @@ server.on('error', (err) => {
1425
1521
 
1426
1522
  server.listen(PROXY_PORT, () => {
1427
1523
  log('Paytaca AI Proxy running on http://localhost:' + PROXY_PORT);
1428
- log('Forwarding to Django at ' + BACKEND_URL);
1524
+ log('Forwarding to ' + BACKEND_URL);
1429
1525
  log('Discovery: http://localhost:' + PROXY_PORT + '/v1/config');
1430
1526
  log('Managed by OpenCode plugin');
1431
1527
  });
1432
1528
 
1433
- // Start heartbeat checker after server is created
1434
- heartbeatChecker = setInterval(() => {
1435
- if (!checkHeartbeat()) {
1436
- clearInterval(heartbeatChecker);
1437
- log('Closing server due to missing heartbeat');
1438
- server.close(() => {
1439
- process.exit(0);
1440
- });
1441
- // Force exit after 2 seconds if graceful shutdown fails
1442
- setTimeout(() => process.exit(0), 2000);
1443
- }
1444
- }, 5000);
1445
-
1446
1529
  // Graceful shutdown
1447
1530
  process.on('SIGTERM', () => {
1448
1531
  log('Shutting down proxy...');
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/bundled/proxy.ts"],"names":[],"mappings":";AAAA,0DAA0D;AAC1D,6DAA6D;;;AAEhD,QAAA,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA06CnC,CAAC"}
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/bundled/proxy.ts"],"names":[],"mappings":";AAAA,0DAA0D;AAC1D,6DAA6D;;;AAEhD,QAAA,oBAAoB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6/CnC,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const WRAPPER_SCRIPT_CONTENT = "#!/usr/bin/env node\n/**\n * Paytaca Pay Wrapper \u2014 handles large request bodies by reading from a file.\n * Imports paytaca-cli modules directly (avoids CLI argument size limits).\n */\n\nimport { readFileSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\n// Find paytaca-cli installation\nfunction findPaytacaCliPath() {\n const possiblePaths = [];\n \n // Try to get global npm root\n try {\n const globalPath = execSync('npm root -g', { encoding: 'utf8' }).trim();\n possiblePaths.push(\n join(globalPath, 'paytaca-cli'),\n join(globalPath, 'opencode-plugin', 'node_modules', 'paytaca-cli'),\n );\n } catch {}\n \n // Common global locations\n possiblePaths.push(\n '/usr/lib/node_modules/paytaca-cli',\n '/usr/local/lib/node_modules/paytaca-cli',\n '/opt/homebrew/lib/node_modules/paytaca-cli',\n );\n \n // Try current file's node_modules (for bundled installs)\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const currentDir = dirname(currentFile);\n possiblePaths.push(\n join(currentDir, '..', 'node_modules', 'paytaca-cli'),\n join(currentDir, '..', '..', 'node_modules', 'paytaca-cli'),\n );\n } catch {}\n \n // Find first valid path\n for (const basePath of possiblePaths) {\n try {\n const walletPath = join(basePath, 'dist', 'wallet', 'index.js');\n readFileSync(walletPath);\n return basePath;\n } catch {}\n }\n \n throw new Error('paytaca-cli not found. Try reinstalling opencode-plugin: npm install @paytaca/opencode-plugin');\n}\n\n// Load paytaca-cli modules\nlet loadMnemonic, loadWallet, LibauthHDWallet, X402Payer, parsePaymentRequiredJson, selectBchPaymentRequirements, BCH_DERIVATION_PATH;\n\ntry {\n const basePath = findPaytacaCliPath();\n \n ({ loadMnemonic, loadWallet } = await import(join(basePath, 'dist', 'wallet', 'index.js')));\n ({ LibauthHDWallet } = await import(join(basePath, 'dist', 'wallet', 'keys.js')));\n ({ X402Payer } = await import(join(basePath, 'dist', 'wallet', 'x402.js')));\n ({ parsePaymentRequiredJson, selectBchPaymentRequirements } = await import(join(basePath, 'dist', 'utils', 'x402.js')));\n ({ BCH_DERIVATION_PATH } = await import(join(basePath, 'dist', 'utils', 'network.js')));\n} catch (err) {\n console.log(JSON.stringify({ success: false, error: 'Failed to load paytaca-cli: ' + err.message }));\n process.exit(1);\n}\n\nasync function main() {\n const configPath = process.argv[2];\n if (!configPath) {\n console.log(JSON.stringify({ success: false, error: 'Usage: node paytaca-pay-wrapper.mjs <config.json>' }));\n process.exit(1);\n }\n\n const config = JSON.parse(readFileSync(configPath, 'utf8'));\n const { url, method, headers, bodyFile, chipnet, confirmed } = config;\n\n const body = readFileSync(bodyFile, 'utf8');\n\n const data = loadMnemonic();\n if (!data) {\n console.log(JSON.stringify({ success: false, error: 'No wallet found. Run paytaca wallet create first.' }));\n process.exit(1);\n }\n\n const wallet = loadWallet();\n const isChipnet = Boolean(chipnet);\n const bchWallet = wallet.forNetwork(isChipnet);\n const hdWallet = new LibauthHDWallet(data.mnemonic, BCH_DERIVATION_PATH, isChipnet ? 'chipnet' : 'mainnet');\n const x402Payer = new X402Payer({ hdWallet, addressIndex: 0 });\n\n try {\n const result = await executePay(url, method, headers, body, bchWallet, x402Payer, isChipnet, confirmed);\n console.log(JSON.stringify(result, null, 2));\n } catch (err) {\n console.log(JSON.stringify({ success: false, error: err.message || String(err) }, null, 2));\n process.exit(1);\n }\n}\n\nasync function executePay(url, method, headers, body, bchWallet, x402Payer, isChipnet, confirmed) {\n const response = await fetch(url, {\n method,\n headers,\n body: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined,\n signal: AbortSignal.timeout(120000),\n });\n\n const responseHeaders = {};\n response.headers.forEach((value, key) => { responseHeaders[key] = value; });\n const responseText = await response.text();\n let responseData;\n try { responseData = JSON.parse(responseText); } catch { responseData = responseText; }\n\n if (response.status === 402) {\n const paymentRequired = parsePaymentRequiredJson(responseData);\n if (!paymentRequired) {\n return { success: false, status: 402, error: 'Could not parse PaymentRequired from 402 response body' };\n }\n const requirements = selectBchPaymentRequirements(paymentRequired, isChipnet ? 'chipnet' : 'mainnet');\n if (!requirements) {\n return {\n success: false, status: 402, error: 'Server does not accept BCH payment',\n data: { acceptedSchemes: paymentRequired.accepts.map(a => ({ scheme: a.scheme, network: a.network })) },\n };\n }\n\n const payerAddress = x402Payer.getPayerAddress();\n const address = requirements.payTo;\n const amountBch = Number(requirements.amount) / 1e8;\n const changeAddressSet = bchWallet.getAddressSetAt(0);\n const changeAddress = changeAddressSet.change;\n\n if (!confirmed) {\n return {\n success: false, status: 402, error: 'Payment not confirmed.',\n payment: { required: true, amount: requirements.amount, payTo: address },\n };\n }\n\n const sendResult = await bchWallet.sendBch(amountBch, address, changeAddress);\n if (!sendResult.success) {\n return { success: false, status: 402, payment: { required: true, error: sendResult.error }, error: sendResult.error };\n }\n\n const txid = sendResult.txid;\n const paymentPayload = await x402Payer.createPaymentPayload(requirements, paymentRequired.resource.url, txid, 0, requirements.amount);\n headers['PAYMENT-SIGNATURE'] = JSON.stringify(paymentPayload);\n\n let retryResponse;\n try {\n retryResponse = await fetch(url, {\n method,\n headers,\n body: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined,\n signal: AbortSignal.timeout(120000),\n });\n } catch (e) {\n if (e.name === 'AbortError') {\n return { success: false, timeout: true, error: 'Response timed out from server.' };\n }\n throw e;\n }\n const retryResponseHeaders = {};\n retryResponse.headers.forEach((value, key) => { retryResponseHeaders[key] = value; });\n const retryResponseText = await retryResponse.text();\n let retryResponseData;\n try { retryResponseData = JSON.parse(retryResponseText); } catch { retryResponseData = retryResponseText; }\n\n return {\n success: retryResponse.ok,\n status: retryResponse.status,\n statusText: retryResponse.statusText,\n headers: retryResponseHeaders,\n data: retryResponseData,\n payment: { required: true, txid, recipientAddress: address },\n };\n }\n\n return {\n success: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n data: responseData,\n payment: { required: false },\n };\n}\n\nmain();\n";
1
+ export declare const WRAPPER_SCRIPT_CONTENT = "#!/usr/bin/env node\n/**\n * Paytaca Pay Wrapper \u2014 handles large request bodies by reading from a file.\n * Imports paytaca-cli modules directly (avoids CLI argument size limits).\n */\n\nimport { readFileSync } from 'fs';\nimport { execSync } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\n\n// How long to wait for the server to respond before treating the payment as timed out.\n// Default 240s so heavy non-streaming generations (large context / long output) can\n// complete; override with PAYTACA_PAY_TIMEOUT_MS.\nconst PAY_TIMEOUT_MS = Number(process.env.PAYTACA_PAY_TIMEOUT_MS || 240000);\n\n// Find paytaca-cli installation\nfunction findPaytacaCliPath() {\n const possiblePaths = [];\n \n // Try to get global npm root\n try {\n const globalPath = execSync('npm root -g', { encoding: 'utf8' }).trim();\n possiblePaths.push(\n join(globalPath, 'paytaca-cli'),\n join(globalPath, 'opencode-plugin', 'node_modules', 'paytaca-cli'),\n );\n } catch {}\n \n // Common global locations\n possiblePaths.push(\n '/usr/lib/node_modules/paytaca-cli',\n '/usr/local/lib/node_modules/paytaca-cli',\n '/opt/homebrew/lib/node_modules/paytaca-cli',\n );\n \n // Try current file's node_modules (for bundled installs)\n try {\n const currentFile = fileURLToPath(import.meta.url);\n const currentDir = dirname(currentFile);\n possiblePaths.push(\n join(currentDir, '..', 'node_modules', 'paytaca-cli'),\n join(currentDir, '..', '..', 'node_modules', 'paytaca-cli'),\n );\n } catch {}\n \n // Find first valid path\n for (const basePath of possiblePaths) {\n try {\n const walletPath = join(basePath, 'dist', 'wallet', 'index.js');\n readFileSync(walletPath);\n return basePath;\n } catch {}\n }\n \n throw new Error('paytaca-cli not found. Try reinstalling opencode-plugin: npm install @paytaca/opencode-plugin');\n}\n\n// Load paytaca-cli modules\nlet loadMnemonic, loadWallet, LibauthHDWallet, X402Payer, parsePaymentRequiredJson, selectBchPaymentRequirements, BCH_DERIVATION_PATH;\n\ntry {\n const basePath = findPaytacaCliPath();\n \n ({ loadMnemonic, loadWallet } = await import(join(basePath, 'dist', 'wallet', 'index.js')));\n ({ LibauthHDWallet } = await import(join(basePath, 'dist', 'wallet', 'keys.js')));\n ({ X402Payer } = await import(join(basePath, 'dist', 'wallet', 'x402.js')));\n ({ parsePaymentRequiredJson, selectBchPaymentRequirements } = await import(join(basePath, 'dist', 'utils', 'x402.js')));\n ({ BCH_DERIVATION_PATH } = await import(join(basePath, 'dist', 'utils', 'network.js')));\n} catch (err) {\n console.log(JSON.stringify({ success: false, error: 'Failed to load paytaca-cli: ' + err.message }));\n process.exit(1);\n}\n\nasync function main() {\n const configPath = process.argv[2];\n if (!configPath) {\n console.log(JSON.stringify({ success: false, error: 'Usage: node paytaca-pay-wrapper.mjs <config.json>' }));\n process.exit(1);\n }\n\n const config = JSON.parse(readFileSync(configPath, 'utf8'));\n const { url, method, headers, bodyFile, chipnet, confirmed } = config;\n\n const body = readFileSync(bodyFile, 'utf8');\n\n const data = loadMnemonic();\n if (!data) {\n console.log(JSON.stringify({ success: false, error: 'No wallet found. Run paytaca wallet create first.' }));\n process.exit(1);\n }\n\n const wallet = loadWallet();\n const isChipnet = Boolean(chipnet);\n const bchWallet = wallet.forNetwork(isChipnet);\n const hdWallet = new LibauthHDWallet(data.mnemonic, BCH_DERIVATION_PATH, isChipnet ? 'chipnet' : 'mainnet');\n const x402Payer = new X402Payer({ hdWallet, addressIndex: 0 });\n\n try {\n const result = await executePay(url, method, headers, body, bchWallet, x402Payer, isChipnet, confirmed);\n console.log(JSON.stringify(result, null, 2));\n } catch (err) {\n console.log(JSON.stringify({ success: false, error: err.message || String(err) }, null, 2));\n process.exit(1);\n }\n}\n\nasync function executePay(url, method, headers, body, bchWallet, x402Payer, isChipnet, confirmed) {\n const response = await fetch(url, {\n method,\n headers,\n body: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined,\n signal: AbortSignal.timeout(PAY_TIMEOUT_MS),\n });\n\n const responseHeaders = {};\n response.headers.forEach((value, key) => { responseHeaders[key] = value; });\n const responseText = await response.text();\n let responseData;\n try { responseData = JSON.parse(responseText); } catch { responseData = responseText; }\n\n if (response.status === 402) {\n const paymentRequired = parsePaymentRequiredJson(responseData);\n if (!paymentRequired) {\n return { success: false, status: 402, error: 'Could not parse PaymentRequired from 402 response body' };\n }\n const requirements = selectBchPaymentRequirements(paymentRequired, isChipnet ? 'chipnet' : 'mainnet');\n if (!requirements) {\n return {\n success: false, status: 402, error: 'Server does not accept BCH payment',\n data: { acceptedSchemes: paymentRequired.accepts.map(a => ({ scheme: a.scheme, network: a.network })) },\n };\n }\n\n const payerAddress = x402Payer.getPayerAddress();\n const address = requirements.payTo;\n const amountBch = Number(requirements.amount) / 1e8;\n const changeAddressSet = bchWallet.getAddressSetAt(0);\n const changeAddress = changeAddressSet.change;\n\n if (!confirmed) {\n return {\n success: false, status: 402, error: 'Payment not confirmed.',\n payment: { required: true, amount: requirements.amount, payTo: address },\n };\n }\n\n const sendResult = await bchWallet.sendBch(amountBch, address, changeAddress);\n if (!sendResult.success) {\n return { success: false, status: 402, payment: { required: true, error: sendResult.error }, error: sendResult.error };\n }\n\n const txid = sendResult.txid;\n const paymentPayload = await x402Payer.createPaymentPayload(requirements, paymentRequired.resource.url, txid, 0, requirements.amount);\n headers['PAYMENT-SIGNATURE'] = JSON.stringify(paymentPayload);\n\n let retryResponse;\n try {\n retryResponse = await fetch(url, {\n method,\n headers,\n body: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined,\n signal: AbortSignal.timeout(PAY_TIMEOUT_MS),\n });\n } catch (e) {\n if (e.name === 'AbortError') {\n return { success: false, timeout: true, error: 'Response timed out from server.' };\n }\n throw e;\n }\n const retryResponseHeaders = {};\n retryResponse.headers.forEach((value, key) => { retryResponseHeaders[key] = value; });\n const retryResponseText = await retryResponse.text();\n let retryResponseData;\n try { retryResponseData = JSON.parse(retryResponseText); } catch { retryResponseData = retryResponseText; }\n\n return {\n success: retryResponse.ok,\n status: retryResponse.status,\n statusText: retryResponse.statusText,\n headers: retryResponseHeaders,\n data: retryResponseData,\n payment: { required: true, txid, recipientAddress: address },\n };\n }\n\n return {\n success: response.ok,\n status: response.status,\n statusText: response.statusText,\n headers: responseHeaders,\n data: responseData,\n payment: { required: false },\n };\n}\n\nmain();\n";
2
2
  //# sourceMappingURL=wrapper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../src/bundled/wrapper.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,i/NAgMlC,CAAC"}
1
+ {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../src/bundled/wrapper.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,ozOAqMlC,CAAC"}
@@ -15,6 +15,11 @@ import { execSync } from 'child_process';
15
15
  import { fileURLToPath } from 'url';
16
16
  import { dirname, join } from 'path';
17
17
 
18
+ // How long to wait for the server to respond before treating the payment as timed out.
19
+ // Default 240s so heavy non-streaming generations (large context / long output) can
20
+ // complete; override with PAYTACA_PAY_TIMEOUT_MS.
21
+ const PAY_TIMEOUT_MS = Number(process.env.PAYTACA_PAY_TIMEOUT_MS || 240000);
22
+
18
23
  // Find paytaca-cli installation
19
24
  function findPaytacaCliPath() {
20
25
  const possiblePaths = [];
@@ -111,7 +116,7 @@ async function executePay(url, method, headers, body, bchWallet, x402Payer, isCh
111
116
  method,
112
117
  headers,
113
118
  body: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined,
114
- signal: AbortSignal.timeout(120000),
119
+ signal: AbortSignal.timeout(PAY_TIMEOUT_MS),
115
120
  });
116
121
 
117
122
  const responseHeaders = {};
@@ -161,7 +166,7 @@ async function executePay(url, method, headers, body, bchWallet, x402Payer, isCh
161
166
  method,
162
167
  headers,
163
168
  body: ['POST', 'PUT', 'PATCH'].includes(method) ? body : undefined,
164
- signal: AbortSignal.timeout(120000),
169
+ signal: AbortSignal.timeout(PAY_TIMEOUT_MS),
165
170
  });
166
171
  } catch (e) {
167
172
  if (e.name === 'AbortError') {
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../../src/bundled/wrapper.ts"],"names":[],"mappings":";AAAA,oEAAoE;AACpE,4EAA4E;AAC5E,mFAAmF;;;AAEtE,QAAA,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgMrC,CAAC"}
1
+ {"version":3,"file":"wrapper.js","sourceRoot":"","sources":["../../src/bundled/wrapper.ts"],"names":[],"mappings":";AAAA,oEAAoE;AACpE,4EAA4E;AAC5E,mFAAmF;;;AAEtE,QAAA,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqMrC,CAAC"}
package/dist/config.d.ts CHANGED
@@ -10,7 +10,4 @@ export declare function getPidFile(configDir: string): string;
10
10
  export declare function getProxyScript(configDir: string): string;
11
11
  export declare function getLogFile(configDir: string): string;
12
12
  export declare function getWrapperScript(configDir: string): string;
13
- export declare function getHeartbeatFile(configDir: string): string;
14
- export declare function updateHeartbeat(configDir: string): void;
15
- export declare function startHeartbeat(configDir: string): NodeJS.Timeout;
16
13
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,eAAO,MAAM,mBAAmB,2BAA2B,CAAC;AAC5D,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAGvC,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAIvD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAiCpD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAIlE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D;AAGD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAQvD;AAGD,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,OAAO,CAOhE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAGjC,eAAO,MAAM,mBAAmB,2BAA2B,CAAC;AAC5D,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAGvC,wBAAgB,YAAY,IAAI,MAAM,CAGrC;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAIvD;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAkCpD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAIlE;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1D"}
package/dist/config.js CHANGED
@@ -43,9 +43,6 @@ exports.getPidFile = getPidFile;
43
43
  exports.getProxyScript = getProxyScript;
44
44
  exports.getLogFile = getLogFile;
45
45
  exports.getWrapperScript = getWrapperScript;
46
- exports.getHeartbeatFile = getHeartbeatFile;
47
- exports.updateHeartbeat = updateHeartbeat;
48
- exports.startHeartbeat = startHeartbeat;
49
46
  const fs = __importStar(require("fs"));
50
47
  const path = __importStar(require("path"));
51
48
  // Default production backend
@@ -84,6 +81,7 @@ function loadConfig(configDir) {
84
81
  proxyPort: parsed.proxyPort || exports.DEFAULT_PROXY_PORT,
85
82
  walletHash: parsed.walletHash,
86
83
  proxyPid: parsed.proxyPid,
84
+ proxyScriptHash: parsed.proxyScriptHash,
87
85
  };
88
86
  }
89
87
  catch (err) {
@@ -113,27 +111,4 @@ function getLogFile(configDir) {
113
111
  function getWrapperScript(configDir) {
114
112
  return path.join(configDir, 'paytaca-pay-wrapper.mjs');
115
113
  }
116
- function getHeartbeatFile(configDir) {
117
- return path.join(configDir, 'heartbeat');
118
- }
119
- // Touch the heartbeat file to signal the proxy we're still alive
120
- function updateHeartbeat(configDir) {
121
- const heartbeatFile = getHeartbeatFile(configDir);
122
- try {
123
- // Write current timestamp
124
- fs.writeFileSync(heartbeatFile, Date.now().toString());
125
- }
126
- catch (err) {
127
- // Ignore errors
128
- }
129
- }
130
- // Start heartbeat interval that updates every 5 seconds
131
- function startHeartbeat(configDir) {
132
- // Update immediately
133
- updateHeartbeat(configDir);
134
- // Then every 5 seconds
135
- return setInterval(() => {
136
- updateHeartbeat(configDir);
137
- }, 5000);
138
- }
139
114
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,oCAGC;AAED,0CAIC;AAED,sCAEC;AAED,gCAkCC;AAED,gCAIC;AAED,gCAEC;AAED,wCAEC;AAED,gCAEC;AAED,4CAEC;AAhFD,uCAAyB;AACzB,2CAA6B;AAG7B,6BAA6B;AAChB,QAAA,mBAAmB,GAAG,wBAAwB,CAAC;AAC/C,QAAA,kBAAkB,GAAG,IAAI,CAAC;AAEvC,yCAAyC;AACzC,SAAgB,YAAY;IAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC;IAChE,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;AAC9C,CAAC;AAED,SAAgB,eAAe,CAAC,SAAiB;IAC/C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,SAAgB,aAAa,CAAC,SAAiB;IAC7C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAC7C,CAAC;AAED,SAAgB,UAAU,CAAC,SAAiB;IAC1C,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAE5C,uEAAuE;IACvE,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IACtD,IAAI,aAAa,EAAE,CAAC;QAClB,OAAO;YACL,UAAU,EAAE,aAAa;YACzB,SAAS,EAAE,0BAAkB;SAC9B,CAAC;IACJ,CAAC;IAED,0BAA0B;IAC1B,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,OAAO;gBACL,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,2BAAmB;gBACpD,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,0BAAkB;gBACjD,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,eAAe,EAAE,MAAM,CAAC,eAAe;aACxC,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,GAAG,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,yCAAyC;IACzC,OAAO;QACL,UAAU,EAAE,2BAAmB;QAC/B,SAAS,EAAE,0BAAkB;KAC9B,CAAC;AACJ,CAAC;AAED,SAAgB,UAAU,CAAC,SAAiB,EAAE,MAAc;IAC1D,MAAM,UAAU,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IAC5C,eAAe,CAAC,SAAS,CAAC,CAAC;IAC3B,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,SAAgB,UAAU,CAAC,SAAiB;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,cAAc,CAAC,SAAiB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,UAAU,CAAC,SAAiB;IAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,gBAAgB,CAAC,SAAiB;IAChD,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,yBAAyB,CAAC,CAAC;AACzD,CAAC"}
package/dist/proxy.d.ts CHANGED
@@ -8,7 +8,6 @@ export declare function getProxyStatus(configDir: string): Promise<{
8
8
  pid?: number;
9
9
  }>;
10
10
  export declare function startProxy(configDir: string, config: Config): Promise<ProxyInfo>;
11
- export declare function stopProxy(configDir: string): Promise<void>;
12
11
  export declare function getProxyConfig(configDir: string): Promise<{
13
12
  backendUrl: string;
14
13
  port: number;