wangchuan 2.10.0 → 2.12.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 (43) hide show
  1. package/dist/bin/wangchuan.js +19 -1
  2. package/dist/bin/wangchuan.js.map +1 -1
  3. package/dist/src/commands/doctor.d.ts +14 -0
  4. package/dist/src/commands/doctor.d.ts.map +1 -0
  5. package/dist/src/commands/doctor.js +204 -0
  6. package/dist/src/commands/doctor.js.map +1 -0
  7. package/dist/src/commands/history.d.ts +11 -0
  8. package/dist/src/commands/history.d.ts.map +1 -0
  9. package/dist/src/commands/history.js +40 -0
  10. package/dist/src/commands/history.js.map +1 -0
  11. package/dist/src/commands/pull.d.ts.map +1 -1
  12. package/dist/src/commands/pull.js +22 -1
  13. package/dist/src/commands/pull.js.map +1 -1
  14. package/dist/src/commands/push.d.ts.map +1 -1
  15. package/dist/src/commands/push.js +25 -0
  16. package/dist/src/commands/push.js.map +1 -1
  17. package/dist/src/commands/sync.d.ts.map +1 -1
  18. package/dist/src/commands/sync.js +27 -1
  19. package/dist/src/commands/sync.js.map +1 -1
  20. package/dist/src/core/sync-history.d.ts +20 -0
  21. package/dist/src/core/sync-history.d.ts.map +1 -0
  22. package/dist/src/core/sync-history.js +39 -0
  23. package/dist/src/core/sync-history.js.map +1 -0
  24. package/dist/src/core/sync-lock.d.ts +31 -0
  25. package/dist/src/core/sync-lock.d.ts.map +1 -0
  26. package/dist/src/core/sync-lock.js +95 -0
  27. package/dist/src/core/sync-lock.js.map +1 -0
  28. package/dist/src/core/sync.d.ts +15 -0
  29. package/dist/src/core/sync.d.ts.map +1 -1
  30. package/dist/src/core/sync.js +89 -0
  31. package/dist/src/core/sync.js.map +1 -1
  32. package/dist/src/i18n.d.ts.map +1 -1
  33. package/dist/src/i18n.js +42 -0
  34. package/dist/src/i18n.js.map +1 -1
  35. package/dist/src/utils/logger.d.ts +10 -1
  36. package/dist/src/utils/logger.d.ts.map +1 -1
  37. package/dist/src/utils/logger.js +43 -10
  38. package/dist/src/utils/logger.js.map +1 -1
  39. package/dist/test/sync-history.test.d.ts +5 -0
  40. package/dist/test/sync-history.test.d.ts.map +1 -0
  41. package/dist/test/sync-history.test.js +111 -0
  42. package/dist/test/sync-history.test.js.map +1 -0
  43. package/package.json +2 -2
@@ -1,6 +1,13 @@
1
1
  /**
2
- * logger.ts — 统一日志输出,支持着色与级别过滤
2
+ * logger.ts — Structured logging with level filtering, timestamps, and JSON output
3
+ *
4
+ * Supports:
5
+ * WANGCHUAN_LOG_LEVEL env: silent | error | warn | info | debug (default: info)
6
+ * WANGCHUAN_LOG_FORMAT env: json — outputs JSON lines for machine parsing
7
+ * Timestamp prefix in debug mode: [ISO8601] [LEVEL] message
8
+ * trace() method for very verbose output (only emitted in debug level)
3
9
  */
