@soederpop/luca 0.0.29 → 0.0.30
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/commands/try-all-challenges.ts +1 -1
- package/docs/TABLE-OF-CONTENTS.md +0 -3
- package/docs/tutorials/20-browser-esm.md +234 -0
- package/package.json +1 -1
- package/src/agi/container.server.ts +4 -0
- package/src/agi/features/assistant.ts +62 -1
- package/src/agi/features/browser-use.ts +623 -0
- package/src/bootstrap/generated.ts +236 -308
- package/src/cli/build-info.ts +2 -2
- package/src/clients/rest.ts +7 -7
- package/src/commands/chat.ts +22 -0
- package/src/commands/describe.ts +67 -2
- package/src/commands/prompt.ts +23 -3
- package/src/container.ts +411 -113
- package/src/helper.ts +189 -5
- package/src/introspection/generated.agi.ts +17148 -11148
- package/src/introspection/generated.node.ts +5179 -2200
- package/src/introspection/generated.web.ts +379 -291
- package/src/introspection/index.ts +7 -0
- package/src/introspection/scan.ts +224 -7
- package/src/node/container.ts +31 -10
- package/src/node/features/content-db.ts +7 -7
- package/src/node/features/disk-cache.ts +11 -11
- package/src/node/features/esbuild.ts +3 -3
- package/src/node/features/file-manager.ts +15 -15
- package/src/node/features/fs.ts +23 -22
- package/src/node/features/git.ts +10 -10
- package/src/node/features/ink.ts +13 -13
- package/src/node/features/ipc-socket.ts +8 -8
- package/src/node/features/networking.ts +3 -3
- package/src/node/features/os.ts +7 -7
- package/src/node/features/package-finder.ts +15 -15
- package/src/node/features/proc.ts +1 -1
- package/src/node/features/ui.ts +13 -13
- package/src/node/features/vm.ts +4 -4
- package/src/scaffolds/generated.ts +1 -1
- package/src/servers/express.ts +6 -6
- package/src/servers/mcp.ts +4 -4
- package/src/servers/socket.ts +6 -6
- package/docs/apis/features/node/window-manager.md +0 -445
- package/docs/examples/window-manager-layouts.md +0 -180
- package/docs/examples/window-manager.md +0 -125
- package/docs/window-manager-fix.md +0 -249
- package/scripts/test-window-manager-lifecycle.ts +0 -86
- package/scripts/test-window-manager.ts +0 -43
- package/src/node/features/window-manager.ts +0 -1603
|
@@ -75,6 +75,11 @@ export type HelperIntrospection = {
|
|
|
75
75
|
envVars?: string[]
|
|
76
76
|
// class-level @example blocks from JSDoc
|
|
77
77
|
examples?: ExampleIntrospection[]
|
|
78
|
+
// referenced type definitions resolved from parameter and return types
|
|
79
|
+
types?: Record<string, {
|
|
80
|
+
description: string;
|
|
81
|
+
properties: Record<string, { type: string; description: string; optional?: boolean }>
|
|
82
|
+
}>
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
export type RegistryIntrospection = {
|
|
@@ -203,6 +208,7 @@ export function setBuildTimeData(key: string, data: HelperIntrospection) {
|
|
|
203
208
|
getters: data.getters || existing?.getters || {},
|
|
204
209
|
envVars: existing?.envVars || data.envVars || [],
|
|
205
210
|
examples: data.examples || existing?.examples,
|
|
211
|
+
types: { ...(existing?.types || {}), ...(data.types || {}) },
|
|
206
212
|
})
|
|
207
213
|
}
|
|
208
214
|
|
|
@@ -259,6 +265,7 @@ export function interceptRegistration(registry: any, helperConstructor: any) {
|
|
|
259
265
|
? helperConstructor.envVars
|
|
260
266
|
: (existing?.envVars || []),
|
|
261
267
|
examples: existing?.examples,
|
|
268
|
+
types: existing?.types,
|
|
262
269
|
}
|
|
263
270
|
|
|
264
271
|
// Always populate state and options from Zod schemas at runtime
|
|
@@ -189,6 +189,7 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
189
189
|
const getters = this.extractGetters(classNode, sourceFile);
|
|
190
190
|
const events = this.extractEvents(classNode, sourceFile);
|
|
191
191
|
const examples = this.extractJSDocExamples(classNode);
|
|
192
|
+
const types = this.collectReferencedTypes(methods, getters, sourceFile);
|
|
192
193
|
|
|
193
194
|
// state and options are derived at runtime from Zod schemas
|
|
194
195
|
// via interceptRegistration — no need to extract them at build time
|
|
@@ -204,6 +205,7 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
204
205
|
options: {},
|
|
205
206
|
envVars: [],
|
|
206
207
|
...(examples.length > 0 ? { examples } : {}),
|
|
208
|
+
...(Object.keys(types).length > 0 ? { types } : {}),
|
|
207
209
|
};
|
|
208
210
|
}
|
|
209
211
|
|
|
@@ -230,7 +232,7 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
230
232
|
* These are framework internals (class references, utility objects) not useful in inspection.
|
|
231
233
|
*/
|
|
232
234
|
private static CONTAINER_SKIP_GETTERS = new Set([
|
|
233
|
-
'Feature', 'Helper', 'State', 'z',
|
|
235
|
+
'Feature', 'Helper', 'State', 'z',
|
|
234
236
|
]);
|
|
235
237
|
|
|
236
238
|
private extendsContainer(classNode: ts.ClassDeclaration): boolean {
|
|
@@ -298,6 +300,9 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
298
300
|
// Skip methods starting with _ (internal methods like _hide)
|
|
299
301
|
if (methodName.startsWith('_')) continue;
|
|
300
302
|
|
|
303
|
+
// Skip methods tagged with @internal
|
|
304
|
+
if (this.hasJSDocTag(member, 'internal')) continue;
|
|
305
|
+
|
|
301
306
|
const description = this.extractJSDocDescription(member) || '';
|
|
302
307
|
const parameters = this.extractParameters(member);
|
|
303
308
|
const required = this.extractRequiredParameters(member);
|
|
@@ -327,6 +332,9 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
327
332
|
// Skip container framework getters
|
|
328
333
|
if (IntrospectionScannerFeature.CONTAINER_SKIP_GETTERS.has(getterName)) continue;
|
|
329
334
|
|
|
335
|
+
// Skip getters tagged with @internal
|
|
336
|
+
if (this.hasJSDocTag(member, 'internal')) continue;
|
|
337
|
+
|
|
330
338
|
// Skip private getters unless includePrivate is true
|
|
331
339
|
const isPrivate = member.modifiers?.some(mod => mod.kind === ts.SyntaxKind.PrivateKeyword);
|
|
332
340
|
if (isPrivate && !this.options.includePrivate) continue;
|
|
@@ -336,7 +344,7 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
336
344
|
if (isStatic) continue;
|
|
337
345
|
|
|
338
346
|
const description = this.extractJSDocDescriptionFromAccessor(member) || '';
|
|
339
|
-
const returns = member
|
|
347
|
+
const returns = this.resolveReturnType(member, member.type, 'any');
|
|
340
348
|
const examples = this.extractJSDocExamples(member);
|
|
341
349
|
|
|
342
350
|
getters[getterName] = {
|
|
@@ -486,6 +494,9 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
486
494
|
const isStatic = member.modifiers?.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
|
|
487
495
|
if (isStatic) continue;
|
|
488
496
|
|
|
497
|
+
// Skip methods tagged with @internal
|
|
498
|
+
if (this.hasJSDocTag(member, 'internal')) continue;
|
|
499
|
+
|
|
489
500
|
const description = this.extractJSDocDescription(member) || '';
|
|
490
501
|
const parameters = this.extractParameters(member);
|
|
491
502
|
const required = this.extractRequiredParameters(member);
|
|
@@ -539,8 +550,11 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
539
550
|
const isStatic = member.modifiers?.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword);
|
|
540
551
|
if (isStatic) continue;
|
|
541
552
|
|
|
553
|
+
// Skip getters tagged with @internal
|
|
554
|
+
if (this.hasJSDocTag(member, 'internal')) continue;
|
|
555
|
+
|
|
542
556
|
const description = this.extractJSDocDescriptionFromAccessor(member) || '';
|
|
543
|
-
const returns = member
|
|
557
|
+
const returns = this.resolveReturnType(member, member.type, 'any');
|
|
544
558
|
const examples = this.extractJSDocExamples(member);
|
|
545
559
|
|
|
546
560
|
getters[getterName] = {
|
|
@@ -554,6 +568,57 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
554
568
|
return getters;
|
|
555
569
|
}
|
|
556
570
|
|
|
571
|
+
/**
|
|
572
|
+
* Extract a return type from a node, preferring explicit TS annotation,
|
|
573
|
+
* falling back to JSDoc @returns {Type}, then to the given default.
|
|
574
|
+
*/
|
|
575
|
+
private resolveReturnType(node: ts.Node, typeNode: ts.TypeNode | undefined, fallback: string): string {
|
|
576
|
+
if (typeNode) return typeNode.getText();
|
|
577
|
+
|
|
578
|
+
const sourceFile = node.getSourceFile();
|
|
579
|
+
const fullText = sourceFile.getFullText();
|
|
580
|
+
const ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());
|
|
581
|
+
|
|
582
|
+
if (ranges && ranges.length > 0) {
|
|
583
|
+
for (let i = ranges.length - 1; i >= 0; i--) {
|
|
584
|
+
const range = ranges[i];
|
|
585
|
+
if (!range) continue;
|
|
586
|
+
const commentText = fullText.substring(range.pos, range.end);
|
|
587
|
+
if (!commentText.startsWith('/**')) continue;
|
|
588
|
+
|
|
589
|
+
const match = commentText.match(/@returns?\s*\{([^}]+)\}/);
|
|
590
|
+
if (match) return match[1].trim();
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return fallback;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Check if a node's JSDoc comment contains a specific @tag (e.g. @internal).
|
|
599
|
+
*/
|
|
600
|
+
private hasJSDocTag(node: ts.Node, tagName: string): boolean {
|
|
601
|
+
const sourceFile = node.getSourceFile();
|
|
602
|
+
const fullText = sourceFile.getFullText();
|
|
603
|
+
const ranges = ts.getLeadingCommentRanges(fullText, node.getFullStart());
|
|
604
|
+
|
|
605
|
+
if (!ranges || ranges.length === 0) return false;
|
|
606
|
+
|
|
607
|
+
for (let i = ranges.length - 1; i >= 0; i--) {
|
|
608
|
+
const range = ranges[i];
|
|
609
|
+
if (!range) continue;
|
|
610
|
+
const commentText = fullText.substring(range.pos, range.end);
|
|
611
|
+
if (!commentText.startsWith('/**')) continue;
|
|
612
|
+
|
|
613
|
+
// Check for @tagName anywhere in the JSDoc
|
|
614
|
+
if (new RegExp(`@${tagName}\\b`).test(commentText)) {
|
|
615
|
+
return true;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
|
|
557
622
|
private extractJSDocDescriptionFromAccessor(node: ts.GetAccessorDeclaration): string | null {
|
|
558
623
|
const sourceFile = node.getSourceFile();
|
|
559
624
|
const fullText = sourceFile.getFullText();
|
|
@@ -781,6 +846,161 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
781
846
|
return Object.keys(members).length > 0 ? members : null as any;
|
|
782
847
|
}
|
|
783
848
|
|
|
849
|
+
/**
|
|
850
|
+
* Collect all non-primitive type names referenced in method parameters and return types,
|
|
851
|
+
* then resolve each to its properties from the source file. Returns a map of type name
|
|
852
|
+
* to its property definitions, suitable for emitting as standalone interface declarations.
|
|
853
|
+
*/
|
|
854
|
+
private collectReferencedTypes(
|
|
855
|
+
methods: Record<string, MethodIntrospection>,
|
|
856
|
+
getters: Record<string, { returns: string; description: string }>,
|
|
857
|
+
sourceFile: ts.SourceFile
|
|
858
|
+
): Record<string, { description: string; properties: Record<string, { type: string; description: string; optional?: boolean }> }> {
|
|
859
|
+
const types: Record<string, { description: string; properties: Record<string, { type: string; description: string; optional?: boolean }> }> = {};
|
|
860
|
+
const seen = new Set<string>();
|
|
861
|
+
|
|
862
|
+
const tryResolve = (rawType: string) => {
|
|
863
|
+
// Extract type names from complex type strings
|
|
864
|
+
// e.g. "Promise<GrepMatch[]>" -> "GrepMatch"
|
|
865
|
+
// e.g. "GrepOptions" -> "GrepOptions"
|
|
866
|
+
// e.g. "string | Foo" -> "Foo"
|
|
867
|
+
// e.g. "Omit<GrepOptions, 'pattern'>" -> "GrepOptions"
|
|
868
|
+
const typeNames = this.extractTypeNames(rawType);
|
|
869
|
+
|
|
870
|
+
for (const typeName of typeNames) {
|
|
871
|
+
if (seen.has(typeName)) continue;
|
|
872
|
+
seen.add(typeName);
|
|
873
|
+
|
|
874
|
+
if (IntrospectionScannerFeature.PRIMITIVE_TYPES.has(typeName)) continue;
|
|
875
|
+
|
|
876
|
+
const properties = this.resolveTypePropertiesWithOptional(typeName, sourceFile);
|
|
877
|
+
if (properties && Object.keys(properties).length > 0) {
|
|
878
|
+
// Extract JSDoc description from the type declaration itself
|
|
879
|
+
const description = this.extractTypeDescription(typeName, sourceFile) || '';
|
|
880
|
+
types[typeName] = { description, properties };
|
|
881
|
+
|
|
882
|
+
// Recursively resolve types referenced in properties
|
|
883
|
+
for (const prop of Object.values(properties)) {
|
|
884
|
+
tryResolve(prop.type);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
// Collect from method parameters and return types
|
|
891
|
+
for (const method of Object.values(methods)) {
|
|
892
|
+
for (const param of Object.values(method.parameters || {})) {
|
|
893
|
+
tryResolve(param.type);
|
|
894
|
+
}
|
|
895
|
+
if (method.returns) {
|
|
896
|
+
tryResolve(method.returns);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// Collect from getter return types
|
|
901
|
+
for (const getter of Object.values(getters)) {
|
|
902
|
+
if (getter.returns) {
|
|
903
|
+
tryResolve(getter.returns);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
return types;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Extract concrete type names from a potentially complex type string.
|
|
912
|
+
* Handles generics, unions, intersections, arrays, and utility types.
|
|
913
|
+
*/
|
|
914
|
+
private extractTypeNames(typeStr: string): string[] {
|
|
915
|
+
if (!typeStr) return [];
|
|
916
|
+
|
|
917
|
+
// Remove Promise<...> wrapper but keep the inner type
|
|
918
|
+
// Remove array suffix [], Partial<>, Omit<>, Pick<>, etc.
|
|
919
|
+
// Split on | and & for unions/intersections
|
|
920
|
+
const names: string[] = [];
|
|
921
|
+
|
|
922
|
+
// Match identifiers that look like type names (PascalCase or known patterns)
|
|
923
|
+
// This regex finds all capitalized identifiers that aren't JS built-ins
|
|
924
|
+
const matches = typeStr.match(/\b([A-Z][A-Za-z0-9_]*)\b/g) || [];
|
|
925
|
+
const BUILTIN_TYPES = new Set([
|
|
926
|
+
'Promise', 'Array', 'Record', 'Map', 'Set', 'WeakMap', 'WeakSet',
|
|
927
|
+
'Partial', 'Required', 'Readonly', 'Pick', 'Omit', 'Exclude', 'Extract',
|
|
928
|
+
'Buffer', 'Date', 'RegExp', 'Error', 'Function', 'Symbol',
|
|
929
|
+
'AsyncIterable', 'Iterable', 'Iterator', 'AsyncIterator',
|
|
930
|
+
'InstanceType', 'ReturnType', 'Parameters',
|
|
931
|
+
'HTMLElement', 'Event', 'EventTarget',
|
|
932
|
+
'Stats', // Node.js fs.Stats — commonly used but not resolvable in-file
|
|
933
|
+
]);
|
|
934
|
+
|
|
935
|
+
for (const match of matches) {
|
|
936
|
+
if (!BUILTIN_TYPES.has(match) && !IntrospectionScannerFeature.PRIMITIVE_TYPES.has(match.toLowerCase())) {
|
|
937
|
+
names.push(match);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
return [...new Set(names)];
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Like resolveTypeProperties but also captures whether each property is optional (has ?).
|
|
946
|
+
*/
|
|
947
|
+
private resolveTypePropertiesWithOptional(typeName: string, sourceFile: ts.SourceFile): Record<string, { type: string; description: string; optional?: boolean }> | null {
|
|
948
|
+
for (const statement of sourceFile.statements) {
|
|
949
|
+
if (ts.isTypeAliasDeclaration(statement) && statement.name.text === typeName) {
|
|
950
|
+
if (ts.isTypeLiteralNode(statement.type)) {
|
|
951
|
+
return this.extractTypeLiteralMembersWithOptional(statement.type, sourceFile);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
if (ts.isInterfaceDeclaration(statement) && statement.name.text === typeName) {
|
|
955
|
+
return this.extractInterfaceMembersWithOptional(statement, sourceFile);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
private extractTypeLiteralMembersWithOptional(node: ts.TypeLiteralNode, sourceFile: ts.SourceFile): Record<string, { type: string; description: string; optional?: boolean }> | null {
|
|
962
|
+
const members: Record<string, { type: string; description: string; optional?: boolean }> = {};
|
|
963
|
+
for (const member of node.members) {
|
|
964
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
965
|
+
const name = member.name.getText();
|
|
966
|
+
const type = member.type ? member.type.getText() : 'any';
|
|
967
|
+
const description = this.extractJSDocFromNode(member, sourceFile) || '';
|
|
968
|
+
const optional = !!member.questionToken;
|
|
969
|
+
members[name] = { type, description, ...(optional ? { optional } : {}) };
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return Object.keys(members).length > 0 ? members : null;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
private extractInterfaceMembersWithOptional(node: ts.InterfaceDeclaration, sourceFile: ts.SourceFile): Record<string, { type: string; description: string; optional?: boolean }> | null {
|
|
976
|
+
const members: Record<string, { type: string; description: string; optional?: boolean }> = {};
|
|
977
|
+
for (const member of node.members) {
|
|
978
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
979
|
+
const name = member.name.getText();
|
|
980
|
+
const type = member.type ? member.type.getText() : 'any';
|
|
981
|
+
const description = this.extractJSDocFromNode(member, sourceFile) || '';
|
|
982
|
+
const optional = !!member.questionToken;
|
|
983
|
+
members[name] = { type, description, ...(optional ? { optional } : {}) };
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return Object.keys(members).length > 0 ? members : null;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Extract the JSDoc description from a type alias or interface declaration.
|
|
991
|
+
*/
|
|
992
|
+
private extractTypeDescription(typeName: string, sourceFile: ts.SourceFile): string | null {
|
|
993
|
+
for (const statement of sourceFile.statements) {
|
|
994
|
+
if (
|
|
995
|
+
(ts.isTypeAliasDeclaration(statement) || ts.isInterfaceDeclaration(statement)) &&
|
|
996
|
+
statement.name.text === typeName
|
|
997
|
+
) {
|
|
998
|
+
return this.extractJSDocFromNode(statement, sourceFile);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return null;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
784
1004
|
/**
|
|
785
1005
|
* Extracts a JSDoc description from any node that may have leading comments.
|
|
786
1006
|
*/
|
|
@@ -828,10 +1048,7 @@ export class IntrospectionScannerFeature extends Feature<IntrospectionScannerSta
|
|
|
828
1048
|
}
|
|
829
1049
|
|
|
830
1050
|
private extractReturnType(method: ts.MethodDeclaration): string {
|
|
831
|
-
|
|
832
|
-
return method.type.getText();
|
|
833
|
-
}
|
|
834
|
-
return 'void';
|
|
1051
|
+
return this.resolveReturnType(method, method.type, 'void');
|
|
835
1052
|
}
|
|
836
1053
|
|
|
837
1054
|
private extractEvents(classNode: ts.ClassDeclaration, sourceFile: ts.SourceFile): Record<string, EventIntrospection> {
|
package/src/node/container.ts
CHANGED
|
@@ -57,7 +57,6 @@ import "./features/google-sheets";
|
|
|
57
57
|
import "./features/google-calendar";
|
|
58
58
|
import "./features/google-docs";
|
|
59
59
|
import "./features/google-mail";
|
|
60
|
-
import "./features/window-manager";
|
|
61
60
|
import "./features/nlp";
|
|
62
61
|
import "./features/process-manager"
|
|
63
62
|
import "./features/tts";
|
|
@@ -102,7 +101,6 @@ import type { GoogleSheets } from './features/google-sheets';
|
|
|
102
101
|
import type { GoogleCalendar } from './features/google-calendar';
|
|
103
102
|
import type { GoogleDocs } from './features/google-docs';
|
|
104
103
|
import type { GoogleMail } from './features/google-mail';
|
|
105
|
-
import type { WindowManager } from './features/window-manager';
|
|
106
104
|
import type { NLP } from './features/nlp';
|
|
107
105
|
import type { ProcessManager } from './features/process-manager'
|
|
108
106
|
import type { TTS } from './features/tts';
|
|
@@ -142,7 +140,6 @@ export {
|
|
|
142
140
|
type GoogleCalendar,
|
|
143
141
|
type GoogleDocs,
|
|
144
142
|
type GoogleMail,
|
|
145
|
-
type WindowManager,
|
|
146
143
|
type NLP,
|
|
147
144
|
type ProcessManager,
|
|
148
145
|
type TTS,
|
|
@@ -208,7 +205,6 @@ export interface NodeFeatures extends AvailableFeatures {
|
|
|
208
205
|
googleCalendar: typeof GoogleCalendar;
|
|
209
206
|
googleDocs: typeof GoogleDocs;
|
|
210
207
|
googleMail: typeof GoogleMail;
|
|
211
|
-
windowManager: typeof WindowManager;
|
|
212
208
|
nlp: typeof NLP;
|
|
213
209
|
processManager: typeof ProcessManager;
|
|
214
210
|
tts: typeof TTS;
|
|
@@ -233,9 +229,35 @@ export interface NodeContainerState extends ContainerState {
|
|
|
233
229
|
}
|
|
234
230
|
*/
|
|
235
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Server-side container for Node.js and Bun environments. Extends the base Container with
|
|
234
|
+
* file system access, process management, git integration, and other server-side capabilities.
|
|
235
|
+
*
|
|
236
|
+
* Auto-enables core features on construction: fs, proc, git, grep, os, networking, ui, vm, esbuild, helpers.
|
|
237
|
+
* Also attaches Client, Server, Command, Endpoint, and Selector helper types, providing
|
|
238
|
+
* `container.client()`, `container.server()`, `container.command()`, etc. factory methods.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* ```ts
|
|
242
|
+
* import container from '@soederpop/luca/node'
|
|
243
|
+
*
|
|
244
|
+
* // File operations
|
|
245
|
+
* const content = container.fs.readFile('README.md')
|
|
246
|
+
*
|
|
247
|
+
* // Path utilities (scoped to cwd)
|
|
248
|
+
* const full = container.paths.resolve('src/index.ts')
|
|
249
|
+
*
|
|
250
|
+
* // Create a REST client
|
|
251
|
+
* const api = container.client('rest', { baseURL: 'https://api.example.com' })
|
|
252
|
+
*
|
|
253
|
+
* // Start an Express server
|
|
254
|
+
* const app = container.server('express', { port: 3000 })
|
|
255
|
+
* await app.start()
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
236
258
|
export class NodeContainer<
|
|
237
259
|
Features extends NodeFeatures = NodeFeatures,
|
|
238
|
-
K extends ContainerState = ContainerState
|
|
260
|
+
K extends ContainerState = ContainerState
|
|
239
261
|
> extends Container<Features, K> {
|
|
240
262
|
fs!: FS;
|
|
241
263
|
git!: Git;
|
|
@@ -268,7 +290,6 @@ export class NodeContainer<
|
|
|
268
290
|
googleCalendar?: GoogleCalendar;
|
|
269
291
|
googleDocs?: GoogleDocs;
|
|
270
292
|
googleMail?: GoogleMail;
|
|
271
|
-
windowManager?: WindowManager;
|
|
272
293
|
nlp?: NLP;
|
|
273
294
|
processManager?: ProcessManager;
|
|
274
295
|
tts?: TTS;
|
|
@@ -318,7 +339,7 @@ export class NodeContainer<
|
|
|
318
339
|
}
|
|
319
340
|
|
|
320
341
|
/** Returns the parsed package.json manifest for the current working directory. */
|
|
321
|
-
get manifest() {
|
|
342
|
+
get manifest(): Record<string, any> {
|
|
322
343
|
try {
|
|
323
344
|
const packageJson = this.fs.findUp("packageon");
|
|
324
345
|
|
|
@@ -339,19 +360,19 @@ export class NodeContainer<
|
|
|
339
360
|
}
|
|
340
361
|
|
|
341
362
|
/** Returns the parsed command-line arguments (from minimist). */
|
|
342
|
-
get argv() {
|
|
363
|
+
get argv(): Record<string, any> & { _: string[] } {
|
|
343
364
|
return this.options as any;
|
|
344
365
|
}
|
|
345
366
|
|
|
346
367
|
/** Returns URL utility functions for parsing URIs. */
|
|
347
|
-
get urlUtils() {
|
|
368
|
+
get urlUtils(): { parse: (uri: string) => url.URL | null } {
|
|
348
369
|
return {
|
|
349
370
|
parse: (uri: string) => url.parse(uri)
|
|
350
371
|
}
|
|
351
372
|
}
|
|
352
373
|
|
|
353
374
|
/** Returns path utility functions scoped to the current working directory (join, resolve, relative, dirname, parse). */
|
|
354
|
-
get paths() {
|
|
375
|
+
get paths(): { dirname: (path: string) => string; join: (...paths: string[]) => string; resolve: (...paths: string[]) => string; relative: (...paths: string[]) => string; basename: typeof basename; parse: typeof parse } {
|
|
355
376
|
const { cwd } = this;
|
|
356
377
|
return {
|
|
357
378
|
dirname(path: string) {
|
|
@@ -118,15 +118,15 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/** Whether the content database has been loaded. */
|
|
121
|
-
get isLoaded() {
|
|
122
|
-
return this.state.get('started')
|
|
121
|
+
get isLoaded(): boolean {
|
|
122
|
+
return !!this.state.get('started')
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
_collection?: Collection
|
|
126
126
|
private _contentbaseSeeded = false
|
|
127
127
|
|
|
128
128
|
/** Returns the lazily-initialized Collection instance for the configured rootPath. */
|
|
129
|
-
get collection() {
|
|
129
|
+
get collection(): Collection {
|
|
130
130
|
if (this._collection) return this._collection
|
|
131
131
|
|
|
132
132
|
const opts: any = { rootPath: this.options.rootPath }
|
|
@@ -172,7 +172,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
|
|
|
172
172
|
}
|
|
173
173
|
|
|
174
174
|
/** Returns the absolute resolved path to the collection root directory. */
|
|
175
|
-
get collectionPath() {
|
|
175
|
+
get collectionPath(): string {
|
|
176
176
|
return this.container.paths.resolve(this.options.rootPath)
|
|
177
177
|
}
|
|
178
178
|
|
|
@@ -429,7 +429,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
|
|
|
429
429
|
return this.collection.generateModelSummary(options)
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
-
get modelDefinitionTable() {
|
|
432
|
+
get modelDefinitionTable(): Record<string, { description: string; glob: string; routePatterns: string[] }> {
|
|
433
433
|
return Object.fromEntries(this.collection.modelDefinitions.map(d => {
|
|
434
434
|
|
|
435
435
|
const prefixPattern = this.container.paths.relative(this.collection.resolve(d.prefix))
|
|
@@ -442,7 +442,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
|
|
|
442
442
|
}))
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
get fileTree() {
|
|
445
|
+
get fileTree(): string {
|
|
446
446
|
return this.collection.renderFileTree()
|
|
447
447
|
}
|
|
448
448
|
|
|
@@ -579,7 +579,7 @@ export class ContentDb extends Feature<ContentDbState, ContentDbOptions> {
|
|
|
579
579
|
/**
|
|
580
580
|
* Get the current search index status.
|
|
581
581
|
*/
|
|
582
|
-
get searchIndexStatus() {
|
|
582
|
+
get searchIndexStatus(): { exists: boolean; documentCount: number; chunkCount: number; embeddingCount: number; lastIndexedAt: any; provider: any; model: any; dimensions: number; dbSizeBytes: number } {
|
|
583
583
|
if (!this._semanticSearch?.state?.get('dbReady')) {
|
|
584
584
|
if (!this._hasSearchIndex()) {
|
|
585
585
|
return { exists: false, documentCount: 0, chunkCount: 0, embeddingCount: 0, lastIndexedAt: null, provider: null, model: null, dimensions: 0, dbSizeBytes: 0 }
|
|
@@ -37,7 +37,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
37
37
|
static { Feature.register(this, 'diskCache') }
|
|
38
38
|
|
|
39
39
|
/** Returns the underlying cacache instance configured with the cache directory path. */
|
|
40
|
-
get cache() {
|
|
40
|
+
get cache(): ReturnType<typeof this.create> {
|
|
41
41
|
if(this._cache) {
|
|
42
42
|
return this._cache
|
|
43
43
|
}
|
|
@@ -61,7 +61,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
61
61
|
* await diskCache.saveFile('encodedImage', './images/photo.jpg', true)
|
|
62
62
|
* ```
|
|
63
63
|
*/
|
|
64
|
-
async saveFile(key: string, outputPath: string, isBase64 = false) {
|
|
64
|
+
async saveFile(key: string, outputPath: string, isBase64 = false): Promise<Buffer | string> {
|
|
65
65
|
const outPath = this.container.paths.resolve(outputPath)
|
|
66
66
|
const content = await this.get(key)
|
|
67
67
|
const data = isBase64 ? Buffer.from(content, 'base64') : content
|
|
@@ -79,7 +79,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
79
79
|
* await diskCache.ensure('config', JSON.stringify(defaultConfig))
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
|
-
async ensure(key: string, content: string) {
|
|
82
|
+
async ensure(key: string, content: string): Promise<string> {
|
|
83
83
|
const exists = await this.has(key)
|
|
84
84
|
|
|
85
85
|
if (!exists) {
|
|
@@ -102,7 +102,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
102
102
|
* await diskCache.copy('file1', 'file2', true) // force overwrite
|
|
103
103
|
* ```
|
|
104
104
|
*/
|
|
105
|
-
async copy(source: string, destination: string, overwrite: boolean = false) {
|
|
105
|
+
async copy(source: string, destination: string, overwrite: boolean = false): Promise<string> {
|
|
106
106
|
if(!overwrite && (await this.has(destination))) {
|
|
107
107
|
throw new Error('Destination already exists')
|
|
108
108
|
}
|
|
@@ -125,7 +125,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
125
125
|
* await diskCache.move('old_key', 'new_key', true) // force overwrite
|
|
126
126
|
* ```
|
|
127
127
|
*/
|
|
128
|
-
async move(source: string, destination: string, overwrite: boolean = false) {
|
|
128
|
+
async move(source: string, destination: string, overwrite: boolean = false): Promise<string> {
|
|
129
129
|
if(!overwrite && (await this.has(destination))) {
|
|
130
130
|
throw new Error('Destination already exists')
|
|
131
131
|
}
|
|
@@ -147,7 +147,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
147
147
|
* }
|
|
148
148
|
* ```
|
|
149
149
|
*/
|
|
150
|
-
async has(key: string) {
|
|
150
|
+
async has(key: string): Promise<boolean> {
|
|
151
151
|
return this.cache.get.info(key).then((r:any) => r != null)
|
|
152
152
|
}
|
|
153
153
|
|
|
@@ -162,7 +162,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
162
162
|
* const data = await diskCache.get('myData', true) // parse as JSON
|
|
163
163
|
* ```
|
|
164
164
|
*/
|
|
165
|
-
async get(key: string, json = false) {
|
|
165
|
+
async get(key: string, json = false): Promise<any> {
|
|
166
166
|
const val = this.options.encrypt
|
|
167
167
|
? await this.securely.get(key)
|
|
168
168
|
: await this.cache.get(key).then((data: any) => data.data.toString())
|
|
@@ -191,7 +191,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
191
191
|
* await diskCache.set('file', content, { size: 1024, type: 'image' })
|
|
192
192
|
* ```
|
|
193
193
|
*/
|
|
194
|
-
async set(key: string, value: any, meta?: any) {
|
|
194
|
+
async set(key: string, value: any, meta?: any): Promise<any> {
|
|
195
195
|
if (this.options.encrypt) {
|
|
196
196
|
return this.securely.set(key, value, meta)
|
|
197
197
|
}
|
|
@@ -216,7 +216,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
216
216
|
* await diskCache.rm('obsoleteKey')
|
|
217
217
|
* ```
|
|
218
218
|
*/
|
|
219
|
-
async rm(key: string) {
|
|
219
|
+
async rm(key: string): Promise<any> {
|
|
220
220
|
return this.cache.rm.entry(key)
|
|
221
221
|
}
|
|
222
222
|
|
|
@@ -230,7 +230,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
230
230
|
* await diskCache.clearAll(true) // Must explicitly confirm
|
|
231
231
|
* ```
|
|
232
232
|
*/
|
|
233
|
-
async clearAll(confirm = false) {
|
|
233
|
+
async clearAll(confirm = false): Promise<this> {
|
|
234
234
|
if(confirm !== true) {
|
|
235
235
|
throw new Error('Must confirm with clearAll(true)')
|
|
236
236
|
}
|
|
@@ -281,7 +281,7 @@ export class DiskCache extends Feature<FeatureState,DiskCacheOptions> {
|
|
|
281
281
|
* const decrypted = await cache.securely.get('sensitive')
|
|
282
282
|
* ```
|
|
283
283
|
*/
|
|
284
|
-
get securely() {
|
|
284
|
+
get securely(): { set(name: string, payload: any, meta?: any): Promise<any>; get(name: string): Promise<any> } {
|
|
285
285
|
const { secret, encrypt } = this.options
|
|
286
286
|
|
|
287
287
|
if (!encrypt) {
|
|
@@ -27,7 +27,7 @@ export class ESBuild extends Feature {
|
|
|
27
27
|
* @param options - The options to pass to esbuild
|
|
28
28
|
* @returns The transformed code
|
|
29
29
|
*/
|
|
30
|
-
transformSync(code: string, options?: esbuild.TransformOptions) {
|
|
30
|
+
transformSync(code: string, options?: esbuild.TransformOptions): esbuild.TransformResult {
|
|
31
31
|
return esbuild.transformSync(code, {
|
|
32
32
|
loader: 'ts',
|
|
33
33
|
format: 'esm',
|
|
@@ -44,7 +44,7 @@ export class ESBuild extends Feature {
|
|
|
44
44
|
* @param options - The options to pass to esbuild
|
|
45
45
|
* @returns The transformed code
|
|
46
46
|
*/
|
|
47
|
-
async transform(code: string, options?: esbuild.TransformOptions) {
|
|
47
|
+
async transform(code: string, options?: esbuild.TransformOptions): Promise<esbuild.TransformResult> {
|
|
48
48
|
return esbuild.transform(code, {
|
|
49
49
|
loader: 'ts',
|
|
50
50
|
format: 'esm',
|
|
@@ -63,7 +63,7 @@ export class ESBuild extends Feature {
|
|
|
63
63
|
* @param options - esbuild BuildOptions overrides
|
|
64
64
|
* @returns The build result with outputFiles when write is false
|
|
65
65
|
*/
|
|
66
|
-
async bundle(entryPoints: string[], options?: esbuild.BuildOptions) {
|
|
66
|
+
async bundle(entryPoints: string[], options?: esbuild.BuildOptions): Promise<esbuild.BuildResult> {
|
|
67
67
|
return esbuild.build({
|
|
68
68
|
entryPoints,
|
|
69
69
|
bundle: true,
|