@townco/cli 0.1.82 → 0.1.83

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.
Files changed (51) hide show
  1. package/dist/commands/batch-wrapper.d.ts +26 -0
  2. package/dist/commands/batch-wrapper.js +35 -0
  3. package/dist/commands/batch.js +2 -2
  4. package/dist/commands/create-wrapper.d.ts +29 -0
  5. package/dist/commands/create-wrapper.js +92 -0
  6. package/dist/commands/create.js +38 -12
  7. package/dist/commands/deploy.d.ts +14 -0
  8. package/dist/commands/deploy.js +88 -0
  9. package/dist/commands/login.js +1 -1
  10. package/dist/commands/run-wrapper.d.ts +32 -0
  11. package/dist/commands/run-wrapper.js +32 -0
  12. package/dist/commands/run.js +23 -16
  13. package/dist/commands/secret.d.ts +62 -0
  14. package/dist/commands/secret.js +119 -0
  15. package/dist/components/LogsPane.d.ts +1 -1
  16. package/dist/components/LogsPane.js +12 -3
  17. package/dist/components/ProcessPane.d.ts +1 -1
  18. package/dist/components/ProcessPane.js +9 -1
  19. package/dist/components/StatusLine.js +4 -1
  20. package/dist/components/TabbedOutput.d.ts +1 -1
  21. package/dist/components/TabbedOutput.js +2 -1
  22. package/dist/index.js +21 -338
  23. package/dist/lib/command.d.ts +8 -0
  24. package/dist/lib/command.js +1 -0
  25. package/package.json +11 -15
  26. package/dist/commands/delete.d.ts +0 -1
  27. package/dist/commands/delete.js +0 -60
  28. package/dist/commands/edit.d.ts +0 -1
  29. package/dist/commands/edit.js +0 -92
  30. package/dist/commands/list.d.ts +0 -1
  31. package/dist/commands/list.js +0 -55
  32. package/dist/commands/mcp-add.d.ts +0 -14
  33. package/dist/commands/mcp-add.js +0 -494
  34. package/dist/commands/mcp-list.d.ts +0 -3
  35. package/dist/commands/mcp-list.js +0 -63
  36. package/dist/commands/mcp-remove.d.ts +0 -3
  37. package/dist/commands/mcp-remove.js +0 -120
  38. package/dist/commands/tool-add.d.ts +0 -6
  39. package/dist/commands/tool-add.js +0 -349
  40. package/dist/commands/tool-list.d.ts +0 -3
  41. package/dist/commands/tool-list.js +0 -61
  42. package/dist/commands/tool-register.d.ts +0 -7
  43. package/dist/commands/tool-register.js +0 -291
  44. package/dist/commands/tool-remove.d.ts +0 -3
  45. package/dist/commands/tool-remove.js +0 -202
  46. package/dist/components/MergedLogsPane.d.ts +0 -11
  47. package/dist/components/MergedLogsPane.js +0 -205
  48. package/dist/lib/auth-storage.d.ts +0 -38
  49. package/dist/lib/auth-storage.js +0 -89
  50. package/dist/lib/mcp-storage.d.ts +0 -32
  51. package/dist/lib/mcp-storage.js +0 -111
