@dot-agent/cli 1.0.1 → 1.0.3

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.
@@ -1,79 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import { mkdir, writeFile, stat } from 'fs/promises'
16
- import { join } from 'path'
17
- import { createHash } from 'crypto'
18
- import { UnpackOptions, UnpackResult } from '../types.js'
19
- import { readZip, validateZipBomb, validateMagicBytes, extractFiles } from '../core/zip.js'
20
- import { parseAboutme } from '../core/envelope.js'
21
-
22
- export async function unpack(options: UnpackOptions): Promise<UnpackResult> {
23
- const { file, out, force = false } = options
24
-
25
- // Validate magic bytes
26
- await validateMagicBytes(file)
27
-
28
- // Validate ZIP bomb
29
- await validateZipBomb(file)
30
-
31
- // Load ZIP
32
- const zip = await readZip(file)
33
-
34
- // Read aboutme.json
35
- const aboutmeFile = zip.file('.agent/aboutme.json')
36
- if (!aboutmeFile) {
37
- throw new Error('Missing .agent/aboutme.json in ZIP')
38
- }
39
-
40
- const aboutmeText = await aboutmeFile.async('text')
41
- const aboutme = parseAboutme(JSON.parse(aboutmeText))
42
-
43
- // Extract output directory
44
- const outDir = out || `./${aboutme.name}`
45
-
46
- // Check if directory exists
47
- try {
48
- await stat(outDir)
49
- if (!force) {
50
- throw new Error(`Output directory already exists: ${outDir}. Use --force to overwrite.`)
51
- }
52
- } catch (err: any) {
53
- if (err.code !== 'ENOENT') throw err
54
- }
55
-
56
- // Extract all files from root
57
- const files = await extractFiles(zip)
58
- await mkdir(outDir, { recursive: true })
59
-
60
- const extractedFiles: string[] = []
61
-
62
- for (const [path, content] of files) {
63
- if (path.startsWith('.agent/')) continue
64
-
65
- const fullPath = join(outDir, path)
66
- const dir = fullPath.substring(0, fullPath.lastIndexOf('/'))
67
-
68
- await mkdir(dir, { recursive: true })
69
- await writeFile(fullPath, content)
70
- extractedFiles.push(path)
71
- }
72
-
73
- return {
74
- dir: outDir,
75
- id: aboutme.id,
76
- files: extractedFiles,
77
- aboutme,
78
- }
79
- }
@@ -1,83 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import { AboutMe, Integrity, Skill } from '../types.js'
16
-
17
- export function parseAboutme(json: any): AboutMe {
18
- if (!json.schemaVersion) throw new Error('Missing schemaVersion in aboutme.json')
19
- if (!json.id) throw new Error('Missing id in aboutme.json')
20
- if (!json.name) throw new Error('Missing name in aboutme.json')
21
- if (!json.description) throw new Error('Missing description in aboutme.json')
22
- if (!json.version) throw new Error('Missing version in aboutme.json')
23
- if (!json.domain) throw new Error('Missing domain in aboutme.json')
24
- if (!json.license) throw new Error('Missing license in aboutme.json')
25
- if (!json.persona) throw new Error('Missing persona in aboutme.json')
26
- if (!json.compiler) throw new Error('Missing compiler in aboutme.json')
27
- if (!Array.isArray(json.skills)) throw new Error('Missing skills array in aboutme.json')
28
- if (!Array.isArray(json.requires)) throw new Error('Missing requires array in aboutme.json')
29
- if (!json.integrity) throw new Error('Missing integrity in aboutme.json')
30
-
31
- return {
32
- schemaVersion: json.schemaVersion,
33
- id: json.id,
34
- name: json.name,
35
- description: json.description,
36
- version: json.version,
37
- domain: json.domain,
38
- license: json.license,
39
- persona: json.persona,
40
- compiler: json.compiler,
41
- commit: json.commit,
42
- skills: json.skills,
43
- requires: json.requires,
44
- integrity: json.integrity,
45
- }
46
- }
47
-
48
- export interface BuildAboutmeOptions {
49
- id: string
50
- name: string
51
- description: string
52
- version: string
53
- domain: string
54
- license?: string
55
- persona: string
56
- compiler: string
57
- commit?: string
58
- skills?: Skill[]
59
- requires?: string[]
60
- integrity: Integrity
61
- }
62
-
63
- export function buildAboutme(opts: BuildAboutmeOptions): AboutMe {
64
- return {
65
- schemaVersion: 'dot-agent/1.0',
66
- id: opts.id,
67
- name: opts.name,
68
- description: opts.description,
69
- version: opts.version,
70
- domain: opts.domain,
71
- license: opts.license || 'Apache-2.0',
72
- persona: opts.persona,
73
- compiler: opts.compiler,
74
- commit: opts.commit,
75
- skills: opts.skills || [],
76
- requires: opts.requires || [],
77
- integrity: opts.integrity,
78
- }
79
- }
80
-
81
- export function aboutmeToJson(aboutme: AboutMe): string {
82
- return JSON.stringify(aboutme, null, 2)
83
- }
package/src/core/id.ts DELETED
@@ -1,55 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- export interface IdParts {
16
- namespace: string
17
- name: string
18
- version: string
19
- digest: string
20
- }
21
-
22
- export function parseId(id: string): IdParts {
23
- const match = id.match(/^(.+)\/(.+):(.+)~(.+)$/)
24
- if (!match) {
25
- throw new Error(`Invalid agent ID format: ${id}. Expected: namespace/name:version~digest`)
26
- }
27
- const [, namespace, name, version, digest] = match
28
- return { namespace, name, version, digest }
29
- }
30
-
31
- export function buildId(parts: IdParts): string {
32
- const { namespace, name, version, digest } = parts
33
- if (!namespace || !name || !version || !digest) {
34
- throw new Error('Missing required ID parts: namespace, name, version, digest')
35
- }
36
- return `${namespace}/${name}:${version}~${digest}`
37
- }
38
-
39
- export function extractDigest(id: string): string {
40
- try {
41
- const parts = parseId(id)
42
- return parts.digest
43
- } catch {
44
- return ''
45
- }
46
- }
47
-
48
- export function extractName(id: string): string {
49
- try {
50
- const parts = parseId(id)
51
- return parts.name
52
- } catch {
53
- return ''
54
- }
55
- }
package/src/core/lint.ts DELETED
@@ -1,216 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import { createRequire } from 'module'
16
- import { Parser, Language } from 'web-tree-sitter'
17
- import { AgentDSLKernel, init as initKernel } from '@dot-agent/kernel-dsl'
18
- import { LintMessage } from '../types.js'
19
-
20
- const require = createRequire(import.meta.url)
21
- const { agentWasmPath, behaviorWasmPath } = require('@dot-agent/tree-sitter')
22
-
23
- declare module '@dot-agent/tree-sitter' {
24
- export const agentWasmPath: string
25
- export const behaviorWasmPath: string
26
- }
27
-
28
- let _initialized = false
29
- let _agentParser: Parser
30
- let _behaviorParser: Parser
31
- let _agentLang: any
32
- let _behaviorLang: any
33
-
34
- async function ensureWasmInit() {
35
- if (_initialized) return
36
-
37
- try {
38
- await Parser.init()
39
- } catch (err) {
40
- throw err
41
- }
42
-
43
- _agentParser = new Parser()
44
- _behaviorParser = new Parser()
45
-
46
- try {
47
- _agentLang = await Language.load(agentWasmPath)
48
- _behaviorLang = await Language.load(behaviorWasmPath)
49
- } catch (err) {
50
- throw err
51
- }
52
-
53
- try {
54
- await initKernel()
55
- } catch (err) {
56
- // Silently fail - kernel might not be available in all environments
57
- // We can continue with tree-sitter only for syntax checking
58
- }
59
-
60
- _initialized = true
61
- }
62
-
63
- function collectSyntaxErrors(node: Parser.SyntaxNode): Parser.SyntaxNode[] {
64
- const errors: Parser.SyntaxNode[] = []
65
- if (node.isError || node.isMissing) errors.push(node)
66
- for (const child of node.children) {
67
- errors.push(...collectSyntaxErrors(child))
68
- }
69
- return errors
70
- }
71
-
72
- function getLineCol(node: Parser.SyntaxNode): { line: number; col: number } {
73
- return { line: node.startPosition.row + 1, col: node.startPosition.column + 1 }
74
- }
75
-
76
- export async function lintDescription(text: string): Promise<LintMessage[]> {
77
- await ensureWasmInit()
78
-
79
- _agentParser.setLanguage(_agentLang)
80
- const tree = _agentParser.parse(text)
81
- const messages: LintMessage[] = []
82
-
83
- const syntaxErrors = collectSyntaxErrors(tree.rootNode)
84
- if (syntaxErrors.length > 0) {
85
- for (const error of syntaxErrors) {
86
- const { line, col } = getLineCol(error)
87
- messages.push({
88
- file: 'agent.description',
89
- line,
90
- col,
91
- severity: 'error',
92
- code: 'E004',
93
- message: 'Syntax error in description',
94
- })
95
- }
96
- }
97
-
98
- const walk = (node: Parser.SyntaxNode) => {
99
- if (node.type === 'domain_declaration' && node.childCount > 0) {
100
- const valueNode = node.child(node.childCount - 1)
101
- if (valueNode && valueNode.text === 'example.com') {
102
- const { line, col } = getLineCol(valueNode)
103
- messages.push({
104
- file: 'agent.description',
105
- line,
106
- col,
107
- severity: 'warning',
108
- code: 'W003',
109
- message: 'domain still has default value "example.com"',
110
- })
111
- }
112
- }
113
- for (const child of node.children) walk(child)
114
- }
115
- walk(tree.rootNode)
116
-
117
- return messages
118
- }
119
-
120
- export async function lintBehavior(text: string): Promise<LintMessage[]> {
121
- await ensureWasmInit()
122
-
123
- _behaviorParser.setLanguage(_behaviorLang)
124
- const tree = _behaviorParser.parse(text)
125
- const messages: LintMessage[] = []
126
-
127
- const syntaxErrors = collectSyntaxErrors(tree.rootNode)
128
- if (syntaxErrors.length > 0) {
129
- for (const error of syntaxErrors) {
130
- const { line, col } = getLineCol(error)
131
- messages.push({
132
- file: 'agent.behavior',
133
- line,
134
- col,
135
- severity: 'error',
136
- code: 'E004',
137
- message: 'Syntax error in behavior DSL',
138
- })
139
- }
140
- }
141
-
142
- const walk = (node: Parser.SyntaxNode) => {
143
- if (node.type === 'text_block') {
144
- if (node.text.length > 280) {
145
- const { line, col } = getLineCol(node)
146
- messages.push({
147
- file: 'agent.behavior',
148
- line,
149
- col,
150
- severity: 'warning',
151
- code: 'W002',
152
- message: `Text block exceeds 280 characters (${node.text.length})`,
153
- })
154
- }
155
- }
156
- for (const child of node.children) walk(child)
157
- }
158
- walk(tree.rootNode)
159
-
160
- try {
161
- const kernel = new AgentDSLKernel()
162
- try {
163
- const effects = kernel.load_behavior(text)
164
-
165
- if (Array.isArray(effects)) {
166
- for (const effect of effects) {
167
- if (effect.type === 'parse_error') {
168
- messages.push({
169
- file: 'agent.behavior',
170
- line: 1,
171
- col: 1,
172
- severity: 'error',
173
- code: 'E006',
174
- message: `Parse error: ${effect.message}`,
175
- })
176
- }
177
- }
178
- }
179
-
180
- const graph = kernel.get_graph()
181
- if (graph?.states) {
182
- for (const stateName of Object.keys(graph.states)) {
183
- const state = graph.states[stateName]
184
- const hasIncoming = graph.transitions?.some((t: any) => t.to === stateName)
185
- const hasOutgoing = graph.transitions?.some((t: any) => t.from === stateName)
186
-
187
- if (!hasIncoming && !hasOutgoing && stateName !== graph.current) {
188
- messages.push({
189
- file: 'agent.behavior',
190
- line: 1,
191
- col: 1,
192
- severity: 'warning',
193
- code: 'W001',
194
- message: `state "${stateName}" has no transitions`,
195
- })
196
- }
197
- }
198
- }
199
- } catch (kernelErr: any) {
200
- // Kernel load failed - skip semantic checks, syntax errors already caught by tree-sitter
201
- }
202
- } catch (err: any) {
203
- // Could not initialize kernel - skip semantic checks
204
- }
205
-
206
- return messages
207
- }
208
-
209
- export async function createLinter() {
210
- await ensureWasmInit()
211
-
212
- return {
213
- lintDescription,
214
- lintBehavior,
215
- }
216
- }
package/src/core/types.ts DELETED
@@ -1,60 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import { ParsedDescription } from '../types.js'
16
-
17
- export function buildTypesJson(description: ParsedDescription): object | null {
18
- const capabilities = description.capabilities || []
19
-
20
- const publicCapabilities = capabilities.filter((cap: any) => cap.public !== false)
21
- if (publicCapabilities.length === 0) return null
22
-
23
- const types: any = {
24
- input: [],
25
- output: [],
26
- $defs: {},
27
- }
28
-
29
- for (const cap of publicCapabilities) {
30
- if (cap.inputType) {
31
- if (typeof cap.inputType === 'string' && cap.inputType.startsWith('std.')) {
32
- types.input.push({
33
- $ref: `https://dot-agent.dev/schema/std/v1/${cap.inputType.replace('std.', '')}.json`,
34
- })
35
- } else if (typeof cap.inputType === 'object') {
36
- const typeName = cap.name ? `${cap.name}Input` : 'Input'
37
- types.input.push({ $ref: `#/$defs/${typeName}` })
38
- types.$defs[typeName] = cap.inputType
39
- }
40
- }
41
-
42
- if (cap.outputType) {
43
- if (typeof cap.outputType === 'string' && cap.outputType.startsWith('std.')) {
44
- types.output.push({
45
- $ref: `https://dot-agent.dev/schema/std/v1/${cap.outputType.replace('std.', '')}.json`,
46
- })
47
- } else if (typeof cap.outputType === 'object') {
48
- const typeName = cap.name ? `${cap.name}Output` : 'Output'
49
- types.output.push({ $ref: `#/$defs/${typeName}` })
50
- types.$defs[typeName] = cap.outputType
51
- }
52
- }
53
- }
54
-
55
- if (Object.keys(types.$defs).length === 0) delete types.$defs
56
-
57
- if (types.input.length === 0 && types.output.length === 0) return null
58
-
59
- return types
60
- }
package/src/core/zip.ts DELETED
@@ -1,90 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import JSZip from 'jszip'
16
- import { readFile, writeFile } from 'fs/promises'
17
-
18
- const MAX_ZIP_SIZE = 500 * 1024 * 1024
19
- const MAX_COMPRESSION_RATIO = 100
20
-
21
- export async function readZip(filePath: string): Promise<JSZip> {
22
- const data = await readFile(filePath)
23
- return JSZip.loadAsync(data)
24
- }
25
-
26
- export async function validateZipBomb(filePath: string): Promise<boolean> {
27
- const data = await readFile(filePath)
28
- const zip = await JSZip.loadAsync(data)
29
-
30
- let totalUncompressed = 0
31
- zip.forEach((relativePath, file) => {
32
- if (!file.dir) {
33
- totalUncompressed += file._data?.uncompressedSize || 0
34
- }
35
- })
36
-
37
- const compressedSize = data.length
38
- const ratio = totalUncompressed / compressedSize
39
-
40
- if (totalUncompressed > MAX_ZIP_SIZE) {
41
- throw new Error(`ZIP uncompressed size exceeds 500MB limit: ${totalUncompressed}`)
42
- }
43
-
44
- if (ratio > MAX_COMPRESSION_RATIO) {
45
- throw new Error(`ZIP compression ratio exceeds 100x limit: ${ratio.toFixed(1)}x`)
46
- }
47
-
48
- return true
49
- }
50
-
51
- export async function extractFiles(
52
- zip: JSZip,
53
- filter?: string[]
54
- ): Promise<Map<string, string>> {
55
- const files = new Map<string, string>()
56
-
57
- const promises: Promise<void>[] = []
58
- zip.forEach((relativePath, file) => {
59
- if (file.dir) return
60
- if (filter && !filter.some(f => relativePath.startsWith(f))) return
61
-
62
- promises.push(
63
- file.async('text').then(content => {
64
- files.set(relativePath, content)
65
- })
66
- )
67
- })
68
-
69
- await Promise.all(promises)
70
- return files
71
- }
72
-
73
- export function createZip(): JSZip {
74
- return new JSZip()
75
- }
76
-
77
- export async function writeZip(zip: JSZip, outPath: string): Promise<void> {
78
- const data = await zip.generateAsync({ type: 'arraybuffer' })
79
- await writeFile(outPath, Buffer.from(data))
80
- }
81
-
82
- export async function validateMagicBytes(filePath: string): Promise<boolean> {
83
- const data = await readFile(filePath)
84
- const magicBytes = data.subarray(0, 4)
85
- const isZip = magicBytes[0] === 0x50 && magicBytes[1] === 0x4b && magicBytes[2] === 0x03 && magicBytes[3] === 0x04
86
- if (!isZip) {
87
- throw new Error('File is not a valid ZIP (invalid magic bytes)')
88
- }
89
- return true
90
- }
package/src/index.ts DELETED
@@ -1,36 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- export { init } from './commands/init.js'
16
- export { pack } from './commands/pack.js'
17
- export { unpack } from './commands/unpack.js'
18
- export { run } from './commands/run.js'
19
-
20
- export type {
21
- InitOptions,
22
- InitResult,
23
- PackOptions,
24
- PackResult,
25
- UnpackOptions,
26
- UnpackResult,
27
- RunOptions,
28
- AgentContext,
29
- FileEntry,
30
- LintMessage,
31
- AboutMe,
32
- Skill,
33
- Integrity,
34
- ParsedDescription,
35
- ParsedBehavior,
36
- } from './types.js'
@@ -1,35 +0,0 @@
1
- // Copyright 2026 Danilo Borges
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- declare module '@dot-agent/kernel-dsl' {
16
- export class AgentDSLKernel {
17
- constructor()
18
- get_current_state(): string
19
- get_graph(): any
20
- get_memory(): any
21
- get_valid_intents(): string[]
22
- load_behavior(text: string): any
23
- observe(callback: Function): void
24
- send_complete(): any
25
- send_event(event: string): any
26
- send_failed(): any
27
- send_fallback(): any
28
- send_intent(intent: string): any
29
- send_offtopic(): any
30
- tick_prompt(): any
31
- free(): void
32
- }
33
-
34
- export function init(): Promise<void>
35
- }