@output.ai/cli 0.7.5 → 0.7.6

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.
@@ -6,6 +6,7 @@ export default class Dev extends Command {
6
6
  static flags: {
7
7
  'compose-file': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
8
  'no-watch': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ 'image-pull-policy': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
9
10
  };
10
11
  private dockerProcess;
11
12
  run(): Promise<void>;
@@ -73,7 +73,8 @@ export default class Dev extends Command {
73
73
  static examples = [
74
74
  '<%= config.bin %> <%= command.id %>',
75
75
  '<%= config.bin %> <%= command.id %> --no-watch',
76
- '<%= config.bin %> <%= command.id %> --compose-file ./custom-docker-compose.yml'
76
+ '<%= config.bin %> <%= command.id %> --compose-file ./custom-docker-compose.yml',
77
+ '<%= config.bin %> <%= command.id %> --image-pull-policy missing'
77
78
  ];
78
79
  static args = {};
79
80
  static flags = {
@@ -85,6 +86,11 @@ export default class Dev extends Command {
85
86
  'no-watch': Flags.boolean({
86
87
  description: 'Disable automatic container restart on file changes',
87
88
  default: false
89
+ }),
90
+ 'image-pull-policy': Flags.string({
91
+ description: 'Image pull policy for docker compose (always, missing, never)',
92
+ options: ['always', 'missing', 'never'],
93
+ default: 'always'
88
94
  })
89
95
  };
90
96
  dockerProcess = null;
@@ -120,8 +126,9 @@ export default class Dev extends Command {
120
126
  };
121
127
  process.on('SIGINT', cleanup);
122
128
  process.on('SIGTERM', cleanup);
129
+ const pullPolicy = flags['image-pull-policy'];
123
130
  try {
124
- const { process: dockerProc, waitForHealthy } = await startDockerCompose(dockerComposePath, !flags['no-watch']);
131
+ const { process: dockerProc, waitForHealthy } = await startDockerCompose(dockerComposePath, !flags['no-watch'], pullPolicy);
125
132
  this.dockerProcess = dockerProc;
126
133
  dockerProc.on('error', error => {
127
134
  this.error(`Docker process error: ${getErrorMessage(error)}`, { exit: 1 });
@@ -78,6 +78,11 @@ describe('dev command', () => {
78
78
  expect(Dev.flags['compose-file'].required).toBe(false);
79
79
  expect(Dev.flags['compose-file'].char).toBe('f');
80
80
  });
81
+ it('should have image-pull-policy flag defined', () => {
82
+ expect(Dev.flags).toBeDefined();
83
+ expect(Dev.flags['image-pull-policy']).toBeDefined();
84
+ expect(Dev.flags['image-pull-policy'].description).toContain('pull policy');
85
+ });
81
86
  });
82
87
  describe('command instantiation', () => {
83
88
  it('should be instantiable', () => {
@@ -130,14 +135,15 @@ describe('dev command', () => {
130
135
  cmd.error = vi.fn();
131
136
  // Mock parse to return flags
132
137
  Object.defineProperty(cmd, 'parse', {
133
- value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined }, args: {} }),
138
+ value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'always' }, args: {} }),
134
139
  configurable: true
135
140
  });
136
141
  // Run the command but don't await it since it waits forever after startup
137
142
  const runPromise = cmd.run();
138
143
  // Wait a tick for startDockerCompose to be called
139
144
  await new Promise(resolve => setImmediate(resolve));
140
- expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true // enableWatch should be true
145
+ expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true, // enableWatch should be true
146
+ 'always' // default pull policy
141
147
  );
142
148
  expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('File watching enabled'));
143
149
  // Cancel the promise (it will be rejected but we don't care)
@@ -149,14 +155,15 @@ describe('dev command', () => {
149
155
  cmd.error = vi.fn();
150
156
  // Mock parse to return flags
151
157
  Object.defineProperty(cmd, 'parse', {
152
- value: vi.fn().mockResolvedValue({ flags: { 'no-watch': true, 'compose-file': undefined }, args: {} }),
158
+ value: vi.fn().mockResolvedValue({ flags: { 'no-watch': true, 'compose-file': undefined, 'image-pull-policy': 'always' }, args: {} }),
153
159
  configurable: true
154
160
  });
155
161
  // Run the command but don't await it since it waits forever after startup
156
162
  const runPromise = cmd.run();
157
163
  // Wait a tick for startDockerCompose to be called
158
164
  await new Promise(resolve => setImmediate(resolve));
159
- expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', false // enableWatch should be false
165
+ expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', false, // enableWatch should be false
166
+ 'always' // default pull policy
160
167
  );
161
168
  expect(cmd.log).toHaveBeenCalledWith(expect.stringContaining('File watching disabled'));
162
169
  // Cancel the promise (it will be rejected but we don't care)
@@ -176,11 +183,47 @@ describe('dev command', () => {
176
183
  cmd.error = vi.fn();
177
184
  // Mock parse to return flags
178
185
  Object.defineProperty(cmd, 'parse', {
179
- value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined }, args: {} }),
186
+ value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'always' }, args: {} }),
180
187
  configurable: true
181
188
  });
