wendkeep 0.70.0 → 0.72.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/src/observer.mjs CHANGED
@@ -2,20 +2,24 @@ import { homedir } from 'node:os';
2
2
  import { isAbsolute, resolve } from 'node:path';
3
3
  import { appendObserverEvent, readObserverIndex, registerObserverProject } from './observer-store.mjs';
4
4
  import { buildProjectSnapshot } from './observer-snapshot.mjs';
5
+ import { compareMemoryParity } from './observer-memory-publish.mjs';
6
+ import { publishObserverSql } from './observer-sql-publish.mjs';
5
7
  import { startObserverServer } from './observer-server.mjs';
8
+ import { ensureObserverDatabase, migrateObserverDatabase, listSqlProjects, OBSERVER_SQL_FILE, OBSERVER_SQL_SCHEMA_VERSION } from './observer-sql-store.mjs';
6
9
  import { resolveProjectVault } from '../packages/vault/src/project-vault.mjs';
7
10
 
8
11
  export const OBSERVER_HELP = `wendkeep observer — Observer local multi-projeto
9
12
 
10
13
  Uso:
11
- wendkeep observer serve [--data-dir P] [--host 127.0.0.1] [--port 8787] [--token T]
14
+ wendkeep observer serve [--data-dir P] [--host 127.0.0.1] [--port 8787]
12
15
  [--allow-non-loopback]
13
16
  wendkeep observer register --project P [--vault V] [--data-dir D] [--json]
14
17
  wendkeep observer publish --project P [--vault V] [--data-dir D] [--json]
18
+ wendkeep observer memory import --project P [--vault V] [--url U] [--json]
15
19
  wendkeep observer status [--data-dir D] [--json]
16
20
 
17
- O vault continua local e é a fonte oficial. O Observer armazena apenas snapshots sanitizados
18
- e um índice reconstruível; o container não monta nem copia vaults de projetos.
21
+ O Observer local pode manter snapshots operacionais e uma cópia completa da memória em volume
22
+ Docker. O comando memory import faz a primeira migração de um vault para o container.
19
23
  `;
20
24
 
