@buzzni/saycode-cli 0.9.8 → 0.9.9

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.
@@ -48,6 +48,15 @@ function contributionList(value, path2, allowedKeys, parse) {
48
48
  return parse(entry, entryPath);
49
49
  });
50
50
  }
51
+ const ENTRYPOINT_PATTERN = /\.(?:cjs|mjs|js)$/;
52
+ function relativePath(value, path2, requireScript = false) {
53
+ const parsed = string(value, path2);
54
+ const segments = parsed.split("/");
55
+ if (parsed.startsWith("/") || parsed.includes("\\") || segments.some((segment) => segment === "" || segment === "." || segment === "..") || requireScript && !ENTRYPOINT_PATTERN.test(parsed)) {
56
+ fail$5(path2, "expected safe relative path");
57
+ }
58
+ return parsed;
59
+ }
51
60
  function keys$1(value, allowed) {
52
61
  const result = record$1(value, "widget");
53
62
  assertOnlyKeys(result, new Set(allowed), "widget");
@@ -138,6 +147,23 @@ function assertExtensionFileViewers(extensionId2, viewers, panels, permissions)
138
147
  if (!permissions.includes("filePreviews.read") || !viewer.id.startsWith(`${extensionId2}.`) || !viewer.panelId.startsWith(`${extensionId2}.`) || !panels.some((panel) => panel.id === viewer.panelId)) fail$5("manifest.contributes.fileViewers", "requires filePreviews.read and an owned panel");
139
148
  }
140
149
  }