@@ -0,0 +1,26 @@
1
+ declare const _default: {
2
+ def: import("@optique/core").Parser<{
3
+ readonly command: "batch";
4
+ readonly name: string;
5
+ readonly queries: readonly string[];
6
+ readonly file: string | undefined;
7
+ readonly concurrency: number | undefined;
8
+ readonly port: number | undefined;
9
+ }, ["matched", string] | ["parsing", {
10
+ readonly command: "batch";
11
+ readonly name: import("@optique/core").ValueParserResult<string> | undefined;
12
+ readonly queries: readonly (import("@optique/core").ValueParserResult<string> | undefined)[];
13
+ readonly file: [import("@optique/core").ValueParserResult<string> | undefined] | undefined;
14
+ readonly concurrency: [import("@optique/core").ValueParserResult<number> | undefined] | undefined;
15
+ readonly port: [import("@optique/core").ValueParserResult<number> | undefined] | undefined;
16
+ }] | undefined>;
17
+ impl: (def: {
18
+ readonly command: "batch";
19
+ readonly name: string;
20
+ readonly queries: readonly string[];
21
+ readonly file: string | undefined;
22
+ readonly concurrency: number | undefined;
23
+ readonly port: number | undefined;
24
+ }) => unknown;
25
+ };
26
+ export default _default;
@@ -0,0 +1,35 @@
1
+ import { argument, command, constant, message, multiple, object, option, optional, string, } from "@optique/core";
2
+ import { integer } from "@optique/core/valueparser";
3
+ import { createCommand } from "@/lib/command";
4
+ import { batchCommand } from "./batch";
5
+ export default createCommand({
6
+ def: command("batch", object({
7
+ command: constant("batch"),
8
+ name: argument(string({ metavar: "NAME" })),
9
+ queries: multiple(argument(string({ metavar: "QUERY" }))),
10
+ file: optional(option("-f", "--file", string())),
11
+ concurrency: optional(option("-c", "--concurrency", integer())),
12
+ port: optional(option("-p", "--port", integer())),
13
+ }), {
14
+ brief: message `Run multiple queries in parallel against an agent.`,
15
+ description: message `Run multiple queries in parallel, each in its own session. Queries can be provided as arguments or loaded from a file.`,
16
+ }),
17
+ impl: async ({ name, queries, file, concurrency, port }) => {
18
+ const options = {
19
+ name,
20
+ };
21
+ if (queries.length > 0) {
22
+ options.queries = [...queries]; // Convert readonly array to mutable
23
+ }
24
+ if (file !== null && file !== undefined) {
25
+ options.file = file;
26
+ }
27
+ if (concurrency !== null && concurrency !== undefined) {
28
+ options.concurrency = concurrency;
29
+ }
30
+ if (port !== null && port !== undefined) {
31
+ options.port = port;
32
+ }
33
+ await batchCommand(options);
34
+ },
35
+ });
@@ -183,7 +183,7 @@ export async function batchCommand(options) {
183
183
  try {
184
184
  process.kill(-agentProcess.pid, "SIGINT");
185
185
  }
186
- catch (e) {
186
+ catch (_e) {
187
187
  // Process may already be dead
188
188
  }
189
189
  }
@@ -191,7 +191,7 @@ export async function batchCommand(options) {
191
191
  try {
192
192
  process.kill(-debuggerProcess.pid, "SIGKILL");
193
193
  }
194
- catch (e) {
194
+ catch (_e) {
195
195
  // Process may already be dead
196
196
  }
197
197
  }
@@ -0,0 +1,29 @@
1
+ declare const _default: {
2
+ def: import("@optique/core").Parser<{
3
+ readonly command: "create";
4
+ readonly name: string | undefined;
5
+ readonly model: string | undefined;
6
+ readonly tools: readonly string[];
7
+ readonly systemPrompt: string | undefined;
8
+ readonly init: string | undefined;
9
+ readonly claude: true | undefined;
10
+ }, ["matched", string] | ["parsing", {
11
+ readonly command: "create";
12
+ readonly name: [import("@optique/core").ValueParserResult<string> | undefined] | undefined;
13
+ readonly model: [import("@optique/core").ValueParserResult<string> | undefined] | undefined;
14
+ readonly tools: readonly (import("@optique/core").ValueParserResult<string> | undefined)[];
15
+ readonly systemPrompt: [import("@optique/core").ValueParserResult<string> | undefined] | undefined;
16
+ readonly init: [import("@optique/core").ValueParserResult<string> | undefined] | undefined;
17
+ readonly claude: [import("@optique/core").ValueParserResult<true> | undefined] | undefined;
18
+ }] | undefined>;
19
+ impl: (def: {
20
+ readonly command: "create";
21
+ readonly name: string | undefined;
22
+ readonly model: string | undefined;
23
+ readonly tools: readonly string[];
24
+ readonly systemPrompt: string | undefined;
25
+ readonly init: string | undefined;
26
+ readonly claude: true | undefined;
27
+ }) => unknown;
28
+ };
29
+ export default _default;
@@ -0,0 +1,92 @@
1
+ import { join } from "node:path";
2
+ import { command, constant, flag, message, multiple, object, option, optional, string, } from "@optique/core";
3
+ import { initForClaudeCode } from "@townco/agent/scaffold";
4
+ import { isInsideTownProject } from "@townco/agent/storage";
5
+ import inquirer from "inquirer";
6
+ import { createCommand } from "@/lib/command";
7
+ import { createCommand as createAgentCommand } from "./create";
8
+ import { createProjectCommand } from "./create-project";
9
+ export default createCommand({
10
+ def: command("create", object({
11
+ command: constant("create"),
12
+ name: optional(option("-n", "--name", string())),
13
+ model: optional(option("-m", "--model", string())),
14
+ tools: multiple(option("-t", "--tool", string())),
15
+ systemPrompt: optional(option("-p", "--prompt", string())),
16
+ init: optional(option("--init", string())),
17
+ claude: optional(flag("--claude")),
18
+ }), {
19
+ brief: message `Create a new agent or project (with --init <path>). Use --claude to add Claude Code integration.`,
20
+ }),
21
+ impl: async ({ name, model, tools, systemPrompt, init, claude }) => {
22
+ // Handle --claude flag (initialize .claude in existing project)
23
+ if (claude === true) {
24
+ if (init !== null && init !== undefined) {
25
+ console.error("Error: --claude flag is redundant with --init (projects include .claude by default)");
26
+ process.exit(1);
27
+ }
28
+ // Check if we're in a Town project
29
+ const projectRoot = await isInsideTownProject();
30
+ if (projectRoot === null) {
31
+ console.error("Error: Not inside a Town project. Use 'town create --init <path>' to create a new project.");
32
+ process.exit(1);
33
+ }
34
+ // Initialize .claude directory only
35
+ await initForClaudeCode(projectRoot);
36
+ console.log("\n✓ Claude Code workspace initialized successfully!");
37
+ return;
38
+ }
39
+ // Check if --init flag is present for project scaffolding
40
+ if (init !== null && init !== undefined) {
41
+ // Project mode - scaffold a standalone project
42
+ await createProjectCommand({
43
+ path: init,
44
+ });
45
+ }
46
+ else {
47
+ // Check if we're inside a Town project
48
+ const projectRoot = await isInsideTownProject();
49
+ if (projectRoot === null) {
50
+ // Not in a project - prompt user to initialize
51
+ const answer = await inquirer.prompt([
52
+ {
53
+ type: "confirm",
54
+ name: "initProject",
55
+ message: "Not inside a Town project. Initialize project in current directory?",
56
+ default: true,
57
+ },
58
+ ]);
59
+ if (answer.initProject) {
60
+ // Initialize project first
61
+ await createProjectCommand({ path: process.cwd() });
62
+ // Then create agent
63
+ await createAgentCommand({
64
+ ...(name !== undefined && { name }),
65
+ ...(model !== undefined && { model }),
66
+ ...(tools.length > 0 && { tools }),
67
+ ...(systemPrompt !== undefined && { systemPrompt }),
68
+ agentsDir: join(process.cwd(), "agents"),
69
+ });
70
+ }
71
+ else {
72
+ // User declined
73
+ console.log("\nPlease run 'town create' inside a project directory, or run:\n" +
74
+ " town create --init <path>\n" +
75
+ "to create a project.");
76
+ process.exit(1);
77
+ }
78
+ }
79
+ else {
80
+ // Agent mode - create agent in existing project
81
+ // Create command starts a long-running Ink session
82
+ await createAgentCommand({
83
+ ...(name !== undefined && { name }),
84
+ ...(model !== undefined && { model }),
85
+ ...(tools.length > 0 && { tools }),
86
+ ...(systemPrompt !== undefined && { systemPrompt }),
87
+ agentsDir: join(projectRoot, "agents"),
88
+ });
89
+ }
90
+ }
91
+ },
92
+ });
@@ -1,10 +1,9 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { scaffoldAgent } from "@townco/agent/scaffold";
3
- import { InputBox, MultiSelect, SingleSelect } from "@townco/ui/tui";
3
+ import { MultiSelect, SingleSelect } from "@townco/ui/tui";
4
4
  import { Box, render, Text, useInput } from "ink";
5
5
  import TextInput from "ink-text-input";
6
6
  import { useEffect, useState } from "react";
7
- import { openInEditor } from "../lib/editor-utils";
8
7
  const AVAILABLE_MODELS = [
9
8
  {
10
9
  label: "Claude Sonnet 4.5",
@@ -65,7 +64,11 @@ function NameInput({ nameInput, setNameInput, onSubmit }) {
65
64
  process.exit(0);
66
65
  }
67
66
  });
68
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Enter agent name:" }) }), _jsxs(Box, { children: [_jsxs(Text, { children: [">", " "] }), _jsx(TextInput, { value: nameInput, onChange: setNameInput, onSubmit: onSubmit })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: Continue \u2022 Esc: Cancel" }) })] }));
67
+ return (_jsxs(Box, { flexDirection: "column", children: [
68
+ _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Enter agent name:" }) }), _jsxs(Box, { children: [
69
+ _jsxs(Text, { children: [">", " "] }), _jsx(TextInput, { value: nameInput, onChange: setNameInput, onSubmit: onSubmit })
70
+ ] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Enter: Continue \u2022 Esc: Cancel" }) })
71
+ ] }));
69
72
  }
