@lark-apaas/openclaw-extension-miaoda-coding 1.0.10 → 1.0.11

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/dist/index.mjs CHANGED
@@ -1,10 +1,11 @@
1
- import{createRequire as e}from"node:module";import{spawn as t}from"node:child_process";import n,{readFileSync as r}from"node:fs";import i,{dirname as a,join as o,resolve as s}from"node:path";import{appendFile as c,mkdir as l,readFile as u,readdir as d,rm as f,writeFile as p}from"node:fs/promises";import{fileURLToPath as m}from"node:url";var h=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),g=e(import.meta.url);function _(){return s(process.env.OPENCLAW_STATE_DIR||process.cwd())}function v(e){return o(e,`.agent`)}function y(e,t){return o(e,t)}function b(e){let t=typeof e.workspaceDir==`string`&&e.workspaceDir!==``,n=typeof e.agentDir==`string`&&e.agentDir!==``;if(!t||!n)throw Error(`resolveToolPaths: workspaceDir and agentDir are required in tool factory context`);return{workspaceDir:e.workspaceDir,agentDir:e.agentDir,agentId:e.agentId,sessionKey:e.sessionKey}}var x=h(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{DEFAULT_CLOCK_TOLERANCE_SEC:()=>f,DEFAULT_JWT_EXPIRE_TIME_MS:()=>d,FileTokenProvider:()=>w,HttpClient:()=>L,HttpError:()=>P,generateJWTToken:()=>m,parseJWTTokenWithVerify:()=>h,registerPlatformPlugin:()=>k,resolvePlatformBaseURL:()=>O}),t.exports=c(l);var u=g(`crypto`),d=1800*1e3,f=60,p={alg:`HS256`,typ:`JWT`};function m(e,t){let n=Math.floor(Date.now()/1e3),r={...e,iss:e.access_key,iat:n,nbf:n,exp:n+Math.floor(t.expireTimeMs/1e3),jti:(0,u.randomUUID)()},i=v(JSON.stringify(p)),a=v(JSON.stringify(r));return`${i}.${a}.${_(`${i}.${a}`,t.secretKey)}`}function h(e,t,n){let r=e.split(`.`);if(r.length!==3)throw Error(`invalid JWT token format`);let[i,a,o]=r;if(JSON.parse(y(i)).alg!==`HS256`)throw Error(`unsupported JWT alg`);let s=_(`${i}.${a}`,t.secretKey),c=Buffer.from(s),l=Buffer.from(o);if(c.length!==l.length||!(0,u.timingSafeEqual)(c,l))throw Error(`JWT signature verification failed`);let d=JSON.parse(y(a));if(!n?.skipExpiration){let e=n?.clockTolerance??f,t=Math.floor(Date.now()/1e3);if(d.exp!==void 0&&d.exp+e<t)throw Error(`JWT token expired at ${new Date(d.exp*1e3).toISOString()}`);if(d.nbf!==void 0&&d.nbf-e>t)throw Error(`JWT token not yet valid, will be valid at ${new Date(d.nbf*1e3).toISOString()}`);if(d.iat!==void 0&&d.iat-e>t)throw Error(`JWT token issued in the future at ${new Date(d.iat*1e3).toISOString()}`)}return d}function _(e,t){return(0,u.createHmac)(`sha256`,t).update(e).digest(`base64`).replace(/=/g,``).replace(/\+/g,`-`).replace(/\//g,`_`)}function v(e){return Buffer.from(e).toString(`base64`).replace(/=/g,``).replace(/\+/g,`-`).replace(/\//g,`_`)}function y(e){let t=(4-(e.length%4||4))%4,n=`${e}${`=`.repeat(t)}`.replace(/-/g,`+`).replace(/_/g,`/`);return Buffer.from(n,`base64`).toString(`utf8`)}var b=class{cache=new Map;config;constructor(e){this.config={refreshBeforeMs:300*1e3,...e}}getToken(e){let t=this.getCacheKey(e),n=this.cache.get(t),r=Date.now();if(n&&n.expiresAtMs-r>this.config.refreshBeforeMs)return n.token;let i=m(e,this.config),a=r+this.config.expireTimeMs;return this.cache.set(t,{token:i,expiresAtMs:a}),i}clearCache(){this.cache.clear()}clearCacheFor(e){let t=this.getCacheKey(e);this.cache.delete(t)}getCacheKey(e){return[e.access_key,e.tenant_id?.toString()||``,e.user_id||``,e.app_id||``,e.app_env||``,e.sandbox_id||``].join(`:`)}getCacheStats(){let e=Date.now(),t=0,n=0;for(let r of this.cache.values())r.expiresAtMs>e?t++:n++;return{total:this.cache.size,valid:t,expired:n}}},x=g(`fs`),S=300*1e3,C=[`/home/gem/workspace/.force/openclaw/miaoda-provider-key`],w=class{cache=new Map;paths;refreshBeforeMs;constructor(e){this.paths=e?.paths??C,this.refreshBeforeMs=e?.refreshBeforeMs??S}getToken(){let e=Date.now();for(let t of this.paths){let n=this.cache.get(t);if(n&&n.expiresAtMs-e>this.refreshBeforeMs)return{token:n.token,accessKey:n.accessKey};let r=this.readAndParse(t);if(r&&(this.cache.set(t,r),r.expiresAtMs>e))return{token:r.token,accessKey:r.accessKey};if(n&&n.expiresAtMs>e)return{token:n.token,accessKey:n.accessKey}}return null}clearCache(){this.cache.clear()}readAndParse(e){try{let t=(0,x.readFileSync)(e,`utf-8`).trim();if(!t)return null;let n=this.decodePayload(t);return n?.exp?{token:t,accessKey:n.access_key??``,expiresAtMs:n.exp*1e3}:null}catch{return null}}decodePayload(e){try{let t=e.split(`.`);if(t.length!==3)return null;let n=(4-(t[1].length%4||4))%4,r=`${t[1]}${`=`.repeat(n)}`.replace(/-/g,`+`).replace(/_/g,`/`);return JSON.parse(Buffer.from(r,`base64`).toString(`utf8`))}catch{return null}}},T=`FORCE_AUTHN_INNERAPI_DOMAIN`,E=`FORCE_AUTHN_ACCESS_KEY`,D=`FORCE_AUTHN_ACCESS_SECRET`;function O(e){if(!e?.enabled)return e?.baseURL;if(e.baseURL)return e.baseURL;let t=e.domainEnv||T,n=process.env[t];if(!n)throw Error(`\u5E73\u53F0\u6A21\u5F0F\u9700\u8981\u57FA\u7840\u57DF\u540D\uFF0C\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${t}`);return n}function k(e,t){if(!t.enabled)return;A(`platform.defaultClaims`,t.defaultClaims);let n;t.tokenProvider?.type===`file`&&(n=new w({paths:t.tokenProvider.paths,refreshBeforeMs:t.refreshBeforeMs}));let r=t.accessKeyEnv||E,i=t.secretKeyEnv||D,a=t.accessKey??(process.env[r]||``),o=new b({accessKey:a,secretKey:t.secretKey??(process.env[i]||``),expireTimeMs:t.expireTimeMs??d,refreshBeforeMs:t.refreshBeforeMs});return e.request.use(e=>{if(n){let t=n.getToken();if(t){let n={...e.headers,Authorization:`Bearer ${t.token}`,"x-api-key":t.accessKey};return{...e,headers:n}}}A(`request.platformAuth.customClaims`,e.platformAuth?.customClaims);let r={...t.defaultClaims||{},...e.platformAuth?.customClaims||{},access_key:a},i=o.getToken(r),s={...e.headers,Authorization:`Bearer ${i}`,"x-api-key":a};return{...e,headers:s}}),o}function A(e,t){if(t&&Object.prototype.hasOwnProperty.call(t,`access_key`))throw Error(`${e} \u4E0D\u5141\u8BB8\u8BBE\u7F6E access_key`)}var j=class{interceptors=[];use(e,t){return this.interceptors.push({onFulfilled:e,onRejected:t}),this.interceptors.length-1}eject(e){this.interceptors[e]&&(this.interceptors[e]=null)}clear(){this.interceptors=[]}forEach(e){this.interceptors.forEach(t=>{t!==null&&e(t)})}};function M(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function N(e){if(!e)return{};if(e instanceof Headers){let t={};return e.forEach((e,n)=>{t[n]=e}),t}if(Array.isArray(e))return e.reduce((e,[t,n])=>(e[t]=n,e),{});let t={};return Object.entries(e).forEach(([e,n])=>{t[e]=Array.isArray(n)?n.join(`,`):n}),t}var P=class e extends Error{isHttpError=!0;response;config;constructor(t,n,r){super(r||`Request failed with status ${t?.status||`unknown`}`),this.name=`HttpError`,this.response=t,this.config=F(n),Object.setPrototypeOf(this,e.prototype)}};function F(e){let{headers:t,...n}=e;return{...n,headers:I(t)}}function I(e){if(!e)return;let t=N(e),n={},r=[`authorization`,`x-api-key`,`cookie`,`x-secret`];for(let[e,i]of Object.entries(t)){let t=e.toLowerCase();r.includes(t)?n[e]=`[REDACTED]`:n[e]=i}return n}var L=class{defaultConfig;securityConfig;interceptors={request:new j,response:new j};constructor(e){let{platform:t,security:n,...r}=e||{};this.defaultConfig={timeout:5e3,...r};let i=n?.strictMode??!1;this.securityConfig={allowedProtocols:i?[`http:`,`https:`]:null,maxResponseSize:i?50*1024*1024:0,strictMode:i,...n},t?.enabled&&(this.defaultConfig.baseURL||(this.defaultConfig.baseURL=O(t)),k(this.interceptors,t))}async get(e,t){return this.request({...t,url:e,method:`GET`})}async post(e,t,n){let r={...n?.headers},i=t;return M(t)&&(i=JSON.stringify(t),Object.keys(r).some(e=>e.toLowerCase()===`content-type`)||(r[`Content-Type`]=`application/json`)),this.request({...n,url:e,method:`POST`,body:i,headers:r})}async put(e,t,n){let r={...n?.headers},i=t;return M(t)&&(i=JSON.stringify(t),Object.keys(r).some(e=>e.toLowerCase()===`content-type`)||(r[`Content-Type`]=`application/json`)),this.request({...n,url:e,method:`PUT`,body:i,headers:r})}async delete(e,t){return this.request({...t,url:e,method:`DELETE`})}async patch(e,t,n){let r={...n?.headers},i=t;return M(t)&&(i=JSON.stringify(t),Object.keys(r).some(e=>e.toLowerCase()===`content-type`)||(r[`Content-Type`]=`application/json`)),this.request({...n,url:e,method:`PATCH`,body:i,headers:r})}async request(e){let t=N(this.defaultConfig.headers),n=N(e.headers),r={};for(let e in t)r[e.toLowerCase()]={key:e,value:t[e]};for(let e in n)r[e.toLowerCase()]={key:e,value:n[e]};let i={};for(let e in r){let{key:t,value:n}=r[e];i[t]=n}let a={...this.defaultConfig,...e,headers:i};try{a=await this.runRequestInterceptors(a)}catch(e){return Promise.reject(e)}a.headers=N(a.headers);let o=this.buildUrl(a.url,a.params),s=a.timeout||this.defaultConfig.timeout||5e3,c=new AbortController,l=setTimeout(()=>c.abort(),s),u=a.signal;u&&(u.aborted?(clearTimeout(l),c.abort()):u.addEventListener(`abort`,()=>c.abort(),{once:!0}));let{platformAuth:d,params:f,timeout:p,baseURL:m,url:h,...g}=a;try{let e=await fetch(o,{...g,signal:c.signal});if(clearTimeout(l),this.securityConfig.maxResponseSize>0){let t=e.headers.get(`content-length`);if(t){let e=parseInt(t,10);if(e>this.securityConfig.maxResponseSize)throw Error(`Response size ${e} bytes exceeds limit of ${this.securityConfig.maxResponseSize} bytes`)}}if(!e.ok){let t=new P(e,a);return this.runResponseInterceptors(Promise.reject(t))}return this.runResponseInterceptors(Promise.resolve(e))}catch(e){clearTimeout(l);let t=e instanceof P?e:new P(void 0,a,e.name===`AbortError`?`Request aborted`:e.message);return this.runResponseInterceptors(Promise.reject(t))}}runRequestInterceptors(e){let t=Promise.resolve(e),n=[];this.interceptors.request.forEach(e=>{n.push(e)});for(let e of n)t=t.then(e.onFulfilled,e.onRejected);return t}async runResponseInterceptors(e){let t=e,n=[];this.interceptors.response.forEach(e=>{n.push(e)});for(let e of n)t=t.then(e.onFulfilled,e.onRejected);return t}buildUrl(e,t){let n=this.defaultConfig.baseURL,r;r=!n||/^https?:\/\//i.test(e)?e:n.replace(/\/+$/,``)+`/`+e.replace(/^\/+/,``);let i=new URL(r),{allowedProtocols:a}=this.securityConfig;if(a&&!a.includes(i.protocol))throw Error(`Protocol ${i.protocol} is not allowed. Allowed: ${a.join(`, `)}`);if(t)for(let[e,n]of Object.entries(t))n!=null&&i.searchParams.set(e,String(n));return i.href}}}))();let S;function C(){if(S)return S;try{let e=s(a(m(import.meta.url)),`..`,`package.json`);S=JSON.parse(r(e,`utf-8`))}catch(e){console.warn(`[miaoda-coding] failed to read package.json: ${e instanceof Error?e.message:e}`),S={}}return S}function w(){let e=process.env.FORCE_AUTHN_INNERAPI_DOMAIN;if(!e)throw Error(`studio-client: 缺少环境变量,需要: FORCE_AUTHN_INNERAPI_DOMAIN`);return{apiUrl:e}}const T=new Set([`k_st_ec_400002683`]);var E=class extends Error{userFacing=!0;constructor(e,t){super(e),this.statusCode=t,this.name=`UserFacingError`}};function D(e){return!!e&&typeof e==`object`&&!Array.isArray(e)&&Object.values(e).every(e=>typeof e==`string`)}function O(...e){for(let t of e)if(typeof t==`string`&&t)return t}async function*k(e){let t=new TextDecoder,n=``,r;for await(let i of e){n+=t.decode(i,{stream:!0});let e=n.split(`
1
+ import{createRequire as e}from"node:module";import{execFile as t,spawn as n}from"node:child_process";import r,{readFileSync as i}from"node:fs";import a,{dirname as o,join as s,resolve as c}from"node:path";import{appendFile as l,mkdir as u,readFile as d,readdir as f,rm as p,writeFile as m}from"node:fs/promises";import{fileURLToPath as h}from"node:url";var g=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),_=e(import.meta.url);function v(){return c(process.env.OPENCLAW_STATE_DIR||process.cwd())}function y(e){return s(e,`.agent`)}function b(e,t){return s(e,t)}function x(e){let t=typeof e.workspaceDir==`string`&&e.workspaceDir!==``,n=typeof e.agentDir==`string`&&e.agentDir!==``;if(!t||!n)throw Error(`resolveToolPaths: workspaceDir and agentDir are required in tool factory context`);return{workspaceDir:e.workspaceDir,agentDir:e.agentDir,agentId:e.agentId,sessionKey:e.sessionKey}}var S=g(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{DEFAULT_CLOCK_TOLERANCE_SEC:()=>f,DEFAULT_JWT_EXPIRE_TIME_MS:()=>d,FileTokenProvider:()=>w,HttpClient:()=>L,HttpError:()=>P,generateJWTToken:()=>m,parseJWTTokenWithVerify:()=>h,registerPlatformPlugin:()=>k,resolvePlatformBaseURL:()=>O}),t.exports=c(l);var u=_(`crypto`),d=1800*1e3,f=60,p={alg:`HS256`,typ:`JWT`};function m(e,t){let n=Math.floor(Date.now()/1e3),r={...e,iss:e.access_key,iat:n,nbf:n,exp:n+Math.floor(t.expireTimeMs/1e3),jti:(0,u.randomUUID)()},i=v(JSON.stringify(p)),a=v(JSON.stringify(r));return`${i}.${a}.${g(`${i}.${a}`,t.secretKey)}`}function h(e,t,n){let r=e.split(`.`);if(r.length!==3)throw Error(`invalid JWT token format`);let[i,a,o]=r;if(JSON.parse(y(i)).alg!==`HS256`)throw Error(`unsupported JWT alg`);let s=g(`${i}.${a}`,t.secretKey),c=Buffer.from(s),l=Buffer.from(o);if(c.length!==l.length||!(0,u.timingSafeEqual)(c,l))throw Error(`JWT signature verification failed`);let d=JSON.parse(y(a));if(!n?.skipExpiration){let e=n?.clockTolerance??f,t=Math.floor(Date.now()/1e3);if(d.exp!==void 0&&d.exp+e<t)throw Error(`JWT token expired at ${new Date(d.exp*1e3).toISOString()}`);if(d.nbf!==void 0&&d.nbf-e>t)throw Error(`JWT token not yet valid, will be valid at ${new Date(d.nbf*1e3).toISOString()}`);if(d.iat!==void 0&&d.iat-e>t)throw Error(`JWT token issued in the future at ${new Date(d.iat*1e3).toISOString()}`)}return d}function g(e,t){return(0,u.createHmac)(`sha256`,t).update(e).digest(`base64`).replace(/=/g,``).replace(/\+/g,`-`).replace(/\//g,`_`)}function v(e){return Buffer.from(e).toString(`base64`).replace(/=/g,``).replace(/\+/g,`-`).replace(/\//g,`_`)}function y(e){let t=(4-(e.length%4||4))%4,n=`${e}${`=`.repeat(t)}`.replace(/-/g,`+`).replace(/_/g,`/`);return Buffer.from(n,`base64`).toString(`utf8`)}var b=class{cache=new Map;config;constructor(e){this.config={refreshBeforeMs:300*1e3,...e}}getToken(e){let t=this.getCacheKey(e),n=this.cache.get(t),r=Date.now();if(n&&n.expiresAtMs-r>this.config.refreshBeforeMs)return n.token;let i=m(e,this.config),a=r+this.config.expireTimeMs;return this.cache.set(t,{token:i,expiresAtMs:a}),i}clearCache(){this.cache.clear()}clearCacheFor(e){let t=this.getCacheKey(e);this.cache.delete(t)}getCacheKey(e){return[e.access_key,e.tenant_id?.toString()||``,e.user_id||``,e.app_id||``,e.app_env||``,e.sandbox_id||``].join(`:`)}getCacheStats(){let e=Date.now(),t=0,n=0;for(let r of this.cache.values())r.expiresAtMs>e?t++:n++;return{total:this.cache.size,valid:t,expired:n}}},x=_(`fs`),S=300*1e3,C=[`/home/gem/workspace/.force/openclaw/miaoda-provider-key`],w=class{cache=new Map;paths;refreshBeforeMs;constructor(e){this.paths=e?.paths??C,this.refreshBeforeMs=e?.refreshBeforeMs??S}getToken(){let e=Date.now();for(let t of this.paths){let n=this.cache.get(t);if(n&&n.expiresAtMs-e>this.refreshBeforeMs)return{token:n.token,accessKey:n.accessKey};let r=this.readAndParse(t);if(r&&(this.cache.set(t,r),r.expiresAtMs>e))return{token:r.token,accessKey:r.accessKey};if(n&&n.expiresAtMs>e)return{token:n.token,accessKey:n.accessKey}}return null}clearCache(){this.cache.clear()}readAndParse(e){try{let t=(0,x.readFileSync)(e,`utf-8`).trim();if(!t)return null;let n=this.decodePayload(t);return n?.exp?{token:t,accessKey:n.access_key??``,expiresAtMs:n.exp*1e3}:null}catch{return null}}decodePayload(e){try{let t=e.split(`.`);if(t.length!==3)return null;let n=(4-(t[1].length%4||4))%4,r=`${t[1]}${`=`.repeat(n)}`.replace(/-/g,`+`).replace(/_/g,`/`);return JSON.parse(Buffer.from(r,`base64`).toString(`utf8`))}catch{return null}}},T=`FORCE_AUTHN_INNERAPI_DOMAIN`,E=`FORCE_AUTHN_ACCESS_KEY`,D=`FORCE_AUTHN_ACCESS_SECRET`;function O(e){if(!e?.enabled)return e?.baseURL;if(e.baseURL)return e.baseURL;let t=e.domainEnv||T,n=process.env[t];if(!n)throw Error(`\u5E73\u53F0\u6A21\u5F0F\u9700\u8981\u57FA\u7840\u57DF\u540D\uFF0C\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF ${t}`);return n}function k(e,t){if(!t.enabled)return;A(`platform.defaultClaims`,t.defaultClaims);let n;t.tokenProvider?.type===`file`&&(n=new w({paths:t.tokenProvider.paths,refreshBeforeMs:t.refreshBeforeMs}));let r=t.accessKeyEnv||E,i=t.secretKeyEnv||D,a=t.accessKey??(process.env[r]||``),o=new b({accessKey:a,secretKey:t.secretKey??(process.env[i]||``),expireTimeMs:t.expireTimeMs??d,refreshBeforeMs:t.refreshBeforeMs});return e.request.use(e=>{if(n){let t=n.getToken();if(t){let n={...e.headers,Authorization:`Bearer ${t.token}`,"x-api-key":t.accessKey};return{...e,headers:n}}}A(`request.platformAuth.customClaims`,e.platformAuth?.customClaims);let r={...t.defaultClaims||{},...e.platformAuth?.customClaims||{},access_key:a},i=o.getToken(r),s={...e.headers,Authorization:`Bearer ${i}`,"x-api-key":a};return{...e,headers:s}}),o}function A(e,t){if(t&&Object.prototype.hasOwnProperty.call(t,`access_key`))throw Error(`${e} \u4E0D\u5141\u8BB8\u8BBE\u7F6E access_key`)}var j=class{interceptors=[];use(e,t){return this.interceptors.push({onFulfilled:e,onRejected:t}),this.interceptors.length-1}eject(e){this.interceptors[e]&&(this.interceptors[e]=null)}clear(){this.interceptors=[]}forEach(e){this.interceptors.forEach(t=>{t!==null&&e(t)})}};function M(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function N(e){if(!e)return{};if(e instanceof Headers){let t={};return e.forEach((e,n)=>{t[n]=e}),t}if(Array.isArray(e))return e.reduce((e,[t,n])=>(e[t]=n,e),{});let t={};return Object.entries(e).forEach(([e,n])=>{t[e]=Array.isArray(n)?n.join(`,`):n}),t}var P=class e extends Error{isHttpError=!0;response;config;constructor(t,n,r){super(r||`Request failed with status ${t?.status||`unknown`}`),this.name=`HttpError`,this.response=t,this.config=F(n),Object.setPrototypeOf(this,e.prototype)}};function F(e){let{headers:t,...n}=e;return{...n,headers:I(t)}}function I(e){if(!e)return;let t=N(e),n={},r=[`authorization`,`x-api-key`,`cookie`,`x-secret`];for(let[e,i]of Object.entries(t)){let t=e.toLowerCase();r.includes(t)?n[e]=`[REDACTED]`:n[e]=i}return n}var L=class{defaultConfig;securityConfig;interceptors={request:new j,response:new j};constructor(e){let{platform:t,security:n,...r}=e||{};this.defaultConfig={timeout:5e3,...r};let i=n?.strictMode??!1;this.securityConfig={allowedProtocols:i?[`http:`,`https:`]:null,maxResponseSize:i?50*1024*1024:0,strictMode:i,...n},t?.enabled&&(this.defaultConfig.baseURL||(this.defaultConfig.baseURL=O(t)),k(this.interceptors,t))}async get(e,t){return this.request({...t,url:e,method:`GET`})}async post(e,t,n){let r={...n?.headers},i=t;return M(t)&&(i=JSON.stringify(t),Object.keys(r).some(e=>e.toLowerCase()===`content-type`)||(r[`Content-Type`]=`application/json`)),this.request({...n,url:e,method:`POST`,body:i,headers:r})}async put(e,t,n){let r={...n?.headers},i=t;return M(t)&&(i=JSON.stringify(t),Object.keys(r).some(e=>e.toLowerCase()===`content-type`)||(r[`Content-Type`]=`application/json`)),this.request({...n,url:e,method:`PUT`,body:i,headers:r})}async delete(e,t){return this.request({...t,url:e,method:`DELETE`})}async patch(e,t,n){let r={...n?.headers},i=t;return M(t)&&(i=JSON.stringify(t),Object.keys(r).some(e=>e.toLowerCase()===`content-type`)||(r[`Content-Type`]=`application/json`)),this.request({...n,url:e,method:`PATCH`,body:i,headers:r})}async request(e){let t=N(this.defaultConfig.headers),n=N(e.headers),r={};for(let e in t)r[e.toLowerCase()]={key:e,value:t[e]};for(let e in n)r[e.toLowerCase()]={key:e,value:n[e]};let i={};for(let e in r){let{key:t,value:n}=r[e];i[t]=n}let a={...this.defaultConfig,...e,headers:i};try{a=await this.runRequestInterceptors(a)}catch(e){return Promise.reject(e)}a.headers=N(a.headers);let o=this.buildUrl(a.url,a.params),s=a.timeout||this.defaultConfig.timeout||5e3,c=new AbortController,l=setTimeout(()=>c.abort(),s),u=a.signal;u&&(u.aborted?(clearTimeout(l),c.abort()):u.addEventListener(`abort`,()=>c.abort(),{once:!0}));let{platformAuth:d,params:f,timeout:p,baseURL:m,url:h,...g}=a;try{let e=await fetch(o,{...g,signal:c.signal});if(clearTimeout(l),this.securityConfig.maxResponseSize>0){let t=e.headers.get(`content-length`);if(t){let e=parseInt(t,10);if(e>this.securityConfig.maxResponseSize)throw Error(`Response size ${e} bytes exceeds limit of ${this.securityConfig.maxResponseSize} bytes`)}}if(!e.ok){let t=new P(e,a);return this.runResponseInterceptors(Promise.reject(t))}return this.runResponseInterceptors(Promise.resolve(e))}catch(e){clearTimeout(l);let t=e instanceof P?e:new P(void 0,a,e.name===`AbortError`?`Request aborted`:e.message);return this.runResponseInterceptors(Promise.reject(t))}}runRequestInterceptors(e){let t=Promise.resolve(e),n=[];this.interceptors.request.forEach(e=>{n.push(e)});for(let e of n)t=t.then(e.onFulfilled,e.onRejected);return t}async runResponseInterceptors(e){let t=e,n=[];this.interceptors.response.forEach(e=>{n.push(e)});for(let e of n)t=t.then(e.onFulfilled,e.onRejected);return t}buildUrl(e,t){let n=this.defaultConfig.baseURL,r;r=!n||/^https?:\/\//i.test(e)?e:n.replace(/\/+$/,``)+`/`+e.replace(/^\/+/,``);let i=new URL(r),{allowedProtocols:a}=this.securityConfig;if(a&&!a.includes(i.protocol))throw Error(`Protocol ${i.protocol} is not allowed. Allowed: ${a.join(`, `)}`);if(t)for(let[e,n]of Object.entries(t))n!=null&&i.searchParams.set(e,String(n));return i.href}}}))();let C;function w(){if(C)return C;try{let e=c(o(h(import.meta.url)),`..`,`package.json`);C=JSON.parse(i(e,`utf-8`))}catch(e){console.warn(`[miaoda-coding] failed to read package.json: ${e instanceof Error?e.message:e}`),C={}}return C}function T(){let e=process.env.FORCE_AUTHN_INNERAPI_DOMAIN;if(!e)throw Error(`studio-client: 缺少环境变量,需要: FORCE_AUTHN_INNERAPI_DOMAIN`);return{apiUrl:e}}const E=new Set([`k_st_ec_400002683`]);var D=class extends Error{userFacing=!0;constructor(e,t,n){super(e),this.statusCode=t,this.logId=n,this.name=`UserFacingError`}};function O(e){return!!e&&typeof e==`object`&&!Array.isArray(e)&&Object.values(e).every(e=>typeof e==`string`)}function k(...e){for(let t of e)if(typeof t==`string`&&t)return t}async function*A(e){let t=new TextDecoder,n=``,r;for await(let i of e){n+=t.decode(i,{stream:!0});let e=n.split(`
2
2
 
3
3
  `);n=e.pop();for(let t of e){let e=t.split(`
4
- `),n=e.find(e=>e.startsWith(`id:`));n&&(r=n.slice(3).trim());let i=e.find(e=>e.startsWith(`data:`));if(!i)continue;let a=i.slice(5).trim();if(a){if(a===`[DONE]`){yield{data:null,id:r,done:!0};return}try{yield{data:JSON.parse(a),id:r,done:!1}}catch{}}}}}var A=class{#e;#t;constructor(){this.#t=w();let e=C().headers;this.#e=new x.HttpClient({baseURL:this.#t.apiUrl,timeout:9e5,...process.env.FORCE_FRAMEWORK_ENVIRONMENT!==`online`&&D(e)?{headers:e}:{},platform:{enabled:!0,tokenProvider:{type:`file`}}})}async createSubApp(e={}){let t=`${this.#t.apiUrl}/api/v1/studio/innerapi/openclaw/sub_app/create`,n={};e.message&&(n.message=e.message),e.agentId&&(n.agentId=e.agentId);let r;try{r=await this.#e.post(t,n,{headers:{"Content-Type":`application/json`}})}catch(e){if(e instanceof x.HttpError&&e.response){let t=e.response.headers.get(`x-tt-logid`)??``;throw Error(`createSubApp 请求失败 (logID: ${t}): ${e.response.status}`)}throw e}let i=r.headers?.get?.(`x-tt-logid`)??``,a=await r.json();if(a.BaseResp?.StatusCode!==0&&a.BaseResp?.StatusMessage)throw Error(`createSubApp 业务错误 (logID: ${i}): ${a.BaseResp.StatusMessage}`);if(a.error_msg&&a.status_code!==`0`)throw T.has(a.status_code)?new E(a.error_msg,a.status_code):Error(`createSubApp 服务错误 (logID: ${i}): ${a.error_msg}`);let o=O(a.appID,a.appId,a.app_id,a.data?.appID,a.data?.appId,a.data?.app_id),s=O(a.conversationID,a.conversationId,a.conversation_id,a.data?.conversationID,a.data?.conversationId,a.data?.conversation_id);if(!o){let e=Object.keys(a).join(`,`)||`(none)`,t=a.data&&typeof a.data==`object`&&Object.keys(a.data).join(`,`)||`(none)`;throw Error(`createSubApp 返回异常 (logID: ${i}): 缺少 appID,topLevelKeys=${e} dataKeys=${t}`)}return{appID:o,conversationID:s,logId:i}}async*streamChat(e){let t=`${this.#t.apiUrl}/api/v1/studio/innerapi/openclaw/sub_app/${e.appID}/stream_chat`,n={appID:e.appID,message:e.message};e.workDir&&(n.workDir=e.workDir);let r={"Content-Type":`application/json`,Accept:`text/event-stream`};e.lastEventID&&(r[`Last-Event-ID`]=e.lastEventID);let i;try{i=await this.#e.post(t,n,{headers:r})}catch(e){if(e instanceof x.HttpError&&e.response){let t=e.response.headers.get(`x-tt-logid`)??``,n=Error(`streamChat 请求失败 (logID: ${t}): ${e.response.status}`);throw n.logId=t,n}let t=e.cause?` cause: ${e.cause.message||e.cause}`:``,n=e.code?` code: ${e.code}`:``;throw Error(`streamChat 网络错误: ${e.message}${n}${t}`)}yield{__meta:!0,logId:i.headers?.get?.(`x-tt-logid`)??``},yield*k(i.body)}};const j=`.spark`,M=`meta.json`;function N(e){return o(e,j,M)}async function P(e){try{return JSON.parse(await u(N(e),`utf8`))}catch{return null}}async function F(e){for(let t of[o(e,`..`,`..`,j,M),o(e,`..`,j,M)])try{return JSON.parse(await u(t,`utf8`))}catch{}return null}async function I(e,t){await l(o(e,j),{recursive:!0});let n=await P(e),r={...n??{},...t};if(r.features||=[],!n){let t=await F(e);if(t)for(let[e,n]of Object.entries(t))e in r||(r[e]=n)}await p(N(e),JSON.stringify(r,null,2),`utf8`)}function L(e){let t=e;return t?.data?.message??t?.Data?.Message??null}async function ee(e){let t=e.maxReconnect??2,n=e.maxTotalReconnect??10,r=e.reconnectDelayMs??1500,i,a=0,o=0,s=0,c,l;for(;;){let u=!1,d=!1,f=!1,p=i;try{for await(let t of e.createStream(p)){if(e.signal?.aborted)return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:`cancelled`};let n=t;if(n?.__meta){l=n.logId||l;continue}let r=n;if(r.id&&(i=r.id),r.id&&p&&r.id===p)continue;if(r.done)break;s++,a>0&&(a=0);let d=L(r.data);if(d?.type===`metadata`&&d?.status!==`in_progress`&&(u=!0),d?.role===`assistant`&&d?.type===`message`&&d?.status===`completed`&&typeof d?.content==`string`&&d.content&&(c=d.content),e.onEvent&&e.onEvent(r,{lastEventId:i,reconnectCount:o})===!1){f=!0;break}}}catch(e){if(!u){let t=e instanceof Error?e.message:String(e);if(/\b4[0-9]{2}\b/.test(t)||t.includes(`业务错误`))return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:t};d=!0}}if(f)break;if(s===0&&!u&&!d)return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:`stream returned no events`};if(u)break;if(d||i){if(a++,o++,a<=t&&o<=n&&i){if(e.onReconnect?.(o,i),await new Promise(t=>{let n=setTimeout(t,r);e.signal&&e.signal.addEventListener(`abort`,()=>{clearTimeout(n),t()},{once:!0})}),e.signal?.aborted)return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:`cancelled`};continue}let u=`stream disconnected, reconnect exhausted`;return e.onFailed?.(u),{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:u}}break}return e.onComplete?.({totalEvents:s,reconnectCount:o,lastEventId:i}),{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!0}}let R=``,z=0;function te(e){try{let t=ne(e);if(!t)return null;let n=t.replace(/\s*\(.*\)$/,``).replace(/\s*:.*$/,``),r=Date.now();return n===R&&r-z<3e4?null:(R=n,z=r,t)}catch{return null}}function ne(e){let t=e.role,n=e.tool_calls,r=e.content;if(e.finish_reason,t===`assistant`&&n?.length)for(let e of n){let t=e?.function?.name??e?.name,n={};try{let t=e?.function?.arguments??e?.arguments;n=typeof t==`string`?JSON.parse(t):t??{}}catch{}if(t===`bash`){let e=String(n.command??``);if(e.includes(`init`))return`初始化项目`;if(e.includes(`db sql`))return`数据库操作`;if(e.includes(`deploy`))return`部署中`}}return t===`tool`&&typeof r==`string`&&(r.startsWith(`执行 run_shell_command 工具失败`)||r.startsWith(`command exitcode:`))?`失败: ${r.slice(0,200)+(r.length>200?`...`:``)}`:null}const B=`.spark`,re=new Intl.DateTimeFormat(`en-CA`,{timeZone:`Asia/Shanghai`,year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1,timeZoneName:`longOffset`});function V(e){return o(e,B,`progress.txt`)}function H(){let e=Object.fromEntries(re.formatToParts(new Date).filter(e=>e.type!==`literal`).map(e=>[e.type,e.value])),t=e.timeZoneName===`GMT`?`+00:00`:e.timeZoneName.replace(/^GMT/,``);return`${e.year}-${e.month}-${e.day}T${e.hour}:${e.minute}:${e.second}${t}`}function ie(e){let t=[],n=``,r=``,i=``,a=e.includes(`## 已完成`)||e.includes(`## 当前任务`),o=!1,s=!a;for(let c of e.split(`
5
- `)){let e=c.trim();if(a){if(e===`## 已完成`){o=!0,s=!1;continue}if(e.startsWith(`## 当前任务`)){s=!0,o=!1;continue}if(e===``)continue}if(o&&e)t.push(e);else if(s&&e){let t=e.match(/^\[([^\]]+)\]\s*(.*)/);if(t){let e=t[2];i=t[1],r=e,!a&&e.startsWith(`需求: `)&&(n=e.slice(4))}else n||=e}}return{history:t,taskDesc:n,lastStatus:r,lastTimestamp:i}}async function ae(e,t){await l(o(e,B),{recursive:!0});let n=V(e),r=[];try{let e=await u(n,`utf8`);if(e.trim()){let t=ie(e);if(r.push(...t.history),t.taskDesc){let e=t.lastTimestamp||H(),n;n=t.lastStatus.startsWith(`失败:`)?t.lastStatus:t.lastStatus===`处理完成`?`成功`:t.lastStatus||`未知状态`,r.push(`[${e}] ${t.taskDesc} — ${n}`)}}}catch{}await p(n,`## 已完成\n\n${r.length?r.join(`
4
+ `),n=e.find(e=>e.startsWith(`id:`));n&&(r=n.slice(3).trim());let i=e.find(e=>e.startsWith(`data:`));if(!i)continue;let a=i.slice(5).trim();if(a){if(a===`[DONE]`){yield{data:null,id:r,done:!0};return}try{yield{data:JSON.parse(a),id:r,done:!1}}catch{}}}}}var j=class{#e;#t;constructor(){this.#t=T();let e=w().headers;this.#e=new S.HttpClient({baseURL:this.#t.apiUrl,timeout:9e5,...process.env.FORCE_FRAMEWORK_ENVIRONMENT!==`online`&&O(e)?{headers:e}:{},platform:{enabled:!0,tokenProvider:{type:`file`}}})}async createSubApp(e={}){let t=`${this.#t.apiUrl}/api/v1/studio/innerapi/openclaw/sub_app/create`,n={};e.message&&(n.message=e.message),e.agentId&&(n.agentId=e.agentId);let r;try{r=await this.#e.post(t,n,{headers:{"Content-Type":`application/json`}})}catch(e){if(e instanceof S.HttpError&&e.response){let t=e.response.headers.get(`x-tt-logid`)??``;throw Error(`createSubApp 请求失败 (logID: ${t}): ${e.response.status}`)}throw e}let i=r.headers?.get?.(`x-tt-logid`)??``,a=await r.json();if(a.BaseResp?.StatusCode!==0&&a.BaseResp?.StatusMessage)throw Error(`createSubApp 业务错误 (logID: ${i}): ${a.BaseResp.StatusMessage}`);if(a.error_msg&&a.status_code!==`0`)throw E.has(a.status_code)?new D(a.error_msg,a.status_code,i):Error(`createSubApp 服务错误 (logID: ${i}): ${a.error_msg}`);let o=k(a.appID,a.appId,a.app_id,a.data?.appID,a.data?.appId,a.data?.app_id),s=k(a.conversationID,a.conversationId,a.conversation_id,a.data?.conversationID,a.data?.conversationId,a.data?.conversation_id);if(!o){let e=Object.keys(a).join(`,`)||`(none)`,t=a.data&&typeof a.data==`object`&&Object.keys(a.data).join(`,`)||`(none)`;throw Error(`createSubApp 返回异常 (logID: ${i}): 缺少 appID,topLevelKeys=${e} dataKeys=${t}`)}return{appID:o,conversationID:s,logId:i}}async*streamChat(e){let t=`${this.#t.apiUrl}/api/v1/studio/innerapi/openclaw/sub_app/${e.appID}/stream_chat`,n={appID:e.appID,message:e.message};e.workDir&&(n.workDir=e.workDir);let r={"Content-Type":`application/json`,Accept:`text/event-stream`};e.lastEventID&&(r[`Last-Event-ID`]=e.lastEventID);let i;try{i=await this.#e.post(t,n,{headers:r})}catch(e){if(e instanceof S.HttpError&&e.response){let t=e.response.headers.get(`x-tt-logid`)??``,n=Error(`streamChat 请求失败 (logID: ${t}): ${e.response.status}`);throw n.logId=t,n}let t=e.cause?` cause: ${e.cause.message||e.cause}`:``,n=e.code?` code: ${e.code}`:``;throw Error(`streamChat 网络错误: ${e.message}${n}${t}`)}yield{__meta:!0,logId:i.headers?.get?.(`x-tt-logid`)??``},yield*A(i.body)}};const M=`.spark`,N=`meta.json`;function P(e){return s(e,M,N)}async function F(e){try{return JSON.parse(await d(P(e),`utf8`))}catch{return null}}async function I(e){for(let t of[s(e,`..`,`..`,M,N),s(e,`..`,M,N)])try{return JSON.parse(await d(t,`utf8`))}catch{}return null}async function L(e,t){await u(s(e,M),{recursive:!0});let n=await F(e),r={...n??{},...t};if(r.features||=[],!n){let t=await I(e);if(t)for(let[e,n]of Object.entries(t))e in r||(r[e]=n)}await m(P(e),JSON.stringify(r,null,2),`utf8`)}function R(e){let t=e;return t?.data?.message??t?.Data?.Message??null}async function ee(e){let t=e.maxReconnect??2,n=e.maxTotalReconnect??10,r=e.reconnectDelayMs??1500,i,a=0,o=0,s=0,c,l;for(;;){let u=!1,d=!1,f=!1,p=i;try{for await(let t of e.createStream(p)){if(e.signal?.aborted)return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:`cancelled`};let n=t;if(n?.__meta){l=n.logId||l;continue}let r=n;if(r.id&&(i=r.id),r.id&&p&&r.id===p)continue;if(r.done)break;s++,a>0&&(a=0);let d=R(r.data);if(d?.type===`metadata`&&d?.status!==`in_progress`&&(u=!0),d?.role===`assistant`&&d?.type===`message`&&d?.status===`completed`&&typeof d?.content==`string`&&d.content&&(c=d.content),e.onEvent&&e.onEvent(r,{lastEventId:i,reconnectCount:o})===!1){f=!0;break}}}catch(e){if(!u){let t=e instanceof Error?e.message:String(e);if(/\b4[0-9]{2}\b/.test(t)||t.includes(`业务错误`))return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:t};d=!0}}if(f)break;if(s===0&&!u&&!d)return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:`stream returned no events`};if(u)break;if(d||i){if(a++,o++,a<=t&&o<=n&&i){if(e.onReconnect?.(o,i),await new Promise(t=>{let n=setTimeout(t,r);e.signal&&e.signal.addEventListener(`abort`,()=>{clearTimeout(n),t()},{once:!0})}),e.signal?.aborted)return{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:`cancelled`};continue}let u=`stream disconnected, reconnect exhausted`;return e.onFailed?.(u),{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!1,error:u}}break}return e.onComplete?.({totalEvents:s,reconnectCount:o,lastEventId:i}),{totalEvents:s,reconnectCount:o,lastEventId:i,finalText:c,logId:l,success:!0}}let z=``,B=0;function te(e){try{let t=ne(e);if(!t)return null;let n=t.replace(/\s*\(.*\)$/,``).replace(/\s*:.*$/,``),r=Date.now();return n===z&&r-B<3e4?null:(z=n,B=r,t)}catch{return null}}function ne(e){let t=e.role,n=e.tool_calls,r=e.content;if(e.finish_reason,t===`assistant`&&n?.length)for(let e of n){let t=e?.function?.name??e?.name,n={};try{let t=e?.function?.arguments??e?.arguments;n=typeof t==`string`?JSON.parse(t):t??{}}catch{}if(t===`bash`){let e=String(n.command??``);if(e.includes(`init`))return`初始化项目`;if(e.includes(`db sql`))return`数据库操作`;if(e.includes(`deploy`))return`部署中`}}return t===`tool`&&typeof r==`string`&&(r.startsWith(`执行 run_shell_command 工具失败`)||r.startsWith(`command exitcode:`))?`失败: ${r.slice(0,200)+(r.length>200?`...`:``)}`:null}const V=`.spark`,re=new Intl.DateTimeFormat(`en-CA`,{timeZone:`Asia/Shanghai`,year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`,second:`2-digit`,hour12:!1,timeZoneName:`longOffset`});function H(e){return s(e,V,`progress.txt`)}function U(){let e=Object.fromEntries(re.formatToParts(new Date).filter(e=>e.type!==`literal`).map(e=>[e.type,e.value])),t=e.timeZoneName===`GMT`?`+00:00`:e.timeZoneName.replace(/^GMT/,``);return`${e.year}-${e.month}-${e.day}T${e.hour}:${e.minute}:${e.second}${t}`}function ie(e){let t=[],n=``,r=``,i=``,a=e.includes(`## 已完成`)||e.includes(`## 当前任务`),o=!1,s=!a;for(let c of e.split(`
5
+ `)){let e=c.trim();if(a){if(e===`## 已完成`){o=!0,s=!1;continue}if(e.startsWith(`## 当前任务`)){s=!0,o=!1;continue}if(e===``)continue}if(o&&e)t.push(e);else if(s&&e){let t=e.match(/^\[([^\]]+)\]\s*(.*)/);if(t){let e=t[2];i=t[1],r=e,!a&&e.startsWith(`需求: `)&&(n=e.slice(4))}else n||=e}}return{history:t,taskDesc:n,lastStatus:r,lastTimestamp:i}}async function ae(e,t){await u(s(e,V),{recursive:!0});let n=H(e),r=[];try{let e=await d(n,`utf8`);if(e.trim()){let t=ie(e);if(r.push(...t.history),t.taskDesc){let e=t.lastTimestamp||U(),n;n=t.lastStatus.startsWith(`失败:`)?t.lastStatus:t.lastStatus===`处理完成`?`成功`:t.lastStatus||`未知状态`,r.push(`[${e}] ${t.taskDesc} — ${n}`)}}}catch{}await m(n,`## 已完成\n\n${r.length?r.join(`
6
6
  `)+`
7
- `:``}\n## 当前任务\n\n${t}\n`,`utf8`)}async function U(e,t){await c(V(e),`[${H()}] ${t}\n`,`utf8`)}async function*oe(e){let{cwd:t,task:n,agentId:r,signal:i}=e;try{let a=new A,o=e.requestText??n;await ae(t,o.slice(0,200)+(o.length>200?`...`:``)),await U(t,`已接收需求`),yield{type:`progress`,message:`已接收需求`};let s=await P(t),c=!s?.appId,l=H();if(c){let e;try{e=await a.createSubApp({message:n,agentId:r})}catch(e){let n=e instanceof Error?e.message:String(e),r=e instanceof E;try{await U(t,`失败: ${n}`)}catch{}yield{type:`failed`,error:n,phase:`create`,userFacing:r};return}let{appID:i,conversationID:o,logId:c}=e;s={appId:i,conversationId:o,createdAt:l,updatedAt:l},await I(t,s),await U(t,`应用已创建(appId: ${i})`),yield{type:`app_created`,appId:i,conversationId:o,logId:c}}await U(t,`正在处理`),yield{type:`progress`,message:`正在处理`};let u=!1,d=Date.now(),f=setInterval(()=>{U(t,`正在开发中(已 ${Math.floor((Date.now()-d)/6e4)} 分钟)`).catch(()=>{})},6e4),p;try{p=await ee({createStream:e=>a.streamChat({appID:s.appId,message:n,workDir:t,...e?{lastEventID:e}:{}}),onEvent:e=>{if(i?.aborted)return!1;let n=L(e.data),r=n?.delta?.type;!u&&n?.role===`assistant`&&(n?.status===`in_progress`||r===`text`)&&(u=!0,U(t,`正在输出结果`).catch(()=>{}));try{if(n?.status===`completed`){let e=te(n);e&&U(t,e).catch(()=>{})}}catch{}},onReconnect:(t,n)=>{e.onReconnect?.(t,n)},onFailed:n=>{e.onStreamFailed?.(n),U(t,`失败: ${n}`).catch(()=>{})},signal:i})}finally{clearInterval(f)}if(!p.success){let e=p.error?.includes(`disconnected`)||p.error?.includes(`no events`);yield{type:`failed`,error:p.error||`stream failed`,retryable:e};return}let{finalText:m,logId:h}=p;if(u&&(yield{type:`progress`,message:`正在输出结果`,logId:h||void 0}),i?.aborted){await U(t,`失败: cancelled`),yield{type:`failed`,error:`cancelled`};return}let g=H();await I(t,{...s,updatedAt:g,finalText:m}),m&&await U(t,`Agent 回复: ${m.slice(0,200)+(m.length>200?`...`:``)}`),await U(t,`处理完成`),yield{type:`completed`,appId:s.appId,logId:h||void 0,finalText:m}}catch(e){let n=e instanceof Error?e.message:String(e);try{await U(t,`失败: ${n}`)}catch{}yield{type:`failed`,error:n,logId:e?.logId}}}const W=[`research`,`design`,`feedback`];function se(e){return e.replace(/[^a-zA-Z0-9\u4e00-\u9fff-]/g,`-`).replace(/-+/g,`-`).replace(/^-|-$/g,``).slice(0,60)||`reference`}async function ce(e){let t;try{t=await d(e)}catch{return 1}let n=0;for(let e of t){let t=e.match(/^(\d{3})-/);if(t){let e=parseInt(t[1],10);e>n&&(n=e)}}return n+1}async function le(e){try{let t=await u(o(e,`reference`,`manifest.json`),`utf8`);return JSON.parse(t)}catch{return{updatedAt:new Date().toISOString(),files:[]}}}async function ue(e,t){await l(o(e,`reference`),{recursive:!0}),await p(o(e,`reference`,`manifest.json`),JSON.stringify(t,null,2),`utf8`)}async function de(e){let{appCwd:t,category:n,content:r,filename:i,mode:a=`append`}=e,s=o(t,`reference`,n);if(a===`replace`)try{await f(s,{recursive:!0,force:!0})}catch{}await l(s,{recursive:!0});let c=await ce(s),u=`${n}/${`${String(c).padStart(3,`0`)}-${se(i??`reference`)}.md`}`,d=`reference/${u}`;await p(o(t,d),r,`utf8`);let m=await le(t);return a===`replace`&&(m.files=m.files.filter(e=>e.category!==n)),m.files.push({path:u,category:n,writtenAt:new Date().toISOString()}),m.updatedAt=new Date().toISOString(),await ue(t,m),{relativePath:d,category:n,seq:c}}async function G(e){let t=o(e,`reference`),n;try{n=await d(t)}catch{return{totalFiles:0,categories:{}}}let r={},i=0;for(let e of n){if(!W.includes(e))continue;let n;try{n=(await d(o(t,e))).filter(e=>e.endsWith(`.md`)).sort()}catch{continue}n.length>0&&(r[e]={count:n.length,files:n},i+=n.length)}return{totalFiles:i,categories:r}}async function fe(e){return(await G(e)).totalFiles>0}const pe=/^[a-z0-9][a-z0-9-]{0,63}$/,me=new Set([`.`,`..`,`con`,`nul`,`prn`,`aux`]);function K(e){let t=e?.config?.plugins?.entries?.[`openclaw-extension-miaoda-coding`]?.config??{};return{verbose:t?.verbose===!0,hooks:{allowPromptInjection:t?.hooks?.allowPromptInjection!==!1}}}function q(e,n){if(!e)return;let r=i.dirname(process.execPath),a=t(`openclaw`,[`message`,`send`,`--channel`,`feishu`,`--target`,e,`--message`,n],{stdio:[`ignore`,`ignore`,`pipe`],env:{...process.env,PATH:`${r}:${process.env.PATH||``}`}});a.stderr.on(`data`,()=>{}),a.on(`close`,()=>{}),a.unref()}function J(e){return String(e??``).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,``).replace(/\s+/g,` `).trim()}function he(e){let t=String(e??``).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,``).trim();if(!t)throw Error(`task 不能为空`);if(t.length>4e3)throw Error(`task 不能超过 4000 个字符`);return t}function Y(e){let t=J(e);if(!X(t))throw Error(`project_id 不合法: "${t}"`);return t}function X(e){return pe.test(e)&&!me.has(e)}function ge(e){if(e==null||e===``)return;let t=J(e);if(!/^(user|chat|thread):\S+$/.test(t))throw Error(`target 不合法`);return t}function _e(e){try{return n.readFileSync(e,`utf8`).split(`
8
- `).map(e=>e.trim()).filter(Boolean)}catch{return[]}}function Z(e){return e.replace(/^\[[^\]]+\]\s*/,``)}function Q(e){return e.replace(/应用已创建(appId:\s*[^)]+)/g,`应用已创建`)}function $(e){return e.slice(-3).map(Z).map(Q).join(` | `)||`暂无进度记录`}function ve(e){let t=$(e);return t.includes(`失败:`)?`failed`:t.includes(`处理完成`)?`completed`:t===`暂无进度记录`?`pending`:`running`}var ye={id:`openclaw-extension-miaoda-coding`,name:`妙搭`,description:`妙搭制品创建与修改`,register(e){let t=`[miaoda-coding]`,r={info:n=>e.logger.info?.(`${t} ${n}`),warn:n=>e.logger.warn?.(`${t} ${n}`),error:n=>e.logger.error?.(`${t} ${n}`)},a={verbose:!1,hooks:{allowPromptInjection:!0}},o=new Set([`miaoda_coding`]),s=new Set([`miaoda_check_progress`]);e.on(`before_tool_call`,(e,t)=>{let n=e.toolName,i=t?.sessionKey??``,a=i.toLowerCase().split(`:`).includes(`subagent`);if(o.has(n)&&!a)return r.warn(`blocked ${n} from non-subagent session: ${i}`),{block:!0,blockReason:`${n} 只能由 subagent 调用。请通过 sessions_spawn(runtime="subagent", mode="run") 派活,不要直接调用此工具。读 miaoda-coding skill 了解完整流程。`};if(s.has(n)&&a)return r.warn(`blocked ${n} from subagent session: ${i}`),{block:!0,blockReason:`${n} 不能由 subagent 调用。subagent 不应直接操作参考资料或查询进度。`}}),e.on(`before_prompt_build`,(e,t)=>{if(K(t).hooks.allowPromptInjection)return{prependSystemContext:[`【硬规则 — 妙搭】`,`当用户要求创建或修改应用、网页、页面时,必须通过 sessions_spawn(runtime="subagent", mode="run") 交给妙搭。`,`读 miaoda-coding skill 后严格按指示操作。只传 runtime、mode、task 三个参数,不要传 streamTo/sandbox 等。`,`绝对不要自己写代码或用 exec 生成文件。如果 sessions_spawn 调用本身返回错误,去掉多余参数后重试。如果 subagent 执行失败需要重试,generation_request 填"继续"(不要重复发完整需求),详见 skill 中的重试流程。`,`如果对话中有需要传递给 code agent 的上下文(研究结果、用户偏好等),先调 miaoda_write_reference 写入 reference 目录,再 spawn。用户反悔或需要替换之前的上下文时,使用 miaoda_write_reference 的 mode=replace。妙搭只做制品生成,不做研究。`,`不要用 sessions_history 读取子 agent 的 session,查进度用 miaoda_check_progress tool。`,`subagent 完成后只发一条消息,严禁发多条。不要调 message tool。如果 result 的 hint 说预览链接已自动发送,则只发纯文字总结,不要再带任何链接;否则附上预览链接,格式用加粗 Markdown 链接 **[url](url)**,不要发裸 URL。`].join(` `)}}),e.registerTool(e=>{let t=b(e);return{name:`list_projects`,label:`列出妙搭项目`,description:`列出已创建的妙搭项目,返回 project_id、appId、updatedAt、finalText、status 和进度摘要。修改已有项目时必须先调此工具。`,parameters:{type:`object`,properties:{}},async execute(){let e=[];try{e=n.readdirSync(t.workspaceDir,{withFileTypes:!0}).filter(e=>e.isDirectory()&&X(e.name)).map(e=>{let r=i.join(t.workspaceDir,e.name,`.spark`,`meta.json`),a=i.join(t.workspaceDir,e.name,`.spark`,`progress.txt`),o=null;try{o=JSON.parse(n.readFileSync(r,`utf8`))}catch{}let s=_e(a);return{project_id:e.name,appId:o?.appId??null,updatedAt:o?.updatedAt??null,finalText:o?.finalText??null,status:ve(s),progressSummary:$(s)}})}catch{}return{content:[{type:`text`,text:JSON.stringify({projects:e},null,2)}]}}}}),e.registerTool(e=>{let t=b(e);return{name:`miaoda_check_progress`,label:`查看妙搭项目进度`,description:`查看妙搭项目的生成进度。用户问"做到哪了"、"进度如何"、"好了吗"时调此工具。`,parameters:{type:`object`,properties:{project_id:{type:`string`,description:`项目目录名(从 list_projects 获取)`}},required:[`project_id`]},async execute(e,r){try{let e=Y(r.project_id),a=i.join(t.workspaceDir,e,`.spark`,`progress.txt`),o=n.readFileSync(a,`utf8`).trim().split(`
9
- `).filter(Boolean),s=null;try{s=JSON.parse(n.readFileSync(i.join(t.workspaceDir,e,`.spark`,`meta.json`),`utf8`))}catch{}let c=!!s?.finalText,l=o.slice(-20).map(Q).join(`
10
- `);return{content:[{type:`text`,text:JSON.stringify({status:`ok`,project_id:e,lines:o.length,progress:l,delivery_ready:c,finalTextAvailable:c,note:`miaoda_check_progress 只用于查看进度,不能根据 appId 或进度文本推导预览链接;最终链接只以 subagent completion result 中的 output/finalText 为准。`})}]}}catch{return{content:[{type:`text`,text:JSON.stringify({status:`no_progress`,project_id:r.project_id,message:`暂无进度记录`,delivery_ready:!1,finalTextAvailable:!1,note:`miaoda_check_progress 只用于查看进度,不能根据 appId 或进度文本推导预览链接;最终链接只以 subagent completion result 中的 output/finalText 为准。`})}]}}}}}),e.registerTool(e=>{let t=b(e);return{name:`miaoda_write_reference`,label:`写入妙搭参考资料`,description:`将参考资料写入妙搭项目的 reference 目录,供 code agent 在生成时自行查阅。按 category 分类写入。mode=append(默认)追加,mode=replace 替换该 category 全部内容(用于反悔/调整场景)。`,parameters:{type:`object`,properties:{project_id:{type:`string`,description:`项目目录名`},category:{type:`string`,enum:[`research`,`design`,`feedback`],description:`资料类别:research(调研结果)、design(设计要求)、feedback(用户反馈)`},content:{type:`string`,description:`参考资料内容(Markdown 格式,摘要+关键原文引用)`},filename:{type:`string`,description:`可选文件名(不含扩展名)`},mode:{type:`string`,enum:[`append`,`replace`],description:`写入模式:append(追加,默认)、replace(替换该 category 全部内容)`}},required:[`project_id`,`category`,`content`]},async execute(e,n){try{let e=Y(n.project_id);if(!W.includes(n.category))throw Error(`category 不合法,必须是 ${W.join(`、`)} 之一`);let r=n.content?.trim();if(!r)throw Error(`content 不能为空`);if(r.length>5e4)throw Error(`content 不能超过 50000 个字符`);let i=n.mode===`replace`?`replace`:`append`,a=y(t.workspaceDir,e),o=await de({appCwd:a,category:n.category,content:r,filename:n.filename,mode:i}),s=await G(a);return{content:[{type:`text`,text:JSON.stringify({status:`ok`,...o,summary:s},null,2)}]}}catch(e){return{content:[{type:`text`,text:JSON.stringify({status:`error`,error:e instanceof Error?e.message:String(e)})}]}}}}});let c=900*1e3;e.registerTool(e=>{let t=b(e);return{name:`miaoda_coding`,label:`妙搭`,description:`创建或修改妙搭应用/网页。只接受生成指令,不接受搜索/调研等任务。如有参考资料,应事先通过 miaoda_write_reference 写入 reference 目录。修改已有项目时必须先调 list_projects 获取 project_id。返回结构化 JSON(status/appId/finalText/output/meta)。`,parameters:{type:`object`,properties:{generation_request:{type:`string`,description:`生成指令(如'做一个学习网站,包含章节、题库、测验')`},project_id:{type:`string`,description:`本地项目目录名,仅允许小写字母、数字和短横线`},target:{type:`string`,description:`飞书消息推送目标:单聊用 user:ou_xxx,群聊用 chat:oc_xxx`}},required:[`generation_request`,`project_id`]},async execute(e,o){try{let e=he(o.generation_request),s=Y(o.project_id),l=ge(o.target),u=y(t.workspaceDir,s);n.mkdirSync(u,{recursive:!0}),r.info(`miaoda_coding called (project=${s}, isNew=${!n.existsSync(i.join(u,`.spark`,`meta.json`))})`);let d=await fe(u)?`工作目录下有 reference/ 目录,包含参考资料,请先查阅再开始工作。\n\n${e}`:e,f=i.join(u,`.agent`);if(!n.existsSync(f)){let e=v(_());if(n.existsSync(e))try{n.symlinkSync(e,f)}catch{}}let p=[],m,h=!1,g=!1,b=null,x=new AbortController,S=setTimeout(()=>x.abort(),c);try{for await(let n of oe({cwd:u,task:d,requestText:e,agentId:t.agentId,signal:x.signal,onReconnect:(e,t)=>{r.warn(`stream reconnect (project=${s}, attempt=${e}, lastEventId=${t??`none`})`)},onStreamFailed:e=>{r.error(`stream failed (project=${s}): ${e}`)}})){if(`logId`in n&&n.logId&&p.push(n.logId),a.verbose&&l)switch(n.type){case`app_created`:q(l,`应用已创建(appId: ${n.appId})`);break;case`failed`:q(l,`处理失败: ${n.error}`);break}if(n.type===`completed`&&(b={appId:n.appId,finalText:n.finalText}),n.type===`failed`&&(m=n.error,h=n.retryable??!1,n.phase===`create`)){r.error(`createSubApp failed (project=${s}): ${n.error}`),clearTimeout(S),l&&q(l,n.userFacing?n.error:`createSubApp 失败: ${n.error}`);let e=n.userFacing?{status:`error`,project_id:s,appId:null,meta:null,hint:`创建失败,详情已通过飞书发送给用户,不要重复发送错误内容`}:{status:`error`,project_id:s,appId:null,error:n.error,meta:null,hint:`createSubApp 失败,错误已通过飞书发送。根据 error 内容用通俗语言告诉用户具体原因,不要重试`};return{content:[{type:`text`,text:JSON.stringify(e)}]}}}}catch{}clearTimeout(S),g=x.signal.aborted;let C=null;try{C=JSON.parse(n.readFileSync(i.join(u,`.spark`,`meta.json`),`utf8`))}catch{}let w={status:g?`timeout`:m?`error`:`ok`,project_id:s,appId:b?.appId??C?.appId??null,finalText:b?.finalText??C?.finalText,output:b?.finalText??C?.finalText,logIds:p.length?p:void 0,meta:C};if(r.info(`miaoda_coding done (project=${s}, status=${w.status}, logIds=${p.join(`,`)})`),g?(w.error=`执行超时(${c/1e3}秒)`,w.retryable=!0,w.hint=`执行超时,任务可能仍在后台运行。建议先调用 miaoda_check_progress 查看进度,再决定是否重试`):m&&(w.error=m,h&&(w.retryable=!0,w.hint=`网络连接中断,任务可能已部分完成。建议先调用 miaoda_check_progress 查看当前状态,再决定下一步`)),l&&w.status===`ok`){let e=C?.appUrl;if(r.info(`delivery check (project=${s}, target=${l}, appUrl=${e??`missing`})`),typeof e==`string`&&e.trim()){let t=e.includes(`?`)?`&`:`?`,n=`${e.trim()}${t}mode=sidebar-semi`;q(l,`**[${n}](${n})**`),r.info(`delivery sent (project=${s}, url=${n})`),w.hint=`预览链接已自动发送给用户,announce 时不要再带预览链接`}else r.warn(`delivery skipped: no appUrl in meta.json (project=${s})`)}return{content:[{type:`text`,text:JSON.stringify(w,null,2)}]}}catch(e){return{content:[{type:`text`,text:JSON.stringify({status:`error`,error:e instanceof Error?e.message:String(e)})}]}}}}}),e.registerService({id:`miaoda-coding-config`,async start(e){a=K(e)},async stop(){}})}};export{ye as default};
7
+ `:``}\n## 当前任务\n\n${t}\n`,`utf8`)}async function W(e,t){await l(H(e),`[${U()}] ${t}\n`,`utf8`)}async function*oe(e){let{cwd:t,task:n,agentId:r,signal:i}=e;try{let a=new j,o=e.requestText??n;await ae(t,o.slice(0,200)+(o.length>200?`...`:``)),await W(t,`已接收需求`),yield{type:`progress`,message:`已接收需求`};let s=await F(t),c=!s?.appId,l=U();if(c){let e;try{e=await a.createSubApp({message:n,agentId:r})}catch(e){let n=e instanceof Error?e.message:String(e),r=e instanceof D,i=e?.logId;try{await W(t,`失败: ${n}`)}catch{}yield{type:`failed`,error:n,phase:`create`,userFacing:r,logId:i};return}let{appID:i,conversationID:o,logId:c}=e;s={appId:i,conversationId:o,createdAt:l,updatedAt:l},await L(t,s),await W(t,`应用已创建(appId: ${i})`),yield{type:`app_created`,appId:i,conversationId:o,logId:c}}await W(t,`正在处理`),yield{type:`progress`,message:`正在处理`};let u=!1,d=Date.now(),f=setInterval(()=>{W(t,`正在开发中(已 ${Math.floor((Date.now()-d)/6e4)} 分钟)`).catch(()=>{})},6e4),p;try{p=await ee({createStream:e=>a.streamChat({appID:s.appId,message:n,workDir:t,...e?{lastEventID:e}:{}}),onEvent:e=>{if(i?.aborted)return!1;let n=R(e.data),r=n?.delta?.type;!u&&n?.role===`assistant`&&(n?.status===`in_progress`||r===`text`)&&(u=!0,W(t,`正在输出结果`).catch(()=>{}));try{if(n?.status===`completed`){let e=te(n);e&&W(t,e).catch(()=>{})}}catch{}},onReconnect:(t,n)=>{e.onReconnect?.(t,n)},onFailed:n=>{e.onStreamFailed?.(n),W(t,`失败: ${n}`).catch(()=>{})},signal:i})}finally{clearInterval(f)}if(!p.success){let e=p.error?.includes(`disconnected`)||p.error?.includes(`no events`);yield{type:`failed`,error:p.error||`stream failed`,retryable:e};return}let{finalText:m,logId:h}=p;if(u&&(yield{type:`progress`,message:`正在输出结果`,logId:h||void 0}),i?.aborted){await W(t,`失败: cancelled`),yield{type:`failed`,error:`cancelled`};return}let g=U();await L(t,{...s,updatedAt:g,finalText:m}),m&&await W(t,`Agent 回复: ${m.slice(0,200)+(m.length>200?`...`:``)}`),await W(t,`处理完成`),yield{type:`completed`,appId:s.appId,logId:h||void 0,finalText:m}}catch(e){let n=e instanceof Error?e.message:String(e);try{await W(t,`失败: ${n}`)}catch{}yield{type:`failed`,error:n,logId:e?.logId}}}const G=[`research`,`design`,`feedback`];function se(e){return e.replace(/[^a-zA-Z0-9\u4e00-\u9fff-]/g,`-`).replace(/-+/g,`-`).replace(/^-|-$/g,``).slice(0,60)||`reference`}async function ce(e){let t;try{t=await f(e)}catch{return 1}let n=0;for(let e of t){let t=e.match(/^(\d{3})-/);if(t){let e=parseInt(t[1],10);e>n&&(n=e)}}return n+1}async function le(e){try{let t=await d(s(e,`reference`,`manifest.json`),`utf8`);return JSON.parse(t)}catch{return{updatedAt:new Date().toISOString(),files:[]}}}async function ue(e,t){await u(s(e,`reference`),{recursive:!0}),await m(s(e,`reference`,`manifest.json`),JSON.stringify(t,null,2),`utf8`)}async function de(e){let{appCwd:t,category:n,content:r,filename:i,mode:a=`append`}=e,o=s(t,`reference`,n);if(a===`replace`)try{await p(o,{recursive:!0,force:!0})}catch{}await u(o,{recursive:!0});let c=await ce(o),l=`${n}/${`${String(c).padStart(3,`0`)}-${se(i??`reference`)}.md`}`,d=`reference/${l}`;await m(s(t,d),r,`utf8`);let f=await le(t);return a===`replace`&&(f.files=f.files.filter(e=>e.category!==n)),f.files.push({path:l,category:n,writtenAt:new Date().toISOString()}),f.updatedAt=new Date().toISOString(),await ue(t,f),{relativePath:d,category:n,seq:c}}async function K(e){let t=s(e,`reference`),n;try{n=await f(t)}catch{return{totalFiles:0,categories:{}}}let r={},i=0;for(let e of n){if(!G.includes(e))continue;let n;try{n=(await f(s(t,e))).filter(e=>e.endsWith(`.md`)).sort()}catch{continue}n.length>0&&(r[e]={count:n.length,files:n},i+=n.length)}return{totalFiles:i,categories:r}}async function fe(e){return(await K(e)).totalFiles>0}function pe(e){let t=e?.log,n=e?.timeoutMs??5e3,r=null,i=null,a=null;return{onSessionStart(e){t?.info?.(`db-schema: session_start sessionId=${e??`unknown`}, clearing cache`),r=null,i=null,a=e??null},async getSchemaSummary(e){return e&&a!==null&&e!==a?(t?.warn?.(`db-schema: session changed without session_start: ${a} -> ${e}, resetting cache`),r=null,i=null,a=e):e&&a===null&&(a=e),r===null?i||(i=(async()=>{t?.info?.(`db-schema: fetching (timeout=${n}ms)`);try{let{stdout:e}=await me(he(),n),i=ge(e);if(i.length===0)return t?.info?.(`db-schema: empty, skip injection for this session`),r=``,null;let a=_e(i);return r=a,t?.info?.(`db-schema: cached ${i.length} table(s), ${a.length} chars`),a}catch(e){let n=e instanceof Error?e.message:String(e);return t?.warn?.(`db-schema: fetch failed (will not retry this session): ${n}`),r=``,null}finally{i=null}})(),i):r===``?null:r}}}function me(e,t){return new Promise((n,r)=>{let i=setTimeout(()=>r(Error(`timeout after ${t}ms`)),t);e.then(e=>{clearTimeout(i),n(e)},e=>{clearTimeout(i),r(e)})})}function he(){return new Promise((e,n)=>{t(`npx`,[`-y`,`@lark-apaas/miaoda-data-cli`,`db`,`schema`,`--json`],{timeout:3e4,maxBuffer:10*1024*1024},(t,r,i)=>{if(t)return n(Error(`execFile failed: ${t.message} | stderr: ${i?.slice(0,500)??``} | stdout: ${r?.slice(0,500)??``}`));e({stdout:r,stderr:i})})})}function ge(e){let t=e.trim();if(!t)return[];let n=JSON.parse(t);return Array.isArray(n)?n:n&&typeof n==`object`&&n.tableName?[n]:[]}function _e(e){return`【当前数据库表结构】\n${e.map(e=>{let t=e.fields.map(e=>`${e.fieldName}(${e.type})`).join(`, `),n=e.comment?` (${e.comment})`:``;return`- ${e.tableName}${n}: ${t}`}).join(`
8
+ `)}`}const ve=/^[a-z0-9][a-z0-9-]{0,63}$/,ye=new Set([`.`,`..`,`con`,`nul`,`prn`,`aux`]);function q(e){let t=e?.config?.plugins?.entries?.[`openclaw-extension-miaoda-coding`]?.config??{};return{verbose:t?.verbose===!0,hooks:{allowPromptInjection:t?.hooks?.allowPromptInjection!==!1}}}function J(e,t){if(!e)return;let r=a.dirname(process.execPath),i=n(`openclaw`,[`message`,`send`,`--channel`,`feishu`,`--target`,e,`--message`,t],{stdio:[`ignore`,`ignore`,`pipe`],env:{...process.env,PATH:`${r}:${process.env.PATH||``}`}});i.stderr.on(`data`,()=>{}),i.on(`close`,()=>{}),i.unref()}function Y(e){return String(e??``).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,``).replace(/\s+/g,` `).trim()}function be(e){let t=String(e??``).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,``).trim();if(!t)throw Error(`task 不能为空`);if(t.length>4e3)throw Error(`task 不能超过 4000 个字符`);return t}function X(e){let t=Y(e);if(!Z(t))throw Error(`project_id 不合法: "${t}"`);return t}function Z(e){return ve.test(e)&&!ye.has(e)}function xe(e){if(e==null||e===``)return;let t=Y(e);if(t!==`none`){if(!/^(user|chat|thread):\S+$/.test(t))throw Error(`target 不合法`);return t}}function Se(e){try{return r.readFileSync(e,`utf8`).split(`
9
+ `).map(e=>e.trim()).filter(Boolean)}catch{return[]}}function Ce(e){return e.replace(/^\[[^\]]+\]\s*/,``)}function Q(e){return e.replace(/应用已创建(appId:\s*[^)]+)/g,`应用已创建`)}function $(e){return e.slice(-3).map(Ce).map(Q).join(` | `)||`暂无进度记录`}function we(e){let t=$(e);return t.includes(`失败:`)?`failed`:t.includes(`处理完成`)?`completed`:t===`暂无进度记录`?`pending`:`running`}var Te={id:`openclaw-extension-miaoda-coding`,name:`妙搭`,description:`妙搭制品创建与修改`,register(e){let t=`[miaoda-coding]`,n={info:n=>e.logger.info?.(`${t} ${n}`),warn:n=>e.logger.warn?.(`${t} ${n}`),error:n=>e.logger.error?.(`${t} ${n}`)},i={verbose:!1,hooks:{allowPromptInjection:!0}},o=pe({log:n});n.info(`plugin registered`),e.on(`session_start`,(e,t)=>{try{o.onSessionStart(e?.sessionId)}catch(e){n.warn(`session_start reset failed: ${String(e)}`)}});let s=new Set([`miaoda_coding`]),c=new Set([`miaoda_check_progress`]);e.on(`before_tool_call`,(e,t)=>{let r=e.toolName,i=t?.sessionKey??``,a=i.toLowerCase().split(`:`).includes(`subagent`);if(s.has(r)&&!a)return n.warn(`blocked ${r} from non-subagent session: ${i}`),{block:!0,blockReason:`${r} 只能由 subagent 调用。请通过 sessions_spawn(runtime="subagent", mode="run") 派活,不要直接调用此工具。读 miaoda-coding skill 了解完整流程。`};if(c.has(r)&&a)return n.warn(`blocked ${r} from subagent session: ${i}`),{block:!0,blockReason:`${r} 不能由 subagent 调用。subagent 不应直接操作参考资料或查询进度。`}}),e.on(`before_prompt_build`,async(e,t)=>{if(!q(t).hooks.allowPromptInjection)return;let r={prependSystemContext:[`【硬规则 — 妙搭】`,`当用户要求创建或修改应用、网页、页面时,必须通过 sessions_spawn(runtime="subagent", mode="run") 交给妙搭。`,`读 miaoda-coding skill 后严格按指示操作。只传 runtime、mode、task 三个参数,不要传 streamTo/sandbox 等。`,`绝对不要自己写代码或用 exec 生成文件。如果 sessions_spawn 调用本身返回错误,去掉多余参数后重试。如果 subagent 执行失败需要重试,generation_request 填"继续"(不要重复发完整需求),详见 skill 中的重试流程。`,`如果对话中有需要传递给 code agent 的上下文(研究结果、用户偏好等),先调 miaoda_write_reference 写入 reference 目录,再 spawn。用户反悔或需要替换之前的上下文时,使用 miaoda_write_reference 的 mode=replace。妙搭只做制品生成,不做研究。`,`不要用 sessions_history 读取子 agent 的 session,查进度用 miaoda_check_progress tool(仅限建应用场景,数据库操作等不要用)。`,`subagent 完成后只发一条消息,严禁发多条。不要调 message tool。如果 result 的 hint 说预览链接已自动发送,则只发纯文字总结,不要再带任何链接;否则附上预览链接,格式用加粗 Markdown 链接 **[url](url)**,不要发裸 URL。`].join(` `)};try{let e=await o.getSchemaSummary(t?.sessionId);e&&(r.appendSystemContext=e)}catch(e){n.error(`db schema injection threw unexpected error: ${String(e)}`)}return r}),e.registerTool(e=>{let t=x(e);return{name:`list_projects`,label:`列出妙搭项目`,description:`列出已创建的妙搭项目,返回 project_id、appId、updatedAt、finalText、status 和进度摘要。修改已有项目时必须先调此工具。`,parameters:{type:`object`,properties:{}},async execute(){let e=[];try{e=r.readdirSync(t.workspaceDir,{withFileTypes:!0}).filter(e=>e.isDirectory()&&Z(e.name)).map(e=>{let n=a.join(t.workspaceDir,e.name,`.spark`,`meta.json`),i=a.join(t.workspaceDir,e.name,`.spark`,`progress.txt`),o=null;try{o=JSON.parse(r.readFileSync(n,`utf8`))}catch{}let s=Se(i);return{project_id:e.name,appId:o?.appId??null,updatedAt:o?.updatedAt??null,finalText:o?.finalText??null,status:we(s),progressSummary:$(s)}})}catch{}return{content:[{type:`text`,text:JSON.stringify({projects:e},null,2)}]}}}}),e.registerTool(e=>{let t=x(e);return{name:`miaoda_check_progress`,label:`查看妙搭应用生成进度`,description:`查看通过 miaoda_coding 创建的应用的生成进度。仅用于 sessions_spawn 派活建应用后查看进度,不适用于数据库操作等其他场景。`,parameters:{type:`object`,properties:{project_id:{type:`string`,description:`项目目录名(从 list_projects 获取)`}},required:[`project_id`]},async execute(e,n){try{let e=X(n.project_id),i=a.join(t.workspaceDir,e,`.spark`,`progress.txt`),o=r.readFileSync(i,`utf8`).trim().split(`
10
+ `).filter(Boolean),s=null;try{s=JSON.parse(r.readFileSync(a.join(t.workspaceDir,e,`.spark`,`meta.json`),`utf8`))}catch{}let c=!!s?.finalText,l=o.slice(-20).map(Q).join(`
11
+ `);return{content:[{type:`text`,text:JSON.stringify({status:`ok`,project_id:e,lines:o.length,progress:l,delivery_ready:c,finalTextAvailable:c,note:`miaoda_check_progress 只用于查看进度,不能根据 appId 或进度文本推导预览链接;最终链接只以 subagent completion result 中的 output/finalText 为准。`})}]}}catch{return{content:[{type:`text`,text:JSON.stringify({status:`no_progress`,project_id:n.project_id,message:`暂无进度记录`,delivery_ready:!1,finalTextAvailable:!1,note:`miaoda_check_progress 只用于查看进度,不能根据 appId 或进度文本推导预览链接;最终链接只以 subagent completion result 中的 output/finalText 为准。`})}]}}}}}),e.registerTool(e=>{let t=x(e);return{name:`miaoda_write_reference`,label:`写入妙搭参考资料`,description:`将参考资料写入妙搭项目的 reference 目录,供 code agent 在生成时自行查阅。按 category 分类写入。mode=append(默认)追加,mode=replace 替换该 category 全部内容(用于反悔/调整场景)。`,parameters:{type:`object`,properties:{project_id:{type:`string`,description:`项目目录名`},category:{type:`string`,enum:[`research`,`design`,`feedback`],description:`资料类别:research(调研结果)、design(设计要求)、feedback(用户反馈)`},content:{type:`string`,description:`参考资料内容(Markdown 格式,摘要+关键原文引用)`},filename:{type:`string`,description:`可选文件名(不含扩展名)`},mode:{type:`string`,enum:[`append`,`replace`],description:`写入模式:append(追加,默认)、replace(替换该 category 全部内容)`}},required:[`project_id`,`category`,`content`]},async execute(e,n){try{let e=X(n.project_id);if(!G.includes(n.category))throw Error(`category 不合法,必须是 ${G.join(`、`)} 之一`);let r=n.content?.trim();if(!r)throw Error(`content 不能为空`);if(r.length>5e4)throw Error(`content 不能超过 50000 个字符`);let i=n.mode===`replace`?`replace`:`append`,a=b(t.workspaceDir,e),o=await de({appCwd:a,category:n.category,content:r,filename:n.filename,mode:i}),s=await K(a);return{content:[{type:`text`,text:JSON.stringify({status:`ok`,...o,summary:s},null,2)}]}}catch(e){return{content:[{type:`text`,text:JSON.stringify({status:`error`,error:e instanceof Error?e.message:String(e)})}]}}}}});let l=900*1e3;e.registerTool(e=>{let t=x(e);return{name:`miaoda_coding`,label:`妙搭`,description:`创建或修改妙搭应用/网页。只接受生成指令,不接受搜索/调研等任务。如有参考资料,应事先通过 miaoda_write_reference 写入 reference 目录。修改已有项目时必须先调 list_projects 获取 project_id。返回结构化 JSON(status/appId/finalText/output/meta)。`,parameters:{type:`object`,properties:{generation_request:{type:`string`,description:`生成指令(如'做一个学习网站,包含章节、题库、测验')`},project_id:{type:`string`,description:`本地项目目录名,仅允许小写字母、数字和短横线`},target:{type:`string`,description:`飞书消息推送目标:单聊用 user:ou_xxx,群聊用 chat:oc_xxx,非飞书渠道传 none`}},required:[`generation_request`,`project_id`,`target`]},async execute(e,o){try{let e=be(o.generation_request),s=X(o.project_id),c=xe(o.target),u=b(t.workspaceDir,s);r.mkdirSync(u,{recursive:!0}),n.info(`miaoda_coding called (project=${s}, isNew=${!r.existsSync(a.join(u,`.spark`,`meta.json`))})`);let d=await fe(u)?`工作目录下有 reference/ 目录,包含参考资料,请先查阅再开始工作。\n\n${e}`:e,f=a.join(u,`.agent`);if(!r.existsSync(f)){let e=y(v());if(r.existsSync(e))try{r.symlinkSync(e,f)}catch{}}let p=[],m,h=!1,g=!1,_=null,x=new AbortController,S=setTimeout(()=>x.abort(),l);try{for await(let r of oe({cwd:u,task:d,requestText:e,agentId:t.agentId,signal:x.signal,onReconnect:(e,t)=>{n.warn(`stream reconnect (project=${s}, attempt=${e}, lastEventId=${t??`none`})`)},onStreamFailed:e=>{n.error(`stream failed (project=${s}): ${e}`)}})){if(`logId`in r&&r.logId&&p.push(r.logId),i.verbose&&c)switch(r.type){case`app_created`:J(c,`应用已创建(appId: ${r.appId})`);break;case`failed`:J(c,`处理失败: ${r.error}`);break}if(r.type===`completed`&&(_={appId:r.appId,finalText:r.finalText}),r.type===`failed`&&(m=r.error,h=r.retryable??!1,r.phase===`create`)){n.error(`createSubApp failed (project=${s}): ${r.error}`),clearTimeout(S);let e=!!c;if(c){let e=r.userFacing?r.error:`createSubApp 失败: ${r.error}`;n.info(`delivering create error via feishu (project=${s}, target=${c})`),J(c,e)}else n.warn(`create failed but no target for feishu delivery (project=${s})`);let t=r.userFacing?{status:`error`,project_id:s,appId:null,meta:null,logIds:p.length?p:void 0,hint:e?`创建失败,详情已通过飞书发送给用户,不要重复发送错误内容`:`创建失败,将 error 字段的内容原样发送给用户,不要改写、不要删减、不要重试`,error:e?void 0:r.error}:{status:`error`,project_id:s,appId:null,error:r.error,meta:null,logIds:p.length?p:void 0,hint:e?`createSubApp 失败,错误已通过飞书发送。根据 error 内容用通俗语言告诉用户具体原因,不要重试`:`createSubApp 失败,根据 error 内容用通俗语言告诉用户具体原因,不要重试`};return{content:[{type:`text`,text:JSON.stringify(t)}]}}}}catch{}clearTimeout(S),g=x.signal.aborted;let C=null;try{C=JSON.parse(r.readFileSync(a.join(u,`.spark`,`meta.json`),`utf8`))}catch{}let w={status:g?`timeout`:m?`error`:`ok`,project_id:s,appId:_?.appId??C?.appId??null,finalText:_?.finalText??C?.finalText,output:_?.finalText??C?.finalText,logIds:p.length?p:void 0,meta:C};if(n.info(`miaoda_coding done (project=${s}, status=${w.status}, logIds=${p.join(`,`)})`),g?(w.error=`执行超时(${l/1e3}秒)`,w.retryable=!0,w.hint=`执行超时,任务可能仍在后台运行。建议先调用 miaoda_check_progress 查看进度,再决定是否重试`):m&&(w.error=m,h&&(w.retryable=!0,w.hint=`网络连接中断,任务可能已部分完成。建议先调用 miaoda_check_progress 查看当前状态,再决定下一步`)),c&&w.status===`ok`){let e=C?.appUrl;if(n.info(`delivery check (project=${s}, target=${c}, appUrl=${e??`missing`})`),typeof e==`string`&&e.trim()){let t=e.includes(`?`)?`&`:`?`,r=`${e.trim()}${t}mode=sidebar-semi`;J(c,`**[${r}](${r})**`),n.info(`delivery sent (project=${s}, url=${r})`),w.hint=`预览链接已自动发送给用户,announce 时不要再带预览链接`}else n.warn(`delivery skipped: no appUrl in meta.json (project=${s})`)}return{content:[{type:`text`,text:JSON.stringify(w,null,2)}]}}catch(e){return{content:[{type:`text`,text:JSON.stringify({status:`error`,error:e instanceof Error?e.message:String(e)})}]}}}}}),e.registerService({id:`miaoda-coding-config`,async start(e){i=q(e)},async stop(){}})}};export{Te as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/openclaw-extension-miaoda-coding",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "妙搭 Coding Agent — 通过 AI 创建和修改妙搭应用",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -112,7 +112,7 @@ user-invocable: false
112
112
  - project_id: "<project_id>"
113
113
  - name: "<面向人类可读的应用名称>"
114
114
  - description: "<根据用户需求整理的一句话简介,单行,80 字以内>"
115
- - target: 根据消息来源判断——群聊中用 "chat:<chat_id>"(如 chat:oc_xxx),单聊中用 "user:<sender_id>"(如 user:ou_xxx
115
+ - target:(**必填,不可省略**)根据消息来源判断——群聊中用 "chat:<chat_id>"(如 chat:oc_xxx),单聊中用 "user:<sender_id>"(如 user:ou_xxx),非飞书渠道传 "none"。缺少 target 会导致错误消息无法投递给用户。
116
116
 
117
117
  如果 reference/ 目录已有参考资料,tool 会自动提示 code agent 查阅。
118
118
 
@@ -136,7 +136,7 @@ tool 返回的 JSON 里如果有 `hint` 字段,严格按 hint 指示行事。
136
136
  3. 调用 miaoda_coding tool,参数:
137
137
  - generation_request: "<修改要求>"
138
138
  - project_id: "<上一步取到的 project_id>"
139
- - target: 根据消息来源判断——群聊中用 "chat:<chat_id>"(如 chat:oc_xxx),单聊中用 "user:<sender_id>"(如 user:ou_xxx
139
+ - target:(**必填,不可省略**)根据消息来源判断——群聊中用 "chat:<chat_id>"(如 chat:oc_xxx),单聊中用 "user:<sender_id>"(如 user:ou_xxx),非飞书渠道传 "none"。缺少 target 会导致错误消息无法投递给用户。
140
140
 
141
141
  tool 会返回结构化 JSON(status/appId/finalText/output 等)。
142
142
 
@@ -193,16 +193,9 @@ subagent 返回的结果 JSON 中可能包含 `status: "error"` 或 `status: "ti
193
193
 
194
194
  - `meta.json`:应用元信息(appId、appUrl 等),用于获取预览链接
195
195
  - `progress.txt`:关键节点进度日志(轻量,适合快速了解当前状态)
196
- - `messages.jsonl`:完整对话记录,每行一条 OpenAI Messages 格式的 JSON(适合失败诊断、了解完整执行过程)
197
-
198
- 注意:messages.jsonl 可能较长,不要一次性读取全部内容。
199
- - 查看最新进度:读 progress.txt 或 tail messages.jsonl 最后几行
200
- - 诊断失败原因:读 messages.jsonl 最后 10-20 行,定位失败前后的操作
201
- - 了解完整过程:分段读取,避免超出上下文限制
202
196
 
203
197
  ## 判定任务是否完成
204
198
 
205
199
  subagent announce 后,读 workspace/<project_id>/.spark/progress.txt 了解实际执行情况。
206
- 需要更多细节时读 messages.jsonl。
207
200
 
208
201
  基于对实际情况的了解,自行判断下一步行动。
@@ -67,8 +67,8 @@ Database initialized.
67
67
  # 1. 初始化数据库
68
68
  npx -y @lark-apaas/miaoda-data-cli db init
69
69
 
70
- # 2. 建表
71
- npx -y @lark-apaas/miaoda-data-cli db sql "CREATE TABLE note (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title VARCHAR(255) NOT NULL)"
70
+ # 2. 建表(建议添加表注释和字段注释,便于后续理解用途)
71
+ npx -y @lark-apaas/miaoda-data-cli db sql "CREATE TABLE note (id UUID PRIMARY KEY DEFAULT gen_random_uuid(), title VARCHAR(255) NOT NULL); COMMENT ON TABLE note IS '笔记表';"
72
72
 
73
73
  # 3. 查看表结构
74
74
  npx -y @lark-apaas/miaoda-data-cli db schema note