@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
package/src/helper.ts
CHANGED
|
@@ -73,6 +73,31 @@ export abstract class Helper<T extends HelperState = HelperState, K extends Help
|
|
|
73
73
|
return presentIntrospectionJSONAsMarkdown(introspection, depth, section)
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Returns the introspection data formatted as a TypeScript interface declaration.
|
|
78
|
+
* Useful for AI agents that reason better with structured type information,
|
|
79
|
+
* or for generating `.d.ts` files that accurately describe a helper's public API.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* console.log(container.feature('fs').inspectAsType())
|
|
84
|
+
* // interface FS {
|
|
85
|
+
* // readonly cwd: string;
|
|
86
|
+
* // readFile(path: string): Promise<string>;
|
|
87
|
+
* // ...
|
|
88
|
+
* // }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
static inspectAsType(section?: IntrospectionSection) : string {
|
|
92
|
+
return this.introspectAsType(section)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static introspectAsType(section?: IntrospectionSection) : string {
|
|
96
|
+
const introspection = this.introspect()
|
|
97
|
+
if (!introspection) return ''
|
|
98
|
+
return presentIntrospectionAsTypeScript(introspection, section)
|
|
99
|
+
}
|
|
100
|
+
|
|
76
101
|
|
|
77
102
|
/**
|
|
78
103
|
* All Helpers can be introspect()ed and, assuming the introspection data has been loaded into the registry,
|
|
@@ -90,8 +115,8 @@ export abstract class Helper<T extends HelperState = HelperState, K extends Help
|
|
|
90
115
|
|
|
91
116
|
/** Alias for introspect */
|
|
92
117
|
inspect(section?: IntrospectionSection) : HelperIntrospection | undefined {
|
|
93
|
-
return this.introspect()
|
|
94
|
-
}
|
|
118
|
+
return this.introspect(section)
|
|
119
|
+
}
|
|
95
120
|
|
|
96
121
|
/**
|
|
97
122
|
* Returns the introspection data formatted as a markdown string.
|
|
@@ -110,7 +135,23 @@ export abstract class Helper<T extends HelperState = HelperState, K extends Help
|
|
|
110
135
|
inspectAsText(sectionOrDepth?: IntrospectionSection | number, startHeadingDepth?: number) : string {
|
|
111
136
|
return this.introspectAsText(sectionOrDepth, startHeadingDepth)
|
|
112
137
|
}
|
|
113
|
-
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Returns the introspection data formatted as a TypeScript interface declaration.
|
|
141
|
+
* Useful for AI agents that reason better with structured type information,
|
|
142
|
+
* or for generating `.d.ts` files that accurately describe a helper's public API.
|
|
143
|
+
*/
|
|
144
|
+
introspectAsType(section?: IntrospectionSection) : string {
|
|
145
|
+
const introspection = this.introspect()
|
|
146
|
+
if (!introspection) return ''
|
|
147
|
+
return presentIntrospectionAsTypeScript(introspection, section)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Alias for introspectAsType */
|
|
151
|
+
inspectAsType(section?: IntrospectionSection) : string {
|
|
152
|
+
return this.introspectAsType(section)
|
|
153
|
+
}
|
|
154
|
+
|
|
114
155
|
constructor(options: K, context: ContainerContext) {
|
|
115
156
|
const optionSchema = (this.constructor as any).optionsSchema
|
|
116
157
|
if (optionSchema && typeof optionSchema.safeParse === 'function') {
|
|
@@ -226,7 +267,7 @@ export abstract class Helper<T extends HelperState = HelperState, K extends Help
|
|
|
226
267
|
*/
|
|
227
268
|
toTools(): { schemas: Record<string, z.ZodType>, handlers: Record<string, Function> } {
|
|
228
269
|
// Walk the prototype chain collecting static tools (parent-first, child overwrites)
|
|
229
|
-
const merged: Record<string, { schema: z.ZodType, handler?: Function }> = {}
|
|
270
|
+
const merged: Record<string, { schema: z.ZodType, description?: string, handler?: Function }> = {}
|
|
230
271
|
const chain: Function[] = []
|
|
231
272
|
|
|
232
273
|
let current = this.constructor as any
|
|
@@ -248,7 +289,11 @@ export abstract class Helper<T extends HelperState = HelperState, K extends Help
|
|
|
248
289
|
const handlers: Record<string, Function> = {}
|
|
249
290
|
|
|
250
291
|
for (const [name, entry] of Object.entries(merged)) {
|
|
251
|
-
|
|
292
|
+
// If the tool entry has a description but the schema doesn't, attach it
|
|
293
|
+
// so addTool() picks it up from jsonSchema.description.
|
|
294
|
+
schemas[name] = entry.description && !entry.schema.description
|
|
295
|
+
? entry.schema.describe(entry.description)
|
|
296
|
+
: entry.schema
|
|
252
297
|
if (entry.handler) {
|
|
253
298
|
handlers[name] = (args: any) => entry.handler!(args, this)
|
|
254
299
|
} else if (typeof (this as any)[name] === 'function') {
|
|
@@ -589,6 +634,7 @@ function normalizeLang(lang: string): string {
|
|
|
589
634
|
}
|
|
590
635
|
|
|
591
636
|
export { presentIntrospectionJSONAsMarkdown as presentIntrospectionAsMarkdown }
|
|
637
|
+
export { presentIntrospectionAsTypeScript, renderTypeScriptParams, normalizeTypeString, isGenericObjectType }
|
|
592
638
|
|
|
593
639
|
function presentIntrospectionJSONAsMarkdown(introspection: HelperIntrospection, startHeadingDepth: number = 1, section?: IntrospectionSection) {
|
|
594
640
|
const sections: string[] = []
|
|
@@ -622,3 +668,141 @@ function presentIntrospectionJSONAsMarkdown(introspection: HelperIntrospection,
|
|
|
622
668
|
|
|
623
669
|
return sections.join('\n\n')
|
|
624
670
|
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Renders introspection data as a TypeScript interface declaration.
|
|
674
|
+
* Produces a valid interface string that describes the helper's public API —
|
|
675
|
+
* getters, methods, state shape, options shape, and event listener signatures.
|
|
676
|
+
*/
|
|
677
|
+
function presentIntrospectionAsTypeScript(introspection: HelperIntrospection, section?: IntrospectionSection): string {
|
|
678
|
+
const interfaceName = introspection.className || introspection.id.split('.').pop() || 'Unknown'
|
|
679
|
+
const members: string[] = []
|
|
680
|
+
|
|
681
|
+
const shouldRender = (s: IntrospectionSection) => !section || section === s
|
|
682
|
+
|
|
683
|
+
// Getters
|
|
684
|
+
if (shouldRender('getters') && introspection.getters && Object.keys(introspection.getters).length > 0) {
|
|
685
|
+
for (const [name, info] of Object.entries(introspection.getters)) {
|
|
686
|
+
const returnType = normalizeTypeString(info.returns || 'any')
|
|
687
|
+
if (info.description) {
|
|
688
|
+
members.push(` /** ${info.description} */`)
|
|
689
|
+
}
|
|
690
|
+
members.push(` readonly ${name}: ${returnType};`)
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Methods
|
|
695
|
+
if (shouldRender('methods') && introspection.methods && Object.keys(introspection.methods).length > 0) {
|
|
696
|
+
if (members.length > 0) members.push('')
|
|
697
|
+
for (const [name, info] of Object.entries(introspection.methods)) {
|
|
698
|
+
if (info.description) {
|
|
699
|
+
members.push(` /** ${info.description} */`)
|
|
700
|
+
}
|
|
701
|
+
const params = renderTypeScriptParams(info)
|
|
702
|
+
const returnType = normalizeTypeString(info.returns || 'void')
|
|
703
|
+
members.push(` ${name}(${params}): ${returnType};`)
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
// State
|
|
708
|
+
if (shouldRender('state') && introspection.state && Object.keys(introspection.state).length > 0) {
|
|
709
|
+
if (members.length > 0) members.push('')
|
|
710
|
+
const stateMembers = Object.entries(introspection.state)
|
|
711
|
+
.map(([name, info]) => {
|
|
712
|
+
const comment = info.description ? ` /** ${info.description} */\n` : ''
|
|
713
|
+
return `${comment} ${name}: ${normalizeTypeString(info.type || 'any')};`
|
|
714
|
+
})
|
|
715
|
+
.join('\n')
|
|
716
|
+
members.push(` state: {\n${stateMembers}\n };`)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// Options
|
|
720
|
+
if (shouldRender('options') && introspection.options && Object.keys(introspection.options).length > 0) {
|
|
721
|
+
if (members.length > 0) members.push('')
|
|
722
|
+
const optionMembers = Object.entries(introspection.options)
|
|
723
|
+
.map(([name, info]) => {
|
|
724
|
+
const comment = info.description ? ` /** ${info.description} */\n` : ''
|
|
725
|
+
return `${comment} ${name}?: ${normalizeTypeString(info.type || 'any')};`
|
|
726
|
+
})
|
|
727
|
+
.join('\n')
|
|
728
|
+
members.push(` options: {\n${optionMembers}\n };`)
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Events — rendered as on() overloads
|
|
732
|
+
if (shouldRender('events') && introspection.events && Object.keys(introspection.events).length > 0) {
|
|
733
|
+
if (members.length > 0) members.push('')
|
|
734
|
+
for (const [eventName, eventInfo] of Object.entries(introspection.events)) {
|
|
735
|
+
const args = Object.entries(eventInfo.arguments || {})
|
|
736
|
+
const listenerParams = args.length > 0
|
|
737
|
+
? args.map(([argName, argInfo]) => `${argName}: ${normalizeTypeString(argInfo.type || 'any')}`).join(', ')
|
|
738
|
+
: ''
|
|
739
|
+
if (eventInfo.description) {
|
|
740
|
+
members.push(` /** ${eventInfo.description} */`)
|
|
741
|
+
}
|
|
742
|
+
members.push(` on(event: '${eventName}', listener: (${listenerParams}) => void): this;`)
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
const description = introspection.description
|
|
747
|
+
? `/**\n * ${introspection.description.split('\n').join('\n * ')}\n */\n`
|
|
748
|
+
: ''
|
|
749
|
+
|
|
750
|
+
const mainInterface = `${description}interface ${interfaceName} {\n${members.join('\n')}\n}`
|
|
751
|
+
|
|
752
|
+
// Emit referenced type declarations above the main interface
|
|
753
|
+
const types = introspection.types
|
|
754
|
+
if (types && Object.keys(types).length > 0) {
|
|
755
|
+
const typeDeclarations = Object.entries(types).map(([typeName, typeInfo]) => {
|
|
756
|
+
const typeDesc = typeInfo.description
|
|
757
|
+
? `/**\n * ${typeInfo.description.split('\n').join('\n * ')}\n */\n`
|
|
758
|
+
: ''
|
|
759
|
+
const props = Object.entries(typeInfo.properties)
|
|
760
|
+
.map(([propName, propInfo]) => {
|
|
761
|
+
const comment = propInfo.description ? ` /** ${propInfo.description} */\n` : ''
|
|
762
|
+
const opt = propInfo.optional ? '?' : ''
|
|
763
|
+
return `${comment} ${propName}${opt}: ${normalizeTypeString(propInfo.type || 'any')};`
|
|
764
|
+
})
|
|
765
|
+
.join('\n')
|
|
766
|
+
return `${typeDesc}interface ${typeName} {\n${props}\n}`
|
|
767
|
+
})
|
|
768
|
+
|
|
769
|
+
return typeDeclarations.join('\n\n') + '\n\n' + mainInterface
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return mainInterface
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/** Build a TypeScript parameter list string from method introspection */
|
|
776
|
+
function renderTypeScriptParams(method: { parameters: Record<string, { type: string, description?: string, properties?: Record<string, { type: string, description?: string }> }>, required: string[] }): string {
|
|
777
|
+
const entries = Object.entries(method.parameters || {})
|
|
778
|
+
if (entries.length === 0) return ''
|
|
779
|
+
|
|
780
|
+
return entries.map(([name, info]) => {
|
|
781
|
+
const isRequired = (method.required || []).includes(name)
|
|
782
|
+
let type = normalizeTypeString(info.type || 'any')
|
|
783
|
+
|
|
784
|
+
// If the parameter has expanded sub-properties but a generic type string,
|
|
785
|
+
// render an inline object type from those properties instead
|
|
786
|
+
if (info.properties && Object.keys(info.properties).length > 0 && isGenericObjectType(type)) {
|
|
787
|
+
const props = Object.entries(info.properties)
|
|
788
|
+
.map(([propName, propInfo]) => `${propName}?: ${normalizeTypeString(propInfo.type || 'any')}`)
|
|
789
|
+
.join('; ')
|
|
790
|
+
type = `{ ${props} }`
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
return `${name}${isRequired ? '' : '?'}: ${type}`
|
|
794
|
+
}).join(', ')
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** Returns true if the type string looks like a generic object type that sub-properties would refine */
|
|
798
|
+
function isGenericObjectType(type: string): boolean {
|
|
799
|
+
const lower = type.toLowerCase()
|
|
800
|
+
return lower === 'object' || lower === 'any' || lower === 'record<string, any>' || lower === '{}'
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/** Clean up type strings from introspection data for use in interface declarations */
|
|
804
|
+
function normalizeTypeString(type: string): string {
|
|
805
|
+
if (!type) return 'any'
|
|
806
|
+
// The AST scanner sometimes wraps types in quotes
|
|
807
|
+
return type.replace(/^["']|["']$/g, '')
|
|
808
|
+
}
|