70
73
  function CreateApp({ name: initialName, model: initialModel, tools: initialTools, systemPrompt: initialSystemPrompt, overwrite = false, agentsDir, }) {
71
74
  // Determine the starting stage based on what's provided
@@ -164,7 +167,8 @@ function CreateApp({ name: initialName, model: initialModel, tools: initialTools
164
167
  }
165
168
  // Model selection stage
166
169
  if (stage === "model") {
167
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, children: ["Select model for agent: ", agentDef.name] }) }), _jsx(SingleSelect, { options: AVAILABLE_MODELS, selected: agentDef.model || null, onChange: (model) => setAgentDef({ ...agentDef, model }), onSubmit: (model) => {
170
+ return (_jsxs(Box, { flexDirection: "column", children: [
171
+ _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, children: ["Select model for agent: ", agentDef.name] }) }), _jsx(SingleSelect, { options: AVAILABLE_MODELS, selected: agentDef.model || null, onChange: (model) => setAgentDef({ ...agentDef, model }), onSubmit: (model) => {
168
172
  setAgentDef({ ...agentDef, model });
169
173
  if (isEditingFromReview) {
170
174
  setIsEditingFromReview(false);
@@ -180,24 +184,33 @@ function CreateApp({ name: initialName, model: initialModel, tools: initialTools
180
184
  else {
181
185
  setStage("name");
182
186
  }
183
- } })] }));
187
+ } })
188
+ ] }));
184
189
  }
