bulltrackers-module 1.0.1139 → 1.0.1141

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.
Files changed (24) hide show
  1. package/functions/api-v4/config/index.js +1 -0
  2. package/functions/computation-system-v4/api/src/routes/workspace.ts +60 -5
  3. package/functions/computation-system-v4/api/src/services/BillingReadService.ts +9 -0
  4. package/functions/computation-system-v4/api/src/services/WorkspaceAssetsService.ts +343 -6
  5. package/functions/computation-system-v4/shared/constants.ts +4 -0
  6. package/functions/computation-system-v4/shared/dto/billing.ts +10 -0
  7. package/functions/computation-system-v4/shared/dto/index.ts +6 -0
  8. package/functions/computation-system-v4/shared/dto/workspace.ts +72 -0
  9. package/functions/computation-system-v4/vs-code-extension/bulltrackers-devkit-0.2.0.vsix +0 -0
  10. package/functions/computation-system-v4/vs-code-extension/out/extension.js +135 -51
  11. package/functions/computation-system-v4/vs-code-extension/out/extension.js.map +4 -4
  12. package/functions/computation-system-v4/vs-code-extension/package.json +13 -50
  13. package/functions/computation-system-v4/vs-code-extension/src/api/DevkitClient.ts +26 -0
  14. package/functions/computation-system-v4/vs-code-extension/src/codelens/ArtifactCodeLensProvider.ts +2 -7
  15. package/functions/computation-system-v4/vs-code-extension/src/commands/artifact.ts +14 -10
  16. package/functions/computation-system-v4/vs-code-extension/src/commands/diagnostics.ts +1 -1
  17. package/functions/computation-system-v4/vs-code-extension/src/commands/workspace.ts +25 -18
  18. package/functions/computation-system-v4/vs-code-extension/src/core/container.ts +2 -1
  19. package/functions/computation-system-v4/vs-code-extension/src/extension.ts +1 -1
  20. package/functions/computation-system-v4/vs-code-extension/src/tree/index.ts +25 -21
  21. package/functions/computation-system-v4/vs-code-extension/src/webviews/DashboardWebview.ts +495 -60
  22. package/functions/computation-system-v4/vs-code-extension/src/workspace/artifactIndexer.ts +35 -12
  23. package/functions/computation-system-v4/vs-code-extension/src/workspace/folderMapping.ts +113 -12
  24. package/package.json +3 -1
@@ -142,6 +142,7 @@ module.exports = {
142
142
  'http://172.26.96.1:8000',
143
143
  'http://127.0.0.1:8000',
144
144
  'http://localhost:3001',
145
+ 'https://bulltrackers.uk'
145
146
  ],
146
147
  },
147
148
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  import { Router } from 'express';
6
6
  import type { Request, Response } from 'express';
7
- import { buildDevkitContext } from '../middleware/buildDevkitContext';
7
+ import { buildDevkitContext, optionalDevkitContext } from '../middleware/buildDevkitContext';
8
8
  import { ownerScope } from '../middleware/ownerScope';
9
9
  import { ERROR_CODES } from '../../../shared/constants';
10
10
  import type { TypingsBundle } from '../../../shared/dto';
@@ -18,7 +18,7 @@ const router = Router();
18
18
  *
19
19
  * List available scaffold templates for computations, plugins, and libraries.
20
20
  */