150
+ const ELEMENT_PICK_INSPECT_PERMISSION = "browserPicks.inspect";
151
+ function parseExtensionElementPickInspectors(value, apiVersion) {
152
+ if (apiVersion < 4) fail$5("manifest.contributes.elementPickInspectors", "requires Extension API version 4");
153
+ return contributionList(value, "manifest.contributes.elementPickInspectors", ["id", "title", "script"], (entry, path2) => ({
154
+ id: id(entry.id, `${path2}.id`),
155
+ title: string(entry.title, `${path2}.title`),
156
+ script: relativePath(entry.script, `${path2}.script`, true)
157
+ }));
158
+ }
159
+ function assertExtensionElementPickInspectors(extensionId2, inspectors, permissions) {
160
+ if (inspectors.length > 0 !== permissions.includes(ELEMENT_PICK_INSPECT_PERMISSION)) {
161
+ fail$5("manifest.contributes.elementPickInspectors", `requires ${ELEMENT_PICK_INSPECT_PERMISSION} permission and an inspector that uses it`);
162
+ }
163
+ for (const inspector of inspectors) {
164
+ if (!inspector.id.startsWith(`${extensionId2}.`)) fail$5("manifest.contributes.elementPickInspectors", "expected an id declared by the same extension");
165
+ }
166
+ }
141
167
  function parseMachineCommands(value, extensionId2, apiVersion) {
142
168
  if (apiVersion < 3) fail$5("manifest.machineCommands", "requires Extension API version 3");
143
169
  const root = record$1(value, "manifest.machineCommands");
@@ -594,6 +620,7 @@ const EXTENSION_PERMISSIONS = [
594
620
  "browserViewer.installChrome",
595
621
  "artifacts.publishPublic",
596
622
  "filePreviews.read",
623
+ "browserPicks.inspect",
597
624
  // One permission per channel wire method (`src/domain/channelProtocol.ts`), deliberately not a
598
625
  // single adapter grant. Receiving and acking messages is transport work; reading a session,
599
626
  // driving it, or approving a tool call once are three larger and separately-refusable
@@ -633,9 +660,8 @@ class UnsupportedExtensionApiVersionError extends Error {
633
660
  }
634
661
  const SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
635
662
  const ENGINE_RANGE_PATTERN = /^(?:\^|~|>=|<=|>|<)?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
636
- const ENTRYPOINT_PATTERN = /\.(?:cjs|mjs|js)$/;
637
663
  const LOCALE_PATTERN = /^[a-z]{2,3}(?:-[A-Z]{2})?$/;
638
- const CONTRIBUTION_KEYS = /* @__PURE__ */ new Set(["chatWidgets", "commands", "settings", "panels", "projectTemplates", "machineActions", "projectTools", "artifactActions", "channels", "fileViewers"]);
664
+ const CONTRIBUTION_KEYS = /* @__PURE__ */ new Set(["chatWidgets", "commands", "settings", "panels", "projectTemplates", "machineActions", "projectTools", "artifactActions", "channels", "fileViewers", "elementPickInspectors"]);
639
665
  const V2_PERMISSIONS = /* @__PURE__ */ new Set([
640
666
  "browserViewer.open",
641
667
  "browserViewer.install",
@@ -707,14 +733,6 @@ function parseApiVersion(value, options) {
707
733
  }
708
734
  return value;
709
735
  }
710
- function relativePath(value, path2, requireScript = false) {
711
- const parsed = string(value, path2);
712
- const segments = parsed.split("/");
713
- if (parsed.startsWith("/") || parsed.includes("\\") || segments.some((segment) => segment === "" || segment === "." || segment === "..") || requireScript && !ENTRYPOINT_PATTERN.test(parsed)) {
714
- fail$5(path2, "expected safe relative path");
715
- }
716
- return parsed;
717
- }
718
736
  function parseChannelApiVersion(value, apiVersion) {
719
737
  if (value === void 0) return void 0;
720
738
  if (apiVersion < 3) fail$5("manifest.channelApiVersion", "requires Extension API version 3");
@@ -738,6 +756,7 @@ function parseContributions(value, apiVersion, channelApiVersion) {
738
756
  }
739
757
  const result = {};
740
758
  if (parsed.fileViewers !== void 0) result.fileViewers = parseExtensionFileViewers(parsed.fileViewers, apiVersion);
759
+ if (parsed.elementPickInspectors !== void 0) result.elementPickInspectors = parseExtensionElementPickInspectors(parsed.elementPickInspectors, apiVersion);
741
760
  if (parsed.commands !== void 0) {
742
761
  result.commands = contributionList(
743
762
  parsed.commands,
@@ -860,7 +879,8 @@ function parseContributions(value, apiVersion, channelApiVersion) {
860
879
  ...result.machineActions ?? [],
861
880
  ...result.projectTools ?? [],
862
881
  ...result.artifactActions ?? [],
863
- ...result.channels ?? []
882
+ ...result.channels ?? [],
883
+ ...result.elementPickInspectors ?? []
864
884
  ].map((entry) => entry.id);
865
885
  if (new Set(ids).size !== ids.length) fail$5("manifest.contributes", "duplicate contribution id");
866
886
  return result;
@@ -893,6 +913,7 @@ function parseExtensionManifest(value, options) {
893
913
  const typed = parsed;
894
914
  if (typed.startsWith("sessionContext.") && apiVersion !== 5) fail$5("manifest.permissions", "sessionContext requires API 5");
895
915
  if (typed === "filePreviews.read" && apiVersion < 4) fail$5("manifest.permissions", "filePreviews.read requires Extension API version 4");
916
+ if (typed === "browserPicks.inspect" && apiVersion < 4) fail$5("manifest.permissions", "browserPicks.inspect requires Extension API version 4");
896
917
  if (apiVersion < 2 && V2_PERMISSIONS.has(typed)) {
897
918
  fail$5(`manifest.permissions[${index}]`, "requires Extension API version 2");
898
919
  }
@@ -971,6 +992,7 @@ function parseExtensionManifest(value, options) {
971
992
  }
972
993
  }
973
994
  assertExtensionFileViewers(parsedId, contributes.fileViewers ?? [], contributes.panels ?? [], permissions);
995
+ assertExtensionElementPickInspectors(parsedId, contributes.elementPickInspectors ?? [], permissions);
974
996
  assertExtensionProjectToolTargets(parsedId, contributes.projectTools ?? [], contributes.commands ?? []);
975
997
  return {
976
998
  id: parsedId,
@@ -1120,6 +1142,13 @@ function parseRequest(message) {
1120
1142
  connectionRevision: Number(params.connectionRevision)
1121
1143
  } };
1122
1144
  }
1145
+ case "channel.intake": {
1146
+ exactKeys(params, ["apiVersion", "channelId", "connectionId", "provider", "connectionRevision"], "request.params");
1147
+ const lifecycle = parseRequest({ kind: "request", id: id2, method: "channel.lifecycle", params: { ...params, state: "start" } });
1148
+ if (lifecycle.method !== "channel.lifecycle") fail("invalid channel intake");
1149
+ const { state: _state, ...identity } = lifecycle.params;
1150
+ return { kind: "request", id: id2, method, params: identity };
1151
+ }
1123
1152
  case "channel.formatReply": {
1124
1153
  const formatKeys = [
1125
1154
  "apiVersion",
@@ -1268,6 +1297,7 @@ class ExtensionHostRuntime {
1268
1297
  widgets = /* @__PURE__ */ new Map();
1269
1298
  channelHandlers = /* @__PURE__ */ new Map();
1270
1299
  channelFormatters = /* @__PURE__ */ new Map();
1300
+ channelIntakeHandlers = /* @__PURE__ */ new Map();
1271
1301
  channelStates = /* @__PURE__ */ new Map();
1272
1302
  channelCalls = /* @__PURE__ */ new Map();
1273
1303
  channelEpoch = 0;
@@ -1402,6 +1432,7 @@ class ExtensionHostRuntime {
1402
1432
  this.commandHandlers.clear();
1403
1433
  this.channelHandlers.clear();
1404
1434
  this.channelFormatters.clear();
1435
+ this.channelIntakeHandlers.clear();
1405
1436
  this.channelStates.clear();
1406
1437
  this.channelCalls.clear();
1407
1438
  }
@@ -1444,6 +1475,7 @@ class ExtensionHostRuntime {
1444
1475
  this.commandHandlers.clear();
1445
1476
  this.channelHandlers.clear();
1446
1477
  this.channelFormatters.clear();
1478
+ this.channelIntakeHandlers.clear();
1447
1479
  this.channelStates.clear();
1448
1480
  this.channelCalls.clear();
1449
1481
  this.deactivating = false;
@@ -1463,6 +1495,7 @@ class ExtensionHostRuntime {
1463
1495
  }
1464
1496
  if (request.method === "channel.lifecycle") return this.deliverChannel(request.params);
1465
1497
  if (request.method === "channel.formatReply") return this.formatChannelReply(request.params);
1498
+ if (request.method === "channel.intake") return this.deliverChannelIntake(request.params);
1466
1499
  const handler = this.commandHandlers.get(request.params.commandId);
1467
1500
  if (!handler) {
1468
1501
  throw { code: "COMMAND_NOT_REGISTERED", message: `extension command is not registered: ${request.params.commandId}` };
@@ -1511,6 +1544,14 @@ class ExtensionHostRuntime {
1511
1544
  if (this.channelCalls.get(key) === next) this.channelCalls.delete(key);
1512
1545
  }
1513
1546
  }
1547
+ /** A hint for a live connection only; anything else is answered and dropped, never an error. */
1548
+ async deliverChannelIntake(input) {
1549
+ const current = this.channelStates.get(JSON.stringify([input.channelId, input.connectionId]));
1550
+ const handler = this.channelIntakeHandlers.get(input.channelId);
1551
+ if (!handler || this.deactivating || current?.state !== "start" || current.provider !== input.provider || current.connectionRevision !== input.connectionRevision) return null;
1552
+ await handler(Object.freeze({ ...input }));
1553
+ return null;
1554
+ }
1514
1555
  async formatChannelReply(input) {
1515
1556
  const key = JSON.stringify([input.channelId, input.connectionId]);
1516
1557
  const current = this.channelStates.get(key);
@@ -1555,6 +1596,7 @@ class ExtensionHostRuntime {
1555
1596
  const registered = /* @__PURE__ */ new Map();
1556
1597
  const registeredChannels = /* @__PURE__ */ new Map();
1557
1598
  const registeredFormatters = /* @__PURE__ */ new Map();
1599
+ const registeredIntake = /* @__PURE__ */ new Map();
1558
1600
  await loaded.activate({
1559
1601
  extensionId: this.options.manifest.id,
1560
1602
  chatWidgets: { register: (id2, render) => {
@@ -1573,6 +1615,12 @@ class ExtensionHostRuntime {
1573
1615
  throw { code: "INVALID_CHANNEL_REGISTRATION", message: "formatter must be declared and registered once" };
1574
1616
  }
1575
1617
  registeredFormatters.set(channelId, formatter);
1618
+ },
1619
+ onIntake: (channelId, handler) => {
1620
+ if (!this.activating || this.options.manifest.channelApiVersion !== 1 || !this.options.manifest.contributes.channels?.some((channel) => channel.id === channelId) || typeof handler !== "function" || registeredIntake.has(channelId)) {
1621
+ throw { code: "INVALID_CHANNEL_REGISTRATION", message: "intake handler must be declared and registered once" };
1622
+ }
1623
+ registeredIntake.set(channelId, handler);
1576
1624
  }
1577
1625
  },
1578
1626
  commands: {
@@ -1592,6 +1640,8 @@ class ExtensionHostRuntime {
1592
1640
  this.channelHandlers.clear();
1593
1641
  this.channelFormatters.clear();
1594
1642
  for (const [id2, formatter] of registeredFormatters) this.channelFormatters.set(id2, formatter);
1643
+ this.channelIntakeHandlers.clear();
1644
+ for (const [id2, handler] of registeredIntake) this.channelIntakeHandlers.set(id2, handler);
1595
1645
  for (const [id2, handler] of registeredChannels) this.channelHandlers.set(id2, handler);
1596
1646
  this.commandHandlers.clear();
1597
1647
  for (const [commandId, handler] of registered) this.commandHandlers.set(commandId, handler);