185
190
  // Tools selection stage
186
191
  if (stage === "tools") {
187
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, children: ["Select tools for agent: ", agentDef.name] }) }), _jsx(MultiSelect, { options: AVAILABLE_TOOLS, selected: agentDef.tools || [], onChange: (tools) => setAgentDef({ ...agentDef, tools }), onSubmit: () => {
192
+ return (_jsxs(Box, { flexDirection: "column", children: [
193
+ _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { bold: true, children: ["Select tools for agent: ", agentDef.name] }) }), _jsx(MultiSelect, { options: AVAILABLE_TOOLS, selected: agentDef.tools || [], onChange: (tools) => setAgentDef({ ...agentDef, tools }), onSubmit: () => {
188
194
  // If editing from review, just go back to review
189
195
  if (isEditingFromReview) {
190
196
  setIsEditingFromReview(false);
191
197
  }
192
198
  // Go directly to review stage
193
199
  setStage("review");
194
- }, onCancel: () => setStage("model") })] }));
200
+ }, onCancel: () => setStage("model") })
201
+ ] }));
195
202
  }
196
203
  // Review stage
197
204
  if (stage === "review") {
198
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Review Agent Configuration:" }) }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Name: " }), agentDef.name] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Model: " }), agentDef.model] }), _jsxs(Text, { children: [_jsx(Text, { bold: true, children: "Tools: " }), agentDef.tools && agentDef.tools.length > 0
205
+ return (_jsxs(Box, { flexDirection: "column", children: [
206
+ _jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, children: "Review Agent Configuration:" }) }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
207
+ _jsxs(Text, { children: [
208
+ _jsx(Text, { bold: true, children: "Name: " }), agentDef.name] }), _jsxs(Text, { children: [
209
+ _jsx(Text, { bold: true, children: "Model: " }), agentDef.model] }), _jsxs(Text, { children: [
210
+ _jsx(Text, { bold: true, children: "Tools: " }), agentDef.tools && agentDef.tools.length > 0
199
211
  ? agentDef.tools.join(", ")
200
- : "none"] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { bold: true, children: "System Prompt:" }) }), _jsx(Box, { paddingLeft: 2, flexDirection: "column", children: agentDef.systemPrompt?.split("\n").map((line) => (_jsx(Text, { dimColor: true, children: line || " " }, line))) })] }), _jsx(SingleSelect, { options: [
212
+ : "none"] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { bold: true, children: "System Prompt:" }) }), _jsx(Box, { paddingLeft: 2, flexDirection: "column", children: agentDef.systemPrompt?.split("\n").map((line) => (_jsx(Text, { dimColor: true, children: line || " " }, line))) })
213
+ ] }), _jsx(SingleSelect, { options: [
201
214
  {
202
215
  label: "Looks good, continue",
203
216
  value: "continue",
@@ -233,7 +246,8 @@ function CreateApp({ name: initialName, model: initialModel, tools: initialTools
233
246
  setIsEditingFromReview(true);
234
247
  setStage(value);
235
248
  }
236
- }, onCancel: () => setStage("tools") })] }));
249
+ }, onCancel: () => setStage("tools") })
250
+ ] }));
237
251
  }
