@voiden/runner 2.3.0-beta.13 → 2.3.0-beta.15

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
@@ -3,6 +3,39 @@
3
3
  All notable changes to `@voiden/runner` are documented here. This package is
4
4
  versioned and released independently of the Voiden desktop app.
5
5
 
6
+ ## v2.3.0-beta.15 - 2026-09-07
7
+
8
+ ### Added
9
+ - Two new fixed MCP tools, alongside the existing `list_void_files`/`list_requests`/`run_request`/
10
+ `write_result` — `list_environments` and `select_environment` (`mcpServing.ts`, exposed by
11
+ `voiden-runner mcp serve`/`mcp install`; `@voiden/mcp` is unaffected, see below). `list_environments`
12
+ discovers every env profile in the project (`.voiden/env-{profile}-public/private.yaml`, or the
13
+ `default` profile's unsuffixed files) and, for each, either its YAML environment hierarchy as
14
+ dotted paths (e.g. `"staging"`, `"staging.eu"` for a nested child) or — if that profile has no YAML
15
+ environments — the plain `.env*` file(s) it falls back to. `select_environment` resolves a chosen
16
+ profile (+ optional dotted environment path within it) and makes its variables the default base
17
+ layer for every `run_request` call for the rest of the session; an explicit `envFile`/`envVars` on
18
+ a given `run_request` call still overrides it for that call only. Returns variable *keys* only, not
19
+ values — `env-*-private.yaml` can hold real secrets, and echoing them into a tool-call response
20
+ that likely ends up in the calling agent's own transcript is an avoidable exposure `run_request`'s
21
+ existing `envVars` param doesn't already create.
22
+ - `envFile.ts`: exported `parseYamlEnv`, added and exported `listYamlEnvironmentNames` (full nested
23
+ environment tree as dotted paths) and `findEnvironmentByPath` (resolves a tree by exact dotted
24
+ path rather than searching for a bare key anywhere — needed because two different parents can each
25
+ have a same-named child, e.g. `staging.eu` vs `prod.eu`; only a full-path walk tells them apart).
26
+ - New `envProfiles.ts` — headless profile discovery/resolution (no Electron dependency), a
27
+ deliberately simplified port of `apps/electron/src/main/env.ts`'s profile logic, same precedent as
28
+ `plugins/voiden-mcp-tool/src/lib/toolCapability.ts`'s `loadProjectEnvironmentVars`.
29
+ - Note: `@voiden/mcp` does not call `registerFixedTools` at all today (a pre-existing gap, not
30
+ changed by this release) — none of the 6 fixed tools, old or new, currently reach connections made
31
+ through that package, only `voiden-runner mcp serve`/`mcp install`.
32
+
33
+ ## v2.3.0-beta.14 - 2026-08-26
34
+
35
+ ### Fixed
36
+ - `plugin list`'s core-plugin status badge now checks whether a `runner.js` actually exists (bundled in this package install, or cached in `~/.voiden/extensions/<id>/runner.js`), not just whether the plugin is enabled in `~/.voiden/plugins.json`. Previously it could report "✓ enabled" for a plugin with no usable runner anywhere — exactly the state `loadEnabledPlugins()` treats as unavailable at request-run time (`activePlugins: []`, "not installed" errors from `run_request`) — so the two disagreed with no way to tell why. Now shows "not installed [no runner]" in that case, matching what actually gets loaded.
37
+ - A `linkedBlock`/`linkedFile` pointer node could get resolved as if it *were* the target content instead of the real block it points to, when its own `uid` collided with the `blockUid` being searched for (e.g. a hand-authored file that copied a block's `uid` onto the wrapper instead of generating a fresh one, most likely when linking a block into the same file it lives in). `resolveLinkedBlocks`/`resolveLinkedFiles` now exclude `linkedBlock`/`linkedFile` nodes from ever matching as resolved content — a colliding `uid` now resolves to the real block if one still carries it, or is left unresolved, instead of silently substituting the pointer itself. `@voiden/executors` bumped to `0.1.9`.
38
+
6
39
  ## v2.3.0-beta.13 - 2026-08-21
7
40
 
8
41
  ### Fixed
@@ -1 +1 @@
1
- "use strict";var y=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var A=(r,e)=>()=>(r&&(e=r(r=0)),e);var w=(r,e)=>{for(var t in e)y(r,t,{get:e[t],enumerable:!0})},D=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of R(e))!T.call(r,s)&&s!==t&&y(r,s,{get:()=>e[s],enumerable:!(n=C(e,s))||n.enumerable});return r};var E=r=>D(y({},"__esModule",{value:!0}),r);function F(r,e){let t=r.trim();if(t==="status"||t==="statusCode")return e.response.status;if(t==="statusText")return e.response.statusText;if(t==="responseTime"||t==="duration")return e.response.timing?.duration||0;if(t.startsWith("header.")||t.startsWith("headers.")){let n=t.split("."),s=n[1];if(s.toLowerCase()==="set-cookie"){let i=(0,x.parseCookies)(e.response.headers);if(n.length===2)return i;let c=n.slice(2).join(".");return l(i,c)}return e.response.headers.find(i=>i.key.toLowerCase()===s.toLowerCase())?.value}if(t==="body"||t==="response.body")return e.response.body;if(t.startsWith("body.")||t.startsWith("response.body.")){let n=t.replace(/^(response\.)?body\./,"");return l(e.response.body,n)}if(t.startsWith("response.")){let n=t.replace(/^response\./,"");return l(e.response,n)}return l(e.response.body,t)}function l(r,e){if(!r)return;let t=e.split(/\.|\[/).map(s=>s.replace(/\]$/,"")),n=r;for(let s of t){if(n==null)return;/^\d+$/.test(s)?n=n[parseInt(s,10)]:n=n[s]}return n}function O(r,e){try{if(r.enabled===!1)return{assertion:r,passed:!0,actualValue:null,error:"Skipped (disabled)"};let t=j(r.field),n=t.field,s=t.operator||r.operator||"equals",o=r.expectedValue,i=F(n,e),c=W(s,i,o);return{assertion:r,passed:c,actualValue:i}}catch(t){return{assertion:r,passed:!1,actualValue:null,error:t.message||String(t)}}}function j(r){let e=["equals","eq","==","===","not-equals","ne","!=","!==","contains","includes","not-contains","not-includes","starts-with","startswith","ends-with","endswith","matches","regex","exists","is-defined","not-exists","is-null","is-undefined","greater-than","gt",">","less-than","lt","<","greater-equal","gte",">=","less-equal","lte","<=","is-empty","empty","not-empty","is-truthy","truthy","is-falsy","falsy","type-is","typeof"],t=r.trim().split(/\s+/);if(t.length>=2){let n=t[t.length-1].toLowerCase();if(e.includes(n))return{field:t.slice(0,-1).join(" "),operator:n}}return{field:r.trim(),operator:null}}function W(r,e,t){switch(r.toLowerCase().trim()){case"equals":case"eq":case"==":case"===":return e==null?!1:String(e)===t;case"not-equals":case"ne":case"!=":case"!==":return e==null?!1:String(e)!==t;case"contains":case"includes":return e==null?!1:String(e).includes(t);case"not-contains":case"not-includes":return e==null?!1:!String(e).includes(t);case"starts-with":case"startswith":return e==null?!1:String(e).startsWith(t);case"ends-with":case"endswith":return e==null?!1:String(e).endsWith(t);case"matches":case"regex":if(e==null)return!1;try{return new RegExp(t).test(String(e))}catch{return!1}case"exists":case"is-defined":return e!=null;case"not-exists":case"is-null":case"is-undefined":return e==null;case"greater-than":case"gt":case">":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s>o}case"less-than":case"lt":case"<":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s<o}case"greater-equal":case"gte":case">=":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s>=o}case"less-equal":case"lte":case"<=":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s<=o}case"is-empty":case"empty":return e==null?!1:Array.isArray(e)?e.length===0:typeof e=="string"?e==="":typeof e=="object"?Object.keys(e).length===0:!1;case"not-empty":return e==null?!1:Array.isArray(e)?e.length>0:typeof e=="string"?e!=="":typeof e=="object"?Object.keys(e).length>0:!0;case"is-truthy":case"truthy":return!!e;case"is-falsy":case"falsy":return!e;case"type-is":case"typeof":return typeof e===t.toLowerCase();default:throw new Error(`Unknown operator: ${r}`)}}function S(r,e){return r.map(t=>O(t,e))}var x,N=A(()=>{"use strict";x=require("@voiden/sdk/shared")});var q={};w(q,{postProcessAssertionsHook:()=>$});function v(r){let e=[];if(!r||!r.content)return e;function t(n){if(n.type==="assertions-table"){let s=L(n);e.push(...s)}n.content&&Array.isArray(n.content)&&n.content.forEach(s=>t(s))}return t(r),e}function L(r){let e=[];try{if(r.attrs&&Array.isArray(r.attrs.rows)){let s=r.attrs.rows;for(let o of s)!o.description&&!o.field&&!o.operator&&!o.expectedValue||o.enabled!==!1&&e.push({description:o.description||"",field:o.field||"",operator:o.operator||"equals",expectedValue:o.expectedValue||"",enabled:!0});return e}let t=Array.isArray(r.content)?r.content.find(s=>s.type==="table"||s.type==="assertions-table"):null;if(!t)return e;let n=t.rows||t.content||[];for(let s=0;s<n.length;s++){let o=n[s];if(Array.isArray(o.row)){if(o.attrs?.disabled===!0)continue;let[a,g,h,b]=o.row;if(!a&&!g&&!h&&!b)continue;e.push({description:String(a||"").trim(),field:String(g||"").trim(),operator:String(h||"equals").trim()||"equals",expectedValue:String(b||"").trim(),enabled:!0});continue}if(o.type!=="tableRow"||o.attrs&&o.attrs.disabled===!0)continue;let i=o.content||[];if(i.length<4)continue;let c=d(i[0]).trim(),p=d(i[1]).trim(),f=d(i[2]).trim(),u=d(i[3]).trim();!c&&!p&&!f&&!u||!(!(i.length>=5)||P(i[4]))||e.push({description:c,field:p,operator:f||"equals",expectedValue:u,enabled:!0})}}catch(t){console.error("Error extracting assertions from table:",t)}return e}function d(r){return r?r.type==="text"?r.text||"":r.content&&Array.isArray(r.content)?r.content.map(e=>d(e)).join(""):"":""}function P(r){if(!r||!r.content)return!0;let e=r.content.find(t=>t.type==="checkbox");return e&&e.attrs?e.attrs.checked!==!1:!0}async function $(r){let{requestState:e,responseState:t,metadata:n}=r;try{let s=e?.metadata?.editorDocument||n?.requestDocument||n?.editorDocument;if(!s)return;let o=v(s);if(o.length===0)return;let i=(typeof window<"u"?window:void 0)?.electron?.env?.replaceVariables;if(i)for(let a of o)a.expectedValue&&a.expectedValue.includes("{{")&&(a.expectedValue=await i(a.expectedValue)),a.field&&a.field.includes("{{")&&(a.field=await i(a.field));let c=t.body;if(typeof c=="string"&&c.trim())try{c=JSON.parse(c)}catch{}let p={response:{status:t.status,statusText:t.statusText,headers:t.headers||[],body:c,contentType:t.contentType,timing:t.timing}},f=S(o,p),u=f.filter(a=>a.passed).length,m=f.filter(a=>!a.passed).length;t.metadata||(t.metadata={}),t.metadata.assertionResults={results:f,totalAssertions:o.length,passedAssertions:u,failedAssertions:m}}catch(s){console.error("[Simple Assertions] Error in post-process assertions hook:",s)}}var k=A(()=>{"use strict";N()});var J={};w(J,{default:()=>z});module.exports=E(J);var H=r=>({onload(){r.pipeline.registerHook("pre-processing",async e=>{e.editor&&(e.requestState.metadata||(e.requestState.metadata={}),e.requestState.metadata.editorDocument=e.editor.getJSON())},5),r.pipeline.registerHook("post-processing",async e=>{let{postProcessAssertionsHook:t}=await Promise.resolve().then(()=>(k(),q));await t(e)},15),r.pipeline.registerHook("post-processing",async e=>{let t=e.responseState?.metadata?.assertionResults;if(t?.results?.length){e.responseState.metadata||(e.responseState.metadata={}),Array.isArray(e.responseState.metadata.reportEntries)||(e.responseState.metadata.reportEntries=[]);for(let n of t.results){let s=n.assertion?.description?.trim()||`${n.assertion?.field??""} ${n.assertion?.operator??""} ${n.assertion?.expectedValue??""}`.trim();e.responseState.metadata.reportEntries.push({type:"assertion",message:s||"Assertion",passed:n.passed,actual:n.actualValue,expected:n.assertion?.expectedValue,operator:n.assertion?.operator})}}},50)}}),z=H;
1
+ "use strict";var y=Object.defineProperty;var C=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var A=(r,e)=>()=>(r&&(e=r(r=0)),e);var w=(r,e)=>{for(var t in e)y(r,t,{get:e[t],enumerable:!0})},D=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of R(e))!T.call(r,s)&&s!==t&&y(r,s,{get:()=>e[s],enumerable:!(n=C(e,s))||n.enumerable});return r};var W=r=>D(y({},"__esModule",{value:!0}),r);function v(r,e){let t=r.trim();if(t==="status"||t==="statusCode")return e.response.status;if(t==="statusText")return e.response.statusText;if(t==="responseTime"||t==="duration")return e.response.timing?.duration||0;if(t.startsWith("requestHeader.")||t.startsWith("requestHeaders.")){let s=t.split(".")[1];return e.request?.headers.find(i=>i.key.toLowerCase()===s.toLowerCase())?.value}if(t.startsWith("header.")||t.startsWith("headers.")){let n=t.split("."),s=n[1];if(s.toLowerCase()==="set-cookie"){let i=(0,x.parseCookies)(e.response.headers);if(n.length===2)return i;let c=n.slice(2).join(".");return l(i,c)}return e.response.headers.find(i=>i.key.toLowerCase()===s.toLowerCase())?.value}if(t==="body"||t==="response.body")return e.response.body;if(t.startsWith("body.")||t.startsWith("response.body.")){let n=t.replace(/^(response\.)?body\./,"");return l(e.response.body,n)}if(t.startsWith("response.")){let n=t.replace(/^response\./,"");return l(e.response,n)}return l(e.response.body,t)}function l(r,e){if(!r)return;let t=e.split(/\.|\[/).map(s=>s.replace(/\]$/,"")),n=r;for(let s of t){if(n==null)return;/^\d+$/.test(s)?n=n[parseInt(s,10)]:n=n[s]}return n}function E(r,e){try{if(r.enabled===!1)return{assertion:r,passed:!0,actualValue:null,error:"Skipped (disabled)"};let t=F(r.field),n=t.field,s=t.operator||r.operator||"equals",o=r.expectedValue,i=v(n,e),c=L(s,i,o);return{assertion:r,passed:c,actualValue:i}}catch(t){return{assertion:r,passed:!1,actualValue:null,error:t.message||String(t)}}}function F(r){let e=["equals","eq","==","===","not-equals","ne","!=","!==","contains","includes","not-contains","not-includes","starts-with","startswith","ends-with","endswith","matches","regex","exists","is-defined","not-exists","is-null","is-undefined","greater-than","gt",">","less-than","lt","<","greater-equal","gte",">=","less-equal","lte","<=","is-empty","empty","not-empty","is-truthy","truthy","is-falsy","falsy","type-is","typeof"],t=r.trim().split(/\s+/);if(t.length>=2){let n=t[t.length-1].toLowerCase();if(e.includes(n))return{field:t.slice(0,-1).join(" "),operator:n}}return{field:r.trim(),operator:null}}function L(r,e,t){switch(r.toLowerCase().trim()){case"equals":case"eq":case"==":case"===":return e==null?!1:String(e)===t;case"not-equals":case"ne":case"!=":case"!==":return e==null?!1:String(e)!==t;case"contains":case"includes":return e==null?!1:String(e).includes(t);case"not-contains":case"not-includes":return e==null?!1:!String(e).includes(t);case"starts-with":case"startswith":return e==null?!1:String(e).startsWith(t);case"ends-with":case"endswith":return e==null?!1:String(e).endsWith(t);case"matches":case"regex":if(e==null)return!1;try{return new RegExp(t).test(String(e))}catch{return!1}case"exists":case"is-defined":return e!=null;case"not-exists":case"is-null":case"is-undefined":return e==null;case"greater-than":case"gt":case">":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s>o}case"less-than":case"lt":case"<":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s<o}case"greater-equal":case"gte":case">=":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s>=o}case"less-equal":case"lte":case"<=":{let s=Number(e),o=Number(t);return!isNaN(s)&&!isNaN(o)&&s<=o}case"is-empty":case"empty":return e==null?!1:Array.isArray(e)?e.length===0:typeof e=="string"?e==="":typeof e=="object"?Object.keys(e).length===0:!1;case"not-empty":return e==null?!1:Array.isArray(e)?e.length>0:typeof e=="string"?e!=="":typeof e=="object"?Object.keys(e).length>0:!0;case"is-truthy":case"truthy":return!!e;case"is-falsy":case"falsy":return!e;case"type-is":case"typeof":return typeof e===t.toLowerCase();default:throw new Error(`Unknown operator: ${r}`)}}function S(r,e){return r.map(t=>E(t,e))}var x,q=A(()=>{"use strict";x=require("@voiden/sdk/shared")});var N={};w(N,{postProcessAssertionsHook:()=>P});function O(r){let e=[];if(!r||!r.content)return e;function t(n){if(n.type==="assertions-table"){let s=j(n);e.push(...s)}n.content&&Array.isArray(n.content)&&n.content.forEach(s=>t(s))}return t(r),e}function j(r){let e=[];try{if(r.attrs&&Array.isArray(r.attrs.rows)){let s=r.attrs.rows;for(let o of s)!o.description&&!o.field&&!o.operator&&!o.expectedValue||o.enabled!==!1&&e.push({description:o.description||"",field:o.field||"",operator:o.operator||"equals",expectedValue:o.expectedValue||"",enabled:!0});return e}let t=Array.isArray(r.content)?r.content.find(s=>s.type==="table"||s.type==="assertions-table"):null;if(!t)return e;let n=t.rows||t.content||[];for(let s=0;s<n.length;s++){let o=n[s];if(Array.isArray(o.row)){if(o.attrs?.disabled===!0)continue;let[a,h,g,b]=o.row;if(!a&&!h&&!g&&!b)continue;e.push({description:String(a||"").trim(),field:String(h||"").trim(),operator:String(g||"equals").trim()||"equals",expectedValue:String(b||"").trim(),enabled:!0});continue}if(o.type!=="tableRow"||o.attrs&&o.attrs.disabled===!0)continue;let i=o.content||[];if(i.length<4)continue;let c=f(i[0]).trim(),p=f(i[1]).trim(),d=f(i[2]).trim(),u=f(i[3]).trim();!c&&!p&&!d&&!u||!(!(i.length>=5)||H(i[4]))||e.push({description:c,field:p,operator:d||"equals",expectedValue:u,enabled:!0})}}catch(t){console.error("Error extracting assertions from table:",t)}return e}function f(r){return r?r.type==="text"?r.text||"":r.content&&Array.isArray(r.content)?r.content.map(e=>f(e)).join(""):"":""}function H(r){if(!r||!r.content)return!0;let e=r.content.find(t=>t.type==="checkbox");return e&&e.attrs?e.attrs.checked!==!1:!0}async function P(r){let{requestState:e,responseState:t,metadata:n}=r;try{let s=e?.metadata?.editorDocument||n?.requestDocument||n?.editorDocument;if(!s)return;let o=O(s);if(o.length===0)return;let i=(typeof window<"u"?window:void 0)?.electron?.env?.replaceVariables;if(i)for(let a of o)a.expectedValue&&a.expectedValue.includes("{{")&&(a.expectedValue=await i(a.expectedValue)),a.field&&a.field.includes("{{")&&(a.field=await i(a.field));let c=t.body;if(typeof c=="string"&&c.trim())try{c=JSON.parse(c)}catch{}let p={response:{status:t.status,statusText:t.statusText,headers:t.headers||[],body:c,contentType:t.contentType,timing:t.timing},request:{headers:t.requestMeta?.headers||[]}},d=S(o,p),u=d.filter(a=>a.passed).length,m=d.filter(a=>!a.passed).length;t.metadata||(t.metadata={}),t.metadata.assertionResults={results:d,totalAssertions:o.length,passedAssertions:u,failedAssertions:m}}catch(s){console.error("[Simple Assertions] Error in post-process assertions hook:",s)}}var k=A(()=>{"use strict";q()});var J={};w(J,{default:()=>z});module.exports=W(J);var $=r=>({onload(){r.pipeline.registerHook("pre-processing",async e=>{e.editor&&(e.requestState.metadata||(e.requestState.metadata={}),e.requestState.metadata.editorDocument=e.editor.getJSON())},5),r.pipeline.registerHook("post-processing",async e=>{let{postProcessAssertionsHook:t}=await Promise.resolve().then(()=>(k(),N));await t(e)},15),r.pipeline.registerHook("post-processing",async e=>{let t=e.responseState?.metadata?.assertionResults;if(t?.results?.length){e.responseState.metadata||(e.responseState.metadata={}),Array.isArray(e.responseState.metadata.reportEntries)||(e.responseState.metadata.reportEntries=[]);for(let n of t.results){let s=n.assertion?.description?.trim()||`${n.assertion?.field??""} ${n.assertion?.operator??""} ${n.assertion?.expectedValue??""}`.trim();e.responseState.metadata.reportEntries.push({type:"assertion",message:s||"Assertion",passed:n.passed,actual:n.actualValue,expected:n.assertion?.expectedValue,operator:n.assertion?.operator})}}},50)}}),z=$;
@@ -1,11 +1,11 @@
1
1
  {
2
- "simple-assertions": "1.0.4",
3
- "voiden-advanced-auth": "2.0.7",
4
- "voiden-faker": "1.1.0",
5
- "voiden-graphql": "1.0.6",
6
- "voiden-mcp-client": "0.1.0",
7
- "voiden-mcp-tool": "0.1.0",
8
- "voiden-rest-api": "1.4.11",
2
+ "simple-assertions": "1.1.0",
3
+ "voiden-advanced-auth": "2.0.8",
4
+ "voiden-faker": "1.1.1",
5
+ "voiden-graphql": "1.0.7",
6
+ "voiden-mcp-client": "0.1.1",
7
+ "voiden-mcp-tool": "0.1.1",
8
+ "voiden-rest-api": "1.4.12",
9
9
  "voiden-scripting": "1.1.6",
10
- "voiden-sockets-grpcs": "1.1.6"
10
+ "voiden-sockets-grpcs": "1.1.7"
11
11
  }
