@ubiquity-os/plugin-sdk 3.6.3 → 3.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -41,7 +41,6 @@ module.exports = __toCommonJS(src_exports);
41
41
 
42
42
  // src/actions.ts
43
43
  var core = __toESM(require("@actions/core"));
44
- var github2 = __toESM(require("@actions/github"));
45
44
  var import_value3 = require("@sinclair/typebox/value");
46
45
 
47
46
  // ../../node_modules/@ubiquity-os/ubiquity-os-logger/dist/index.js
@@ -379,8 +378,56 @@ var Logs = class _Logs {
379
378
  // src/actions.ts
380
379
  var import_dotenv = require("dotenv");
381
380
 
382
- // src/helpers/runtime-info.ts
383
- var import_github = __toESM(require("@actions/github"));
381
+ // src/error.ts
382
+ function getErrorStatus(err) {
383
+ if (!err || typeof err !== "object") return null;
384
+ const candidate = err;
385
+ const directStatus = candidate.status ?? candidate.response?.status;
386
+ if (typeof directStatus === "number" && Number.isFinite(directStatus)) return directStatus;
387
+ if (typeof directStatus === "string" && directStatus.trim()) {
388
+ const parsed = Number.parseInt(directStatus, 10);
389
+ if (Number.isFinite(parsed)) return parsed;
390
+ }
391
+ if (err instanceof Error) {
392
+ const match = /LLM API error:\s*(\d{3})/i.exec(err.message);
393
+ if (match) {
394
+ const parsed = Number.parseInt(match[1], 10);
395
+ if (Number.isFinite(parsed)) return parsed;
396
+ }
397
+ }
398
+ return null;
399
+ }
400
+ function logByStatus(context, message, metadata) {
401
+ const status = getErrorStatus(metadata.err);
402
+ const payload = { ...metadata, ...status ? { status } : {} };
403
+ if (status && status >= 500) return context.logger.error(message, payload);
404
+ if (status && status >= 400) return context.logger.warn(message, payload);
405
+ if (status && status >= 300) return context.logger.debug(message, payload);
406
+ if (status && status >= 200) return context.logger.ok(message, payload);
407
+ if (status && status >= 100) return context.logger.info(message, payload);
408
+ return context.logger.error(message, payload);
409
+ }
410
+ function transformError(context, error) {
411
+ if (error instanceof LogReturn) {
412
+ return error;
413
+ }
414
+ if (error instanceof AggregateError) {
415
+ const message = error.errors.map((err) => {
416
+ if (err instanceof LogReturn) {
417
+ return err.logMessage.raw;
418
+ }
419
+ if (err instanceof Error) {
420
+ return err.message;
421
+ }
422
+ return String(err);
423
+ }).join("\n\n");
424
+ return logByStatus(context, message, { err: error });
425
+ }
426
+ if (error instanceof Error) {
427
+ return logByStatus(context, error.message, { err: error });
428
+ }
429
+ return logByStatus(context, String(error), { err: error });
430
+ }
384
431
 
385
432
  // ../../node_modules/hono/dist/helper/adapter/index.js
386
433
  var env = (c, runtime) => {
@@ -432,6 +479,21 @@ var checkUserAgentEquals = (platform) => {
432
479
  return userAgent.startsWith(platform);
433
480
  };
434
481
 
482
+ // src/helpers/github-context.ts
483
+ var github = __toESM(require("@actions/github"));
484
+ function getGithubContext() {
485
+ const override = globalThis.__UOS_GITHUB_CONTEXT__;
486
+ if (override) {
487
+ return override;
488
+ }
489
+ const module2 = github;
490
+ const context = module2.context ?? module2.default?.context;
491
+ if (!context) {
492
+ throw new Error("GitHub context is unavailable.");
493
+ }
494
+ return context;
495
+ }
496
+
435
497
  // src/helpers/runtime-info.ts
436
498
  var PluginRuntimeInfo = class _PluginRuntimeInfo {
437
499
  static _instance = null;
@@ -480,10 +542,11 @@ var CfRuntimeInfo = class extends PluginRuntimeInfo {
480
542
  };
481
543
  var NodeRuntimeInfo = class extends PluginRuntimeInfo {
482
544
  get version() {
483
- return import_github.default.context.sha;
545
+ return getGithubContext().sha;
484
546
  }
485
547
  get runUrl() {
486
- return import_github.default.context.payload.repository ? `${import_github.default.context.payload.repository?.html_url}/actions/runs/${import_github.default.context.runId}` : "http://localhost";
548
+ const context = getGithubContext();
549
+ return context.payload.repository ? `${context.payload.repository?.html_url}/actions/runs/${context.runId}` : "http://localhost";
487
550
  }
488
551
  };
489
552
  var DenoRuntimeInfo = class extends PluginRuntimeInfo {
@@ -573,14 +636,23 @@ function getPluginOptions(options) {
573
636
  }
574
637
 
575
638
  // src/comment.ts
639
+ function logByStatus2(logger, message, status, metadata) {
640
+ const payload = { ...metadata, ...status ? { status } : {} };
641
+ if (status && status >= 500) return logger.error(message, payload);
642
+ if (status && status >= 400) return logger.warn(message, payload);
643
+ if (status && status >= 300) return logger.debug(message, payload);
644
+ if (status && status >= 200) return logger.ok(message, payload);
645
+ if (status && status >= 100) return logger.info(message, payload);
646
+ return logger.error(message, payload);
647
+ }
576
648
  var CommentHandler = class _CommentHandler {
577
649
  static HEADER_NAME = "UbiquityOS";
578
650
  _lastCommentId = { reviewCommentId: null, issueCommentId: null };
579
- async _updateIssueComment(context2, params) {
651
+ async _updateIssueComment(context, params) {
580
652
  if (!this._lastCommentId.issueCommentId) {
581
- throw context2.logger.error("issueCommentId is missing");
653
+ throw context.logger.error("issueCommentId is missing");
582
654
  }
583
- const commentData = await context2.octokit.rest.issues.updateComment({
655
+ const commentData = await context.octokit.rest.issues.updateComment({
584
656
  owner: params.owner,
585
657
  repo: params.repo,
586
658
  comment_id: this._lastCommentId.issueCommentId,
@@ -588,11 +660,11 @@ var CommentHandler = class _CommentHandler {
588
660
  });
589
661
  return { ...commentData.data, issueNumber: params.issueNumber };
590
662
  }
591
- async _updateReviewComment(context2, params) {
663
+ async _updateReviewComment(context, params) {
592
664
  if (!this._lastCommentId.reviewCommentId) {
593
- throw context2.logger.error("reviewCommentId is missing");
665
+ throw context.logger.error("reviewCommentId is missing");
594
666
  }
595
- const commentData = await context2.octokit.rest.pulls.updateReviewComment({
667
+ const commentData = await context.octokit.rest.pulls.updateReviewComment({
596
668
  owner: params.owner,
597
669
  repo: params.repo,
598
670
  comment_id: this._lastCommentId.reviewCommentId,
@@ -600,9 +672,9 @@ var CommentHandler = class _CommentHandler {
600
672
  });
601
673
  return { ...commentData.data, issueNumber: params.issueNumber };
602
674
  }
603
- async _createNewComment(context2, params) {
675
+ async _createNewComment(context, params) {
604
676
  if (params.commentId) {
605
- const commentData2 = await context2.octokit.rest.pulls.createReplyForReviewComment({
677
+ const commentData2 = await context.octokit.rest.pulls.createReplyForReviewComment({
606
678
  owner: params.owner,
607
679
  repo: params.repo,
608
680
  pull_number: params.issueNumber,
@@ -612,7 +684,7 @@ var CommentHandler = class _CommentHandler {
612
684
  this._lastCommentId.reviewCommentId = commentData2.data.id;
613
685
  return { ...commentData2.data, issueNumber: params.issueNumber };
614
686
  }
615
- const commentData = await context2.octokit.rest.issues.createComment({
687
+ const commentData = await context.octokit.rest.issues.createComment({
616
688
  owner: params.owner,
617
689
  repo: params.repo,
618
690
  issue_number: params.issueNumber,
@@ -621,54 +693,58 @@ var CommentHandler = class _CommentHandler {
621
693
  this._lastCommentId.issueCommentId = commentData.data.id;
622
694
  return { ...commentData.data, issueNumber: params.issueNumber };
623
695
  }
624
- _getIssueNumber(context2) {
625
- if ("issue" in context2.payload) return context2.payload.issue.number;
626
- if ("pull_request" in context2.payload) return context2.payload.pull_request.number;
627
- if ("discussion" in context2.payload) return context2.payload.discussion.number;
696
+ _getIssueNumber(context) {
697
+ if ("issue" in context.payload) return context.payload.issue.number;
698
+ if ("pull_request" in context.payload) return context.payload.pull_request.number;
699
+ if ("discussion" in context.payload) return context.payload.discussion.number;
628
700
  return void 0;
629
701
  }
630
- _getCommentId(context2) {
631
- return "pull_request" in context2.payload && "comment" in context2.payload ? context2.payload.comment.id : void 0;
702
+ _getCommentId(context) {
703
+ return "pull_request" in context.payload && "comment" in context.payload ? context.payload.comment.id : void 0;
632
704
  }
633
- _extractIssueContext(context2) {
634
- if (!("repository" in context2.payload) || !context2.payload.repository?.owner?.login) {
705
+ _extractIssueContext(context) {
706
+ if (!("repository" in context.payload) || !context.payload.repository?.owner?.login) {
635
707
  return null;
636
708
  }
637
- const issueNumber = this._getIssueNumber(context2);
709
+ const issueNumber = this._getIssueNumber(context);
638
710
  if (!issueNumber) return null;
639
711
  return {
640
712
  issueNumber,
641
- commentId: this._getCommentId(context2),
642
- owner: context2.payload.repository.owner.login,
643
- repo: context2.payload.repository.name
713
+ commentId: this._getCommentId(context),
714
+ owner: context.payload.repository.owner.login,
715
+ repo: context.payload.repository.name
644
716
  };
645
717
  }
646
- _processMessage(context2, message) {
718
+ _processMessage(context, message) {
647
719
  if (message instanceof Error) {
648
720
  const metadata2 = {
649
721
  message: message.message,
650
722
  name: message.name,
651
723
  stack: message.stack
652
724
  };
653
- return { metadata: metadata2, logMessage: context2.logger.error(message.message).logMessage };
725
+ const status = getErrorStatus(message);
726
+ const logReturn = logByStatus2(context.logger, message.message, status, metadata2);
727
+ return { metadata: { ...metadata2, ...status ? { status } : {} }, logMessage: logReturn.logMessage };
654
728
  }
729
+ const stackLine = message.metadata?.error?.stack?.split("\n")[2];
730
+ const callerMatch = stackLine ? /at (\S+)/.exec(stackLine) : null;
655
731
  const metadata = message.metadata ? {
656
732
  ...message.metadata,
657
733
  message: message.metadata.message,
658
734
  stack: message.metadata.stack || message.metadata.error?.stack,
659
- caller: message.metadata.caller || message.metadata.error?.stack?.split("\n")[2]?.match(/at (\S+)/)?.[1]
735
+ caller: message.metadata.caller || callerMatch?.[1]
660
736
  } : { ...message };
661
737
  return { metadata, logMessage: message.logMessage };
662
738
  }
663
- _getInstigatorName(context2) {
664
- if ("installation" in context2.payload && context2.payload.installation && "account" in context2.payload.installation && context2.payload.installation?.account?.name) {
665
- return context2.payload.installation?.account?.name;
739
+ _getInstigatorName(context) {
740
+ if ("installation" in context.payload && context.payload.installation && "account" in context.payload.installation && context.payload.installation?.account?.name) {
741
+ return context.payload.installation?.account?.name;
666
742
  }
667
- return context2.payload.sender?.login || _CommentHandler.HEADER_NAME;
743
+ return context.payload.sender?.login || _CommentHandler.HEADER_NAME;
668
744
  }
669
- _createMetadataContent(context2, metadata) {
745
+ _createMetadataContent(context, metadata) {
670
746
  const jsonPretty = sanitizeMetadata(metadata);
671
- const instigatorName = this._getInstigatorName(context2);
747
+ const instigatorName = this._getInstigatorName(context);
672
748
  const runUrl = PluginRuntimeInfo.getInstance().runUrl;
673
749
  const version = PluginRuntimeInfo.getInstance().version;
674
750
  const callingFnName = metadata.caller || "anonymous";
@@ -685,63 +761,39 @@ var CommentHandler = class _CommentHandler {
685
761
  /*
686
762
  * Creates the body for the comment, embeds the metadata and the header hidden in the body as well.
687
763
  */
688
- createCommentBody(context2, message, options) {
689
- return this._createCommentBody(context2, message, options);
764
+ createCommentBody(context, message, options) {
765
+ return this._createCommentBody(context, message, options);
690
766
  }
691
- _createCommentBody(context2, message, options) {
692
- const { metadata, logMessage } = this._processMessage(context2, message);
693
- const { header, jsonPretty } = this._createMetadataContent(context2, metadata);
767
+ _createCommentBody(context, message, options) {
768
+ const { metadata, logMessage } = this._processMessage(context, message);
769
+ const { header, jsonPretty } = this._createMetadataContent(context, metadata);
694
770
  const metadataContent = this._formatMetadataContent(logMessage, header, jsonPretty);
695
771
  return `${options?.raw ? logMessage?.raw : logMessage?.diff}
696
772
 
697
773
  ${metadataContent}
698
774
  `;
699
775
  }
700
- async postComment(context2, message, options = { updateComment: true, raw: false }) {
701
- const issueContext = this._extractIssueContext(context2);
776
+ async postComment(context, message, options = { updateComment: true, raw: false }) {
777
+ const issueContext = this._extractIssueContext(context);
702
778
  if (!issueContext) {
703
- context2.logger.warn("Cannot post comment: missing issue context in payload");
779
+ context.logger.warn("Cannot post comment: missing issue context in payload");
704
780
  return null;
705
781
  }
706
- const body = this._createCommentBody(context2, message, options);
782
+ const body = this._createCommentBody(context, message, options);
707
783
  const { issueNumber, commentId, owner, repo } = issueContext;
708
784
  const params = { owner, repo, body, issueNumber };
709
785
  if (options.updateComment) {
710
- if (this._lastCommentId.issueCommentId && !("pull_request" in context2.payload && "comment" in context2.payload)) {
711
- return this._updateIssueComment(context2, params);
786
+ if (this._lastCommentId.issueCommentId && !("pull_request" in context.payload && "comment" in context.payload)) {
787
+ return this._updateIssueComment(context, params);
712
788
  }
713
- if (this._lastCommentId.reviewCommentId && "pull_request" in context2.payload && "comment" in context2.payload) {
714
- return this._updateReviewComment(context2, params);
789
+ if (this._lastCommentId.reviewCommentId && "pull_request" in context.payload && "comment" in context.payload) {
790
+ return this._updateReviewComment(context, params);
715
791
  }
716
792
  }
717
- return this._createNewComment(context2, { ...params, commentId });
793
+ return this._createNewComment(context, { ...params, commentId });
718
794
  }
719
795
  };
720
796
 
721
- // src/error.ts
722
- function transformError(context2, error) {
723
- let loggerError;
724
- if (error instanceof AggregateError) {
725
- loggerError = context2.logger.error(
726
- error.errors.map((err) => {
727
- if (err instanceof LogReturn) {
728
- return err.logMessage.raw;
729
- } else if (err instanceof Error) {
730
- return err.message;
731
- } else {
732
- return err;
733
- }
734
- }).join("\n\n"),
735
- { error }
736
- );
737
- } else if (error instanceof Error || error instanceof LogReturn) {
738
- loggerError = error;
739
- } else {
740
- loggerError = context2.logger.error(String(error));
741
- }
742
- return loggerError;
743
- }
744
-
745
797
  // src/helpers/command.ts
746
798
  var import_value = require("@sinclair/typebox/value");
747
799
  function getCommand(inputs, pluginOptions) {
@@ -974,7 +1026,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
974
1026
  ref: inputs.ref,
975
1027
  command: inputs.command
976
1028
  };
977
- const pemContents = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").trim();
1029
+ const pemContents = publicKeyPem.replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", "").replace(/\s+/g, "");
978
1030
  const binaryDer = Uint8Array.from(atob(pemContents), (c) => c.charCodeAt(0));
979
1031
  const publicKey = await crypto.subtle.importKey(
980
1032
  "spki",
@@ -1027,16 +1079,12 @@ var inputSchema = import_typebox3.Type.Object({
1027
1079
 
1028
1080
  // src/actions.ts
1029
1081
  (0, import_dotenv.config)();
1030
- async function handleError(context2, pluginOptions, error) {
1082
+ async function handleError(context, pluginOptions, error) {
1031
1083
  console.error(error);
1032
- const loggerError = transformError(context2, error);
1033
- if (loggerError instanceof LogReturn) {
1034
- core.setFailed(loggerError.logMessage.diff);
1035
- } else if (loggerError instanceof Error) {
1036
- core.setFailed(loggerError);
1037
- }
1084
+ const loggerError = transformError(context, error);
1085
+ core.setFailed(loggerError.logMessage.diff);
1038
1086
  if (pluginOptions.postCommentOnError && loggerError) {
1039
- await context2.commentHandler.postComment(context2, loggerError);
1087
+ await context.commentHandler.postComment(context, loggerError);
1040
1088
  }
1041
1089
  }
1042
1090
  async function createActionsPlugin(handler, options) {
@@ -1046,7 +1094,8 @@ async function createActionsPlugin(handler, options) {
1046
1094
  core.setFailed("Error: PLUGIN_GITHUB_TOKEN env is not set");
1047
1095
  return;
1048
1096
  }
1049
- const body = github2.context.payload.inputs;
1097
+ const githubContext = getGithubContext();
1098
+ const body = githubContext.payload.inputs;
1050
1099
  const inputSchemaErrors = [...import_value3.Value.Errors(inputSchema, body)];
1051
1100
  if (inputSchemaErrors.length) {
1052
1101
  console.dir(inputSchemaErrors, { depth: null });
@@ -1084,7 +1133,7 @@ async function createActionsPlugin(handler, options) {
1084
1133
  env2 = process.env;
1085
1134
  }
1086
1135
  const command = getCommand(inputs, pluginOptions);
1087
- const context2 = {
1136
+ const context = {
1088
1137
  eventName: inputs.eventName,
1089
1138
  payload: inputs.eventPayload,
1090
1139
  command,
@@ -1097,20 +1146,21 @@ async function createActionsPlugin(handler, options) {
1097
1146
  commentHandler: new CommentHandler()
1098
1147
  };
1099
1148
  try {
1100
- const result = await handler(context2);
1149
+ const result = await handler(context);
1101
1150
  core.setOutput("result", result);
1102
1151
  if (pluginOptions?.returnDataToKernel) {
1103
1152
  await returnDataToKernel(pluginGithubToken, inputs.stateId, result);
1104
1153
  }
1105
1154
  } catch (error) {
1106
- await handleError(context2, pluginOptions, error);
1155
+ await handleError(context, pluginOptions, error);
1107
1156
  }
1108
1157
  }
1109
1158
  async function returnDataToKernel(repoToken, stateId, output) {
1159
+ const githubContext = getGithubContext();
1110
1160
  const octokit = new customOctokit({ auth: repoToken });
1111
1161
  await octokit.rest.repos.createDispatchEvent({
1112
- owner: github2.context.repo.owner,
1113
- repo: github2.context.repo.repo,
1162
+ owner: githubContext.repo.owner,
1163
+ repo: githubContext.repo.repo,
1114
1164
  event_type: "return-data-to-ubiquity-os-kernel",
1115
1165
  client_payload: {
1116
1166
  state_id: stateId,
@@ -1177,7 +1227,7 @@ var import_value4 = require("@sinclair/typebox/value");
1177
1227
 
1178
1228
  // ../../node_modules/hono/dist/compose.js
1179
1229
  var compose = (middleware, onError, onNotFound) => {
1180
- return (context2, next) => {
1230
+ return (context, next) => {
1181
1231
  let index = -1;
1182
1232
  return dispatch(0);
1183
1233
  async function dispatch(i) {
@@ -1190,31 +1240,31 @@ var compose = (middleware, onError, onNotFound) => {
1190
1240
  let handler;
1191
1241
  if (middleware[i]) {
1192
1242
  handler = middleware[i][0][0];
1193
- context2.req.routeIndex = i;
1243
+ context.req.routeIndex = i;
1194
1244
  } else {
1195
1245
  handler = i === middleware.length && next || void 0;
1196
1246
  }
1197
1247
  if (handler) {
1198
1248
  try {
1199
- res = await handler(context2, () => dispatch(i + 1));
1249
+ res = await handler(context, () => dispatch(i + 1));
1200
1250
  } catch (err) {
1201
1251
  if (err instanceof Error && onError) {
1202
- context2.error = err;
1203
- res = await onError(err, context2);
1252
+ context.error = err;
1253
+ res = await onError(err, context);
1204
1254
  isError = true;
1205
1255
  } else {
1206
1256
  throw err;
1207
1257
  }
1208
1258
  }
1209
1259
  } else {
1210
- if (context2.finalized === false && onNotFound) {
1211
- res = await onNotFound(context2);
1260
+ if (context.finalized === false && onNotFound) {
1261
+ res = await onNotFound(context);
1212
1262
  }
1213
1263
  }
1214
- if (res && (context2.finalized === false || isError)) {
1215
- context2.res = res;
1264
+ if (res && (context.finalized === false || isError)) {
1265
+ context.res = res;
1216
1266
  }
1217
- return context2;
1267
+ return context;
1218
1268
  }
1219
1269
  };
1220
1270
  };
@@ -1616,7 +1666,7 @@ var raw = (value, callbacks) => {
1616
1666
  escapedString.callbacks = callbacks;
1617
1667
  return escapedString;
1618
1668
  };
1619
- var resolveCallback = async (str, phase, preserveCallbacks, context2, buffer) => {
1669
+ var resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {
1620
1670
  if (typeof str === "object" && !(str instanceof String)) {
1621
1671
  if (!(str instanceof Promise)) {
1622
1672
  str = str.toString();
@@ -1634,9 +1684,9 @@ var resolveCallback = async (str, phase, preserveCallbacks, context2, buffer) =>
1634
1684
  } else {
1635
1685
  buffer = [str];
1636
1686
  }
1637
- const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context: context2 }))).then(
1687
+ const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then(
1638
1688
  (res) => Promise.all(
1639
- res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context2, buffer))
1689
+ res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))
1640
1690
  ).then(() => buffer[0])
1641
1691
  );
1642
1692
  if (preserveCallbacks) {
@@ -2029,13 +2079,13 @@ var Hono = class {
2029
2079
  const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);
2030
2080
  return (async () => {
2031
2081
  try {
2032
- const context2 = await composed(c);
2033
- if (!context2.finalized) {
2082
+ const context = await composed(c);
2083
+ if (!context.finalized) {
2034
2084
  throw new Error(
2035
2085
  "Context is not finalized. Did you forget to return a Response object or `await next()`?"
2036
2086
  );
2037
2087
  }
2038
- return context2.res;
2088
+ return context.res;
2039
2089
  } catch (err) {
2040
2090
  return this.#handleError(err, c);
2041
2091
  }
@@ -2094,7 +2144,7 @@ var Node = class {
2094
2144
  #index;
2095
2145
  #varIndex;
2096
2146
  #children = /* @__PURE__ */ Object.create(null);
2097
- insert(tokens, index, paramMap, context2, pathErrorCheckOnly) {
2147
+ insert(tokens, index, paramMap, context, pathErrorCheckOnly) {
2098
2148
  if (tokens.length === 0) {
2099
2149
  if (this.#index !== void 0) {
2100
2150
  throw PATH_ERROR;
@@ -2132,7 +2182,7 @@ var Node = class {
2132
2182
  }
2133
2183
  node = this.#children[regexpStr] = new Node();
2134
2184
  if (name !== "") {
2135
- node.#varIndex = context2.varIndex++;
2185
+ node.#varIndex = context.varIndex++;
2136
2186
  }
2137
2187
  }
2138
2188
  if (!pathErrorCheckOnly && name !== "") {
@@ -2152,7 +2202,7 @@ var Node = class {
2152
2202
  node = this.#children[token] = new Node();
2153
2203
  }
2154
2204
  }
2155
- node.insert(restTokens, index, paramMap, context2, pathErrorCheckOnly);
2205
+ node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);
2156
2206
  }
2157
2207
  buildRegExpStr() {
2158
2208
  const childKeys = Object.keys(this.#children).sort(compareKey);
@@ -2694,11 +2744,11 @@ var HTTPException = class extends Error {
2694
2744
  };
2695
2745
 
2696
2746
  // src/server.ts
2697
- async function handleError2(context2, pluginOptions, error) {
2747
+ async function handleError2(context, pluginOptions, error) {
2698
2748
  console.error(error);
2699
- const loggerError = transformError(context2, error);
2749
+ const loggerError = transformError(context, error);
2700
2750
  if (pluginOptions.postCommentOnError && loggerError) {
2701
- await context2.commentHandler.postComment(context2, loggerError);
2751
+ await context.commentHandler.postComment(context, loggerError);
2702
2752
  }
2703
2753
  throw new HTTPException(500, { message: "Unexpected error" });
2704
2754
  }
@@ -2749,7 +2799,7 @@ function createPlugin(handler, manifest, options) {
2749
2799
  const workerName = new URL(inputs.ref).hostname.split(".")[0];
2750
2800
  PluginRuntimeInfo.getInstance({ ...env2, CLOUDFLARE_WORKER_NAME: workerName });
2751
2801
  const command = getCommand(inputs, pluginOptions);
2752
- const context2 = {
2802
+ const context = {
2753
2803
  eventName: inputs.eventName,
2754
2804
  payload: inputs.eventPayload,
2755
2805
  command,
@@ -2762,10 +2812,10 @@ function createPlugin(handler, manifest, options) {
2762
2812
  commentHandler: new CommentHandler()
2763
2813
  };
2764
2814
  try {
2765
- const result = await handler(context2);
2815
+ const result = await handler(context);
2766
2816
  return ctx.json({ stateId: inputs.stateId, output: result ?? {} });
2767
2817
  } catch (error) {
2768
- await handleError2(context2, pluginOptions, error);
2818
+ await handleError2(context, pluginOptions, error);
2769
2819
  }
2770
2820
  });
2771
2821
  return app;
@@ -2780,6 +2830,14 @@ function normalizeBaseUrl(baseUrl) {
2780
2830
  }
2781
2831
  return normalized;
2782
2832
  }
2833
+ var MAX_LLM_RETRIES = 2;
2834
+ var RETRY_BACKOFF_MS = [250, 750];
2835
+ function getRetryDelayMs(attempt) {
2836
+ return RETRY_BACKOFF_MS[Math.min(attempt, RETRY_BACKOFF_MS.length - 1)] ?? 750;
2837
+ }
2838
+ function sleep(ms) {
2839
+ return new Promise((resolve) => setTimeout(resolve, ms));
2840
+ }
2783
2841
  function getEnvString(name) {
2784
2842
  if (typeof process === "undefined" || !process?.env) return EMPTY_STRING;
2785
2843
  return String(process.env[name] ?? EMPTY_STRING).trim();
@@ -2794,7 +2852,11 @@ function getAiBaseUrl(options) {
2794
2852
  }
2795
2853
  async function callLlm(options, input) {
2796
2854
  const authToken = String(input.authToken ?? EMPTY_STRING).trim();
2797
- if (!authToken) throw new Error("Missing authToken in input");
2855
+ if (!authToken) {
2856
+ const err = new Error("Missing authToken in input");
2857
+ err.status = 401;
2858
+ throw err;
2859
+ }
2798
2860
  const kernelToken = "ubiquityKernelToken" in input ? input.ubiquityKernelToken : void 0;
2799
2861
  const payload = getPayload(input);
2800
2862
  const { owner, repo, installationId } = getRepoMetadata(payload);
@@ -2814,13 +2876,7 @@ async function callLlm(options, input) {
2814
2876
  installationId,
2815
2877
  ubiquityKernelToken: kernelToken
2816
2878
  });
2817
- const response = await fetch(url, { method: "POST", headers, body });
2818
- if (!response.ok) {
2819
- const err = await response.text();
2820
- const error = new Error(`LLM API error: ${response.status} - ${err}`);
2821
- error.status = response.status;
2822
- throw error;
2823
- }
2879
+ const response = await fetchWithRetry(url, { method: "POST", headers, body }, MAX_LLM_RETRIES);
2824
2880
  if (isStream) {
2825
2881
  if (!response.body) {
2826
2882
  throw new Error("LLM API error: missing response body for streaming request");
@@ -2832,17 +2888,46 @@ async function callLlm(options, input) {
2832
2888
  function ensureKernelToken(authToken, kernelToken) {
2833
2889
  const isKernelTokenRequired = authToken.startsWith("gh");
2834
2890
  if (isKernelTokenRequired && !kernelToken) {
2835
- throw new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
2891
+ const err = new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
2892
+ err.status = 401;
2893
+ throw err;
2836
2894
  }
2837
2895
  }
2838
2896
  function ensureMessages(messages) {
2839
2897
  if (!Array.isArray(messages) || messages.length === 0) {
2840
- throw new Error("messages must be a non-empty array");
2898
+ const err = new Error("messages must be a non-empty array");
2899
+ err.status = 400;
2900
+ throw err;
2841
2901
  }
2842
2902
  }
2843
2903
  function buildAiUrl(options, baseUrl) {
2844
2904
  return `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
2845
2905
  }
2906
+ async function fetchWithRetry(url, options, maxRetries) {
2907
+ let attempt = 0;
2908
+ let lastError;
2909
+ while (attempt <= maxRetries) {
2910
+ try {
2911
+ const response = await fetch(url, options);
2912
+ if (response.ok) return response;
2913
+ const errText = await response.text();
2914
+ if (response.status >= 500 && attempt < maxRetries) {
2915
+ await sleep(getRetryDelayMs(attempt));
2916
+ attempt += 1;
2917
+ continue;
2918
+ }
2919
+ const error = new Error(`LLM API error: ${response.status} - ${errText}`);
2920
+ error.status = response.status;
2921
+ throw error;
2922
+ } catch (error) {
2923
+ lastError = error;
2924
+ if (attempt >= maxRetries) throw error;
2925
+ await sleep(getRetryDelayMs(attempt));
2926
+ attempt += 1;
2927
+ }
2928
+ }
2929
+ throw lastError ?? new Error("LLM API error: request failed after retries");
2930
+ }
2846
2931
  function getPayload(input) {
2847
2932
  if ("payload" in input) {
2848
2933
  return input.payload;
@@ -2904,7 +2989,7 @@ function getEventData(event) {
2904
2989
  if (!event.trim()) return null;
2905
2990
  const dataLines = event.split("\n").filter((line) => line.startsWith("data:"));
2906
2991
  if (!dataLines.length) return null;
2907
- const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, "")).join("\n");
2992
+ const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, EMPTY_STRING)).join("\n");
2908
2993
  return data || null;
2909
2994
  }
2910
2995
  function parseEventData(data) {