@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.cjs
CHANGED
|
@@ -5784,8 +5784,8 @@ var CoolifyService = class {
|
|
|
5784
5784
|
environment_uuid: options.environmentUuid,
|
|
5785
5785
|
server_uuid: options.serverUuid
|
|
5786
5786
|
};
|
|
5787
|
+
if (options.githubRepoUrl) body.git_repository = options.githubRepoUrl.replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
5787
5788
|
if (appType === "public" || appType === "private-github-app" || appType === "private-deploy-key") {
|
|
5788
|
-
if (options.githubRepoUrl) body.git_repository = options.githubRepoUrl.replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
|
|
5789
5789
|
if (options.githubAppUuid) body.github_app_uuid = options.githubAppUuid;
|
|
5790
5790
|
body.git_branch = options.branch || "main";
|
|
5791
5791
|
body.build_pack = options.buildPack || "dockerfile";
|
|
@@ -5827,7 +5827,8 @@ var CoolifyService = class {
|
|
|
5827
5827
|
body: JSON.stringify({
|
|
5828
5828
|
key,
|
|
5829
5829
|
value,
|
|
5830
|
-
is_preview: false
|
|
5830
|
+
is_preview: false,
|
|
5831
|
+
is_buildtime: false
|
|
5831
5832
|
})
|
|
5832
5833
|
});
|
|
5833
5834
|
if (result.error && result.error.includes("already exists")) {
|
|
@@ -5840,7 +5841,8 @@ var CoolifyService = class {
|
|
|
5840
5841
|
body: JSON.stringify({
|
|
5841
5842
|
key,
|
|
5842
5843
|
value,
|
|
5843
|
-
is_preview: false
|
|
5844
|
+
is_preview: false,
|
|
5845
|
+
is_buildtime: false
|
|
5844
5846
|
})
|
|
5845
5847
|
});
|
|
5846
5848
|
if (repost.error) {
|
|
@@ -5886,8 +5888,8 @@ var CoolifyService = class {
|
|
|
5886
5888
|
* @param isBuildTime - Whether the variable is available at build time (only for new vars)
|
|
5887
5889
|
* @returns Result indicating success or error
|
|
5888
5890
|
*/
|
|
5889
|
-
async setEnvironmentVariable(appUuid, key, value,
|
|
5890
|
-
log.info(`Setting environment variable ${key} for ${appUuid}`);
|
|
5891
|
+
async setEnvironmentVariable(appUuid, key, value, isBuildTime = false) {
|
|
5892
|
+
log.info(`Setting environment variable ${key} for ${appUuid} (buildtime: ${isBuildTime})`);
|
|
5891
5893
|
const existingVars = await this.getEnvironmentVariables(appUuid);
|
|
5892
5894
|
if (isErr(existingVars)) return err(existingVars.error);
|
|
5893
5895
|
const exists = existingVars.value.some((ev) => ev.key === key);
|
|
@@ -5897,7 +5899,8 @@ var CoolifyService = class {
|
|
|
5897
5899
|
method: "PATCH",
|
|
5898
5900
|
body: JSON.stringify({
|
|
5899
5901
|
key,
|
|
5900
|
-
value
|
|
5902
|
+
value,
|
|
5903
|
+
is_buildtime: isBuildTime
|
|
5901
5904
|
})
|
|
5902
5905
|
});
|
|
5903
5906
|
if (result.error) {
|
|
@@ -5910,7 +5913,8 @@ var CoolifyService = class {
|
|
|
5910
5913
|
method: "POST",
|
|
5911
5914
|
body: JSON.stringify({
|
|
5912
5915
|
key,
|
|
5913
|
-
value
|
|
5916
|
+
value,
|
|
5917
|
+
is_buildtime: isBuildTime
|
|
5914
5918
|
})
|
|
5915
5919
|
});
|
|
5916
5920
|
if (result.error) {
|
|
@@ -6167,6 +6171,15 @@ var CoolifyService = class {
|
|
|
6167
6171
|
if (options.dockerComposeDomains) body.docker_compose_domains = options.dockerComposeDomains;
|
|
6168
6172
|
if (options.isForceHttpsEnabled !== void 0) body.is_force_https_enabled = options.isForceHttpsEnabled;
|
|
6169
6173
|
if (options.isAutoDeployEnabled !== void 0) body.is_auto_deploy_enabled = options.isAutoDeployEnabled;
|
|
6174
|
+
if (options.healthCheckEnabled !== void 0) body.health_check_enabled = options.healthCheckEnabled;
|
|
6175
|
+
if (options.healthCheckPath) body.health_check_path = options.healthCheckPath;
|
|
6176
|
+
if (options.healthCheckPort) body.health_check_port = String(options.healthCheckPort);
|
|
6177
|
+
if (options.healthCheckMethod) body.health_check_method = options.healthCheckMethod;
|
|
6178
|
+
if (options.healthCheckInterval) body.health_check_interval = options.healthCheckInterval;
|
|
6179
|
+
if (options.healthCheckTimeout) body.health_check_timeout = options.healthCheckTimeout;
|
|
6180
|
+
if (options.healthCheckRetries) body.health_check_retries = options.healthCheckRetries;
|
|
6181
|
+
if (options.healthCheckStartPeriod) body.health_check_start_period = options.healthCheckStartPeriod;
|
|
6182
|
+
if (options.healthCheckReturnCode) body.health_check_return_code = options.healthCheckReturnCode;
|
|
6170
6183
|
const result = await this.request(`/applications/${appUuid}`, {
|
|
6171
6184
|
method: "PATCH",
|
|
6172
6185
|
body: JSON.stringify(body)
|
|
@@ -6625,6 +6638,125 @@ var CoolifyService = class {
|
|
|
6625
6638
|
});
|
|
6626
6639
|
}
|
|
6627
6640
|
/**
|
|
6641
|
+
* Gets the full infrastructure tree: Projects → Environments → Resources.
|
|
6642
|
+
*
|
|
6643
|
+
* Fetches all projects, apps, databases, and services in parallel,
|
|
6644
|
+
* then groups them by environment_id into a hierarchical tree.
|
|
6645
|
+
*
|
|
6646
|
+
* @returns Result with the full infrastructure tree or error
|
|
6647
|
+
*/
|
|
6648
|
+
async getInfrastructureTree() {
|
|
6649
|
+
log.info("Building infrastructure tree");
|
|
6650
|
+
const [projectsResult, appsResult, dbsResult, svcsResult, serversResult] = await Promise.all([
|
|
6651
|
+
this.listProjects(),
|
|
6652
|
+
this.listApplications(),
|
|
6653
|
+
this.listDatabases(),
|
|
6654
|
+
this.listServices(),
|
|
6655
|
+
this.listServers()
|
|
6656
|
+
]);
|
|
6657
|
+
if (isErr(projectsResult)) return err(projectsResult.error);
|
|
6658
|
+
if (isErr(appsResult)) return err(appsResult.error);
|
|
6659
|
+
if (isErr(serversResult)) return err(serversResult.error);
|
|
6660
|
+
const projects = projectsResult.value;
|
|
6661
|
+
const apps = appsResult.value;
|
|
6662
|
+
const dbs = isErr(dbsResult) ? [] : dbsResult.value;
|
|
6663
|
+
const svcs = isErr(svcsResult) ? [] : svcsResult.value;
|
|
6664
|
+
const servers = serversResult.value;
|
|
6665
|
+
const envResults = await Promise.allSettled(projects.map((p) => this.getProjectEnvironments(p.uuid)));
|
|
6666
|
+
const envIdMap = new Map();
|
|
6667
|
+
for (let i = 0; i < projects.length; i++) {
|
|
6668
|
+
const envResult = envResults[i];
|
|
6669
|
+
if (envResult.status === "fulfilled" && !isErr(envResult.value)) for (const env of envResult.value.value) envIdMap.set(env.id, {
|
|
6670
|
+
projectUuid: projects[i].uuid,
|
|
6671
|
+
envName: env.name,
|
|
6672
|
+
envUuid: env.uuid
|
|
6673
|
+
});
|
|
6674
|
+
}
|
|
6675
|
+
const projectNodes = projects.map((p) => ({
|
|
6676
|
+
uuid: p.uuid,
|
|
6677
|
+
name: p.name,
|
|
6678
|
+
description: p.description,
|
|
6679
|
+
environments: []
|
|
6680
|
+
}));
|
|
6681
|
+
const projectMap = new Map();
|
|
6682
|
+
for (const node of projectNodes) projectMap.set(node.uuid, node);
|
|
6683
|
+
const envNodeMap = new Map();
|
|
6684
|
+
for (const [envId, info] of envIdMap) {
|
|
6685
|
+
const project = projectMap.get(info.projectUuid);
|
|
6686
|
+
if (!project) continue;
|
|
6687
|
+
let envNode = project.environments.find((e) => e.id === envId);
|
|
6688
|
+
if (!envNode) {
|
|
6689
|
+
envNode = {
|
|
6690
|
+
id: envId,
|
|
6691
|
+
uuid: info.envUuid,
|
|
6692
|
+
name: info.envName,
|
|
6693
|
+
resources: []
|
|
6694
|
+
};
|
|
6695
|
+
project.environments.push(envNode);
|
|
6696
|
+
}
|
|
6697
|
+
envNodeMap.set(envId, envNode);
|
|
6698
|
+
}
|
|
6699
|
+
for (const app of apps) {
|
|
6700
|
+
const envNode = app.environment_id ? envNodeMap.get(app.environment_id) : void 0;
|
|
6701
|
+
const resource = {
|
|
6702
|
+
uuid: app.uuid,
|
|
6703
|
+
name: app.name,
|
|
6704
|
+
kind: "app",
|
|
6705
|
+
status: app.status,
|
|
6706
|
+
fqdn: app.fqdn
|
|
6707
|
+
};
|
|
6708
|
+
if (envNode) envNode.resources.push(resource);
|
|
6709
|
+
}
|
|
6710
|
+
for (const db of dbs) {
|
|
6711
|
+
const envNode = db.environment_id ? envNodeMap.get(db.environment_id) : void 0;
|
|
6712
|
+
const resource = {
|
|
6713
|
+
uuid: db.uuid,
|
|
6714
|
+
name: db.name,
|
|
6715
|
+
kind: "database",
|
|
6716
|
+
status: db.status,
|
|
6717
|
+
dbType: db.type
|
|
6718
|
+
};
|
|
6719
|
+
if (envNode) envNode.resources.push(resource);
|
|
6720
|
+
}
|
|
6721
|
+
for (const svc of svcs) {
|
|
6722
|
+
const envNode = svc.environment_id ? envNodeMap.get(svc.environment_id) : void 0;
|
|
6723
|
+
const resource = {
|
|
6724
|
+
uuid: svc.uuid,
|
|
6725
|
+
name: svc.name,
|
|
6726
|
+
kind: "service",
|
|
6727
|
+
status: svc.status
|
|
6728
|
+
};
|
|
6729
|
+
if (envNode) envNode.resources.push(resource);
|
|
6730
|
+
}
|
|
6731
|
+
const populatedProjects = projectNodes.filter((p) => p.environments.some((e) => e.resources.length > 0));
|
|
6732
|
+
const allStatuses = [
|
|
6733
|
+
...apps.map((a) => a.status),
|
|
6734
|
+
...dbs.map((d) => d.status),
|
|
6735
|
+
...svcs.map((s) => s.status)
|
|
6736
|
+
];
|
|
6737
|
+
const counts = {
|
|
6738
|
+
projects: populatedProjects.length,
|
|
6739
|
+
apps: apps.length,
|
|
6740
|
+
databases: dbs.length,
|
|
6741
|
+
services: svcs.length,
|
|
6742
|
+
healthy: allStatuses.filter((s) => s.includes("healthy")).length,
|
|
6743
|
+
running: allStatuses.filter((s) => s.startsWith("running") && !s.includes("healthy")).length,
|
|
6744
|
+
stopped: allStatuses.filter((s) => s.includes("exited")).length,
|
|
6745
|
+
unhealthy: allStatuses.filter((s) => s.includes("unhealthy")).length
|
|
6746
|
+
};
|
|
6747
|
+
const server = servers[0] || { name: "Unknown" };
|
|
6748
|
+
log.success(`Infrastructure tree built: ${counts.projects} projects, ${counts.apps} apps, ${counts.databases} dbs, ${counts.services} svcs`);
|
|
6749
|
+
return ok({
|
|
6750
|
+
server: {
|
|
6751
|
+
name: server.name,
|
|
6752
|
+
ip: server.ip,
|
|
6753
|
+
uuid: server.uuid
|
|
6754
|
+
},
|
|
6755
|
+
projects: populatedProjects,
|
|
6756
|
+
counts
|
|
6757
|
+
});
|
|
6758
|
+
}
|
|
6759
|
+
/**
|
|
6628
6760
|
* Starts a service.
|
|
6629
6761
|
* Note: Coolify API uses GET for service start/stop/restart.
|
|
6630
6762
|
*
|
|
@@ -7063,6 +7195,22 @@ var CoolifyService = class {
|
|
|
7063
7195
|
return ok(deployments);
|
|
7064
7196
|
}
|
|
7065
7197
|
/**
|
|
7198
|
+
* Gets full application details by UUID.
|
|
7199
|
+
*
|
|
7200
|
+
* Makes a direct GET request to /api/v1/applications/{uuid} which returns
|
|
7201
|
+
* complete application data including project_uuid and environment_uuid.
|
|
7202
|
+
*
|
|
7203
|
+
* @param appUuid - Application UUID
|
|
7204
|
+
* @returns Result with full application details or error
|
|
7205
|
+
*/
|
|
7206
|
+
async getApplication(appUuid) {
|
|
7207
|
+
log.info(`Getting application details: ${appUuid}`);
|
|
7208
|
+
const result = await this.request(`/applications/${appUuid}`);
|
|
7209
|
+
if (result.error) return err(new Error(result.error));
|
|
7210
|
+
if (!result.data) return err(new Error("Application not found"));
|
|
7211
|
+
return ok(result.data);
|
|
7212
|
+
}
|
|
7213
|
+
/**
|
|
7066
7214
|
* Resolves an application by UUID, name, or domain (FQDN).
|
|
7067
7215
|
*
|
|
7068
7216
|
* @param query - UUID, name, or domain to search for
|
|
@@ -7455,6 +7603,117 @@ var ApplicationsResource = class {
|
|
|
7455
7603
|
async deleteEnv(uuid, key) {
|
|
7456
7604
|
return unwrap(await this.svc.deleteEnvironmentVariable(uuid, key));
|
|
7457
7605
|
}
|
|
7606
|
+
/**
|
|
7607
|
+
* Sync environment variables from a local .env file to Coolify.
|
|
7608
|
+
*
|
|
7609
|
+
* @param uuid - Application UUID
|
|
7610
|
+
* @param options - Sync options
|
|
7611
|
+
* @returns Sync result with changes applied
|
|
7612
|
+
*/
|
|
7613
|
+
async syncEnv(uuid, options = {}) {
|
|
7614
|
+
const { filePath, dryRun = false, prune = false, onProgress } = options;
|
|
7615
|
+
let envContent;
|
|
7616
|
+
try {
|
|
7617
|
+
envContent = await Bun.file(filePath || ".env").text();
|
|
7618
|
+
} catch {
|
|
7619
|
+
throw new Error(filePath ? `Cannot read file: ${filePath}` : "No .env file found in current directory");
|
|
7620
|
+
}
|
|
7621
|
+
const localVars = this.parseEnvContent(envContent);
|
|
7622
|
+
if (localVars.size === 0) return {
|
|
7623
|
+
added: [],
|
|
7624
|
+
updated: [],
|
|
7625
|
+
removed: [],
|
|
7626
|
+
skipped: 0
|
|
7627
|
+
};
|
|
7628
|
+
const currentVarsList = await this.envVars(uuid);
|
|
7629
|
+
const currentVars = new Map(currentVarsList.map((v) => [v.key, v.value]));
|
|
7630
|
+
const toAdd = [];
|
|
7631
|
+
const toUpdate = [];
|
|
7632
|
+
const toRemove = [];
|
|
7633
|
+
for (const [key, value] of localVars.entries()) {
|
|
7634
|
+
const currentValue = currentVars.get(key);
|
|
7635
|
+
if (!currentValue) toAdd.push({
|
|
7636
|
+
key,
|
|
7637
|
+
value
|
|
7638
|
+
});
|
|
7639
|
+
else if (currentValue !== value) toUpdate.push({
|
|
7640
|
+
key,
|
|
7641
|
+
value,
|
|
7642
|
+
oldValue: currentValue
|
|
7643
|
+
});
|
|
7644
|
+
}
|
|
7645
|
+
if (prune) {
|
|
7646
|
+
for (const key of currentVars.keys()) if (!localVars.has(key)) toRemove.push(key);
|
|
7647
|
+
}
|
|
7648
|
+
if (!dryRun) {
|
|
7649
|
+
for (const { key, value } of toAdd) {
|
|
7650
|
+
await this.setEnv(uuid, key, value, false);
|
|
7651
|
+
onProgress?.({
|
|
7652
|
+
type: "add",
|
|
7653
|
+
key,
|
|
7654
|
+
value
|
|
7655
|
+
});
|
|
7656
|
+
}
|
|
7657
|
+
for (const { key, value } of toUpdate) {
|
|
7658
|
+
await this.setEnv(uuid, key, value, false);
|
|
7659
|
+
onProgress?.({
|
|
7660
|
+
type: "update",
|
|
7661
|
+
key,
|
|
7662
|
+
value
|
|
7663
|
+
});
|
|
7664
|
+
}
|
|
7665
|
+
for (const key of toRemove) {
|
|
7666
|
+
await this.deleteEnv(uuid, key);
|
|
7667
|
+
onProgress?.({
|
|
7668
|
+
type: "remove",
|
|
7669
|
+
key
|
|
7670
|
+
});
|
|
7671
|
+
}
|
|
7672
|
+
} else {
|
|
7673
|
+
for (const { key, value } of toAdd) onProgress?.({
|
|
7674
|
+
type: "add",
|
|
7675
|
+
key,
|
|
7676
|
+
value
|
|
7677
|
+
});
|
|
7678
|
+
for (const { key, value } of toUpdate) onProgress?.({
|
|
7679
|
+
type: "update",
|
|
7680
|
+
key,
|
|
7681
|
+
value
|
|
7682
|
+
});
|
|
7683
|
+
for (const key of toRemove) onProgress?.({
|
|
7684
|
+
type: "remove",
|
|
7685
|
+
key
|
|
7686
|
+
});
|
|
7687
|
+
}
|
|
7688
|
+
return {
|
|
7689
|
+
added: toAdd,
|
|
7690
|
+
updated: toUpdate,
|
|
7691
|
+
removed: toRemove,
|
|
7692
|
+
skipped: currentVars.size - toUpdate.length - toRemove.length
|
|
7693
|
+
};
|
|
7694
|
+
}
|
|
7695
|
+
/**
|
|
7696
|
+
* Parse .env file content into a Map.
|
|
7697
|
+
* Handles comments, empty lines, and quoted values.
|
|
7698
|
+
*
|
|
7699
|
+
* @param content - The .env file content
|
|
7700
|
+
* @returns Map of environment variables
|
|
7701
|
+
*/
|
|
7702
|
+
parseEnvContent(content) {
|
|
7703
|
+
const envVars = new Map();
|
|
7704
|
+
const lines = content.split("\n");
|
|
7705
|
+
for (const line of lines) {
|
|
7706
|
+
const trimmedLine = line.trim();
|
|
7707
|
+
if (!trimmedLine || trimmedLine.startsWith("#")) continue;
|
|
7708
|
+
const eqIndex = trimmedLine.indexOf("=");
|
|
7709
|
+
if (eqIndex === -1) continue;
|
|
7710
|
+
const key = trimmedLine.slice(0, eqIndex).trim();
|
|
7711
|
+
let value = trimmedLine.slice(eqIndex + 1).trim();
|
|
7712
|
+
if (value.startsWith("\"") && value.endsWith("\"") || value.startsWith("'") && value.endsWith("'")) value = value.slice(1, -1);
|
|
7713
|
+
envVars.set(key, value);
|
|
7714
|
+
}
|
|
7715
|
+
return envVars;
|
|
7716
|
+
}
|
|
7458
7717
|
};
|
|
7459
7718
|
var DatabasesResource = class {
|
|
7460
7719
|
constructor(svc) {
|
|
@@ -9326,7 +9585,10 @@ var init_network = __esm({ "src/network.ts"() {
|
|
|
9326
9585
|
//#region src/cli/coolify-state.ts
|
|
9327
9586
|
const STATE_FILE = ".coolify.json";
|
|
9328
9587
|
/**
|
|
9329
|
-
* Loads .coolify.json from the current working directory.
|
|
9588
|
+
* Loads .coolify.json from the current working directory (single-app format).
|
|
9589
|
+
*
|
|
9590
|
+
* Handles both single-app and multi-app formats. For multi-app state with
|
|
9591
|
+
* exactly one app, returns a synthesized single-app state for backward compat.
|
|
9330
9592
|
*
|
|
9331
9593
|
* @returns The deploy state if found, null otherwise
|
|
9332
9594
|
*/
|
|
@@ -9336,6 +9598,27 @@ function loadCoolifyState() {
|
|
|
9336
9598
|
try {
|
|
9337
9599
|
const content = (0, node_fs.readFileSync)(statePath, "utf-8");
|
|
9338
9600
|
const state = JSON.parse(content);
|
|
9601
|
+
if (Array.isArray(state.apps)) {
|
|
9602
|
+
if (state.apps.length === 1) {
|
|
9603
|
+
const app = state.apps[0];
|
|
9604
|
+
return {
|
|
9605
|
+
appUuid: app.uuid,
|
|
9606
|
+
appName: app.name,
|
|
9607
|
+
serverUuid: state.serverUuid,
|
|
9608
|
+
serverName: state.serverName,
|
|
9609
|
+
projectUuid: state.projectUuid,
|
|
9610
|
+
projectName: state.projectName,
|
|
9611
|
+
environmentUuid: state.environmentUuid,
|
|
9612
|
+
environmentName: state.environmentName,
|
|
9613
|
+
domain: app.domain,
|
|
9614
|
+
branch: state.branch,
|
|
9615
|
+
gitRepository: state.gitRepository,
|
|
9616
|
+
coolifyUrl: state.coolifyUrl,
|
|
9617
|
+
updatedAt: state.updatedAt
|
|
9618
|
+
};
|
|
9619
|
+
}
|
|
9620
|
+
return null;
|
|
9621
|
+
}
|
|
9339
9622
|
if (!state.appUuid) return null;
|
|
9340
9623
|
return state;
|
|
9341
9624
|
} catch {
|
|
@@ -9343,18 +9626,45 @@ function loadCoolifyState() {
|
|
|
9343
9626
|
}
|
|
9344
9627
|
}
|
|
9345
9628
|
/**
|
|
9629
|
+
* Loads .coolify.json in multi-app format.
|
|
9630
|
+
*
|
|
9631
|
+
* If the file is in single-app format, returns null (use loadCoolifyState instead).
|
|
9632
|
+
*
|
|
9633
|
+
* @returns The multi-app state if found, null otherwise
|
|
9634
|
+
*/
|
|
9635
|
+
function loadMultiAppState() {
|
|
9636
|
+
const statePath = (0, node_path.join)(process.cwd(), STATE_FILE);
|
|
9637
|
+
if (!(0, node_fs.existsSync)(statePath)) return null;
|
|
9638
|
+
try {
|
|
9639
|
+
const content = (0, node_fs.readFileSync)(statePath, "utf-8");
|
|
9640
|
+
const state = JSON.parse(content);
|
|
9641
|
+
if (!Array.isArray(state.apps)) return null;
|
|
9642
|
+
return state;
|
|
9643
|
+
} catch {
|
|
9644
|
+
return null;
|
|
9645
|
+
}
|
|
9646
|
+
}
|
|
9647
|
+
/**
|
|
9346
9648
|
* Resolves a UUID argument — if not provided, tries to read from .coolify.json.
|
|
9347
9649
|
*
|
|
9650
|
+
* Supports both single-app and multi-app state formats. For multi-app state
|
|
9651
|
+
* with exactly one app, auto-selects that app. For multiple apps, returns null
|
|
9652
|
+
* (user must specify explicitly).
|
|
9653
|
+
*
|
|
9348
9654
|
* @param uuid - UUID from CLI argument (may be undefined)
|
|
9349
9655
|
* @param field - Which field to read from .coolify.json (default: appUuid)
|
|
9350
9656
|
* @returns The resolved UUID or null if not found
|
|
9351
9657
|
*/
|
|
9352
9658
|
function resolveUuid(uuid, field = "appUuid") {
|
|
9353
|
-
if (uuid) return uuid;
|
|
9659
|
+
if (uuid && /^[a-z0-9]{16,40}$/.test(uuid)) return uuid;
|
|
9354
9660
|
const state = loadCoolifyState();
|
|
9355
|
-
if (
|
|
9356
|
-
|
|
9357
|
-
|
|
9661
|
+
if (state) {
|
|
9662
|
+
const value = state[field];
|
|
9663
|
+
return typeof value === "string" ? value : null;
|
|
9664
|
+
}
|
|
9665
|
+
const multiState = loadMultiAppState();
|
|
9666
|
+
if (multiState && multiState.apps.length === 1 && field === "appUuid") return multiState.apps[0].uuid;
|
|
9667
|
+
return null;
|
|
9358
9668
|
}
|
|
9359
9669
|
|
|
9360
9670
|
//#endregion
|