238
252
  // Done stage
239
253
  if (stage === "done") {
@@ -241,11 +255,23 @@ function CreateApp({ name: initialName, model: initialModel, tools: initialTools
241
255
  return (_jsx(Box, { flexDirection: "column", children: _jsxs(Text, { children: ["\u23F3 Creating agent ", agentDef.name, "..."] }) }));
242
256
  }
243
257
  if (scaffoldStatus === "error") {
244
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: "\u2717 Error creating agent package" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: scaffoldError }) })] }));
258
+ return (_jsxs(Box, { flexDirection: "column", children: [
259
+ _jsx(Text, { color: "red", children: "\u2717 Error creating agent package" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { children: scaffoldError }) })
260
+ ] }));
245
261
  }
246
262
  if (scaffoldStatus === "done") {
247
263
  const modelLabel = agentDef.model?.replace("claude-", "") || "";
248
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Agent created successfully!" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { children: [_jsx(Text, { color: "green", children: "\u25CF" }), " ", _jsx(Text, { bold: true, children: agentDef.name })] }), _jsxs(Text, { dimColor: true, children: [" Model: ", modelLabel] }), agentDef.tools && agentDef.tools.length > 0 && (_jsxs(Text, { dimColor: true, children: [" Tools: ", agentDef.tools.join(", ")] })), _jsxs(Text, { dimColor: true, children: [" Path: ", agentPath] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["Run agent: town run ", agentDef.name] }), _jsxs(Text, { dimColor: true, children: ["With GUI: town run ", agentDef.name, " --gui"] })] })] }));
264
+ return (_jsxs(Box, { flexDirection: "column", children: [
265
+ _jsx(Text, { bold: true, children: "Agent created successfully!" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
266
+ _jsxs(Text, { children: [
267
+ _jsx(Text, { color: "green", children: "\u25CF" }),
268
+ " ",
269
+ _jsx(Text, { bold: true, children: agentDef.name })
270
+ ] }), _jsxs(Text, { dimColor: true, children: [" Model: ", modelLabel] }), agentDef.tools && agentDef.tools.length > 0 && (_jsxs(Text, { dimColor: true, children: [" Tools: ", agentDef.tools.join(", ")] })), _jsxs(Text, { dimColor: true, children: [" Path: ", agentPath] })
271
+ ] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [
272
+ _jsxs(Text, { dimColor: true, children: ["Run agent: town run ", agentDef.name] }), _jsxs(Text, { dimColor: true, children: ["With GUI: town run ", agentDef.name, " --gui"] })
273
+ ] })
274
+ ] }));
249
275
  }
