@nuucognition/flint-cli 0.2.0-beta.2 → 0.3.0-alpha.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.
@@ -0,0 +1,303 @@
1
+ import {
2
+ generateConnectionMetadata,
3
+ getConnectionMetadataPath,
4
+ getConnectionsMetadataDir,
5
+ getFlintConfigDir
6
+ } from "./chunk-KVE7WLA2.js";
7
+ import {
8
+ readFlintToml,
9
+ writeFlintToml
10
+ } from "./chunk-KLFK7CPM.js";
11
+ import {
12
+ findFlintByName
13
+ } from "./chunk-OIXBMYAA.js";
14
+
15
+ // ../../packages/flint/dist/chunk-AUWQSMUZ.js
16
+ import { readFile, writeFile, mkdir, access, rm } from "fs/promises";
17
+ import { join as join2 } from "path";
18
+ import { stat } from "fs/promises";
19
+ import { dirname, join } from "path";
20
+ async function findFlintRoot(startPath) {
21
+ let current = startPath;
22
+ if (!current.startsWith("/")) {
23
+ current = join(process.cwd(), current);
24
+ }
25
+ while (current !== dirname(current)) {
26
+ const flintConfigDir = join(current, ".flint");
27
+ const flintToml = join(current, "flint.toml");
28
+ try {
29
+ const configStat = await stat(flintConfigDir);
30
+ if (configStat.isDirectory()) {
31
+ return current;
32
+ }
33
+ } catch {
34
+ }
35
+ try {
36
+ const tomlStat = await stat(flintToml);
37
+ if (tomlStat.isFile()) {
38
+ return current;
39
+ }
40
+ } catch {
41
+ }
42
+ current = dirname(current);
43
+ }
44
+ return null;
45
+ }
46
+ async function isInsideFlint(path) {
47
+ const flintRoot = await findFlintRoot(path);
48
+ return flintRoot !== null;
49
+ }
50
+ function getConnectionsDir(flintPath) {
51
+ return getConnectionsMetadataDir(flintPath);
52
+ }
53
+ function getConnectionDir(flintPath, _name) {
54
+ return getConnectionsMetadataDir(flintPath);
55
+ }
56
+ function getConnectionsStatePath(flintPath) {
57
+ return join2(getFlintConfigDir(flintPath), "connections.json");
58
+ }
59
+ function getConnectionFilePath(flintPath, name) {
60
+ return getConnectionMetadataPath(flintPath, name);
61
+ }
62
+ async function readConnectionsState(flintPath) {
63
+ try {
64
+ const content = await readFile(getConnectionsStatePath(flintPath), "utf-8");
65
+ return JSON.parse(content);
66
+ } catch {
67
+ return { version: 1, connections: [] };
68
+ }
69
+ }
70
+ async function writeConnectionsState(flintPath, state) {
71
+ const statePath = getConnectionsStatePath(flintPath);
72
+ await mkdir(getFlintConfigDir(flintPath), { recursive: true });
73
+ await writeFile(statePath, JSON.stringify(state, null, 2));
74
+ }
75
+ async function getConnectionDeclarations(flintPath) {
76
+ const config = await readFlintToml(flintPath);
77
+ return config?.connections?.flints || [];
78
+ }
79
+ async function addConnectionDeclaration(flintPath, name) {
80
+ const registeredFlint = await findFlintByName(name);
81
+ if (!registeredFlint) {
82
+ throw new Error(
83
+ `"${name}" is not a registered Flint. Use 'flint list' to see registered Flints, or 'flint register <path>' to register it first.`
84
+ );
85
+ }
86
+ const config = await readFlintToml(flintPath);
87
+ if (!config) {
88
+ throw new Error("Could not read flint.toml");
89
+ }
90
+ if (!config.connections) {
91
+ config.connections = { flints: [] };
92
+ }
93
+ if (!config.connections.flints) {
94
+ config.connections.flints = [];
95
+ }
96
+ const existing = config.connections.flints.find(
97
+ (c) => c.name.toLowerCase() === name.toLowerCase()
98
+ );
99
+ if (existing) {
100
+ throw new Error(`Connection to "${name}" is already declared`);
101
+ }
102
+ config.connections.flints.push({ name });
103
+ await writeFlintToml(flintPath, config);
104
+ }
105
+ async function removeConnectionDeclaration(flintPath, name) {
106
+ const config = await readFlintToml(flintPath);
107
+ if (!config?.connections?.flints) return false;
108
+ const normalizedName = name.toLowerCase();
109
+ const index = config.connections.flints.findIndex(
110
+ (c) => c.name.toLowerCase() === normalizedName
111
+ );
112
+ if (index === -1) return false;
113
+ config.connections.flints.splice(index, 1);
114
+ if (config.connections.flints.length === 0) {
115
+ delete config.connections.flints;
116
+ }
117
+ if (Object.keys(config.connections).length === 0) {
118
+ delete config.connections;
119
+ }
120
+ await writeFlintToml(flintPath, config);
121
+ return true;
122
+ }
123
+ async function fulfillConnection(flintPath, name, targetPath) {
124
+ const declarations = await getConnectionDeclarations(flintPath);
125
+ const declaration = declarations.find(
126
+ (d) => d.name.toLowerCase() === name.toLowerCase()
127
+ );
128
+ if (!declaration) {
129
+ throw new Error(
130
+ `Connection "${name}" is not declared. Use 'flint connection add "${name}"' first.`
131
+ );
132
+ }
133
+ let resolvedPath = targetPath;
134
+ if (!resolvedPath) {
135
+ const registeredFlint = await findFlintByName(name);
136
+ if (!registeredFlint) {
137
+ throw new Error(
138
+ `"${name}" is not in the registry. Provide a path explicitly or register the Flint first.`
139
+ );
140
+ }
141
+ resolvedPath = registeredFlint.path;
142
+ }
143
+ const isFlint = await isInsideFlint(resolvedPath);
144
+ if (!isFlint) {
145
+ throw new Error(`Target is not a valid Flint: ${resolvedPath}`);
146
+ }
147
+ const fulfillment = {
148
+ name: declaration.name,
149
+ // Use declared name (proper case)
150
+ path: resolvedPath,
151
+ fulfilled: (/* @__PURE__ */ new Date()).toISOString()
152
+ };
153
+ const state = await readConnectionsState(flintPath);
154
+ const existingIndex = state.connections.findIndex(
155
+ (c) => c.name.toLowerCase() === name.toLowerCase()
156
+ );
157
+ if (existingIndex >= 0) {
158
+ state.connections[existingIndex] = fulfillment;
159
+ } else {
160
+ state.connections.push(fulfillment);
161
+ }
162
+ await writeConnectionsState(flintPath, state);
163
+ return fulfillment;
164
+ }
165
+ async function unfulfillConnection(flintPath, name) {
166
+ const normalizedName = name.toLowerCase();
167
+ const state = await readConnectionsState(flintPath);
168
+ const index = state.connections.findIndex(
169
+ (c) => c.name.toLowerCase() === normalizedName
170
+ );
171
+ if (index >= 0) {
172
+ state.connections.splice(index, 1);
173
+ await writeConnectionsState(flintPath, state);
174
+ return true;
175
+ }
176
+ return false;
177
+ }
178
+ async function getConnectionStatus(flintPath) {
179
+ const declarations = await getConnectionDeclarations(flintPath);
180
+ const state = await readConnectionsState(flintPath);
181
+ const results = [];
182
+ for (const declaration of declarations) {
183
+ const fulfillment = state.connections.find(
184
+ (c) => c.name.toLowerCase() === declaration.name.toLowerCase()
185
+ );
186
+ const status = {
187
+ name: declaration.name,
188
+ declared: true,
189
+ fulfilled: !!fulfillment
190
+ };
191
+ if (fulfillment) {
192
+ status.path = fulfillment.path;
193
+ status.fulfilledAt = fulfillment.fulfilled;
194
+ try {
195
+ await access(fulfillment.path);
196
+ const isFlint = await isInsideFlint(fulfillment.path);
197
+ status.valid = isFlint;
198
+ } catch {
199
+ status.valid = false;
200
+ }
201
+ }
202
+ results.push(status);
203
+ }
204
+ return results;
205
+ }
206
+ async function getConnection(flintPath, name) {
207
+ const statuses = await getConnectionStatus(flintPath);
208
+ return statuses.find((s) => s.name.toLowerCase() === name.toLowerCase()) || null;
209
+ }
210
+ async function listConnections(flintPath) {
211
+ return getConnectionStatus(flintPath);
212
+ }
213
+ async function getUnfulfilledConnections(flintPath) {
214
+ const statuses = await getConnectionStatus(flintPath);
215
+ return statuses.filter((s) => !s.fulfilled).map((s) => ({ name: s.name }));
216
+ }
217
+ async function addConnection(flintPath, name, autoFulfill = true) {
218
+ await addConnectionDeclaration(flintPath, name);
219
+ await generateConnectionMetadata(flintPath, { name });
220
+ if (autoFulfill) {
221
+ try {
222
+ await fulfillConnection(flintPath, name);
223
+ } catch {
224
+ }
225
+ }
226
+ const status = await getConnection(flintPath, name);
227
+ if (!status) {
228
+ throw new Error("Failed to add connection");
229
+ }
230
+ return status;
231
+ }
232
+ async function removeConnection(flintPath, name) {
233
+ await unfulfillConnection(flintPath, name);
234
+ try {
235
+ const metadataPath = getConnectionMetadataPath(flintPath, name);
236
+ await rm(metadataPath);
237
+ } catch {
238
+ }
239
+ return removeConnectionDeclaration(flintPath, name);
240
+ }
241
+ async function migrateConnections(flintPath) {
242
+ const result = { migrated: 0, errors: [] };
243
+ try {
244
+ const oldConfigPath = getConnectionsStatePath(flintPath);
245
+ const oldContent = await readFile(oldConfigPath, "utf-8");
246
+ const oldConfig = JSON.parse(oldContent);
247
+ if (!oldConfig.connections || oldConfig.connections.length === 0) {
248
+ return result;
249
+ }
250
+ const firstConnection = oldConfig.connections[0];
251
+ if (!firstConnection.path) {
252
+ return result;
253
+ }
254
+ for (const oldEntry of oldConfig.connections) {
255
+ try {
256
+ await addConnectionDeclaration(flintPath, oldEntry.name);
257
+ await generateConnectionMetadata(flintPath, { name: oldEntry.name });
258
+ await fulfillConnection(flintPath, oldEntry.name, oldEntry.path);
259
+ const oldConnectionDir = join2(flintPath, "Connections", oldEntry.name);
260
+ const oldFolders = ["inbox", "outbox", "mirror"];
261
+ for (const folder of oldFolders) {
262
+ try {
263
+ await rm(join2(oldConnectionDir, folder), { recursive: true });
264
+ } catch {
265
+ }
266
+ }
267
+ try {
268
+ await rm(join2(oldConnectionDir, "connection.md"));
269
+ } catch {
270
+ }
271
+ result.migrated++;
272
+ } catch (err) {
273
+ result.errors.push(
274
+ `Failed to migrate "${oldEntry.name}": ${err instanceof Error ? err.message : String(err)}`
275
+ );
276
+ }
277
+ }
278
+ } catch {
279
+ }
280
+ return result;
281
+ }
282
+
283
+ export {
284
+ findFlintRoot,
285
+ getConnectionsDir,
286
+ getConnectionDir,
287
+ getConnectionsStatePath,
288
+ getConnectionFilePath,
289
+ readConnectionsState,
290
+ writeConnectionsState,
291
+ getConnectionDeclarations,
292
+ addConnectionDeclaration,
293
+ removeConnectionDeclaration,
294
+ fulfillConnection,
295
+ unfulfillConnection,
296
+ getConnectionStatus,
297
+ getConnection,
298
+ listConnections,
299
+ getUnfulfilledConnections,
300
+ addConnection,
301
+ removeConnection,
302
+ migrateConnections
303
+ };
@@ -0,0 +1,43 @@
1
+ import {
2
+ addConnection,
3
+ addConnectionDeclaration,
4
+ fulfillConnection,
5
+ getConnection,
6
+ getConnectionDeclarations,
7
+ getConnectionDir,
8
+ getConnectionFilePath,
9
+ getConnectionStatus,
10
+ getConnectionsDir,
11
+ getConnectionsStatePath,
12
+ getUnfulfilledConnections,
13
+ listConnections,
14
+ migrateConnections,
15
+ readConnectionsState,
16
+ removeConnection,
17
+ removeConnectionDeclaration,
18
+ unfulfillConnection,
19
+ writeConnectionsState
20
+ } from "./chunk-T7VT4BN3.js";
21
+ import "./chunk-KVE7WLA2.js";
22
+ import "./chunk-KLFK7CPM.js";
23
+ import "./chunk-OIXBMYAA.js";
24
+ export {
25
+ addConnection,
26
+ addConnectionDeclaration,
27
+ fulfillConnection,
28
+ getConnection,
29
+ getConnectionDeclarations,
30
+ getConnectionDir,
31
+ getConnectionFilePath,
32
+ getConnectionStatus,
33
+ getConnectionsDir,
34
+ getConnectionsStatePath,
35
+ getUnfulfilledConnections,
36
+ listConnections,
37
+ migrateConnections,
38
+ readConnectionsState,
39
+ removeConnection,
40
+ removeConnectionDeclaration,
41
+ unfulfillConnection,
42
+ writeConnectionsState
43
+ };
@@ -5,7 +5,7 @@ import {
5
5
  cleanupStaleExports,
6
6
  scanExportEligible,
7
7
  scanExports
8
- } from "./chunk-K5QP4OME.js";
8
+ } from "./chunk-ODSWKVNZ.js";
9
9
  export {
10
10
  buildAllExports,
11
11
  buildExport,