@x-otto/env 0.1.0-alpha.0 → 0.1.0-alpha.2

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/README.md CHANGED
@@ -1,13 +1,13 @@
1
- # @otto/env
1
+ # @x-otto/env
2
2
 
3
3
  > Environment variable standardization, path constants, and resilience configuration. Zero internal dependencies — the foundational layer of the monorepo.
4
4
 
5
- `@otto/env` provides the canonical path constants (dot-otto root, auth path, session dirs, etc.), environment variable normalization helpers, Claude compatibility toggles, process enumeration/liveness probes, config-layer resolution, and the unified resilience config contract. Every other package in the monorepo depends on it — it is the leaf of the dependency graph.
5
+ `@x-otto/env` provides the canonical path constants (dot-otto root, auth path, session dirs, etc.), environment variable normalization helpers, Claude compatibility toggles, process enumeration/liveness probes, config-layer resolution, and the unified resilience config contract. Every other package in the monorepo depends on it — it is the leaf of the dependency graph.
6
6
 
7
7
  ## Installation
8
8
 
9
9
  ```bash
10
- pnpm add @otto/env
10
+ pnpm add @x-otto/env
11
11
  ```
12
12
 
13
13
  ## Usage
@@ -23,7 +23,7 @@ import {
23
23
  resolveConfigLayers,
24
24
  listOttoProcesses,
25
25
  isProcessAlive,
26
- } from '@otto/env'
26
+ } from '@x-otto/env'
27
27
 
28
28
  // Canonical paths
29
29
  console.log(OTTO_HOME) // ~/.otto (overridable via OTTO_HOME env)
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/build-id.d.ts
2
- declare const BUILD_ID = 4512;
2
+ declare const BUILD_ID = 4570;
3
3
  //#endregion
4
4
  //#region src/config-layers.d.ts
5
5
  /**
@@ -10,7 +10,7 @@ declare const BUILD_ID = 4512;
10
10
  * 不含信任门。纯函数、确定性、无副作用。
11
11
  *
12
12
  * 现状(RFC-044 review S1 + RFC-081 终局复审定档):真实职责 = 派生插件根
13
- * (`@otto/coding discoverPlugins` 取 otto 层得 `<.otto>/plugins`)。**「四轴收编中央源」经复审判定不迁**
13
+ * (`@x-otto/coding discoverPlugins` 取 otto 层得 `<.otto>/plugins`)。**「四轴收编中央源」经复审判定不迁**
14
14
  * (不是待办):command/agent 两个目录扫描轴的 precedence 已完全一致
15
15
  * (`plugin < user/claude < user/otto < project/claude < project/otto`,各有 RFC-032 测试守护漂移),
16
16
  * 强迁会把清晰的本地 layer 数组换成 `resolveConfigLayers().reverse().filter().map()` 徒增耦合;
@@ -127,18 +127,18 @@ declare function isContainerEnvironment(): boolean;
127
127
  /**
128
128
  * 统一重试/超时/退避配置契约(RFC-230)。
129
129
  *
130
- * 归口在零依赖叶包 `@otto/env`(不新建独立包,量级参照 RFC-157 已判定的 `@otto/invariant`
130
+ * 归口在零依赖叶包 `@x-otto/env`(不新建独立包,量级参照 RFC-157 已判定的 `@x-otto/invariant`
131
131
  * 29 行不该独立成包先例)。四层配置源优先级(右覆盖左):
132
132
  *
133
133
  * 硬编码默认值 → env(`OTTO_RESILIENCE_CONFIG`,JSON 字符串)→ settings(`.otto/config.json`
134
134
  * 的 `resilience` 字段)→ 程序化注入(`AppOptions.resilience`)
135
135
  *
136
136
  * 本文件只负责契约类型 + 默认值 + 纯函数解析/合并逻辑,不认识 `App`/`SettingsManager` 等
137
- * 任何上层概念——消费方(`@otto/setting`/`@otto/coding` 等)各自在正确时机调用
137
+ * 任何上层概念——消费方(`@x-otto/setting`/`@x-otto/coding` 等)各自在正确时机调用
138
138
  * `resolveResilienceConfig()`,不由本文件决定何时求值(见 RFC-230 §3 D5 的"延迟冻结"设计,
139
139
  * 那是消费方 `App` 的职责,不是本包的职责)。
140
140
  *
141
- * 不引入 zod(`@otto/env` 当前零外部依赖,见 package.json),字段级容错走手写递归校验。
141
+ * 不引入 zod(`@x-otto/env` 当前零外部依赖,见 package.json),字段级容错走手写递归校验。
142
142
  */
