@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.
- package/dist/configuration.d.mts +4 -0
- package/dist/configuration.d.ts +4 -0
- package/dist/configuration.js +27 -20
- package/dist/configuration.mjs +27 -20
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +108 -44
- package/dist/index.mjs +108 -44
- package/dist/llm.d.mts +1 -1
- package/dist/llm.d.ts +1 -1
- package/dist/llm.js +107 -43
- package/dist/llm.mjs +107 -43
- package/package.json +1 -1
package/dist/configuration.d.mts
CHANGED
|
@@ -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;
|
package/dist/configuration.d.ts
CHANGED
|
@@ -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;
|
package/dist/configuration.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
2828
|
+
this._logger.ok("Downloaded configuration file", { owner, repository });
|
|
2828
2829
|
if (!rawData) {
|
|
2829
|
-
this._logger.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
2907
|
+
this._logger.warn("Error parsing YAML", { error });
|
|
2901
2908
|
return { errors: [error], yaml: null };
|
|
2902
2909
|
}
|
|
2903
|
-
this._logger.
|
|
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.
|
|
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.
|
|
2966
|
+
this._logger.warn("Manifest validation error", { error });
|
|
2960
2967
|
}
|
|
2961
2968
|
throw new Error("Manifest is invalid.");
|
|
2962
2969
|
}
|
package/dist/configuration.mjs
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
2801
|
+
this._logger.ok("Downloaded configuration file", { owner, repository });
|
|
2801
2802
|
if (!rawData) {
|
|
2802
|
-
this._logger.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
2880
|
+
this._logger.warn("Error parsing YAML", { error });
|
|
2874
2881
|
return { errors: [error], yaml: null };
|
|
2875
2882
|
}
|
|
2876
|
-
this._logger.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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] ??
|
|
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("
|
|
2791
|
+
const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
|
|
2787
2792
|
if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
|
|
2788
|
-
return "https://ai
|
|
2793
|
+
return "https://ai-ubq-fi.deno.dev";
|
|
2789
2794
|
}
|
|
2790
2795
|
async function callLlm(options, input) {
|
|
2791
|
-
const authToken = input
|
|
2792
|
-
|
|
2793
|
-
const
|
|
2794
|
-
const
|
|
2795
|
-
const repo = payload
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
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:
|
|
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
|
-
|
|
2820
|
+
const error = new Error(`LLM API error: ${response.status} - ${err}`);
|
|
2821
|
+
error.status = response.status;
|
|
2822
|
+
throw error;
|
|
2826
2823
|
}
|
|
2827
|
-
if (
|
|
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 (
|
|
2881
|
+
const { value, done: isDone } = await reader.read();
|
|
2882
|
+
if (isDone) break;
|
|
2840
2883
|
buffer += decoder.decode(value, { stream: true });
|
|
2841
|
-
const events = buffer
|
|
2842
|
-
buffer =
|
|
2884
|
+
const { events, remainder } = splitSseEvents(buffer);
|
|
2885
|
+
buffer = remainder;
|
|
2843
2886
|
for (const event of events) {
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
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.
|
|
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
|
-
|
|
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] ??
|
|
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("
|
|
2750
|
+
const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
|
|
2746
2751
|
if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
|
|
2747
|
-
return "https://ai
|
|
2752
|
+
return "https://ai-ubq-fi.deno.dev";
|
|
2748
2753
|
}
|
|
2749
2754
|
async function callLlm(options, input) {
|
|
2750
|
-
const authToken = input
|
|
2751
|
-
|
|
2752
|
-
const
|
|
2753
|
-
const
|
|
2754
|
-
const repo = payload
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
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:
|
|
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
|
-
|
|
2779
|
+
const error = new Error(`LLM API error: ${response.status} - ${err}`);
|
|
2780
|
+
error.status = response.status;
|
|
2781
|
+
throw error;
|
|
2785
2782
|
}
|
|
2786
|
-
if (
|
|
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 (
|
|
2840
|
+
const { value, done: isDone } = await reader.read();
|
|
2841
|
+
if (isDone) break;
|
|
2799
2842
|
buffer += decoder.decode(value, { stream: true });
|
|
2800
|
-
const events = buffer
|
|
2801
|
-
buffer =
|
|
2843
|
+
const { events, remainder } = splitSseEvents(buffer);
|
|
2844
|
+
buffer = remainder;
|
|
2802
2845
|
for (const event of events) {
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
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
|
-
|
|
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] ??
|
|
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("
|
|
42
|
+
const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
|
|
38
43
|
if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
|
|
39
|
-
return "https://ai
|
|
44
|
+
return "https://ai-ubq-fi.deno.dev";
|
|
40
45
|
}
|
|
41
46
|
async function callLlm(options, input) {
|
|
42
|
-
const authToken = input
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
const repo = payload
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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:
|
|
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
|
-
|
|
71
|
+
const error = new Error(`LLM API error: ${response.status} - ${err}`);
|
|
72
|
+
error.status = response.status;
|
|
73
|
+
throw error;
|
|
77
74
|
}
|
|
78
|
-
if (
|
|
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 (
|
|
132
|
+
const { value, done: isDone } = await reader.read();
|
|
133
|
+
if (isDone) break;
|
|
91
134
|
buffer += decoder.decode(value, { stream: true });
|
|
92
|
-
const events = buffer
|
|
93
|
-
buffer =
|
|
135
|
+
const { events, remainder } = splitSseEvents(buffer);
|
|
136
|
+
buffer = remainder;
|
|
94
137
|
for (const event of events) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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] ??
|
|
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("
|
|
18
|
+
const envBaseUrl = getEnvString("UOS_AI_URL") || getEnvString("UOS_AI_BASE_URL");
|
|
14
19
|
if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
|
|
15
|
-
return "https://ai
|
|
20
|
+
return "https://ai-ubq-fi.deno.dev";
|
|
16
21
|
}
|
|
17
22
|
async function callLlm(options, input) {
|
|
18
|
-
const authToken = input
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const repo = payload
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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:
|
|
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
|
-
|
|
47
|
+
const error = new Error(`LLM API error: ${response.status} - ${err}`);
|
|
48
|
+
error.status = response.status;
|
|
49
|
+
throw error;
|
|
53
50
|
}
|
|
54
|
-
if (
|
|
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 (
|
|
108
|
+
const { value, done: isDone } = await reader.read();
|
|
109
|
+
if (isDone) break;
|
|
67
110
|
buffer += decoder.decode(value, { stream: true });
|
|
68
|
-
const events = buffer
|
|
69
|
-
buffer =
|
|
111
|
+
const { events, remainder } = splitSseEvents(buffer);
|
|
112
|
+
buffer = remainder;
|
|
70
113
|
for (const event of events) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
};
|