c8ctl-plugin-nano 1.35.0 → 1.35.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.
Files changed (2) hide show
  1. package/c8ctl-plugin.js +59 -5
  2. package/package.json +8 -8
package/c8ctl-plugin.js CHANGED
@@ -2549,14 +2549,18 @@ function resourceContentUrl(baseUrl, resourceKey) {
2549
2549
  // is surfaced as a ProvisionError so the caller fails the job with a clear
2550
2550
  // provisioning message (never runs an agent with a silently-empty prompt).
2551
2551
  async function fetchLinkedResourceContent(resourceKey, opts = {}) {
2552
- const { baseUrl, token, fetchImpl = fetch, timeoutMs = 15_000 } = opts;
2552
+ const { baseUrl, token, authHeaders, fetchImpl = fetch, timeoutMs = 15_000 } = opts;
2553
2553
  const url = resourceContentUrl(baseUrl, resourceKey);
2554
2554
  const controller = new AbortController();
2555
2555
  const timer = timeoutMs > 0 ? setTimeout(() => controller.abort(), timeoutMs) : null;
2556
2556
  let res;
2557
2557
  try {
2558
2558
  const headers = { Accept: 'application/octet-stream' };
2559
- if (token) headers.Authorization = `Bearer ${token}`;
2559
+ // A ready-made auth-headers map (from the activating SDK client's
2560
+ // getAuthHeaders(), covering OAuth/basic/none) wins over a bare token. An
2561
+ // empty map means unauthenticated — deliberately no Authorization header.
2562
+ if (authHeaders && typeof authHeaders === 'object') Object.assign(headers, authHeaders);
2563
+ else if (token) headers.Authorization = `Bearer ${token}`;
2560
2564
  res = await fetchImpl(url, { method: 'GET', headers, signal: controller.signal });
2561
2565
  } catch (err) {
2562
2566
  throw new ProvisionError(`prompt resource ${resourceKey} fetch failed: ${err && err.message ? err.message : String(err)}`);
@@ -2581,14 +2585,57 @@ async function fetchLinkedResourceContent(resourceKey, opts = {}) {
2581
2585
  // prompt resource cannot be fetched — a provisioning failure, not a silent
2582
2586
  // empty-prompt run.
2583
2587
  async function resolveLinkedPrompt(customHeaders, opts = {}) {
2584
- const { linkName = DEFAULT_PROMPT_LINK_NAME, baseUrl, token, fetchImpl, timeoutMs } = opts;
2588
+ const { linkName = DEFAULT_PROMPT_LINK_NAME, baseUrl, token, authHeaders, fetchImpl, timeoutMs } = opts;
2585
2589
  const entry = pickLinkedResource(parseLinkedResources(customHeaders), linkName);
2586
2590
  if (!entry) return null;
2587
2591
  const resourceKey = entry.resourceKey;
2588
- const basePrompt = await fetchLinkedResourceContent(resourceKey, { baseUrl, token, fetchImpl, timeoutMs });
2592
+ const basePrompt = await fetchLinkedResourceContent(resourceKey, { baseUrl, token, authHeaders, fetchImpl, timeoutMs });
2589
2593
  return { basePrompt, resourceKey, resourceType: entry.resourceType ?? null, linkName: String(linkName) };
2590
2594
  }
2591
2595
 
2596
+ // Normalize a client-configured REST address for use as a linked-resource fetch
2597
+ // base. The SDK's restAddress may or may not already include the `/v2` API
2598
+ // prefix (CAMUNDA_REST_ADDRESS accepts either); resourceContentUrl re-adds it,
2599
+ // so strip a trailing `/v2` (and any trailing slashes) to avoid a double `/v2`.
2600
+ function normalizeRestBase(addr) {
2601
+ return String(addr || '').replace(/\/+$/, '').replace(/\/v2$/i, '');
2602
+ }
2603
+
2604
+ // Derive the linked-resource fetch base URL + auth headers from the SAME SDK
2605
+ // client that activated the job. A linked-resource `resourceKey` is broker-local,
2606
+ // so prompt content must be fetched from the broker this worker is connected to
2607
+ // — never a localhost default (the cause of "prompt resource N fetch failed").
2608
+ // An explicit NANO_REST_URL / NANO_REST_TOKEN override still wins as an operator
2609
+ // escape hatch. Both getConfig()/getAuthHeaders() are guarded so an older or
2610
+ // atypical client runtime degrades to the override/legacy path rather than throw.
2611
+ //
2612
+ // TODO: once c8ctl bumps @camunda8/orchestration-cluster-api to v10 (10.0.0-alpha
2613
+ // exposes the typed camunda.getResourceContentBinary({resourceKey}) → Blob), drop
2614
+ // this raw /content/binary fetch and call that method directly. The pinned ^9.1.0
2615
+ // SDK only exposes the deprecated getResourceContent, which 406s for generic
2616
+ // (Markdown) prompt resources — see camunda/orchestration-cluster-api-js.
2617
+ async function resolveLinkedPromptSource(camunda, env = process.env) {
2618
+ let baseUrl = env.NANO_REST_URL || '';
2619
+ if (!baseUrl && camunda && typeof camunda.getConfig === 'function') {
2620
+ try {
2621
+ baseUrl = normalizeRestBase(camunda.getConfig().restAddress);
2622
+ } catch {
2623
+ // ignore — fall back to the legacy resolveBrokerRestConfig base below
2624
+ }
2625
+ }
2626
+ let authHeaders;
2627
+ if (env.NANO_REST_TOKEN) {
2628
+ authHeaders = { Authorization: `Bearer ${env.NANO_REST_TOKEN}` };
2629
+ } else if (camunda && typeof camunda.getAuthHeaders === 'function') {
2630
+ try {
2631
+ authHeaders = await camunda.getAuthHeaders();
2632
+ } catch {
2633
+ authHeaders = undefined;
2634
+ }
2635
+ }
2636
+ return { baseUrl, authHeaders };
2637
+ }
2638
+
2592
2639
  // Secrets are referenced by NAME, never by value, in the model. A resolver maps
2593
2640
  // a name → value at run time. The wrapper injects them into the child ENV, so
2594
2641
  // values never appear in argv or `docker inspect`.
@@ -4533,8 +4580,13 @@ async function workAgent(req, flags) {
4533
4580
  let promptResourceKey = null;
4534
4581
  let basePromptOverride;
4535
4582
  try {
4583
+ // Fetch the prompt from the broker the SDK client is connected to,
4584
+ // deriving base URL + auth from that client (not restConfig, whose base
4585
+ // defaults to localhost) — the resourceKey is broker-local.
4586
+ const promptSource = await resolveLinkedPromptSource(camunda);
4536
4587
  const linked = await resolveLinkedPrompt(job.customHeaders ?? {}, {
4537
- baseUrl: restConfig.baseUrl,
4588
+ baseUrl: promptSource.baseUrl || restConfig.baseUrl,
4589
+ authHeaders: promptSource.authHeaders,
4538
4590
  token: restConfig.token,
4539
4591
  });
4540
4592
  if (linked) {
@@ -7865,6 +7917,8 @@ export {
7865
7917
  resourceContentUrl,
7866
7918
  fetchLinkedResourceContent,
7867
7919
  resolveLinkedPrompt,
7920
+ resolveLinkedPromptSource,
7921
+ normalizeRestBase,
7868
7922
  coerceBool,
7869
7923
  coerceInt,
7870
7924
  deepMerge,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c8ctl-plugin-nano",
3
- "version": "1.35.0",
3
+ "version": "1.35.1",
4
4
  "type": "module",
5
5
  "description": "c8ctl plugin to start, inspect, and stop a local Nano BPM (nanobpmn) cluster",
6
6
  "main": "c8ctl-plugin.js",
@@ -57,12 +57,12 @@
57
57
  },
58
58
  "optionalDependencies": {
59
59
  "node-pty": "^1.0.0",
60
- "@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.35.0",
61
- "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.35.0",
62
- "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.35.0",
63
- "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.35.0",
64
- "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.35.0",
65
- "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.35.0",
66
- "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.35.0"
60
+ "@nanobpm/c8ctl-plugin-nano-darwin-arm64": "1.35.1",
61
+ "@nanobpm/c8ctl-plugin-nano-darwin-x64": "1.35.1",
62
+ "@nanobpm/c8ctl-plugin-nano-linux-x64": "1.35.1",
63
+ "@nanobpm/c8ctl-plugin-nano-linux-arm64": "1.35.1",
64
+ "@nanobpm/c8ctl-plugin-nano-linux-armv7": "1.35.1",
65
+ "@nanobpm/c8ctl-plugin-nano-linux-armv6": "1.35.1",
66
+ "@nanobpm/c8ctl-plugin-nano-win32-x64": "1.35.1"
67
67
  }
68
68
  }