143
143
  /** 统一重试/超时/退避配置的完整形状(六个子系统,全部字段均为必填——`DEFAULT_RESILIENCE_CONFIG` 保证完整性)。 */
144
144
  interface ResilienceConfig {
@@ -178,7 +178,7 @@ declare const DEFAULT_RESILIENCE_CONFIG: ResilienceConfig;
178
178
  /**
179
179
  * `ResilienceConfig` 的深度可选覆盖形状。
180
180
  *
181
- * 手写局部类型,不引入 `@otto/shared` 的 `DeepPartial`——`@otto/env` 是比 `@otto/shared`
181
+ * 手写局部类型,不引入 `@x-otto/shared` 的 `DeepPartial`——`@x-otto/env` 是比 `@x-otto/shared`
182
182
  * 更底层的依赖叶子(`shared` 依赖 `env`,反向引入会成环),见 RFC-230 §3 D2 依赖方向核实。
183
183
  */
184
184
  interface ResilienceConfigOverride {
@@ -206,12 +206,12 @@ declare function parseResilienceEnv(raw: string | undefined, warn?: (message: st
206
206
  * 合并四层配置源,返回完整的 `ResilienceConfig`(永远齐全,不含 undefined 字段)。
207
207
  *
208
208
  * 合并顺序(右覆盖左):默认值 → env → settings → 程序化注入。
209
- * 消费方(`@otto/coding` 的 `App`)负责在正确时机(settings 已加载后)传入 `fromSettings`,
209
+ * 消费方(`@x-otto/coding` 的 `App`)负责在正确时机(settings 已加载后)传入 `fromSettings`,
210
210
  * 本函数是纯合并逻辑,不关心调用时机(见 RFC-230 §3 D5)。
211
211
  *
212
212
  * 三层输入(env/fromSettings/explicit)**全部**经 `validateResilienceOverride` 校验,不只校验
213
213
  * env 层——`fromSettings`/`explicit` 虽然编译期类型是 `ResilienceConfigOverride`,但运行时仍可能
214
- * 携带越界数值(如 settings 层理论上应已被 `@otto/setting` 的 zod schema 拦下,但那是另一个包
214
+ * 携带越界数值(如 settings 层理论上应已被 `@x-otto/setting` 的 zod schema 拦下,但那是另一个包
215
215
  * 的独立校验实现,不能假设它与本包的规则永远同步;`explicit` 是程序化注入,TS 类型只保证是
216
216
  * `number` 不保证范围)。统一在合并入口做一次权威校验,避免"同一条规则两处实现、日后漂移"。
217
217
  */
@@ -234,16 +234,16 @@ declare function normalizePath(path: string): string;
234
234
  * 从 `startDir` 向上查找最近的 `pnpm-workspace.yaml`(monorepo root 标志文件),返回其所在目录。
235
235
  * 找不到(如在非本仓的用户项目里运行)返回 `undefined`。
236
236
  *
237
- * RFC-130:单一真源实现——原先在 `@otto/coding`(`install-plugin.ts`,供 CLI `--repo` 消费)与
238
- * `@otto/plugin`(`discovery.ts`,供仓库级插件发现消费)各自维护一份逐字相同的实现(copy-paste),
239
- * 无法互相 import(`@otto/plugin` 是 `@otto/coding` 的依赖,反向 import 会成环)。收敛到零依赖的
240
- * `@otto/env`(两者共同的底层依赖)消除维护漂移风险(终局审核 finding,2026-07-09)。
237
+ * RFC-130:单一真源实现——原先在 `@x-otto/coding`(`install-plugin.ts`,供 CLI `--repo` 消费)与
238
+ * `@x-otto/plugin`(`discovery.ts`,供仓库级插件发现消费)各自维护一份逐字相同的实现(copy-paste),
239
+ * 无法互相 import(`@x-otto/plugin` 是 `@x-otto/coding` 的依赖,反向 import 会成环)。收敛到零依赖的
240
+ * `@x-otto/env`(两者共同的底层依赖)消除维护漂移风险(终局审核 finding,2026-07-09)。
241
241
  *
242
242
  * 终止条件用 `dirname(dir) === dir`(到达文件系统根)而非固定层数上限——后者在深层嵌套路径下
243
243
  * 会误截断(旧实现的 `i < 20` 上限已移除,审核 finding:该上限是冗余约束)。
244
244
  */
245
245
  declare function findWorkspaceRoot(startDir: string): string | undefined;
246
- /** SDK 版本号(`@otto/env` package.json version),会话创建时戳入 metadata。 */
246
+ /** SDK 版本号(`@x-otto/env` package.json version),会话创建时戳入 metadata。 */
247
247
  declare const SDK_VERSION: string;
248
248
  declare const OTTO_USER_AGENT: string;
249
249
  declare const OTTO_ROOT = ".otto";
@@ -322,7 +322,7 @@ declare const SETTING_OFFLINE_MODE_ENABLED: boolean;
322
322
  * 而非 offline 的模块级 const)。
323
323
  *
324
324
  * 为什么必须函数式而非 const:`OTTO_SHADOW` 由 `packages/cli/src/cli.ts` 在**运行时**
325
- * 解析 `--shadow` flag 后写入 `process.env`,而 `@otto/env` 在 cli.ts 顶部就被 import——
325
+ * 解析 `--shadow` flag 后写入 `process.env`,而 `@x-otto/env` 在 cli.ts 顶部就被 import——
326
326
  * 若用模块级 const,常量在 flag 写入前就已冻结为 false。调用时求值规避此时序陷阱,
327
327
  * 也便于测试注入。(offline 模式可用 const 是因为 `OTTO_OFFLINE` 由外部 shell 预设、
328
328
  * node 启动前就存在——两者注入时机不同,范式不能照搬。)
@@ -330,7 +330,7 @@ declare const SETTING_OFFLINE_MODE_ENABLED: boolean;
330
330
  * 语义:为真时该进程及其全部子进程/worker 在整个生命周期内不向磁盘写入任何 otto 自有
331
331
  * 状态(凭据/会话/记忆/配置/trace/日志),既有磁盘状态照常只读继承(overlay 语义)。
332
332
  *
333
- * 传染面:`OTTO_SHADOW` 走 `OTTO_` 前缀,由 `@otto/shared` 的 `buildAllowedEnv` 白名单
333
+ * 传染面:`OTTO_SHADOW` 走 `OTTO_` 前缀,由 `@x-otto/shared` 的 `buildAllowedEnv` 白名单
334
334
  * 自动透传给全部 spawn 子进程;worker_threads 共享 `process.env` 天然继承——无需额外接线。
335
335
  *
336
336
  * 判断该开关的位置受 RFC-345 §D1 白名单约束(架构门禁强制),不得在业务层散落 `if (shadow)`。
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import{homedir as e,hostname as t}from"node:os";import{existsSync as n,readFileSync as r}from"node:fs";import{dirname as i,join as a,normalize as o,sep as s}from"node:path";import{spawnSync as c}from"node:child_process";const l=4512;function u(e){let{cwd:t,homedir:n,workspaceRoot:r}=e,i=e.claudeCompat??q(),o=[];return o.push({kind:`otto`,scope:`project`,dir:j(a(t,`.otto`))}),r&&j(r)!==j(t)&&o.push({kind:`otto`,scope:`repository`,dir:j(a(r,`.otto`))}),i&&o.push({kind:`claude`,scope:`project`,dir:j(a(t,`.claude`))}),n&&o.push({kind:`otto`,scope:`user`,dir:j(a(n,`.otto`))}),i&&n&&o.push({kind:`claude`,scope:`user`,dir:j(a(n,`.claude`))}),o}function d(e){try{return process.kill(e,0),!0}catch{return!1}}function f(){return t()}function p(e,t,n){return`${e}:${t}:${n}`}function m(e){let t=e.indexOf(`:`);if(t===-1)return{token:e};let n=e.indexOf(`:`,t+1);if(n===-1)return{token:e};let r=e.slice(0,t),i=e.slice(t+1,n),a=e.slice(n+1),o=Number(i),s=Number.isFinite(o)&&o>0?o:void 0;return s!==void 0&&a.length>0?{token:r,pid:s,host:a}:{token:r}}function h(e){let t=e.trim().match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);if(!t)return 0;let[,n,r,i,a]=t;return(Number(n??0)*86400+Number(r??0)*3600+Number(i)*60+Number(a))*1e3}function ee(e){return e.match(/--continue\s+([0-9a-f-]{36})/)?.[1]}function te(e){let t=e.match(/--restart-generation=(\d+)/);return t?Number(t[1]):void 0}function g(e=`bin/otto.js`){let t=c(`ps`,[`-axo`,`pid=,ppid=,rss=,tty=,etime=,args=`],{encoding:`utf8`,timeout:1e4,maxBuffer:16*1024*1024});return t.status!==0||!t.stdout?[]:_(t.stdout,e)}function _(e,t=`bin/otto.js`){let n=[];for(let r of e.split(`
2
- `)){if(!r.includes(t))continue;let e=r.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/);if(!e)continue;let[,i,a,o,s,c,l]=e;l.includes(`spawn-helper`)||n.push({pid:Number(i),ppid:Number(a),rssKb:Number(o),tty:s,etimeMs:h(c),command:l,sessionId:ee(l),generation:te(l)})}return n}function v(){if(process.platform===`darwin`)return!1;try{if(n(`/.dockerenv`))return!0;if(n(`/proc/1/cgroup`)){let e=r(`/proc/1/cgroup`,`utf8`);return/docker|kubepods|containerd/.test(e)}}catch{}return!1}const y={stream:{maxRetries:5,initialDelayMs:500,backoffFactor:2,maxDelayMs:6e4},networkDisconnect:{maxRetries:5,intervalMs:3e4},mcp:{maxReconnectAttempts:3,reconnectDelayMs:500,requestTimeoutMs:3e4},schedule:{retryBackoffMs:0},errorRetry:{maxAttempts:5,intervalMs:3e4},oauth:{refreshLockTimeoutMs:15e3}},b={stream:[`maxRetries`,`initialDelayMs`,`backoffFactor`,`maxDelayMs`],networkDisconnect:[`maxRetries`,`intervalMs`],mcp:[`maxReconnectAttempts`,`reconnectDelayMs`,`requestTimeoutMs`],schedule:[`retryBackoffMs`],errorRetry:[`maxAttempts`,`intervalMs`],oauth:[`refreshLockTimeoutMs`]},x=Object.keys(b),S=new Set([`mcp.maxReconnectAttempts`,`schedule.retryBackoffMs`]);function ne(e,t,n){return typeof n!=`number`||!Number.isFinite(n)||(S.has(`${e}.${t}`)?n<0:n<=0)?!1:e===`oauth`&&t===`refreshLockTimeoutMs`?n>=1e3:!0}function C(e,t){if(typeof e!=`object`||!e||Array.isArray(e)){t(`resilience config must be an object, got: ${typeof e}`);return}let n={},r=e;for(let e of Object.keys(r)){if(!x.includes(e)){t(`resilience config has unknown top-level key "${e}", ignored`);continue}let i=e,a=r[e];if(typeof a!=`object`||!a||Array.isArray(a)){t(`resilience.${i} must be an object, got: ${typeof a}, ignored`);continue}let o=a,s={};for(let e of Object.keys(o)){if(!b[i].includes(e)){t(`resilience.${i}.${e} is not a recognized field, ignored`);continue}let n=o[e];if(!ne(i,e,n)){t(`resilience.${i}.${e} has invalid value (${JSON.stringify(n)}), ignored`);continue}s[e]=n}Object.keys(s).length>0&&(n[i]=s)}return n}function w(e,t=e=>console.warn(`[resilience-config] ${e}`)){if(!e)return;let n;try{n=JSON.parse(e)}catch(e){t(`OTTO_RESILIENCE_CONFIG is not valid JSON, ignoring entirely: ${e instanceof Error?e.message:String(e)}`);return}return C(n,t)}function T(e,t){return t?{...e,...t}:e}function re(e,t,n=e=>console.warn(`[resilience-config] ${e}`)){let r=[w(process.env.OTTO_RESILIENCE_CONFIG,n),t?C(t,n):void 0,e?C(e,n):void 0],i={stream:{...y.stream},networkDisconnect:{...y.networkDisconnect},mcp:{...y.mcp},schedule:{...y.schedule},errorRetry:{...y.errorRetry},oauth:{...y.oauth}};for(let e of r)e&&(i={stream:T(i.stream,e.stream),networkDisconnect:T(i.networkDisconnect,e.networkDisconnect),mcp:T(i.mcp,e.mcp),schedule:T(i.schedule,e.schedule),errorRetry:T(i.errorRetry,e.errorRetry),oauth:T(i.oauth,e.oauth)});return i}const E=[];function D(e){let t=E.splice(0);for(let n of t)e(n)}function O(){return E.splice(0)}const k=(e,t)=>{if(e===void 0)return t;let n=parseInt(e,10);return isNaN(n)||n<=0?(E.push(`Invalid environment variable value: ${e}. Using default: ${t}`),t):n},A=(e,t)=>{if(e===void 0)return t;let n=parseFloat(e);return isNaN(n)||n<=0||n>1?(E.push(`Invalid fraction environment variable value: ${e}. Using default: ${t}`),t):n};function j(e){return o(e).replaceAll(s===`\\`?`\\`:s,`/`)}function M(e){let t=e;for(;;){if(n(a(t,`pnpm-workspace.yaml`)))return t;let e=i(t);if(e===t)return;t=e}}function N(){try{let e=j(`${j(new URL(`.`,import.meta.url).pathname)}/../package.json`);return JSON.parse(r(e,`utf-8`)).version||`unknown`}catch(e){return console.warn(`Failed to read package version:`,e),`unknown`}}const P=N(),F=`otto/${P}`,I=`.otto`,L=process.env.OTTO_CONFIG_FILENAME||`config.json`,R=`${I}/${L}`,z=j(process.env.OTTO_HOME||`${e()}/.otto`),B=`${z}/sessions`,V=`${B}/locks`,H=`${z}/daemon`,U=j(e()+`/.config/otto`),W=j(`${process.cwd()}/${I}`),G=`.claude`,K=j(`${e()}/${G}`);function q(){let e=process.env.OTTO_CLAUDE_COMPAT?.toLowerCase();return!(e===`0`||e===`false`||e===`no`||e===`off`)}function J(){if(!q())return!1;let e=process.env.OTTO_CLAUDE_COMPAT_SETTINGS?.toLowerCase();return e===`1`||e===`true`||e===`yes`||e===`on`}function Y(){if(!q())return!1;let e=process.env.OTTO_CLAUDE_COMPAT_HOOKS?.toLowerCase();return e===`1`||e===`true`||e===`yes`||e===`on`}const X=process.env.OTTO_LOG_FILE?j(process.env.OTTO_LOG_FILE):``,Z=k(process.env.OTTO_SERVICE_PORT,8417),ie=process.env.OTTO_SERVICE_URL??`http://127.0.0.1:${Z}`,ae=()=>(process.env.OTTO_NPM_SNIFF_REGISTRIES??``).split(`,`).map(e=>e.trim().replace(/\/+$/,``)).filter(e=>e.length>0),oe=process.env.OTTO_LOG_LEVEL||(process.env.NODE_ENV===`production`?`info`:`trace`),se=process.env.OTTO_OFFLINE===`1`||process.env.OTTO_OFFLINE?.toLowerCase()===`true`||process.env.OTTO_OFFLINE?.toLowerCase()===`yes`;function Q(){let e=process.env.OTTO_SHADOW?.toLowerCase();return e===`1`||e===`true`||e===`yes`||e===`on`}const ce=j(process.env.OTTO_AUTH_PATH||`${U}/auth.json`),$=j(process.env.OTTO_IDENTITY_PATH||`${z}/identity.json`);async function le(e){let t=e??$;try{let{readFile:e}=await import(`node:fs/promises`),n=await e(t,`utf-8`),r=JSON.parse(n);return typeof r.userId==`string`&&typeof r.token==`string`&&typeof r.serviceUrl==`string`&&typeof r.loginAt==`number`?r:null}catch{return null}}export{ce as AUTH_PATH,l as BUILD_ID,K as CLAUDE_HOME,G as CLAUDE_ROOT,y as DEFAULT_RESILIENCE_CONFIG,X as LOG_FILE,oe as LOG_LEVEL,L as OTTO_CONFIG_FILENAME,H as OTTO_DAEMON_DIR,z as OTTO_HOME,$ as OTTO_IDENTITY_PATH,R as OTTO_PROJECT_CONFIG_RELPATH,W as OTTO_PROJECT_DIR,I as OTTO_ROOT,B as OTTO_SESSIONS_DIR,V as OTTO_SESSION_LOCKS_DIR,F as OTTO_USER_AGENT,U as OTTO_USER_CONFIG_DIR,P as SDK_VERSION,Z as SERVICE_PORT,ie as SERVICE_URL,se as SETTING_OFFLINE_MODE_ENABLED,f as currentHostname,m as decodeLeaseToken,O as drainEnvWarnings,p as encodeLeaseToken,M as findWorkspaceRoot,D as flushEnvWarnings,q as isClaudeCompatEnabled,Y as isClaudeHooksCompatEnabled,J as isClaudeSettingsCompatEnabled,v as isContainerEnvironment,d as isProcessAlive,Q as isShadowModeEnabled,g as listOttoProcesses,le as loadUserIdentity,k as normalizeEnv,A as normalizeEnvFraction,j as normalizePath,h as parseEtimeMs,_ as parseOttoProcessTable,w as parseResilienceEnv,u as resolveConfigLayers,ae as resolveNpmSniffRegistries,re as resolveResilienceConfig,C as validateResilienceOverride};
1
+ import{homedir as e,hostname as t}from"node:os";import{existsSync as n,readFileSync as r}from"node:fs";import{dirname as i,join as a,normalize as o,sep as s}from"node:path";import{spawnSync as c}from"node:child_process";const l=4570;function u(e){let{cwd:t,homedir:n,workspaceRoot:r}=e,i=e.claudeCompat??q(),o=[];return o.push({kind:`otto`,scope:`project`,dir:j(a(t,`.otto`))}),r&&j(r)!==j(t)&&o.push({kind:`otto`,scope:`repository`,dir:j(a(r,`.otto`))}),i&&o.push({kind:`claude`,scope:`project`,dir:j(a(t,`.claude`))}),n&&o.push({kind:`otto`,scope:`user`,dir:j(a(n,`.otto`))}),i&&n&&o.push({kind:`claude`,scope:`user`,dir:j(a(n,`.claude`))}),o}function d(e){try{return process.kill(e,0),!0}catch{return!1}}function f(){return t()}function p(e,t,n){return`${e}:${t}:${n}`}function m(e){let t=e.indexOf(`:`);if(t===-1)return{token:e};let n=e.indexOf(`:`,t+1);if(n===-1)return{token:e};let r=e.slice(0,t),i=e.slice(t+1,n),a=e.slice(n+1),o=Number(i),s=Number.isFinite(o)&&o>0?o:void 0;return s!==void 0&&a.length>0?{token:r,pid:s,host:a}:{token:r}}function h(e){let t=e.trim().match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);if(!t)return 0;let[,n,r,i,a]=t;return(Number(n??0)*86400+Number(r??0)*3600+Number(i)*60+Number(a))*1e3}function ee(e){return e.match(/--continue\s+([0-9a-f-]{36})/)?.[1]}function te(e){let t=e.match(/--restart-generation=(\d+)/);return t?Number(t[1]):void 0}function g(e=`bin/otto.js`){let t=c(`ps`,[`-axo`,`pid=,ppid=,rss=,tty=,etime=,args=`],{encoding:`utf8`,timeout:1e4,maxBuffer:16*1024*1024});return t.status!==0||!t.stdout?[]:_(t.stdout,e)}function _(e,t=`bin/otto.js`){let n=[];for(let r of e.split(`
2
+ `)){if(!r.includes(t))continue;let e=r.match(/^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.*)$/);if(!e)continue;let[,i,a,o,s,c,l]=e;l.includes(`spawn-helper`)||n.push({pid:Number(i),ppid:Number(a),rssKb:Number(o),tty:s,etimeMs:h(c),command:l,sessionId:ee(l),generation:te(l)})}return n}function v(){if(process.platform===`darwin`)return!1;try{if(n(`/.dockerenv`))return!0;if(n(`/proc/1/cgroup`)){let e=r(`/proc/1/cgroup`,`utf8`);return/docker|kubepods|containerd/.test(e)}}catch{}return!1}const y={stream:{maxRetries:5,initialDelayMs:500,backoffFactor:2,maxDelayMs:6e4},networkDisconnect:{maxRetries:5,intervalMs:3e4},mcp:{maxReconnectAttempts:3,reconnectDelayMs:500,requestTimeoutMs:3e4},schedule:{retryBackoffMs:0},errorRetry:{maxAttempts:5,intervalMs:3e4},oauth:{refreshLockTimeoutMs:15e3}},b={stream:[`maxRetries`,`initialDelayMs`,`backoffFactor`,`maxDelayMs`],networkDisconnect:[`maxRetries`,`intervalMs`],mcp:[`maxReconnectAttempts`,`reconnectDelayMs`,`requestTimeoutMs`],schedule:[`retryBackoffMs`],errorRetry:[`maxAttempts`,`intervalMs`],oauth:[`refreshLockTimeoutMs`]},x=Object.keys(b),S=new Set([`mcp.maxReconnectAttempts`,`schedule.retryBackoffMs`]);function ne(e,t,n){return typeof n!=`number`||!Number.isFinite(n)||(S.has(`${e}.${t}`)?n<0:n<=0)?!1:e===`oauth`&&t===`refreshLockTimeoutMs`?n>=1e3:!0}function C(e,t){if(typeof e!=`object`||!e||Array.isArray(e)){t(`resilience config must be an object, got: ${typeof e}`);return}let n={},r=e;for(let e of Object.keys(r)){if(!x.includes(e)){t(`resilience config has unknown top-level key "${e}", ignored`);continue}let i=e,a=r[e];if(typeof a!=`object`||!a||Array.isArray(a)){t(`resilience.${i} must be an object, got: ${typeof a}, ignored`);continue}let o=a,s={};for(let e of Object.keys(o)){if(!b[i].includes(e)){t(`resilience.${i}.${e} is not a recognized field, ignored`);continue}let n=o[e];if(!ne(i,e,n)){t(`resilience.${i}.${e} has invalid value (${JSON.stringify(n)}), ignored`);continue}s[e]=n}Object.keys(s).length>0&&(n[i]=s)}return n}function w(e,t=e=>console.warn(`[resilience-config] ${e}`)){if(!e)return;let n;try{n=JSON.parse(e)}catch(e){t(`OTTO_RESILIENCE_CONFIG is not valid JSON, ignoring entirely: ${e instanceof Error?e.message:String(e)}`);return}return C(n,t)}function T(e,t){return t?{...e,...t}:e}function re(e,t,n=e=>console.warn(`[resilience-config] ${e}`)){let r=[w(process.env.OTTO_RESILIENCE_CONFIG,n),t?C(t,n):void 0,e?C(e,n):void 0],i={stream:{...y.stream},networkDisconnect:{...y.networkDisconnect},mcp:{...y.mcp},schedule:{...y.schedule},errorRetry:{...y.errorRetry},oauth:{...y.oauth}};for(let e of r)e&&(i={stream:T(i.stream,e.stream),networkDisconnect:T(i.networkDisconnect,e.networkDisconnect),mcp:T(i.mcp,e.mcp),schedule:T(i.schedule,e.schedule),errorRetry:T(i.errorRetry,e.errorRetry),oauth:T(i.oauth,e.oauth)});return i}const E=[];function D(e){let t=E.splice(0);for(let n of t)e(n)}function O(){return E.splice(0)}const k=(e,t)=>{if(e===void 0)return t;let n=parseInt(e,10);return isNaN(n)||n<=0?(E.push(`Invalid environment variable value: ${e}. Using default: ${t}`),t):n},A=(e,t)=>{if(e===void 0)return t;let n=parseFloat(e);return isNaN(n)||n<=0||n>1?(E.push(`Invalid fraction environment variable value: ${e}. Using default: ${t}`),t):n};function j(e){return o(e).replaceAll(s===`\\`?`\\`:s,`/`)}function M(e){let t=e;for(;;){if(n(a(t,`pnpm-workspace.yaml`)))return t;let e=i(t);if(e===t)return;t=e}}function N(){try{let e=j(`${j(new URL(`.`,import.meta.url).pathname)}/../package.json`);return JSON.parse(r(e,`utf-8`)).version||`unknown`}catch{return`unknown`}}const P=N(),F=`otto/${P}`,I=`.otto`,L=process.env.OTTO_CONFIG_FILENAME||`config.json`,R=`${I}/${L}`,z=j(process.env.OTTO_HOME||`${e()}/.otto`),B=`${z}/sessions`,V=`${B}/locks`,H=`${z}/daemon`,U=j(e()+`/.config/otto`),W=j(`${process.cwd()}/${I}`),G=`.claude`,K=j(`${e()}/${G}`);function q(){let e=process.env.OTTO_CLAUDE_COMPAT?.toLowerCase();return!(e===`0`||e===`false`||e===`no`||e===`off`)}function J(){if(!q())return!1;let e=process.env.OTTO_CLAUDE_COMPAT_SETTINGS?.toLowerCase();return e===`1`||e===`true`||e===`yes`||e===`on`}function Y(){if(!q())return!1;let e=process.env.OTTO_CLAUDE_COMPAT_HOOKS?.toLowerCase();return e===`1`||e===`true`||e===`yes`||e===`on`}const X=process.env.OTTO_LOG_FILE?j(process.env.OTTO_LOG_FILE):``,Z=k(process.env.OTTO_SERVICE_PORT,8417),ie=process.env.OTTO_SERVICE_URL??`http://127.0.0.1:${Z}`,ae=()=>(process.env.OTTO_NPM_SNIFF_REGISTRIES??``).split(`,`).map(e=>e.trim().replace(/\/+$/,``)).filter(e=>e.length>0),oe=process.env.OTTO_LOG_LEVEL||(process.env.NODE_ENV===`production`?`info`:`trace`),se=process.env.OTTO_OFFLINE===`1`||process.env.OTTO_OFFLINE?.toLowerCase()===`true`||process.env.OTTO_OFFLINE?.toLowerCase()===`yes`;function Q(){let e=process.env.OTTO_SHADOW?.toLowerCase();return e===`1`||e===`true`||e===`yes`||e===`on`}const ce=j(process.env.OTTO_AUTH_PATH||`${U}/auth.json`),$=j(process.env.OTTO_IDENTITY_PATH||`${z}/identity.json`);async function le(e){let t=e??$;try{let{readFile:e}=await import(`node:fs/promises`),n=await e(t,`utf-8`),r=JSON.parse(n);return typeof r.userId==`string`&&typeof r.token==`string`&&typeof r.serviceUrl==`string`&&typeof r.loginAt==`number`?r:null}catch{return null}}export{ce as AUTH_PATH,l as BUILD_ID,K as CLAUDE_HOME,G as CLAUDE_ROOT,y as DEFAULT_RESILIENCE_CONFIG,X as LOG_FILE,oe as LOG_LEVEL,L as OTTO_CONFIG_FILENAME,H as OTTO_DAEMON_DIR,z as OTTO_HOME,$ as OTTO_IDENTITY_PATH,R as OTTO_PROJECT_CONFIG_RELPATH,W as OTTO_PROJECT_DIR,I as OTTO_ROOT,B as OTTO_SESSIONS_DIR,V as OTTO_SESSION_LOCKS_DIR,F as OTTO_USER_AGENT,U as OTTO_USER_CONFIG_DIR,P as SDK_VERSION,Z as SERVICE_PORT,ie as SERVICE_URL,se as SETTING_OFFLINE_MODE_ENABLED,f as currentHostname,m as decodeLeaseToken,O as drainEnvWarnings,p as encodeLeaseToken,M as findWorkspaceRoot,D as flushEnvWarnings,q as isClaudeCompatEnabled,Y as isClaudeHooksCompatEnabled,J as isClaudeSettingsCompatEnabled,v as isContainerEnvironment,d as isProcessAlive,Q as isShadowModeEnabled,g as listOttoProcesses,le as loadUserIdentity,k as normalizeEnv,A as normalizeEnvFraction,j as normalizePath,h as parseEtimeMs,_ as parseOttoProcessTable,w as parseResilienceEnv,u as resolveConfigLayers,ae as resolveNpmSniffRegistries,re as resolveResilienceConfig,C as validateResilienceOverride};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x-otto/env",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.2",
4
4
  "files": [
5
5
  "dist"
6
6
  ],
@@ -25,4 +25,4 @@
25
25
  "typecheck": "tsc --noEmit",
26
26
  "clean": "rm -rf dist"
27
27
  }
28
- }
28
+ }