@hazeljs/cli 1.0.6 → 2.0.1
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/@template-ai-native/README.md +19 -0
- package/@template-ai-native/src/examples/skillgate.example.ts +29 -0
- package/cli-manifest.json +82 -1
- package/dist/commands/agent-templates.d.ts +27 -0
- package/dist/commands/agent-templates.js +480 -0
- package/dist/commands/agent.d.ts +5 -1
- package/dist/commands/agent.js +723 -3
- package/dist/commands/agent.test.d.ts +1 -0
- package/dist/commands/agent.test.js +104 -0
- package/dist/commands/skillgate.d.ts +6 -0
- package/dist/commands/skillgate.js +147 -0
- package/dist/commands/store.d.ts +7 -0
- package/dist/commands/store.js +203 -0
- package/dist/commands/store.test.d.ts +1 -0
- package/dist/commands/store.test.js +120 -0
- package/dist/index.js +4 -0
- package/dist/utils/packages-registry.js +19 -0
- package/package.json +12 -7
|
@@ -36,6 +36,25 @@ A complete AI-native backend application with HazelJS, featuring:
|
|
|
36
36
|
- Inspector: http://localhost:3000/\_\_hazel
|
|
37
37
|
- Health: http://localhost:3000/health
|
|
38
38
|
|
|
39
|
+
## Skillgate (optional)
|
|
40
|
+
|
|
41
|
+
Turn selected REST controllers into governed agent skills:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install @hazeljs/skillgate @hazeljs/swagger
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
See `src/examples/skillgate.example.ts` and https://hazeljs.ai/docs/guides/skillgate
|
|
48
|
+
|
|
49
|
+
Tag controllers with `@ApiTags('agent')` or `@AgentSkill`, then:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
Skillgate.fromModule(AppModule, { invoke: { baseUrl: 'http://127.0.0.1:3000' } }).register(
|
|
53
|
+
registry,
|
|
54
|
+
'api-concierge'
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
39
58
|
## Available Endpoints
|
|
40
59
|
|
|
41
60
|
### AI Chat
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional Skillgate example (not imported by AppModule).
|
|
3
|
+
*
|
|
4
|
+
* Install: npm install @hazeljs/skillgate @hazeljs/swagger
|
|
5
|
+
* Then wire `registerApiSkills()` from your bootstrap after the HTTP server listens.
|
|
6
|
+
*
|
|
7
|
+
* Docs: https://hazeljs.ai/docs/guides/skillgate
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
import { Skillgate } from '@hazeljs/skillgate';
|
|
12
|
+
import { ToolRegistry } from '@hazeljs/agent';
|
|
13
|
+
import { AppModule } from '../app.module';
|
|
14
|
+
|
|
15
|
+
export function registerApiSkills(registry: ToolRegistry = new ToolRegistry()) {
|
|
16
|
+
const gate = Skillgate.fromModule(AppModule, {
|
|
17
|
+
include: { tags: ['agent'] },
|
|
18
|
+
swagger: {
|
|
19
|
+
title: 'AI-native API',
|
|
20
|
+
servers: [{ url: process.env.API_BASE_URL || 'http://127.0.0.1:3000' }],
|
|
21
|
+
},
|
|
22
|
+
invoke: { baseUrl: process.env.API_BASE_URL || 'http://127.0.0.1:3000' },
|
|
23
|
+
});
|
|
24
|
+
gate.register(registry, 'api-concierge');
|
|
25
|
+
return { gate, registry };
|
|
26
|
+
}
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export {};
|
package/cli-manifest.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"description": "Machine-readable manifest of all CLI commands and options for LLM agent tool-use",
|
|
5
5
|
"cli": {
|
|
6
6
|
"name": "hazel",
|
|
7
|
-
"version": "
|
|
7
|
+
"version": "2.0.1",
|
|
8
8
|
"description": "CLI for generating HazelJS components and applications"
|
|
9
9
|
},
|
|
10
10
|
"commands": [
|
|
@@ -874,6 +874,87 @@
|
|
|
874
874
|
"description": "Display information about the current HazelJS project",
|
|
875
875
|
"args": [],
|
|
876
876
|
"options": []
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
"name": "store",
|
|
880
|
+
"usage": "hazel store <subcommand>",
|
|
881
|
+
"description": "Local Agent OS package registry (publish / install DNA packages)",
|
|
882
|
+
"subcommands": [
|
|
883
|
+
{
|
|
884
|
+
"name": "publish",
|
|
885
|
+
"usage": "hazel store publish <file> [--registry <dir>]",
|
|
886
|
+
"description": "Publish marketplace/DNA JSON to ~/.hazel/registry",
|
|
887
|
+
"args": [
|
|
888
|
+
{
|
|
889
|
+
"name": "file",
|
|
890
|
+
"type": "string",
|
|
891
|
+
"required": true,
|
|
892
|
+
"description": "Path to .dna.json or marketplace package JSON"
|
|
893
|
+
}
|
|
894
|
+
],
|
|
895
|
+
"options": [
|
|
896
|
+
{
|
|
897
|
+
"name": "registry",
|
|
898
|
+
"type": "string",
|
|
899
|
+
"description": "Override registry root (default: ~/.hazel/registry)"
|
|
900
|
+
}
|
|
901
|
+
]
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
"name": "install",
|
|
905
|
+
"usage": "hazel store install <spec> [--cwd <dir>] [--registry <dir>]",
|
|
906
|
+
"description": "Install package into project .hazel/agents (file path or name@version from local registry)",
|
|
907
|
+
"args": [
|
|
908
|
+
{
|
|
909
|
+
"name": "spec",
|
|
910
|
+
"type": "string",
|
|
911
|
+
"required": true,
|
|
912
|
+
"description": "File path or package name[@version]"
|
|
913
|
+
}
|
|
914
|
+
],
|
|
915
|
+
"options": [
|
|
916
|
+
{
|
|
917
|
+
"name": "cwd",
|
|
918
|
+
"type": "string",
|
|
919
|
+
"default": ".",
|
|
920
|
+
"description": "Project root"
|
|
921
|
+
},
|
|
922
|
+
{
|
|
923
|
+
"name": "registry",
|
|
924
|
+
"type": "string",
|
|
925
|
+
"description": "Override registry root"
|
|
926
|
+
}
|
|
927
|
+
]
|
|
928
|
+
},
|
|
929
|
+
{
|
|
930
|
+
"name": "list",
|
|
931
|
+
"usage": "hazel store list [query]",
|
|
932
|
+
"description": "List packages in the local registry"
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
"name": "remove",
|
|
936
|
+
"usage": "hazel store remove <name[@version]>",
|
|
937
|
+
"description": "Remove a package from the local registry"
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
"name": "doctor",
|
|
941
|
+
"usage": "hazel store doctor",
|
|
942
|
+
"description": "Check local registry health"
|
|
943
|
+
}
|
|
944
|
+
]
|
|
945
|
+
},
|
|
946
|
+
{
|
|
947
|
+
"name": "install",
|
|
948
|
+
"usage": "hazel install <spec> [--cwd <dir>] [--registry <dir>]",
|
|
949
|
+
"description": "Alias for hazel store install",
|
|
950
|
+
"args": [
|
|
951
|
+
{
|
|
952
|
+
"name": "spec",
|
|
953
|
+
"type": "string",
|
|
954
|
+
"required": true,
|
|
955
|
+
"description": "File path or package name[@version]"
|
|
956
|
+
}
|
|
957
|
+
]
|
|
877
958
|
}
|
|
878
959
|
],
|
|
879
960
|
"globalOptions": [
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent OS project templates for `hazel agent new`.
|
|
3
|
+
*
|
|
4
|
+
* DNA ≈ OpenAPI for agents (contract). App code = real tool implementations.
|
|
5
|
+
* `hazel agent run` on DNA alone uses stubs — production needs the app tools.
|
|
6
|
+
*/
|
|
7
|
+
export type AgentTemplateId = 'bare' | 'agent-os' | 'skillgate';
|
|
8
|
+
export interface AgentTemplateMeta {
|
|
9
|
+
id: AgentTemplateId;
|
|
10
|
+
label: string;
|
|
11
|
+
description: string;
|
|
12
|
+
}
|
|
13
|
+
export declare const AGENT_TEMPLATES: AgentTemplateMeta[];
|
|
14
|
+
export declare function listAgentTemplates(): AgentTemplateMeta[];
|
|
15
|
+
export declare function resolveAgentTemplate(id: string): AgentTemplateId;
|
|
16
|
+
export interface ScaffoldAgentProjectOptions {
|
|
17
|
+
name: string;
|
|
18
|
+
destDir: string;
|
|
19
|
+
template: AgentTemplateId;
|
|
20
|
+
force?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface ScaffoldAgentProjectResult {
|
|
23
|
+
path: string;
|
|
24
|
+
template: AgentTemplateId;
|
|
25
|
+
files: string[];
|
|
26
|
+
}
|
|
27
|
+
export declare function scaffoldAgentProject(options: ScaffoldAgentProjectOptions): ScaffoldAgentProjectResult;
|
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Agent OS project templates for `hazel agent new`.
|
|
4
|
+
*
|
|
5
|
+
* DNA ≈ OpenAPI for agents (contract). App code = real tool implementations.
|
|
6
|
+
* `hazel agent run` on DNA alone uses stubs — production needs the app tools.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.AGENT_TEMPLATES = void 0;
|
|
43
|
+
exports.listAgentTemplates = listAgentTemplates;
|
|
44
|
+
exports.resolveAgentTemplate = resolveAgentTemplate;
|
|
45
|
+
exports.scaffoldAgentProject = scaffoldAgentProject;
|
|
46
|
+
const fs = __importStar(require("fs"));
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
exports.AGENT_TEMPLATES = [
|
|
49
|
+
{
|
|
50
|
+
id: 'bare',
|
|
51
|
+
label: 'Bare DNA package',
|
|
52
|
+
description: 'Marketplace DNA only — publish/install/run with stub tools (packaging smoke)',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'agent-os',
|
|
56
|
+
label: 'Agent OS mini-app',
|
|
57
|
+
description: 'DNA + real @Agent/@Tool TypeScript app (HITL refund demo) + store lock',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'skillgate',
|
|
61
|
+
label: 'Skillgate concierge',
|
|
62
|
+
description: 'DNA + Skillgate fromOpenApi sample + register sketch for API concierge',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
function listAgentTemplates() {
|
|
66
|
+
return [...exports.AGENT_TEMPLATES];
|
|
67
|
+
}
|
|
68
|
+
function resolveAgentTemplate(id) {
|
|
69
|
+
const found = exports.AGENT_TEMPLATES.find((t) => t.id === id);
|
|
70
|
+
if (!found) {
|
|
71
|
+
throw new Error(`Unknown agent template "${id}". Available: ${exports.AGENT_TEMPLATES.map((t) => t.id).join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
return found.id;
|
|
74
|
+
}
|
|
75
|
+
function writeFile(root, rel, content) {
|
|
76
|
+
const full = path.join(root, rel);
|
|
77
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
78
|
+
fs.writeFileSync(full, content.endsWith('\n') ? content : content + '\n');
|
|
79
|
+
}
|
|
80
|
+
function sanitizeNpmName(name) {
|
|
81
|
+
return (name
|
|
82
|
+
.trim()
|
|
83
|
+
.toLowerCase()
|
|
84
|
+
.replace(/[^a-z0-9-_]/g, '-')
|
|
85
|
+
.replace(/^-+|-+$/g, '') || 'my-agent');
|
|
86
|
+
}
|
|
87
|
+
function agentDnaName(projectName) {
|
|
88
|
+
return sanitizeNpmName(projectName).replace(/-/g, '_').slice(0, 48) || 'my_agent';
|
|
89
|
+
}
|
|
90
|
+
function marketplacePackage(projectName, opts) {
|
|
91
|
+
const dnaName = agentDnaName(projectName);
|
|
92
|
+
const pkg = {
|
|
93
|
+
name: `@local/${sanitizeNpmName(projectName)}-agent`,
|
|
94
|
+
version: '1.0.0',
|
|
95
|
+
description: opts.description,
|
|
96
|
+
dna: {
|
|
97
|
+
format: 'hazeljs.agent.dna',
|
|
98
|
+
version: '1.0.0',
|
|
99
|
+
name: dnaName,
|
|
100
|
+
description: opts.description,
|
|
101
|
+
systemPrompt: opts.systemPrompt,
|
|
102
|
+
tools: opts.tools,
|
|
103
|
+
policies: opts.policies ?? [],
|
|
104
|
+
contracts: [{ name: `${dnaName}-slo`, maxLatencyMs: 30000 }],
|
|
105
|
+
exportedAt: new Date().toISOString(),
|
|
106
|
+
},
|
|
107
|
+
readme: opts.description,
|
|
108
|
+
keywords: opts.keywords,
|
|
109
|
+
};
|
|
110
|
+
return JSON.stringify(pkg, null, 2);
|
|
111
|
+
}
|
|
112
|
+
function bareFiles(projectName) {
|
|
113
|
+
const pkgJson = marketplacePackage(projectName, {
|
|
114
|
+
description: `${projectName} — DNA-only agent package`,
|
|
115
|
+
systemPrompt: `You are ${projectName}. Be concise. Use tools when available; otherwise explain what you would do.`,
|
|
116
|
+
tools: [
|
|
117
|
+
{ name: 'ping', description: 'Health ping (stub in CLI run)' },
|
|
118
|
+
{ name: 'echo', description: 'Echo input (stub in CLI run)' },
|
|
119
|
+
],
|
|
120
|
+
keywords: ['agent-os', 'dna', 'bare'],
|
|
121
|
+
});
|
|
122
|
+
return {
|
|
123
|
+
'package.json': JSON.stringify({
|
|
124
|
+
name: sanitizeNpmName(projectName),
|
|
125
|
+
version: '1.0.0',
|
|
126
|
+
private: true,
|
|
127
|
+
description: `DNA package for ${projectName}`,
|
|
128
|
+
scripts: {
|
|
129
|
+
'agent:run': 'hazel agent run dna/agent.marketplace.json',
|
|
130
|
+
'store:publish': 'hazel store publish dna/agent.marketplace.json',
|
|
131
|
+
'store:install': 'hazel store install dna/agent.marketplace.json',
|
|
132
|
+
},
|
|
133
|
+
keywords: ['hazeljs', 'agent-os', 'dna'],
|
|
134
|
+
license: 'Apache-2.0',
|
|
135
|
+
}, null, 2),
|
|
136
|
+
'dna/agent.marketplace.json': pkgJson,
|
|
137
|
+
'README.md': `# ${projectName}
|
|
138
|
+
|
|
139
|
+
Bare **Agent DNA** package (contract only — like OpenAPI for an agent).
|
|
140
|
+
|
|
141
|
+
## Commands
|
|
142
|
+
|
|
143
|
+
\`\`\`bash
|
|
144
|
+
# Smoke-run (Agent OS engine + stub tools + mock/real LLM)
|
|
145
|
+
npx hazel agent run dna/agent.marketplace.json "hello"
|
|
146
|
+
|
|
147
|
+
# Publish to local registry, then install into a project
|
|
148
|
+
npx hazel store publish dna/agent.marketplace.json
|
|
149
|
+
npx hazel store install @local/${sanitizeNpmName(projectName)}-agent --cwd /path/to/app
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
## Important
|
|
153
|
+
|
|
154
|
+
\`hazel agent run\` executes the **runtime** with **stub** tool handlers.
|
|
155
|
+
Wire real \`@Tool\` / Skillgate handlers in your Hazel app for production behavior.
|
|
156
|
+
`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function agentOsFiles(projectName) {
|
|
160
|
+
const dnaName = agentDnaName(projectName);
|
|
161
|
+
const npm = sanitizeNpmName(projectName);
|
|
162
|
+
const marketplace = marketplacePackage(projectName, {
|
|
163
|
+
description: `${projectName} — Agent OS support-style agent with real tools`,
|
|
164
|
+
systemPrompt: `You are ${projectName}, a support desk agent.
|
|
165
|
+
Use lookupOrder for facts. Call processRefund only after lookup. Be concise.`,
|
|
166
|
+
tools: [
|
|
167
|
+
{ name: 'lookupOrder', description: 'Look up an order by id' },
|
|
168
|
+
{
|
|
169
|
+
name: 'processRefund',
|
|
170
|
+
description: 'Process a refund (requires approval)',
|
|
171
|
+
requiresApproval: true,
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
policies: [
|
|
175
|
+
{
|
|
176
|
+
id: 'refund-needs-approval',
|
|
177
|
+
tool: 'processRefund',
|
|
178
|
+
effect: 'require_approval',
|
|
179
|
+
priority: 20,
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
keywords: ['agent-os', 'hitl', 'support'],
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
'package.json': JSON.stringify({
|
|
186
|
+
name: npm,
|
|
187
|
+
version: '1.0.0',
|
|
188
|
+
private: true,
|
|
189
|
+
description: `Agent OS mini-app: ${projectName}`,
|
|
190
|
+
scripts: {
|
|
191
|
+
build: 'tsc',
|
|
192
|
+
start: 'node dist/main.js',
|
|
193
|
+
dev: 'ts-node --transpile-only src/main.ts',
|
|
194
|
+
'agent:run': 'hazel agent run dna/agent.marketplace.json',
|
|
195
|
+
'store:publish': 'hazel store publish dna/agent.marketplace.json',
|
|
196
|
+
'store:install': 'hazel store install dna/agent.marketplace.json --cwd .',
|
|
197
|
+
},
|
|
198
|
+
dependencies: {
|
|
199
|
+
'@hazeljs/agent': '^1.0.6',
|
|
200
|
+
'reflect-metadata': '^0.2.2',
|
|
201
|
+
},
|
|
202
|
+
devDependencies: {
|
|
203
|
+
'@types/node': '^20.19.39',
|
|
204
|
+
'ts-node': '^10.9.2',
|
|
205
|
+
typescript: '^5.9.3',
|
|
206
|
+
},
|
|
207
|
+
license: 'Apache-2.0',
|
|
208
|
+
}, null, 2),
|
|
209
|
+
'tsconfig.json': JSON.stringify({
|
|
210
|
+
compilerOptions: {
|
|
211
|
+
target: 'ES2022',
|
|
212
|
+
module: 'commonjs',
|
|
213
|
+
outDir: 'dist',
|
|
214
|
+
rootDir: 'src',
|
|
215
|
+
strict: true,
|
|
216
|
+
esModuleInterop: true,
|
|
217
|
+
skipLibCheck: true,
|
|
218
|
+
experimentalDecorators: true,
|
|
219
|
+
emitDecoratorMetadata: true,
|
|
220
|
+
},
|
|
221
|
+
include: ['src/**/*'],
|
|
222
|
+
}, null, 2),
|
|
223
|
+
'dna/agent.marketplace.json': marketplace,
|
|
224
|
+
'src/orders.ts': `export type Order = { id: string; status: string; totalUsd: number };
|
|
225
|
+
|
|
226
|
+
const ORDERS: Record<string, Order> = {
|
|
227
|
+
'ORD-1001': { id: 'ORD-1001', status: 'shipped', totalUsd: 128 },
|
|
228
|
+
'ORD-1002': { id: 'ORD-1002', status: 'delivered', totalUsd: 64 },
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
export function getOrder(id: string): Order | undefined {
|
|
232
|
+
return ORDERS[id.toUpperCase()];
|
|
233
|
+
}
|
|
234
|
+
`,
|
|
235
|
+
'src/support.agent.ts': `import 'reflect-metadata';
|
|
236
|
+
import { Agent, Tool } from '@hazeljs/agent';
|
|
237
|
+
import { getOrder } from './orders';
|
|
238
|
+
|
|
239
|
+
@Agent({
|
|
240
|
+
name: '${dnaName}',
|
|
241
|
+
description: '${projectName} support agent',
|
|
242
|
+
systemPrompt: \`You are ${projectName}. Use lookupOrder for facts. Use processRefund only after lookup.\`,
|
|
243
|
+
maxSteps: 8,
|
|
244
|
+
})
|
|
245
|
+
export class SupportAgent {
|
|
246
|
+
@Tool({
|
|
247
|
+
name: 'lookupOrder',
|
|
248
|
+
description: 'Look up an order by id (e.g. ORD-1001)',
|
|
249
|
+
parameters: [{ name: 'orderId', type: 'string', required: true }],
|
|
250
|
+
})
|
|
251
|
+
async lookupOrder({ orderId }: { orderId: string }) {
|
|
252
|
+
const order = getOrder(orderId);
|
|
253
|
+
if (!order) return { found: false, error: \`No order \${orderId}\` };
|
|
254
|
+
return { found: true, order };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
@Tool({
|
|
258
|
+
name: 'processRefund',
|
|
259
|
+
description: 'Process a refund — requires human approval',
|
|
260
|
+
requiresApproval: true,
|
|
261
|
+
parameters: [
|
|
262
|
+
{ name: 'orderId', type: 'string', required: true },
|
|
263
|
+
{ name: 'amount', type: 'number', required: true },
|
|
264
|
+
],
|
|
265
|
+
})
|
|
266
|
+
async processRefund({ orderId, amount }: { orderId: string; amount: number }) {
|
|
267
|
+
const order = getOrder(orderId);
|
|
268
|
+
if (!order) return { ok: false, error: \`No order \${orderId}\` };
|
|
269
|
+
return { ok: true, orderId: order.id, amount, status: 'refunded' };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
`,
|
|
273
|
+
'src/main.ts': `/**
|
|
274
|
+
* Mini Agent OS app — real tools (not DNA stubs).
|
|
275
|
+
* DNA in dna/ can be hot-reloaded / store-installed for prompt+policy overlays.
|
|
276
|
+
*/
|
|
277
|
+
import 'reflect-metadata';
|
|
278
|
+
import {
|
|
279
|
+
AgentRuntime,
|
|
280
|
+
AgentEventType,
|
|
281
|
+
createMockLlmProvider,
|
|
282
|
+
type AgentEvent,
|
|
283
|
+
} from '@hazeljs/agent';
|
|
284
|
+
import { SupportAgent } from './support.agent';
|
|
285
|
+
|
|
286
|
+
async function main() {
|
|
287
|
+
const input = process.argv.slice(2).join(' ') || 'Where is ORD-1001?';
|
|
288
|
+
// Demo uses mock LLM. Wire OpenAI (or another provider) in production apps.
|
|
289
|
+
const llm = createMockLlmProvider(
|
|
290
|
+
'Used real @Tool handlers on AgentRuntime. (Replace createMockLlmProvider with your LLM.)'
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
const runtime = new AgentRuntime({
|
|
294
|
+
llmProvider: llm,
|
|
295
|
+
enableRetry: false,
|
|
296
|
+
enableCircuitBreaker: false,
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
runtime.registerAgent(SupportAgent);
|
|
300
|
+
runtime.registerAgentInstance('${dnaName}', new SupportAgent());
|
|
301
|
+
|
|
302
|
+
// Auto-approve refunds unless HITL=1
|
|
303
|
+
if (process.env.HITL !== '1') {
|
|
304
|
+
runtime.on(AgentEventType.TOOL_APPROVAL_REQUESTED, (event) => {
|
|
305
|
+
const data = (event as AgentEvent<{ requestId?: string }>).data;
|
|
306
|
+
if (data?.requestId) runtime.approveToolExecution(data.requestId, 'demo');
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// DNA file is for store publish/install + CLI smoke.
|
|
311
|
+
// Do not installAgentPackage here if it would re-register tools without handlers.
|
|
312
|
+
// Use DNA overlay for prompt/policies in production only when tools are wired.
|
|
313
|
+
|
|
314
|
+
const result = await runtime.execute('${dnaName}', input, { maxSteps: 8 });
|
|
315
|
+
console.log(JSON.stringify({ response: result.response, state: result.state, steps: result.steps.length }, null, 2));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
main().catch((e) => {
|
|
319
|
+
console.error(e);
|
|
320
|
+
process.exit(1);
|
|
321
|
+
});
|
|
322
|
+
`,
|
|
323
|
+
'README.md': `# ${projectName}
|
|
324
|
+
|
|
325
|
+
**Agent OS mini-app** — DNA contract + **real** \`@Tool\` implementations.
|
|
326
|
+
|
|
327
|
+
| Path | Role |
|
|
328
|
+
| --- | --- |
|
|
329
|
+
| \`dna/agent.marketplace.json\` | DNA / marketplace package (OpenAPI-for-agents) |
|
|
330
|
+
| \`src/support.agent.ts\` | Real tool handlers |
|
|
331
|
+
| \`src/main.ts\` | AgentRuntime bootstrap + optional DNA overlay |
|
|
332
|
+
|
|
333
|
+
## Quick start
|
|
334
|
+
|
|
335
|
+
\`\`\`bash
|
|
336
|
+
npm install
|
|
337
|
+
npm run dev
|
|
338
|
+
npm run dev -- I want a refund for ORD-1002
|
|
339
|
+
|
|
340
|
+
# DNA smoke (stubs — not the same as npm run dev)
|
|
341
|
+
npx hazel agent run dna/agent.marketplace.json "hello"
|
|
342
|
+
|
|
343
|
+
# Package+Store
|
|
344
|
+
npm run store:publish
|
|
345
|
+
npm run store:install
|
|
346
|
+
\`\`\`
|
|
347
|
+
|
|
348
|
+
Set \`HITL=1\` to pause on \`processRefund\` approvals.
|
|
349
|
+
`,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
function skillgateFiles(projectName) {
|
|
353
|
+
const npm = sanitizeNpmName(projectName);
|
|
354
|
+
const marketplace = marketplacePackage(projectName, {
|
|
355
|
+
description: `${projectName} — API concierge (Skillgate-governed REST skills)`,
|
|
356
|
+
systemPrompt: `You are an API concierge. Prefer read skills first. Writes need approval.`,
|
|
357
|
+
tools: [
|
|
358
|
+
{ name: 'getOrder', description: 'GET order by id (Skillgate read)' },
|
|
359
|
+
{
|
|
360
|
+
name: 'createRefund',
|
|
361
|
+
description: 'POST refund (Skillgate write + approval)',
|
|
362
|
+
requiresApproval: true,
|
|
363
|
+
},
|
|
364
|
+
],
|
|
365
|
+
keywords: ['skillgate', 'agent-os', 'openapi'],
|
|
366
|
+
});
|
|
367
|
+
return {
|
|
368
|
+
'package.json': JSON.stringify({
|
|
369
|
+
name: npm,
|
|
370
|
+
version: '1.0.0',
|
|
371
|
+
private: true,
|
|
372
|
+
description: `Skillgate agent starter: ${projectName}`,
|
|
373
|
+
scripts: {
|
|
374
|
+
build: 'tsc',
|
|
375
|
+
report: 'ts-node --transpile-only src/report.ts',
|
|
376
|
+
'agent:run': 'hazel agent run dna/agent.marketplace.json',
|
|
377
|
+
'store:publish': 'hazel store publish dna/agent.marketplace.json',
|
|
378
|
+
},
|
|
379
|
+
dependencies: {
|
|
380
|
+
'@hazeljs/agent': '^1.0.6',
|
|
381
|
+
'@hazeljs/skillgate': '^1.0.6',
|
|
382
|
+
},
|
|
383
|
+
devDependencies: {
|
|
384
|
+
'@types/node': '^20.19.39',
|
|
385
|
+
'ts-node': '^10.9.2',
|
|
386
|
+
typescript: '^5.9.3',
|
|
387
|
+
},
|
|
388
|
+
license: 'Apache-2.0',
|
|
389
|
+
}, null, 2),
|
|
390
|
+
'tsconfig.json': JSON.stringify({
|
|
391
|
+
compilerOptions: {
|
|
392
|
+
target: 'ES2022',
|
|
393
|
+
module: 'commonjs',
|
|
394
|
+
outDir: 'dist',
|
|
395
|
+
rootDir: 'src',
|
|
396
|
+
strict: true,
|
|
397
|
+
esModuleInterop: true,
|
|
398
|
+
skipLibCheck: true,
|
|
399
|
+
},
|
|
400
|
+
include: ['src/**/*'],
|
|
401
|
+
}, null, 2),
|
|
402
|
+
'dna/agent.marketplace.json': marketplace,
|
|
403
|
+
'openapi/sample.openapi.json': JSON.stringify({
|
|
404
|
+
openapi: '3.0.3',
|
|
405
|
+
info: { title: `${projectName} API`, version: '1.0.0' },
|
|
406
|
+
servers: [{ url: 'http://127.0.0.1:3000' }],
|
|
407
|
+
paths: {
|
|
408
|
+
'/orders/{id}': {
|
|
409
|
+
get: {
|
|
410
|
+
operationId: 'getOrder',
|
|
411
|
+
tags: ['agent'],
|
|
412
|
+
summary: 'Fetch an order by id',
|
|
413
|
+
parameters: [{ name: 'id', in: 'path', required: true, schema: { type: 'string' } }],
|
|
414
|
+
'x-hazel-skill': { readOnly: true, class: 'read' },
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
'/refunds': {
|
|
418
|
+
post: {
|
|
419
|
+
operationId: 'createRefund',
|
|
420
|
+
tags: ['agent'],
|
|
421
|
+
summary: 'Create a refund',
|
|
422
|
+
'x-hazel-skill': { requiresApproval: true, class: 'write' },
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
}, null, 2),
|
|
427
|
+
'src/report.ts': `import * as fs from 'fs';
|
|
428
|
+
import * as path from 'path';
|
|
429
|
+
import { Skillgate, type OpenApiLike } from '@hazeljs/skillgate';
|
|
430
|
+
import { ToolRegistry } from '@hazeljs/agent';
|
|
431
|
+
|
|
432
|
+
const specPath = path.join(__dirname, '..', 'openapi', 'sample.openapi.json');
|
|
433
|
+
const spec = JSON.parse(fs.readFileSync(specPath, 'utf8')) as OpenApiLike;
|
|
434
|
+
|
|
435
|
+
const gate = Skillgate.fromOpenApi(spec, {
|
|
436
|
+
include: { tags: ['agent'] },
|
|
437
|
+
classify: { writeRequiresApproval: true },
|
|
438
|
+
invoke: { baseUrl: process.env.API_BASE_URL || 'http://127.0.0.1:3000' },
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
const registry = new ToolRegistry();
|
|
442
|
+
gate.register(registry, 'api-concierge');
|
|
443
|
+
|
|
444
|
+
console.log(JSON.stringify(gate.report(), null, 2));
|
|
445
|
+
console.log('Registered tools:', registry.getAgentTools('api-concierge').map((t) => t.name));
|
|
446
|
+
`,
|
|
447
|
+
'README.md': `# ${projectName}
|
|
448
|
+
|
|
449
|
+
**Skillgate** starter — OpenAPI → governed agent skills + DNA package.
|
|
450
|
+
|
|
451
|
+
\`\`\`bash
|
|
452
|
+
npm install
|
|
453
|
+
npm run report # Skillgate included/denied report
|
|
454
|
+
npx hazel agent run dna/agent.marketplace.json
|
|
455
|
+
npx hazel store publish dna/agent.marketplace.json
|
|
456
|
+
\`\`\`
|
|
457
|
+
|
|
458
|
+
See also: full showcase \`hazeljs-skillgate-agent-starter\` in the monorepo.
|
|
459
|
+
`,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function scaffoldAgentProject(options) {
|
|
463
|
+
const template = resolveAgentTemplate(options.template);
|
|
464
|
+
const root = path.resolve(options.destDir);
|
|
465
|
+
if (fs.existsSync(root) && fs.readdirSync(root).length > 0 && !options.force) {
|
|
466
|
+
throw new Error(`Destination not empty: ${root} (pass force to overwrite)`);
|
|
467
|
+
}
|
|
468
|
+
const files = template === 'bare'
|
|
469
|
+
? bareFiles(options.name)
|
|
470
|
+
: template === 'skillgate'
|
|
471
|
+
? skillgateFiles(options.name)
|
|
472
|
+
: agentOsFiles(options.name);
|
|
473
|
+
fs.mkdirSync(root, { recursive: true });
|
|
474
|
+
const written = [];
|
|
475
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
476
|
+
writeFile(root, rel, content);
|
|
477
|
+
written.push(rel);
|
|
478
|
+
}
|
|
479
|
+
return { path: root, template, files: written };
|
|
480
|
+
}
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
/**
|
|
3
|
+
* `hazel agent new` — scaffold Agent OS / DNA templates (G2 template unification).
|
|
3
4
|
* `hazel agent install <file.dna.json>` — validate / print marketplace install plan.
|
|
4
|
-
*
|
|
5
|
+
* `hazel agent run` — live execute from DNA (AOS-011).
|
|
6
|
+
* `hazel agent apply|get|describe|delete|reconcile|events` — declarative platform resources (local control plane).
|
|
7
|
+
* `hazel agent logs` / `doctor` — timeline + environment checks.
|
|
8
|
+
* `hazel agent runs list|inspect|cancel|resume|approve` — durable store ops.
|
|
5
9
|
*/
|
|
6
10
|
export declare function registerAgentCommand(program: Command): void;
|