@feltdb/core 0.6.3 → 0.6.4

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.
@@ -8,7 +8,7 @@ import net from 'net';
8
8
  import { createRequire } from 'module';
9
9
  import { spawn, spawnSync } from 'child_process';
10
10
  import { createFeltDB, diffFlowSpec, formatFlowSpec, parseFlowSpec, planFlowSpecMigration, validateFlowSpec } from '@feltdb/core';
11
- import { discoverWorkspace, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace } from './workspace-integration.js';
11
+ import { discoverWorkspace, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace, startPairingDiscoveryServer } from './workspace-integration.js';
12
12
  function loadProjectEnvironment(file = path.resolve('.env.local')) {
13
13
  if (!fs.existsSync(file))
14
14
  return;
@@ -488,7 +488,7 @@ async function handleDev(args) {
488
488
  ? process.env.VITE_FELTDB_MANAGED_NAMESPACE || config.namespace
489
489
  : config.namespace;
490
490
  const requestedAppPort = Number(args.includes('--port') ? args[args.indexOf('--port') + 1] || '5173' : '5173');
491
- const requestedStudioPort = Number(args.includes('--studio-port') ? args[args.indexOf('--studio-port') + 1] || '3000' : '3000');
491
+ const requestedStudioPort = Number(args.includes('--studio-port') ? args[args.indexOf('--studio-port') + 1] || '7701' : '7701');
492
492
  const appPort = String(await availablePort(requestedAppPort));
493
493
  const studioPort = String(await availablePort(requestedStudioPort));
494
494
  if (appPort !== String(requestedAppPort))
@@ -522,11 +522,19 @@ async function handleDev(args) {
522
522
  // Generate pairing token for the workspace
523
523
  // This enables browsers and IDEs to discover and connect to the workspace
524
524
  let pairingToken = null;
525
+ let pairingDiscoveryServer = null;
525
526
  if (workspace) {
526
527
  const token = generatePairingToken();
527
528
  token.workspaceId = workspace.workspaceId;
528
529
  token.endpoint = process.env.VITE_FELTDB_URL;
530
+ token.authorityEndpoint = token.endpoint;
531
+ token.namespace = runtimeNamespace || config.namespace;
532
+ if (!token.endpoint) {
533
+ throw new Error('Development Workspace pairing requires a FeltDB authority endpoint');
534
+ }
529
535
  persistPairingToken(process.cwd(), token);
536
+ const discoveryPort = Number(args.includes('--discovery-port') ? args[args.indexOf('--discovery-port') + 1] || '7799' : '7799');
537
+ pairingDiscoveryServer = await startPairingDiscoveryServer(token, discoveryPort);
530
538
  pairingToken = token.token;
531
539
  }
532
540
  console.log('FeltDB Dev Server');
@@ -539,6 +547,7 @@ async function handleDev(args) {
539
547
  console.log(`🔗 Development Workspace`);
540
548
  if (pairingToken) {
541
549
  console.log(` Pairing Code: ${pairingToken}`);
550
+ console.log(` Discovery: http://127.0.0.1:${(pairingDiscoveryServer?.address()).port}`);
542
551
  console.log(` Clients can use this code to auto-discover the workspace`);
543
552
  }
544
553
  }
@@ -551,7 +560,7 @@ async function handleDev(args) {
551
560
  let shuttingDown = false;
552
561
  const stopVite = () => { if (!vite.killed)
553
562
  vite.kill('SIGTERM'); };
554
- const stopAll = () => { shuttingDown = true; stopVite(); stopSelfHosted(); };
563
+ const stopAll = () => { shuttingDown = true; stopVite(); pairingDiscoveryServer?.close(); stopSelfHosted(); };
555
564
  // In an inherited terminal Ctrl-C can reach Vite before this parent process.
556
565
  // Never leave Studio (or its port) running after the application exits.
557
566
  vite.once('exit', code => {
@@ -817,8 +826,8 @@ async function handleStudio(args) {
817
826
  ? args[args.indexOf('--connect') + 1]
818
827
  : undefined;
819
828
  const port = args.includes('--port')
820
- ? args[args.indexOf('--port') + 1] || '3000'
821
- : '3000';
829
+ ? args[args.indexOf('--port') + 1] || '7701'
830
+ : '7701';
822
831
  const host = args.includes('--host')
823
832
  ? args[args.indexOf('--host') + 1] || '127.0.0.1'
824
833
  : '127.0.0.1';
@@ -919,7 +928,7 @@ Commands:
919
928
  Studio:
920
929
  feltdb studio Launch local Studio
921
930
  feltdb studio --connect <url> Connect to remote instance
922
- feltdb studio --port 3000 --no-open Start on port 3000 without opening browser
931
+ feltdb studio --port 7701 --no-open Start on port 7701 without opening browser
923
932
 
924
933
  Server Options:
925
934
  feltdb server [--port 7700] [--data ./data] [--auth]
package/dist/cli/index.js CHANGED
@@ -23,7 +23,7 @@ import * as path from 'path';
23
23
  import * as readline from 'readline';
24
24
  import { getClient } from './api-client.js';
25
25
  import { loadFeltDBConfig, createDefaultConfig, validateModel, } from './config.js';
26
- const VERSION = '0.6.3';
26
+ const VERSION = '0.6.4';
27
27
  function prompt(question) {
28
28
  const rl = readline.createInterface({
29
29
  input: process.stdin,
@@ -10,6 +10,43 @@
10
10
  import fs from 'fs';
11
11
  import path from 'path';
12
12
  import { randomBytes } from 'crypto';
13
+ import http from 'http';
14
+ export function startPairingDiscoveryServer(token, port = 7799, host = '127.0.0.1') {
15
+ const authorityEndpoint = token.authorityEndpoint || token.endpoint;
16
+ if (!token.workspaceId || !authorityEndpoint || !token.namespace) {
17
+ throw new Error('Pairing discovery requires workspaceId, endpoint, and namespace');
18
+ }
19
+ const route = `/api/v1/development/pairing/${encodeURIComponent(token.token)}`;
20
+ const server = http.createServer((request, response) => {
21
+ response.setHeader('Access-Control-Allow-Origin', '*');
22
+ response.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
23
+ response.setHeader('Access-Control-Allow-Headers', 'Accept, Content-Type');
24
+ response.setHeader('Cache-Control', 'no-store');
25
+ if (request.method === 'OPTIONS') {
26
+ response.statusCode = 204;
27
+ response.end();
28
+ return;
29
+ }
30
+ const pathname = new URL(request.url || '/', `http://${host}`).pathname;
31
+ if (request.method !== 'GET' || pathname !== route || Date.now() >= token.expiresAt) {
32
+ response.statusCode = 404;
33
+ response.setHeader('Content-Type', 'application/json; charset=utf-8');
34
+ response.end(JSON.stringify({ error: 'PAIRING_CODE_NOT_FOUND' }));
35
+ return;
36
+ }
37
+ response.setHeader('Content-Type', 'application/json; charset=utf-8');
38
+ response.end(JSON.stringify({
39
+ workspaceId: token.workspaceId,
40
+ endpoint: authorityEndpoint,
41
+ expiresAt: token.expiresAt,
42
+ namespace: token.namespace,
43
+ }));
44
+ });
45
+ return new Promise((resolve, reject) => {
46
+ server.once('error', reject);
47
+ server.listen(port, host, () => resolve(server));
48
+ });
49
+ }
13
50
  /**
14
51
  * Discover workspace identity from .feltdb/workspace.json
15
52
  * Returns null if workspace is not initialized
@@ -1234,7 +1234,7 @@ NODE_ENV=development
1234
1234
  framework,
1235
1235
  port: 7700,
1236
1236
  replicationPort: 7701,
1237
- studioPort: 3000,
1237
+ studioPort: 7701,
1238
1238
  };
1239
1239
  fs.writeFileSync(path.join(projectDir, 'docker-compose.yml'), generateDockerCompose(dockerConfig));
1240
1240
  fs.writeFileSync(path.join(projectDir, 'Dockerfile'), generateDockerfile(dockerConfig, framework));
@@ -1,4 +1,4 @@
1
1
  // One release train keeps generated applications installable. The repository
2
2
  // validation script checks these values against every workspace manifest.
3
- export const FELTDB_PACKAGE_VERSION = '0.6.3';
3
+ export const FELTDB_PACKAGE_VERSION = '0.6.4';
4
4
  export const feltdbPackageRange = `^${FELTDB_PACKAGE_VERSION}`;
@@ -12,7 +12,7 @@
12
12
  * to the same shared development workspace.
13
13
  */
14
14
  export type { DevelopmentWorkspace, ClientType, WorkspaceEventPayload, DevelopmentTask, CodeChange, VerificationResult, SourceLocation, WorkspaceClient, WorkspaceDiscovery, } from './workspace-types.js';
15
- export type { WorkspaceConnectionOptions, WorkspaceMetadata, PairingTokenData, ClientInfo, } from './workspace-connection.js';
15
+ export type { WorkspaceConnectionOptions, WorkspaceMetadata, PairingTokenData, BrowserPairingResolution, PairingDiscoveryOptions, ClientInfo, } from './workspace-connection.js';
16
16
  export { connectDevelopmentWorkspace, discoverDevelopmentWorkspace, resolvePairingCode, DevelopmentWorkspaceConnection, } from './workspace-connection.js';
17
17
  export { createWorkspaceId, isValidWorkspaceId, createDevelopmentWorkspace, updateWorkspaceTimestamp, discoverWorkspace, persistWorkspaceDiscovery, } from './workspace-identity.js';
18
18
  export { DevelopmentNode, getDevelopmentNode, shutdownDevelopmentNode, } from './development-node.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,YAAY,EACV,oBAAoB,EACpB,UAAU,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,gBAAgB,EAChB,UAAU,GACX,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/workspace/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,YAAY,EACV,oBAAoB,EACpB,UAAU,EACV,qBAAqB,EACrB,eAAe,EACf,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACV,0BAA0B,EAC1B,iBAAiB,EACjB,gBAAgB,EAChB,wBAAwB,EACxB,uBAAuB,EACvB,UAAU,GACX,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,2BAA2B,EAC3B,4BAA4B,EAC5B,kBAAkB,EAClB,8BAA8B,GAC/B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,0BAA0B,EAC1B,wBAAwB,EACxB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -13,6 +13,7 @@ export interface WorkspaceConnectionOptions {
13
13
  clientId?: string;
14
14
  clientType?: ClientType;
15
15
  endpoint?: string;
16
+ discoveryEndpoint?: string;
16
17
  auth?: {
17
18
  token: string;
18
19
  apiKey?: string;
@@ -28,6 +29,17 @@ export interface PairingTokenData {
28
29
  workspaceId: string;
29
30
  expiresAt: number;
30
31
  endpoint?: string;
32
+ authorityEndpoint?: string;
33
+ namespace?: string;
34
+ }
35
+ export interface BrowserPairingResolution {
36
+ workspaceId: string;
37
+ endpoint: string;
38
+ expiresAt: number;
39
+ namespace: string;
40
+ }
41
+ export interface PairingDiscoveryOptions {
42
+ endpoint?: string;
31
43
  }
32
44
  export interface ClientInfo {
33
45
  clientId: string;
@@ -56,7 +68,8 @@ export declare function discoverDevelopmentWorkspace(options: {
56
68
  *
57
69
  * Used by clients to auto-discover workspace without manual entry.
58
70
  */
59
- export declare function resolvePairingCode(pairingCode: string, projectDir?: string): Promise<string>;
71
+ export declare function resolvePairingCode(pairingCode: string, projectDir: string): Promise<string>;
72
+ export declare function resolvePairingCode(pairingCode: string, options?: PairingDiscoveryOptions): Promise<BrowserPairingResolution>;
60
73
  /**
61
74
  * Connect to a Development Workspace
62
75
  *
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-connection.d.ts","sourceRoot":"","sources":["../../src/workspace/workspace-connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workspace-connection.d.ts","sourceRoot":"","sources":["../../src/workspace/workspace-connection.ts"],"names":[],"mappings":"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"}
@@ -4,9 +4,8 @@
4
4
  * Public API for clients (browser, IDE, agent) to discover and connect
5
5
  * to a FeltDB Development Workspace.
6
6
  */
7
- import fs from 'fs';
8
- import path from 'path';
9
7
  import { HttpJsDb } from '../http-db.js';
8
+ const DEFAULT_PAIRING_DISCOVERY_ENDPOINT = 'http://127.0.0.1:7799';
10
9
  /**
11
10
  * Discover workspace from .feltdb/workspace.json
12
11
  *
@@ -18,6 +17,10 @@ import { HttpJsDb } from '../http-db.js';
18
17
  * ```
19
18
  */
20
19
  export async function discoverDevelopmentWorkspace(options) {
20
+ const [{ default: fs }, { default: path }] = await Promise.all([
21
+ import('node:fs'),
22
+ import('node:path'),
23
+ ]);
21
24
  const workspacePath = path.join(options.projectDir, '.feltdb', 'workspace.json');
22
25
  if (!fs.existsSync(workspacePath)) {
23
26
  throw new Error(`Development Workspace not found at ${workspacePath}. Run "feltdb dev" or create-feltdb to initialize.`);
@@ -31,20 +34,17 @@ export async function discoverDevelopmentWorkspace(options) {
31
34
  throw new Error(`Failed to read workspace metadata: ${error instanceof Error ? error.message : String(error)}`);
32
35
  }
33
36
  }
34
- /**
35
- * Resolve pairing code to workspace ID
36
- *
37
- * Pairing codes are short-lived tokens (FELT-XXXX) that resolve to
38
- * workspace IDs. They expire after 15 minutes.
39
- *
40
- * Used by clients to auto-discover workspace without manual entry.
41
- */
42
- export async function resolvePairingCode(pairingCode, projectDir) {
37
+ export async function resolvePairingCode(pairingCode, source = {}) {
43
38
  if (!pairingCode.startsWith('FELT-')) {
44
39
  throw new Error('Invalid pairing code format');
45
40
  }
46
- // When projectDir is provided, try local resolution first
47
- if (projectDir) {
41
+ // Keep filesystem resolution for CLI and IDE callers that explicitly pass a project directory.
42
+ if (typeof source === 'string') {
43
+ const [{ default: fs }, { default: path }] = await Promise.all([
44
+ import('node:fs'),
45
+ import('node:path'),
46
+ ]);
47
+ const projectDir = source;
48
48
  const pairingPath = path.join(projectDir, '.feltdb', 'pairing.json');
49
49
  if (fs.existsSync(pairingPath)) {
50
50
  try {
@@ -59,6 +59,26 @@ export async function resolvePairingCode(pairingCode, projectDir) {
59
59
  }
60
60
  }
61
61
  }
62
+ else {
63
+ const discoveryEndpoint = (source.endpoint || DEFAULT_PAIRING_DISCOVERY_ENDPOINT).replace(/\/$/, '');
64
+ let response;
65
+ try {
66
+ response = await fetch(`${discoveryEndpoint}/api/v1/development/pairing/${encodeURIComponent(pairingCode)}`, {
67
+ headers: { Accept: 'application/json' },
68
+ });
69
+ }
70
+ catch (error) {
71
+ throw new Error(`Pairing discovery unavailable at ${discoveryEndpoint}: ${error instanceof Error ? error.message : String(error)}`);
72
+ }
73
+ if (!response.ok) {
74
+ throw new Error(`Pairing code ${pairingCode} not found or expired (discovery returned ${response.status})`);
75
+ }
76
+ const resolution = await response.json();
77
+ if (!resolution.workspaceId || !resolution.endpoint || !resolution.expiresAt || !resolution.namespace) {
78
+ throw new Error('Pairing discovery returned an invalid response');
79
+ }
80
+ return resolution;
81
+ }
62
82
  throw new Error(`Pairing code ${pairingCode} not found or expired. Ensure "feltdb dev" is running and the code is correct.`);
63
83
  }
64
84
  /**
@@ -96,9 +116,19 @@ export async function resolvePairingCode(pairingCode, projectDir) {
96
116
  */
97
117
  export async function connectDevelopmentWorkspace(options) {
98
118
  let workspaceId = options.workspaceId;
119
+ let authorityEndpoint = options.endpoint;
99
120
  // Resolve pairing code to workspace ID if provided
100
121
  if (options.pairingCode && !workspaceId) {
101
- workspaceId = await resolvePairingCode(options.pairingCode, options.projectDir);
122
+ if (options.projectDir) {
123
+ workspaceId = await resolvePairingCode(options.pairingCode, options.projectDir);
124
+ }
125
+ else {
126
+ const resolution = await resolvePairingCode(options.pairingCode, {
127
+ endpoint: options.discoveryEndpoint || options.endpoint,
128
+ });
129
+ workspaceId = resolution.workspaceId;
130
+ authorityEndpoint = resolution.endpoint;
131
+ }
102
132
  }
103
133
  // Discover workspace if no ID provided
104
134
  if (!workspaceId && options.projectDir) {
@@ -110,7 +140,7 @@ export async function connectDevelopmentWorkspace(options) {
110
140
  }
111
141
  const clientId = options.clientId || generateClientId(options.clientType);
112
142
  const clientType = options.clientType || 'other';
113
- const connection = new DevelopmentWorkspaceConnection(workspaceId, clientId, clientType, options);
143
+ const connection = new DevelopmentWorkspaceConnection(workspaceId, clientId, clientType, { ...options, endpoint: authorityEndpoint });
114
144
  await connection.connect();
115
145
  return connection;
116
146
  }
@@ -289,10 +319,14 @@ export class DevelopmentWorkspaceConnection {
289
319
  if (environmentEndpoint)
290
320
  return environmentEndpoint;
291
321
  if (this.options.pairingCode && this.options.projectDir) {
322
+ const [{ default: fs }, { default: path }] = await Promise.all([
323
+ import('node:fs'),
324
+ import('node:path'),
325
+ ]);
292
326
  const pairingPath = path.join(this.options.projectDir, '.feltdb', 'pairing.json');
293
327
  const pairing = JSON.parse(fs.readFileSync(pairingPath, 'utf8'));
294
- if (pairing.endpoint)
295
- return pairing.endpoint;
328
+ if (pairing.authorityEndpoint || pairing.endpoint)
329
+ return pairing.authorityEndpoint || pairing.endpoint;
296
330
  }
297
331
  throw new Error('Cannot connect: workspace authority endpoint is required');
298
332
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feltdb/core",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "description": "FeltDB Core - Application-facing database with Browser, Local, and Server runtimes. Durable state, reactive APIs.",
5
5
  "license": "MIT",
6
6
  "type": "module",