@agentvalet/register 0.1.0
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/README.md +91 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +309 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @agentvalet/register
|
|
2
|
+
|
|
3
|
+
Self-register AI agents with [AgentValet](https://agentvalet.ai) — the governance and proxy layer that sits between AI agents and SaaS platforms.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# One-off use (no install):
|
|
9
|
+
npx @agentvalet/register --name "My Agent" --scope slack:chat:write
|
|
10
|
+
|
|
11
|
+
# Or install globally:
|
|
12
|
+
npm install -g @agentvalet/register
|
|
13
|
+
agentvalet-register --name "My Agent" --scope slack:chat:write
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Interactive mode
|
|
17
|
+
|
|
18
|
+
Run with no flags for a guided wizard:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx @agentvalet/register
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Flags
|
|
25
|
+
|
|
26
|
+
| Flag | Default | Description |
|
|
27
|
+
|------|---------|-------------|
|
|
28
|
+
| `--owner <uuid>` | `AGENTVALET_OWNER` env | Owner ID from https://agentvalet.ai/settings |
|
|
29
|
+
| `--name <name>` | prompted | Agent name |
|
|
30
|
+
| `--description <desc>` | — | Agent description |
|
|
31
|
+
| `--scope <platform:scope>` | — | Repeatable, e.g. `--scope slack:chat:write --scope gmail:send` |
|
|
32
|
+
| `--api-url <url>` | `https://api.agentvalet.ai` | AgentValet API base URL |
|
|
33
|
+
| `--output <path>` | `./.env.agentvalet` | Path to write env vars |
|
|
34
|
+
| `--key-path <path>` | `./agentvalet.key` | Path to write private key |
|
|
35
|
+
| `--json` | — | Machine-readable JSON output |
|
|
36
|
+
| `--no-wait` | — | Register only, don't wait for approval |
|
|
37
|
+
| `--timeout <seconds>` | `300` | Approval wait timeout |
|
|
38
|
+
|
|
39
|
+
## Exit codes
|
|
40
|
+
|
|
41
|
+
| Code | Meaning |
|
|
42
|
+
|------|---------|
|
|
43
|
+
| `0` | Approved |
|
|
44
|
+
| `1` | Denied |
|
|
45
|
+
| `2` | Timeout |
|
|
46
|
+
| `3` | Network or other error |
|
|
47
|
+
|
|
48
|
+
## Integration examples
|
|
49
|
+
|
|
50
|
+
**Claude Code (`CLAUDE.md`):**
|
|
51
|
+
```markdown
|
|
52
|
+
On startup, register with AgentValet:
|
|
53
|
+
npx @agentvalet/register --owner {uuid} --name "Claude Code Agent" --scope slack:chat:write
|
|
54
|
+
|
|
55
|
+
Then source ./.env.agentvalet before calling any tool.
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Python agent:**
|
|
59
|
+
```python
|
|
60
|
+
import subprocess
|
|
61
|
+
subprocess.run([
|
|
62
|
+
"npx", "@agentvalet/register",
|
|
63
|
+
"--name", "MyAgent",
|
|
64
|
+
"--owner", owner_id,
|
|
65
|
+
"--scope", "slack:chat:write",
|
|
66
|
+
])
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**LangChain / CrewAI / bash script:**
|
|
70
|
+
|
|
71
|
+
Shell out to `npx @agentvalet/register` and parse the output env file:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npx @agentvalet/register --owner $OWNER_ID --name "BashAgent" --scope slack:chat:write
|
|
75
|
+
source ./.env.agentvalet
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## How it works
|
|
79
|
+
|
|
80
|
+
1. POSTs to `/v1/register` with your owner ID, agent name, and requested scopes
|
|
81
|
+
2. Prints the approval URL — the owner approves in the dashboard
|
|
82
|
+
3. Waits for approval via SSE (with polling fallback)
|
|
83
|
+
4. On approval, writes the private key to `--key-path` and env vars to `--output`
|
|
84
|
+
|
|
85
|
+
The agent **never holds SaaS credentials** — AgentValet injects them at proxy time.
|
|
86
|
+
|
|
87
|
+
## Links
|
|
88
|
+
|
|
89
|
+
- Dashboard: https://agentvalet.ai
|
|
90
|
+
- Docs: https://agentvalet.ai/docs
|
|
91
|
+
- GitHub: https://github.com/MCSEdwin/agentvalet
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import chalk2 from "chalk";
|
|
6
|
+
|
|
7
|
+
// src/wizard.ts
|
|
8
|
+
import prompts from "prompts";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
async function runWizard(prefilled) {
|
|
11
|
+
console.log(chalk.bold("\nAgentValet \u2014 Register Agent\n"));
|
|
12
|
+
const questions = [];
|
|
13
|
+
if (!prefilled.ownerId) {
|
|
14
|
+
questions.push({
|
|
15
|
+
type: "text",
|
|
16
|
+
name: "ownerId",
|
|
17
|
+
message: "Owner ID (from https://agentvalet.ai/settings):",
|
|
18
|
+
validate: (v) => v.trim() ? true : "Owner ID is required"
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
if (!prefilled.agentName) {
|
|
22
|
+
questions.push({
|
|
23
|
+
type: "text",
|
|
24
|
+
name: "agentName",
|
|
25
|
+
message: "Agent name:",
|
|
26
|
+
initial: "My Agent",
|
|
27
|
+
validate: (v) => v.trim() ? true : "Name is required"
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
questions.push(
|
|
31
|
+
{
|
|
32
|
+
type: "text",
|
|
33
|
+
name: "description",
|
|
34
|
+
message: "Description (optional):",
|
|
35
|
+
initial: prefilled.description
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
name: "scopesRaw",
|
|
40
|
+
message: "Scopes (comma-separated, e.g. slack:chat:write,gmail:send):",
|
|
41
|
+
initial: prefilled.scopes.join(",")
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
const answers = await prompts(questions, {
|
|
45
|
+
onCancel: () => {
|
|
46
|
+
console.log(chalk.yellow("\nCancelled."));
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
const ownerId = answers.ownerId ?? prefilled.ownerId;
|
|
51
|
+
const agentName = answers.agentName ?? prefilled.agentName;
|
|
52
|
+
const description = answers.description ?? prefilled.description;
|
|
53
|
+
const scopesRaw = answers.scopesRaw;
|
|
54
|
+
const scopes = scopesRaw?.split(",").map((s) => s.trim()).filter(Boolean) ?? prefilled.scopes;
|
|
55
|
+
return { ownerId: ownerId.trim(), agentName: agentName.trim(), description, scopes };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/register.ts
|
|
59
|
+
import { fetch } from "undici";
|
|
60
|
+
async function registerAgent(apiUrl, body) {
|
|
61
|
+
const res = await fetch(`${apiUrl}/v1/register`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: { "Content-Type": "application/json" },
|
|
64
|
+
body: JSON.stringify(body)
|
|
65
|
+
});
|
|
66
|
+
const json = await res.json();
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
const wrapped = json.data;
|
|
69
|
+
const errMsg = wrapped?.error ?? json.error ?? `HTTP ${res.status}`;
|
|
70
|
+
throw new Error(errMsg);
|
|
71
|
+
}
|
|
72
|
+
const data = json.data ?? json;
|
|
73
|
+
const token = data.registration_token;
|
|
74
|
+
if (!token) throw new Error("No registration_token in response");
|
|
75
|
+
return token;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/wait.ts
|
|
79
|
+
import { fetch as fetch2 } from "undici";
|
|
80
|
+
async function waitForApproval(apiUrl, token, timeoutMs, onTick) {
|
|
81
|
+
const deadline = Date.now() + timeoutMs;
|
|
82
|
+
const sseResult = await trySSE(apiUrl, token, Math.min(3e4, timeoutMs), onTick);
|
|
83
|
+
if (sseResult) return sseResult;
|
|
84
|
+
return poll(apiUrl, token, deadline, onTick);
|
|
85
|
+
}
|
|
86
|
+
async function trySSE(apiUrl, token, windowMs, onTick) {
|
|
87
|
+
const controller = new AbortController();
|
|
88
|
+
const timer = setTimeout(() => controller.abort(), windowMs);
|
|
89
|
+
try {
|
|
90
|
+
const res = await fetch2(`${apiUrl}/v1/register/stream/${token}`, {
|
|
91
|
+
signal: controller.signal,
|
|
92
|
+
headers: { Accept: "text/event-stream" }
|
|
93
|
+
});
|
|
94
|
+
if (!res.ok || !res.body) return null;
|
|
95
|
+
const reader = res.body.getReader();
|
|
96
|
+
const decoder = new TextDecoder();
|
|
97
|
+
let buffer = "";
|
|
98
|
+
while (true) {
|
|
99
|
+
const { done, value } = await reader.read();
|
|
100
|
+
if (done) break;
|
|
101
|
+
buffer += decoder.decode(value, { stream: true });
|
|
102
|
+
const lines = buffer.split("\n");
|
|
103
|
+
buffer = lines.pop() ?? "";
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
if (line.startsWith(":")) {
|
|
106
|
+
onTick?.();
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (line.startsWith("data:")) {
|
|
110
|
+
const dataStr = line.slice(5).trim();
|
|
111
|
+
if (!dataStr) continue;
|
|
112
|
+
try {
|
|
113
|
+
const data = JSON.parse(dataStr);
|
|
114
|
+
const parsed = parseEvent(data);
|
|
115
|
+
if (parsed) return parsed;
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
} catch {
|
|
123
|
+
return null;
|
|
124
|
+
} finally {
|
|
125
|
+
clearTimeout(timer);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async function poll(apiUrl, token, deadline, onTick) {
|
|
129
|
+
while (Date.now() < deadline) {
|
|
130
|
+
await sleep(5e3);
|
|
131
|
+
onTick?.();
|
|
132
|
+
try {
|
|
133
|
+
const res = await fetch2(`${apiUrl}/v1/register/status/${token}`);
|
|
134
|
+
if (!res.ok) continue;
|
|
135
|
+
const json = await res.json();
|
|
136
|
+
const data = json.data ?? json;
|
|
137
|
+
const parsed = parseEvent(data);
|
|
138
|
+
if (parsed) return parsed;
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { status: "timeout" };
|
|
143
|
+
}
|
|
144
|
+
function parseEvent(data) {
|
|
145
|
+
if (data.status === "approved") {
|
|
146
|
+
const agentId = data.agent_id ?? data.agentId;
|
|
147
|
+
if (!agentId) return null;
|
|
148
|
+
return { status: "approved", agentId };
|
|
149
|
+
}
|
|
150
|
+
if (data.status === "rejected") return { status: "rejected" };
|
|
151
|
+
if (data.status === "timeout" || data.status === "expired") return { status: "expired" };
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function sleep(ms) {
|
|
155
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/save.ts
|
|
159
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
160
|
+
import { dirname, resolve } from "path";
|
|
161
|
+
async function saveOutput(opts) {
|
|
162
|
+
const keyPath = resolve(opts.keyFilePath);
|
|
163
|
+
const envPath = resolve(opts.envFilePath);
|
|
164
|
+
await mkdir(dirname(keyPath), { recursive: true });
|
|
165
|
+
await mkdir(dirname(envPath), { recursive: true });
|
|
166
|
+
await writeFile(keyPath, "", { mode: 384 });
|
|
167
|
+
const envContent = [
|
|
168
|
+
`# AgentValet \u2014 generated by agentvalet-register`,
|
|
169
|
+
`AGENTVALET_AGENT_ID=${opts.agentId}`,
|
|
170
|
+
`AGENTVALET_API_URL=${opts.apiUrl}`,
|
|
171
|
+
`AGENTVALET_PRIVATE_KEY_PATH=${keyPath}`,
|
|
172
|
+
""
|
|
173
|
+
].join("\n");
|
|
174
|
+
await writeFile(envPath, envContent, "utf-8");
|
|
175
|
+
return { keyPath, envPath };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// src/index.ts
|
|
179
|
+
function collectScope(value, previous) {
|
|
180
|
+
return [...previous, value];
|
|
181
|
+
}
|
|
182
|
+
var program = new Command();
|
|
183
|
+
program.name("agentvalet-register").description("Self-register an AI agent with AgentValet").version("0.1.0").option("--owner <uuid>", "Owner ID (or set AGENTVALET_OWNER env var)").option("--name <name>", "Agent name").option("--description <desc>", "Agent description").option("--scope <platform:scope>", "Requested scope, repeatable", collectScope, []).option("--api-url <url>", "AgentValet API base URL", "https://api.agentvalet.ai").option("--output <path>", "Path to write .env file", "./.env.agentvalet").option("--key-path <path>", "Path to write private key", "./agentvalet.key").option("--json", "Machine-readable JSON output").option("--no-wait", "Register only, do not wait for approval").option("--timeout <seconds>", "Approval wait timeout in seconds", "300").action(async (opts) => {
|
|
184
|
+
const isJson = !!opts.json;
|
|
185
|
+
let ownerId = opts.owner ?? process.env.AGENTVALET_OWNER ?? "";
|
|
186
|
+
let agentName = opts.name ?? "";
|
|
187
|
+
let description = opts.description ?? "";
|
|
188
|
+
let scopes = opts.scope;
|
|
189
|
+
if (!agentName) {
|
|
190
|
+
if (isJson) {
|
|
191
|
+
console.error(JSON.stringify({ error: "--name is required in non-interactive mode" }));
|
|
192
|
+
process.exit(3);
|
|
193
|
+
}
|
|
194
|
+
const answers = await runWizard({ ownerId, agentName, description, scopes });
|
|
195
|
+
ownerId = answers.ownerId;
|
|
196
|
+
agentName = answers.agentName;
|
|
197
|
+
description = answers.description;
|
|
198
|
+
scopes = answers.scopes;
|
|
199
|
+
}
|
|
200
|
+
if (!ownerId) {
|
|
201
|
+
if (isJson) {
|
|
202
|
+
console.error(JSON.stringify({ error: "--owner or AGENTVALET_OWNER is required" }));
|
|
203
|
+
process.exit(3);
|
|
204
|
+
}
|
|
205
|
+
console.error(chalk2.red("Error: --owner or AGENTVALET_OWNER environment variable is required"));
|
|
206
|
+
process.exit(3);
|
|
207
|
+
}
|
|
208
|
+
const scopeMap = /* @__PURE__ */ new Map();
|
|
209
|
+
for (const s of scopes) {
|
|
210
|
+
const colon = s.indexOf(":");
|
|
211
|
+
if (colon === -1) {
|
|
212
|
+
console.error(
|
|
213
|
+
isJson ? JSON.stringify({ error: `Invalid scope format: ${s} (expected platform:scope)` }) : chalk2.red(`Invalid scope format: ${s} (expected platform:scope)`)
|
|
214
|
+
);
|
|
215
|
+
process.exit(3);
|
|
216
|
+
}
|
|
217
|
+
const platform = s.slice(0, colon);
|
|
218
|
+
const scope = s.slice(colon + 1);
|
|
219
|
+
const existing = scopeMap.get(platform) ?? [];
|
|
220
|
+
scopeMap.set(platform, [...existing, scope]);
|
|
221
|
+
}
|
|
222
|
+
const requestedScopes = Array.from(scopeMap.entries()).map(([platformId, s]) => ({
|
|
223
|
+
platformId,
|
|
224
|
+
scopes: s
|
|
225
|
+
}));
|
|
226
|
+
let token;
|
|
227
|
+
try {
|
|
228
|
+
token = await registerAgent(opts.apiUrl, {
|
|
229
|
+
owner_id: ownerId,
|
|
230
|
+
agent_name: agentName,
|
|
231
|
+
agent_description: description || void 0,
|
|
232
|
+
requested_scopes: requestedScopes
|
|
233
|
+
});
|
|
234
|
+
} catch (err) {
|
|
235
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
236
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk2.red(`Registration failed: ${msg}`));
|
|
237
|
+
process.exit(3);
|
|
238
|
+
}
|
|
239
|
+
if (!isJson) {
|
|
240
|
+
console.log(chalk2.cyan(`
|
|
241
|
+
Approve at: https://agentvalet.ai/registrations
|
|
242
|
+
`));
|
|
243
|
+
}
|
|
244
|
+
if (!opts.wait) {
|
|
245
|
+
if (isJson) {
|
|
246
|
+
console.log(JSON.stringify({ status: "pending", token }));
|
|
247
|
+
} else {
|
|
248
|
+
console.log(chalk2.yellow("Registration submitted (--no-wait). Token:"), token);
|
|
249
|
+
}
|
|
250
|
+
process.exit(0);
|
|
251
|
+
}
|
|
252
|
+
const timeoutMs = parseInt(opts.timeout, 10) * 1e3;
|
|
253
|
+
let result;
|
|
254
|
+
try {
|
|
255
|
+
result = await waitForApproval(opts.apiUrl, token, timeoutMs, isJson ? void 0 : () => {
|
|
256
|
+
});
|
|
257
|
+
} catch (err) {
|
|
258
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
259
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk2.red(`Wait failed: ${msg}`));
|
|
260
|
+
process.exit(3);
|
|
261
|
+
}
|
|
262
|
+
if (result.status === "rejected") {
|
|
263
|
+
if (isJson) {
|
|
264
|
+
console.log(JSON.stringify({ status: "rejected" }));
|
|
265
|
+
} else {
|
|
266
|
+
console.log(chalk2.red("\nRegistration denied by owner."));
|
|
267
|
+
}
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
if (result.status === "timeout" || result.status === "expired") {
|
|
271
|
+
if (isJson) {
|
|
272
|
+
console.log(JSON.stringify({ status: "timeout" }));
|
|
273
|
+
} else {
|
|
274
|
+
console.log(chalk2.yellow("\nTimed out waiting for approval."));
|
|
275
|
+
}
|
|
276
|
+
process.exit(2);
|
|
277
|
+
}
|
|
278
|
+
const { agentId } = result;
|
|
279
|
+
let keyPath;
|
|
280
|
+
let envPath;
|
|
281
|
+
try {
|
|
282
|
+
({ keyPath, envPath } = await saveOutput({
|
|
283
|
+
agentId,
|
|
284
|
+
apiUrl: opts.apiUrl,
|
|
285
|
+
keyFilePath: opts.keyPath,
|
|
286
|
+
envFilePath: opts.output
|
|
287
|
+
}));
|
|
288
|
+
} catch (err) {
|
|
289
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
290
|
+
console.error(isJson ? JSON.stringify({ error: msg }) : chalk2.red(`Failed to save output: ${msg}`));
|
|
291
|
+
process.exit(3);
|
|
292
|
+
}
|
|
293
|
+
if (isJson) {
|
|
294
|
+
console.log(JSON.stringify({ status: "approved", agentId, keyPath, envPath }));
|
|
295
|
+
} else {
|
|
296
|
+
console.log(chalk2.green(`
|
|
297
|
+
Approved! Agent ID: ${agentId}`));
|
|
298
|
+
console.log(chalk2.gray(`Key: ${keyPath}`));
|
|
299
|
+
console.log(chalk2.gray(`Env file: ${envPath}`));
|
|
300
|
+
console.log(`
|
|
301
|
+
Source the env file before calling any tool:`);
|
|
302
|
+
console.log(chalk2.cyan(` source ${envPath}`));
|
|
303
|
+
}
|
|
304
|
+
process.exit(0);
|
|
305
|
+
});
|
|
306
|
+
program.parseAsync().catch((err) => {
|
|
307
|
+
console.error(err);
|
|
308
|
+
process.exit(3);
|
|
309
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentvalet/register",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Self-register AI agents with AgentValet",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentvalet-register": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"typecheck": "tsc --noEmit",
|
|
18
|
+
"prepublishOnly": "pnpm build"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"chalk": "^5.3.0",
|
|
22
|
+
"commander": "^12.0.0",
|
|
23
|
+
"ora": "^8.0.1",
|
|
24
|
+
"prompts": "^2.4.2",
|
|
25
|
+
"undici": "^6.0.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"@types/prompts": "^2.4.0",
|
|
30
|
+
"tsup": "^8.0.0",
|
|
31
|
+
"tsx": "^4.0.0",
|
|
32
|
+
"typescript": "^5.0.0",
|
|
33
|
+
"vitest": "^1.5.0"
|
|
34
|
+
},
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/MCSEdwin/agentvalet.git",
|
|
41
|
+
"directory": "apps/cli"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"ai",
|
|
45
|
+
"agents",
|
|
46
|
+
"auth",
|
|
47
|
+
"credentials",
|
|
48
|
+
"mcp",
|
|
49
|
+
"valet",
|
|
50
|
+
"agentvalet"
|
|
51
|
+
],
|
|
52
|
+
"license": "MIT"
|
|
53
|
+
}
|