@loopress/cli 0.13.0 → 0.14.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.
Files changed (38) hide show
  1. package/README.md +62 -28
  2. package/dist/commands/init.js +5 -5
  3. package/dist/commands/plugin/add.d.ts +0 -2
  4. package/dist/commands/plugin/add.js +6 -47
  5. package/dist/commands/plugin/pull.js +4 -6
  6. package/dist/commands/plugin/push.d.ts +1 -2
  7. package/dist/commands/plugin/push.js +18 -50
  8. package/dist/commands/status.js +6 -2
  9. package/dist/commands/telemetry/disable.d.ts +6 -0
  10. package/dist/commands/telemetry/disable.js +14 -0
  11. package/dist/commands/telemetry/enable.d.ts +6 -0
  12. package/dist/commands/telemetry/enable.js +14 -0
  13. package/dist/config/auth.manager.d.ts +4 -2
  14. package/dist/config/auth.manager.js +15 -5
  15. package/dist/config/project-config.manager.d.ts +7 -2
  16. package/dist/config/project-config.manager.js +35 -6
  17. package/dist/hooks/finally.js +11 -2
  18. package/dist/hooks/init.js +8 -16
  19. package/dist/lib/local-callback-server.d.ts +3 -3
  20. package/dist/lib/local-callback-server.js +14 -4
  21. package/dist/lib/open-browser.d.ts +1 -1
  22. package/dist/lib/open-browser.js +3 -10
  23. package/dist/lib/sentry.d.ts +0 -1
  24. package/dist/lib/sentry.js +6 -8
  25. package/dist/lib/wp-authorize-flow.d.ts +15 -3
  26. package/dist/lib/wp-authorize-flow.js +31 -24
  27. package/dist/lib/wp-client.d.ts +1 -1
  28. package/dist/lib/wp-client.js +1 -1
  29. package/dist/types/config.d.ts +1 -1
  30. package/dist/types/global-config.generated.d.ts +11 -1
  31. package/dist/types/plugin.d.ts +4 -5
  32. package/dist/types/project-config.generated.d.ts +1 -4
  33. package/dist/utils/plugins.d.ts +3 -7
  34. package/dist/utils/plugins.js +27 -13
  35. package/oclif.manifest.json +133 -90
  36. package/package.json +6 -2
  37. package/schemas/global-config.schema.json +17 -1
  38. package/schemas/project-config.schema.json +3 -4
package/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @loopress/cli
21
21
  $ lps COMMAND
22
22
  running command...
23
23
  $ lps (--version)
24
- @loopress/cli/0.13.0 linux-x64 node-v24.18.0
24
+ @loopress/cli/0.14.0 linux-x64 node-v24.18.0
25
25
  $ lps --help [COMMAND]
26
26
  USAGE
27
27
  $ lps COMMAND
@@ -35,8 +35,9 @@ Loopress sends crash reports to our Sentry project so we can find and fix bugs.
35
35
 
36
36
  To opt out, either:
37
37
 
38
- - set `LOOPRESS_TELEMETRY_DISABLED=1`, or
39
- - pass `--no-error-reporting` on the command.
38
+ - run `lps telemetry disable` (persists across all future commands), or
39
+ - set `LOOPRESS_TELEMETRY_DISABLED=1` (overrides the persisted preference for a single run,
40
+ useful in CI).
40
41
 
41
42
  A crash report includes the command name, its flags/args as typed, your Node.js
42
43
  version, and OS. WordPress credentials are configured via `lps project config`