21
25
  function optionValue(argv, name) {
@@ -41,8 +45,8 @@ function vaultBase(argv, root) {
41
45
  return resolveProjectVault({ startDir: root }).base;
42
46
  }
43
47
 
44
- function print(value, asJson) {
45
- process.stdout.write(`${asJson ? JSON.stringify(value, null, 2) : String(value)}\n`);
48
+ function print(value, asJson, write = (chunk) => process.stdout.write(chunk)) {
49
+ write((asJson ? JSON.stringify(value, null, 2) : String(value)) + '\n');
46
50
  }
47
51
 
48
52
  function summary(index) {
@@ -52,7 +56,22 @@ function summary(index) {
52
56
  };
53
57
  }
54
58
 
55
- export async function runObserver(argv = []) {
59
+ function databaseSummary(dir) {
60
+ const db = ensureObserverDatabase(dir);
61
+ try {
62
+ const migrations = migrateObserverDatabase(db);
63
+ return {
64
+ engine: 'sqlite',
65
+ file: OBSERVER_SQL_FILE,
66
+ schema_version: OBSERVER_SQL_SCHEMA_VERSION,
67
+ migrations: migrations.applied.length,
68
+ projects: listSqlProjects(db).length,
69
+ ready: true,
70
+ };
71
+ } finally { db.close(); }
72
+ }
73
+
74
+ export async function runObserver(argv = [], { write = (chunk) => process.stdout.write(chunk) } = {}) {
56
75
  const [sub] = argv;
57
76
  const asJson = argv.includes('--json');
58
77
  if (!sub || sub === 'help') {
@@ -62,30 +81,67 @@ export async function runObserver(argv = []) {
62
81
  const dir = dataDir(argv);
63
82
 
64
83
  if (sub === 'status') {
65
- print(summary(readObserverIndex(dir)), asJson);
84
+ print({ ...summary(readObserverIndex(dir)), database: databaseSummary(dir) }, asJson, write);
66
85
  return 0;
67
86
  }
68
87
 
69
88
  if (sub === 'serve') {
70
- const token = optionValue(argv, '--token') || process.env.WENDKEEP_OBSERVER_TOKEN || '';
71
89
  const host = optionValue(argv, '--host') || '127.0.0.1';
72
90
  const server = await startObserverServer({
73
91
  dataDir: dir,
74
92
  host,
75
93
  port: Number(optionValue(argv, '--port') || 8787),
76
- token,
77
94
  allowNonLoopback: argv.includes('--allow-non-loopback'),
78
95
  });
79
- if (!token) {
80
- await server.close();
81
- throw new Error('Observer exige --token ou WENDKEEP_OBSERVER_TOKEN.');
82
- }
83
96
  const address = server.address();
84
97
  process.stdout.write(`wendkeep observer listening: http://${address.address}:${address.port}\n`);
85
98
  return 0;
86
99
  }
87
100
 
88
- if (!['register', 'publish'].includes(sub)) throw new Error(`observer: subcomando desconhecido: ${sub}`);
101
+ if (sub === 'memory') {
102
+ const action = argv[1] || '';
103
+ if (action !== 'import') throw new Error('observer memory: use memory import.');
104
+ const root = projectRoot(argv);
105
+ const vault = vaultBase(argv, root);
106
+ const snapshot = buildProjectSnapshot({ vaultBase: vault, projectRoot: root });
107
+ const url = optionValue(argv, '--url') || process.env.WENDKEEP_OBSERVER_URL || '';
108
+ if (!url) throw new Error('observer memory import: --url ou WENDKEEP_OBSERVER_URL é obrigatório.');
109
+ const headers = { 'content-type': 'application/json', accept: 'application/json' };
110
+ const registration = await fetch(
111
+ String(url).replace(/\/$/, '') + '/v1/projects/' + encodeURIComponent(snapshot.project_id),
112
+ {
113
+ method: 'PUT',
114
+ headers,
115
+ body: JSON.stringify({
116
+ project_id: snapshot.project_id,
117
+ project_name: snapshot.project_name,
118
+ wendkeep_version: snapshot.wendkeep_version,
119
+ }),
120
+ },
121
+ );
122
+ if (!registration.ok) throw new Error('Observer não registrou o projeto: HTTP ' + registration.status + '.');
123
+ const sql = await publishObserverSql({
124
+ vaultBase: vault,
125
+ projectId: snapshot.project_id,
126
+ url,
127
+ });
128
+ const parity = await compareMemoryParity({
129
+ vaultBase: vault,
130
+ projectId: snapshot.project_id,
131
+ url,
132
+ });
133
+ const result = {
134
+ ok: sql.ok && parity.missing === 0 && parity.mismatched === 0,
135
+ project_id: snapshot.project_id,
136
+ sql,
137
+ memory: { ...sql, authority: 'sqlite' },
138
+ parity,
139
+ };
140
+ print(result, asJson, write);
141
+ return result.ok ? 0 : 1;
142
+ }
143
+
144
+ if (!['register', 'publish'].includes(sub)) throw new Error('observer: subcomando desconhecido: ' + sub);
89
145
  const root = projectRoot(argv);
90
146
  const vault = vaultBase(argv, root);
91
147
  const snapshot = buildProjectSnapshot({ vaultBase: vault, projectRoot: root });
@@ -97,12 +153,12 @@ export async function runObserver(argv = []) {
97
153
  wendkeepVersion: snapshot.wendkeep_version,
98
154
  });
99
155
  if (!result.registered) throw new Error(result.errors.join(' '));
100
- print(result, asJson);
156
+ print(result, asJson, write);
101
157
  return 0;
102
158
  }
103
159
 
104
160
  const result = appendObserverEvent(dir, snapshot);
105
161
  if (!result.accepted && !result.duplicate) throw new Error(result.errors.join(' '));
106
- print({ ok: true, ...result }, asJson);
162
+ print({ ok: true, ...result }, asJson, write);
107
163
  return 0;
108
164
  }