snipara-companion 3.4.0 → 3.4.1

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/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  Release notes for `snipara-companion`, newest first.
4
4
 
5
+ ## New In 3.4.1
6
+
7
+ - Declares provider API keys by environment-variable name and supports
8
+ `authorization` or `x-api-key` headers without persisting secret values.
9
+ - Resolves declared Codex and Claude CLI workers to explicit native adapters;
10
+ unsupported generic CLI profiles fail closed.
11
+ - Preserves opt-in local routing behavior while forwarding provider auth metadata
12
+ to the orchestrator catalog.
13
+
5
14
  ## New In 3.4.0
6
15
 
7
16
  - Adds repeatable `workers execute --output-fragment` contracts.
package/dist/index.d.ts CHANGED
@@ -4478,6 +4478,8 @@ interface LocalWorkerDeclaration {
4478
4478
  endpointType: "local";
4479
4479
  provider: string;
4480
4480
  baseUrl: string;
4481
+ apiKeyEnv?: string;
4482
+ apiKeyHeader?: "authorization" | "x-api-key";
4481
4483
  command?: string;
4482
4484
  model?: string;
4483
4485
  preferModel?: string;
@@ -4496,11 +4498,16 @@ interface LocalWorkersConfig {
4496
4498
  interface LocalWorkerRoutingDefaults {
4497
4499
  worker: LocalWorkerDeclaration;
4498
4500
  routeLocalWorkers: true;
4501
+ routingLocalTransport: "openai_http" | "cli";
4502
+ routingLocalAdapter?: "codex_app_server" | "claude_cli";
4503
+ routingLocalCommand?: string;
4499
4504
  routingWorkerRole: string;
4500
4505
  routingLocalBaseUrl: string;
4501
4506
  routingLocalModel?: string;
4502
4507
  routingLocalPreferModel?: string;
4503
4508
  routingLocalProvider: string;
4509
+ routingLocalApiKeyEnv?: string;
4510
+ routingLocalApiKeyHeader?: "authorization" | "x-api-key";
4504
4511
  routingPreferredEndpoints: string[];
4505
4512
  routingAllowedEndpoints: string[];
4506
4513
  plannerRetainsReasoning: true;
@@ -4520,6 +4527,8 @@ interface LocalWorkerAddOptions {
4520
4527
  json?: boolean;
4521
4528
  transport?: "openai_http" | "cli";
4522
4529
  command?: string;
4530
+ apiKeyEnv?: string;
4531
+ apiKeyHeader?: "authorization" | "x-api-key";
4523
4532
  }
4524
4533
  interface LocalWorkerStatusOptions {
4525
4534
  json?: boolean;
@@ -4542,6 +4551,8 @@ interface LocalWorkerProbeOptions {
4542
4551
  writeScope?: string[];
4543
4552
  reasoning?: "low" | "medium" | "high";
4544
4553
  contextWindow?: number;
4554
+ apiKeyEnv?: string;
4555
+ apiKeyHeader?: "authorization" | "x-api-key";
4545
4556
  json?: boolean;
4546
4557
  }
4547
4558
  declare function workersLocalAddCommand(options: LocalWorkerAddOptions): void;
package/dist/index.js CHANGED
@@ -9755,7 +9755,9 @@ function normalizeWorkerProfileTransport(value) {
9755
9755
  baseUrl,
9756
9756
  ...stringValue(value.model) ? { model: stringValue(value.model) } : {},
9757
9757
  ...stringValue(value.preferModel) ? { preferModel: stringValue(value.preferModel) } : {},
9758
- ...stringValue(value.provider) ? { provider: stringValue(value.provider) } : {}
9758
+ ...stringValue(value.provider) ? { provider: stringValue(value.provider) } : {},
9759
+ ...safeEnvironmentVariableName(value.apiKeyEnv) ? { apiKeyEnv: safeEnvironmentVariableName(value.apiKeyEnv) } : {},
9760
+ ...value.apiKeyHeader === "authorization" || value.apiKeyHeader === "x-api-key" ? { apiKeyHeader: value.apiKeyHeader } : {}
9759
9761
  };
9760
9762
  }
9761
9763
  if (kind === "cli") {
@@ -9897,6 +9899,10 @@ function numberValue(value) {
9897
9899
  function booleanValue(value) {
9898
9900
  return value === true;
9899
9901
  }
9902
+ function safeEnvironmentVariableName(value) {
9903
+ const normalized = stringValue(value);
9904
+ return normalized && /^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized) ? normalized : void 0;
9905
+ }
9900
9906
  function stringValue(value) {
9901
9907
  if (value === null || value === void 0) {
9902
9908
  return void 0;
@@ -19627,6 +19633,9 @@ function workersLocalAddCommand(options) {
19627
19633
  printKeyValue("Role:", result.worker.workerRole);
19628
19634
  printKeyValue("Provider:", result.worker.provider);
19629
19635
  printKeyValue("Base URL:", result.worker.baseUrl);
19636
+ if (result.worker.apiKeyEnv) {
19637
+ printKeyValue("API key env:", result.worker.apiKeyEnv);
19638
+ }
19630
19639
  if (result.worker.model) {
19631
19640
  printKeyValue("Model:", result.worker.model);
19632
19641
  } else if (result.worker.preferModel) {
@@ -19718,7 +19727,9 @@ function workersLocalProbeCommand(options) {
19718
19727
  capabilities: normalizeStringList3(options.capabilities),
19719
19728
  writeScope: normalizeStringList3(options.writeScope),
19720
19729
  model,
19721
- preferModel
19730
+ preferModel,
19731
+ apiKeyEnv: options.apiKeyEnv,
19732
+ apiKeyHeader: options.apiKeyHeader
19722
19733
  });
19723
19734
  const catalogContextWindow = resolveContextWindowFromCatalog(catalog, model, preferModel);
19724
19735
  const contextWindow = normalizeContextWindow(
@@ -19737,7 +19748,9 @@ function workersLocalProbeCommand(options) {
19737
19748
  baseUrl,
19738
19749
  ...model ? { model } : {},
19739
19750
  ...preferModel ? { preferModel } : {},
19740
- provider
19751
+ provider,
19752
+ ...options.apiKeyEnv ? { apiKeyEnv: options.apiKeyEnv } : {},
19753
+ ...options.apiKeyHeader ? { apiKeyHeader: options.apiKeyHeader } : {}
19741
19754
  },
19742
19755
  capabilities: {
19743
19756
  roles: [role],
@@ -19891,7 +19904,26 @@ function resolveLocalWorkerRoutingDefaults(options = {}) {
19891
19904
  throw new Error(`Local worker ${requestedId} is not declared.`);
19892
19905
  }
19893
19906
  if (!worker.baseUrl || worker.baseUrl === "cli://command") {
19894
- return null;
19907
+ const command = worker.command?.toLowerCase() ?? "";
19908
+ const adapter = command.includes("codex") ? "codex_app_server" : command.includes("claude") ? "claude_cli" : void 0;
19909
+ if (!adapter) {
19910
+ throw new Error(
19911
+ `CLI worker ${worker.id} is not mapped to a supported native adapter; use a Codex or Claude command.`
19912
+ );
19913
+ }
19914
+ return {
19915
+ worker,
19916
+ routeLocalWorkers: true,
19917
+ routingLocalTransport: "cli",
19918
+ routingLocalBaseUrl: "cli://command",
19919
+ routingLocalAdapter: adapter,
19920
+ routingLocalCommand: worker.command,
19921
+ routingWorkerRole: worker.workerRole,
19922
+ routingLocalProvider: worker.provider,
19923
+ routingPreferredEndpoints: ["local"],
19924
+ routingAllowedEndpoints: ["local"],
19925
+ plannerRetainsReasoning: true
19926
+ };
19895
19927
  }
19896
19928
  if (!worker.model && !worker.preferModel) {
19897
19929
  const hasCatalogHint = true;
@@ -19900,11 +19932,14 @@ function resolveLocalWorkerRoutingDefaults(options = {}) {
19900
19932
  return {
19901
19933
  worker,
19902
19934
  routeLocalWorkers: true,
19935
+ routingLocalTransport: "openai_http",
19903
19936
  routingWorkerRole: worker.workerRole,
19904
19937
  routingLocalBaseUrl: worker.baseUrl,
19905
19938
  ...worker.model ? { routingLocalModel: worker.model } : {},
19906
19939
  ...worker.preferModel ? { routingLocalPreferModel: worker.preferModel } : {},
19907
19940
  routingLocalProvider: worker.provider,
19941
+ ...worker.apiKeyEnv ? { routingLocalApiKeyEnv: worker.apiKeyEnv } : {},
19942
+ ...worker.apiKeyHeader ? { routingLocalApiKeyHeader: worker.apiKeyHeader } : {},
19908
19943
  routingPreferredEndpoints: ["local"],
19909
19944
  routingAllowedEndpoints: ["local"],
19910
19945
  plannerRetainsReasoning: true
@@ -20012,7 +20047,9 @@ function normalizeWorkerProfileRecord(value) {
20012
20047
  transport: {
20013
20048
  ...transport,
20014
20049
  baseUrl,
20015
- provider: normalizeProvider(transport.provider ?? "lm-studio")
20050
+ provider: normalizeProvider(transport.provider ?? "lm-studio"),
20051
+ ...transport.apiKeyEnv ? { apiKeyEnv: transport.apiKeyEnv } : {},
20052
+ ...transport.apiKeyHeader ? { apiKeyHeader: transport.apiKeyHeader } : {}
20016
20053
  }
20017
20054
  };
20018
20055
  }
@@ -20047,7 +20084,9 @@ function toProfileFromLegacyWorker(worker) {
20047
20084
  baseUrl: worker.baseUrl,
20048
20085
  ...worker.model ? { model: worker.model } : {},
20049
20086
  ...worker.preferModel ? { preferModel: worker.preferModel } : {},
20050
- provider: worker.provider
20087
+ provider: worker.provider,
20088
+ ...worker.apiKeyEnv ? { apiKeyEnv: worker.apiKeyEnv } : {},
20089
+ ...worker.apiKeyHeader ? { apiKeyHeader: worker.apiKeyHeader } : {}
20051
20090
  },
20052
20091
  capabilities: {
20053
20092
  roles: [normalizeWorkerRole(worker.workerRole)],
@@ -20090,6 +20129,8 @@ function toLocalWorkerDeclaration(profile) {
20090
20129
  endpointType: "local",
20091
20130
  provider: normalizeProvider(profile.transport.provider ?? "lm-studio"),
20092
20131
  baseUrl: profile.transport.baseUrl,
20132
+ ...profile.transport.apiKeyEnv ? { apiKeyEnv: profile.transport.apiKeyEnv } : {},
20133
+ ...profile.transport.apiKeyHeader ? { apiKeyHeader: profile.transport.apiKeyHeader } : {},
20093
20134
  ...stringValue6(profile.transport.model) ? { model: stringValue6(profile.transport.model) } : {},
20094
20135
  ...stringValue6(profile.transport.preferModel) && !profile.transport.model ? { preferModel: stringValue6(profile.transport.preferModel) } : {},
20095
20136
  capabilities: normalizeWorkerCapabilities(profile.capabilities.roles),
@@ -20195,6 +20236,12 @@ function runOrchestratorLocalCatalog(options) {
20195
20236
  if (options.preferModel) {
20196
20237
  args.push("--prefer-model", options.preferModel);
20197
20238
  }
20239
+ if (options.apiKeyEnv) {
20240
+ args.push("--api-key-env", options.apiKeyEnv);
20241
+ }
20242
+ if (options.apiKeyHeader) {
20243
+ args.push("--api-key-header", options.apiKeyHeader);
20244
+ }
20198
20245
  try {
20199
20246
  const result = (0, import_node_child_process9.execFileSync)("snipara-orchestrator", args, {
20200
20247
  encoding: "utf8",
@@ -20330,12 +20377,15 @@ function normalizeWorkerTransport(options) {
20330
20377
  const baseUrl = normalizeBaseUrl(options.baseUrl ?? "http://127.0.0.1:1234");
20331
20378
  const model = stringValue6(options.model);
20332
20379
  const preferModel = stringValue6(options.preferModel);
20380
+ const apiKeyEnv = normalizeApiKeyEnvironmentName(options.apiKeyEnv);
20333
20381
  return {
20334
20382
  kind: "openai_http",
20335
20383
  baseUrl,
20336
20384
  ...model ? { model } : {},
20337
20385
  ...preferModel ? { preferModel } : {},
20338
- provider: normalizeProvider(stringValue6(options.provider) ?? "lm-studio")
20386
+ provider: normalizeProvider(stringValue6(options.provider) ?? "lm-studio"),
20387
+ ...apiKeyEnv ? { apiKeyEnv } : {},
20388
+ ...options.apiKeyHeader ? { apiKeyHeader: options.apiKeyHeader } : {}
20339
20389
  };
20340
20390
  }
20341
20391
  function normalizeProvider(value) {
@@ -20357,6 +20407,8 @@ function normalizeLegacyWorkerDeclaration(value) {
20357
20407
  endpointType: "local",
20358
20408
  provider: normalizeProvider(stringValue6(value.provider) ?? "lm-studio"),
20359
20409
  baseUrl,
20410
+ ...stringValue6(value.apiKeyEnv) ? { apiKeyEnv: stringValue6(value.apiKeyEnv) } : {},
20411
+ ...value.apiKeyHeader === "authorization" || value.apiKeyHeader === "x-api-key" ? { apiKeyHeader: value.apiKeyHeader } : {},
20360
20412
  ...stringValue6(value.model) ? { model: stringValue6(value.model) } : {},
20361
20413
  ...stringValue6(value.preferModel) ? { preferModel: stringValue6(value.preferModel) } : {},
20362
20414
  capabilities: normalizeStringList3(value.capabilities).length > 0 ? normalizeStringList3(value.capabilities) : defaultCapabilitiesForWorker(normalizeWorkerRole(value.workerRole)),
@@ -20434,6 +20486,16 @@ function stringValue6(value) {
20434
20486
  const stringified = String(value).trim();
20435
20487
  return stringified || void 0;
20436
20488
  }
20489
+ function normalizeApiKeyEnvironmentName(value) {
20490
+ const normalized = stringValue6(value);
20491
+ if (!normalized) {
20492
+ return void 0;
20493
+ }
20494
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized)) {
20495
+ throw new Error("API key env must be a shell-safe environment variable name.");
20496
+ }
20497
+ return normalized;
20498
+ }
20437
20499
  function isRecord10(value) {
20438
20500
  return typeof value === "object" && value !== null && !Array.isArray(value);
20439
20501
  }
@@ -25425,7 +25487,7 @@ function shouldResolveAdaptiveRoutingLocally(options, routing) {
25425
25487
  return preferred.includes("local") || allowed.length === 0 || allowed.includes("local");
25426
25488
  }
25427
25489
  function hasLocalRoutingRequest(options) {
25428
- return options.routeLocalWorkers === true || Boolean(options.routingLocalWorker) || Boolean(options.routingLocalBaseUrl) || Boolean(options.routingLocalModel) || Boolean(options.routingLocalPreferModel) || Boolean(options.routingLocalProvider) || normalizeRoutingEndpointTypes(options.routingPreferredEndpoints).includes("local") || normalizeRoutingEndpointTypes(options.routingAllowedEndpoints).includes("local");
25490
+ return options.routeLocalWorkers === true || Boolean(options.routingLocalWorker) || Boolean(options.routingLocalBaseUrl) || Boolean(options.routingLocalModel) || Boolean(options.routingLocalPreferModel) || Boolean(options.routingLocalProvider) || Boolean(options.routingLocalApiKeyEnv) || normalizeRoutingEndpointTypes(options.routingPreferredEndpoints).includes("local") || normalizeRoutingEndpointTypes(options.routingAllowedEndpoints).includes("local");
25429
25491
  }
25430
25492
  function applyLocalWorkerRoutingDefaults(options, defaults) {
25431
25493
  if (!defaults) {
@@ -25434,6 +25496,9 @@ function applyLocalWorkerRoutingDefaults(options, defaults) {
25434
25496
  return {
25435
25497
  ...options,
25436
25498
  routeLocalWorkers: options.routeLocalWorkers ?? defaults.routeLocalWorkers,
25499
+ routingLocalTransport: options.routingLocalTransport ?? defaults.routingLocalTransport,
25500
+ routingLocalAdapter: options.routingLocalAdapter ?? defaults.routingLocalAdapter,
25501
+ routingLocalCommand: options.routingLocalCommand ?? defaults.routingLocalCommand,
25437
25502
  routingWorkerRole: options.routingWorkerRole ?? defaults.routingWorkerRole,
25438
25503
  routingPreferredEndpoints: options.routingPreferredEndpoints && options.routingPreferredEndpoints.length > 0 ? options.routingPreferredEndpoints : defaults.routingPreferredEndpoints,
25439
25504
  routingAllowedEndpoints: options.routingAllowedEndpoints && options.routingAllowedEndpoints.length > 0 ? options.routingAllowedEndpoints : defaults.routingAllowedEndpoints,
@@ -25441,6 +25506,8 @@ function applyLocalWorkerRoutingDefaults(options, defaults) {
25441
25506
  routingLocalModel: options.routingLocalModel ?? defaults.routingLocalModel,
25442
25507
  routingLocalPreferModel: options.routingLocalPreferModel ?? defaults.routingLocalPreferModel,
25443
25508
  routingLocalProvider: options.routingLocalProvider ?? defaults.routingLocalProvider,
25509
+ routingLocalApiKeyEnv: options.routingLocalApiKeyEnv ?? defaults.routingLocalApiKeyEnv,
25510
+ routingLocalApiKeyHeader: options.routingLocalApiKeyHeader ?? defaults.routingLocalApiKeyHeader,
25444
25511
  plannerRetainsReasoning: options.plannerRetainsReasoning ?? defaults.plannerRetainsReasoning
25445
25512
  };
25446
25513
  }
@@ -25455,6 +25522,67 @@ function runOrchestratorJsonCommand(args, cwd = process.cwd()) {
25455
25522
  }
25456
25523
  function enrichAdaptiveRoutingWithLocalOrchestrator(routing, options, cwd = process.cwd()) {
25457
25524
  try {
25525
+ if (options.routingLocalTransport === "cli") {
25526
+ const candidateId = options.routingLocalAdapter ?? options.routingLocalCommand ?? "declared-cli";
25527
+ const candidate = {
25528
+ candidateId,
25529
+ workerClass: routing.requirements.workerRole,
25530
+ catalogSource: "declared_cli_worker",
25531
+ endpointType: "local",
25532
+ workerRoles: [routing.requirements.workerRole],
25533
+ capabilities: routing.requirements.capabilities ?? [],
25534
+ writeScope: routing.requirements.writeScope ?? [],
25535
+ reasoning: routing.requirements.reasoning,
25536
+ speed: "normal",
25537
+ cost: "balanced",
25538
+ contextBudget: routing.requirements.contextBudget,
25539
+ qualityScore: null,
25540
+ isAvailable: true,
25541
+ requiresApiKey: false,
25542
+ metadata: {
25543
+ adapter: options.routingLocalAdapter,
25544
+ command: options.routingLocalCommand
25545
+ }
25546
+ };
25547
+ const resolution2 = normalizeAdaptiveRoutingResolution(
25548
+ runOrchestratorJsonCommand(
25549
+ [
25550
+ "route",
25551
+ "--dry-run",
25552
+ "--json",
25553
+ "--work-profile-json",
25554
+ JSON.stringify(routing.workProfile),
25555
+ "--requirements-json",
25556
+ JSON.stringify(routing.requirements),
25557
+ "--candidate-json",
25558
+ JSON.stringify(candidate)
25559
+ ],
25560
+ cwd
25561
+ )
25562
+ );
25563
+ return {
25564
+ ...routing,
25565
+ gateway: {
25566
+ source: "local_orchestrator",
25567
+ success: true,
25568
+ resolutionStatus: resolution2?.status ?? "resolved",
25569
+ candidateCount: 1,
25570
+ fallback: "main_agent",
25571
+ warnings: []
25572
+ },
25573
+ runtimeCatalog: {
25574
+ source: "declared_cli_worker",
25575
+ candidates: [candidate],
25576
+ workerEndpoints: {
25577
+ [candidateId]: {
25578
+ adapter: options.routingLocalAdapter,
25579
+ command: options.routingLocalCommand
25580
+ }
25581
+ }
25582
+ },
25583
+ resolution: resolution2
25584
+ };
25585
+ }
25458
25586
  const catalogArgs = [
25459
25587
  "local-model-catalog",
25460
25588
  "--json",
@@ -25477,6 +25605,12 @@ function enrichAdaptiveRoutingWithLocalOrchestrator(routing, options, cwd = proc
25477
25605
  if (options.routingLocalProvider) {
25478
25606
  catalogArgs.push("--provider", options.routingLocalProvider);
25479
25607
  }
25608
+ if (options.routingLocalApiKeyEnv) {
25609
+ catalogArgs.push("--api-key-env", options.routingLocalApiKeyEnv);
25610
+ }
25611
+ if (options.routingLocalApiKeyHeader) {
25612
+ catalogArgs.push("--api-key-header", options.routingLocalApiKeyHeader);
25613
+ }
25480
25614
  const runtimeCatalog = normalizeAdaptiveRoutingCatalog(
25481
25615
  runOrchestratorJsonCommand(catalogArgs, cwd)
25482
25616
  );
@@ -27367,7 +27501,7 @@ async function workflowRunCommand(options) {
27367
27501
  }
27368
27502
  function shouldBuildAdaptiveRouting(options) {
27369
27503
  return Boolean(
27370
- options.adaptiveRoutingDryRun || options.routeLocalWorkers || options.routingLocalWorker || options.routingWorkerRole || options.routingPreferredEndpoints?.length || options.routingAllowedEndpoints?.length || options.routingLocalBaseUrl || options.routingLocalModel || options.routingLocalPreferModel || options.routingLocalProvider || options.plannerRetainsReasoning
27504
+ options.adaptiveRoutingDryRun || options.routeLocalWorkers || options.routingLocalWorker || options.routingWorkerRole || options.routingPreferredEndpoints?.length || options.routingAllowedEndpoints?.length || options.routingLocalBaseUrl || options.routingLocalModel || options.routingLocalPreferModel || options.routingLocalProvider || options.routingLocalApiKeyEnv || options.plannerRetainsReasoning
27371
27505
  );
27372
27506
  }
27373
27507
  function buildWorkflowAdaptiveRouting(options, policy = null, intentWarnings = []) {
@@ -38817,6 +38951,12 @@ workflow.command("run").description("Run a LITE/STANDARD/FULL/orchestrate workfl
38817
38951
  "--routing-local-prefer-model <text>",
38818
38952
  "Prefer a local /v1/models entry containing this text during worker routing"
38819
38953
  ).option("--routing-local-provider <provider>", "Provider label for local worker routing").option(
38954
+ "--routing-local-api-key-env <env>",
38955
+ "Environment variable containing the local/remote provider API key"
38956
+ ).option(
38957
+ "--routing-local-api-key-header <header>",
38958
+ "Provider API key header: authorization or x-api-key"
38959
+ ).option(
38820
38960
  "--planner-retains-reasoning",
38821
38961
  "Mark the main planner as retaining deep reasoning while the worker executes scoped work"
38822
38962
  ).option("--write-plan-file <file>", "Write the generated FULL-mode plan as workflow JSON").option(
@@ -38844,6 +38984,8 @@ workflow.command("run").description("Run a LITE/STANDARD/FULL/orchestrate workfl
38844
38984
  routingLocalModel: options.routingLocalModel,
38845
38985
  routingLocalPreferModel: options.routingLocalPreferModel,
38846
38986
  routingLocalProvider: options.routingLocalProvider,
38987
+ routingLocalApiKeyEnv: options.routingLocalApiKeyEnv,
38988
+ routingLocalApiKeyHeader: options.routingLocalApiKeyHeader,
38847
38989
  plannerRetainsReasoning: options.plannerRetainsReasoning ? true : void 0,
38848
38990
  writePlanFile: options.writePlanFile,
38849
38991
  startWorkflowFromPlan: Boolean(options.startWorkflowFromPlan),
@@ -38944,7 +39086,11 @@ localWorkers.command("add").description("Declare a local worker and enable local
38944
39086
  "--role <role>",
38945
39087
  "Worker role, for example coding, documentation, tests, or review",
38946
39088
  "coding"
38947
- ).option("--provider <provider>", "Provider label", "lm-studio").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--model <id>", "Exact model id exposed by the local runtime").option(
39089
+ ).option("--provider <provider>", "Provider label", "lm-studio").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--api-key-env <env>", "Environment variable containing the provider API key").option(
39090
+ "--api-key-header <header>",
39091
+ "Provider API key header: authorization or x-api-key",
39092
+ "authorization"
39093
+ ).option("--model <id>", "Exact model id exposed by the local runtime").option(
38948
39094
  "--prefer-model <text>",
38949
39095
  "Fallback model id substring to prefer when no exact model is set"
38950
39096
  ).option("--transport <openai_http|cli>", "Worker transport: openai_http (default) or cli").option("--command <command>", "CLI command to execute when transport is cli").option("--capability <capability>", "Worker capability; repeatable", collectOption, []).option(
@@ -38971,6 +39117,8 @@ localWorkers.command("add").description("Declare a local worker and enable local
38971
39117
  preferModel: options.preferModel,
38972
39118
  transport: options.transport,
38973
39119
  command: options.command,
39120
+ apiKeyEnv: options.apiKeyEnv,
39121
+ apiKeyHeader: options.apiKeyHeader,
38974
39122
  capabilities: options.capability,
38975
39123
  reasoning: options.reasoning ? options.reasoning.toLowerCase() : void 0,
38976
39124
  contextWindow: options.contextWindow,
@@ -38988,7 +39136,11 @@ localWorkers.command("list").description("List declared local workers").option("
38988
39136
  localWorkers.command("remove").description("Remove a declared local worker").argument("id", "Local worker id").option("--json", "Print raw JSON").action((id, options) => {
38989
39137
  workersLocalRemoveCommand({ id, json: options.json });
38990
39138
  });
38991
- localWorkers.command("probe").description("Probe an OpenAI-compatible endpoint and draft a worker suggestion").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--provider <provider>", "Provider label", "lm-studio").option("--model <id>", "Exact model id exposed by the local runtime").option("--prefer-model <text>", "Fallback local model substring when exact model is unset").option(
39139
+ localWorkers.command("probe").description("Probe an OpenAI-compatible endpoint and draft a worker suggestion").option("--base-url <url>", "OpenAI-compatible local runtime base URL", "http://127.0.0.1:1234").option("--api-key-env <env>", "Environment variable containing the provider API key").option(
39140
+ "--api-key-header <header>",
39141
+ "Provider API key header: authorization or x-api-key",
39142
+ "authorization"
39143
+ ).option("--provider <provider>", "Provider label", "lm-studio").option("--model <id>", "Exact model id exposed by the local runtime").option("--prefer-model <text>", "Fallback local model substring when exact model is unset").option(
38992
39144
  "--role <role>",
38993
39145
  "Worker role; for example coding, documentation, tests, or review",
38994
39146
  "coding"
@@ -39019,6 +39171,8 @@ localWorkers.command("probe").description("Probe an OpenAI-compatible endpoint a
39019
39171
  reasoning: options.reasoning ? options.reasoning.toLowerCase() : void 0,
39020
39172
  contextWindow: options.contextWindow,
39021
39173
  writeScope: options.writeScope,
39174
+ apiKeyEnv: options.apiKeyEnv,
39175
+ apiKeyHeader: options.apiKeyHeader,
39022
39176
  json: options.json
39023
39177
  });
39024
39178
  });
@@ -464,7 +464,8 @@ snipara-companion workers local add \
464
464
  --role coding \
465
465
  --provider lm-studio \
466
466
  --base-url http://127.0.0.1:1234 \
467
- --model openai/gpt-oss-20b
467
+ --model openai/gpt-oss-20b \
468
+ --api-key-env LM_STUDIO_API_KEY
468
469
  ```
469
470
 
470
471
  The declaration is written under `.snipara/workers/<worker-id>.json`; Companion
@@ -499,7 +500,11 @@ Do not store secrets in worker profiles. Local endpoints such as
499
500
  `http://127.0.0.1:1234` are safe to commit when they contain no credentials, but
500
501
  cloud CLI transports or authenticated endpoints must reference secrets through
501
502
  environment variables rather than embedding API keys, bearer tokens, passwords,
502
- or private URLs directly in `.snipara/workers/*.json`.
503
+ or private URLs directly in `.snipara/workers/*.json`. `--api-key-env` stores
504
+ only the variable name; pair it with `--api-key-header authorization` (default)
505
+ or `--api-key-header x-api-key`. Native Codex and Claude profiles use their
506
+ host-managed credentials. A generic `cli://command` profile fails closed unless
507
+ the command maps to a supported Codex or Claude adapter.
503
508
 
504
509
  Use `workers local probe` to query a local endpoint and preview a declaration
505
510
  proposal before committing it:
@@ -559,6 +564,24 @@ snipara-orchestrator local-model-catalog \
559
564
  --json > .snipara/local-devstral-runtime-catalog.json
560
565
  ```
561
566
 
567
+ For a remote OpenAI-compatible runtime, use an explicit allowlist and an
568
+ environment-backed key when running the native host:
569
+
570
+ ```bash
571
+ snipara-orchestrator host run \
572
+ --adapter openai_compatible \
573
+ --base-url https://provider.example.com --allow-remote \
574
+ --api-key-env PROVIDER_API_KEY --api-key-header authorization \
575
+ --model provider/coder --task "Bounded task" --workspace . \
576
+ --write-scope docs/** --proof "git diff --check" --execute
577
+ ```
578
+
579
+ Native host proof commands run automatically after dispatch. Use
580
+ `--no-run-proof` only when a reviewer will validate the receipt; the state then
581
+ remains `verification_required`. Use `--require-approval` with both an approval
582
+ receipt id and `--approval-receipt-file <json>` for unattended execution; an id
583
+ alone never bypasses the approval gate.
584
+
562
585
  The local catalog records the OpenAI-compatible routes exposed by LM Studio:
563
586
  `GET /v1/models`, `POST /v1/responses`, `POST /v1/chat/completions`,
564
587
  `POST /v1/completions`, and `POST /v1/embeddings`. `--prefer-model devstral`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snipara-companion",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "Local-first CLI that asks your repo what breaks before an AI coding agent edits it.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {