twenty-sdk 2.7.0 → 2.8.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/{catalog-sync-5PU93p3u.mjs → catalog-sync-C3eVfUoF.mjs} +1 -1
- package/dist/{catalog-sync-B7123fv7.js → catalog-sync-CPVoL1dR.js} +1 -1
- package/dist/cli/utilities/api/api-client.d.ts +3 -0
- package/dist/cli/utilities/api/api-service.d.ts +1 -0
- package/dist/cli/utilities/build/manifest/manifest-extract-config.d.ts +4 -0
- package/dist/cli/utilities/build/manifest/utils/validate-view-filter-operands.d.ts +6 -0
- package/dist/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state.d.ts +0 -1
- package/dist/cli.cjs +7 -7
- package/dist/cli.mjs +859 -855
- package/dist/define/index.cjs +14 -14
- package/dist/define/index.cjs.map +1 -1
- package/dist/define/index.d.ts +66 -546
- package/dist/define/index.mjs +1623 -1977
- package/dist/define/index.mjs.map +1 -1
- package/dist/front-component/index.cjs +13 -13
- package/dist/front-component/index.cjs.map +1 -1
- package/dist/front-component/index.d.ts +10 -2
- package/dist/front-component/index.mjs +1420 -1312
- package/dist/front-component/index.mjs.map +1 -1
- package/dist/{get-function-input-schema-BZ7_XyUh-CSpgPUBa.mjs → get-function-input-schema-BZ7_XyUh-CTltddyp.mjs} +1 -1
- package/dist/{get-function-input-schema-BZ7_XyUh-BprcNdqJ.js → get-function-input-schema-BZ7_XyUh-CsGwFW9C.js} +1 -1
- package/dist/{login-oauth-CLUI-EwN.js → login-oauth-Bjd38X4F.js} +62 -51
- package/dist/{login-oauth-OKavkVCc.mjs → login-oauth-DxrMUIpb.mjs} +3593 -3811
- package/dist/operations.cjs +1 -1
- package/dist/operations.mjs +2 -2
- package/dist/ui/index.cjs +20 -20
- package/dist/ui/index.mjs +6277 -6170
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./login-oauth-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("./login-oauth-Bjd38X4F.js"),r=require("chalk"),l=e=>e&&e.__esModule?e:{default:e},t=l(r);class s{async execute(c){c.remote&&o.ConfigService.setActiveRemote(c.remote),console.log(t.default.blue("Syncing marketplace catalog..."));const a=await new o.ApiService().syncMarketplaceCatalog();a.success||(console.error(t.default.red(`Catalog sync failed: ${a.error instanceof Error?a.error.message:String(a.error)}`)),process.exit(1)),console.log(t.default.green("✓ Marketplace catalog synced successfully"))}}exports.CatalogSyncCommand=s;
|
|
@@ -11,6 +11,9 @@ export declare class ApiClient {
|
|
|
11
11
|
token?: string;
|
|
12
12
|
skipAuth?: boolean;
|
|
13
13
|
});
|
|
14
|
+
getFrontendUrl(): Promise<string | null>;
|
|
15
|
+
getWorkspaceFrontendUrl(): Promise<string | null>;
|
|
16
|
+
private getCurrentWorkspaceFrontendUrl;
|
|
14
17
|
validateAuth(): Promise<{
|
|
15
18
|
authValid: boolean;
|
|
16
19
|
serverUp: boolean;
|
|
@@ -20,6 +20,7 @@ export declare class ApiService {
|
|
|
20
20
|
authValid: boolean;
|
|
21
21
|
serverUp: boolean;
|
|
22
22
|
}>;
|
|
23
|
+
getWorkspaceFrontendUrl(): Promise<string | null>;
|
|
23
24
|
refreshToken(): Promise<string | null>;
|
|
24
25
|
findApplicationRegistrationByUniversalIdentifier(...args: Parameters<ApplicationApi['findApplicationRegistrationByUniversalIdentifier']>): Promise<ApiResponse<{
|
|
25
26
|
id: string;
|
|
@@ -2,10 +2,12 @@ export declare enum TargetFunction {
|
|
|
2
2
|
DefineApplication = "defineApplication",
|
|
3
3
|
DefineApplicationRole = "defineApplicationRole",
|
|
4
4
|
DefineField = "defineField",
|
|
5
|
+
DefineIndex = "defineIndex",
|
|
5
6
|
DefineLogicFunction = "defineLogicFunction",
|
|
6
7
|
DefinePostInstallLogicFunction = "definePostInstallLogicFunction",
|
|
7
8
|
DefinePreInstallLogicFunction = "definePreInstallLogicFunction",
|
|
8
9
|
DefineObject = "defineObject",
|
|
10
|
+
DefinePermissionFlag = "definePermissionFlag",
|
|
9
11
|
DefineRole = "defineRole",
|
|
10
12
|
DefineSkill = "defineSkill",
|
|
11
13
|
DefineAgent = "defineAgent",
|
|
@@ -20,8 +22,10 @@ export declare enum TargetFunction {
|
|
|
20
22
|
export declare enum ManifestEntityKey {
|
|
21
23
|
Application = "application",
|
|
22
24
|
Fields = "fields",
|
|
25
|
+
Indexes = "indexes",
|
|
23
26
|
LogicFunctions = "logicFunctions",
|
|
24
27
|
Objects = "objects",
|
|
28
|
+
PermissionFlags = "permissionFlags",
|
|
25
29
|
Roles = "roles",
|
|
26
30
|
Skills = "skills",
|
|
27
31
|
Agents = "agents",
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type FieldManifest, type ObjectManifest, type ViewManifest } from 'twenty-shared/application';
|
|
2
|
+
export declare const validateViewFilterOperands: ({ views, objects, fields, }: {
|
|
3
|
+
views: ViewManifest[];
|
|
4
|
+
objects: ObjectManifest[];
|
|
5
|
+
fields: FieldManifest[];
|
|
6
|
+
}) => string[];
|
package/dist/cli.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var lm=Object.create;var Fo=Object.defineProperty;var cm=Object.getOwnPropertyDescriptor;var fm=Object.getOwnPropertyNames;var dm=Object.getPrototypeOf,pm=Object.prototype.hasOwnProperty;var hm=(l,s,r,c)=>{if(s&&typeof s=="object"||typeof s=="function")for(let d of fm(s))!pm.call(l,d)&&d!==r&&Fo(l,d,{get:()=>s[d],enumerable:!(c=cm(s,d))||c.enumerable});return l};var gm=(l,s,r)=>(r=l!=null?lm(dm(l)):{},hm(s||!l||!l.__esModule?Fo(r,"default",{value:l,enumerable:!0}):r,l));const q=require("path"),m=require("./login-oauth-
|
|
2
|
+
"use strict";var lm=Object.create;var Fo=Object.defineProperty;var cm=Object.getOwnPropertyDescriptor;var fm=Object.getOwnPropertyNames;var dm=Object.getPrototypeOf,pm=Object.prototype.hasOwnProperty;var hm=(l,s,r,c)=>{if(s&&typeof s=="object"||typeof s=="function")for(let d of fm(s))!pm.call(l,d)&&d!==r&&Fo(l,d,{get:()=>s[d],enumerable:!(c=cm(s,d))||c.enumerable});return l};var gm=(l,s,r)=>(r=l!=null?lm(dm(l)):{},hm(s||!l||!l.__esModule?Fo(r,"default",{value:l,enumerable:!0}):r,l));const q=require("path"),m=require("./login-oauth-Bjd38X4F.js"),tt=require("node:fs/promises"),mm=require("chalk"),vm=require("inquirer"),Q=require("uuid"),ym=require("chokidar"),_m=require("crypto"),Tn=require("node:child_process"),wm=require("node:path"),T=require("react/jsx-runtime"),At=require("react"),Xo=require("commander"),Sm=require("util"),Mt=l=>l&&l.__esModule?l:{default:l},Ji=Mt(q),y=Mt(mm),je=Mt(vm),Qo=Mt(ym),xm=Mt(_m),Lo=Mt(wm),eu=Mt(At),Z=process.env.INIT_CWD||process.cwd(),pe=l=>l&&!l?.startsWith("/")?q.join(Z,l):l,Im=async l=>{const s=q.join(l,"package.json");if(await m.pathExists(s))try{const r=await tt.readFile(s,"utf-8"),c=JSON.parse(r);return c.dependencies?.["twenty-sdk"]??c.devDependencies?.["twenty-sdk"]??void 0}catch{return}},No=l=>{const s=l.match(/(\d+)\./);if(s)return parseInt(s[1],10);const r=l.match(/^[~^>=<]*(\d+)$/);return r?parseInt(r[1],10):null},tn=async l=>{const s=m.packageJson.version,r=await Im(l);if(!r)return;const c=No(s),d=No(r);c===null||d===null||c!==d&&(console.warn(y.default.yellow(`⚠ Version mismatch: your app requires twenty-sdk@${d}.x but the CLI running is v${s}. Major version mismatches may cause unexpected behavior.
|
|
3
3
|
Update with: npm install -g twenty-sdk@${d}`)),console.warn(""))};class tu{async execute(s){s.private?await this.executePrivate(s):await this.executeNpm(s)}async executeNpm(s){const r=s.appPath??Z;await tn(r),console.log(y.default.blue("Publishing to npm...")),console.log(y.default.gray(`App path: ${r}
|
|
4
4
|
`));const c=await m.appPublish({appPath:r,npmTag:s.tag,onProgress:d=>console.log(y.default.gray(d))});c.success||(console.error(y.default.red(c.error.message)),process.exit(1)),console.log(y.default.green("✓ Published to npm successfully"))}async executePrivate(s){const r=s.appPath??Z;await tn(r),console.log(y.default.blue("Deploying application...")),console.log(y.default.gray(`App path: ${r}
|
|
5
5
|
`));const c=D=>console.log(y.default.gray(D)),d=await m.appBuild({appPath:r,tarball:!0,onProgress:c});d.success||(console.error(y.default.red(d.error.message)),process.exit(1));const x=await m.appDeploy({tarballPath:d.data.tarballPath,remote:s.remote,onProgress:c});x.success||(console.error(y.default.red(x.error.message)),process.exit(1));const E=await m.readJson(Ji.default.join(r,"package.json")).catch(()=>{}),C=E?.name??x.data.name,R=E?.version??"unknown",U=m.ConfigService.getActiveRemote();console.log(y.default.green(`
|
|
@@ -327,7 +327,7 @@ ${R}
|
|
|
327
327
|
// },
|
|
328
328
|
// ],
|
|
329
329
|
});
|
|
330
|
-
`},Km="src/application.config.ts",Ir="src";class iu{async execute(s,r){try{const c=s??await this.getEntity(),d=this.getFolderName(c),x=r?q.join(Z,r):q.join(Z,Ir,d);await m.ensureDir(x);const{name:E,file:C}=await this.getEntityData(c),R=q.join(x,this.getFileName(E,c));if(await m.pathExists(R)){const{overwrite:U}=await this.handleFileExist();if(!U)return}await tt.writeFile(R,C),console.log(y.default.green(`✓ Created ${d}:`),y.default.cyan(q.relative(Z,R))),c===m.e.Object&&await this.promptAndCreateObjectCompanions(E,r),c===m.e.ConnectionProvider&&await this.registerConnectionProviderServerVariables(E)}catch(c){console.error(y.default.red("Add new entity failed:"),c instanceof Error?c.message:c),process.exit(1)}}async getEntityData(s){switch(s){case m.e.Object:{const r=await this.getObjectData(),c=r.nameSingular,d=Q.v4(),x=Q.v4();this.lastObjectUniversalIdentifier=d,this.lastNameFieldUniversalIdentifier=x,this.lastObjectLabelSingular=r.labelSingular;const E=Dm({data:r,universalIdentifier:d,nameFieldUniversalIdentifier:x});return{name:c,file:E}}case m.e.Field:{const r=await this.getFieldData(),c=r.name,d=Om({data:r});return{name:c,file:d}}case m.e.LogicFunction:{const r=await this.getEntityName(s),c=$m({name:r});return{name:r,file:c}}case m.e.FrontComponent:{const r=await this.getEntityName(s),c=Um({name:r});return{name:r,file:c}}case m.e.Role:{const r=await this.getEntityName(s),c=Vm({name:r});return{name:r,file:c}}case m.e.Skill:{const r=await this.getEntityName(s),c=Hm({name:r});return{name:r,file:c}}case m.e.Agent:{const r=await this.getEntityName(s),c=Gm({name:r});return{name:r,file:c}}case m.e.ConnectionProvider:{const r=await this.getEntityName(s),c=qm({name:r});return{name:r,file:c}}case m.e.View:{const c=(await this.getViewData()).name,d=Ki({name:c});return{name:c,file:d}}case m.e.NavigationMenuItem:{const r=await this.getEntityName(s),c=$o({name:r});return{name:r,file:c}}case m.e.PageLayout:{const r=await this.getEntityName(s),c=Wm({name:r});return{name:r,file:c}}case m.e.PageLayoutTab:{const r=await this.getEntityName(s),c=Bm({name:r});return{name:r,file:c}}case m.e.CommandMenuItem:{const r=await this.getEntityName(s),c=Mm({name:r});return{name:r,file:c}}default:m
|
|
330
|
+
`},Km="src/application.config.ts",Ir="src";class iu{async execute(s,r){try{const c=s??await this.getEntity(),d=this.getFolderName(c),x=r?q.join(Z,r):q.join(Z,Ir,d);await m.ensureDir(x);const{name:E,file:C}=await this.getEntityData(c),R=q.join(x,this.getFileName(E,c));if(await m.pathExists(R)){const{overwrite:U}=await this.handleFileExist();if(!U)return}await tt.writeFile(R,C),console.log(y.default.green(`✓ Created ${d}:`),y.default.cyan(q.relative(Z,R))),c===m.e.Object&&await this.promptAndCreateObjectCompanions(E,r),c===m.e.ConnectionProvider&&await this.registerConnectionProviderServerVariables(E)}catch(c){console.error(y.default.red("Add new entity failed:"),c instanceof Error?c.message:c),process.exit(1)}}async getEntityData(s){switch(s){case m.e.Object:{const r=await this.getObjectData(),c=r.nameSingular,d=Q.v4(),x=Q.v4();this.lastObjectUniversalIdentifier=d,this.lastNameFieldUniversalIdentifier=x,this.lastObjectLabelSingular=r.labelSingular;const E=Dm({data:r,universalIdentifier:d,nameFieldUniversalIdentifier:x});return{name:c,file:E}}case m.e.Field:{const r=await this.getFieldData(),c=r.name,d=Om({data:r});return{name:c,file:d}}case m.e.LogicFunction:{const r=await this.getEntityName(s),c=$m({name:r});return{name:r,file:c}}case m.e.FrontComponent:{const r=await this.getEntityName(s),c=Um({name:r});return{name:r,file:c}}case m.e.Role:{const r=await this.getEntityName(s),c=Vm({name:r});return{name:r,file:c}}case m.e.Skill:{const r=await this.getEntityName(s),c=Hm({name:r});return{name:r,file:c}}case m.e.Agent:{const r=await this.getEntityName(s),c=Gm({name:r});return{name:r,file:c}}case m.e.ConnectionProvider:{const r=await this.getEntityName(s),c=qm({name:r});return{name:r,file:c}}case m.e.View:{const c=(await this.getViewData()).name,d=Ki({name:c});return{name:c,file:d}}case m.e.NavigationMenuItem:{const r=await this.getEntityName(s),c=$o({name:r});return{name:r,file:c}}case m.e.PageLayout:{const r=await this.getEntityName(s),c=Wm({name:r});return{name:r,file:c}}case m.e.PageLayoutTab:{const r=await this.getEntityName(s),c=Bm({name:r});return{name:r,file:c}}case m.e.CommandMenuItem:{const r=await this.getEntityName(s),c=Mm({name:r});return{name:r,file:c}}default:m.$()}}async promptAndCreateObjectCompanions(s,r){const{createCompanions:c}=await je.default.prompt([{type:"confirm",name:"createCompanions",message:"Also create a view, navigation menu item, and record page layout for this object? (recommended)",default:!0}]);if(!c||!this.lastObjectUniversalIdentifier)return;const d=Q.v4(),x=Q.v4(),E=Ki({name:`all-${ue(s)}`,universalIdentifier:d,objectUniversalIdentifier:this.lastObjectUniversalIdentifier,fields:this.lastNameFieldUniversalIdentifier?[{fieldMetadataUniversalIdentifier:this.lastNameFieldUniversalIdentifier,position:0,isVisible:!0,size:200}]:[]}),C=r?q.join(Z,r):q.join(Z,Ir,this.getFolderName(m.e.View));await m.ensureDir(C);const R=`all-${ue(s)}.ts`,U=q.join(C,R);if(await m.pathExists(U)){const{overwrite:Ee}=await this.handleFileExist();if(!Ee)return}await tt.writeFile(U,E),console.log(y.default.green("✓ Created view:"),y.default.cyan(q.relative(Z,U)));const D=this.buildRecordPageFieldsViewFields(this.lastNameFieldUniversalIdentifier),H=Ki({name:`${ue(s)}-record-page-fields`,universalIdentifier:x,objectUniversalIdentifier:this.lastObjectUniversalIdentifier,type:m._E.FIELDS_WIDGET,fields:D}),ht=`${ue(s)}-record-page-fields.ts`,Se=q.join(C,ht);if(await m.pathExists(Se)){const{overwrite:Ee}=await this.handleFileExist();if(!Ee)return}await tt.writeFile(Se,H),console.log(y.default.green("✓ Created record-page-fields view:"),y.default.cyan(q.relative(Z,Se)));const ke=$o({name:s,type:"OBJECT",targetObjectUniversalIdentifier:this.lastObjectUniversalIdentifier}),bt=r?q.join(Z,r):q.join(Z,Ir,this.getFolderName(m.e.NavigationMenuItem));await m.ensureDir(bt);const xe=`${ue(s)}.ts`,Ye=q.join(bt,xe);if(await m.pathExists(Ye)){const{overwrite:Ee}=await this.handleFileExist();if(!Ee)return}await tt.writeFile(Ye,ke),console.log(y.default.green("✓ Created navigation menu item:"),y.default.cyan(q.relative(Z,Ye)));const Pn=km({objectLabelSingular:this.lastObjectLabelSingular??s,objectUniversalIdentifier:this.lastObjectUniversalIdentifier,fieldsWidgetViewUniversalIdentifier:x}),Fe=r?q.join(Z,r):q.join(Z,Ir,this.getFolderName(m.e.PageLayout));await m.ensureDir(Fe);const Ct=`${ue(s)}-record-page-layout.ts`,Ie=q.join(Fe,Ct);if(await m.pathExists(Ie)){const{overwrite:Ee}=await this.handleFileExist();if(!Ee)return}await tt.writeFile(Ie,Pn),console.log(y.default.green("✓ Created record page layout:"),y.default.cyan(q.relative(Z,Ie)))}async registerConnectionProviderServerVariables(s){const r=ue(s).toUpperCase().replace(/-/g,"_"),c=[{name:`${r}_CLIENT_ID`,description:"OAuth client ID issued by the third-party provider. Filled in once by the server admin on the application registration.",isSecret:!1},{name:`${r}_CLIENT_SECRET`,description:"OAuth client secret issued by the third-party provider. Stored encrypted; never echoed in API responses.",isSecret:!0}],d=await Rm({projectRoot:Z,variables:c});switch(d.status){case"appended":case"created":{console.log(y.default.green(`✓ Added ${c.map(x=>x.name).join(" + ")} to defineApplication.serverVariables:`),y.default.cyan(q.relative(Z,d.file)));break}case"skipped-existing":console.log(y.default.dim(` (${c.map(x=>x.name).join(" / ")} already declared on defineApplication.serverVariables)`));break;case"skipped-no-config":case"skipped-no-app-call":console.log(y.default.yellow(`! Couldn't auto-update ${Km}. Add these manually to defineApplication.serverVariables:
|
|
331
331
|
`+c.map(x=>` ${x.name}: { description: '...', isSecret: ${x.isSecret}, isRequired: true },`).join(`
|
|
332
332
|
`)));break}}buildRecordPageFieldsViewFields(s){const c=["createdAt","updatedAt","createdBy","updatedBy"].map(x=>({defaultFieldName:x,isVisible:!0,size:200}));return(s?[{fieldMetadataUniversalIdentifier:s,isVisible:!0,size:200},...c]:c).map((x,E)=>({...x,position:E}))}async getEntity(){const{entity:s}=await je.default.prompt([{type:"select",name:"entity",message:"What entity do you want to create?",default:"",choices:Object.values(m.e)}]);return s}async handleFileExist(){return await je.default.prompt([{type:"confirm",name:"overwrite",message:"File already exists. Do you want to overwrite it?",default:!1}])}async getEntityName(s){const{name:r}=await je.default.prompt([{type:"input",name:"name",message:`Enter a name for your new ${s}:`,default:"",validate:c=>c.length===0?`${s} name is required`:!0}]);return r}async getFieldData(){return je.default.prompt([{type:"input",name:"name",message:"Enter a name for your field:",default:"",validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0},{type:"input",name:"label",message:"Enter a label for your field:",default:s=>zi(s.name),validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0},{type:"select",name:"type",message:"Select the field type:",choices:Object.values(m.R),default:m.R.TEXT},{type:"input",name:"objectUniversalIdentifier",message:"Enter the universalIdentifier of the object this field belongs to:",default:"fill-later",validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0},{type:"input",name:"description",message:"Enter a description for your field (optional):",default:""}])}async getObjectData(){return je.default.prompt([{type:"input",name:"nameSingular",message:"Enter a name singular for your object (eg: company):",default:"",validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0},{type:"input",name:"namePlural",message:"Enter a name plural for your object (eg: companies):",default:"",validate:(s,r)=>s.trim()===r?.nameSingular.trim()?"Name plural must be different from name singular":!s||s.trim().length===0?"Please enter a non empty string":!0},{type:"input",name:"labelSingular",message:"Enter a label singular for your object:",default:s=>zi(s.nameSingular),validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0},{type:"input",name:"labelPlural",message:"Enter a label plural for your object:",default:s=>zi(s.namePlural),validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0}])}async getViewData(){return je.default.prompt([{type:"input",name:"name",message:"Enter a name for your view:",default:"",validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0},{type:"input",name:"objectUniversalIdentifier",message:"Enter the universalIdentifier of the object this view belongs to:",default:"fill-later",validate:s=>!s||s.trim().length===0?"Please enter a non empty string":!0}])}getFolderName(s){return`${ue(s)}s`}getFileName(s,r){return r===m.e.FrontComponent?`${ue(s)}.tsx`:`${ue(s)}.ts`}}class su{async execute(s){const r=s.appPath??Z;await tn(r),console.log(y.default.blue("Building application...")),console.log(y.default.gray(`App path: ${r}
|
|
333
333
|
`));const c=await m.appBuild({appPath:r,tarball:s.tarball,onProgress:d=>console.log(y.default.gray(d))});c.success||(console.error(y.default.red(c.error.message)),process.exit(1)),console.log(y.default.green(`✓ Build succeeded (${c.data.fileCount} file${c.data.fileCount===1?"":"s"})`)),console.log(y.default.gray(`Output: ${c.data.outputDir}`)),c.data.tarballPath&&console.log(y.default.gray(`Tarball: ${c.data.tarballPath}`))}}class au{async execute({appPath:s=Z,postInstall:r=!1,preInstall:c=!1,functionUniversalIdentifier:d,functionName:x,payload:E="{}"}){let C;try{C=JSON.parse(E)}catch{console.error(y.default.red("Invalid JSON payload. Please provide valid JSON.")),process.exit(1)}const R=r?"post install":c?"pre install":d??x;console.log(y.default.blue(`🚀 Executing function "${R}"...`)),console.log(y.default.gray(` Payload: ${JSON.stringify(C)}
|
|
@@ -337,7 +337,7 @@ ${R}
|
|
|
337
337
|
`)),H.error.stackTrace&&(console.log(y.default.gray("Stack trace:")),console.log(y.default.gray(H.error.stackTrace)))),H.logs&&(console.log(y.default.bold("Logs:")),console.log(y.default.gray(H.logs))),console.log(y.default.cyan("─".repeat(60))),H.status!=="SUCCESS"&&process.exit(1)}}class ou{constructor(){this.apiService=new m.ApiService}async execute({appPath:s=Z,functionUniversalIdentifier:r,functionName:c}){try{const d=await m.readManifestFromFile(s);d||process.exit(1),this.logWatchInfo({appName:d.application.displayName,functionUniversalIdentifier:r,functionName:c}),await this.apiService.subscribeToLogs({applicationUniversalIdentifier:d.application.universalIdentifier,functionUniversalIdentifier:r,functionName:c})}catch(d){console.error(y.default.red("Watch logs failed:"),d instanceof Error?d.message:d),process.exit(1)}}logWatchInfo({appName:s,functionUniversalIdentifier:r,functionName:c}){const d=s??"Twenty Application",x=r||c?`function "${r||c}"`:"functions";console.log(y.default.blue(`🚀 Watching ${d} ${x} logs:
|
|
338
338
|
`))}}const jm=l=>`${y.default.cyan(l.file)}:${y.default.yellow(String(l.line))}:${y.default.yellow(String(l.column+1))} - ${y.default.red("error")} ${l.text}`;class uu{async execute(s){const r=s.appPath??Z;console.log(y.default.blue("Running type check...")),console.log(y.default.gray(`App path: ${r}
|
|
339
339
|
`));const c=await m.runTypecheck(r);c.length===0&&(console.log(y.default.green("✓ No type errors found")),process.exit(0));for(const d of c)console.log(jm(d));console.log(`
|
|
340
|
-
`,y.default.red(`✗ Found ${c.length} type error${c.length===1?"":"s"}`)),process.exit(1)}}const et=(l,s)=>console.warn(y.default.yellow(`⚠ \`twenty ${l}\` is deprecated. Use \`twenty ${s}\` instead.`)),Ym=l=>{const s=new su,r=new uu,c=new ou,d=new au,x=new iu,E=new tu,C=new nu,R=new ru;l.command("build [appPath]",{hidden:!0}).option("--tarball","Also pack into a .tgz tarball").action(async(U,D)=>{et("build","dev:build"),await s.execute({appPath:pe(U),tarball:D.tarball})}),l.command("typecheck [appPath]",{hidden:!0}).action(async U=>{et("typecheck","dev:typecheck"),await r.execute({appPath:pe(U)})}),l.command("logs [appPath]",{hidden:!0}).option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Only show logs for the function with this universal ID").option("-n, --functionName <functionName>","Only show logs for the function with this name").action(async(U,D)=>{et("logs","dev:function:logs"),await c.execute({...D,appPath:pe(U)})}),l.command("exec [appPath]",{hidden:!0}).option("--postInstall","Execute post-install logic function if defined").option("--preInstall","Execute pre-install logic function if defined").option("-p, --payload <payload>","JSON payload to send to the function","{}").option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Universal ID of the function to execute").option("-n, --functionName <functionName>","Name of the function to execute").action(async(U,D)=>{et("exec","dev:function:exec"),!D?.postInstall&&!D?.preInstall&&!D?.functionUniversalIdentifier&&!D?.functionName&&(console.error(y.default.red("Error: Either --postInstall, --preInstall, --functionName (-n), or --functionUniversalIdentifier (-u) is required.")),process.exit(1)),await d.execute({...D,payload:D?.payload??"{}",appPath:pe(U)})}),l.command("add [entityType]",{hidden:!0}).option("--path <path>","Path in which the entity should be created.").action(async(U,D)=>{et("add","dev:add"),await x.execute(U,D?.path)}),l.command("publish [appPath]",{hidden:!0}).option("--tag <tag>","npm dist-tag (e.g. beta, next)").action(async(U,D)=>{et("publish","app:publish"),await E.execute({appPath:pe(U),tag:D.tag})}),l.command("deploy [appPath]",{hidden:!0}).option("-r, --remote <name>","Deploy to a specific remote").action(async(U,D)=>{et("deploy","app:publish --private"),await E.execute({appPath:pe(U),private:!0,remote:D.remote})}),l.command("install [appPath]",{hidden:!0}).option("-r, --remote <name>","Install on a specific remote").action(async(U,D)=>{et("install","app:install"),await C.execute({appPath:pe(U),remote:D.remote})}),l.command("uninstall [appPath]",{hidden:!0}).option("-y, --yes","Skip confirmation prompt").action(async(U,D)=>{et("uninstall","app:uninstall");try{const H=await R.execute({appPath:pe(U),askForConfirmation:!D?.yes});process.exit(H.success?0:1)}catch{process.exit(1)}}),l.command("catalog-sync",{hidden:!0}).option("-r, --remote <name>","Sync on a specific remote").action(async U=>{et("catalog-sync","dev:catalog-sync");const{CatalogSyncCommand:D}=await Promise.resolve().then(()=>require("./catalog-sync-
|
|
340
|
+
`,y.default.red(`✗ Found ${c.length} type error${c.length===1?"":"s"}`)),process.exit(1)}}const et=(l,s)=>console.warn(y.default.yellow(`⚠ \`twenty ${l}\` is deprecated. Use \`twenty ${s}\` instead.`)),Ym=l=>{const s=new su,r=new uu,c=new ou,d=new au,x=new iu,E=new tu,C=new nu,R=new ru;l.command("build [appPath]",{hidden:!0}).option("--tarball","Also pack into a .tgz tarball").action(async(U,D)=>{et("build","dev:build"),await s.execute({appPath:pe(U),tarball:D.tarball})}),l.command("typecheck [appPath]",{hidden:!0}).action(async U=>{et("typecheck","dev:typecheck"),await r.execute({appPath:pe(U)})}),l.command("logs [appPath]",{hidden:!0}).option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Only show logs for the function with this universal ID").option("-n, --functionName <functionName>","Only show logs for the function with this name").action(async(U,D)=>{et("logs","dev:function:logs"),await c.execute({...D,appPath:pe(U)})}),l.command("exec [appPath]",{hidden:!0}).option("--postInstall","Execute post-install logic function if defined").option("--preInstall","Execute pre-install logic function if defined").option("-p, --payload <payload>","JSON payload to send to the function","{}").option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Universal ID of the function to execute").option("-n, --functionName <functionName>","Name of the function to execute").action(async(U,D)=>{et("exec","dev:function:exec"),!D?.postInstall&&!D?.preInstall&&!D?.functionUniversalIdentifier&&!D?.functionName&&(console.error(y.default.red("Error: Either --postInstall, --preInstall, --functionName (-n), or --functionUniversalIdentifier (-u) is required.")),process.exit(1)),await d.execute({...D,payload:D?.payload??"{}",appPath:pe(U)})}),l.command("add [entityType]",{hidden:!0}).option("--path <path>","Path in which the entity should be created.").action(async(U,D)=>{et("add","dev:add"),await x.execute(U,D?.path)}),l.command("publish [appPath]",{hidden:!0}).option("--tag <tag>","npm dist-tag (e.g. beta, next)").action(async(U,D)=>{et("publish","app:publish"),await E.execute({appPath:pe(U),tag:D.tag})}),l.command("deploy [appPath]",{hidden:!0}).option("-r, --remote <name>","Deploy to a specific remote").action(async(U,D)=>{et("deploy","app:publish --private"),await E.execute({appPath:pe(U),private:!0,remote:D.remote})}),l.command("install [appPath]",{hidden:!0}).option("-r, --remote <name>","Install on a specific remote").action(async(U,D)=>{et("install","app:install"),await C.execute({appPath:pe(U),remote:D.remote})}),l.command("uninstall [appPath]",{hidden:!0}).option("-y, --yes","Skip confirmation prompt").action(async(U,D)=>{et("uninstall","app:uninstall");try{const H=await R.execute({appPath:pe(U),askForConfirmation:!D?.yes});process.exit(H.success?0:1)}catch{process.exit(1)}}),l.command("catalog-sync",{hidden:!0}).option("-r, --remote <name>","Sync on a specific remote").action(async U=>{et("catalog-sync","dev:catalog-sync");const{CatalogSyncCommand:D}=await Promise.resolve().then(()=>require("./catalog-sync-CPVoL1dR.js"));await new D().execute({remote:U.remote})})};class Jm{constructor({state:s,notify:r}){this.state=s,this.notify=r}async execute(s){const r=this.state.steps.buildManifest;r.status="in_progress",this.state.updatePipeline({status:"building"});const c=[{message:"Building manifest",status:"info"}],d=await m.buildAndValidateManifest(s.appPath);if(!d.success){for(const E of d.errors)c.push({message:E,status:"error"});return r.output={result:null},r.status="error",this.state.updatePipeline({status:"error"}),this.state.applyStepEvents(c),null}if(d.warnings.length>0)for(const E of d.warnings)c.push({message:`⚠ ${E}`,status:"warning"});c.push({message:"Successfully built manifest",status:"success"});const x={manifest:d.manifest,filePaths:d.filePaths};return r.output={result:x},r.status="done",this.state.updatePipeline({appName:d.manifest.application.displayName}),this.state.updateEntitiesFromManifest(d.filePaths),this.state.applyStepEvents(c),x}}class Zm{constructor({apiService:s,state:r,notify:c}){this.hasRetried=!1,this.apiService=s,this.state=r,this.notify=c}async execute(){const s=this.state.steps.checkServer,r=await this.apiService.validateAuth();if(!r.serverUp){const d=await m.detectLocalServer();return d&&!this.hasRetried?(this.hasRetried=!0,await new m.ConfigService().setConfig({apiUrl:d}),this.execute()):(s.output.errorLogged||(s.output={isReady:!1,errorLogged:!0},s.status="error",this.state.applyStepEvents([{message:`Cannot reach Twenty server.
|
|
341
341
|
|
|
342
342
|
Start a local server:
|
|
343
343
|
yarn twenty docker:start
|
|
@@ -345,9 +345,9 @@ ${R}
|
|
|
345
345
|
Check server status:
|
|
346
346
|
yarn twenty docker:status
|
|
347
347
|
|
|
348
|
-
Waiting for server...`,status:"error"}]),this.state.updatePipeline({status:"error"})),!1)}if(!r.authValid)return s.output.errorLogged||(s.output={isReady:!1,errorLogged:!0},s.status="error",this.state.applyStepEvents([{message:"Authentication failed. Run `yarn twenty remote:add --local` to authenticate.",status:"error"}]),this.state.updatePipeline({status:"error"})),!1;const c=s.output.isReady;
|
|
349
|
-
`);this.buffer=r.pop()??"";for(const c of r)this.processLine(c)}processLine(s){if(s.includes("Starting compilation in watch mode...")||s.includes("Starting incremental compilation...")){this.pendingErrors=[];return}if(s.includes("Watching for file changes.")){const c=this.hasErrors;this.hasErrors=this.pendingErrors.length>0,(this.hasErrors||c)&&this.onErrors(this.pendingErrors),this.pendingErrors=[];return}const r=m.parseTscOutputLine(s);r&&this.pendingErrors.push(r)}}const tv=new Set(["node_modules","dist",".twenty"]);class nv{constructor(s){this.watcher=null,this.appPath=s.appPath,this.handleChangeDetected=s.handleChangeDetected,this.verbose=s.verbose??!1}async start(){const s=this.appPath;this.watcher=Qo.default.watch(this.appPath,{ignoreInitial:!this.verbose,ignored:r=>{const c=q.relative(s,r);if(c==="")return!1;const d=c.split(Ji.default.sep)[0];return tv.has(d)||d.startsWith(".")},awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}),this.watcher.on("all",(r,c)=>{if(r==="addDir"||r==="unlinkDir")return;const d=q.relative(this.appPath,c),x=d.startsWith(m._),E=["package.json","yarn.lock"].includes(d);!(d.endsWith(".ts")||d.endsWith(".tsx"))&&!x&&!E||this.handleChangeDetected(d,r)})}async close(){await this.watcher?.close()}}class rv{constructor(s){this.manifestWatcher=null,this.logicFunctionsWatcher=null,this.frontComponentsWatcher=null,this.assetWatcher=null,this.dependencyWatcher=null,this.tscWatcher=null,this.state=s.state,this.scheduleSync=s.scheduleSync,this.notify=s.notify,this.onFileBuilt=s.onFileBuilt,this.shouldSkipTypecheck=s.shouldSkipTypecheck,this.verbose=s.verbose??!1}async start(){this.state.steps.startWatchers.status="in_progress",this.notify(),this.manifestWatcher=new nv({appPath:this.state.appPath,handleChangeDetected:this.handleChangeDetected.bind(this),verbose:this.verbose}),await this.manifestWatcher.start()}async handleWatcherRestarts(s){const{logicFunctions:r,frontComponents:c}=s.filePaths;if(!this.state.steps.startWatchers.output.watchersStarted){this.state.steps.startWatchers.output.watchersStarted=!0,this.state.steps.startWatchers.status="done",await this.startFileWatchers(r,c);return}this.logicFunctionsWatcher?.shouldRestart(r)&&await this.logicFunctionsWatcher.restart(r),this.frontComponentsWatcher?.shouldRestart(c)&&await this.frontComponentsWatcher.restart(c)}async close(){this.tscWatcher?.close(),await Promise.all([this.manifestWatcher?.close(),this.logicFunctionsWatcher?.close(),this.frontComponentsWatcher?.close(),this.assetWatcher?.close(),this.dependencyWatcher?.close()])}handleChangeDetected(s,r){this.state.addEvent({message:`Change detected: ${s}`,status:"info"}),r==="unlink"?this.state.removeEntity(s):this.state.updateEntityStatus(s,"building"),this.notify(),this.scheduleSync()}handleFileBuildError(s){this.state.addEvent({message:"Build failed:",status:"error"});for(const r of s)this.state.addEvent({message:r.error,status:"error"});this.notify()}handleFileBuilt(s){this.verbose&&this.state.addEvent({message:`Successfully built ${s.builtPath}`,status:"success"}),this.state.steps.uploadFiles.output.builtFileInfos.set(s.builtPath,{checksum:s.checksum,builtPath:s.builtPath,sourcePath:s.sourcePath,fileFolder:s.fileFolder,usesSdkClient:s.usesSdkClient}),this.onFileBuilt(s),this.notify(),this.scheduleSync()}async startFileWatchers(s,r){await Promise.all([this.startTscWatcher(),this.startLogicFunctionsWatcher(s),this.startFrontComponentsWatcher(r),this.startAssetWatcher(),this.startDependencyWatcher()])}async startLogicFunctionsWatcher(s){this.logicFunctionsWatcher=m.createLogicFunctionsWatcher({appPath:this.state.appPath,sourcePaths:s,shouldSkipTypecheck:this.shouldSkipTypecheck,handleBuildError:this.handleFileBuildError.bind(this),handleFileBuilt:this.handleFileBuilt.bind(this)}),await this.logicFunctionsWatcher.start()}async startFrontComponentsWatcher(s){this.frontComponentsWatcher=m.createFrontComponentsWatcher({appPath:this.state.appPath,sourcePaths:s,shouldSkipTypecheck:this.shouldSkipTypecheck,handleBuildError:this.handleFileBuildError.bind(this),handleFileBuilt:this.handleFileBuilt.bind(this)}),await this.frontComponentsWatcher.start()}async startAssetWatcher(){this.assetWatcher=new Do({appPath:this.state.appPath,fileFolder:m.b.PublicAsset,watchPaths:[m._],handleFileBuilt:this.handleFileBuilt.bind(this)}),await this.assetWatcher.start()}async startDependencyWatcher(){this.dependencyWatcher=new Do({appPath:this.state.appPath,fileFolder:m.b.Dependencies,watchPaths:["package.json","yarn.lock"],handleFileBuilt:this.handleFileBuilt.bind(this)}),this.dependencyWatcher.start()}async startTscWatcher(){this.tscWatcher=new ev({appPath:this.state.appPath,onErrors:this.handleTypecheckErrors.bind(this)}),await this.tscWatcher.start()}handleTypecheckErrors(s){s.length===0?this.state.addEvent({message:"Typecheck passed",status:"success"}):this.state.applyStepEvents(s.map(r=>({message:`Type error in ${r.file}(${r.line},${r.column}): ${r.text}`,status:"error"}))),this.notify()}}class iv{constructor({apiService:s,state:r,notify:c,verbose:d}){this.apiService=s,this.state=r,this.notify=c,this.verbose=d??!1}async execute(s){const r=this.state.steps.syncApplication;r.status="in_progress",this.state.updatePipeline({status:"syncing"});const c=[],d=m.manifestUpdateChecksums({manifest:s.manifest,builtFileInfos:s.builtFileInfos});c.push({message:"Manifest checksums set",status:"info"}),await m.writeManifestToOutput(s.appPath,d),c.push({message:"Manifest saved to output directory",status:"info"}),c.push({message:"Syncing manifest",status:"info"});const x=await this.apiService.syncApplication(d);if(x.success){c.push({message:"✓ Synced",status:"success"}),r.output={syncStatus:"synced",error:null},r.status="done",this.state.updatePipeline({status:"synced",error:null}),this.state.updateAllEntitiesStatus("success"),this.state.applyStepEvents(c);return}const E=this.verbose?null:m.formatManifestValidationErrors(x.error);E?(c.push(...E),c.push({message:"Add --verbose to see full error log",status:"info"})):c.push({message:`Sync failed with error: ${m.serializeError(x.error)}`,status:"error"});const C=E?E[0].message:"Sync failed";r.output={syncStatus:"error",error:C},r.status="error",this.state.updatePipeline({status:"error",error:C}),this.state.updateAllEntitiesStatus("error"),this.state.applyStepEvents(c)}}class sv{constructor({state:s,notify:r,verbose:c}){this.uploadedCount=0,this.failedCount=0,this.totalQueued=0,this.state=s,this.notify=r,this.verbose=c??!1}get isInitialized(){return m.o(this.state.steps.uploadFiles.output.fileUploader)}initialize(s){const r=this.state.steps.uploadFiles;r.output={...r.output,fileUploader:new m.FileUploader({appPath:s.appPath,applicationUniversalIdentifier:s.universalIdentifier})},r.status="in_progress",this.notify(),this.uploadPendingFiles()}uploadFile(s,r,c){const d=this.state.steps.uploadFiles;if(!d.output.fileUploader)return;d.status="in_progress",this.totalQueued++,this.verbose&&this.state.addEvent({message:`Uploading ${s}`,status:"info"}),this.state.updateEntityStatus(r,"uploading"),this.notify();const x=d.output.fileUploader.uploadFile({builtPath:s,fileFolder:c}).then(E=>{E.success?(this.uploadedCount++,this.verbose&&this.state.addEvent({message:`Successfully uploaded ${s}`,status:"success"}),this.state.updateEntityStatus(r,"success")):(this.failedCount++,this.state.addEvent({message:`Failed to upload ${s}: ${E.error}`,status:"error"}))}).catch(E=>{this.failedCount++,this.state.addEvent({message:`Upload failed for ${s}: ${E}`,status:"error"})}).finally(()=>{d.output.activeUploads.delete(x),d.output.activeUploads.size===0&&(this.logUploadSummary(),d.status="done",this.notify())});d.output.activeUploads.add(x)}async waitForUploads(){const s=this.state.steps.uploadFiles;for(;s.output.activeUploads.size>0;)await Promise.all(s.output.activeUploads);s.status="done",this.notify()}logUploadSummary(){if(this.totalQueued===0){this.resetCounters();return}this.failedCount>0&&this.state.addEvent({message:`Uploaded ${this.uploadedCount}/${this.totalQueued} files (${this.failedCount} failed)`,status:"error"}),this.state.addEvent({message:`Successfully uploaded ${this.uploadedCount} file${this.uploadedCount!==1?"s":""}`,status:"success"}),this.resetCounters()}resetCounters(){this.uploadedCount=0,this.failedCount=0,this.totalQueued=0}uploadPendingFiles(){for(const[s,{fileFolder:r,sourcePath:c}]of this.state.steps.uploadFiles.output.builtFileInfos.entries())this.uploadFile(s,c,r)}}class av{constructor(s){this.syncTimer=null,this.serverCheckInterval=null,this.skipTypecheck=!0,this.debounceMs=s.debounceMs??2e3,this.state=s.state,this.verbose=s.verbose??!1,this.apiService=new m.ApiService({disableInterceptors:!0});const r=this.apiService,c=new m.ConfigService;this.clientService=new m.ClientService;const d={state:this.state,notify:()=>this.state.notify()};this.checkServerStep=new Zm({...d,apiService:r}),this.buildManifestStep=new Jm(d),this.registerAppStep=new Qm({...d,apiService:r,configService:c}),this.uploadFilesStep=new sv({...d,verbose:this.verbose}),this.generateApiClientStep=new Xm({...d,clientService:this.clientService,configService:c}),this.syncApplicationStep=new iv({...d,apiService:r,verbose:this.verbose}),this.startWatchersStep=new rv({...d,scheduleSync:this.scheduleSync.bind(this),onFileBuilt:this.handleFileBuilt.bind(this),shouldSkipTypecheck:()=>this.skipTypecheck,verbose:this.verbose})}async start(){const s=Ji.default.join(this.state.appPath,m.u);await m.ensureDir(s),await m.emptyDir(s),this.verbose||(this.state.addEvent({message:"Add --verbose to see fully detailed logs",status:"info"}),this.state.notify()),await this.startWatchersStep.start(),this.serverCheckInterval=setInterval(()=>{this.checkServerHealth()},2e3)}async close(){this.serverCheckInterval&&clearInterval(this.serverCheckInterval),await this.startWatchersStep.close()}getState(){return this.state}handleFileBuilt(s){this.state.steps.uploadFiles.output.fileUploader&&this.uploadFilesStep.uploadFile(s.builtPath,s.sourcePath,s.fileFolder)}async checkServerHealth(){const s=this.state.steps.checkServer.output.isReady;await this.checkServerStep.execute()&&!s&&this.scheduleSync()}scheduleSync(){this.syncTimer&&clearTimeout(this.syncTimer),this.syncTimer=setTimeout(()=>{this.syncTimer=null,this.performSync()},this.debounceMs)}async performSync(){if(!this.state.pipeline.isSyncing){this.state.updatePipeline({isSyncing:!0});try{await this.runSyncPipeline()}catch(s){this.state.addEvent({message:`Sync failed with error: ${m.serializeError(s)}`,status:"error"}),this.state.updatePipeline({status:"error"}),this.state.updateAllEntitiesStatus("error")}finally{this.state.updatePipeline({isSyncing:!1})}}}async runSyncPipeline(){if(!await this.checkServerStep.execute())return;this.state.steps.ensureValidTokens.status="done";const r=await this.buildManifestStep.execute({appPath:this.state.appPath});if(!r||(await this.startWatchersStep.handleWatcherRestarts(r),!this.uploadFilesStep.isInitialized&&!await this.initializePipeline(r.manifest)))return;const c=this.state.hasObjectsOrFieldsChanged(r.manifest);await this.uploadFilesStep.waitForUploads(),await this.syncApplicationStep.execute({manifest:r.manifest,builtFileInfos:this.state.steps.uploadFiles.output.builtFileInfos,appPath:this.state.appPath}),this.state.steps.syncApplication.status!=="error"&&c&&(await this.generateApiClientStep.execute({appPath:this.state.appPath,credentials:this.registerAppStep.registrationCredentials}),this.skipTypecheck=!1)}async initializePipeline(s){await this.registerAppStep.execute({manifest:s});const r=await this.apiService.createDevelopmentApplication({universalIdentifier:s.application.universalIdentifier,name:s.application.displayName});return!r.success||!r.data?(this.state.applyStepEvents([{message:"Failed to install development application",status:"error"},{message:JSON.stringify(r,null,2),status:"error"}]),this.state.updatePipeline({status:"error"}),!1):(this.state.steps.resolveApplication.output={applicationId:r.data.id,universalIdentifier:r.data.universalIdentifier},this.state.steps.resolveApplication.status="done",this.state.applyStepEvents([{message:"Application installed",status:"success"}]),this.uploadFilesStep.initialize({appPath:this.state.appPath,universalIdentifier:s.application.universalIdentifier}),!0)}}const ov={objects:m.e.Object,fields:m.e.Field,logicFunctions:m.e.LogicFunction,frontComponents:m.e.FrontComponent,roles:m.e.Role,skills:m.e.Skill,connectionProviders:m.e.ConnectionProvider,views:m.e.View,navigationMenuItems:m.e.NavigationMenuItem,pageLayouts:m.e.PageLayout,pageLayoutTabs:m.e.PageLayoutTab,commandMenuItems:m.e.CommandMenuItem},uv=200,lv={pending:["building","uploading","success","error"],building:["pending","uploading","success","error"],uploading:["pending","success","error"],success:["pending","building","uploading","error"],error:["pending","building","uploading","success"]};class cv{constructor(s){this.eventIdCounter=0,this.appPath=s.appPath,this.frontendUrl=s.frontendUrl,this.previousObjectsFieldsFingerprint=null,this.steps={checkServer:{output:{isReady:!1,errorLogged:!1},status:"idle"},ensureValidTokens:{output:{},status:"idle"},resolveApplication:{output:{applicationId:null,universalIdentifier:null},status:"idle"},buildManifest:{output:{result:null},status:"idle"},uploadFiles:{output:{fileUploader:null,builtFileInfos:new Map,activeUploads:new Set},status:"idle"},generateApiClient:{output:{},status:"idle"},syncApplication:{output:{syncStatus:"idle",error:null},status:"idle"},startWatchers:{output:{watchersStarted:!1},status:"idle"}},this.pipeline={status:"idle",isSyncing:!1,error:null,appName:null},this.versionInfo=null,this.entities=new Map,this.events=[]}setVersionInfo(s){this.versionInfo=s,this.notify()}notify(){this.onChange?.()}updatePipeline(s){Object.assign(this.pipeline,s),this.notify()}applyStepEvents(s){const r=s.map(c=>(this.eventIdCounter+=1,{...c,id:this.eventIdCounter,timestamp:new Date,message:c.message.slice(0,5e3)}));this.events=[...this.events.slice(-(uv-r.length)),...r]}addEvent(s){this.applyStepEvents([s])}updateEntityStatus(s,r){const c=new Map(this.entities),d=c.get(s);d?.status&&!lv[d.status].includes(r)||(c.set(s,d?{...d,status:r}:{name:s,path:s,status:r}),this.entities=c)}removeEntity(s){const r=new Map(this.entities);r.delete(s),this.entities=r}updateAllEntitiesStatus(s){const r=new Map(this.entities);for(const[c,d]of r)r.set(c,{...d,status:s});this.entities=r}updateEntitiesFromManifest(s){const r=new Map;for(const[d,x]of Object.entries(s)){const E=ov[d];if(E)for(const C of x)r.set(C,E)}const c=new Map(this.entities);for(const[d,x]of c)c.set(d,{...x,type:r.get(d)});for(const[d,x]of r)c.has(d)||c.set(d,{name:d,path:d,type:x,status:"pending"});this.entities=c}hasObjectsOrFieldsChanged(s){const r=JSON.stringify({objects:s.objects,fields:s.fields}),c=r!==this.previousObjectsFieldsFingerprint;return this.previousObjectsFieldsFingerprint=r,c}}const Be={idle:{color:"gray",icon:"○"},in_progress:{color:"yellow",icon:"spinner"},uploading:{color:"cyan",icon:"upload"},done:{color:"green",icon:"✓"},error:{color:"red",icon:"✗"}},fv=l=>({idle:"idle",in_progress:"in_progress",done:"done",error:"error"})[l],dv=l=>({pending:"idle",building:"in_progress",uploading:"uploading",success:"done",error:"error"})[l],pv=l=>({idle:"idle",building:"in_progress",syncing:"in_progress",synced:"done",error:"error"})[l],hv={idle:"Idle",building:"Building...",syncing:"Syncing...",synced:"Synced",error:"Error"},ji=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],Yi=["↑","⇡","↟","⤒"],lu={[m.e.Object]:"Objects",[m.e.Field]:"Fields",[m.e.LogicFunction]:"Logic functions",[m.e.FrontComponent]:"Front components",[m.e.CommandMenuItem]:"Command menu items",[m.e.Role]:"Roles",[m.e.Skill]:"Skills",[m.e.View]:"Views",[m.e.NavigationMenuItem]:"Navigation menu items",[m.e.PageLayout]:"Page layouts",[m.e.PageLayoutTab]:"Page layout tabs",[m.e.Agent]:"Agents",[m.e.ConnectionProvider]:"Connection providers"},cu=Object.keys(lu),gv={info:"gray",success:"green",error:"red",warning:"yellow"},mv=l=>l.toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"}),vv=(l,s=40)=>{if(l.length<=s)return l;const r=l.split("/");return r.length<=2?l:`.../${r.slice(-2).join("/")}`},yv=l=>{const s=new Map;for(const r of cu)s.set(r,[]);for(const r of l.values()){if(!r.type)continue;const c=s.get(r.type)??[];c.push(r),s.set(r.type,c)}return s},_v=l=>!l.frontendUrl||!l.steps.resolveApplication.output.universalIdentifier?null:`${l.frontendUrl}/settings/applications`,wv=l=>l.some(s=>s==="error")?"error":l.some(s=>s==="in_progress")?"in_progress":l.every(s=>s==="done")?"done":"idle",Sv=l=>{const s=[...l.entities.values()],r=s.some(E=>E.status==="error"),c=s.some(E=>E.status==="building"),d=s.length>0&&s.every(E=>E.status==="uploading"||E.status==="success"),x=r?"error":c?"in_progress":d?"done":"idle";return[{label:"Application Initialization",status:wv([l.steps.checkServer.status,l.steps.ensureValidTokens.status,l.steps.resolveApplication.status])},{label:"Resources Build",status:x},{label:"Resources Upload",status:l.steps.uploadFiles.status},{label:"Manifest Build",status:l.steps.buildManifest.status},{label:"Application Synchronization",status:l.steps.syncApplication.status},{label:"Api Client Generation",status:l.steps.generateApiClient.status}]},fu=120,xv=Math.round(200/fu),Mo=()=>{const l=Math.floor(Date.now()/fu);return{spinnerFrame:ji[l%ji.length],uploadFrame:Yi[Math.floor(l/xv)%Yi.length]}},Zi=l=>{const s=Be[l];return s.icon==="spinner"?Mo().spinnerFrame:s.icon==="upload"?Mo().uploadFrame:s.icon},du=eu.default.createContext(null),Iv=du.Provider,nt=()=>{const l=eu.default.useContext(du);if(!l)throw new Error("useInk must be used within InkProvider");return l},Ev=({uiStatus:l})=>{const{Text:s}=nt(),r=Zi(l),c=Be[l];return T.jsxs(s,{color:c.color,children:[r," "]})},Av=({entity:l})=>{const{Box:s,Text:r}=nt();return T.jsxs(s,{children:[T.jsx(Ev,{uiStatus:dv(l.status)}),T.jsx(r,{children:l.name}),l.path!==l.name&&T.jsxs(r,{dimColor:!0,children:[" (",vv(l.path),")"]})]})},bv=({type:l,entities:s})=>{const{Box:r,Text:c}=nt();return s.length===0?null:T.jsxs(r,{flexDirection:"column",marginTop:1,children:[T.jsx(c,{bold:!0,dimColor:!0,children:lu[l]}),s.map(d=>T.jsx(Av,{entity:d},d.path))]})},Cv=()=>{const{Box:l,Text:s}=nt();return T.jsx(l,{marginTop:1,children:T.jsxs(s,{dimColor:!0,children:[T.jsx(s,{color:Be.idle.color,children:Be.idle.icon})," ","pending"," ",T.jsx(s,{color:Be.in_progress.color,children:ji[0]})," ","building"," ",T.jsx(s,{color:Be.uploading.color,children:Yi[0]})," ","uploading"," ",T.jsx(s,{color:Be.done.color,children:Be.done.icon})," ","success"," ",T.jsx(s,{color:Be.error.color,children:Be.error.icon})," ","error"]})})},Tv=7,Pv=({versionInfo:l})=>{const{Box:s,Text:r}=nt();if(l===null)return null;const{cliVersion:c,localServerVersion:d,latestServerVersion:x,isMinorOrMajorBehind:E,daysBehind:C}=l,R=E&&C!==null&&C>Tv,U=d!==null&&x!==null&&d===x;return T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Versions: "}),T.jsx(r,{dimColor:!0,children:"CLI "}),T.jsxs(r,{children:["v",c]}),d!==null&&T.jsxs(T.Fragment,{children:[T.jsx(r,{dimColor:!0,children:" • Server "}),T.jsxs(r,{color:R?"yellow":void 0,children:["v",d]}),U&&T.jsx(r,{color:"green",children:" ✓ latest"}),R&&x!==null&&T.jsxs(T.Fragment,{children:[T.jsxs(r,{color:"yellow",children:[" → v",x]}),C!==null&&T.jsxs(r,{dimColor:!0,children:[" (",C,"d behind)"]})]})]})]})},Rv=({state:l})=>{const{Text:s}=nt(),r=pv(l.pipeline.status),c=Zi(r),d=Be[r],x=hv[l.pipeline.status];return T.jsxs(s,{color:d.color,children:[c," ",x]})},Fv=({label:l,status:s})=>{const{Box:r,Text:c}=nt(),d=fv(s),x=Zi(d),E=Be[d];return T.jsxs(r,{children:[T.jsxs(c,{dimColor:!0,children:[l,": "]}),T.jsxs(c,{color:E.color,children:[x," ",s.replace("_"," ")]})]})},Lv=({state:l})=>{const{Box:s,Text:r}=nt(),c=yv(l.entities),d=_v(l);return T.jsxs(s,{flexDirection:"column",borderStyle:"classic",borderColor:"gray",paddingX:1,children:[T.jsx(r,{bold:!0,color:"cyan",children:"Application"}),T.jsxs(s,{marginLeft:2,flexDirection:"column",children:[T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Name: "}),T.jsx(r,{bold:!0,children:l.pipeline.appName??"Loading..."})]}),T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Overall Status: "}),T.jsx(Rv,{state:l})]}),d&&T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Open:"}),T.jsxs(r,{bold:!0,color:"cyan",children:[" ",d]})]}),T.jsx(Pv,{versionInfo:l.versionInfo})]}),T.jsx(s,{marginLeft:2,flexDirection:"column",marginTop:1,children:Sv(l).map(x=>T.jsx(Fv,{label:x.label,status:x.status},x.label))}),T.jsx(s,{marginLeft:2,flexDirection:"column",children:cu.map(x=>{const E=c.get(x)??[];return T.jsx(bv,{type:x,entities:E},x)})})]})},Nv=({event:l})=>{const{Box:s,Text:r}=nt(),c=gv[l.status],d=mv(l.timestamp);return T.jsxs(s,{children:[T.jsxs(r,{dimColor:!0,children:[d," "]}),T.jsx(r,{color:c,children:l.message})]})},Ov=new Set(["building","syncing"]),Wo=120,Uv=80,$v=({uiStateManager:l})=>{const{Box:s,Static:r}=nt(),[,c]=At.useReducer(R=>R+1,0),d=At.useRef(null),x=At.useRef(0),E=At.useCallback(()=>{d.current&&clearTimeout(d.current),d.current=setTimeout(()=>{d.current=null,x.current=Date.now(),c()},Uv)},[]);At.useEffect(()=>l.subscribe(()=>{E()}),[l,E]),At.useEffect(()=>{const R=setInterval(()=>{const U=l.getSnapshot();Ov.has(U.pipeline.status)&&(Date.now()-x.current<Wo||c())},Wo);return()=>clearInterval(R)},[l]),At.useEffect(()=>()=>{d.current&&clearTimeout(d.current)},[]);const C=l.getSnapshot();return T.jsxs(T.Fragment,{children:[T.jsx(r,{items:C.events,children:R=>T.jsx(Nv,{event:R},R.id)}),T.jsxs(s,{marginTop:1,flexDirection:"column",children:[T.jsx(Lv,{state:C}),T.jsx(Cv,{})]})]})},Dv=async l=>{const s=await import("ink"),{render:r,Box:c,Text:d,Static:x}=s,{unmount:E}=r(T.jsx(Iv,{value:{Box:c,Text:d,Static:x},children:T.jsx($v,{uiStateManager:l})}),{incrementalRendering:!0});return{unmount:E}};class Mv{constructor(s){this.listeners=new Set,this.pendingNotify=!1,this.orchestratorState=s}getSnapshot(){return this.orchestratorState}subscribe(s){return this.listeners.add(s),s(this.orchestratorState),()=>this.listeners.delete(s)}notify(){this.pendingNotify||(this.pendingNotify=!0,queueMicrotask(()=>{this.pendingNotify=!1;for(const s of this.listeners)s(this.orchestratorState)}))}}class Wv{constructor(){this.orchestrator=null,this.unmountUI=null}async close(){this.unmountUI?.(),await this.orchestrator?.close()}getOrchestrator(){return this.orchestrator}async execute(s){const r=s.appPath??Z;await tn(r),s.headless&&await m.checkServerVersionCompatibility();const c=await new m.ConfigService().getConfig(),d=new cv({appPath:r,frontendUrl:c.apiUrl});if(!s.headless){const x=new Mv(d);d.onChange=()=>x.notify();const{unmount:E}=await Dv(x);this.unmountUI=E,m.getVersionInfo().then(C=>d.setVersionInfo(C))}this.orchestrator=new av({state:d,verbose:s.verbose,debounceMs:s.debounceMs}),await this.orchestrator.start(),s.headless||this.setupGracefulShutdown()}setupGracefulShutdown(){const s=()=>{this.close().then(()=>process.exit(0))};process.on("SIGINT",s),process.on("SIGTERM",s)}}class Bv{async execute(s){const r=s.appPath??Z;await tn(r),console.log(y.default.blue("Syncing application...")),console.log(y.default.gray(`App path: ${r}
|
|
348
|
+
Waiting for server...`,status:"error"}]),this.state.updatePipeline({status:"error"})),!1)}if(!r.authValid)return s.output.errorLogged||(s.output={isReady:!1,errorLogged:!0},s.status="error",this.state.applyStepEvents([{message:"Authentication failed. Run `yarn twenty remote:add --local` to authenticate.",status:"error"}]),this.state.updatePipeline({status:"error"})),!1;const c=s.output.isReady;if(s.output={isReady:!0,errorLogged:!1},s.status="done",!m.o(this.state.frontendUrl)){const d=await this.apiService.getWorkspaceFrontendUrl();m.o(d)&&(this.state.frontendUrl=d)}return c||this.notify(),!0}}class Xm{constructor({clientService:s,configService:r,state:c,notify:d}){this.clientService=s,this.configService=r,this.state=c,this.notify=d}async execute(s){const r=this.state.steps.generateApiClient;r.status="in_progress",this.notify();try{const c=await m.ensureAppAccessTokenIsValidOrRefresh(this.configService,s.credentials);await this.clientService.generateCoreClient({appPath:s.appPath,appAccessToken:c}),r.status="done"}catch(c){this.state.applyStepEvents([{message:`Failed to generate API client: ${c instanceof Error?c.message:String(c)}`,status:"error"}]),r.status="error"}this.notify()}}class Qm{constructor({apiService:s,configService:r,state:c,notify:d}){this.apiService=s,this.configService=r,this.state=c,this.notify=d}async execute(s){try{const r=await m.ensureAppRegistration(this.apiService,this.configService,{name:s.manifest.application.displayName,universalIdentifier:s.manifest.application.universalIdentifier});this.registrationCredentials={clientId:r.clientId,clientSecret:r.clientSecret},this.state.applyStepEvents([{message:r.isNewRegistration?`App registration created: ${s.manifest.application.displayName}`:"Existing app registration found",status:r.isNewRegistration?"success":"info"},...r.isNewRegistration?[{message:"Credentials saved to config.",status:"info"}]:[]])}catch(r){throw this.state.applyStepEvents([{message:`Failed to register app: ${r instanceof Error?r.message:String(r)}`,status:"error"}]),r}this.notify()}}class Do{constructor(s){this.watcher=null,this.appPath=s.appPath,this.fileFolder=s.fileFolder,this.watchPaths=s.watchPaths,this.handleFileBuilt=s.handleFileBuilt}async start(){const s=(await Promise.all(this.watchPaths.map(async r=>{const c=q.join(this.appPath,r);return await m.pathExists(c)?c:null}))).filter(r=>r!==null);s.length!==0&&(this.watcher=Qo.default.watch(s,{awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}),this.watcher.on("all",(r,c)=>{r==="addDir"||r==="unlinkDir"||((r==="add"||r==="change")&&this.copyAndNotify(c),r==="unlink"&&this.handleUnlink(c))}))}async close(){await this.watcher?.close()}async copyAndNotify(s){const r=q.relative(this.appPath,s),c=q.join(m.u,r),d=q.join(this.appPath,c);await m.ensureDir(q.dirname(d)),await m.copy(s,d);const x=await tt.readFile(d),E=xm.default.createHash("md5").update(x).digest("hex");this.handleFileBuilt({fileFolder:this.fileFolder,builtPath:c,sourcePath:r,checksum:E})}async handleUnlink(s){const r=q.relative(this.appPath,s),c=q.join(m.u,r),d=q.join(this.appPath,c);await m.remove(d)}}class ev{constructor(s){this.process=null,this.pendingErrors=[],this.buffer="",this.hasErrors=!1,this.appPath=s.appPath,this.onErrors=s.onErrors}async start(){const s=Lo.default.join(this.appPath,"node_modules",".bin","tsc");if(!await m.pathExists(s))return;const r=Lo.default.join(this.appPath,"tsconfig.json");this.process=Tn.spawn(s,["--watch","--noEmit","--pretty","false","-p",r],{cwd:this.appPath,stdio:["ignore","pipe","pipe"]}),this.process.on("error",()=>{this.process=null}),this.process.stdout?.on("data",c=>{this.handleOutput(c.toString())}),this.process.stderr?.on("data",c=>{this.handleOutput(c.toString())})}close(){this.process?.kill(),this.process=null}handleOutput(s){this.buffer+=s;const r=this.buffer.split(`
|
|
349
|
+
`);this.buffer=r.pop()??"";for(const c of r)this.processLine(c)}processLine(s){if(s.includes("Starting compilation in watch mode...")||s.includes("Starting incremental compilation...")){this.pendingErrors=[];return}if(s.includes("Watching for file changes.")){const c=this.hasErrors;this.hasErrors=this.pendingErrors.length>0,(this.hasErrors||c)&&this.onErrors(this.pendingErrors),this.pendingErrors=[];return}const r=m.parseTscOutputLine(s);r&&this.pendingErrors.push(r)}}const tv=new Set(["node_modules","dist",".twenty"]);class nv{constructor(s){this.watcher=null,this.appPath=s.appPath,this.handleChangeDetected=s.handleChangeDetected,this.verbose=s.verbose??!1}async start(){const s=this.appPath;this.watcher=Qo.default.watch(this.appPath,{ignoreInitial:!this.verbose,ignored:r=>{const c=q.relative(s,r);if(c==="")return!1;const d=c.split(Ji.default.sep)[0];return tv.has(d)||d.startsWith(".")},awaitWriteFinish:{stabilityThreshold:100,pollInterval:50}}),this.watcher.on("all",(r,c)=>{if(r==="addDir"||r==="unlinkDir")return;const d=q.relative(this.appPath,c),x=d.startsWith(m._),E=["package.json","yarn.lock"].includes(d);!(d.endsWith(".ts")||d.endsWith(".tsx"))&&!x&&!E||this.handleChangeDetected(d,r)})}async close(){await this.watcher?.close()}}class rv{constructor(s){this.manifestWatcher=null,this.logicFunctionsWatcher=null,this.frontComponentsWatcher=null,this.assetWatcher=null,this.dependencyWatcher=null,this.tscWatcher=null,this.state=s.state,this.scheduleSync=s.scheduleSync,this.notify=s.notify,this.onFileBuilt=s.onFileBuilt,this.shouldSkipTypecheck=s.shouldSkipTypecheck,this.verbose=s.verbose??!1}async start(){this.state.steps.startWatchers.status="in_progress",this.notify(),this.manifestWatcher=new nv({appPath:this.state.appPath,handleChangeDetected:this.handleChangeDetected.bind(this),verbose:this.verbose}),await this.manifestWatcher.start()}async handleWatcherRestarts(s){const{logicFunctions:r,frontComponents:c}=s.filePaths;if(!this.state.steps.startWatchers.output.watchersStarted){this.state.steps.startWatchers.output.watchersStarted=!0,this.state.steps.startWatchers.status="done",await this.startFileWatchers(r,c);return}this.logicFunctionsWatcher?.shouldRestart(r)&&await this.logicFunctionsWatcher.restart(r),this.frontComponentsWatcher?.shouldRestart(c)&&await this.frontComponentsWatcher.restart(c)}async close(){this.tscWatcher?.close(),await Promise.all([this.manifestWatcher?.close(),this.logicFunctionsWatcher?.close(),this.frontComponentsWatcher?.close(),this.assetWatcher?.close(),this.dependencyWatcher?.close()])}handleChangeDetected(s,r){this.state.addEvent({message:`Change detected: ${s}`,status:"info"}),r==="unlink"?this.state.removeEntity(s):this.state.updateEntityStatus(s,"building"),this.notify(),this.scheduleSync()}handleFileBuildError(s){this.state.addEvent({message:"Build failed:",status:"error"});for(const r of s)this.state.addEvent({message:r.error,status:"error"});this.notify()}handleFileBuilt(s){this.verbose&&this.state.addEvent({message:`Successfully built ${s.builtPath}`,status:"success"}),this.state.steps.uploadFiles.output.builtFileInfos.set(s.builtPath,{checksum:s.checksum,builtPath:s.builtPath,sourcePath:s.sourcePath,fileFolder:s.fileFolder,usesSdkClient:s.usesSdkClient}),this.onFileBuilt(s),this.notify(),this.scheduleSync()}async startFileWatchers(s,r){await Promise.all([this.startTscWatcher(),this.startLogicFunctionsWatcher(s),this.startFrontComponentsWatcher(r),this.startAssetWatcher(),this.startDependencyWatcher()])}async startLogicFunctionsWatcher(s){this.logicFunctionsWatcher=m.createLogicFunctionsWatcher({appPath:this.state.appPath,sourcePaths:s,shouldSkipTypecheck:this.shouldSkipTypecheck,handleBuildError:this.handleFileBuildError.bind(this),handleFileBuilt:this.handleFileBuilt.bind(this)}),await this.logicFunctionsWatcher.start()}async startFrontComponentsWatcher(s){this.frontComponentsWatcher=m.createFrontComponentsWatcher({appPath:this.state.appPath,sourcePaths:s,shouldSkipTypecheck:this.shouldSkipTypecheck,handleBuildError:this.handleFileBuildError.bind(this),handleFileBuilt:this.handleFileBuilt.bind(this)}),await this.frontComponentsWatcher.start()}async startAssetWatcher(){this.assetWatcher=new Do({appPath:this.state.appPath,fileFolder:m.b.PublicAsset,watchPaths:[m._],handleFileBuilt:this.handleFileBuilt.bind(this)}),await this.assetWatcher.start()}async startDependencyWatcher(){this.dependencyWatcher=new Do({appPath:this.state.appPath,fileFolder:m.b.Dependencies,watchPaths:["package.json","yarn.lock"],handleFileBuilt:this.handleFileBuilt.bind(this)}),this.dependencyWatcher.start()}async startTscWatcher(){this.tscWatcher=new ev({appPath:this.state.appPath,onErrors:this.handleTypecheckErrors.bind(this)}),await this.tscWatcher.start()}handleTypecheckErrors(s){s.length===0?this.state.addEvent({message:"Typecheck passed",status:"success"}):this.state.applyStepEvents(s.map(r=>({message:`Type error in ${r.file}(${r.line},${r.column}): ${r.text}`,status:"error"}))),this.notify()}}class iv{constructor({apiService:s,state:r,notify:c,verbose:d}){this.apiService=s,this.state=r,this.notify=c,this.verbose=d??!1}async execute(s){const r=this.state.steps.syncApplication;r.status="in_progress",this.state.updatePipeline({status:"syncing"});const c=[],d=m.manifestUpdateChecksums({manifest:s.manifest,builtFileInfos:s.builtFileInfos});c.push({message:"Manifest checksums set",status:"info"}),await m.writeManifestToOutput(s.appPath,d),c.push({message:"Manifest saved to output directory",status:"info"}),c.push({message:"Syncing manifest",status:"info"});const x=await this.apiService.syncApplication(d);if(x.success){c.push({message:"✓ Synced",status:"success"}),r.output={syncStatus:"synced",error:null},r.status="done",this.state.updatePipeline({status:"synced",error:null}),this.state.updateAllEntitiesStatus("success"),this.state.applyStepEvents(c);return}const E=this.verbose?null:m.formatManifestValidationErrors(x.error);E?(c.push(...E),c.push({message:"Add --verbose to see full error log",status:"info"})):c.push({message:`Sync failed with error: ${m.serializeError(x.error)}`,status:"error"});const C=E?E[0].message:"Sync failed";r.output={syncStatus:"error",error:C},r.status="error",this.state.updatePipeline({status:"error",error:C}),this.state.updateAllEntitiesStatus("error"),this.state.applyStepEvents(c)}}class sv{constructor({state:s,notify:r,verbose:c}){this.uploadedCount=0,this.failedCount=0,this.totalQueued=0,this.state=s,this.notify=r,this.verbose=c??!1}get isInitialized(){return m.o(this.state.steps.uploadFiles.output.fileUploader)}initialize(s){const r=this.state.steps.uploadFiles;r.output={...r.output,fileUploader:new m.FileUploader({appPath:s.appPath,applicationUniversalIdentifier:s.universalIdentifier})},r.status="in_progress",this.notify(),this.uploadPendingFiles()}uploadFile(s,r,c){const d=this.state.steps.uploadFiles;if(!d.output.fileUploader)return;d.status="in_progress",this.totalQueued++,this.verbose&&this.state.addEvent({message:`Uploading ${s}`,status:"info"}),this.state.updateEntityStatus(r,"uploading"),this.notify();const x=d.output.fileUploader.uploadFile({builtPath:s,fileFolder:c}).then(E=>{E.success?(this.uploadedCount++,this.verbose&&this.state.addEvent({message:`Successfully uploaded ${s}`,status:"success"}),this.state.updateEntityStatus(r,"success")):(this.failedCount++,this.state.addEvent({message:`Failed to upload ${s}: ${E.error}`,status:"error"}))}).catch(E=>{this.failedCount++,this.state.addEvent({message:`Upload failed for ${s}: ${E}`,status:"error"})}).finally(()=>{d.output.activeUploads.delete(x),d.output.activeUploads.size===0&&(this.logUploadSummary(),d.status="done",this.notify())});d.output.activeUploads.add(x)}async waitForUploads(){const s=this.state.steps.uploadFiles;for(;s.output.activeUploads.size>0;)await Promise.all(s.output.activeUploads);s.status="done",this.notify()}logUploadSummary(){if(this.totalQueued===0){this.resetCounters();return}this.failedCount>0&&this.state.addEvent({message:`Uploaded ${this.uploadedCount}/${this.totalQueued} files (${this.failedCount} failed)`,status:"error"}),this.state.addEvent({message:`Successfully uploaded ${this.uploadedCount} file${this.uploadedCount!==1?"s":""}`,status:"success"}),this.resetCounters()}resetCounters(){this.uploadedCount=0,this.failedCount=0,this.totalQueued=0}uploadPendingFiles(){for(const[s,{fileFolder:r,sourcePath:c}]of this.state.steps.uploadFiles.output.builtFileInfos.entries())this.uploadFile(s,c,r)}}class av{constructor(s){this.syncTimer=null,this.serverCheckInterval=null,this.skipTypecheck=!0,this.debounceMs=s.debounceMs??2e3,this.state=s.state,this.verbose=s.verbose??!1,this.apiService=new m.ApiService({disableInterceptors:!0});const r=this.apiService,c=new m.ConfigService;this.clientService=new m.ClientService;const d={state:this.state,notify:()=>this.state.notify()};this.checkServerStep=new Zm({...d,apiService:r}),this.buildManifestStep=new Jm(d),this.registerAppStep=new Qm({...d,apiService:r,configService:c}),this.uploadFilesStep=new sv({...d,verbose:this.verbose}),this.generateApiClientStep=new Xm({...d,clientService:this.clientService,configService:c}),this.syncApplicationStep=new iv({...d,apiService:r,verbose:this.verbose}),this.startWatchersStep=new rv({...d,scheduleSync:this.scheduleSync.bind(this),onFileBuilt:this.handleFileBuilt.bind(this),shouldSkipTypecheck:()=>this.skipTypecheck,verbose:this.verbose})}async start(){const s=Ji.default.join(this.state.appPath,m.u);await m.ensureDir(s),await m.emptyDir(s),this.verbose||(this.state.addEvent({message:"Add --verbose to see fully detailed logs",status:"info"}),this.state.notify()),await this.startWatchersStep.start(),this.serverCheckInterval=setInterval(()=>{this.checkServerHealth()},2e3)}async close(){this.serverCheckInterval&&clearInterval(this.serverCheckInterval),await this.startWatchersStep.close()}getState(){return this.state}handleFileBuilt(s){this.state.steps.uploadFiles.output.fileUploader&&this.uploadFilesStep.uploadFile(s.builtPath,s.sourcePath,s.fileFolder)}async checkServerHealth(){const s=this.state.steps.checkServer.output.isReady;await this.checkServerStep.execute()&&!s&&this.scheduleSync()}scheduleSync(){this.syncTimer&&clearTimeout(this.syncTimer),this.syncTimer=setTimeout(()=>{this.syncTimer=null,this.performSync()},this.debounceMs)}async performSync(){if(!this.state.pipeline.isSyncing){this.state.updatePipeline({isSyncing:!0});try{await this.runSyncPipeline()}catch(s){this.state.addEvent({message:`Sync failed with error: ${m.serializeError(s)}`,status:"error"}),this.state.updatePipeline({status:"error"}),this.state.updateAllEntitiesStatus("error")}finally{this.state.updatePipeline({isSyncing:!1})}}}async runSyncPipeline(){if(!await this.checkServerStep.execute())return;this.state.steps.ensureValidTokens.status="done";const r=await this.buildManifestStep.execute({appPath:this.state.appPath});if(!r||(await this.startWatchersStep.handleWatcherRestarts(r),!this.uploadFilesStep.isInitialized&&!await this.initializePipeline(r.manifest)))return;const c=this.state.hasObjectsOrFieldsChanged(r.manifest);await this.uploadFilesStep.waitForUploads(),await this.syncApplicationStep.execute({manifest:r.manifest,builtFileInfos:this.state.steps.uploadFiles.output.builtFileInfos,appPath:this.state.appPath}),this.state.steps.syncApplication.status!=="error"&&c&&(await this.generateApiClientStep.execute({appPath:this.state.appPath,credentials:this.registerAppStep.registrationCredentials}),this.skipTypecheck=!1)}async initializePipeline(s){await this.registerAppStep.execute({manifest:s});const r=await this.apiService.createDevelopmentApplication({universalIdentifier:s.application.universalIdentifier,name:s.application.displayName});return!r.success||!r.data?(this.state.applyStepEvents([{message:"Failed to install development application",status:"error"},{message:JSON.stringify(r,null,2),status:"error"}]),this.state.updatePipeline({status:"error"}),!1):(this.state.steps.resolveApplication.output={applicationId:r.data.id,universalIdentifier:r.data.universalIdentifier},this.state.steps.resolveApplication.status="done",this.state.applyStepEvents([{message:"Application installed",status:"success"}]),this.uploadFilesStep.initialize({appPath:this.state.appPath,universalIdentifier:s.application.universalIdentifier}),!0)}}const ov={objects:m.e.Object,fields:m.e.Field,logicFunctions:m.e.LogicFunction,frontComponents:m.e.FrontComponent,roles:m.e.Role,skills:m.e.Skill,connectionProviders:m.e.ConnectionProvider,views:m.e.View,navigationMenuItems:m.e.NavigationMenuItem,pageLayouts:m.e.PageLayout,pageLayoutTabs:m.e.PageLayoutTab,commandMenuItems:m.e.CommandMenuItem},uv=200,lv={pending:["building","uploading","success","error"],building:["pending","uploading","success","error"],uploading:["pending","success","error"],success:["pending","building","uploading","error"],error:["pending","building","uploading","success"]};class cv{constructor(s){this.eventIdCounter=0,this.appPath=s.appPath,this.previousObjectsFieldsFingerprint=null,this.steps={checkServer:{output:{isReady:!1,errorLogged:!1},status:"idle"},ensureValidTokens:{output:{},status:"idle"},resolveApplication:{output:{applicationId:null,universalIdentifier:null},status:"idle"},buildManifest:{output:{result:null},status:"idle"},uploadFiles:{output:{fileUploader:null,builtFileInfos:new Map,activeUploads:new Set},status:"idle"},generateApiClient:{output:{},status:"idle"},syncApplication:{output:{syncStatus:"idle",error:null},status:"idle"},startWatchers:{output:{watchersStarted:!1},status:"idle"}},this.pipeline={status:"idle",isSyncing:!1,error:null,appName:null},this.versionInfo=null,this.entities=new Map,this.events=[]}setVersionInfo(s){this.versionInfo=s,this.notify()}notify(){this.onChange?.()}updatePipeline(s){Object.assign(this.pipeline,s),this.notify()}applyStepEvents(s){const r=s.map(c=>(this.eventIdCounter+=1,{...c,id:this.eventIdCounter,timestamp:new Date,message:c.message.slice(0,5e3)}));this.events=[...this.events.slice(-(uv-r.length)),...r]}addEvent(s){this.applyStepEvents([s])}updateEntityStatus(s,r){const c=new Map(this.entities),d=c.get(s);d?.status&&!lv[d.status].includes(r)||(c.set(s,d?{...d,status:r}:{name:s,path:s,status:r}),this.entities=c)}removeEntity(s){const r=new Map(this.entities);r.delete(s),this.entities=r}updateAllEntitiesStatus(s){const r=new Map(this.entities);for(const[c,d]of r)r.set(c,{...d,status:s});this.entities=r}updateEntitiesFromManifest(s){const r=new Map;for(const[d,x]of Object.entries(s)){const E=ov[d];if(E)for(const C of x)r.set(C,E)}const c=new Map(this.entities);for(const[d,x]of c)c.set(d,{...x,type:r.get(d)});for(const[d,x]of r)c.has(d)||c.set(d,{name:d,path:d,type:x,status:"pending"});this.entities=c}hasObjectsOrFieldsChanged(s){const r=JSON.stringify({objects:s.objects,fields:s.fields}),c=r!==this.previousObjectsFieldsFingerprint;return this.previousObjectsFieldsFingerprint=r,c}}const Be={idle:{color:"gray",icon:"○"},in_progress:{color:"yellow",icon:"spinner"},uploading:{color:"cyan",icon:"upload"},done:{color:"green",icon:"✓"},error:{color:"red",icon:"✗"}},fv=l=>({idle:"idle",in_progress:"in_progress",done:"done",error:"error"})[l],dv=l=>({pending:"idle",building:"in_progress",uploading:"uploading",success:"done",error:"error"})[l],pv=l=>({idle:"idle",building:"in_progress",syncing:"in_progress",synced:"done",error:"error"})[l],hv={idle:"Idle",building:"Building...",syncing:"Syncing...",synced:"Synced",error:"Error"},ji=["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"],Yi=["↑","⇡","↟","⤒"],lu={[m.e.Object]:"Objects",[m.e.Field]:"Fields",[m.e.LogicFunction]:"Logic functions",[m.e.FrontComponent]:"Front components",[m.e.CommandMenuItem]:"Command menu items",[m.e.Role]:"Roles",[m.e.Skill]:"Skills",[m.e.View]:"Views",[m.e.NavigationMenuItem]:"Navigation menu items",[m.e.PageLayout]:"Page layouts",[m.e.PageLayoutTab]:"Page layout tabs",[m.e.Agent]:"Agents",[m.e.ConnectionProvider]:"Connection providers"},cu=Object.keys(lu),gv={info:"gray",success:"green",error:"red",warning:"yellow"},mv=l=>l.toLocaleTimeString("en-US",{hour12:!1,hour:"2-digit",minute:"2-digit",second:"2-digit"}),vv=(l,s=40)=>{if(l.length<=s)return l;const r=l.split("/");return r.length<=2?l:`.../${r.slice(-2).join("/")}`},yv=l=>{const s=new Map;for(const r of cu)s.set(r,[]);for(const r of l.values()){if(!r.type)continue;const c=s.get(r.type)??[];c.push(r),s.set(r.type,c)}return s},_v=l=>{const s=l.steps.resolveApplication.output.applicationId;return!m.o(l.frontendUrl)||!m.o(s)?null:`${l.frontendUrl}/settings/applications/${s}`},wv=l=>l.some(s=>s==="error")?"error":l.some(s=>s==="in_progress")?"in_progress":l.every(s=>s==="done")?"done":"idle",Sv=l=>{const s=[...l.entities.values()],r=s.some(E=>E.status==="error"),c=s.some(E=>E.status==="building"),d=s.length>0&&s.every(E=>E.status==="uploading"||E.status==="success"),x=r?"error":c?"in_progress":d?"done":"idle";return[{label:"Application Initialization",status:wv([l.steps.checkServer.status,l.steps.ensureValidTokens.status,l.steps.resolveApplication.status])},{label:"Resources Build",status:x},{label:"Resources Upload",status:l.steps.uploadFiles.status},{label:"Manifest Build",status:l.steps.buildManifest.status},{label:"Application Synchronization",status:l.steps.syncApplication.status},{label:"Api Client Generation",status:l.steps.generateApiClient.status}]},fu=120,xv=Math.round(200/fu),Mo=()=>{const l=Math.floor(Date.now()/fu);return{spinnerFrame:ji[l%ji.length],uploadFrame:Yi[Math.floor(l/xv)%Yi.length]}},Zi=l=>{const s=Be[l];return s.icon==="spinner"?Mo().spinnerFrame:s.icon==="upload"?Mo().uploadFrame:s.icon},du=eu.default.createContext(null),Iv=du.Provider,nt=()=>{const l=eu.default.useContext(du);if(!l)throw new Error("useInk must be used within InkProvider");return l},Ev=({uiStatus:l})=>{const{Text:s}=nt(),r=Zi(l),c=Be[l];return T.jsxs(s,{color:c.color,children:[r," "]})},Av=({entity:l})=>{const{Box:s,Text:r}=nt();return T.jsxs(s,{children:[T.jsx(Ev,{uiStatus:dv(l.status)}),T.jsx(r,{children:l.name}),l.path!==l.name&&T.jsxs(r,{dimColor:!0,children:[" (",vv(l.path),")"]})]})},bv=({type:l,entities:s})=>{const{Box:r,Text:c}=nt();return s.length===0?null:T.jsxs(r,{flexDirection:"column",marginTop:1,children:[T.jsx(c,{bold:!0,dimColor:!0,children:lu[l]}),s.map(d=>T.jsx(Av,{entity:d},d.path))]})},Cv=()=>{const{Box:l,Text:s}=nt();return T.jsx(l,{marginTop:1,children:T.jsxs(s,{dimColor:!0,children:[T.jsx(s,{color:Be.idle.color,children:Be.idle.icon})," ","pending"," ",T.jsx(s,{color:Be.in_progress.color,children:ji[0]})," ","building"," ",T.jsx(s,{color:Be.uploading.color,children:Yi[0]})," ","uploading"," ",T.jsx(s,{color:Be.done.color,children:Be.done.icon})," ","success"," ",T.jsx(s,{color:Be.error.color,children:Be.error.icon})," ","error"]})})},Tv=7,Pv=({versionInfo:l})=>{const{Box:s,Text:r}=nt();if(l===null)return null;const{cliVersion:c,localServerVersion:d,latestServerVersion:x,isMinorOrMajorBehind:E,daysBehind:C}=l,R=E&&C!==null&&C>Tv,U=d!==null&&x!==null&&d===x;return T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Versions: "}),T.jsx(r,{dimColor:!0,children:"CLI "}),T.jsxs(r,{children:["v",c]}),d!==null&&T.jsxs(T.Fragment,{children:[T.jsx(r,{dimColor:!0,children:" • Server "}),T.jsxs(r,{color:R?"yellow":void 0,children:["v",d]}),U&&T.jsx(r,{color:"green",children:" ✓ latest"}),R&&x!==null&&T.jsxs(T.Fragment,{children:[T.jsxs(r,{color:"yellow",children:[" → v",x]}),C!==null&&T.jsxs(r,{dimColor:!0,children:[" (",C,"d behind)"]})]})]})]})},Rv=({state:l})=>{const{Text:s}=nt(),r=pv(l.pipeline.status),c=Zi(r),d=Be[r],x=hv[l.pipeline.status];return T.jsxs(s,{color:d.color,children:[c," ",x]})},Fv=({label:l,status:s})=>{const{Box:r,Text:c}=nt(),d=fv(s),x=Zi(d),E=Be[d];return T.jsxs(r,{children:[T.jsxs(c,{dimColor:!0,children:[l,": "]}),T.jsxs(c,{color:E.color,children:[x," ",s.replace("_"," ")]})]})},Lv=({state:l})=>{const{Box:s,Text:r}=nt(),c=yv(l.entities),d=_v(l);return T.jsxs(s,{flexDirection:"column",borderStyle:"classic",borderColor:"gray",paddingX:1,children:[T.jsx(r,{bold:!0,color:"cyan",children:"Application"}),T.jsxs(s,{marginLeft:2,flexDirection:"column",children:[T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Name: "}),T.jsx(r,{bold:!0,children:l.pipeline.appName??"Loading..."})]}),T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Overall Status: "}),T.jsx(Rv,{state:l})]}),d&&T.jsxs(s,{children:[T.jsx(r,{dimColor:!0,children:"Open:"}),T.jsxs(r,{bold:!0,color:"cyan",children:[" ",d]})]}),T.jsx(Pv,{versionInfo:l.versionInfo})]}),T.jsx(s,{marginLeft:2,flexDirection:"column",marginTop:1,children:Sv(l).map(x=>T.jsx(Fv,{label:x.label,status:x.status},x.label))}),T.jsx(s,{marginLeft:2,flexDirection:"column",children:cu.map(x=>{const E=c.get(x)??[];return T.jsx(bv,{type:x,entities:E},x)})})]})},Nv=({event:l})=>{const{Box:s,Text:r}=nt(),c=gv[l.status],d=mv(l.timestamp);return T.jsxs(s,{children:[T.jsxs(r,{dimColor:!0,children:[d," "]}),T.jsx(r,{color:c,children:l.message})]})},Ov=new Set(["building","syncing"]),Wo=120,Uv=80,$v=({uiStateManager:l})=>{const{Box:s,Static:r}=nt(),[,c]=At.useReducer(R=>R+1,0),d=At.useRef(null),x=At.useRef(0),E=At.useCallback(()=>{d.current&&clearTimeout(d.current),d.current=setTimeout(()=>{d.current=null,x.current=Date.now(),c()},Uv)},[]);At.useEffect(()=>l.subscribe(()=>{E()}),[l,E]),At.useEffect(()=>{const R=setInterval(()=>{const U=l.getSnapshot();Ov.has(U.pipeline.status)&&(Date.now()-x.current<Wo||c())},Wo);return()=>clearInterval(R)},[l]),At.useEffect(()=>()=>{d.current&&clearTimeout(d.current)},[]);const C=l.getSnapshot();return T.jsxs(T.Fragment,{children:[T.jsx(r,{items:C.events,children:R=>T.jsx(Nv,{event:R},R.id)}),T.jsxs(s,{marginTop:1,flexDirection:"column",children:[T.jsx(Lv,{state:C}),T.jsx(Cv,{})]})]})},Dv=async l=>{const s=await import("ink"),{render:r,Box:c,Text:d,Static:x}=s,{unmount:E}=r(T.jsx(Iv,{value:{Box:c,Text:d,Static:x},children:T.jsx($v,{uiStateManager:l})}),{incrementalRendering:!0});return{unmount:E}};class Mv{constructor(s){this.listeners=new Set,this.pendingNotify=!1,this.orchestratorState=s}getSnapshot(){return this.orchestratorState}subscribe(s){return this.listeners.add(s),s(this.orchestratorState),()=>this.listeners.delete(s)}notify(){this.pendingNotify||(this.pendingNotify=!0,queueMicrotask(()=>{this.pendingNotify=!1;for(const s of this.listeners)s(this.orchestratorState)}))}}class Wv{constructor(){this.orchestrator=null,this.unmountUI=null}async close(){this.unmountUI?.(),await this.orchestrator?.close()}getOrchestrator(){return this.orchestrator}async execute(s){const r=s.appPath??Z;await tn(r),s.headless&&await m.checkServerVersionCompatibility();const c=new cv({appPath:r});if(!s.headless){const d=new Mv(c);c.onChange=()=>d.notify();const{unmount:x}=await Dv(d);this.unmountUI=x,m.getVersionInfo().then(E=>c.setVersionInfo(E))}this.orchestrator=new av({state:c,verbose:s.verbose,debounceMs:s.debounceMs}),await this.orchestrator.start(),s.headless||this.setupGracefulShutdown()}setupGracefulShutdown(){const s=()=>{this.close().then(()=>process.exit(0))};process.on("SIGINT",s),process.on("SIGTERM",s)}}class Bv{async execute(s){const r=s.appPath??Z;await tn(r),console.log(y.default.blue("Syncing application...")),console.log(y.default.gray(`App path: ${r}
|
|
350
350
|
`));const c=await m.appDevOnce({appPath:r,verbose:s.verbose,onProgress:d=>console.log(y.default.gray(d))});c.success||(console.error(y.default.red(c.error.message)),process.exit(1)),console.log(y.default.green(`
|
|
351
|
-
✓ Synced ${c.data.applicationDisplayName} (${c.data.fileCount} file${c.data.fileCount===1?"":"s"})`)),console.log(y.default.gray(`Output: ${c.data.outputDir}`))}}const kv=l=>{const s=new ou,r=new au;l.command("dev:function:logs [appPath]").description("Stream logic function logs").option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Only show logs for the function with this universal ID").option("-n, --functionName <functionName>","Only show logs for the function with this name").action(async(c,d)=>{await s.execute({...d,appPath:pe(c)})}),l.command("dev:function:exec [appPath]").description("Execute a logic function").option("--postInstall","Execute post-install logic function if defined").option("--preInstall","Execute pre-install logic function if defined").option("-p, --payload <payload>","JSON payload to send to the function","{}").option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Universal ID of the function to execute").option("-n, --functionName <functionName>","Name of the function to execute").action(async(c,d)=>{!d?.postInstall&&!d?.preInstall&&!d?.functionUniversalIdentifier&&!d?.functionName&&(console.error(y.default.red("Error: Either --postInstall, --preInstall, --functionName (-n), or --functionUniversalIdentifier (-u) is required.")),process.exit(1)),await r.execute({...d,payload:d?.payload??"{}",appPath:pe(c)})})},Vv=l=>{const s=new su,r=new Wv,c=new Bv,d=new uu,x=new iu,E=async(C,R)=>{const U={appPath:pe(C),verbose:R.verbose||R.debug,debounceMs:R.debounceMs?parseInt(R.debounceMs,10):void 0};if(R.once){await c.execute(U);return}await r.execute(U)};l.command("dev [appPath]",{isDefault:!0}).description("Build and sync local changes (default command)").option("-o, --once","Build and sync once, then exit (useful for CI, scripts, and pre-commit hooks)").option("--debounceMs <ms>","Debounce in ms (default: 2 000)").option("-v, --verbose","Show detailed logs").option("-d, --debug","Show detailed logs (alias for --verbose)").action(E),l.command("dev:build [appPath]").description("Build and generate API client").option("--tarball","Also pack into a .tgz tarball").action(async(C,R)=>{await s.execute({appPath:pe(C),tarball:R.tarball})}),l.command("dev:typecheck [appPath]").description("Run TypeScript type checking").action(async C=>{await d.execute({appPath:pe(C)})}),l.command("dev:add [entityType]").description(`Scaffold a new entity (${Object.values(m.e).join("|")})`).option("--path <path>","Path in which the entity should be created.").action(async(C,R)=>{await x.execute(C,R?.path)}),l.command("dev:catalog-sync").description("Trigger marketplace catalog sync").option("-r, --remote <name>","Sync on a specific remote").action(async C=>{const{CatalogSyncCommand:R}=await Promise.resolve().then(()=>require("./catalog-sync-
|
|
352
|
-
Start one with: yarn twenty docker:start`)),process.exit(1)),console.log(y.default.gray(`Found local server at ${C}`)),d=C):d=m.
|
|
351
|
+
✓ Synced ${c.data.applicationDisplayName} (${c.data.fileCount} file${c.data.fileCount===1?"":"s"})`)),console.log(y.default.gray(`Output: ${c.data.outputDir}`))}}const kv=l=>{const s=new ou,r=new au;l.command("dev:function:logs [appPath]").description("Stream logic function logs").option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Only show logs for the function with this universal ID").option("-n, --functionName <functionName>","Only show logs for the function with this name").action(async(c,d)=>{await s.execute({...d,appPath:pe(c)})}),l.command("dev:function:exec [appPath]").description("Execute a logic function").option("--postInstall","Execute post-install logic function if defined").option("--preInstall","Execute pre-install logic function if defined").option("-p, --payload <payload>","JSON payload to send to the function","{}").option("-u, --functionUniversalIdentifier <functionUniversalIdentifier>","Universal ID of the function to execute").option("-n, --functionName <functionName>","Name of the function to execute").action(async(c,d)=>{!d?.postInstall&&!d?.preInstall&&!d?.functionUniversalIdentifier&&!d?.functionName&&(console.error(y.default.red("Error: Either --postInstall, --preInstall, --functionName (-n), or --functionUniversalIdentifier (-u) is required.")),process.exit(1)),await r.execute({...d,payload:d?.payload??"{}",appPath:pe(c)})})},Vv=l=>{const s=new su,r=new Wv,c=new Bv,d=new uu,x=new iu,E=async(C,R)=>{const U={appPath:pe(C),verbose:R.verbose||R.debug,debounceMs:R.debounceMs?parseInt(R.debounceMs,10):void 0};if(R.once){await c.execute(U);return}await r.execute(U)};l.command("dev [appPath]",{isDefault:!0}).description("Build and sync local changes (default command)").option("-o, --once","Build and sync once, then exit (useful for CI, scripts, and pre-commit hooks)").option("--debounceMs <ms>","Debounce in ms (default: 2 000)").option("-v, --verbose","Show detailed logs").option("-d, --debug","Show detailed logs (alias for --verbose)").action(E),l.command("dev:build [appPath]").description("Build and generate API client").option("--tarball","Also pack into a .tgz tarball").action(async(C,R)=>{await s.execute({appPath:pe(C),tarball:R.tarball})}),l.command("dev:typecheck [appPath]").description("Run TypeScript type checking").action(async C=>{await d.execute({appPath:pe(C)})}),l.command("dev:add [entityType]").description(`Scaffold a new entity (${Object.values(m.e).join("|")})`).option("--path <path>","Path in which the entity should be created.").action(async(C,R)=>{await x.execute(C,R?.path)}),l.command("dev:catalog-sync").description("Trigger marketplace catalog sync").option("-r, --remote <name>","Sync on a specific remote").action(async C=>{const{CatalogSyncCommand:R}=await Promise.resolve().then(()=>require("./catalog-sync-CPVoL1dR.js"));await new R().execute({remote:C.remote})}),kv(l)},Bo=async l=>{const s=l.test?m.DEFAULT_TEST_PORT:m.DEFAULT_PORT,r=l.port?parseInt(l.port,10):s;(isNaN(r)||r<1||r>65535)&&(console.error(y.default.red("Invalid port number.")),process.exit(1));const c=await m.serverStart({port:r,test:l.test,onProgress:d=>console.log(y.default.gray(d))});c.success||(console.error(y.default.red(c.error.message)),process.exit(1))},ko=l=>{const s=l.test?m.TEST_CONTAINER_NAME:m.CONTAINER_NAME;if(!m.containerExists(s)){console.log(y.default.yellow("No Twenty server container found."));return}Tn.execSync(`docker stop ${s}`,{stdio:"ignore"}),console.log(y.default.green("Twenty server stopped."))},Vo=l=>{const s=l.test?m.TEST_CONTAINER_NAME:m.CONTAINER_NAME;if(!m.containerExists(s)){console.log(y.default.yellow("No Twenty server container found."));return}try{Tn.spawnSync("docker",["logs","-f","--tail",l.lines,s],{stdio:"inherit"})}catch{}},Go=async l=>{const s=l.test?m.TEST_CONTAINER_NAME:m.CONTAINER_NAME,r=l.test?m.DEFAULT_TEST_PORT:m.DEFAULT_PORT;if(!m.containerExists(s)){console.log(` Status: ${y.default.gray("not created")}`),console.log(y.default.gray(` Run 'yarn twenty docker:start${l.test?" --test":""}' to create one.`));return}const c=m.isContainerRunning(s),d=c?m.getContainerPort(s):r,x=c?await m.checkServerHealth(d):!1,E=x?y.default.green("running (healthy)"):c?y.default.yellow("running (starting...)"):y.default.gray("stopped"),C=m.getContainerEnvVar("APP_VERSION",s);console.log(` Status: ${E}`),console.log(` URL: http://localhost:${d}`),C&&console.log(` Version: ${y.default.gray(C)}`),x&&console.log(y.default.gray(" Login: tim@apple.dev / tim@apple.dev"))},qo=l=>{const s=l.test?m.TEST_CONTAINER_NAME:m.CONTAINER_NAME,r=l.test?"twenty-app-dev-test-data":"twenty-app-dev-data",c=l.test?"twenty-app-dev-test-storage":"twenty-app-dev-storage";m.containerExists(s)&&Tn.execSync(`docker rm -f ${s}`,{stdio:"ignore"});try{Tn.execSync(`docker volume rm ${r} ${c}`,{stdio:"ignore"})}catch{}console.log(y.default.green("Twenty server data reset.")),console.log(y.default.gray(`Run 'yarn twenty docker:start${l.test?" --test":""}' to start a fresh instance.`))},Ho=async(l,s)=>{const r=await m.serverUpgrade({version:l??"latest",test:s.test,onProgress:d=>console.log(y.default.gray(d))});r.success||(console.error(y.default.red(r.error.message)),process.exit(1));const{data:c}=r;if(!c.imageUpdated){console.log(y.default.green(` Already up to date (${c.image}).`));return}console.log(y.default.green(` Upgraded to: ${c.image}`)),c.containerRecreated&&console.log(y.default.gray(` Run 'yarn twenty docker:start${s.test?" --test":""}' to wait for the server to be ready.`))},Gv=l=>{l.command("docker:start").description("Start the local Twenty container").option("-p, --port <port>","HTTP port").option("--test","Start a separate test instance (port 2021)").action(Bo),l.command("docker:stop").description("Stop the local Twenty container").option("--test","Stop the test instance").action(ko),l.command("docker:logs").description("Stream container logs").option("-n, --lines <lines>","Number of lines to show","50").option("--test","Show logs for the test instance").action(Vo),l.command("docker:status").description("Show container status").option("--test","Show status of the test instance").action(Go),l.command("docker:reset").description("Delete all data and start fresh").option("--test","Reset the test instance").action(qo),l.command("docker:upgrade [version]").description("Upgrade the Docker image").option("--test","Upgrade the test instance").action(Ho);const s=l.command("server",{hidden:!0}).description("Manage a Twenty server (local instance and server-side actions)"),r=(c,d)=>console.warn(y.default.yellow(`⚠ \`twenty server ${c}\` is deprecated. Use \`twenty ${d}\` instead.`));s.command("start").option("-p, --port <port>","HTTP port").option("--test","Start a separate test instance (port 2021)").action(async c=>{r("start","docker:start"),await Bo(c)}),s.command("stop").option("--test","Stop the test instance").action(c=>{r("stop","docker:stop"),ko(c)}),s.command("logs").option("-n, --lines <lines>","Number of lines to show","50").option("--test","Show logs for the test instance").action(c=>{r("logs","docker:logs"),Vo(c)}),s.command("status").option("--test","Show status of the test instance").action(async c=>{r("status","docker:status"),await Go(c)}),s.command("reset").option("--test","Reset the test instance").action(c=>{r("reset","docker:reset"),qo(c)}),s.command("upgrade [version]").option("--test","Upgrade the test instance").action(async(c,d)=>{r("upgrade","docker:upgrade"),await Ho(c,d)}),s.command("catalog-sync").option("-r, --remote <name>","Sync on a specific remote").action(async c=>{r("catalog-sync","dev:catalog-sync");const{CatalogSyncCommand:d}=await Promise.resolve().then(()=>require("./catalog-sync-CPVoL1dR.js"));await new d().execute({remote:c.remote})})},qv=l=>{try{return new URL(l).hostname.replace(/\./g,"-")}catch{return"remote"}},zo=async(l,s)=>s?((await m.authLogin({apiKey:s,apiUrl:l})).success||(console.error(y.default.red("✗ Authentication failed.")),process.exit(1)),"API key"):Hv(l),Hv=async l=>{console.log(y.default.gray("Opening browser for authentication..."));const s=await m.authLoginOAuth({apiUrl:l});if(s.success)return"OAuth";console.log(y.default.yellow(s.error.message));const r=await je.default.prompt([{type:"password",name:"apiKey",message:"API Key:",mask:"*",validate:d=>d.length>0||"API key is required"}]);return(await m.authLogin({apiKey:r.apiKey,apiUrl:l})).success||(console.error(y.default.red("✗ Authentication failed.")),process.exit(1)),"API key"},Ko=async l=>{l.apiUrl&&console.warn(y.default.yellow("⚠ --api-url is deprecated. Use --url instead."));const s=l.test?m.getConfigPath(!0):void 0,r=new m.ConfigService(s?{configPath:s}:void 0),c=await r.getRemotes();if(l.as!==void 0&&c.includes(l.as)){const C=await r.getConfigForRemote(l.as);m.ConfigService.setActiveRemote(l.as);const R=await zo(C.apiUrl,l.apiKey);console.log(y.default.green(`✓ Re-authenticated "${l.as}" via ${R}.`)),await r.setDefaultRemote(l.as),console.log(y.default.green(`✓ Default remote set to "${l.as}".`));return}let d=l.url??l.apiUrl;if(d)d=m.Ra(d);else{const C=await m.detectLocalServer();l.local?(C||(console.error(y.default.red(`No local Twenty server found.
|
|
352
|
+
Start one with: yarn twenty docker:start`)),process.exit(1)),console.log(y.default.gray(`Found local server at ${C}`)),d=C):d=m.Ra((await je.default.prompt([{type:"input",name:"serverUrl",message:"Twenty server URL:",validate:R=>{try{return new URL(R),!0}catch{return"Please enter a valid URL"}}}])).serverUrl)}const x=l.as??qv(d);m.ConfigService.setActiveRemote(x);const E=await zo(d,l.apiKey);console.log(y.default.green(`✓ Remote "${x}" added (${d}) via ${E}.`)),await r.setDefaultRemote(x),console.log(y.default.green(`✓ Default remote set to "${x}".`))},jo=async()=>{const l=new m.ConfigService,s=await l.getRemotes(),r=await l.getDefaultRemote();if(s.length===0){console.log("No remotes configured."),console.log("Use 'twenty remote:add' to add one.");return}console.log("");for(const c of s){const d=await l.getConfigForRemote(c),x=d.twentyCLIAccessToken?"oauth":d.apiKey?"api-key":"none",E=c===r,C=E?"* ":" ",R=E?y.default.bold(c):c;console.log(`${C}${R} ${y.default.gray(d.apiUrl)} [${x}]`)}console.log(`
|
|
353
353
|
`,y.default.gray("Use 'twenty remote:use <name>' to change default"))},Yo=async l=>{const s=new m.ConfigService,r=l??(await je.default.prompt([{type:"list",name:"remote",message:"Select default remote:",choices:await s.getRemotes()}])).remote;(await s.getRemotes()).includes(r)||(console.error(y.default.red(`Remote "${r}" not found.`)),process.exit(1)),await s.setDefaultRemote(r),console.log(y.default.green(`✓ Default remote set to "${r}".`))},Jo=async()=>{const l=new m.ConfigService,s=new m.ApiService,r=m.ConfigService.getActiveRemote(),c=await l.getConfig(),d=c.twentyCLIAccessToken?"oauth":c.apiKey?"api-key":"none";if(console.log(` Remote: ${y.default.bold(r)}`),console.log(` Server: ${c.apiUrl}`),d==="none"){console.log(` Auth: ${y.default.yellow("not configured")}`);return}const{authValid:x}=await s.validateAuth(),E=x?y.default.green(`${d} (valid)`):y.default.red(`${d} (invalid)`);console.log(` Auth: ${E}`)},Zo=async l=>{const s=new m.ConfigService;(await s.getRemotes()).includes(l)||(console.error(y.default.red(`Remote "${l}" not found.`)),process.exit(1)),m.ConfigService.setActiveRemote(l),await s.clearConfig(),console.log(y.default.green(`✓ Remote "${l}" removed.`))},zv=l=>{l.command("remote:add").description("Add or re-authenticate a remote").option("--as <name>","Name for this remote").option("--api-key <apiKey>","API key for non-interactive auth").option("--url <url>","Server URL").option("--api-url <apiUrl>","[deprecated: use --url]").option("--local","Connect to a local Twenty server (auto-detect)").option("--test","Write to config.test.json (for integration tests)").action(Ko),l.command("remote:list").description("List all configured remotes").action(jo),l.command("remote:use [name]").description("Set the default remote").action(Yo),l.command("remote:status").description("Show active remote auth status").action(Jo),l.command("remote:remove <name>").description("Remove a remote").action(Zo);const s=l.command("remote",{hidden:!0}).description("Manage remote Twenty servers"),r=(c,d)=>console.warn(y.default.yellow(`⚠ \`twenty remote ${c}\` is deprecated. Use \`twenty ${d}\` instead.`));s.command("add").option("--as <name>","Name for this remote").option("--api-key <apiKey>","API key for non-interactive auth").option("--url <url>","Server URL").option("--api-url <apiUrl>","[deprecated: use --url]").option("--local","Connect to a local Twenty server (auto-detect)").option("--test","Write to config.test.json (for integration tests)").action(async c=>{r("add","remote:add"),await Ko(c)}),s.command("list").action(async()=>{r("list","remote:list"),await jo()}),s.command("switch [name]").action(async c=>{r("switch","remote:use"),await Yo(c)}),s.command("status").action(async()=>{r("status","remote:status"),await Jo()}),s.command("remove <name>").action(async c=>{r("remove","remote:remove"),await Zo(c)})},Kv=l=>{Vv(l),Em(l),Gv(l),zv(l),Ym(l)};Sm.inspect.defaultOptions.depth=10;const nn=new Xo.Command;nn.name("twenty").description("CLI for Twenty application development").version(m.packageJson.version);nn.option("-r, --remote <name>","Use a specific remote (overrides the default set by remote:use)");nn.hook("preAction",async l=>{let r=(l.optsWithGlobals?l.optsWithGlobals():l.opts()).remote;r?console.log(y.default.gray(`Using remote: ${r}`)):r=await new m.ConfigService().getDefaultRemote(),m.ConfigService.setActiveRemote(r)});Kv(nn);nn.exitOverride();try{nn.parse()}catch(l){l instanceof Xo.CommanderError&&process.exit(l.exitCode),l instanceof Error&&(console.error(y.default.red("Error:"),l.message),process.exit(1))}
|