@mks2508/coolify-mks-cli-mcp 0.6.3 → 0.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/cli/coolify-state.d.ts +92 -4
- package/dist/cli/coolify-state.d.ts.map +1 -1
- package/dist/cli/index.js +22149 -11456
- package/dist/cli/ui/highlighter.d.ts +28 -0
- package/dist/cli/ui/highlighter.d.ts.map +1 -0
- package/dist/cli/ui/index.d.ts +9 -0
- package/dist/cli/ui/index.d.ts.map +1 -0
- package/dist/cli/ui/spinners.d.ts +100 -0
- package/dist/cli/ui/spinners.d.ts.map +1 -0
- package/dist/cli/ui/tables.d.ts +103 -0
- package/dist/cli/ui/tables.d.ts.map +1 -0
- package/dist/coolify/index.d.ts +22 -3
- package/dist/coolify/index.d.ts.map +1 -1
- package/dist/coolify/types.d.ts +99 -1
- package/dist/coolify/types.d.ts.map +1 -1
- package/dist/examples/demo-ui.d.ts +8 -0
- package/dist/examples/demo-ui.d.ts.map +1 -0
- package/dist/index.cjs +322 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +322 -12
- package/dist/index.js.map +1 -1
- package/dist/sdk.d.ts +41 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/server/stdio.js +258 -9
- package/package.json +16 -4
- package/src/cli/actions.ts +9 -2
- package/src/cli/commands/create.ts +71 -5
- package/src/cli/commands/db.ts +37 -0
- package/src/cli/commands/delete.ts +6 -2
- package/src/cli/commands/deploy.ts +347 -49
- package/src/cli/commands/deployments.ts +6 -2
- package/src/cli/commands/diagnose.ts +3 -3
- package/src/cli/commands/env.ts +121 -22
- package/src/cli/commands/exec.ts +6 -2
- package/src/cli/commands/init.ts +937 -0
- package/src/cli/commands/logs.ts +224 -24
- package/src/cli/commands/main-menu.ts +21 -0
- package/src/cli/commands/projects.ts +312 -29
- package/src/cli/commands/restart.ts +6 -2
- package/src/cli/commands/service-logs.ts +14 -0
- package/src/cli/commands/show.ts +6 -2
- package/src/cli/commands/start.ts +6 -2
- package/src/cli/commands/status.ts +538 -0
- package/src/cli/commands/stop.ts +6 -2
- package/src/cli/commands/update.ts +27 -2
- package/src/cli/coolify-state.ts +164 -11
- package/src/cli/index.ts +91 -10
- package/src/cli/name-resolver.ts +228 -0
- package/src/cli/ui/banner.ts +276 -0
- package/src/cli/ui/highlighter.ts +176 -0
- package/src/cli/ui/index.ts +9 -0
- package/src/cli/ui/prompts.ts +155 -0
- package/src/cli/ui/screen.ts +606 -0
- package/src/cli/ui/select.ts +280 -0
- package/src/cli/ui/spinners.ts +256 -0
- package/src/cli/ui/tables.ts +407 -0
- package/src/coolify/index.ts +257 -12
- package/src/coolify/types.ts +103 -1
- package/src/examples/demo-ui.ts +78 -0
- package/src/sdk.ts +162 -0
package/dist/index.js
CHANGED
|
@@ -5766,8 +5766,8 @@ var CoolifyService = class {
|
|
|
5766
5766
|
environment_uuid: options.environmentUuid,
|
|
5767
5767
|
server_uuid: options.serverUuid
|
|
5768
5768
|
};
|
|
5769
|
+
if (options.githubRepoUrl) body.git_repository = options.githubRepoUrl.replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
5769
5770
|
if (appType === "public" || appType === "private-github-app" || appType === "private-deploy-key") {
|
|
5770
|
-
if (options.githubRepoUrl) body.git_repository = options.githubRepoUrl.replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
5771
5771
|
if (options.githubAppUuid) body.github_app_uuid = options.githubAppUuid;
|
|
5772
5772
|
body.git_branch = options.branch || "main";
|
|
5773
5773
|
body.build_pack = options.buildPack || "dockerfile";
|
|
@@ -5809,7 +5809,8 @@ var CoolifyService = class {
|
|
|
5809
5809
|
body: JSON.stringify({
|
|
5810
5810
|
key,
|
|
5811
5811
|
value,
|
|
5812
|
-
is_preview: false
|
|
5812
|
+
is_preview: false,
|
|
5813
|
+
is_buildtime: false
|
|
5813
5814
|
})
|
|
5814
5815
|
});
|
|
5815
5816
|
if (result.error && result.error.includes("already exists")) {
|
|
@@ -5822,7 +5823,8 @@ var CoolifyService = class {
|
|
|
5822
5823
|
body: JSON.stringify({
|
|
5823
5824
|
key,
|
|
5824
5825
|
value,
|
|
5825
|
-
is_preview: false
|
|
5826
|
+
is_preview: false,
|
|
5827
|
+
is_buildtime: false
|
|
5826
5828
|
})
|
|
5827
5829
|
});
|
|
5828
5830
|
if (repost.error) {
|
|
@@ -5868,8 +5870,8 @@ var CoolifyService = class {
|
|
|
5868
5870
|
* @param isBuildTime - Whether the variable is available at build time (only for new vars)
|
|
5869
5871
|
* @returns Result indicating success or error
|
|
5870
5872
|
*/
|
|
5871
|
-
async setEnvironmentVariable(appUuid, key, value,
|
|
5872
|
-
log.info(`Setting environment variable ${key} for ${appUuid}`);
|
|
5873
|
+
async setEnvironmentVariable(appUuid, key, value, isBuildTime = false) {
|
|
5874
|
+
log.info(`Setting environment variable ${key} for ${appUuid} (buildtime: ${isBuildTime})`);
|
|
5873
5875
|
const existingVars = await this.getEnvironmentVariables(appUuid);
|
|
5874
5876
|
if (isErr(existingVars)) return err(existingVars.error);
|
|
5875
5877
|
const exists = existingVars.value.some((ev) => ev.key === key);
|
|
@@ -5879,7 +5881,8 @@ var CoolifyService = class {
|
|
|
5879
5881
|
method: "PATCH",
|
|
5880
5882
|
body: JSON.stringify({
|
|
5881
5883
|
key,
|
|
5882
|
-
value
|
|
5884
|
+
value,
|
|
5885
|
+
is_buildtime: isBuildTime
|
|
5883
5886
|
})
|
|
5884
5887
|
});
|
|
5885
5888
|
if (result.error) {
|
|
@@ -5892,7 +5895,8 @@ var CoolifyService = class {
|
|
|
5892
5895
|
method: "POST",
|
|
5893
5896
|
body: JSON.stringify({
|
|
5894
5897
|
key,
|
|
5895
|
-
value
|
|
5898
|
+
value,
|
|
5899
|
+
is_buildtime: isBuildTime
|
|
5896
5900
|
})
|
|
5897
5901
|
});
|
|
5898
5902
|
if (result.error) {
|
|
@@ -6149,6 +6153,15 @@ var CoolifyService = class {
|
|
|
6149
6153
|
if (options.dockerComposeDomains) body.docker_compose_domains = options.dockerComposeDomains;
|
|
6150
6154
|
if (options.isForceHttpsEnabled !== void 0) body.is_force_https_enabled = options.isForceHttpsEnabled;
|
|
6151
6155
|
if (options.isAutoDeployEnabled !== void 0) body.is_auto_deploy_enabled = options.isAutoDeployEnabled;
|
|
6156
|
+
if (options.healthCheckEnabled !== void 0) body.health_check_enabled = options.healthCheckEnabled;
|
|
6157
|
+
if (options.healthCheckPath) body.health_check_path = options.healthCheckPath;
|
|
6158
|
+
if (options.healthCheckPort) body.health_check_port = String(options.healthCheckPort);
|
|
6159
|
+
if (options.healthCheckMethod) body.health_check_method = options.healthCheckMethod;
|
|
6160
|
+
if (options.healthCheckInterval) body.health_check_interval = options.healthCheckInterval;
|
|
6161
|
+
if (options.healthCheckTimeout) body.health_check_timeout = options.healthCheckTimeout;
|
|
6162
|
+
if (options.healthCheckRetries) body.health_check_retries = options.healthCheckRetries;
|
|
6163
|
+
if (options.healthCheckStartPeriod) body.health_check_start_period = options.healthCheckStartPeriod;
|
|
6164
|
+
if (options.healthCheckReturnCode) body.health_check_return_code = options.healthCheckReturnCode;
|
|
6152
6165
|
const result = await this.request(`/applications/${appUuid}`, {
|
|
6153
6166
|
method: "PATCH",
|
|
6154
6167
|
body: JSON.stringify(body)
|
|
@@ -6607,6 +6620,125 @@ var CoolifyService = class {
|
|
|
6607
6620
|
});
|
|
6608
6621
|
}
|
|
6609
6622
|
/**
|
|
6623
|
+
* Gets the full infrastructure tree: Projects → Environments → Resources.
|
|
6624
|
+
*
|
|
6625
|
+
* Fetches all projects, apps, databases, and services in parallel,
|
|
6626
|
+
* then groups them by environment_id into a hierarchical tree.
|
|
6627
|
+
*
|
|
6628
|
+
* @returns Result with the full infrastructure tree or error
|
|
6629
|
+
*/
|
|
6630
|
+
async getInfrastructureTree() {
|
|
6631
|
+
log.info("Building infrastructure tree");
|
|
6632
|
+
const [projectsResult, appsResult, dbsResult, svcsResult, serversResult] = await Promise.all([
|
|
6633
|
+
this.listProjects(),
|
|
6634
|
+
this.listApplications(),
|
|
6635
|
+
this.listDatabases(),
|
|
6636
|
+
this.listServices(),
|
|
6637
|
+
this.listServers()
|
|
6638
|
+
]);
|
|
6639
|
+
if (isErr(projectsResult)) return err(projectsResult.error);
|
|
6640
|
+
if (isErr(appsResult)) return err(appsResult.error);
|
|
6641
|
+
if (isErr(serversResult)) return err(serversResult.error);
|
|
6642
|
+
const projects = projectsResult.value;
|
|
6643
|
+
const apps = appsResult.value;
|
|
6644
|
+
const dbs = isErr(dbsResult) ? [] : dbsResult.value;
|
|
6645
|
+
const svcs = isErr(svcsResult) ? [] : svcsResult.value;
|
|
6646
|
+
const servers = serversResult.value;
|
|
6647
|
+
const envResults = await Promise.allSettled(projects.map((p) => this.getProjectEnvironments(p.uuid)));
|
|
6648
|
+
const envIdMap = new Map();
|
|
6649
|
+
for (let i = 0; i < projects.length; i++) {
|
|
6650
|
+
const envResult = envResults[i];
|
|
6651
|
+
if (envResult.status === "fulfilled" && !isErr(envResult.value)) for (const env of envResult.value.value) envIdMap.set(env.id, {
|
|
6652
|
+
projectUuid: projects[i].uuid,
|
|
6653
|
+
envName: env.name,
|
|
6654
|
+
envUuid: env.uuid
|
|
6655
|
+
});
|
|
6656
|
+
}
|
|
6657
|
+
const projectNodes = projects.map((p) => ({
|
|
6658
|
+
uuid: p.uuid,
|
|
6659
|
+
name: p.name,
|
|
6660
|
+
description: p.description,
|
|
6661
|
+
environments: []
|
|
6662
|
+
}));
|
|
6663
|
+
const projectMap = new Map();
|
|
6664
|
+
for (const node of projectNodes) projectMap.set(node.uuid, node);
|
|
6665
|
+
const envNodeMap = new Map();
|
|
6666
|
+
for (const [envId, info] of envIdMap) {
|
|
6667
|
+
const project = projectMap.get(info.projectUuid);
|
|
6668
|
+
if (!project) continue;
|
|
6669
|
+
let envNode = project.environments.find((e) => e.id === envId);
|
|
6670
|
+
if (!envNode) {
|
|
6671
|
+
envNode = {
|
|
6672
|
+
id: envId,
|
|
6673
|
+
uuid: info.envUuid,
|
|
6674
|
+
name: info.envName,
|
|
6675
|
+
resources: []
|
|
6676
|
+
};
|
|
6677
|
+
project.environments.push(envNode);
|
|
6678
|
+
}
|
|
6679
|
+
envNodeMap.set(envId, envNode);
|
|
6680
|
+
}
|
|
6681
|
+
for (const app of apps) {
|
|
6682
|
+
const envNode = app.environment_id ? envNodeMap.get(app.environment_id) : void 0;
|
|
6683
|
+
const resource = {
|
|
6684
|
+
uuid: app.uuid,
|
|
6685
|
+
name: app.name,
|
|
6686
|
+
kind: "app",
|
|
6687
|
+
status: app.status,
|
|
6688
|
+
fqdn: app.fqdn
|
|
6689
|
+
};
|
|
6690
|
+
if (envNode) envNode.resources.push(resource);
|
|
6691
|
+
}
|
|
6692
|
+
for (const db of dbs) {
|
|
6693
|
+
const envNode = db.environment_id ? envNodeMap.get(db.environment_id) : void 0;
|
|
6694
|
+
const resource = {
|
|
6695
|
+
uuid: db.uuid,
|
|
6696
|
+
name: db.name,
|
|
6697
|
+
kind: "database",
|
|
6698
|
+
status: db.status,
|
|
6699
|
+
dbType: db.type
|
|
6700
|
+
};
|
|
6701
|
+
if (envNode) envNode.resources.push(resource);
|
|
6702
|
+
}
|
|
6703
|
+
for (const svc of svcs) {
|
|
6704
|
+
const envNode = svc.environment_id ? envNodeMap.get(svc.environment_id) : void 0;
|
|
6705
|
+
const resource = {
|
|
6706
|
+
uuid: svc.uuid,
|
|
6707
|
+
name: svc.name,
|
|
6708
|
+
kind: "service",
|
|
6709
|
+
status: svc.status
|
|
6710
|
+
};
|
|
6711
|
+
if (envNode) envNode.resources.push(resource);
|
|
6712
|
+
}
|
|
6713
|
+
const populatedProjects = projectNodes.filter((p) => p.environments.some((e) => e.resources.length > 0));
|
|
6714
|
+
const allStatuses = [
|
|
6715
|
+
...apps.map((a) => a.status),
|
|
6716
|
+
...dbs.map((d) => d.status),
|
|
6717
|
+
...svcs.map((s) => s.status)
|
|
6718
|
+
];
|
|
6719
|
+
const counts = {
|
|
6720
|
+
projects: populatedProjects.length,
|
|
6721
|
+
apps: apps.length,
|
|
6722
|
+
databases: dbs.length,
|
|
6723
|
+
services: svcs.length,
|
|
6724
|
+
healthy: allStatuses.filter((s) => s.includes("healthy")).length,
|
|
6725
|
+
running: allStatuses.filter((s) => s.startsWith("running") && !s.includes("healthy")).length,
|
|
6726
|
+
stopped: allStatuses.filter((s) => s.includes("exited")).length,
|
|
6727
|
+
unhealthy: allStatuses.filter((s) => s.includes("unhealthy")).length
|
|
6728
|
+
};
|
|
6729
|
+
const server = servers[0] || { name: "Unknown" };
|
|
6730
|
+
log.success(`Infrastructure tree built: ${counts.projects} projects, ${counts.apps} apps, ${counts.databases} dbs, ${counts.services} svcs`);
|
|
6731
|
+
return ok({
|
|
6732
|
+
server: {
|
|
6733
|
+
name: server.name,
|
|
6734
|
+
ip: server.ip,
|
|
6735
|
+
uuid: server.uuid
|
|
6736
|
+
},
|
|
6737
|
+
projects: populatedProjects,
|
|
6738
|
+
counts
|
|
6739
|
+
});
|
|
6740
|
+
}
|
|
6741
|
+
/**
|
|
6610
6742
|
* Starts a service.
|
|
6611
6743
|
* Note: Coolify API uses GET for service start/stop/restart.
|
|
6612
6744
|
*
|
|
@@ -7045,6 +7177,22 @@ var CoolifyService = class {
|
|
|
7045
7177
|
return ok(deployments);
|
|
7046
7178
|
}
|
|
7047
7179
|
/**
|
|
7180
|
+
* Gets full application details by UUID.
|
|
7181
|
+
*
|
|
7182
|
+
* Makes a direct GET request to /api/v1/applications/{uuid} which returns
|
|
7183
|
+
* complete application data including project_uuid and environment_uuid.
|
|
7184
|
+
*
|
|
7185
|
+
* @param appUuid - Application UUID
|
|
7186
|
+
* @returns Result with full application details or error
|
|
7187
|
+
*/
|
|
7188
|
+
async getApplication(appUuid) {
|
|
7189
|
+
log.info(`Getting application details: ${appUuid}`);
|
|
7190
|
+
const result = await this.request(`/applications/${appUuid}`);
|
|
7191
|
+
if (result.error) return err(new Error(result.error));
|
|
7192
|
+
if (!result.data) return err(new Error("Application not found"));
|
|
7193
|
+
return ok(result.data);
|
|
7194
|
+
}
|
|
7195
|
+
/**
|
|
7048
7196
|
* Resolves an application by UUID, name, or domain (FQDN).
|
|
7049
7197
|
*
|
|
7050
7198
|
* @param query - UUID, name, or domain to search for
|
|
@@ -7437,6 +7585,117 @@ var ApplicationsResource = class {
|
|
|
7437
7585
|
async deleteEnv(uuid, key) {
|
|
7438
7586
|
return unwrap(await this.svc.deleteEnvironmentVariable(uuid, key));
|
|
7439
7587
|
}
|
|
7588
|
+
/**
|
|
7589
|
+
* Sync environment variables from a local .env file to Coolify.
|
|
7590
|
+
*
|
|
7591
|
+
* @param uuid - Application UUID
|
|
7592
|
+
* @param options - Sync options
|
|
7593
|
+
* @returns Sync result with changes applied
|
|
7594
|
+
*/
|
|
7595
|
+
async syncEnv(uuid, options = {}) {
|
|
7596
|
+
const { filePath, dryRun = false, prune = false, onProgress } = options;
|
|
7597
|
+
let envContent;
|
|
7598
|
+
try {
|
|
7599
|
+
envContent = await Bun.file(filePath || ".env").text();
|
|
7600
|
+
} catch {
|
|
7601
|
+
throw new Error(filePath ? `Cannot read file: ${filePath}` : "No .env file found in current directory");
|
|
7602
|
+
}
|
|
7603
|
+
const localVars = this.parseEnvContent(envContent);
|
|
7604
|
+
if (localVars.size === 0) return {
|
|
7605
|
+
added: [],
|
|
7606
|
+
updated: [],
|
|
7607
|
+
removed: [],
|
|
7608
|
+
skipped: 0
|
|
7609
|
+
};
|
|
7610
|
+
const currentVarsList = await this.envVars(uuid);
|
|
7611
|
+
const currentVars = new Map(currentVarsList.map((v) => [v.key, v.value]));
|
|
7612
|
+
const toAdd = [];
|
|
7613
|
+
const toUpdate = [];
|
|
7614
|
+
const toRemove = [];
|
|
7615
|
+
for (const [key, value] of localVars.entries()) {
|
|
7616
|
+
const currentValue = currentVars.get(key);
|
|
7617
|
+
if (!currentValue) toAdd.push({
|
|
7618
|
+
key,
|
|
7619
|
+
value
|
|
7620
|
+
});
|
|
7621
|
+
else if (currentValue !== value) toUpdate.push({
|
|
7622
|
+
key,
|
|
7623
|
+
value,
|
|
7624
|
+
oldValue: currentValue
|
|
7625
|
+
});
|
|
7626
|
+
}
|
|
7627
|
+
if (prune) {
|
|
7628
|
+
for (const key of currentVars.keys()) if (!localVars.has(key)) toRemove.push(key);
|
|
7629
|
+
}
|
|
7630
|
+
if (!dryRun) {
|
|
7631
|
+
for (const { key, value } of toAdd) {
|
|
7632
|
+
await this.setEnv(uuid, key, value, false);
|
|
7633
|
+
onProgress?.({
|
|
7634
|
+
type: "add",
|
|
7635
|
+
key,
|
|
7636
|
+
value
|
|
7637
|
+
});
|
|
7638
|
+
}
|
|
7639
|
+
for (const { key, value } of toUpdate) {
|
|
7640
|
+
await this.setEnv(uuid, key, value, false);
|
|
7641
|
+
onProgress?.({
|
|
7642
|
+
type: "update",
|
|
7643
|
+
key,
|
|
7644
|
+
value
|
|
7645
|
+
});
|
|
7646
|
+
}
|
|
7647
|
+
for (const key of toRemove) {
|
|
7648
|
+
await this.deleteEnv(uuid, key);
|
|
7649
|
+
onProgress?.({
|
|
7650
|
+
type: "remove",
|
|
7651
|
+
key
|
|
7652
|
+
});
|
|
7653
|
+
}
|
|
7654
|
+
} else {
|
|
7655
|
+
for (const { key, value } of toAdd) onProgress?.({
|
|
7656
|
+
type: "add",
|
|
7657
|
+
key,
|
|
7658
|
+
value
|
|
7659
|
+
});
|
|
7660
|
+
for (const { key, value } of toUpdate) onProgress?.({
|
|
7661
|
+
type: "update",
|
|
7662
|
+
key,
|
|
7663
|
+
value
|
|
7664
|
+
});
|
|
7665
|
+
for (const key of toRemove) onProgress?.({
|
|
7666
|
+
type: "remove",
|
|
7667
|
+
key
|
|
7668
|
+
});
|
|
7669
|
+
}
|
|
7670
|
+
return {
|
|
7671
|
+
added: toAdd,
|
|
7672
|
+
updated: toUpdate,
|
|
7673
|
+
removed: toRemove,
|
|
7674
|
+
skipped: currentVars.size - toUpdate.length - toRemove.length
|
|
7675
|
+
};
|
|
7676
|
+
}
|
|
7677
|
+
/**
|
|
7678
|
+
* Parse .env file content into a Map.
|
|
7679
|
+
* Handles comments, empty lines, and quoted values.
|
|
7680
|
+
*
|
|
7681
|
+
* @param content - The .env file content
|
|
7682
|
+
* @returns Map of environment variables
|
|
7683
|
+
*/
|
|
7684
|
+
parseEnvContent(content) {
|
|
7685
|
+
const envVars = new Map();
|
|
7686
|
+
const lines = content.split("\n");
|
|
7687
|
+
for (const line of lines) {
|
|
7688
|
+
const trimmedLine = line.trim();
|
|
7689
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) continue;
|
|
7690
|
+
const eqIndex = trimmedLine.indexOf("=");
|
|
7691
|
+
if (eqIndex === -1) continue;
|
|
7692
|
+
const key = trimmedLine.slice(0, eqIndex).trim();
|
|
7693
|
+
let value = trimmedLine.slice(eqIndex + 1).trim();
|
|
7694
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
7695
|
+
envVars.set(key, value);
|
|
7696
|
+
}
|
|
7697
|
+
return envVars;
|
|
7698
|
+
}
|
|
7440
7699
|
};
|
|
7441
7700
|
var DatabasesResource = class {
|
|
7442
7701
|
constructor(svc) {
|
|
@@ -9308,7 +9567,10 @@ var init_network = __esm({ "src/network.ts"() {
|
|
|
9308
9567
|
//#region src/cli/coolify-state.ts
|
|
9309
9568
|
const STATE_FILE = ".coolify.json";
|
|
9310
9569
|
/**
|
|
9311
|
-
* Loads .coolify.json from the current working directory.
|
|
9570
|
+
* Loads .coolify.json from the current working directory (single-app format).
|
|
9571
|
+
*
|
|
9572
|
+
* Handles both single-app and multi-app formats. For multi-app state with
|
|
9573
|
+
* exactly one app, returns a synthesized single-app state for backward compat.
|
|
9312
9574
|
*
|
|
9313
9575
|
* @returns The deploy state if found, null otherwise
|
|
9314
9576
|
*/
|
|
@@ -9318,6 +9580,27 @@ function loadCoolifyState() {
|
|
|
9318
9580
|
try {
|
|
9319
9581
|
const content = readFileSync(statePath, "utf-8");
|
|
9320
9582
|
const state = JSON.parse(content);
|
|
9583
|
+
if (Array.isArray(state.apps)) {
|
|
9584
|
+
if (state.apps.length === 1) {
|
|
9585
|
+
const app = state.apps[0];
|
|
9586
|
+
return {
|
|
9587
|
+
appUuid: app.uuid,
|
|
9588
|
+
appName: app.name,
|
|
9589
|
+
serverUuid: state.serverUuid,
|
|
9590
|
+
serverName: state.serverName,
|
|
9591
|
+
projectUuid: state.projectUuid,
|
|
9592
|
+
projectName: state.projectName,
|
|
9593
|
+
environmentUuid: state.environmentUuid,
|
|
9594
|
+
environmentName: state.environmentName,
|
|
9595
|
+
domain: app.domain,
|
|
9596
|
+
branch: state.branch,
|
|
9597
|
+
gitRepository: state.gitRepository,
|
|
9598
|
+
coolifyUrl: state.coolifyUrl,
|
|
9599
|
+
updatedAt: state.updatedAt
|
|
9600
|
+
};
|
|
9601
|
+
}
|
|
9602
|
+
return null;
|
|
9603
|
+
}
|
|
9321
9604
|
if (!state.appUuid) return null;
|
|
9322
9605
|
return state;
|
|
9323
9606
|
} catch {
|
|
@@ -9325,18 +9608,45 @@ function loadCoolifyState() {
|
|
|
9325
9608
|
}
|
|
9326
9609
|
}
|
|
9327
9610
|
/**
|
|
9611
|
+
* Loads .coolify.json in multi-app format.
|
|
9612
|
+
*
|
|
9613
|
+
* If the file is in single-app format, returns null (use loadCoolifyState instead).
|
|
9614
|
+
*
|
|
9615
|
+
* @returns The multi-app state if found, null otherwise
|
|
9616
|
+
*/
|
|
9617
|
+
function loadMultiAppState() {
|
|
9618
|
+
const statePath = join(process.cwd(), STATE_FILE);
|
|
9619
|
+
if (!existsSync(statePath)) return null;
|
|
9620
|
+
try {
|
|
9621
|
+
const content = readFileSync(statePath, "utf-8");
|
|
9622
|
+
const state = JSON.parse(content);
|
|
9623
|
+
if (!Array.isArray(state.apps)) return null;
|
|
9624
|
+
return state;
|
|
9625
|
+
} catch {
|
|
9626
|
+
return null;
|
|
9627
|
+
}
|
|
9628
|
+
}
|
|
9629
|
+
/**
|
|
9328
9630
|
* Resolves a UUID argument — if not provided, tries to read from .coolify.json.
|
|
9329
9631
|
*
|
|
9632
|
+
* Supports both single-app and multi-app state formats. For multi-app state
|
|
9633
|
+
* with exactly one app, auto-selects that app. For multiple apps, returns null
|
|
9634
|
+
* (user must specify explicitly).
|
|
9635
|
+
*
|
|
9330
9636
|
* @param uuid - UUID from CLI argument (may be undefined)
|
|
9331
9637
|
* @param field - Which field to read from .coolify.json (default: appUuid)
|
|
9332
9638
|
* @returns The resolved UUID or null if not found
|
|
9333
9639
|
*/
|
|
9334
9640
|
function resolveUuid(uuid, field = "appUuid") {
|
|
9335
|
-
if (uuid) return uuid;
|
|
9641
|
+
if (uuid && /^[a-z0-9]{16,40}$/.test(uuid)) return uuid;
|
|
9336
9642
|
const state = loadCoolifyState();
|
|
9337
|
-
if (
|
|
9338
|
-
|
|
9339
|
-
|
|
9643
|
+
if (state) {
|
|
9644
|
+
const value = state[field];
|
|
9645
|
+
return typeof value === "string" ? value : null;
|
|
9646
|
+
}
|
|
9647
|
+
const multiState = loadMultiAppState();
|
|
9648
|
+
if (multiState && multiState.apps.length === 1 && field === "appUuid") return multiState.apps[0].uuid;
|
|
9649
|
+
return null;
|
|
9340
9650
|
}
|
|
9341
9651
|
|
|
9342
9652
|
//#endregion
|