@zhama/tego-plugin-sdk 1.36.0 → 1.36.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -3
- package/bin/tego-plugin.mjs +2 -2
- package/dist/chunk-2ORO36BY.js +1 -0
- package/dist/chunk-KMN5RHLX.cjs +1 -0
- package/dist/{chunk-3KUOBGJN.js → chunk-P2Q2AKWP.js} +13 -13
- package/dist/{chunk-THMWRUAT.cjs → chunk-PWSTCQRN.cjs} +1 -1
- package/dist/dev.cjs +275 -0
- package/dist/dev.d.cts +108 -0
- package/dist/dev.d.ts +108 -0
- package/dist/dev.js +275 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/{manifest-types-DQARCQJt.d.cts → manifest-types-CUTNfJTa.d.cts} +1 -1
- package/dist/{manifest-types-DQARCQJt.d.ts → manifest-types-CUTNfJTa.d.ts} +1 -1
- package/dist/manifest.d.cts +660 -660
- package/dist/manifest.d.ts +660 -660
- package/dist/pack.cjs +1 -1
- package/dist/pack.d.cts +3 -2
- package/dist/pack.d.ts +3 -2
- package/dist/pack.js +1 -1
- package/dist/publish.cjs +1 -1
- package/dist/publish.d.cts +1 -1
- package/dist/publish.d.ts +1 -1
- package/dist/publish.js +1 -1
- package/dist/scaffold.cjs +2 -1
- package/dist/scaffold.js +2 -1
- package/dist/server.d.cts +33 -33
- package/dist/server.d.ts +33 -33
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -563,7 +563,10 @@ import { resolve } from 'node:path';
|
|
|
563
563
|
import { tegoPluginFrontend, tegoPluginTailwind } from '@tego/plugin-sdk/vite';
|
|
564
564
|
import tailwindcss from 'tailwindcss';
|
|
565
565
|
|
|
566
|
-
const config = tegoPluginFrontend({
|
|
566
|
+
const config = tegoPluginFrontend({
|
|
567
|
+
entry: 'frontend/src/index.tsx',
|
|
568
|
+
pluginId: 'acme-crm',
|
|
569
|
+
});
|
|
567
570
|
config.css = {
|
|
568
571
|
postcss: {
|
|
569
572
|
plugins: [
|
|
@@ -600,6 +603,45 @@ await packPlugin({ root: process.cwd(), backend: { external: ['sharp'] } });
|
|
|
600
603
|
Without this, changing one line of frontend code costs a pack, an upload, an
|
|
601
604
|
install and a refresh.
|
|
602
605
|
|
|
606
|
+
### Preview without a Tego server: `tego-plugin dev`
|
|
607
|
+
|
|
608
|
+
```bash
|
|
609
|
+
pnpm run dev # scaffolded plugins have "dev": "tego-plugin dev"
|
|
610
|
+
DATABASE_URL=postgres://me:pw@localhost:5432/dev pnpm run dev # with the backend
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
It prints the address (`http://127.0.0.1:5280/p/<id>/`, or the next free port;
|
|
614
|
+
`--port 0` takes any) and a machine-readable `TEGO_DEV_READY {"url":…}` line.
|
|
615
|
+
Everything is served from that one origin:
|
|
616
|
+
|
|
617
|
+
- **Frontend**: a stand-in for the shell's plugin page mounts your entry from
|
|
618
|
+
source through Vite, with your own `vite.config.ts` and Tailwind. Saving
|
|
619
|
+
reloads the page. The context is built as the shell builds it — `basePath`
|
|
620
|
+
`/p/<id>`, history routing, `ctx.api` at `/api/v1/tego-os/plugin-api/<id>/*`,
|
|
621
|
+
the frontend config's defaults — and the top bar switches page, language
|
|
622
|
+
(your locale files, the access UI's strings merged in), light/dark, and the
|
|
623
|
+
user and roles the API calls are made as (`ctx.can` answers for any nav key
|
|
624
|
+
the manifest declares).
|
|
625
|
+
- **Backend** (with `DATABASE_URL`, unless `--no-backend`): built with the
|
|
626
|
+
packer's own settings and started with the environment `serveBackend` reads.
|
|
627
|
+
Requests carry the same signed identity headers the gateway adds. Migrations
|
|
628
|
+
run first, each file in its own transaction with `search_path` set to
|
|
629
|
+
`tego_dev_<id>` alone, recorded in a `tego_dev_migrations` table in that
|
|
630
|
+
schema so each file runs once; the backend's connection is pinned to the
|
|
631
|
+
schema too. Saving under `backend/` or `shared/` rebuilds, migrates and
|
|
632
|
+
restarts it. The database is yours to provide; any PostgreSQL works.
|
|
633
|
+
- **Host API stand-in**: `config` (the backend section's defaults), `log`,
|
|
634
|
+
`storage` (files under `.tego-dev/storage/`), `events` and `notifications`
|
|
635
|
+
(printed), and a `directory` that knows the users you previewed as. Anything
|
|
636
|
+
else — mail, knowledge, accounts, delegations, AI, Office, live events —
|
|
637
|
+
answers 501 naming the path; test those on a real server.
|
|
638
|
+
|
|
639
|
+
Without `DATABASE_URL` the page still works and API calls answer 503 saying
|
|
640
|
+
why. `.tego-dev/` holds the generated shell, the backend build and local
|
|
641
|
+
storage; keep it out of git. Python backends run on a server only.
|
|
642
|
+
|
|
643
|
+
### Inside the shell
|
|
644
|
+
|
|
603
645
|
```bash
|
|
604
646
|
pnpm exec tego-plugin-pack --watch # serves the bundle on :5180
|
|
605
647
|
VITE_TEGO_LOCAL_PLUGINS=acme-crm=http://localhost:5180/index.js pnpm -F @tego/ai-studio dev
|
|
@@ -614,7 +656,7 @@ Hot replacement across the SystemJS boundary means unmounting the plugin's React
|
|
|
614
656
|
root and re-registering shared modules, which is a lot of machinery for
|
|
615
657
|
something that would silently desync; a one-second reload is always right.
|
|
616
658
|
|
|
617
|
-
|
|
659
|
+
Against a full local server, the backend runs on the host instead of in a container:
|
|
618
660
|
|
|
619
661
|
```bash
|
|
620
662
|
TEGO_PLUGIN_BACKEND_BASE_URL=http://localhost:18800 pnpm -F @tego/server dev
|
|
@@ -779,7 +821,12 @@ const unregister = ctx.ai?.registerForm({
|
|
|
779
821
|
element: formElement,
|
|
780
822
|
getSnapshot: () => ({
|
|
781
823
|
fields: [
|
|
782
|
-
{
|
|
824
|
+
{
|
|
825
|
+
name: 'quantity',
|
|
826
|
+
label: '数量',
|
|
827
|
+
type: 'number',
|
|
828
|
+
value: state.quantity,
|
|
829
|
+
},
|
|
783
830
|
{ name: 'note', label: '说明', type: 'text', value: state.note },
|
|
784
831
|
],
|
|
785
832
|
instructions: '数量使用件,不推测未提供的数量。',
|
package/bin/tego-plugin.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
`)}catch(
|
|
2
|
+
var x=Object.defineProperty;var i=(e,r)=>x(e,"name",{value:r,configurable:!0});import{readFileSync as E}from"node:fs";const a=process.argv.slice(2),p=a[0];if(p==="create"){const{createStandalonePlugin:e}=await import("../dist/scaffold.js"),r=a.indexOf("--dir");try{const o=e(E(0,"utf8"),{parent:r===-1?".":a[r+1]??".",cwd:process.cwd()});process.stdout.write(`${JSON.stringify(o)}
|
|
3
|
+
`)}catch(o){console.error(o instanceof Error?o.message:String(o)),process.exitCode=1}process.exit()}if(p==="dev"){const{startDevServer:e}=await import("../dist/dev.js"),r=a.slice(1),o=i(c=>{const g=r.indexOf(`--${c}`);return g===-1?void 0:r[g+1]},"value"),n=o("port");n!==void 0&&!/^\d+$/.test(n)&&(console.error(`\u2716 --port must be a number, not ${n}`),process.exit(2));let d;try{d=await e({root:process.cwd(),port:n===void 0?void 0:Number(n),host:o("host"),backend:!r.includes("--no-backend"),databaseUrl:process.env.DATABASE_URL||void 0})}catch(c){console.error(`\u2716 ${c instanceof Error?c.message:String(c)}`),process.exit(1)}let u=!1;const f=i(async()=>{u||(u=!0,setTimeout(()=>process.exit(0),5e3).unref(),await d.close().catch(()=>{}),process.exit(0))},"stop");process.once("SIGINT",f),process.once("SIGTERM",f),await new Promise(()=>{})}else p!=="publish"&&(console.error("usage: tego-plugin create [--dir <parent>] | tego-plugin dev [--port <n>] [--no-backend] | tego-plugin publish [options]"),process.exit(2));const{publishPlugin:b}=await import("../dist/publish.js"),t=a.slice(1);function s(e){const r=t.indexOf(`--${e}`);return r===-1?void 0:t[r+1]}i(s,"flag");function w(e){const r=[];for(let o=0;o<t.length;o++)t[o]===`--${e}`&&r.push(t[o+1]);return r}i(w,"all");const v=s("server")??process.env.TEGO_SERVER_URL,m=s("token")??process.env.TEGO_ADMIN_TOKEN;(!v||!m)&&(console.error("\u2716 --server (TEGO_SERVER_URL) and --token (TEGO_ADMIN_TOKEN) are both required"),process.exit(2));const h=w("avatar").filter(Boolean),l={};s("category")&&(l.category=s("category")),s("homepage")&&(l.homepage=s("homepage")),s("icon")&&(l.icon=s("icon"));try{const e=await b({root:s("root")??process.cwd(),server:v,token:m,...h.length>0?{avatarIds:h}:{},...Object.keys(l).length>0?{meta:l}:{},approve:!t.includes("--no-approve"),install:!t.includes("--no-install")}),r=e.release.semanticVersion;console.log(`\u2705 ${e.release.pluginId}@${r} \u2014 release ${e.release.status}`)}catch(e){console.error(`\u2716 ${e instanceof Error?e.message:String(e)}`),process.exitCode=1}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var p=Object.defineProperty;var o=(i,t)=>p(i,"name",{value:t,configurable:!0});import{localizedTextToString as C}from"./chunk-BTHXQWHT.js";var m=["frontend","backend","claw"],l="__OPENCLAW_REDACTED__",c={fieldCount:0,required:[],sensitive:[]};function v(i){if(!i)return c;const t=Object.keys(i.schema.properties),n=i.uiHints??{};return{fieldCount:t.length,required:(i.schema.required??[]).filter(e=>t.includes(e)),sensitive:t.filter(e=>n[e]?.sensitive===!0)}}o(v,"summarizeConfigDeclaration");function _(i){if(!i)return c;const t=i.configSchema;if(typeof t!="object"||t===null)return c;const n=t.properties,e=typeof n=="object"&&n!==null?Object.keys(n):[],r=t.required,s=Array.isArray(r)?r.filter(u=>typeof u=="string"&&e.includes(u)):[],f=i.uiHints,a=typeof f=="object"&&f!==null?f:{},d=e.filter(u=>a[u]?.sensitive===!0);return{fieldCount:e.length,required:s,sensitive:d}}o(_,"summarizeClawConfig");function b(i,t){const n={...i};for(const e of t)n[e]===void 0||n[e]===null||(n[e]=l);return n}o(b,"redactSecrets");function E(i,t,n){const e={...i};for(const r of n){if(e[r]!==l)continue;const s=t[r];if(s===void 0){delete e[r];continue}e[r]=s}return e}o(E,"restoreSecrets");function N(i,t){const n={...t};for(const[e,r]of Object.entries(i.properties??{}))n[e]===void 0&&r.default!==void 0&&(n[e]=r.default);return n}o(N,"applyConfigDefaults");function S(i,t,n,e){return t?.label?C(t.label,e):n?.title?n.title:i}o(S,"configFieldLabel");export{c as EMPTY_CONFIG_SECTION_SUMMARY,l as PLUGIN_CONFIG_SECRET_SENTINEL,m as PLUGIN_CONFIG_SECTIONS,N as applyConfigDefaults,S as configFieldLabel,b as redactSecrets,E as restoreSecrets,_ as summarizeClawConfig,v as summarizeConfigDeclaration};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var a=Object.defineProperty;var o=(n,t)=>a(n,"name",{value:t,configurable:!0});var chunkROFUQLOZ_cjs=require("./chunk-ROFUQLOZ.cjs"),PLUGIN_CONFIG_SECTIONS=["frontend","backend","claw"],PLUGIN_CONFIG_SECRET_SENTINEL="__OPENCLAW_REDACTED__",EMPTY_CONFIG_SECTION_SUMMARY={fieldCount:0,required:[],sensitive:[]};function summarizeConfigDeclaration(n){if(!n)return EMPTY_CONFIG_SECTION_SUMMARY;const t=Object.keys(n.schema.properties),r=n.uiHints??{};return{fieldCount:t.length,required:(n.schema.required??[]).filter(e=>t.includes(e)),sensitive:t.filter(e=>r[e]?.sensitive===!0)}}o(summarizeConfigDeclaration,"summarizeConfigDeclaration");function summarizeClawConfig(n){if(!n)return EMPTY_CONFIG_SECTION_SUMMARY;const t=n.configSchema;if(typeof t!="object"||t===null)return EMPTY_CONFIG_SECTION_SUMMARY;const r=t.properties,e=typeof r=="object"&&r!==null?Object.keys(r):[],i=t.required,s=Array.isArray(i)?i.filter(u=>typeof u=="string"&&e.includes(u)):[],c=n.uiHints,f=typeof c=="object"&&c!==null?c:{},l=e.filter(u=>f[u]?.sensitive===!0);return{fieldCount:e.length,required:s,sensitive:l}}o(summarizeClawConfig,"summarizeClawConfig");function redactSecrets(n,t){const r={...n};for(const e of t)r[e]===void 0||r[e]===null||(r[e]=PLUGIN_CONFIG_SECRET_SENTINEL);return r}o(redactSecrets,"redactSecrets");function restoreSecrets(n,t,r){const e={...n};for(const i of r){if(e[i]!==PLUGIN_CONFIG_SECRET_SENTINEL)continue;const s=t[i];if(s===void 0){delete e[i];continue}e[i]=s}return e}o(restoreSecrets,"restoreSecrets");function applyConfigDefaults(n,t){const r={...t};for(const[e,i]of Object.entries(n.properties??{}))r[e]===void 0&&i.default!==void 0&&(r[e]=i.default);return r}o(applyConfigDefaults,"applyConfigDefaults");function configFieldLabel(n,t,r,e){return t?.label?chunkROFUQLOZ_cjs.localizedTextToString(t.label,e):r?.title?r.title:n}o(configFieldLabel,"configFieldLabel"),exports.EMPTY_CONFIG_SECTION_SUMMARY=EMPTY_CONFIG_SECTION_SUMMARY,exports.PLUGIN_CONFIG_SECRET_SENTINEL=PLUGIN_CONFIG_SECRET_SENTINEL,exports.PLUGIN_CONFIG_SECTIONS=PLUGIN_CONFIG_SECTIONS,exports.applyConfigDefaults=applyConfigDefaults,exports.configFieldLabel=configFieldLabel,exports.redactSecrets=redactSecrets,exports.restoreSecrets=restoreSecrets,exports.summarizeClawConfig=summarizeClawConfig,exports.summarizeConfigDeclaration=summarizeConfigDeclaration;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
var dt=Object.defineProperty;var l=(t,n)=>dt(t,"name",{value:n,configurable:!0});import{TEGO_PLUGIN_DEV_OVERRIDES as
|
|
2
|
-
`}l(
|
|
3
|
-
`}l(Ct,"mergeLocale");var Lt=_t(Pt),tt="315532800",Ot=["src","resources","migrations"],et="requirements.in",
|
|
4
|
-
${e}`)}}l(
|
|
1
|
+
var dt=Object.defineProperty;var l=(t,n)=>dt(t,"name",{value:n,configurable:!0});import{TEGO_PLUGIN_DEV_OVERRIDES as B}from"./chunk-7K37PVGV.js";import{parseTegoPluginManifest as wt,checkPluginAccessCatalog as ht,PYTHON_LOCK_FILENAME as T,parseRequirementsLock as U,normalizePackageName as mt,PYTHON_WHEELS_DIR as pt,checkPythonVendoring as yt,PYTHON_RUNNER_VERSION as J,parseWheelFilename as gt}from"./chunk-5CU33JVY.js";import{resolvePluginParts as R,PLUGIN_MANIFEST_FILENAME as O,CLAW_MANIFEST_FILENAME as kt}from"./chunk-BTHXQWHT.js";import{createHash as q}from"crypto";import{readFile as m,readdir as b,access as bt,rm as D,mkdir as y,writeFile as $,cp as g,stat as G}from"fs/promises";import{createServer as $t}from"http";import{join as o,relative as K,sep as vt,resolve as f,dirname as v,basename as x}from"path";import Et from"jszip";import{execFile as Pt}from"child_process";import{promisify as _t}from"util";var V="template.config.json",Y={IDENTITY:"identityMd",SOUL:"soulMd",AGENTS:"agentsMd",TOOLS:"toolsMd",BOOTSTRAP:"bootstrapMd",HEARTBEAT:"heartbeatMd",USER:"userMd",MEMORY:"memoryMd"};async function z(t){const{agents:n,...a}=JSON.parse(await m(o(t,V),"utf8")),e=a.slug??t,i={...a.content,...await X(o(t,"workspace"))},s=await At(o(t,"workspace"));if(s.length>0&&(i.workspaceFiles=s),n){if(n[0]?.id!=="main")throw new Error(`${e}: the first of "agents" must be main`);if(a.content?.agentList)throw new Error(`${e}: declare the team in "agents" or in "content.agentList", not both`);const r=n.slice(1),c=(await xt(o(t,"experts"))).sort(),u=r.map(d=>d.id).sort();if(c.join()!==u.join())throw new Error(`${e}: experts/ holds [${c.join(", ")}] but "agents" declares [${u.join(", ")}]`);if(r.length>0){const d={};for(const w of r)d[w.id]=await X(o(t,"experts",w.id));i.subAgentWorkspaces=d}i.agentList=n.map(({id:d,name:w,description:h,emoji:k})=>({id:d,name:w,...h?{description:h}:{},identity:{name:w,emoji:k},...d==="main"&&r.length>0?{subagents:{allowAgents:r.map(E=>E.id)}}:{}}))}return`${JSON.stringify({...a,content:i},null,2)}
|
|
2
|
+
`}l(z,"buildAgentTemplate");async function X(t){const n=new Set(await Tt(t)),a={};for(const[e,i]of Object.entries(Y)){const s=`${e}.md`;n.has(s)&&(a[i]=await Z(o(t,s)))}return a}l(X,"readRootFiles");async function At(t){const n=new Set(Object.keys(Y).map(e=>`${e}.md`)),a=(await Q(t)).map(e=>K(t,e).split(vt).join("/")).filter(e=>!n.has(e)).sort();return Promise.all(a.map(async e=>({path:e,content:await Z(o(t,e)),...e.endsWith(".md")?{contentType:"text/markdown"}:{}})))}l(At,"readExtraFiles");async function Z(t){return(await m(t,"utf8")).replace(/\n$/,"")}l(Z,"readDocument");async function Q(t){const n=await b(t,{withFileTypes:!0}).catch(()=>[]),a=[];for(const e of n){if(e.name.startsWith("."))continue;const i=o(t,e.name);e.isDirectory()?a.push(...await Q(i)):e.isFile()&&a.push(i)}return a}l(Q,"walk");async function Tt(t){return(await b(t,{withFileTypes:!0}).catch(()=>[])).filter(a=>a.isFile()).map(a=>a.name)}l(Tt,"fileNames");async function xt(t){return(await b(t,{withFileTypes:!0}).catch(()=>[])).filter(a=>a.isDirectory()&&!a.name.startsWith(".")).map(a=>a.name)}l(xt,"subdirectories");var F="@tego/plugin-access-ui";async function M(t,n,a){const e=f(t,a),i=await b(e).then(c=>c.filter(u=>u.endsWith(".json")).sort(),c=>{if(c.code==="ENOENT")return[];throw c}),s=new Map,r=n.access&&i.length>0?await Dt(t):null;for(const c of i){const u=await m(o(e,c),"utf8");if(!r){s.set(c,u);continue}s.set(c,Ct(c,u,r,await Nt(r,c)))}return s}l(M,"readPluginLocales");async function Dt(t){for(let n=f(t);;n=v(n)){const a=o(n,"node_modules",F);if(await bt(o(a,"package.json")).then(()=>!0,()=>!1))return o(a,"locales");if(v(n)===n)break}throw new Error(`The manifest declares access, so the frontend renders ${F} \u2014 add it to this plugin's dependencies.`)}l(Dt,"accessUiLocaleDir");async function Nt(t,n){try{return await m(o(t,n),"utf8")}catch{throw new Error(`${F} has no ${n}; the plugin ships that locale, so its access screens would show bare keys.`)}}l(Nt,"readAccessLocale");function Ct(t,n,a,e){const i=JSON.parse(n),s=JSON.parse(e);for(const r of Object.keys(s))if(r in i)throw new Error(`Locale ${t} defines "${r}", which ${F} (${a}) owns \u2014 rename the plugin's key.`);return`${JSON.stringify({...i,...s},null,2)}
|
|
3
|
+
`}l(Ct,"mergeLocale");var Lt=_t(Pt),tt="315532800",Ot=["src","resources","migrations"],et="requirements.in",I="tego-plugin-sdk",Ft={"linux/amd64":"x86_64","linux/arm64":"aarch64"};function St(t){const n=Ft[t],a=[`manylinux2014_${n}`];for(let e=17;e<=36;e++)a.push(`manylinux_2_${e}_${n}`);return n==="x86_64"&&a.push("manylinux2010_x86_64","manylinux1_x86_64"),a.flatMap(e=>["--platform",e])}l(St,"pipPlatformArgs");async function A(t){try{return await G(t),!0}catch{return!1}}l(A,"exists");async function nt(t,n){if(n)return f(t,n);let a=t;for(;;){const e=o(a,"packages","plugin-sdk-python");if(await A(o(e,"pyproject.toml")))return e;const i=v(a);if(i===a)throw new Error("Cannot find packages/plugin-sdk-python above the plugin root; pass backend.python.sdkProject.");a=i}}l(nt,"findSdkProject");async function S(t,n={}){try{await Lt("uv",t,{env:{...process.env,...n},maxBuffer:64*1024*1024})}catch(a){const e=a.stderr?.trim()||(a instanceof Error?a.message:String(a));throw a.code==="ENOENT"?new Error("`uv` is required to pack a python backend: https://docs.astral.sh/uv/"):new Error(`uv ${t[0]} failed:
|
|
4
|
+
${e}`)}}l(S,"uv");async function at(t,n){await S(["build","--quiet","--wheel","--out-dir",n,t],{SOURCE_DATE_EPOCH:tt})}l(at,"buildSdkWheel");async function jt(t,n,a={}){const e=f(t,n.dir),i=o(e,et);if(!await A(i))throw new Error(`${n.dir}/${et} not found`);const s=o(t,"build","python-sdk");await D(s,{recursive:!0,force:!0}),await at(await nt(t,a.sdkProject),s);const r=o(e,T),{major:c,minor:u}=J;return await S(["pip","compile","--quiet",i,"--output-file",r,"--generate-hashes","--no-header","--python-version",`${c}.${u}`,"--python-platform","x86_64-manylinux_2_36","--find-links",s,"--upgrade-package",I]),r}l(jt,"lockPythonBackend");async function it(t){return q("sha256").update(await m(t)).digest("hex")}l(it,"sha256");async function Rt(t){const{major:n,minor:a}=J,e=o(t.scratch,"downloads");await y(e,{recursive:!0});for(const s of t.platforms)await S(["tool","run","--from","pip","pip","download","--quiet","--no-deps","--prefer-binary","--require-hashes","--python-version",`${n}.${a}`,"--implementation","cp","--abi",`cp${n}${a}`,"--abi","abi3","--abi","none",...St(s),"--find-links",t.sdkDist,"--requirement",t.lockPath,"--dest",e]);const i=o(t.scratch,"built");for(const s of await b(e))s.endsWith(".whl")||await S(["build","--quiet","--wheel","--out-dir",i,o(e,s)],{SOURCE_DATE_EPOCH:tt});await y(t.wheelsDir,{recursive:!0});for(const s of[e,i])if(await A(s))for(const r of(await b(s)).sort())r.endsWith(".whl")&&await g(o(s,r),o(t.wheelsDir,r))}l(Rt,"vendorWheels");async function Mt(t,n,a){const{requirements:e}=U(t),i=new Map;for(const c of(await b(n)).sort()){const u=gt(c);if(!u)continue;const d=`${u.name}==${u.version}`;i.set(d,[...i.get(d)??[],await it(o(n,c))])}const r=`${e.map(c=>{const u=i.get(`${c.name}==${c.version}`)??[];return[`${c.name}==${c.version}`,...u.map(d=>`--hash=sha256:${d}`)].join(" ")}).join(`
|
|
5
5
|
`)}
|
|
6
|
-
`;return await $(a,r,"utf8"),r}l(Mt,"writePackagedLock");async function It(t){const{root:n,staging:a,part:e,options:i={},log:s}=t,r=f(n,e.dir),c=o(a,e.dir);await y(c,{recursive:!0});const u=f(n,e.entryPath);if(!await A(u))throw new Error(`backend entry not found at ${u}`);await g(u,o(a,e.entryPath));for(const P of i.include??Ot){const _=o(r,P);await A(_)&&await g(_,o(c,P),{recursive:!0,filter:l(j=>!/(^|\/)(__pycache__|\.pytest_cache|\.venv)(\/|$)|\.pyc$/.test(j),"filter")})}const d=f(n,e.migrationsDirPath);await A(d)&&await g(d,o(a,e.migrationsDirPath),{recursive:!0});const w=o(r,T);if(!await A(w))throw new Error(`${e.dir}/${T} not found; run \`tego-plugin-pack lock\``);const h=await m(w,"utf8"),{errors:k,requirements:E}=
|
|
7
|
-
`));if(!E.some(P=>P.name===mt(
|
|
8
|
-
`))}finally{await D(
|
|
9
|
-
`,"utf8"),await zt(n,r,e),i.frontend&&(a("\u203A building frontend"),await Yt(n,r,e,i.frontend,t.frontend)),i.publicFrontend&&(a("\u203A building public pages"),await Xt(n,r,i.publicFrontend,t.publicFrontend)),i.backend?.runtime==="python"?(a("\u203A staging python backend"),await It({root:n,staging:r,part:i.backend,...t.backend?.python?{options:t.backend.python}:{},log:a})):i.backend&&(a("\u203A building backend"),await Zt(n,r,i.backend,t.backend)),i.claw&&(a("\u203A building claw"),await Qt(n,r,i.claw,e,t.claw)),await ot(n,e,a);for(const w of e.agentTemplates??[])for(const h of[w.entry,w.icon].filter(k=>!!k)){const k=f(n,h);await p(k,`agent template asset ${h}`);const E=o(r,h);await y(v(E),{recursive:!0}),await g(k,E)}const c=o(s,t.fileName??`${e.id}-${e.version}.zip`),u=await te(r,c),d=
|
|
6
|
+
`;return await $(a,r,"utf8"),r}l(Mt,"writePackagedLock");async function It(t){const{root:n,staging:a,part:e,options:i={},log:s}=t,r=f(n,e.dir),c=o(a,e.dir);await y(c,{recursive:!0});const u=f(n,e.entryPath);if(!await A(u))throw new Error(`backend entry not found at ${u}`);await g(u,o(a,e.entryPath));for(const P of i.include??Ot){const _=o(r,P);await A(_)&&await g(_,o(c,P),{recursive:!0,filter:l(j=>!/(^|\/)(__pycache__|\.pytest_cache|\.venv)(\/|$)|\.pyc$/.test(j),"filter")})}const d=f(n,e.migrationsDirPath);await A(d)&&await g(d,o(a,e.migrationsDirPath),{recursive:!0});const w=o(r,T);if(!await A(w))throw new Error(`${e.dir}/${T} not found; run \`tego-plugin-pack lock\``);const h=await m(w,"utf8"),{errors:k,requirements:E}=U(h);if(k.length>0)throw new Error([`${e.dir}/${T} is invalid:`,...k].join(`
|
|
7
|
+
`));if(!E.some(P=>P.name===mt(I)))throw new Error(`${e.dir}/${T} must lock ${I}`);const L=o(n,"build","python-scratch");await D(L,{recursive:!0,force:!0});try{const P=o(L,"sdk");await at(await nt(n,i.sdkProject),P);const _=o(c,pt);s(` \u203A vendoring ${E.length} packages for ${e.platforms.join(", ")}`),await Rt({lockPath:w,platforms:e.platforms,sdkDist:P,wheelsDir:_,scratch:L});const j=await Mt(h,_,o(c,T)),ft=await Promise.all((await b(_)).map(async W=>({filename:W,sha256:await it(o(_,W))}))),H=yt({lockText:j,wheels:ft,platforms:e.platforms});if(H.length>0)throw new Error(["Python backend would not install in the runner:",...H].join(`
|
|
8
|
+
`))}finally{await D(L,{recursive:!0,force:!0})}}l(It,"stagePythonBackend");var Ht=["pg","node:*"],Wt=["openclaw","openclaw/*","node:*"],rt=new Date(Date.UTC(1980,0,1));async function Bt(t){const n=f(t.root),a=t.log===null?()=>{}:t.log??(c=>console.log(c)),e=await N(n),i=R(e),s=f(n,t.outDir??"build"),r=o(s,"package");await D(r,{recursive:!0,force:!0}),await y(r,{recursive:!0});try{await $(o(r,O),`${JSON.stringify(e,null,2)}
|
|
9
|
+
`,"utf8"),await zt(n,r,e),i.frontend&&(a("\u203A building frontend"),await Yt(n,r,e,i.frontend,t.frontend)),i.publicFrontend&&(a("\u203A building public pages"),await Xt(n,r,i.publicFrontend,t.publicFrontend)),i.backend?.runtime==="python"?(a("\u203A staging python backend"),await It({root:n,staging:r,part:i.backend,...t.backend?.python?{options:t.backend.python}:{},log:a})):i.backend&&(a("\u203A building backend"),await Zt(n,r,i.backend,t.backend)),i.claw&&(a("\u203A building claw"),await Qt(n,r,i.claw,e,t.claw)),await ot(n,e,a);for(const w of e.agentTemplates??[])for(const h of[w.entry,w.icon].filter(k=>!!k)){const k=f(n,h);await p(k,`agent template asset ${h}`);const E=o(r,h);await y(v(E),{recursive:!0}),await g(k,E)}const c=o(s,t.fileName??`${e.id}-${e.version}.zip`),u=await te(r,c),d=q("sha256").update(await m(c)).digest("hex");return a(`\u2705 ${K(n,c)} \u2014 ${ee(u)}`),{file:c,bytes:u,sha256:d,manifest:e}}finally{await D(r,{recursive:!0,force:!0})}}l(Bt,"packPlugin");async function Ut(t){const n=f(t.root),a=t.log===null?()=>{}:t.log??(e=>console.log(e));return ot(n,await N(n),a)}l(Ut,"buildPluginTemplates");async function ot(t,n,a){const e=[];for(const i of n.agentTemplates??[]){const s=f(t,i.entry),r=v(s);if(!await C(o(r,V)))continue;const c=await z(r),{content:u}=JSON.parse(c);u?.usageGuide?.summary?.trim()||a(`\u26A0\uFE0F ${i.key}: missing content.usageGuide.summary (recommended for onboarding)`),await m(s,"utf8").catch(()=>null)!==c&&(await $(s,c,"utf8"),a(`\u203A rebuilt ${i.entry}`),e.push(i.entry))}return e}l(ot,"writeAgentTemplates");async function Jt(t){const n=f(t.root),a=R(await N(n)).backend;if(a?.runtime!=="python")throw new Error("Only a python backend part has a lock to compile.");return jt(n,a,t.python)}l(Jt,"lockPlugin");async function qt(t){const n=f(t.root),a=t.log===null?()=>{}:t.log??(h=>console.log(h)),e=await N(n),i=R(e).frontend;if(!i)throw new Error(`${e.id} declares no frontend part to watch.`);const s=f(n,t.frontend?.buildDir??`${i.dir}/dist`),r=f(n,t.frontend?.viteConfig??`${i.dir}/vite.config.ts`);await p(r,"frontend vite config");const c=t.port??Gt,u=Vt(s,c,()=>M(n,e,i.i18nDirPath)),{build:d}=await import("vite");return(await d({root:n,configFile:r,logLevel:"warn",...B,build:{...B.build,watch:{}}})).on("event",h=>{h.code==="END"&&(a(`\u21BB rebuilt \u2014 reloading ${u.clientCount()} page(s)`),u.notify())}),a([`Serving ${e.id} from http://localhost:${c}/${x(i.entryPath)}`,"","Point the shell at it and restart its dev server:",` VITE_TEGO_LOCAL_PLUGINS=${e.id}=http://localhost:${c}/${x(i.entryPath)}`,"","The release still has to be installed and enabled \u2014 this only replaces","where its bundle is fetched from. Ctrl-C to stop."].join(`
|
|
10
10
|
`)),new Promise(()=>{})}l(qt,"watchPluginFrontend");var Gt=5180,Kt="/__tego_plugin_reload",st="/i18n/";function Vt(t,n,a){const e=new Set;return $t((s,r)=>{r.setHeader("Access-Control-Allow-Origin","*"),r.setHeader("Access-Control-Allow-Headers","*");const c=new URL(s.url??"/","http://localhost").pathname;if(c===Kt){r.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"}),r.write(`: connected
|
|
11
11
|
|
|
12
12
|
`),e.add(r),s.on("close",()=>e.delete(r));return}if(c.startsWith(st)){const d=c.slice(st.length);a().then(w=>{const h=w.get(d);if(h===void 0){r.writeHead(404).end();return}r.writeHead(200,{"Content-Type":ct(d),"Cache-Control":"no-store"}),r.end(h)},w=>{r.writeHead(500,{"Content-Type":"text/plain; charset=utf-8"}).end(w instanceof Error?w.message:String(w))});return}const u=f(t,`.${c}`);if(!u.startsWith(t)){r.writeHead(403).end();return}m(u).then(d=>{r.writeHead(200,{"Content-Type":ct(u),"Cache-Control":"no-store"}),r.end(d)},()=>r.writeHead(404).end())}).listen(n,"127.0.0.1"),{notify:l(()=>{for(const s of e)s.write(`data: rebuilt
|
|
13
13
|
|
|
14
|
-
`)},"notify"),clientCount:l(()=>e.size,"clientCount")}}l(Vt,"serveBuildDir");function ct(t){return t.endsWith(".js")?"text/javascript; charset=utf-8":t.endsWith(".json")?"application/json; charset=utf-8":t.endsWith(".css")?"text/css; charset=utf-8":"application/octet-stream"}l(ct,"contentTypeFor");async function
|
|
15
|
-
`));return e.manifest}l(
|
|
16
|
-
`));await y(v(o(n,e)),{recursive:!0}),await $(o(n,e),s,"utf8")}l(zt,"stageAccessCatalog");async function Xt(t,n,a,e){const i=f(t,e?.viteConfig??`${a.dir}/vite.config.ts`);await p(i,"public pages vite config");const{build:s}=await import("vite");await s({root:f(t,a.dir),configFile:i,logLevel:"warn"});const r=f(t,e?.buildDir??`${a.dir}/dist`);await p(o(r,x(a.entryPath)),`public pages (expected the vite build to write ${x(a.entryPath)})`),await y(o(n,a.dir),{recursive:!0}),await g(r,o(n,a.dir),{recursive:!0})}l(Xt,"stagePublicFrontend");async function Zt(t,n,a,e){await lt(a,{root:t,outDir:n,...e?.entry?{entry:e.entry}:{},...e?.external?{external:e.external}:{}})}l(Zt,"stageBackend");async function lt(t,n){const{root:a,outDir:e}=n,i=f(a,n.entry??`${t.dir}/src/index.ts`);await p(i,"backend entry");const{build:s}=await import("esbuild");await s({entryPoints:[i],outfile:o(e,t.entryPath),bundle:!0,platform:"node",target:"node22",format:"esm",banner:{js:"import { createRequire as __tegoCreateRequire } from 'node:module'; const require = __tegoCreateRequire(import.meta.url);"},external:[...Ht,...n.external??[]],logLevel:"warning"});for(const c of t.compatibilityRepairs??[]){const u=`${t.dir}/${c.sqlFile}`;await p(f(a,u),"schema repair"),await y(v(o(e,u)),{recursive:!0}),await g(f(a,u),o(e,u))}const r=f(a,t.migrationsDirPath);await
|
|
14
|
+
`)},"notify"),clientCount:l(()=>e.size,"clientCount")}}l(Vt,"serveBuildDir");function ct(t){return t.endsWith(".js")?"text/javascript; charset=utf-8":t.endsWith(".json")?"application/json; charset=utf-8":t.endsWith(".css")?"text/css; charset=utf-8":"application/octet-stream"}l(ct,"contentTypeFor");async function N(t){const n=o(t,O);let a;try{a=await m(n,"utf8")}catch{throw new Error(`${O} not found at ${n}`)}const e=wt(JSON.parse(a));if(!e.ok||!e.manifest)throw new Error([`${O} is invalid:`,...e.errors].join(`
|
|
15
|
+
`));return e.manifest}l(N,"readManifest");async function Yt(t,n,a,e,i){const s=f(t,i?.viteConfig??`${e.dir}/vite.config.ts`);await p(s,"frontend vite config");const{build:r}=await import("vite");await r({root:t,configFile:s,logLevel:"warn"});const c=f(t,i?.buildDir??`${e.dir}/dist`);await p(o(c,x(e.entryPath)),`frontend bundle (expected the vite build to write ${x(e.entryPath)})`),await y(o(n,v(e.entryPath)),{recursive:!0}),await g(c,o(n,v(e.entryPath)),{recursive:!0});const u=await M(t,a,e.i18nDirPath);u.size>0&&await y(o(n,e.i18nDirPath),{recursive:!0});for(const[d,w]of u)await $(o(n,e.i18nDirPath,d),w,"utf8")}l(Yt,"stageFrontend");async function zt(t,n,a){const e=a.access?.catalog;if(!e)return;const i=f(t,e);await p(i,`access catalog ${e}`);const s=await m(i,"utf8"),r=ht(a,JSON.parse(s));if(!r.ok)throw new Error(["The access catalog is invalid:",...r.errors].join(`
|
|
16
|
+
`));await y(v(o(n,e)),{recursive:!0}),await $(o(n,e),s,"utf8")}l(zt,"stageAccessCatalog");async function Xt(t,n,a,e){const i=f(t,e?.viteConfig??`${a.dir}/vite.config.ts`);await p(i,"public pages vite config");const{build:s}=await import("vite");await s({root:f(t,a.dir),configFile:i,logLevel:"warn"});const r=f(t,e?.buildDir??`${a.dir}/dist`);await p(o(r,x(a.entryPath)),`public pages (expected the vite build to write ${x(a.entryPath)})`),await y(o(n,a.dir),{recursive:!0}),await g(r,o(n,a.dir),{recursive:!0})}l(Xt,"stagePublicFrontend");async function Zt(t,n,a,e){await lt(a,{root:t,outDir:n,...e?.entry?{entry:e.entry}:{},...e?.external?{external:e.external}:{}})}l(Zt,"stageBackend");async function lt(t,n){const{root:a,outDir:e}=n,i=f(a,n.entry??`${t.dir}/src/index.ts`);await p(i,"backend entry");const{build:s}=await import("esbuild");await s({entryPoints:[i],outfile:o(e,t.entryPath),bundle:!0,platform:"node",target:"node22",format:"esm",banner:{js:"import { createRequire as __tegoCreateRequire } from 'node:module'; const require = __tegoCreateRequire(import.meta.url);"},external:[...Ht,...n.external??[]],logLevel:"warning"});for(const c of t.compatibilityRepairs??[]){const u=`${t.dir}/${c.sqlFile}`;await p(f(a,u),"schema repair"),await y(v(o(e,u)),{recursive:!0}),await g(f(a,u),o(e,u))}const r=f(a,t.migrationsDirPath);await C(r)&&await g(r,o(e,t.migrationsDirPath),{recursive:!0}),await $(o(e,t.dir,"package.json"),`${JSON.stringify({type:"module"},null,2)}
|
|
17
17
|
`,"utf8")}l(lt,"buildPluginBackend");async function Qt(t,n,a,e,i){const s=f(t,i?.entry??`${a.dir}/src/index.ts`);await p(s,"claw entry");const{build:r}=await import("esbuild");await r({entryPoints:[s],outfile:o(n,a.dir,"index.js"),bundle:!0,platform:"node",target:"node22",format:"esm",external:[...Wt,...i?.external??[]],logLevel:"warning"});const c=f(t,a.manifestPath);await p(c,kt);const u=JSON.parse(await m(c,"utf8"));await $(o(n,a.manifestPath),`${JSON.stringify({...u,version:e.version},null,2)}
|
|
18
|
-
`,"utf8");const d=f(t,a.dir,"package.json"),w=await
|
|
19
|
-
`,"utf8");const h=f(t,i?.skillsDir??`${a.dir}/skills`);await
|
|
18
|
+
`,"utf8");const d=f(t,a.dir,"package.json"),w=await C(d)?JSON.parse(await m(d,"utf8")):{name:e.id,private:!0,type:"module"};await $(o(n,a.dir,"package.json"),`${JSON.stringify({...w,version:e.version,openclaw:{extensions:["./index.js"]}},null,2)}
|
|
19
|
+
`,"utf8");const h=f(t,i?.skillsDir??`${a.dir}/skills`);await C(h)&&await g(h,o(n,a.dir,"skills"),{recursive:!0})}l(Qt,"stageClaw");async function te(t,n){const a=new Et;await ut(a,t,"");const e=await a.generateAsync({type:"nodebuffer",compression:"DEFLATE",compressionOptions:{level:9}});return await $(n,e),e.byteLength}l(te,"zipDirectory");async function ut(t,n,a){const e=await b(n,{withFileTypes:!0});e.sort((i,s)=>i.name.localeCompare(s.name));for(const i of e){const s=`${a}${i.name}`;i.isDirectory()?(t.file(`${s}/`,null,{dir:!0,date:rt}),await ut(t,o(n,i.name),`${s}/`)):t.file(s,await m(o(n,i.name)),{date:rt,createFolders:!1})}}l(ut,"addDirectory");async function C(t){try{return await G(t),!0}catch{return!1}}l(C,"exists2");async function p(t,n){if(!await C(t))throw new Error(`${n} not found at ${t}`)}l(p,"assertExists");function ee(t){return t<1024*1024?`${(t/1024).toFixed(1)} KB`:`${(t/1024/1024).toFixed(2)} MB`}l(ee,"formatBytes");export{z as buildAgentTemplate,lt as buildPluginBackend,Ut as buildPluginTemplates,Jt as lockPlugin,Bt as packPlugin,N as readManifest,M as readPluginLocales,qt as watchPluginFrontend};
|
|
@@ -16,4 +16,4 @@ ${t}`)}}c(uv,"uv");async function buildSdkWheel(e,n){await uv(["build","--quiet"
|
|
|
16
16
|
`));await promises.mkdir(path.dirname(path.join(n,t)),{recursive:!0}),await promises.writeFile(path.join(n,t),o,"utf8")}c(stageAccessCatalog,"stageAccessCatalog");async function stagePublicFrontend(e,n,i,t){const a=path.resolve(e,t?.viteConfig??`${i.dir}/vite.config.ts`);await assertExists(a,"public pages vite config");const{build:o}=await import("vite");await o({root:path.resolve(e,i.dir),configFile:a,logLevel:"warn"});const r=path.resolve(e,t?.buildDir??`${i.dir}/dist`);await assertExists(path.join(r,path.basename(i.entryPath)),`public pages (expected the vite build to write ${path.basename(i.entryPath)})`),await promises.mkdir(path.join(n,i.dir),{recursive:!0}),await promises.cp(r,path.join(n,i.dir),{recursive:!0})}c(stagePublicFrontend,"stagePublicFrontend");async function stageBackend(e,n,i,t){await buildPluginBackend(i,{root:e,outDir:n,...t?.entry?{entry:t.entry}:{},...t?.external?{external:t.external}:{}})}c(stageBackend,"stageBackend");async function buildPluginBackend(e,n){const{root:i,outDir:t}=n,a=path.resolve(i,n.entry??`${e.dir}/src/index.ts`);await assertExists(a,"backend entry");const{build:o}=await import("esbuild");await o({entryPoints:[a],outfile:path.join(t,e.entryPath),bundle:!0,platform:"node",target:"node22",format:"esm",banner:{js:"import { createRequire as __tegoCreateRequire } from 'node:module'; const require = __tegoCreateRequire(import.meta.url);"},external:[...BACKEND_EXTERNAL,...n.external??[]],logLevel:"warning"});for(const s of e.compatibilityRepairs??[]){const l=`${e.dir}/${s.sqlFile}`;await assertExists(path.resolve(i,l),"schema repair"),await promises.mkdir(path.dirname(path.join(t,l)),{recursive:!0}),await promises.cp(path.resolve(i,l),path.join(t,l))}const r=path.resolve(i,e.migrationsDirPath);await exists2(r)&&await promises.cp(r,path.join(t,e.migrationsDirPath),{recursive:!0}),await promises.writeFile(path.join(t,e.dir,"package.json"),`${JSON.stringify({type:"module"},null,2)}
|
|
17
17
|
`,"utf8")}c(buildPluginBackend,"buildPluginBackend");async function stageClaw(e,n,i,t,a){const o=path.resolve(e,a?.entry??`${i.dir}/src/index.ts`);await assertExists(o,"claw entry");const{build:r}=await import("esbuild");await r({entryPoints:[o],outfile:path.join(n,i.dir,"index.js"),bundle:!0,platform:"node",target:"node22",format:"esm",external:[...CLAW_EXTERNAL,...a?.external??[]],logLevel:"warning"});const s=path.resolve(e,i.manifestPath);await assertExists(s,chunkROFUQLOZ_cjs.CLAW_MANIFEST_FILENAME);const l=JSON.parse(await promises.readFile(s,"utf8"));await promises.writeFile(path.join(n,i.manifestPath),`${JSON.stringify({...l,version:t.version},null,2)}
|
|
18
18
|
`,"utf8");const u=path.resolve(e,i.dir,"package.json"),d=await exists2(u)?JSON.parse(await promises.readFile(u,"utf8")):{name:t.id,private:!0,type:"module"};await promises.writeFile(path.join(n,i.dir,"package.json"),`${JSON.stringify({...d,version:t.version,openclaw:{extensions:["./index.js"]}},null,2)}
|
|
19
|
-
`,"utf8");const f=path.resolve(e,a?.skillsDir??`${i.dir}/skills`);await exists2(f)&&await promises.cp(f,path.join(n,i.dir,"skills"),{recursive:!0})}c(stageClaw,"stageClaw");async function zipDirectory(e,n){const i=new JSZip__default.default;await addDirectory(i,e,"");const t=await i.generateAsync({type:"nodebuffer",compression:"DEFLATE",compressionOptions:{level:9}});return await promises.writeFile(n,t),t.byteLength}c(zipDirectory,"zipDirectory");async function addDirectory(e,n,i){const t=await promises.readdir(n,{withFileTypes:!0});t.sort((a,o)=>a.name.localeCompare(o.name));for(const a of t){const o=`${i}${a.name}`;a.isDirectory()?(e.file(`${o}/`,null,{dir:!0,date:ZIP_EPOCH}),await addDirectory(e,path.join(n,a.name),`${o}/`)):e.file(o,await promises.readFile(path.join(n,a.name)),{date:ZIP_EPOCH,createFolders:!1})}}c(addDirectory,"addDirectory");async function exists2(e){try{return await promises.stat(e),!0}catch{return!1}}c(exists2,"exists2");async function assertExists(e,n){if(!await exists2(e))throw new Error(`${n} not found at ${e}`)}c(assertExists,"assertExists");function formatBytes(e){return e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`}c(formatBytes,"formatBytes"),exports.buildAgentTemplate=buildAgentTemplate,exports.buildPluginBackend=buildPluginBackend,exports.buildPluginTemplates=buildPluginTemplates,exports.lockPlugin=lockPlugin,exports.packPlugin=packPlugin,exports.watchPluginFrontend=watchPluginFrontend;
|
|
19
|
+
`,"utf8");const f=path.resolve(e,a?.skillsDir??`${i.dir}/skills`);await exists2(f)&&await promises.cp(f,path.join(n,i.dir,"skills"),{recursive:!0})}c(stageClaw,"stageClaw");async function zipDirectory(e,n){const i=new JSZip__default.default;await addDirectory(i,e,"");const t=await i.generateAsync({type:"nodebuffer",compression:"DEFLATE",compressionOptions:{level:9}});return await promises.writeFile(n,t),t.byteLength}c(zipDirectory,"zipDirectory");async function addDirectory(e,n,i){const t=await promises.readdir(n,{withFileTypes:!0});t.sort((a,o)=>a.name.localeCompare(o.name));for(const a of t){const o=`${i}${a.name}`;a.isDirectory()?(e.file(`${o}/`,null,{dir:!0,date:ZIP_EPOCH}),await addDirectory(e,path.join(n,a.name),`${o}/`)):e.file(o,await promises.readFile(path.join(n,a.name)),{date:ZIP_EPOCH,createFolders:!1})}}c(addDirectory,"addDirectory");async function exists2(e){try{return await promises.stat(e),!0}catch{return!1}}c(exists2,"exists2");async function assertExists(e,n){if(!await exists2(e))throw new Error(`${n} not found at ${e}`)}c(assertExists,"assertExists");function formatBytes(e){return e<1024*1024?`${(e/1024).toFixed(1)} KB`:`${(e/1024/1024).toFixed(2)} MB`}c(formatBytes,"formatBytes"),exports.buildAgentTemplate=buildAgentTemplate,exports.buildPluginBackend=buildPluginBackend,exports.buildPluginTemplates=buildPluginTemplates,exports.lockPlugin=lockPlugin,exports.packPlugin=packPlugin,exports.readManifest=readManifest,exports.readPluginLocales=readPluginLocales,exports.watchPluginFrontend=watchPluginFrontend;
|
package/dist/dev.cjs
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";var V=Object.defineProperty;var i=(e,t)=>V(e,"name",{value:t,configurable:!0});var chunkIMAVYQ4U_cjs=require("./chunk-IMAVYQ4U.cjs"),chunkPWSTCQRN_cjs=require("./chunk-PWSTCQRN.cjs");require("./chunk-W7C357A6.cjs");var chunkKMN5RHLX_cjs=require("./chunk-KMN5RHLX.cjs");require("./chunk-3WA3M347.cjs");var chunkROFUQLOZ_cjs=require("./chunk-ROFUQLOZ.cjs");require("./chunk-C2OCOPZQ.cjs");var chunkBN6TGHSB_cjs=require("./chunk-BN6TGHSB.cjs");require("./chunk-AJY7ZHXK.cjs"),require("./chunk-OP64Y4IN.cjs");var crypto=require("crypto"),fs=require("fs"),promises=require("fs/promises"),http=require("http"),module$1=require("module"),path=require("path"),child_process=require("child_process"),net=require("net"),DEV_USER_HEADER="x-tego-dev-user-id",DEV_ROLES_HEADER="x-tego-dev-roles",DEV_TENANT_ID="dev-tenant",DEFAULT_DEV_USER_ID="00000000-0000-4000-8000-000000000001";function devCallerOf(e){const t=i(r=>(Array.isArray(r)?r[0]:r)?.trim()??"","single"),n=t(e[DEV_USER_HEADER])||DEFAULT_DEV_USER_ID;let a=[];try{const r=JSON.parse(t(e[DEV_ROLES_HEADER])||"[]");Array.isArray(r)&&(a=r.filter(o=>typeof o=="string"))}catch{}return{userId:n,roles:a}}i(devCallerOf,"devCallerOf");function gatewayHeaders(e,t,n,a=Date.now()){const r={};for(const[s,c]of Object.entries(e)){const u=s.toLowerCase();c===void 0||u.startsWith("x-tego-")||u==="cookie"||u==="authorization"||u==="host"||u==="connection"||(r[u]=Array.isArray(c)?c.join(", "):c)}const o=JSON.stringify(t.roles),l=String(a);return r[chunkBN6TGHSB_cjs.PLUGIN_CONTEXT_HEADERS.userId]=t.userId,r[chunkBN6TGHSB_cjs.PLUGIN_CONTEXT_HEADERS.tenantId]=DEV_TENANT_ID,r[chunkBN6TGHSB_cjs.PLUGIN_CONTEXT_HEADERS.roles]=o,r[chunkBN6TGHSB_cjs.PLUGIN_CONTEXT_HEADERS.issuedAt]=l,r[chunkBN6TGHSB_cjs.PLUGIN_CONTEXT_HEADERS.signature]=chunkIMAVYQ4U_cjs.signPluginContext({userId:t.userId,tenantId:DEV_TENANT_ID,rolesHeader:o,avatarId:"",issuedAt:l},n),r}i(gatewayHeaders,"gatewayHeaders");var DEV_MIGRATIONS_TABLE="tego_dev_migrations";function devSchemaName(e){return`tego_dev_${e.toLowerCase().replace(/[^a-z0-9_]/g,"_")}`.slice(0,63)}i(devSchemaName,"devSchemaName");function devDatabaseUrl(e,t){const n=new URL(e);n.searchParams.delete("options");const a=n.search.replace(/^\?/,"");return n.search=`?${a?`${a}&`:""}options=${encodeURIComponent(`-c search_path=${t}`)}`,n.toString()}i(devDatabaseUrl,"devDatabaseUrl");async function connect(e){let t;try{t=await import("pg")}catch(r){throw new Error(`The local backend needs the "pg" driver; add it to the plugin's dependencies.`,{cause:r})}const n=t.default?.Client??t.Client;if(!n)throw new Error('The "pg" module has no Client constructor.');const a=new n({connectionString:e});try{await a.connect()}catch(r){throw new Error(`Cannot connect to DATABASE_URL (${redactUrl(e)}): ${r instanceof Error?r.message:String(r)}`,{cause:r})}return a}i(connect,"connect");function redactUrl(e){try{const t=new URL(e);return t.password&&(t.password="***"),t.toString()}catch{return"<unparseable DATABASE_URL>"}}i(redactUrl,"redactUrl");var quote=i(e=>`"${e.replace(/"/g,'""')}"`,"quote");async function applyDevMigrations(e){const t=await promises.readdir(e.migrationsDir).then(o=>o.filter(l=>l.endsWith(".sql")).sort(),o=>{if(o.code==="ENOENT")return[];throw o}),n=await connect(e.databaseUrl),a=quote(e.schema),r=`${a}.${quote(DEV_MIGRATIONS_TABLE)}`;try{await n.query(`CREATE SCHEMA IF NOT EXISTS ${a}`),await n.query(`CREATE TABLE IF NOT EXISTS ${r} (name text PRIMARY KEY, applied_at timestamptz NOT NULL DEFAULT now())`);const o=new Set((await n.query(`SELECT name FROM ${r}`)).rows.map(s=>String(s.name))),l=[];for(const s of t){if(o.has(s))continue;const c=await promises.readFile(path.join(e.migrationsDir,s),"utf8");await n.query("BEGIN");try{await n.query(`SET LOCAL search_path TO ${a}`),await n.query("SELECT set_config('app.tenant_id', $1, true)",[e.tenantId]),await n.query(c),await n.query(`INSERT INTO ${r} (name) VALUES ($1)`,[s]),await n.query("COMMIT")}catch(u){throw await n.query("ROLLBACK").catch(()=>{}),new Error(`Migration ${s} failed: ${u instanceof Error?u.message:String(u)}`,{cause:u})}l.push(s)}return l}finally{await n.end().catch(()=>{})}}i(applyDevMigrations,"applyDevMigrations");var REBUILD_DEBOUNCE_MS=250,STOP_TIMEOUT_MS=5e3;function freePort(){return new Promise((e,t)=>{const n=net.createServer();n.once("error",t),n.listen(0,"127.0.0.1",()=>{const a=n.address(),r=typeof a=="object"&&a?a.port:0;n.close(()=>e(r))})})}i(freePort,"freePort");function stopChild(e){return!e||e.exitCode!==null||e.signalCode!==null?Promise.resolve():new Promise(t=>{const n=setTimeout(()=>{e.kill("SIGKILL"),t()},STOP_TIMEOUT_MS);e.once("exit",()=>{clearTimeout(n),t()}),e.kill("SIGTERM")})}i(stopChild,"stopChild");async function startDevBackend(e){const{part:t,log:n}=e;if(t.runtime==="python")throw new Error("The local preview runs TypeScript backends only; a python backend runs through the platform.");const a=await freePort(),r=devSchemaName(e.pluginId),o=path.resolve(e.root,t.migrationsDirPath),l=path.join(e.outDir,t.entryPath),s={...process.env,NODE_ENV:"development",TEGO_PLUGIN_ID:e.pluginId,TEGO_PLUGIN_VERSION:e.version,TEGO_PLUGIN_INSTALL_ID:"dev-install",TEGO_TENANT_ID:DEV_TENANT_ID,TEGO_PLUGIN_PORT:String(a),TEGO_PLUGIN_HEALTH_PATH:t.healthPath,TEGO_PLUGIN_HOST_API_URL:e.hostApiUrl,TEGO_PLUGIN_HOST_API_TOKEN:e.hostApiToken,TEGO_PLUGIN_GATEWAY_KEY:e.gatewayKey,DATABASE_URL:devDatabaseUrl(e.databaseUrl,r)};let c=null,u=!1;async function p(){await promises.rm(e.outDir,{recursive:!0,force:!0}),await chunkPWSTCQRN_cjs.buildPluginBackend(t,{root:e.root,outDir:e.outDir});const f=await applyDevMigrations({databaseUrl:e.databaseUrl,schema:r,migrationsDir:o,tenantId:DEV_TENANT_ID});for(const y of f)n(`[backend] applied migration ${y}`)}i(p,"build");function m(){const f=child_process.spawn(process.execPath,[l],{env:s,stdio:["ignore","pipe","pipe"]});c=f,f.stdout?.on("data",y=>process.stdout.write(y)),f.stderr?.on("data",y=>process.stderr.write(y)),f.on("exit",y=>{c===f&&!u&&(c=null,n(`[backend] exited (code ${y??"null"}); it starts again on the next save`))})}i(m,"start"),await p(),m(),n(`[backend] ${e.pluginId} on 127.0.0.1:${a}, schema ${r} in ${redactUrl(e.databaseUrl)}`);let v=null,h=null,_=!1;const k=i(async()=>{if(h){_=!0;return}h=(async()=>{try{await p();const f=c;c=null,await stopChild(f),u||(m(),n("[backend] rebuilt and restarted"))}catch(f){n(`[backend] rebuild failed, the previous build keeps running: ${f instanceof Error?f.message:String(f)}`)}})(),await h,h=null,_&&!u&&(_=!1,await k())},"rebuild"),x=[];for(const f of[t.dir,"shared"]){const y=path.resolve(e.root,f);fs.existsSync(y)&&x.push(fs.watch(y,{recursive:!0},(I,S)=>{S&&/(^|[\\/])(node_modules|dist)([\\/]|$)/.test(String(S))||(v&&clearTimeout(v),v=setTimeout(()=>void k(),REBUILD_DEBOUNCE_MS))}))}return{port:a,schema:r,async close(){u=!0,v&&clearTimeout(v);for(const f of x)f.close();await h,await stopChild(c)}}}i(startDevBackend,"startDevBackend");var FAKE_HOST_PREFIX="/__tego/host",FAKE_FILES_PREFIX="/__tego/files",MAX_JSON_BYTES=1024*1024;async function readBody(e,t=1/0){const n=[];let a=0;for await(const r of e){if(a+=r.length,a>t)throw Object.assign(new Error("body too large"),{status:413});n.push(r)}return Buffer.concat(n)}i(readBody,"readBody");function send(e,t,n){if(n===void 0){e.writeHead(t),e.end();return}e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(n))}i(send,"send");function storagePath(e,t){const n=path.resolve(e),a=path.resolve(n,t);if(!t||path.isAbsolute(t)||!a.startsWith(n+path.sep))throw Object.assign(new Error(`invalid storage key ${JSON.stringify(t)}`),{status:400});return a}i(storagePath,"storagePath");var metaOf=i(e=>`${e}.tego-meta.json`,"metaOf");async function head(e){const t=await promises.stat(e).catch(()=>null);if(!t?.isFile())return null;const n=await promises.readFile(metaOf(e),"utf8").then(a=>JSON.parse(a),()=>({}));return{size:t.size,contentType:n.contentType??null,versionTag:crypto.createHash("sha1").update(`${t.size}:${t.mtimeMs}`).digest("hex")}}i(head,"head");async function put(e,t,n){await promises.mkdir(path.dirname(e),{recursive:!0}),await promises.writeFile(e,t),await promises.writeFile(metaOf(e),JSON.stringify({contentType:n??null}))}i(put,"put");async function handleFakeHost(e,t,n){const a=new URL(e.url??"/","http://preview.local");try{if(a.pathname.startsWith(FAKE_FILES_PREFIX)){const s=storagePath(n.storageDir,a.searchParams.get("key")??""),c=await head(s);if(!c)return send(t,404);t.writeHead(200,{"content-type":c.contentType??"application/octet-stream"}),t.end(await promises.readFile(s));return}if(e.headers.authorization!==`Bearer ${n.token}`)return send(t,401,{error:"bad host API token"});const r=a.pathname.slice(FAKE_HOST_PREFIX.length)||"/",o=i(async()=>JSON.parse((await readBody(e,MAX_JSON_BYTES)).toString("utf8")||"{}"),"json"),l=i(s=>storagePath(n.storageDir,String(s??"")),"keyFile");switch(r){case"/config":return send(t,200,{data:{values:n.config()}});case"/log":{const{level:s,message:c,fields:u}=await o();return n.log(`[backend ${String(s??"info")}] ${String(c??"")}${u?` ${JSON.stringify(u)}`:""}`),send(t,200,{data:{}})}case"/events":{const{name:s,payload:c}=await o();return n.log(`[event] ${String(s)} ${JSON.stringify(c??null)}`),send(t,200,{data:{}})}case"/notifications":return n.log(`[notification] ${JSON.stringify(await o())}`),send(t,200,{data:{id:crypto.randomUUID()}});case"/directory/identity":{const{userId:s}=await o(),c=n.users().get(String(s));return send(t,200,{data:{active:!!c,roleNames:c??[],memberships:[],checkedAt:new Date().toISOString()}})}case"/directory/list":{const{kind:s,search:c,ids:u}=await o(),p=typeof c=="string"?c.toLowerCase():"",m=Array.isArray(u)?new Set(u.map(String)):void 0,v=s==="users"?[...n.users().keys()].filter(h=>(!m||m.has(h))&&h.toLowerCase().includes(p)).map(h=>({id:h,displayName:h,active:!0})):[];return send(t,200,{data:{items:v,hasMore:!1}})}case"/directory/match":{const{userId:s,principals:c}=await o(),u=Array.isArray(c)?c:[];return send(t,200,{data:{active:n.users().has(String(s)),matched:u.flatMap((p,m)=>p.kind==="user"&&p.id===s?[m]:[]),checkedAt:new Date().toISOString()}})}case"/directory/validate":{const{principals:s}=await o(),c=Array.isArray(s)?s:[],u=n.users();return send(t,200,{data:{invalid:c.flatMap((p,m)=>p.kind==="user"&&u.has(String(p.id))?[]:[m])}})}case"/storage/get":case"/storage/stream":{const s=l(a.searchParams.get("key"));if(e.method==="POST"&&r==="/storage/stream")return await put(s,await readBody(e),e.headers["content-type"]),send(t,200,{data:{}});const c=await head(s);if(!c)return send(t,404);let u=await promises.readFile(s);const p=Number(a.searchParams.get("bytes"));Number.isFinite(p)&&p>0&&(u=u.subarray(0,p)),t.writeHead(200,{"content-type":c.contentType??"application/octet-stream"}),t.end(u);return}case"/storage/put":return await put(l(a.searchParams.get("key")),await readBody(e),e.headers["content-type"]),send(t,200,{data:{}});case"/storage/head":{const s=await head(l((await o()).key));return s?send(t,200,{data:s}):send(t,404)}case"/storage/delete":{const s=l((await o()).key);return await promises.rm(s,{force:!0}),await promises.rm(metaOf(s),{force:!0}),send(t,200,{data:{}})}case"/storage/copy":{const{sourceKey:s,destinationKey:c}=await o(),u=l(s),p=l(c);return await head(u)?(await promises.mkdir(path.dirname(p),{recursive:!0}),await promises.copyFile(u,p),await promises.copyFile(metaOf(u),metaOf(p)).catch(()=>{}),send(t,200,{data:await head(p)})):send(t,404)}case"/storage/signed-url":{const{key:s}=await o();l(s);const c=`${FAKE_FILES_PREFIX}?key=${encodeURIComponent(String(s))}`;return send(t,200,{data:{url:`${n.origin()}${c}`,path:c}})}default:return n.log(`[host] ${r} is not available in local preview`),send(t,501,{error:{code:"not_available_in_preview",message:`${r} is not available in local preview`}})}}catch(r){const o=r.status??500;send(t,o,{error:{message:r instanceof Error?r.message:String(r)}})}}i(handleFakeHost,"handleFakeHost");var SDK_PACKAGE="@tego/plugin-sdk",SDK_IMPORT=JSON.stringify(SDK_PACKAGE),SDK_FRONTEND_IMPORT=JSON.stringify(`${SDK_PACKAGE}/frontend`);function shellFiles(e){return[{path:"index.html",contents:indexHtml(e)},{path:"main.tsx",contents:mainTsx(e)},{path:"shell.css",contents:SHELL_CSS},{path:"plugin-surface.tsx",contents:PLUGIN_SURFACE_STUB}]}i(shellFiles,"shellFiles");var escapeHtml=i(e=>e.replace(/[&<>"]/g,t=>({"&":"&","<":"<",">":">",'"':"""})[t]),"escapeHtml");function indexHtml({displayName:e}){return`<!doctype html>
|
|
2
|
+
<html lang="en" class="h-full">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>${escapeHtml(e)} \xB7 local preview</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body class="h-full">
|
|
9
|
+
<div id="tego-dev-bar"></div>
|
|
10
|
+
<div id="tego-dev-plugin"></div>
|
|
11
|
+
<script type="module" src="/main.tsx"><\/script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
14
|
+
`}i(indexHtml,"indexHtml");var SHELL_CSS=`@tailwind base;
|
|
15
|
+
@tailwind components;
|
|
16
|
+
@tailwind utilities;
|
|
17
|
+
|
|
18
|
+
html,
|
|
19
|
+
body {
|
|
20
|
+
height: 100%;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
body {
|
|
24
|
+
display: flex;
|
|
25
|
+
flex-direction: column;
|
|
26
|
+
@apply bg-background text-foreground antialiased;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#tego-dev-plugin {
|
|
30
|
+
flex: 1;
|
|
31
|
+
min-height: 0;
|
|
32
|
+
overflow: auto;
|
|
33
|
+
}
|
|
34
|
+
`,PLUGIN_SURFACE_STUB=`import type { ReactNode } from 'react';
|
|
35
|
+
|
|
36
|
+
function Unavailable(): ReactNode {
|
|
37
|
+
return (
|
|
38
|
+
<div className='flex h-full items-center justify-center p-6 text-sm text-muted-foreground'>
|
|
39
|
+
Chat is not available in local preview.
|
|
40
|
+
</div>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const ChatSurface = Unavailable;
|
|
45
|
+
export const ChatPanel = Unavailable;
|
|
46
|
+
export function useTopic() {
|
|
47
|
+
return { topic: undefined, topics: [], loading: false };
|
|
48
|
+
}
|
|
49
|
+
`;function mainTsx(e){const t=JSON.stringify({pluginId:e.pluginId,displayName:e.displayName,nav:e.nav.map(a=>({key:a.key,labelKey:a.labelKey,path:a.path??"/"})),config:e.config,locales:e.locales,userHeader:DEV_USER_HEADER,rolesHeader:DEV_ROLES_HEADER,defaultUser:DEFAULT_DEV_USER_ID},null,2),n=e.entry?`import plugin from ${JSON.stringify(e.entry)};`:"const plugin = undefined;";return`// Generated by \`tego-plugin dev\` on every start; edits here are overwritten.
|
|
50
|
+
import '@tego/ui/styles/tokens.css';
|
|
51
|
+
import './shell.css';
|
|
52
|
+
|
|
53
|
+
import { pluginI18nNamespace } from ${SDK_IMPORT};
|
|
54
|
+
import type { PluginFrontendModule, PluginHostContext, PluginRoute } from ${SDK_FRONTEND_IMPORT};
|
|
55
|
+
import {
|
|
56
|
+
Button,
|
|
57
|
+
Input,
|
|
58
|
+
Select,
|
|
59
|
+
SelectContent,
|
|
60
|
+
SelectItem,
|
|
61
|
+
SelectTrigger,
|
|
62
|
+
SelectValue,
|
|
63
|
+
Toaster,
|
|
64
|
+
TooltipProvider,
|
|
65
|
+
} from '@tego/ui';
|
|
66
|
+
import i18next from 'i18next';
|
|
67
|
+
import { useState, useSyncExternalStore } from 'react';
|
|
68
|
+
import { createRoot } from 'react-dom/client';
|
|
69
|
+
import { initReactI18next } from 'react-i18next';
|
|
70
|
+
|
|
71
|
+
${n}
|
|
72
|
+
|
|
73
|
+
const DEV = ${t};
|
|
74
|
+
const BASE = '/p/' + DEV.pluginId;
|
|
75
|
+
const NAMESPACE = pluginI18nNamespace(DEV.pluginId);
|
|
76
|
+
|
|
77
|
+
function stored(key: string, fallback: string): string {
|
|
78
|
+
try {
|
|
79
|
+
return localStorage.getItem('tegoDev.' + key) ?? fallback;
|
|
80
|
+
} catch {
|
|
81
|
+
return fallback;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function store(key: string, value: string): void {
|
|
85
|
+
try {
|
|
86
|
+
localStorage.setItem('tegoDev.' + key, value);
|
|
87
|
+
} catch {
|
|
88
|
+
// storage blocked: the choice lasts until the next reload
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const user = {
|
|
93
|
+
userId: stored('userId', DEV.defaultUser),
|
|
94
|
+
roles: stored('roles', '').split(',').map(role => role.trim()).filter(Boolean),
|
|
95
|
+
};
|
|
96
|
+
const language = DEV.locales.includes(stored('lang', 'zh-CN')) ? stored('lang', 'zh-CN') : (DEV.locales[0] ?? 'en');
|
|
97
|
+
if (stored('theme', 'light') === 'dark') document.documentElement.classList.add('dark');
|
|
98
|
+
|
|
99
|
+
async function loadLocale(tag: string): Promise<void> {
|
|
100
|
+
if (i18next.hasResourceBundle(tag, NAMESPACE)) return;
|
|
101
|
+
const response = await fetch('/i18n/' + tag + '.json');
|
|
102
|
+
if (response.ok) i18next.addResourceBundle(tag, NAMESPACE, await response.json(), true, true);
|
|
103
|
+
}
|
|
104
|
+
await i18next.use(initReactI18next).init({
|
|
105
|
+
lng: language,
|
|
106
|
+
fallbackLng: false,
|
|
107
|
+
ns: [NAMESPACE],
|
|
108
|
+
defaultNS: NAMESPACE,
|
|
109
|
+
resources: {},
|
|
110
|
+
interpolation: { escapeValue: false },
|
|
111
|
+
});
|
|
112
|
+
await loadLocale(language);
|
|
113
|
+
|
|
114
|
+
const listeners = new Set<() => void>();
|
|
115
|
+
function readRoute(): PluginRoute {
|
|
116
|
+
const path = location.pathname.slice(BASE.length) || '/';
|
|
117
|
+
return { path: path.startsWith('/') ? path : '/' + path, search: location.search };
|
|
118
|
+
}
|
|
119
|
+
let route = readRoute();
|
|
120
|
+
function routeChanged(): void {
|
|
121
|
+
const next = readRoute();
|
|
122
|
+
if (next.path === route.path && next.search === route.search) return;
|
|
123
|
+
route = next;
|
|
124
|
+
for (const listener of listeners) listener();
|
|
125
|
+
}
|
|
126
|
+
addEventListener('popstate', routeChanged);
|
|
127
|
+
if (!location.pathname.startsWith(BASE)) history.replaceState(null, '', BASE + '/');
|
|
128
|
+
|
|
129
|
+
function navigate(to: string): void {
|
|
130
|
+
history.pushState(null, '', to.startsWith('/p/') ? to : BASE + (to.startsWith('/') ? to : '/' + to));
|
|
131
|
+
routeChanged();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const api: PluginHostContext['api'] = {
|
|
135
|
+
request: (path, init = {}) =>
|
|
136
|
+
fetch('/api/v1/tego-os/plugin-api/' + DEV.pluginId + (path.startsWith('/') ? path : '/' + path), {
|
|
137
|
+
...init,
|
|
138
|
+
headers: {
|
|
139
|
+
...(init.headers as Record<string, string> | undefined),
|
|
140
|
+
[DEV.userHeader]: user.userId,
|
|
141
|
+
[DEV.rolesHeader]: JSON.stringify(user.roles),
|
|
142
|
+
},
|
|
143
|
+
}),
|
|
144
|
+
async json<T>(path: string, init?: RequestInit): Promise<T> {
|
|
145
|
+
const response = await api.request(path, init);
|
|
146
|
+
if (!response.ok) throw new Error((init?.method ?? 'GET') + ' ' + path + ': ' + response.status);
|
|
147
|
+
return (await response.json()) as T;
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const navKeys = DEV.nav.map(entry => entry.key);
|
|
152
|
+
const context: PluginHostContext = {
|
|
153
|
+
pluginId: DEV.pluginId,
|
|
154
|
+
layout: { collapseSidebar: () => () => {} },
|
|
155
|
+
basePath: BASE,
|
|
156
|
+
publicPagesUrl: location.origin + '/public/' + DEV.pluginId + '/',
|
|
157
|
+
hostUrl: path => new URL(path, location.origin).toString(),
|
|
158
|
+
user: { userId: user.userId, tenantId: 'dev-tenant', roles: user.roles, displayName: user.userId },
|
|
159
|
+
api,
|
|
160
|
+
route: {
|
|
161
|
+
current: () => route,
|
|
162
|
+
subscribe: listener => {
|
|
163
|
+
listeners.add(listener);
|
|
164
|
+
return () => listeners.delete(listener);
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
events: { subscribe: () => () => {}, connected: () => false, subscribeStatus: () => () => {} },
|
|
168
|
+
config: DEV.config,
|
|
169
|
+
navigate,
|
|
170
|
+
can: navKey => navKeys.includes(navKey),
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
function useRoute(): PluginRoute {
|
|
174
|
+
return useSyncExternalStore(context.route.subscribe, context.route.current);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function DevBar() {
|
|
178
|
+
const current = useRoute();
|
|
179
|
+
const [lang, setLang] = useState(language);
|
|
180
|
+
const [dark, setDark] = useState(document.documentElement.classList.contains('dark'));
|
|
181
|
+
const [userId, setUserId] = useState(user.userId);
|
|
182
|
+
const [roles, setRoles] = useState(user.roles.join(', '));
|
|
183
|
+
const switchUser = () => {
|
|
184
|
+
store('userId', userId.trim() || DEV.defaultUser);
|
|
185
|
+
store('roles', roles);
|
|
186
|
+
location.reload();
|
|
187
|
+
};
|
|
188
|
+
return (
|
|
189
|
+
<div className='flex shrink-0 items-center gap-2 border-b border-border bg-card px-3 py-2 text-sm'>
|
|
190
|
+
<span className='shrink-0 font-medium'>{DEV.displayName}</span>
|
|
191
|
+
<span className='shrink-0 rounded-control bg-sunken px-1.5 py-0.5 font-mono text-xs text-muted-foreground'>preview</span>
|
|
192
|
+
<nav className='flex min-w-0 flex-1 items-center gap-1 overflow-x-auto'>
|
|
193
|
+
{DEV.nav.map(entry => (
|
|
194
|
+
<Button
|
|
195
|
+
key={entry.key}
|
|
196
|
+
size='sm'
|
|
197
|
+
variant={current.path === entry.path ? 'secondary' : 'ghost'}
|
|
198
|
+
onClick={() => navigate(entry.path)}
|
|
199
|
+
>
|
|
200
|
+
{i18next.t(entry.labelKey, { ns: NAMESPACE, lng: lang })}
|
|
201
|
+
</Button>
|
|
202
|
+
))}
|
|
203
|
+
</nav>
|
|
204
|
+
<Select
|
|
205
|
+
value={lang}
|
|
206
|
+
onValueChange={async next => {
|
|
207
|
+
await loadLocale(next);
|
|
208
|
+
await i18next.changeLanguage(next);
|
|
209
|
+
store('lang', next);
|
|
210
|
+
setLang(next);
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
<SelectTrigger className='h-8 w-28' aria-label='Language'>
|
|
214
|
+
<SelectValue />
|
|
215
|
+
</SelectTrigger>
|
|
216
|
+
<SelectContent>
|
|
217
|
+
{DEV.locales.map(tag => (
|
|
218
|
+
<SelectItem key={tag} value={tag}>
|
|
219
|
+
{tag}
|
|
220
|
+
</SelectItem>
|
|
221
|
+
))}
|
|
222
|
+
</SelectContent>
|
|
223
|
+
</Select>
|
|
224
|
+
<Button
|
|
225
|
+
size='sm'
|
|
226
|
+
variant='ghost'
|
|
227
|
+
onClick={() => {
|
|
228
|
+
document.documentElement.classList.toggle('dark', !dark);
|
|
229
|
+
store('theme', dark ? 'light' : 'dark');
|
|
230
|
+
setDark(!dark);
|
|
231
|
+
}}
|
|
232
|
+
>
|
|
233
|
+
{dark ? 'Light' : 'Dark'}
|
|
234
|
+
</Button>
|
|
235
|
+
<form
|
|
236
|
+
className='flex shrink-0 items-center gap-1'
|
|
237
|
+
onSubmit={event => {
|
|
238
|
+
event.preventDefault();
|
|
239
|
+
switchUser();
|
|
240
|
+
}}
|
|
241
|
+
>
|
|
242
|
+
<Input className='h-8 w-32' aria-label='User id' value={userId} onChange={event => setUserId(event.target.value)} />
|
|
243
|
+
<Input
|
|
244
|
+
className='h-8 w-36'
|
|
245
|
+
aria-label='Roles, comma-separated'
|
|
246
|
+
placeholder='roles'
|
|
247
|
+
value={roles}
|
|
248
|
+
onChange={event => setRoles(event.target.value)}
|
|
249
|
+
/>
|
|
250
|
+
<Button size='sm' variant='outline' type='submit'>
|
|
251
|
+
Sign in as
|
|
252
|
+
</Button>
|
|
253
|
+
</form>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
createRoot(document.getElementById('tego-dev-bar')!).render(
|
|
259
|
+
<TooltipProvider>
|
|
260
|
+
<DevBar />
|
|
261
|
+
<Toaster />
|
|
262
|
+
</TooltipProvider>
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
const mountPoint = document.getElementById('tego-dev-plugin')!;
|
|
266
|
+
const frontend = plugin as PluginFrontendModule | undefined;
|
|
267
|
+
if (frontend) {
|
|
268
|
+
frontend.mount(mountPoint, context);
|
|
269
|
+
} else {
|
|
270
|
+
mountPoint.innerHTML =
|
|
271
|
+
'<p style="padding:24px">This plugin has no frontend part; its API is served at /api/v1/tego-os/plugin-api/' +
|
|
272
|
+
DEV.pluginId +
|
|
273
|
+
'/</p>';
|
|
274
|
+
}
|
|
275
|
+
`}i(mainTsx,"mainTsx");var DEFAULT_DEV_PORT=5280,PORT_ATTEMPTS=20,DEV_READY_PREFIX="TEGO_DEV_READY ",DEV_DIR=".tego-dev",PLUGIN_API_PREFIX="/api/v1/tego-os/plugin-api/";function displayNameOf(e){const{displayName:t}=e;return typeof t=="string"?t:t.en??t["zh-CN"]??Object.values(t)[0]??e.id}i(displayNameOf,"displayNameOf");function packageDir(e,t){for(let n=path.resolve(e);;n=path.dirname(n)){const a=path.join(n,"node_modules",...t.split("/"));if(fs.existsSync(path.join(a,"package.json")))return fs.realpathSync(a);if(path.dirname(n)===n)return}}i(packageDir,"packageDir");function sendJson(e,t,n){e.writeHead(t,{"content-type":"application/json"}),e.end(JSON.stringify(n))}i(sendJson,"sendJson");function listen(e,t,n){return new Promise((a,r)=>{const o=i(s=>{e.off("listening",l),r(s)},"onError"),l=i(()=>{e.off("error",o);const s=e.address();a(typeof s=="object"&&s?s.port:t)},"onListening");e.once("error",o),e.once("listening",l),e.listen(t,n)})}i(listen,"listen");async function shellTailwind(e,t){const n=module$1.createRequire(path.join(e,"vite.config.ts"));let a,r;try{a=n("@tego/ui/tailwind-preset"),r=path.dirname(n.resolve("@tego/ui"))}catch(l){throw new Error(`The preview cannot load @tego/ui's Tailwind preset from ${e}: ${l instanceof Error?l.message:String(l)}. Install the plugin's dependencies.`,{cause:l})}const o=(await import("tailwindcss")).default;return o({presets:[a],content:[path.join(r,"**/*.{js,cjs}"),path.join(t,"*.tsx")]})}i(shellTailwind,"shellTailwind");function perStylesheet(e,t,n){return{postcssPlugin:"tego-dev-stylesheets",async Once(a,r){const o=a.source?.input.file,l=o&&path.resolve(o)===e?[t]:n;if(l.length===0)return;const s=await r.postcss(l).process(a,{from:o});r.result.messages.push(...s.messages)}}}i(perStylesheet,"perStylesheet");function servePlugins(e){return Array.isArray(e)?e.flat(1/0).map(t=>{if(!t||typeof t!="object")return t;const{buildStart:n}=t;return typeof n!="function"?t:{...t,buildStart(...a){const r=new Proxy(this,{get(o,l){if(l==="emitFile")return()=>"";const s=Reflect.get(o,l);return typeof s=="function"?s.bind(o):s}});return n.apply(r,a)}}}):e}i(servePlugins,"servePlugins");async function startDevServer(e){const t=path.resolve(e.root),n=e.log??(d=>console.log(d)),a=await chunkPWSTCQRN_cjs.readManifest(t),r=chunkROFUQLOZ_cjs.resolvePluginParts(a),o=path.join(t,DEV_DIR),l=path.join(o,"shell"),s=crypto.randomBytes(32).toString("hex"),c=crypto.randomBytes(24).toString("hex");let u="";const p=new Map;let m,v={};const h=r.frontend?path.resolve(t,r.frontend.dir):t,{loadConfigFromFile:_,createServer:k,mergeConfig:x}=await import("vite");if(r.frontend){const d=path.join(h,"vite.config.ts");fs.existsSync(d)&&(v=(await _({command:"serve",mode:"development"},d,t))?.config??{});const g=v.build?.rollupOptions?.input,w=path.resolve(t,typeof g=="string"?g:`${r.frontend.dir}/src/index.tsx`);if(!fs.existsSync(w))throw new Error(`The frontend entry ${path.relative(t,w)} does not exist.`);m=path.relative(l,w).split(path.sep).join("/"),m.startsWith(".")||(m=`./${m}`)}const f=i(()=>r.frontend?chunkPWSTCQRN_cjs.readPluginLocales(t,a,r.frontend.i18nDirPath):Promise.resolve(new Map),"localeFiles"),y=[...(await f()).keys()].map(d=>d.replace(/\.json$/,""));await promises.mkdir(l,{recursive:!0});for(const d of shellFiles({pluginId:a.id,displayName:displayNameOf(a),nav:r.frontend?.nav??[],config:r.frontend?.config?chunkKMN5RHLX_cjs.applyConfigDefaults(r.frontend.config.schema,{}):{},entry:m,locales:y}))await promises.writeFile(path.join(l,d.path),d.contents);let I,S;const C=i(()=>r.backend?.config?chunkKMN5RHLX_cjs.applyConfigDefaults(r.backend.config.schema,{}):{},"backendConfig"),D=http.createServer((d,g)=>void j(d,g));async function j(d,g){const w=(d.url??"/").split("?")[0];try{if(w.startsWith(FAKE_HOST_PREFIX)||w.startsWith(FAKE_FILES_PREFIX)){await handleFakeHost(d,g,{token:c,storageDir:path.join(o,"storage"),config:C,origin:i(()=>u,"origin"),users:i(()=>p,"users"),log:n});return}const E=/^\/i18n\/([A-Za-z-]+)\.json$/.exec(w);if(E){const T=(await f()).get(`${E[1]}.json`);if(T===void 0)return sendJson(g,404,{error:`no ${E[1]}.json`});g.writeHead(200,{"content-type":"application/json"}),g.end(T);return}const b=`${PLUGIN_API_PREFIX}${a.id}`;if(w===b||w.startsWith(`${b}/`)){H(d,g,(d.url??"/").slice(b.length)||"/");return}$.middlewares(d,g,()=>sendJson(g,404,{error:`${w} not found`}))}catch(E){sendJson(g,500,{error:E instanceof Error?E.message:String(E)})}}i(j,"route");function H(d,g,w){if(!I){sendJson(g,503,{error:S??"The plugin has no backend part."});return}const E=devCallerOf(d.headers);p.set(E.userId,E.roles);const b=http.request({host:"127.0.0.1",port:I.port,method:d.method,path:w,headers:gatewayHeaders(d.headers,E,s)},T=>{g.writeHead(T.statusCode??502,T.headers),T.pipe(g)});b.on("error",T=>{g.headersSent?g.destroy():sendJson(g,502,{error:`The backend is not answering (${T.message}); see the terminal.`})}),d.pipe(b)}i(H,"proxy");const B=path.join(l,"shell.css"),F=(()=>{const d=v.css?.postcss;return d&&typeof d=="object"?d.plugins??[]:[]})(),A=(()=>{const d=packageDir(h,"react-i18next");return(d&&packageDir(d,"i18next"))??packageDir(h,"i18next")})(),{css:M,mode:G,build:K,...P}=v;P.plugins=servePlugins(P.plugins);const $=await k(x(P,{configFile:!1,root:l,mode:"development",appType:"spa",clearScreen:!1,logLevel:"info",esbuild:{jsx:"automatic",jsxDev:!1},css:{postcss:{plugins:[perStylesheet(B,await shellTailwind(h,l),F)]}},resolve:{alias:[{find:"@tego/chat/plugin-surface",replacement:path.join(l,"plugin-surface.tsx")},...A?[{find:/^i18next$/,replacement:A}]:[]]},server:{middlewareMode:!0,hmr:{server:D},fs:{allow:[t,l,...A?[A]:[]]}},optimizeDeps:{entries:[path.join(l,"index.html")]}})),N=e.host??"127.0.0.1";let R;const O=e.port??DEFAULT_DEV_PORT;for(let d=0;R===void 0;d++)try{R=await listen(D,O===0?0:O+d,N)}catch(g){if(g.code!=="EADDRINUSE"||O===0||d>=PORT_ATTEMPTS)throw await $.close(),g}const L=N==="0.0.0.0"||N==="::"?"127.0.0.1":N;if(u=`http://${L.includes(":")?`[${L}]`:L}:${R}`,!r.backend)S="The plugin has no backend part.";else if(e.backend===!1)S="The backend is off (--no-backend).";else if(!e.databaseUrl)S="The backend needs a PostgreSQL: start the preview with DATABASE_URL=postgres://\u2026 (each plugin gets its own schema).";else try{I=await startDevBackend({root:t,pluginId:a.id,version:a.version,part:r.backend,databaseUrl:e.databaseUrl,outDir:path.join(o,"backend"),hostApiUrl:`${u}${FAKE_HOST_PREFIX}`,hostApiToken:c,gatewayKey:s,log:n})}catch(d){S=`The backend did not start: ${d instanceof Error?d.message:String(d)}`}S&&r.backend&&n(`[backend] ${S}`);const U=`${u}/p/${a.id}/`;return n(`${a.id} ${a.version}: local preview at ${U}`),e.databaseUrl&&I&&n(` database ${redactUrl(e.databaseUrl)}, schema ${I.schema}`),n(`${DEV_READY_PREFIX}${JSON.stringify({url:U})}`),{url:U,async close(){await I?.close(),await $.close(),D.closeAllConnections(),await new Promise(d=>D.close(()=>d()))}}}i(startDevServer,"startDevServer"),exports.DEFAULT_DEV_PORT=DEFAULT_DEV_PORT,exports.DEV_DIR=DEV_DIR,exports.DEV_READY_PREFIX=DEV_READY_PREFIX,exports.DEV_ROLES_HEADER=DEV_ROLES_HEADER,exports.DEV_TENANT_ID=DEV_TENANT_ID,exports.DEV_USER_HEADER=DEV_USER_HEADER,exports.applyDevMigrations=applyDevMigrations,exports.devCallerOf=devCallerOf,exports.devDatabaseUrl=devDatabaseUrl,exports.devSchemaName=devSchemaName,exports.gatewayHeaders=gatewayHeaders,exports.handleFakeHost=handleFakeHost,exports.shellFiles=shellFiles,exports.startDevServer=startDevServer,exports.storagePath=storagePath;
|