@launchmatic/cli 0.3.1 → 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 +178 -0
- package/dist/index.js +306 -3
- package/package.json +37 -37
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# @launchmatic/cli
|
|
2
|
+
|
|
3
|
+
Deploy from your terminal. The official CLI for [Launchmatic](https://launchmatic.io) — detect, build, and ship your apps to the cloud in seconds.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @launchmatic/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
lm login # Authenticate with GitHub
|
|
15
|
+
lm quicklaunch # Detect, configure, and deploy — one command
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or use **Lightspeed** to create an app from a prompt:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
c -c a real-time chat app with rooms
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Commands
|
|
25
|
+
|
|
26
|
+
### Core
|
|
27
|
+
|
|
28
|
+
| Command | Alias | Description |
|
|
29
|
+
|---------|-------|-------------|
|
|
30
|
+
| `lm login` | | Authenticate via GitHub OAuth |
|
|
31
|
+
| `lm init` | | Initialize a project in the current directory |
|
|
32
|
+
| `lm deploy` | | Build and deploy the current service |
|
|
33
|
+
| `lm quicklaunch` | `lm ql` | Detect, configure, and deploy in one shot |
|
|
34
|
+
| `lm status` | | Show service status, URL, and last deployment |
|
|
35
|
+
| `lm logs` | | Stream live deployment logs |
|
|
36
|
+
| `lm destroy` | | Tear down a service (requires confirmation) |
|
|
37
|
+
|
|
38
|
+
### AI / Lightspeed
|
|
39
|
+
|
|
40
|
+
| Command | Description |
|
|
41
|
+
|---------|-------------|
|
|
42
|
+
| `c -c <prompt>` | Create a new app from a natural language description |
|
|
43
|
+
| `c <prompt>` | Modify an existing service with AI |
|
|
44
|
+
| `lm lightspeed` | Same as `c`, integrated into the main CLI |
|
|
45
|
+
|
|
46
|
+
### Environment & Domains
|
|
47
|
+
|
|
48
|
+
| Command | Description |
|
|
49
|
+
|---------|-------------|
|
|
50
|
+
| `lm env set KEY=VALUE ...` | Set environment variables |
|
|
51
|
+
| `lm env list` | List environment variables |
|
|
52
|
+
| `lm domains add <hostname>` | Add a custom domain (shows DNS config) |
|
|
53
|
+
|
|
54
|
+
### Deployments
|
|
55
|
+
|
|
56
|
+
| Command | Alias | Description |
|
|
57
|
+
|---------|-------|-------------|
|
|
58
|
+
| `lm deployments list` | `lm deploys ls` | View deployment history |
|
|
59
|
+
| `lm deployments info <id>` | | Deployment details |
|
|
60
|
+
| `lm deployments cancel <id>` | | Cancel a running deployment |
|
|
61
|
+
| `lm deployments rollback <id>` | | Roll back to a previous deployment |
|
|
62
|
+
|
|
63
|
+
### Preview Environments
|
|
64
|
+
|
|
65
|
+
| Command | Description |
|
|
66
|
+
|---------|-------------|
|
|
67
|
+
| `lm preview list` | List active preview environments |
|
|
68
|
+
| `lm preview create <branch>` | Create a preview deployment for a branch |
|
|
69
|
+
| `lm preview delete <id>` | Delete a preview environment |
|
|
70
|
+
|
|
71
|
+
### Database
|
|
72
|
+
|
|
73
|
+
| Command | Alias | Description |
|
|
74
|
+
|---------|-------|-------------|
|
|
75
|
+
| `lm db create` | | Provision a new database |
|
|
76
|
+
| `lm db list` | `lm db ls` | List databases |
|
|
77
|
+
| `lm db connect` | `lm db url` | Get connection string |
|
|
78
|
+
| `lm db query <sql>` | `lm db q` | Run a SQL query |
|
|
79
|
+
| `lm db shell` | `lm db sh` | Interactive SQL shell |
|
|
80
|
+
| `lm db tables` | | List tables |
|
|
81
|
+
| `lm db dump` | | Export database dump |
|
|
82
|
+
| `lm db seed` | | Seed database |
|
|
83
|
+
| `lm db credentials` | `lm db creds` | Show database credentials |
|
|
84
|
+
| `lm db link` | | Link a database to a service |
|
|
85
|
+
| `lm db delete` | `lm db rm` | Delete a database |
|
|
86
|
+
|
|
87
|
+
Supports PostgreSQL, Redis, and MongoDB via `--engine`.
|
|
88
|
+
|
|
89
|
+
### GitHub Repo
|
|
90
|
+
|
|
91
|
+
| Command | Alias | Description |
|
|
92
|
+
|---------|-------|-------------|
|
|
93
|
+
| `lm repo view` | `lm repo v` | Show repo info |
|
|
94
|
+
| `lm repo prs` | | List pull requests |
|
|
95
|
+
| `lm repo issues` | | List issues |
|
|
96
|
+
| `lm repo issue-create` | `lm repo ic` | Create an issue |
|
|
97
|
+
| `lm repo branches` | `lm repo br` | List branches |
|
|
98
|
+
| `lm repo commits` | `lm repo log` | Show commit history |
|
|
99
|
+
| `lm repo releases` | `lm repo rl` | List releases |
|
|
100
|
+
| `lm repo actions` | `lm repo runs` | View GitHub Actions runs |
|
|
101
|
+
| `lm repo browse` | `lm repo web` | Open repo in browser |
|
|
102
|
+
| `lm repo clone` | | Clone the repo |
|
|
103
|
+
| `lm repo diff` | | Show diff |
|
|
104
|
+
| `lm repo stash` | | Stash changes |
|
|
105
|
+
|
|
106
|
+
### Infrastructure
|
|
107
|
+
|
|
108
|
+
| Command | Description |
|
|
109
|
+
|---------|-------------|
|
|
110
|
+
| `lm infra list -t <team>` | List pods with CPU, memory, and readiness |
|
|
111
|
+
| `lm infra logs <pod>` | Stream pod logs |
|
|
112
|
+
| `lm infra describe <pod>` | Describe a pod |
|
|
113
|
+
| `lm infra restart <pod>` | Restart a pod |
|
|
114
|
+
| `lm infra delete <pod>` | Delete a pod |
|
|
115
|
+
|
|
116
|
+
### Browser Automation
|
|
117
|
+
|
|
118
|
+
| Command | Alias | Description |
|
|
119
|
+
|---------|-------|-------------|
|
|
120
|
+
| `lm browser screenshot [url]` | `lm b ss` | Take a screenshot |
|
|
121
|
+
| `lm browser pdf [url]` | | Generate a PDF |
|
|
122
|
+
| `lm browser open [url]` | | Open in headed browser |
|
|
123
|
+
| `lm browser test [url]` | | Run smoke tests (links, console, a11y) |
|
|
124
|
+
| `lm browser wait [url]` | | Wait for a page to be reachable |
|
|
125
|
+
| `lm browser codegen [url]` | | Record and generate test code |
|
|
126
|
+
| `lm browser devices` | | List available device profiles |
|
|
127
|
+
|
|
128
|
+
### Utilities
|
|
129
|
+
|
|
130
|
+
| Command | Description |
|
|
131
|
+
|---------|-------------|
|
|
132
|
+
| `lm doctor` | Scan project for deployment issues and fixes |
|
|
133
|
+
| `lm usage summary` | View CPU, memory, network, and replica stats |
|
|
134
|
+
| `lm usage history` | Resource usage over time (default 24h) |
|
|
135
|
+
| `lm api-key list` | List API keys |
|
|
136
|
+
| `lm api-key create <name>` | Create an API key |
|
|
137
|
+
| `lm api-key delete <id>` | Delete an API key |
|
|
138
|
+
|
|
139
|
+
## Examples
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Deploy a project from the current directory
|
|
143
|
+
lm init
|
|
144
|
+
lm deploy
|
|
145
|
+
|
|
146
|
+
# One-command deploy with auto-detection
|
|
147
|
+
lm quicklaunch
|
|
148
|
+
|
|
149
|
+
# Create an app with AI
|
|
150
|
+
c -c a markdown blog with syntax highlighting
|
|
151
|
+
|
|
152
|
+
# Modify an existing app
|
|
153
|
+
c add a dark mode toggle to the settings page
|
|
154
|
+
|
|
155
|
+
# Check project health before deploying
|
|
156
|
+
lm doctor
|
|
157
|
+
|
|
158
|
+
# Manage environment variables
|
|
159
|
+
lm env set DATABASE_URL=postgres://... REDIS_URL=redis://...
|
|
160
|
+
|
|
161
|
+
# Stream logs
|
|
162
|
+
lm logs
|
|
163
|
+
|
|
164
|
+
# Roll back a bad deployment
|
|
165
|
+
lm deployments rollback <deployment-id>
|
|
166
|
+
|
|
167
|
+
# Interactive database shell
|
|
168
|
+
lm db shell
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Requirements
|
|
172
|
+
|
|
173
|
+
- Node.js >= 20
|
|
174
|
+
- A [Launchmatic](https://launchmatic.io) account
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -20211,6 +20211,60 @@ async function api(path6, options = {}) {
|
|
|
20211
20211
|
}
|
|
20212
20212
|
return res.json();
|
|
20213
20213
|
}
|
|
20214
|
+
async function* streamApi(path6, options = {}) {
|
|
20215
|
+
const { accessToken } = getTokens();
|
|
20216
|
+
const url = `${getApiUrl()}${path6}`;
|
|
20217
|
+
const headers = {
|
|
20218
|
+
"Content-Type": "application/json",
|
|
20219
|
+
Accept: "text/event-stream",
|
|
20220
|
+
...options.headers || {}
|
|
20221
|
+
};
|
|
20222
|
+
if (accessToken) headers["Authorization"] = `Bearer ${accessToken}`;
|
|
20223
|
+
const res = await fetch(url, { ...options, headers });
|
|
20224
|
+
if (!res.ok || !res.body) {
|
|
20225
|
+
let message = `HTTP ${res.status}`;
|
|
20226
|
+
try {
|
|
20227
|
+
const body = await res.json();
|
|
20228
|
+
if (body.error) message = body.error;
|
|
20229
|
+
} catch {
|
|
20230
|
+
}
|
|
20231
|
+
throw new ApiError(res.status, message);
|
|
20232
|
+
}
|
|
20233
|
+
const reader = res.body.getReader();
|
|
20234
|
+
const decoder = new TextDecoder();
|
|
20235
|
+
let buffer = "";
|
|
20236
|
+
try {
|
|
20237
|
+
while (true) {
|
|
20238
|
+
const { value, done } = await reader.read();
|
|
20239
|
+
if (done) break;
|
|
20240
|
+
buffer += decoder.decode(value, { stream: true });
|
|
20241
|
+
let boundary = buffer.indexOf("\n\n");
|
|
20242
|
+
while (boundary !== -1) {
|
|
20243
|
+
const frame = buffer.slice(0, boundary);
|
|
20244
|
+
buffer = buffer.slice(boundary + 2);
|
|
20245
|
+
const dataLines = [];
|
|
20246
|
+
for (const line of frame.split("\n")) {
|
|
20247
|
+
if (line.startsWith("data:")) {
|
|
20248
|
+
dataLines.push(line.slice(5).trimStart());
|
|
20249
|
+
}
|
|
20250
|
+
}
|
|
20251
|
+
if (dataLines.length > 0) {
|
|
20252
|
+
const json = dataLines.join("\n");
|
|
20253
|
+
try {
|
|
20254
|
+
yield JSON.parse(json);
|
|
20255
|
+
} catch {
|
|
20256
|
+
}
|
|
20257
|
+
}
|
|
20258
|
+
boundary = buffer.indexOf("\n\n");
|
|
20259
|
+
}
|
|
20260
|
+
}
|
|
20261
|
+
} finally {
|
|
20262
|
+
try {
|
|
20263
|
+
reader.releaseLock();
|
|
20264
|
+
} catch {
|
|
20265
|
+
}
|
|
20266
|
+
}
|
|
20267
|
+
}
|
|
20214
20268
|
|
|
20215
20269
|
// src/commands/login.ts
|
|
20216
20270
|
function authPage(success, error) {
|
|
@@ -20242,7 +20296,7 @@ function authPage(success, error) {
|
|
|
20242
20296
|
<body>
|
|
20243
20297
|
<div class="card">
|
|
20244
20298
|
<div class="logo">
|
|
20245
|
-
<svg width="24" height="24" viewBox="0 0 52 36" fill="none"><polygon points="44,18 8,6 13,18 8,30" stroke="#C8E0FF" stroke-width="2.5" fill="none"/></svg>
|
|
20299
|
+
<svg width="24" height="24" viewBox="0 0 52 36" fill="none" style="transform:rotate(-45deg)"><polygon points="44,18 8,6 13,18 8,30" stroke="#C8E0FF" stroke-width="2.5" fill="none"/></svg>
|
|
20246
20300
|
Launchmatic
|
|
20247
20301
|
</div>
|
|
20248
20302
|
<div class="icon">${icon}</div>
|
|
@@ -20303,7 +20357,9 @@ function waitForOAuth(apiUrl) {
|
|
|
20303
20357
|
if (settled) return false;
|
|
20304
20358
|
settled = true;
|
|
20305
20359
|
rl.close();
|
|
20306
|
-
process.stdin.unref
|
|
20360
|
+
if (typeof process.stdin.unref === "function") {
|
|
20361
|
+
process.stdin.unref();
|
|
20362
|
+
}
|
|
20307
20363
|
server.close();
|
|
20308
20364
|
server.unref();
|
|
20309
20365
|
return true;
|
|
@@ -21328,6 +21384,20 @@ function detectLocal(cwd) {
|
|
|
21328
21384
|
if (existsSync2(join(cwd, "package.json"))) {
|
|
21329
21385
|
const pkg = readPkg(cwd);
|
|
21330
21386
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
21387
|
+
if (deps?.["stirrup-ai"]) {
|
|
21388
|
+
return {
|
|
21389
|
+
runtime: "nodejs",
|
|
21390
|
+
framework: "Stirrup",
|
|
21391
|
+
buildCmd: "npm install",
|
|
21392
|
+
startCmd: "npx stirrup serve",
|
|
21393
|
+
port: 3711,
|
|
21394
|
+
confidence: "high",
|
|
21395
|
+
recommendedStorageGi: 2,
|
|
21396
|
+
recommendedStorageMountPath: "/data",
|
|
21397
|
+
recommendedMemory: "1Gi",
|
|
21398
|
+
requiredEnvVars: ["ANTHROPIC_API_KEY"]
|
|
21399
|
+
};
|
|
21400
|
+
}
|
|
21331
21401
|
const hasIndex = existsSync2(join(cwd, "index.html")) || existsSync2(join(cwd, "public", "index.html"));
|
|
21332
21402
|
const hasServerDep = Object.keys(deps || {}).some(
|
|
21333
21403
|
(d) => /^(express|fastify|next|nuxt|remix|gatsby|astro|vite|svelte|@angular|react-scripts|koa|hapi|nestjs)/.test(
|
|
@@ -25179,9 +25249,241 @@ function registerDeployments(program3) {
|
|
|
25179
25249
|
});
|
|
25180
25250
|
}
|
|
25181
25251
|
|
|
25252
|
+
// src/commands/agent.ts
|
|
25253
|
+
import readline6 from "readline";
|
|
25254
|
+
async function callAgent(message, threadId, systemHint) {
|
|
25255
|
+
return api("/api/v1/agent", {
|
|
25256
|
+
method: "POST",
|
|
25257
|
+
body: JSON.stringify({
|
|
25258
|
+
message,
|
|
25259
|
+
thread_id: threadId,
|
|
25260
|
+
system_hint: systemHint
|
|
25261
|
+
})
|
|
25262
|
+
});
|
|
25263
|
+
}
|
|
25264
|
+
async function streamAgentToTerminal(message, threadId, systemHint, showTools) {
|
|
25265
|
+
const events = streamApi("/api/v1/agent/stream", {
|
|
25266
|
+
method: "POST",
|
|
25267
|
+
body: JSON.stringify({
|
|
25268
|
+
message,
|
|
25269
|
+
thread_id: threadId,
|
|
25270
|
+
system_hint: systemHint
|
|
25271
|
+
})
|
|
25272
|
+
});
|
|
25273
|
+
const summary = {
|
|
25274
|
+
threadId: null,
|
|
25275
|
+
text: "",
|
|
25276
|
+
toolCalls: 0,
|
|
25277
|
+
iterations: 0,
|
|
25278
|
+
failed: false
|
|
25279
|
+
};
|
|
25280
|
+
let textStarted = false;
|
|
25281
|
+
for await (const event of events) {
|
|
25282
|
+
switch (event.type) {
|
|
25283
|
+
case "thread":
|
|
25284
|
+
summary.threadId = event.thread_id;
|
|
25285
|
+
break;
|
|
25286
|
+
case "iteration_start":
|
|
25287
|
+
summary.iterations = event.n;
|
|
25288
|
+
break;
|
|
25289
|
+
case "text_delta":
|
|
25290
|
+
if (!textStarted) {
|
|
25291
|
+
process.stdout.write(source_default.cyan("agent: "));
|
|
25292
|
+
textStarted = true;
|
|
25293
|
+
}
|
|
25294
|
+
process.stdout.write(event.delta);
|
|
25295
|
+
summary.text += event.delta;
|
|
25296
|
+
break;
|
|
25297
|
+
case "tool_call":
|
|
25298
|
+
if (showTools) {
|
|
25299
|
+
if (textStarted) process.stdout.write("\n");
|
|
25300
|
+
process.stdout.write(source_default.dim(` \u2192 ${event.name}`));
|
|
25301
|
+
summary.toolCalls++;
|
|
25302
|
+
}
|
|
25303
|
+
break;
|
|
25304
|
+
case "tool_result":
|
|
25305
|
+
if (showTools) {
|
|
25306
|
+
if (event.ok) process.stdout.write(source_default.dim(` \u2713 ${event.summary}
|
|
25307
|
+
`));
|
|
25308
|
+
else process.stdout.write(source_default.red(` \u2717 ${event.summary}
|
|
25309
|
+
`));
|
|
25310
|
+
textStarted = false;
|
|
25311
|
+
}
|
|
25312
|
+
break;
|
|
25313
|
+
case "error":
|
|
25314
|
+
if (textStarted) process.stdout.write("\n");
|
|
25315
|
+
process.stdout.write(source_default.red(`error: ${event.message}
|
|
25316
|
+
`));
|
|
25317
|
+
summary.failed = true;
|
|
25318
|
+
break;
|
|
25319
|
+
case "done":
|
|
25320
|
+
if (textStarted) process.stdout.write("\n");
|
|
25321
|
+
break;
|
|
25322
|
+
}
|
|
25323
|
+
}
|
|
25324
|
+
return summary;
|
|
25325
|
+
}
|
|
25326
|
+
function buildSystemHint(opts) {
|
|
25327
|
+
const parts = [];
|
|
25328
|
+
if (contextExists()) {
|
|
25329
|
+
try {
|
|
25330
|
+
const ctx = readContext();
|
|
25331
|
+
parts.push(`Default project: ${ctx.projectId}. Default service: ${ctx.serviceId}.`);
|
|
25332
|
+
} catch {
|
|
25333
|
+
}
|
|
25334
|
+
}
|
|
25335
|
+
if (opts.service) parts.push(`User explicitly scoped this session to service: ${opts.service}.`);
|
|
25336
|
+
if (opts.environment) parts.push(`User explicitly scoped this session to environment: ${opts.environment}.`);
|
|
25337
|
+
return parts.length > 0 ? parts.join(" ") : null;
|
|
25338
|
+
}
|
|
25339
|
+
function printToolTrace(calls) {
|
|
25340
|
+
for (const call of calls) {
|
|
25341
|
+
const icon = call.error ? source_default.red("\u2717") : source_default.dim("\u2192");
|
|
25342
|
+
const summary = call.error ? source_default.red(call.error) : source_default.dim("ok");
|
|
25343
|
+
console.log(` ${icon} ${source_default.cyan(call.name)} ${summary}`);
|
|
25344
|
+
}
|
|
25345
|
+
}
|
|
25346
|
+
function prompt5(question) {
|
|
25347
|
+
const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
|
|
25348
|
+
return new Promise((resolve4) => {
|
|
25349
|
+
rl.question(question, (answer) => {
|
|
25350
|
+
rl.close();
|
|
25351
|
+
resolve4(answer);
|
|
25352
|
+
});
|
|
25353
|
+
});
|
|
25354
|
+
}
|
|
25355
|
+
function registerAgent(program3) {
|
|
25356
|
+
program3.command("agent").description("Talk to the Launchmatic agent \u2014 multi-step reasoning over your services").option("-p, --prompt <text>", "Run a single prompt and exit (non-interactive)").option("--json", "Emit machine-readable JSON instead of human-friendly text (disables streaming)").option("--no-stream", "Buffer the full response before printing (default: stream)").option("--thread-id <id>", "Continue an existing thread").option("--service <serviceId>", "Scope the session to a specific service").option("--environment <env>", "Scope the session to a specific environment (e.g. preview, production)").option("--show-tools", "Show each tool call the agent makes (default off in human mode)").action(async (opts) => {
|
|
25357
|
+
if (!isLoggedIn()) {
|
|
25358
|
+
console.error(source_default.red('Not logged in. Run "lm login" first.'));
|
|
25359
|
+
process.exitCode = 1;
|
|
25360
|
+
return;
|
|
25361
|
+
}
|
|
25362
|
+
const systemHint = buildSystemHint(opts);
|
|
25363
|
+
let threadId = opts.threadId ?? null;
|
|
25364
|
+
const useStream = opts.stream !== false && !opts.json;
|
|
25365
|
+
if (opts.prompt) {
|
|
25366
|
+
if (opts.json) {
|
|
25367
|
+
try {
|
|
25368
|
+
const res = await callAgent(opts.prompt, threadId, systemHint);
|
|
25369
|
+
console.log(JSON.stringify(res, null, 2));
|
|
25370
|
+
} catch (err) {
|
|
25371
|
+
console.error(source_default.red(`Agent error: ${err instanceof Error ? err.message : String(err)}`));
|
|
25372
|
+
process.exitCode = 1;
|
|
25373
|
+
}
|
|
25374
|
+
return;
|
|
25375
|
+
}
|
|
25376
|
+
if (useStream) {
|
|
25377
|
+
try {
|
|
25378
|
+
const summary = await streamAgentToTerminal(opts.prompt, threadId, systemHint, opts.showTools ?? false);
|
|
25379
|
+
if (summary.failed) {
|
|
25380
|
+
process.exitCode = 1;
|
|
25381
|
+
return;
|
|
25382
|
+
}
|
|
25383
|
+
if (summary.threadId) {
|
|
25384
|
+
console.log();
|
|
25385
|
+
console.log(source_default.dim(`Thread: ${summary.threadId} (resume with --thread-id ${summary.threadId})`));
|
|
25386
|
+
}
|
|
25387
|
+
} catch (err) {
|
|
25388
|
+
console.error(source_default.red(`Agent error: ${err instanceof Error ? err.message : String(err)}`));
|
|
25389
|
+
process.exitCode = 1;
|
|
25390
|
+
}
|
|
25391
|
+
return;
|
|
25392
|
+
}
|
|
25393
|
+
const spinner = ora("Thinking...").start();
|
|
25394
|
+
try {
|
|
25395
|
+
const res = await callAgent(opts.prompt, threadId, systemHint);
|
|
25396
|
+
spinner.stop();
|
|
25397
|
+
if (opts.showTools && res.tool_calls.length > 0) {
|
|
25398
|
+
printToolTrace(res.tool_calls);
|
|
25399
|
+
console.log();
|
|
25400
|
+
}
|
|
25401
|
+
console.log(res.text);
|
|
25402
|
+
console.log();
|
|
25403
|
+
console.log(source_default.dim(`Thread: ${res.thread_id} (resume with --thread-id ${res.thread_id})`));
|
|
25404
|
+
} catch (err) {
|
|
25405
|
+
spinner.fail(source_default.red(`Agent error: ${err instanceof Error ? err.message : String(err)}`));
|
|
25406
|
+
process.exitCode = 1;
|
|
25407
|
+
}
|
|
25408
|
+
return;
|
|
25409
|
+
}
|
|
25410
|
+
console.log(source_default.bold.cyan("\n Launchmatic Agent") + source_default.dim(" \u2014 type your request, /help for commands, /quit to exit"));
|
|
25411
|
+
if (threadId) console.log(source_default.dim(` Resuming thread ${threadId}
|
|
25412
|
+
`));
|
|
25413
|
+
else console.log();
|
|
25414
|
+
const rl = readline6.createInterface({ input: process.stdin, output: process.stdout });
|
|
25415
|
+
const handleLine = async (line) => {
|
|
25416
|
+
const text = line.trim();
|
|
25417
|
+
if (!text) return false;
|
|
25418
|
+
if (text === "/quit" || text === "/exit") return true;
|
|
25419
|
+
if (text === "/help") {
|
|
25420
|
+
console.log(source_default.dim(" /thread show current thread id"));
|
|
25421
|
+
console.log(source_default.dim(" /new start a new thread"));
|
|
25422
|
+
console.log(source_default.dim(" /tools toggle tool-call display"));
|
|
25423
|
+
console.log(source_default.dim(" /quit exit"));
|
|
25424
|
+
return false;
|
|
25425
|
+
}
|
|
25426
|
+
if (text === "/thread") {
|
|
25427
|
+
console.log(source_default.dim(` ${threadId ?? "(no thread yet)"}`));
|
|
25428
|
+
return false;
|
|
25429
|
+
}
|
|
25430
|
+
if (text === "/new") {
|
|
25431
|
+
threadId = null;
|
|
25432
|
+
console.log(source_default.dim(" Started fresh thread."));
|
|
25433
|
+
return false;
|
|
25434
|
+
}
|
|
25435
|
+
if (text === "/tools") {
|
|
25436
|
+
opts.showTools = !opts.showTools;
|
|
25437
|
+
console.log(source_default.dim(` show-tools: ${opts.showTools ? "on" : "off"}`));
|
|
25438
|
+
return false;
|
|
25439
|
+
}
|
|
25440
|
+
if (useStream) {
|
|
25441
|
+
try {
|
|
25442
|
+
const summary = await streamAgentToTerminal(text, threadId, systemHint, opts.showTools ?? false);
|
|
25443
|
+
if (summary.threadId) threadId = summary.threadId;
|
|
25444
|
+
console.log();
|
|
25445
|
+
} catch (err) {
|
|
25446
|
+
console.error(source_default.red(`Agent error: ${err instanceof Error ? err.message : String(err)}`));
|
|
25447
|
+
}
|
|
25448
|
+
return false;
|
|
25449
|
+
}
|
|
25450
|
+
const spinner = ora("Thinking...").start();
|
|
25451
|
+
try {
|
|
25452
|
+
const res = await callAgent(text, threadId, systemHint);
|
|
25453
|
+
threadId = res.thread_id;
|
|
25454
|
+
spinner.stop();
|
|
25455
|
+
if (opts.showTools && res.tool_calls.length > 0) {
|
|
25456
|
+
printToolTrace(res.tool_calls);
|
|
25457
|
+
console.log();
|
|
25458
|
+
}
|
|
25459
|
+
console.log(source_default.cyan("agent:"), res.text);
|
|
25460
|
+
console.log();
|
|
25461
|
+
} catch (err) {
|
|
25462
|
+
spinner.fail(source_default.red(`Agent error: ${err instanceof Error ? err.message : String(err)}`));
|
|
25463
|
+
}
|
|
25464
|
+
return false;
|
|
25465
|
+
};
|
|
25466
|
+
let done = false;
|
|
25467
|
+
while (!done) {
|
|
25468
|
+
const line = await new Promise((resolve4) => {
|
|
25469
|
+
rl.question(source_default.cyan("> "), (answer) => resolve4(answer));
|
|
25470
|
+
rl.once("close", () => resolve4(null));
|
|
25471
|
+
});
|
|
25472
|
+
if (line === null) {
|
|
25473
|
+
done = true;
|
|
25474
|
+
break;
|
|
25475
|
+
}
|
|
25476
|
+
done = await handleLine(line);
|
|
25477
|
+
}
|
|
25478
|
+
rl.close();
|
|
25479
|
+
console.log(source_default.dim("\n Bye."));
|
|
25480
|
+
void prompt5;
|
|
25481
|
+
});
|
|
25482
|
+
}
|
|
25483
|
+
|
|
25182
25484
|
// src/index.ts
|
|
25183
25485
|
var program2 = new Command();
|
|
25184
|
-
program2.name("lm").description("Launchmatic CLI \u2014 deploy from your terminal").version("0.
|
|
25486
|
+
program2.name("lm").description("Launchmatic CLI \u2014 deploy from your terminal").version("0.4.0");
|
|
25185
25487
|
registerLogin(program2);
|
|
25186
25488
|
registerInit(program2);
|
|
25187
25489
|
registerDeploy(program2);
|
|
@@ -25201,4 +25503,5 @@ registerUsage(program2);
|
|
|
25201
25503
|
registerPreview(program2);
|
|
25202
25504
|
registerApiKey(program2);
|
|
25203
25505
|
registerDeployments(program2);
|
|
25506
|
+
registerAgent(program2);
|
|
25204
25507
|
program2.parse();
|
package/package.json
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@launchmatic/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Launchmatic CLI — deploy from your terminal",
|
|
5
|
-
"private": false,
|
|
6
|
-
"type": "module",
|
|
7
|
-
"bin": {
|
|
8
|
-
"lm": "dist/index.js",
|
|
9
|
-
"c": "dist/c.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist"
|
|
13
|
-
],
|
|
14
|
-
"scripts": {
|
|
15
|
-
"dev": "tsx src/index.ts",
|
|
16
|
-
"build": "tsup",
|
|
17
|
-
"start": "node dist/index.js"
|
|
18
|
-
},
|
|
19
|
-
"dependencies": {
|
|
20
|
-
"playwright-core": "^1.58.2"
|
|
21
|
-
},
|
|
22
|
-
"devDependencies": {
|
|
23
|
-
"@launchmatic/tsconfig": "workspace:*",
|
|
24
|
-
"@launchmatic/validator": "workspace:*",
|
|
25
|
-
"@types/node": "^22.0.0",
|
|
26
|
-
"@types/ws": "^8.5.12",
|
|
27
|
-
"chalk": "^5.3.0",
|
|
28
|
-
"commander": "^12.1.0",
|
|
29
|
-
"conf": "^13.0.1",
|
|
30
|
-
"open": "^10.1.0",
|
|
31
|
-
"ora": "^8.1.0",
|
|
32
|
-
"tsup": "^8.5.1",
|
|
33
|
-
"tsx": "^4.19.0",
|
|
34
|
-
"typescript": "^5.6.3",
|
|
35
|
-
"ws": "^8.18.0"
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@launchmatic/cli",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Launchmatic CLI — deploy from your terminal",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"lm": "dist/index.js",
|
|
9
|
+
"c": "dist/c.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"start": "node dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"playwright-core": "^1.58.2"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@launchmatic/tsconfig": "workspace:*",
|
|
24
|
+
"@launchmatic/validator": "workspace:*",
|
|
25
|
+
"@types/node": "^22.0.0",
|
|
26
|
+
"@types/ws": "^8.5.12",
|
|
27
|
+
"chalk": "^5.3.0",
|
|
28
|
+
"commander": "^12.1.0",
|
|
29
|
+
"conf": "^13.0.1",
|
|
30
|
+
"open": "^10.1.0",
|
|
31
|
+
"ora": "^8.1.0",
|
|
32
|
+
"tsup": "^8.5.1",
|
|
33
|
+
"tsx": "^4.19.0",
|
|
34
|
+
"typescript": "^5.6.3",
|
|
35
|
+
"ws": "^8.18.0"
|
|
36
|
+
}
|
|
37
|
+
}
|