agency-lang 0.0.59 → 0.0.60

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 CHANGED
@@ -22,10 +22,10 @@ agency run infile.agency
22
22
  agency infile.agency
23
23
  ```
24
24
 
25
- Note that the generated files use several other libraries that you will need to install as well:
25
+ Note that the generated files use a couple of other libraries that you will need to install as well:
26
26
 
27
27
  ```bash
28
- pnpm i nanoid openai zod smoltalk
28
+ pnpm i nanoid zod smoltalk
29
29
  ```
30
30
 
31
31
  ## troubleshooting
@@ -5,7 +5,6 @@ export declare function readStdin(): Promise<string>;
5
5
  export declare function parse(contents: string, config: AgencyConfig): AgencyProgram;
6
6
  export declare function readFile(inputFile: string): string;
7
7
  export declare function renderGraph(contents: string, config: AgencyConfig): void;
8
- export declare function getImports(program: AgencyProgram): string[];
9
8
  export declare function compile(config: AgencyConfig, inputFile: string, _outputFile?: string): string | null;
10
9
  export declare function run(config: AgencyConfig, inputFile: string, outputFile?: string): void;
11
10
  export declare function format(contents: string, config: AgencyConfig): Promise<string>;
@@ -6,7 +6,7 @@ import { spawn } from "child_process";
6
6
  import * as fs from "fs";
7
7
  import * as path from "path";
8
8
  import { parseAgency } from "../parser.js";
9
- import { findRecursively } from "./util.js";
9
+ import { findRecursively, getImports } from "./util.js";
10
10
  // Load configuration from agency.json
11
11
  export function loadConfig(configPath, verbose = false) {
12
12
  let config = {};
@@ -83,17 +83,6 @@ export function renderGraph(contents, config) {
83
83
  console.log(subgraph);
84
84
  });
85
85
  }
86
- export function getImports(program) {
87
- const toolAndNodeImports = program.nodes
88
- .filter((node) => node.type === "importNodeStatement" ||
89
- node.type === "importToolStatement")
90
- .map((node) => node.agencyFile.trim());
91
- // this makes compile() try to parse non-agency files
92
- const importStatements = program.nodes
93
- .filter((node) => node.type === "importStatement" && node.modulePath.endsWith(".agency"))
94
- .map((node) => node.modulePath.trim());
95
- return [...toolAndNodeImports, ...importStatements];
96
- }
97
86
  const compiledFiles = new Set();
98
87
  export function compile(config, inputFile, _outputFile) {
99
88
  // Check if the input is a directory
@@ -0,0 +1,3 @@
1
+ import { AgencyConfig } from "../config.js";
2
+ import { Result } from "../types/result.js";
3
+ export declare function remoteRun(config: AgencyConfig, filename: string): Promise<Result<any>>;
@@ -0,0 +1,23 @@
1
+ import { getStatelogClient } from "../statelogClient.js";
2
+ import { getImportsRecursively } from "./util.js";
3
+ import fs from "fs";
4
+ export async function remoteRun(config, filename) {
5
+ const client = getStatelogClient({
6
+ host: config.log?.host || "http://localhost:1065",
7
+ projectId: config.log?.projectId || "agency-lang",
8
+ debugMode: config.log?.debugMode || false,
9
+ });
10
+ const imports = getImportsRecursively(filename);
11
+ const allFiles = [filename, ...imports];
12
+ const files = allFiles.map((file) => ({
13
+ name: file,
14
+ contents: fs.readFileSync(file, "utf-8"),
15
+ }));
16
+ const result = await client.remoteRun({
17
+ files,
18
+ entrypoint: filename,
19
+ args: [],
20
+ });
21
+ console.log(JSON.stringify(result));
22
+ return result;
23
+ }
@@ -0,0 +1,3 @@
1
+ import { AgencyConfig } from "../config.js";
2
+ import { UploadResult } from "../statelogClient.js";
3
+ export declare function upload(config: AgencyConfig, inputFile: string): Promise<UploadResult>;
@@ -0,0 +1,50 @@
1
+ import { getStatelogClient, mergeUploadResults, } from "../statelogClient.js";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import { findRecursively, getImportsRecursively } from "./util.js";
5
+ export async function upload(config, inputFile) {
6
+ // Check if the input is a directory
7
+ const stats = fs.statSync(inputFile);
8
+ const verbose = config.verbose ?? false;
9
+ if (stats.isDirectory()) {
10
+ const results = [];
11
+ for (const { path } of findRecursively(inputFile)) {
12
+ const result = await upload(config, path);
13
+ results.push(result);
14
+ }
15
+ const allResults = mergeUploadResults(results);
16
+ if (!allResults.success) {
17
+ console.log("Failure uploading files", allResults.error);
18
+ }
19
+ else {
20
+ console.log("Successfully uploaded files to Statelog");
21
+ console.log(allResults.value);
22
+ }
23
+ return allResults;
24
+ }
25
+ const imports = getImportsRecursively(inputFile);
26
+ const allFiles = [inputFile, ...imports];
27
+ const files = allFiles.map((file) => ({
28
+ name: path.relative(process.cwd(), file),
29
+ contents: fs.readFileSync(file, "utf-8"),
30
+ }));
31
+ const client = getStatelogClient({
32
+ host: config.log?.host || "http://localhost:1065",
33
+ projectId: config.log?.projectId || "agency-lang",
34
+ debugMode: config.log?.debugMode || false,
35
+ });
36
+ console.log(files);
37
+ const result = await client.upload({
38
+ projectId: config.log?.projectId || "agency-lang",
39
+ entrypoint: inputFile,
40
+ files,
41
+ });
42
+ if (!result.success) {
43
+ console.log("Failure uploading files", result.error);
44
+ }
45
+ else {
46
+ console.log("Successfully uploaded files to Statelog");
47
+ console.log(result.value);
48
+ }
49
+ return result;
50
+ }
@@ -1,4 +1,4 @@
1
- import { GraphNodeDefinition, VariableType } from "../types.js";
1
+ import { AgencyProgram, GraphNodeDefinition, VariableType } from "../types.js";
2
2
  import { AgencyConfig } from "../config.js";
3
3
  export declare function parseTarget(target: string): {
4
4
  filename: string;
@@ -40,3 +40,5 @@ export declare function executeJudge(actualOutput: string, expectedOutput: strin
40
40
  export declare function findRecursively(dirName: string, ext?: string, searched?: string[]): Generator<{
41
41
  path: string;
42
42
  }>;
43
+ export declare function getImportsRecursively(filename: string, visited?: Set<string>): string[];
44
+ export declare function getImports(program: AgencyProgram): string[];
@@ -5,6 +5,7 @@ import { execSync } from "child_process";
5
5
  import renderEvaluate from "../templates/cli/evaluate.js";
6
6
  import renderJudgeEvaluate from "../templates/cli/judgeEvaluate.js";
7
7
  import { compile } from "./commands.js";
8
+ import { parseAgency } from "../parser.js";
8
9
  export function parseTarget(target) {
9
10
  const colonIndex = target.lastIndexOf(":");
10
11
  if (colonIndex === -1) {
@@ -179,3 +180,38 @@ export function* findRecursively(dirName, ext = ".agency", searched = []) {
179
180
  }
180
181
  }
181
182
  }
183
+ export function getImportsRecursively(filename, visited = new Set()) {
184
+ if (visited.has(filename)) {
185
+ return [];
186
+ }
187
+ visited.add(filename);
188
+ const contents = fs.readFileSync(filename, "utf-8");
189
+ const parsed = parseAgency(contents, { verbose: false });
190
+ if (!parsed.success) {
191
+ console.error(`Error parsing ${filename}:`, parsed);
192
+ return [];
193
+ }
194
+ const program = parsed.result;
195
+ const imports = getImports(program);
196
+ for (const imp of imports) {
197
+ const importedFile = path.resolve(path.dirname(filename), imp);
198
+ if (fs.existsSync(importedFile)) {
199
+ imports.push(...getImportsRecursively(importedFile, visited));
200
+ }
201
+ else {
202
+ console.warn(`Warning: Imported file ${importedFile} not found.`);
203
+ }
204
+ }
205
+ return imports;
206
+ }
207
+ export function getImports(program) {
208
+ const toolAndNodeImports = program.nodes
209
+ .filter((node) => node.type === "importNodeStatement" ||
210
+ node.type === "importToolStatement")
211
+ .map((node) => node.agencyFile.trim());
212
+ // this makes compile() try to parse non-agency files
213
+ const importStatements = program.nodes
214
+ .filter((node) => node.type === "importStatement" && node.modulePath.endsWith(".agency"))
215
+ .map((node) => node.modulePath.trim());
216
+ return [...toolAndNodeImports, ...importStatements];
217
+ }
@@ -3,3 +3,4 @@ export * from "./types.js";
3
3
  export * from "./backends/index.js";
4
4
  export * from "./simplemachine/index.js";
5
5
  export * from "./statelogClient.js";
6
+ export * from "smoltalk";
package/dist/lib/index.js CHANGED
@@ -3,3 +3,4 @@ export * from "./types.js";
3
3
  export * from "./backends/index.js";
4
4
  export * from "./simplemachine/index.js";
5
5
  export * from "./statelogClient.js";
6
+ export * from "smoltalk";
@@ -1,4 +1,5 @@
1
1
  import { BUILTIN_FUNCTIONS, BUILTIN_FUNCTIONS_TO_ASYNC, } from "../config.js";
2
+ import { getImportedNames, } from "../types.js";
2
3
  import { getAllVariablesInBody, walkNodes } from "../utils/node.js";
3
4
  export class TypescriptPreprocessor {
4
5
  program;
@@ -926,8 +927,10 @@ export class TypescriptPreprocessor {
926
927
  }
927
928
  }
928
929
  else if (node.type === "importStatement") {
929
- // todo imported names need to get parsed better,
930
- // into an array
930
+ const importedNames = node.importedNames.map(getImportedNames).flat();
931
+ importedNames.forEach((n) => {
932
+ varNameToScope[n] = "global";
933
+ });
931
934
  }
932
935
  else if (node.type === "importNodeStatement") {
933
936
  node.importedNodes.forEach((n) => {
@@ -1,4 +1,13 @@
1
1
  import { JSONEdge } from "./types.js";
2
+ import { Result } from "./types/result.js";
3
+ export type AgencyFile = {
4
+ name: string;
5
+ contents: string;
6
+ };
7
+ export type UploadResult = Result<{
8
+ endpointUrls: string[];
9
+ }>;
10
+ export declare function mergeUploadResults(_results: UploadResult[]): UploadResult;
2
11
  export declare class StatelogClient {
3
12
  private host;
4
13
  private debugMode;
@@ -70,5 +79,28 @@ export declare class StatelogClient {
70
79
  model?: string;
71
80
  timeTaken?: number;
72
81
  }): Promise<void>;
82
+ upload({ projectId, entrypoint, files, }: {
83
+ projectId: string;
84
+ entrypoint: string;
85
+ files: AgencyFile[];
86
+ }): Promise<UploadResult>;
87
+ remoteRun({ files, entrypoint, args, }: {
88
+ files: AgencyFile[];
89
+ entrypoint: string;
90
+ args?: any[];
91
+ }): Promise<Result<any>>;
92
+ hitServer({ userId, projectId, filename, nodeName, body, }: {
93
+ userId: string;
94
+ projectId: string;
95
+ filename: string;
96
+ nodeName: string;
97
+ body: string;
98
+ }): Promise<Result<any>>;
73
99
  post(body: Record<string, any>): Promise<void>;
74
100
  }
101
+ export declare function getStatelogClient(config: {
102
+ host: string;
103
+ traceId?: string;
104
+ projectId: string;
105
+ debugMode?: boolean;
106
+ }): StatelogClient;
@@ -1,4 +1,16 @@
1
1
  import { nanoid } from "nanoid";
2
+ import { failure, success } from "./types/result.js";
3
+ import { mergeResults } from "smoltalk";
4
+ export function mergeUploadResults(_results) {
5
+ const results = mergeResults(_results);
6
+ if (!results.success) {
7
+ return failure(results.error);
8
+ }
9
+ const endpointUrls = results.value.flatMap((r) => r.endpointUrls);
10
+ return success({
11
+ endpointUrls,
12
+ });
13
+ }
2
14
  export class StatelogClient {
3
15
  host;
4
16
  debugMode;
@@ -106,6 +118,128 @@ export class StatelogClient {
106
118
  timeTaken,
107
119
  });
108
120
  }
121
+ async upload({ projectId, entrypoint, files, }) {
122
+ try {
123
+ const fullUrl = new URL(`/api/projects/${projectId}/upload`, this.host);
124
+ const url = fullUrl.toString();
125
+ const postBody = JSON.stringify({ entrypoint, files });
126
+ console.log({ entrypoint, files }, postBody);
127
+ const result = await fetch(url, {
128
+ method: "POST",
129
+ headers: {
130
+ "Content-Type": "application/json",
131
+ Authorization: `Bearer ${this.apiKey}`,
132
+ },
133
+ body: postBody,
134
+ }).catch((err) => {
135
+ if (this.debugMode)
136
+ console.error("Failed to send statelog:", err);
137
+ });
138
+ if (result) {
139
+ if (!result.ok) {
140
+ if (this.debugMode)
141
+ console.error("Failed to upload files to statelog:", {
142
+ result,
143
+ url,
144
+ files,
145
+ });
146
+ return failure("Failed to upload files to statelog");
147
+ }
148
+ return (await result.json());
149
+ }
150
+ }
151
+ catch (err) {
152
+ if (this.debugMode)
153
+ console.error("Error sending log in statelog client:", err, {
154
+ host: this.host,
155
+ });
156
+ }
157
+ return failure("Error uploading files to statelog");
158
+ }
159
+ async remoteRun({ files, entrypoint, args, }) {
160
+ try {
161
+ const fullUrl = new URL(`/api/run`, this.host);
162
+ const url = fullUrl.toString();
163
+ const body = JSON.stringify({
164
+ files,
165
+ entrypoint,
166
+ args,
167
+ });
168
+ console.log({ entrypoint, args }, body);
169
+ const result = await fetch(url, {
170
+ method: "POST",
171
+ headers: {
172
+ "Content-Type": "application/json",
173
+ Authorization: `Bearer ${this.apiKey}`,
174
+ },
175
+ body,
176
+ }).catch((err) => {
177
+ if (this.debugMode)
178
+ console.error("Failed to run on statelog:", err);
179
+ });
180
+ if (result) {
181
+ if (!result.ok) {
182
+ if (this.debugMode) {
183
+ const responseBody = await result.text();
184
+ console.error("Failed to run on statelog:", {
185
+ result,
186
+ url,
187
+ body,
188
+ responseBody,
189
+ });
190
+ }
191
+ return failure("Failed to run on statelog");
192
+ }
193
+ return (await result.json());
194
+ }
195
+ }
196
+ catch (err) {
197
+ if (this.debugMode)
198
+ console.error("Error running on statelog client:", err, {
199
+ host: this.host,
200
+ });
201
+ }
202
+ return failure("Error running on statelog");
203
+ }
204
+ async hitServer({ userId, projectId, filename, nodeName, body, }) {
205
+ try {
206
+ const fullUrl = new URL(`/run/${userId}/${projectId}/${filename}/${nodeName}`, this.host);
207
+ const url = fullUrl.toString();
208
+ const result = await fetch(url, {
209
+ method: "POST",
210
+ headers: {
211
+ "Content-Type": "application/json",
212
+ Authorization: `Bearer ${this.apiKey}`,
213
+ },
214
+ body: body,
215
+ }).catch((err) => {
216
+ if (this.debugMode)
217
+ console.error("Failed to run on statelog:", err);
218
+ });
219
+ if (result) {
220
+ if (!result.ok) {
221
+ if (this.debugMode) {
222
+ const responseBody = await result.text();
223
+ console.error("Failed to run on statelog:", {
224
+ result,
225
+ url,
226
+ body,
227
+ responseBody,
228
+ });
229
+ }
230
+ return failure("Failed to run on statelog");
231
+ }
232
+ return (await result.json());
233
+ }
234
+ }
235
+ catch (err) {
236
+ if (this.debugMode)
237
+ console.error("Error running on statelog client:", err, {
238
+ host: this.host,
239
+ });
240
+ }
241
+ return failure("Error running on statelog");
242
+ }
109
243
  async post(body) {
110
244
  if (!this.host) {
111
245
  return;
@@ -142,3 +276,14 @@ export class StatelogClient {
142
276
  }
143
277
  }
144
278
  }
279
+ export function getStatelogClient(config) {
280
+ const statelogConfig = {
281
+ host: config.host,
282
+ traceId: config.traceId || nanoid(),
283
+ apiKey: process.env.STATELOG_API_KEY || "",
284
+ projectId: config.projectId,
285
+ debugMode: config.debugMode || false,
286
+ };
287
+ const client = new StatelogClient(statelogConfig);
288
+ return client;
289
+ }
@@ -0,0 +1,13 @@
1
+ export type Success<T> = {
2
+ success: true;
3
+ value: T;
4
+ };
5
+ export type Failure = {
6
+ success: false;
7
+ error: string;
8
+ };
9
+ export type Result<T> = Success<T> | Failure;
10
+ export declare function success<T>(value: T): Success<T>;
11
+ export declare function failure(error: string): Failure;
12
+ export declare function mergeResults<T>(results: Result<T>[]): Result<T[]>;
13
+ export declare function resultMap<T, U>(result: Result<T>, fn: (value: T) => U): Result<U>;
@@ -0,0 +1,30 @@
1
+ export function success(value) {
2
+ return { success: true, value };
3
+ }
4
+ export function failure(error) {
5
+ return { success: false, error };
6
+ }
7
+ export function mergeResults(results) {
8
+ const values = [];
9
+ const failures = [];
10
+ for (const result of results) {
11
+ if (!result.success) {
12
+ failures.push(result.error);
13
+ }
14
+ else {
15
+ values.push(result.value);
16
+ }
17
+ }
18
+ if (failures.length > 0) {
19
+ return failure(failures.join(", "));
20
+ }
21
+ return success(values);
22
+ }
23
+ export function resultMap(result, fn) {
24
+ if (result.success) {
25
+ return success(fn(result.value));
26
+ }
27
+ else {
28
+ return result;
29
+ }
30
+ }
@@ -0,0 +1,3 @@
1
+ export declare const __dirname: string;
2
+ export declare const rootDir: string;
3
+ export declare function loadEnv(): void;
@@ -0,0 +1,30 @@
1
+ // parse .env file if it exists into process.env
2
+ import { existsSync, readFileSync } from "fs";
3
+ import { resolve, join } from "path";
4
+ export const __dirname = import.meta.dirname;
5
+ export const rootDir = join(__dirname, "../../");
6
+ export function loadEnv() {
7
+ const envfiles = [
8
+ resolve(rootDir, "..", ".env.local"),
9
+ resolve(rootDir, "..", ".env"),
10
+ ];
11
+ for (const envfile of envfiles) {
12
+ if (tryEnvFile(envfile)) {
13
+ break;
14
+ }
15
+ }
16
+ }
17
+ function tryEnvFile(envfile) {
18
+ if (existsSync(envfile)) {
19
+ const env = readFileSync(envfile, "utf-8");
20
+ env.split("\n").forEach((line) => {
21
+ if (line.trim() === "" || line.startsWith("#"))
22
+ return;
23
+ const [key, value] = line.split("=");
24
+ process.env[key] = value;
25
+ // console.log("Setting", color.yellow(key), "to", color.yellow(value));
26
+ });
27
+ return true;
28
+ }
29
+ return false;
30
+ }
@@ -9,6 +9,10 @@ import { Command } from "commander";
9
9
  import * as fs from "fs";
10
10
  import { TarsecError } from "tarsec";
11
11
  import process from "process";
12
+ import { upload } from "../lib/cli/upload.js";
13
+ import { loadEnv } from "../lib/utils/envfile.js";
14
+ import { remoteRun } from "../lib/cli/remoteRun.js";
15
+ loadEnv();
12
16
  const program = new Command();
13
17
  program
14
18
  .name("agency")
@@ -221,6 +225,27 @@ program
221
225
  if (hasErrors)
222
226
  process.exit(1);
223
227
  });
228
+ program
229
+ .command("upload")
230
+ .alias("up")
231
+ .alias("deploy")
232
+ .description("Upload files to Statelog")
233
+ .argument("[inputs...]", "Paths to .test.json files or directories")
234
+ .action(async (testFile) => {
235
+ console.log("Uploading", testFile);
236
+ for (const file of testFile) {
237
+ await upload(getConfig(), file);
238
+ }
239
+ });
240
+ program
241
+ .command("remote-run")
242
+ .alias("rr")
243
+ .description("Run files on Statelog remotely")
244
+ .argument("[filename]", "Paths to .test.json files or directories")
245
+ .action(async (filename) => {
246
+ console.log("Running files on Statelog remotely");
247
+ await remoteRun(getConfig(), filename);
248
+ });
224
249
  // Default: treat unknown args as a file to run
225
250
  program.arguments("[file]").action((file) => {
226
251
  if (!file) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agency-lang",
3
- "version": "0.0.59",
3
+ "version": "0.0.60",
4
4
  "description": "The Agency language",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {