@get-bb/plugin-sdk 0.4.9 → 0.4.10
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 +43 -2
- package/bundled-types/bb-plugin-sdk-app.d.ts +158 -27
- package/bundled-types/bb-plugin-sdk-internal-file-navigation-validation.d.ts +42 -0
- package/bundled-types/bb-plugin-sdk-internal-host-policy.d.ts +45 -8
- package/bundled-types/bb-plugin-sdk-provider-bridge.d.ts +294 -2
- package/bundled-types/bb-plugin-sdk-testing-app.d.ts +33 -2
- package/bundled-types/bb-plugin-sdk.d.ts +934 -340
- package/dist/app.js +8 -0
- package/dist/internal/file-navigation-validation.js +135 -0
- package/dist/internal/host-policy.js +104 -0
- package/dist/internal/plugin-app-collector.js +22 -1
- package/dist/provider-bridge.js +1122 -966
- package/dist/testing/app.js +325 -1
- package/dist/testing/index.js +103 -0
- package/package.json +7 -1
package/dist/testing/app.js
CHANGED
|
@@ -251,6 +251,137 @@ function collectComposerCustomization(registration, seenIds, onRejected) {
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// src/internal/file-navigation-validation.ts
|
|
255
|
+
var FILE_PATH_MAX_LENGTH = 32768;
|
|
256
|
+
var WINDOWS_DRIVE_ABSOLUTE_PATH = /^[A-Za-z]:[\\/]/u;
|
|
257
|
+
var WINDOWS_UNC_ABSOLUTE_PATH = /^\\\\/u;
|
|
258
|
+
function isJsonObject(value) {
|
|
259
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
const prototype = Object.getPrototypeOf(value);
|
|
263
|
+
return prototype === Object.prototype || prototype === null;
|
|
264
|
+
}
|
|
265
|
+
function hasExactKeys(value, keys) {
|
|
266
|
+
const actualKeys = Object.keys(value);
|
|
267
|
+
return actualKeys.length === keys.length && keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
|
|
268
|
+
}
|
|
269
|
+
function isNonEmptyIdentity(value) {
|
|
270
|
+
return typeof value === "string" && value.length > 0 && value.length <= FILE_PATH_MAX_LENGTH && value.trim() === value;
|
|
271
|
+
}
|
|
272
|
+
function hasControlCharacter(value) {
|
|
273
|
+
for (const character of value) {
|
|
274
|
+
const codePoint = character.codePointAt(0);
|
|
275
|
+
if (codePoint !== void 0 && codePoint < 32) return true;
|
|
276
|
+
}
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
function hasUnpairedSurrogate(value) {
|
|
280
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
281
|
+
const codeUnit = value.charCodeAt(index);
|
|
282
|
+
if (codeUnit >= 55296 && codeUnit <= 56319) {
|
|
283
|
+
if (index + 1 >= value.length) return true;
|
|
284
|
+
const nextCodeUnit = value.charCodeAt(index + 1);
|
|
285
|
+
if (nextCodeUnit < 56320 || nextCodeUnit > 57343) return true;
|
|
286
|
+
index += 1;
|
|
287
|
+
} else if (codeUnit >= 56320 && codeUnit <= 57343) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
function isValidPathSegment(segment) {
|
|
294
|
+
return segment.length > 0 && segment !== "." && segment !== "..";
|
|
295
|
+
}
|
|
296
|
+
function isPositiveSafeInteger(value) {
|
|
297
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
|
|
298
|
+
}
|
|
299
|
+
function isValidRelativeFilePath(value) {
|
|
300
|
+
if (typeof value !== "string" || value.length === 0 || value.length > FILE_PATH_MAX_LENGTH || value.trim() !== value || value.includes("\\") || hasControlCharacter(value) || hasUnpairedSurrogate(value)) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
return value.split("/").every(isValidPathSegment);
|
|
304
|
+
}
|
|
305
|
+
function isValidAbsoluteHostFilePath(value) {
|
|
306
|
+
if (typeof value !== "string" || value.length === 0 || value.length > FILE_PATH_MAX_LENGTH || value.trim() !== value || hasControlCharacter(value) || hasUnpairedSurrogate(value)) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
if (value.startsWith("/") && !value.startsWith("//")) {
|
|
310
|
+
const segments = value.slice(1).split("/");
|
|
311
|
+
return segments.length > 0 && segments.every(isValidPathSegment);
|
|
312
|
+
}
|
|
313
|
+
if (WINDOWS_DRIVE_ABSOLUTE_PATH.test(value)) {
|
|
314
|
+
const segments = value.slice(3).split(/[\\/]/u);
|
|
315
|
+
return segments.length > 0 && segments.every(isValidPathSegment);
|
|
316
|
+
}
|
|
317
|
+
if (WINDOWS_UNC_ABSOLUTE_PATH.test(value)) {
|
|
318
|
+
const segments = value.slice(2).split(/[\\/]/u);
|
|
319
|
+
return segments.length >= 3 && segments.every(isValidPathSegment);
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
function normalizeExperimentalLiveFileTarget(value) {
|
|
324
|
+
if (!isJsonObject(value) || typeof value.kind !== "string") return null;
|
|
325
|
+
switch (value.kind) {
|
|
326
|
+
case "workspace":
|
|
327
|
+
if (!hasExactKeys(value, ["kind", "environmentId", "path"]) || !isNonEmptyIdentity(value.environmentId) || !isValidRelativeFilePath(value.path)) {
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
kind: value.kind,
|
|
332
|
+
environmentId: value.environmentId,
|
|
333
|
+
path: value.path
|
|
334
|
+
};
|
|
335
|
+
case "host":
|
|
336
|
+
if (!hasExactKeys(value, ["kind", "hostId", "path"]) || !isNonEmptyIdentity(value.hostId) || !isValidAbsoluteHostFilePath(value.path)) {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
return { kind: value.kind, hostId: value.hostId, path: value.path };
|
|
340
|
+
case "thread-storage":
|
|
341
|
+
if (!hasExactKeys(value, ["kind", "threadId", "path"]) || !isNonEmptyIdentity(value.threadId) || !isValidRelativeFilePath(value.path)) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
return { kind: value.kind, threadId: value.threadId, path: value.path };
|
|
345
|
+
default:
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function normalizeExperimentalFileLocation(value) {
|
|
350
|
+
if (value === null) return null;
|
|
351
|
+
if (!isJsonObject(value) || typeof value.kind !== "string") return void 0;
|
|
352
|
+
switch (value.kind) {
|
|
353
|
+
case "line":
|
|
354
|
+
if (!hasExactKeys(value, ["kind", "line", "column"]) || !isPositiveSafeInteger(value.line) || value.column !== null && !isPositiveSafeInteger(value.column)) {
|
|
355
|
+
return void 0;
|
|
356
|
+
}
|
|
357
|
+
return {
|
|
358
|
+
kind: value.kind,
|
|
359
|
+
line: value.line,
|
|
360
|
+
column: value.column
|
|
361
|
+
};
|
|
362
|
+
case "range":
|
|
363
|
+
if (!hasExactKeys(value, ["kind", "startLine", "endLine"]) || !isPositiveSafeInteger(value.startLine) || !isPositiveSafeInteger(value.endLine) || value.endLine < value.startLine) {
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
kind: value.kind,
|
|
368
|
+
startLine: value.startLine,
|
|
369
|
+
endLine: value.endLine
|
|
370
|
+
};
|
|
371
|
+
default:
|
|
372
|
+
return void 0;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function normalizeExperimentalFileOpenOptions(value) {
|
|
376
|
+
if (!isJsonObject(value) || !hasExactKeys(value, ["target", "location"])) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
const target = normalizeExperimentalLiveFileTarget(value.target);
|
|
380
|
+
const location = normalizeExperimentalFileLocation(value.location);
|
|
381
|
+
if (target === null || location === void 0) return null;
|
|
382
|
+
return { target, location };
|
|
383
|
+
}
|
|
384
|
+
|
|
254
385
|
// src/internal/plugin-app-collector.ts
|
|
255
386
|
function collectPluginAppRegistrations(definition, onComposerCustomizationRejected = (reason) => console.warn(reason)) {
|
|
256
387
|
const collected = {
|
|
@@ -324,6 +455,7 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
324
455
|
const kind = "slots.navPanel";
|
|
325
456
|
const id = requireSlotId(kind, registration?.id);
|
|
326
457
|
requireUniqueId(kind, seenIds.navPanel, id);
|
|
458
|
+
const panelId = id;
|
|
327
459
|
const path = requireNonEmptyString(kind, "path", registration.path);
|
|
328
460
|
if (!PLUGIN_SLOT_ID_PATTERN.test(path)) {
|
|
329
461
|
throw new Error(
|
|
@@ -359,8 +491,25 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
359
491
|
`${fixedTabKind}: "layout" must be "padded" or "flush" when set`
|
|
360
492
|
);
|
|
361
493
|
}
|
|
494
|
+
const fixedTabPanelId = requireNonEmptyString(
|
|
495
|
+
fixedTabKind,
|
|
496
|
+
"panelId",
|
|
497
|
+
fixedTab?.panelId
|
|
498
|
+
);
|
|
499
|
+
if (fixedTabPanelId !== panelId) {
|
|
500
|
+
throw new Error(
|
|
501
|
+
`${fixedTabKind}: "panelId" must match its containing navPanel id ${JSON.stringify(panelId)}`
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
const experimentalTarget = fixedTab?.experimental_target;
|
|
505
|
+
if (experimentalTarget !== void 0 && (typeof experimentalTarget !== "object" || experimentalTarget === null || typeof Reflect.get(experimentalTarget, "validate") !== "function")) {
|
|
506
|
+
throw new Error(
|
|
507
|
+
`${fixedTabKind}: "experimental_target.validate" must be a function when set`
|
|
508
|
+
);
|
|
509
|
+
}
|
|
362
510
|
return {
|
|
363
511
|
id: id2,
|
|
512
|
+
panelId: fixedTabPanelId,
|
|
364
513
|
title: requireNonEmptyString(
|
|
365
514
|
fixedTabKind,
|
|
366
515
|
"title",
|
|
@@ -372,7 +521,10 @@ function collectPluginAppRegistrations(definition, onComposerCustomizationReject
|
|
|
372
521
|
fixedTab?.icon
|
|
373
522
|
),
|
|
374
523
|
component: requireComponent(fixedTabKind, fixedTab?.component),
|
|
375
|
-
...layout === void 0 ? {} : { layout }
|
|
524
|
+
...layout === void 0 ? {} : { layout },
|
|
525
|
+
...experimentalTarget === void 0 ? {} : {
|
|
526
|
+
experimental_target: experimentalTarget
|
|
527
|
+
}
|
|
376
528
|
};
|
|
377
529
|
});
|
|
378
530
|
})();
|
|
@@ -679,6 +831,65 @@ function TestThreadChat({
|
|
|
679
831
|
function TestMarkdown({ content, className }) {
|
|
680
832
|
return /* @__PURE__ */ jsx("div", { "data-testid": "bb-markdown", className, children: content });
|
|
681
833
|
}
|
|
834
|
+
function TestUrlLink({
|
|
835
|
+
href,
|
|
836
|
+
onClick,
|
|
837
|
+
rel,
|
|
838
|
+
target,
|
|
839
|
+
...anchorProps
|
|
840
|
+
}) {
|
|
841
|
+
const navigate = useSlotEnv("experimental_UrlLink").navigate;
|
|
842
|
+
const normalizedTarget = target?.toLowerCase();
|
|
843
|
+
const opensNewBrowsingContext = normalizedTarget !== void 0 && normalizedTarget !== "" && normalizedTarget !== "_self" && normalizedTarget !== "_parent" && normalizedTarget !== "_top" && normalizedTarget !== "_unfencedtop";
|
|
844
|
+
const relTokens = rel?.split(/\s+/u).filter(Boolean) ?? [];
|
|
845
|
+
const normalizedRelTokens = relTokens.map((token) => token.toLowerCase());
|
|
846
|
+
const resolvedRel = opensNewBrowsingContext && !normalizedRelTokens.includes("opener") ? [
|
|
847
|
+
...relTokens,
|
|
848
|
+
...normalizedRelTokens.includes("noopener") ? [] : ["noopener"],
|
|
849
|
+
...normalizedRelTokens.includes("noreferrer") ? [] : ["noreferrer"]
|
|
850
|
+
].join(" ") : rel;
|
|
851
|
+
return /* @__PURE__ */ jsx(
|
|
852
|
+
"a",
|
|
853
|
+
{
|
|
854
|
+
...anchorProps,
|
|
855
|
+
href,
|
|
856
|
+
target,
|
|
857
|
+
rel: resolvedRel,
|
|
858
|
+
onClick: (event) => {
|
|
859
|
+
onClick?.(event);
|
|
860
|
+
if (event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.currentTarget.hasAttribute("download") || event.currentTarget.hasAttribute("target")) {
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
if (navigate.experimental_openUrl(href)) event.preventDefault();
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
);
|
|
867
|
+
}
|
|
868
|
+
function TestFileLink({
|
|
869
|
+
target,
|
|
870
|
+
location = null,
|
|
871
|
+
onClick,
|
|
872
|
+
...anchorProps
|
|
873
|
+
}) {
|
|
874
|
+
const navigate = useSlotEnv("experimental_FileLink").navigate;
|
|
875
|
+
const options = normalizeExperimentalFileOpenOptions({ target, location });
|
|
876
|
+
const href = options === null ? void 0 : `./${encodeURIComponent(options.target.path)}`;
|
|
877
|
+
return /* @__PURE__ */ jsx(
|
|
878
|
+
"a",
|
|
879
|
+
{
|
|
880
|
+
...anchorProps,
|
|
881
|
+
href,
|
|
882
|
+
onClick: (event) => {
|
|
883
|
+
onClick?.(event);
|
|
884
|
+
if (options === null || event.defaultPrevented || event.button !== 0 || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.currentTarget.hasAttribute("download")) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
event.preventDefault();
|
|
888
|
+
navigate.experimental_openFilePreview(options);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
);
|
|
892
|
+
}
|
|
682
893
|
function TestNewThreadComposer({
|
|
683
894
|
defaultProjectId,
|
|
684
895
|
defaultProviderId,
|
|
@@ -829,6 +1040,30 @@ var testPluginSdkApp = {
|
|
|
829
1040
|
useBbNavigate() {
|
|
830
1041
|
return useSlotEnv("useBbNavigate").navigate;
|
|
831
1042
|
},
|
|
1043
|
+
experimental_useAppPanel() {
|
|
1044
|
+
return useSlotEnv("experimental_useAppPanel").appPanel;
|
|
1045
|
+
},
|
|
1046
|
+
experimental_useFixedTabTarget(tab) {
|
|
1047
|
+
const store = useSlotEnv("experimental_useFixedTabTarget").fixedTabTarget;
|
|
1048
|
+
const state = useSyncExternalStore(
|
|
1049
|
+
store.subscribe,
|
|
1050
|
+
store.getSnapshot,
|
|
1051
|
+
store.getSnapshot
|
|
1052
|
+
);
|
|
1053
|
+
if (state === null || state.panelId !== tab.panelId || state.tabId !== tab.id || tab.experimental_target === void 0) {
|
|
1054
|
+
return null;
|
|
1055
|
+
}
|
|
1056
|
+
try {
|
|
1057
|
+
if (!tab.experimental_target.validate(state.target)) return null;
|
|
1058
|
+
} catch {
|
|
1059
|
+
return null;
|
|
1060
|
+
}
|
|
1061
|
+
return {
|
|
1062
|
+
clear: () => store.clear(state.sequence),
|
|
1063
|
+
sequence: state.sequence,
|
|
1064
|
+
target: state.target
|
|
1065
|
+
};
|
|
1066
|
+
},
|
|
832
1067
|
useComposer() {
|
|
833
1068
|
const composer = useSlotEnv("useComposer").composer;
|
|
834
1069
|
const version = useSyncExternalStore(
|
|
@@ -847,6 +1082,8 @@ var testPluginSdkApp = {
|
|
|
847
1082
|
},
|
|
848
1083
|
ThreadChat: TestThreadChat,
|
|
849
1084
|
Markdown: TestMarkdown,
|
|
1085
|
+
experimental_FileLink: TestFileLink,
|
|
1086
|
+
experimental_UrlLink: TestUrlLink,
|
|
850
1087
|
experimental_NewThreadComposer: TestNewThreadComposer,
|
|
851
1088
|
experimental_SourceCode: TestSourceCode,
|
|
852
1089
|
experimental_Diff: TestDiff,
|
|
@@ -1086,6 +1323,70 @@ function renderSlot(registration, props, options = {}) {
|
|
|
1086
1323
|
}
|
|
1087
1324
|
};
|
|
1088
1325
|
const navigateCalls = [];
|
|
1326
|
+
const experimental_fixedTabOpenCalls = [];
|
|
1327
|
+
let fixedTabTargetSnapshot = options.experimental_fixedTabTarget === void 0 ? null : {
|
|
1328
|
+
panelId: options.experimental_fixedTabTarget.panelId,
|
|
1329
|
+
sequence: 1,
|
|
1330
|
+
tabId: options.experimental_fixedTabTarget.tabId,
|
|
1331
|
+
target: strictJsonRoundTrip(
|
|
1332
|
+
options.experimental_fixedTabTarget.target,
|
|
1333
|
+
"fixed tab target"
|
|
1334
|
+
)
|
|
1335
|
+
};
|
|
1336
|
+
const fixedTabTargetListeners = /* @__PURE__ */ new Set();
|
|
1337
|
+
const fixedTabTarget = {
|
|
1338
|
+
getSnapshot: () => fixedTabTargetSnapshot,
|
|
1339
|
+
subscribe(listener) {
|
|
1340
|
+
fixedTabTargetListeners.add(listener);
|
|
1341
|
+
return () => fixedTabTargetListeners.delete(listener);
|
|
1342
|
+
},
|
|
1343
|
+
clear(sequence) {
|
|
1344
|
+
if (fixedTabTargetSnapshot?.sequence !== sequence) return;
|
|
1345
|
+
fixedTabTargetSnapshot = null;
|
|
1346
|
+
for (const listener of fixedTabTargetListeners) listener();
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
const appPanel = {
|
|
1350
|
+
openFixedTab(panelOptions) {
|
|
1351
|
+
let target;
|
|
1352
|
+
if (panelOptions.target !== void 0) {
|
|
1353
|
+
try {
|
|
1354
|
+
target = strictJsonRoundTrip(
|
|
1355
|
+
panelOptions.target,
|
|
1356
|
+
"fixed tab open target"
|
|
1357
|
+
);
|
|
1358
|
+
} catch {
|
|
1359
|
+
return false;
|
|
1360
|
+
}
|
|
1361
|
+
if (panelOptions.tab.experimental_target === void 0) return false;
|
|
1362
|
+
try {
|
|
1363
|
+
if (!panelOptions.tab.experimental_target.validate(target)) {
|
|
1364
|
+
return false;
|
|
1365
|
+
}
|
|
1366
|
+
} catch {
|
|
1367
|
+
return false;
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
const call = {
|
|
1371
|
+
surface: panelOptions.surface,
|
|
1372
|
+
panelId: panelOptions.tab.panelId,
|
|
1373
|
+
tabId: panelOptions.tab.id,
|
|
1374
|
+
...target === void 0 ? {} : { target }
|
|
1375
|
+
};
|
|
1376
|
+
experimental_fixedTabOpenCalls.push(call);
|
|
1377
|
+
const accepted = options.experimental_openFixedTab?.(call) ?? false;
|
|
1378
|
+
if (accepted && target !== void 0) {
|
|
1379
|
+
fixedTabTargetSnapshot = {
|
|
1380
|
+
panelId: panelOptions.tab.panelId,
|
|
1381
|
+
sequence: (fixedTabTargetSnapshot?.sequence ?? 0) + 1,
|
|
1382
|
+
tabId: panelOptions.tab.id,
|
|
1383
|
+
target
|
|
1384
|
+
};
|
|
1385
|
+
for (const listener of fixedTabTargetListeners) listener();
|
|
1386
|
+
}
|
|
1387
|
+
return accepted;
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1089
1390
|
const sidebarActionCalls = [];
|
|
1090
1391
|
const sidebarPullRequests = new Map(
|
|
1091
1392
|
Object.entries(options.sidebarPullRequests ?? {})
|
|
@@ -1151,6 +1452,24 @@ function renderSlot(registration, props, options = {}) {
|
|
|
1151
1452
|
options: panelOptions
|
|
1152
1453
|
});
|
|
1153
1454
|
return options.openThreadPanel?.(panelOptions) ?? false;
|
|
1455
|
+
},
|
|
1456
|
+
experimental_openUrl(url) {
|
|
1457
|
+
navigateCalls.push({ method: "experimental_openUrl", url });
|
|
1458
|
+
return options.openUrl?.(url) ?? false;
|
|
1459
|
+
},
|
|
1460
|
+
experimental_openFilePreview(fileOptions) {
|
|
1461
|
+
navigateCalls.push({
|
|
1462
|
+
method: "experimental_openFilePreview",
|
|
1463
|
+
options: fileOptions
|
|
1464
|
+
});
|
|
1465
|
+
return options.openFilePreview?.(fileOptions) ?? false;
|
|
1466
|
+
},
|
|
1467
|
+
experimental_openFileExternally(fileOptions) {
|
|
1468
|
+
navigateCalls.push({
|
|
1469
|
+
method: "experimental_openFileExternally",
|
|
1470
|
+
options: fileOptions
|
|
1471
|
+
});
|
|
1472
|
+
return options.openFileExternally?.(fileOptions) ?? false;
|
|
1154
1473
|
}
|
|
1155
1474
|
};
|
|
1156
1475
|
const projectId = options.context?.projectId ?? null;
|
|
@@ -1252,6 +1571,9 @@ ${block}
|
|
|
1252
1571
|
bbContext: { projectId, threadId },
|
|
1253
1572
|
navigate,
|
|
1254
1573
|
navigateCalls,
|
|
1574
|
+
appPanel,
|
|
1575
|
+
experimental_fixedTabOpenCalls,
|
|
1576
|
+
fixedTabTarget,
|
|
1255
1577
|
composer,
|
|
1256
1578
|
composerLog,
|
|
1257
1579
|
sidebarThreads,
|
|
@@ -1307,6 +1629,7 @@ ${block}
|
|
|
1307
1629
|
setComposerText,
|
|
1308
1630
|
setComposerScope,
|
|
1309
1631
|
navigateCalls,
|
|
1632
|
+
experimental_fixedTabOpenCalls,
|
|
1310
1633
|
sidebarActionCalls,
|
|
1311
1634
|
composer: composerLog,
|
|
1312
1635
|
behavior: {
|
|
@@ -1318,6 +1641,7 @@ ${block}
|
|
|
1318
1641
|
inspection: {
|
|
1319
1642
|
rpcCalls,
|
|
1320
1643
|
navigateCalls,
|
|
1644
|
+
experimental_fixedTabOpenCalls,
|
|
1321
1645
|
sidebarActionCalls,
|
|
1322
1646
|
composer: composerLog
|
|
1323
1647
|
},
|
package/dist/testing/index.js
CHANGED
|
@@ -66,6 +66,7 @@ var PLUGIN_AGENT_DYNAMIC_INSTRUCTIONS_MAX_CHARS = 4096;
|
|
|
66
66
|
var PLUGIN_AGENT_TOOL_PARAMETERS_MAX_BYTES = 128 * 1024;
|
|
67
67
|
var MENTION_PROVIDER_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
68
68
|
var PROVIDER_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,63}$/;
|
|
69
|
+
var PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES = 64 * 1024;
|
|
69
70
|
var SETTING_KEY_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
70
71
|
var settingsBaseFields = {
|
|
71
72
|
label: z2.string().min(1),
|
|
@@ -267,6 +268,70 @@ function validateProviderLiteralArray(args) {
|
|
|
267
268
|
}
|
|
268
269
|
return Object.freeze(normalized);
|
|
269
270
|
}
|
|
271
|
+
function normalizeProviderBridgeOptions(providerId, value) {
|
|
272
|
+
const active = /* @__PURE__ */ new Set();
|
|
273
|
+
function visit(current, path) {
|
|
274
|
+
if (current === null || typeof current === "string" || typeof current === "boolean") {
|
|
275
|
+
return current;
|
|
276
|
+
}
|
|
277
|
+
if (typeof current === "number") {
|
|
278
|
+
if (!Number.isFinite(current)) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`provider "${providerId}" experimental_bridgeOptions${path} must be finite JSON`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
return current;
|
|
284
|
+
}
|
|
285
|
+
if (typeof current !== "object") {
|
|
286
|
+
throw new Error(
|
|
287
|
+
`provider "${providerId}" experimental_bridgeOptions${path} must be JSON`
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
if (active.has(current)) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`provider "${providerId}" experimental_bridgeOptions must not contain cycles`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
active.add(current);
|
|
296
|
+
try {
|
|
297
|
+
if (Array.isArray(current)) {
|
|
298
|
+
const normalized3 = current.map(
|
|
299
|
+
(entry, index) => visit(entry, `${path}[${index}]`)
|
|
300
|
+
);
|
|
301
|
+
Object.freeze(normalized3);
|
|
302
|
+
return normalized3;
|
|
303
|
+
}
|
|
304
|
+
const prototype = Object.getPrototypeOf(current);
|
|
305
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
306
|
+
throw new Error(
|
|
307
|
+
`provider "${providerId}" experimental_bridgeOptions${path} must contain only plain JSON objects`
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
const normalized2 = Object.fromEntries(
|
|
311
|
+
Object.entries(current).map(([key, entry]) => [
|
|
312
|
+
key,
|
|
313
|
+
visit(entry, `${path}.${key}`)
|
|
314
|
+
])
|
|
315
|
+
);
|
|
316
|
+
Object.freeze(normalized2);
|
|
317
|
+
return normalized2;
|
|
318
|
+
} finally {
|
|
319
|
+
active.delete(current);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
const normalized = visit(value, "");
|
|
323
|
+
if (normalized === null || Array.isArray(normalized) || typeof normalized !== "object") {
|
|
324
|
+
throw new Error(
|
|
325
|
+
`provider "${providerId}" experimental_bridgeOptions must be an object`
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
if (Buffer.byteLength(JSON.stringify(normalized), "utf8") > PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES) {
|
|
329
|
+
throw new Error(
|
|
330
|
+
`provider "${providerId}" experimental_bridgeOptions exceeds ${PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES} bytes`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
333
|
+
return normalized;
|
|
334
|
+
}
|
|
270
335
|
function validatePluginProviderDeclaration(declaration) {
|
|
271
336
|
if (typeof declaration !== "object" || declaration === null) {
|
|
272
337
|
throw new Error("provider declaration must be an object");
|
|
@@ -304,6 +369,24 @@ function validatePluginProviderDeclaration(declaration) {
|
|
|
304
369
|
if (typeof capabilities !== "object" || capabilities === null) {
|
|
305
370
|
throw new Error(`provider "${id}" capabilities must be an object`);
|
|
306
371
|
}
|
|
372
|
+
const experimentalProviderHealth = capabilities.experimental_providerHealth ?? false;
|
|
373
|
+
const experimentalProviderUsage = capabilities.experimental_providerUsage ?? false;
|
|
374
|
+
const experimentalProviderInstallation = capabilities.experimental_providerInstallation ?? false;
|
|
375
|
+
if (typeof experimentalProviderHealth !== "boolean") {
|
|
376
|
+
throw new Error(
|
|
377
|
+
`provider "${id}" capabilities.experimental_providerHealth must be a boolean`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
if (typeof experimentalProviderUsage !== "boolean") {
|
|
381
|
+
throw new Error(
|
|
382
|
+
`provider "${id}" capabilities.experimental_providerUsage must be a boolean`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
if (typeof experimentalProviderInstallation !== "boolean") {
|
|
386
|
+
throw new Error(
|
|
387
|
+
`provider "${id}" capabilities.experimental_providerInstallation must be a boolean`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
307
390
|
const booleanCapabilityFields = [
|
|
308
391
|
"supportsServiceTier",
|
|
309
392
|
"supportsNativeUserQuestion",
|
|
@@ -325,6 +408,9 @@ function validatePluginProviderDeclaration(declaration) {
|
|
|
325
408
|
);
|
|
326
409
|
}
|
|
327
410
|
const normalizedCapabilities = Object.freeze({
|
|
411
|
+
experimental_providerHealth: experimentalProviderHealth,
|
|
412
|
+
experimental_providerUsage: experimentalProviderUsage,
|
|
413
|
+
experimental_providerInstallation: experimentalProviderInstallation,
|
|
328
414
|
supportsServiceTier: capabilities.supportsServiceTier,
|
|
329
415
|
supportsNativeUserQuestion: capabilities.supportsNativeUserQuestion,
|
|
330
416
|
fork: capabilities.fork,
|
|
@@ -354,10 +440,27 @@ function validatePluginProviderDeclaration(declaration) {
|
|
|
354
440
|
allowed: PLUGIN_PROVIDER_COMPOSER_ACTION_VALUES,
|
|
355
441
|
requireNonEmpty: false
|
|
356
442
|
});
|
|
443
|
+
const bridgeOptions = declaration.experimental_bridgeOptions === void 0 ? void 0 : normalizeProviderBridgeOptions(
|
|
444
|
+
id,
|
|
445
|
+
declaration.experimental_bridgeOptions
|
|
446
|
+
);
|
|
447
|
+
const visibility = declaration.experimental_visibility ?? "always";
|
|
448
|
+
if (visibility !== "always" && visibility !== "installed") {
|
|
449
|
+
throw new Error(
|
|
450
|
+
`provider "${id}" experimental_visibility must be "always" or "installed"`
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
if (visibility === "installed" && !normalizedCapabilities.experimental_providerHealth) {
|
|
454
|
+
throw new Error(
|
|
455
|
+
`provider "${id}" experimental_visibility "installed" requires experimental_providerHealth`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
357
458
|
return Object.freeze({
|
|
358
459
|
id,
|
|
359
460
|
displayName,
|
|
360
461
|
...icon === void 0 ? {} : { icon },
|
|
462
|
+
...bridgeOptions === void 0 ? {} : { experimental_bridgeOptions: bridgeOptions },
|
|
463
|
+
experimental_visibility: visibility,
|
|
361
464
|
capabilities: normalizedCapabilities,
|
|
362
465
|
composerActions
|
|
363
466
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@get-bb/plugin-sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.10",
|
|
4
4
|
"homepage": "https://github.com/get-bb/bb#readme",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/get-bb/bb/issues"
|
|
@@ -56,6 +56,12 @@
|
|
|
56
56
|
"import": "./dist/internal/composer-view.js",
|
|
57
57
|
"default": "./dist/internal/composer-view.js"
|
|
58
58
|
},
|
|
59
|
+
"./internal/file-navigation-validation": {
|
|
60
|
+
"source": "./src/internal/file-navigation-validation.ts",
|
|
61
|
+
"types": "./bundled-types/bb-plugin-sdk-internal-file-navigation-validation.d.ts",
|
|
62
|
+
"import": "./dist/internal/file-navigation-validation.js",
|
|
63
|
+
"default": "./dist/internal/file-navigation-validation.js"
|
|
64
|
+
},
|
|
59
65
|
"./internal/host-policy": {
|
|
60
66
|
"source": "./src/internal/host-policy.ts",
|
|
61
67
|
"types": "./bundled-types/bb-plugin-sdk-internal-host-policy.d.ts",
|