@rogatio/cli 6.1.0 → 6.3.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.
@@ -3339,6 +3339,37 @@ var EditorControllerImpl = class {
3339
3339
  this.applyGroupEnablementLabel(button, enabled.has(groupId), groupName);
3340
3340
  }
3341
3341
  }
3342
+ /**
3343
+ * Scroll a rendered rule card into view and focus it. Explicit `behavior`
3344
+ * overrides the stylesheet's `scroll-behavior`, so the reduced-motion
3345
+ * preference has to be read here too.
3346
+ */
3347
+ revealRuleCard(groupId, ruleId) {
3348
+ const card = this.document.getElementById(ruleAnchorId(groupId, ruleId));
3349
+ if (!card) return;
3350
+ const view = this.document.defaultView;
3351
+ const reduceMotion = typeof view?.matchMedia === "function" && view.matchMedia("(prefers-reduced-motion: reduce)").matches;
3352
+ card.scrollIntoView({
3353
+ block: "start",
3354
+ behavior: reduceMotion ? "auto" : "smooth"
3355
+ });
3356
+ card.focus({ preventScroll: true });
3357
+ }
3358
+ /**
3359
+ * Deep-link to a rule: route to the group that owns it, render, then reveal
3360
+ * the rule card. Resolves against the draft, because the editor renders the
3361
+ * draft and a rule id from committed storage may have been renamed there. An
3362
+ * unknown rule still lands on its group; an unknown group falls back to
3363
+ * Overview. Never throws, so a stale link lands somewhere real rather than
3364
+ * failing.
3365
+ */
3366
+ navigateToRule(groupId, ruleId) {
3367
+ this.navigateToGroup(groupId);
3368
+ const resolvedGroup = safeText(this.currentGroupId());
3369
+ const resolvedRule = safeText(ruleId);
3370
+ if (resolvedGroup.length === 0 || resolvedRule.length === 0) return;
3371
+ this.revealRuleCard(resolvedGroup, resolvedRule);
3372
+ }
3342
3373
  navigateToSearchResult(path) {
3343
3374
  const segments = decodePointer(path);
3344
3375
  if (segments?.[0] !== "groups") {
@@ -3357,12 +3388,7 @@ var EditorControllerImpl = class {
3357
3388
  this.statusMessage = "Jumped to rule.";
3358
3389
  this.render();
3359
3390
  if (rule && typeof rule.id === "string") {
3360
- const anchor = `rogatio-rule-${safeText(group.id)}-${safeText(rule.id)}`;
3361
- const card = this.document.getElementById(anchor);
3362
- if (card) {
3363
- card.scrollIntoView({ block: "start", behavior: "smooth" });
3364
- card.focus({ preventScroll: true });
3365
- }
3391
+ this.revealRuleCard(group.id, rule.id);
3366
3392
  }
3367
3393
  }
3368
3394
  navigateToPath(path) {
@@ -3872,7 +3898,7 @@ var EditorControllerImpl = class {
3872
3898
  const card = this.document.createElement("article");
3873
3899
  card.dataset.ruleCard = "true";
3874
3900
  card.dataset.ruleId = ruleId;
3875
- card.id = `rogatio-rule-${groupId}-${ruleId}`;
3901
+ card.id = ruleAnchorId(groupId, ruleId);
3876
3902
  card.tabIndex = -1;
3877
3903
  const headingRow = this.document.createElement("div");
3878
3904
  headingRow.dataset.ruleHeading = "true";
@@ -4551,6 +4577,17 @@ function saveFailureDiagnostic(value) {
4551
4577
  const message = typeof value.message === "string" && value.message.length > 0 ? value.message : "The host could not save the project.";
4552
4578
  return diagnostic(code, path, message);
4553
4579
  }
4580
+ function encodeAnchorSegment(value) {
4581
+ return encodeURIComponent(value).replace(
4582
+ /[!'()*]/g,
4583
+ (character) => `%${character.charCodeAt(0).toString(16).toUpperCase()}`
4584
+ );
4585
+ }
4586
+ function ruleAnchorId(groupId, ruleId) {
4587
+ return `rogatio-rule-${encodeAnchorSegment(safeText(groupId))}:${encodeAnchorSegment(
4588
+ safeText(ruleId)
4589
+ )}`;
4590
+ }
4554
4591
  function resolveGroupRoute(groupId, groupIds) {
4555
4592
  if (typeof groupId === "string" && groupId.length > 0 && groupIds.has(groupId)) {
4556
4593
  return { kind: "group", groupId };
@@ -4595,5 +4632,6 @@ export {
4595
4632
  createRequestBodyRuleType,
4596
4633
  createResponseBodyRuleType,
4597
4634
  queryRuleType,
4635
+ ruleAnchorId,
4598
4636
  urlToExactRegex
4599
4637
  };
@@ -21706,7 +21706,8 @@ var ENVELOPE_MESSAGE_TYPES = /* @__PURE__ */ new Set([
21706
21706
  "mock.response",
21707
21707
  "ai.complete",
21708
21708
  "ai.stream.chunk",
21709
- "ai.error"
21709
+ "ai.error",
21710
+ "ai.status"
21710
21711
  ]);
21711
21712
  var EnvelopeError = class extends Error {
21712
21713
  constructor(message) {
@@ -23313,9 +23314,36 @@ function createNativeRuntimeController(options) {
23313
23314
  let capability;
23314
23315
  let activePolicy = null;
23315
23316
  const mockTokens = /* @__PURE__ */ new Map();
23317
+ let aiConfigInForce = options.aiProviderConfig ?? null;
23316
23318
  let aiClient;
23317
- if (options.aiProviderConfig) {
23318
- aiClient = createAIClient(options.aiProviderConfig);
23319
+ if (aiConfigInForce) {
23320
+ aiClient = createAIClient(aiConfigInForce);
23321
+ }
23322
+ function isUsableAIProviderConfig(value) {
23323
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
23324
+ return false;
23325
+ }
23326
+ const record = value;
23327
+ return typeof record.providerUrl === "string" && record.providerUrl.length > 0 && typeof record.model === "string" && record.model.length > 0 && typeof record.apiKey === "string" && record.apiKey.length > 0;
23328
+ }
23329
+ function canonicalAIConfig(config) {
23330
+ return config === null ? "null" : JSON.stringify([config.providerUrl, config.model, config.apiKey]);
23331
+ }
23332
+ async function refreshAIProviderConfig() {
23333
+ const reader = options.aiConfigReader;
23334
+ if (!reader) return aiConfigInForce;
23335
+ let next = null;
23336
+ try {
23337
+ const read = await reader();
23338
+ next = isUsableAIProviderConfig(read) ? read : null;
23339
+ } catch {
23340
+ next = null;
23341
+ }
23342
+ if (canonicalAIConfig(next) !== canonicalAIConfig(aiConfigInForce)) {
23343
+ aiConfigInForce = next;
23344
+ aiClient = next ? createAIClient(next) : void 0;
23345
+ }
23346
+ return aiConfigInForce;
23319
23347
  }
23320
23348
  async function bindInterception(current, sessionConfig) {
23321
23349
  if (sessionConfig.pacRoutes.length === 0) {
@@ -23747,7 +23775,23 @@ function createNativeRuntimeController(options) {
23747
23775
  metadata: { state, presetDigest: preset.digest }
23748
23776
  };
23749
23777
  }
23778
+ case "ai.status": {
23779
+ const config = await refreshAIProviderConfig();
23780
+ const metadata = config !== null ? {
23781
+ configured: true,
23782
+ providerUrl: config.providerUrl,
23783
+ model: config.model
23784
+ } : { configured: false };
23785
+ return {
23786
+ protocol: "v1",
23787
+ type: "ai.status",
23788
+ ...requestId !== void 0 ? { requestId } : {},
23789
+ timestamp,
23790
+ metadata
23791
+ };
23792
+ }
23750
23793
  case "ai.complete": {
23794
+ await refreshAIProviderConfig();
23751
23795
  if (!aiClient) {
23752
23796
  return {
23753
23797
  protocol: "v1",
@@ -23765,7 +23809,7 @@ function createNativeRuntimeController(options) {
23765
23809
  try {
23766
23810
  const result = await aiClient.complete({
23767
23811
  messages: [...meta.messages],
23768
- model: meta.model || options.aiProviderConfig?.model || "",
23812
+ model: meta.model || aiConfigInForce?.model || "",
23769
23813
  temperature: meta.temperature,
23770
23814
  responseFormat: meta.responseFormat
23771
23815
  });
@@ -23794,6 +23838,7 @@ function createNativeRuntimeController(options) {
23794
23838
  }
23795
23839
  }
23796
23840
  case "ai.stream.chunk": {
23841
+ await refreshAIProviderConfig();
23797
23842
  if (!aiClient) {
23798
23843
  return {
23799
23844
  protocol: "v1",
@@ -23811,7 +23856,7 @@ function createNativeRuntimeController(options) {
23811
23856
  try {
23812
23857
  for await (const chunk of aiClient.stream({
23813
23858
  messages: [...meta.messages],
23814
- model: meta.model || options.aiProviderConfig?.model || "",
23859
+ model: meta.model || aiConfigInForce?.model || "",
23815
23860
  temperature: meta.temperature
23816
23861
  })) {
23817
23862
  return {
@@ -24331,6 +24376,7 @@ function createNativeHost(options) {
24331
24376
  fileRoot: options.fileRoot,
24332
24377
  ...options.mockPort !== void 0 ? { mockPort: options.mockPort } : {},
24333
24378
  ...options.aiProviderConfig !== void 0 ? { aiProviderConfig: options.aiProviderConfig } : {},
24379
+ ...options.aiConfigReader !== void 0 ? { aiConfigReader: options.aiConfigReader } : {},
24334
24380
  ...options.clock ? { clock: options.clock } : {}
24335
24381
  });
24336
24382
  let faucet = null;
@@ -27262,6 +27308,7 @@ async function runtimeHostCommand(args, options = {}) {
27262
27308
  fileRoot: root,
27263
27309
  ...mockPort !== void 0 ? { mockPort } : {},
27264
27310
  ...aiProviderConfig2 !== null ? { aiProviderConfig: aiProviderConfig2 } : {},
27311
+ aiConfigReader: readProviderConfig,
27265
27312
  onReady: () => console.error(
27266
27313
  "rogatio runtime-host active (deferred, waiting for project)"
27267
27314
  )
@@ -27323,6 +27370,7 @@ async function runtimeHostCommand(args, options = {}) {
27323
27370
  fileRoot: rootDir,
27324
27371
  ...mockPort !== void 0 ? { mockPort } : {},
27325
27372
  ...aiProviderConfig !== null ? { aiProviderConfig } : {},
27373
+ aiConfigReader: readProviderConfig,
27326
27374
  onReady: () => console.error("rogatio runtime-host active")
27327
27375
  });
27328
27376
  return 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rogatio/cli",
3
- "version": "6.1.0",
3
+ "version": "6.3.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Local-first browser request and response rules — editor host, file verification, test runner, and runtime dispatch.",