@daileyos/cli 0.3.0 → 0.4.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.
@@ -1 +1 @@
1
- {"version":3,"file":"scale.js","sourceRoot":"","sources":["../../src/commands/scale.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,6BAA6B,CAAC;SACtC,WAAW,CAAC,8BAA8B,CAAC;SAC3C,MAAM,CACL,gBAAgB,CAAC,KAAK,EAAE,QAAiB,EAAE,QAAiB,EAAE,EAAE;QAC9D,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC;QAC7C,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC,CAAC;YACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,OAAO,CAAC,IAAI,QAAQ,KAAK,cAAc,CAAC,CAAC,KAAK,EAAE,CAAC;QACjF,MAAM,GAAG,CAAC,aAAa,OAAO,CAAC,EAAE,QAAQ,EAAE;YACzC,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE;SAC1B,CAAC,CAAC;QACH,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,OAAO,CAAC,IAAI,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC;IACjF,CAAC,CAAC,CACH,CAAC;IAEJ,OAAO;SACJ,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,0CAA0C,CAAC;SACvD,MAAM,CAAC,QAAQ,EAAE,gBAAgB,CAAC;SAClC,MAAM,CACL,gBAAgB,CAAC,KAAK,EAAE,QAAiB,EAAE,IAAwB,EAAE,EAAE;QACrE,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEvD,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAE/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC,CAAC;QAC5E,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,UAAU,OAAO,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;QACrE,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC,CAAC,CACH,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"scale.js","sourceRoot":"","sources":["../../src/commands/scale.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -13,6 +13,9 @@ import { registerOpenCommand } from './commands/open.js';
13
13
  import { registerPlatformCommands } from './commands/platform.js';
14
14
  import { registerStorageCommands } from './commands/storage.js';
15
15
  import { registerJobCommands } from './commands/jobs.js';
16
+ import { registerImageCommands } from './commands/images.js';
17
+ import { registerLinkCommands } from './commands/links.js';
18
+ import { registerResourceCommands } from './commands/resources.js';
16
19
  const program = new Command();
17
20
  program
18
21
  .name('dailey')
@@ -31,5 +34,8 @@ registerOpenCommand(program);
31
34
  registerPlatformCommands(program);
32
35
  registerStorageCommands(program);
33
36
  registerJobCommands(program);
37
+ registerImageCommands(program);
38
+ registerLinkCommands(program);
39
+ registerResourceCommands(program);
34
40
  program.parse();
35
41
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,gEAAgE,CAAC;KAC7E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAClC,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE7B,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,gEAAgE,CAAC;KAC7E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,kBAAkB,CAAC,OAAO,CAAC,CAAC;AAC5B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAClC,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daileyos/cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Dailey OS CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,9 +14,12 @@ function prompt(question: string, hidden = false): Promise<string> {
14
14
  });
15
15
 
16
16
  if (hidden) {
17
- // For password input, write prompt manually and mute output
17
+ // For password input, use raw mode to suppress echo
18
18
  process.stdout.write(question);
19
+ rl.close(); // close readline so it doesn't interfere
20
+
19
21
  const stdin = process.stdin;
22
+ stdin.resume();
20
23
  const wasRaw = stdin.isRaw;
21
24
  if (stdin.isTTY) {
22
25
  stdin.setRawMode(true);
@@ -29,18 +32,23 @@ function prompt(question: string, hidden = false): Promise<string> {
29
32
  if (stdin.isTTY && wasRaw !== undefined) {
30
33
  stdin.setRawMode(wasRaw);
31
34
  }
35
+ stdin.pause();
32
36
  process.stdout.write('\n');
33
- rl.close();
34
37
  resolve(input);
35
38
  } else if (c === '\u0003') {
36
39
  process.exit(0);
37
40
  } else if (c === '\u007f' || c === '\b') {
38
- input = input.slice(0, -1);
41
+ if (input.length > 0) {
42
+ input = input.slice(0, -1);
43
+ process.stdout.write('\b \b'); // erase the * character
44
+ }
39
45
  } else {
40
46
  input += c;
47
+ process.stdout.write('*'); // show asterisks instead of characters
41
48
  }
42
49
  };
43
50
  stdin.on('data', onData);
51
+ return; // don't fall through to readline
44
52
  } else {
45
53
  rl.question(question, (answer) => {
46
54
  rl.close();
@@ -7,18 +7,34 @@ import { resolveProject, formatTable, handleJson, withErrorHandler } from '../ut
7
7
  export function registerDeployCommands(program: Command): void {
8
8
  program
9
9
  .command('deploy <idOrName>')
10
- .description('Trigger a deploy (shows push instructions)')
10
+ .description('Trigger a new deploy (works for both git repos and Docker images)')
11
+ .option('--force', 'Force redeploy even if up to date')
11
12
  .action(
12
- withErrorHandler(async (idOrName: unknown) => {
13
+ withErrorHandler(async (idOrName: unknown, opts: { force?: boolean }) => {
13
14
  const project = await resolveProject(String(idOrName));
14
- console.log(chalk.bold(`Deploy "${project.name}"`));
15
- console.log();
16
- console.log(chalk.gray('Deploys are triggered automatically when you push to the configured branch.'));
17
- console.log(chalk.gray('Push your code to trigger a new deployment:'));
18
- console.log();
19
- console.log(chalk.blue(` git push origin ${project.branch || 'main'}`));
20
- console.log();
21
- console.log(chalk.gray(`View deploy history with: ${chalk.white(`dailey deploys ${project.name}`)}`));
15
+ const spinner = ora(`Deploying "${project.name}"...`).start();
16
+
17
+ try {
18
+ const result = await api<any>('/deploys', {
19
+ method: 'POST',
20
+ body: { project_id: project.id, commit_sha: 'HEAD' },
21
+ });
22
+
23
+ spinner.succeed(chalk.green(`Deploy triggered for "${project.name}"`));
24
+ console.log();
25
+ console.log(chalk.gray(' Build ID: ') + chalk.white(result.build_id));
26
+ if (result.mode === 'image') {
27
+ console.log(chalk.gray(' Mode: ') + chalk.cyan('Docker image (no build step)'));
28
+ } else {
29
+ console.log(chalk.gray(' Mode: ') + chalk.cyan('Git repo (clone → build → deploy)'));
30
+ }
31
+ console.log(chalk.gray(' Status: ') + chalk.yellow(result.status));
32
+ console.log();
33
+ console.log(chalk.gray(`View logs: ${chalk.white(`dailey logs ${String(idOrName)}`)}`));
34
+ console.log(chalk.gray(`View deploys: ${chalk.white(`dailey deploys ${String(idOrName)}`)}`));
35
+ } catch (err: any) {
36
+ spinner.fail(chalk.red(`Deploy failed: ${err.message}`));
37
+ }
22
38
  }),
23
39
  );
24
40
 
@@ -0,0 +1,204 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../api.js';
5
+ import { resolveProject, formatTable, handleJson, withErrorHandler } from '../util.js';
6
+
7
+ export function registerImageCommands(program: Command): void {
8
+ // Deploy a Docker image to a project
9
+ program
10
+ .command('run <image>')
11
+ .description('Deploy a Docker image (e.g., wordpress:6-apache, nginx:alpine)')
12
+ .option('-n, --name <name>', 'Project name (auto-generated if not provided)')
13
+ .option('--db', 'Provision a managed MySQL database')
14
+ .option('--json', 'Output as JSON')
15
+ .action(
16
+ withErrorHandler(async (image: unknown, opts: { name?: string; db?: boolean; json?: boolean }) => {
17
+ const imageStr = String(image);
18
+ const projectName = opts.name || imageStr.split('/').pop()?.split(':')[0] || 'app';
19
+
20
+ const spinner = ora(`Deploying ${chalk.cyan(imageStr)}...`).start();
21
+
22
+ // Create the project (this also triggers the deploy automatically)
23
+ const project = await api<any>('/projects', {
24
+ method: 'POST',
25
+ body: {
26
+ name: projectName,
27
+ repo_url: imageStr,
28
+ branch: 'main',
29
+ needs_database: opts.db || false,
30
+ },
31
+ });
32
+
33
+ spinner.text = `Project created: ${chalk.green(project.slug)}. Triggering deploy...`;
34
+
35
+ if (project.slug_auto_generated && project.slug_note) {
36
+ spinner.info(chalk.yellow(project.slug_note));
37
+ }
38
+
39
+ // Trigger the deploy
40
+ let deploy: any = {};
41
+ try {
42
+ deploy = await api<any>('/deploys', {
43
+ method: 'POST',
44
+ body: { project_id: project.id, commit_sha: 'HEAD' },
45
+ });
46
+ } catch (err: any) {
47
+ // Deploy trigger might fail but project is created
48
+ deploy = { build_id: 'pending' };
49
+ }
50
+
51
+ spinner.succeed(chalk.green(`Deployed ${chalk.bold(imageStr)} → https://${project.slug}.dailey.cloud`));
52
+
53
+ console.log();
54
+ console.log(chalk.gray(' Project: ') + chalk.white(project.name));
55
+ console.log(chalk.gray(' URL: ') + chalk.cyan(`https://${project.slug}.dailey.cloud`));
56
+ console.log(chalk.gray(' Build ID: ') + chalk.white(deploy.build_id || 'pending'));
57
+
58
+ if (project.database) {
59
+ console.log(chalk.gray(' Database: ') + chalk.green(project.database.database));
60
+ }
61
+
62
+ if (deploy.credentials) {
63
+ console.log();
64
+ console.log(chalk.bold(' Credentials:'));
65
+ for (const [key, val] of Object.entries(deploy.credentials)) {
66
+ if (key !== 'note' && key !== 'label') {
67
+ console.log(chalk.gray(` ${key}: `) + chalk.yellow(String(val)));
68
+ }
69
+ }
70
+ }
71
+
72
+ console.log();
73
+ console.log(chalk.gray(`View logs: ${chalk.white(`dailey logs ${project.slug}`)}`));
74
+ console.log(chalk.gray(`View info: ${chalk.white(`dailey projects info ${project.slug}`)}`));
75
+
76
+ handleJson(opts.json, { project, deploy });
77
+ }),
78
+ );
79
+
80
+ // List marketplace catalog
81
+ program
82
+ .command('catalog')
83
+ .description('List available marketplace apps')
84
+ .option('--json', 'Output as JSON')
85
+ .action(
86
+ withErrorHandler(async (opts: { json?: boolean }) => {
87
+ const spinner = ora('Fetching catalog...').start();
88
+ const data = await api<{ apps: any[] }>('/projects/catalog');
89
+ spinner.stop();
90
+
91
+ handleJson(opts.json, data.apps);
92
+
93
+ console.log(chalk.bold('Marketplace Apps'));
94
+ console.log(chalk.gray('Deploy any of these with: dailey run <image>'));
95
+ console.log();
96
+
97
+ console.log(
98
+ formatTable(data.apps, [
99
+ { key: 'name', label: 'App' },
100
+ { key: 'image', label: 'Image' },
101
+ { key: 'needs_database', label: 'DB' },
102
+ { key: 'memory_mb', label: 'Memory' },
103
+ { key: 'description', label: 'Description' },
104
+ ]),
105
+ );
106
+ }),
107
+ );
108
+
109
+ // Analyze a Docker image before deploying
110
+ program
111
+ .command('inspect <image>')
112
+ .description('Analyze a Docker image before deploying')
113
+ .option('--json', 'Output as JSON')
114
+ .action(
115
+ withErrorHandler(async (image: unknown, opts: { json?: boolean }) => {
116
+ const imageStr = String(image);
117
+ const spinner = ora(`Analyzing ${chalk.cyan(imageStr)}...`).start();
118
+
119
+ const data = await api<any>('/projects/analyze', {
120
+ method: 'POST',
121
+ body: { repo_url: imageStr },
122
+ });
123
+
124
+ spinner.stop();
125
+ handleJson(opts.json, data);
126
+
127
+ if (data._isDockerImage) {
128
+ console.log(chalk.bold('Docker Image Analysis'));
129
+ console.log();
130
+ console.log(chalk.gray(' Image: ') + chalk.cyan(data.image?.name || imageStr));
131
+ console.log(chalk.gray(' Registry: ') + chalk.white(data.image?.registry || 'Docker Hub'));
132
+ console.log(chalk.gray(' Size: ') + chalk.white(`${data.image?.size_mb || '?'} MB`));
133
+ console.log(chalk.gray(' Port: ') + chalk.white(data.image?.port || 3000));
134
+
135
+ if (data.image?.description) {
136
+ console.log(chalk.gray(' About: ') + chalk.white(data.image.description));
137
+ }
138
+ if (data.image?.catalog_match) {
139
+ console.log(chalk.gray(' Catalog: ') + chalk.green(`✓ ${data.image.catalog_match} (pre-configured)`));
140
+ }
141
+ if (data.database?.needed) {
142
+ console.log(chalk.gray(' Database: ') + chalk.yellow('Recommended'));
143
+ }
144
+
145
+ if (data.estimates) {
146
+ console.log();
147
+ console.log(chalk.gray(` Pull time: ~${data.estimates.pull_time_seconds || '?'}s`));
148
+ console.log(chalk.gray(` Deploy time: ~${data.estimates.deploy_time_seconds || '?'}s`));
149
+ }
150
+
151
+ if (data.warnings?.length) {
152
+ console.log();
153
+ for (const w of data.warnings) {
154
+ console.log(chalk.yellow(` ⚠ ${w}`));
155
+ }
156
+ }
157
+
158
+ if (data.issues?.length) {
159
+ console.log();
160
+ for (const issue of data.issues) {
161
+ console.log(chalk.red(` ✗ ${issue}`));
162
+ }
163
+ }
164
+
165
+ if (!data.issues?.length) {
166
+ console.log();
167
+ console.log(chalk.green(` ✓ Ready to deploy: ${chalk.white(`dailey run ${imageStr}`)}`));
168
+ }
169
+ } else {
170
+ console.log(chalk.bold('Git Repository Analysis'));
171
+ console.log(chalk.gray(` Stack: ${data.stack?.name || 'unknown'}`));
172
+ }
173
+ }),
174
+ );
175
+
176
+ // List project credentials
177
+ program
178
+ .command('credentials <idOrName>')
179
+ .alias('creds')
180
+ .description('Show auto-generated credentials for a project')
181
+ .option('--json', 'Output as JSON')
182
+ .action(
183
+ withErrorHandler(async (idOrName: unknown, opts: { json?: boolean }) => {
184
+ const project = await resolveProject(String(idOrName));
185
+ const data = await api<{ credentials: any[] }>(`/projects/${project.id}/credentials`);
186
+
187
+ handleJson(opts.json, data.credentials);
188
+
189
+ if (!data.credentials?.length) {
190
+ console.log(chalk.gray('No auto-generated credentials for this project.'));
191
+ return;
192
+ }
193
+
194
+ console.log(chalk.bold(`Credentials for ${project.name}`));
195
+ console.log();
196
+ for (const cred of data.credentials) {
197
+ console.log(chalk.gray(` ${cred.label}:`));
198
+ console.log(chalk.yellow(` ${cred.value}`));
199
+ }
200
+ }),
201
+ );
202
+
203
+ // Show project resources — moved to resources.ts to avoid duplicate 'resources' command registration
204
+ }
@@ -0,0 +1,96 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../api.js';
5
+ import { resolveProject, formatTable, withErrorHandler } from '../util.js';
6
+
7
+ export function registerLinkCommands(program: Command): void {
8
+ // List service links
9
+ program
10
+ .command('links <idOrName>')
11
+ .description('List linked services for a project')
12
+ .option('--json', 'Output as JSON')
13
+ .action(
14
+ withErrorHandler(async (idOrName: unknown, opts: { json?: boolean }) => {
15
+ const project = await resolveProject(String(idOrName));
16
+ const spinner = ora('Fetching links...').start();
17
+ const data = await api<{ links: any[] }>(`/projects/${project.id}/links`);
18
+ spinner.stop();
19
+
20
+ if (opts.json) {
21
+ console.log(JSON.stringify(data.links, null, 2));
22
+ return;
23
+ }
24
+
25
+ if (!data.links?.length) {
26
+ console.log(chalk.gray('No linked services.'));
27
+ console.log(chalk.gray(`Link a service: ${chalk.white(`dailey link ${String(idOrName)} <target>`)}`));
28
+ return;
29
+ }
30
+
31
+ console.log(chalk.bold(`Linked services for ${project.name}:`));
32
+ console.log(
33
+ formatTable(data.links, [
34
+ { key: 'target_name', label: 'Target' },
35
+ { key: 'env_key', label: 'Env Variable' },
36
+ { key: 'url', label: 'Internal URL' },
37
+ { key: 'target_status', label: 'Status' },
38
+ ]),
39
+ );
40
+ }),
41
+ );
42
+
43
+ // Create a service link
44
+ program
45
+ .command('link <project> <target>')
46
+ .description('Link a project to another service (injects internal URL as env var)')
47
+ .option('--env-key <key>', 'Custom env variable name (default: auto-generated)')
48
+ .action(
49
+ withErrorHandler(async (projectName: unknown, targetName: unknown, opts: { envKey?: string }) => {
50
+ const source = await resolveProject(String(projectName));
51
+ const target = await resolveProject(String(targetName));
52
+
53
+ const spinner = ora(`Linking ${chalk.cyan(target.name)} to ${chalk.cyan(source.name)}...`).start();
54
+
55
+ const result = await api<{ link: any; hint: string }>(`/projects/${source.id}/links`, {
56
+ method: 'POST',
57
+ body: { target_project_id: target.id, env_key: opts.envKey || undefined },
58
+ });
59
+
60
+ spinner.succeed(
61
+ chalk.green(`Linked ${chalk.bold(target.name)} → ${chalk.bold(source.name)}`),
62
+ );
63
+
64
+ console.log();
65
+ console.log(chalk.gray(' Env var: ') + chalk.white(result.link.env_key));
66
+ console.log(chalk.gray(' URL: ') + chalk.cyan(result.link.url));
67
+ console.log();
68
+ console.log(chalk.yellow(` Redeploy ${source.name} to pick up the new env var.`));
69
+ }),
70
+ );
71
+
72
+ // Remove a service link
73
+ program
74
+ .command('unlink <project> <target>')
75
+ .description('Remove a service link between two projects')
76
+ .action(
77
+ withErrorHandler(async (projectName: unknown, targetName: unknown) => {
78
+ const source = await resolveProject(String(projectName));
79
+ const target = await resolveProject(String(targetName));
80
+
81
+ const spinner = ora('Removing link...').start();
82
+
83
+ // Find the link
84
+ const data = await api<{ links: any[] }>(`/projects/${source.id}/links`);
85
+ const link = data.links?.find((l: any) => l.target_project_id === target.id);
86
+
87
+ if (!link) {
88
+ spinner.fail(chalk.red(`No link found from ${source.name} to ${target.name}`));
89
+ return;
90
+ }
91
+
92
+ await api(`/projects/${source.id}/links/${link.id}`, { method: 'DELETE' });
93
+ spinner.succeed(chalk.green(`Unlinked ${chalk.bold(target.name)} from ${chalk.bold(source.name)}`));
94
+ }),
95
+ );
96
+ }
@@ -10,7 +10,9 @@ export function registerOpenCommand(program: Command): void {
10
10
  .action(
11
11
  withErrorHandler(async (idOrName: unknown) => {
12
12
  const project = await resolveProject(String(idOrName));
13
- const url = project.url as string | undefined;
13
+ const url = (project.url as string | undefined)
14
+ || (project.domain ? `https://${project.domain}` : null)
15
+ || (project.slug ? `https://${project.slug}.dailey.cloud` : null);
14
16
  if (!url) {
15
17
  console.error(chalk.red(`No URL found for project "${project.name}".`));
16
18
  process.exit(1);
@@ -0,0 +1,147 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import ora from 'ora';
4
+ import { api } from '../api.js';
5
+ import { resolveProject, formatTable, handleJson, withErrorHandler } from '../util.js';
6
+
7
+ export function registerResourceCommands(program: Command): void {
8
+ const resources = program
9
+ .command('resources')
10
+ .description('Manage per-project resource allocation (CPU, memory, storage)');
11
+
12
+ resources
13
+ .command('get <idOrName>')
14
+ .description('Show current resource allocation for a project')
15
+ .option('--json', 'Output as JSON')
16
+ .action(
17
+ withErrorHandler(async (idOrName: unknown, opts: { json?: boolean }) => {
18
+ const project = await resolveProject(String(idOrName));
19
+ const spinner = ora('Loading resource config...').start();
20
+ const data = await api<any>(`/projects/${project.id}/resource-config`);
21
+ spinner.stop();
22
+
23
+ handleJson(opts.json, data);
24
+
25
+ console.log(chalk.bold(`\nResource Config: ${project.name}`));
26
+ console.log(chalk.blue('\n Project allocation:'));
27
+ console.log(` CPU request: ${data.project.cpu_request || chalk.gray('(default)')}`);
28
+ console.log(` CPU limit: ${data.project.cpu_limit || chalk.gray('(default)')}`);
29
+ console.log(` Memory request: ${data.project.memory_request || chalk.gray('(default)')}`);
30
+ console.log(` Memory limit: ${data.project.memory_limit || chalk.gray('(default)')}`);
31
+ console.log(` Storage limit: ${data.project.storage_limit || chalk.gray('(default)')}`);
32
+ console.log(` Replicas: ${data.project.replicas}`);
33
+
34
+ console.log(chalk.blue(`\n Pool (${data.pool.plan} plan):`));
35
+ console.log(` Total CPU: ${data.pool.total.cpu}`);
36
+ console.log(` Total Memory: ${data.pool.total.memory}`);
37
+ console.log(` Used CPU: ${data.pool.used.cpu}`);
38
+ console.log(` Used Memory: ${data.pool.used.memory}`);
39
+ console.log(` Available CPU: ${chalk.green(data.pool.available.cpu)}`);
40
+ console.log(` Available Mem: ${chalk.green(data.pool.available.memory)}`);
41
+ console.log(` This project: ${data.pool.used_by_this_project.cpu} CPU, ${data.pool.used_by_this_project.memory} memory`);
42
+ console.log();
43
+ }),
44
+ );
45
+
46
+ resources
47
+ .command('set <idOrName>')
48
+ .description('Set resource allocation for a project')
49
+ .option('--cpu <value>', 'CPU limit (e.g., 2000m, 4000m)')
50
+ .option('--memory <value>', 'Memory limit (e.g., 2Gi, 8Gi)')
51
+ .option('--storage <value>', 'Storage limit (e.g., 5Gi, 10Gi)')
52
+ .option('--json', 'Output as JSON')
53
+ .action(
54
+ withErrorHandler(async (idOrName: unknown, opts: { cpu?: string; memory?: string; storage?: string; json?: boolean }) => {
55
+ if (!opts.cpu && !opts.memory && !opts.storage) {
56
+ console.error(chalk.red('Provide at least one of: --cpu, --memory, --storage'));
57
+ process.exit(1);
58
+ }
59
+
60
+ const project = await resolveProject(String(idOrName));
61
+ const body: Record<string, string> = {};
62
+ if (opts.cpu) body.cpu_limit = opts.cpu;
63
+ if (opts.memory) body.memory_limit = opts.memory;
64
+ if (opts.storage) body.storage_limit = opts.storage;
65
+
66
+ const spinner = ora(`Updating resources for "${project.name}"...`).start();
67
+ const data = await api<any>(`/projects/${project.id}/resource-config`, {
68
+ method: 'PUT',
69
+ body,
70
+ });
71
+ spinner.succeed(chalk.green(`Resources updated for "${project.name}".`));
72
+
73
+ handleJson(opts.json, data);
74
+
75
+ if (data.warning) {
76
+ console.log(chalk.yellow(` Warning: ${data.warning}`));
77
+ }
78
+ if (data.k8s_applied) {
79
+ console.log(chalk.gray(' Applied to running deployment.'));
80
+ }
81
+
82
+ console.log(` CPU: ${data.project.cpu_request || '-'} / ${data.project.cpu_limit || '-'}`);
83
+ console.log(` Memory: ${data.project.memory_request || '-'} / ${data.project.memory_limit || '-'}`);
84
+ console.log(` Storage: ${data.project.storage_limit || '-'}`);
85
+ }),
86
+ );
87
+
88
+ resources
89
+ .command('pool')
90
+ .description('Show total resource pool and usage across all projects')
91
+ .option('--json', 'Output as JSON')
92
+ .action(
93
+ withErrorHandler(async (opts: { json?: boolean }) => {
94
+ const spinner = ora('Loading projects...').start();
95
+ const { projects } = await api<{ projects: any[] }>('/projects');
96
+
97
+ if (projects.length === 0) {
98
+ spinner.stop();
99
+ console.log(chalk.gray('No projects found.'));
100
+ return;
101
+ }
102
+
103
+ // Fetch resource config for each project
104
+ const configs = await Promise.all(
105
+ projects.map(async (p: any) => {
106
+ try {
107
+ const cfg = await api<any>(`/projects/${p.id}/resource-config`);
108
+ return { name: p.name, slug: p.slug, ...cfg };
109
+ } catch {
110
+ return { name: p.name, slug: p.slug, project: {}, pool: null };
111
+ }
112
+ }),
113
+ );
114
+ spinner.stop();
115
+
116
+ // Use the pool info from the first successful response
117
+ const poolInfo = configs.find((c: any) => c.pool)?.pool;
118
+
119
+ handleJson(opts.json, { pool: poolInfo, projects: configs });
120
+
121
+ if (poolInfo) {
122
+ console.log(chalk.bold(`\nResource Pool (${poolInfo.plan} plan)`));
123
+ console.log(` Total: ${poolInfo.total.cpu} CPU | ${poolInfo.total.memory} memory | ${poolInfo.total.storage} storage`);
124
+ console.log(` Used: ${poolInfo.used.cpu} CPU | ${poolInfo.used.memory} memory`);
125
+ console.log(` Available: ${chalk.green(poolInfo.available.cpu)} CPU | ${chalk.green(poolInfo.available.memory)} memory`);
126
+ }
127
+
128
+ console.log(chalk.bold('\nPer-project allocation:'));
129
+ const rows = configs.map((c: any) => ({
130
+ name: c.name || c.slug,
131
+ cpu_limit: c.project?.cpu_limit || '(default)',
132
+ memory_limit: c.project?.memory_limit || '(default)',
133
+ storage_limit: c.project?.storage_limit || '(default)',
134
+ }));
135
+
136
+ console.log(
137
+ formatTable(rows, [
138
+ { key: 'name', label: 'PROJECT' },
139
+ { key: 'cpu_limit', label: 'CPU LIMIT' },
140
+ { key: 'memory_limit', label: 'MEM LIMIT' },
141
+ { key: 'storage_limit', label: 'STORAGE' },
142
+ ]),
143
+ );
144
+ console.log();
145
+ }),
146
+ );
147
+ }
@@ -26,6 +26,55 @@ export function registerScaleCommands(program: Command): void {
26
26
  }),
27
27
  );
28
28
 
29
+ program
30
+ .command('stop <idOrName>')
31
+ .description('Stop an app (scale to 0 replicas)')
32
+ .action(
33
+ withErrorHandler(async (idOrName: unknown) => {
34
+ const project = await resolveProject(String(idOrName));
35
+ const spinner = ora(`Stopping "${project.name}"...`).start();
36
+ await api(`/projects/${project.id}/scale`, {
37
+ method: 'POST',
38
+ body: { replicas: 0 },
39
+ });
40
+ spinner.succeed(chalk.green(`Stopped "${project.name}" — scaled to 0 replicas.`));
41
+ console.log(chalk.gray(` Start again: ${chalk.white(`dailey start ${String(idOrName)}`)}`));
42
+ }),
43
+ );
44
+
45
+ program
46
+ .command('start <idOrName>')
47
+ .description('Start an app (scale to 1 replica)')
48
+ .option('-r, --replicas <n>', 'Number of replicas to start with', '1')
49
+ .action(
50
+ withErrorHandler(async (idOrName: unknown, opts: { replicas: string }) => {
51
+ const project = await resolveProject(String(idOrName));
52
+ const count = parseInt(opts.replicas, 10) || 1;
53
+ const spinner = ora(`Starting "${project.name}" with ${count} replica${count > 1 ? 's' : ''}...`).start();
54
+ await api(`/projects/${project.id}/scale`, {
55
+ method: 'POST',
56
+ body: { replicas: count },
57
+ });
58
+ spinner.succeed(chalk.green(`Started "${project.name}" with ${count} replica${count > 1 ? 's' : ''}.`));
59
+ console.log(chalk.gray(` View: ${chalk.white(`https://${project.slug}.dailey.cloud`)}`));
60
+ }),
61
+ );
62
+
63
+ program
64
+ .command('restart <idOrName>')
65
+ .description('Restart an app (rolling restart, no downtime)')
66
+ .action(
67
+ withErrorHandler(async (idOrName: unknown) => {
68
+ const project = await resolveProject(String(idOrName));
69
+ const spinner = ora(`Restarting "${project.name}"...`).start();
70
+ // Scale to 0 then back to current replicas
71
+ const replicas = Number(project.replicas) || 1;
72
+ await api(`/projects/${project.id}/scale`, { method: 'POST', body: { replicas: 0 } });
73
+ await api(`/projects/${project.id}/scale`, { method: 'POST', body: { replicas } });
74
+ spinner.succeed(chalk.green(`Restarted "${project.name}" with ${replicas} replica${replicas > 1 ? 's' : ''}.`));
75
+ }),
76
+ );
77
+
29
78
  program
30
79
  .command('status <idOrName>')
31
80
  .description('Show app status (pods, replicas, health)')