250
276
  }
251
277
  return null;
@@ -0,0 +1,14 @@
1
+ declare const _default: {
2
+ def: import("@optique/core").Parser<{
3
+ readonly command: "deploy";
4
+ readonly agent: string;
5
+ }, ["matched", string] | ["parsing", {
6
+ readonly command: "deploy";
7
+ readonly agent: import("@optique/core").ValueParserResult<string> | undefined;
8
+ }] | undefined>;
9
+ impl: (def: {
10
+ readonly command: "deploy";
11
+ readonly agent: string;
12
+ }) => unknown;
13
+ };
14
+ export default _default;
@@ -0,0 +1,88 @@
1
+ import fs from "node:fs";
2
+ import afs from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { command, constant, message, object, option, string, } from "@optique/core";
5
+ import { isInsideTownProject } from "@townco/agent/storage";
6
+ import { findRoot } from "@townco/core/path";
7
+ import { createTRPCClient, httpLink, httpSubscriptionLink, splitLink, } from "@trpc/client";
8
+ import archiver from "archiver";
9
+ import { EventSource } from "eventsource";
10
+ import walk from "ignore-walk";
11
+ import superjson from "superjson";
12
+ import { getValidCredentials } from "@/lib/auth-fetch";
13
+ import { createCommand } from "@/lib/command";
14
+ const MAX_ARCHIVE_SIZE = 4.5 * 1024 * 1024; // 4.5MB
15
+ export default createCommand({
16
+ def: command("deploy", object({
17
+ command: constant("deploy"),
18
+ agent: option("-a", "--agent", string()),
19
+ }), {
20
+ brief: message `Deploy agents.`,
21
+ description: message `Deploy agents to the Town cloud.`,
22
+ }),
23
+ impl: async ({ agent }) => {
24
+ const projectRoot = await isInsideTownProject();
25
+ if (!projectRoot)
26
+ throw new Error("Not inside a Town project");
27
+ if (!(await afs.exists(join(projectRoot, "agents", agent))))
28
+ throw new Error(`Agent ${agent} not found`);
29
+ const { accessToken, shedUrl } = await getValidCredentials();
30
+ const authHeader = { Authorization: `Bearer ${accessToken}` };
31
+ const url = `${shedUrl}/api/trpc`;
32
+ const baseLinkOpts = { url, transformer: superjson };
33
+ const client = createTRPCClient({
34
+ links: [
35
+ splitLink({
36
+ condition: (op) => op.type === "subscription",
37
+ true: httpSubscriptionLink({
38
+ ...baseLinkOpts,
39
+ EventSource,
40
+ eventSourceOptions: {
41
+ fetch: async (url, init) => fetch(url, {
42
+ ...init,
43
+ headers: { ...init.headers, ...authHeader },
44
+ }),
45
+ },
46
+ }),
47
+ false: httpLink({ ...baseLinkOpts, headers: authHeader }),
48
+ }),
49
+ ],
50
+ });
51
+ console.log("Creating archive...");
52
+ const root = await findRoot({ rootMarker: "package.json" });
53
+ const chunks = [];
54
+ const arc = archiver("tar", { gzip: true });
55
+ const done = new Promise((ok, err) => {
56
+ arc.on("data", (chunk) => chunks.push(chunk));
57
+ arc.on("end", () => {
58
+ console.log("Archive created");
59
+ ok(Buffer.concat(chunks));
60
+ });
61
+ arc.on("error", err);
62
+ });
63
+ (await walk({ path: root, ignoreFiles: [".gitignore"] }))
64
+ .filter((path) => path.split("/")[0] !== ".git")
65
+ .reduce((totalSize, path) => {
66
+ const stats = fs.statSync(path);
67
+ if (!stats.isFile())
68
+ return totalSize;
69
+ const fileSize = fs.statSync(path).size;
70
+ const newTotal = totalSize + fileSize;
71
+ if (newTotal > MAX_ARCHIVE_SIZE) {
72
+ throw new Error(`Archive size limit exceeded: ${(newTotal / 1024 / 1024).toFixed(2)}MB > 4.5MB`);
73
+ }
74
+ arc.append(fs.createReadStream(path), { name: path });
75
+ return newTotal;
76
+ }, 0);
77
+ arc.finalize();
78
+ console.log("Uploading archive...");
79
+ const { sha256, cached } = await client.uploadArchive.mutate(await done);
80
+ console.log(`Archive uploaded: ${sha256} (${cached ? "cached" : "new"})`);
81
+ console.log("Deploying...");
82
+ client.deploy.subscribe({ sha256, agent, shedUrl }, {
83
+ onData: ({ status, error }) => console.log(status ? status : error),
84
+ onError: (err) => console.error(err),
85
+ onComplete: () => console.log("\n✓ Deployment complete!"),
86
+ });
87
+ },
88
+ });
@@ -80,7 +80,7 @@ export async function loginCommand() {
80
80
  console.error(`\nLogin failed: ${errorData.error || "Unknown error"}`);
81
81
  if (response.status === 401) {
82
82
  console.log("\nPlease check your email and password and try again.");
83
- console.log("If you don't have an account, sign up at " + shedUrl);
83
+ console.log(`If you don't have an account, sign up at ${shedUrl}`);
84
84
  }
85
85
  process.exit(1);
86
86
  }
