ccprofiles-core 0.7.1 → 0.7.3

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/apply.js CHANGED
@@ -11,8 +11,26 @@ function configPathFor(dir, home, agent) {
11
11
  return join(dir, 'config.toml');
12
12
  return dir === join(home, '.claude') ? join(home, '.claude.json') : join(dir, '.claude.json');
13
13
  }
14
- function sortKeys(o) {
15
- return Object.fromEntries(Object.entries(o).sort(([a], [b]) => a.localeCompare(b)));
14
+ /**
15
+ * Deep, order-insensitive normalization for drift comparison. Plain-object keys are sorted
16
+ * recursively at every depth (array element order is preserved — arrays like `args` are
17
+ * order-significant). Without recursing into nested objects, two semantically identical
18
+ * server defs could compare unequal purely from key-insertion order — which genuinely
19
+ * happens: Zod's `.passthrough()` always places schema-declared keys before passthrough
20
+ * (unknown) keys in its output, e.g. Codex's `startup_timeout_sec` (not in McpServerSchema)
21
+ * ends up after `env` once a manifest round-trips through the schema, while the live
22
+ * TOML file has it before `env` in source order — a manifest reload would otherwise never
23
+ * converge to "in sync" for any Codex server combining an `env` table with a vendor field.
24
+ */
25
+ function sortKeys(value) {
26
+ if (Array.isArray(value))
27
+ return value.map(sortKeys);
28
+ if (value && typeof value === 'object') {
29
+ return Object.fromEntries(Object.entries(value)
30
+ .map(([k, v]) => [k, sortKeys(v)])
31
+ .sort(([a], [b]) => a.localeCompare(b)));
32
+ }
33
+ return value;
16
34
  }
17
35
  const SECRET_PREFIX = 'secret://';
18
36
  // exported so adopt.ts (buildManifest) can recognize pooled session-dir symlinks with the
package/dist/assets.js CHANGED
@@ -4,7 +4,13 @@ import { join, relative } from 'node:path';
4
4
  import { renderPath } from './platform.js';
5
5
  import { atomicWrite, backupFiles } from './fsutil.js';
6
6
  import { physicalLinkEntry } from './links.js';
7
- const HUB_DIRS = ['skills', 'commands', 'plugins'];
7
+ // Deliberately NOT 'plugins': plugin state syncs via the manifest's declarative
8
+ // `plugins[]`/`marketplaces{}` fields (already inside manifestYaml) plus a `claude plugin
9
+ // install` reconcile after sync/import — never by copying the plugins/ directory tree.
10
+ // That tree is marketplace git clones + installed plugin caches, routinely multi-GB; walking
11
+ // and JSON.stringify-ing it here blew past V8's max string length in sealJson on a real user's
12
+ // machine (their ~/.claude-oauth/plugins/ alone was 3.9 GB).
13
+ const HUB_DIRS = ['skills', 'commands'];
8
14
  function safeParts(rel) {
9
15
  if (rel.includes('\\') || rel.includes('\0'))
10
16
  throw new Error(`unsafe asset path: ${JSON.stringify(rel)}`);
@@ -95,8 +95,11 @@ export async function startSyncServer(deps) {
95
95
  }
96
96
  return send(res, 404, { error: 'not found' });
97
97
  }
98
- catch {
99
- // never leak internal error detail (paths, parse internals) to callers
98
+ catch (e) {
99
+ // never leak internal error detail (paths, parse internals) to callers — but log it
100
+ // locally (this process's own stderr, never sent over the wire) so whoever runs
101
+ // `ccprofiles serve` can actually diagnose the failure instead of a bare 500.
102
+ process.stderr.write(`ccprofiles serve error: ${e.stack ?? e}\n`);
100
103
  return send(res, 500, { error: 'internal error' });
101
104
  }
102
105
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccprofiles-core",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Core library for ccprofiles — discovery, manifest, secrets, apply, and LAN sync for Claude Code multi-account setups",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",