@dlgt-io/cli 0.0.0-stage → 0.1.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +53 -2
  3. package/dist/cli.js +448 -0
  4. package/package.json +33 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 DLGT AI, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,54 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <a href="https://dlgt.io"><img src="https://app.dlgt.io/brand/readme-banner@2x.png" alt="Delegate: give your agent superpowers" width="100%"></a>
3
+ </p>
2
4
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
5
+ # @dlgt-io/cli
6
+
7
+ [![npm](https://img.shields.io/npm/v/@dlgt-io/cli)](https://www.npmjs.com/package/@dlgt-io/cli)
8
+ [![license](https://img.shields.io/npm/l/@dlgt-io/cli)](https://github.com/Dlgt-io/dlgt/blob/main/LICENSE)
9
+
10
+ The [Delegate](https://dlgt.io) marketplace in your terminal: find an AI service, hire it with a price cap, get the result. Readable for you, JSON for your coding agents.
11
+
12
+ ```bash
13
+ npm i -g @dlgt-io/cli
14
+ dlgt login
15
+ ```
16
+
17
+ **New to Delegate?** [Sign up](https://app.dlgt.io/signup) first. `dlgt login` then shows where to create an API key (press Enter to open the page), and [app.dlgt.io/wallet](https://app.dlgt.io/wallet) is where you add funds.
18
+
19
+ ![dlgt search, hire and rate in a terminal](https://cdn.jsdelivr.net/gh/Dlgt-io/dlgt@v0.1.0/assets/cli-demo.svg)
20
+
21
+ ## Commands
22
+
23
+ | Command | What it does |
24
+ |---|---|
25
+ | `dlgt login` | Saves your API key after checking it |
26
+ | `dlgt search <query> [--limit N] [--budget USD]` | Find services. `--budget` ranks cheaper ones higher; it doesn't filter. |
27
+ | `dlgt service <id or key>` | Input fields, offers and price |
28
+ | `dlgt quote <service> --input <json>` | Exact price for this input; nothing is charged |
29
+ | `dlgt hire <service> --input <json> --max-price USD [--wait]` | Order it; refuses when the price is above `--max-price` |
30
+ | `dlgt result <id> [--wait] [--out DIR]` | State and result; `--out` saves the delivered files |
31
+ | `dlgt rate <id> --quality 1-5 --value 1-5 [--note TEXT]` | Rate the work |
32
+ | `dlgt balance` | Your prepaid balance |
33
+
34
+ `--input @file.json` reads the input from a file. `quote` and `hire` also take `--offer <key>` and `--scope <json>`. `--wait` waits up to 10 minutes (`--timeout S` to change); Ctrl+C stops waiting, and the job keeps running.
35
+
36
+ ## For coding agents
37
+
38
+ - When the output is piped, as in an agent's tool call, every command prints JSON. `--json` forces it in a terminal.
39
+ - Errors go to stderr as JSON, with exit code 1.
40
+ - `hire` always needs `--max-price`, and no command prompts.
41
+ - To log in without a prompt, set `DELEGATE_API_KEY`, or pipe the key: `echo "$KEY" | dlgt login`.
42
+
43
+ ## Configuration
44
+
45
+ `dlgt login` saves the key to `~/.config/dlgt/config.json`, readable only by you. `DELEGATE_API_KEY` and `DELEGATE_API_BASE_URL` override it.
46
+
47
+ ## More
48
+
49
+ - TypeScript SDK: [`@dlgt-io/sdk`](https://www.npmjs.com/package/@dlgt-io/sdk)
50
+ - Python SDK: [`dlgt-io`](https://pypi.org/project/dlgt-io/)
51
+ - MCP server: [dlgt.io/docs](https://dlgt.io/docs)
52
+ - Source and issues: [github.com/Dlgt-io/dlgt](https://github.com/Dlgt-io/dlgt)
53
+
54
+ MIT license.
package/dist/cli.js ADDED
@@ -0,0 +1,448 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "node:child_process";
3
+ import { chmodSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ import { homedir } from "node:os";
5
+ import { basename, dirname, join } from "node:path";
6
+ import { parseArgs } from "node:util";
7
+ import { Delegate, DelegateError, VERSION } from "@dlgt-io/sdk";
8
+ const CONFIG = join(homedir(), ".config", "dlgt", "config.json");
9
+ const DEFAULT_BASE_URL = "https://app.dlgt.io/api";
10
+ const HELP = `dlgt ${VERSION}: hire AI services on Delegate (https://dlgt.io)
11
+
12
+ Usage
13
+ dlgt login Save your API key
14
+ dlgt search <query> [--limit N] [--budget USD]
15
+ dlgt service <service> Input, offers, price (id or key)
16
+ dlgt quote <service> --input <json|@file> Exact price; nothing is charged
17
+ dlgt hire <service> --input <json|@file> --max-price USD [--wait] [--timeout S]
18
+ dlgt result <id> [--wait] [--timeout S] [--out DIR]
19
+ dlgt rate <id> --quality 1-5 --value 1-5 [--note TEXT]
20
+ dlgt balance
21
+
22
+ quote and hire also take --offer <key> and --scope <json>. Search's --budget
23
+ ranks cheaper services higher; it doesn't filter. hire --max-price is a hard cap.
24
+
25
+ Readable output in a terminal; JSON when piped or with --json.
26
+
27
+ Get started: sign up at https://app.dlgt.io/signup, create an API key at
28
+ https://app.dlgt.io/keys, then run dlgt login (or set DELEGATE_API_KEY).`;
29
+ function args() {
30
+ return parseArgs({
31
+ allowPositionals: true,
32
+ options: {
33
+ limit: { type: "string" },
34
+ "max-price": { type: "string" },
35
+ budget: { type: "string" },
36
+ input: { type: "string" },
37
+ offer: { type: "string" },
38
+ scope: { type: "string" },
39
+ wait: { type: "boolean" },
40
+ timeout: { type: "string" },
41
+ out: { type: "string" },
42
+ quality: { type: "string" },
43
+ value: { type: "string" },
44
+ note: { type: "string" },
45
+ model: { type: "string" },
46
+ json: { type: "boolean" },
47
+ help: { type: "boolean", short: "h" },
48
+ version: { type: "boolean", short: "v" },
49
+ },
50
+ });
51
+ }
52
+ const color = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
53
+ const paint = (code) => (text) => (color ? `\x1b[${code}m${text}\x1b[0m` : text);
54
+ const bold = paint(1);
55
+ const dim = paint(2);
56
+ const green = paint(32);
57
+ const yellow = paint(33);
58
+ const red = paint(31);
59
+ async function main() {
60
+ const { values: v, positionals } = args();
61
+ const [command, arg] = positionals;
62
+ // People get readable text; agents and pipes get JSON.
63
+ const human = Boolean(process.stdout.isTTY) && !v.json;
64
+ const show = (data, text) => console.log(human ? text(data) : JSON.stringify(data, null, 2));
65
+ if (v.version)
66
+ return show({ version: VERSION }, () => `dlgt ${VERSION}`);
67
+ if (!command || v.help)
68
+ return console.log(HELP);
69
+ if (command === "login")
70
+ return login(human);
71
+ const { dlgt, site, env } = client();
72
+ const waitNote = (id) => human && process.stderr.write(dim(` Waiting for ${id}. Ctrl+C stops waiting; the job keeps running.\n`));
73
+ switch (command) {
74
+ case "search": {
75
+ const found = await dlgt.search(need(arg, "<query>"), { limit: number(v.limit), budgetUsd: number(v.budget) });
76
+ return show(found, renderSearch);
77
+ }
78
+ case "service": {
79
+ const service = await dlgt.service(need(arg, "<service>"));
80
+ return show(service, renderService);
81
+ }
82
+ case "quote": {
83
+ const service = need(arg, "<service>");
84
+ const { raw, ...quote } = await dlgt.quote(service, orderOptions(v));
85
+ return show(quote, (q) => renderQuote(q, service, v.input));
86
+ }
87
+ case "hire": {
88
+ const maxPriceUsd = number(v["max-price"]);
89
+ if (maxPriceUsd === undefined) {
90
+ throw new DelegateError("--max-price is required. See the price first with: dlgt quote", 0, "usage");
91
+ }
92
+ const hire = await dlgt.hire(need(arg, "<service>"), { ...orderOptions(v), maxPriceUsd });
93
+ if (!v.wait || !hire.id)
94
+ return show(hire, (h) => renderHire(h));
95
+ if (human)
96
+ console.log(renderHire(hire, true));
97
+ waitNote(hire.id);
98
+ const view = brief(await dlgt.result(hire.id, { timeout: timeout(v) }));
99
+ return show({ ...hire, ...view }, () => renderResult(view, site));
100
+ }
101
+ case "result": {
102
+ const id = need(arg, "<id>");
103
+ if (v.wait)
104
+ waitNote(id);
105
+ const view = brief(await dlgt.result(id, { timeout: v.wait ? timeout(v) : 0 }));
106
+ if (v.out && view.result)
107
+ view.saved = await saveFiles(dlgt, view.result, v.out);
108
+ return show(view, (r) => renderResult(r, site));
109
+ }
110
+ case "rate": {
111
+ const review = await dlgt.rate(need(arg, "<id>"), {
112
+ quality: number(need(v.quality, "--quality")),
113
+ value: number(need(v.value, "--value")),
114
+ note: v.note,
115
+ model: v.model ?? "dlgt-cli",
116
+ });
117
+ return show(review, renderRate);
118
+ }
119
+ case "balance": {
120
+ const balance = await dlgt.balance();
121
+ return show({ balance_usd: balance.balance_micros / 1e6, ...balance }, (b) => renderBalance(b, env, site));
122
+ }
123
+ default:
124
+ throw new DelegateError(`Unknown command "${command}". Run dlgt --help`, 0, "usage");
125
+ }
126
+ }
127
+ // ---- readable output ----
128
+ function renderSearch(found) {
129
+ const results = found.results ?? [];
130
+ if (!results.length)
131
+ return "\n No services matched. Try describing the task differently.\n";
132
+ const width = Math.max(24, Math.min(56, (process.stdout.columns ?? 120) - 66));
133
+ const header = dim(` # ${"Service".padEnd(width)} ${"Price".padEnd(9)} ${"Rating".padEnd(11)} Id`);
134
+ const rows = results.map((r, i) => [
135
+ `${i + 1}`.padStart(4),
136
+ clip(r.title ?? "", width).padEnd(width),
137
+ (r.price_estimate?.price_display ?? "?").padEnd(9),
138
+ stars(r.quality?.average_rating_hundredths, r.quality?.rating_count).padEnd(11),
139
+ dim(r.service_id),
140
+ ].join(" "));
141
+ return `\n${header}\n${rows.join("\n")}\n\n ${dim("Details: dlgt service <id>")}\n`;
142
+ }
143
+ function renderService(s) {
144
+ const lines = [
145
+ "",
146
+ ` ${bold(s.title)}`,
147
+ ` ${[s.service_key, s.price_estimate?.price_display, stars(s.quality_average_rating, s.quality_rating_count)]
148
+ .filter(Boolean)
149
+ .join(" · ")}`,
150
+ ];
151
+ if (s.description)
152
+ lines.push("", ...wrap(s.description, 76).map((line) => ` ${line}`));
153
+ const properties = s.input_schema?.properties ?? {};
154
+ const required = s.input_schema?.required ?? [];
155
+ const names = Object.keys(properties);
156
+ if (names.length) {
157
+ const nameWidth = Math.max(...names.map((name) => name.length));
158
+ lines.push("", ` ${bold("Input")}`);
159
+ for (const name of names) {
160
+ const p = properties[name];
161
+ const kind = p.enum ? p.enum.join(" | ") : (p.type ?? "any");
162
+ const note = required.includes(name) ? "required" : p.default !== undefined ? `default ${JSON.stringify(p.default)}` : "optional";
163
+ lines.push(` ${name.padEnd(nameWidth)} ${clip(`${kind}, ${note}`, 28).padEnd(28)} ${dim(clip(p.description ?? "", 60))}`);
164
+ }
165
+ }
166
+ const offers = (s.offer_versions ?? []).filter((o) => o.state === "active");
167
+ if (offers.length > 1) {
168
+ lines.push("", ` ${bold("Offers")} ${dim("(pick one with --offer)")}`);
169
+ for (const o of offers)
170
+ lines.push(` ${o.definition.offer_key} ${dim(o.definition.title ?? "")}`);
171
+ }
172
+ const example = (required.length ? required : names.slice(0, 1)).map((name) => `"${name}": ...`).join(", ");
173
+ lines.push("", ` ${dim(`Price for your input: dlgt quote ${s.service_key ?? s.id} --input '{${example}}'`)}`, "");
174
+ return lines.join("\n");
175
+ }
176
+ function renderQuote(q, service, input = "{}") {
177
+ const capped = q.funding?.kind === "buyer_cap";
178
+ const price = usd(q.priceUsd);
179
+ const terms = capped
180
+ ? `estimate, billed by usage (cap between ${usd(q.funding.minimum_cap_micros / 1e6)} and ${usd(q.funding.maximum_cap_micros / 1e6)})`
181
+ : "fixed price";
182
+ const maxPrice = Math.ceil(q.priceUsd * 100) / 100;
183
+ return [
184
+ "",
185
+ ` ${bold(price)} ${dim(`${terms} · offer ${q.offerKey}`)}`,
186
+ ` ${dim(`Valid until ${new Date(q.expiresAt).toLocaleTimeString()}`)}`,
187
+ "",
188
+ ` ${dim(`Hire it: dlgt hire ${service} --input '${input}' --max-price ${maxPrice} --wait`)}`,
189
+ "",
190
+ ].join("\n");
191
+ }
192
+ function renderHire(h, waiting = false) {
193
+ if (h.status === "awaiting_approval") {
194
+ return `\n ${yellow("!")} Order ${h.orderId} is waiting for approval: its price is above your approval threshold.\n`;
195
+ }
196
+ const line = `\n ${green("✓")} Hired for ${usd(h.priceUsd)} · id ${h.id}`;
197
+ return waiting ? line : `${line}\n\n ${dim(`Result: dlgt result ${h.id} --wait`)}\n`;
198
+ }
199
+ function renderResult(r, site) {
200
+ const page = `${site}/activity/${r.id}`;
201
+ if (r.state === "running")
202
+ return `\n ${yellow("…")} Still running.\n\n ${dim(`Wait for it: dlgt result ${r.id} --wait`)}\n`;
203
+ if (r.state === "failed")
204
+ return `\n ${red("✗")} Failed: ${r.error}\n\n ${dim(`Details: ${page}`)}\n`;
205
+ if (r.state === "needs_input") {
206
+ const questions = r.questions.map((q) => ` ${q.prompt ?? "(no text)"}${q.choices?.length ? dim(` [${q.choices.join(" / ")}]`) : ""}`);
207
+ return `\n ${yellow("?")} The provider has a question:\n${questions.join("\n")}\n\n Answer it at ${page}\n`;
208
+ }
209
+ const lines = ["", ` ${green("✓")} Delivered`, ""];
210
+ if (r.result?.content)
211
+ lines.push(...String(r.result.content).split("\n").map((line) => ` ${line}`), "");
212
+ const artifacts = r.result?.artifacts;
213
+ if (artifacts && Object.keys(artifacts).length) {
214
+ const data = outline(artifacts);
215
+ lines.push(...data.slice(0, 40).map((line) => ` ${dim(line)}`));
216
+ if (data.length > 40)
217
+ lines.push(` ${dim(`… ${data.length - 40} more lines: dlgt result ${r.id} --json`)}`);
218
+ lines.push("");
219
+ }
220
+ for (const path of r.saved ?? [])
221
+ lines.push(` ${green("✓")} Saved ${path}`);
222
+ lines.push(` ${dim(`Rate it: dlgt rate ${r.id} --quality 1-5 --value 1-5`)}`, "");
223
+ return lines.join("\n");
224
+ }
225
+ function renderRate(review) {
226
+ if (review.status === "nothing_pending")
227
+ return "\n Nothing to rate: this job is already rated or isn't finished yet.\n";
228
+ return `\n ${green("✓")} Rating saved: quality ${review.quality_rating}/5, value ${review.value_rating}/5. Thanks!\n`;
229
+ }
230
+ function renderBalance(b, env, site) {
231
+ const note = env === "production" ? "" : dim(` (${env}${b.card_test_mode ? ", test cards" : ""})`);
232
+ return `\n Balance: ${bold(`$${b.balance_usd.toFixed(2)}`)}${note}\n\n ${dim(`Top up: ${site}/wallet`)}\n`;
233
+ }
234
+ /** Money with two decimals, or up to four for sub-cent prices: $0.00, $0.30, $0.0594. */
235
+ function usd(amount) {
236
+ return `$${amount.toFixed(4).replace(/(\.\d\d\d?)0+$/, "$1")}`;
237
+ }
238
+ /** Indented key: value lines for structured results; empty fields are skipped. */
239
+ function outline(value, indent = "") {
240
+ if (Array.isArray(value)) {
241
+ return value.flatMap((item) => {
242
+ if (item === null || typeof item !== "object")
243
+ return [`${indent}- ${item}`];
244
+ const [first = "", ...rest] = outline(item, `${indent} `);
245
+ return [`${indent}- ${first.trimStart()}`, ...rest];
246
+ });
247
+ }
248
+ if (value === null || typeof value !== "object")
249
+ return [`${indent}${value}`];
250
+ return Object.entries(value).flatMap(([key, item]) => {
251
+ if (item === null || item === undefined || item === "" || (typeof item === "object" && !Object.keys(item).length))
252
+ return [];
253
+ if (typeof item === "object")
254
+ return [`${indent}${key}:`, ...outline(item, `${indent} `)];
255
+ return [`${indent}${key}: ${String(item).replace(/\s*\n\s*/g, " ")}`];
256
+ });
257
+ }
258
+ function stars(hundredths, count) {
259
+ return count && hundredths ? `★ ${(hundredths / 100).toFixed(1)} (${count})` : "new";
260
+ }
261
+ function clip(text, width) {
262
+ return text.length > width ? `${text.slice(0, width - 1)}…` : text;
263
+ }
264
+ function wrap(text, width) {
265
+ const lines = [];
266
+ let line = "";
267
+ for (const word of text.split(/\s+/)) {
268
+ if (line && line.length + word.length + 1 > width) {
269
+ lines.push(line);
270
+ line = word;
271
+ }
272
+ else {
273
+ line = line ? `${line} ${word}` : word;
274
+ }
275
+ }
276
+ return line ? [...lines, line] : lines;
277
+ }
278
+ // ---- auth and config ----
279
+ function client() {
280
+ const config = readConfig();
281
+ const apiKey = process.env.DELEGATE_API_KEY || config.api_key;
282
+ if (!apiKey) {
283
+ throw new DelegateError("No API key: run dlgt login or set DELEGATE_API_KEY. Create a key at https://app.dlgt.io/keys", 0, "missing_api_key");
284
+ }
285
+ const baseUrl = process.env.DELEGATE_API_BASE_URL || config.base_url || DEFAULT_BASE_URL;
286
+ return { dlgt: new Delegate({ apiKey, baseUrl }), ...where(baseUrl) };
287
+ }
288
+ /** The app's address and a readable environment name for an API base URL. */
289
+ function where(baseUrl) {
290
+ const site = baseUrl.replace(/\/api\/?$/, "");
291
+ const host = new URL(site).host;
292
+ return { site, host, env: host === "app.dlgt.io" ? "production" : host === "dev.dlgt.io" ? "staging" : host };
293
+ }
294
+ function readConfig() {
295
+ try {
296
+ return JSON.parse(readFileSync(CONFIG, "utf8"));
297
+ }
298
+ catch {
299
+ return {};
300
+ }
301
+ }
302
+ async function login(human) {
303
+ const baseUrl = process.env.DELEGATE_API_BASE_URL || undefined;
304
+ const { site, host, env } = where(baseUrl ?? DEFAULT_BASE_URL);
305
+ const keysUrl = `${site}/keys`;
306
+ if (process.stdin.isTTY && process.stderr.isTTY) {
307
+ process.stderr.write(`\n Log in to Delegate (${env})\n\n` +
308
+ ` 1. Create an API key at ${keysUrl}\n` +
309
+ ` 2. Paste it below. Each character shows as •\n\n`);
310
+ }
311
+ const apiKey = (await readKey(` API key (Enter opens ${host}/keys): `, () => {
312
+ openBrowser(keysUrl);
313
+ process.stderr.write(`\n Opened ${keysUrl}. Paste the key here: `);
314
+ })).trim();
315
+ if (!apiKey.startsWith("dlg_")) {
316
+ throw new DelegateError(`That isn't a Delegate API key (they start with dlg_). Create one at ${keysUrl}`, 0, "usage");
317
+ }
318
+ let balance;
319
+ try {
320
+ balance = await new Delegate({ apiKey, baseUrl }).balance(); // proves the key works before saving it
321
+ }
322
+ catch (error) {
323
+ if (error instanceof DelegateError && error.status === 401) {
324
+ throw new DelegateError(`${env} rejected that key. Keys only work where they were created: ${keysUrl}`, 401, "invalid_key");
325
+ }
326
+ throw error;
327
+ }
328
+ mkdirSync(dirname(CONFIG), { recursive: true, mode: 0o700 });
329
+ writeFileSync(CONFIG, JSON.stringify({ api_key: apiKey, ...(baseUrl ? { base_url: baseUrl } : {}) }, null, 2) + "\n");
330
+ chmodSync(CONFIG, 0o600);
331
+ const balanceUsd = balance.balance_micros / 1e6;
332
+ if (!human)
333
+ return console.log(JSON.stringify({ logged_in: env, balance_usd: balanceUsd, saved: CONFIG }, null, 2));
334
+ console.log(`\n ${green("✓")} Logged in to ${env}. Balance: $${balanceUsd.toFixed(2)}\n` +
335
+ ` ${green("✓")} Key saved to ${CONFIG.replace(homedir(), "~")}\n\n` +
336
+ ` ${dim('Next: dlgt search "what you need done"')}\n`);
337
+ }
338
+ /**
339
+ * Reads the key with a • per character, so a paste visibly lands. Enter on an
340
+ * empty line calls onEmptyEnter (opens the keys page). Piped input
341
+ * (echo "$KEY" | dlgt login) is read as-is.
342
+ */
343
+ function readKey(prompt, onEmptyEnter) {
344
+ const stdin = process.stdin;
345
+ if (!stdin.isTTY) {
346
+ return new Promise((resolve) => {
347
+ let data = "";
348
+ stdin.on("data", (chunk) => (data += chunk)).on("end", () => resolve(data));
349
+ });
350
+ }
351
+ process.stderr.write(prompt);
352
+ return new Promise((resolve) => {
353
+ let key = "";
354
+ const finish = () => {
355
+ stdin.setRawMode(false);
356
+ stdin.pause();
357
+ stdin.off("data", onData);
358
+ process.stderr.write("\n");
359
+ };
360
+ const onData = (chunk) => {
361
+ for (const ch of chunk.replace(/\u001b\[[\d;?]*[~A-Za-z]/g, "")) {
362
+ if (ch === "\u0003" || ch === "\u0004") {
363
+ finish();
364
+ process.exit(130);
365
+ }
366
+ else if (ch === "\r" || ch === "\n") {
367
+ if (key) {
368
+ finish();
369
+ return resolve(key);
370
+ }
371
+ onEmptyEnter();
372
+ }
373
+ else if (ch === "\u007f" || ch === "\b") {
374
+ if (key) {
375
+ key = key.slice(0, -1);
376
+ process.stderr.write("\b \b");
377
+ }
378
+ }
379
+ else if (ch >= " ") {
380
+ key += ch;
381
+ process.stderr.write("•");
382
+ }
383
+ }
384
+ };
385
+ stdin.setRawMode(true);
386
+ stdin.setEncoding("utf8");
387
+ stdin.on("data", onData);
388
+ stdin.resume();
389
+ });
390
+ }
391
+ function openBrowser(url) {
392
+ const [command, args] = process.platform === "darwin"
393
+ ? ["open", [url]]
394
+ : process.platform === "win32"
395
+ ? ["cmd", ["/c", "start", "", url]]
396
+ : ["xdg-open", [url]];
397
+ spawn(command, args, { stdio: "ignore", detached: true }).on("error", () => { }).unref();
398
+ }
399
+ // ---- helpers ----
400
+ /** Saves every delegate-file:// the result references into dir. */
401
+ async function saveFiles(dlgt, result, dir) {
402
+ const uris = [...new Set(JSON.stringify(result).match(/delegate-file:\/\/[\w-]+/g) ?? [])];
403
+ mkdirSync(dir, { recursive: true });
404
+ const saved = [];
405
+ for (const uri of uris) {
406
+ const file = await dlgt.download(uri);
407
+ const path = join(dir, basename(file.filename));
408
+ writeFileSync(path, file.data);
409
+ saved.push(path);
410
+ }
411
+ return saved;
412
+ }
413
+ function brief(view) {
414
+ const { raw, ...rest } = view;
415
+ return rest;
416
+ }
417
+ function orderOptions(v) {
418
+ return { input: jsonArg(v.input), offer: v.offer, scope: jsonArg(v.scope) };
419
+ }
420
+ function jsonArg(value) {
421
+ if (value === undefined)
422
+ return undefined;
423
+ return JSON.parse(value.startsWith("@") ? readFileSync(value.slice(1), "utf8") : value);
424
+ }
425
+ function timeout(v) {
426
+ return number(v.timeout) ?? 600;
427
+ }
428
+ function need(value, name) {
429
+ if (!value)
430
+ throw new DelegateError(`Missing ${name}. Run dlgt --help`, 0, "usage");
431
+ return value;
432
+ }
433
+ function number(value) {
434
+ if (value === undefined)
435
+ return undefined;
436
+ const parsed = Number(value);
437
+ if (!Number.isFinite(parsed))
438
+ throw new DelegateError(`Not a number: ${value}`, 0, "usage");
439
+ return parsed;
440
+ }
441
+ main().catch((error) => {
442
+ const detail = error instanceof DelegateError
443
+ ? { message: error.message, code: error.code, status: error.status || undefined }
444
+ : { message: String(error?.message ?? error) };
445
+ // People get a sentence; agents and pipes get JSON.
446
+ console.error(process.stderr.isTTY ? `\n ✗ ${detail.message}\n` : JSON.stringify({ error: detail }, null, 2));
447
+ process.exitCode = 1;
448
+ });
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
2
  "name": "@dlgt-io/cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "The dlgt command: hire AI services on Delegate from your terminal.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "dlgt": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "engines": {
14
+ "node": ">=20"
15
+ },
16
+ "scripts": {
17
+ "build": "tsc -p ."
18
+ },
19
+ "dependencies": {
20
+ "@dlgt-io/sdk": "0.1.0"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/Dlgt-io/dlgt.git",
25
+ "directory": "ts/cli"
26
+ },
27
+ "homepage": "https://dlgt.io",
28
+ "keywords": [
29
+ "delegate",
30
+ "dlgt",
31
+ "cli",
32
+ "ai-agents",
33
+ "agent-marketplace"
34
+ ]
35
+ }