@@ -0,0 +1,32 @@
1
+ declare const _default: {
2
+ def: import("@optique/core").Parser<{
3
+ readonly command: "run";
4
+ readonly name: string;
5
+ readonly http: true | undefined;
6
+ readonly gui: true | undefined;
7
+ readonly cli: true | undefined;
8
+ readonly prompt: string | undefined;
9
+ readonly port: number | undefined;
10
+ readonly noSession: true | undefined;
11
+ }, ["matched", string] | ["parsing", {
12
+ readonly command: "run";
13
+ readonly name: import("@optique/core").ValueParserResult<string> | undefined;
14
+ readonly http: [import("@optique/core").ValueParserResult<true> | undefined] | undefined;
15
+ readonly gui: [import("@optique/core").ValueParserResult<true> | undefined] | undefined;
16
+ readonly cli: [import("@optique/core").ValueParserResult<true> | undefined] | undefined;
17
+ readonly prompt: [import("@optique/core").ValueParserResult<string> | undefined] | undefined;
18
+ readonly port: [import("@optique/core").ValueParserResult<number> | undefined] | undefined;
19
+ readonly noSession: [import("@optique/core").ValueParserResult<true> | undefined] | undefined;
20
+ }] | undefined>;
21
+ impl: (def: {
22
+ readonly command: "run";
23
+ readonly name: string;
24
+ readonly http: true | undefined;
25
+ readonly gui: true | undefined;
26
+ readonly cli: true | undefined;
27
+ readonly prompt: string | undefined;
28
+ readonly port: number | undefined;
29
+ readonly noSession: true | undefined;
30
+ }) => unknown;
31
+ };
32
+ export default _default;
@@ -0,0 +1,32 @@
1
+ import { argument, command, constant, flag, message, object, option, optional, string, } from "@optique/core";
2
+ import { integer } from "@optique/core/valueparser";
3
+ import { createCommand } from "@/lib/command";
4
+ import { runCommand } from "./run.js";
5
+ export default createCommand({
6
+ def: command("run", object({
7
+ command: constant("run"),
8
+ name: argument(string({ metavar: "NAME" })),
9
+ http: optional(flag("--http")),
10
+ gui: optional(flag("--gui")),
11
+ cli: optional(flag("--cli")),
12
+ prompt: optional(argument(string({ metavar: "PROMPT" }))),
13
+ port: optional(option("-p", "--port", integer())),
14
+ noSession: optional(flag("--no-session")),
15
+ }), { brief: message `Run an agent.` }),
16
+ impl: async ({ name, http, gui, cli, prompt, port, noSession }) => {
17
+ const options = {
18
+ name,
19
+ http: http === true,
20
+ gui: gui === true,
21
+ cli: cli === true,
22
+ noSession: noSession === true,
23
+ };
24
+ if (prompt !== null && prompt !== undefined) {
25
+ options.prompt = prompt;
26
+ }
27
+ if (port !== null && port !== undefined) {
28
+ options.port = port;
29
+ }
30
+ await runCommand(options);
31
+ },
32
+ });