glove-foundry 0.3.3 → 0.4.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 +195 -4
- package/dist/{chunk-P65RT7H5.js → chunk-25BGNRJN.js} +1452 -178
- package/dist/{chunk-CRWY7M66.js → chunk-5LDW2N2E.js} +1086 -91
- package/dist/{chunk-3GCUECPA.js → chunk-GKZLWD5M.js} +66 -2
- package/dist/cli.js +186 -40
- package/dist/{client-CLkZREDr.d.ts → client-D0-pKEZM.d.ts} +365 -12
- package/dist/client.d.ts +4 -1
- package/dist/client.js +1 -1
- package/dist/config.d.ts +15 -1
- package/dist/execution-agent.js +1 -1
- package/dist/index.d.ts +91 -5
- package/dist/index.js +428 -6
- package/docs/architecture.md +51 -0
- package/docs/building-with-foundry.md +336 -4
- package/docs/evaluation-checklist.md +10 -1
- package/docs/guidance.md +146 -0
- package/docs/inspector.md +39 -1
- package/docs/release-verification.md +74 -0
- package/package.json +15 -13
- package/templates/minimal/README.md +30 -2
- package/templates/travel-concierge/README.md +33 -5
|
@@ -9,7 +9,7 @@ async function readResponse(response) {
|
|
|
9
9
|
function routePath(route) {
|
|
10
10
|
return route.split("/").map(encodeURIComponent).join("/");
|
|
11
11
|
}
|
|
12
|
-
var FoundryRunHandle = class {
|
|
12
|
+
var FoundryRunHandle = class _FoundryRunHandle {
|
|
13
13
|
id;
|
|
14
14
|
initial;
|
|
15
15
|
client;
|
|
@@ -24,6 +24,14 @@ var FoundryRunHandle = class {
|
|
|
24
24
|
cancel() {
|
|
25
25
|
return this.client.cancelRun(this.id);
|
|
26
26
|
}
|
|
27
|
+
async steer(message) {
|
|
28
|
+
const result = await this.client.steerRun(this.id, message);
|
|
29
|
+
return {
|
|
30
|
+
fromRunId: result.fromRunId,
|
|
31
|
+
interrupted: result.interrupted,
|
|
32
|
+
run: new _FoundryRunHandle(result.run.id, result.run, this.client)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
27
35
|
events() {
|
|
28
36
|
return this.client.getEvents({ runId: this.id });
|
|
29
37
|
}
|
|
@@ -62,7 +70,16 @@ var FoundryClient = class {
|
|
|
62
70
|
/\/$/,
|
|
63
71
|
""
|
|
64
72
|
);
|
|
65
|
-
|
|
73
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
74
|
+
this.fetcher = options.authorization ? async (input, init = {}) => {
|
|
75
|
+
const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
|
|
76
|
+
const method = init.method ?? (input instanceof Request ? input.method : "GET");
|
|
77
|
+
const headers = new Headers(input instanceof Request ? input.headers : void 0);
|
|
78
|
+
new Headers(init.headers).forEach((value, key) => headers.set(key, value));
|
|
79
|
+
const authorized = new Headers(await options.authorization.headers({ url, method }));
|
|
80
|
+
authorized.forEach((value, key) => headers.set(key, value));
|
|
81
|
+
return fetcher(input, { ...init, headers });
|
|
82
|
+
} : fetcher;
|
|
66
83
|
}
|
|
67
84
|
async health() {
|
|
68
85
|
return readResponse(await this.fetcher(`${this.baseUrl}/health`));
|
|
@@ -119,6 +136,24 @@ var FoundryClient = class {
|
|
|
119
136
|
async conversations(agentId) {
|
|
120
137
|
return readResponse(await this.fetcher(`${this.baseUrl}/api/conversations?agent=${encodeURIComponent(agentId)}`));
|
|
121
138
|
}
|
|
139
|
+
async updateConversation(agentId, conversationId, options) {
|
|
140
|
+
return readResponse(await this.fetcher(
|
|
141
|
+
`${this.baseUrl}/api/conversations/${encodeURIComponent(conversationId)}`,
|
|
142
|
+
{
|
|
143
|
+
method: "PATCH",
|
|
144
|
+
headers: { "content-type": "application/json" },
|
|
145
|
+
body: JSON.stringify({ agentId, ...options })
|
|
146
|
+
}
|
|
147
|
+
));
|
|
148
|
+
}
|
|
149
|
+
async conversationTranscript(agentId, conversationId, options = {}) {
|
|
150
|
+
const query = new URLSearchParams({ agent: agentId });
|
|
151
|
+
if (options.offset !== void 0) query.set("offset", String(options.offset));
|
|
152
|
+
if (options.limit !== void 0) query.set("limit", String(options.limit));
|
|
153
|
+
return readResponse(await this.fetcher(
|
|
154
|
+
`${this.baseUrl}/api/conversations/${encodeURIComponent(conversationId)}/messages?${query}`
|
|
155
|
+
));
|
|
156
|
+
}
|
|
122
157
|
async workspaceEntries(workspaceId) {
|
|
123
158
|
return readResponse(await this.fetcher(`${this.baseUrl}/api/workspaces/${encodeURIComponent(workspaceId)}/entries`));
|
|
124
159
|
}
|
|
@@ -252,6 +287,18 @@ var FoundryClient = class {
|
|
|
252
287
|
const payload = await readResponse(response);
|
|
253
288
|
return payload.cancelled;
|
|
254
289
|
}
|
|
290
|
+
async steerRun(runId, message) {
|
|
291
|
+
return readResponse(
|
|
292
|
+
await this.fetcher(
|
|
293
|
+
`${this.baseUrl}/api/runs/${encodeURIComponent(runId)}/steer`,
|
|
294
|
+
{
|
|
295
|
+
method: "POST",
|
|
296
|
+
headers: { "content-type": "application/json" },
|
|
297
|
+
body: JSON.stringify({ message })
|
|
298
|
+
}
|
|
299
|
+
)
|
|
300
|
+
);
|
|
301
|
+
}
|
|
255
302
|
async getEvents(filter) {
|
|
256
303
|
const query = new URLSearchParams();
|
|
257
304
|
if (filter?.runId) query.set("runId", filter.runId);
|
|
@@ -265,6 +312,23 @@ var FoundryClient = class {
|
|
|
265
312
|
);
|
|
266
313
|
return readResponse(response);
|
|
267
314
|
}
|
|
315
|
+
async approvals(filter = {}) {
|
|
316
|
+
const query = new URLSearchParams();
|
|
317
|
+
if (filter.runId) query.set("runId", filter.runId);
|
|
318
|
+
if (filter.status) query.set("status", filter.status);
|
|
319
|
+
const suffix = query.size ? `?${query}` : "";
|
|
320
|
+
return readResponse(await this.fetcher(`${this.baseUrl}/api/approvals${suffix}`));
|
|
321
|
+
}
|
|
322
|
+
async resolveApproval(approvalId, decision) {
|
|
323
|
+
return readResponse(await this.fetcher(
|
|
324
|
+
`${this.baseUrl}/api/approvals/${encodeURIComponent(approvalId)}`,
|
|
325
|
+
{
|
|
326
|
+
method: "POST",
|
|
327
|
+
headers: { "content-type": "application/json" },
|
|
328
|
+
body: JSON.stringify({ decision })
|
|
329
|
+
}
|
|
330
|
+
));
|
|
331
|
+
}
|
|
268
332
|
async manifest() {
|
|
269
333
|
return readResponse(
|
|
270
334
|
await this.fetcher(`${this.baseUrl}/api/manifest`)
|
package/dist/cli.js
CHANGED
|
@@ -3,20 +3,20 @@ import {
|
|
|
3
3
|
FoundryRuntime,
|
|
4
4
|
FoundryServer,
|
|
5
5
|
writeGeneratedTypes
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-25BGNRJN.js";
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_FOUNDRY_CONFIG
|
|
9
9
|
} from "./chunk-ZFNMFE3T.js";
|
|
10
10
|
import {
|
|
11
11
|
EMPTY_FOUNDRY_APPLICATION,
|
|
12
12
|
isFoundryApplication
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-5LDW2N2E.js";
|
|
14
14
|
|
|
15
15
|
// src/cli.ts
|
|
16
16
|
import { watch } from "node:fs";
|
|
17
17
|
import { access as access2 } from "node:fs/promises";
|
|
18
|
-
import { spawn } from "node:child_process";
|
|
19
|
-
import { relative as relative2, resolve as
|
|
18
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
19
|
+
import { relative as relative2, resolve as resolve4, sep as sep2 } from "node:path";
|
|
20
20
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
21
21
|
import process2 from "node:process";
|
|
22
22
|
|
|
@@ -73,16 +73,16 @@ var TEMPLATE_DEPENDENCIES = [
|
|
|
73
73
|
];
|
|
74
74
|
var FALLBACK_RANGES = Object.freeze({
|
|
75
75
|
effect: "^3.22.1",
|
|
76
|
-
"glove-core": "^
|
|
77
|
-
"glove-js": "^0.4.
|
|
78
|
-
"glove-lisp": "^0.4.
|
|
79
|
-
"glove-mcp": "^1.1.
|
|
80
|
-
"glove-memory": "^
|
|
81
|
-
"glove-python": "^0.3.
|
|
82
|
-
"glove-working-environment": "^0.6.
|
|
76
|
+
"glove-core": "^4.0.0",
|
|
77
|
+
"glove-js": "^0.4.3",
|
|
78
|
+
"glove-lisp": "^0.4.3",
|
|
79
|
+
"glove-mcp": "^1.1.3",
|
|
80
|
+
"glove-memory": "^2.0.0",
|
|
81
|
+
"glove-python": "^0.3.3",
|
|
82
|
+
"glove-working-environment": "^0.6.1",
|
|
83
83
|
zod: "^4.3.6"
|
|
84
84
|
});
|
|
85
|
-
var FALLBACK_FOUNDRY_RANGE = "^0.
|
|
85
|
+
var FALLBACK_FOUNDRY_RANGE = "^0.3.3";
|
|
86
86
|
function toRange(value) {
|
|
87
87
|
if (typeof value !== "string" || value.length === 0) return null;
|
|
88
88
|
if (value.startsWith("workspace:") || value.startsWith("link:") || value.startsWith("file:")) {
|
|
@@ -105,7 +105,19 @@ async function resolveTemplateVersions() {
|
|
|
105
105
|
const dependencies = { ...FALLBACK_RANGES };
|
|
106
106
|
const fellBack = [];
|
|
107
107
|
for (const name of TEMPLATE_DEPENDENCIES) {
|
|
108
|
-
|
|
108
|
+
let range = toRange(declared[name]);
|
|
109
|
+
if (!range && typeof declared[name] === "string" && declared[name].startsWith("workspace:")) {
|
|
110
|
+
try {
|
|
111
|
+
const dependency = JSON.parse(await readFile2(resolve(
|
|
112
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
113
|
+
"../node_modules",
|
|
114
|
+
name,
|
|
115
|
+
"package.json"
|
|
116
|
+
), "utf8"));
|
|
117
|
+
range = toRange(dependency.version);
|
|
118
|
+
} catch {
|
|
119
|
+
}
|
|
120
|
+
}
|
|
109
121
|
if (range) dependencies[name] = range;
|
|
110
122
|
else fellBack.push(name);
|
|
111
123
|
}
|
|
@@ -196,6 +208,7 @@ async function detectPackageManager(rootDir) {
|
|
|
196
208
|
if (await exists(resolve2(rootDir, "pnpm-lock.yaml"))) return "pnpm";
|
|
197
209
|
if (await exists(resolve2(rootDir, "yarn.lock"))) return "yarn";
|
|
198
210
|
if (await exists(resolve2(rootDir, "bun.lockb"))) return "bun";
|
|
211
|
+
if (await exists(resolve2(rootDir, "bun.lock"))) return "bun";
|
|
199
212
|
if (await exists(resolve2(rootDir, "package-lock.json"))) return "npm";
|
|
200
213
|
const agent = process.env.npm_config_user_agent ?? "";
|
|
201
214
|
if (agent.startsWith("yarn")) return "yarn";
|
|
@@ -520,6 +533,85 @@ function displayPath(rootDir) {
|
|
|
520
533
|
return relative(process.cwd(), rootDir) || ".";
|
|
521
534
|
}
|
|
522
535
|
|
|
536
|
+
// src/init.ts
|
|
537
|
+
import { spawn } from "node:child_process";
|
|
538
|
+
import { resolve as resolve3 } from "node:path";
|
|
539
|
+
var InitCancelled = class extends Error {
|
|
540
|
+
};
|
|
541
|
+
async function promptInit(options) {
|
|
542
|
+
const ui = await import("@clack/prompts");
|
|
543
|
+
ui.intro("Glove Foundry \xB7 Build an agent application");
|
|
544
|
+
const answer = (value) => {
|
|
545
|
+
if (ui.isCancel(value)) {
|
|
546
|
+
ui.cancel("Setup cancelled. No project files were created.");
|
|
547
|
+
throw new InitCancelled();
|
|
548
|
+
}
|
|
549
|
+
return value;
|
|
550
|
+
};
|
|
551
|
+
const directory = options.directory ?? answer(await ui.text({
|
|
552
|
+
message: "Where should the project live?",
|
|
553
|
+
placeholder: "my-agent-app",
|
|
554
|
+
defaultValue: "my-agent-app",
|
|
555
|
+
validate: (value) => !value?.trim() ? "Enter a directory, or . for this directory." : void 0
|
|
556
|
+
}));
|
|
557
|
+
const detected = await detectScaffoldTarget(resolve3(directory));
|
|
558
|
+
const target = options.target ?? answer(await ui.select({
|
|
559
|
+
message: "How will you use Foundry?",
|
|
560
|
+
initialValue: detected,
|
|
561
|
+
options: [
|
|
562
|
+
{ value: "standalone", label: "Standalone agent application", hint: "runtime + inspector; connect any frontend" },
|
|
563
|
+
{ value: "nextjs", label: "Add to a Next.js app", hint: "colocated agents; preserve existing app files" }
|
|
564
|
+
]
|
|
565
|
+
}));
|
|
566
|
+
const template = options.template ?? answer(await ui.select({
|
|
567
|
+
message: "Choose your starting point",
|
|
568
|
+
initialValue: "travel-concierge",
|
|
569
|
+
options: [
|
|
570
|
+
{ value: "travel-concierge", label: "Guided example", hint: "recommended \xB7 keyless demo, tools, apps, memory, schedules, VFS + REPL" },
|
|
571
|
+
{ value: "minimal", label: "Minimal agent", hint: "one agent and one tool; bring an OpenRouter key" }
|
|
572
|
+
]
|
|
573
|
+
}));
|
|
574
|
+
const packageManager = options.packageManager ?? answer(await ui.select({
|
|
575
|
+
message: "Which package manager do you use?",
|
|
576
|
+
initialValue: await detectPackageManager(resolve3(directory)),
|
|
577
|
+
options: ["pnpm", "npm", "yarn", "bun"].map((value) => ({
|
|
578
|
+
value,
|
|
579
|
+
label: value
|
|
580
|
+
}))
|
|
581
|
+
}));
|
|
582
|
+
const install = options.install ?? answer(await ui.confirm({
|
|
583
|
+
message: "Install dependencies after creating the project?",
|
|
584
|
+
initialValue: true
|
|
585
|
+
}));
|
|
586
|
+
ui.note([
|
|
587
|
+
`Directory: ${resolve3(directory)}`,
|
|
588
|
+
`Project: ${target === "nextjs" ? "Existing Next.js app" : "Standalone"}`,
|
|
589
|
+
`Starter: ${template === "minimal" ? "Minimal agent" : "Guided travel concierge"}`,
|
|
590
|
+
`Packages: ${packageManager}${install ? " \xB7 install now" : " \xB7 install later"}`,
|
|
591
|
+
"Templates use disposable demo storage. The README explains production persistence.",
|
|
592
|
+
"No API keys are requested or stored by this wizard."
|
|
593
|
+
].join("\n"), "Review your project");
|
|
594
|
+
if (!answer(await ui.confirm({ message: "Create this project?", initialValue: true }))) {
|
|
595
|
+
ui.cancel("Setup cancelled. No project files were created.");
|
|
596
|
+
throw new InitCancelled();
|
|
597
|
+
}
|
|
598
|
+
return { directory: directory.trim(), target, template, packageManager, install };
|
|
599
|
+
}
|
|
600
|
+
async function installProject(directory, manager) {
|
|
601
|
+
await new Promise((done, reject) => {
|
|
602
|
+
const command = process.platform === "win32" ? "cmd.exe" : manager;
|
|
603
|
+
const args = process.platform === "win32" ? ["/d", "/s", "/c", `${manager} install`] : ["install"];
|
|
604
|
+
const child = spawn(command, args, { cwd: directory, stdio: "inherit", shell: false });
|
|
605
|
+
child.once("error", reject);
|
|
606
|
+
child.once("exit", (code) => code === 0 ? done() : reject(new Error(`Dependency installation exited with code ${code ?? "signal"}.`)));
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
function useInteractiveInit(flags, terminal) {
|
|
610
|
+
if (flags.interactive && (flags.yes || flags["no-interactive"])) throw new Error("--interactive cannot be combined with --yes or --no-interactive.");
|
|
611
|
+
if (flags.interactive && !terminal) throw new Error("Interactive setup needs a terminal. Use --yes with explicit options in scripts.");
|
|
612
|
+
return terminal && !flags.yes && !flags["no-interactive"];
|
|
613
|
+
}
|
|
614
|
+
|
|
523
615
|
// src/cli.ts
|
|
524
616
|
var HELP = `Glove Foundry \u2014 a file-routed runtime for agents
|
|
525
617
|
|
|
@@ -541,6 +633,10 @@ Create options:
|
|
|
541
633
|
Next.js app gets the nextjs target, which adds
|
|
542
634
|
agents under foundry/ and leaves the app alone.
|
|
543
635
|
--package-manager <name> pnpm, npm, yarn, or bun (detected by lockfile)
|
|
636
|
+
--yes Use defaults without interactive prompts
|
|
637
|
+
--no-interactive Same non-interactive behavior, suitable for CI
|
|
638
|
+
--interactive Require an interactive terminal
|
|
639
|
+
--install / --no-install Install dependencies now / leave installation to you
|
|
544
640
|
|
|
545
641
|
Run options:
|
|
546
642
|
--root <directory> Project root (default: current directory)
|
|
@@ -557,13 +653,17 @@ function parseArgs(raw) {
|
|
|
557
653
|
const args = [...raw];
|
|
558
654
|
const gloveSyntax = args[0] === "foundry";
|
|
559
655
|
if (gloveSyntax) args.shift();
|
|
560
|
-
const command = args[0];
|
|
656
|
+
const command = args[0]?.startsWith("--") ? void 0 : args[0];
|
|
561
657
|
const positional = [];
|
|
562
658
|
const flags = {};
|
|
563
|
-
for (let index = 1; index < args.length; index++) {
|
|
659
|
+
for (let index = command ? 1 : 0; index < args.length; index++) {
|
|
564
660
|
const value = args[index];
|
|
565
661
|
if (value.startsWith("--")) {
|
|
566
662
|
const name = value.slice(2);
|
|
663
|
+
if (["yes", "no-interactive", "interactive", "install", "no-install", "help", "no-watch"].includes(name)) {
|
|
664
|
+
flags[name] = true;
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
567
667
|
const next = args[index + 1];
|
|
568
668
|
if (next && !next.startsWith("--")) {
|
|
569
669
|
flags[name] = next;
|
|
@@ -588,7 +688,7 @@ async function pathExists(path) {
|
|
|
588
688
|
async function loadConfig(rootDir) {
|
|
589
689
|
const candidates = ["foundry.config.ts", "foundry.config.mts", "foundry.config.js", "foundry.config.mjs"];
|
|
590
690
|
for (const candidate of candidates) {
|
|
591
|
-
const path =
|
|
691
|
+
const path = resolve4(rootDir, candidate);
|
|
592
692
|
if (!await pathExists(path)) continue;
|
|
593
693
|
const url = pathToFileURL(path);
|
|
594
694
|
url.searchParams.set("t", String(Date.now()));
|
|
@@ -602,7 +702,7 @@ async function loadConfig(rootDir) {
|
|
|
602
702
|
}
|
|
603
703
|
async function loadApplication(rootDir, config) {
|
|
604
704
|
const relativePath = config.applicationFile ?? DEFAULT_FOUNDRY_CONFIG.applicationFile;
|
|
605
|
-
const path =
|
|
705
|
+
const path = resolve4(rootDir, relativePath);
|
|
606
706
|
if (!await pathExists(path)) return EMPTY_FOUNDRY_APPLICATION;
|
|
607
707
|
const url = pathToFileURL(path);
|
|
608
708
|
url.searchParams.set("t", String(Date.now()));
|
|
@@ -613,18 +713,18 @@ async function loadApplication(rootDir, config) {
|
|
|
613
713
|
return imported.default;
|
|
614
714
|
}
|
|
615
715
|
async function runWorker(parsed) {
|
|
616
|
-
const rootDir =
|
|
716
|
+
const rootDir = resolve4(
|
|
617
717
|
typeof parsed.flags.root === "string" ? parsed.flags.root : process2.cwd()
|
|
618
718
|
);
|
|
619
|
-
await loadEnvFile(
|
|
620
|
-
await loadEnvFile(
|
|
719
|
+
await loadEnvFile(resolve4(rootDir, ".env"));
|
|
720
|
+
await loadEnvFile(resolve4(rootDir, ".env.local"));
|
|
621
721
|
const config = await loadConfig(rootDir);
|
|
622
722
|
const application = await loadApplication(rootDir, config);
|
|
623
|
-
const applicationFilePath =
|
|
723
|
+
const applicationFilePath = resolve4(
|
|
624
724
|
rootDir,
|
|
625
725
|
config.applicationFile ?? DEFAULT_FOUNDRY_CONFIG.applicationFile
|
|
626
726
|
);
|
|
627
|
-
const agentsDir =
|
|
727
|
+
const agentsDir = resolve4(
|
|
628
728
|
rootDir,
|
|
629
729
|
config.agentsDir ?? DEFAULT_FOUNDRY_CONFIG.agentsDir
|
|
630
730
|
);
|
|
@@ -643,7 +743,9 @@ async function runWorker(parsed) {
|
|
|
643
743
|
await runtime.start();
|
|
644
744
|
const server = new FoundryServer(runtime, {
|
|
645
745
|
host: typeof parsed.flags.host === "string" ? parsed.flags.host : config.server?.host ?? DEFAULT_FOUNDRY_CONFIG.server.host,
|
|
646
|
-
port: typeof parsed.flags.port === "string" ? Number(parsed.flags.port) : config.server?.port ?? DEFAULT_FOUNDRY_CONFIG.server.port
|
|
746
|
+
port: typeof parsed.flags.port === "string" ? Number(parsed.flags.port) : config.server?.port ?? DEFAULT_FOUNDRY_CONFIG.server.port,
|
|
747
|
+
branding: config.branding,
|
|
748
|
+
messageBodyBytes: config.server?.messageBodyBytes
|
|
647
749
|
});
|
|
648
750
|
const listening = await server.listen();
|
|
649
751
|
await runtime.health();
|
|
@@ -672,11 +774,11 @@ async function runWorker(parsed) {
|
|
|
672
774
|
}
|
|
673
775
|
function shouldRestart(rootDir, changed, agentsDir) {
|
|
674
776
|
if (!changed) return false;
|
|
675
|
-
const normalized = relative2(rootDir,
|
|
777
|
+
const normalized = relative2(rootDir, resolve4(rootDir, changed)).split(sep2).join("/");
|
|
676
778
|
return normalized.startsWith(`${agentsDir}/`) || normalized === ".env" || normalized === ".env.local" || /^(?:[\w.-]+\/)*foundry\.application\.(?:ts|mts|js|mjs)$/.test(normalized) || /^foundry\.config\.(?:ts|mts|js|mjs)$/.test(normalized);
|
|
677
779
|
}
|
|
678
780
|
async function superviseDev(parsed) {
|
|
679
|
-
const rootDir =
|
|
781
|
+
const rootDir = resolve4(
|
|
680
782
|
typeof parsed.flags.root === "string" ? parsed.flags.root : process2.cwd()
|
|
681
783
|
);
|
|
682
784
|
const agentsDir = (await loadConfig(rootDir)).agentsDir ?? DEFAULT_FOUNDRY_CONFIG.agentsDir;
|
|
@@ -698,7 +800,7 @@ async function superviseDev(parsed) {
|
|
|
698
800
|
...typeof parsed.flags.host === "string" ? ["--host", parsed.flags.host] : []
|
|
699
801
|
];
|
|
700
802
|
const start = () => {
|
|
701
|
-
child =
|
|
803
|
+
child = spawn2(process2.execPath, workerArgs, {
|
|
702
804
|
cwd: rootDir,
|
|
703
805
|
env: process2.env,
|
|
704
806
|
stdio: "inherit"
|
|
@@ -742,7 +844,7 @@ async function superviseDev(parsed) {
|
|
|
742
844
|
(_event, filename) => onChange(filename?.toString() ?? null)
|
|
743
845
|
)
|
|
744
846
|
);
|
|
745
|
-
const watchedAgentsDir =
|
|
847
|
+
const watchedAgentsDir = resolve4(rootDir, agentsDir);
|
|
746
848
|
if (await pathExists(watchedAgentsDir)) {
|
|
747
849
|
watchers.push(
|
|
748
850
|
watch(
|
|
@@ -761,7 +863,7 @@ async function superviseDev(parsed) {
|
|
|
761
863
|
"memory",
|
|
762
864
|
"inboxes"
|
|
763
865
|
]) {
|
|
764
|
-
const watched =
|
|
866
|
+
const watched = resolve4(rootDir, directory);
|
|
765
867
|
if (await pathExists(watched)) {
|
|
766
868
|
watchers.push(
|
|
767
869
|
watch(
|
|
@@ -791,10 +893,10 @@ async function superviseDev(parsed) {
|
|
|
791
893
|
process2.once("SIGTERM", stop);
|
|
792
894
|
}
|
|
793
895
|
async function superviseStart(parsed) {
|
|
794
|
-
const rootDir =
|
|
896
|
+
const rootDir = resolve4(
|
|
795
897
|
typeof parsed.flags.root === "string" ? parsed.flags.root : process2.cwd()
|
|
796
898
|
);
|
|
797
|
-
const child =
|
|
899
|
+
const child = spawn2(
|
|
798
900
|
process2.execPath,
|
|
799
901
|
[
|
|
800
902
|
"--import",
|
|
@@ -842,6 +944,10 @@ function flagValue(parsed, name, allowed) {
|
|
|
842
944
|
return raw;
|
|
843
945
|
}
|
|
844
946
|
async function create(directory, parsed) {
|
|
947
|
+
const supported = /* @__PURE__ */ new Set(["template", "target", "package-manager", "yes", "no-interactive", "interactive", "install", "no-install"]);
|
|
948
|
+
for (const flag of Object.keys(parsed.flags)) if (!supported.has(flag)) throw new UsageError(`Unknown create option --${flag}.`);
|
|
949
|
+
if (parsed.positional.length > 1) throw new UsageError("Provide only one project directory.");
|
|
950
|
+
if (parsed.flags.install && parsed.flags["no-install"]) throw new UsageError("Choose either --install or --no-install.");
|
|
845
951
|
const template = flagValue(parsed, "template", FOUNDRY_TEMPLATES);
|
|
846
952
|
const target = flagValue(parsed, "target", ["standalone", "nextjs"]);
|
|
847
953
|
const packageManager = flagValue(
|
|
@@ -849,12 +955,45 @@ async function create(directory, parsed) {
|
|
|
849
955
|
"package-manager",
|
|
850
956
|
["pnpm", "npm", "yarn", "bun"]
|
|
851
957
|
);
|
|
852
|
-
|
|
853
|
-
|
|
958
|
+
let interactive;
|
|
959
|
+
try {
|
|
960
|
+
interactive = useInteractiveInit(parsed.flags, Boolean(process2.stdin.isTTY && process2.stdout.isTTY));
|
|
961
|
+
} catch (error) {
|
|
962
|
+
throw new UsageError(error instanceof Error ? error.message : String(error));
|
|
963
|
+
}
|
|
964
|
+
const selected = {
|
|
965
|
+
...directory ? { directory } : {},
|
|
854
966
|
...template ? { template } : {},
|
|
855
967
|
...target ? { target } : {},
|
|
856
|
-
...packageManager ? { packageManager } : {}
|
|
857
|
-
|
|
968
|
+
...packageManager ? { packageManager } : {},
|
|
969
|
+
...parsed.flags.install ? { install: true } : parsed.flags["no-install"] ? { install: false } : {}
|
|
970
|
+
};
|
|
971
|
+
const options = interactive ? await promptInit(selected) : { ...selected, directory: directory ?? "glove-foundry-app", install: selected.install ?? false };
|
|
972
|
+
const ui = interactive ? await import("@clack/prompts") : void 0;
|
|
973
|
+
const progress = ui?.spinner();
|
|
974
|
+
progress?.start("Creating your agent application");
|
|
975
|
+
let result;
|
|
976
|
+
try {
|
|
977
|
+
result = await scaffoldFoundryProject(options);
|
|
978
|
+
} catch (error) {
|
|
979
|
+
progress?.stop("Project was not created");
|
|
980
|
+
throw error;
|
|
981
|
+
}
|
|
982
|
+
progress?.stop("Project files are ready");
|
|
983
|
+
let installed = false;
|
|
984
|
+
if (options.install) {
|
|
985
|
+
ui?.log.step(`Installing dependencies with ${result.packageManager}`);
|
|
986
|
+
try {
|
|
987
|
+
await installProject(result.rootDir, result.packageManager);
|
|
988
|
+
installed = true;
|
|
989
|
+
} catch (error) {
|
|
990
|
+
process2.exitCode = 1;
|
|
991
|
+
const message = `Project files are safe. Installation failed: ${error instanceof Error ? error.message : String(error)} Run ${result.packageManager} install in the project to retry.`;
|
|
992
|
+
if (ui) ui.log.warn(message);
|
|
993
|
+
else process2.stderr.write(`${message}
|
|
994
|
+
`);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
858
997
|
const where = displayPath(result.rootDir);
|
|
859
998
|
const pm = result.packageManager;
|
|
860
999
|
const run = (script) => pm === "npm" ? `npm run ${script}` : `${pm} ${script}`;
|
|
@@ -870,7 +1009,7 @@ async function create(directory, parsed) {
|
|
|
870
1009
|
lines.push("");
|
|
871
1010
|
lines.push(" Next:");
|
|
872
1011
|
if (where !== ".") lines.push(` cd ${where}`);
|
|
873
|
-
lines.push(` ${install}`);
|
|
1012
|
+
if (!installed) lines.push(` ${install}`);
|
|
874
1013
|
lines.push(` ${run("foundry:dev")} # runtime + inspector on :4141`);
|
|
875
1014
|
lines.push(` ${run("dev")} # your Next.js app`);
|
|
876
1015
|
} else {
|
|
@@ -883,7 +1022,7 @@ async function create(directory, parsed) {
|
|
|
883
1022
|
lines.push(" Next:");
|
|
884
1023
|
if (where !== ".") lines.push(` cd ${where}`);
|
|
885
1024
|
lines.push(" cp .env.example .env.local");
|
|
886
|
-
lines.push(` ${install}`);
|
|
1025
|
+
if (!installed) lines.push(` ${install}`);
|
|
887
1026
|
lines.push(` ${run("dev")}`);
|
|
888
1027
|
lines.push("");
|
|
889
1028
|
lines.push(" Then open http://127.0.0.1:4141 and press Start a run.");
|
|
@@ -898,7 +1037,10 @@ async function create(directory, parsed) {
|
|
|
898
1037
|
lines.push(` ${result.versions.fellBack.join(", ")}`);
|
|
899
1038
|
}
|
|
900
1039
|
lines.push("");
|
|
901
|
-
|
|
1040
|
+
if (ui) {
|
|
1041
|
+
ui.note(lines.join("\n").trim(), "Next steps");
|
|
1042
|
+
ui.outro(process2.exitCode ? "Project created. Retry dependency installation before starting." : "Your agent application is ready to explore. Open the README to begin.");
|
|
1043
|
+
} else process2.stdout.write(lines.join("\n"));
|
|
902
1044
|
}
|
|
903
1045
|
async function main() {
|
|
904
1046
|
const parsed = parseArgs(process2.argv.slice(2));
|
|
@@ -906,13 +1048,13 @@ async function main() {
|
|
|
906
1048
|
await runWorker(parsed);
|
|
907
1049
|
return;
|
|
908
1050
|
}
|
|
909
|
-
if (["help", "--help", "-h"].includes(parsed.command ?? "")) {
|
|
1051
|
+
if (parsed.flags.help || ["help", "--help", "-h"].includes(parsed.command ?? "")) {
|
|
910
1052
|
process2.stdout.write(HELP);
|
|
911
1053
|
return;
|
|
912
1054
|
}
|
|
913
1055
|
const createByGloveSyntax = parsed.gloveSyntax && parsed.command !== "dev" && parsed.command !== "start";
|
|
914
|
-
if (parsed.command === "init" || createByGloveSyntax || parsed.
|
|
915
|
-
const directory = parsed.command === "init" ? parsed.positional[0]
|
|
1056
|
+
if (parsed.command === "init" || createByGloveSyntax || !parsed.command && (process2.stdin.isTTY || parsed.flags.yes)) {
|
|
1057
|
+
const directory = parsed.command === "init" ? parsed.positional[0] : parsed.command ?? parsed.positional[0];
|
|
916
1058
|
await create(directory, parsed);
|
|
917
1059
|
return;
|
|
918
1060
|
}
|
|
@@ -927,6 +1069,10 @@ async function main() {
|
|
|
927
1069
|
process2.stdout.write(HELP);
|
|
928
1070
|
}
|
|
929
1071
|
main().catch((error) => {
|
|
1072
|
+
if (error instanceof InitCancelled) {
|
|
1073
|
+
process2.exitCode = 130;
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
930
1076
|
if (error instanceof UsageError) {
|
|
931
1077
|
process2.stderr.write(`
|
|
932
1078
|
${error.message}
|