bulltrackers-module 1.0.768 → 1.0.769
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/functions/computation-system-v2/UserPortfolioMetrics.js +50 -0
- package/functions/computation-system-v2/computations/BehavioralAnomaly.js +557 -337
- package/functions/computation-system-v2/computations/GlobalAumPerAsset30D.js +103 -0
- package/functions/computation-system-v2/computations/PIDailyAssetAUM.js +134 -0
- package/functions/computation-system-v2/computations/PiFeatureVectors.js +227 -0
- package/functions/computation-system-v2/computations/PiRecommender.js +359 -0
- package/functions/computation-system-v2/computations/SignedInUserList.js +51 -0
- package/functions/computation-system-v2/computations/SignedInUserMirrorHistory.js +138 -0
- package/functions/computation-system-v2/computations/SignedInUserPIProfileMetrics.js +106 -0
- package/functions/computation-system-v2/computations/SignedInUserProfileMetrics.js +324 -0
- package/functions/computation-system-v2/config/bulltrackers.config.js +30 -128
- package/functions/computation-system-v2/core-api.js +17 -9
- package/functions/computation-system-v2/data_schema_reference.MD +108 -0
- package/functions/computation-system-v2/devtools/builder/builder.js +362 -0
- package/functions/computation-system-v2/devtools/builder/examples/user-metrics.yaml +26 -0
- package/functions/computation-system-v2/devtools/index.js +36 -0
- package/functions/computation-system-v2/devtools/shared/MockDataFactory.js +235 -0
- package/functions/computation-system-v2/devtools/shared/SchemaTemplates.js +475 -0
- package/functions/computation-system-v2/devtools/shared/SystemIntrospector.js +517 -0
- package/functions/computation-system-v2/devtools/shared/index.js +16 -0
- package/functions/computation-system-v2/devtools/simulation/DAGAnalyzer.js +243 -0
- package/functions/computation-system-v2/devtools/simulation/MockDataFetcher.js +306 -0
- package/functions/computation-system-v2/devtools/simulation/MockStorageManager.js +336 -0
- package/functions/computation-system-v2/devtools/simulation/SimulationEngine.js +525 -0
- package/functions/computation-system-v2/devtools/simulation/SimulationServer.js +581 -0
- package/functions/computation-system-v2/devtools/simulation/index.js +17 -0
- package/functions/computation-system-v2/devtools/simulation/simulate.js +324 -0
- package/functions/computation-system-v2/devtools/vscode-computation/package.json +90 -0
- package/functions/computation-system-v2/devtools/vscode-computation/snippets/computation.json +128 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/extension.ts +401 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/codeActions.ts +152 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/completions.ts +207 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/diagnostics.ts +205 -0
- package/functions/computation-system-v2/devtools/vscode-computation/src/providers/hover.ts +205 -0
- package/functions/computation-system-v2/devtools/vscode-computation/tsconfig.json +22 -0
- package/functions/computation-system-v2/docs/HowToCreateComputations.MD +602 -0
- package/functions/computation-system-v2/framework/data/DataFetcher.js +250 -184
- package/functions/computation-system-v2/framework/data/MaterializedViewManager.js +84 -0
- package/functions/computation-system-v2/framework/data/QueryBuilder.js +38 -38
- package/functions/computation-system-v2/framework/execution/Orchestrator.js +215 -129
- package/functions/computation-system-v2/framework/scheduling/ScheduleValidator.js +17 -19
- package/functions/computation-system-v2/framework/storage/StateRepository.js +32 -2
- package/functions/computation-system-v2/framework/storage/StorageManager.js +105 -67
- package/functions/computation-system-v2/framework/testing/ComputationTester.js +12 -6
- package/functions/computation-system-v2/handlers/dispatcher.js +57 -29
- package/functions/computation-system-v2/legacy/PiAssetRecommender.js.old +115 -0
- package/functions/computation-system-v2/legacy/PiSimilarityMatrix.js +104 -0
- package/functions/computation-system-v2/legacy/PiSimilarityVector.js +71 -0
- package/functions/computation-system-v2/scripts/debug_aggregation.js +25 -0
- package/functions/computation-system-v2/scripts/test-invalidation-scenarios.js +234 -0
- package/package.json +1 -1
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview VS Code Extension Entry Point
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Autocomplete for tables, fields, rules
|
|
6
|
+
* - Diagnostics for validation errors
|
|
7
|
+
* - Hover documentation
|
|
8
|
+
* - Code actions (quick fixes)
|
|
9
|
+
* - Status bar for DAG position
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as vscode from 'vscode';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
|
|
16
|
+
// Providers
|
|
17
|
+
import { ComputationCompletionProvider } from './providers/completions';
|
|
18
|
+
import { ComputationDiagnosticsProvider } from './providers/diagnostics';
|
|
19
|
+
import { ComputationHoverProvider } from './providers/hover';
|
|
20
|
+
import { ComputationCodeActionProvider } from './providers/codeActions';
|
|
21
|
+
|
|
22
|
+
// State
|
|
23
|
+
let diagnosticsProvider: ComputationDiagnosticsProvider | null = null;
|
|
24
|
+
let statusBarItem: vscode.StatusBarItem | null = null;
|
|
25
|
+
|
|
26
|
+
export function activate(context: vscode.ExtensionContext) {
|
|
27
|
+
console.log('Computation Developer extension is now active');
|
|
28
|
+
|
|
29
|
+
// Load system knowledge
|
|
30
|
+
const systemKnowledge = loadSystemKnowledge();
|
|
31
|
+
if (!systemKnowledge) {
|
|
32
|
+
vscode.window.showWarningMessage(
|
|
33
|
+
'Computation Developer: Could not load system configuration. Some features may be limited.'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Register completion provider
|
|
38
|
+
const completionProvider = new ComputationCompletionProvider(systemKnowledge);
|
|
39
|
+
context.subscriptions.push(
|
|
40
|
+
vscode.languages.registerCompletionItemProvider(
|
|
41
|
+
{ language: 'javascript', pattern: '**/computation*/**/*.js' },
|
|
42
|
+
completionProvider,
|
|
43
|
+
'.', "'", '"' // Trigger characters
|
|
44
|
+
)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Register diagnostics provider
|
|
48
|
+
diagnosticsProvider = new ComputationDiagnosticsProvider(systemKnowledge);
|
|
49
|
+
context.subscriptions.push(diagnosticsProvider);
|
|
50
|
+
|
|
51
|
+
// Register hover provider
|
|
52
|
+
const hoverProvider = new ComputationHoverProvider(systemKnowledge);
|
|
53
|
+
context.subscriptions.push(
|
|
54
|
+
vscode.languages.registerHoverProvider(
|
|
55
|
+
{ language: 'javascript', pattern: '**/computation*/**/*.js' },
|
|
56
|
+
hoverProvider
|
|
57
|
+
)
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Register code action provider
|
|
61
|
+
const codeActionProvider = new ComputationCodeActionProvider(systemKnowledge);
|
|
62
|
+
context.subscriptions.push(
|
|
63
|
+
vscode.languages.registerCodeActionsProvider(
|
|
64
|
+
{ language: 'javascript', pattern: '**/computation*/**/*.js' },
|
|
65
|
+
codeActionProvider,
|
|
66
|
+
{ providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] }
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Create status bar item
|
|
71
|
+
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, 100);
|
|
72
|
+
statusBarItem.command = 'computationDeveloper.showDAG';
|
|
73
|
+
context.subscriptions.push(statusBarItem);
|
|
74
|
+
|
|
75
|
+
// Update status bar on active editor change
|
|
76
|
+
context.subscriptions.push(
|
|
77
|
+
vscode.window.onDidChangeActiveTextEditor(updateStatusBar)
|
|
78
|
+
);
|
|
79
|
+
updateStatusBar(vscode.window.activeTextEditor);
|
|
80
|
+
|
|
81
|
+
// Register commands
|
|
82
|
+
context.subscriptions.push(
|
|
83
|
+
vscode.commands.registerCommand('computationDeveloper.validate', () => {
|
|
84
|
+
if (diagnosticsProvider && vscode.window.activeTextEditor) {
|
|
85
|
+
diagnosticsProvider.validateDocument(vscode.window.activeTextEditor.document);
|
|
86
|
+
vscode.window.showInformationMessage('Validation complete');
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
context.subscriptions.push(
|
|
92
|
+
vscode.commands.registerCommand('computationDeveloper.simulate', async () => {
|
|
93
|
+
const editor = vscode.window.activeTextEditor;
|
|
94
|
+
if (!editor) return;
|
|
95
|
+
|
|
96
|
+
const computationName = extractComputationName(editor.document.getText());
|
|
97
|
+
if (!computationName) {
|
|
98
|
+
vscode.window.showErrorMessage('Could not identify computation name');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const config = vscode.workspace.getConfiguration('computationDeveloper');
|
|
103
|
+
const serverUrl = config.get<string>('simulationServerUrl') || 'http://localhost:3210';
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const response = await fetch(`${serverUrl}/api/simulate`, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers: { 'Content-Type': 'application/json' },
|
|
109
|
+
body: JSON.stringify({ computation: computationName })
|
|
110
|
+
});
|
|
111
|
+
const result = await response.json();
|
|
112
|
+
|
|
113
|
+
const output = vscode.window.createOutputChannel('Computation Simulation');
|
|
114
|
+
output.clear();
|
|
115
|
+
output.appendLine(`Simulation: ${computationName}`);
|
|
116
|
+
output.appendLine(`Status: ${result.success ? '✓ Success' : '✗ Failed'}`);
|
|
117
|
+
if (result.stats) {
|
|
118
|
+
output.appendLine(`Time: ${result.stats.executionTimeMs}ms`);
|
|
119
|
+
output.appendLine(`Entities: ${result.stats.entitiesProcessed}`);
|
|
120
|
+
}
|
|
121
|
+
output.appendLine('\nResults:');
|
|
122
|
+
output.appendLine(JSON.stringify(result.results || result, null, 2));
|
|
123
|
+
output.show();
|
|
124
|
+
} catch (e: any) {
|
|
125
|
+
vscode.window.showErrorMessage(`Simulation failed: ${e.message}. Is the server running?`);
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
context.subscriptions.push(
|
|
131
|
+
vscode.commands.registerCommand('computationDeveloper.showDAG', async () => {
|
|
132
|
+
const editor = vscode.window.activeTextEditor;
|
|
133
|
+
if (!editor) return;
|
|
134
|
+
|
|
135
|
+
const computationName = extractComputationName(editor.document.getText());
|
|
136
|
+
if (!computationName) {
|
|
137
|
+
vscode.window.showErrorMessage('Could not identify computation name');
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const config = vscode.workspace.getConfiguration('computationDeveloper');
|
|
142
|
+
const serverUrl = config.get<string>('simulationServerUrl') || 'http://localhost:3210';
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetch(`${serverUrl}/api/info?computation=${computationName}`);
|
|
146
|
+
const info = await response.json();
|
|
147
|
+
|
|
148
|
+
const panel = vscode.window.createWebviewPanel(
|
|
149
|
+
'dagView',
|
|
150
|
+
`DAG: ${computationName}`,
|
|
151
|
+
vscode.ViewColumn.Beside,
|
|
152
|
+
{}
|
|
153
|
+
);
|
|
154
|
+
panel.webview.html = getDAGWebviewContent(info);
|
|
155
|
+
} catch (e: any) {
|
|
156
|
+
vscode.window.showErrorMessage(`Could not load DAG info: ${e.message}`);
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
context.subscriptions.push(
|
|
162
|
+
vscode.commands.registerCommand('computationDeveloper.generateBoilerplate', async () => {
|
|
163
|
+
const name = await vscode.window.showInputBox({
|
|
164
|
+
prompt: 'Computation name (e.g., UserProfileMetrics)',
|
|
165
|
+
validateInput: (value) => {
|
|
166
|
+
if (!value || !/^[A-Z][a-zA-Z0-9]+$/.test(value)) {
|
|
167
|
+
return 'Name must be PascalCase';
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
if (!name) return;
|
|
173
|
+
|
|
174
|
+
const type = await vscode.window.showQuickPick(['per-entity', 'global'], {
|
|
175
|
+
placeHolder: 'Computation type'
|
|
176
|
+
});
|
|
177
|
+
if (!type) return;
|
|
178
|
+
|
|
179
|
+
const boilerplate = generateBoilerplate(name, type);
|
|
180
|
+
|
|
181
|
+
const doc = await vscode.workspace.openTextDocument({
|
|
182
|
+
content: boilerplate,
|
|
183
|
+
language: 'javascript'
|
|
184
|
+
});
|
|
185
|
+
await vscode.window.showTextDocument(doc);
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
// Watch for document changes
|
|
190
|
+
context.subscriptions.push(
|
|
191
|
+
vscode.workspace.onDidChangeTextDocument(event => {
|
|
192
|
+
if (event.document.languageId === 'javascript') {
|
|
193
|
+
diagnosticsProvider?.validateDocument(event.document);
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Validate open documents
|
|
199
|
+
vscode.workspace.textDocuments.forEach(doc => {
|
|
200
|
+
if (doc.languageId === 'javascript') {
|
|
201
|
+
diagnosticsProvider?.validateDocument(doc);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function deactivate() {
|
|
207
|
+
if (diagnosticsProvider) {
|
|
208
|
+
diagnosticsProvider.dispose();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// =========================================================================
|
|
213
|
+
// HELPERS
|
|
214
|
+
// =========================================================================
|
|
215
|
+
|
|
216
|
+
interface SystemKnowledge {
|
|
217
|
+
tables: string[];
|
|
218
|
+
tableMetadata: Record<string, any>;
|
|
219
|
+
ruleModules: string[];
|
|
220
|
+
ruleFunctions: Record<string, string[]>;
|
|
221
|
+
maxLookback: number;
|
|
222
|
+
validTypes: string[];
|
|
223
|
+
validCategories: string[];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function loadSystemKnowledge(): SystemKnowledge | null {
|
|
227
|
+
try {
|
|
228
|
+
// Try to find the config file
|
|
229
|
+
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
|
|
230
|
+
if (!workspaceFolder) return null;
|
|
231
|
+
|
|
232
|
+
// Look for bulltrackers.config.js
|
|
233
|
+
const configPaths = [
|
|
234
|
+
path.join(workspaceFolder.uri.fsPath, 'config', 'bulltrackers.config.js'),
|
|
235
|
+
path.join(workspaceFolder.uri.fsPath, 'functions', 'computation-system-v2', 'config', 'bulltrackers.config.js')
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
for (const configPath of configPaths) {
|
|
239
|
+
if (fs.existsSync(configPath)) {
|
|
240
|
+
// For now, return hardcoded knowledge
|
|
241
|
+
// In production, would parse the config
|
|
242
|
+
return {
|
|
243
|
+
tables: [
|
|
244
|
+
'portfolio_snapshots',
|
|
245
|
+
'trade_history_snapshots',
|
|
246
|
+
'pi_rankings',
|
|
247
|
+
'social_post_snapshots',
|
|
248
|
+
'pi_ratings',
|
|
249
|
+
'pi_page_views',
|
|
250
|
+
'watchlist_membership',
|
|
251
|
+
'pi_alert_history',
|
|
252
|
+
'pi_master_list',
|
|
253
|
+
'ticker_mappings',
|
|
254
|
+
'sector_mappings',
|
|
255
|
+
'asset_prices'
|
|
256
|
+
],
|
|
257
|
+
tableMetadata: {
|
|
258
|
+
portfolio_snapshots: { dateField: 'date', entityField: 'user_id' },
|
|
259
|
+
pi_rankings: { dateField: 'date', entityField: 'pi_id' },
|
|
260
|
+
trade_history_snapshots: { dateField: 'date', entityField: 'user_id' }
|
|
261
|
+
},
|
|
262
|
+
ruleModules: ['portfolio', 'metrics', 'rankings', 'trades', 'social', 'instruments'],
|
|
263
|
+
ruleFunctions: {
|
|
264
|
+
portfolio: ['extractPositions', 'extractMirrors', 'getInvested', 'getNetProfit', 'extractPortfolioData'],
|
|
265
|
+
metrics: ['calculateSharpeRatio', 'calculateVolatility', 'calculateDrawdown'],
|
|
266
|
+
rankings: ['getRank', 'getPercentile'],
|
|
267
|
+
trades: ['getOpenTrades', 'getClosedTrades', 'getTradeHistory'],
|
|
268
|
+
social: ['getPostCount', 'getEngagement'],
|
|
269
|
+
instruments: ['getTicker', 'getSector', 'getPrice']
|
|
270
|
+
},
|
|
271
|
+
maxLookback: 90,
|
|
272
|
+
validTypes: ['per-entity', 'global'],
|
|
273
|
+
validCategories: ['signed_in_user', 'popular_investor', 'global', 'instrument']
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return null;
|
|
279
|
+
} catch (e) {
|
|
280
|
+
console.error('Failed to load system knowledge:', e);
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function extractComputationName(text: string): string | null {
|
|
286
|
+
// Look for class definition
|
|
287
|
+
const classMatch = text.match(/class\s+(\w+)\s+extends\s+Computation/);
|
|
288
|
+
if (classMatch) return classMatch[1];
|
|
289
|
+
|
|
290
|
+
// Look for name in getConfig
|
|
291
|
+
const nameMatch = text.match(/name:\s*['"](\w+)['"]/);
|
|
292
|
+
if (nameMatch) return nameMatch[1];
|
|
293
|
+
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function updateStatusBar(editor: vscode.TextEditor | undefined) {
|
|
298
|
+
if (!statusBarItem) return;
|
|
299
|
+
|
|
300
|
+
if (editor && editor.document.languageId === 'javascript' &&
|
|
301
|
+
editor.document.fileName.includes('computation')) {
|
|
302
|
+
const name = extractComputationName(editor.document.getText());
|
|
303
|
+
if (name) {
|
|
304
|
+
statusBarItem.text = `$(graph) ${name}`;
|
|
305
|
+
statusBarItem.tooltip = 'Click to show DAG position';
|
|
306
|
+
statusBarItem.show();
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
statusBarItem.hide();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function generateBoilerplate(name: string, type: string): string {
|
|
315
|
+
return `const { Computation } = require('../framework');
|
|
316
|
+
|
|
317
|
+
class ${name} extends Computation {
|
|
318
|
+
static getConfig() {
|
|
319
|
+
return {
|
|
320
|
+
name: '${name}',
|
|
321
|
+
description: '',
|
|
322
|
+
type: '${type}',
|
|
323
|
+
category: '${type === 'global' ? 'global' : 'signed_in_user'}',
|
|
324
|
+
isHistorical: false,
|
|
325
|
+
requires: {
|
|
326
|
+
'portfolio_snapshots': {
|
|
327
|
+
lookback: 0,
|
|
328
|
+
mandatory: true,
|
|
329
|
+
fields: ['user_id', 'date', 'portfolio_data']
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
storage: {
|
|
333
|
+
bigquery: true,
|
|
334
|
+
firestore: {
|
|
335
|
+
enabled: false,
|
|
336
|
+
path: 'users/{entityId}/${name.toLowerCase()}',
|
|
337
|
+
merge: true
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
async process(data, { entityId, targetDate, rules, getDependency }) {
|
|
344
|
+
const snapshots = data.portfolio_snapshots[entityId] || [];
|
|
345
|
+
|
|
346
|
+
// TODO: Implement computation logic
|
|
347
|
+
|
|
348
|
+
return {
|
|
349
|
+
// result
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
module.exports = ${name};
|
|
355
|
+
`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function getDAGWebviewContent(info: any): string {
|
|
359
|
+
return `<!DOCTYPE html>
|
|
360
|
+
<html>
|
|
361
|
+
<head>
|
|
362
|
+
<style>
|
|
363
|
+
body { font-family: -apple-system, sans-serif; padding: 20px; background: #1e1e1e; color: #d4d4d4; }
|
|
364
|
+
h1 { margin-bottom: 20px; }
|
|
365
|
+
.info { background: #2d2d2d; padding: 15px; border-radius: 8px; margin-bottom: 15px; }
|
|
366
|
+
.label { color: #888; font-size: 0.9em; }
|
|
367
|
+
.value { font-size: 1.1em; margin-top: 4px; }
|
|
368
|
+
.deps { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
|
|
369
|
+
.dep { background: #3d3d3d; padding: 4px 12px; border-radius: 4px; }
|
|
370
|
+
.pass { background: #4a4a9a; color: white; padding: 4px 12px; border-radius: 4px; display: inline-block; }
|
|
371
|
+
</style>
|
|
372
|
+
</head>
|
|
373
|
+
<body>
|
|
374
|
+
<h1>${info.name || 'Unknown'}</h1>
|
|
375
|
+
<div class="info">
|
|
376
|
+
<div class="label">Pass Level</div>
|
|
377
|
+
<div class="value"><span class="pass">Pass ${info.passLevel || '?'}</span> of ${info.totalPasses || '?'}</div>
|
|
378
|
+
</div>
|
|
379
|
+
<div class="info">
|
|
380
|
+
<div class="label">Dependencies</div>
|
|
381
|
+
<div class="deps">
|
|
382
|
+
${(info.dependencies || []).map((d: string) => `<span class="dep">← ${d}</span>`).join('') || 'None'}
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
<div class="info">
|
|
386
|
+
<div class="label">Dependents</div>
|
|
387
|
+
<div class="deps">
|
|
388
|
+
${(info.dependents || []).map((d: string) => `<span class="dep">→ ${d}</span>`).join('') || 'None'}
|
|
389
|
+
</div>
|
|
390
|
+
</div>
|
|
391
|
+
<div class="info">
|
|
392
|
+
<div class="label">Type</div>
|
|
393
|
+
<div class="value">${info.config?.type || 'unknown'}</div>
|
|
394
|
+
</div>
|
|
395
|
+
<div class="info">
|
|
396
|
+
<div class="label">Category</div>
|
|
397
|
+
<div class="value">${info.config?.category || 'unknown'}</div>
|
|
398
|
+
</div>
|
|
399
|
+
</body>
|
|
400
|
+
</html>`;
|
|
401
|
+
}
|
package/functions/computation-system-v2/devtools/vscode-computation/src/providers/codeActions.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Code Action Provider
|
|
3
|
+
*
|
|
4
|
+
* Provides quick fixes for:
|
|
5
|
+
* - Unknown table names (suggest similar)
|
|
6
|
+
* - Lookback exceeds max (reduce to max)
|
|
7
|
+
* - Missing required fields (add template)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import * as vscode from 'vscode';
|
|
11
|
+
|
|
12
|
+
export class ComputationCodeActionProvider implements vscode.CodeActionProvider {
|
|
13
|
+
private knowledge: SystemKnowledge | null;
|
|
14
|
+
|
|
15
|
+
constructor(knowledge: SystemKnowledge | null) {
|
|
16
|
+
this.knowledge = knowledge;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
provideCodeActions(
|
|
20
|
+
document: vscode.TextDocument,
|
|
21
|
+
range: vscode.Range | vscode.Selection,
|
|
22
|
+
context: vscode.CodeActionContext,
|
|
23
|
+
token: vscode.CancellationToken
|
|
24
|
+
): vscode.CodeAction[] | null {
|
|
25
|
+
const actions: vscode.CodeAction[] = [];
|
|
26
|
+
|
|
27
|
+
for (const diagnostic of context.diagnostics) {
|
|
28
|
+
if (diagnostic.source !== 'computation') continue;
|
|
29
|
+
|
|
30
|
+
const message = diagnostic.message;
|
|
31
|
+
|
|
32
|
+
// Unknown table - suggest similar
|
|
33
|
+
if (message.startsWith('Unknown table:')) {
|
|
34
|
+
const tableName = message.match(/Unknown table: (\w+)/)?.[1];
|
|
35
|
+
if (tableName) {
|
|
36
|
+
const suggestions = this.findSimilarTables(tableName);
|
|
37
|
+
for (const suggestion of suggestions) {
|
|
38
|
+
const action = new vscode.CodeAction(
|
|
39
|
+
`Change to '${suggestion}'`,
|
|
40
|
+
vscode.CodeActionKind.QuickFix
|
|
41
|
+
);
|
|
42
|
+
action.edit = new vscode.WorkspaceEdit();
|
|
43
|
+
action.edit.replace(document.uri, diagnostic.range, `'${suggestion}'`);
|
|
44
|
+
action.diagnostics = [diagnostic];
|
|
45
|
+
actions.push(action);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Lookback exceeds max
|
|
51
|
+
if (message.includes('exceeds maximum')) {
|
|
52
|
+
const action = new vscode.CodeAction(
|
|
53
|
+
'Reduce to maximum (90)',
|
|
54
|
+
vscode.CodeActionKind.QuickFix
|
|
55
|
+
);
|
|
56
|
+
action.edit = new vscode.WorkspaceEdit();
|
|
57
|
+
action.edit.replace(document.uri, diagnostic.range, 'lookback: 90');
|
|
58
|
+
action.diagnostics = [diagnostic];
|
|
59
|
+
actions.push(action);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Missing required field
|
|
63
|
+
if (message.startsWith('Missing required config field:')) {
|
|
64
|
+
const field = message.match(/field: (\w+)/)?.[1];
|
|
65
|
+
if (field) {
|
|
66
|
+
const action = this.createAddFieldAction(document, diagnostic, field);
|
|
67
|
+
if (action) actions.push(action);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return actions;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private findSimilarTables(name: string): string[] {
|
|
76
|
+
if (!this.knowledge) return [];
|
|
77
|
+
|
|
78
|
+
const suggestions: Array<{ name: string; score: number }> = [];
|
|
79
|
+
|
|
80
|
+
for (const table of this.knowledge.tables) {
|
|
81
|
+
const score = this.similarity(name.toLowerCase(), table.toLowerCase());
|
|
82
|
+
if (score > 0.3) {
|
|
83
|
+
suggestions.push({ name: table, score });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return suggestions
|
|
88
|
+
.sort((a, b) => b.score - a.score)
|
|
89
|
+
.slice(0, 3)
|
|
90
|
+
.map(s => s.name);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private similarity(a: string, b: string): number {
|
|
94
|
+
// Simple Jaccard similarity on character bigrams
|
|
95
|
+
const bigramsA = new Set<string>();
|
|
96
|
+
const bigramsB = new Set<string>();
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < a.length - 1; i++) bigramsA.add(a.slice(i, i + 2));
|
|
99
|
+
for (let i = 0; i < b.length - 1; i++) bigramsB.add(b.slice(i, i + 2));
|
|
100
|
+
|
|
101
|
+
let intersection = 0;
|
|
102
|
+
for (const bi of bigramsA) {
|
|
103
|
+
if (bigramsB.has(bi)) intersection++;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const union = bigramsA.size + bigramsB.size - intersection;
|
|
107
|
+
return union === 0 ? 0 : intersection / union;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private createAddFieldAction(
|
|
111
|
+
document: vscode.TextDocument,
|
|
112
|
+
diagnostic: vscode.Diagnostic,
|
|
113
|
+
field: string
|
|
114
|
+
): vscode.CodeAction | null {
|
|
115
|
+
const templates: Record<string, string> = {
|
|
116
|
+
name: "name: 'ComputationName',",
|
|
117
|
+
type: "type: 'per-entity',",
|
|
118
|
+
category: "category: 'signed_in_user',"
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const template = templates[field];
|
|
122
|
+
if (!template) return null;
|
|
123
|
+
|
|
124
|
+
const action = new vscode.CodeAction(
|
|
125
|
+
`Add ${field} field`,
|
|
126
|
+
vscode.CodeActionKind.QuickFix
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// Find position to insert (after 'return {')
|
|
130
|
+
const text = document.getText();
|
|
131
|
+
const returnIdx = text.indexOf('return {');
|
|
132
|
+
if (returnIdx === -1) return null;
|
|
133
|
+
|
|
134
|
+
const insertPosition = document.positionAt(returnIdx + 'return {'.length);
|
|
135
|
+
|
|
136
|
+
action.edit = new vscode.WorkspaceEdit();
|
|
137
|
+
action.edit.insert(document.uri, insertPosition, `\n ${template}`);
|
|
138
|
+
action.diagnostics = [diagnostic];
|
|
139
|
+
|
|
140
|
+
return action;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
interface SystemKnowledge {
|
|
145
|
+
tables: string[];
|
|
146
|
+
tableMetadata: Record<string, any>;
|
|
147
|
+
ruleModules: string[];
|
|
148
|
+
ruleFunctions: Record<string, string[]>;
|
|
149
|
+
maxLookback: number;
|
|
150
|
+
validTypes: string[];
|
|
151
|
+
validCategories: string[];
|
|
152
|
+
}
|