@feltdb/core 0.7.4 → 0.8.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/README.md +25 -0
- package/dist/application-development.d.ts +3 -1
- package/dist/application-development.d.ts.map +1 -1
- package/dist/application-development.js +16 -0
- package/dist/application-manifest.d.ts +1 -0
- package/dist/application-manifest.d.ts.map +1 -1
- 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/cli.js +1 -1
- package/dist/cli/commands.js +225 -42
- 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 +12 -3
- package/dist/cli/publish-engine.js +7 -4
- package/dist/cli/source-sync.js +47 -3
- package/dist/create/cli.js +10 -1
- package/dist/create/create.js +10 -12
- package/dist/create/managed-account.js +71 -7
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/application.rs +10 -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/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 +1777 -259
- 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 +183 -21
- 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/flowspec.d.ts +1 -0
- package/dist/flowspec.d.ts.map +1 -1
- package/dist/flowspec.js +46 -12
- package/dist/http-client.d.ts +135 -14
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +79 -31
- package/dist/index-core.d.ts +2 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +2 -0
- package/dist/module-intent.d.ts +37 -0
- package/dist/module-intent.d.ts.map +1 -0
- package/dist/module-intent.js +81 -0
- package/dist/module.d.ts +76 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +61 -0
- package/dist/proposal.d.ts +80 -4
- package/dist/proposal.d.ts.map +1 -1
- package/dist/proposal.js +132 -5
- 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-CNSNrmA9.js +2787 -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 +53 -0
- package/dist/studio/utils/proposal-api.d.ts.map +1 -0
- package/dist/studio/utils/proposal-api.js +91 -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/{dist-DA1MYSzp.js → dist-CCOk39Uc.js} +1 -1
- package/dist/studio-app/assets/{feltdb_wasm-B-Ccgccb.js → feltdb_wasm-CSB6KVgw.js} +1 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-Dd1fnO9U.wasm +0 -0
- package/dist/studio-app/assets/index-Bncji3aN.js +43 -0
- package/dist/studio-app/assets/{index-BRrmvaKw.css → index-yazaWMUN.css} +1 -1
- 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.map +1 -1
- package/dist/workspace/local-development-authority.js +64 -3
- package/package.json +1 -1
- package/dist/studio/components-Bm4sjhcX.js +0 -2162
- package/dist/studio-app/assets/feltdb_wasm_bg-wH3enJIA.wasm +0 -0
- package/dist/studio-app/assets/index-CEIqowCh.js +0 -42
package/dist/cli/commands.js
CHANGED
|
@@ -8,16 +8,19 @@ 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, createModuleImplementationProposal, diffFlowSpec, formatFlowSpec, InvestigationLifecycleManager, listFeltDBModules, parseFlowSpec, planFlowSpecMigration, renderProposalDiagnostic, resolveFeltDBModule, resolveModuleIntent, startLocalDevelopmentAuthority, validateFlowSpec } from '@feltdb/core';
|
|
12
13
|
import { discoverWorkspace, ensureWorkspaceGitIgnored, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace, startPairingDiscoveryServer } from './workspace-integration.js';
|
|
13
14
|
import { applicationUrlCandidate, detectApplication, discoverApplicationUrl } from './application.js';
|
|
14
15
|
import { resolveApplicationLifecycle, startManagedApplication, waitForManagedApplication } from './application-lifecycle.js';
|
|
15
16
|
import { createDevelopmentSession, developmentSessionEnvironment, developmentSessionSummary, transitionDevelopmentSession } from './development-session.js';
|
|
16
17
|
import { openApplication, openStudio } from './browser-opening.js';
|
|
17
18
|
import { publishApplication } from './publish-engine.js';
|
|
18
|
-
import {
|
|
19
|
+
import { applyFlowProposalAtomic, assertProposalSourcePlan, describeProposal, loadLocalDevelopmentContract, repositoryContext, reviewFlowProposal, syncGeneratedSource } from './source-sync.js';
|
|
19
20
|
import { configureAiModel, generateConfiguredFlowProposal, publicAiModelConfiguration } from './ai-model-config.js';
|
|
20
21
|
import { proposalService } from './proposal-client.js';
|
|
22
|
+
import { resolveManagedAuthorityEnvironment } from './managed-environment.js';
|
|
23
|
+
import { readManagedProjectState } from './managed-project.js';
|
|
21
24
|
function loadProjectEnvironment(file = path.resolve('.env.local')) {
|
|
22
25
|
if (!fs.existsSync(file))
|
|
23
26
|
return;
|
|
@@ -87,6 +90,9 @@ export async function handleCommand(command, args) {
|
|
|
87
90
|
return handleAi(args);
|
|
88
91
|
case 'proposals':
|
|
89
92
|
return handleProposals(args);
|
|
93
|
+
case 'module':
|
|
94
|
+
case 'modules':
|
|
95
|
+
return handleModules(args);
|
|
90
96
|
case 'sync':
|
|
91
97
|
return handleSourceSync(args);
|
|
92
98
|
case 'help':
|
|
@@ -94,6 +100,24 @@ export async function handleCommand(command, args) {
|
|
|
94
100
|
return handleHelp();
|
|
95
101
|
}
|
|
96
102
|
}
|
|
103
|
+
function handleModules(args) {
|
|
104
|
+
const action = args[0] || 'list';
|
|
105
|
+
if (action === 'list') {
|
|
106
|
+
for (const module of listFeltDBModules())
|
|
107
|
+
console.log(`${module.id}\t${module.version}\t${module.compatibility.join(', ')}`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (action === 'inspect' && args[1]) {
|
|
111
|
+
const flowPath = path.resolve('feltdb.flow');
|
|
112
|
+
const spec = args.includes('--application') && fs.existsSync(flowPath) ? parseFlowSpec(fs.readFileSync(flowPath, 'utf8')) : undefined;
|
|
113
|
+
const declaration = spec?.modules?.find(value => value.name === args[1]);
|
|
114
|
+
const module = resolveFeltDBModule(declaration ?? { name: args[1], provider: args.find((value, index) => index > 1 && !value.startsWith('--')) || 'stripe-compatible' });
|
|
115
|
+
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)}`)) ?? [];
|
|
116
|
+
console.log(JSON.stringify({ ...module, application_usage: usage }, null, 2));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
throw new Error('Usage: feltdb module [list|inspect <id> [provider]]');
|
|
120
|
+
}
|
|
97
121
|
function proposalFile(args) {
|
|
98
122
|
const value = args.find(argument => !argument.startsWith('--') && !['review', 'apply'].includes(argument));
|
|
99
123
|
if (!value)
|
|
@@ -103,12 +127,18 @@ function proposalFile(args) {
|
|
|
103
127
|
function validateRepositoryBuild(args) {
|
|
104
128
|
const packagePath = path.resolve('package.json');
|
|
105
129
|
const projectPackage = fs.existsSync(packagePath) ? JSON.parse(fs.readFileSync(packagePath, 'utf8')) : {};
|
|
106
|
-
if (
|
|
130
|
+
if (args.includes('--no-build'))
|
|
131
|
+
return;
|
|
132
|
+
const verification = ['typecheck', 'test', 'build'].filter(name => projectPackage.scripts?.[name]);
|
|
133
|
+
if (!verification.length)
|
|
107
134
|
return;
|
|
108
135
|
console.log('Validating the updated application before recording applied status…');
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
136
|
+
for (const name of verification) {
|
|
137
|
+
const checked = spawnSync(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['run', name], { cwd: process.cwd(), stdio: 'inherit' });
|
|
138
|
+
if (checked.status !== 0)
|
|
139
|
+
throw new Error(`APPLICATION_VALIDATION_FAILED: ${name} exited ${checked.status ?? 'without a status'}; repository restored and proposal remains approved`);
|
|
140
|
+
console.log(`✓ ${name} passed`);
|
|
141
|
+
}
|
|
112
142
|
}
|
|
113
143
|
async function handleAi(args) {
|
|
114
144
|
loadProjectEnvironment();
|
|
@@ -141,6 +171,44 @@ async function handleAi(args) {
|
|
|
141
171
|
return handleStudio(args, studioUrl => console.log(`Ask FeltDB: ${studioUrl}`));
|
|
142
172
|
}
|
|
143
173
|
const action = args[0];
|
|
174
|
+
if (action === 'proposals')
|
|
175
|
+
return handleProposals(['list', ...args.slice(1)]);
|
|
176
|
+
if (action === 'proposal') {
|
|
177
|
+
const id = args[1];
|
|
178
|
+
if (!id)
|
|
179
|
+
throw new Error('Usage: feltdb ai proposal <id> [--history]');
|
|
180
|
+
return handleProposals(['get', id, ...args.slice(2)]);
|
|
181
|
+
}
|
|
182
|
+
if (action === 'approve') {
|
|
183
|
+
const id = args[1];
|
|
184
|
+
if (!id)
|
|
185
|
+
throw new Error('Usage: feltdb ai approve <id> [--yes]');
|
|
186
|
+
const client = proposalService();
|
|
187
|
+
const proposal = await client.get(id);
|
|
188
|
+
const readiness = await client.status(id);
|
|
189
|
+
if (proposal.status !== 'previewed')
|
|
190
|
+
throw new Error(`Cannot approve Proposal ${id}. Current status: ${proposal.status}. A current preview is required.`);
|
|
191
|
+
if (readiness.blockers.length)
|
|
192
|
+
throw new Error(`Cannot approve Proposal ${id}. ${readiness.blockers.join(', ')}. Regenerate the Proposal before approval.`);
|
|
193
|
+
if (!proposal.preview_id)
|
|
194
|
+
throw new Error(`Cannot approve Proposal ${id}. Preview is missing.`);
|
|
195
|
+
await client.getPreview(proposal.preview_id);
|
|
196
|
+
console.log(renderProposalDiagnostic(proposal, { readiness }).join('\n'));
|
|
197
|
+
console.log('Repository source will NOT be modified by approval.');
|
|
198
|
+
let approved = args.includes('--yes');
|
|
199
|
+
if (!approved && process.stdin.isTTY) {
|
|
200
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
201
|
+
approved = /^y(?:es)?$/i.test(await prompt.question('Approve this proposal? [y/N] '));
|
|
202
|
+
prompt.close();
|
|
203
|
+
}
|
|
204
|
+
if (!approved) {
|
|
205
|
+
console.log('Approval cancelled.');
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const value = await client.approve(id, 'cli-developer', args.includes('--approve-authorization'));
|
|
209
|
+
console.log(`✓ Proposal ${value.id} approved`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
144
212
|
if (action === 'context') {
|
|
145
213
|
const context = await loadLocalDevelopmentContract();
|
|
146
214
|
console.log(JSON.stringify({ snapshot: context.snapshot, flowHash: context.flowHash }, null, 2));
|
|
@@ -166,7 +234,7 @@ async function handleAi(args) {
|
|
|
166
234
|
proposedFlow: stored.proposed_flow, summary: stored.summary, changes: stored.contract_diff, warnings: stored.warnings }
|
|
167
235
|
: JSON.parse(fs.readFileSync(path.resolve(target), 'utf8'));
|
|
168
236
|
if (stored && stored.status !== 'approved')
|
|
169
|
-
throw new Error(`
|
|
237
|
+
throw new Error(`Cannot apply Proposal ${stored.id}. Current status: ${stored.status}. Approval is required. Run: feltdb ai approve ${stored.id}`);
|
|
170
238
|
if (!stored && !args.includes('--approve'))
|
|
171
239
|
throw new Error('Explicit approval is required: rerun with --approve');
|
|
172
240
|
const current = await loadLocalDevelopmentContract();
|
|
@@ -177,12 +245,26 @@ async function handleAi(args) {
|
|
|
177
245
|
if (alreadyWritten) {
|
|
178
246
|
console.log('Detected an approved proposal whose flow is already present; reconciling the interrupted application.');
|
|
179
247
|
const recoveredReview = await reviewFlowProposal({ ...proposal, baseContractHash: current.snapshot.contract_hash, baseFlowHash: current.flowHash });
|
|
180
|
-
|
|
181
|
-
|
|
248
|
+
await applyFlowProposalAtomic({ ...proposal, baseContractHash: current.snapshot.contract_hash, baseFlowHash: current.flowHash, proposedFlow: current.source }, process.cwd(), {
|
|
249
|
+
approveAuthorization: true, replaceGenerated: args.includes('--replace-generated'), verify: () => validateRepositoryBuild(args),
|
|
250
|
+
});
|
|
182
251
|
await proposalService().transition(stored.id, 'applied', 'cli-repository-applier', true);
|
|
183
252
|
console.log(`✓ Reconciled and marked ${stored.id} applied`);
|
|
184
253
|
return;
|
|
185
254
|
}
|
|
255
|
+
if (stored) {
|
|
256
|
+
const client = proposalService();
|
|
257
|
+
const readiness = await client.status(stored.id);
|
|
258
|
+
if (readiness.blockers.length)
|
|
259
|
+
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.`);
|
|
260
|
+
if (!stored.preview_id)
|
|
261
|
+
throw new Error(`Cannot apply Proposal ${stored.id}. A current preview is required.`);
|
|
262
|
+
await client.getPreview(stored.preview_id);
|
|
263
|
+
assertProposalSourcePlan(stored.source_plan);
|
|
264
|
+
const repository = await repositoryContext(process.cwd(), stored.source_plan.files.map(value => value.path));
|
|
265
|
+
if (repository.dirty.length && !args.includes('--allow-dirty'))
|
|
266
|
+
throw new Error(`Cannot apply Proposal ${stored.id}. Repository conflict: ${repository.dirty.join(', ')}. Resolve the conflict and retry.`);
|
|
267
|
+
}
|
|
186
268
|
const reviewOnly = await reviewFlowProposal(proposal);
|
|
187
269
|
console.log(`\n${proposal.summary || 'FeltDB change proposal'}`);
|
|
188
270
|
for (const line of describeProposal(reviewOnly))
|
|
@@ -192,20 +274,67 @@ async function handleAi(args) {
|
|
|
192
274
|
console.log('✓ Preview complete. No files, flow, contract, or published state were changed.');
|
|
193
275
|
return;
|
|
194
276
|
}
|
|
195
|
-
|
|
277
|
+
let confirmed = args.includes('--yes');
|
|
278
|
+
if (!confirmed && process.stdin.isTTY) {
|
|
279
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
280
|
+
confirmed = /^y(?:es)?$/i.test(await prompt.question(`Apply ${stored?.id || 'this proposal'} to repository source? [y/N] `));
|
|
281
|
+
prompt.close();
|
|
282
|
+
}
|
|
283
|
+
if (!confirmed)
|
|
284
|
+
throw new Error('Apply cancelled. Re-run with --yes for non-interactive application.');
|
|
285
|
+
const review = await applyFlowProposalAtomic(proposal, process.cwd(), {
|
|
196
286
|
allowDirty: args.includes('--allow-dirty'),
|
|
197
287
|
approveAuthorization: args.includes('--approve-authorization'),
|
|
198
288
|
replaceGenerated: args.includes('--replace-generated'),
|
|
289
|
+
verify: () => validateRepositoryBuild(args),
|
|
199
290
|
});
|
|
200
291
|
console.log(`✓ Applied validated flow proposal (${review.proposedContract.hashes.application})`);
|
|
201
292
|
for (const file of review.written)
|
|
202
293
|
console.log(` ${file}`);
|
|
203
|
-
validateRepositoryBuild(args);
|
|
204
294
|
if (stored)
|
|
205
295
|
await proposalService().transition(stored.id, 'applied', 'cli-repository-applier', true);
|
|
206
296
|
return;
|
|
207
297
|
}
|
|
208
|
-
|
|
298
|
+
const developerIntent = args.filter(value => !value.startsWith('--')).join(' ').trim();
|
|
299
|
+
if (developerIntent) {
|
|
300
|
+
const resolved = resolveModuleIntent(developerIntent);
|
|
301
|
+
if (resolved) {
|
|
302
|
+
console.log('Understanding application...');
|
|
303
|
+
const current = await loadLocalDevelopmentContract();
|
|
304
|
+
console.log('✓ Application contract loaded');
|
|
305
|
+
const proposal = await createModuleImplementationProposal({ intent: developerIntent, snapshot: current.snapshot, currentFlow: current.source });
|
|
306
|
+
console.log(`✓ ${proposal.implementation?.module.name} ${proposal.implementation?.module.version} resolved`);
|
|
307
|
+
console.log(`✓ Billing owner: ${proposal.implementation?.owner}`);
|
|
308
|
+
const review = await reviewFlowProposal(proposal);
|
|
309
|
+
if (proposal.implementation?.alreadyInstalled) {
|
|
310
|
+
console.log(`${proposal.implementation.module.name} is already installed.`);
|
|
311
|
+
const missing = [...review.diff.added, ...review.diff.changed];
|
|
312
|
+
if (!missing.length) {
|
|
313
|
+
console.log('Current implementation is complete. No proposal created.');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
console.log('Missing:');
|
|
317
|
+
for (const value of missing)
|
|
318
|
+
console.log(` ${value}`);
|
|
319
|
+
console.log('Creating a completion proposal.');
|
|
320
|
+
}
|
|
321
|
+
console.log('Generating implementation proposal...');
|
|
322
|
+
const client = proposalService();
|
|
323
|
+
const parent = (await client.list({ status: 'applied', limit: 100 })).proposals.find(value => value.metadata?.module === resolved.module);
|
|
324
|
+
let stored = await client.create({ application_id: current.snapshot.application.application_id, environment: current.snapshot.application.environment,
|
|
325
|
+
base_contract_hash: proposal.baseContractHash, base_flow_hash: proposal.baseFlowHash, proposed_flow: proposal.proposedFlow,
|
|
326
|
+
proposal_version: 1, contract_diff: [], source_plan: { files: [] },
|
|
327
|
+
summary: proposal.summary, warnings: proposal.warnings, actor: 'cli-developer', parent_proposal_id: parent?.id, metadata: { created_from: 'module-intent', module: resolved.module, module_version: resolved.version } });
|
|
328
|
+
await client.validate(stored.id, 'feltdb-validator', review.proposedContract.manifest);
|
|
329
|
+
const preview = await client.preview(stored.id, 'cli-developer');
|
|
330
|
+
stored = preview.proposal;
|
|
331
|
+
const readiness = await client.status(stored.id);
|
|
332
|
+
console.log(renderProposalDiagnostic(stored, { readiness, preview: preview }).join('\n'));
|
|
333
|
+
console.log(`Use:\n feltdb ai proposal ${stored.id}`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
throw new Error('Usage: feltdb ai ["implement <module>"|context|proposals|proposal <id>|approve <id>|review <proposal.json>|apply <id>]');
|
|
209
338
|
}
|
|
210
339
|
async function handleProposals(args) {
|
|
211
340
|
loadProjectEnvironment();
|
|
@@ -213,9 +342,18 @@ async function handleProposals(args) {
|
|
|
213
342
|
const action = args[0] || 'list';
|
|
214
343
|
const id = args[1];
|
|
215
344
|
if (action === 'list') {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
345
|
+
const option = (name) => args.includes(name) ? args[args.indexOf(name) + 1] : undefined;
|
|
346
|
+
const result = await client.list({ status: option('--status'), environment: option('--environment'), limit: option('--limit') ? Number(option('--limit')) : undefined, cursor: option('--cursor') });
|
|
347
|
+
if (args.includes('--json')) {
|
|
348
|
+
console.log(JSON.stringify(result, null, 2));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const { proposals } = result;
|
|
352
|
+
console.log('PROPOSALS');
|
|
353
|
+
for (const value of proposals)
|
|
354
|
+
console.log(`${value.id}\t${value.status.toUpperCase()}\t${value.summary}\t${value.created_at}`);
|
|
355
|
+
if (result.pagination?.nextCursor)
|
|
356
|
+
console.log(`Next: --cursor ${result.pagination.nextCursor}`);
|
|
219
357
|
if (!proposals.length)
|
|
220
358
|
console.log('No proposals.');
|
|
221
359
|
return;
|
|
@@ -223,7 +361,14 @@ async function handleProposals(args) {
|
|
|
223
361
|
if (!id)
|
|
224
362
|
throw new Error(`feltdb proposals ${action} requires a proposal id`);
|
|
225
363
|
if (action === 'get') {
|
|
226
|
-
|
|
364
|
+
const proposal = await client.get(id);
|
|
365
|
+
const [readiness, history] = await Promise.all([client.status(id), client.history(id)]);
|
|
366
|
+
const preview = proposal.preview_id ? await client.getPreview(proposal.preview_id).catch(() => undefined) : undefined;
|
|
367
|
+
const result = { proposal, readiness, preview, history: history.events };
|
|
368
|
+
if (args.includes('--json'))
|
|
369
|
+
console.log(JSON.stringify(result, null, 2));
|
|
370
|
+
else
|
|
371
|
+
console.log(renderProposalDiagnostic(proposal, { readiness, history: history.events, preview: preview }).join('\n'));
|
|
227
372
|
return;
|
|
228
373
|
}
|
|
229
374
|
if (action === 'diff') {
|
|
@@ -233,7 +378,7 @@ async function handleProposals(args) {
|
|
|
233
378
|
return;
|
|
234
379
|
}
|
|
235
380
|
if (action === 'approve' || action === 'reject') {
|
|
236
|
-
const value =
|
|
381
|
+
const value = action === 'approve' ? await client.approve(id, 'local-developer', args.includes('--approve-authorization')) : await client.reject(id, 'local-developer');
|
|
237
382
|
console.log(`✓ ${value.id} ${value.status}`);
|
|
238
383
|
return;
|
|
239
384
|
}
|
|
@@ -617,9 +762,11 @@ For more info, visit: https://github.com/rkendel1/feltdb
|
|
|
617
762
|
}
|
|
618
763
|
async function handleConnect(args) {
|
|
619
764
|
loadProjectEnvironment();
|
|
765
|
+
const managed = readManagedProjectState();
|
|
620
766
|
const projectFile = path.resolve('feltdb.config.json');
|
|
621
767
|
const project = fs.existsSync(projectFile) ? JSON.parse(fs.readFileSync(projectFile, 'utf8')) : {};
|
|
622
768
|
const url = (args[0]
|
|
769
|
+
|| managed.url
|
|
623
770
|
|| process.env.VITE_FELTDB_MANAGED_URL
|
|
624
771
|
|| process.env.VITE_FELTDB_URL
|
|
625
772
|
|| project.server?.url
|
|
@@ -634,8 +781,8 @@ async function handleConnect(args) {
|
|
|
634
781
|
let health;
|
|
635
782
|
try {
|
|
636
783
|
const response = await fetch(`${url}/health`, {
|
|
637
|
-
headers: process.env.
|
|
638
|
-
? { Authorization: `Bearer ${process.env.
|
|
784
|
+
headers: process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY
|
|
785
|
+
? { Authorization: `Bearer ${process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY}` }
|
|
639
786
|
: {},
|
|
640
787
|
signal: controller.signal,
|
|
641
788
|
});
|
|
@@ -658,14 +805,14 @@ async function handleConnect(args) {
|
|
|
658
805
|
fs.writeFileSync(connFile, JSON.stringify({
|
|
659
806
|
url,
|
|
660
807
|
connected: new Date().toISOString(),
|
|
661
|
-
authenticated: Boolean(process.env.
|
|
662
|
-
namespace:
|
|
808
|
+
authenticated: Boolean(process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY),
|
|
809
|
+
namespace: managed.namespace || project.namespace || 'default',
|
|
663
810
|
runtime: project.runtime || 'self-hosted',
|
|
664
811
|
}, null, 2));
|
|
665
812
|
console.log('✅ Connected to FeltDB\n');
|
|
666
813
|
console.log('Endpoint: ' + url);
|
|
667
|
-
console.log(`Namespace: ${
|
|
668
|
-
console.log(`Authenticated: ${process.env.
|
|
814
|
+
console.log(`Namespace: ${managed.namespace || project.namespace || 'default'}`);
|
|
815
|
+
console.log(`Authenticated: ${process.env.FELTDB_TOKEN || process.env.VITE_FELTDB_API_KEY ? 'yes' : 'no'}`);
|
|
669
816
|
console.log(`Runtime: ${project.runtime || 'self-hosted'}`);
|
|
670
817
|
console.log(`Health: ${health.status || 'healthy'}\n`);
|
|
671
818
|
console.log('Use: feltdb status (to check connection)\n');
|
|
@@ -780,7 +927,7 @@ async function handleWorkspaceConnect(args) {
|
|
|
780
927
|
console.log(' (when @feltdb/client workspace APIs are available)\n');
|
|
781
928
|
}
|
|
782
929
|
async function handleDev(args) {
|
|
783
|
-
var _a
|
|
930
|
+
var _a;
|
|
784
931
|
const projectDir = process.cwd();
|
|
785
932
|
if (args.includes('--help')) {
|
|
786
933
|
console.log('Usage: feltdb dev [--app-url URL] [--studio-port 7701] [--authority-port 7700] [--discovery-port 7799] [--no-open]');
|
|
@@ -833,14 +980,10 @@ async function handleDev(args) {
|
|
|
833
980
|
? { ...configDefaults, ...JSON.parse(fs.readFileSync(configPath, 'utf-8')) }
|
|
834
981
|
: configDefaults;
|
|
835
982
|
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.');
|
|
983
|
+
resolveManagedAuthorityEnvironment(process.env);
|
|
841
984
|
}
|
|
842
985
|
const runtimeNamespace = config.runtime === 'managed'
|
|
843
|
-
?
|
|
986
|
+
? readManagedProjectState(projectDir).namespace || config.namespace
|
|
844
987
|
: config.namespace;
|
|
845
988
|
const applicationLifecycle = resolveApplicationLifecycle(config);
|
|
846
989
|
// Application URL precedence:
|
|
@@ -929,7 +1072,7 @@ async function handleDev(args) {
|
|
|
929
1072
|
if (compose.status !== 0)
|
|
930
1073
|
throw new Error(`docker compose failed with status ${compose.status ?? 'unknown'}`);
|
|
931
1074
|
selfHostedStarted = true;
|
|
932
|
-
(
|
|
1075
|
+
(_a = process.env).VITE_FELTDB_URL || (_a.VITE_FELTDB_URL = 'http://127.0.0.1:7700');
|
|
933
1076
|
}
|
|
934
1077
|
if (!process.env.VITE_FELTDB_URL) {
|
|
935
1078
|
const authorityPort = Number(args.includes('--authority-port') ? args[args.indexOf('--authority-port') + 1] || '7700' : '7700');
|
|
@@ -1403,10 +1546,11 @@ async function handleStudio(args, onReady) {
|
|
|
1403
1546
|
loadProjectEnvironment();
|
|
1404
1547
|
const projectConfigPath = path.resolve('feltdb.config.json');
|
|
1405
1548
|
const projectConfig = fs.existsSync(projectConfigPath) ? JSON.parse(fs.readFileSync(projectConfigPath, 'utf8')) : {};
|
|
1549
|
+
const managedProject = readManagedProjectState();
|
|
1406
1550
|
const projectIsManaged = projectConfig.runtime === 'managed';
|
|
1407
1551
|
const connectUrl = args.includes('--connect')
|
|
1408
1552
|
? args[args.indexOf('--connect') + 1]
|
|
1409
|
-
: projectIsManaged ? process.env.VITE_FELTDB_MANAGED_URL : undefined;
|
|
1553
|
+
: projectIsManaged ? process.env.VITE_FELTDB_MANAGED_URL || managedProject.url : undefined;
|
|
1410
1554
|
const port = args.includes('--port')
|
|
1411
1555
|
? args[args.indexOf('--port') + 1] || '7701'
|
|
1412
1556
|
: '7701';
|
|
@@ -1416,10 +1560,16 @@ async function handleStudio(args, onReady) {
|
|
|
1416
1560
|
const open = !args.includes('--no-open');
|
|
1417
1561
|
const namespace = args.includes('--namespace')
|
|
1418
1562
|
? args[args.indexOf('--namespace') + 1] || 'default'
|
|
1419
|
-
: projectIsManaged ?
|
|
1563
|
+
: projectIsManaged ? managedProject.namespace || projectConfig.namespace || 'default' : 'default';
|
|
1420
1564
|
const runtime = args.includes('--runtime')
|
|
1421
1565
|
? args[args.indexOf('--runtime') + 1] || 'browser'
|
|
1422
1566
|
: projectIsManaged ? 'managed' : connectUrl ? 'remote' : 'browser';
|
|
1567
|
+
const applicationId = args.includes('--application')
|
|
1568
|
+
? args[args.indexOf('--application') + 1] || ''
|
|
1569
|
+
: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || managedProject.applicationId || '';
|
|
1570
|
+
const environment = args.includes('--environment')
|
|
1571
|
+
? args[args.indexOf('--environment') + 1] || 'production'
|
|
1572
|
+
: managedProject.environment || 'production';
|
|
1423
1573
|
const appUrl = args.includes('--app-url')
|
|
1424
1574
|
? args[args.indexOf('--app-url') + 1]
|
|
1425
1575
|
: undefined;
|
|
@@ -1465,14 +1615,14 @@ async function handleStudio(args, onReady) {
|
|
|
1465
1615
|
response.setHeader('Cache-Control', 'no-store');
|
|
1466
1616
|
response.setHeader('Pragma', 'no-cache');
|
|
1467
1617
|
const managedPublish = runtime === 'managed' ? {
|
|
1468
|
-
url:
|
|
1469
|
-
namespace:
|
|
1470
|
-
tenantId:
|
|
1471
|
-
applicationId: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || '',
|
|
1472
|
-
environment:
|
|
1618
|
+
url: connectUrl || managedProject.url || '',
|
|
1619
|
+
namespace: managedProject.namespace || namespace,
|
|
1620
|
+
tenantId: managedProject.tenantId || '',
|
|
1621
|
+
applicationId: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || managedProject.applicationId || '',
|
|
1622
|
+
environment: managedProject.environment || 'production',
|
|
1473
1623
|
publishEndpoint: '/_feltdb/publish',
|
|
1474
1624
|
} : undefined;
|
|
1475
|
-
response.end(JSON.stringify({ token: connectUrl ? process.env.VITE_FELTDB_API_KEY ||
|
|
1625
|
+
response.end(JSON.stringify({ token: connectUrl ? process.env.VITE_FELTDB_API_KEY || '' : '', managedPublish, session, sourceSync: { enabled: true, webllmEnabled, previewOnly, token: studioMutationToken, aiModel: publicAiModelConfiguration() } }));
|
|
1476
1626
|
return;
|
|
1477
1627
|
}
|
|
1478
1628
|
if (pathname === '/_feltdb/contract') {
|
|
@@ -1484,12 +1634,12 @@ async function handleStudio(args, onReady) {
|
|
|
1484
1634
|
? JSON.parse(fs.readFileSync(path.resolve('.feltdb/last-published.json'), 'utf8')) : undefined;
|
|
1485
1635
|
let runningContract;
|
|
1486
1636
|
let runningSnapshot;
|
|
1487
|
-
const applicationId = process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || lastPublished?.applicationId;
|
|
1637
|
+
const applicationId = process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || managedProject.applicationId || lastPublished?.applicationId;
|
|
1488
1638
|
if (connectUrl && applicationId) {
|
|
1489
1639
|
try {
|
|
1490
1640
|
const endpoint = new URL('/v1/application', connectUrl);
|
|
1491
1641
|
endpoint.searchParams.set('application_id', applicationId);
|
|
1492
|
-
endpoint.searchParams.set('environment',
|
|
1642
|
+
endpoint.searchParams.set('environment', managedProject.environment || lastPublished?.environment || 'production');
|
|
1493
1643
|
const headers = {};
|
|
1494
1644
|
const credential = process.env.FELTDB_MANAGED_CONTROL_API_KEY || process.env.VITE_FELTDB_API_KEY;
|
|
1495
1645
|
if (credential)
|
|
@@ -1514,6 +1664,29 @@ async function handleStudio(args, onReady) {
|
|
|
1514
1664
|
}
|
|
1515
1665
|
return;
|
|
1516
1666
|
}
|
|
1667
|
+
if (pathname === '/_feltdb/repository-readiness' && request.method === 'GET') {
|
|
1668
|
+
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
1669
|
+
response.setHeader('Cache-Control', 'no-store');
|
|
1670
|
+
try {
|
|
1671
|
+
const proposalId = new URL(request.url || '/', 'http://localhost').searchParams.get('proposal');
|
|
1672
|
+
if (!proposalId)
|
|
1673
|
+
throw new Error('proposal is required');
|
|
1674
|
+
const proposal = await proposalClient.get(proposalId);
|
|
1675
|
+
assertProposalSourcePlan(proposal.source_plan);
|
|
1676
|
+
const local = await loadLocalDevelopmentContract();
|
|
1677
|
+
const repository = await repositoryContext(process.cwd(), proposal.source_plan.files.map(value => value.path));
|
|
1678
|
+
const contractMatches = local.snapshot.contract_hash === proposal.base_contract_hash;
|
|
1679
|
+
const flowMatches = local.flowHash === proposal.base_flow_hash;
|
|
1680
|
+
response.end(JSON.stringify({ ready: contractMatches && flowMatches && repository.dirty.length === 0,
|
|
1681
|
+
contractMatches, flowMatches, gitClean: repository.dirty.length === 0, conflicts: repository.dirty,
|
|
1682
|
+
branch: repository.branch, commit: repository.commit }));
|
|
1683
|
+
}
|
|
1684
|
+
catch (error) {
|
|
1685
|
+
response.statusCode = 409;
|
|
1686
|
+
response.end(JSON.stringify({ error: error instanceof Error ? error.message : String(error) }));
|
|
1687
|
+
}
|
|
1688
|
+
return;
|
|
1689
|
+
}
|
|
1517
1690
|
if (pathname === '/_feltdb/proposals/list' && request.method === 'GET') {
|
|
1518
1691
|
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
1519
1692
|
response.setHeader('Cache-Control', 'no-store');
|
|
@@ -1578,6 +1751,7 @@ async function handleStudio(args, onReady) {
|
|
|
1578
1751
|
stored = await proposalClient.create({ application_id: current.snapshot.application.application_id,
|
|
1579
1752
|
environment: current.snapshot.application.environment, base_contract_hash: proposal.baseContractHash,
|
|
1580
1753
|
base_flow_hash: proposal.baseFlowHash, proposed_flow: proposal.proposedFlow, summary: proposal.summary,
|
|
1754
|
+
proposal_version: 1, contract_diff: [], source_plan: { files: [] },
|
|
1581
1755
|
warnings: proposal.warnings, actor: 'studio-developer', metadata: { model: publicAiModelConfiguration().model, created_from: 'studio' } });
|
|
1582
1756
|
await proposalClient.validate(stored.id, 'feltdb-validator', review.proposedContract.manifest);
|
|
1583
1757
|
stored = (await proposalClient.preview(stored.id, 'studio-developer')).proposal;
|
|
@@ -1715,6 +1889,9 @@ async function handleStudio(args, onReady) {
|
|
|
1715
1889
|
const parameters = new URLSearchParams({ namespace, runtime });
|
|
1716
1890
|
if (connectUrl)
|
|
1717
1891
|
parameters.set('connect', connectUrl);
|
|
1892
|
+
if (applicationId)
|
|
1893
|
+
parameters.set('application_id', applicationId);
|
|
1894
|
+
parameters.set('environment', environment);
|
|
1718
1895
|
if (appUrl)
|
|
1719
1896
|
parameters.set('app', appUrl);
|
|
1720
1897
|
if (session.workspaceId)
|
|
@@ -1751,9 +1928,14 @@ Commands:
|
|
|
1751
1928
|
ai model show Show the selected local or user-provided model
|
|
1752
1929
|
ai model set Select WebLLM or an OpenAI-compatible model
|
|
1753
1930
|
ai context Show the secret-free contract grounding snapshot
|
|
1931
|
+
ai proposals List bounded proposals [--status/--environment/--json]
|
|
1932
|
+
ai proposal <id> Show canonical proposal, readiness, preview, and history
|
|
1933
|
+
ai approve <id> Approve a current preview [--yes]
|
|
1754
1934
|
ai review <file> Validate and review a WebLLM flow proposal
|
|
1755
|
-
ai apply <id>
|
|
1935
|
+
ai apply <id> Atomically apply an approved proposal [--yes]
|
|
1756
1936
|
proposals List durable FeltDB application proposals
|
|
1937
|
+
module list List deterministic FeltDB modules
|
|
1938
|
+
module inspect <id> Inspect a resolved module semantic contract
|
|
1757
1939
|
proposals get <id> Show a proposal and its lifecycle history
|
|
1758
1940
|
proposals diff <id> Show contract and structured source impact
|
|
1759
1941
|
proposals approve <id> Approve a previewed proposal
|
|
@@ -1776,6 +1958,7 @@ Commands:
|
|
|
1776
1958
|
Studio:
|
|
1777
1959
|
feltdb studio Launch local Studio
|
|
1778
1960
|
feltdb studio --connect <url> Connect to remote instance
|
|
1961
|
+
--application <id> --environment <name> Select one application environment
|
|
1779
1962
|
feltdb studio --port 7701 --no-open Start on port 7701 without opening browser
|
|
1780
1963
|
|
|
1781
1964
|
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
|
+
}
|
|
@@ -9,10 +9,11 @@ export function developmentProposalConnection(cwd = process.cwd()) {
|
|
|
9
9
|
throw new Error('No running FeltDB proposal service. Start feltdb dev.');
|
|
10
10
|
const config = fs.existsSync(path.join(cwd, 'feltdb.config.json')) ? JSON.parse(fs.readFileSync(path.join(cwd, 'feltdb.config.json'), 'utf8')) : {};
|
|
11
11
|
const published = fs.existsSync(path.join(cwd, '.feltdb', 'last-published.json')) ? JSON.parse(fs.readFileSync(path.join(cwd, '.feltdb', 'last-published.json'), 'utf8')) : {};
|
|
12
|
+
const managed = fs.existsSync(path.join(cwd, '.feltdb', 'managed.json')) ? JSON.parse(fs.readFileSync(path.join(cwd, '.feltdb', 'managed.json'), 'utf8')) : {};
|
|
12
13
|
const flowApplication = fs.existsSync(path.join(cwd, 'feltdb.flow')) ? parseFlowSpec(fs.readFileSync(path.join(cwd, 'feltdb.flow'), 'utf8')).app : undefined;
|
|
13
14
|
return { endpoint: endpoint.replace(/\/$/, ''), proposalAuthorityToken: stored?.proposalAuthorityToken,
|
|
14
|
-
applicationId: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || published.applicationId || config.applicationId || flowApplication,
|
|
15
|
-
environment:
|
|
15
|
+
applicationId: process.env.VITE_FELTDB_MANAGED_APPLICATION_ID || managed.applicationId || published.applicationId || config.applicationId || flowApplication,
|
|
16
|
+
environment: managed.environment || published.environment || 'local' };
|
|
16
17
|
}
|
|
17
18
|
export class ProposalServiceClient {
|
|
18
19
|
constructor(connection, token = process.env.FELTDB_MANAGED_CONTROL_API_KEY || '') {
|
|
@@ -38,11 +39,19 @@ export class ProposalServiceClient {
|
|
|
38
39
|
}
|
|
39
40
|
actor(input = {}) { return { application_id: this.connection.applicationId, environment: this.connection.environment, ...input }; }
|
|
40
41
|
create(input) { return this.request('/v1/proposals', { method: 'POST', body: JSON.stringify(input) }); }
|
|
41
|
-
list() {
|
|
42
|
+
list(filters = {}) { const query = new URLSearchParams(); if (this.connection.applicationId)
|
|
43
|
+
query.set('application_id', this.connection.applicationId); query.set('environment', filters.environment || this.connection.environment || 'local'); if (filters.status)
|
|
44
|
+
query.set('status', filters.status); if (filters.limit)
|
|
45
|
+
query.set('limit', String(filters.limit)); if (filters.cursor)
|
|
46
|
+
query.set('cursor', filters.cursor); return this.request(`/v1/proposals?${query}`); }
|
|
42
47
|
get(id) { return this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}`)); }
|
|
43
48
|
history(id) { return this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/history`)); }
|
|
49
|
+
status(id) { return this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/status`)); }
|
|
44
50
|
validate(id, actor = 'local-developer', proposedManifest) { return this.request(`/v1/proposals/${encodeURIComponent(id)}/validate`, { method: 'POST', body: JSON.stringify(this.actor({ actor, proposed_manifest: proposedManifest })) }); }
|
|
45
51
|
preview(id, actor = 'local-developer') { return this.request(`/v1/proposals/${encodeURIComponent(id)}/preview`, { method: 'POST', body: JSON.stringify(this.actor({ actor })) }); }
|
|
52
|
+
getPreview(id) { return this.request(this.scoped(`/v1/previews/${encodeURIComponent(id)}`)); }
|
|
53
|
+
approve(id, actor = 'local-developer', approveAuthorization = false) { return this.request(`/v1/proposals/${encodeURIComponent(id)}/approve`, { method: 'POST', body: JSON.stringify(this.actor({ actor, approve_authorization: approveAuthorization })) }); }
|
|
54
|
+
reject(id, actor = 'local-developer', reason) { return this.request(`/v1/proposals/${encodeURIComponent(id)}/reject`, { method: 'POST', body: JSON.stringify(this.actor({ actor, reason })) }); }
|
|
46
55
|
transition(id, status, actor = 'local-developer', repositoryAuthority = false, approveAuthorization = false) {
|
|
47
56
|
return this.request(`/v1/proposals/${encodeURIComponent(id)}/status`, { method: 'PATCH', headers: repositoryAuthority && this.connection.proposalAuthorityToken
|
|
48
57
|
? { 'FeltDB-Proposal-Authority': this.connection.proposalAuthorityToken, 'FeltDB-Repository-Apply': '1' } : {}, body: JSON.stringify(this.actor({ status, actor, approve_authorization: approveAuthorization })) });
|
|
@@ -25,12 +25,14 @@ export function discoverPublishConfiguration(cwd = process.cwd(), environmentOve
|
|
|
25
25
|
throw new Error('feltdb.config.json is required for publish');
|
|
26
26
|
const project = JSON.parse(fs.readFileSync(configFile, 'utf8'));
|
|
27
27
|
const file = loadEnv(path.join(cwd, '.env.local'));
|
|
28
|
+
const managedFile = path.join(cwd, '.feltdb', 'managed.json');
|
|
29
|
+
const managed = fs.existsSync(managedFile) ? JSON.parse(fs.readFileSync(managedFile, 'utf8')) : {};
|
|
28
30
|
const value = (name) => process.env[name] || file[name];
|
|
29
31
|
const configuration = {
|
|
30
|
-
url: value('VITE_FELTDB_MANAGED_URL'), token: value('FELTDB_MANAGED_CONTROL_API_KEY'),
|
|
31
|
-
tenantId: value('VITE_FELTDB_MANAGED_TENANT_ID'), applicationId: value('VITE_FELTDB_MANAGED_APPLICATION_ID'),
|
|
32
|
-
namespace: value('VITE_FELTDB_MANAGED_NAMESPACE') || project.namespace,
|
|
33
|
-
environment: environmentOverride || value('VITE_FELTDB_MANAGED_ENVIRONMENT') || 'production',
|
|
32
|
+
url: value('VITE_FELTDB_MANAGED_URL') || managed.url, token: value('FELTDB_MANAGED_CONTROL_API_KEY'),
|
|
33
|
+
tenantId: managed.tenantId || value('VITE_FELTDB_MANAGED_TENANT_ID'), applicationId: value('VITE_FELTDB_MANAGED_APPLICATION_ID') || managed.applicationId,
|
|
34
|
+
namespace: managed.namespace || value('VITE_FELTDB_MANAGED_NAMESPACE') || project.namespace,
|
|
35
|
+
environment: environmentOverride || managed.environment || value('VITE_FELTDB_MANAGED_ENVIRONMENT') || 'production',
|
|
34
36
|
};
|
|
35
37
|
const missing = Object.entries(configuration).filter(([, value]) => !value).map(([name]) => name);
|
|
36
38
|
if (missing.length)
|
|
@@ -75,6 +77,7 @@ export function buildPublishBundle(cwd, configuration) {
|
|
|
75
77
|
'feltdb.agent_hash': contractHash(manifest.agents),
|
|
76
78
|
'feltdb.capability_hash': contractHash(manifest.capabilities),
|
|
77
79
|
'feltdb.contract_hash': contractHash({ dsl_version: spec.version, manifest }),
|
|
80
|
+
'feltdb.flow_hash': `sha256:${sha256(flowSource.replace(/\r\n/g, '\n'))}`,
|
|
78
81
|
'feltdb.artifact_hash': artifactHash,
|
|
79
82
|
'feltdb.namespace': configuration.namespace,
|
|
80
83
|
};
|