@ubiquity-os/plugin-sdk 3.5.5 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,10 @@ The `createActionsPlugin` function allows users to create plugins that will be a
17
17
 
18
18
  The `createPlugin` function enables users to create a plugin that will run on Cloudflare Workers environment.
19
19
 
20
+ ### `callLlm`
21
+
22
+ The `callLlm` function sends chat completion requests to `ai.ubq.fi` using the auth token and repository context supplied by the kernel.
23
+
20
24
  ### `postComment`
21
25
 
22
26
  The `postComment` function enables users to easily post a comment to an issue, a pull-request, or a pull request review thread.
@@ -59,6 +63,19 @@ To start Jest tests, run:
59
63
  bun run test
60
64
  ```
61
65
 
66
+ ## LLM Utility
67
+
68
+ ```ts
69
+ import { callLlm } from "@ubiquity-os/plugin-sdk";
70
+
71
+ const result = await callLlm(
72
+ {
73
+ messages: [{ role: "user", content: "Summarize this issue." }],
74
+ },
75
+ context
76
+ );
77
+ ```
78
+
62
79
  ## Markdown Cleaning Utility
63
80
 
64
81
  `cleanMarkdown` removes top-level HTML comments and configured HTML tags while preserving content inside fenced/indented code blocks, inline code spans, and blockquotes.
@@ -2,7 +2,7 @@ import { Value } from '@sinclair/typebox/value';
2
2
  import YAML from 'js-yaml';
3
3
  import * as _sinclair_typebox from '@sinclair/typebox';
4
4
  import { StaticDecode, TLiteral } from '@sinclair/typebox';
5
- import { C as Context } from './context-3Ck9sBZI.mjs';
5
+ import { C as Context } from './context-sqbr2o6i.mjs';
6
6
  import { Manifest } from './manifest.mjs';
7
7
  import '@octokit/webhooks';
8
8
  import '@ubiquity-os/ubiquity-os-logger';
@@ -2,7 +2,7 @@ import { Value } from '@sinclair/typebox/value';
2
2
  import YAML from 'js-yaml';
3
3
  import * as _sinclair_typebox from '@sinclair/typebox';
4
4
  import { StaticDecode, TLiteral } from '@sinclair/typebox';
5
- import { C as Context } from './context-DOUnUNNN.js';
5
+ import { C as Context } from './context-BbEmsEct.js';
6
6
  import { Manifest } from './manifest.js';
7
7
  import '@octokit/webhooks';
8
8
  import '@ubiquity-os/ubiquity-os-logger';
@@ -97,6 +97,8 @@ interface Context<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSuppor
97
97
  [K in TSupportedEvents]: K extends EmitterWebhookEventName ? EmitterWebhookEvent<K> : never;
98
98
  }[TSupportedEvents]["payload"];
99
99
  command: TCommand | null;
100
+ authToken: string;
101
+ ubiquityKernelToken?: string;
100
102
  octokit: InstanceType<typeof customOctokit>;
101
103
  config: TConfig;
102
104
  env: TEnv;
@@ -97,6 +97,8 @@ interface Context<TConfig = unknown, TEnv = unknown, TCommand = unknown, TSuppor
97
97
  [K in TSupportedEvents]: K extends EmitterWebhookEventName ? EmitterWebhookEvent<K> : never;
98
98
  }[TSupportedEvents]["payload"];
99
99
  command: TCommand | null;
100
+ authToken: string;
101
+ ubiquityKernelToken?: string;
100
102
  octokit: InstanceType<typeof customOctokit>;
101
103
  config: TConfig;
102
104
  env: TEnv;
package/dist/index.d.mts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { EmitterWebhookEventName } from '@octokit/webhooks';
2
- import { C as Context } from './context-3Ck9sBZI.mjs';
3
- export { a as CommentHandler } from './context-3Ck9sBZI.mjs';
2
+ import { C as Context } from './context-sqbr2o6i.mjs';
3
+ export { a as CommentHandler } from './context-sqbr2o6i.mjs';
4
4
  import { TSchema, TAnySchema } from '@sinclair/typebox';
5
5
  import { LogLevel } from '@ubiquity-os/ubiquity-os-logger';
6
+ export { LlmCallOptions, callLlm } from './llm.mjs';
6
7
  import * as hono_types from 'hono/types';
7
8
  import { Hono } from 'hono';
8
9
  import { Manifest } from './manifest.mjs';
@@ -13,6 +14,7 @@ import '@octokit/plugin-paginate-graphql';
13
14
  import '@octokit/plugin-paginate-rest';
14
15
  import '@octokit/webhooks/node_modules/@octokit/request-error';
15
16
  import '@octokit/core';
17
+ import 'openai/resources/chat/completions';
16
18
 
17
19
  type Return = Record<string, unknown> | undefined | void;
18
20
  type HandlerReturn = Promise<Return> | Return;
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { EmitterWebhookEventName } from '@octokit/webhooks';
2
- import { C as Context } from './context-DOUnUNNN.js';
3
- export { a as CommentHandler } from './context-DOUnUNNN.js';
2
+ import { C as Context } from './context-BbEmsEct.js';
3
+ export { a as CommentHandler } from './context-BbEmsEct.js';
4
4
  import { TSchema, TAnySchema } from '@sinclair/typebox';
5
5
  import { LogLevel } from '@ubiquity-os/ubiquity-os-logger';
6
+ export { LlmCallOptions, callLlm } from './llm.js';
6
7
  import * as hono_types from 'hono/types';
7
8
  import { Hono } from 'hono';
8
9
  import { Manifest } from './manifest.js';
@@ -13,6 +14,7 @@ import '@octokit/plugin-paginate-graphql';
13
14
  import '@octokit/plugin-paginate-rest';
14
15
  import '@octokit/webhooks/node_modules/@octokit/request-error';
15
16
  import '@octokit/core';
17
+ import 'openai/resources/chat/completions';
16
18
 
17
19
  type Return = Record<string, unknown> | undefined | void;
18
20
  type HandlerReturn = Promise<Return> | Return;
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  CommentHandler: () => CommentHandler,
34
+ callLlm: () => callLlm,
34
35
  cleanMarkdown: () => cleanMarkdown,
35
36
  createActionsPlugin: () => createActionsPlugin,
36
37
  createPlugin: () => createPlugin,
@@ -427,6 +428,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
427
428
  eventPayload: inputs.eventPayload,
428
429
  settings: inputs.settings,
429
430
  authToken: inputs.authToken,
431
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
430
432
  ref: inputs.ref,
431
433
  command: inputs.command
432
434
  };
@@ -475,6 +477,7 @@ var inputSchema = import_typebox3.Type.Object({
475
477
  eventPayload: jsonType(import_typebox3.Type.Record(import_typebox3.Type.String(), import_typebox3.Type.Any()), true),
476
478
  command: jsonType(commandCallSchema),
477
479
  authToken: import_typebox3.Type.String(),
480
+ ubiquityKernelToken: import_typebox3.Type.Optional(import_typebox3.Type.String()),
478
481
  settings: jsonType(import_typebox3.Type.Record(import_typebox3.Type.String(), import_typebox3.Type.Any())),
479
482
  ref: import_typebox3.Type.String(),
480
483
  signature: import_typebox3.Type.String()
@@ -543,6 +546,8 @@ async function createActionsPlugin(handler, options) {
543
546
  eventName: inputs.eventName,
544
547
  payload: inputs.eventPayload,
545
548
  command,
549
+ authToken: inputs.authToken,
550
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
546
551
  octokit: new customOctokit({ auth: inputs.authToken }),
547
552
  config: config2,
548
553
  env,
@@ -625,6 +630,96 @@ function processSegment(segment, extraTags, shouldCollapseEmptyLines) {
625
630
  return s;
626
631
  }
627
632
 
633
+ // src/llm/index.ts
634
+ function normalizeBaseUrl(baseUrl) {
635
+ let normalized = baseUrl.trim();
636
+ while (normalized.endsWith("/")) {
637
+ normalized = normalized.slice(0, -1);
638
+ }
639
+ return normalized;
640
+ }
641
+ function getEnvString(name) {
642
+ if (typeof process === "undefined" || !process?.env) return "";
643
+ return String(process.env[name] ?? "").trim();
644
+ }
645
+ function getAiBaseUrl(options) {
646
+ if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
647
+ return normalizeBaseUrl(options.baseUrl);
648
+ }
649
+ const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
650
+ if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
651
+ return "https://ai.ubq.fi";
652
+ }
653
+ async function callLlm(options, input) {
654
+ const inputPayload = input;
655
+ const authToken = inputPayload.authToken;
656
+ const ubiquityKernelToken = inputPayload.ubiquityKernelToken;
657
+ const payload = inputPayload.payload ?? inputPayload.eventPayload;
658
+ const owner = payload?.repository?.owner?.login ?? "";
659
+ const repo = payload?.repository?.name ?? "";
660
+ const installationId = payload?.installation?.id;
661
+ if (!authToken) throw new Error("Missing authToken in inputs");
662
+ const isKernelTokenRequired = authToken.trim().startsWith("gh");
663
+ if (isKernelTokenRequired && !ubiquityKernelToken) {
664
+ throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
665
+ }
666
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
667
+ const url = `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
668
+ const body = JSON.stringify({
669
+ ...rest,
670
+ ...model ? { model } : {},
671
+ messages,
672
+ stream: isStream ?? false
673
+ });
674
+ const headers = {
675
+ Authorization: `Bearer ${authToken}`,
676
+ "Content-Type": "application/json"
677
+ };
678
+ if (owner) headers["X-GitHub-Owner"] = owner;
679
+ if (repo) headers["X-GitHub-Repo"] = repo;
680
+ if (typeof installationId === "number" && Number.isFinite(installationId)) {
681
+ headers["X-GitHub-Installation-Id"] = String(installationId);
682
+ }
683
+ if (ubiquityKernelToken) {
684
+ headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
685
+ }
686
+ const response = await fetch(url, { method: "POST", headers, body });
687
+ if (!response.ok) {
688
+ const err = await response.text();
689
+ throw new Error(`LLM API error: ${response.status} - ${err}`);
690
+ }
691
+ if (isStream) {
692
+ if (!response.body) {
693
+ throw new Error("LLM API error: missing response body for streaming request");
694
+ }
695
+ return parseSseStream(response.body);
696
+ }
697
+ return response.json();
698
+ }
699
+ async function* parseSseStream(body) {
700
+ const reader = body.getReader();
701
+ const decoder = new TextDecoder();
702
+ let buffer = "";
703
+ try {
704
+ while (true) {
705
+ const { value, done: isDone } = await reader.read();
706
+ if (isDone) break;
707
+ buffer += decoder.decode(value, { stream: true });
708
+ const events = buffer.split("\n\n");
709
+ buffer = events.pop() || "";
710
+ for (const event of events) {
711
+ if (event.startsWith("data: ")) {
712
+ const data = event.slice(6);
713
+ if (data === "[DONE]") return;
714
+ yield JSON.parse(data);
715
+ }
716
+ }
717
+ }
718
+ } finally {
719
+ reader.releaseLock();
720
+ }
721
+ }
722
+
628
723
  // src/server.ts
