@feltdb/core 0.8.0 → 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/dist/application-development.d.ts +7 -2
- package/dist/application-development.d.ts.map +1 -1
- package/dist/application-development.js +7 -13
- package/dist/application-manifest.d.ts +1 -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/cli/ai-model-config.js +3 -3
- package/dist/cli/commands.js +131 -38
- package/dist/cli/proposal-client.js +5 -1
- package/dist/cli/publish-engine.js +1 -1
- package/dist/cli/repository-evidence.js +55 -0
- package/dist/cli/source-sync.js +190 -75
- package/dist/convergence.d.ts +55 -0
- package/dist/convergence.d.ts.map +1 -0
- package/dist/convergence.js +63 -0
- package/dist/create/managed-account.js +1 -0
- package/dist/create/package-versions.js +1 -1
- package/dist/create/server-source/crates/feltdb/src/application.rs +4 -0
- package/dist/create/server-source/crates/feltdb-server/src/connections.rs +14 -0
- package/dist/create/server-source/crates/feltdb-server/src/main.rs +174 -24
- package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +8 -1
- 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.map +1 -1
- package/dist/flowspec.js +6 -1
- package/dist/http-client.d.ts +32 -1
- package/dist/http-client.d.ts.map +1 -1
- package/dist/http-client.js +14 -2
- 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 +3 -0
- package/dist/index-core.d.ts.map +1 -1
- package/dist/index-core.js +3 -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 +19 -5
- package/dist/module-intent.d.ts.map +1 -1
- package/dist/module-intent.js +211 -62
- package/dist/module.d.ts +41 -12
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +543 -38
- package/dist/proposal.d.ts +13 -1
- package/dist/proposal.d.ts.map +1 -1
- package/dist/proposal.js +21 -4
- package/dist/studio/components/AskFeltDB.d.ts.map +1 -1
- package/dist/studio/components/ProposalBrowser.d.ts +1 -1
- package/dist/studio/components/ProposalBrowser.d.ts.map +1 -1
- package/dist/studio/components/index.js +1 -1
- package/dist/studio/{components-CNSNrmA9.js → components-Duq2xQWt.js} +251 -193
- package/dist/studio/index.js +1 -1
- package/dist/studio/studio.css +1 -1
- package/dist/studio/utils/proposal-api.d.ts +4 -1
- package/dist/studio/utils/proposal-api.d.ts.map +1 -1
- package/dist/studio/utils/proposal-api.js +3 -0
- package/dist/studio-app/assets/{feltdb_wasm-CSB6KVgw.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-yazaWMUN.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 +65 -2
- package/package.json +1 -1
- package/dist/studio-app/assets/dist-CCOk39Uc.js +0 -1
- package/dist/studio-app/assets/feltdb_wasm_bg-Dd1fnO9U.wasm +0 -0
- package/dist/studio-app/assets/index-Bncji3aN.js +0 -43
- package/dist/studio-app/assets/lib-B2_7fcn7.js +0 -69
- package/dist/studio-app/assets/worker-CARZ5g7K.js +0 -69
package/dist/cli/commands.js
CHANGED
|
@@ -9,7 +9,9 @@ import { createRequire } from 'module';
|
|
|
9
9
|
import { spawn, spawnSync } from 'child_process';
|
|
10
10
|
import { randomBytes } from 'crypto';
|
|
11
11
|
import { createInterface } from 'node:readline/promises';
|
|
12
|
-
import { createFeltDB,
|
|
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';
|
|
13
15
|
import { discoverWorkspace, ensureWorkspaceGitIgnored, generatePairingToken, persistPairingToken, displayWorkspaceStatus, initializeWorkspace, startPairingDiscoveryServer } from './workspace-integration.js';
|
|
14
16
|
import { applicationUrlCandidate, detectApplication, discoverApplicationUrl } from './application.js';
|
|
15
17
|
import { resolveApplicationLifecycle, startManagedApplication, waitForManagedApplication } from './application-lifecycle.js';
|
|
@@ -111,7 +113,8 @@ function handleModules(args) {
|
|
|
111
113
|
const flowPath = path.resolve('feltdb.flow');
|
|
112
114
|
const spec = args.includes('--application') && fs.existsSync(flowPath) ? parseFlowSpec(fs.readFileSync(flowPath, 'utf8')) : undefined;
|
|
113
115
|
const declaration = spec?.modules?.find(value => value.name === args[1]);
|
|
114
|
-
const
|
|
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 || '' });
|
|
115
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)}`)) ?? [];
|
|
116
119
|
console.log(JSON.stringify({ ...module, application_usage: usage }, null, 2));
|
|
117
120
|
return;
|
|
@@ -140,6 +143,30 @@ function validateRepositoryBuild(args) {
|
|
|
140
143
|
console.log(`✓ ${name} passed`);
|
|
141
144
|
}
|
|
142
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;
|
|
169
|
+
}
|
|
143
170
|
async function handleAi(args) {
|
|
144
171
|
loadProjectEnvironment();
|
|
145
172
|
if (args[0] === 'model') {
|
|
@@ -171,6 +198,16 @@ async function handleAi(args) {
|
|
|
171
198
|
return handleStudio(args, studioUrl => console.log(`Ask FeltDB: ${studioUrl}`));
|
|
172
199
|
}
|
|
173
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
|
+
}
|
|
174
211
|
if (action === 'proposals')
|
|
175
212
|
return handleProposals(['list', ...args.slice(1)]);
|
|
176
213
|
if (action === 'proposal') {
|
|
@@ -185,7 +222,7 @@ async function handleAi(args) {
|
|
|
185
222
|
throw new Error('Usage: feltdb ai approve <id> [--yes]');
|
|
186
223
|
const client = proposalService();
|
|
187
224
|
const proposal = await client.get(id);
|
|
188
|
-
const readiness = await client
|
|
225
|
+
const readiness = await currentProposalReadiness(client, proposal);
|
|
189
226
|
if (proposal.status !== 'previewed')
|
|
190
227
|
throw new Error(`Cannot approve Proposal ${id}. Current status: ${proposal.status}. A current preview is required.`);
|
|
191
228
|
if (readiness.blockers.length)
|
|
@@ -231,7 +268,7 @@ async function handleAi(args) {
|
|
|
231
268
|
throw new Error('A proposal id or JSON file is required');
|
|
232
269
|
const stored = target.startsWith('p_') ? await proposalService().get(target) : undefined;
|
|
233
270
|
const proposal = stored ? { baseContractHash: stored.base_contract_hash, baseFlowHash: stored.base_flow_hash,
|
|
234
|
-
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 }
|
|
235
272
|
: JSON.parse(fs.readFileSync(path.resolve(target), 'utf8'));
|
|
236
273
|
if (stored && stored.status !== 'approved')
|
|
237
274
|
throw new Error(`Cannot apply Proposal ${stored.id}. Current status: ${stored.status}. Approval is required. Run: feltdb ai approve ${stored.id}`);
|
|
@@ -250,6 +287,7 @@ async function handleAi(args) {
|
|
|
250
287
|
});
|
|
251
288
|
await proposalService().transition(stored.id, 'applied', 'cli-repository-applier', true);
|
|
252
289
|
console.log(`✓ Reconciled and marked ${stored.id} applied`);
|
|
290
|
+
await verifyAndRecord({ ...stored, status: 'applied' }, false);
|
|
253
291
|
return;
|
|
254
292
|
}
|
|
255
293
|
if (stored) {
|
|
@@ -291,50 +329,43 @@ async function handleAi(args) {
|
|
|
291
329
|
console.log(`✓ Applied validated flow proposal (${review.proposedContract.hashes.application})`);
|
|
292
330
|
for (const file of review.written)
|
|
293
331
|
console.log(` ${file}`);
|
|
294
|
-
if (stored)
|
|
295
|
-
await proposalService().transition(stored.id, 'applied', 'cli-repository-applier', true);
|
|
332
|
+
if (stored) {
|
|
333
|
+
const applied = await proposalService().transition(stored.id, 'applied', 'cli-repository-applier', true);
|
|
334
|
+
await verifyAndRecord(applied, false);
|
|
335
|
+
}
|
|
296
336
|
return;
|
|
297
337
|
}
|
|
298
338
|
const developerIntent = args.filter(value => !value.startsWith('--')).join(' ').trim();
|
|
299
339
|
if (developerIntent) {
|
|
300
340
|
const resolved = resolveModuleIntent(developerIntent);
|
|
301
341
|
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
342
|
const client = proposalService();
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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'));
|
|
333
364
|
console.log(`Use:\n feltdb ai proposal ${stored.id}`);
|
|
334
365
|
return;
|
|
335
366
|
}
|
|
336
367
|
}
|
|
337
|
-
throw new Error('Usage: feltdb ai ["implement <module>"|context|proposals|proposal <id>|approve <id>|review <proposal.json>|apply <id>]');
|
|
368
|
+
throw new Error('Usage: feltdb ai ["implement <module>"|context|proposals|proposal <id>|approve <id>|review <proposal.json>|apply <id>|verify <id>|converge <id>]');
|
|
338
369
|
}
|
|
339
370
|
async function handleProposals(args) {
|
|
340
371
|
loadProjectEnvironment();
|
|
@@ -362,7 +393,7 @@ async function handleProposals(args) {
|
|
|
362
393
|
throw new Error(`feltdb proposals ${action} requires a proposal id`);
|
|
363
394
|
if (action === 'get') {
|
|
364
395
|
const proposal = await client.get(id);
|
|
365
|
-
const [readiness, history] = await Promise.all([client
|
|
396
|
+
const [readiness, history] = await Promise.all([currentProposalReadiness(client, proposal), client.history(id)]);
|
|
366
397
|
const preview = proposal.preview_id ? await client.getPreview(proposal.preview_id).catch(() => undefined) : undefined;
|
|
367
398
|
const result = { proposal, readiness, preview, history: history.events };
|
|
368
399
|
if (args.includes('--json'))
|
|
@@ -384,6 +415,18 @@ async function handleProposals(args) {
|
|
|
384
415
|
}
|
|
385
416
|
throw new Error('Usage: feltdb proposals [list|get|diff|approve|reject] [id]');
|
|
386
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
|
+
}
|
|
387
430
|
async function handleSourceSync(args) {
|
|
388
431
|
loadProjectEnvironment();
|
|
389
432
|
const context = await loadLocalDevelopmentContract();
|
|
@@ -1080,6 +1123,7 @@ async function handleDev(args) {
|
|
|
1080
1123
|
dataDir: path.join(projectDir, '.feltdb-data', 'authority'),
|
|
1081
1124
|
port: authorityPort,
|
|
1082
1125
|
proposalContext: () => loadLocalDevelopmentContract(projectDir),
|
|
1126
|
+
intentObservations: () => collectRepositoryEvidence(projectDir),
|
|
1083
1127
|
});
|
|
1084
1128
|
process.env.VITE_FELTDB_URL = localAuthority.endpoint;
|
|
1085
1129
|
const connectionsPath = path.join(projectDir, '.feltdb', 'connections.json');
|
|
@@ -1699,6 +1743,55 @@ async function handleStudio(args, onReady) {
|
|
|
1699
1743
|
}
|
|
1700
1744
|
return;
|
|
1701
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
|
+
}
|
|
1702
1795
|
const proposalDetail = pathname.match(/^\/_feltdb\/proposals\/(p_[A-Za-z0-9]+)$/);
|
|
1703
1796
|
if (proposalDetail && request.method === 'GET') {
|
|
1704
1797
|
response.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
@@ -39,6 +39,8 @@ export class ProposalServiceClient {
|
|
|
39
39
|
}
|
|
40
40
|
actor(input = {}) { return { application_id: this.connection.applicationId, environment: this.connection.environment, ...input }; }
|
|
41
41
|
create(input) { return this.request('/v1/proposals', { method: 'POST', body: JSON.stringify(input) }); }
|
|
42
|
+
executeIntent(rawRequest, actor = 'cli-developer') { return this.request('/v1/intents', { method: 'POST', body: JSON.stringify(this.actor({ raw_request: rawRequest, actor })) }); }
|
|
43
|
+
intents() { return this.request(this.scoped('/v1/intents')); }
|
|
42
44
|
list(filters = {}) { const query = new URLSearchParams(); if (this.connection.applicationId)
|
|
43
45
|
query.set('application_id', this.connection.applicationId); query.set('environment', filters.environment || this.connection.environment || 'local'); if (filters.status)
|
|
44
46
|
query.set('status', filters.status); if (filters.limit)
|
|
@@ -46,12 +48,14 @@ export class ProposalServiceClient {
|
|
|
46
48
|
query.set('cursor', filters.cursor); return this.request(`/v1/proposals?${query}`); }
|
|
47
49
|
get(id) { return this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}`)); }
|
|
48
50
|
history(id) { return this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/history`)); }
|
|
49
|
-
status(id) {
|
|
51
|
+
status(id, currentEvidenceHash) { const path = this.scoped(`/v1/proposals/${encodeURIComponent(id)}/status`); return this.request(currentEvidenceHash ? `${path}¤t_evidence_hash=${encodeURIComponent(currentEvidenceHash)}` : path); }
|
|
50
52
|
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 })) }); }
|
|
51
53
|
preview(id, actor = 'local-developer') { return this.request(`/v1/proposals/${encodeURIComponent(id)}/preview`, { method: 'POST', body: JSON.stringify(this.actor({ actor })) }); }
|
|
52
54
|
getPreview(id) { return this.request(this.scoped(`/v1/previews/${encodeURIComponent(id)}`)); }
|
|
53
55
|
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
56
|
reject(id, actor = 'local-developer', reason) { return this.request(`/v1/proposals/${encodeURIComponent(id)}/reject`, { method: 'POST', body: JSON.stringify(this.actor({ actor, reason })) }); }
|
|
57
|
+
convergence(id) { return this.request(this.scoped(`/v1/proposals/${encodeURIComponent(id)}/convergence`)); }
|
|
58
|
+
recordConvergence(id, result) { return this.request(`/v1/proposals/${encodeURIComponent(id)}/convergence`, { method: 'POST', body: JSON.stringify(this.actor(result)) }); }
|
|
55
59
|
transition(id, status, actor = 'local-developer', repositoryAuthority = false, approveAuthorization = false) {
|
|
56
60
|
return this.request(`/v1/proposals/${encodeURIComponent(id)}/status`, { method: 'PATCH', headers: repositoryAuthority && this.connection.proposalAuthorityToken
|
|
57
61
|
? { 'FeltDB-Proposal-Authority': this.connection.proposalAuthorityToken, 'FeltDB-Repository-Apply': '1' } : {}, body: JSON.stringify(this.actor({ status, actor, approve_authorization: approveAuthorization })) });
|
|
@@ -81,7 +81,7 @@ export function buildPublishBundle(cwd, configuration) {
|
|
|
81
81
|
'feltdb.artifact_hash': artifactHash,
|
|
82
82
|
'feltdb.namespace': configuration.namespace,
|
|
83
83
|
};
|
|
84
|
-
manifest.artifacts = artifacts.map(value => ({ name: value.path, uri: `
|
|
84
|
+
manifest.artifacts = artifacts.map(value => ({ name: value.path, uri: `artifact://${value.hash}`, media_type: value.mediaType, ...(value.path === 'feltdb.flow' ? { content: value.content } : {}) }));
|
|
85
85
|
const git = spawnSync('git', ['rev-parse', 'HEAD'], { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
86
86
|
const sourceRevision = git.status === 0 ? git.stdout.trim() : undefined;
|
|
87
87
|
if (sourceRevision)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
const sourceExtensions = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.rs']);
|
|
5
|
+
function sourceFiles(root) {
|
|
6
|
+
const found = [];
|
|
7
|
+
const visit = (directory) => {
|
|
8
|
+
if (found.length >= 300 || !fs.existsSync(directory))
|
|
9
|
+
return;
|
|
10
|
+
for (const entry of fs.readdirSync(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
11
|
+
if (found.length >= 300)
|
|
12
|
+
break;
|
|
13
|
+
const absolute = path.join(directory, entry.name);
|
|
14
|
+
if (entry.isDirectory() && !['node_modules', 'dist', 'build', '.git', '.feltdb-data'].includes(entry.name))
|
|
15
|
+
visit(absolute);
|
|
16
|
+
else if (entry.isFile() && sourceExtensions.has(path.extname(entry.name)) && fs.statSync(absolute).size <= 64 * 1024)
|
|
17
|
+
found.push(absolute);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
visit(path.join(root, 'src'));
|
|
21
|
+
return found;
|
|
22
|
+
}
|
|
23
|
+
/** Produces bounded claims and file references; source text never leaves this function. */
|
|
24
|
+
export function collectRepositoryEvidence(cwd = process.cwd()) {
|
|
25
|
+
const observations = [];
|
|
26
|
+
try {
|
|
27
|
+
const status = execFileSync('git', ['status', '--porcelain=v1'], { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
28
|
+
observations.push({ source: 'git', claim: 'git.working_tree', value: status.trim() ? 'dirty' : 'clean', source_ref: '.git' });
|
|
29
|
+
}
|
|
30
|
+
catch { }
|
|
31
|
+
for (const absolute of sourceFiles(cwd)) {
|
|
32
|
+
if (observations.length >= 50)
|
|
33
|
+
break;
|
|
34
|
+
const relative = path.relative(cwd, absolute).split(path.sep).join('/');
|
|
35
|
+
let source = '';
|
|
36
|
+
try {
|
|
37
|
+
source = fs.readFileSync(absolute, 'utf8');
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const add = (claim, value) => observations.push({ source: 'repository', claim, value, source_ref: relative });
|
|
43
|
+
if (/\b(authenticated|useSession|session|signIn|signOut)\b/i.test(source))
|
|
44
|
+
add('repository.authentication', 'authenticated session implementation observed');
|
|
45
|
+
if (/\borganization(?:Id|_id)?\b/i.test(source) && /\b(project|settings|billing)\b/i.test(source))
|
|
46
|
+
add('repository.organization_boundary', 'organization-scoped implementation observed');
|
|
47
|
+
if (/\buser(?:Id|_id|\.id)?\b/i.test(source) && /\b(project|settings|billing)\b/i.test(source))
|
|
48
|
+
add('repository.user_boundary', 'user-scoped implementation observed');
|
|
49
|
+
if (/\b(checkout|subscription|billing|stripe)\b/i.test(source))
|
|
50
|
+
add('repository.billing_surface', 'billing implementation surface observed');
|
|
51
|
+
if (/\b(github|repository|installation|webhook)\b/i.test(source))
|
|
52
|
+
add('repository.integration_surface', 'external collaboration implementation surface observed');
|
|
53
|
+
}
|
|
54
|
+
return observations.slice(0, 50);
|
|
55
|
+
}
|