@feltdb/core 0.7.4 → 0.8.1
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/README.md +25 -0
- package/dist/application-development.d.ts +10 -3
- package/dist/application-development.d.ts.map +1 -1
- package/dist/application-development.js +11 -1
- package/dist/application-manifest.d.ts +2 -0
- package/dist/application-manifest.d.ts.map +1 -1
- package/dist/application-manifest.js +1 -1
- package/dist/application-understanding.d.ts +76 -0
- package/dist/application-understanding.d.ts.map +1 -0
- package/dist/application-understanding.js +282 -0
- package/dist/canonical-application.d.ts +1 -0
- package/dist/canonical-application.d.ts.map +1 -1
- package/dist/canonical-application.js +3 -2
- package/dist/cli/ai-model-config.js +3 -3
- package/dist/cli/cli.js +1 -1
- package/dist/cli/commands.js +321 -45
- package/dist/cli/index.js +1 -1
- package/dist/cli/managed-environment.js +6 -0
- package/dist/cli/managed-project.js +6 -0
- package/dist/cli/proposal-client.js +16 -3
- package/dist/cli/publish-engine.js +8 -5
- package/dist/cli/repository-evidence.js +55 -0
- package/dist/cli/source-sync.js +220 -61
- package/dist/convergence.d.ts +55 -0
- package/dist/convergence.d.ts.map +1 -0
- package/dist/convergence.js +63 -0
- package/dist/create/cli.js +10 -1
- package/dist/create/create.js +10 -12
- package/dist/create/managed-account.js +72 -7
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/application.rs +14 -0
- package/dist/create/server-source/crates/feltdb/src/lib.rs +20 -2
- package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +12 -24
- package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +11 -3
- package/dist/create/server-source/crates/feltdb-server/src/authenticated_principal.rs +0 -1
- package/dist/create/server-source/crates/feltdb-server/src/certification_harness.rs +23 -22
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +14 -0
- package/dist/create/server-source/crates/feltdb-server/src/delegation_token.rs +5 -15
- package/dist/create/server-source/crates/feltdb-server/src/durable_operations.rs +13 -28
- package/dist/create/server-source/crates/feltdb-server/src/identity.rs +79 -14
- package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +28 -7
- package/dist/create/server-source/crates/feltdb-server/src/lib.rs +5 -5
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +1929 -261
- package/dist/create/server-source/crates/feltdb-server/src/managed_diagnostics.rs +10 -30
- package/dist/create/server-source/crates/feltdb-server/src/membership_policy.rs +12 -4
- package/dist/create/server-source/crates/feltdb-server/src/request_telemetry.rs +8 -6
- package/dist/create/server-source/crates/feltdb-server/src/snapshot_cursor.rs +10 -14
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +191 -22
- package/dist/create/server-source/crates/feltdb-server/src/tenant_policies.rs +9 -21
- package/dist/create/server-source/crates/feltdb-server/src/transaction_idempotency.rs +5 -3
- package/dist/create/server-source/crates/feltdb-server/src/transaction_recovery.rs +9 -8
- package/dist/create/server-source/crates/feltdb-server/tests/revision_recovery_integration_test.rs +1 -4
- package/dist/create/template/default-project/feltdb.flow +5 -0
- package/dist/create/template/default-project/src/context/AuthContext.tsx +9 -1
- package/dist/create/template/default-project/src/pages/SignUp.tsx +1 -1
- package/dist/flowspec.d.ts +1 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +51 -12
- package/dist/http-client.d.ts +166 -14
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +91 -31
- package/dist/http-db.d.ts +3 -0
- package/dist/http-db.d.ts.map +1 -1
- package/dist/http-db.js +28 -3
- package/dist/index-core.d.ts +5 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +5 -0
- package/dist/intent.d.ts +98 -0
- package/dist/intent.d.ts.map +1 -0
- package/dist/intent.js +343 -0
- package/dist/module-intent.d.ts +51 -0
- package/dist/module-intent.d.ts.map +1 -0
- package/dist/module-intent.js +230 -0
- package/dist/module.d.ts +105 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +566 -0
- package/dist/proposal.d.ts +92 -4
- package/dist/proposal.d.ts.map +1 -1
- package/dist/proposal.js +150 -6
- package/dist/studio/app.d.ts +3 -1
- package/dist/studio/app.d.ts.map +1 -1
- package/dist/studio/components/AskFeltDB.d.ts +2 -1
- package/dist/studio/components/AskFeltDB.d.ts.map +1 -1
- package/dist/studio/components/ContractInspector.d.ts +7 -0
- package/dist/studio/components/ContractInspector.d.ts.map +1 -0
- package/dist/studio/components/ManagedInstancePanel.d.ts.map +1 -1
- package/dist/studio/components/ManagedInstancePanel.js +1 -5
- package/dist/studio/components/ModuleExplorer.d.ts +6 -0
- package/dist/studio/components/ModuleExplorer.d.ts.map +1 -0
- package/dist/studio/components/ProposalBrowser.d.ts +10 -1
- package/dist/studio/components/ProposalBrowser.d.ts.map +1 -1
- package/dist/studio/components/StateExplorer.d.ts +6 -7
- package/dist/studio/components/StateExplorer.d.ts.map +1 -1
- package/dist/studio/components/index.d.ts +2 -0
- package/dist/studio/components/index.d.ts.map +1 -1
- package/dist/studio/components/index.js +4 -4
- package/dist/studio/components-Duq2xQWt.js +2845 -0
- package/dist/studio/index.js +193 -147
- package/dist/studio/studio.css +1 -1
- package/dist/studio/utils/index.d.ts +2 -0
- package/dist/studio/utils/index.d.ts.map +1 -1
- package/dist/studio/utils/index.js +14 -12
- package/dist/studio/utils/managed-instance.d.ts +0 -4
- package/dist/studio/utils/managed-instance.d.ts.map +1 -1
- package/dist/studio/utils/managed-instance.js +0 -4
- package/dist/studio/utils/proposal-api.d.ts +56 -0
- package/dist/studio/utils/proposal-api.d.ts.map +1 -0
- package/dist/studio/utils/proposal-api.js +94 -0
- package/dist/studio/utils/service-api.d.ts +81 -0
- package/dist/studio/utils/service-api.d.ts.map +1 -0
- package/dist/studio/utils/service-api.js +51 -0
- package/dist/studio-app/assets/{feltdb_wasm-B-Ccgccb.js → feltdb_wasm-bIqcRzAr.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-BE79okwX.wasm +0 -0
- package/dist/studio-app/assets/{index-BRrmvaKw.css → index-Cd_NPw14.css} +1 -1
- package/dist/studio-app/assets/index-XGYdlElN.js +29 -0
- package/dist/studio-app/index.html +2 -2
- package/dist/wasm/feltdb_wasm_bg.wasm +0 -0
- package/dist/workspace/local-development-authority.d.ts +2 -0
- package/dist/workspace/local-development-authority.d.ts.map +1 -1
- package/dist/workspace/local-development-authority.js +128 -4
- package/package.json +1 -1
- package/dist/studio/components-Bm4sjhcX.js +0 -2162
- package/dist/studio-app/assets/dist-DA1MYSzp.js +0 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-wH3enJIA.wasm +0 -0
- package/dist/studio-app/assets/index-CEIqowCh.js +0 -42
- package/dist/studio-app/assets/lib-B2_7fcn7.js +0 -69
- package/dist/studio-app/assets/worker-CARZ5g7K.js +0 -69
package/dist/cli/cli.js
CHANGED
package/dist/cli/commands.js
CHANGED
|
@@ -8,16 +8,21 @@ import net from 'net';
|
|
|
8
8
|
import { createRequire } from 'module';
|
|
9
9
|
import { spawn, spawnSync } from 'child_process';
|
|
10
10
|
import { randomBytes } from 'crypto';
|
|
11
|
-
import {
|
|
11
|
+
import { createInterface } from 'node:readline/promises';
|
|
12
|
+
import { createFeltDB, diffFlowSpec, formatFlowSpec, InvestigationLifecycleManager, listFeltDBModules, parseFlowSpec, planFlowSpecMigration, renderConvergenceDiagnostic, renderProposalDiagnostic, resolveFeltDBModule, resolveModuleIntent, startLocalDevelopmentAuthority, validateFlowSpec, verifyProposalConvergence } from '@feltdb/core';
|
|
13
|
+
import { collectApplicationUnderstanding } from '@feltdb/core';
|
|
14
|
+
import { collectRepositoryEvidence } from './repository-evidence.js';
|
|
12
15
|
import { discoverWorkspace, ensureWorkspaceGitIgnored, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace, startPairingDiscoveryServer } from './workspace-integration.js';
|
|
13
16
|
import { applicationUrlCandidate, detectApplication, discoverApplicationUrl } from './application.js';
|
|
14
17
|
import { resolveApplicationLifecycle, startManagedApplication, waitForManagedApplication } from './application-lifecycle.js';
|
|
15
18
|
import { createDevelopmentSession, developmentSessionEnvironment, developmentSessionSummary, transitionDevelopmentSession } from './development-session.js';
|
|
16
19
|
import { openApplication, openStudio } from './browser-opening.js';
|
|
17
20
|
import { publishApplication } from './publish-engine.js';
|
|
18
|
-
import {
|
|
21
|
+
import { applyFlowProposalAtomic, assertProposalSourcePlan, describeProposal, loadLocalDevelopmentContract, repositoryContext, reviewFlowProposal, syncGeneratedSource } from './source-sync.js';
|
|
19
22
|
import { configureAiModel, generateConfiguredFlowProposal, publicAiModelConfiguration } from './ai-model-config.js';
|
|
20
23
|
import { proposalService } from './proposal-client.js';
|
|
24
|
+
import { resolveManagedAuthorityEnvironment } from './managed-environment.js';
|
|
25
|
+
import { readManagedProjectState } from './managed-project.js';
|
|
21
26
|
function loadProjectEnvironment(file = path.resolve('.env.local')) {
|
|
22
27
|
if (!fs.existsSync(file))
|
|
23
28
|
return;
|
|
@@ -87,6 +92,9 @@ export async function handleCommand(command, args) {
|
|
|
87
92
|
return handleAi(args);
|
|
88
93
|
case 'proposals':
|
|
89
94
|
return handleProposals(args);
|
|
95
|
+
case 'module':
|
|
96
|
+
case 'modules':
|
|
97
|
+
return handleModules(args);
|
|
90
98
|
case 'sync':
|
|
91
99
|
return handleSourceSync(args);
|
|
92
100
|
case 'help':
|
|
@@ -94,6 +102,25 @@ export async function handleCommand(command, args) {
|
|
|
94
102
|
return handleHelp();
|
|
95
103
|
}
|
|
96
104
|
}
|
|
105
|
+
function handleModules(args) {
|
|
106
|
+
const action = args[0] || 'list';
|
|
107
|
+
if (action === 'list') {
|
|
108
|
+
for (const module of listFeltDBModules())
|
|
109
|
+
console.log(`${module.id}\t${module.version}\t${module.compatibility.join(', ')}`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (action === 'inspect' && args[1]) {
|
|
113
|
+
const flowPath = path.resolve('feltdb.flow');
|
|
114
|
+
const spec = args.includes('--application') && fs.existsSync(flowPath) ? parseFlowSpec(fs.readFileSync(flowPath, 'utf8')) : undefined;
|
|
115
|
+
const declaration = spec?.modules?.find(value => value.name === args[1]);
|
|
116
|
+
const registered = listFeltDBModules().find(value => value.id === args[1]);
|
|
117
|
+
const module = resolveFeltDBModule(declaration ?? { name: args[1], provider: args.find((value, index) => index > 1 && !value.startsWith('--')) || registered?.provider || '' });
|
|
118
|
+
const usage = spec?.collections.flatMap(collection => collection.fields.filter(field => field.type.startsWith(`ref ${module.id}.`)).map(field => `${collection.name}.${field.name} -> ${field.type.slice(4)}`)) ?? [];
|
|
119
|
+
console.log(JSON.stringify({ ...module, application_usage: usage }, null, 2));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
throw new Error('Usage: feltdb module [list|inspect <id> [provider]]');
|
|
123
|
+
}
|
|
97
124
|
function proposalFile(args) {
|
|
98
125
|
const value = args.find(argument => !argument.startsWith('--') && !['review', 'apply'].includes(argument));
|
|
99
126
|
if (!value)
|
|
@@ -103,12 +130,42 @@ function proposalFile(args) {
|
|
|
103
130
|
function validateRepositoryBuild(args) {
|
|
104
131
|
const packagePath = path.resolve('package.json');
|
|
105
132
|
const projectPackage = fs.existsSync(packagePath) ? JSON.parse(fs.readFileSync(packagePath, 'utf8')) : {};
|
|
106
|
-
if (
|
|
133
|
+
if (args.includes('--no-build'))
|
|
134
|
+
return;
|
|
135
|
+
const verification = ['typecheck', 'test', 'build'].filter(name => projectPackage.scripts?.[name]);
|
|
136
|
+
if (!verification.length)
|
|
107
137
|
return;
|
|
108
138
|
console.log('Validating the updated application before recording applied status…');
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
139
|
+
for (const name of verification) {
|
|
140
|
+
const checked = spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', name], { cwd: process.cwd(), stdio: 'inherit' });
|
|
141
|
+
if (checked.status !== 0)
|
|
142
|
+
throw new Error(`APPLICATION_VALIDATION_FAILED: ${name} exited ${checked.status ?? 'without a status'}; repository restored and proposal remains approved`);
|
|
143
|
+
console.log(`✓ ${name} passed`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function verificationChecks(run = true) {
|
|
147
|
+
const packagePath = path.resolve('package.json');
|
|
148
|
+
const projectPackage = fs.existsSync(packagePath) ? JSON.parse(fs.readFileSync(packagePath, 'utf8')) : {};
|
|
149
|
+
const results = [];
|
|
150
|
+
for (const name of ['typecheck', 'test', 'build'].filter(value => projectPackage.scripts?.[value])) {
|
|
151
|
+
if (!run) {
|
|
152
|
+
results.push({ surface: name === 'test' ? 'test' : 'build', claim: `repository.${name}`, status: 'satisfied', evidence: [`npm run ${name}`] });
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
const checked = spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', name], { cwd: process.cwd(), stdio: 'inherit' });
|
|
156
|
+
results.push({ surface: name === 'test' ? 'test' : 'build', claim: `repository.${name}`, status: checked.status === 0 ? 'satisfied' : 'missing', evidence: [`npm run ${name} exited ${checked.status ?? 'without status'}`] });
|
|
157
|
+
}
|
|
158
|
+
if (!results.length)
|
|
159
|
+
results.push({ surface: 'test', claim: 'repository.validation', status: 'unverified', evidence: ['No typecheck, test, or build script is declared.'] });
|
|
160
|
+
return results;
|
|
161
|
+
}
|
|
162
|
+
async function verifyAndRecord(proposal, runChecks = true) {
|
|
163
|
+
const current = await loadLocalDevelopmentContract();
|
|
164
|
+
const source = proposal.source_plan.files.map(file => ({ path: file.path, status: (fs.existsSync(path.resolve(file.path)) ? 'implemented' : 'missing'), evidence: [fs.existsSync(path.resolve(file.path)) ? `repository path ${file.path}` : `missing repository path ${file.path}`] }));
|
|
165
|
+
const result = await verifyProposalConvergence({ proposal, currentFlow: current.source, snapshot: current.snapshot, source, verification: verificationChecks(runChecks), observations: collectRepositoryEvidence() });
|
|
166
|
+
const stored = await proposalService().recordConvergence(proposal.id, result);
|
|
167
|
+
console.log(renderConvergenceDiagnostic(stored).join('\n'));
|
|
168
|
+
return stored;
|
|
112
169
|
}
|
|
113
170
|
async function handleAi(args) {
|
|
114
171
|
loadProjectEnvironment();
|
|
@@ -141,6 +198,54 @@ async function handleAi(args) {
|
|
|
141
198
|
return handleStudio(args, studioUrl => console.log(`Ask FeltDB: ${studioUrl}`));
|
|
142
199
|
}
|
|
143
200
|
const action = args[0];
|
|
201
|
+
if (action === 'verify' || action === 'converge') {
|
|
202
|
+
const id = args[1];
|
|
203
|
+
if (!id)
|
|
204
|
+
throw new Error(`Usage: feltdb ai ${action} <proposal-id>`);
|
|
205
|
+
const proposal = await proposalService().get(id);
|
|
206
|
+
if (proposal.status !== 'applied')
|
|
207
|
+
throw new Error(`Cannot verify Proposal ${id}. Current status: ${proposal.status}. Verification follows application.`);
|
|
208
|
+
await verifyAndRecord(proposal, !args.includes('--no-build'));
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (action === 'proposals')
|
|
212
|
+
return handleProposals(['list', ...args.slice(1)]);
|
|
213
|
+
if (action === 'proposal') {
|
|
214
|
+
const id = args[1];
|
|
215
|
+
if (!id)
|
|
216
|
+
throw new Error('Usage: feltdb ai proposal <id> [--history]');
|
|
217
|
+
return handleProposals(['get', id, ...args.slice(2)]);
|
|
218
|
+
}
|
|
219
|
+
if (action === 'approve') {
|
|
220
|
+
const id = args[1];
|
|
221
|
+
if (!id)
|
|
222
|
+
throw new Error('Usage: feltdb ai approve <id> [--yes]');
|
|
223
|
+
const client = proposalService();
|
|
224
|
+
const proposal = await client.get(id);
|
|
225
|
+
const readiness = await currentProposalReadiness(client, proposal);
|
|
226
|
+
if (proposal.status !== 'previewed')
|
|
227
|
+
throw new Error(`Cannot approve Proposal ${id}. Current status: ${proposal.status}. A current preview is required.`);
|
|
228
|
+
if (readiness.blockers.length)
|
|
229
|
+
throw new Error(`Cannot approve Proposal ${id}. ${readiness.blockers.join(', ')}. Regenerate the Proposal before approval.`);
|
|
230
|
+
if (!proposal.preview_id)
|
|
231
|
+
throw new Error(`Cannot approve Proposal ${id}. Preview is missing.`);
|
|
232
|
+
await client.getPreview(proposal.preview_id);
|
|
233
|
+
console.log(renderProposalDiagnostic(proposal, { readiness }).join('\n'));
|
|
234
|
+
console.log('Repository source will NOT be modified by approval.');
|
|
235
|
+
let approved = args.includes('--yes');
|
|
236
|
+
if (!approved && process.stdin.isTTY) {
|
|
237
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
238
|
+
approved = /^y(?:es)?$/i.test(await prompt.question('Approve this proposal? [y/N] '));
|
|
239
|
+
prompt.close();
|
|
240
|
+
}
|
|
241
|
+
if (!approved) {
|
|
242
|
+
console.log('Approval cancelled.');
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const value = await client.approve(id, 'cli-developer', args.includes('--approve-authorization'));
|
|
246
|
+
console.log(`✓ Proposal ${value.id} approved`);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
144
249
|
if (action === 'context') {
|
|
145
250
|
const context = await loadLocalDevelopmentContract();
|
|
146
251
|
console.log(JSON.stringify({ snapshot: context.snapshot, flowHash: context.flowHash }, null, 2));
|
|
@@ -163,10 +268,10 @@ async function handleAi(args) {
|
|
|
163
268
|
throw new Error('A proposal id or JSON file is required');
|
|
164
269
|
const stored = target.startsWith('p_') ? await proposalService().get(target) : undefined;
|
|
165
270
|
const proposal = stored ? { baseContractHash: stored.base_contract_hash, baseFlowHash: stored.base_flow_hash,
|
|
166
|
-
proposedFlow: stored.proposed_flow, summary: stored.summary, changes: stored.contract_diff, warnings: stored.warnings }
|
|
271
|
+
proposedFlow: stored.proposed_flow, summary: stored.summary, changes: stored.contract_diff, warnings: stored.warnings, source_plan: stored.source_plan }
|
|
167
272
|
: JSON.parse(fs.readFileSync(path.resolve(target), 'utf8'));
|
|
168
273
|
if (stored && stored.status !== 'approved')
|
|
169
|
-
throw new Error(`
|
|
274
|
+
throw new Error(`Cannot apply Proposal ${stored.id}. Current status: ${stored.status}. Approval is required. Run: feltdb ai approve ${stored.id}`);
|
|
170
275
|
if (!stored && !args.includes('--approve'))
|
|
171
276
|
throw new Error('Explicit approval is required: rerun with --approve');
|
|
172
277
|
const current = await loadLocalDevelopmentContract();
|
|
@@ -177,12 +282,27 @@ async function handleAi(args) {
|
|
|
177
282
|
if (alreadyWritten) {
|
|
178
283
|
console.log('Detected an approved proposal whose flow is already present; reconciling the interrupted application.');
|
|
179
284
|
const recoveredReview = await reviewFlowProposal({ ...proposal, baseContractHash: current.snapshot.contract_hash, baseFlowHash: current.flowHash });
|
|
180
|
-
|
|
181
|
-
|
|
285
|
+
await applyFlowProposalAtomic({ ...proposal, baseContractHash: current.snapshot.contract_hash, baseFlowHash: current.flowHash, proposedFlow: current.source }, process.cwd(), {
|
|
286
|
+
approveAuthorization: true, replaceGenerated: args.includes('--replace-generated'), verify: () => validateRepositoryBuild(args),
|
|
287
|
+
});
|
|
182
288
|
await proposalService().transition(stored.id, 'applied', 'cli-repository-applier', true);
|
|
183
289
|
console.log(`✓ Reconciled and marked ${stored.id} applied`);
|
|
290
|
+
await verifyAndRecord({ ...stored, status: 'applied' }, false);
|
|
184
291
|
return;
|
|
185
292
|
}
|
|
293
|
+
if (stored) {
|
|
294
|
+
const client = proposalService();
|
|
295
|
+
const readiness = await client.status(stored.id);
|
|
296
|
+
if (readiness.blockers.length)
|
|
297
|
+
throw new Error(`Cannot apply Proposal ${stored.id}. Proposal is stale: ${readiness.blockers.join(', ')}. Proposal contract: ${stored.base_contract_hash}. Current contract: ${readiness.current_contract_hash}. Regenerate the Proposal before applying.`);
|
|
298
|
+
if (!stored.preview_id)
|
|
299
|
+
throw new Error(`Cannot apply Proposal ${stored.id}. A current preview is required.`);
|
|
300
|
+
await client.getPreview(stored.preview_id);
|
|
301
|
+
assertProposalSourcePlan(stored.source_plan);
|
|
302
|
+
const repository = await repositoryContext(process.cwd(), stored.source_plan.files.map(value => value.path));
|
|
303
|
+
if (repository.dirty.length && !args.includes('--allow-dirty'))
|
|
304
|
+
throw new Error(`Cannot apply Proposal ${stored.id}. Repository conflict: ${repository.dirty.join(', ')}. Resolve the conflict and retry.`);
|
|
305
|
+
}
|
|
186
306
|
const reviewOnly = await reviewFlowProposal(proposal);
|
|
187
307
|
console.log(`\n${proposal.summary || 'FeltDB change proposal'}`);
|
|
188
308
|
for (const line of describeProposal(reviewOnly))
|
|
@@ -192,20 +312,60 @@ async function handleAi(args) {
|
|
|
192
312
|
console.log('✓ Preview complete. No files, flow, contract, or published state were changed.');
|
|
193
313
|
return;
|
|
194
314
|
}
|
|
195
|
-
|
|
315
|
+
let confirmed = args.includes('--yes');
|
|
316
|
+
if (!confirmed && process.stdin.isTTY) {
|
|
317
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
318
|
+
confirmed = /^y(?:es)?$/i.test(await prompt.question(`Apply ${stored?.id || 'this proposal'} to repository source? [y/N] `));
|
|
319
|
+
prompt.close();
|
|
320
|
+
}
|
|
321
|
+
if (!confirmed)
|
|
322
|
+
throw new Error('Apply cancelled. Re-run with --yes for non-interactive application.');
|
|
323
|
+
const review = await applyFlowProposalAtomic(proposal, process.cwd(), {
|
|
196
324
|
allowDirty: args.includes('--allow-dirty'),
|
|
197
325
|
approveAuthorization: args.includes('--approve-authorization'),
|
|
198
326
|
replaceGenerated: args.includes('--replace-generated'),
|
|
327
|
+
verify: () => validateRepositoryBuild(args),
|
|
199
328
|
});
|
|
200
329
|
console.log(`✓ Applied validated flow proposal (${review.proposedContract.hashes.application})`);
|
|
201
330
|
for (const file of review.written)
|
|
202
331
|
console.log(` ${file}`);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
await
|
|
332
|
+
if (stored) {
|
|
333
|
+
const applied = await proposalService().transition(stored.id, 'applied', 'cli-repository-applier', true);
|
|
334
|
+
await verifyAndRecord(applied, false);
|
|
335
|
+
}
|
|
206
336
|
return;
|
|
207
337
|
}
|
|
208
|
-
|
|
338
|
+
const developerIntent = args.filter(value => !value.startsWith('--')).join(' ').trim();
|
|
339
|
+
if (developerIntent) {
|
|
340
|
+
const resolved = resolveModuleIntent(developerIntent);
|
|
341
|
+
if (resolved) {
|
|
342
|
+
const client = proposalService();
|
|
343
|
+
console.log('Executing intent through the FeltDB Intent Service...');
|
|
344
|
+
const execution = await client.executeIntent(developerIntent);
|
|
345
|
+
if (execution.blockers.length)
|
|
346
|
+
throw new Error(`INTENT_BLOCKED: ${execution.blockers.join(', ')}`);
|
|
347
|
+
const ownerClaim = execution.plan?.module.placement.owner_claim;
|
|
348
|
+
const ownerInference = execution.understanding?.inferences.find(value => value.claim === ownerClaim);
|
|
349
|
+
console.log(`✓ Intent ${execution.intent.id}: ${execution.intent.action} ${execution.intent.module}`);
|
|
350
|
+
console.log(`✓ Evidence collected (${execution.understanding?.evidence.length ?? 0} normalized facts)`);
|
|
351
|
+
if (execution.understanding?.completeness)
|
|
352
|
+
console.log(`✓ Understanding completeness: ${execution.understanding.completeness.score}%`);
|
|
353
|
+
if (ownerInference)
|
|
354
|
+
console.log(`✓ Integration owner: ${String(ownerInference.value)} · ${Math.round(ownerInference.confidence * 100)}% · ${ownerInference.authority}`);
|
|
355
|
+
if (!execution.proposal) {
|
|
356
|
+
console.log(`✓ ${execution.intent.module} is already installed and converged. No proposal created.`);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const stored = execution.proposal;
|
|
360
|
+
if (execution.plan?.already_installed)
|
|
361
|
+
console.log('Creating a completion proposal for the existing module implementation.');
|
|
362
|
+
const readiness = await client.status(stored.id, stored.evidence_hash);
|
|
363
|
+
console.log(renderProposalDiagnostic(stored, { readiness, preview: execution.preview }).join('\n'));
|
|
364
|
+
console.log(`Use:\n feltdb ai proposal ${stored.id}`);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
throw new Error('Usage: feltdb ai ["implement <module>"|context|proposals|proposal <id>|approve <id>|review <proposal.json>|apply <id>|verify <id>|converge <id>]');
|
|
209
369
|
}
|
|
210
370
|
async function handleProposals(args) {
|
|
211
371
|
loadProjectEnvironment();
|
|
@@ -213,9 +373,18 @@ async function handleProposals(args) {
|
|
|
213
373
|
const action = args[0] || 'list';
|
|
214
374
|
const id = args[1];
|
|
215
375
|
if (action === 'list') {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
376
|
+
const option = (name) => args.includes(name) ? args[args.indexOf(name) + 1] : undefined;
|
|
377
|
+
const result = await client.list({ status: option('--status'), environment: option('--environment'), limit: option('--limit') ? Number(option('--limit')) : undefined, cursor: option('--cursor') });
|
|
378
|
+
if (args.includes('--json')) {
|
|
379
|
+
console.log(JSON.stringify(result, null, 2));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
const { proposals } = result;
|
|
383
|
+
console.log('PROPOSALS');
|
|
384
|
+
for (const value of proposals)
|
|
385
|
+
console.log(`${value.id}\t${value.status.toUpperCase()}\t${value.summary}\t${value.created_at}`);
|
|
386
|
+
if (result.pagination?.nextCursor)
|
|
387
|
+
console.log(`Next: --cursor ${result.pagination.nextCursor}`);
|
|
219
388
|
if (!proposals.length)
|
|
220
389
|
console.log('No proposals.');
|
|
221
390
|
return;
|
|
@@ -223,7 +392,14 @@ async function handleProposals(args) {
|
|
|
223
392
|
if (!id)
|
|
224
393
|
throw new Error(`feltdb proposals ${action} requires a proposal id`);
|
|
225
394
|
if (action === 'get') {
|
|
226
|
-
|
|
395
|
+
const proposal = await client.get(id);
|
|
396
|
+
const [readiness, history] = await Promise.all([currentProposalReadiness(client, proposal), client.history(id)]);
|
|
397
|
+
const preview = proposal.preview_id ? await client.getPreview(proposal.preview_id).catch(() => undefined) : undefined;
|
|
398
|
+
const result = { proposal, readiness, preview, history: history.events };
|
|
399
|
+
if (args.includes('--json'))
|
|
400
|
+
console.log(JSON.stringify(result, null, 2));
|
|
401
|
+
else
|
|
402
|
+
console.log(renderProposalDiagnostic(proposal, { readiness, history: history.events, preview: preview }).join('\n'));
|
|
227
403
|
return;
|
|
228
404
|
}
|
|
229
405
|
if (action === 'diff') {
|
|
@@ -233,12 +409,24 @@ async function handleProposals(args) {
|
|
|
233
409
|
return;
|
|
234
410
|
}
|
|
235
411
|
if (action === 'approve' || action === 'reject') {
|
|
236
|
-
const value =
|
|
412
|
+
const value = action === 'approve' ? await client.approve(id, 'local-developer', args.includes('--approve-authorization')) : await client.reject(id, 'local-developer');
|
|
237
413
|
console.log(`✓ ${value.id} ${value.status}`);
|
|
238
414
|
return;
|
|
239
415
|
}
|
|
240
416
|
throw new Error('Usage: feltdb proposals [list|get|diff|approve|reject] [id]');
|
|
241
417
|
}
|
|
418
|
+
async function currentProposalReadiness(client, proposal) {
|
|
419
|
+
if (!proposal.understanding)
|
|
420
|
+
return client.status(proposal.id);
|
|
421
|
+
const current = await loadLocalDevelopmentContract();
|
|
422
|
+
let moduleIntent;
|
|
423
|
+
try {
|
|
424
|
+
moduleIntent = resolveModuleIntent(proposal.understanding.intent);
|
|
425
|
+
}
|
|
426
|
+
catch { }
|
|
427
|
+
const understanding = await collectApplicationUnderstanding({ intent: proposal.understanding.intent, snapshot: current.snapshot, currentFlow: current.source, moduleIntent, observations: collectRepositoryEvidence(), now: proposal.understanding.created_at });
|
|
428
|
+
return client.status(proposal.id, understanding.evidence_hash);
|
|
429
|
+
}
|
|
242
430
|
async function handleSourceSync(args) {
|
|
243
431
|
loadProjectEnvironment();
|
|
244
432
|
const context = await loadLocalDevelopmentContract();
|
|
@@ -617,9 +805,11 @@ For more info, visit: https://github.com/rkendel1/feltdb
|
|
|
617
805
|
}
|
|
618
806
|
async function handleConnect(args) {
|
|
619
807
|
loadProjectEnvironment();
|
|
808
|
+
const managed = readManagedProjectState();
|
|
620
809
|
const projectFile = path.resolve('feltdb.config.json');
|
|
621
810
|
const project = fs.existsSync(projectFile) ? JSON.parse(fs.readFileSync(projectFile, 'utf8')) : {};
|
|
622
811
|
const url = (args[0]
|
|
812
|
+
|| managed.url
|
|
623
813
|
|| process.env.VITE_FELTDB_MANAGED_URL
|
|
624
814
|
|| process.env.VITE_FELTDB_URL
|
|
625
815
|
|| project.server?.url
|
|
@@ -634,8 +824,8 @@ async function handleConnect(args) {
|
|
|
634
824
|
let health;
|
|
635
825
|
try {
|
|
636
826
|
const response = await fetch(`${url}/health`, {
|
|
637
|
-
headers: process.env.
|
|
638
|
-
? { Authorization: `Bearer ${process.env.
|
|
827
|
+
headers: process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY
|
|
828
|
+
? { Authorization: `Bearer ${process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY}` }
|
|
639
829
|
: {},
|
|
640
830
|
signal: controller.signal,
|
|
641
831
|
});
|
|
@@ -658,14 +848,14 @@ async function handleConnect(args) {
|
|
|
658
848
|
fs.writeFileSync(connFile, JSON.stringify({
|
|
659
849
|
url,
|
|
660
850
|
connected: new Date().toISOString(),
|
|
661
|
-
authenticated: Boolean(process.env.
|
|
662
|
-
namespace:
|
|
851
|
+
authenticated: Boolean(process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY),
|
|
852
|
+
namespace: managed.namespace || project.namespace || 'default',
|
|
663
853
|
runtime: project.runtime || 'self-hosted',
|
|
664
854
|
}, null, 2));
|
|
665
855
|
console.log('✅ Connected to FeltDB\n');
|
|
666
856
|
console.log('Endpoint: ' + url);
|
|
667
|
-
console.log(`Namespace: ${
|
|
668
|
-
console.log(`Authenticated: ${process.env.
|
|
857
|
+
console.log(`Namespace: ${managed.namespace || project.namespace || 'default'}`);
|
|
858
|
+
console.log(`Authenticated: ${process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY ? 'yes' : 'no'}`);
|
|
669
859
|
console.log(`Runtime: ${project.runtime || 'self-hosted'}`);
|
|
670
860
|
console.log(`Health: ${health.status || 'healthy'}\n`);
|
|
671
861
|
console.log('Use: feltdb status (to check connection)\n');
|
|
@@ -780,7 +970,7 @@ async function handleWorkspaceConnect(args) {
|
|
|
780
970
|
console.log(' (when @feltdb/client workspace APIs are available)\n');
|
|
781
971
|
}
|
|
782
972
|
async function handleDev(args) {
|
|
783
|
-
var _a
|
|
973
|
+
var _a;
|
|
784
974
|
const projectDir = process.cwd();
|
|
785
975
|
if (args.includes('--help')) {
|
|
786
976
|
console.log('Usage: feltdb dev [--app-url URL] [--studio-port 7701] [--authority-port 7700] [--discovery-port 7799] [--no-open]');
|
|
@@ -833,14 +1023,10 @@ async function handleDev(args) {
|
|
|
833
1023
|
? { ...configDefaults, ...JSON.parse(fs.readFileSync(configPath, 'utf-8')) }
|
|
834
1024
|
: configDefaults;
|
|
835
1025
|
if (config.runtime === 'managed') {
|
|
836
|
-
(
|
|
837
|
-
(_b = process.env).VITE_FELTDB_API_KEY || (_b.VITE_FELTDB_API_KEY = process.env.VITE_FELTDB_MANAGED_API_KEY);
|
|
838
|
-
}
|
|
839
|
-
if (config.runtime === 'managed' && (!process.env.VITE_FELTDB_URL || !process.env.VITE_FELTDB_API_KEY)) {
|
|
840
|
-
throw new Error('Managed runtime requires VITE_FELTDB_MANAGED_URL and VITE_FELTDB_MANAGED_API_KEY in .env.local. Re-run managed setup or add your managed connection.');
|
|
1026
|
+
resolveManagedAuthorityEnvironment(process.env);
|
|
841
1027
|
}
|
|
842
1028
|
const runtimeNamespace = config.runtime === 'managed'
|
|
843
|
-
?
|
|
1029
|
+
? readManagedProjectState(projectDir).namespace || config.namespace
|
|
844
1030
|
: config.namespace;
|
|
845
1031
|
const applicationLifecycle = resolveApplicationLifecycle(config);
|
|
846
1032
|
// Application URL precedence:
|
|
@@ -929,7 +1115,7 @@ async function handleDev(args) {
|
|
|
929
1115
|
if (compose.status !== 0)
|
|
930
1116
|
throw new Error(`docker compose failed with status ${compose.status ?? 'unknown'}`);
|
|
931
1117
|
selfHostedStarted = true;
|
|
932
|
-
(
|
|
1118
|
+
(_a = process.env).VITE_FELTDB_URL || (_a.VITE_FELTDB_URL = 'http://127.0.0.1:7700');
|
|
933
1119
|
}
|
|
934
1120
|
if (!process.env.VITE_FELTDB_URL) {
|
|
935
1121
|
const authorityPort = Number(args.includes('--authority-port') ? args[args.indexOf('--authority-port') + 1] || '7700' : '7700');
|
|
@@ -937,6 +1123,7 @@ async function handleDev(args) {
|
|
|
937
1123
|
dataDir: path.join(projectDir, '.feltdb-data', 'authority'),
|
|
938
1124
|
port: authorityPort,
|
|
939
1125
|
proposalContext: () => loadLocalDevelopmentContract(projectDir),
|
|
1126
|
+
intentObservations: () => collectRepositoryEvidence(projectDir),
|
|
940
1127
|
});
|
|
941
1128
|
process.env.VITE_FELTDB_URL = localAuthority.endpoint;
|
|
942
1129
|
const connectionsPath = path.join(projectDir, '.feltdb', 'connections.json');
|
|
@@ -1403,10 +1590,11 @@ async function handleStudio(args, onReady) {
|
|
|
1403
1590
|
loadProjectEnvironment();
|
|
1404
1591
|
const projectConfigPath = path.resolve('feltdb.config.json');
|
|
1405
1592
|
const projectConfig = fs.existsSync(projectConfigPath) ? JSON.parse(fs.readFileSync(projectConfigPath, 'utf8')) : {};
|
|
1593
|
+
const managedProject = readManagedProjectState();
|
|
1406
1594
|
const projectIsManaged = projectConfig.runtime === 'managed';
|
|
1407
1595
|
const connectUrl = args.includes('--connect')
|
|
1408
1596
|
? args[args.indexOf('--connect') + 1]
|
|
1409
|
-
: projectIsManaged ? process.env.VITE_FELTDB_MANAGED_URL : undefined;
|
|
1597
|
+
: projectIsManaged ? process.env.VITE_FELTDB_MANAGED_URL || managedProject.url : undefined;
|
|
1410
1598
|
const port = args.includes('--port')
|
|
1411
1599
|
? args[args.indexOf('--port') + 1] || '7701'
|
|
1412
1600
|
: '7701';
|
|
@@ -1416,10 +1604,16 @@ async function handleStudio(args, onReady) {
|
|
|
1416
1604
|
const open = !args.includes('--no-open');
|
|
1417
1605
|
const namespace = args.includes('--namespace')
|
|
1418
1606
|
? args[args.indexOf('--namespace') + 1] || 'default'
|
|
1419
|
-
: projectIsManaged ?
|
|
1607
|
+
: projectIsManaged ? managedProject.namespace || projectConfig.namespace || 'default' : 'default';
|
|
1420
1608
|
const runtime = args.includes('--runtime')
|
|
1421
1609
|
? args[args.indexOf('--runtime') + 1] || 'browser'
|
|
1422
1610
|
: projectIsManaged ? 'managed' : connectUrl ? 'remote' : 'browser';
|
|
1611
|
+
const applicationId = args.includes('--application')
|
|
1612
|
+
? args[args.indexOf('--application') + 1] || ''
|
|
1613
|
+
: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || managedProject.applicationId || '';
|
|
1614
|
+
const environment = args.includes('--environment')
|
|
1615
|
+
? args[args.indexOf('--environment') + 1] || 'production'
|
|
1616
|
+
: managedProject.environment || 'production';
|
|
1423
1617
|
const appUrl = args.includes('--app-url')
|
|
1424
1618
|
? args[args.indexOf('--app-url') + 1]
|
|
1425
1619
|
: undefined;
|
|
@@ -1465,14 +1659,14 @@ async function handleStudio(args, onReady) {
|
|
|
1465
1659
|
response.setHeader('Cache-Control', 'no-store');
|
|
1466
1660
|
response.setHeader('Pragma', 'no-cache');
|
|
1467
1661
|
const managedPublish = runtime === 'managed' ? {
|
|
1468
|
-
url:
|
|
1469
|
-
namespace:
|
|
1470
|
-
tenantId:
|
|
1471
|
-
applicationId: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || '',
|
|
1472
|
-
environment:
|
|
1662
|
+
url: connectUrl || managedProject.url || '',
|
|
1663
|
+
namespace: managedProject.namespace || namespace,
|
|
1664
|
+
tenantId: managedProject.tenantId || '',
|
|
1665
|
+
applicationId: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || managedProject.applicationId || '',
|
|
1666
|
+
environment: managedProject.environment || 'production',
|
|
1473
1667
|
publishEndpoint: '/_feltdb/publish',
|
|
1474
1668
|
} : undefined;
|
|
1475
|
-
response.end(JSON.stringify({ token: connectUrl ? process.env.VITE_FELTDB_API_KEY ||
|
|
1669
|
+
response.end(JSON.stringify({ token: connectUrl ? process.env.VITE_FELTDB_API_KEY || '' : '', managedPublish, session, sourceSync: { enabled: true, webllmEnabled, previewOnly, token: studioMutationToken, aiModel: publicAiModelConfiguration() } }));
|
|
1476
1670
|
return;
|
|
1477
1671
|
}
|
|
1478
1672
|
if (pathname === '/_feltdb/contract') {
|
|
@@ -1484,12 +1678,12 @@ async function handleStudio(args, onReady) {
|
|
|
1484
1678
|
? JSON.parse(fs.readFileSync(path.resolve('.feltdb/last-published.json'), 'utf8')) : undefined;
|
|
1485
1679
|
let runningContract;
|
|
1486
1680
|
let runningSnapshot;
|
|
1487
|
-
const applicationId = process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || lastPublished?.applicationId;
|
|
1681
|
+
const applicationId = process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || managedProject.applicationId || lastPublished?.applicationId;
|
|
1488
1682
|
if (connectUrl && applicationId) {
|
|
1489
1683
|
try {
|
|
1490
1684
|
const endpoint = new URL('/v1/application', connectUrl);
|
|
1491
1685
|
endpoint.searchParams.set('application_id', applicationId);
|
|
1492
|
-
endpoint.searchParams.set('environment',
|
|
1686
|
+
endpoint.searchParams.set('environment', managedProject.environment || lastPublished?.environment || 'production');
|
|
1493
1687
|
const headers = {};
|
|
1494
1688
|
const credential = process.env.FELTDB_MANAGED_CONTROL_API_KEY || process.env.VITE_FELTDB_API_KEY;
|
|
1495
1689
|
if (credential)
|
|
@@ -1514,6 +1708,29 @@ async function handleStudio(args, onReady) {
|
|
|
1514
1708
|
}
|
|
1515
1709
|
return;
|
|
1516
1710
|
}
|
|
1711
|
+
if (pathname === '/_feltdb/repository-readiness' && request.method === 'GET') {
|
|
1712
|
+
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
1713
|
+
response.setHeader('Cache-Control', 'no-store');
|
|
1714
|
+
try {
|
|
1715
|
+
const proposalId = new URL(request.url || '/', 'http://localhost').searchParams.get('proposal');
|
|
1716
|
+
if (!proposalId)
|
|
1717
|
+
throw new Error('proposal is required');
|
|
1718
|
+
const proposal = await proposalClient.get(proposalId);
|
|
1719
|
+
assertProposalSourcePlan(proposal.source_plan);
|
|
1720
|
+
const local = await loadLocalDevelopmentContract();
|
|
1721
|
+
const repository = await repositoryContext(process.cwd(), proposal.source_plan.files.map(value => value.path));
|
|
1722
|
+
const contractMatches = local.snapshot.contract_hash === proposal.base_contract_hash;
|
|
1723
|
+
const flowMatches = local.flowHash === proposal.base_flow_hash;
|
|
1724
|
+
response.end(JSON.stringify({ ready: contractMatches && flowMatches && repository.dirty.length === 0,
|
|
1725
|
+
contractMatches, flowMatches, gitClean: repository.dirty.length === 0, conflicts: repository.dirty,
|
|
1726
|
+
branch: repository.branch, commit: repository.commit }));
|
|
1727
|
+
}
|
|
1728
|
+
catch (error) {
|
|
1729
|
+
response.statusCode = 409;
|
|
1730
|
+
response.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
1731
|
+
}
|
|
1732
|
+
return;
|
|
1733
|
+
}
|
|
1517
1734
|
if (pathname === '/_feltdb/proposals/list' && request.method === 'GET') {
|
|
1518
1735
|
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
1519
1736
|
response.setHeader('Cache-Control', 'no-store');
|
|
@@ -1526,6 +1743,55 @@ async function handleStudio(args, onReady) {
|
|
|
1526
1743
|
}
|
|
1527
1744
|
return;
|
|
1528
1745
|
}
|
|
1746
|
+
if (pathname === '/_feltdb/intents' && request.method === 'POST') {
|
|
1747
|
+
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
1748
|
+
response.setHeader('Cache-Control', 'no-store');
|
|
1749
|
+
if (request.headers['x-feltdb-studio-token'] !== studioMutationToken) {
|
|
1750
|
+
response.statusCode = 403;
|
|
1751
|
+
response.end(JSON.stringify({ error: 'Invalid Studio token' }));
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
try {
|
|
1755
|
+
const chunks = [];
|
|
1756
|
+
for await (const chunk of request)
|
|
1757
|
+
chunks.push(Buffer.from(chunk));
|
|
1758
|
+
const input = JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
1759
|
+
if (!input.intent?.trim())
|
|
1760
|
+
throw new Error('An intent is required');
|
|
1761
|
+
const execution = await proposalClient.executeIntent(input.intent, 'studio-developer');
|
|
1762
|
+
if (!execution.proposal) {
|
|
1763
|
+
response.statusCode = execution.blockers.length ? 409 : 200;
|
|
1764
|
+
response.end(JSON.stringify(execution));
|
|
1765
|
+
return;
|
|
1766
|
+
}
|
|
1767
|
+
const flowProposal = {
|
|
1768
|
+
baseContractHash: execution.proposal.base_contract_hash,
|
|
1769
|
+
baseFlowHash: execution.proposal.base_flow_hash,
|
|
1770
|
+
proposedFlow: execution.proposal.proposed_flow,
|
|
1771
|
+
summary: execution.proposal.summary,
|
|
1772
|
+
changes: execution.proposal.contract_diff,
|
|
1773
|
+
warnings: execution.proposal.warnings,
|
|
1774
|
+
source_plan: execution.proposal.source_plan,
|
|
1775
|
+
implementation: execution.plan ? {
|
|
1776
|
+
intent: { action: execution.intent.action === 'extend' ? 'extend' : 'implement', module: execution.plan.module.id, provider: execution.plan.module.provider, version: execution.plan.module.version, original: execution.intent.raw_request },
|
|
1777
|
+
module: execution.plan.module, alreadyInstalled: execution.plan.already_installed, owner: Object.values(execution.plan.ownership)[0], ownerClaim: execution.plan.module.placement.owner_claim, primaryApplicationModel: execution.plan.module.placement.primary_application_model,
|
|
1778
|
+
applicationModels: execution.plan.state_changes, relationships: execution.plan.relationships, events: execution.plan.events,
|
|
1779
|
+
workflows: execution.plan.workflows, capabilities: execution.plan.capabilities, authorization: execution.plan.authorization,
|
|
1780
|
+
configuration: execution.plan.configuration, secrets: execution.plan.secrets, dependencies: execution.plan.dependencies, stateFlow: [], warnings: execution.plan.warnings,
|
|
1781
|
+
} : undefined,
|
|
1782
|
+
understanding: execution.understanding,
|
|
1783
|
+
};
|
|
1784
|
+
const review = await reviewFlowProposal(flowProposal);
|
|
1785
|
+
response.end(JSON.stringify({ execution, proposal: flowProposal, proposalId: execution.proposal.id,
|
|
1786
|
+
changes: describeProposal(review), sourcePlan: execution.proposal.source_plan.files.map(value => value.path),
|
|
1787
|
+
authorizationChanges: review.authorizationChanges, migration: review.migration, contractHash: review.proposedContract.hashes.application }));
|
|
1788
|
+
}
|
|
1789
|
+
catch (error) {
|
|
1790
|
+
response.statusCode = 400;
|
|
1791
|
+
response.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
1792
|
+
}
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1529
1795
|
const proposalDetail = pathname.match(/^\/_feltdb\/proposals\/(p_[A-Za-z0-9]+)$/);
|
|
1530
1796
|
if (proposalDetail && request.method === 'GET') {
|
|
1531
1797
|
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
@@ -1578,6 +1844,7 @@ async function handleStudio(args, onReady) {
|
|
|
1578
1844
|
stored = await proposalClient.create({ application_id: current.snapshot.application.application_id,
|
|
1579
1845
|
environment: current.snapshot.application.environment, base_contract_hash: proposal.baseContractHash,
|
|
1580
1846
|
base_flow_hash: proposal.baseFlowHash, proposed_flow: proposal.proposedFlow, summary: proposal.summary,
|
|
1847
|
+
proposal_version: 1, contract_diff: [], source_plan: { files: [] },
|
|
1581
1848
|
warnings: proposal.warnings, actor: 'studio-developer', metadata: { model: publicAiModelConfiguration().model, created_from: 'studio' } });
|
|
1582
1849
|
await proposalClient.validate(stored.id, 'feltdb-validator', review.proposedContract.manifest);
|
|
1583
1850
|
stored = (await proposalClient.preview(stored.id, 'studio-developer')).proposal;
|
|
@@ -1715,6 +1982,9 @@ async function handleStudio(args, onReady) {
|
|
|
1715
1982
|
const parameters = new URLSearchParams({ namespace, runtime });
|
|
1716
1983
|
if (connectUrl)
|
|
1717
1984
|
parameters.set('connect', connectUrl);
|
|
1985
|
+
if (applicationId)
|
|
1986
|
+
parameters.set('application_id', applicationId);
|
|
1987
|
+
parameters.set('environment', environment);
|
|
1718
1988
|
if (appUrl)
|
|
1719
1989
|
parameters.set('app', appUrl);
|
|
1720
1990
|
if (session.workspaceId)
|
|
@@ -1751,9 +2021,14 @@ Commands:
|
|
|
1751
2021
|
ai model show Show the selected local or user-provided model
|
|
1752
2022
|
ai model set Select WebLLM or an OpenAI-compatible model
|
|
1753
2023
|
ai context Show the secret-free contract grounding snapshot
|
|
2024
|
+
ai proposals List bounded proposals [--status/--environment/--json]
|
|
2025
|
+
ai proposal <id> Show canonical proposal, readiness, preview, and history
|
|
2026
|
+
ai approve <id> Approve a current preview [--yes]
|
|
1754
2027
|
ai review <file> Validate and review a WebLLM flow proposal
|
|
1755
|
-
ai apply <id>
|
|
2028
|
+
ai apply <id> Atomically apply an approved proposal [--yes]
|
|
1756
2029
|
proposals List durable FeltDB application proposals
|
|
2030
|
+
module list List deterministic FeltDB modules
|
|
2031
|
+
module inspect <id> Inspect a resolved module semantic contract
|
|
1757
2032
|
proposals get <id> Show a proposal and its lifecycle history
|
|
1758
2033
|
proposals diff <id> Show contract and structured source impact
|
|
1759
2034
|
proposals approve <id> Approve a previewed proposal
|
|
@@ -1776,6 +2051,7 @@ Commands:
|
|
|
1776
2051
|
Studio:
|
|
1777
2052
|
feltdb studio Launch local Studio
|
|
1778
2053
|
feltdb studio --connect <url> Connect to remote instance
|
|
2054
|
+
--application <id> --environment <name> Select one application environment
|
|
1779
2055
|
feltdb studio --port 7701 --no-open Start on port 7701 without opening browser
|
|
1780
2056
|
|
|
1781
2057
|
Server Options:
|
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.
|
|
26
|
+
const VERSION = '0.8.0';
|
|
27
27
|
function prompt(question) {
|
|
28
28
|
const rl = readline.createInterface({
|
|
29
29
|
input: process.stdin,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export function resolveManagedAuthorityEnvironment(environment) {
|
|
2
|
+
environment.VITE_FELTDB_URL || (environment.VITE_FELTDB_URL = environment.VITE_FELTDB_MANAGED_URL);
|
|
3
|
+
if (!environment.VITE_FELTDB_URL) {
|
|
4
|
+
throw new Error('Managed runtime requires VITE_FELTDB_MANAGED_URL in .env.local. Re-run managed setup or add your managed connection.');
|
|
5
|
+
}
|
|
6
|
+
}
|