package/dist/envFile.d.ts CHANGED
@@ -1,3 +1,12 @@
1
+ /**
2
+ * Structurally merges two parsed YAML env trees (e.g. a profile's public +
3
+ * private content) — `b`'s `variables:` win over `a`'s per node on key
4
+ * conflict (matches the app's own public/private precedence, see env.ts's
5
+ * loadYamlEnvironments), `children:` are merged recursively rather than one
6
+ * replacing the other wholesale, so a child defined only in `a` still shows
7
+ * up even if `b` also defines siblings under the same parent.
8
+ */
9
+ export declare function mergeYamlEnvTrees(a: unknown, b: unknown): Record<string, unknown>;
1
10
  /**
2
11
  * `environmentName`, when given, scopes YAML loading to exactly one named
3
12
  * environment (e.g. "dev") instead of the default "flatten every
@@ -10,4 +19,21 @@
10
19
  * treated as an error, since there's genuinely nothing to scope to.
11
20
  */
12
21
  export declare function loadEnvFile(envPath: string, environmentName?: string): Record<string, string>;
22
+ /**
23
+ * Navigates a tree by an exact dotted path (as produced by
24
+ * listYamlEnvironmentNames, e.g. "staging.eu") rather than searching for a
25
+ * bare key anywhere — unlike findEnvironment above (kept as-is for the
26
+ * CLI's existing --environment <name> flag, which intentionally matches by
27
+ * bare name wherever it occurs), a dotted path must resolve deterministically:
28
+ * two different parents can each have a child literally named "eu", and
29
+ * only a full-path walk tells them apart. Same inheritance order (each
30
+ * segment's own variables merge on top of everything above it).
31
+ */
32
+ export declare function findEnvironmentByPath(tree: Record<string, unknown>, dottedPath: string): Record<string, string> | null;
33
+ /** Every named environment in the tree, at any depth, as dotted paths
34
+ * (e.g. "staging", "staging.eu") — unlike topLevelEnvironmentNames, this
35
+ * IS meant to show the full nested shape (used by list_environments to
36
+ * show a profile's environment hierarchy, not just an error hint). */
37
+ export declare function listYamlEnvironmentNames(content: string): string[];
38
+ export declare function parseYamlEnv(content: string, environmentName?: string): Record<string, string>;
13
39
  //# sourceMappingURL=envFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"envFile.d.ts","sourceRoot":"","sources":["../src/envFile.ts"],"names":[],"mappings":"AASA;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS7F"}
