@slashfi/agents-sdk 0.70.0 → 0.70.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.
package/src/adk-check.ts CHANGED
@@ -13,10 +13,16 @@
13
13
  * adk run --no-check -e "code" Execute inline without type-checking
14
14
  */
15
15
 
16
- import { join, dirname, basename, resolve } from "node:path";
17
- import { homedir } from "node:os";
18
- import { existsSync, writeFileSync, readFileSync, unlinkSync, copyFileSync } from "node:fs";
19
16
  import { spawnSync } from "node:child_process";
17
+ import {
18
+ copyFileSync,
19
+ existsSync,
20
+ readFileSync,
21
+ unlinkSync,
22
+ writeFileSync,
23
+ } from "node:fs";
24
+ import { homedir } from "node:os";
25
+ import { basename, dirname, join, resolve } from "node:path";
20
26
 
21
27
  export interface CheckOptions {
22
28
  file?: string;
@@ -26,62 +32,94 @@ export interface CheckOptions {
26
32
  configDir?: string;
27
33
  }
28
34
 
29
- const INLINE_PREAMBLE_CHECK = (
30
- `import "./.adk_types";\n` +
31
- `import { createAdk, createLocalFsStore } from "@slashfi/agents-sdk";\n` +
32
- `import { join } from "node:path";\n` +
33
- `import { homedir } from "node:os";\n` +
34
- `const adk = createAdk(\n` +
35
- ` createLocalFsStore(process.env.ADK_CONFIG_DIR ?? join(homedir(), ".adk")),\n` +
36
- ` { token: process.env.ATLAS_TOKEN ?? process.env.ADK_TOKEN ?? "" },\n` +
37
- `);\n`
38
- );
39
-
40
- const INLINE_PREAMBLE_RUN = (
41
- `import { createAdk, createLocalFsStore } from "@slashfi/agents-sdk";\n` +
42
- `import { join } from "node:path";\n` +
43
- `import { homedir } from "node:os";\n` +
44
- `const adk = createAdk(\n` +
45
- ` createLocalFsStore(process.env.ADK_CONFIG_DIR ?? join(homedir(), ".adk")),\n` +
46
- ` { token: process.env.ATLAS_TOKEN ?? process.env.ADK_TOKEN ?? "" },\n` +
47
- `);\n`
48
- );
49
-
50
- export async function adkCheck(opts: CheckOptions): Promise<{ ok: boolean; exitCode: number }> {
51
- const configDir = opts.configDir ?? process.env.ADK_CONFIG_DIR ?? join(homedir(), ".adk");
35
+ /**
36
+ * Resolve the absolute path to the @slashfi/agents-sdk package.
37
+ * This ensures the preamble works regardless of the script's working directory,
38
+ * since bare specifiers like "@slashfi/agents-sdk" may not resolve from arbitrary cwds.
39
+ */
40
+ function resolveAgentsSdkPath(): string {
41
+ try {
42
+ // require.resolve gives us the main entry point, e.g.
43
+ // /root/.bun/install/global/node_modules/@slashfi/agents-sdk/dist/index.js
44
+ const resolved = require.resolve("@slashfi/agents-sdk");
45
+ // Extract package root path
46
+ const marker = "@slashfi/agents-sdk";
47
+ const idx = resolved.indexOf(marker);
48
+ if (idx !== -1) {
49
+ return resolved.substring(0, idx + marker.length);
50
+ }
51
+ return "@slashfi/agents-sdk";
52
+ } catch {
53
+ return "@slashfi/agents-sdk";
54
+ }
55
+ }
56
+
57
+ function makePreambleCheck(sdkPath: string): string {
58
+ return `import "./.adk_types";\nimport { createAdk, createLocalFsStore } from "${sdkPath}";\nimport { join } from "node:path";\nimport { homedir } from "node:os";\nconst adk = createAdk(\n createLocalFsStore(process.env.ADK_CONFIG_DIR ?? join(homedir(), ".adk")),\n { token: process.env.ATLAS_TOKEN ?? process.env.ADK_TOKEN ?? "" },\n);\n`;
59
+ }
60
+
61
+ function makePreambleRun(sdkPath: string): string {
62
+ return `import { createAdk, createLocalFsStore } from "${sdkPath}";\nimport { join } from "node:path";\nimport { homedir } from "node:os";\nconst adk = createAdk(\n createLocalFsStore(process.env.ADK_CONFIG_DIR ?? join(homedir(), ".adk")),\n { token: process.env.ATLAS_TOKEN ?? process.env.ADK_TOKEN ?? "" },\n);\n`;
63
+ }
64
+
65
+ /**
66
+ * Create a run wrapper file that injects the adk preamble before the user's code.
67
+ * Works for both inline (-e) and file mode.
68
+ */
69
+ function writeRunWrapper(
70
+ tmpFile: string,
71
+ preamble: string,
72
+ userCode: string,
73
+ ): void {
74
+ writeFileSync(tmpFile, preamble + userCode);
75
+ }
76
+
77
+ export async function adkCheck(
78
+ opts: CheckOptions,
79
+ ): Promise<{ ok: boolean; exitCode: number }> {
80
+ const configDir =
81
+ opts.configDir ?? process.env.ADK_CONFIG_DIR ?? join(homedir(), ".adk");
52
82
  const adkTypes = join(configDir, "adk.d.ts");
53
83
  const hasTypes = existsSync(adkTypes);
54
84
  const isInline = !!opts.code;
85
+ const sdkPath = resolveAgentsSdkPath();
86
+ const PREAMBLE_RUN = makePreambleRun(sdkPath);
55
87
 
56
88
  // --no-check: skip typecheck, just run
57
89
  if (opts.noCheck && opts.run) {
58
- if (isInline) {
59
- const tmpFile = join(process.cwd(), ".adk_run.ts");
60
- writeFileSync(tmpFile, INLINE_PREAMBLE_RUN + opts.code);
61
- const r = spawnSync("bun", ["run", tmpFile], { cwd: process.cwd(), stdio: "inherit", env: process.env });
62
- try { unlinkSync(tmpFile); } catch {}
63
- return { ok: r.status === 0, exitCode: r.status ?? 1 };
64
- } else {
65
- const r = spawnSync("bun", ["run", resolve(opts.file!)], {
66
- cwd: dirname(resolve(opts.file!)), stdio: "inherit", env: process.env,
67
- });
68
- return { ok: r.status === 0, exitCode: r.status ?? 1 };
69
- }
90
+ const cwd = isInline ? process.cwd() : dirname(resolve(opts.file!));
91
+ const userCode = isInline
92
+ ? opts.code!
93
+ : readFileSync(resolve(opts.file!), "utf-8");
94
+ const tmpFile = join(cwd, ".adk_run.ts");
95
+ writeRunWrapper(tmpFile, PREAMBLE_RUN, userCode);
96
+ const r = spawnSync("bun", ["run", tmpFile], {
97
+ cwd,
98
+ stdio: "inherit",
99
+ env: process.env,
100
+ });
101
+ try {
102
+ unlinkSync(tmpFile);
103
+ } catch {}
104
+ return { ok: r.status === 0, exitCode: r.status ?? 1 };
70
105
  }
71
106
 
72
107
  if (!hasTypes) {
73
- console.error("\x1b[33m\u26a0\x1b[0m No adk.d.ts found. Run `adk sync` first to generate agent types.");
108
+ console.error(
109
+ "\x1b[33m\u26a0\x1b[0m No adk.d.ts found. Run `adk sync` first to generate agent types.",
110
+ );
74
111
  }
75
112
 
76
113
  let cwd: string;
77
114
  let checkFile: string;
78
115
  let originalFile: string | undefined;
79
116
  const cleanup: string[] = [];
117
+ const PREAMBLE_CHECK = makePreambleCheck(sdkPath);
80
118
 
81
119
  if (isInline) {
82
120
  cwd = process.cwd();
83
121
  checkFile = join(cwd, ".adk_check.ts");
84
- writeFileSync(checkFile, INLINE_PREAMBLE_CHECK + opts.code);
122
+ writeFileSync(checkFile, PREAMBLE_CHECK + opts.code);
85
123
  cleanup.push(checkFile);
86
124
  } else if (opts.file) {
87
125
  originalFile = resolve(opts.file);
@@ -91,8 +129,16 @@ export async function adkCheck(opts: CheckOptions): Promise<{ ok: boolean; exitC
91
129
  }
92
130
  cwd = dirname(originalFile);
93
131
  checkFile = join(cwd, `.adk_check_${basename(originalFile)}`);
132
+ // File mode: inject preamble so `adk` is typed and available
94
133
  const typesImport = hasTypes ? `import "./.adk_types";\n` : "";
95
- writeFileSync(checkFile, typesImport + readFileSync(originalFile, "utf-8"));
134
+ const preambleNoTypes = PREAMBLE_CHECK.replace(
135
+ `import "./.adk_types";\n`,
136
+ "",
137
+ );
138
+ writeFileSync(
139
+ checkFile,
140
+ typesImport + preambleNoTypes + readFileSync(originalFile, "utf-8"),
141
+ );
96
142
  cleanup.push(checkFile);
97
143
  } else {
98
144
  console.error('Usage: adk check <file> | adk check -e "<code>"');
@@ -108,31 +154,74 @@ export async function adkCheck(opts: CheckOptions): Promise<{ ok: boolean; exitC
108
154
 
109
155
  // Write minimal tsconfig
110
156
  const tsconfigFile = join(cwd, ".adk_tsconfig.json");
111
- writeFileSync(tsconfigFile, JSON.stringify({
112
- compilerOptions: {
113
- target: "ES2022",
114
- module: "ESNext",
115
- moduleResolution: "bundler",
116
- esModuleInterop: true,
117
- strict: true,
118
- noEmit: true,
119
- skipLibCheck: true,
120
- types: ["node"],
121
- },
122
- include: [basename(checkFile)],
123
- }));
157
+ writeFileSync(
158
+ tsconfigFile,
159
+ JSON.stringify({
160
+ compilerOptions: {
161
+ target: "ES2022",
162
+ module: "ESNext",
163
+ moduleResolution: "bundler",
164
+ esModuleInterop: true,
165
+ strict: true,
166
+ noEmit: true,
167
+ skipLibCheck: true,
168
+ types: ["node"],
169
+ },
170
+ include: [basename(checkFile)],
171
+ }),
172
+ );
124
173
  cleanup.push(tsconfigFile);
125
174
 
126
175
  // Find type checker
127
176
  const checker = findTypeChecker();
128
177
 
178
+ if (!checker) {
179
+ console.error(
180
+ "\x1b[33m\u26a0\x1b[0m No TypeScript type checker found (tsgo or tsc). Skipping type check.",
181
+ );
182
+ for (const f of cleanup) {
183
+ try {
184
+ unlinkSync(f);
185
+ } catch {}
186
+ }
187
+
188
+ // Still run if requested
189
+ if (opts.run) {
190
+ const userCode = isInline
191
+ ? opts.code!
192
+ : readFileSync(originalFile!, "utf-8");
193
+ const tmpFile = join(cwd, ".adk_run.ts");
194
+ writeRunWrapper(tmpFile, PREAMBLE_RUN, userCode);
195
+ const r = spawnSync("bun", ["run", tmpFile], {
196
+ cwd,
197
+ stdio: "inherit",
198
+ env: process.env,
199
+ });
200
+ try {
201
+ unlinkSync(tmpFile);
202
+ } catch {}
203
+ return { ok: r.status === 0, exitCode: r.status ?? 1 };
204
+ }
205
+ return { ok: true, exitCode: 0 };
206
+ }
207
+
129
208
  // Type-check
130
- const result = spawnSync(checker.cmd, [...checker.args, "--project", tsconfigFile], {
131
- cwd, stdio: "inherit", env: process.env,
132
- });
209
+ const result = spawnSync(
210
+ checker.cmd,
211
+ [...checker.args, "--project", tsconfigFile],
212
+ {
213
+ cwd,
214
+ stdio: "inherit",
215
+ env: process.env,
216
+ },
217
+ );
133
218
 
134
219
  // Clean up
135
- for (const f of cleanup) { try { unlinkSync(f); } catch {} }
220
+ for (const f of cleanup) {
221
+ try {
222
+ unlinkSync(f);
223
+ } catch {}
224
+ }
136
225
 
137
226
  if (result.status !== 0) {
138
227
  return { ok: false, exitCode: result.status ?? 1 };
@@ -142,25 +231,73 @@ export async function adkCheck(opts: CheckOptions): Promise<{ ok: boolean; exitC
142
231
 
143
232
  // Run
144
233
  if (opts.run) {
145
- let runResult;
146
- if (isInline) {
147
- const tmpFile = join(cwd, ".adk_run.ts");
148
- writeFileSync(tmpFile, INLINE_PREAMBLE_RUN + opts.code);
149
- runResult = spawnSync("bun", ["run", tmpFile], { cwd, stdio: "inherit", env: process.env });
150
- try { unlinkSync(tmpFile); } catch {}
151
- } else {
152
- runResult = spawnSync("bun", ["run", originalFile!], { cwd, stdio: "inherit", env: process.env });
153
- }
234
+ const userCode = isInline
235
+ ? opts.code!
236
+ : readFileSync(originalFile!, "utf-8");
237
+ const tmpFile = join(cwd, ".adk_run.ts");
238
+ writeRunWrapper(tmpFile, PREAMBLE_RUN, userCode);
239
+ const runResult = spawnSync("bun", ["run", tmpFile], {
240
+ cwd,
241
+ stdio: "inherit",
242
+ env: process.env,
243
+ });
244
+ try {
245
+ unlinkSync(tmpFile);
246
+ } catch {}
154
247
  return { ok: runResult.status === 0, exitCode: runResult.status ?? 1 };
155
248
  }
156
249
 
157
250
  return { ok: true, exitCode: 0 };
158
251
  }
159
252
 
160
- function findTypeChecker(): { cmd: string; args: string[] } {
253
+ /**
254
+ * Find a working TypeScript type checker.
255
+ * Returns null if none found — callers should skip type checking gracefully.
256
+ *
257
+ * Checks:
258
+ * 1. tsgo binary (fastest — native TypeScript compiler)
259
+ * 2. npx tsgo (if installed as a package)
260
+ * 3. tsc binary (standard TypeScript compiler, verified via --version)
261
+ * 4. npx tsc — but ONLY if it's the real TypeScript compiler, not the
262
+ * unrelated `tsc` npm package that prints "This is not the tsc command
263
+ * you are looking for"
264
+ */
265
+ function findTypeChecker(): { cmd: string; args: string[] } | null {
266
+ // 1. tsgo binary
161
267
  const tsgo = spawnSync("which", ["tsgo"], { stdio: "pipe" });
162
268
  if (tsgo.status === 0) return { cmd: "tsgo", args: [] };
163
- const npxTsgo = spawnSync("npx", ["tsgo", "--version"], { stdio: "pipe", timeout: 5000 });
269
+
270
+ // 2. npx tsgo
271
+ const npxTsgo = spawnSync("npx", ["tsgo", "--version"], {
272
+ stdio: "pipe",
273
+ timeout: 5000,
274
+ });
164
275
  if (npxTsgo.status === 0) return { cmd: "npx", args: ["tsgo"] };
165
- return { cmd: "npx", args: ["tsc"] };
276
+
277
+ // 3. tsc binary (e.g. from `npm install -g typescript`)
278
+ const tscWhich = spawnSync("which", ["tsc"], { stdio: "pipe" });
279
+ if (tscWhich.status === 0) {
280
+ // Verify it's the real TypeScript compiler, not the bogus `tsc` npm package
281
+ const tscVersion = spawnSync("tsc", ["--version"], {
282
+ stdio: "pipe",
283
+ timeout: 5000,
284
+ });
285
+ const output = tscVersion.stdout?.toString() ?? "";
286
+ if (tscVersion.status === 0 && output.includes("Version")) {
287
+ return { cmd: "tsc", args: [] };
288
+ }
289
+ }
290
+
291
+ // 4. npx tsc — validate it's actually TypeScript
292
+ const npxTsc = spawnSync("npx", ["--no-install", "tsc", "--version"], {
293
+ stdio: "pipe",
294
+ timeout: 10000,
295
+ });
296
+ const npxOutput = npxTsc.stdout?.toString() ?? "";
297
+ if (npxTsc.status === 0 && npxOutput.includes("Version")) {
298
+ return { cmd: "npx", args: ["tsc"] };
299
+ }
300
+
301
+ // No type checker found
302
+ return null;
166
303
  }
@@ -436,18 +436,20 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
436
436
  return data;
437
437
  }
438
438
 
439
- /** Call an MCP server directly with a bearer token (bypasses registry). */
439
+ /** Call an MCP server directly (bypasses registry). */
440
440
  async function callMcpDirect(
441
441
  serverUrl: string,
442
442
  toolName: string,
443
443
  params: Record<string, unknown>,
444
444
  token: string,
445
+ extraHeaders?: Record<string, string>,
445
446
  ): Promise<CallAgentResponse> {
446
447
  const url = serverUrl.replace(/\/$/, "");
447
448
  const headers: Record<string, string> = {
448
449
  "Content-Type": "application/json",
449
450
  Accept: "application/json, text/event-stream",
450
- Authorization: `Bearer ${token}`,
451
+ ...(token && !extraHeaders && { Authorization: `Bearer ${token}` }),
452
+ ...extraHeaders,
451
453
  };
452
454
 
453
455
  let reqId = 0;
@@ -913,14 +915,31 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
913
915
  const entry = findRef(config.refs ?? [], name);
914
916
  if (!entry) throw new Error(`Ref "${name}" not found`);
915
917
 
916
- let accessToken = await readRefSecret(name, "access_token");
918
+ let accessToken = await readRefSecret(name, "access_token")
919
+ ?? await readRefSecret(name, "api_key")
920
+ ?? await readRefSecret(name, "token");
921
+
922
+ // Resolve custom headers from config (e.g. { "X-API-Key": "secret:..." })
923
+ const refConfig = (entry.config ?? {}) as Record<string, unknown>;
924
+ const rawHeaders = refConfig.headers as Record<string, string> | undefined;
925
+ let resolvedHeaders: Record<string, string> | undefined;
926
+ if (rawHeaders && typeof rawHeaders === 'object') {
927
+ resolvedHeaders = {};
928
+ for (const [k, v] of Object.entries(rawHeaders)) {
929
+ if (typeof v === 'string' && v.startsWith(SECRET_PREFIX) && options.encryptionKey) {
930
+ resolvedHeaders[k] = await decryptSecret(v.slice(SECRET_PREFIX.length), options.encryptionKey);
931
+ } else if (typeof v === 'string') {
932
+ resolvedHeaders[k] = v;
933
+ }
934
+ }
935
+ }
917
936
 
918
937
  const doCall = async (token: string | null) => {
919
938
  // Direct MCP only for redirect/proxy agents with an MCP upstream.
920
939
  // API-mode agents must go through the registry (it does REST translation).
921
940
  const agentMode = (entry as any).mode ?? 'redirect';
922
941
  if (token && entry.url && agentMode !== 'api') {
923
- return callMcpDirect(entry.url, tool, params ?? {}, token);
942
+ return callMcpDirect(entry.url, tool, params ?? {}, token, resolvedHeaders);
924
943
  }
925
944
 
926
945
  const consumer = await buildConsumerForRef(entry);
@@ -933,6 +952,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
933
952
  params: {
934
953
  ...(params ?? {}),
935
954
  ...(token && { accessToken: token }),
955
+ ...(resolvedHeaders && { _headers: resolvedHeaders }),
936
956
  },
937
957
  });
938
958
  };
@@ -382,10 +382,10 @@ export async function syncAllRefs(
382
382
  // ============================================
383
383
 
384
384
  /**
385
- * Generate a root `adk.d.ts` that populates `AdkAgentRegistry` via module
386
- * augmentation. When the registry is populated, `adk.ref.call()` switches
387
- * from the loose `(string, string)` fallback to a strict typed signature
388
- * that rejects unknown agent paths and tool names at compile time.
385
+ * Generate a root `adk.d.ts` with ambient type declarations.
386
+ * Declares `AdkAgentRegistry` as a global interface and types the `adk` global
387
+ * so `adk.ref.call()` gets strict typed signatures without requiring
388
+ * `@slashfi/agents-sdk` to be installed locally.
389
389
  *
390
390
  * Include in tsconfig or add: /// <reference path="~/.adk/adk.d.ts" />
391
391
  */
@@ -397,14 +397,8 @@ export function generateRootTypes(
397
397
  `// Auto-generated by adk sync — do not edit`,
398
398
  `// Run \`adk sync\` to regenerate`,
399
399
  `//`,
400
- `// Import in your entry file or use adk check/run:`,
401
- `// import '${configDir}/adk';`,
402
- `// adk check myfile.ts`,
403
- `//`,
404
- `// Then adk.ref.call() gets typed agent paths, tool names, and params.`,
405
- ``,
406
- `// Import the SDK to make this a module augmentation (not ambient declaration)`,
407
- `import '@slashfi/agents-sdk';`,
400
+ `// Fully ambient no external imports required.`,
401
+ `// Provides typed adk.ref.call() when used with adk check / adk run.`,
408
402
  ``,
409
403
  ];
410
404
 
@@ -430,16 +424,31 @@ export function generateRootTypes(
430
424
 
431
425
  if (typedRefs.length === 0) return;
432
426
 
433
- // --- Module augmentation: populate AdkAgentRegistry ---
434
- lines.push(`declare module "@slashfi/agents-sdk" {`);
435
- lines.push(` interface AdkAgentRegistry {`);
427
+ // --- Ambient declarations: populate AdkAgentRegistry ---
428
+ lines.push(`interface AdkAgentRegistry {`);
436
429
  for (const ref of typedRefs) {
437
430
  const relPath = `./refs/${ref.name}/types/${ref.name}`;
438
- lines.push(` ${JSON.stringify(ref.name)}: import(${JSON.stringify(relPath)}).${ref.interfaceName};`);
431
+ lines.push(` ${JSON.stringify(ref.name)}: import(${JSON.stringify(relPath)}).${ref.interfaceName};`);
439
432
  }
440
- lines.push(` }`);
441
433
  lines.push(`}`);
442
434
  lines.push(``);
443
435
 
436
+ // Add ambient helper types and global `adk` declaration
437
+ lines.push(`type _AdkAgentPath = keyof AdkAgentRegistry;`);
438
+ lines.push(`type _AdkToolsOf<A extends _AdkAgentPath> = keyof AdkAgentRegistry[A] & string;`);
439
+ lines.push(`type _AdkParamsOf<`);
440
+ lines.push(` A extends _AdkAgentPath,`);
441
+ lines.push(` T extends _AdkToolsOf<A>,`);
442
+ lines.push(`> = AdkAgentRegistry[A][T] extends { params: infer P } ? P : Record<string, unknown>;`);
443
+ lines.push(``);
444
+ lines.push(`declare const adk: {`);
445
+ lines.push(` ref: {`);
446
+ lines.push(` call<A extends _AdkAgentPath, T extends _AdkToolsOf<A>>(`);
447
+ lines.push(` name: A, tool: T, params: _AdkParamsOf<A, T>`);
448
+ lines.push(` ): Promise<unknown>;`);
449
+ lines.push(` };`);
450
+ lines.push(`};`);
451
+ lines.push(``);
452
+
444
453
  ensureWrite(join(configDir, "adk.d.ts"), lines.join("\n"));
445
454
  }