629
724
  var import_value4 = require("@sinclair/typebox/value");
630
725
  var import_ubiquity_os_logger4 = require("@ubiquity-os/ubiquity-os-logger");
@@ -690,6 +785,8 @@ function createPlugin(handler, manifest, options) {
690
785
  eventName: inputs.eventName,
691
786
  payload: inputs.eventPayload,
692
787
  command,
788
+ authToken: inputs.authToken,
789
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
693
790
  octokit: new customOctokit({ auth: inputs.authToken }),
694
791
  config: config2,
695
792
  env,
@@ -708,6 +805,7 @@ function createPlugin(handler, manifest, options) {
708
805
  // Annotate the CommonJS export names for ESM import in node:
709
806
  0 && (module.exports = {
710
807
  CommentHandler,
808
+ callLlm,
711
809
  cleanMarkdown,
712
810
  createActionsPlugin,
713
811
  createPlugin,
package/dist/index.mjs CHANGED
@@ -387,6 +387,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
387
387
  eventPayload: inputs.eventPayload,
388
388
  settings: inputs.settings,
389
389
  authToken: inputs.authToken,
390
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
390
391
  ref: inputs.ref,
391
392
  command: inputs.command
392
393
  };
@@ -435,6 +436,7 @@ var inputSchema = T2.Object({
435
436
  eventPayload: jsonType(T2.Record(T2.String(), T2.Any()), true),
436
437
  command: jsonType(commandCallSchema),
437
438
  authToken: T2.String(),
439
+ ubiquityKernelToken: T2.Optional(T2.String()),
438
440
  settings: jsonType(T2.Record(T2.String(), T2.Any())),
439
441
  ref: T2.String(),
440
442
  signature: T2.String()
@@ -503,6 +505,8 @@ async function createActionsPlugin(handler, options) {
503
505
  eventName: inputs.eventName,
504
506
  payload: inputs.eventPayload,
505
507
  command,
508
+ authToken: inputs.authToken,
509
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
506
510
  octokit: new customOctokit({ auth: inputs.authToken }),
507
511
  config: config2,
508
512
  env,
@@ -585,6 +589,96 @@ function processSegment(segment, extraTags, shouldCollapseEmptyLines) {
585
589
  return s;
586
590
  }
587
591
 
592
+ // src/llm/index.ts
593
+ function normalizeBaseUrl(baseUrl) {
594
+ let normalized = baseUrl.trim();
595
+ while (normalized.endsWith("/")) {
596
+ normalized = normalized.slice(0, -1);
597
+ }
598
+ return normalized;
599
+ }
600
+ function getEnvString(name) {
601
+ if (typeof process === "undefined" || !process?.env) return "";
602
+ return String(process.env[name] ?? "").trim();
603
+ }
604
+ function getAiBaseUrl(options) {
605
+ if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
606
+ return normalizeBaseUrl(options.baseUrl);
607
+ }
608
+ const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
609
+ if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
610
+ return "https://ai.ubq.fi";
611
+ }
612
+ async function callLlm(options, input) {
613
+ const inputPayload = input;
614
+ const authToken = inputPayload.authToken;
615
+ const ubiquityKernelToken = inputPayload.ubiquityKernelToken;
616
+ const payload = inputPayload.payload ?? inputPayload.eventPayload;
617
+ const owner = payload?.repository?.owner?.login ?? "";
618
+ const repo = payload?.repository?.name ?? "";
619
+ const installationId = payload?.installation?.id;
620
+ if (!authToken) throw new Error("Missing authToken in inputs");
621
+ const isKernelTokenRequired = authToken.trim().startsWith("gh");
622
+ if (isKernelTokenRequired && !ubiquityKernelToken) {
623
+ throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
624
+ }
625
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
626
+ const url = `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
627
+ const body = JSON.stringify({
628
+ ...rest,
629
+ ...model ? { model } : {},
630
+ messages,
631
+ stream: isStream ?? false
632
+ });
633
+ const headers = {
634
+ Authorization: `Bearer ${authToken}`,
635
+ "Content-Type": "application/json"
636
+ };
637
+ if (owner) headers["X-GitHub-Owner"] = owner;
638
+ if (repo) headers["X-GitHub-Repo"] = repo;
639
+ if (typeof installationId === "number" && Number.isFinite(installationId)) {
640
+ headers["X-GitHub-Installation-Id"] = String(installationId);
641
+ }
642
+ if (ubiquityKernelToken) {
643
+ headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
644
+ }
645
+ const response = await fetch(url, { method: "POST", headers, body });
646
+ if (!response.ok) {
647
+ const err = await response.text();
648
+ throw new Error(`LLM API error: ${response.status} - ${err}`);
649
+ }
650
+ if (isStream) {
651
+ if (!response.body) {
652
+ throw new Error("LLM API error: missing response body for streaming request");
653
+ }
654
+ return parseSseStream(response.body);
655
+ }
656
+ return response.json();
657
+ }
658
+ async function* parseSseStream(body) {
659
+ const reader = body.getReader();
660
+ const decoder = new TextDecoder();
661
+ let buffer = "";
662
+ try {
663
+ while (true) {
664
+ const { value, done: isDone } = await reader.read();
665
+ if (isDone) break;
666
+ buffer += decoder.decode(value, { stream: true });
667
+ const events = buffer.split("\n\n");
668
+ buffer = events.pop() || "";
669
+ for (const event of events) {
670
+ if (event.startsWith("data: ")) {
671
+ const data = event.slice(6);
672
+ if (data === "[DONE]") return;
673
+ yield JSON.parse(data);
674
+ }
675
+ }
676
+ }
677
+ } finally {
678
+ reader.releaseLock();
679
+ }
680
+ }
681
+
588
682
  // src/server.ts
589
683
  import { Value as Value4 } from "@sinclair/typebox/value";
590
684
  import { Logs as Logs2 } from "@ubiquity-os/ubiquity-os-logger";
@@ -650,6 +744,8 @@ function createPlugin(handler, manifest, options) {
650
744
  eventName: inputs.eventName,
651
745
  payload: inputs.eventPayload,
652
746
  command,
747
+ authToken: inputs.authToken,
748
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
653
749
  octokit: new customOctokit({ auth: inputs.authToken }),
654
750
  config: config2,
655
751
  env,
@@ -667,6 +763,7 @@ function createPlugin(handler, manifest, options) {
667
763
  }
668
764
  export {
669
765
  CommentHandler,
766
+ callLlm,
670
767
  cleanMarkdown,
671
768
  createActionsPlugin,
672
769
  createPlugin,
package/dist/llm.d.mts ADDED
@@ -0,0 +1,60 @@
1
+ import { ChatCompletionMessageParam, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
2
+ import { C as Context } from './context-sqbr2o6i.mjs';
3
+ import '@octokit/webhooks';
4
+ import '@ubiquity-os/ubiquity-os-logger';
5
+ import '@octokit/plugin-rest-endpoint-methods';
6
+ import './octokit.mjs';
7
+ import '@octokit/core/types';
8
+ import '@octokit/plugin-paginate-graphql';
9
+ import '@octokit/plugin-paginate-rest';
10
+ import '@octokit/webhooks/node_modules/@octokit/request-error';
11
+ import '@octokit/core';
12
+
13
+ type LlmResponseFormat = {
14
+ type: "json_object" | "text";
15
+ } | {
16
+ type: string;
17
+ [key: string]: unknown;
18
+ };
19
+ type LlmPayload = {
20
+ repository?: {
21
+ owner?: {
22
+ login?: string;
23
+ };
24
+ name?: string;
25
+ };
26
+ installation?: {
27
+ id?: number;
28
+ };
29
+ };
30
+ type LlmAuthContext = {
31
+ authToken?: string;
32
+ ubiquityKernelToken?: string;
33
+ payload?: LlmPayload;
34
+ eventPayload?: LlmPayload;
35
+ };
36
+ type LlmCallOptions = {
37
+ baseUrl?: string;
38
+ model?: string;
39
+ stream?: boolean;
40
+ messages: ChatCompletionMessageParam[];
41
+ max_tokens?: number;
42
+ max_completion_tokens?: number;
43
+ temperature?: number;
44
+ top_p?: number;
45
+ frequency_penalty?: number;
46
+ presence_penalty?: number;
47
+ response_format?: LlmResponseFormat;
48
+ stop?: string | string[];
49
+ n?: number;
50
+ logit_bias?: Record<string, number>;
51
+ seed?: number;
52
+ user?: string;
53
+ metadata?: Record<string, unknown>;
54
+ tools?: unknown[];
55
+ tool_choice?: string | Record<string, unknown>;
56
+ [key: string]: unknown;
57
+ };
58
+ declare function callLlm(options: LlmCallOptions, input: Context | LlmAuthContext): Promise<ChatCompletion | AsyncIterable<ChatCompletionChunk>>;
59
+
60
+ export { type LlmCallOptions, callLlm };
package/dist/llm.d.ts ADDED
@@ -0,0 +1,60 @@
1
+ import { ChatCompletionMessageParam, ChatCompletion, ChatCompletionChunk } from 'openai/resources/chat/completions';
2
+ import { C as Context } from './context-BbEmsEct.js';
3
+ import '@octokit/webhooks';
4
+ import '@ubiquity-os/ubiquity-os-logger';
5
+ import '@octokit/plugin-rest-endpoint-methods';
6
+ import './octokit.js';
7
+ import '@octokit/core/types';
8
+ import '@octokit/plugin-paginate-graphql';
9
+ import '@octokit/plugin-paginate-rest';
10
+ import '@octokit/webhooks/node_modules/@octokit/request-error';
11
+ import '@octokit/core';
12
+
13
+ type LlmResponseFormat = {
14
+ type: "json_object" | "text";
15
+ } | {
16
+ type: string;
17
+ [key: string]: unknown;
18
+ };
19
+ type LlmPayload = {
20
+ repository?: {
21
+ owner?: {
22
+ login?: string;
23
+ };
24
+ name?: string;
25
+ };
26
+ installation?: {
27
+ id?: number;
28
+ };
29
+ };
30
+ type LlmAuthContext = {
31
+ authToken?: string;
32
+ ubiquityKernelToken?: string;
33
+ payload?: LlmPayload;
34
+ eventPayload?: LlmPayload;
35
+ };
36
+ type LlmCallOptions = {
37
+ baseUrl?: string;
38
+ model?: string;
39
+ stream?: boolean;
40
+ messages: ChatCompletionMessageParam[];
41
+ max_tokens?: number;
42
+ max_completion_tokens?: number;
43
+ temperature?: number;
44
+ top_p?: number;
45
+ frequency_penalty?: number;
46
+ presence_penalty?: number;
47
+ response_format?: LlmResponseFormat;
48
+ stop?: string | string[];
49
+ n?: number;
50
+ logit_bias?: Record<string, number>;
51
+ seed?: number;
52
+ user?: string;
53
+ metadata?: Record<string, unknown>;
54
+ tools?: unknown[];
55
+ tool_choice?: string | Record<string, unknown>;
56
+ [key: string]: unknown;
57
+ };
58
+ declare function callLlm(options: LlmCallOptions, input: Context | LlmAuthContext): Promise<ChatCompletion | AsyncIterable<ChatCompletionChunk>>;
59
+
60
+ export { type LlmCallOptions, callLlm };
package/dist/llm.js ADDED
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/llm/index.ts
21
+ var llm_exports = {};
22
+ __export(llm_exports, {
23
+ callLlm: () => callLlm
24
+ });
25
+ module.exports = __toCommonJS(llm_exports);
26
+ function normalizeBaseUrl(baseUrl) {
27
+ let normalized = baseUrl.trim();
28
+ while (normalized.endsWith("/")) {
29
+ normalized = normalized.slice(0, -1);
30
+ }
31
+ return normalized;
32
+ }
33
+ function getEnvString(name) {
34
+ if (typeof process === "undefined" || !process?.env) return "";
35
+ return String(process.env[name] ?? "").trim();
36
+ }
37
+ function getAiBaseUrl(options) {
38
+ if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
39
+ return normalizeBaseUrl(options.baseUrl);
40
+ }
41
+ const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
42
+ if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
43
+ return "https://ai.ubq.fi";
44
+ }
45
+ async function callLlm(options, input) {
46
+ const inputPayload = input;
47
+ const authToken = inputPayload.authToken;
48
+ const ubiquityKernelToken = inputPayload.ubiquityKernelToken;
49
+ const payload = inputPayload.payload ?? inputPayload.eventPayload;
50
+ const owner = payload?.repository?.owner?.login ?? "";
51
+ const repo = payload?.repository?.name ?? "";
52
+ const installationId = payload?.installation?.id;
53
+ if (!authToken) throw new Error("Missing authToken in inputs");
54
+ const isKernelTokenRequired = authToken.trim().startsWith("gh");
55
+ if (isKernelTokenRequired && !ubiquityKernelToken) {
56
+ throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
57
+ }
58
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
59
+ const url = `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
60
+ const body = JSON.stringify({
61
+ ...rest,
62
+ ...model ? { model } : {},
63
+ messages,
64
+ stream: isStream ?? false
65
+ });
66
+ const headers = {
67
+ Authorization: `Bearer ${authToken}`,
68
+ "Content-Type": "application/json"
69
+ };
70
+ if (owner) headers["X-GitHub-Owner"] = owner;
71
+ if (repo) headers["X-GitHub-Repo"] = repo;
72
+ if (typeof installationId === "number" && Number.isFinite(installationId)) {
73
+ headers["X-GitHub-Installation-Id"] = String(installationId);
74
+ }
75
+ if (ubiquityKernelToken) {
76
+ headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
77
+ }
78
+ const response = await fetch(url, { method: "POST", headers, body });
79
+ if (!response.ok) {
80
+ const err = await response.text();
81
+ throw new Error(`LLM API error: ${response.status} - ${err}`);
82
+ }
83
+ if (isStream) {
84
+ if (!response.body) {
85
+ throw new Error("LLM API error: missing response body for streaming request");
86
+ }
87
+ return parseSseStream(response.body);
88
+ }
89
+ return response.json();
90
+ }
91
+ async function* parseSseStream(body) {
92
+ const reader = body.getReader();
93
+ const decoder = new TextDecoder();
94
+ let buffer = "";
95
+ try {
96
+ while (true) {
97
+ const { value, done: isDone } = await reader.read();
98
+ if (isDone) break;
99
+ buffer += decoder.decode(value, { stream: true });
100
+ const events = buffer.split("\n\n");
101
+ buffer = events.pop() || "";
102
+ for (const event of events) {
103
+ if (event.startsWith("data: ")) {
104
+ const data = event.slice(6);
105
+ if (data === "[DONE]") return;
106
+ yield JSON.parse(data);
107
+ }
108
+ }
109
+ }
110
+ } finally {
111
+ reader.releaseLock();
112
+ }
113
+ }
114
+ // Annotate the CommonJS export names for ESM import in node:
115
+ 0 && (module.exports = {
116
+ callLlm
117
+ });
package/dist/llm.mjs ADDED
@@ -0,0 +1,92 @@
1
+ // src/llm/index.ts
2
+ function normalizeBaseUrl(baseUrl) {
3
+ let normalized = baseUrl.trim();
4
+ while (normalized.endsWith("/")) {
5
+ normalized = normalized.slice(0, -1);
6
+ }
7
+ return normalized;
8
+ }
9
+ function getEnvString(name) {
10
+ if (typeof process === "undefined" || !process?.env) return "";
11
+ return String(process.env[name] ?? "").trim();
12
+ }
13
+ function getAiBaseUrl(options) {
14
+ if (typeof options.baseUrl === "string" && options.baseUrl.trim()) {
15
+ return normalizeBaseUrl(options.baseUrl);
16
+ }
17
+ const envBaseUrl = getEnvString("UBQ_AI_BASE_URL") || getEnvString("UBQ_AI_URL");
18
+ if (envBaseUrl) return normalizeBaseUrl(envBaseUrl);
19
+ return "https://ai.ubq.fi";
20
+ }
21
+ async function callLlm(options, input) {
22
+ const inputPayload = input;
23
+ const authToken = inputPayload.authToken;
24
+ const ubiquityKernelToken = inputPayload.ubiquityKernelToken;
25
+ const payload = inputPayload.payload ?? inputPayload.eventPayload;
26
+ const owner = payload?.repository?.owner?.login ?? "";
27
+ const repo = payload?.repository?.name ?? "";
28
+ const installationId = payload?.installation?.id;
29
+ if (!authToken) throw new Error("Missing authToken in inputs");
30
+ const isKernelTokenRequired = authToken.trim().startsWith("gh");
31
+ if (isKernelTokenRequired && !ubiquityKernelToken) {
32
+ throw new Error("Missing ubiquityKernelToken in inputs (kernel attestation is required for GitHub auth)");
33
+ }
34
+ const { baseUrl, model, stream: isStream, messages, ...rest } = options;
35
+ const url = `${getAiBaseUrl({ ...options, baseUrl })}/v1/chat/completions`;
36
+ const body = JSON.stringify({
37
+ ...rest,
38
+ ...model ? { model } : {},
39
+ messages,
40
+ stream: isStream ?? false
41
+ });
42
+ const headers = {
43
+ Authorization: `Bearer ${authToken}`,
44
+ "Content-Type": "application/json"
45
+ };
46
+ if (owner) headers["X-GitHub-Owner"] = owner;
47
+ if (repo) headers["X-GitHub-Repo"] = repo;
48
+ if (typeof installationId === "number" && Number.isFinite(installationId)) {
49
+ headers["X-GitHub-Installation-Id"] = String(installationId);
50
+ }
51
+ if (ubiquityKernelToken) {
52
+ headers["X-Ubiquity-Kernel-Token"] = ubiquityKernelToken;
53
+ }
54
+ const response = await fetch(url, { method: "POST", headers, body });
55
+ if (!response.ok) {
56
+ const err = await response.text();
57
+ throw new Error(`LLM API error: ${response.status} - ${err}`);
58
+ }
59
+ if (isStream) {
60
+ if (!response.body) {
61
+ throw new Error("LLM API error: missing response body for streaming request");
62
+ }
63
+ return parseSseStream(response.body);
64
+ }
65
+ return response.json();
66
+ }
67
+ async function* parseSseStream(body) {
68
+ const reader = body.getReader();
69
+ const decoder = new TextDecoder();
70
+ let buffer = "";
71
+ try {
72
+ while (true) {
73
+ const { value, done: isDone } = await reader.read();
74
+ if (isDone) break;
75
+ buffer += decoder.decode(value, { stream: true });
76
+ const events = buffer.split("\n\n");
77
+ buffer = events.pop() || "";
78
+ for (const event of events) {
79
+ if (event.startsWith("data: ")) {
80
+ const data = event.slice(6);
81
+ if (data === "[DONE]") return;
82
+ yield JSON.parse(data);
83
+ }
84
+ }
85
+ }
86
+ } finally {
87
+ reader.releaseLock();
88
+ }
89
+ }
90
+ export {
91
+ callLlm
92
+ };
@@ -15,9 +15,10 @@ declare class PluginInput<T extends EmitterWebhookEventName = EmitterWebhookEven
15
15
  eventPayload: EmitterWebhookEvent<T>["payload"];
16
16
  settings: unknown;
17
17
  authToken: string;
18
+ ubiquityKernelToken?: string;
18
19
  ref: string;
19
20
  command: CommandCall;
20
- constructor(privateKey: string, stateId: string, eventName: T, eventPayload: EmitterWebhookEvent<T>["payload"], settings: unknown, authToken: string, ref: string, command: CommandCall);
21
+ constructor(privateKey: string, stateId: string, eventName: T, eventPayload: EmitterWebhookEvent<T>["payload"], settings: unknown, authToken: string, ref: string, command: CommandCall, ubiquityKernelToken?: string);
21
22
  getInputs(): Promise<{
22
23
  signature: string;
23
24
  stateId: string;
@@ -25,6 +26,7 @@ declare class PluginInput<T extends EmitterWebhookEventName = EmitterWebhookEven
25
26
  eventPayload: string;
26
27
  settings: string;
27
28
  authToken: string;
29
+ ubiquityKernelToken: string | undefined;
28
30
  ref: string;
29
31
  command: string;
30
32
  }>;
@@ -34,6 +36,7 @@ interface Inputs {
34
36
  eventName: unknown;
35
37
  eventPayload: unknown;
36
38
  authToken: unknown;
39
+ ubiquityKernelToken?: unknown;
37
40
  settings: unknown;
38
41
  ref: unknown;
39
42
  command: unknown;
@@ -15,9 +15,10 @@ declare class PluginInput<T extends EmitterWebhookEventName = EmitterWebhookEven
15
15
  eventPayload: EmitterWebhookEvent<T>["payload"];
16
16
  settings: unknown;
17
17
  authToken: string;
18
+ ubiquityKernelToken?: string;
18
19
  ref: string;
19
20
  command: CommandCall;
20
- constructor(privateKey: string, stateId: string, eventName: T, eventPayload: EmitterWebhookEvent<T>["payload"], settings: unknown, authToken: string, ref: string, command: CommandCall);
21
+ constructor(privateKey: string, stateId: string, eventName: T, eventPayload: EmitterWebhookEvent<T>["payload"], settings: unknown, authToken: string, ref: string, command: CommandCall, ubiquityKernelToken?: string);
21
22
  getInputs(): Promise<{
22
23
  signature: string;
23
24
  stateId: string;
@@ -25,6 +26,7 @@ declare class PluginInput<T extends EmitterWebhookEventName = EmitterWebhookEven
25
26
  eventPayload: string;
26
27
  settings: string;
27
28
  authToken: string;
29
+ ubiquityKernelToken: string | undefined;
28
30
  ref: string;
29
31
  command: string;
30
32
  }>;
@@ -34,6 +36,7 @@ interface Inputs {
34
36
  eventName: unknown;
35
37
  eventPayload: unknown;
36
38
  authToken: unknown;
39
+ ubiquityKernelToken?: unknown;
37
40
  settings: unknown;
38
41
  ref: unknown;
39
42
  command: unknown;
package/dist/signature.js CHANGED
@@ -42,9 +42,10 @@ var PluginInput = class {
42
42
  eventPayload;
43
43
  settings;
44
44
  authToken;
45
+ ubiquityKernelToken;
45
46
  ref;
46
47
  command;
47
- constructor(privateKey, stateId, eventName, eventPayload, settings, authToken, ref, command) {
48
+ constructor(privateKey, stateId, eventName, eventPayload, settings, authToken, ref, command, ubiquityKernelToken) {
48
49
  this._privateKey = privateKey;
49
50
  this.stateId = stateId;
50
51
  this.eventName = eventName;
@@ -53,6 +54,7 @@ var PluginInput = class {
53
54
  this.authToken = authToken;
54
55
  this.ref = ref;
55
56
  this.command = command;
57
+ this.ubiquityKernelToken = ubiquityKernelToken;
56
58
  }
57
59
  async getInputs() {
58
60
  const inputs = {
@@ -61,6 +63,7 @@ var PluginInput = class {
61
63
  eventPayload: compressString(JSON.stringify(this.eventPayload)),
62
64
  settings: JSON.stringify(this.settings),
63
65
  authToken: this.authToken,
66
+ ubiquityKernelToken: this.ubiquityKernelToken,
64
67
  ref: this.ref,
65
68
  command: JSON.stringify(this.command)
66
69
  };
@@ -79,6 +82,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
79
82
  eventPayload: inputs.eventPayload,
80
83
  settings: inputs.settings,
81
84
  authToken: inputs.authToken,
85
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
82
86
  ref: inputs.ref,
83
87
  command: inputs.command
84
88
  };
@@ -14,9 +14,10 @@ var PluginInput = class {
14
14
  eventPayload;
15
15
  settings;
16
16
  authToken;
17
+ ubiquityKernelToken;
17
18
  ref;
18
19
  command;
19
- constructor(privateKey, stateId, eventName, eventPayload, settings, authToken, ref, command) {
20
+ constructor(privateKey, stateId, eventName, eventPayload, settings, authToken, ref, command, ubiquityKernelToken) {
20
21
  this._privateKey = privateKey;
21
22
  this.stateId = stateId;
22
23
  this.eventName = eventName;
@@ -25,6 +26,7 @@ var PluginInput = class {
25
26
  this.authToken = authToken;
26
27
  this.ref = ref;
27
28
  this.command = command;
29
+ this.ubiquityKernelToken = ubiquityKernelToken;
28
30
  }
29
31
  async getInputs() {
30
32
  const inputs = {
@@ -33,6 +35,7 @@ var PluginInput = class {
33
35
  eventPayload: compressString(JSON.stringify(this.eventPayload)),
34
36
  settings: JSON.stringify(this.settings),
35
37
  authToken: this.authToken,
38
+ ubiquityKernelToken: this.ubiquityKernelToken,
36
39
  ref: this.ref,
37
40
  command: JSON.stringify(this.command)
38
41
  };
@@ -51,6 +54,7 @@ async function verifySignature(publicKeyPem, inputs, signature) {
51
54
  eventPayload: inputs.eventPayload,
52
55
  settings: inputs.settings,
53
56
  authToken: inputs.authToken,
57
+ ubiquityKernelToken: inputs.ubiquityKernelToken,
54
58
  ref: inputs.ref,
55
59
  command: inputs.command
56
60
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ubiquity-os/plugin-sdk",
3
- "version": "3.5.5",
3
+ "version": "3.6.0",
4
4
  "description": "SDK for plugin support.",
5
5
  "author": "Ubiquity DAO",
6
6
  "license": "MIT",
@@ -33,6 +33,9 @@
33
33
  ],
34
34
  "configuration": [
35
35
  "dist/configuration.d.ts"
36
+ ],
37
+ "llm": [
38
+ "dist/llm.d.ts"
36
39
  ]
37
40
  }
38
41
  },
@@ -76,6 +79,11 @@
76
79
  "types": "./dist/configuration.d.ts",
77
80
  "import": "./dist/configuration.mjs",
78
81
  "require": "./dist/configuration.js"
82
+ },
83
+ "./llm": {
84
+ "types": "./dist/llm.d.ts",
85
+ "import": "./dist/llm.mjs",
86
+ "require": "./dist/llm.js"
79
87
  }
80
88
  },
81
89
  "files": [
@@ -113,7 +121,8 @@
113
121
  "@ubiquity-os/ubiquity-os-logger": "^1.4.0",
114
122
  "dotenv": "^17.2.3",
115
123
  "hono": "^4.10.6",
116
- "js-yaml": "^4.1.1"
124
+ "js-yaml": "^4.1.1",
125
+ "openai": "^4.70.2"
117
126
  },
118
127
  "peerDependencies": {
119
128
  "@sinclair/typebox": "0.34.41"