@lark-apaas/openclaw-extension-miaoda-coding 1.0.1 → 1.0.3-beta.0

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,7 +1,7 @@
1
- import{createRequire as e}from"node:module";import{spawn as t}from"node:child_process";import n from"node:fs";import r,{join as i,resolve as a}from"node:path";import{appendFile as o,mkdir as s,readFile as c,writeFile as l}from"node:fs/promises";var u=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),d=e(import.meta.url);function f(){return a(process.env.OPENCLAW_STATE_DIR||process.cwd())}function p(e){return i(e,`workspace`)}function m(e,t){return i(e,`workspace`,t)}function h(e){return i(e,`.agent`)}var g=u(((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:()=>p,DEFAULT_JWT_EXPIRE_TIME_MS:()=>f,HttpClient:()=>N,HttpError:()=>A,generateJWTToken:()=>h,parseJWTTokenWithVerify:()=>g,registerPlatformPlugin:()=>T,resolvePlatformBaseURL:()=>w}),t.exports=c(l);var u=d(`crypto`),f=1800*1e3,p=60,m={alg:`HS256`,typ:`JWT`};function h(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(m)),a=v(JSON.stringify(r));return`${i}.${a}.${_(`${i}.${a}`,t.secretKey)}`}function g(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??p,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=h(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=`FORCE_AUTHN_INNERAPI_DOMAIN`,S=`FORCE_AUTHN_ACCESS_KEY`,C=`FORCE_AUTHN_ACCESS_SECRET`;function w(e){if(!e?.enabled)return e?.baseURL;if(e.baseURL)return e.baseURL;let t=e.domainEnv||x,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 T(e,t){if(!t.enabled)return;E(`platform.defaultClaims`,t.defaultClaims);let n=t.accessKeyEnv||S,r=t.secretKeyEnv||C,i=t.accessKey??(process.env[n]||``),a=new b({accessKey:i,secretKey:t.secretKey??(process.env[r]||``),expireTimeMs:t.expireTimeMs??f,refreshBeforeMs:t.refreshBeforeMs});return e.request.use(e=>{E(`request.platformAuth.customClaims`,e.platformAuth?.customClaims);let n={...t.defaultClaims||{},...e.platformAuth?.customClaims||{},access_key:i},r=a.getToken(n),o={...e.headers,Authorization:`Bearer ${r}`,"x-api-key":i};return{...e,headers:o}}),a}function E(e,t){if(t&&Object.prototype.hasOwnProperty.call(t,`access_key`))throw Error(`${e} \u4E0D\u5141\u8BB8\u8BBE\u7F6E access_key`)}var D=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 O(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function k(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 A=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=j(n),Object.setPrototypeOf(this,e.prototype)}};function j(e){let{headers:t,...n}=e;return{...n,headers:M(t)}}function M(e){if(!e)return;let t=k(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 N=class{defaultConfig;securityConfig;interceptors={request:new D,response:new D};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=w(t)),T(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 O(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 O(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 O(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=k(this.defaultConfig.headers),n=k(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=k(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 A(e,a);return this.runResponseInterceptors(Promise.reject(t))}return this.runResponseInterceptors(Promise.resolve(e))}catch(e){clearTimeout(l);let t=e instanceof A?e:new A(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}}}))();function _(){let e=process.env.app_id,t=process.env.FORCE_AUTHN_INNERAPI_DOMAIN,n=process.env.FORCE_AUTHN_ACCESS_SECRET,r=process.env.FORCE_AUTHN_ACCESS_KEY;if(!e||!t||!n||!r)throw Error(`studio-client: 缺少环境变量,需要: app_id, FORCE_AUTHN_INNERAPI_DOMAIN, FORCE_AUTHN_ACCESS_KEY, FORCE_AUTHN_ACCESS_SECRET`);return{appId:e,apiUrl:t,accessSecret:n,accessKey:r}}function v(...e){for(let t of e)if(typeof t==`string`&&t)return t}async function*y(e){let t=new TextDecoder,n=``;for await(let r of e){n+=t.decode(r,{stream:!0});let e=n.split(`
1
+ import{createRequire as e}from"node:module";import{spawn as t}from"node:child_process";import n from"node:fs";import r,{join as i,resolve as a}from"node:path";import{appendFile as o,mkdir as s,readFile as c,readdir as l,rm as u,writeFile as d}from"node:fs/promises";var f=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),p=e(import.meta.url);function m(){return a(process.env.OPENCLAW_STATE_DIR||process.cwd())}function h(e){return i(e,`workspace`)}function g(e,t){return i(e,`workspace`,t)}function _(e){return i(e,`.agent`)}var v=f(((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,HttpClient:()=>N,HttpError:()=>A,generateJWTToken:()=>h,parseJWTTokenWithVerify:()=>g,registerPlatformPlugin:()=>T,resolvePlatformBaseURL:()=>w}),t.exports=c(l);var u=p(`crypto`),d=1800*1e3,f=60,m={alg:`HS256`,typ:`JWT`};function h(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(m)),a=v(JSON.stringify(r));return`${i}.${a}.${_(`${i}.${a}`,t.secretKey)}`}function g(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=h(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=`FORCE_AUTHN_INNERAPI_DOMAIN`,S=`FORCE_AUTHN_ACCESS_KEY`,C=`FORCE_AUTHN_ACCESS_SECRET`;function w(e){if(!e?.enabled)return e?.baseURL;if(e.baseURL)return e.baseURL;let t=e.domainEnv||x,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 T(e,t){if(!t.enabled)return;E(`platform.defaultClaims`,t.defaultClaims);let n=t.accessKeyEnv||S,r=t.secretKeyEnv||C,i=t.accessKey??(process.env[n]||``),a=new b({accessKey:i,secretKey:t.secretKey??(process.env[r]||``),expireTimeMs:t.expireTimeMs??d,refreshBeforeMs:t.refreshBeforeMs});return e.request.use(e=>{E(`request.platformAuth.customClaims`,e.platformAuth?.customClaims);let n={...t.defaultClaims||{},...e.platformAuth?.customClaims||{},access_key:i},r=a.getToken(n),o={...e.headers,Authorization:`Bearer ${r}`,"x-api-key":i};return{...e,headers:o}}),a}function E(e,t){if(t&&Object.prototype.hasOwnProperty.call(t,`access_key`))throw Error(`${e} \u4E0D\u5141\u8BB8\u8BBE\u7F6E access_key`)}var D=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 O(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function k(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 A=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=j(n),Object.setPrototypeOf(this,e.prototype)}};function j(e){let{headers:t,...n}=e;return{...n,headers:M(t)}}function M(e){if(!e)return;let t=k(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 N=class{defaultConfig;securityConfig;interceptors={request:new D,response:new D};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=w(t)),T(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 O(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 O(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 O(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=k(this.defaultConfig.headers),n=k(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=k(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 A(e,a);return this.runResponseInterceptors(Promise.reject(t))}return this.runResponseInterceptors(Promise.resolve(e))}catch(e){clearTimeout(l);let t=e instanceof A?e:new A(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}}}))();function y(){let e=process.env.app_id,t=process.env.FORCE_AUTHN_INNERAPI_DOMAIN,n=process.env.FORCE_AUTHN_ACCESS_SECRET,r=process.env.FORCE_AUTHN_ACCESS_KEY;if(!e||!t||!n||!r)throw Error(`studio-client: 缺少环境变量,需要: app_id, FORCE_AUTHN_INNERAPI_DOMAIN, FORCE_AUTHN_ACCESS_KEY, FORCE_AUTHN_ACCESS_SECRET`);return{appId:e,apiUrl:t,accessSecret:n,accessKey:r}}function b(...e){for(let t of e)if(typeof t==`string`&&t)return t}async function*x(e){let t=new TextDecoder,n=``;for await(let r of e){n+=t.decode(r,{stream:!0});let e=n.split(`
2
2
 
3
3
  `);n=e.pop();for(let t of e){let e=t.split(`
4
- `).find(e=>e.startsWith(`data:`));if(!e)continue;let n=e.slice(5).trim();if(!(!n||n===`[DONE]`))try{yield JSON.parse(n)}catch{}}}}var b=class{#e;#t;constructor(){this.#t=_(),this.#e=new g.HttpClient({baseURL:this.#t.apiUrl,timeout:9e5,platform:{enabled:!0,baseURL:this.#t.apiUrl,accessKey:this.#t.accessKey,secretKey:this.#t.accessSecret,defaultClaims:{appId:this.#t.appId}}})}async createSubApp(e={}){let t=`${this.#t.apiUrl}/api/v1/studio/innerapi/openclaw/sub_app/create`,n={};e.name&&(n.name=e.name),e.description&&(n.description=e.description);let r;try{r=await this.#e.post(t,n,{headers:{"Content-Type":`application/json`}})}catch(e){if(e instanceof g.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 Error(`createSubApp 服务错误 (logID: ${i}): ${a.error_msg}`);let o=v(a.appID,a.appId,a.app_id,a.data?.appID,a.data?.appId,a.data?.app_id),s=v(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;try{r=await this.#e.post(t,n,{headers:{"Content-Type":`application/json`,Accept:`text/event-stream`}})}catch(e){if(e instanceof g.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:r.headers?.get?.(`x-tt-logid`)??``},yield*y(r.body)}};const x=`.spark`,S=`meta.json`;function C(e){return i(e,x,S)}async function w(e){try{return JSON.parse(await c(C(e),`utf8`))}catch{return null}}async function T(e){for(let t of[i(e,`..`,`..`,x,S),i(e,`..`,x,S)])try{return JSON.parse(await c(t,`utf8`))}catch{}return null}async function E(e,t){await s(i(e,x),{recursive:!0});let n=await w(e),r={...n??{},...t};if(!n){let t=await T(e);if(t)for(let[e,n]of Object.entries(t))e in r||(r[e]=n)}await l(C(e),JSON.stringify(r,null,2),`utf8`)}const D=`.spark`;function O(e){return i(e,D,`progress.txt`)}async function k(e){await s(i(e,D),{recursive:!0}),await l(O(e),``,`utf8`)}async function A(e,t){await o(O(e),`[${new Date().toISOString()}] ${t}\n`,`utf8`)}function j(e){return e?.data?.message??e?.Data?.Message??null}async function*M(e){let{cwd:t,task:n,name:r,description:i,signal:a}=e;try{let e=new b;await k(t),await A(t,`已接收需求`),yield{type:`progress`,message:`已接收需求`};let o=await w(t),s=!o?.appId,c=new Date().toISOString();if(s){let{appID:n,conversationID:a,logId:s}=await e.createSubApp({name:r,description:i});o={appId:n,conversationId:a,createdAt:c,updatedAt:c},await E(t,o),await A(t,`应用已创建(appId: ${n})`),yield{type:`app_created`,appId:n,conversationId:a,logId:s}}await A(t,`正在处理`),yield{type:`progress`,message:`正在处理`};let l=``,u=!1,d;for await(let r of e.streamChat({appID:o.appId,message:n,workDir:t})){if(a?.aborted){await A(t,`失败: cancelled`),yield{type:`failed`,error:`cancelled`};return}let e=r;if(e?.__meta){l=e.logId||``;continue}let n=j(e),i=n?.status,o=n?.type,s=n?.role,c=typeof n?.content==`string`?n.content:``,f=n?.delta?.type;!u&&s===`assistant`&&(i===`in_progress`||f===`text`)&&(u=!0,await A(t,`正在输出结果`),yield{type:`progress`,message:`正在输出结果`,logId:l||void 0}),!(s!==`assistant`||o!==`message`||i!==`completed`||!c)&&(d=c)}if(a?.aborted){await A(t,`失败: cancelled`),yield{type:`failed`,error:`cancelled`};return}let f=new Date().toISOString();await E(t,{...o,updatedAt:f,finalText:d}),await A(t,`处理完成`),yield{type:`completed`,appId:o.appId,logId:l||void 0,finalText:d}}catch(e){let n=e instanceof Error?e.message:String(e);try{await k(t),await A(t,`已接收需求`),await A(t,`失败: ${n}`)}catch{}yield{type:`failed`,error:n,logId:e?.logId}}}const N=/^[a-z0-9][a-z0-9-]{0,63}$/,P=new Set([`.`,`..`,`con`,`nul`,`prn`,`aux`]);function F(e){let t=e?.config?.plugins?.entries?.[`openclaw-extension-miaoda-coding`]?.config??{};return{verbose:t?.verbose===!0,hooks:{allowPromptInjection:t?.hooks?.allowPromptInjection!==!1}}}function I(e,n){if(!e)return;let i=r.dirname(process.execPath),a=t(`openclaw`,[`message`,`send`,`--channel`,`feishu`,`--target`,e,`--message`,n],{stdio:[`ignore`,`ignore`,`pipe`],env:{...process.env,PATH:`${i}:${process.env.PATH||``}`}});a.stderr.on(`data`,()=>{}),a.on(`close`,()=>{}),a.unref()}function L(e){return String(e??``).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,``).replace(/\s+/g,` `).trim()}function R(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 z(e){let t=L(e);if(!B(t))throw Error(`project_id 不合法: "${t}"`);return t}function B(e){return N.test(e)&&!P.has(e)}function V(e){if(e==null||e===``)return;let t=L(e);if(!/^(user|chat|thread):\S+$/.test(t))throw Error(`target 不合法`);return t}function H(e,t,n){if(e==null||e===``)return;let r=L(e);if(r){if(r.length>n)throw Error(`${t} 不能超过 ${n} 个字符`);return r}}function U(e){try{return n.readFileSync(e,`utf8`).split(`
5
- `).map(e=>e.trim()).filter(Boolean)}catch{return[]}}function W(e){return e.replace(/^\[[^\]]+\]\s*/,``)}function G(e){return e.replace(/应用已创建(appId:\s*[^)]+)/g,`应用已创建`)}function K(e){return e.slice(-3).map(W).map(G).join(` | `)||`暂无进度记录`}function q(e){let t=K(e);return t.includes(`失败:`)?`failed`:t.includes(`处理完成`)?`completed`:t===`暂无进度记录`?`pending`:`running`}var J={id:`openclaw-extension-miaoda-coding`,name:`妙搭`,description:`妙搭制品创建与修改`,register(e){let t=f(),i=p(t),a={verbose:!1,hooks:{allowPromptInjection:!0}};e.on(`before_prompt_build`,(e,t)=>{if(F(t).hooks.allowPromptInjection)return{prependSystemContext:[`【硬规则 — 妙搭】`,`当用户要求创建或修改应用、网页、页面时,必须通过 sessions_spawn(runtime="subagent", mode="run") 交给妙搭。`,`读 miaoda-coding skill 后严格按指示操作。只传 runtime、mode、task 三个参数,不要传 streamTo/sandbox 等。`,`绝对不要自己写代码或用 exec 生成文件。如果 spawn 失败,重试而不是 fallback。`,`不要用 sessions_history 读取子 agent 的 session,查进度用 miaoda_check_progress tool。`].join(` `)}}),e.registerTool({name:`list_projects`,label:`列出妙搭项目`,description:`列出已创建的妙搭项目,返回 project_id、appId、updatedAt、finalText、status 和进度摘要。修改已有项目时必须先调此工具。`,parameters:{type:`object`,properties:{}},async execute(){let e=[];try{e=n.readdirSync(i,{withFileTypes:!0}).filter(e=>e.isDirectory()&&B(e.name)).map(e=>{let t=r.join(i,e.name,`.spark`,`meta.json`),a=r.join(i,e.name,`.spark`,`progress.txt`),o=null;try{o=JSON.parse(n.readFileSync(t,`utf8`))}catch{}let s=U(a);return{project_id:e.name,appId:o?.appId??null,updatedAt:o?.updatedAt??null,finalText:o?.finalText??null,status:q(s),progressSummary:K(s)}})}catch{}return{content:[{type:`text`,text:JSON.stringify({projects:e},null,2)}]}}}),e.registerTool({name:`miaoda_check_progress`,label:`查看妙搭项目进度`,description:`查看妙搭项目的生成进度。用户问"做到哪了"、"进度如何"、"好了吗"时调此工具。`,parameters:{type:`object`,properties:{project_id:{type:`string`,description:`项目目录名(从 list_projects 获取)`}},required:[`project_id`]},async execute(e,t){try{let e=z(t.project_id),a=r.join(i,e,`.spark`,`progress.txt`),o=n.readFileSync(a,`utf8`).trim().split(`
6
- `).filter(Boolean),s=null;try{s=JSON.parse(n.readFileSync(r.join(i,e,`.spark`,`meta.json`),`utf8`))}catch{}let c=!!s?.finalText,l=o.slice(-20).map(G).join(`
7
- `);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:t.project_id,message:`暂无进度记录`,delivery_ready:!1,finalTextAvailable:!1,note:`miaoda_check_progress 只用于查看进度,不能根据 appId 或进度文本推导预览链接;最终链接只以 subagent completion result 中的 output/finalText 为准。`})}]}}}});let o=900*1e3;e.registerTool({name:`miaoda_coding`,label:`妙搭`,description:`创建或修改妙搭应用/网页。修改已有项目时必须先调 list_projects 获取 project_id,再调此工具。返回结构化 JSON(status/appId/finalText/output/meta)。`,parameters:{type:`object`,properties:{task:{type:`string`,description:`用户的需求描述(如"帮我写一个网页,展示数字 7")`},project_id:{type:`string`,description:`本地项目目录名,仅允许小写字母、数字和短横线`},target:{type:`string`,description:`飞书消息推送目标(如 user:ou_xxx)`},name:{type:`string`,description:`创建新应用时的人类可读名称`},description:{type:`string`,description:`创建新应用时的单行简介`}},required:[`task`,`project_id`]},async execute(e,i){try{let e=R(i.task),s=z(i.project_id),c=V(i.target),l=H(i.name,`name`,60),u=H(i.description,`description`,120),d=m(t,s);n.mkdirSync(d,{recursive:!0});let f=r.join(d,`.agent`);if(!n.existsSync(f)){let e=h(t);if(n.existsSync(e))try{n.symlinkSync(e,f)}catch{}}let p=[],g,_=!1,v=null,y=new AbortController,b=setTimeout(()=>y.abort(),o);try{for await(let t of M({cwd:d,task:e,name:l,description:u,signal:y.signal})){if(`logId`in t&&t.logId&&p.push(t.logId),a.verbose&&c)switch(t.type){case`app_created`:I(c,`应用已创建(appId: ${t.appId})`);break;case`failed`:I(c,`处理失败: ${t.error}`);break}t.type===`completed`&&(v={appId:t.appId,finalText:t.finalText}),t.type===`failed`&&(g=t.error)}}catch{}clearTimeout(b),_=y.signal.aborted;let x=null;try{x=JSON.parse(n.readFileSync(r.join(d,`.spark`,`meta.json`),`utf8`))}catch{}let S={status:_?`timeout`:g?`error`:`ok`,project_id:s,appId:v?.appId??x?.appId??null,finalText:v?.finalText??x?.finalText,output:v?.finalText??x?.finalText,logIds:p.length?p:void 0,meta:x};return _?S.error=`执行超时(${o/1e3}秒)`:g&&(S.error=g),{content:[{type:`text`,text:JSON.stringify(S,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=F(e)},async stop(){}})}};export{J as default};
4
+ `).find(e=>e.startsWith(`data:`));if(!e)continue;let n=e.slice(5).trim();if(!(!n||n===`[DONE]`))try{yield JSON.parse(n)}catch{}}}}var S=class{#e;#t;constructor(){this.#t=y(),this.#e=new v.HttpClient({baseURL:this.#t.apiUrl,timeout:9e5,platform:{enabled:!0,baseURL:this.#t.apiUrl,accessKey:this.#t.accessKey,secretKey:this.#t.accessSecret,defaultClaims:{appId:this.#t.appId}}})}async createSubApp(e={}){let t=`${this.#t.apiUrl}/api/v1/studio/innerapi/openclaw/sub_app/create`,n={};e.message&&(n.message=e.message);let r;try{r=await this.#e.post(t,n,{headers:{"Content-Type":`application/json`}})}catch(e){if(e instanceof v.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 Error(`createSubApp 服务错误 (logID: ${i}): ${a.error_msg}`);let o=b(a.appID,a.appId,a.app_id,a.data?.appID,a.data?.appId,a.data?.app_id),s=b(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;try{r=await this.#e.post(t,n,{headers:{"Content-Type":`application/json`,Accept:`text/event-stream`}})}catch(e){if(e instanceof v.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:r.headers?.get?.(`x-tt-logid`)??``},yield*x(r.body)}};const C=`.spark`,w=`meta.json`;function T(e){return i(e,C,w)}async function E(e){try{return JSON.parse(await c(T(e),`utf8`))}catch{return null}}async function D(e){for(let t of[i(e,`..`,`..`,C,w),i(e,`..`,C,w)])try{return JSON.parse(await c(t,`utf8`))}catch{}return null}async function O(e,t){await s(i(e,C),{recursive:!0});let n=await E(e),r={...n??{},...t};if(!n){let t=await D(e);if(t)for(let[e,n]of Object.entries(t))e in r||(r[e]=n)}await d(T(e),JSON.stringify(r,null,2),`utf8`)}const k=`.spark`;function A(e){return i(e,k,`progress.txt`)}async function j(e){await s(i(e,k),{recursive:!0}),await d(A(e),``,`utf8`)}async function M(e,t){await o(A(e),`[${new Date().toISOString()}] ${t}\n`,`utf8`)}function N(e){return e?.data?.message??e?.Data?.Message??null}async function*P(e){let{cwd:t,task:n,signal:r}=e;try{let e=new S;await j(t),await M(t,`已接收需求`),yield{type:`progress`,message:`已接收需求`};let i=await E(t),a=!i?.appId,o=new Date().toISOString();if(a){let{appID:r,conversationID:a,logId:s}=await e.createSubApp({message:n});i={appId:r,conversationId:a,createdAt:o,updatedAt:o},await O(t,i),await M(t,`应用已创建(appId: ${r})`),yield{type:`app_created`,appId:r,conversationId:a,logId:s}}await M(t,`正在处理`),yield{type:`progress`,message:`正在处理`};let s=``,c=!1,l;for await(let a of e.streamChat({appID:i.appId,message:n,workDir:t})){if(r?.aborted){await M(t,`失败: cancelled`),yield{type:`failed`,error:`cancelled`};return}let e=a;if(e?.__meta){s=e.logId||``;continue}let n=N(e),i=n?.status,o=n?.type,u=n?.role,d=typeof n?.content==`string`?n.content:``,f=n?.delta?.type;!c&&u===`assistant`&&(i===`in_progress`||f===`text`)&&(c=!0,await M(t,`正在输出结果`),yield{type:`progress`,message:`正在输出结果`,logId:s||void 0}),!(u!==`assistant`||o!==`message`||i!==`completed`||!d)&&(l=d)}if(r?.aborted){await M(t,`失败: cancelled`),yield{type:`failed`,error:`cancelled`};return}let u=new Date().toISOString();await O(t,{...i,updatedAt:u,finalText:l}),await M(t,`处理完成`),yield{type:`completed`,appId:i.appId,logId:s||void 0,finalText:l}}catch(e){let n=e instanceof Error?e.message:String(e);try{await j(t),await M(t,`已接收需求`),await M(t,`失败: ${n}`)}catch{}yield{type:`failed`,error:n,logId:e?.logId}}}const F=[`research`,`design`,`feedback`];function I(e){return e.replace(/[^a-zA-Z0-9\u4e00-\u9fff-]/g,`-`).replace(/-+/g,`-`).replace(/^-|-$/g,``).slice(0,60)||`reference`}async function L(e){let t;try{t=await l(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 R(e){try{let t=await c(i(e,`reference`,`manifest.json`),`utf8`);return JSON.parse(t)}catch{return{updatedAt:new Date().toISOString(),files:[]}}}async function z(e,t){await s(i(e,`reference`),{recursive:!0}),await d(i(e,`reference`,`manifest.json`),JSON.stringify(t,null,2),`utf8`)}async function B(e){let{appCwd:t,category:n,content:r,filename:a,mode:o=`append`}=e,c=i(t,`reference`,n);if(o===`replace`)try{await u(c,{recursive:!0,force:!0})}catch{}await s(c,{recursive:!0});let l=await L(c),f=`${n}/${`${String(l).padStart(3,`0`)}-${I(a??`reference`)}.md`}`,p=`reference/${f}`;await d(i(t,p),r,`utf8`);let m=await R(t);return o===`replace`&&(m.files=m.files.filter(e=>e.category!==n)),m.files.push({path:f,category:n,writtenAt:new Date().toISOString()}),m.updatedAt=new Date().toISOString(),await z(t,m),{relativePath:p,category:n,seq:l}}async function V(e){let t=i(e,`reference`),n;try{n=await l(t)}catch{return{totalFiles:0,categories:{}}}let r={},a=0;for(let e of n){if(!F.includes(e))continue;let n;try{n=(await l(i(t,e))).filter(e=>e.endsWith(`.md`)).sort()}catch{continue}n.length>0&&(r[e]={count:n.length,files:n},a+=n.length)}return{totalFiles:a,categories:r}}async function H(e){return(await V(e)).totalFiles>0}const U=/^[a-z0-9][a-z0-9-]{0,63}$/,W=new Set([`.`,`..`,`con`,`nul`,`prn`,`aux`]);function G(e){let t=e?.config?.plugins?.entries?.[`openclaw-extension-miaoda-coding`]?.config??{};return{verbose:t?.verbose===!0,hooks:{allowPromptInjection:t?.hooks?.allowPromptInjection!==!1}}}function K(e,n){if(!e)return;let i=r.dirname(process.execPath),a=t(`openclaw`,[`message`,`send`,`--channel`,`feishu`,`--target`,e,`--message`,n],{stdio:[`ignore`,`ignore`,`pipe`],env:{...process.env,PATH:`${i}:${process.env.PATH||``}`}});a.stderr.on(`data`,()=>{}),a.on(`close`,()=>{}),a.unref()}function q(e){return String(e??``).replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g,``).replace(/\s+/g,` `).trim()}function J(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=q(e);if(!X(t))throw Error(`project_id 不合法: "${t}"`);return t}function X(e){return U.test(e)&&!W.has(e)}function Z(e){if(e==null||e===``)return;let t=q(e);if(!/^(user|chat|thread):\S+$/.test(t))throw Error(`target 不合法`);return t}function ee(e){try{return n.readFileSync(e,`utf8`).split(`
5
+ `).map(e=>e.trim()).filter(Boolean)}catch{return[]}}function te(e){return e.replace(/^\[[^\]]+\]\s*/,``)}function Q(e){return e.replace(/应用已创建(appId:\s*[^)]+)/g,`应用已创建`)}function $(e){return e.slice(-3).map(te).map(Q).join(` | `)||`暂无进度记录`}function ne(e){let t=$(e);return t.includes(`失败:`)?`failed`:t.includes(`处理完成`)?`completed`:t===`暂无进度记录`?`pending`:`running`}var re={id:`openclaw-extension-miaoda-coding`,name:`妙搭`,description:`妙搭制品创建与修改`,register(e){let t=m(),i=h(t),a={verbose:!1,hooks:{allowPromptInjection:!0}},o=new Set([`miaoda_coding`]),s=new Set([`miaoda_write_reference`,`miaoda_check_progress`]);e.on(`before_tool_call`,(e,t)=>{let n=e.toolName,r=(t?.sessionKey??``).toLowerCase().split(`:`).includes(`subagent`);if(o.has(n)&&!r)return{block:!0,blockReason:`${n} 只能由 subagent 调用。请通过 sessions_spawn(runtime="subagent", mode="run") 派活,不要直接调用此工具。读 miaoda-coding skill 了解完整流程。`};if(s.has(n)&&r)return{block:!0,blockReason:`${n} 不能由 subagent 调用。subagent 不应直接操作参考资料或查询进度。`}}),e.on(`before_prompt_build`,(e,t)=>{if(G(t).hooks.allowPromptInjection)return{prependSystemContext:[`【硬规则 — 妙搭】`,`当用户要求创建或修改应用、网页、页面时,必须通过 sessions_spawn(runtime="subagent", mode="run") 交给妙搭。`,`读 miaoda-coding skill 后严格按指示操作。只传 runtime、mode、task 三个参数,不要传 streamTo/sandbox 等。`,`绝对不要自己写代码或用 exec 生成文件。如果 spawn 失败,重试而不是 fallback。`,`如果对话中有需要传递给 code agent 的上下文(研究结果、用户偏好等),先调 miaoda_write_reference 写入 reference 目录,再 spawn。用户反悔或需要替换之前的上下文时,使用 miaoda_write_reference 的 mode=replace。妙搭只做制品生成,不做研究。`,`不要用 sessions_history 读取子 agent 的 session,查进度用 miaoda_check_progress tool。`,`subagent 完成后只发一条消息(总结+预览链接),严禁发多条。不要调 message tool。预览链接必须使用加粗 Markdown 链接格式 **[url](url)**,不要发裸 URL。`].join(` `)}}),e.registerTool({name:`list_projects`,label:`列出妙搭项目`,description:`列出已创建的妙搭项目,返回 project_id、appId、updatedAt、finalText、status 和进度摘要。修改已有项目时必须先调此工具。`,parameters:{type:`object`,properties:{}},async execute(){let e=[];try{e=n.readdirSync(i,{withFileTypes:!0}).filter(e=>e.isDirectory()&&X(e.name)).map(e=>{let t=r.join(i,e.name,`.spark`,`meta.json`),a=r.join(i,e.name,`.spark`,`progress.txt`),o=null;try{o=JSON.parse(n.readFileSync(t,`utf8`))}catch{}let s=ee(a);return{project_id:e.name,appId:o?.appId??null,updatedAt:o?.updatedAt??null,finalText:o?.finalText??null,status:ne(s),progressSummary:$(s)}})}catch{}return{content:[{type:`text`,text:JSON.stringify({projects:e},null,2)}]}}}),e.registerTool({name:`miaoda_check_progress`,label:`查看妙搭项目进度`,description:`查看妙搭项目的生成进度。用户问"做到哪了"、"进度如何"、"好了吗"时调此工具。`,parameters:{type:`object`,properties:{project_id:{type:`string`,description:`项目目录名(从 list_projects 获取)`}},required:[`project_id`]},async execute(e,t){try{let e=Y(t.project_id),a=r.join(i,e,`.spark`,`progress.txt`),o=n.readFileSync(a,`utf8`).trim().split(`
6
+ `).filter(Boolean),s=null;try{s=JSON.parse(n.readFileSync(r.join(i,e,`.spark`,`meta.json`),`utf8`))}catch{}let c=!!s?.finalText,l=o.slice(-20).map(Q).join(`
7
+ `);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:t.project_id,message:`暂无进度记录`,delivery_ready:!1,finalTextAvailable:!1,note:`miaoda_check_progress 只用于查看进度,不能根据 appId 或进度文本推导预览链接;最终链接只以 subagent completion result 中的 output/finalText 为准。`})}]}}}}),e.registerTool({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(!F.includes(n.category))throw Error(`category 不合法,必须是 ${F.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=g(t,e),o=await B({appCwd:a,category:n.category,content:r,filename:n.filename,mode:i}),s=await V(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({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)`}},required:[`generation_request`,`project_id`]},async execute(e,i){try{let e=J(i.generation_request),o=Y(i.project_id),s=Z(i.target),l=g(t,o);n.mkdirSync(l,{recursive:!0});let u=await H(l)?`工作目录下有 reference/ 目录,包含参考资料,请先查阅再开始工作。\n\n${e}`:e,d=r.join(l,`.agent`);if(!n.existsSync(d)){let e=_(t);if(n.existsSync(e))try{n.symlinkSync(e,d)}catch{}}let f=[],p,m=!1,h=null,v=new AbortController,y=setTimeout(()=>v.abort(),c);try{for await(let e of P({cwd:l,task:u,signal:v.signal})){if(`logId`in e&&e.logId&&f.push(e.logId),a.verbose&&s)switch(e.type){case`app_created`:K(s,`应用已创建(appId: ${e.appId})`);break;case`failed`:K(s,`处理失败: ${e.error}`);break}e.type===`completed`&&(h={appId:e.appId,finalText:e.finalText}),e.type===`failed`&&(p=e.error)}}catch{}clearTimeout(y),m=v.signal.aborted;let b=null;try{b=JSON.parse(n.readFileSync(r.join(l,`.spark`,`meta.json`),`utf8`))}catch{}let x={status:m?`timeout`:p?`error`:`ok`,project_id:o,appId:h?.appId??b?.appId??null,finalText:h?.finalText??b?.finalText,output:h?.finalText??b?.finalText,logIds:f.length?f:void 0,meta:b};return m?x.error=`执行超时(${c/1e3}秒)`:p&&(x.error=p),{content:[{type:`text`,text:JSON.stringify(x,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=G(e)},async stop(){}})}};export{re 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.1",
3
+ "version": "1.0.3-beta.0",
4
4
  "description": "妙搭 Coding Agent — 通过 AI 创建和修改妙搭应用",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -1,12 +1,49 @@
1
1
  ---
2
2
  name: miaoda-coding
3
- description: 当用户想创建或修改任何东西(应用、网页、页面、PPT、幻灯片、演示文稿)或提到"妙搭"、"vibe coding"、"做一个"、"帮我写"、"加个"、"修改"、"更新"、"改一下"、"优化"时,必须读此 skill 后严格按指示操作。绝对不要自己写代码或 exec 命令,必须通过 sessions_spawn + miaoda_coding tool 完成。
3
+ description: 用户要求创建或修改应用、网页、页面、PPT、幻灯片、演示文稿,或提到"妙搭"、"vibe coding"时适用。
4
4
  user-invocable: false
5
5
  ---
6
6
 
7
7
  # 妙搭
8
8
 
9
- **重要:任何涉及创建或修改应用、网页、代码、PPT、幻灯片、演示文稿的请求,都必须通过 sessions_spawn 交给妙搭。绝对不要自己动手写代码。**
9
+ **重要:创建或修改应用、网页、PPT 等制品的执行环节,必须通过 sessions_spawn 交给妙搭。绝对不要自己动手写代码。**
10
+
11
+ ## 判断是否需要先做前置工作
12
+
13
+ 妙搭只负责**生成制品**(网页、应用、PPT),不负责搜索、分析、总结等研究工作。
14
+
15
+ 收到用户请求后,先判断:
16
+
17
+ - **纯建站/改站请求**(如"做一个计算器"、"改一下颜色")→ 直接派活给妙搭
18
+ - **对话中有需要传递给 code agent 的上下文**(之前讨论的调研结果、用户偏好、素材等)→ 先调 `miaoda_write_reference` 写入参考资料,再派活
19
+ - **复合请求**(如"先搜集 XX 资料,再生成网站")→ 主 agent 先完成研究,**整理为摘要+关键原文引用**,调 `miaoda_write_reference` 写入,再派活
20
+
21
+ **错误做法**:把"先搜集再生成"的整个需求原样丢给妙搭——妙搭不会搜索,只会基于给定内容生成页面。
22
+
23
+ ### 写入参考资料
24
+
25
+ 用 `miaoda_write_reference` 将上下文写入项目的 reference 目录:
26
+
27
+ 参数:
28
+ - `project_id`: 项目 ID
29
+ - `category`: `"research"`(调研结果)、`"design"`(设计要求)、`"feedback"`(用户反馈)
30
+ - `content`: Markdown 格式的参考资料(摘要+关键原文引用)
31
+ - `filename`: 可选,自定义文件名
32
+ - `mode`: `"append"`(默认,追加)或 `"replace"`(替换该 category 全部内容,用于用户反悔/调整)
33
+
34
+ 整理原则:
35
+ - 结构化摘要:提炼对话中的关键结论、决策、需求
36
+ - 保留关键原文:用户的原话、重要数据、具体要求原样引用
37
+ - 不要把整段对话历史塞进去,提炼有价值的信息
38
+
39
+ ### 调用者边界
40
+
41
+ | Tool | 谁调 |
42
+ |------|------|
43
+ | `miaoda_write_reference` | 主 agent(non-subagent 会话) |
44
+ | `miaoda_coding` | subagent |
45
+ | `miaoda_check_progress` | 主 agent(non-subagent 会话) |
46
+ | `list_projects` | 主 agent 或 subagent |
10
47
 
11
48
  ## 派活
12
49
 
@@ -14,19 +51,25 @@ user-invocable: false
14
51
 
15
52
  - `runtime`: `"subagent"`
16
53
  - `mode`: `"run"`
17
- - `task`: 按下面模板填写(替换 `<用户需求>`、`<sender_id>` 和 `<project_id>`)
54
+ - `task`: 按下面模板填写
18
55
 
19
56
  如果 sessions_spawn 返回错误,去掉多余参数后重试,**绝对不要 fallback 到自己写代码**。
20
57
 
21
58
  **创建新项目:**
59
+
60
+ 1. 如有上下文需要传递,先调 `miaoda_write_reference`
61
+ 2. 调 `sessions_spawn`,task 内容:
62
+
22
63
  ```
23
64
  调用 miaoda_coding tool,参数:
24
- - task: "<用户需求>"
65
+ - generation_request: "<生成指令>"
25
66
  - project_id: "<project_id>"
26
67
  - name: "<面向人类可读的应用名称>"
27
68
  - description: "<根据用户需求整理的一句话简介,单行,80 字以内>"
28
69
  - target: "user:<sender_id>"
29
70
 
71
+ 如果 reference/ 目录已有参考资料,tool 会自动提示 code agent 查阅。
72
+
30
73
  tool 会返回结构化 JSON(status/appId/finalText/output 等)。
31
74
 
32
75
  如果结果里有 `output`,announce 给主 agent 时优先带 `output` 原文,不要自己改写预览链接,不要提前拼 `mode=sidebar-semi`。
@@ -35,13 +78,17 @@ tool 会返回结构化 JSON(status/appId/finalText/output 等)。
35
78
  ```
36
79
 
37
80
  **修改已有项目:**
81
+
82
+ 1. 如有新反馈/调整,先调 `miaoda_write_reference`(category="feedback",mode 按需选 append 或 replace)
83
+ 2. 调 `sessions_spawn`,task 内容:
84
+
38
85
  ```
39
86
  你只能调用以下两个 tool,按顺序执行,不得使用任何其他 tool(exec、ls、read 等均禁止):
40
87
 
41
88
  1. 调用 list_projects tool,无需任何参数。
42
89
  2. 从返回的 projects 数组中找到与用户需求匹配的项目,取其 project_id。
43
90
  3. 调用 miaoda_coding tool,参数:
44
- - task: "<用户需求>"
91
+ - generation_request: "<修改要求>"
45
92
  - project_id: "<上一步取到的 project_id>"
46
93
  - target: "user:<sender_id>"
47
94
 
@@ -61,19 +108,17 @@ tool 会返回结构化 JSON(status/appId/finalText/output 等)。
61
108
 
62
109
  ## 你(主 agent)的行为
63
110
 
64
- 1. 读完这个 skill 后,只做一件事:调 sessions_spawn
65
- 2. 回复用户"交给妙搭了,稍等"
66
- 3. **不要** 调 sessions_history、subagents、或任何 poll 操作
67
- 4. **不要** 自己执行任何命令
68
- 5. subagent announce 回来后,如果结果里有 `output` / `finalText`:
69
- - 优先从其中提取 `预览链接:<url>`
70
- - 由主 agent 自己给这个链接补 `mode=sidebar-semi`
71
- - 如果成功提取到预览链接,先单独发送一条只包含补好 query 后的原始 URL 的消息;不要加 `预览链接:` 前缀,不要做 `[url](url)` 包裹
72
- - 然后再发送总结消息
73
- - 总结消息里只要再次出现预览链接,也必须使用原样 Markdown 链接格式 `[<url>](<url>)`
74
- - 不允许直接输出裸 URL,不允许写成 `预览链接:https://...`
75
- - 预览消息和总结消息都不要提系统、子任务、announce 等内部细节
76
- 6. `miaoda_check_progress` 只用于查看进度,绝对不要根据 `appId`、进度文本或固定域名模式自己猜测预览链接
77
- 7. 如果 subagent completion event 还没到,即使进度里出现“处理完成”,也只能回复进度状态,不能提前发最终链接
78
- 8. 如果没找到预览链接,就按无链接结果回复,不要猜
79
- 9. 收到 announce 后回复 `NO_REPLY`(最终用户消息由主 agent 统一处理)
111
+ 1. 读完这个 skill 后,判断是否需要写参考资料,需要则调 `miaoda_write_reference`
112
+ 2. 调 sessions_spawn
113
+ 3. 回复用户"交给妙搭了,稍等"
114
+ 4. **不要** 调 sessions_history、subagents、或任何 poll 操作
115
+ 5. **不要** 自己执行任何命令
116
+ 6. subagent announce 回来后,**只发一条消息**,包含:
117
+ - 简短总结(做了什么、包含哪些内容)
118
+ - 如果结果里有预览链接(`output` / `finalText` 中提取),补上 `mode=sidebar-semi` query 参数后,以加粗 Markdown 链接格式 `**[<url>](<url>)**` 放在总结里
119
+ - 如果没找到预览链接,就按无链接结果回复,不要猜
120
+ - **严禁发多条消息**:不要先发链接再发总结,不要发完总结再补链接
121
+ - 不要提系统、子任务、announce、subagent 等内部细节
122
+ 7. `miaoda_check_progress` 只用于用户主动问进度时使用,绝对不要根据 `appId`、进度文本或固定域名模式自己猜测预览链接
123
+ 8. 如果 subagent completion event 还没到,即使进度里出现"处理完成",也只能回复进度状态,不能提前发最终链接
124
+ 9. **不要** message tool 自己推送消息,所有回复通过正常对话投递