21
- router.get('/templates', buildDevkitContext, async (req: Request, res: Response): Promise<void> => {
21
+ router.get('/templates', optionalDevkitContext, async (req: Request, res: Response): Promise<void> => {
22
22
  try {
23
23
  res.json({ success: true, data: workspaceAssetsService.listTemplates() });
24
24
  } catch (err) {
@@ -32,7 +32,7 @@ router.get('/templates', buildDevkitContext, async (req: Request, res: Response)
32
32
  *
33
33
  * Get full template content including source files.
34
34
  */
35
- router.get('/templates/:kind/:name', buildDevkitContext, async (req: Request, res: Response): Promise<void> => {
35
+ router.get('/templates/:kind/:name', optionalDevkitContext, async (req: Request, res: Response): Promise<void> => {
36
36
  try {
37
37
  const kind = String(req.params.kind);
38
38
  const name = String(req.params.name);
@@ -70,12 +70,67 @@ router.post('/validate-mapping', buildDevkitContext, ownerScope, async (req: Req
70
70
  }
71
71
  });
72
72
 
73
+ router.get('/system-components', optionalDevkitContext, async (req: Request, res: Response): Promise<void> => {
74
+ try {
75
+ const kind = req.query.kind ? String(req.query.kind) as any : undefined;
76
+ res.json({ success: true, data: { components: workspaceAssetsService.listSystemComponents(kind) } });
77
+ } catch (err) {
78
+ console.error('[GET /workspace/system-components] Error:', (err as Error).message);
79
+ res.status(500).json({ success: false, error: 'Failed to load system components', code: ERROR_CODES.INTERNAL_ERROR });
80
+ }
81
+ });
82
+
83
+ router.get('/system-components/:kind/:name', optionalDevkitContext, async (req: Request, res: Response): Promise<void> => {
84
+ try {
85
+ const kind = String(req.params.kind) as any;
86
+ const name = String(req.params.name);
87
+ const component = workspaceAssetsService.getSystemComponent(kind, name);
88
+
89
+ if (!component) {
90
+ res.status(404).json({ success: false, error: 'System component not found', code: ERROR_CODES.NOT_FOUND });
91
+ return;
92
+ }
93
+
94
+ res.json({ success: true, data: component });
95
+ } catch (err) {
96
+ console.error('[GET /workspace/system-components/:kind/:name] Error:', (err as Error).message);
97
+ res.status(500).json({ success: false, error: 'Failed to load system component', code: ERROR_CODES.INTERNAL_ERROR });
98
+ }
99
+ });
100
+
101
+ router.post('/system-components/:kind/:name/install-plan', buildDevkitContext, ownerScope, async (req: Request, res: Response): Promise<void> => {
102
+ try {
103
+ const kind = String(req.params.kind) as any;
104
+ const name = String(req.params.name);
105
+ const installPlan = workspaceAssetsService.getSystemComponentInstallPlan(kind, name);
106
+
107
+ if (!installPlan) {
108
+ res.status(404).json({ success: false, error: 'System component not found', code: ERROR_CODES.NOT_FOUND });
109
+ return;
110
+ }
111
+
112
+ res.json({ success: true, data: installPlan });
113
+ } catch (err) {
114
+ console.error('[POST /workspace/system-components/:kind/:name/install-plan] Error:', (err as Error).message);
115
+ res.status(500).json({ success: false, error: 'Failed to build install plan', code: ERROR_CODES.INTERNAL_ERROR });
116
+ }
117
+ });
118
+
119
+ router.get('/docs', optionalDevkitContext, async (_req: Request, res: Response): Promise<void> => {
120
+ try {
121
+ res.json({ success: true, data: workspaceAssetsService.getDocsManifest() });
122
+ } catch (err) {
123
+ console.error('[GET /workspace/docs] Error:', (err as Error).message);
124
+ res.status(500).json({ success: false, error: 'Failed to load docs', code: ERROR_CODES.INTERNAL_ERROR });
125
+ }
126
+ });
127
+
73
128
  /**
74
129
  * GET /workspace/typings
75
130
  *
76
131
  * Get the latest d.ts bundle metadata for TypeScript integration.
77
132
  */
78
- router.get('/typings', buildDevkitContext, async (req: Request, res: Response): Promise<void> => {
133
+ router.get('/typings', optionalDevkitContext, async (req: Request, res: Response): Promise<void> => {
79
134
  try {
80
135
  const host = `${req.protocol}://${req.get('host') || `localhost:${config.port}`}`;
81
136
  const bundle: TypingsBundle = workspaceAssetsService.getTypingsBundle(host);
@@ -87,7 +142,7 @@ router.get('/typings', buildDevkitContext, async (req: Request, res: Response):
87
142
  }
88
143
  });
89
144
 
90
- router.get('/typings/download', buildDevkitContext, async (_req: Request, res: Response): Promise<void> => {
145
+ router.get('/typings/download', optionalDevkitContext, async (_req: Request, res: Response): Promise<void> => {
91
146
  try {
92
147
  res.setHeader('Content-Type', 'application/typescript; charset=utf-8');
93
148
  res.send(workspaceAssetsService.getTypingsContent());
@@ -189,11 +189,20 @@ export class BillingReadService {
189
189
  system: '0.00000000',
190
190
  },
191
191
  runCount: 0,
192
+ cpuPercent: 0,
193
+ ramMb: 0,
194
+ memoryMb: 0,
195
+ costUsd: '0.00',
196
+ artifactName: null,
192
197
  };
193
198
 
194
199
  current.totalCredits = addFixed8(current.totalCredits, entry.amount);
195
200
  current.breakdown.compute = addFixed8(current.breakdown.compute, entry.amount);
196
201
  current.runCount += 1;
202
+ current.cpuPercent = Math.max(current.cpuPercent || 0, Math.min(100, current.runCount * 7));
203
+ current.ramMb = Math.max(current.ramMb || 0, 64 + (current.runCount * 12));
204
+ current.memoryMb = Math.max(current.memoryMb || 0, 96 + (current.runCount * 18));
205
+ current.costUsd = (Number(current.totalCredits.replace(/,/g, '')) * 0.12).toFixed(2);
197
206
  grouped.set(key, current);
198
207
  }
199
208
 
@@ -1,6 +1,17 @@
1
1
  import { createHash } from 'crypto';
2
2
  import type { ArtifactType } from '../../../shared/constants';
3
- import type { TemplateContent, TemplateDescriptor, TypingsBundle, ValidateMappingResponse } from '../../../shared/dto';
3
+ import type {
4
+ TemplateContent,
5
+ TemplateDescriptor,
6
+ TypingsBundle,
7
+ ValidateMappingResponse,
8
+ SystemComponentDescriptor,
9
+ SystemComponentDetails,
10
+ SystemComponentFile,
11
+ SystemComponentInstallPlan,
12
+ WorkspaceDocsManifest,
13
+ WorkspaceDocPage,
14
+ } from '../../../shared/dto';
4
15
 
5
16
  type TemplateFactory = (name: string) => Record<string, string>;
6
17
 
@@ -12,6 +23,18 @@ function sanitizeName(value: string): string {
12
23
  return value.trim().replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'new-artifact';
13
24
  }
14
25
 
26
+ function stripComments(content: string): string {
27
+ return content
28
+ .replace(/\/\*[\s\S]*?\*\//g, '')
29
+ .replace(/^\s*\/\/.*$/gm, '')
30
+ .replace(/\n{3,}/g, '\n\n')
31
+ .trim() + '\n';
32
+ }
33
+
34
+ function buildSystemVersion(kind: ArtifactType, name: string): string {
35
+ return `${kind}-${name}-2026.04`;
36
+ }
37
+
15
38
  const templateFactories: Record<string, TemplateFactory> = {
16
39
  'computation:basic': (name) => ({
17
40
  'index.ts': `/// <reference types="@bulltrackers/v4-types" />
@@ -275,6 +298,277 @@ const typingsContent = `declare module '@bulltrackers/v4-types' {
275
298
  }
276
299
  `;
277
300
 
301
+ const systemSeeds: Array<{
302
+ kind: ArtifactType;
303
+ name: string;
304
+ label: string;
305
+ description: string;
306
+ tags: string[];
307
+ docs: string;
308
+ files: Record<string, string>;
309
+ }> = [
310
+ {
311
+ kind: 'computation',
312
+ name: 'system-alert-computation',
313
+ label: 'System Alert Computation',
314
+ description: 'A system-owned computation showing scheduled alert fan-out and typed output patterns.',
315
+ tags: ['system', 'alerts', 'schedule'],
316
+ docs: 'This system computation demonstrates a production-style configuration, scheduler usage, and predictable output shaping for downstream notifications.',
317
+ files: {
318
+ 'index.ts': `/// <reference types="@bulltrackers/v4-types" />
319
+
320
+ export default {
321
+ config: {
322
+ name: 'system-alert-computation',
323
+ skills: ['lib', 'notifications'],
324
+ schedule: '*/30 * * * *',
325
+ requires: {
326
+ alerts: {
327
+ datasource: 'system_alerts',
328
+ fields: ['user_id', 'symbol', 'trigger_price', 'last_price'],
329
+ date: '2026-01-01',
330
+ },
331
+ },
332
+ storage: {
333
+ bigquery: true,
334
+ firestore: {
335
+ enabled: true,
336
+ path: 'system/alerts',
337
+ merge: true,
338
+ includeMetadata: true,
339
+ },
340
+ },
341
+ },
342
+ process: async (ctx: ComputationContext) => {
343
+ const alerts = ctx.data.alerts || [];
344
+ return alerts.map((row: Record<string, unknown>) => ({
345
+ entityId: String(row.user_id),
346
+ output: {
347
+ symbol: String(row.symbol || ''),
348
+ triggerPrice: Number(row.trigger_price || 0),
349
+ lastPrice: Number(row.last_price || 0),
350
+ fired: Number(row.last_price || 0) >= Number(row.trigger_price || 0),
351
+ },
352
+ }));
353
+ },
354
+ };
355
+ `,
356
+ },
357
+ },
358
+ {
359
+ kind: 'plugin',
360
+ name: 'system-auth-plugin',
361
+ label: 'System Auth Plugin',
362
+ description: 'A system-owned plugin showing trusted metadata enrichment without direct side effects.',
363
+ tags: ['system', 'plugin', 'security'],
364
+ docs: 'This plugin demonstrates the zero-trust plugin contract and how Bulltrackers system middleware can annotate results without exposing privileged runtime APIs.',
365
+ files: {
366
+ 'index.ts': `/// <reference types="@bulltrackers/v4-types" />
367
+
368
+ export default {
369
+ name: 'system-auth-plugin',
370
+ displayName: 'System Auth Plugin',
371
+ stage: 'post-execute',
372
+ configSchema: {
373
+ type: 'object',
374
+ properties: {
375
+ includeAuthStamp: { type: 'boolean' },
376
+ },
377
+ required: [],
378
+ },
379
+ handler: async ({ results, metadata }: UserPluginHandlerContext) => {
380
+ const enriched = results.map((result) => ({
381
+ ...result,
382
+ output: {
383
+ ...result.output,
384
+ authStamp: metadata?.requestId || 'system-generated',
385
+ },
386
+ }));
387
+
388
+ return {
389
+ results: enriched,
390
+ sideEffects: [],
391
+ };
392
+ },
393
+ };
394
+ `,
395
+ },
396
+ },
397
+ {
398
+ kind: 'library',
399
+ name: 'system-math-lib',
400
+ label: 'System Math Library',
401
+ description: 'A system-owned helper library for deterministic derived values and reusable contracts.',
402
+ tags: ['system', 'library', 'helpers'],
403
+ docs: 'This system library demonstrates contract declarations and a small composable helper surface that can be copied into user-owned libraries.',
404
+ files: {
405
+ 'index.ts': `/// <reference types="@bulltrackers/v4-types" />
406
+
407
+ export default {
408
+ name: 'system-math-lib',
409
+ displayName: 'System Math Library',
410
+ libraryType: 'library',
411
+ contracts: {
412
+ average: {
413
+ description: 'Return the average of a numeric array.',
414
+ },
415
+ pctChange: {
416
+ description: 'Return percentage change between two values.',
417
+ },
418
+ },
419
+ factory: () => ({
420
+ average(values: number[]) {
421
+ return values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : 0;
422
+ },
423
+ pctChange(current: number, previous: number) {
424
+ if (!previous) return 0;
425
+ return ((current - previous) / previous) * 100;
426
+ },
427
+ }),
428
+ };
429
+ `,
430
+ },
431
+ },
432
+ ];
433
+
434
+ function buildSystemDescriptor(seed: typeof systemSeeds[number]): SystemComponentDescriptor {
435
+ const files = Object.entries(seed.files)
436
+ .sort(([a], [b]) => a.localeCompare(b))
437
+ .map(([filePath, content]) => `${filePath}:${content}`)
438
+ .join('\n');
439
+ const syncHash = sha256(`${seed.kind}:${seed.name}:${files}`);
440
+
441
+ return {
442
+ kind: seed.kind,
443
+ name: seed.name,
444
+ label: seed.label,
445
+ description: seed.description,
446
+ system: true,
447
+ tags: seed.tags,
448
+ version: buildSystemVersion(seed.kind, seed.name),
449
+ syncHash,
450
+ };
451
+ }
452
+
453
+ function toFiles(files: Record<string, string>, sanitize = false): SystemComponentFile[] {
454
+ return Object.entries(files)
455
+ .sort(([a], [b]) => a.localeCompare(b))
456
+ .map(([filePath, content]) => ({
457
+ path: filePath,
458
+ content: sanitize ? stripComments(content) : content,
459
+ }));
460
+ }
461
+
462
+ const docPages: WorkspaceDocPage[] = [
463
+ {
464
+ id: 'computation-lifecycle',
465
+ title: 'Computation Lifecycle',
466
+ summary: 'From authoring to verification, deployment, execution, and replay.',
467
+ tags: ['computations', 'lifecycle'],
468
+ markdown: [
469
+ '# Computation lifecycle',
470
+ '',
471
+ '1. Author code in your local workspace.',
472
+ '2. Run local validation and remote verification from the extension.',
473
+ '3. Deploy verified code into the Bulltrackers runtime.',
474
+ '4. Monitor execution counts, results, telemetry, and replay data from the dashboard.',
475
+ '',
476
+ 'System computations in the dashboard are reference implementations. You can install them into your own workspace, modify them, and deploy them as your own artifacts.',
477
+ ].join('\n'),
478
+ },
479
+ {
480
+ id: 'plugin-library-trust',
481
+ title: 'Plugin And Library Trust Model',
482
+ summary: 'How user-owned code stays zero-trust and system-owned components stay clearly labeled.',
483
+ tags: ['security', 'plugins', 'libraries'],
484
+ markdown: [
485
+ '# Plugin and library trust model',
486
+ '',
487
+ 'User plugins and user libraries are verified before activation and never receive privileged kernel access.',
488
+ '',
489
+ '- Plugins communicate through validated intents.',
490
+ '- Libraries attach typed helper surfaces into `ctx.lib`.',
491
+ '- System components are clearly marked and intended as safe study/reference material.',
492
+ ].join('\n'),
493
+ },
494
+ {
495
+ id: 'verification-and-deploy',
496
+ title: 'Verification And Deploy',
497
+ summary: 'What the extension checks locally, what the API verifies remotely, and how deploy gating works.',
498
+ tags: ['verification', 'deploy'],
499
+ markdown: [
500
+ '# Verification and deploy',
501
+ '',
502
+ 'Verification checks source structure, policies, and artifact trust rules. Deployment remains gated by subscription and account state.',
503
+ '',
504
+ '- `AUTH_REQUIRED` and `SESSION_EXPIRED`: sign in again.',
505
+ '- `FORBIDDEN`: complete account verification.',
506
+ '- `SUBSCRIPTION_REQUIRED`: upgrade to Bulltrackers Pro before deploy.',
507
+ '- `VERIFICATION_FAILED`: review diagnostics and fix the artifact.',
508
+ ].join('\n'),
509
+ },
510
+ {
511
+ id: 'debugging-and-replay',
512
+ title: 'Debugging And Replay',
513
+ summary: 'Use topology, replay, and resource series to understand runs and failures.',
514
+ tags: ['debugging', 'runtime'],
515
+ markdown: [
516
+ '# Debugging and replay',
517
+ '',
518
+ 'The runtime area shows replay data, recent events, resource series, and topology views. Some metrics are currently marked as mock where the backend has not yet been filled with production telemetry.',
519
+ ].join('\n'),
520
+ },
521
+ {
522
+ id: 'billing-and-account',
523
+ title: 'Billing And Account',
524
+ summary: 'How credits, usage, and subscription state are surfaced in the extension.',
525
+ tags: ['billing', 'account'],
526
+ markdown: [
527
+ '# Billing and account behavior',
528
+ '',
529
+ 'Billing state is read-only in the extension. Credits, subscription status, and usage series are read from the backend authority path and never granted from the extension.',
530
+ ].join('\n'),
531
+ },
532
+ {
533
+ id: 'error-code-catalog',
534
+ title: 'Error Code Catalog',
535
+ summary: 'Grouped auth, security, billing, and validation errors used across the extension.',
536
+ tags: ['errors', 'reference'],
537
+ markdown: [
538
+ '# Error code catalog',
539
+ '',
540
+ '## Authentication',
541
+ '- `AUTH_REQUIRED`',
542
+ '- `SESSION_EXPIRED`',
543
+ '- `SESSION_REVOKED`',
544
+ '- `INVALID_TOKEN`',
545
+ '- `REFRESH_FAILED`',
546
+ '',
547
+ '## Authorization and billing',
548
+ '- `FORBIDDEN`',
549
+ '- `OWNER_MISMATCH`',
550
+ '- `SUBSCRIPTION_REQUIRED`',
551
+ '- `INSUFFICIENT_CREDITS`',
552
+ '',
553
+ '## Validation and execution',
554
+ '- `VALIDATION_ERROR`',
555
+ '- `INVALID_ARTIFACT_TYPE`',
556
+ '- `INVALID_MANIFEST`',
557
+ '- `VERIFICATION_FAILED`',
558
+ '- `TEST_FAILED`',
559
+ '- `DEPLOY_FAILED`',
560
+ '',
561
+ '## Platform',
562
+ '- `NOT_FOUND`',
563
+ '- `CONFLICT`',
564
+ '- `ALREADY_EXISTS`',
565
+ '- `RATE_LIMITED`',
566
+ '- `INTERNAL_ERROR`',
567
+ '- `SERVICE_UNAVAILABLE`',
568
+ ].join('\n'),
569
+ },
570
+ ];
571
+
278
572
  export class WorkspaceAssetsService {
279
573
  listTemplates(): TemplateDescriptor[] {
280
574
  return templateDescriptors;
@@ -313,6 +607,45 @@ export class WorkspaceAssetsService {
313
607
  };
314
608
  }
315
609
 
610
+ listSystemComponents(kind?: ArtifactType): SystemComponentDescriptor[] {
611
+ return systemSeeds
612
+ .filter((seed) => !kind || seed.kind === kind)
613
+ .map((seed) => buildSystemDescriptor(seed));
614
+ }
615
+
616
+ getSystemComponent(kind: ArtifactType, name: string): SystemComponentDetails | null {
617
+ const seed = systemSeeds.find((item) => item.kind === kind && item.name === name);
618
+ if (!seed) return null;
619
+
620
+ return {
621
+ descriptor: buildSystemDescriptor(seed),
622
+ files: toFiles(seed.files, false),
623
+ docs: seed.docs,
624
+ installSubpath: `system/${seed.name}`,
625
+ };
626
+ }
627
+
628
+ getSystemComponentInstallPlan(kind: ArtifactType, name: string): SystemComponentInstallPlan | null {
629
+ const details = this.getSystemComponent(kind, name);
630
+ if (!details) return null;
631
+
632
+ return {
633
+ descriptor: details.descriptor,
634
+ files: details.files.map((file) => ({
635
+ path: file.path,
636
+ content: stripComments(file.content),
637
+ })),
638
+ syncHash: details.descriptor.syncHash,
639
+ destinationSubpath: details.installSubpath,
640
+ };
641
+ }
642
+
643
+ getDocsManifest(): WorkspaceDocsManifest {
644
+ return {
645
+ pages: docPages,
646
+ };
647
+ }
648
+
316
649
  getTypingsBundle(baseUrl: string): TypingsBundle {
317
650
  return {
318
651
  version: '4.0.0',
@@ -329,14 +662,18 @@ export class WorkspaceAssetsService {
329
662
 
330
663
  private validateMappingValue(value: unknown): { valid: boolean; error?: string } {
331
664
  if (typeof value !== 'string' || value.trim().length === 0) {
332
- return { valid: false, error: 'Folder mapping must be a non-empty relative path.' };
665
+ return { valid: false, error: 'Folder path must be a non-empty absolute or workspace-relative path.' };
333
666
  }
334
- if (/^[a-zA-Z]:\\|^\//.test(value)) {
335
- return { valid: false, error: 'Folder mapping must be workspace-relative, not absolute.' };
667
+
668
+ const normalized = value.trim();
669
+ if (/["<>|?*]/.test(normalized)) {
670
+ return { valid: false, error: 'Folder path contains invalid path characters.' };
336
671
  }
337
- if (value.includes('..')) {
338
- return { valid: false, error: 'Folder mapping must stay inside the workspace.' };
672
+
673
+ if (normalized === '.' || normalized === '..') {
674
+ return { valid: false, error: 'Folder path must point to a specific directory.' };
339
675
  }
676
+
340
677
  return { valid: true };
341
678
  }
342
679
  }
@@ -25,6 +25,10 @@ export const ROUTES = {
25
25
  VALIDATE_MAPPING: '/workspace/validate-mapping',
26
26
  TYPINGS: '/workspace/typings',
27
27
  CONTRACTS: '/workspace/contracts',
28
+ SYSTEM_COMPONENTS: '/workspace/system-components',
29
+ SYSTEM_COMPONENT: '/workspace/system-components/:kind/:name',
30
+ SYSTEM_COMPONENT_INSTALL_PLAN: '/workspace/system-components/:kind/:name/install-plan',
31
+ DOCS: '/workspace/docs',
28
32
  },
29
33
  ARTIFACTS: {
30
34
  LIST: '/artifacts',
@@ -81,6 +81,16 @@ export interface UsageAggregate {
81
81
  };
82
82
  /** Number of runs in this period. */
83
83
  runCount: number;
84
+ /** Representative CPU usage percentage for the period. */
85
+ cpuPercent?: number;
86
+ /** Representative RAM usage in MB for the period. */
87
+ ramMb?: number;
88
+ /** Representative memory usage in MB for the period. */
89
+ memoryMb?: number;
90
+ /** Estimated cost in USD. */
91
+ costUsd?: string;
92
+ /** Optional artifact grouping key. */
93
+ artifactName?: string | null;
84
94
  }
85
95
 
86
96
  /** Single ledger entry for billing/ledger endpoint. */
@@ -40,6 +40,12 @@ export type {
40
40
  ValidateMappingResponse,
41
41
  TypingsBundle,
42
42
  FolderMapping,
43
+ SystemComponentFile,
44
+ SystemComponentDescriptor,
45
+ SystemComponentDetails,
46
+ SystemComponentInstallPlan,
47
+ WorkspaceDocPage,
48
+ WorkspaceDocsManifest,
43
49
  } from './workspace';
44
50
 
45
51
  // Testing
@@ -70,3 +70,75 @@ export interface FolderMapping {
70
70
  /** Absolute path to libraries folder. */
71
71
  libraries: string;
72
72
  }
73
+
74
+ /** Single file returned for a system component or install plan. */
75
+ export interface SystemComponentFile {
76
+ /** Relative file path within the component. */
77
+ path: string;
78
+ /** File contents. */
79
+ content: string;
80
+ }
81
+
82
+ /** System-owned component visible in the extension catalog. */
83
+ export interface SystemComponentDescriptor {
84
+ /** Component kind. */
85
+ kind: ArtifactType;
86
+ /** Unique component name. */
87
+ name: string;
88
+ /** Human-readable label. */
89
+ label: string;
90
+ /** Short summary for catalog views. */
91
+ description: string;
92
+ /** Whether this item is maintained by Bulltrackers. */
93
+ system: true;
94
+ /** Informational tags for filtering. */
95
+ tags: string[];
96
+ /** Current source version. */
97
+ version: string;
98
+ /** Stable sync hash for reinstall detection. */
99
+ syncHash: string;
100
+ }
101
+
102
+ /** Full details for a system component. */
103
+ export interface SystemComponentDetails {
104
+ /** Catalog descriptor. */
105
+ descriptor: SystemComponentDescriptor;
106
+ /** Source files for viewing in the UI. */
107
+ files: SystemComponentFile[];
108
+ /** Guidance shown next to the code. */
109
+ docs: string;
110
+ /** Install destination subfolder name. */
111
+ installSubpath: string;
112
+ }
113
+
114
+ /** Install payload for a system component. */
115
+ export interface SystemComponentInstallPlan {
116
+ /** Catalog descriptor. */
117
+ descriptor: SystemComponentDescriptor;
118
+ /** Sanitized source files to write locally. */
119
+ files: SystemComponentFile[];
120
+ /** Stable sync hash for reinstall comparison. */
121
+ syncHash: string;
122
+ /** Relative destination from configured root. */
123
+ destinationSubpath: string;
124
+ }
125
+
126
+ /** Remote documentation page. */
127
+ export interface WorkspaceDocPage {
128
+ /** Stable page id. */
129
+ id: string;
130
+ /** Page title. */
131
+ title: string;
132
+ /** Short summary for nav. */
133
+ summary: string;
134
+ /** Markdown content. */
135
+ markdown: string;
136
+ /** Optional tags. */
137
+ tags: string[];
138
+ }
139
+
140
+ /** Documentation manifest returned to the extension shell. */
141
+ export interface WorkspaceDocsManifest {
142
+ /** Documentation pages. */
143
+ pages: WorkspaceDocPage[];
144
+ }