1
+ {"version":3,"file":"envFile.d.ts","sourceRoot":"","sources":["../src/envFile.ts"],"names":[],"mappings":"AASA;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBjF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAS7F;AA6DD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAWtH;AAED;;;uEAGuE;AACvE,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBlE;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAsC9F"}
package/dist/envFile.js CHANGED
@@ -1,6 +1,39 @@
1
1
  import { readFileSync } from 'fs';
2
2
  import { extname } from 'path';
3
3
  import YAML from 'yaml';
4
+ /**
5
+ * Structurally merges two parsed YAML env trees (e.g. a profile's public +
6
+ * private content) — `b`'s `variables:` win over `a`'s per node on key
7
+ * conflict (matches the app's own public/private precedence, see env.ts's
8
+ * loadYamlEnvironments), `children:` are merged recursively rather than one
9
+ * replacing the other wholesale, so a child defined only in `a` still shows
10
+ * up even if `b` also defines siblings under the same parent.
11
+ */
12
+ export function mergeYamlEnvTrees(a, b) {
13
+ const treeA = a && typeof a === 'object' && !Array.isArray(a) ? a : {};
14
+ const treeB = b && typeof b === 'object' && !Array.isArray(b) ? b : {};
15
+ const keys = new Set([...Object.keys(treeA), ...Object.keys(treeB)]);
16
+ const result = {};
17
+ for (const key of keys) {
18
+ const nodeA = treeA[key];
19
+ const nodeB = treeB[key];
20
+ const isNodeA = nodeA != null && typeof nodeA === 'object' && !Array.isArray(nodeA);
21
+ const isNodeB = nodeB != null && typeof nodeB === 'object' && !Array.isArray(nodeB);
22
+ if (!isNodeA && !isNodeB) {
23
+ result[key] = nodeB ?? nodeA;
24
+ continue;
25
+ }
26
+ const a2 = (isNodeA ? nodeA : {});
27
+ const b2 = (isNodeB ? nodeB : {});
28
+ result[key] = {
29
+ variables: { ...(a2.variables ?? {}), ...(b2.variables ?? {}) },
30
+ ...(a2.children || b2.children
31
+ ? { children: mergeYamlEnvTrees(a2.children ?? {}, b2.children ?? {}) }
32
+ : {}),
33
+ };
34
+ }
35
+ return result;
36
+ }
4
37
  /**
5
38
  * `environmentName`, when given, scopes YAML loading to exactly one named
6
39
  * environment (e.g. "dev") instead of the default "flatten every
@@ -78,7 +111,54 @@ function topLevelEnvironmentNames(tree) {
78
111
  .filter(([, value]) => value != null && typeof value === 'object' && !Array.isArray(value))
79
112
  .map(([key]) => key);
80
113
  }
81
- function parseYamlEnv(content, environmentName) {
114
+ /**
115
+ * Navigates a tree by an exact dotted path (as produced by
116
+ * listYamlEnvironmentNames, e.g. "staging.eu") rather than searching for a
117
+ * bare key anywhere — unlike findEnvironment above (kept as-is for the
118
+ * CLI's existing --environment <name> flag, which intentionally matches by
119
+ * bare name wherever it occurs), a dotted path must resolve deterministically:
120
+ * two different parents can each have a child literally named "eu", and
121
+ * only a full-path walk tells them apart. Same inheritance order (each
122
+ * segment's own variables merge on top of everything above it).
123
+ */
124
+ export function findEnvironmentByPath(tree, dottedPath) {
125
+ let current = tree;
126
+ let resolved = {};
127
+ for (const segment of dottedPath.split('.')) {
128
+ const value = current[segment];
129
+ if (value === null || value === undefined || Array.isArray(value) || typeof value !== 'object')
130
+ return null;
131
+ const node = value;
132
+ resolved = { ...resolved, ...ownVariables(node) };
133
+ current = (node.children ?? {});
134
+ }
135
+ return resolved;
136
+ }
137
+ /** Every named environment in the tree, at any depth, as dotted paths
138
+ * (e.g. "staging", "staging.eu") — unlike topLevelEnvironmentNames, this
139
+ * IS meant to show the full nested shape (used by list_environments to
140
+ * show a profile's environment hierarchy, not just an error hint). */
141
+ export function listYamlEnvironmentNames(content) {
142
+ const parsed = YAML.parse(content);
143
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
144
+ return [];
145
+ const names = [];
146
+ const walk = (tree, prefix) => {
147
+ for (const [key, value] of Object.entries(tree)) {
148
+ if (value === null || value === undefined || Array.isArray(value) || typeof value !== 'object')
149
+ continue;
150
+ const path = prefix ? `${prefix}.${key}` : key;
151
+ names.push(path);
152
+ const node = value;
153
+ if (node.children != null && typeof node.children === 'object') {
154
+ walk(node.children, path);
155
+ }
156
+ }
157
+ };
158
+ walk(parsed, '');
159
+ return names;
160
+ }
161
+ export function parseYamlEnv(content, environmentName) {
82
162
  const parsed = YAML.parse(content);
83
163
  if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
84
164
  return {};
@@ -1 +1 @@
1
- {"version":3,"file":"envFile.js","sourceRoot":"","sources":["../src/envFile.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"envFile.js","sourceRoot":"","sources":["../src/envFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAC9B,OAAO,IAAI,MAAM,MAAM,CAAA;AAOvB;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,CAAU,EAAE,CAAU;IACtD,MAAM,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA6B,CAAC,CAAC,CAAC,EAAE,CAAA;IACnG,MAAM,KAAK,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAA6B,CAAC,CAAC,CAAC,EAAE,CAAA;IACnG,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;IACpE,MAAM,MAAM,GAA4B,EAAE,CAAA;IAC1C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;QACxB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;QACxB,MAAM,OAAO,GAAG,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACnF,MAAM,OAAO,GAAG,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QACnF,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;YAAC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC;YAAC,SAAQ;QAAC,CAAC;QACpE,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAgB,CAAA;QAChD,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAgB,CAAA;QAChD,MAAM,CAAC,GAAG,CAAC,GAAG;YACZ,SAAS,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE;YAC/D,GAAG,CAAC,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,QAAQ;gBAC5B,CAAC,CAAC,EAAE,QAAQ,EAAE,iBAAiB,CAAC,EAAE,CAAC,QAAQ,IAAI,EAAE,EAAE,EAAE,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE;gBACvE,CAAC,CAAC,EAAE,CAAC;SACR,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CAAC,OAAe,EAAE,eAAwB;IACnE,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;IAE1C,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;QACtC,OAAO,YAAY,CAAC,OAAO,EAAE,eAAe,CAAC,CAAA;IAC/C,CAAC;IAED,OAAO,WAAW,CAAC,OAAO,CAAC,CAAA;AAC7B,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,GAAG,GAA2B,EAAE,CAAA;IACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAC5B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAQ;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAA;QACnF,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;QACjE,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAA;QAC5E,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;IAChB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,YAAY,CAAC,IAAiB;IACrC,MAAM,IAAI,GAA2B,EAAE,CAAA;IACvC,IAAI,IAAI,CAAC,SAAS,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QACjE,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACpD,IAAI,CAAC,IAAI,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,IAAI,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;+BAK+B;AAC/B,SAAS,eAAe,CACtB,IAA6B,EAC7B,IAAY,EACZ,YAAoC,EAAE;IAEtC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAChD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,SAAQ;QACxG,MAAM,IAAI,GAAG,KAAoB,CAAA;QACjC,MAAM,QAAQ,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,CAAA;QACxD,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO,QAAQ,CAAA;QACjC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,QAAmC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;YACvF,IAAI,KAAK;gBAAE,OAAO,KAAK,CAAA;QACzB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;mCAEmC;AACnC,SAAS,wBAAwB,CAAC,IAA6B;IAC7D,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SACxB,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAC1F,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAA6B,EAAE,UAAkB;IACrF,IAAI,OAAO,GAAG,IAAI,CAAA;IAClB,IAAI,QAAQ,GAA2B,EAAE,CAAA;IACzC,KAAK,MAAM,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;QAC9B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QAC3G,MAAM,IAAI,GAAG,KAAoB,CAAA;QACjC,QAAQ,GAAG,EAAE,GAAG,QAAQ,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,CAAA;QACjD,OAAO,GAAG,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAA4B,CAAA;IAC5D,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED;;;uEAGuE;AACvE,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAA;IAC7E,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,MAAM,IAAI,GAAG,CAAC,IAA6B,EAAE,MAAc,EAAQ,EAAE;QACnE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,SAAQ;YACxG,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA;YAC9C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YAChB,MAAM,IAAI,GAAG,KAAoB,CAAA;YACjC,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC/D,IAAI,CAAC,IAAI,CAAC,QAAmC,EAAE,IAAI,CAAC,CAAA;YACtD,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IACD,IAAI,CAAC,MAAiC,EAAE,EAAE,CAAC,CAAA;IAC3C,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,eAAwB;IACpE,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IAClC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAA;IAC7E,MAAM,IAAI,GAAG,MAAiC,CAAA;IAE9C,IAAI,eAAe,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,EAAE,eAAe,CAAC,CAAA;QACpD,IAAI,KAAK;YAAE,OAAO,KAAK,CAAA;QACvB,uEAAuE;QACvE,sEAAsE;QACtE,0EAA0E;QAC1E,MAAM,SAAS,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAA;QAChD,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,gBAAgB,eAAe,wCAAwC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC9F,CAAA;QACH,CAAC;IACH,CAAC;IAED,MAAM,GAAG,GAA2B,EAAE,CAAA;IACtC,MAAM,OAAO,GAAG,CAAC,IAA6B,EAAQ,EAAE;QACtD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,SAAQ;YAE3E,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC9B,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;gBACxB,SAAQ;YACV,CAAC;YAED,MAAM,OAAO,GAAG,KAAoB,CAAA;YACpC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;YACzC,IAAI,OAAO,CAAC,QAAQ,IAAI,IAAI,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACrE,OAAO,CAAC,OAAO,CAAC,QAAmC,CAAC,CAAA;YACtD,CAAC;QACH,CAAC;IACH,CAAC,CAAA;IACD,OAAO,CAAC,IAAI,CAAC,CAAA;IACb,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Headless env-profile discovery/resolution for the `list_environments`/
3
+ * `select_environment` fixed MCP tools (mcpServing.ts). A deliberately
4
+ * simplified port of apps/electron/src/main/env.ts's profile logic — same
5
+ * precedent as plugins/voiden-mcp-tool/src/lib/toolCapability.ts's
6
+ * loadProjectEnvironmentVars, which already does this for its own narrower
7
+ * needs. No Electron dependency (this package is a standalone CLI/library),
8
+ * no UI "active profile" state, no nested sub-project scanning, no legacy
9
+ * dotted `.env.foo.bar` hierarchy chain — see mcpServing.ts's callers for
10
+ * what's actually needed here.
11
+ */
12
+ export interface EnvProfileInfo {
13
+ name: string;
14
+ source: 'yaml' | 'legacy-dotenv';
15
+ /** Present only when source === 'yaml' and the file actually exists. */
16
+ publicFile?: string;
17
+ privateFile?: string;
18
+ /** Dotted environment paths (e.g. "staging", "staging.eu") — only when source === 'yaml'. */
19
+ environments?: string[];
20
+ /** Discovered .env* files (project root + .voiden/) — only when source === 'legacy-dotenv'. */
21
+ dotEnvFiles?: string[];
22
+ }
23
+ export declare function discoverEnvProfiles(projectRoot: string): EnvProfileInfo[];
24
+ /** Resolves the final variable map for one profile (+ optional named
25
+ * environment within it). Throws if the profile doesn't exist or the
26
+ * named environment isn't found in it (same "available: ..." style error
27
+ * envFile.ts's own parseYamlEnv already throws for an unknown name). */
28
+ export declare function resolveEnvProfile(projectRoot: string, profile: string, environmentName?: string): Record<string, string>;
29
+ //# sourceMappingURL=envProfiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envProfiles.d.ts","sourceRoot":"","sources":["../src/envProfiles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAUH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,GAAG,eAAe,CAAA;IAChC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6FAA6F;IAC7F,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB;AAoDD,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,EAAE,CAyBzE;AAED;;;yEAGyE;AACzE,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,eAAe,CAAC,EAAE,MAAM,GACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA4CxB"}
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Headless env-profile discovery/resolution for the `list_environments`/
3
+ * `select_environment` fixed MCP tools (mcpServing.ts). A deliberately
4
+ * simplified port of apps/electron/src/main/env.ts's profile logic — same
5
+ * precedent as plugins/voiden-mcp-tool/src/lib/toolCapability.ts's
6
+ * loadProjectEnvironmentVars, which already does this for its own narrower
7
+ * needs. No Electron dependency (this package is a standalone CLI/library),
8
+ * no UI "active profile" state, no nested sub-project scanning, no legacy
9
+ * dotted `.env.foo.bar` hierarchy chain — see mcpServing.ts's callers for
10
+ * what's actually needed here.
11
+ */
12
+ import { existsSync, readdirSync, readFileSync } from 'fs';
13
+ import { join } from 'path';
14
+ import { parseYamlEnv, listYamlEnvironmentNames, mergeYamlEnvTrees, findEnvironmentByPath } from './envFile.js';
15
+ import YAML from 'yaml';
16
+ const VOIDEN_DIR = '.voiden';
17
+ const PROFILE_FILE_PATTERN = /^env-([a-z0-9-]+)-(public|private)\.yaml$/;
18
+ function profileFileNames(projectRoot, profile) {
19
+ const dir = join(projectRoot, VOIDEN_DIR);
20
+ if (profile === 'default') {
21
+ return { publicFile: join(dir, 'env-public.yaml'), privateFile: join(dir, 'env-private.yaml') };
22
+ }
23
+ return { publicFile: join(dir, `env-${profile}-public.yaml`), privateFile: join(dir, `env-${profile}-private.yaml`) };
24
+ }
25
+ /** Mirrors env.ts's discoverProfiles — scan .voiden/ for named profile
26
+ * files, "default" always included even with no files on disk yet. */
27
+ function discoverProfileNames(projectRoot) {
28
+ const names = new Set(['default']);
29
+ const dir = join(projectRoot, VOIDEN_DIR);
30
+ let entries;
31
+ try {
32
+ entries = readdirSync(dir);
33
+ }
34
+ catch {
35
+ return Array.from(names);
36
+ }
37
+ for (const entry of entries) {
38
+ const match = PROFILE_FILE_PATTERN.exec(entry);
39
+ if (match)
40
+ names.add(match[1]);
41
+ }
42
+ return Array.from(names);
43
+ }
44
+ function readIfExists(path) {
45
+ return existsSync(path) ? readFileSync(path, 'utf-8') : undefined;
46
+ }
47
+ /** project root + .voiden/, non-recursive — same scope as env.ts's
48
+ * loadProjectEnv fallback, minus the dotted-chain hierarchy machinery
49
+ * (out of scope here, see the plan's "Not doing" section). */
50
+ function discoverDotEnvFiles(projectRoot) {
51
+ const dirs = [projectRoot, join(projectRoot, VOIDEN_DIR)];
52
+ const found = [];
53
+ for (const dir of dirs) {
54
+ let entries;
55
+ try {
56
+ entries = readdirSync(dir);
57
+ }
58
+ catch {
59
+ continue;
60
+ }
61
+ for (const entry of entries) {
62
+ if (entry.startsWith('.env'))
63
+ found.push(join(dir, entry));
64
+ }
65
+ }
66
+ return found;
67
+ }
68
+ export function discoverEnvProfiles(projectRoot) {
69
+ return discoverProfileNames(projectRoot).map((name) => {
70
+ const { publicFile, privateFile } = profileFileNames(projectRoot, name);
71
+ const publicContent = readIfExists(publicFile);
72
+ const privateContent = readIfExists(privateFile);
73
+ if (publicContent || privateContent) {
74
+ const merged = mergeYamlEnvTrees(publicContent ? YAML.parse(publicContent) : {}, privateContent ? YAML.parse(privateContent) : {});
75
+ const environments = listYamlEnvironmentNames(YAML.stringify(merged));
76
+ if (environments.length > 0) {
77
+ return {
78
+ name,
79
+ source: 'yaml',
80
+ ...(publicContent ? { publicFile } : {}),
81
+ ...(privateContent ? { privateFile } : {}),
82
+ environments,
83
+ };
84
+ }
85
+ }
86
+ return { name, source: 'legacy-dotenv', dotEnvFiles: discoverDotEnvFiles(projectRoot) };
87
+ });
88
+ }
89
+ /** Resolves the final variable map for one profile (+ optional named
90
+ * environment within it). Throws if the profile doesn't exist or the
91
+ * named environment isn't found in it (same "available: ..." style error
92
+ * envFile.ts's own parseYamlEnv already throws for an unknown name). */
93
+ export function resolveEnvProfile(projectRoot, profile, environmentName) {
94
+ const profiles = discoverEnvProfiles(projectRoot);
95
+ const info = profiles.find((p) => p.name === profile);
96
+ if (!info) {
97
+ throw new Error(`Unknown profile "${profile}". Available: ${profiles.map((p) => p.name).join(', ')}`);
98
+ }
99
+ if (info.source === 'legacy-dotenv') {
100
+ const env = {};
101
+ for (const path of info.dotEnvFiles ?? []) {
102
+ try {
103
+ Object.assign(env, parseDotEnvLoose(readFileSync(path, 'utf-8')));
104
+ }
105
+ catch {
106
+ // A malformed .env* file shouldn't block resolving the others —
107
+ // select_environment's job is "best available", not strict parsing.
108
+ }
109
+ }
110
+ return env;
111
+ }
112
+ const { publicFile, privateFile } = profileFileNames(projectRoot, profile);
113
+ const publicContent = readIfExists(publicFile) ?? '';
114
+ const privateContent = readIfExists(privateFile) ?? '';
115
+ const merged = mergeYamlEnvTrees(publicContent ? YAML.parse(publicContent) : {}, privateContent ? YAML.parse(privateContent) : {});
116
+ if (environmentName) {
117
+ // Path-based, not envFile.ts's own bare-name findEnvironment — that one
118
+ // searches for a matching key ANYWHERE in the tree (fine for the CLI's
119
+ // simpler --environment <name> flag), but list_environments returns
120
+ // full dotted paths specifically to disambiguate same-named children
121
+ // under different parents (e.g. "staging.eu" vs "prod.eu") — only a
122
+ // full-path walk resolves the right one.
123
+ const resolved = findEnvironmentByPath(merged, environmentName);
124
+ if (!resolved) {
125
+ const available = listYamlEnvironmentNames(YAML.stringify(merged));
126
+ throw new Error(`Environment "${environmentName}" not found in profile "${profile}". Available: ${available.join(', ')}`);
127
+ }
128
+ return resolved;
129
+ }
130
+ return parseYamlEnv(YAML.stringify(merged));
131
+ }
132
+ /** Same shape as envFile.ts's private parseDotEnv, duplicated rather than
133
+ * exported from there since that one throws on a malformed line (correct
134
+ * for an explicit --env <path>) where discovery here wants best-effort. */
135
+ function parseDotEnvLoose(content) {
136
+ const env = {};
137
+ for (const rawLine of content.split('\n')) {
138
+ const line = rawLine.trim();
139
+ if (!line || line.startsWith('#'))
140
+ continue;
141
+ const eq = line.indexOf('=');
142
+ if (eq === -1)
143
+ continue;
144
+ const key = line.slice(0, eq).trim();
145
+ if (!key)
146
+ continue;
147
+ env[key] = line.slice(eq + 1).trim().replace(/^["']|["']$/g, '');
148
+ }
149
+ return env;
150
+ }
151
+ //# sourceMappingURL=envProfiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"envProfiles.js","sourceRoot":"","sources":["../src/envProfiles.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1213,6 +1213,15 @@ pluginCmd
1213
1213
  else if (record !== undefined && !record.enabled) {
1214
1214
  statusBadge = chalk.yellow(' · disabled');
1215
1215
  }
1216
+ else if (!hasCoreRunner(def.name)) {
1217
+ // "enabled" (the plugins.json bit, or no record = default-enabled) is not the
1218
+ // same as "usable" — loadEnabledPlugins() also requires an actual runner.js,
1219
+ // bundled in this package install or cached in ~/.voiden/extensions/. Reporting
1220
+ // "✓ enabled" here regardless would silently disagree with what run_request /
1221
+ // the MCP server can actually load, which is exactly the confusing state this
1222
+ // command exists to surface.
1223
+ statusBadge = chalk.gray(' not installed [no runner]');
1224
+ }
1216
1225
  else {
1217
1226
  statusBadge = chalk.green(' ✓ enabled');
1218
1227
  }
@@ -1298,12 +1307,24 @@ mcpCmd
1298
1307
  .option('--claude', 'Install for Claude Code only')
1299
1308
  .option('--codex', 'Install for Codex only')
1300
1309
  .option('-p, --project <path>', 'Project directory to register the MCP server against', '.')
1301
- .option('--local-server <path>', 'Use `node <path> mcp serve` instead of `npx -y @voiden/runner mcp serve` — for testing against a local build')
1310
+ .option('--local-server <path>', 'Use `node <path> mcp serve` instead of `npx -y @voiden/runner@<this version> mcp serve` — for testing against a local build')
1302
1311
  .action((opts) => {
1303
1312
  const targets = resolveMcpTargets(opts);
1313
+ // Pin to the exact version of this CLI the user is running `mcp install`
1314
+ // from — never a bare, unpinned `npx -y @voiden/runner`. Unpinned resolves
1315
+ // to whatever npm's "latest" dist-tag happens to point at, which can sit
1316
+ // far behind the actively-developed prerelease line this MCP tooling
1317
+ // actually lives on (e.g. "latest" stuck on a stable 2.2.0 cut from
1318
+ // before write_result/list_requests/the plugin registry existed, while
1319
+ // real fixes ship under the "beta" tag) — every agent session would
1320
+ // silently run that stale version forever, with no way to notice short of
1321
+ // diffing tool output against what this CLI's own docs say it does.
1322
+ // Pinning to what's actually installed right now is deterministic: it's
1323
+ // exactly what was tested when `mcp install` ran, and picks up newer
1324
+ // fixes the moment the user updates this CLI and re-runs `mcp install`.
1304
1325
  const serverCommand = opts.localServer
1305
1326
  ? { command: 'node', args: [resolve(opts.localServer), 'mcp', 'serve', resolve(opts.project)] }
1306
- : undefined;
1327
+ : { command: 'npx', args: ['-y', `@voiden/runner@${pkg.version}`, 'mcp', 'serve', resolve(opts.project)] };
1307
1328
  const installed = installMcpIntegration(opts.project, targets, MCP_SKILL_MARKDOWN, serverCommand);
1308
1329
  if (installed.length === 0) {
1309
1330
  console.log(chalk.yellow(' Nothing to install.'));
@@ -1313,9 +1334,12 @@ mcpCmd
1313
1334
  for (const target of installed) {
1314
1335
  console.log(chalk.green(` ✓ ${target === 'claude' ? 'Claude Code' : 'Codex'}`) + chalk.gray(` — skill installed, fixed-tools MCP server registered for ${resolve(opts.project)}`));
1315
1336
  }
1316
- if (serverCommand) {
1337
+ if (opts.localServer) {
1317
1338
  console.log(chalk.gray(` Using local build: node ${serverCommand.args[0]}`));
1318
1339
  }
1340
+ else {
1341
+ console.log(chalk.gray(` Pinned to this CLI's version: @voiden/runner@${pkg.version}`));
1342
+ }
1319
1343
  console.log();
1320
1344
  console.log(chalk.gray(' Restart Claude Code / Codex (or run /mcp) to pick up the new server.'));
1321
1345
  });
@@ -1415,6 +1439,9 @@ mcpCmd
1415
1439
  // Shared across calls so {{process.xxx}} runtime variables chain the
1416
1440
  // same way they do for the stdio path and for @voiden/mcp.
1417
1441
  const runtimeVars = {};
1442
+ // Set by select_environment, read by run_request as its base env layer
1443
+ // — shared by reference across calls the same way runtimeVars is.
1444
+ const selectedEnv = { vars: {} };
1418
1445
  if (opts.http) {
1419
1446
  const port = Number(opts.port);
1420
1447
  const host = opts.host;
@@ -1426,7 +1453,7 @@ mcpCmd
1426
1453
  const httpServer = createHttpServer(async (req, res) => {
1427
1454
  try {
1428
1455
  const requestServer = new McpServer({ name: 'voiden-runner', version: '1.0.0' });
1429
- registerFixedTools(requestServer, projectRoot, runtimeVars, activePlugins);
1456
+ registerFixedTools(requestServer, projectRoot, runtimeVars, activePlugins, selectedEnv);
1430
1457
  registerToolsFromDecisions(requestServer, decisions, env, runtimeVars, activePlugins, commitSha, projectRoot);
1431
1458
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
1432
1459
  await requestServer.connect(transport);
@@ -1447,7 +1474,7 @@ mcpCmd
1447
1474
  });
1448
1475
  httpServer.listen(port, host, () => {
1449
1476
  console.error(chalk.green(` ✓ voiden-runner mcp serve — listening on http://${host}:${port}/mcp`));
1450
- console.error(chalk.gray(` ${servedCount} tool(s) served (plus list_void_files, list_requests, run_request, write_result)`));
1477
+ console.error(chalk.gray(` ${servedCount} tool(s) served (plus list_void_files, list_requests, run_request, write_result, list_environments, select_environment)`));
1451
1478
  if (host !== '127.0.0.1' && host !== 'localhost') {
1452
1479
  console.error(chalk.red(` ⚠ Bound to ${host} — reachable beyond this machine. Make sure that's intended.`));
1453
1480
  }
@@ -1460,7 +1487,7 @@ mcpCmd
1460
1487
  // Startup info goes to stderr only, same discipline @voiden/mcp's
1461
1488
  // own entrypoint already follows (it prints nothing).
1462
1489
  const server = new McpServer({ name: 'voiden-runner', version: '1.0.0' });
1463
- registerFixedTools(server, projectRoot, runtimeVars, activePlugins);
1490
+ registerFixedTools(server, projectRoot, runtimeVars, activePlugins, selectedEnv);
1464
1491
  registerToolsFromDecisions(server, decisions, env, runtimeVars, activePlugins, commitSha, projectRoot);
1465
1492
  await server.connect(new StdioServerTransport());
1466
1493
  }