@ubiquity-os/plugin-sdk 3.6.2 → 3.6.3

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.
@@ -27,6 +27,7 @@ declare const pluginSettingsSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.
27
27
  }>]>;
28
28
  type PluginSettings = StaticDecode<typeof pluginSettingsSchema>;
29
29
  declare const configSchema: _sinclair_typebox.TObject<{
30
+ imports: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TString>>;
30
31
  plugins: _sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnion<[_sinclair_typebox.TNull, _sinclair_typebox.TObject<{
31
32
  with: _sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>;
32
33
  runsOn: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TUnion<[TLiteral<"branch_protection_configuration">, TLiteral<"branch_protection_configuration.disabled">, TLiteral<"branch_protection_configuration.enabled">, TLiteral<"branch_protection_rule">, TLiteral<"branch_protection_rule.created">, TLiteral<"branch_protection_rule.deleted">, TLiteral<"branch_protection_rule.edited">, TLiteral<"check_run">, TLiteral<"check_run.completed">, TLiteral<"check_run.created">, TLiteral<"check_run.requested_action">, TLiteral<"check_run.rerequested">, TLiteral<"check_suite">, TLiteral<"check_suite.completed">, TLiteral<"check_suite.requested">, TLiteral<"check_suite.rerequested">, TLiteral<"code_scanning_alert">, TLiteral<"code_scanning_alert.appeared_in_branch">, TLiteral<"code_scanning_alert.closed_by_user">, TLiteral<"code_scanning_alert.created">, TLiteral<"code_scanning_alert.fixed">, TLiteral<"code_scanning_alert.reopened">, TLiteral<"code_scanning_alert.reopened_by_user">, TLiteral<"commit_comment">, TLiteral<"commit_comment.created">, TLiteral<"create">, TLiteral<"custom_property">, TLiteral<"custom_property.created">, TLiteral<"custom_property.deleted">, TLiteral<"custom_property.promote_to_enterprise">, TLiteral<"custom_property.updated">, TLiteral<"custom_property_values">, TLiteral<"custom_property_values.updated">, TLiteral<"delete">, TLiteral<"dependabot_alert">, TLiteral<"dependabot_alert.auto_dismissed">, TLiteral<"dependabot_alert.auto_reopened">, TLiteral<"dependabot_alert.created">, TLiteral<"dependabot_alert.dismissed">, TLiteral<"dependabot_alert.fixed">, TLiteral<"dependabot_alert.reintroduced">, TLiteral<"dependabot_alert.reopened">, TLiteral<"deploy_key">, TLiteral<"deploy_key.created">, TLiteral<"deploy_key.deleted">, TLiteral<"deployment">, TLiteral<"deployment.created">, TLiteral<"deployment_protection_rule">, TLiteral<"deployment_protection_rule.requested">, TLiteral<"deployment_review">, TLiteral<"deployment_review.approved">, TLiteral<"deployment_review.rejected">, TLiteral<"deployment_review.requested">, TLiteral<"deployment_status">, TLiteral<"deployment_status.created">, TLiteral<"discussion">, TLiteral<"discussion.answered">, TLiteral<"discussion.category_changed">, TLiteral<"discussion.closed">, TLiteral<"discussion.created">, TLiteral<"discussion.deleted">, TLiteral<"discussion.edited">, TLiteral<"discussion.labeled">, TLiteral<"discussion.locked">, TLiteral<"discussion.pinned">, TLiteral<"discussion.reopened">, TLiteral<"discussion.transferred">, TLiteral<"discussion.unanswered">, TLiteral<"discussion.unlabeled">, TLiteral<"discussion.unlocked">, TLiteral<"discussion.unpinned">, TLiteral<"discussion_comment">, TLiteral<"discussion_comment.created">, TLiteral<"discussion_comment.deleted">, TLiteral<"discussion_comment.edited">, TLiteral<"fork">, TLiteral<"github_app_authorization">, TLiteral<"github_app_authorization.revoked">, TLiteral<"gollum">, TLiteral<"installation">, TLiteral<"installation.created">, TLiteral<"installation.deleted">, TLiteral<"installation.new_permissions_accepted">, TLiteral<"installation.suspend">, TLiteral<"installation.unsuspend">, TLiteral<"installation_repositories">, TLiteral<"installation_repositories.added">, TLiteral<"installation_repositories.removed">, TLiteral<"installation_target">, TLiteral<"installation_target.renamed">, TLiteral<"issue_comment">, TLiteral<"issue_comment.created">, TLiteral<"issue_comment.deleted">, TLiteral<"issue_comment.edited">, TLiteral<"issues">, TLiteral<"issues.assigned">, TLiteral<"issues.closed">, TLiteral<"issues.deleted">, TLiteral<"issues.demilestoned">, TLiteral<"issues.edited">, TLiteral<"issues.labeled">, TLiteral<"issues.locked">, TLiteral<"issues.milestoned">, TLiteral<"issues.opened">, TLiteral<"issues.pinned">, TLiteral<"issues.reopened">, TLiteral<"issues.transferred">, TLiteral<"issues.typed">, TLiteral<"issues.unassigned">, TLiteral<"issues.unlabeled">, TLiteral<"issues.unlocked">, TLiteral<"issues.unpinned">, TLiteral<"issues.untyped">, TLiteral<"label">, TLiteral<"label.created">, TLiteral<"label.deleted">, TLiteral<"label.edited">, TLiteral<"marketplace_purchase">, TLiteral<"marketplace_purchase.cancelled">, TLiteral<"marketplace_purchase.changed">, TLiteral<"marketplace_purchase.pending_change">, TLiteral<"marketplace_purchase.pending_change_cancelled">, TLiteral<"marketplace_purchase.purchased">, TLiteral<"member">, TLiteral<"member.added">, TLiteral<"member.edited">, TLiteral<"member.removed">, TLiteral<"membership">, TLiteral<"membership.added">, TLiteral<"membership.removed">, TLiteral<"merge_group">, TLiteral<"merge_group.checks_requested">, TLiteral<"merge_group.destroyed">, TLiteral<"meta">, TLiteral<"meta.deleted">, TLiteral<"milestone">, TLiteral<"milestone.closed">, TLiteral<"milestone.created">, TLiteral<"milestone.deleted">, TLiteral<"milestone.edited">, TLiteral<"milestone.opened">, TLiteral<"org_block">, TLiteral<"org_block.blocked">, TLiteral<"org_block.unblocked">, TLiteral<"organization">, TLiteral<"organization.deleted">, TLiteral<"organization.member_added">, TLiteral<"organization.member_invited">, TLiteral<"organization.member_removed">, TLiteral<"organization.renamed">, TLiteral<"package">, TLiteral<"package.published">, TLiteral<"package.updated">, TLiteral<"page_build">, TLiteral<"personal_access_token_request">, TLiteral<"personal_access_token_request.approved">, TLiteral<"personal_access_token_request.cancelled">, TLiteral<"personal_access_token_request.created">, TLiteral<"personal_access_token_request.denied">, TLiteral<"ping">, TLiteral<"project">, TLiteral<"project.closed">, TLiteral<"project.created">, TLiteral<"project.deleted">, TLiteral<"project.edited">, TLiteral<"project.reopened">, TLiteral<"project_card">, TLiteral<"project_card.converted">, TLiteral<"project_card.created">, TLiteral<"project_card.deleted">, TLiteral<"project_card.edited">, TLiteral<"project_card.moved">, TLiteral<"project_column">, TLiteral<"project_column.created">, TLiteral<"project_column.deleted">, TLiteral<"project_column.edited">, TLiteral<"project_column.moved">, TLiteral<"projects_v2">, TLiteral<"projects_v2.closed">, TLiteral<"projects_v2.created">, TLiteral<"projects_v2.deleted">, TLiteral<"projects_v2.edited">, TLiteral<"projects_v2.reopened">, TLiteral<"projects_v2_item">, TLiteral<"projects_v2_item.archived">, TLiteral<"projects_v2_item.converted">, TLiteral<"projects_v2_item.created">, TLiteral<"projects_v2_item.deleted">, TLiteral<"projects_v2_item.edited">, TLiteral<"projects_v2_item.reordered">, TLiteral<"projects_v2_item.restored">, TLiteral<"projects_v2_status_update">, TLiteral<"projects_v2_status_update.created">, TLiteral<"projects_v2_status_update.deleted">, TLiteral<"projects_v2_status_update.edited">, TLiteral<"public">, TLiteral<"pull_request">, TLiteral<"pull_request.assigned">, TLiteral<"pull_request.auto_merge_disabled">, TLiteral<"pull_request.auto_merge_enabled">, TLiteral<"pull_request.closed">, TLiteral<"pull_request.converted_to_draft">, TLiteral<"pull_request.demilestoned">, TLiteral<"pull_request.dequeued">, TLiteral<"pull_request.edited">, TLiteral<"pull_request.enqueued">, TLiteral<"pull_request.labeled">, TLiteral<"pull_request.locked">, TLiteral<"pull_request.milestoned">, TLiteral<"pull_request.opened">, TLiteral<"pull_request.ready_for_review">, TLiteral<"pull_request.reopened">, TLiteral<"pull_request.review_request_removed">, TLiteral<"pull_request.review_requested">, TLiteral<"pull_request.synchronize">, TLiteral<"pull_request.unassigned">, TLiteral<"pull_request.unlabeled">, TLiteral<"pull_request.unlocked">, TLiteral<"pull_request_review">, TLiteral<"pull_request_review.dismissed">, TLiteral<"pull_request_review.edited">, TLiteral<"pull_request_review.submitted">, TLiteral<"pull_request_review_comment">, TLiteral<"pull_request_review_comment.created">, TLiteral<"pull_request_review_comment.deleted">, TLiteral<"pull_request_review_comment.edited">, TLiteral<"pull_request_review_thread">, TLiteral<"pull_request_review_thread.resolved">, TLiteral<"pull_request_review_thread.unresolved">, TLiteral<"push">, TLiteral<"registry_package">, TLiteral<"registry_package.published">, TLiteral<"registry_package.updated">, TLiteral<"release">, TLiteral<"release.created">, TLiteral<"release.deleted">, TLiteral<"release.edited">, TLiteral<"release.prereleased">, TLiteral<"release.published">, TLiteral<"release.released">, TLiteral<"release.unpublished">, TLiteral<"repository">, TLiteral<"repository.archived">, TLiteral<"repository.created">, TLiteral<"repository.deleted">, TLiteral<"repository.edited">, TLiteral<"repository.privatized">, TLiteral<"repository.publicized">, TLiteral<"repository.renamed">, TLiteral<"repository.transferred">, TLiteral<"repository.unarchived">, TLiteral<"repository_advisory">, TLiteral<"repository_advisory.published">, TLiteral<"repository_advisory.reported">, TLiteral<"repository_dispatch">, TLiteral<"repository_dispatch.sample.collected">, TLiteral<"repository_import">, TLiteral<"repository_ruleset">, TLiteral<"repository_ruleset.created">, TLiteral<"repository_ruleset.deleted">, TLiteral<"repository_ruleset.edited">, TLiteral<"repository_vulnerability_alert">, TLiteral<"repository_vulnerability_alert.create">, TLiteral<"repository_vulnerability_alert.dismiss">, TLiteral<"repository_vulnerability_alert.reopen">, TLiteral<"repository_vulnerability_alert.resolve">, TLiteral<"secret_scanning_alert">, TLiteral<"secret_scanning_alert.created">, TLiteral<"secret_scanning_alert.publicly_leaked">, TLiteral<"secret_scanning_alert.reopened">, TLiteral<"secret_scanning_alert.resolved">, TLiteral<"secret_scanning_alert.validated">, TLiteral<"secret_scanning_alert_location">, TLiteral<"secret_scanning_alert_location.created">, TLiteral<"secret_scanning_scan">, TLiteral<"secret_scanning_scan.completed">, TLiteral<"security_advisory">, TLiteral<"security_advisory.published">, TLiteral<"security_advisory.updated">, TLiteral<"security_advisory.withdrawn">, TLiteral<"security_and_analysis">, TLiteral<"sponsorship">, TLiteral<"sponsorship.cancelled">, TLiteral<"sponsorship.created">, TLiteral<"sponsorship.edited">, TLiteral<"sponsorship.pending_cancellation">, TLiteral<"sponsorship.pending_tier_change">, TLiteral<"sponsorship.tier_changed">, TLiteral<"star">, TLiteral<"star.created">, TLiteral<"star.deleted">, TLiteral<"status">, TLiteral<"sub_issues">, TLiteral<"sub_issues.parent_issue_added">, TLiteral<"sub_issues.parent_issue_removed">, TLiteral<"sub_issues.sub_issue_added">, TLiteral<"sub_issues.sub_issue_removed">, TLiteral<"team">, TLiteral<"team.added_to_repository">, TLiteral<"team.created">, TLiteral<"team.deleted">, TLiteral<"team.edited">, TLiteral<"team.removed_from_repository">, TLiteral<"team_add">, TLiteral<"watch">, TLiteral<"watch.started">, TLiteral<"workflow_dispatch">, TLiteral<"workflow_job">, TLiteral<"workflow_job.completed">, TLiteral<"workflow_job.in_progress">, TLiteral<"workflow_job.queued">, TLiteral<"workflow_job.waiting">, TLiteral<"workflow_run">, TLiteral<"workflow_run.completed">, TLiteral<"workflow_run.in_progress">, TLiteral<"workflow_run.requested">]>>>;
@@ -46,6 +47,7 @@ interface LoggerInterface {
46
47
  debug(message: string, metadata?: Record<string, unknown>): void;
47
48
  error(message: string, metadata?: Record<string, unknown>): void;
48
49
  info(message: string, metadata?: Record<string, unknown>): void;
50
+ ok(message: string, metadata?: Record<string, unknown>): void;
49
51
  warn(message: string, metadata?: Record<string, unknown>): void;
50
52
  }
51
53
  /**
@@ -73,6 +75,7 @@ declare class ConfigurationHandler {
73
75
  * @returns The merged plugin configuration with resolved plugin settings.
74
76
  */
75
77
  getConfiguration(location?: Location): Promise<{
78
+ imports?: string[] | undefined;
76
79
  plugins: {
77
80
  [x: string]: {
78
81
  skipBotEvents?: boolean | undefined;
@@ -95,6 +98,7 @@ declare class ConfigurationHandler {
95
98
  rawData: null;
96
99
  } | {
97
100
  config: {
101
+ imports?: string[] | undefined;
98
102
  plugins: {
99
103
  [x: string]: {
100
104
  skipBotEvents?: boolean | undefined;
@@ -27,6 +27,7 @@ declare const pluginSettingsSchema: _sinclair_typebox.TUnion<[_sinclair_typebox.
27
27
  }>]>;
28
28
  type PluginSettings = StaticDecode<typeof pluginSettingsSchema>;
29
29
  declare const configSchema: _sinclair_typebox.TObject<{
30
+ imports: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TString>>;
30
31
  plugins: _sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnion<[_sinclair_typebox.TNull, _sinclair_typebox.TObject<{
31
32
  with: _sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>;
32
33
  runsOn: _sinclair_typebox.TOptional<_sinclair_typebox.TArray<_sinclair_typebox.TUnion<[TLiteral<"branch_protection_configuration">, TLiteral<"branch_protection_configuration.disabled">, TLiteral<"branch_protection_configuration.enabled">, TLiteral<"branch_protection_rule">, TLiteral<"branch_protection_rule.created">, TLiteral<"branch_protection_rule.deleted">, TLiteral<"branch_protection_rule.edited">, TLiteral<"check_run">, TLiteral<"check_run.completed">, TLiteral<"check_run.created">, TLiteral<"check_run.requested_action">, TLiteral<"check_run.rerequested">, TLiteral<"check_suite">, TLiteral<"check_suite.completed">, TLiteral<"check_suite.requested">, TLiteral<"check_suite.rerequested">, TLiteral<"code_scanning_alert">, TLiteral<"code_scanning_alert.appeared_in_branch">, TLiteral<"code_scanning_alert.closed_by_user">, TLiteral<"code_scanning_alert.created">, TLiteral<"code_scanning_alert.fixed">, TLiteral<"code_scanning_alert.reopened">, TLiteral<"code_scanning_alert.reopened_by_user">, TLiteral<"commit_comment">, TLiteral<"commit_comment.created">, TLiteral<"create">, TLiteral<"custom_property">, TLiteral<"custom_property.created">, TLiteral<"custom_property.deleted">, TLiteral<"custom_property.promote_to_enterprise">, TLiteral<"custom_property.updated">, TLiteral<"custom_property_values">, TLiteral<"custom_property_values.updated">, TLiteral<"delete">, TLiteral<"dependabot_alert">, TLiteral<"dependabot_alert.auto_dismissed">, TLiteral<"dependabot_alert.auto_reopened">, TLiteral<"dependabot_alert.created">, TLiteral<"dependabot_alert.dismissed">, TLiteral<"dependabot_alert.fixed">, TLiteral<"dependabot_alert.reintroduced">, TLiteral<"dependabot_alert.reopened">, TLiteral<"deploy_key">, TLiteral<"deploy_key.created">, TLiteral<"deploy_key.deleted">, TLiteral<"deployment">, TLiteral<"deployment.created">, TLiteral<"deployment_protection_rule">, TLiteral<"deployment_protection_rule.requested">, TLiteral<"deployment_review">, TLiteral<"deployment_review.approved">, TLiteral<"deployment_review.rejected">, TLiteral<"deployment_review.requested">, TLiteral<"deployment_status">, TLiteral<"deployment_status.created">, TLiteral<"discussion">, TLiteral<"discussion.answered">, TLiteral<"discussion.category_changed">, TLiteral<"discussion.closed">, TLiteral<"discussion.created">, TLiteral<"discussion.deleted">, TLiteral<"discussion.edited">, TLiteral<"discussion.labeled">, TLiteral<"discussion.locked">, TLiteral<"discussion.pinned">, TLiteral<"discussion.reopened">, TLiteral<"discussion.transferred">, TLiteral<"discussion.unanswered">, TLiteral<"discussion.unlabeled">, TLiteral<"discussion.unlocked">, TLiteral<"discussion.unpinned">, TLiteral<"discussion_comment">, TLiteral<"discussion_comment.created">, TLiteral<"discussion_comment.deleted">, TLiteral<"discussion_comment.edited">, TLiteral<"fork">, TLiteral<"github_app_authorization">, TLiteral<"github_app_authorization.revoked">, TLiteral<"gollum">, TLiteral<"installation">, TLiteral<"installation.created">, TLiteral<"installation.deleted">, TLiteral<"installation.new_permissions_accepted">, TLiteral<"installation.suspend">, TLiteral<"installation.unsuspend">, TLiteral<"installation_repositories">, TLiteral<"installation_repositories.added">, TLiteral<"installation_repositories.removed">, TLiteral<"installation_target">, TLiteral<"installation_target.renamed">, TLiteral<"issue_comment">, TLiteral<"issue_comment.created">, TLiteral<"issue_comment.deleted">, TLiteral<"issue_comment.edited">, TLiteral<"issues">, TLiteral<"issues.assigned">, TLiteral<"issues.closed">, TLiteral<"issues.deleted">, TLiteral<"issues.demilestoned">, TLiteral<"issues.edited">, TLiteral<"issues.labeled">, TLiteral<"issues.locked">, TLiteral<"issues.milestoned">, TLiteral<"issues.opened">, TLiteral<"issues.pinned">, TLiteral<"issues.reopened">, TLiteral<"issues.transferred">, TLiteral<"issues.typed">, TLiteral<"issues.unassigned">, TLiteral<"issues.unlabeled">, TLiteral<"issues.unlocked">, TLiteral<"issues.unpinned">, TLiteral<"issues.untyped">, TLiteral<"label">, TLiteral<"label.created">, TLiteral<"label.deleted">, TLiteral<"label.edited">, TLiteral<"marketplace_purchase">, TLiteral<"marketplace_purchase.cancelled">, TLiteral<"marketplace_purchase.changed">, TLiteral<"marketplace_purchase.pending_change">, TLiteral<"marketplace_purchase.pending_change_cancelled">, TLiteral<"marketplace_purchase.purchased">, TLiteral<"member">, TLiteral<"member.added">, TLiteral<"member.edited">, TLiteral<"member.removed">, TLiteral<"membership">, TLiteral<"membership.added">, TLiteral<"membership.removed">, TLiteral<"merge_group">, TLiteral<"merge_group.checks_requested">, TLiteral<"merge_group.destroyed">, TLiteral<"meta">, TLiteral<"meta.deleted">, TLiteral<"milestone">, TLiteral<"milestone.closed">, TLiteral<"milestone.created">, TLiteral<"milestone.deleted">, TLiteral<"milestone.edited">, TLiteral<"milestone.opened">, TLiteral<"org_block">, TLiteral<"org_block.blocked">, TLiteral<"org_block.unblocked">, TLiteral<"organization">, TLiteral<"organization.deleted">, TLiteral<"organization.member_added">, TLiteral<"organization.member_invited">, TLiteral<"organization.member_removed">, TLiteral<"organization.renamed">, TLiteral<"package">, TLiteral<"package.published">, TLiteral<"package.updated">, TLiteral<"page_build">, TLiteral<"personal_access_token_request">, TLiteral<"personal_access_token_request.approved">, TLiteral<"personal_access_token_request.cancelled">, TLiteral<"personal_access_token_request.created">, TLiteral<"personal_access_token_request.denied">, TLiteral<"ping">, TLiteral<"project">, TLiteral<"project.closed">, TLiteral<"project.created">, TLiteral<"project.deleted">, TLiteral<"project.edited">, TLiteral<"project.reopened">, TLiteral<"project_card">, TLiteral<"project_card.converted">, TLiteral<"project_card.created">, TLiteral<"project_card.deleted">, TLiteral<"project_card.edited">, TLiteral<"project_card.moved">, TLiteral<"project_column">, TLiteral<"project_column.created">, TLiteral<"project_column.deleted">, TLiteral<"project_column.edited">, TLiteral<"project_column.moved">, TLiteral<"projects_v2">, TLiteral<"projects_v2.closed">, TLiteral<"projects_v2.created">, TLiteral<"projects_v2.deleted">, TLiteral<"projects_v2.edited">, TLiteral<"projects_v2.reopened">, TLiteral<"projects_v2_item">, TLiteral<"projects_v2_item.archived">, TLiteral<"projects_v2_item.converted">, TLiteral<"projects_v2_item.created">, TLiteral<"projects_v2_item.deleted">, TLiteral<"projects_v2_item.edited">, TLiteral<"projects_v2_item.reordered">, TLiteral<"projects_v2_item.restored">, TLiteral<"projects_v2_status_update">, TLiteral<"projects_v2_status_update.created">, TLiteral<"projects_v2_status_update.deleted">, TLiteral<"projects_v2_status_update.edited">, TLiteral<"public">, TLiteral<"pull_request">, TLiteral<"pull_request.assigned">, TLiteral<"pull_request.auto_merge_disabled">, TLiteral<"pull_request.auto_merge_enabled">, TLiteral<"pull_request.closed">, TLiteral<"pull_request.converted_to_draft">, TLiteral<"pull_request.demilestoned">, TLiteral<"pull_request.dequeued">, TLiteral<"pull_request.edited">, TLiteral<"pull_request.enqueued">, TLiteral<"pull_request.labeled">, TLiteral<"pull_request.locked">, TLiteral<"pull_request.milestoned">, TLiteral<"pull_request.opened">, TLiteral<"pull_request.ready_for_review">, TLiteral<"pull_request.reopened">, TLiteral<"pull_request.review_request_removed">, TLiteral<"pull_request.review_requested">, TLiteral<"pull_request.synchronize">, TLiteral<"pull_request.unassigned">, TLiteral<"pull_request.unlabeled">, TLiteral<"pull_request.unlocked">, TLiteral<"pull_request_review">, TLiteral<"pull_request_review.dismissed">, TLiteral<"pull_request_review.edited">, TLiteral<"pull_request_review.submitted">, TLiteral<"pull_request_review_comment">, TLiteral<"pull_request_review_comment.created">, TLiteral<"pull_request_review_comment.deleted">, TLiteral<"pull_request_review_comment.edited">, TLiteral<"pull_request_review_thread">, TLiteral<"pull_request_review_thread.resolved">, TLiteral<"pull_request_review_thread.unresolved">, TLiteral<"push">, TLiteral<"registry_package">, TLiteral<"registry_package.published">, TLiteral<"registry_package.updated">, TLiteral<"release">, TLiteral<"release.created">, TLiteral<"release.deleted">, TLiteral<"release.edited">, TLiteral<"release.prereleased">, TLiteral<"release.published">, TLiteral<"release.released">, TLiteral<"release.unpublished">, TLiteral<"repository">, TLiteral<"repository.archived">, TLiteral<"repository.created">, TLiteral<"repository.deleted">, TLiteral<"repository.edited">, TLiteral<"repository.privatized">, TLiteral<"repository.publicized">, TLiteral<"repository.renamed">, TLiteral<"repository.transferred">, TLiteral<"repository.unarchived">, TLiteral<"repository_advisory">, TLiteral<"repository_advisory.published">, TLiteral<"repository_advisory.reported">, TLiteral<"repository_dispatch">, TLiteral<"repository_dispatch.sample.collected">, TLiteral<"repository_import">, TLiteral<"repository_ruleset">, TLiteral<"repository_ruleset.created">, TLiteral<"repository_ruleset.deleted">, TLiteral<"repository_ruleset.edited">, TLiteral<"repository_vulnerability_alert">, TLiteral<"repository_vulnerability_alert.create">, TLiteral<"repository_vulnerability_alert.dismiss">, TLiteral<"repository_vulnerability_alert.reopen">, TLiteral<"repository_vulnerability_alert.resolve">, TLiteral<"secret_scanning_alert">, TLiteral<"secret_scanning_alert.created">, TLiteral<"secret_scanning_alert.publicly_leaked">, TLiteral<"secret_scanning_alert.reopened">, TLiteral<"secret_scanning_alert.resolved">, TLiteral<"secret_scanning_alert.validated">, TLiteral<"secret_scanning_alert_location">, TLiteral<"secret_scanning_alert_location.created">, TLiteral<"secret_scanning_scan">, TLiteral<"secret_scanning_scan.completed">, TLiteral<"security_advisory">, TLiteral<"security_advisory.published">, TLiteral<"security_advisory.updated">, TLiteral<"security_advisory.withdrawn">, TLiteral<"security_and_analysis">, TLiteral<"sponsorship">, TLiteral<"sponsorship.cancelled">, TLiteral<"sponsorship.created">, TLiteral<"sponsorship.edited">, TLiteral<"sponsorship.pending_cancellation">, TLiteral<"sponsorship.pending_tier_change">, TLiteral<"sponsorship.tier_changed">, TLiteral<"star">, TLiteral<"star.created">, TLiteral<"star.deleted">, TLiteral<"status">, TLiteral<"sub_issues">, TLiteral<"sub_issues.parent_issue_added">, TLiteral<"sub_issues.parent_issue_removed">, TLiteral<"sub_issues.sub_issue_added">, TLiteral<"sub_issues.sub_issue_removed">, TLiteral<"team">, TLiteral<"team.added_to_repository">, TLiteral<"team.created">, TLiteral<"team.deleted">, TLiteral<"team.edited">, TLiteral<"team.removed_from_repository">, TLiteral<"team_add">, TLiteral<"watch">, TLiteral<"watch.started">, TLiteral<"workflow_dispatch">, TLiteral<"workflow_job">, TLiteral<"workflow_job.completed">, TLiteral<"workflow_job.in_progress">, TLiteral<"workflow_job.queued">, TLiteral<"workflow_job.waiting">, TLiteral<"workflow_run">, TLiteral<"workflow_run.completed">, TLiteral<"workflow_run.in_progress">, TLiteral<"workflow_run.requested">]>>>;
@@ -46,6 +47,7 @@ interface LoggerInterface {
46
47
  debug(message: string, metadata?: Record<string, unknown>): void;
47
48
  error(message: string, metadata?: Record<string, unknown>): void;
48
49
  info(message: string, metadata?: Record<string, unknown>): void;
50
+ ok(message: string, metadata?: Record<string, unknown>): void;
49
51
  warn(message: string, metadata?: Record<string, unknown>): void;
50
52
  }
51
53
  /**
@@ -73,6 +75,7 @@ declare class ConfigurationHandler {
73
75
  * @returns The merged plugin configuration with resolved plugin settings.
74
76
  */
75
77
  getConfiguration(location?: Location): Promise<{
78
+ imports?: string[] | undefined;
76
79
  plugins: {
77
80
  [x: string]: {
78
81
  skipBotEvents?: boolean | undefined;
@@ -95,6 +98,7 @@ declare class ConfigurationHandler {
95
98
  rawData: null;
96
99
  } | {
97
100
  config: {
101
+ imports?: string[] | undefined;
98
102
  plugins: {
99
103
  [x: string]: {
100
104
  skipBotEvents?: boolean | undefined;
@@ -502,7 +502,7 @@ var int = new type("tag:yaml.org,2002:int", {
502
502
  decimal: function(obj) {
503
503
  return obj.toString(10);
504
504
  },
505
-
505
+ /* eslint-disable max-len */
506
506
  hexadecimal: function(obj) {
507
507
  return obj >= 0 ? "0x" + obj.toString(16).toUpperCase() : "-0x" + obj.toString(16).toUpperCase().slice(1);
508
508
  }
@@ -2693,6 +2693,7 @@ var pluginSettingsSchema = import_typebox.Type.Union(
2693
2693
  );
2694
2694
  var configSchema = import_typebox.Type.Object(
2695
2695
  {
2696
+ imports: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String(), { default: [] })),
2696
2697
  plugins: import_typebox.Type.Record(import_typebox.Type.String(), pluginSettingsSchema, { default: {} })
2697
2698
  },
2698
2699
  {
@@ -2761,12 +2762,12 @@ var ConfigurationHandler = class {
2761
2762
  async getConfiguration(location) {
2762
2763
  const defaultConfiguration = import_value.Value.Decode(configSchema, import_value.Value.Default(configSchema, {}));
2763
2764
  if (!location) {
2764
- this._logger.debug("No location was provided, using the default configuration");
2765
+ this._logger.info("No location was provided, using the default configuration");
2765
2766
  return defaultConfiguration;
2766
2767
  }
2767
2768
  const { owner, repo } = location;
2768
2769
  let mergedConfiguration = defaultConfiguration;
2769
- this._logger.debug("Fetching configurations from the organization and repository", {
2770
+ this._logger.info("Fetching configurations from the organization and repository", {
2770
2771
  orgRepo: `${owner}/${CONFIG_ORG_REPO}`,
2771
2772
  repo: `${owner}/${repo}`
2772
2773
  });
@@ -2779,13 +2780,13 @@ var ConfigurationHandler = class {
2779
2780
  mergedConfiguration = this.mergeConfigurations(mergedConfiguration, repoConfig.config);
2780
2781
  }
2781
2782
  const resolvedPlugins = {};
2782
- this._logger.debug("Found plugins enabled", { repo: `${owner}/${repo}`, plugins: Object.keys(mergedConfiguration.plugins).length });
2783
+ this._logger.ok("Found plugins enabled", { repo: `${owner}/${repo}`, plugins: Object.keys(mergedConfiguration.plugins).length });
2783
2784
  for (const [pluginKey, pluginSettings] of Object.entries(mergedConfiguration.plugins)) {
2784
2785
  let pluginIdentifier;
2785
2786
  try {
2786
2787
  pluginIdentifier = parsePluginIdentifier(pluginKey);
2787
2788
  } catch (error) {
2788
- this._logger.error("Invalid plugin identifier; skipping", { plugin: pluginKey, err: error });
2789
+ this._logger.warn("Invalid plugin identifier; skipping", { plugin: pluginKey, err: error });
2789
2790
  continue;
2790
2791
  }
2791
2792
  const manifest = await this.getManifest(pluginIdentifier);
@@ -2824,14 +2825,14 @@ var ConfigurationHandler = class {
2824
2825
  repository,
2825
2826
  owner
2826
2827
  });
2827
- this._logger.debug("Downloaded configuration file", { owner, repository });
2828
+ this._logger.ok("Downloaded configuration file", { owner, repository });
2828
2829
  if (!rawData) {
2829
- this._logger.debug("No raw configuration data", { owner, repository });
2830
+ this._logger.warn("No raw configuration data", { owner, repository });
2830
2831
  return { config: null, errors: null, rawData: null };
2831
2832
  }
2832
2833
  const { yaml, errors } = this._parseYaml(rawData);
2833
2834
  const targetRepoConfiguration = yaml;
2834
- this._logger.debug("Decoding configuration", { owner, repository });
2835
+ this._logger.info("Decoding configuration", { owner, repository });
2835
2836
  if (targetRepoConfiguration) {
2836
2837
  try {
2837
2838
  const configSchemaWithDefaults = import_value.Value.Default(configSchema, targetRepoConfiguration);
@@ -2844,16 +2845,16 @@ var ConfigurationHandler = class {
2844
2845
  const decodedConfig = import_value.Value.Decode(configSchema, configSchemaWithDefaults);
2845
2846
  return { config: decodedConfig, errors: errors2.First() ? errors2 : null, rawData };
2846
2847
  } catch (error) {
2847
- this._logger.error("Error decoding configuration; Will ignore.", { err: error, owner, repository });
2848
+ this._logger.warn("Error decoding configuration; Will ignore.", { err: error, owner, repository });
2848
2849
  return { config: null, errors: [error instanceof import_value.TransformDecodeCheckError ? error.error : error], rawData };
2849
2850
  }
2850
2851
  }
2851
- this._logger.error("YAML could not be decoded", { owner, repository, errors });
2852
+ this._logger.warn("YAML could not be decoded", { owner, repository, errors });
2852
2853
  return { config: null, errors, rawData };
2853
2854
  }
2854
2855
  async _download({ repository, owner }) {
2855
2856
  if (!repository || !owner) {
2856
- this._logger.error("Repo or owner is not defined, cannot download the requested file");
2857
+ this._logger.warn("Repo or owner is not defined, cannot download the requested file");
2857
2858
  return null;
2858
2859
  }
2859
2860
  let pathList;
@@ -2869,38 +2870,44 @@ var ConfigurationHandler = class {
2869
2870
  }
2870
2871
  for (const filePath of pathList) {
2871
2872
  try {
2872
- this._logger.debug("Attempting to fetch configuration", { owner, repository, filePath });
2873
+ this._logger.info("Attempting to fetch configuration", { owner, repository, filePath });
2873
2874
  const { data, headers } = await this._octokit.rest.repos.getContent({
2874
2875
  owner,
2875
2876
  repo: repository,
2876
2877
  path: filePath,
2877
2878
  mediaType: { format: "raw" }
2878
2879
  });
2879
- this._logger.debug("Configuration file found", { owner, repository, filePath, rateLimitRemaining: headers?.["x-ratelimit-remaining"], data });
2880
+ this._logger.ok("Configuration file found", { owner, repository, filePath, rateLimitRemaining: headers?.["x-ratelimit-remaining"], data });
2880
2881
  return data;
2881
2882
  } catch (err) {
2882
2883
  if (err && typeof err === "object" && "status" in err && err.status === 404) {
2883
2884
  this._logger.warn("No configuration file found", { owner, repository, filePath });
2884
2885
  } else {
2885
- this._logger.error("Failed to download the requested file", { err, owner, repository, filePath });
2886
+ const status = err && typeof err === "object" && "status" in err ? Number(err.status) : null;
2887
+ const metadata = { err, owner, repository, filePath, ...status ? { status } : {} };
2888
+ if (status && status >= 500) {
2889
+ this._logger.error("Failed to download the requested file", metadata);
2890
+ } else {
2891
+ this._logger.warn("Failed to download the requested file", metadata);
2892
+ }
2886
2893
  }
2887
2894
  }
2888
2895
  }
2889
2896
  return null;
2890
2897
  }
2891
2898
  _parseYaml(data) {
2892
- this._logger.debug("Will attempt to parse YAML data", { data });
2899
+ this._logger.info("Will attempt to parse YAML data", { data });
2893
2900
  try {
2894
2901
  if (data) {
2895
2902
  const parsedData = jsYaml.load(data);
2896
- this._logger.debug("Parsed yaml data", { parsedData });
2903
+ this._logger.ok("Parsed yaml data", { parsedData });
2897
2904
  return { yaml: parsedData ?? null, errors: null };
2898
2905
  }
2899
2906
  } catch (error) {
2900
- this._logger.error("Error parsing YAML", { error });
2907
+ this._logger.warn("Error parsing YAML", { error });
2901
2908
  return { errors: [error], yaml: null };
2902
2909
  }
2903
- this._logger.debug("Could not parse YAML");
2910
+ this._logger.warn("Could not parse YAML");
2904
2911
  return { yaml: null, errors: null };
2905
2912
  }
2906
2913
  mergeConfigurations(configuration1, configuration2) {
@@ -2941,7 +2948,7 @@ var ConfigurationHandler = class {
2941
2948
  return manifest;
2942
2949
  }
2943
2950
  } catch (e) {
2944
- this._logger.error("Could not find a valid manifest", { owner, repo, err: e });
2951
+ this._logger.warn("Could not find a valid manifest", { owner, repo, err: e });
2945
2952
  }
2946
2953
  return null;
2947
2954
  })();
@@ -2956,7 +2963,7 @@ var ConfigurationHandler = class {
2956
2963
  const errors = [...import_value.Value.Errors(manifestSchema, manifest)];
2957
2964
  if (errors.length) {
2958
2965
  for (const error of errors) {
2959
- this._logger.error("Manifest validation error", { error });
2966
+ this._logger.warn("Manifest validation error", { error });
2960
2967
  }
2961
2968
  throw new Error("Manifest is invalid.");
2962
2969
  }
@@ -475,7 +475,7 @@ var int = new type("tag:yaml.org,2002:int", {
475
475
  decimal: function(obj) {
476
476
  return obj.toString(10);
477
477
  },
478
-
478
+ /* eslint-disable max-len */
479
479
  hexadecimal: function(obj) {
480
480
  return obj >= 0 ? "0x" + obj.toString(16).toUpperCase() : "-0x" + obj.toString(16).toUpperCase().slice(1);
481
481
  }
@@ -2666,6 +2666,7 @@ var pluginSettingsSchema = T.Union(
2666
2666
  );
2667
2667
  var configSchema = T.Object(
2668
2668
  {
2669
+ imports: T.Optional(T.Array(T.String(), { default: [] })),
2669
2670
  plugins: T.Record(T.String(), pluginSettingsSchema, { default: {} })
2670
2671
  },
2671
2672
  {
@@ -2734,12 +2735,12 @@ var ConfigurationHandler = class {
2734
2735
  async getConfiguration(location) {
2735
2736
  const defaultConfiguration = Value.Decode(configSchema, Value.Default(configSchema, {}));
2736
2737
  if (!location) {
2737
- this._logger.debug("No location was provided, using the default configuration");
2738
+ this._logger.info("No location was provided, using the default configuration");
2738
2739
  return defaultConfiguration;
2739
2740
  }
2740
2741
  const { owner, repo } = location;
2741
2742
  let mergedConfiguration = defaultConfiguration;
2742
- this._logger.debug("Fetching configurations from the organization and repository", {
2743
+ this._logger.info("Fetching configurations from the organization and repository", {
2743
2744
  orgRepo: `${owner}/${CONFIG_ORG_REPO}`,
2744
2745
  repo: `${owner}/${repo}`
2745
2746
  });
@@ -2752,13 +2753,13 @@ var ConfigurationHandler = class {
2752
2753
  mergedConfiguration = this.mergeConfigurations(mergedConfiguration, repoConfig.config);
2753
2754
  }
2754
2755
  const resolvedPlugins = {};
2755
- this._logger.debug("Found plugins enabled", { repo: `${owner}/${repo}`, plugins: Object.keys(mergedConfiguration.plugins).length });
2756
+ this._logger.ok("Found plugins enabled", { repo: `${owner}/${repo}`, plugins: Object.keys(mergedConfiguration.plugins).length });
2756
2757
  for (const [pluginKey, pluginSettings] of Object.entries(mergedConfiguration.plugins)) {
2757
2758
  let pluginIdentifier;
2758
2759
  try {
2759
2760
  pluginIdentifier = parsePluginIdentifier(pluginKey);
2760
2761
  } catch (error) {
2761
- this._logger.error("Invalid plugin identifier; skipping", { plugin: pluginKey, err: error });
2762
+ this._logger.warn("Invalid plugin identifier; skipping", { plugin: pluginKey, err: error });
2762
2763
  continue;
2763
2764
  }
2764
2765
  const manifest = await this.getManifest(pluginIdentifier);
@@ -2797,14 +2798,14 @@ var ConfigurationHandler = class {
2797
2798
  repository,
2798
2799
  owner
2799
2800
  });
2800
- this._logger.debug("Downloaded configuration file", { owner, repository });
2801
+ this._logger.ok("Downloaded configuration file", { owner, repository });
2801
2802
  if (!rawData) {
2802
- this._logger.debug("No raw configuration data", { owner, repository });
2803
+ this._logger.warn("No raw configuration data", { owner, repository });
2803
2804
  return { config: null, errors: null, rawData: null };
2804
2805
  }
2805
2806
  const { yaml, errors } = this._parseYaml(rawData);
2806
2807
  const targetRepoConfiguration = yaml;
2807
- this._logger.debug("Decoding configuration", { owner, repository });
2808
+ this._logger.info("Decoding configuration", { owner, repository });
2808
2809
  if (targetRepoConfiguration) {
2809
2810
  try {
2810
2811
  const configSchemaWithDefaults = Value.Default(configSchema, targetRepoConfiguration);
@@ -2817,16 +2818,16 @@ var ConfigurationHandler = class {
2817
2818
  const decodedConfig = Value.Decode(configSchema, configSchemaWithDefaults);
2818
2819
  return { config: decodedConfig, errors: errors2.First() ? errors2 : null, rawData };
2819
2820
  } catch (error) {
2820
- this._logger.error("Error decoding configuration; Will ignore.", { err: error, owner, repository });
2821
+ this._logger.warn("Error decoding configuration; Will ignore.", { err: error, owner, repository });
2821
2822
  return { config: null, errors: [error instanceof TransformDecodeCheckError ? error.error : error], rawData };
2822
2823
  }
2823
2824
  }
2824
- this._logger.error("YAML could not be decoded", { owner, repository, errors });
2825
+ this._logger.warn("YAML could not be decoded", { owner, repository, errors });
2825
2826
  return { config: null, errors, rawData };
2826
2827
  }
2827
2828
  async _download({ repository, owner }) {
2828
2829
  if (!repository || !owner) {
2829
- this._logger.error("Repo or owner is not defined, cannot download the requested file");
2830
+ this._logger.warn("Repo or owner is not defined, cannot download the requested file");
2830
2831
  return null;
2831
2832
  }
2832
2833
  let pathList;
@@ -2842,38 +2843,44 @@ var ConfigurationHandler = class {
2842
2843
  }
2843
2844
  for (const filePath of pathList) {
2844
2845
  try {
2845
- this._logger.debug("Attempting to fetch configuration", { owner, repository, filePath });
2846
+ this._logger.info("Attempting to fetch configuration", { owner, repository, filePath });
2846
2847
  const { data, headers } = await this._octokit.rest.repos.getContent({
2847
2848
  owner,
2848
2849
  repo: repository,
2849
2850
  path: filePath,
2850
2851
  mediaType: { format: "raw" }
2851
2852
  });
2852
- this._logger.debug("Configuration file found", { owner, repository, filePath, rateLimitRemaining: headers?.["x-ratelimit-remaining"], data });
2853
+ this._logger.ok("Configuration file found", { owner, repository, filePath, rateLimitRemaining: headers?.["x-ratelimit-remaining"], data });
2853
2854
  return data;
2854
2855
  } catch (err) {
2855
2856
  if (err && typeof err === "object" && "status" in err && err.status === 404) {
2856
2857
  this._logger.warn("No configuration file found", { owner, repository, filePath });
2857
2858
  } else {
2858
- this._logger.error("Failed to download the requested file", { err, owner, repository, filePath });
2859
+ const status = err && typeof err === "object" && "status" in err ? Number(err.status) : null;
2860
+ const metadata = { err, owner, repository, filePath, ...status ? { status } : {} };
2861
+ if (status && status >= 500) {
2862
+ this._logger.error("Failed to download the requested file", metadata);
2863
+ } else {
2864
+ this._logger.warn("Failed to download the requested file", metadata);
2865
+ }
2859
2866
  }
2860
2867
  }
2861
2868
  }
2862
2869
  return null;
2863
2870
  }
2864
2871
  _parseYaml(data) {
2865
- this._logger.debug("Will attempt to parse YAML data", { data });
2872
+ this._logger.info("Will attempt to parse YAML data", { data });
2866
2873
  try {
2867
2874
  if (data) {
2868
2875
  const parsedData = jsYaml.load(data);
2869
- this._logger.debug("Parsed yaml data", { parsedData });
2876
+ this._logger.ok("Parsed yaml data", { parsedData });
2870
2877
  return { yaml: parsedData ?? null, errors: null };
2871
2878
  }
2872
2879
  } catch (error) {
2873
- this._logger.error("Error parsing YAML", { error });
2880
+ this._logger.warn("Error parsing YAML", { error });
2874
2881
  return { errors: [error], yaml: null };
2875
2882
  }
2876
- this._logger.debug("Could not parse YAML");
2883
+ this._logger.warn("Could not parse YAML");
2877
2884
  return { yaml: null, errors: null };
2878
2885
  }
2879
2886
  mergeConfigurations(configuration1, configuration2) {
@@ -2914,7 +2921,7 @@ var ConfigurationHandler = class {
2914
2921
  return manifest;
2915
2922
  }
2916
2923
  } catch (e) {
2917
- this._logger.error("Could not find a valid manifest", { owner, repo, err: e });
2924
+ this._logger.warn("Could not find a valid manifest", { owner, repo, err: e });
2918
2925
  }
2919
2926
  return null;
2920
2927
  })();
@@ -2929,7 +2936,7 @@ var ConfigurationHandler = class {
2929
2936
  const errors = [...Value.Errors(manifestSchema, manifest)];
2930
2937
  if (errors.length) {
2931
2938
  for (const error of errors) {
2932
- this._logger.error("Manifest validation error", { error });
2939
+ this._logger.warn("Manifest validation error", { error });
2933
2940
  }
2934
2941
  throw new Error("Manifest is invalid.");
2935
2942
  }
package/dist/index.d.mts CHANGED
@@ -14,8 +14,8 @@ import '@octokit/plugin-paginate-graphql';
14
14
  import '@octokit/plugin-paginate-rest';
15
15
  import '@octokit/request-error';
16
16
  import '@octokit/core';
17
- import './signature.mjs';
18
17
  import 'openai/resources/chat/completions';
18
+ import './signature.mjs';
19
19
 
20
20
  type Return = Record<string, unknown> | undefined | void;
21
21
  type HandlerReturn = Promise<Return> | Return;
package/dist/index.d.ts CHANGED
@@ -14,8 +14,8 @@ import '@octokit/plugin-paginate-graphql';
14
14
  import '@octokit/plugin-paginate-rest';
15
15
  import '@octokit/request-error';
16
16
  import '@octokit/core';
17
- import './signature.js';
18
17
  import 'openai/resources/chat/completions';
18
+ import './signature.js';
19
19
 
20
20
  type Return = Record<string, unknown> | undefined | void;
21
21
  type HandlerReturn = Promise<Return> | Return;
package/dist/index.js CHANGED
@@ -700,7 +700,7 @@ ${metadataContent}
700
700
  async postComment(context2, message, options = { updateComment: true, raw: false }) {
701
701
  const issueContext = this._extractIssueContext(context2);
702
702
  if (!issueContext) {
703
- context2.logger.info("Cannot post comment: missing issue context in payload");
703
+ context2.logger.warn("Cannot post comment: missing issue context in payload");
704
704
  return null;
705
705
  }
706
706
  const body = this._createCommentBody(context2, message, options);
@@ -2772,86 +2772,150 @@ function createPlugin(handler, manifest, options) {
2772
2772
  }
2773
2773
 
2774
2774
  // src/llm/index.ts
2775
+ var EMPTY_STRING = "";
2775
2776
  function normalizeBaseUrl(baseUrl) {
2776
- return baseUrl.trim().replace(/\/+$/, "");
2777
+ let normalized = baseUrl.trim();
2778
+ while (normalized.endsWith("/")) {
2779
+ normalized = normalized.slice(0, -1);
2780
+ }
2781
+ return normalized;
2777
2782
  }
2778
2783
  function getEnvString(name) {
2779
- if (typeof process === "undefined" || !process?.env) return "";
2780
- return String(process.env[name] ?? "").trim();
2784
+ if (typeof process === "undefined" || !process?.env) return EMPTY_STRING;
2785
+ return String(process.env[name] ?? EMPTY_STRING).trim();
2781
2786
  }
2782
2787
  function getAiBaseUrl(options) {
2783
2788
  if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
2784
2789
  return normalizeBaseUrl(options.baseUrl);
2785
2790
  }
2786
- const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
2791
+ const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
2787
2792
  if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
2788
- return "https://ai.ubq.fi";
2793
+ return "https://ai-ubq-fi.deno.dev";
2789
2794
  }
2790
2795
  async function callLlm(options, input) {
2791
- const authToken = input?.authToken ?? "";
2792
- const ubiquityKernelToken = input?.ubiquityKernelToken ?? "";
2793
- const payload = input?.payload;
2794
- const owner = payload?.repository?.owner?.login ?? "";
2795
- const repo = payload?.repository?.name ?? "";
2796
- const installationId = payload?.installation?.id;
2797
- if (!authToken) throw new Error("Missing authToken in inputs");
2798
- const requiresKernelToken = authToken.trim().startsWith("gh");
2799
- if (requiresKernelToken && !ubiquityKernelToken) {
2800
- throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
2801
- }
2802
- const url = `${getAiBaseUrl(options)}/v1/chat/completions`;
2803
- const { baseUrl: _baseUrl, model, stream, messages, ...rest } = options;
2796
+ const authToken = String(input.authToken ?? EMPTY_STRING).trim();
2797
+ if (!authToken) throw new Error("Missing authToken in input");
2798
+ const kernelToken = "ubiquityKernelToken" in input ? input.ubiquityKernelToken : void 0;
2799
+ const payload = getPayload(input);
2800
+ const { owner, repo, installationId } = getRepoMetadata(payload);
2801
+ ensureKernelToken(authToken, kernelToken);
2802
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
2803
+ ensureMessages(messages);
2804
+ const url = buildAiUrl(options, baseUrl);
2804
2805
  const body = JSON.stringify({
2805
2806
  ...rest,
2806
2807
  ...model ? { model } : {},
2807
2808
  messages,
2808
- stream: stream ?? false
2809
+ stream: isStream ?? false
2810
+ });
2811
+ const headers = buildHeaders(authToken, {
2812
+ owner,
2813
+ repo,
2814
+ installationId,
2815
+ ubiquityKernelToken: kernelToken
2809
2816
  });
2810
- const headers = {
2811
- Authorization: `Bearer ${authToken}`,
2812
- "Content-Type": "application/json"
2813
- };
2814
- if (owner) headers["X-GitHub-Owner"] = owner;
2815
- if (repo) headers["X-GitHub-Repo"] = repo;
2816
- if (typeof installationId === "number" && Number.isFinite(installationId)) {
2817
- headers["X-GitHub-Installation-Id"] = String(installationId);
2818
- }
2819
- if (ubiquityKernelToken) {
2820
- headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
2821
- }
2822
2817
  const response = await fetch(url, { method: "POST", headers, body });
2823
2818
  if (!response.ok) {
2824
2819
  const err = await response.text();
2825
- throw new Error(`LLM API error: ${response.status} - ${err}`);
2820
+ const error = new Error(`LLM API error: ${response.status} - ${err}`);
2821
+ error.status = response.status;
2822
+ throw error;
2826
2823
  }
2827
- if (options.stream) {
2824
+ if (isStream) {
2825
+ if (!response.body) {
2826
+ throw new Error("LLM API error: missing response body for streaming request");
2827
+ }
2828
2828
  return parseSseStream(response.body);
2829
2829
  }
2830
2830
  return response.json();
2831
2831
  }
2832
+ function ensureKernelToken(authToken, kernelToken) {
2833
+ const isKernelTokenRequired = authToken.startsWith("gh");
2834
+ if (isKernelTokenRequired && !kernelToken) {
2835
+ throw new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
2836
+ }
2837
+ }
2838
+ function ensureMessages(messages) {
2839
+ if (!Array.isArray(messages) || messages.length === 0) {
2840
+ throw new Error("messages must be a non-empty array");
2841
+ }
2842
+ }
2843
+ function buildAiUrl(options, baseUrl) {
2844
+ return `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
2845
+ }
2846
+ function getPayload(input) {
2847
+ if ("payload" in input) {
2848
+ return input.payload;
2849
+ }
2850
+ return input.eventPayload;
2851
+ }
2852
+ function getRepoMetadata(payload) {
2853
+ const repoPayload = payload;
2854
+ return {
2855
+ owner: repoPayload?.repository?.owner?.login ?? EMPTY_STRING,
2856
+ repo: repoPayload?.repository?.name ?? EMPTY_STRING,
2857
+ installationId: repoPayload?.installation?.id
2858
+ };
2859
+ }
2860
+ function buildHeaders(authToken, options) {
2861
+ const headers = {
2862
+ Authorization: `Bearer ${authToken}`,
2863
+ "Content-Type": "application/json"
2864
+ };
2865
+ if (options.owner) headers["X-GitHub-Owner"] = options.owner;
2866
+ if (options.repo) headers["X-GitHub-Repo"] = options.repo;
2867
+ if (typeof options.installationId === "number" && Number.isFinite(options.installationId)) {
2868
+ headers["X-GitHub-Installation-Id"] = String(options.installationId);
2869
+ }
2870
+ if (options.ubiquityKernelToken) {
2871
+ headers["X-Ubiquity-Kernel-Token"] = options.ubiquityKernelToken;
2872
+ }
2873
+ return headers;
2874
+ }
2832
2875
  async function* parseSseStream(body) {
2833
2876
  const reader = body.getReader();
2834
2877
  const decoder = new TextDecoder();
2835
- let buffer = "";
2878
+ let buffer = EMPTY_STRING;
2836
2879
  try {
2837
2880
  while (true) {
2838
- const { value, done } = await reader.read();
2839
- if (done) break;
2881
+ const { value, done: isDone } = await reader.read();
2882
+ if (isDone) break;
2840
2883
  buffer += decoder.decode(value, { stream: true });
2841
- const events = buffer.split("\n\n");
2842
- buffer = events.pop() || "";
2884
+ const { events, remainder } = splitSseEvents(buffer);
2885
+ buffer = remainder;
2843
2886
  for (const event of events) {
2844
- if (event.startsWith("data: ")) {
2845
- const data = event.slice(6);
2846
- if (data === "[DONE]") return;
2847
- yield JSON.parse(data);
2848
- }
2887
+ const data = getEventData(event);
2888
+ if (!data) continue;
2889
+ if (data.trim() === "[DONE]") return;
2890
+ yield parseEventData(data);
2849
2891
  }
2850
2892
  }
2851
2893
  } finally {
2852
2894
  reader.releaseLock();
2853
2895
  }
2854
2896
  }
2897
+ function splitSseEvents(buffer) {
2898
+ const normalized = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2899
+ const parts = normalized.split("\n\n");
2900
+ const remainder = parts.pop() ?? EMPTY_STRING;
2901
+ return { events: parts, remainder };
2902
+ }
2903
+ function getEventData(event) {
2904
+ if (!event.trim()) return null;
2905
+ const dataLines = event.split("\n").filter((line) => line.startsWith("data:"));
2906
+ if (!dataLines.length) return null;
2907
+ const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, "")).join("\n");
2908
+ return data || null;
2909
+ }
2910
+ function parseEventData(data) {
2911
+ try {
2912
+ return JSON.parse(data);
2913
+ } catch (error) {
2914
+ const message = error instanceof Error ? error.message : String(error);
2915
+ const preview = data.length > 200 ? `${data.slice(0, 200)}...` : data;
2916
+ throw new Error(`LLM stream parse error: ${message}. Data: ${preview}`);
2917
+ }
2918
+ }
2855
2919
  // Annotate the CommonJS export names for ESM import in node:
2856
2920
  0 && (module.exports = {
2857
2921
  CommentHandler,
package/dist/index.mjs CHANGED
@@ -659,7 +659,7 @@ ${metadataContent}
659
659
  async postComment(context2, message, options = { updateComment: true, raw: false }) {
660
660
  const issueContext = this._extractIssueContext(context2);
661
661
  if (!issueContext) {
662
- context2.logger.info("Cannot post comment: missing issue context in payload");
662
+ context2.logger.warn("Cannot post comment: missing issue context in payload");
663
663
  return null;
664
664
  }
665
665
  const body = this._createCommentBody(context2, message, options);
@@ -2731,86 +2731,150 @@ function createPlugin(handler, manifest, options) {
2731
2731
  }
2732
2732
 
2733
2733
  // src/llm/index.ts
2734
+ var EMPTY_STRING = "";
2734
2735
  function normalizeBaseUrl(baseUrl) {
2735
- return baseUrl.trim().replace(/\/+$/, "");
2736
+ let normalized = baseUrl.trim();
2737
+ while (normalized.endsWith("/")) {
2738
+ normalized = normalized.slice(0, -1);
2739
+ }
2740
+ return normalized;
2736
2741
  }
2737
2742
  function getEnvString(name) {
2738
- if (typeof process === "undefined" || !process?.env) return "";
2739
- return String(process.env[name] ?? "").trim();
2743
+ if (typeof process === "undefined" || !process?.env) return EMPTY_STRING;
2744
+ return String(process.env[name] ?? EMPTY_STRING).trim();
2740
2745
  }
2741
2746
  function getAiBaseUrl(options) {
2742
2747
  if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
2743
2748
  return normalizeBaseUrl(options.baseUrl);
2744
2749
  }
2745
- const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
2750
+ const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
2746
2751
  if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
2747
- return "https://ai.ubq.fi";
2752
+ return "https://ai-ubq-fi.deno.dev";
2748
2753
  }
2749
2754
  async function callLlm(options, input) {
2750
- const authToken = input?.authToken ?? "";
2751
- const ubiquityKernelToken = input?.ubiquityKernelToken ?? "";
2752
- const payload = input?.payload;
2753
- const owner = payload?.repository?.owner?.login ?? "";
2754
- const repo = payload?.repository?.name ?? "";
2755
- const installationId = payload?.installation?.id;
2756
- if (!authToken) throw new Error("Missing authToken in inputs");
2757
- const requiresKernelToken = authToken.trim().startsWith("gh");
2758
- if (requiresKernelToken && !ubiquityKernelToken) {
2759
- throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
2760
- }
2761
- const url = `${getAiBaseUrl(options)}/v1/chat/completions`;
2762
- const { baseUrl: _baseUrl, model, stream, messages, ...rest } = options;
2755
+ const authToken = String(input.authToken ?? EMPTY_STRING).trim();
2756
+ if (!authToken) throw new Error("Missing authToken in input");
2757
+ const kernelToken = "ubiquityKernelToken" in input ? input.ubiquityKernelToken : void 0;
2758
+ const payload = getPayload(input);
2759
+ const { owner, repo, installationId } = getRepoMetadata(payload);
2760
+ ensureKernelToken(authToken, kernelToken);
2761
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
2762
+ ensureMessages(messages);
2763
+ const url = buildAiUrl(options, baseUrl);
2763
2764
  const body = JSON.stringify({
2764
2765
  ...rest,
2765
2766
  ...model ? { model } : {},
2766
2767
  messages,
2767
- stream: stream ?? false
2768
+ stream: isStream ?? false
2769
+ });
2770
+ const headers = buildHeaders(authToken, {
2771
+ owner,
2772
+ repo,
2773
+ installationId,
2774
+ ubiquityKernelToken: kernelToken
2768
2775
  });
2769
- const headers = {
2770
- Authorization: `Bearer ${authToken}`,
2771
- "Content-Type": "application/json"
2772
- };
2773
- if (owner) headers["X-GitHub-Owner"] = owner;
2774
- if (repo) headers["X-GitHub-Repo"] = repo;
2775
- if (typeof installationId === "number" && Number.isFinite(installationId)) {
2776
- headers["X-GitHub-Installation-Id"] = String(installationId);
2777
- }
2778
- if (ubiquityKernelToken) {
2779
- headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
2780
- }
2781
2776
  const response = await fetch(url, { method: "POST", headers, body });
2782
2777
  if (!response.ok) {
2783
2778
  const err = await response.text();
2784
- throw new Error(`LLM API error: ${response.status} - ${err}`);
2779
+ const error = new Error(`LLM API error: ${response.status} - ${err}`);
2780
+ error.status = response.status;
2781
+ throw error;
2785
2782
  }
2786
- if (options.stream) {
2783
+ if (isStream) {
2784
+ if (!response.body) {
2785
+ throw new Error("LLM API error: missing response body for streaming request");
2786
+ }
2787
2787
  return parseSseStream(response.body);
2788
2788
  }
2789
2789
  return response.json();
2790
2790
  }
2791
+ function ensureKernelToken(authToken, kernelToken) {
2792
+ const isKernelTokenRequired = authToken.startsWith("gh");
2793
+ if (isKernelTokenRequired && !kernelToken) {
2794
+ throw new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
2795
+ }
2796
+ }
2797
+ function ensureMessages(messages) {
2798
+ if (!Array.isArray(messages) || messages.length === 0) {
2799
+ throw new Error("messages must be a non-empty array");
2800
+ }
2801
+ }
2802
+ function buildAiUrl(options, baseUrl) {
2803
+ return `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
2804
+ }
2805
+ function getPayload(input) {
2806
+ if ("payload" in input) {
2807
+ return input.payload;
2808
+ }
2809
+ return input.eventPayload;
2810
+ }
2811
+ function getRepoMetadata(payload) {
2812
+ const repoPayload = payload;
2813
+ return {
2814
+ owner: repoPayload?.repository?.owner?.login ?? EMPTY_STRING,
2815
+ repo: repoPayload?.repository?.name ?? EMPTY_STRING,
2816
+ installationId: repoPayload?.installation?.id
2817
+ };
2818
+ }
2819
+ function buildHeaders(authToken, options) {
2820
+ const headers = {
2821
+ Authorization: `Bearer ${authToken}`,
2822
+ "Content-Type": "application/json"
2823
+ };
2824
+ if (options.owner) headers["X-GitHub-Owner"] = options.owner;
2825
+ if (options.repo) headers["X-GitHub-Repo"] = options.repo;
2826
+ if (typeof options.installationId === "number" && Number.isFinite(options.installationId)) {
2827
+ headers["X-GitHub-Installation-Id"] = String(options.installationId);
2828
+ }
2829
+ if (options.ubiquityKernelToken) {
2830
+ headers["X-Ubiquity-Kernel-Token"] = options.ubiquityKernelToken;
2831
+ }
2832
+ return headers;
2833
+ }
2791
2834
  async function* parseSseStream(body) {
2792
2835
  const reader = body.getReader();
2793
2836
  const decoder = new TextDecoder();
2794
- let buffer = "";
2837
+ let buffer = EMPTY_STRING;
2795
2838
  try {
2796
2839
  while (true) {
2797
- const { value, done } = await reader.read();
2798
- if (done) break;
2840
+ const { value, done: isDone } = await reader.read();
2841
+ if (isDone) break;
2799
2842
  buffer += decoder.decode(value, { stream: true });
2800
- const events = buffer.split("\n\n");
2801
- buffer = events.pop() || "";
2843
+ const { events, remainder } = splitSseEvents(buffer);
2844
+ buffer = remainder;
2802
2845
  for (const event of events) {
2803
- if (event.startsWith("data: ")) {
2804
- const data = event.slice(6);
2805
- if (data === "[DONE]") return;
2806
- yield JSON.parse(data);
2807
- }
2846
+ const data = getEventData(event);
2847
+ if (!data) continue;
2848
+ if (data.trim() === "[DONE]") return;
2849
+ yield parseEventData(data);
2808
2850
  }
2809
2851
  }
2810
2852
  } finally {
2811
2853
  reader.releaseLock();
2812
2854
  }
2813
2855
  }
2856
+ function splitSseEvents(buffer) {
2857
+ const normalized = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
2858
+ const parts = normalized.split("\n\n");
2859
+ const remainder = parts.pop() ?? EMPTY_STRING;
2860
+ return { events: parts, remainder };
2861
+ }
2862
+ function getEventData(event) {
2863
+ if (!event.trim()) return null;
2864
+ const dataLines = event.split("\n").filter((line) => line.startsWith("data:"));
2865
+ if (!dataLines.length) return null;
2866
+ const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, "")).join("\n");
2867
+ return data || null;
2868
+ }
2869
+ function parseEventData(data) {
2870
+ try {
2871
+ return JSON.parse(data);
2872
+ } catch (error) {
2873
+ const message = error instanceof Error ? error.message : String(error);
2874
+ const preview = data.length > 200 ? `${data.slice(0, 200)}...` : data;
2875
+ throw new Error(`LLM stream parse error: ${message}. Data: ${preview}`);
2876
+ }
2877
+ }
2814
2878
  export {
2815
2879
  CommentHandler,
2816
2880
  callLlm,
package/dist/llm.d.mts CHANGED
@@ -1,6 +1,6 @@
1
+ import { ChatCompletionMessageParam, ChatCompletionCreateParamsNonStreaming, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
1
2
  import { C as Context } from './context-sqbr2o6i.mjs';
2
3
  import { PluginInput } from './signature.mjs';
3
- import { ChatCompletionMessageParam, ChatCompletionCreateParamsNonStreaming, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
4
4
  import '@octokit/webhooks';
5
5
  import '@ubiquity-os/ubiquity-os-logger';
6
6
  import '@octokit/plugin-rest-endpoint-methods';
package/dist/llm.d.ts CHANGED
@@ -1,6 +1,6 @@
1
+ import { ChatCompletionMessageParam, ChatCompletionCreateParamsNonStreaming, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
1
2
  import { C as Context } from './context-BbEmsEct.js';
2
3
  import { PluginInput } from './signature.js';
3
- import { ChatCompletionMessageParam, ChatCompletionCreateParamsNonStreaming, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
4
4
  import '@octokit/webhooks';
5
5
  import '@ubiquity-os/ubiquity-os-logger';
6
6
  import '@octokit/plugin-rest-endpoint-methods';
package/dist/llm.js CHANGED
@@ -23,86 +23,150 @@ __export(llm_exports, {
23
23
  callLlm: () => callLlm
24
24
  });
25
25
  module.exports = __toCommonJS(llm_exports);
26
+ var EMPTY_STRING = "";
26
27
  function normalizeBaseUrl(baseUrl) {
27
- return baseUrl.trim().replace(/\/+$/, "");
28
+ let normalized = baseUrl.trim();
29
+ while (normalized.endsWith("/")) {
30
+ normalized = normalized.slice(0, -1);
31
+ }
32
+ return normalized;
28
33
  }
29
34
  function getEnvString(name) {
30
- if (typeof process === "undefined" || !process?.env) return "";
31
- return String(process.env[name] ?? "").trim();
35
+ if (typeof process === "undefined" || !process?.env) return EMPTY_STRING;
36
+ return String(process.env[name] ?? EMPTY_STRING).trim();
32
37
  }
33
38
  function getAiBaseUrl(options) {
34
39
  if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
35
40
  return normalizeBaseUrl(options.baseUrl);
36
41
  }
37
- const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
42
+ const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
38
43
  if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
39
- return "https://ai.ubq.fi";
44
+ return "https://ai-ubq-fi.deno.dev";
40
45
  }
41
46
  async function callLlm(options, input) {
42
- const authToken = input?.authToken ?? "";
43
- const ubiquityKernelToken = input?.ubiquityKernelToken ?? "";
44
- const payload = input?.payload;
45
- const owner = payload?.repository?.owner?.login ?? "";
46
- const repo = payload?.repository?.name ?? "";
47
- const installationId = payload?.installation?.id;
48
- if (!authToken) throw new Error("Missing authToken in inputs");
49
- const requiresKernelToken = authToken.trim().startsWith("gh");
50
- if (requiresKernelToken && !ubiquityKernelToken) {
51
- throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
52
- }
53
- const url = `${getAiBaseUrl(options)}/v1/chat/completions`;
54
- const { baseUrl: _baseUrl, model, stream, messages, ...rest } = options;
47
+ const authToken = String(input.authToken ?? EMPTY_STRING).trim();
48
+ if (!authToken) throw new Error("Missing authToken in input");
49
+ const kernelToken = "ubiquityKernelToken" in input ? input.ubiquityKernelToken : void 0;
50
+ const payload = getPayload(input);
51
+ const { owner, repo, installationId } = getRepoMetadata(payload);
52
+ ensureKernelToken(authToken, kernelToken);
53
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
54
+ ensureMessages(messages);
55
+ const url = buildAiUrl(options, baseUrl);
55
56
  const body = JSON.stringify({
56
57
  ...rest,
57
58
  ...model ? { model } : {},
58
59
  messages,
59
- stream: stream ?? false
60
+ stream: isStream ?? false
61
+ });
62
+ const headers = buildHeaders(authToken, {
63
+ owner,
64
+ repo,
65
+ installationId,
66
+ ubiquityKernelToken: kernelToken
60
67
  });
61
- const headers = {
62
- Authorization: `Bearer ${authToken}`,
63
- "Content-Type": "application/json"
64
- };
65
- if (owner) headers["X-GitHub-Owner"] = owner;
66
- if (repo) headers["X-GitHub-Repo"] = repo;
67
- if (typeof installationId === "number" && Number.isFinite(installationId)) {
68
- headers["X-GitHub-Installation-Id"] = String(installationId);
69
- }
70
- if (ubiquityKernelToken) {
71
- headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
72
- }
73
68
  const response = await fetch(url, { method: "POST", headers, body });
74
69
  if (!response.ok) {
75
70
  const err = await response.text();
76
- throw new Error(`LLM API error: ${response.status} - ${err}`);
71
+ const error = new Error(`LLM API error: ${response.status} - ${err}`);
72
+ error.status = response.status;
73
+ throw error;
77
74
  }
78
- if (options.stream) {
75
+ if (isStream) {
76
+ if (!response.body) {
77
+ throw new Error("LLM API error: missing response body for streaming request");
78
+ }
79
79
  return parseSseStream(response.body);
80
80
  }
81
81
  return response.json();
82
82
  }
83
+ function ensureKernelToken(authToken, kernelToken) {
84
+ const isKernelTokenRequired = authToken.startsWith("gh");
85
+ if (isKernelTokenRequired && !kernelToken) {
86
+ throw new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
87
+ }
88
+ }
89
+ function ensureMessages(messages) {
90
+ if (!Array.isArray(messages) || messages.length === 0) {
91
+ throw new Error("messages must be a non-empty array");
92
+ }
93
+ }
94
+ function buildAiUrl(options, baseUrl) {
95
+ return `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
96
+ }
97
+ function getPayload(input) {
98
+ if ("payload" in input) {
99
+ return input.payload;
100
+ }
101
+ return input.eventPayload;
102
+ }
103
+ function getRepoMetadata(payload) {
104
+ const repoPayload = payload;
105
+ return {
106
+ owner: repoPayload?.repository?.owner?.login ?? EMPTY_STRING,
107
+ repo: repoPayload?.repository?.name ?? EMPTY_STRING,
108
+ installationId: repoPayload?.installation?.id
109
+ };
110
+ }
111
+ function buildHeaders(authToken, options) {
112
+ const headers = {
113
+ Authorization: `Bearer ${authToken}`,
114
+ "Content-Type": "application/json"
115
+ };
116
+ if (options.owner) headers["X-GitHub-Owner"] = options.owner;
117
+ if (options.repo) headers["X-GitHub-Repo"] = options.repo;
118
+ if (typeof options.installationId === "number" && Number.isFinite(options.installationId)) {
119
+ headers["X-GitHub-Installation-Id"] = String(options.installationId);
120
+ }
121
+ if (options.ubiquityKernelToken) {
122
+ headers["X-Ubiquity-Kernel-Token"] = options.ubiquityKernelToken;
123
+ }
124
+ return headers;
125
+ }
83
126
  async function* parseSseStream(body) {
84
127
  const reader = body.getReader();
85
128
  const decoder = new TextDecoder();
86
- let buffer = "";
129
+ let buffer = EMPTY_STRING;
87
130
  try {
88
131
  while (true) {
89
- const { value, done } = await reader.read();
90
- if (done) break;
132
+ const { value, done: isDone } = await reader.read();
133
+ if (isDone) break;
91
134
  buffer += decoder.decode(value, { stream: true });
92
- const events = buffer.split("\n\n");
93
- buffer = events.pop() || "";
135
+ const { events, remainder } = splitSseEvents(buffer);
136
+ buffer = remainder;
94
137
  for (const event of events) {
95
- if (event.startsWith("data: ")) {
96
- const data = event.slice(6);
97
- if (data === "[DONE]") return;
98
- yield JSON.parse(data);
99
- }
138
+ const data = getEventData(event);
139
+ if (!data) continue;
140
+ if (data.trim() === "[DONE]") return;
141
+ yield parseEventData(data);
100
142
  }
101
143
  }
102
144
  } finally {
103
145
  reader.releaseLock();
104
146
  }
105
147
  }
148
+ function splitSseEvents(buffer) {
149
+ const normalized = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
150
+ const parts = normalized.split("\n\n");
151
+ const remainder = parts.pop() ?? EMPTY_STRING;
152
+ return { events: parts, remainder };
153
+ }
154
+ function getEventData(event) {
155
+ if (!event.trim()) return null;
156
+ const dataLines = event.split("\n").filter((line) => line.startsWith("data:"));
157
+ if (!dataLines.length) return null;
158
+ const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, "")).join("\n");
159
+ return data || null;
160
+ }
161
+ function parseEventData(data) {
162
+ try {
163
+ return JSON.parse(data);
164
+ } catch (error) {
165
+ const message = error instanceof Error ? error.message : String(error);
166
+ const preview = data.length > 200 ? `${data.slice(0, 200)}...` : data;
167
+ throw new Error(`LLM stream parse error: ${message}. Data: ${preview}`);
168
+ }
169
+ }
106
170
  // Annotate the CommonJS export names for ESM import in node:
107
171
  0 && (module.exports = {
108
172
  callLlm
package/dist/llm.mjs CHANGED
@@ -1,84 +1,148 @@
1
1
  // src/llm/index.ts
2
+ var EMPTY_STRING = "";
2
3
  function normalizeBaseUrl(baseUrl) {
3
- return baseUrl.trim().replace(/\/+$/, "");
4
+ let normalized = baseUrl.trim();
5
+ while (normalized.endsWith("/")) {
6
+ normalized = normalized.slice(0, -1);
7
+ }
8
+ return normalized;
4
9
  }
5
10
  function getEnvString(name) {
6
- if (typeof process === "undefined" || !process?.env) return "";
7
- return String(process.env[name] ?? "").trim();
11
+ if (typeof process === "undefined" || !process?.env) return EMPTY_STRING;
12
+ return String(process.env[name] ?? EMPTY_STRING).trim();
8
13
  }
9
14
  function getAiBaseUrl(options) {
10
15
  if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
11
16
  return normalizeBaseUrl(options.baseUrl);
12
17
  }
13
- const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
18
+ const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
14
19
  if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
15
- return "https://ai.ubq.fi";
20
+ return "https://ai-ubq-fi.deno.dev";
16
21
  }
17
22
  async function callLlm(options, input) {
18
- const authToken = input?.authToken ?? "";
19
- const ubiquityKernelToken = input?.ubiquityKernelToken ?? "";
20
- const payload = input?.payload;
21
- const owner = payload?.repository?.owner?.login ?? "";
22
- const repo = payload?.repository?.name ?? "";
23
- const installationId = payload?.installation?.id;
24
- if (!authToken) throw new Error("Missing authToken in inputs");
25
- const requiresKernelToken = authToken.trim().startsWith("gh");
26
- if (requiresKernelToken && !ubiquityKernelToken) {
27
- throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
28
- }
29
- const url = `${getAiBaseUrl(options)}/v1/chat/completions`;
30
- const { baseUrl: _baseUrl, model, stream, messages, ...rest } = options;
23
+ const authToken = String(input.authToken ?? EMPTY_STRING).trim();
24
+ if (!authToken) throw new Error("Missing authToken in input");
25
+ const kernelToken = "ubiquityKernelToken" in input ? input.ubiquityKernelToken : void 0;
26
+ const payload = getPayload(input);
27
+ const { owner, repo, installationId } = getRepoMetadata(payload);
28
+ ensureKernelToken(authToken, kernelToken);
29
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
30
+ ensureMessages(messages);
31
+ const url = buildAiUrl(options, baseUrl);
31
32
  const body = JSON.stringify({
32
33
  ...rest,
33
34
  ...model ? { model } : {},
34
35
  messages,
35
- stream: stream ?? false
36
+ stream: isStream ?? false
37
+ });
38
+ const headers = buildHeaders(authToken, {
39
+ owner,
40
+ repo,
41
+ installationId,
42
+ ubiquityKernelToken: kernelToken
36
43
  });
37
- const headers = {
38
- Authorization: `Bearer ${authToken}`,
39
- "Content-Type": "application/json"
40
- };
41
- if (owner) headers["X-GitHub-Owner"] = owner;
42
- if (repo) headers["X-GitHub-Repo"] = repo;
43
- if (typeof installationId === "number" && Number.isFinite(installationId)) {
44
- headers["X-GitHub-Installation-Id"] = String(installationId);
45
- }
46
- if (ubiquityKernelToken) {
47
- headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
48
- }
49
44
  const response = await fetch(url, { method: "POST", headers, body });
50
45
  if (!response.ok) {
51
46
  const err = await response.text();
52
- throw new Error(`LLM API error: ${response.status} - ${err}`);
47
+ const error = new Error(`LLM API error: ${response.status} - ${err}`);
48
+ error.status = response.status;
49
+ throw error;
53
50
  }
54
- if (options.stream) {
51
+ if (isStream) {
52
+ if (!response.body) {
53
+ throw new Error("LLM API error: missing response body for streaming request");
54
+ }
55
55
  return parseSseStream(response.body);
56
56
  }
57
57
  return response.json();
58
58
  }
59
+ function ensureKernelToken(authToken, kernelToken) {
60
+ const isKernelTokenRequired = authToken.startsWith("gh");
61
+ if (isKernelTokenRequired && !kernelToken) {
62
+ throw new Error("Missing ubiquityKernelToken in input (kernel attestation is required for GitHub auth)");
63
+ }
64
+ }
65
+ function ensureMessages(messages) {
66
+ if (!Array.isArray(messages) || messages.length === 0) {
67
+ throw new Error("messages must be a non-empty array");
68
+ }
69
+ }
70
+ function buildAiUrl(options, baseUrl) {
71
+ return `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
72
+ }
73
+ function getPayload(input) {
74
+ if ("payload" in input) {
75
+ return input.payload;
76
+ }
77
+ return input.eventPayload;
78
+ }
79
+ function getRepoMetadata(payload) {
80
+ const repoPayload = payload;
81
+ return {
82
+ owner: repoPayload?.repository?.owner?.login ?? EMPTY_STRING,
83
+ repo: repoPayload?.repository?.name ?? EMPTY_STRING,
84
+ installationId: repoPayload?.installation?.id
85
+ };
86
+ }
87
+ function buildHeaders(authToken, options) {
88
+ const headers = {
89
+ Authorization: `Bearer ${authToken}`,
90
+ "Content-Type": "application/json"
91
+ };
92
+ if (options.owner) headers["X-GitHub-Owner"] = options.owner;
93
+ if (options.repo) headers["X-GitHub-Repo"] = options.repo;
94
+ if (typeof options.installationId === "number" && Number.isFinite(options.installationId)) {
95
+ headers["X-GitHub-Installation-Id"] = String(options.installationId);
96
+ }
97
+ if (options.ubiquityKernelToken) {
98
+ headers["X-Ubiquity-Kernel-Token"] = options.ubiquityKernelToken;
99
+ }
100
+ return headers;
101
+ }
59
102
  async function* parseSseStream(body) {
60
103
  const reader = body.getReader();
61
104
  const decoder = new TextDecoder();
62
- let buffer = "";
105
+ let buffer = EMPTY_STRING;
63
106
  try {
64
107
  while (true) {
65
- const { value, done } = await reader.read();
66
- if (done) break;
108
+ const { value, done: isDone } = await reader.read();
109
+ if (isDone) break;
67
110
  buffer += decoder.decode(value, { stream: true });
68
- const events = buffer.split("\n\n");
69
- buffer = events.pop() || "";
111
+ const { events, remainder } = splitSseEvents(buffer);
112
+ buffer = remainder;
70
113
  for (const event of events) {
71
- if (event.startsWith("data: ")) {
72
- const data = event.slice(6);
73
- if (data === "[DONE]") return;
74
- yield JSON.parse(data);
75
- }
114
+ const data = getEventData(event);
115
+ if (!data) continue;
116
+ if (data.trim() === "[DONE]") return;
117
+ yield parseEventData(data);
76
118
  }
77
119
  }
78
120
  } finally {
79
121
  reader.releaseLock();
80
122
  }
81
123
  }
124
+ function splitSseEvents(buffer) {
125
+ const normalized = buffer.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
126
+ const parts = normalized.split("\n\n");
127
+ const remainder = parts.pop() ?? EMPTY_STRING;
128
+ return { events: parts, remainder };
129
+ }
130
+ function getEventData(event) {
131
+ if (!event.trim()) return null;
132
+ const dataLines = event.split("\n").filter((line) => line.startsWith("data:"));
133
+ if (!dataLines.length) return null;
134
+ const data = dataLines.map((line) => line.startsWith("data: ") ? line.slice(6) : line.slice(5).replace(/^ /, "")).join("\n");
135
+ return data || null;
136
+ }
137
+ function parseEventData(data) {
138
+ try {
139
+ return JSON.parse(data);
140
+ } catch (error) {
141
+ const message = error instanceof Error ? error.message : String(error);
142
+ const preview = data.length > 200 ? `${data.slice(0, 200)}...` : data;
143
+ throw new Error(`LLM stream parse error: ${message}. Data: ${preview}`);
144
+ }
145
+ }
82
146
  export {
83
147
  callLlm
84
148
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubiquity-os/plugin-sdk",
3
- "version": "3.6.2",
3
+ "version": "3.6.3",
4
4
  "description": "SDK for plugin support.",
5
5
  "author": "Ubiquity DAO",
6
6
  "license": "MIT",