@@ -52,7 +53,7 @@ report.
52
53
  * [`lps init`](#lps-init)
53
54
  * [`lps login`](#lps-login)
54
55
  * [`lps logout`](#lps-logout)
55
- * [`lps plugin add SLUG [VERSION]`](#lps-plugin-add-slug-version)
56
+ * [`lps plugin add SLUG`](#lps-plugin-add-slug)
56
57
  * [`lps plugin pull`](#lps-plugin-pull)
57
58
  * [`lps plugin push`](#lps-plugin-push)
58
59
  * [`lps project config`](#lps-project-config)
@@ -65,6 +66,8 @@ report.
65
66
  * [`lps snippet pull [PATH]`](#lps-snippet-pull-path)
66
67
  * [`lps snippet push [PATH]`](#lps-snippet-push-path)
67
68
  * [`lps status`](#lps-status)
69
+ * [`lps telemetry disable`](#lps-telemetry-disable)
70
+ * [`lps telemetry enable`](#lps-telemetry-enable)
68
71
 
69
72
  ## `lps composer pull`
70
73
 
@@ -86,7 +89,7 @@ EXAMPLES
86
89
  $ lps composer pull --dry-run
87
90
  ```
88
91
 
89
- _See code: [src/commands/composer/pull.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/composer/pull.ts)_
92
+ _See code: [src/commands/composer/pull.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/composer/pull.ts)_
90
93
 
91
94
  ## `lps composer push`
92
95
 
@@ -108,7 +111,7 @@ EXAMPLES
108
111
  $ lps composer push --dry-run
109
112
  ```
110
113
 
111
- _See code: [src/commands/composer/push.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/composer/push.ts)_
114
+ _See code: [src/commands/composer/push.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/composer/push.ts)_
112
115
 
113
116
  ## `lps help [COMMAND]`
114
117
 
@@ -145,7 +148,7 @@ EXAMPLES
145
148
  $ lps init
146
149
  ```
147
150
 
148
- _See code: [src/commands/init.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/init.ts)_
151
+ _See code: [src/commands/init.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/init.ts)_
149
152
 
150
153
  ## `lps login`
151
154
 
@@ -162,7 +165,7 @@ EXAMPLES
162
165
  $ lps login
163
166
  ```
164
167
 
165
- _See code: [src/commands/login.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/login.ts)_
168
+ _See code: [src/commands/login.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/login.ts)_
166
169
 
167
170
  ## `lps logout`
168
171
 
@@ -179,19 +182,18 @@ EXAMPLES
179
182
  $ lps logout
180
183
  ```
181
184
 
182
- _See code: [src/commands/logout.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/logout.ts)_
185
+ _See code: [src/commands/logout.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/logout.ts)_
183
186
 
184
- ## `lps plugin add SLUG [VERSION]`
187
+ ## `lps plugin add SLUG`
185
188
 
186
189
  Add a WordPress.org plugin to loopress.json
187
190
 
188
191
  ```
189
192
  USAGE
190
- $ lps plugin add SLUG [VERSION] [-d]
193
+ $ lps plugin add SLUG [-d]
191
194
 
192
195
  ARGUMENTS
193
- SLUG Plugin slug on WordPress.org
194
- [VERSION] Version to pin (default: latest)
196
+ SLUG Plugin slug on WordPress.org
195
197
 
196
198
  FLAGS
197
199
  -d, --dry-run Show what would change without making changes
@@ -202,12 +204,10 @@ DESCRIPTION
202
204
  EXAMPLES
203
205
  $ lps plugin add woocommerce
204
206
 
205
- $ lps plugin add woocommerce 8.9.1
206
-
207
207
  $ lps plugin add contact-form-7 --dry-run
208
208
  ```
209
209
 
210
- _See code: [src/commands/plugin/add.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/plugin/add.ts)_
210
+ _See code: [src/commands/plugin/add.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/plugin/add.ts)_
211
211
 
212
212
  ## `lps plugin pull`
213
213
 
@@ -229,7 +229,7 @@ EXAMPLES
229
229
  $ lps plugin pull --dry-run
230
230
  ```
231
231
 
232
- _See code: [src/commands/plugin/pull.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/plugin/pull.ts)_
232
+ _See code: [src/commands/plugin/pull.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/plugin/pull.ts)_
233
233
 
234
234
  ## `lps plugin push`
235
235
 
@@ -251,7 +251,7 @@ EXAMPLES
251
251
  $ lps plugin push --dry-run
252
252
  ```
253
253
 
254
- _See code: [src/commands/plugin/push.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/plugin/push.ts)_
254
+ _See code: [src/commands/plugin/push.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/plugin/push.ts)_
255
255
 
256
256
  ## `lps project config`
257
257
 
@@ -269,7 +269,7 @@ EXAMPLES
269
269
  $ lps project config
270
270
  ```
271
271
 
272
- _See code: [src/commands/project/config.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/project/config.ts)_
272
+ _See code: [src/commands/project/config.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/project/config.ts)_
273
273
 
274
274
  ## `lps project list`
275
275
 
@@ -286,7 +286,7 @@ EXAMPLES
286
286
  $ lps project list
287
287
  ```
288
288
 
289
- _See code: [src/commands/project/list.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/project/list.ts)_
289
+ _See code: [src/commands/project/list.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/project/list.ts)_
290
290
 
291
291
  ## `lps project remove`
292
292
 
@@ -303,7 +303,7 @@ EXAMPLES
303
303
  $ lps project remove
304
304
  ```
305
305
 
306
- _See code: [src/commands/project/remove.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/project/remove.ts)_
306
+ _See code: [src/commands/project/remove.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/project/remove.ts)_
307
307
 
308
308
  ## `lps project switch`
309
309
 
@@ -320,7 +320,7 @@ EXAMPLES
320
320
  $ lps project switch
321
321
  ```
322
322
 
323
- _See code: [src/commands/project/switch.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/project/switch.ts)_
323
+ _See code: [src/commands/project/switch.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/project/switch.ts)_
324
324
 
325
325
  ## `lps project sync`
326
326
 
@@ -337,7 +337,7 @@ EXAMPLES
337
337
  $ lps project sync
338
338
  ```
339
339
 
340
- _See code: [src/commands/project/sync.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/project/sync.ts)_
340
+ _See code: [src/commands/project/sync.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/project/sync.ts)_
341
341
 
342
342
  ## `lps snippet list`
343
343
 
@@ -357,7 +357,7 @@ EXAMPLES
357
357
  $ lps snippet list
358
358
  ```
359
359
 
360
- _See code: [src/commands/snippet/list.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/snippet/list.ts)_
360
+ _See code: [src/commands/snippet/list.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/snippet/list.ts)_
361
361
 
362
362
  ## `lps snippet publish [PATH]`
363
363
 
@@ -380,7 +380,7 @@ EXAMPLES
380
380
  $ lps snippet publish --path ./snippets
381
381
  ```
382
382
 
383
- _See code: [src/commands/snippet/publish.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/snippet/publish.ts)_
383
+ _See code: [src/commands/snippet/publish.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/snippet/publish.ts)_
384
384
 
385
385
  ## `lps snippet pull [PATH]`
386
386
 
@@ -405,7 +405,7 @@ EXAMPLES
405
405
  $ lps snippet pull --path ./snippets
406
406
  ```
407
407
 
408
- _See code: [src/commands/snippet/pull.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/snippet/pull.ts)_
408
+ _See code: [src/commands/snippet/pull.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/snippet/pull.ts)_
409
409
 
410
410
  ## `lps snippet push [PATH]`
411
411
 
@@ -431,7 +431,7 @@ EXAMPLES
431
431
  $ lps snippet push --path ./snippets
432
432
  ```
433
433
 
434
- _See code: [src/commands/snippet/push.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/snippet/push.ts)_
434
+ _See code: [src/commands/snippet/push.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/snippet/push.ts)_
435
435
 
436
436
  ## `lps status`
437
437
 
@@ -448,5 +448,39 @@ EXAMPLES
448
448
  $ lps status
449
449
  ```
450
450
 
451
- _See code: [src/commands/status.ts](https://github.com/loopress/loopress/blob/v0.13.0/src/commands/status.ts)_
451
+ _See code: [src/commands/status.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/status.ts)_
452
+
453
+ ## `lps telemetry disable`
454
+
455
+ Disable error reporting to Sentry
456
+
457
+ ```
458
+ USAGE
459
+ $ lps telemetry disable
460
+
461
+ DESCRIPTION
462
+ Disable error reporting to Sentry
463
+
464
+ EXAMPLES
465
+ $ lps telemetry disable
466
+ ```
467
+
468
+ _See code: [src/commands/telemetry/disable.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/telemetry/disable.ts)_
469
+
470
+ ## `lps telemetry enable`
471
+
472
+ Enable error reporting to Sentry
473
+
474
+ ```
475
+ USAGE
476
+ $ lps telemetry enable
477
+
478
+ DESCRIPTION
479
+ Enable error reporting to Sentry
480
+
481
+ EXAMPLES
482
+ $ lps telemetry enable
483
+ ```
484
+
485
+ _See code: [src/commands/telemetry/enable.ts](https://github.com/loopress/loopress/blob/v0.14.0/src/commands/telemetry/enable.ts)_
452
486
  <!-- commandsstop -->
@@ -3,7 +3,7 @@ import { Command } from '@oclif/core';
3
3
  import { existsSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
5
  import { configManager } from '../config/project-config.manager.js';
6
- import { readLocalConfig, writeLocalConfig } from '../utils/loopress-config.js';
6
+ import { writeLocalConfig } from '../utils/loopress-config.js';
7
7
  // WordPress.org slugs for the two snippet plugins the Loopress WordPress plugin supports
8
8
  // (see SnippetModule.php, which wires up both providers and auto-detects the active one).
9
9
  const SNIPPET_PROVIDERS = [
@@ -76,11 +76,11 @@ export default class Init extends Command {
76
76
  snippetsDir,
77
77
  };
78
78
  await writeLocalConfig(config);
79
- let providerVersion;
79
+ let providerAdded = false;
80
80
  if (providerChoice !== '__none__') {
81
81
  try {
82
82
  await this.config.runCommand('plugin:add', [providerChoice]);
83
- providerVersion = (await readLocalConfig()).plugins?.[providerChoice];
83
+ providerAdded = true;
84
84
  }
85
85
  catch (error) {
86
86
  this.warn(error.message);
@@ -89,8 +89,8 @@ export default class Init extends Command {
89
89
  this.log(`\n✓ loopress.json created`);
90
90
  this.log(` Project: ${projectLabel}`);
91
91
  this.log(` Snippets: ${join(rootDir, snippetsDir)}`);
92
- if (providerChoice !== '__none__' && providerVersion) {
93
- this.log(` Plugin: ${providerChoice}@${providerVersion}`);
92
+ if (providerAdded) {
93
+ this.log(` Plugin: ${providerChoice}`);
94
94
  }
95
95
  }
96
96
  }
@@ -1,9 +1,7 @@
1
1
  import { LoopressCommand } from '../../lib/base.js';
2
- export declare function resolvePluginVersion(slug: string, version: string): Promise<string>;
3
2
  export default class Add extends LoopressCommand {
4
3
  static args: {
5
4
  slug: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
- version: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
5
  };
8
6
  static description: string;
9
7
  static examples: string[];
@@ -1,73 +1,32 @@
1
1
  import { Args } from '@oclif/core';
2
- import got from 'got';
3
2
  import { LoopressCommand } from '../../lib/base.js';
4
3
  import { writeLocalConfig } from '../../utils/loopress-config.js';
5
- const WP_ORG_API = 'https://api.wordpress.org/plugins/info/1.2/';
6
- const WP_ORG_TIMEOUT_MS = 10_000;
7
- export async function resolvePluginVersion(slug, version) {
8
- if (version !== 'latest')
9
- return version;
10
- let info;
11
- try {
12
- info = await got
13
- .get(WP_ORG_API, {
14
- searchParams: {
15
- action: 'plugin_information',
16
- 'request[slug]': slug,
17
- },
18
- timeout: { request: WP_ORG_TIMEOUT_MS },
19
- })
20
- .json();
21
- }
22
- catch {
23
- throw new Error(`Plugin "${slug}" not found on WordPress.org.`);
24
- }
25
- if (info.error)
26
- throw new Error(`Plugin "${slug}" not found on WordPress.org.`);
27
- return info.version;
28
- }
29
4
  export default class Add extends LoopressCommand {
30
5
  static args = {
31
6
  slug: Args.string({ description: 'Plugin slug on WordPress.org', required: true }),
32
- version: Args.string({ description: 'Version to pin (default: latest)' }),
33
7
  };
34
8
  static description = 'Add a WordPress.org plugin to loopress.json';
35
- static examples = [
36
- '$ lps plugin add woocommerce',
37
- '$ lps plugin add woocommerce 8.9.1',
38
- '$ lps plugin add contact-form-7 --dry-run',
39
- ];
9
+ static examples = ['$ lps plugin add woocommerce', '$ lps plugin add contact-form-7 --dry-run'];
40
10
  static flags = {
41
11
  ...LoopressCommand.dryRunFlag,
42
12
  };
43
13
  async run() {
44
14
  const { args } = await this.parse(Add);
45
15
  const { slug } = args;
46
- const requestedVersion = args.version ?? 'latest';
47
- this.log(`Resolving ${slug}@${requestedVersion}...`);
48
- let resolvedVersion;
49
- try {
50
- resolvedVersion = await resolvePluginVersion(slug, requestedVersion);
51
- }
52
- catch (error) {
53
- this.error(error.message);
54
- }
55
- this.log(`Resolved: ${slug}@${resolvedVersion}`);
56
16
  const existing = this.localConfig.plugins ?? {};
57
- if (existing[slug] === resolvedVersion) {
58
- this.log(`${slug}@${resolvedVersion} is already in loopress.json, nothing to do.`);
17
+ if (existing[slug] === 'latest') {
18
+ this.log(`${slug} is already in loopress.json, nothing to do.`);
59
19
  return;
60
20
  }
61
21
  const updated = existing[slug] !== undefined;
62
- const label = updated ? `${existing[slug]} → ${resolvedVersion}` : resolvedVersion;
63
22
  if (this.dryRun) {
64
- this.log(`[dry-run] Would ${updated ? 'update' : 'add'} ${slug}: ${label}`);
23
+ this.log(`[dry-run] Would ${updated ? 'update' : 'add'} ${slug} in loopress.json`);
65
24
  return;
66
25
  }
67
26
  await writeLocalConfig({
68
27
  ...this.localConfig,
69
- plugins: { ...existing, [slug]: resolvedVersion },
28
+ plugins: { ...existing, [slug]: 'latest' },
70
29
  });
71
- this.log(`${updated ? 'Updated' : 'Added'} ${slug}: ${label}`);
30
+ this.log(`${updated ? 'Updated' : 'Added'} ${slug}`);
72
31
  }
73
32
  }
@@ -1,7 +1,7 @@
1
1
  import { LoopressCommand } from '../../lib/base.js';
2
2
  import { getComposerManagedSlugs, readComposerJson } from '../../utils/composer.js';
3
3
  import { writeLocalConfig } from '../../utils/loopress-config.js';
4
- import { mergePluginManifest } from '../../utils/plugins.js';
4
+ import { mergePluginManifest, parseInstalledPlugins } from '../../utils/plugins.js';
5
5
  export default class Pull extends LoopressCommand {
6
6
  static description = 'Pull installed plugins from WordPress into loopress.json';
7
7
  static examples = ['$ lps plugin pull', '$ lps plugin pull --dry-run'];
@@ -11,10 +11,11 @@ export default class Pull extends LoopressCommand {
11
11
  async run() {
12
12
  const { url } = this.siteConfig;
13
13
  this.log(`Pulling plugins from ${url}`);
14
- const installed = await this.wp.get('loopress/v1/plugins');
14
+ const raw = await this.wp.get('wp/v2/plugins');
15
+ const installed = parseInstalledPlugins(raw);
15
16
  const composerJson = await readComposerJson();
16
17
  const composerSlugs = composerJson ? getComposerManagedSlugs(composerJson) : [];
17
- const incoming = Object.fromEntries(installed.filter((p) => !composerSlugs.includes(p.slug)).map((p) => [p.slug, p.version]));
18
+ const incoming = Object.fromEntries(installed.filter((p) => !composerSlugs.includes(p.slug)).map((p) => [p.slug, 'latest']));
18
19
  if (composerSlugs.length > 0) {
19
20
  const found = installed.filter((p) => composerSlugs.includes(p.slug)).map((p) => p.slug);
20
21
  if (found.length > 0) {
@@ -38,8 +39,5 @@ export default class Pull extends LoopressCommand {
38
39
  this.log(` + Added: ${added.join(', ')}`);
39
40
  for (const u of updated)
40
41
  this.log(` ~ Updated: ${u.slug} ${u.from} → ${u.to}`);
41
- if (Object.keys(merged).length > 0) {
42
- await this.wp.post('loopress/v1/plugins/auto-updates/disable', { slugs: Object.keys(merged) });
43
- }
44
42
  }
45
43
  }
@@ -8,7 +8,6 @@ export default class Push extends PushCommand {
8
8
  private failedCount;
9
9
  run(): Promise<void>;
10
10
  private activatePlugin;
11
- private installAndActivate;
11
+ private installPlugin;
12
12
  private performPluginAction;
13
- private syncDrifted;
14
13
  }
@@ -1,8 +1,7 @@
1
- import { confirm } from '@inquirer/prompts';
2
1
  import { Listr } from 'listr2';
3
2
  import { PushCommand } from '../../lib/push-command.js';
4
3
  import { getComposerManagedSlugs, readComposerJson } from '../../utils/composer.js';
5
- import { diffPlugins } from '../../utils/plugins.js';
4
+ import { diffPlugins, parseInstalledPlugins } from '../../utils/plugins.js';
6
5
  export default class Push extends PushCommand {
7
6
  static description = 'Push plugins to WordPress to match loopress.json';
8
7
  static examples = ['$ lps plugin push', '$ lps plugin push --dry-run'];
@@ -25,45 +24,39 @@ export default class Push extends PushCommand {
25
24
  this.log('Run `lps composer push` to deploy them.');
26
25
  }
27
26
  this.log(`Pushing plugins to ${url}`);
28
- const installed = await this.wp.get('loopress/v1/plugins');
29
- const { drifted, toActivate, toInstall } = diffPlugins(filteredManifest, installed);
30
- if (toInstall.length === 0 && toActivate.length === 0 && drifted.length === 0) {
27
+ const raw = await this.wp.get('wp/v2/plugins');
28
+ const installed = parseInstalledPlugins(raw);
29
+ const { toActivate, toInstall } = diffPlugins(filteredManifest, installed);
30
+ if (toInstall.length === 0 && toActivate.length === 0) {
31
31
  this.log('Everything is already in sync.');
32
32
  return;
33
33
  }
34
34
  if (toInstall.length > 0) {
35
35
  this.log(`\nTo install (${toInstall.length}):`);
36
36
  for (const a of toInstall)
37
- this.log(` + ${a.slug} @ ${a.targetVersion}`);
37
+ this.log(` + ${a.slug}`);
38
38
  }
39
39
  if (toActivate.length > 0) {
40
40
  this.log(`\nTo activate (${toActivate.length}):`);
41
41
  for (const a of toActivate)
42
42
  this.log(` ↑ ${a.slug}`);
43
43
  }
44
- if (drifted.length > 0) {
45
- this.log(`\nVersion mismatch (${drifted.length}):`);
46
- for (const a of drifted) {
47
- this.log(` ~ ${a.slug}: site has ${a.currentVersion}, manifest wants ${a.targetVersion}`);
48
- }
49
- }
50
44
  if (this.dryRun)
51
45
  return;
52
- // Install missing plugins and activate them.
46
+ // Installing with `status: active` activates in the same call, so installs never need a
47
+ // separate activation step the way the old custom endpoint's two-step flow did.
53
48
  if (toInstall.length > 0) {
54
49
  await new Listr(toInstall.map((action) => ({
55
- task: async (_ctx, task) => this.installAndActivate(action.slug, action.targetVersion, task),
56
- title: `Install ${action.slug} @ ${action.targetVersion}`,
50
+ task: async (_ctx, task) => this.installPlugin(action.slug, task),
51
+ title: `Install ${action.slug}`,
57
52
  })), { concurrent: false, exitOnError: false }).run();
58
53
  }
59
- // Activate installed-but-inactive plugins without prompting.
60
54
  if (toActivate.length > 0) {
61
55
  await new Listr(toActivate.map((action) => ({
62
- task: async (_ctx, task) => this.activatePlugin(action.slug, task),
56
+ task: async (_ctx, task) => this.activatePlugin(action.file, action.slug, task),
63
57
  title: `Activate ${action.slug}`,
64
58
  })), { concurrent: false, exitOnError: false }).run();
65
59
  }
66
- await this.syncDrifted(drifted);
67
60
  if (this.failedCount > 0) {
68
61
  this.error(`${this.failedCount} plugin${this.failedCount === 1 ? '' : 's'} failed to install or activate.`);
69
62
  }
@@ -75,18 +68,16 @@ export default class Push extends PushCommand {
75
68
  // it falls back to plain logging. Rethrowing on failure (rather than swallowing) is what lets Listr
76
69
  // mark the task as failed (red cross) instead of completed, even though `exitOnError: false` stops
77
70
  // that failure from aborting sibling tasks in the same list.
78
- async activatePlugin(slug, task) {
79
- await this.performPluginAction('activate', { body: { slug }, endpoint: 'loopress/v1/plugins/activate', slug }, task);
71
+ async activatePlugin(file, slug, task) {
72
+ await this.performPluginAction('activate', slug, () => this.wp.put(`wp/v2/plugins/${file}`, { status: 'active' }), task);
80
73
  }
81
- async installAndActivate(slug, version, task) {
82
- await this.performPluginAction('install', { body: { slug, version }, endpoint: 'loopress/v1/plugins/install', slug }, task);
83
- await this.activatePlugin(slug, task);
74
+ async installPlugin(slug, task) {
75
+ await this.performPluginAction('install', slug, () => this.wp.post('wp/v2/plugins', { slug, status: 'active' }), task);
84
76
  }
85
- async performPluginAction(verb, request, task) {
86
- const { body, endpoint, slug } = request;
77
+ async performPluginAction(verb, slug, request, task) {
87
78
  try {
88
- const result = await this.wp.post(endpoint, body);
89
- const message = `✓ ${result.message}`;
79
+ await request();
80
+ const message = `✓ ${slug} ${verb === 'install' ? 'installed and activated' : 'activated'}`;
90
81
  if (task)
91
82
  task.output = message;
92
83
  else
@@ -102,27 +93,4 @@ export default class Push extends PushCommand {
102
93
  throw error;
103
94
  }
104
95
  }
105
- // Prompt per drifted plugin before syncing. Prompts run sequentially on plain stdout,
106
- // before the Listr renderer takes over the terminal for the confirmed subset.
107
- async syncDrifted(drifted) {
108
- const confirmedDrift = [];
109
- for (const action of drifted) {
110
- const proceed = await confirm({
111
- default: false,
112
- message: `${action.slug} is at ${action.currentVersion} on the site but manifest wants ${action.targetVersion}. Sync to ${action.targetVersion}?`,
113
- });
114
- if (proceed) {
115
- confirmedDrift.push(action);
116
- }
117
- else {
118
- this.log(` Skipped ${action.slug}`);
119
- }
120
- }
121
- if (confirmedDrift.length === 0)
122
- return;
123
- await new Listr(confirmedDrift.map((action) => ({
124
- task: async (_ctx, task) => this.installAndActivate(action.slug, action.targetVersion, task),
125
- title: `Sync ${action.slug} to ${action.targetVersion}`,
126
- })), { concurrent: false, exitOnError: false }).run();
127
- }
128
96
  }
@@ -10,9 +10,13 @@ export default class Status extends Command {
10
10
  const localConfig = await readLocalConfig();
11
11
  if (localConfig.projectId) {
12
12
  this.reportPinnedProject(localConfig.projectId);
13
- return;
14
13
  }
15
- this.reportActiveProject();
14
+ else {
15
+ this.reportActiveProject();
16
+ }
17
+ this.log('');
18
+ this.log(`Config dir: ${this.config.configDir}`);
19
+ this.log(`Data dir: ${this.config.dataDir}`);
16
20
  }
17
21
  reportActiveProject() {
18
22
  const env = configManager.getCurrentEnv();
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class TelemetryDisable extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ import { configManager } from '../../config/project-config.manager.js';
3
+ export default class TelemetryDisable extends Command {
4
+ static description = 'Disable error reporting to Sentry';
5
+ static examples = ['$ lps telemetry disable'];
6
+ async run() {
7
+ if (configManager.isTelemetryDisabled()) {
8
+ this.log('Error reporting is already disabled.');
9
+ return;
10
+ }
11
+ configManager.setTelemetryDisabled(true);
12
+ this.log('Error reporting disabled.');
13
+ }
14
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class TelemetryEnable extends Command {
3
+ static description: string;
4
+ static examples: string[];
5
+ run(): Promise<void>;
6
+ }
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ import { configManager } from '../../config/project-config.manager.js';
3
+ export default class TelemetryEnable extends Command {
4
+ static description = 'Enable error reporting to Sentry';
5
+ static examples = ['$ lps telemetry enable'];
6
+ async run() {
7
+ if (!configManager.isTelemetryDisabled()) {
8
+ this.log('Error reporting is already enabled.');
9
+ return;
10
+ }
11
+ configManager.setTelemetryDisabled(false);
12
+ this.log('Error reporting enabled.');
13
+ }
14
+ }
@@ -4,11 +4,13 @@ export interface ConsoleAuth {
4
4
  token: string;
5
5
  }
6
6
  export declare class AuthManager {
7
- private readonly homeDir;
8
- constructor(homeDir?: string);
7
+ private dataDir?;
8
+ constructor(dataDir?: string | undefined);
9
9
  clearAuth(): void;
10
10
  getAuth(): ConsoleAuth | null;
11
11
  getAuthFilePath(): string;
12
12
  setAuth(auth: ConsoleAuth): void;
13
+ setDataDir(dataDir: string): void;
14
+ private requireDataDir;
13
15
  }
14
16
  export declare const authManager: AuthManager;
@@ -1,11 +1,10 @@
1
1
  import { existsSync, unlinkSync } from 'node:fs';
2
- import { homedir } from 'node:os';
3
2
  import { join } from 'node:path';
4
3
  import { readJsonFile, writeJsonFileAtomic } from './json-file.js';
5
4
  export class AuthManager {
6
- homeDir;
7
- constructor(homeDir = homedir()) {
8
- this.homeDir = homeDir;
5
+ dataDir;
6
+ constructor(dataDir) {
7
+ this.dataDir = dataDir;
9
8
  }
10
9
  clearAuth() {
11
10
  const filePath = this.getAuthFilePath();
@@ -16,10 +15,21 @@ export class AuthManager {
16
15
  return readJsonFile(this.getAuthFilePath());
17
16
  }
18
17
  getAuthFilePath() {
19
- return join(this.homeDir, '.loopress', 'auth.json');
18
+ return join(this.requireDataDir(), 'auth.json');
20
19
  }
21
20
  setAuth(auth) {
22
21
  writeJsonFileAtomic(this.getAuthFilePath(), auth);
23
22
  }
23
+ // Real CLI runs get this from the `init` hook (src/hooks/init.ts) before any command runs.
24
+ // Throwing when it's unset (rather than falling back to a hardcoded path) surfaces tests that
25
+ // forgot to configure the manager instead of silently touching some default location.
26
+ setDataDir(dataDir) {
27
+ this.dataDir = dataDir;
28
+ }
29
+ requireDataDir() {
30
+ if (!this.dataDir)
31
+ throw new Error('AuthManager used before setDataDir() was called');
32
+ return this.dataDir;
33
+ }
24
34
  }
25
35
  export const authManager = new AuthManager();