10
+ export type LogLevel = 'silent' | 'error' | 'warn' | 'info' | 'debug';
4
11
  export declare const logger: {
5
12
  readonly info: (...a: unknown[]) => void;
6
13
  readonly ok: (...a: unknown[]) => void;
@@ -8,6 +15,8 @@ export declare const logger: {
8
15
  readonly error: (...a: unknown[]) => void;
9
16
  readonly debug: (...a: unknown[]) => void;
10
17
  readonly step: (...a: unknown[]) => void;
18
+ /** Very verbose tracing — only emitted when log level is debug */
19
+ readonly trace: (...a: unknown[]) => void;
11
20
  readonly banner: (text: string) => void;
12
21
  };
13
22
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyBH,eAAO,MAAM,MAAM;0BACH,OAAO,EAAE;wBACT,OAAO,EAAE;0BACT,OAAO,EAAE;2BACT,OAAO,EAAE;2BACT,OAAO,EAAE;0BACT,OAAO,EAAE;4BAEV,MAAM,KAAG,IAAI;CAKlB,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AA4CtE,eAAO,MAAM,MAAM;0BACH,OAAO,EAAE;wBACT,OAAO,EAAE;0BACT,OAAO,EAAE;2BACT,OAAO,EAAE;2BACT,OAAO,EAAE;0BACT,OAAO,EAAE;IAEvB,kEAAkE;2BACpD,OAAO,EAAE;4BAEV,MAAM,KAAG,IAAI;CAUlB,CAAC"}
@@ -1,21 +1,46 @@
1
1
  /**
2
- * logger.ts — 统一日志输出,支持着色与级别过滤
2
+ * logger.ts — Structured logging with level filtering, timestamps, and JSON output
3
+ *
4
+ * Supports:
5
+ * WANGCHUAN_LOG_LEVEL env: silent | error | warn | info | debug (default: info)
6
+ * WANGCHUAN_LOG_FORMAT env: json — outputs JSON lines for machine parsing
7
+ * Timestamp prefix in debug mode: [ISO8601] [LEVEL] message
8
+ * trace() method for very verbose output (only emitted in debug level)
3
9
  */
4
10
  import chalk from 'chalk';
5
- const LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
6
- const rawLevel = process.env['WANGCHUAN_LOG_LEVEL'] ?? 'info';
11
+ const LEVELS = { silent: -1, error: 3, warn: 2, info: 1, debug: 0 };
12
+ const rawLevel = (process.env['WANGCHUAN_LOG_LEVEL'] ?? 'info');
7
13
  const currentLevel = LEVELS[rawLevel] ?? LEVELS.info;
14
+ const isJsonFormat = process.env['WANGCHUAN_LOG_FORMAT'] === 'json';
15
+ const isDebugLevel = currentLevel === LEVELS.debug;
8
16
  const prefix = {
9
- info: chalk.cyan(''),
10
- ok: chalk.green(''),
11
- warn: chalk.yellow(''),
12
- error: chalk.red(''),
13
- debug: chalk.gray('·'),
14
- step: chalk.blue(''),
17
+ info: chalk.cyan('\u2139'),
18
+ ok: chalk.green('\u2714'),
19
+ warn: chalk.yellow('\u26A0'),
20
+ error: chalk.red('\u2716'),
21
+ debug: chalk.gray('\u00B7'),
22
+ step: chalk.blue('\u203A'),
23
+ trace: chalk.gray('\u2026'),
15
24
  };
25
+ /** Emit a JSON line to stdout */
26
+ function emitJson(level, args) {
27
+ const msg = args.map(a => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ');
28
+ const line = JSON.stringify({ level, msg, ts: new Date().toISOString() });
29
+ console.log(line);
30
+ }
16
31
  function log(level, symbol, ...args) {
17
32
  if (LEVELS[level] < currentLevel)
18
33
  return;
34
+ if (isJsonFormat) {
35
+ emitJson(level, args);
36
+ return;
37
+ }
38
+ if (isDebugLevel) {
39
+ const ts = chalk.gray(`[${new Date().toISOString()}]`);
40
+ const tag = chalk.gray(`[${level.toUpperCase()}]`);
41
+ console.log(ts, tag, symbol, ...args);
42
+ return;
43
+ }
19
44
  console.log(symbol, ...args);
20
45
  }
21
46
  export const logger = {
@@ -25,9 +50,17 @@ export const logger = {
25
50
  error: (...a) => log('error', prefix['error'], chalk.red(String(a[0])), ...a.slice(1)),
26
51
  debug: (...a) => log('debug', prefix['debug'], chalk.gray(String(a[0])), ...a.slice(1)),
27
52
  step: (...a) => log('info', prefix['step'], ...a),
53
+ /** Very verbose tracing — only emitted when log level is debug */
54
+ trace: (...a) => log('debug', prefix['trace'], chalk.gray(String(a[0])), ...a.slice(1)),
28
55
  banner(text) {
56
+ if (currentLevel === LEVELS.silent)
57
+ return;
58
+ if (isJsonFormat) {
59
+ emitJson('info', [`\u2554\u2550\u2550 ${text} \u2550\u2550\u2557`]);
60
+ return;
61
+ }
29
62
  console.log();
30
- console.log(chalk.bold.cyan(` ╔══ ${text} ══╗`));
63
+ console.log(chalk.bold.cyan(` \u2554\u2550\u2550 ${text} \u2550\u2550\u2557`));
31
64
  console.log();
32
65
  },
33
66
  };
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,MAAM,MAAM,GAA6B,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AAElF,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,MAAM,CAAC;AAC9D,MAAM,YAAY,GAAW,MAAM,CAAC,QAAoB,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC;AAEzE,MAAM,MAAM,GAA2B;IACrC,IAAI,EAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACtB,EAAE,EAAK,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC;IACvB,IAAI,EAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;IACrB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACtB,IAAI,EAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;CACvB,CAAC;AAEF,SAAS,GAAG,CAAC,KAAe,EAAE,MAAc,EAAE,GAAG,IAAe;IAC9D,IAAI,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY;QAAE,OAAO;IACzC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,EAAG,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAG,MAAM,CAAC,MAAM,CAAE,EAAG,GAAG,CAAC,CAAC;IAChE,EAAE,EAAK,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAG,MAAM,CAAC,IAAI,CAAE,EAAK,GAAG,CAAC,CAAC;IAChE,IAAI,EAAG,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAG,MAAM,CAAC,MAAM,CAAE,EAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrG,KAAK,EAAE,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAE,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrG,KAAK,EAAE,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAE,EAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACrG,IAAI,EAAG,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAG,MAAM,CAAC,MAAM,CAAE,EAAG,GAAG,CAAC,CAAC;IAEhE,MAAM,CAAC,IAAY;QACjB,OAAO,CAAC,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC,CAAC;QAClD,OAAO,CAAC,GAAG,EAAE,CAAC;IAChB,CAAC;CACO,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../../src/utils/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * sync-history.test.ts — Tests for sync-history and ignore patterns
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=sync-history.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-history.test.d.ts","sourceRoot":"","sources":["../../test/sync-history.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * sync-history.test.ts — Tests for sync-history and ignore patterns
3
+ */
4
+ import { describe, it, after } from 'node:test';
5
+ import assert from 'node:assert/strict';
6
+ import fs from 'fs';
7
+ import os from 'os';
8
+ import path from 'path';
9
+ import { readSyncHistory, appendSyncEvent } from '../src/core/sync-history.js';
10
+ import { matchesIgnore } from '../src/core/sync.js';
11
+ // ── Test fixtures ────────────────────────────────────────────────────
12
+ const TMP = fs.mkdtempSync(path.join(os.tmpdir(), 'wc-history-'));
13
+ after(() => {
14
+ fs.rmSync(TMP, { recursive: true, force: true });
15
+ });
16
+ function mkEvent(overrides) {
17
+ return {
18
+ timestamp: new Date().toISOString(),
19
+ action: 'push',
20
+ environment: 'default',
21
+ fileCount: 5,
22
+ encrypted: 2,
23
+ hostname: 'test-host',
24
+ ...overrides,
25
+ };
26
+ }
27
+ // ── sync-history tests ───────────────────────────────────────────────
28
+ describe('sync-history', () => {
29
+ it('appendSyncEvent + readSyncHistory round-trip', () => {
30
+ const historyPath = path.join(TMP, 'history-roundtrip.json');
31
+ const event = mkEvent({ action: 'pull', fileCount: 10 });
32
+ appendSyncEvent(event, historyPath);
33
+ const events = readSyncHistory(historyPath);
34
+ assert.equal(events.length, 1);
35
+ assert.equal(events[0].action, 'pull');
36
+ assert.equal(events[0].fileCount, 10);
37
+ assert.equal(events[0].hostname, 'test-host');
38
+ });
39
+ it('multiple appends accumulate in order', () => {
40
+ const historyPath = path.join(TMP, 'history-multi.json');
41
+ appendSyncEvent(mkEvent({ action: 'push', fileCount: 1 }), historyPath);
42
+ appendSyncEvent(mkEvent({ action: 'pull', fileCount: 2 }), historyPath);
43
+ appendSyncEvent(mkEvent({ action: 'sync', fileCount: 3 }), historyPath);
44
+ const events = readSyncHistory(historyPath);
45
+ assert.equal(events.length, 3);
46
+ assert.equal(events[0].action, 'push');
47
+ assert.equal(events[1].action, 'pull');
48
+ assert.equal(events[2].action, 'sync');
49
+ });
50
+ it('FIFO rotation: 105 events → only 100 remain', () => {
51
+ const historyPath = path.join(TMP, 'history-fifo.json');
52
+ for (let i = 0; i < 105; i++) {
53
+ appendSyncEvent(mkEvent({ fileCount: i }), historyPath);
54
+ }
55
+ const events = readSyncHistory(historyPath);
56
+ assert.equal(events.length, 100);
57
+ // First event should be the 6th one (index 5), since first 5 were evicted
58
+ assert.equal(events[0].fileCount, 5);
59
+ // Last event should be index 104
60
+ assert.equal(events[99].fileCount, 104);
61
+ });
62
+ it('readSyncHistory returns empty array for missing file', () => {
63
+ const events = readSyncHistory(path.join(TMP, 'nonexistent.json'));
64
+ assert.deepStrictEqual(events, []);
65
+ });
66
+ it('readSyncHistory returns empty array for corrupted file', () => {
67
+ const historyPath = path.join(TMP, 'history-corrupt.json');
68
+ fs.writeFileSync(historyPath, '{ not valid json array!!!', 'utf-8');
69
+ const events = readSyncHistory(historyPath);
70
+ assert.deepStrictEqual(events, []);
71
+ });
72
+ it('optional fields (agent, sha) round-trip correctly', () => {
73
+ const historyPath = path.join(TMP, 'history-optional.json');
74
+ appendSyncEvent(mkEvent({ agent: 'claude', sha: 'abc123' }), historyPath);
75
+ const events = readSyncHistory(historyPath);
76
+ assert.equal(events[0].agent, 'claude');
77
+ assert.equal(events[0].sha, 'abc123');
78
+ });
79
+ });
80
+ // ── matchesIgnore tests ──────────────────────────────────────────────
81
+ describe('matchesIgnore', () => {
82
+ it('matches simple wildcard pattern against basename', () => {
83
+ assert.ok(matchesIgnore('some/dir/file.tmp', ['*.tmp']));
84
+ assert.ok(matchesIgnore('file.log', ['*.log']));
85
+ assert.ok(!matchesIgnore('file.md', ['*.tmp']));
86
+ });
87
+ it('matches exact filename pattern', () => {
88
+ assert.ok(matchesIgnore('some/path/.DS_Store', ['.DS_Store']));
89
+ assert.ok(!matchesIgnore('some/path/file.txt', ['.DS_Store']));
90
+ });
91
+ it('matches ** patterns against full relative path', () => {
92
+ assert.ok(matchesIgnore('a/node_modules/pkg/index.js', ['**/node_modules/**']));
93
+ assert.ok(matchesIgnore('node_modules/pkg/index.js', ['**/node_modules/**']));
94
+ assert.ok(!matchesIgnore('a/other/pkg/index.js', ['**/node_modules/**']));
95
+ });
96
+ it('matches path patterns with /', () => {
97
+ assert.ok(matchesIgnore('build/output.js', ['build/*.js']));
98
+ assert.ok(!matchesIgnore('src/output.js', ['build/*.js']));
99
+ });
100
+ it('returns false for empty patterns list', () => {
101
+ assert.ok(!matchesIgnore('anything.txt', []));
102
+ });
103
+ it('matches multiple patterns (any match → true)', () => {
104
+ const patterns = ['*.tmp', '*.log', '.DS_Store'];
105
+ assert.ok(matchesIgnore('debug.log', patterns));
106
+ assert.ok(matchesIgnore('cache.tmp', patterns));
107
+ assert.ok(matchesIgnore('.DS_Store', patterns));
108
+ assert.ok(!matchesIgnore('readme.md', patterns));
109
+ });
110
+ });
111
+ //# sourceMappingURL=sync-history.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-history.test.js","sourceRoot":"","sources":["../../test/sync-history.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wangchuan",
3
- "version": "2.10.0",
3
+ "version": "2.12.0",
4
4
  "description": "忘川 · AI 记忆同步系统 — 智能体记忆永不遗失",
5
5
  "bin": {
6
6
  "wangchuan": "./dist/bin/wangchuan.js"
@@ -17,7 +17,7 @@
17
17
  "postbuild": "chmod +x dist/bin/wangchuan.js",
18
18
  "prepublishOnly": "npm run build",
19
19
  "dev": "tsx bin/wangchuan.ts",
20
- "test": "node --import tsx/esm --test test/crypto.test.ts test/json-field.test.ts test/sync.test.ts",
20
+ "test": "node --import tsx/esm --test test/crypto.test.ts test/json-field.test.ts test/sync.test.ts test/sync-history.test.ts",
21
21
  "typecheck": "tsc --noEmit"
22
22
  },
23
23
  "keywords": [