plugin-updater 1.3.4 → 1.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.
package/README.md CHANGED
@@ -91,7 +91,7 @@ Deployed automatically to both apps on each `earlyLaunch` (`~/.config/opencode/c
91
91
 
92
92
  ## Configuration
93
93
 
94
- > Config files are **auto-created with defaults on first run** (via core `ensureConfig`). **Global console logging** for every plugin is toggled in `config/settings.json` (`logConsole: true`, the opencode.json-equivalent).
94
+ > Config files are **never auto-created on launch** — settings are registered with defaults (core `defineConfig`) and edited in the loader's **Plugins → Configure** screen (or `/<plugin>-config`); a file is written only when you change a value. **Global console logging** for every plugin is toggled in `config/settings.json` (`logConsole: true`, the opencode.json-equivalent).
95
95
 
96
96
  Config file: `~/.config/opencode/config/plugin-updater.json` (preferred) or `~/.config/opencode/plugin-updater.json` (fallback); same under `~/.claude` for Claude Code.
97
97
 
package/dist/commands.js CHANGED
@@ -1,12 +1,16 @@
1
1
  // @ts-nocheck
2
- // Cross-app slash-command for plugin-updater: /plugin-updater-config (the loaders
3
- // own /plugins). plugin-updater is an npm package (not deployed at plugin/<name>.js),
4
- // so the command shells into this same bundle's index.js by its absolute path.
2
+ // Cross-app slash-commands for plugin-updater:
3
+ // /plugin-updater-config plugin-updater's OWN settings (scoped, like every plugin)
4
+ // /config — the UNIFIED entry: global settings + ANY installed plugin
5
+ // plugin-updater is an npm package (not deployed at plugin/<name>.js), so commands shell
6
+ // into this same bundle's index.js by absolute path. The /config dispatcher (config-all)
7
+ // enumerates plugins.json and resolves each plugin's deployed bundle.
5
8
  import { fileURLToPath } from "url";
6
9
  import { dirname, join } from "path";
7
- import { runConfigCli, deployCommands } from "../lib/core.js";
10
+ import fs from "fs";
11
+ import { runConfigCli, runAllConfigCli, deployCommands, getAppConfigDir as coreGetAppConfigDir } from "../lib/core.js";
12
+ import { getPlugins } from "./config.js";
8
13
  const PLUGIN = "plugin-updater";
9
- // the deployed entry that carries the maybeRunCli guard (dist/index.js, sibling).
10
14
  const SELF = join(dirname(fileURLToPath(import.meta.url)), "index.js");
11
15
  export function deployUpdaterCommands() {
12
16
  try {
@@ -18,19 +22,37 @@ export function deployUpdaterCommands() {
18
22
  shell: `node "${SELF}" config $ARGUMENTS`,
19
23
  body: "Above is the plugin-updater config result. Report it; if the user changed a setting, confirm the new value.",
20
24
  },
25
+ {
26
+ name: "config",
27
+ description: "View/change ANY plugin's settings and the global settings",
28
+ argumentHint: "[global | <plugin>] [list | get <key> | set <key> <value>]",
29
+ shell: `node "${SELF}" config-all $ARGUMENTS`,
30
+ body: "Above is the unified ecosystem config: the global settings block plus one block per installed plugin. Present it clearly. If the user asked to change a setting, run `/config <target> set <key> <value>` (target is `global` or the plugin name) and confirm the new value.",
31
+ },
21
32
  ]);
22
33
  }
23
34
  catch {
24
35
  /* best-effort */
25
36
  }
26
37
  }
27
- // If invoked as `node dist/index.js config …`, run the config CLI and return true
28
- // so the entry exits before the updater/self-activate sequence.
29
38
  export async function maybeRunCli() {
30
39
  const argv = process.argv.slice(2);
31
40
  if (argv[0] === "config") {
32
41
  runConfigCli(PLUGIN, argv.slice(1));
33
42
  return true;
34
43
  }
44
+ if (argv[0] === "config-all") {
45
+ // Use core's getAppConfigDir (respects HUB_OPENCODE_DIR / HUB_CLAUDE_DIR env overrides)
46
+ const configDir = coreGetAppConfigDir();
47
+ const names = getPlugins(configDir).map((p) => p.name);
48
+ const resolveBundle = (name) => {
49
+ if (name === PLUGIN)
50
+ return SELF;
51
+ const p = join(configDir, "plugin", `${name}.js`);
52
+ return fs.existsSync(p) ? p : null;
53
+ };
54
+ runAllConfigCli(argv.slice(1), { plugins: [...names, PLUGIN], resolveBundle });
55
+ return true;
56
+ }
35
57
  return false;
36
58
  }
package/dist/index.js CHANGED
@@ -8,11 +8,13 @@ import { syncPluginsAcrossApps } from "./syncbridge.js";
8
8
  // @ts-ignore — generated bundle, no .d.ts
9
9
  import { maybeRunCli, deployUpdaterCommands } from "./commands.js";
10
10
  // @ts-ignore — generated bundle, no .d.ts
11
- import { ensureConfig } from "../lib/core.js";
11
+ import { defineConfig } from "../lib/core.js";
12
12
  import path from "path";
13
13
  import fs from "fs";
14
14
  // `node dist/index.js config …` (from the /plugin-updater-config command) runs the
15
15
  // config CLI and exits, before the self-activation/updater sequence below.
16
+ // Register config defaults BEFORE the CLI guard so `config schema` sees them (no write).
17
+ defineConfig("plugin-updater", { logging: true });
16
18
  if (await maybeRunCli()) {
17
19
  process.exit(0);
18
20
  }
@@ -72,10 +74,6 @@ export async function earlyLaunch(configDir, plugins) {
72
74
  deployUpdaterCommands();
73
75
  }
74
76
  catch { /* best-effort */ }
75
- try {
76
- ensureConfig("plugin-updater", { logging: true });
77
- }
78
- catch { /* best-effort */ }
79
77
  // pull in any `sync: true` plugins from the other app BEFORE building, then
80
78
  // re-read the list so a freshly-synced-in plugin is cloned/built this pass.
81
79
  await syncPluginsAcrossApps(configDir);
package/lib/core.js CHANGED
@@ -83,19 +83,13 @@ function loadConfig(name, configDir = getAppConfigDir()) {
83
83
  CACHE[key] = data && typeof data === "object" && !Array.isArray(data) ? data : {};
84
84
  return CACHE[key];
85
85
  }
86
- function ensureConfig(name, defaults, configDir = getAppConfigDir()) {
87
- const trivial = Object.keys(defaults).every((k) => k === "logging");
88
- const preferred = join2(configDir, "config", `${name}.json`);
89
- const fallback = join2(configDir, `${name}.json`);
90
- if (!trivial && !existsSync3(preferred) && !existsSync3(fallback)) {
91
- try {
92
- writeJson(preferred, defaults);
93
- } catch {
94
- }
95
- CACHE[configDir + "::" + name] = { ...defaults };
96
- return CACHE[configDir + "::" + name];
97
- }
98
- return { ...defaults, ...loadConfig(name, configDir) };
86
+ var DEFAULTS = {};
87
+ function defineConfig(name, defaults, configDir = getAppConfigDir()) {
88
+ DEFAULTS[name] = { ...DEFAULTS[name] ?? {}, ...defaults };
89
+ return { ...DEFAULTS[name], ...loadConfig(name, configDir) };
90
+ }
91
+ function getConfigDefaults(name) {
92
+ return { ...DEFAULTS[name] ?? {} };
99
93
  }
100
94
  function getConfigValue(name, key, configDir = getAppConfigDir()) {
101
95
  let node = loadConfig(name, configDir);
@@ -243,14 +237,22 @@ function configCommand(pluginName, commandName = `${pluginName}-config`) {
243
237
  // core/src/configcli.ts
244
238
  function runConfigCli(pluginName, argv) {
245
239
  const [action, key, ...rest] = argv;
240
+ if (action === "schema") {
241
+ console.log(JSON.stringify({ name: pluginName, defaults: getConfigDefaults(pluginName), current: listConfig(pluginName) }));
242
+ return;
243
+ }
246
244
  if (!action || action === "list") {
247
- const cfg = listConfig(pluginName);
248
- const keys = Object.keys(cfg);
245
+ const defaults = getConfigDefaults(pluginName);
246
+ const current = listConfig(pluginName);
247
+ const keys = Object.keys({ ...defaults, ...current });
249
248
  if (!keys.length) {
250
- console.log(`${pluginName}: no config set (using defaults).`);
249
+ console.log(`${pluginName}: no configurable settings.`);
251
250
  return;
252
251
  }
253
- for (const k of keys) console.log(`${k} = ${JSON.stringify(cfg[k])}`);
252
+ for (const k of keys) {
253
+ const isSet = Object.prototype.hasOwnProperty.call(current, k);
254
+ console.log(`${k} = ${JSON.stringify(isSet ? current[k] : defaults[k])}${isSet ? "" : " (default)"}`);
255
+ }
254
256
  return;
255
257
  }
256
258
  if (action === "get") {
@@ -271,7 +273,7 @@ function runConfigCli(pluginName, argv) {
271
273
  console.log(`set ${key} = ${JSON.stringify(value)}`);
272
274
  return;
273
275
  }
274
- console.log(`${pluginName} config \u2014 usage: list | get <key> | set <key> <value>`);
276
+ console.log(`${pluginName} config \u2014 usage: list | get <key> | set <key> <value> | schema`);
275
277
  }
276
278
  function maybeRunConfigCli(pluginName) {
277
279
  const argv = process.argv.slice(2);
@@ -283,19 +285,67 @@ function maybeRunConfigCli(pluginName) {
283
285
  }
284
286
  return true;
285
287
  }
288
+
289
+ // core/src/configcli-all.ts
290
+ import { execFileSync } from "child_process";
291
+ var GLOBAL_SETTINGS_DEFAULTS = { logConsole: false, logColor: true };
292
+ var GLOBAL_NAME = "settings";
293
+ function defaultRunChild(bundle, args) {
294
+ return execFileSync(process.execPath, [bundle, "config", ...args], { encoding: "utf8" });
295
+ }
296
+ function msg(e) {
297
+ return String(e?.message ?? e);
298
+ }
299
+ function runAllConfigCli(argv, opts) {
300
+ defineConfig(GLOBAL_NAME, GLOBAL_SETTINGS_DEFAULTS);
301
+ const runChild = opts.runChild ?? defaultRunChild;
302
+ const [target, ...rest] = argv;
303
+ if (!target || target === "list") {
304
+ console.log("# global");
305
+ runConfigCli(GLOBAL_NAME, ["list"]);
306
+ for (const name of opts.plugins) {
307
+ const bundle2 = opts.resolveBundle(name);
308
+ if (!bundle2) continue;
309
+ console.log(`
310
+ # ${name}`);
311
+ try {
312
+ process.stdout.write(runChild(bundle2, ["list"]));
313
+ } catch (e) {
314
+ console.log(` (could not read ${name}: ${msg(e)})`);
315
+ }
316
+ }
317
+ return;
318
+ }
319
+ if (target === "global") {
320
+ runConfigCli(GLOBAL_NAME, rest.length ? rest : ["list"]);
321
+ return;
322
+ }
323
+ const bundle = opts.resolveBundle(target);
324
+ if (!bundle) {
325
+ console.log(`Unknown config target: ${target}`);
326
+ return;
327
+ }
328
+ try {
329
+ process.stdout.write(runChild(bundle, rest.length ? rest : ["list"]));
330
+ } catch (e) {
331
+ console.log(`config ${target} failed: ${msg(e)}`);
332
+ }
333
+ }
286
334
  export {
335
+ GLOBAL_SETTINGS_DEFAULTS,
287
336
  atomicWrite,
288
337
  coerce,
289
338
  configCommand,
290
339
  configPath,
291
340
  createLogger,
341
+ defineConfig,
292
342
  deployCommands,
293
- ensureConfig,
294
343
  ensureDir,
295
344
  existingApps,
296
345
  existingConfigDirs,
297
346
  getApp,
298
347
  getAppConfigDir,
348
+ getConfigDefaults,
299
349
  getConfigValue,
300
350
  globalSetting,
301
351
  isClaude,
@@ -306,6 +356,7 @@ export {
306
356
  makeWriteLog,
307
357
  maybeRunConfigCli,
308
358
  readJson,
359
+ runAllConfigCli,
309
360
  runConfigCli,
310
361
  setConfigValue,
311
362
  writeJson
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "plugin-updater",
3
- "version": "1.3.4",
3
+ "version": "1.4.0",
4
4
  "description": "Plugin lifecycle manager for OpenCode and Claude Code launchers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",