182
189
  await cmd.run();
183
190
  expect(cmd.error).toHaveBeenCalledWith('Docker error', { exit: 1 });
184
191
  });
185
192
  });
193
+ describe('image pull policy', () => {
194
+ it('should pass pull policy to startDockerCompose', async () => {
195
+ const cmd = new Dev([], {});
196
+ cmd.log = vi.fn();
197
+ cmd.error = vi.fn();
198
+ // Mock parse to return flags with missing pull policy
199
+ Object.defineProperty(cmd, 'parse', {
200
+ value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'missing' }, args: {} }),
201
+ configurable: true
202
+ });
203
+ // Run the command but don't await it since it waits forever after startup
204
+ const runPromise = cmd.run();
205
+ // Wait a tick for startDockerCompose to be called
206
+ await new Promise(resolve => setImmediate(resolve));
207
+ expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true, 'missing');
208
+ // Cancel the promise (it will be rejected but we don't care)
209
+ runPromise.catch(() => { });
210
+ });
211
+ it('should use never pull policy when specified', async () => {
212
+ const cmd = new Dev([], {});
213
+ cmd.log = vi.fn();
214
+ cmd.error = vi.fn();
215
+ // Mock parse to return flags with never pull policy
216
+ Object.defineProperty(cmd, 'parse', {
217
+ value: vi.fn().mockResolvedValue({ flags: { 'no-watch': false, 'compose-file': undefined, 'image-pull-policy': 'never' }, args: {} }),
218
+ configurable: true
219
+ });
220
+ // Run the command but don't await it since it waits forever after startup
221
+ const runPromise = cmd.run();
222
+ // Wait a tick for startDockerCompose to be called
223
+ await new Promise(resolve => setImmediate(resolve));
224
+ expect(dockerService.startDockerCompose).toHaveBeenCalledWith('/path/to/docker-compose-dev.yml', true, 'never');
225
+ // Cancel the promise (it will be rejected but we don't care)
226
+ runPromise.catch(() => { });
227
+ });
228
+ });
186
229
  });
@@ -32,6 +32,7 @@ export interface DockerComposeProcess {
32
32
  process: ChildProcess;
33
33
  waitForHealthy: () => Promise<void>;
34
34
  }
35
- export declare function startDockerCompose(dockerComposePath: string, enableWatch?: boolean): Promise<DockerComposeProcess>;
35
+ export type PullPolicy = 'always' | 'missing' | 'never';
36
+ export declare function startDockerCompose(dockerComposePath: string, enableWatch?: boolean, pullPolicy?: PullPolicy): Promise<DockerComposeProcess>;
36
37
  export declare function stopDockerCompose(dockerComposePath: string): Promise<void>;
37
38
  export { isDockerInstalled, isDockerComposeAvailable, isDockerDaemonRunning, DockerValidationError };
@@ -121,13 +121,16 @@ export async function waitForServicesHealthy(dockerComposePath, timeoutMs = 1200
121
121
  logUpdate.done();
122
122
  throw new Error('Timeout waiting for services to become healthy');
123
123
  }
124
- export async function startDockerCompose(dockerComposePath, enableWatch = false) {
124
+ export async function startDockerCompose(dockerComposePath, enableWatch = false, pullPolicy) {
125
125
  const args = [
126
126
  'compose',
127
127
  '-f', dockerComposePath,
128
128
  '--project-directory', process.cwd(),
129
129
  'up'
130
130
  ];
131
+ if (pullPolicy) {
132
+ args.push('--pull', pullPolicy);
133
+ }
131
134
  if (enableWatch) {
132
135
  args.push('--watch');
133
136
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/cli",
3
- "version": "0.7.5",
3
+ "version": "0.7.6",
4
4
  "description": "CLI for Output.ai workflow generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",