fix: make skill scripts work when installed via npm (#460)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
#!/usr/bin/env npx ts-node
|
||||
#!/usr/bin/env npx tsx
|
||||
/**
|
||||
* Find Agents - Search for agents with various filters
|
||||
*
|
||||
* This script is standalone and can be run outside the CLI process.
|
||||
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node find-agents.ts [options]
|
||||
* npx tsx find-agents.ts [options]
|
||||
*
|
||||
* Options:
|
||||
* --name <name> Exact name match
|
||||
@@ -17,9 +20,17 @@
|
||||
* Raw API response from GET /v1/agents
|
||||
*/
|
||||
|
||||
import type Letta from "@letta-ai/letta-client";
|
||||
import { getClient } from "../../../../agent/client";
|
||||
import { settingsManager } from "../../../../settings-manager";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
||||
// (ES module imports don't respect NODE_PATH, but require does)
|
||||
const require = createRequire(import.meta.url);
|
||||
const Letta = require("@letta-ai/letta-client")
|
||||
.default as typeof import("@letta-ai/letta-client").default;
|
||||
type LettaClient = InstanceType<typeof Letta>;
|
||||
|
||||
interface FindAgentsOptions {
|
||||
name?: string;
|
||||
@@ -30,6 +41,38 @@ interface FindAgentsOptions {
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API key from env var or settings file
|
||||
*/
|
||||
function getApiKey(): string {
|
||||
// First check env var (set by CLI's getShellEnv)
|
||||
if (process.env.LETTA_API_KEY) {
|
||||
return process.env.LETTA_API_KEY;
|
||||
}
|
||||
|
||||
// Fall back to settings file
|
||||
const settingsPath = join(homedir(), ".letta", "settings.json");
|
||||
try {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
if (settings.env?.LETTA_API_KEY) {
|
||||
return settings.env.LETTA_API_KEY;
|
||||
}
|
||||
} catch {
|
||||
// Settings file doesn't exist or is invalid
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Letta client with auth from env/settings
|
||||
*/
|
||||
function createClient(): LettaClient {
|
||||
return new Letta({ apiKey: getApiKey() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Find agents matching the given criteria
|
||||
* @param client - Letta client instance
|
||||
@@ -37,7 +80,7 @@ interface FindAgentsOptions {
|
||||
* @returns Array of agent objects from the API
|
||||
*/
|
||||
export async function findAgents(
|
||||
client: Letta,
|
||||
client: LettaClient,
|
||||
options: FindAgentsOptions = {},
|
||||
): Promise<Awaited<ReturnType<typeof client.agents.list>>> {
|
||||
const params: Parameters<typeof client.agents.list>[0] = {
|
||||
@@ -103,13 +146,13 @@ function parseArgs(args: string[]): FindAgentsOptions {
|
||||
return options;
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
// CLI entry point - check if this file is being run directly
|
||||
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
||||
if (isMainModule) {
|
||||
(async () => {
|
||||
try {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
await settingsManager.initialize();
|
||||
const client = await getClient();
|
||||
const client = createClient();
|
||||
const result = await findAgents(client, options);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
@@ -118,7 +161,7 @@ if (require.main === module) {
|
||||
error instanceof Error ? error.message : String(error),
|
||||
);
|
||||
console.error(`
|
||||
Usage: npx ts-node find-agents.ts [options]
|
||||
Usage: npx tsx find-agents.ts [options]
|
||||
|
||||
Options:
|
||||
--name <name> Exact name match
|
||||
|
||||
@@ -1,24 +1,79 @@
|
||||
#!/usr/bin/env npx ts-node
|
||||
#!/usr/bin/env npx tsx
|
||||
/**
|
||||
* Attach Block - Attaches an existing memory block to an agent (sharing)
|
||||
*
|
||||
* This script is standalone and can be run outside the CLI process.
|
||||
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
||||
* It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node attach-block.ts --block-id <block-id> --target-agent-id <agent-id> [--read-only]
|
||||
* npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only]
|
||||
*
|
||||
* This attaches an existing block to another agent, making it shared.
|
||||
* Changes to the block will be visible to all agents that have it attached.
|
||||
*
|
||||
* Options:
|
||||
* --read-only Target agent can read but not modify the block
|
||||
* --agent-id Target agent ID (overrides LETTA_AGENT_ID env var)
|
||||
* --read-only Target agent can read but not modify the block
|
||||
*
|
||||
* Output:
|
||||
* Raw API response from the attach operation
|
||||
*/
|
||||
|
||||
import type Letta from "@letta-ai/letta-client";
|
||||
import { getClient } from "../../../../agent/client";
|
||||
import { getCurrentAgentId } from "../../../../agent/context";
|
||||
import { settingsManager } from "../../../../settings-manager";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
||||
// (ES module imports don't respect NODE_PATH, but require does)
|
||||
const require = createRequire(import.meta.url);
|
||||
const Letta = require("@letta-ai/letta-client")
|
||||
.default as typeof import("@letta-ai/letta-client").default;
|
||||
type LettaClient = InstanceType<typeof Letta>;
|
||||
|
||||
/**
|
||||
* Get API key from env var or settings file
|
||||
*/
|
||||
function getApiKey(): string {
|
||||
if (process.env.LETTA_API_KEY) {
|
||||
return process.env.LETTA_API_KEY;
|
||||
}
|
||||
|
||||
const settingsPath = join(homedir(), ".letta", "settings.json");
|
||||
try {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
if (settings.env?.LETTA_API_KEY) {
|
||||
return settings.env.LETTA_API_KEY;
|
||||
}
|
||||
} catch {
|
||||
// Settings file doesn't exist or is invalid
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent ID from CLI arg, env var, or throw
|
||||
*/
|
||||
function getAgentId(cliArg?: string): string {
|
||||
if (cliArg) return cliArg;
|
||||
if (process.env.LETTA_AGENT_ID) {
|
||||
return process.env.LETTA_AGENT_ID;
|
||||
}
|
||||
throw new Error(
|
||||
"No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Letta client with auth from env/settings
|
||||
*/
|
||||
function createClient(): LettaClient {
|
||||
return new Letta({ apiKey: getApiKey() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach an existing block to the current agent (sharing it)
|
||||
@@ -29,13 +84,13 @@ import { settingsManager } from "../../../../settings-manager";
|
||||
* @returns API response from the attach operation
|
||||
*/
|
||||
export async function attachBlock(
|
||||
client: Letta,
|
||||
client: LettaClient,
|
||||
blockId: string,
|
||||
readOnly = false,
|
||||
targetAgentId?: string,
|
||||
): Promise<Awaited<ReturnType<typeof client.agents.blocks.attach>>> {
|
||||
// Get current agent ID (the agent calling this script) or use provided ID
|
||||
const currentAgentId = targetAgentId ?? getCurrentAgentId();
|
||||
const currentAgentId = getAgentId(targetAgentId);
|
||||
|
||||
const result = await client.agents.blocks.attach(blockId, {
|
||||
agent_id: currentAgentId,
|
||||
@@ -58,8 +113,10 @@ export async function attachBlock(
|
||||
function parseArgs(args: string[]): {
|
||||
blockId: string;
|
||||
readOnly: boolean;
|
||||
agentId?: string;
|
||||
} {
|
||||
const blockIdIndex = args.indexOf("--block-id");
|
||||
const agentIdIndex = args.indexOf("--agent-id");
|
||||
const readOnly = args.includes("--read-only");
|
||||
|
||||
if (blockIdIndex === -1 || blockIdIndex + 1 >= args.length) {
|
||||
@@ -69,17 +126,21 @@ function parseArgs(args: string[]): {
|
||||
return {
|
||||
blockId: args[blockIdIndex + 1] as string,
|
||||
readOnly,
|
||||
agentId:
|
||||
agentIdIndex !== -1 && agentIdIndex + 1 < args.length
|
||||
? (args[agentIdIndex + 1] as string)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
// CLI entry point - check if this file is being run directly
|
||||
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
||||
if (isMainModule) {
|
||||
(async () => {
|
||||
try {
|
||||
const { blockId, readOnly } = parseArgs(process.argv.slice(2));
|
||||
await settingsManager.initialize();
|
||||
const client = await getClient();
|
||||
const result = await attachBlock(client, blockId, readOnly);
|
||||
const { blockId, readOnly, agentId } = parseArgs(process.argv.slice(2));
|
||||
const client = createClient();
|
||||
const result = await attachBlock(client, blockId, readOnly, agentId);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
@@ -91,7 +152,7 @@ if (require.main === module) {
|
||||
error.message.includes("Missing required argument")
|
||||
) {
|
||||
console.error(
|
||||
"\nUsage: npx ts-node attach-block.ts --block-id <block-id> [--read-only]",
|
||||
"\nUsage: npx tsx attach-block.ts --block-id <block-id> [--agent-id <agent-id>] [--read-only]",
|
||||
);
|
||||
}
|
||||
process.exit(1);
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#!/usr/bin/env npx ts-node
|
||||
#!/usr/bin/env npx tsx
|
||||
/**
|
||||
* Copy Block - Copies a memory block to create a new independent block for the current agent
|
||||
*
|
||||
* This script is standalone and can be run outside the CLI process.
|
||||
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
||||
* It reads agent ID from LETTA_AGENT_ID env var or --agent-id arg.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node copy-block.ts --block-id <block-id> [--label <new-label>]
|
||||
* npx tsx copy-block.ts --block-id <block-id> [--label <new-label>] [--agent-id <agent-id>]
|
||||
*
|
||||
* Options:
|
||||
* --label Override the block label (required if you already have a block with that label)
|
||||
* --label Override the block label (required if you already have a block with that label)
|
||||
* --agent-id Target agent ID (overrides LETTA_AGENT_ID env var)
|
||||
*
|
||||
* This creates a new block with the same content as the source block,
|
||||
* then attaches it to the current agent. Changes to the new block
|
||||
@@ -16,17 +21,65 @@
|
||||
* Raw API response from each step (retrieve, create, attach)
|
||||
*/
|
||||
|
||||
import type Letta from "@letta-ai/letta-client";
|
||||
import { getClient } from "../../../../agent/client";
|
||||
import { getCurrentAgentId } from "../../../../agent/context";
|
||||
import { settingsManager } from "../../../../settings-manager";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
||||
// (ES module imports don't respect NODE_PATH, but require does)
|
||||
const require = createRequire(import.meta.url);
|
||||
const Letta = require("@letta-ai/letta-client")
|
||||
.default as typeof import("@letta-ai/letta-client").default;
|
||||
type LettaClient = InstanceType<typeof Letta>;
|
||||
|
||||
interface CopyBlockResult {
|
||||
sourceBlock: Awaited<ReturnType<typeof Letta.prototype.blocks.retrieve>>;
|
||||
newBlock: Awaited<ReturnType<typeof Letta.prototype.blocks.create>>;
|
||||
attachResult: Awaited<
|
||||
ReturnType<typeof Letta.prototype.agents.blocks.attach>
|
||||
>;
|
||||
sourceBlock: Awaited<ReturnType<LettaClient["blocks"]["retrieve"]>>;
|
||||
newBlock: Awaited<ReturnType<LettaClient["blocks"]["create"]>>;
|
||||
attachResult: Awaited<ReturnType<LettaClient["agents"]["blocks"]["attach"]>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API key from env var or settings file
|
||||
*/
|
||||
function getApiKey(): string {
|
||||
if (process.env.LETTA_API_KEY) {
|
||||
return process.env.LETTA_API_KEY;
|
||||
}
|
||||
|
||||
const settingsPath = join(homedir(), ".letta", "settings.json");
|
||||
try {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
if (settings.env?.LETTA_API_KEY) {
|
||||
return settings.env.LETTA_API_KEY;
|
||||
}
|
||||
} catch {
|
||||
// Settings file doesn't exist or is invalid
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get agent ID from CLI arg, env var, or throw
|
||||
*/
|
||||
function getAgentId(cliArg?: string): string {
|
||||
if (cliArg) return cliArg;
|
||||
if (process.env.LETTA_AGENT_ID) {
|
||||
return process.env.LETTA_AGENT_ID;
|
||||
}
|
||||
throw new Error(
|
||||
"No agent ID provided. Use --agent-id or ensure LETTA_AGENT_ID env var is set.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Letta client with auth from env/settings
|
||||
*/
|
||||
function createClient(): LettaClient {
|
||||
return new Letta({ apiKey: getApiKey() });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,12 +90,12 @@ interface CopyBlockResult {
|
||||
* @returns Object containing source block, new block, and attach result
|
||||
*/
|
||||
export async function copyBlock(
|
||||
client: Letta,
|
||||
client: LettaClient,
|
||||
blockId: string,
|
||||
options?: { labelOverride?: string; targetAgentId?: string },
|
||||
): Promise<CopyBlockResult> {
|
||||
// Get current agent ID (the agent calling this script) or use provided ID
|
||||
const currentAgentId = options?.targetAgentId ?? getCurrentAgentId();
|
||||
const currentAgentId = getAgentId(options?.targetAgentId);
|
||||
|
||||
// 1. Get source block details
|
||||
const sourceBlock = await client.blocks.retrieve(blockId);
|
||||
@@ -63,9 +116,14 @@ export async function copyBlock(
|
||||
return { sourceBlock, newBlock, attachResult };
|
||||
}
|
||||
|
||||
function parseArgs(args: string[]): { blockId: string; label?: string } {
|
||||
function parseArgs(args: string[]): {
|
||||
blockId: string;
|
||||
label?: string;
|
||||
agentId?: string;
|
||||
} {
|
||||
const blockIdIndex = args.indexOf("--block-id");
|
||||
const labelIndex = args.indexOf("--label");
|
||||
const agentIdIndex = args.indexOf("--agent-id");
|
||||
|
||||
if (blockIdIndex === -1 || blockIdIndex + 1 >= args.length) {
|
||||
throw new Error("Missing required argument: --block-id <block-id>");
|
||||
@@ -77,17 +135,24 @@ function parseArgs(args: string[]): { blockId: string; label?: string } {
|
||||
labelIndex !== -1 && labelIndex + 1 < args.length
|
||||
? (args[labelIndex + 1] as string)
|
||||
: undefined,
|
||||
agentId:
|
||||
agentIdIndex !== -1 && agentIdIndex + 1 < args.length
|
||||
? (args[agentIdIndex + 1] as string)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
// CLI entry point - check if this file is being run directly
|
||||
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
||||
if (isMainModule) {
|
||||
(async () => {
|
||||
try {
|
||||
const { blockId, label } = parseArgs(process.argv.slice(2));
|
||||
await settingsManager.initialize();
|
||||
const client = await getClient();
|
||||
const result = await copyBlock(client, blockId, { labelOverride: label });
|
||||
const { blockId, label, agentId } = parseArgs(process.argv.slice(2));
|
||||
const client = createClient();
|
||||
const result = await copyBlock(client, blockId, {
|
||||
labelOverride: label,
|
||||
targetAgentId: agentId,
|
||||
});
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
console.error(
|
||||
@@ -99,7 +164,7 @@ if (require.main === module) {
|
||||
error.message.includes("Missing required argument")
|
||||
) {
|
||||
console.error(
|
||||
"\nUsage: npx ts-node copy-block.ts --block-id <block-id> [--label <new-label>]",
|
||||
"\nUsage: npx tsx copy-block.ts --block-id <block-id> [--label <new-label>] [--agent-id <agent-id>]",
|
||||
);
|
||||
}
|
||||
process.exit(1);
|
||||
|
||||
@@ -1,17 +1,58 @@
|
||||
#!/usr/bin/env npx ts-node
|
||||
#!/usr/bin/env npx tsx
|
||||
/**
|
||||
* Get Agent Blocks - Retrieves memory blocks from a specific agent
|
||||
*
|
||||
* This script is standalone and can be run outside the CLI process.
|
||||
* It reads auth from LETTA_API_KEY env var or ~/.letta/settings.json.
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node get-agent-blocks.ts --agent-id <agent-id>
|
||||
* npx tsx get-agent-blocks.ts --agent-id <agent-id>
|
||||
*
|
||||
* Output:
|
||||
* Raw API response from GET /v1/agents/{id}/core-memory/blocks
|
||||
*/
|
||||
|
||||
import type Letta from "@letta-ai/letta-client";
|
||||
import { getClient } from "../../../../agent/client";
|
||||
import { settingsManager } from "../../../../settings-manager";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
||||
// (ES module imports don't respect NODE_PATH, but require does)
|
||||
const require = createRequire(import.meta.url);
|
||||
const Letta = require("@letta-ai/letta-client")
|
||||
.default as typeof import("@letta-ai/letta-client").default;
|
||||
type LettaClient = InstanceType<typeof Letta>;
|
||||
|
||||
/**
|
||||
* Get API key from env var or settings file
|
||||
*/
|
||||
function getApiKey(): string {
|
||||
if (process.env.LETTA_API_KEY) {
|
||||
return process.env.LETTA_API_KEY;
|
||||
}
|
||||
|
||||
const settingsPath = join(homedir(), ".letta", "settings.json");
|
||||
try {
|
||||
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
||||
if (settings.env?.LETTA_API_KEY) {
|
||||
return settings.env.LETTA_API_KEY;
|
||||
}
|
||||
} catch {
|
||||
// Settings file doesn't exist or is invalid
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
"No LETTA_API_KEY found. Set the env var or run the Letta CLI to authenticate.",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Letta client with auth from env/settings
|
||||
*/
|
||||
function createClient(): LettaClient {
|
||||
return new Letta({ apiKey: getApiKey() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory blocks for a specific agent
|
||||
@@ -20,7 +61,7 @@ import { settingsManager } from "../../../../settings-manager";
|
||||
* @returns Array of block objects from the API
|
||||
*/
|
||||
export async function getAgentBlocks(
|
||||
client: Letta,
|
||||
client: LettaClient,
|
||||
agentId: string,
|
||||
): Promise<Awaited<ReturnType<typeof client.agents.blocks.list>>> {
|
||||
return await client.agents.blocks.list(agentId);
|
||||
@@ -34,13 +75,13 @@ function parseArgs(args: string[]): { agentId: string } {
|
||||
return { agentId: args[agentIdIndex + 1] as string };
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
// CLI entry point - check if this file is being run directly
|
||||
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
||||
if (isMainModule) {
|
||||
(async () => {
|
||||
try {
|
||||
const { agentId } = parseArgs(process.argv.slice(2));
|
||||
await settingsManager.initialize();
|
||||
const client = await getClient();
|
||||
const client = createClient();
|
||||
const result = await getAgentBlocks(client, agentId);
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} catch (error) {
|
||||
@@ -53,7 +94,7 @@ if (require.main === module) {
|
||||
error.message.includes("Missing required argument")
|
||||
) {
|
||||
console.error(
|
||||
"\nUsage: npx ts-node get-agent-blocks.ts --agent-id <agent-id>",
|
||||
"\nUsage: npx tsx get-agent-blocks.ts --agent-id <agent-id>",
|
||||
);
|
||||
}
|
||||
process.exit(1);
|
||||
|
||||
@@ -26,9 +26,16 @@
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import Letta from "@letta-ai/letta-client";
|
||||
|
||||
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
||||
// (ES module imports don't respect NODE_PATH, but require does)
|
||||
const require = createRequire(import.meta.url);
|
||||
const Letta = require("@letta-ai/letta-client")
|
||||
.default as typeof import("@letta-ai/letta-client").default;
|
||||
type LettaClient = InstanceType<typeof Letta>;
|
||||
|
||||
interface GetMessagesOptions {
|
||||
startDate?: string;
|
||||
@@ -85,7 +92,7 @@ function getAgentId(cliArg?: string): string {
|
||||
/**
|
||||
* Create a Letta client with auth from env/settings
|
||||
*/
|
||||
function createClient(): Letta {
|
||||
function createClient(): LettaClient {
|
||||
return new Letta({ apiKey: getApiKey() });
|
||||
}
|
||||
|
||||
@@ -96,7 +103,7 @@ function createClient(): Letta {
|
||||
* @returns Array of messages in chronological order
|
||||
*/
|
||||
export async function getMessages(
|
||||
client: Letta,
|
||||
client: LettaClient,
|
||||
options: GetMessagesOptions = {},
|
||||
): Promise<unknown[]> {
|
||||
const agentId = getAgentId(options.agentId);
|
||||
|
||||
@@ -24,9 +24,16 @@
|
||||
*/
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import { homedir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import Letta from "@letta-ai/letta-client";
|
||||
|
||||
// Use createRequire for @letta-ai/letta-client so NODE_PATH is respected
|
||||
// (ES module imports don't respect NODE_PATH, but require does)
|
||||
const require = createRequire(import.meta.url);
|
||||
const Letta = require("@letta-ai/letta-client")
|
||||
.default as typeof import("@letta-ai/letta-client").default;
|
||||
type LettaClient = InstanceType<typeof Letta>;
|
||||
|
||||
interface SearchMessagesOptions {
|
||||
query: string;
|
||||
@@ -83,7 +90,7 @@ function getAgentId(cliArg?: string): string {
|
||||
/**
|
||||
* Create a Letta client with auth from env/settings
|
||||
*/
|
||||
function createClient(): Letta {
|
||||
function createClient(): LettaClient {
|
||||
return new Letta({ apiKey: getApiKey() });
|
||||
}
|
||||
|
||||
@@ -94,7 +101,7 @@ function createClient(): Letta {
|
||||
* @returns Array of search results with scores
|
||||
*/
|
||||
export async function searchMessages(
|
||||
client: Letta,
|
||||
client: LettaClient,
|
||||
options: SearchMessagesOptions,
|
||||
): Promise<Awaited<ReturnType<typeof client.messages.search>>> {
|
||||
// Default to current agent unless --all-agents is specified
|
||||
|
||||
@@ -26,6 +26,24 @@ function getRipgrepBinDir(): string | undefined {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node_modules directory containing this package's dependencies.
|
||||
* Skill scripts use createRequire with NODE_PATH to resolve dependencies.
|
||||
*/
|
||||
function getPackageNodeModulesDir(): string | undefined {
|
||||
try {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const require = createRequire(__filename);
|
||||
// Find where letta-client is installed
|
||||
const clientPath = require.resolve("@letta-ai/letta-client");
|
||||
// Extract node_modules path: /a/b/node_modules/@letta-ai/letta-client/... -> /a/b/node_modules
|
||||
const match = clientPath.match(/^(.+[/\\]node_modules)[/\\]/);
|
||||
return match ? match[1] : undefined;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get enhanced environment variables for shell execution.
|
||||
* Includes bundled tools (like ripgrep) in PATH and Letta context for skill scripts.
|
||||
@@ -59,5 +77,15 @@ export function getShellEnv(): NodeJS.ProcessEnv {
|
||||
}
|
||||
}
|
||||
|
||||
// Add NODE_PATH for skill scripts to resolve @letta-ai/letta-client
|
||||
// ES modules don't respect NODE_PATH, but createRequire does
|
||||
const nodeModulesDir = getPackageNodeModulesDir();
|
||||
if (nodeModulesDir) {
|
||||
const currentNodePath = env.NODE_PATH || "";
|
||||
env.NODE_PATH = currentNodePath
|
||||
? `${nodeModulesDir}${path.delimiter}${currentNodePath}`
|
||||
: nodeModulesDir;
|
||||
}
|
||||
|
||||
return env;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user