@agentuity/cli 0.0.67 → 0.0.68
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/dist/cmd/ai/prompt/agent.js +2 -2
- package/dist/cmd/ai/prompt/api.js +1 -1
- package/dist/cmd/build/ast.js +4 -4
- package/dist/cmd/build/ast.js.map +1 -1
- package/dist/cmd/build/bundler.js +2 -2
- package/dist/cmd/build/bundler.js.map +1 -1
- package/dist/cmd/build/plugin.d.ts.map +1 -1
- package/dist/cmd/build/plugin.js +8 -11
- package/dist/cmd/build/plugin.js.map +1 -1
- package/dist/cmd/dev/index.js +2 -2
- package/dist/cmd/dev/index.js.map +1 -1
- package/dist/cmd/project/download.js +10 -10
- package/dist/cmd/project/download.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +48 -87
- package/dist/config.js.map +1 -1
- package/dist/keychain.d.ts +31 -0
- package/dist/keychain.d.ts.map +1 -0
- package/dist/keychain.js +131 -0
- package/dist/keychain.js.map +1 -0
- package/dist/utils/detectSubagent.d.ts +1 -1
- package/dist/utils/detectSubagent.js +3 -3
- package/dist/utils/detectSubagent.js.map +1 -1
- package/package.json +3 -3
- package/src/cmd/ai/prompt/agent.ts +2 -2
- package/src/cmd/ai/prompt/api.ts +1 -1
- package/src/cmd/build/ast.ts +4 -4
- package/src/cmd/build/bundler.ts +2 -2
- package/src/cmd/build/plugin.ts +8 -11
- package/src/cmd/dev/index.ts +2 -2
- package/src/cmd/project/download.ts +10 -10
- package/src/config.ts +54 -96
- package/src/keychain.ts +178 -0
- package/src/utils/detectSubagent.ts +3 -3
package/dist/keychain.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* macOS Keychain integration for secure auth token storage
|
|
3
|
+
*
|
|
4
|
+
* Stores auth tokens encrypted in the macOS Keychain using a per-device AES-256 key.
|
|
5
|
+
* No user prompts required - fully automatic and secure.
|
|
6
|
+
*/
|
|
7
|
+
const SERVICE_PREFIX = "com.agentuity.cli";
|
|
8
|
+
const KEY_ACCOUNT = "aes-encryption-key";
|
|
9
|
+
/**
|
|
10
|
+
* Check if we're running on macOS
|
|
11
|
+
*/
|
|
12
|
+
export function isMacOS() {
|
|
13
|
+
return process.platform === 'darwin';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get or create an AES encryption key stored in the macOS Keychain
|
|
17
|
+
*/
|
|
18
|
+
async function ensureEncryptionKey(service) {
|
|
19
|
+
// Try to read existing key
|
|
20
|
+
const find = Bun.spawn(['security', 'find-generic-password', '-s', service, '-a', KEY_ACCOUNT, '-w'], { stderr: 'ignore' });
|
|
21
|
+
const stdout = await new Response(find.stdout).text();
|
|
22
|
+
if (stdout.length > 0) {
|
|
23
|
+
const b64 = stdout.trim();
|
|
24
|
+
return Uint8Array.from(Buffer.from(b64, 'base64'));
|
|
25
|
+
}
|
|
26
|
+
// Create a new 32-byte (256-bit) AES key
|
|
27
|
+
const key = crypto.getRandomValues(new Uint8Array(32));
|
|
28
|
+
const b64 = Buffer.from(key).toString('base64');
|
|
29
|
+
// Store in macOS Keychain (no user prompts with -U flag)
|
|
30
|
+
const add = Bun.spawn([
|
|
31
|
+
'security',
|
|
32
|
+
'add-generic-password',
|
|
33
|
+
'-s',
|
|
34
|
+
service,
|
|
35
|
+
'-a',
|
|
36
|
+
KEY_ACCOUNT,
|
|
37
|
+
'-w',
|
|
38
|
+
b64,
|
|
39
|
+
'-U', // Update without user confirmation
|
|
40
|
+
]);
|
|
41
|
+
await add.exited;
|
|
42
|
+
return key;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Encrypt data using AES-256-GCM
|
|
46
|
+
*/
|
|
47
|
+
async function encrypt(data, keyBytes) {
|
|
48
|
+
const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, ['encrypt']);
|
|
49
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
50
|
+
const plaintext = new TextEncoder().encode(data);
|
|
51
|
+
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, plaintext));
|
|
52
|
+
// Combine IV + ciphertext
|
|
53
|
+
const combined = new Uint8Array(iv.length + ciphertext.length);
|
|
54
|
+
combined.set(iv, 0);
|
|
55
|
+
combined.set(ciphertext, iv.length);
|
|
56
|
+
return combined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Decrypt data using AES-256-GCM
|
|
60
|
+
*/
|
|
61
|
+
async function decrypt(combined, keyBytes) {
|
|
62
|
+
const key = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, ['decrypt']);
|
|
63
|
+
const iv = combined.slice(0, 12);
|
|
64
|
+
const ciphertext = combined.slice(12);
|
|
65
|
+
const plaintext = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, ciphertext);
|
|
66
|
+
return new TextDecoder().decode(plaintext);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Store auth data in macOS Keychain
|
|
70
|
+
*/
|
|
71
|
+
export async function saveAuthToKeychain(profileName, authData) {
|
|
72
|
+
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
73
|
+
const account = 'auth-token';
|
|
74
|
+
// Get or create encryption key
|
|
75
|
+
const key = await ensureEncryptionKey(service);
|
|
76
|
+
// Encrypt the auth data
|
|
77
|
+
const json = JSON.stringify(authData);
|
|
78
|
+
const encrypted = await encrypt(json, key);
|
|
79
|
+
const b64 = Buffer.from(encrypted).toString('base64');
|
|
80
|
+
// Store encrypted auth in keychain
|
|
81
|
+
// First try to delete if exists, then add
|
|
82
|
+
const del = Bun.spawn(['security', 'delete-generic-password', '-s', service, '-a', account], { stderr: 'ignore' });
|
|
83
|
+
await del.exited;
|
|
84
|
+
const add = Bun.spawn([
|
|
85
|
+
'security',
|
|
86
|
+
'add-generic-password',
|
|
87
|
+
'-s',
|
|
88
|
+
service,
|
|
89
|
+
'-a',
|
|
90
|
+
account,
|
|
91
|
+
'-w',
|
|
92
|
+
b64,
|
|
93
|
+
'-U',
|
|
94
|
+
]);
|
|
95
|
+
await add.exited;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Retrieve auth data from macOS Keychain
|
|
99
|
+
*/
|
|
100
|
+
export async function getAuthFromKeychain(profileName) {
|
|
101
|
+
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
102
|
+
const account = 'auth-token';
|
|
103
|
+
try {
|
|
104
|
+
// Get the encrypted auth data
|
|
105
|
+
const find = Bun.spawn(['security', 'find-generic-password', '-s', service, '-a', account, '-w'], { stderr: 'ignore' });
|
|
106
|
+
const stdout = await new Response(find.stdout).text();
|
|
107
|
+
if (stdout.length === 0) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
const b64 = stdout.trim();
|
|
111
|
+
const encrypted = Uint8Array.from(Buffer.from(b64, 'base64'));
|
|
112
|
+
// Get the encryption key
|
|
113
|
+
const key = await ensureEncryptionKey(service);
|
|
114
|
+
// Decrypt the auth data
|
|
115
|
+
const json = await decrypt(encrypted, key);
|
|
116
|
+
return JSON.parse(json);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Delete auth data from macOS Keychain
|
|
124
|
+
*/
|
|
125
|
+
export async function deleteAuthFromKeychain(profileName) {
|
|
126
|
+
const service = `${SERVICE_PREFIX}.${profileName}`;
|
|
127
|
+
const account = 'auth-token';
|
|
128
|
+
const del = Bun.spawn(['security', 'delete-generic-password', '-s', service, '-a', account], { stderr: 'ignore' });
|
|
129
|
+
await del.exited;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Detects if a file path represents a subagent based on path structure.
|
|
3
3
|
*
|
|
4
|
-
* Subagents follow the pattern:
|
|
4
|
+
* Subagents follow the pattern: agent/parent/child/agent.ts or agent/parent/child/route.ts
|
|
5
5
|
* The path structure is currently hardcoded to 4 segments but could be made configurable later.
|
|
6
6
|
*
|
|
7
7
|
* @param filePath - The file path to analyze (can include leading './')
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Detects if a file path represents a subagent based on path structure.
|
|
3
3
|
*
|
|
4
|
-
* Subagents follow the pattern:
|
|
4
|
+
* Subagents follow the pattern: agent/parent/child/agent.ts or agent/parent/child/route.ts
|
|
5
5
|
* The path structure is currently hardcoded to 4 segments but could be made configurable later.
|
|
6
6
|
*
|
|
7
7
|
* @param filePath - The file path to analyze (can include leading './')
|
|
@@ -16,9 +16,9 @@ export function detectSubagent(filePath, srcDir) {
|
|
|
16
16
|
}
|
|
17
17
|
// Strip leading './' and split into parts, filtering out empty segments
|
|
18
18
|
const pathParts = normalizedPath.replace(/^\.\//, '').split('/').filter(Boolean);
|
|
19
|
-
// Path structure assumption: ['
|
|
19
|
+
// Path structure assumption: ['agent', 'parent', 'child', 'agent.ts' | 'route.ts' | 'route']
|
|
20
20
|
// Currently hardcoded to 4 segments - consider making configurable in the future
|
|
21
|
-
const isSubagent = pathParts.length === 4 && pathParts[0] === '
|
|
21
|
+
const isSubagent = pathParts.length === 4 && pathParts[0] === 'agent';
|
|
22
22
|
const parentName = isSubagent ? pathParts[1] : null;
|
|
23
23
|
return { isSubagent, parentName };
|
|
24
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detectSubagent.js","sourceRoot":"","sources":["../../src/utils/detectSubagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC7B,QAAgB,EAChB,MAAe;IAEf,IAAI,cAAc,GAAG,QAAQ,CAAC;IAE9B,2BAA2B;IAC3B,IAAI,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,wEAAwE;IACxE,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjF,
|
|
1
|
+
{"version":3,"file":"detectSubagent.js","sourceRoot":"","sources":["../../src/utils/detectSubagent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAC7B,QAAgB,EAChB,MAAe;IAEf,IAAI,cAAc,GAAG,QAAQ,CAAC;IAE9B,2BAA2B;IAC3B,IAAI,MAAM,IAAI,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACjD,cAAc,GAAG,cAAc,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,wEAAwE;IACxE,MAAM,SAAS,GAAG,cAAc,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjF,6FAA6F;IAC7F,iFAAiF;IACjF,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;IACtE,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEpD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC;AACnC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentuity/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.68",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"author": "Agentuity employees and contributors",
|
|
6
6
|
"type": "module",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"prepublishOnly": "bun run clean && bun run build"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@agentuity/core": "0.0.
|
|
39
|
-
"@agentuity/server": "0.0.
|
|
38
|
+
"@agentuity/core": "0.0.68",
|
|
39
|
+
"@agentuity/server": "0.0.68",
|
|
40
40
|
"@datasert/cronjs-parser": "^1.4.0",
|
|
41
41
|
"@terascope/fetch-github-release": "^2.2.1",
|
|
42
42
|
"acorn-loose": "^8.5.2",
|
|
@@ -27,7 +27,7 @@ Each agent folder must contain:
|
|
|
27
27
|
|
|
28
28
|
Example structure:
|
|
29
29
|
\`\`\`
|
|
30
|
-
src/
|
|
30
|
+
src/agent/
|
|
31
31
|
├── hello/
|
|
32
32
|
│ ├── agent.ts
|
|
33
33
|
│ └── route.ts
|
|
@@ -211,7 +211,7 @@ Agents can have subagents organized one level deep. This is useful for grouping
|
|
|
211
211
|
### Directory Structure for Subagents
|
|
212
212
|
|
|
213
213
|
\`\`\`
|
|
214
|
-
src/
|
|
214
|
+
src/agent/
|
|
215
215
|
└── team/ # Parent agent
|
|
216
216
|
├── agent.ts # Parent agent
|
|
217
217
|
├── route.ts # Parent routes
|
package/src/cmd/ai/prompt/api.ts
CHANGED
package/src/cmd/build/ast.ts
CHANGED
|
@@ -928,14 +928,14 @@ export async function parseRoute(
|
|
|
928
928
|
|
|
929
929
|
// Detect if this is a subagent route and build proper path
|
|
930
930
|
const relativePath = relative(rootDir, dir)
|
|
931
|
-
.replace(/^src\/
|
|
932
|
-
.replace(/^src\/
|
|
931
|
+
.replace(/^src\/agent\//, '')
|
|
932
|
+
.replace(/^src\/web\//, '');
|
|
933
933
|
const pathParts = relativePath.split('/').filter(Boolean);
|
|
934
|
-
const isSubagent = pathParts.length === 2 && filename.includes('src/
|
|
934
|
+
const isSubagent = pathParts.length === 2 && filename.includes('src/agent');
|
|
935
935
|
const routeName = isSubagent ? pathParts.join('/') : name;
|
|
936
936
|
|
|
937
937
|
const routes: RouteDefinition = [];
|
|
938
|
-
const routePrefix = filename.includes('src/
|
|
938
|
+
const routePrefix = filename.includes('src/agent') ? '/agent' : '/api';
|
|
939
939
|
|
|
940
940
|
try {
|
|
941
941
|
for (const body of ast.body) {
|
package/src/cmd/build/bundler.ts
CHANGED
|
@@ -85,10 +85,10 @@ export async function bundle({
|
|
|
85
85
|
|
|
86
86
|
const appEntrypoints: string[] = [];
|
|
87
87
|
|
|
88
|
-
for (const folder of ['
|
|
88
|
+
for (const folder of ['web', 'agent']) {
|
|
89
89
|
const dir = join(srcDir, folder);
|
|
90
90
|
if (!existsSync(dir)) {
|
|
91
|
-
if (folder === '
|
|
91
|
+
if (folder === 'agent') {
|
|
92
92
|
throw new AgentsDirNotFoundError({ message: `Expected directory not found: ${dir}` });
|
|
93
93
|
}
|
|
94
94
|
continue;
|
package/src/cmd/build/plugin.ts
CHANGED
|
@@ -142,7 +142,7 @@ function generateAgentRegistry(srcDir: string, agentInfo: Array<Record<string, s
|
|
|
142
142
|
.map(({ name, path, parent }) => {
|
|
143
143
|
const fullName = parent ? `${parent}.${name}` : name;
|
|
144
144
|
const camelName = toCamelCase(fullName.replace('.', '_'));
|
|
145
|
-
const relativePath = path.replace(/^\.\/
|
|
145
|
+
const relativePath = path.replace(/^\.\/agent\//, './');
|
|
146
146
|
return `import ${camelName}Agent from '${relativePath}';`;
|
|
147
147
|
})
|
|
148
148
|
.join('\n');
|
|
@@ -340,11 +340,11 @@ ${reactAgentTypes}
|
|
|
340
340
|
}
|
|
341
341
|
`;
|
|
342
342
|
|
|
343
|
-
const agentsDir = join(srcDir, '
|
|
343
|
+
const agentsDir = join(srcDir, 'agent');
|
|
344
344
|
const registryPath = join(agentsDir, 'registry.generated.ts');
|
|
345
345
|
const legacyTypesPath = join(agentsDir, 'types.generated.d.ts');
|
|
346
346
|
|
|
347
|
-
// Ensure
|
|
347
|
+
// Ensure agent directory exists
|
|
348
348
|
if (!existsSync(agentsDir)) {
|
|
349
349
|
mkdirSync(agentsDir, { recursive: true });
|
|
350
350
|
}
|
|
@@ -435,7 +435,7 @@ const AgentuityBundler: BunPlugin = {
|
|
|
435
435
|
newsource = ns;
|
|
436
436
|
|
|
437
437
|
// Detect if this is a subagent by checking path structure
|
|
438
|
-
// Note: Path structure assumption - 4 segments:
|
|
438
|
+
// Note: Path structure assumption - 4 segments: agent/parent/child/agent.ts
|
|
439
439
|
const { isSubagent, parentName } = detectSubagent(args.path, srcDir);
|
|
440
440
|
if (isSubagent && parentName) {
|
|
441
441
|
md.set('parent', parentName);
|
|
@@ -521,14 +521,11 @@ const AgentuityBundler: BunPlugin = {
|
|
|
521
521
|
|
|
522
522
|
const agentPath = route
|
|
523
523
|
.replace(/\/route$/, '/*')
|
|
524
|
-
.replace('/agents', '/agent')
|
|
525
524
|
.replace('./', '/');
|
|
526
525
|
const routePath = route
|
|
527
526
|
.replace(/\/route$/, '')
|
|
528
|
-
.replace('/
|
|
529
|
-
.replace('/
|
|
530
|
-
.replace('/agents', '/agent')
|
|
531
|
-
.replace('/agents', '/agent')
|
|
527
|
+
.replace('/web/', '/api/')
|
|
528
|
+
.replace('/web', '/api')
|
|
532
529
|
.replace('./', '/');
|
|
533
530
|
|
|
534
531
|
const definitions = await parseRoute(
|
|
@@ -575,7 +572,7 @@ const AgentuityBundler: BunPlugin = {
|
|
|
575
572
|
if (hasAgent) {
|
|
576
573
|
const agentRegistrationName =
|
|
577
574
|
isSubagent && parentName ? `${parentName}.${name}` : name;
|
|
578
|
-
// Build evals path from agent path (e.g., '
|
|
575
|
+
// Build evals path from agent path (e.g., 'agent/eval/agent' -> 'agent/eval/eval.ts')
|
|
579
576
|
const agentDirPath = agent.replace(/\/agent$/, '');
|
|
580
577
|
const evalsPath = join(srcDir, agentDirPath, 'eval.ts');
|
|
581
578
|
const evalsImport = existsSync(evalsPath)
|
|
@@ -694,7 +691,7 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
694
691
|
: join(rootDir, filename);
|
|
695
692
|
|
|
696
693
|
// Convert to path relative to srcDir like route-based agents
|
|
697
|
-
// e.g., /path/to/src/
|
|
694
|
+
// e.g., /path/to/src/agent/lifecycle/agent.ts -> ./agent/lifecycle/agent
|
|
698
695
|
const agentPath = absolutePath.replace(srcDir, '.').replace('.ts', '');
|
|
699
696
|
|
|
700
697
|
// Extract folder name as agent name (same as route-based logic)
|
package/src/cmd/dev/index.ts
CHANGED
|
@@ -1002,10 +1002,10 @@ export const command = createCommand({
|
|
|
1002
1002
|
statSync(absPath).isDirectory() &&
|
|
1003
1003
|
readdirSync(absPath).length === 0
|
|
1004
1004
|
) {
|
|
1005
|
-
if (changedFile?.startsWith('src/
|
|
1005
|
+
if (changedFile?.startsWith('src/agent/')) {
|
|
1006
1006
|
logger.debug('agent directory created: %s', changedFile);
|
|
1007
1007
|
createAgentTemplates(absPath);
|
|
1008
|
-
} else if (changedFile?.startsWith('src/
|
|
1008
|
+
} else if (changedFile?.startsWith('src/web/')) {
|
|
1009
1009
|
logger.debug('api directory created: %s', changedFile);
|
|
1010
1010
|
createAPITemplates(absPath);
|
|
1011
1011
|
}
|
|
@@ -281,22 +281,22 @@ export async function setupProject(options: SetupOptions): Promise<void> {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
// generate and write AGENTS.md for each of the main folders
|
|
284
|
-
const
|
|
285
|
-
if (existsSync(
|
|
286
|
-
const
|
|
287
|
-
await Bun.write(
|
|
284
|
+
const agentDir = join(dest, 'src', 'agent');
|
|
285
|
+
if (existsSync(agentDir)) {
|
|
286
|
+
const agentAPIFile = join(agentDir, 'AGENTS.md');
|
|
287
|
+
await Bun.write(agentAPIFile, generateAgentPrompt());
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
-
const
|
|
291
|
-
if (existsSync(
|
|
292
|
-
const
|
|
293
|
-
await Bun.write(
|
|
290
|
+
const apiDir = join(dest, 'src', 'api');
|
|
291
|
+
if (existsSync(apiDir)) {
|
|
292
|
+
const agentAPIFile = join(apiDir, 'AGENTS.md');
|
|
293
|
+
await Bun.write(agentAPIFile, generateAPIPrompt());
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
const webDir = join(dest, 'src', 'web');
|
|
297
297
|
if (existsSync(webDir)) {
|
|
298
|
-
const
|
|
299
|
-
await Bun.write(
|
|
298
|
+
const webFile = join(webDir, 'AGENTS.md');
|
|
299
|
+
await Bun.write(webFile, generateWebPrompt());
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
package/src/config.ts
CHANGED
|
@@ -15,6 +15,12 @@ import JSON5 from 'json5';
|
|
|
15
15
|
import type { Config, Profile, AuthData } from './types';
|
|
16
16
|
import { ConfigSchema, ProjectSchema } from './types';
|
|
17
17
|
import * as tui from './tui';
|
|
18
|
+
import {
|
|
19
|
+
isMacOS,
|
|
20
|
+
saveAuthToKeychain,
|
|
21
|
+
getAuthFromKeychain,
|
|
22
|
+
deleteAuthFromKeychain,
|
|
23
|
+
} from './keychain';
|
|
18
24
|
|
|
19
25
|
export const defaultProfileName = 'production';
|
|
20
26
|
|
|
@@ -233,11 +239,7 @@ export async function saveConfig(config: Config, customPath?: string): Promise<v
|
|
|
233
239
|
const configPath = customPath || (await getProfile());
|
|
234
240
|
await ensureConfigDir();
|
|
235
241
|
|
|
236
|
-
|
|
237
|
-
const configToSave = { ...config };
|
|
238
|
-
delete configToSave.auth;
|
|
239
|
-
|
|
240
|
-
const content = formatYAML(configToSave);
|
|
242
|
+
const content = formatYAML(config);
|
|
241
243
|
await writeFile(configPath, content + '\n', { mode: 0o600 });
|
|
242
244
|
// Ensure existing files get correct permissions on upgrade
|
|
243
245
|
await chmod(configPath, 0o600);
|
|
@@ -264,47 +266,45 @@ export async function saveAuth(auth: AuthData): Promise<void> {
|
|
|
264
266
|
expires: auth.expires.getTime(),
|
|
265
267
|
};
|
|
266
268
|
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
269
|
+
// On macOS, store in Keychain for better security
|
|
270
|
+
if (isMacOS()) {
|
|
271
|
+
try {
|
|
272
|
+
await saveAuthToKeychain(profileName, authData);
|
|
273
|
+
|
|
274
|
+
// Successfully stored in keychain, remove from config if present
|
|
275
|
+
if (config.auth) {
|
|
276
|
+
delete config.auth;
|
|
277
|
+
await saveConfig(config);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
} catch (error) {
|
|
281
|
+
// Keychain failed, fall back to config file
|
|
282
|
+
console.warn('Failed to store auth in keychain, falling back to config file:', error);
|
|
279
283
|
}
|
|
280
|
-
} catch {
|
|
281
|
-
// Bun.secrets API not available or failed, fallback to config file
|
|
282
|
-
config.auth = authData;
|
|
283
|
-
config.preferences = config.preferences || {};
|
|
284
|
-
(config.preferences as Record<string, unknown>).orgId = '';
|
|
285
|
-
|
|
286
|
-
// Save with auth in config as fallback (saveConfig will try to strip it but we're setting it here)
|
|
287
|
-
const configPath = await getProfile();
|
|
288
|
-
await ensureConfigDir();
|
|
289
|
-
const content = formatYAML(config);
|
|
290
|
-
await writeFile(configPath, content + '\n', { mode: 0o600 });
|
|
291
|
-
await chmod(configPath, 0o600);
|
|
292
|
-
cachedConfig = config;
|
|
293
284
|
}
|
|
285
|
+
|
|
286
|
+
// Store in config file (non-macOS or keychain failed)
|
|
287
|
+
config.auth = authData;
|
|
288
|
+
config.preferences = config.preferences || {};
|
|
289
|
+
(config.preferences as Record<string, unknown>).orgId = '';
|
|
290
|
+
|
|
291
|
+
await saveConfig(config);
|
|
294
292
|
}
|
|
295
293
|
|
|
296
294
|
export async function clearAuth(): Promise<void> {
|
|
297
295
|
const config = await getOrInitConfig();
|
|
298
296
|
const profileName = config.name || defaultProfileName;
|
|
299
297
|
|
|
300
|
-
//
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
298
|
+
// On macOS, clear from Keychain
|
|
299
|
+
if (isMacOS()) {
|
|
300
|
+
try {
|
|
301
|
+
await deleteAuthFromKeychain(profileName);
|
|
302
|
+
} catch {
|
|
303
|
+
// Ignore errors - keychain entry may not exist
|
|
304
|
+
}
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
//
|
|
307
|
+
// Also clear from config file (for backwards compatibility)
|
|
308
308
|
if (config.auth) {
|
|
309
309
|
delete config.auth;
|
|
310
310
|
config.preferences = config.preferences || {};
|
|
@@ -328,59 +328,12 @@ export async function saveOrgId(orgId: string): Promise<void> {
|
|
|
328
328
|
await saveConfig(config);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
async function migrateAuthToSecrets(
|
|
332
|
-
config: Config,
|
|
333
|
-
profileName: string,
|
|
334
|
-
auth: { api_key: string; user_id: string; expires: number }
|
|
335
|
-
): Promise<boolean> {
|
|
336
|
-
try {
|
|
337
|
-
const authData = {
|
|
338
|
-
api_key: auth.api_key,
|
|
339
|
-
user_id: auth.user_id,
|
|
340
|
-
expires: auth.expires,
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
await Bun.secrets.set({
|
|
344
|
-
service: `agentuity.cli.${profileName}`,
|
|
345
|
-
name: 'auth',
|
|
346
|
-
value: JSON.stringify(authData),
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
// Successfully migrated, remove from config file
|
|
350
|
-
delete config.auth;
|
|
351
|
-
await saveConfig(config);
|
|
352
|
-
|
|
353
|
-
return true;
|
|
354
|
-
} catch {
|
|
355
|
-
// Migration failed, leave in config file
|
|
356
|
-
return false;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
331
|
|
|
360
332
|
export async function getAuth(): Promise<AuthData | null> {
|
|
361
333
|
const config = await loadConfig();
|
|
362
334
|
const profileName = config?.name || defaultProfileName;
|
|
363
335
|
|
|
364
|
-
// Priority 1:
|
|
365
|
-
try {
|
|
366
|
-
const authJson = await Bun.secrets.get({
|
|
367
|
-
service: `agentuity.cli.${profileName}`,
|
|
368
|
-
name: 'auth',
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
if (authJson) {
|
|
372
|
-
const auth = JSON.parse(authJson) as { api_key: string; user_id: string; expires: number };
|
|
373
|
-
return {
|
|
374
|
-
apiKey: auth.api_key,
|
|
375
|
-
userId: auth.user_id,
|
|
376
|
-
expires: new Date(auth.expires),
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
} catch {
|
|
380
|
-
// Bun.secrets API not available or failed, fallback to other methods
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Priority 2: Allow automated login from environment variables
|
|
336
|
+
// Priority 1: Allow automated login from environment variables
|
|
384
337
|
if (process.env.AGENTUITY_CLI_API_KEY && process.env.AGENTUITY_USER_ID) {
|
|
385
338
|
return {
|
|
386
339
|
apiKey: process.env.AGENTUITY_CLI_API_KEY,
|
|
@@ -389,7 +342,23 @@ export async function getAuth(): Promise<AuthData | null> {
|
|
|
389
342
|
};
|
|
390
343
|
}
|
|
391
344
|
|
|
392
|
-
// Priority
|
|
345
|
+
// Priority 2: On macOS, try to read from Keychain
|
|
346
|
+
if (isMacOS()) {
|
|
347
|
+
try {
|
|
348
|
+
const keychainAuth = await getAuthFromKeychain(profileName);
|
|
349
|
+
if (keychainAuth) {
|
|
350
|
+
return {
|
|
351
|
+
apiKey: keychainAuth.api_key,
|
|
352
|
+
userId: keychainAuth.user_id,
|
|
353
|
+
expires: new Date(keychainAuth.expires),
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
} catch {
|
|
357
|
+
// Keychain read failed, fall through to config file
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Priority 3: Read from config file (non-macOS or keychain failed)
|
|
393
362
|
if (!config) return null;
|
|
394
363
|
const auth = config.auth as { api_key?: string; user_id?: string; expires?: number } | undefined;
|
|
395
364
|
|
|
@@ -397,18 +366,7 @@ export async function getAuth(): Promise<AuthData | null> {
|
|
|
397
366
|
return null;
|
|
398
367
|
}
|
|
399
368
|
|
|
400
|
-
// Check if token is unexpired
|
|
401
369
|
const expiresDate = new Date(auth.expires || 0);
|
|
402
|
-
const isExpired = expiresDate.getTime() <= Date.now();
|
|
403
|
-
|
|
404
|
-
// If unexpired, attempt to migrate to Bun.secrets
|
|
405
|
-
if (!isExpired) {
|
|
406
|
-
await migrateAuthToSecrets(config, profileName, {
|
|
407
|
-
api_key: auth.api_key,
|
|
408
|
-
user_id: auth.user_id,
|
|
409
|
-
expires: auth.expires || 0,
|
|
410
|
-
});
|
|
411
|
-
}
|
|
412
370
|
|
|
413
371
|
return {
|
|
414
372
|
apiKey: auth.api_key,
|