moshcode 0.24.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/LICENSE +21 -0
- package/README.md +580 -0
- package/bin/moshcode.mjs +674 -0
- package/bin/moshscript.mjs +29 -0
- package/examples/alive.mosh +6 -0
- package/examples/scripting-the-cli.mosh +21 -0
- package/examples/team-secrets.mosh +20 -0
- package/examples/templates/bun-caddy-sqlite/.env.example +14 -0
- package/examples/templates/bun-caddy-sqlite/Caddyfile +18 -0
- package/examples/templates/bun-caddy-sqlite/README.md +97 -0
- package/examples/templates/bun-caddy-sqlite/deploy/moshcode-dns.service +39 -0
- package/examples/templates/bun-caddy-sqlite/deploy/moshpit-service.service +38 -0
- package/examples/templates/bun-caddy-sqlite/package.json +15 -0
- package/examples/templates/bun-caddy-sqlite/src/db.ts +47 -0
- package/examples/templates/bun-caddy-sqlite/src/server.ts +44 -0
- package/examples/templates/bun-caddy-sqlite/template.json +10 -0
- package/examples/templates/caddy-proxy/Caddyfile +36 -0
- package/examples/templates/caddy-proxy/README.md +104 -0
- package/examples/templates/caddy-proxy/deploy/moshcode-dns.service +39 -0
- package/examples/templates/caddy-proxy/template.json +8 -0
- package/examples/templates/caddy-static/Caddyfile +16 -0
- package/examples/templates/caddy-static/README.md +90 -0
- package/examples/templates/caddy-static/deploy/moshcode-dns.service +39 -0
- package/examples/templates/caddy-static/site/index.html +11 -0
- package/examples/templates/caddy-static/template.json +8 -0
- package/install.sh +194 -0
- package/package.json +28 -0
- package/prd/0000-template.md +49 -0
- package/prd/0001-wrap-ugig-and-coinpay-clis.md +121 -0
- package/prd/0002-separate-agent-and-raw-engine-launches.md +113 -0
- package/prd/0003-cross-engine-mcp-and-skill-installation.md +165 -0
- package/prd/0004-moshscript-run-programmable-moshcode.md +344 -0
- package/prd/0005-hosted-moshpit-resolver.md +192 -0
- package/prd/0006-help.md +359 -0
- package/prd/0007-profullstack-site-init.md +1183 -0
- package/prd/README.md +26 -0
- package/src/ads.mjs +58 -0
- package/src/auth.mjs +193 -0
- package/src/cli-schema.mjs +533 -0
- package/src/cli.mjs +118 -0
- package/src/commands.mjs +259 -0
- package/src/completion.mjs +594 -0
- package/src/console.mjs +244 -0
- package/src/dns-system.mjs +404 -0
- package/src/dns.mjs +2872 -0
- package/src/doh-server.mjs +256 -0
- package/src/doh.mjs +218 -0
- package/src/engines.mjs +385 -0
- package/src/escalate.mjs +85 -0
- package/src/help.mjs +443 -0
- package/src/integrations.mjs +265 -0
- package/src/mcp-catalog.mjs +50 -0
- package/src/mcp.mjs +155 -0
- package/src/mirror.mjs +187 -0
- package/src/notify.mjs +86 -0
- package/src/open-url.mjs +34 -0
- package/src/parking-http.mjs +65 -0
- package/src/pins.mjs +190 -0
- package/src/pit-url.mjs +13 -0
- package/src/prd.mjs +341 -0
- package/src/pty.mjs +176 -0
- package/src/pwd.mjs +103 -0
- package/src/registry.mjs +37 -0
- package/src/release-install.mjs +191 -0
- package/src/runtime.mjs +161 -0
- package/src/selfupdate.mjs +215 -0
- package/src/serve.mjs +502 -0
- package/src/skills.mjs +93 -0
- package/src/tabs.mjs +144 -0
- package/src/templates.mjs +456 -0
- package/src/tools.mjs +231 -0
- package/src/trade.mjs +137 -0
- package/src/trust.mjs +712 -0
- package/src/tui.mjs +736 -0
- package/src/ui.mjs +49 -0
- package/src/uninstall.mjs +113 -0
- package/src/upgrade.mjs +217 -0
package/src/tools.mjs
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// Adjacent workflow CLIs moshcode can install and transparently invoke.
|
|
2
|
+
// These are deliberately separate from coding engines: UGig owns marketplace
|
|
3
|
+
// workflows, CoinPay owns payment workflows, c0mpute owns the compute network,
|
|
4
|
+
// the cloud CLIs below own deploys/secrets/infra, and moshcode only conducts
|
|
5
|
+
// their native command lines.
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
import { isInstalled, openPassthrough } from "./engines.mjs";
|
|
11
|
+
|
|
12
|
+
// gh, supabase, and doctl publish only GitHub release binaries — no official
|
|
13
|
+
// cross-platform install script between them — so their install spec runs our
|
|
14
|
+
// own downloader instead of a vendor one. See src/release-install.mjs.
|
|
15
|
+
const RELEASE_INSTALLER = path.join(path.dirname(fileURLToPath(import.meta.url)), "release-install.mjs");
|
|
16
|
+
const releaseInstall = (tool) => ({ cmd: process.execPath, args: [RELEASE_INSTALLER, tool] });
|
|
17
|
+
|
|
18
|
+
export const TOOLS = {
|
|
19
|
+
ugig: {
|
|
20
|
+
desc: "UGig — freelance marketplace CLI for humans and agents",
|
|
21
|
+
bin: "ugig",
|
|
22
|
+
// UGig isn't published to npm — it ships via its own install script.
|
|
23
|
+
install: { cmd: "bash", args: ["-c", "curl -fsSL https://ugig.net/install.sh | bash"] },
|
|
24
|
+
},
|
|
25
|
+
coinpay: {
|
|
26
|
+
desc: "CoinPay — wallets, payments, swaps, escrow, and settlement",
|
|
27
|
+
bin: "coinpay",
|
|
28
|
+
// CoinPay ships via its own install script (fetched from GitHub), not npm.
|
|
29
|
+
install: { cmd: "sh", args: ["-c", "curl -fsSL https://coinpayportal.com/install.sh | sh"] },
|
|
30
|
+
},
|
|
31
|
+
c0mpute: {
|
|
32
|
+
desc: "c0mpute — decentralized compute network CLI",
|
|
33
|
+
bin: "c0mpute",
|
|
34
|
+
// c0mpute ships via its own install script (the v1 stack installer).
|
|
35
|
+
install: { cmd: "sh", args: ["-c", "curl -fsSL https://c0mpute.com/install.sh | sh"] },
|
|
36
|
+
},
|
|
37
|
+
secrets: {
|
|
38
|
+
desc: "LogicSRC — end-to-end-encrypted team credential sharing (login, teams, credentials)",
|
|
39
|
+
// The passthrough target is the `logicsrc` binary; the moshcode command is
|
|
40
|
+
// `/secrets` so it reads as "manage secrets". LOGICSRC_BIN points at a local
|
|
41
|
+
// build before logicsrc ships a global install.
|
|
42
|
+
bin: process.env.LOGICSRC_BIN || "logicsrc",
|
|
43
|
+
// LogicSRC ships via its own install script (same pattern as the others).
|
|
44
|
+
install: { cmd: "sh", args: ["-c", "curl -fsSL https://logicsrc.com/install.sh | sh"] },
|
|
45
|
+
},
|
|
46
|
+
railway: {
|
|
47
|
+
desc: "Railway — deploy and manage Railway projects, services, and env vars",
|
|
48
|
+
bin: "railway",
|
|
49
|
+
// Railway's shell installer wants bash process substitution
|
|
50
|
+
// (`bash <(curl …)`), which does not survive `sh -c "curl … | sh"`, and the
|
|
51
|
+
// agents.railway.com variant additionally runs `railway setup agent`, which
|
|
52
|
+
// rewrites local agent tool configs — a side effect an install command has
|
|
53
|
+
// no business having. The official npm package is cross-platform and
|
|
54
|
+
// idempotent, so it doubles as the upgrade path.
|
|
55
|
+
install: { cmd: "npm", args: ["install", "-g", "@railway/cli"] },
|
|
56
|
+
},
|
|
57
|
+
gh: {
|
|
58
|
+
desc: "GitHub CLI — repos, PRs, issues, releases, and Actions",
|
|
59
|
+
bin: "gh",
|
|
60
|
+
install: releaseInstall("gh"),
|
|
61
|
+
},
|
|
62
|
+
supabase: {
|
|
63
|
+
desc: "Supabase — local stack, migrations, edge functions, and projects",
|
|
64
|
+
bin: "supabase",
|
|
65
|
+
// Supabase does not support a global npm install, so fetching the release
|
|
66
|
+
// binary is what gives you a real `supabase` on PATH instead of `npx supabase`.
|
|
67
|
+
install: releaseInstall("supabase"),
|
|
68
|
+
},
|
|
69
|
+
doppler: {
|
|
70
|
+
desc: "Doppler — sync secrets and run commands with injected env",
|
|
71
|
+
bin: "doppler",
|
|
72
|
+
// Doppler's official script installs to /usr/local/bin via sudo by default;
|
|
73
|
+
// --install-path keeps it user-local (and implies --no-package-manager).
|
|
74
|
+
// The script verifies its own signature, so it needs `gpgv` on PATH — it
|
|
75
|
+
// exits 3 with an explicit message when gnupg is missing.
|
|
76
|
+
install: {
|
|
77
|
+
cmd: "sh",
|
|
78
|
+
args: [
|
|
79
|
+
"-c",
|
|
80
|
+
'mkdir -p "$HOME/.local/bin" && curl -fsSL https://cli.doppler.com/install.sh | sh -s -- --install-path "$HOME/.local/bin"',
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
upgrade: { cmd: "doppler", args: ["update"] },
|
|
84
|
+
},
|
|
85
|
+
doctl: {
|
|
86
|
+
desc: "DigitalOcean — droplets, apps, databases, Kubernetes, and Spaces",
|
|
87
|
+
bin: "doctl",
|
|
88
|
+
install: releaseInstall("doctl"),
|
|
89
|
+
},
|
|
90
|
+
turso: {
|
|
91
|
+
desc: "Turso — libSQL/SQLite at the edge (auth signup/login, db, replicas)",
|
|
92
|
+
bin: "turso",
|
|
93
|
+
// https://github.com/tursodatabase/turso-cli — the official installer, which
|
|
94
|
+
// unpacks to $HOME/.turso and appends that dir to your shell profile. It is
|
|
95
|
+
// therefore on PATH only for the NEXT shell, so PATH alone would report turso
|
|
96
|
+
// as missing (and /turso would fail to launch it) in the session that
|
|
97
|
+
// installed it — and in every already-running one. binDirs searches the
|
|
98
|
+
// install dir after PATH so the binary is found either way.
|
|
99
|
+
binDirs: [path.join(homedir(), ".turso")],
|
|
100
|
+
install: { cmd: "bash", args: ["-c", "curl -sSfL https://get.tur.so/install.sh | bash"] },
|
|
101
|
+
},
|
|
102
|
+
tailscale: {
|
|
103
|
+
desc: "Tailscale — WireGuard mesh VPN (up, status, ssh, serve, funnel)",
|
|
104
|
+
bin: "tailscale",
|
|
105
|
+
// The odd one out: tailscale is a system daemon, not a standalone binary, so
|
|
106
|
+
// it cannot be dropped in ~/.local/bin like gh/supabase/doctl. The official
|
|
107
|
+
// script goes through the distro package manager and enables `tailscaled`,
|
|
108
|
+
// which means it needs root — it finds sudo/doas itself and may prompt for a
|
|
109
|
+
// password (stdio is inherited, so the prompt works). On macOS the same
|
|
110
|
+
// script delegates to the App Store.
|
|
111
|
+
install: { cmd: "sh", args: ["-c", "curl -fsSL https://tailscale.com/install.sh | sh"] },
|
|
112
|
+
// Native updater on Linux (v1.36+) and Windows. macOS updates come from the
|
|
113
|
+
// App Store, so there it fails with tailscale's own message rather than
|
|
114
|
+
// silently re-adding package repos.
|
|
115
|
+
upgrade: { cmd: "tailscale", args: ["update"] },
|
|
116
|
+
},
|
|
117
|
+
alpaca: {
|
|
118
|
+
desc: "Alpaca — paper/live trading, market data, positions, and watchlists",
|
|
119
|
+
bin: "alpaca",
|
|
120
|
+
// Official Go install documented by Alpaca. Go writes to $GOBIN when set,
|
|
121
|
+
// otherwise $GOPATH/bin (normally ~/go/bin); binDirs covers that default in
|
|
122
|
+
// an already-running shell whose PATH has not picked it up yet.
|
|
123
|
+
binDirs: [path.join(homedir(), "go", "bin")],
|
|
124
|
+
install: {
|
|
125
|
+
cmd: "go",
|
|
126
|
+
args: ["install", "github.com/alpacahq/cli/cmd/alpaca@latest"],
|
|
127
|
+
},
|
|
128
|
+
installHelp: "Go is required to install Alpaca; install Go, then retry `moshcode install alpaca`.",
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/** Resolve a name to `[key, tool]`, or null. */
|
|
133
|
+
export function resolveTool(token) {
|
|
134
|
+
if (!token) return null;
|
|
135
|
+
const key = String(token).trim().toLowerCase();
|
|
136
|
+
// Own properties only: TOOLS is a plain object literal, so a name like
|
|
137
|
+
// `constructor` or `__proto__` would otherwise resolve to something off
|
|
138
|
+
// Object.prototype and be handed on as a tool with no bin/install.
|
|
139
|
+
return Object.hasOwn(TOOLS, key) ? [key, TOOLS[key]] : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Tool entries annotated with native executable install status. */
|
|
143
|
+
export function toolStatus() {
|
|
144
|
+
return Object.entries(TOOLS).map(([key, tool]) => ({
|
|
145
|
+
key,
|
|
146
|
+
...tool,
|
|
147
|
+
installed: isInstalled(tool.bin, tool.binDirs),
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function toolList() {
|
|
152
|
+
return Object.entries(TOOLS)
|
|
153
|
+
.map(([key, tool]) => ` ${key.padEnd(10)} ${tool.desc}`)
|
|
154
|
+
.join("\n");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Prefer a native updater when one is added; npm installs are idempotent. */
|
|
158
|
+
export function toolUpgradeSpec(tool) {
|
|
159
|
+
return tool.upgrade || tool.install;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Invoke a tool without parsing or modifying its arguments or streams. */
|
|
163
|
+
export function openTool(tool, args = [], opts = {}) {
|
|
164
|
+
return openPassthrough(tool, args, opts);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Generic utilities used by the app/package surface.
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Format a number as currency
|
|
171
|
+
*/
|
|
172
|
+
export function formatCurrency(amount, currency = 'USD') {
|
|
173
|
+
return new Intl.NumberFormat('en-US', {
|
|
174
|
+
style: 'currency',
|
|
175
|
+
currency
|
|
176
|
+
}).format(amount);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Generate a random ID
|
|
181
|
+
*/
|
|
182
|
+
export function generateId(length = 8) {
|
|
183
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
184
|
+
let result = '';
|
|
185
|
+
for (let i = 0; i < length; i++) {
|
|
186
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Debounce function calls
|
|
193
|
+
*/
|
|
194
|
+
export function debounce(fn, delay = 300) {
|
|
195
|
+
let timeoutId;
|
|
196
|
+
return function (...args) {
|
|
197
|
+
clearTimeout(timeoutId);
|
|
198
|
+
timeoutId = setTimeout(() => fn.apply(this, args), delay);
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Deep clone an object
|
|
204
|
+
*/
|
|
205
|
+
export function deepClone(obj) {
|
|
206
|
+
return JSON.parse(JSON.stringify(obj));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Sleep for ms milliseconds
|
|
211
|
+
*/
|
|
212
|
+
export function sleep(ms) {
|
|
213
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Retry a function with exponential backoff
|
|
218
|
+
*/
|
|
219
|
+
export async function retry(fn, maxAttempts = 3, baseDelay = 1000) {
|
|
220
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
|
|
221
|
+
throw new Error('retry maxAttempts must be a positive integer');
|
|
222
|
+
}
|
|
223
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
224
|
+
try {
|
|
225
|
+
return await fn();
|
|
226
|
+
} catch (error) {
|
|
227
|
+
if (attempt === maxAttempts) throw error;
|
|
228
|
+
await sleep(baseDelay * Math.pow(2, attempt - 1));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
package/src/trade.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Friendly trading vocabulary over Alpaca's native CLI.
|
|
2
|
+
//
|
|
3
|
+
// Alpaca remains a workflow tool: authentication, API calls, structured output,
|
|
4
|
+
// and the full command tree all belong to the `alpaca` binary. This module only
|
|
5
|
+
// translates a small, memorable `moshcode trade` surface into native argv.
|
|
6
|
+
// `moshcode alpaca ...` remains the escape hatch for every command not covered
|
|
7
|
+
// here.
|
|
8
|
+
|
|
9
|
+
const USAGE = `usage: moshcode trade <command> [args…]
|
|
10
|
+
|
|
11
|
+
ticker <symbol> [flags…] look up a tradable asset
|
|
12
|
+
quote <symbol> [flags…] get the latest quote
|
|
13
|
+
analysis <symbol> [flags…] get an analysis-ready market snapshot
|
|
14
|
+
buy <symbol> <qty> [flags…] preview a buy; add --submit to place it
|
|
15
|
+
buy <symbol> --notional <usd> preview a dollar-value buy
|
|
16
|
+
sell <symbol> <qty> [flags…] preview a sell; add --submit to place it
|
|
17
|
+
watch <verb> [args…] manage Alpaca watchlists
|
|
18
|
+
positions [verb] [args…] list/get/close positions
|
|
19
|
+
orders [verb] [args…] list/get/replace/cancel orders
|
|
20
|
+
account [args…] show account details
|
|
21
|
+
login [args…] authenticate an Alpaca profile (paper by default)
|
|
22
|
+
clock [args…] show market status and next open/close
|
|
23
|
+
raw <alpaca args…> invoke the native Alpaca command tree
|
|
24
|
+
|
|
25
|
+
Orders are previews by default. --submit removes the injected --dry-run;
|
|
26
|
+
live trading still requires Alpaca's separate --live opt-in.`;
|
|
27
|
+
|
|
28
|
+
export function tradeUsage() {
|
|
29
|
+
return USAGE;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function symbolCommand(native, args) {
|
|
33
|
+
const [symbol, ...rest] = args;
|
|
34
|
+
if (!symbol || String(symbol).startsWith("-")) {
|
|
35
|
+
return { error: `trade ${native.label} requires a ticker symbol` };
|
|
36
|
+
}
|
|
37
|
+
return { args: [...native.argv, "--symbol", String(symbol).toUpperCase(), ...rest] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function optionValue(args, name) {
|
|
41
|
+
const exact = args.indexOf(name);
|
|
42
|
+
if (exact >= 0) return { value: args[exact + 1], missing: args[exact + 1] == null || String(args[exact + 1]).startsWith("-") };
|
|
43
|
+
const prefix = `${name}=`;
|
|
44
|
+
const joined = args.find((arg) => String(arg).startsWith(prefix));
|
|
45
|
+
return joined == null ? null : { value: String(joined).slice(prefix.length), missing: String(joined).slice(prefix.length) === "" };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function positiveNumber(value) {
|
|
49
|
+
return value != null && Number.isFinite(Number(value)) && Number(value) > 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function orderCommand(side, args) {
|
|
53
|
+
const [symbol, ...afterSymbol] = args;
|
|
54
|
+
if (!symbol || String(symbol).startsWith("-")) {
|
|
55
|
+
return { error: `trade ${side} requires a ticker symbol and quantity or --notional` };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const positionalQty = afterSymbol[0] != null && !String(afterSymbol[0]).startsWith("-")
|
|
59
|
+
? String(afterSymbol[0])
|
|
60
|
+
: null;
|
|
61
|
+
const tail = positionalQty == null ? afterSymbol : afterSymbol.slice(1);
|
|
62
|
+
const qtyOption = optionValue(tail, "--qty");
|
|
63
|
+
const notionalOption = optionValue(tail, "--notional");
|
|
64
|
+
const supplied = [positionalQty != null, qtyOption != null, notionalOption != null].filter(Boolean).length;
|
|
65
|
+
if (supplied === 0) {
|
|
66
|
+
return { error: `trade ${side} requires a positive quantity or --notional amount` };
|
|
67
|
+
}
|
|
68
|
+
if (supplied > 1) {
|
|
69
|
+
return { error: `trade ${side} accepts one of positional quantity, --qty, or --notional` };
|
|
70
|
+
}
|
|
71
|
+
if (qtyOption?.missing) return { error: `trade ${side} --qty requires a positive number` };
|
|
72
|
+
if (notionalOption?.missing) return { error: `trade ${side} --notional requires a positive number` };
|
|
73
|
+
const amount = positionalQty ?? qtyOption?.value ?? notionalOption?.value;
|
|
74
|
+
if (!positiveNumber(amount)) {
|
|
75
|
+
return { error: `trade ${side} ${notionalOption ? "notional amount" : "quantity"} must be a positive number` };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Placing an order is the one place where a friendly shortcut should be
|
|
79
|
+
// safer than its native equivalent. Alpaca deliberately has no confirmation
|
|
80
|
+
// prompts, so buy/sell previews unless the caller explicitly says --submit.
|
|
81
|
+
const submit = tail.includes("--submit");
|
|
82
|
+
const nativeTail = tail.filter((arg) => arg !== "--submit");
|
|
83
|
+
const hasType = nativeTail.some((arg) => arg === "--type" || String(arg).startsWith("--type="));
|
|
84
|
+
const hasDryRun = nativeTail.includes("--dry-run");
|
|
85
|
+
return {
|
|
86
|
+
args: [
|
|
87
|
+
"order", "submit",
|
|
88
|
+
"--symbol", String(symbol).toUpperCase(),
|
|
89
|
+
"--side", side,
|
|
90
|
+
...(positionalQty == null ? [] : ["--qty", positionalQty]),
|
|
91
|
+
...(hasType ? [] : ["--type", "market"]),
|
|
92
|
+
...nativeTail,
|
|
93
|
+
...(!submit && !hasDryRun ? ["--dry-run"] : []),
|
|
94
|
+
],
|
|
95
|
+
preview: !submit || hasDryRun,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Translate `trade` arguments into argv for the native `alpaca` binary. */
|
|
100
|
+
export function tradeArgs(input = []) {
|
|
101
|
+
const [rawCommand, ...rest] = input.map(String);
|
|
102
|
+
const command = rawCommand?.toLowerCase();
|
|
103
|
+
if (!command) return { usage: true };
|
|
104
|
+
|
|
105
|
+
if (command === "ticker" || command === "lookup" || command === "asset") {
|
|
106
|
+
const [symbol, ...tail] = rest;
|
|
107
|
+
if (!symbol || String(symbol).startsWith("-")) return { error: "trade ticker requires a ticker symbol" };
|
|
108
|
+
// Unlike market-data commands, Alpaca asset get names its lookup argument
|
|
109
|
+
// --symbol-or-asset-id (verified against v0.0.13's actual command tree).
|
|
110
|
+
return { args: ["asset", "get", "--symbol-or-asset-id", String(symbol).toUpperCase(), ...tail] };
|
|
111
|
+
}
|
|
112
|
+
if (command === "quote") {
|
|
113
|
+
return symbolCommand({ label: "quote", argv: ["data", "latest-quote"] }, rest);
|
|
114
|
+
}
|
|
115
|
+
if (command === "analysis" || command === "analyze") {
|
|
116
|
+
return symbolCommand({ label: "analysis", argv: ["data", "snapshot"] }, rest);
|
|
117
|
+
}
|
|
118
|
+
if (command === "buy" || command === "sell") return orderCommand(command, rest);
|
|
119
|
+
|
|
120
|
+
if (command === "watch" || command === "watchlist") {
|
|
121
|
+
return { args: ["watchlist", ...(rest.length ? rest : ["list"])] };
|
|
122
|
+
}
|
|
123
|
+
if (command === "positions" || command === "position") {
|
|
124
|
+
return { args: ["position", ...(rest.length ? rest : ["list"])] };
|
|
125
|
+
}
|
|
126
|
+
if (command === "orders" || command === "order") {
|
|
127
|
+
return { args: ["order", ...(rest.length ? rest : ["list"])] };
|
|
128
|
+
}
|
|
129
|
+
if (command === "account") return { args: ["account", ...(rest.length ? rest : ["get"])] };
|
|
130
|
+
if (command === "login") return { args: ["profile", "login", ...rest] };
|
|
131
|
+
if (command === "clock") return { args: ["clock", ...rest] };
|
|
132
|
+
if (command === "raw" || command === "alpaca") {
|
|
133
|
+
return rest.length ? { args: rest } : { error: "trade raw requires an Alpaca command" };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { error: `unknown trade command ${JSON.stringify(rawCommand)}` };
|
|
137
|
+
}
|