@boardwalk-labs/cli 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.
- package/LICENSE +18 -0
- package/README.md +110 -0
- package/bin/boardwalk.js +4 -0
- package/dist/artifact.d.ts +53 -0
- package/dist/artifact.js +267 -0
- package/dist/artifact.js.map +1 -0
- package/dist/auth/discovery.d.ts +8 -0
- package/dist/auth/discovery.js +43 -0
- package/dist/auth/discovery.js.map +1 -0
- package/dist/auth/login.d.ts +13 -0
- package/dist/auth/login.js +74 -0
- package/dist/auth/login.js.map +1 -0
- package/dist/auth/pkce.d.ts +63 -0
- package/dist/auth/pkce.js +197 -0
- package/dist/auth/pkce.js.map +1 -0
- package/dist/auth/resolve.d.ts +12 -0
- package/dist/auth/resolve.js +51 -0
- package/dist/auth/resolve.js.map +1 -0
- package/dist/bundle.d.ts +35 -0
- package/dist/bundle.js +176 -0
- package/dist/bundle.js.map +1 -0
- package/dist/client.d.ts +65 -0
- package/dist/client.js +193 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/cancel.d.ts +14 -0
- package/dist/commands/cancel.js +53 -0
- package/dist/commands/cancel.js.map +1 -0
- package/dist/commands/check.d.ts +7 -0
- package/dist/commands/check.js +34 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.js +56 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.js +128 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/init.d.ts +12 -0
- package/dist/commands/init.js +171 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/run.d.ts +32 -0
- package/dist/commands/run.js +105 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/session.d.ts +13 -0
- package/dist/commands/session.js +58 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +64 -0
- package/dist/config.js.map +1 -0
- package/dist/credentials.d.ts +23 -0
- package/dist/credentials.js +69 -0
- package/dist/credentials.js.map +1 -0
- package/dist/deployment.d.ts +44 -0
- package/dist/deployment.js +100 -0
- package/dist/deployment.js.map +1 -0
- package/dist/dev/host.d.ts +16 -0
- package/dist/dev/host.js +76 -0
- package/dist/dev/host.js.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +147 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +12 -0
- package/dist/manifest.js +64 -0
- package/dist/manifest.js.map +1 -0
- package/dist/project.d.ts +15 -0
- package/dist/project.js +79 -0
- package/dist/project.js.map +1 -0
- package/dist/render/renderer.d.ts +18 -0
- package/dist/render/renderer.js +108 -0
- package/dist/render/renderer.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// `boardwalk init [dir]` — scaffold a new workflow project.
|
|
2
|
+
//
|
|
3
|
+
// Templates come from the examples registry: `--template <name>` fetches
|
|
4
|
+
// `registry.json` and the template's files from the templates base URL
|
|
5
|
+
// ($BOARDWALK_TEMPLATES_URL to point at a fork/mirror). The default `hello` template is
|
|
6
|
+
// BUILT IN — `init` works offline and with zero configuration.
|
|
7
|
+
//
|
|
8
|
+
// Never overwrites: every target path is checked before anything is written.
|
|
9
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
10
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
11
|
+
import { CliError } from "../errors.js";
|
|
12
|
+
/** Where templates live: the examples repo, raw. Overridable for forks/mirrors. */
|
|
13
|
+
const DEFAULT_TEMPLATES_URL = "https://raw.githubusercontent.com/boardwalk-labs/examples/main";
|
|
14
|
+
// ── The built-in `hello` template (offline floor) ───────────────────────────────────────
|
|
15
|
+
const HELLO_PROGRAM = `import { input, output, type WorkflowMeta } from "@boardwalk-labs/workflow";
|
|
16
|
+
|
|
17
|
+
export const meta = {
|
|
18
|
+
name: "{{name}}",
|
|
19
|
+
description: "A starting point — run it locally with \`boardwalk dev\`.",
|
|
20
|
+
triggers: [{ kind: "manual" }],
|
|
21
|
+
} satisfies WorkflowMeta;
|
|
22
|
+
|
|
23
|
+
// A workflow is a script: it runs top to bottom, and top-level await just works.
|
|
24
|
+
const who = typeof input === "string" && input.length > 0 ? input : "world";
|
|
25
|
+
|
|
26
|
+
// Next step: give it a brain. agent() runs a full agent loop and resolves to its answer:
|
|
27
|
+
// const greeting = await agent(\`Write a one-line greeting for \${who}.\`);
|
|
28
|
+
// (agent() needs an engine — \`boardwalk run\` today, the local engine soon.)
|
|
29
|
+
|
|
30
|
+
output(\`Hello, \${who}!\`);
|
|
31
|
+
`;
|
|
32
|
+
const HELLO_PACKAGE_JSON = `{
|
|
33
|
+
"name": "{{name}}",
|
|
34
|
+
"private": true,
|
|
35
|
+
"type": "module",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@boardwalk-labs/workflow": "^0.1.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`;
|
|
41
|
+
const HELLO_ENV_EXAMPLE = `# Secrets for local runs — \`boardwalk dev\` resolves meta.secrets from .env.
|
|
42
|
+
# Copy to .env (gitignored) and fill in real values.
|
|
43
|
+
# MY_API_KEY=…
|
|
44
|
+
`;
|
|
45
|
+
const HELLO_GITIGNORE = `node_modules/
|
|
46
|
+
.env
|
|
47
|
+
.boardwalk/
|
|
48
|
+
.bw-runs/
|
|
49
|
+
`;
|
|
50
|
+
const BUILTIN_TEMPLATES = {
|
|
51
|
+
hello: {
|
|
52
|
+
"index.ts": HELLO_PROGRAM,
|
|
53
|
+
"package.json": HELLO_PACKAGE_JSON,
|
|
54
|
+
".env.example": HELLO_ENV_EXAMPLE,
|
|
55
|
+
".gitignore": HELLO_GITIGNORE,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export async function runInit(opts, deps = {}) {
|
|
59
|
+
const log = deps.log ??
|
|
60
|
+
((line) => {
|
|
61
|
+
console.log(line);
|
|
62
|
+
});
|
|
63
|
+
const builtin = BUILTIN_TEMPLATES[opts.template];
|
|
64
|
+
if (builtin !== undefined) {
|
|
65
|
+
const dir = resolve(opts.dir);
|
|
66
|
+
const name = workflowNameFor(dir);
|
|
67
|
+
const files = Object.fromEntries(Object.entries(builtin).map(([rel, body]) => [rel, body.replaceAll("{{name}}", name)]));
|
|
68
|
+
scaffold(dir, files);
|
|
69
|
+
finish(log, opts, name, opts.template, []);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Remote template: registry lookup, then fetch each listed file.
|
|
73
|
+
const env = deps.env ?? process.env;
|
|
74
|
+
const baseUrl = (env.BOARDWALK_TEMPLATES_URL ?? DEFAULT_TEMPLATES_URL).replace(/\/+$/, "");
|
|
75
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
76
|
+
const registry = await fetchRegistry(baseUrl, fetchImpl);
|
|
77
|
+
const template = registry.find((t) => t.name === opts.template);
|
|
78
|
+
if (template === undefined) {
|
|
79
|
+
const available = [...Object.keys(BUILTIN_TEMPLATES), ...registry.map((t) => t.name)];
|
|
80
|
+
throw new CliError(`Unknown template "${opts.template}".`, `Available templates: ${available.join(", ")}.`);
|
|
81
|
+
}
|
|
82
|
+
const files = {};
|
|
83
|
+
for (const rel of template.files) {
|
|
84
|
+
if (rel.startsWith("/") || rel.split("/").some((s) => s === ".." || s === "")) {
|
|
85
|
+
throw new CliError(`The registry lists an unsafe file path: ${rel}`);
|
|
86
|
+
}
|
|
87
|
+
files[rel] = await fetchText(`${baseUrl}/templates/${template.name}/${rel}`, fetchImpl, `template file ${rel}`);
|
|
88
|
+
}
|
|
89
|
+
scaffold(resolve(opts.dir), files);
|
|
90
|
+
finish(log, opts, template.name, template.name, template.secrets);
|
|
91
|
+
}
|
|
92
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────────────────
|
|
93
|
+
/** All-or-nothing write: refuse if ANY target exists, then write every file. */
|
|
94
|
+
function scaffold(dir, files) {
|
|
95
|
+
mkdirSync(dir, { recursive: true });
|
|
96
|
+
for (const rel of Object.keys(files)) {
|
|
97
|
+
if (existsSync(join(dir, rel))) {
|
|
98
|
+
throw new CliError(`${rel} already exists in ${dir}.`, "boardwalk init only scaffolds into paths it won't overwrite — pick an empty directory.");
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const [rel, contents] of Object.entries(files)) {
|
|
102
|
+
const target = join(dir, rel);
|
|
103
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
104
|
+
writeFileSync(target, contents);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function finish(log, opts, name, template, secrets) {
|
|
108
|
+
log(`✓ scaffolded "${name}" (template: ${template})`);
|
|
109
|
+
log("");
|
|
110
|
+
log("next:");
|
|
111
|
+
log(` cd ${opts.dir === "." ? "." : opts.dir} && npm install`);
|
|
112
|
+
if (secrets.length > 0) {
|
|
113
|
+
log(` cp .env.example .env # fill in: ${secrets.join(", ")}`);
|
|
114
|
+
}
|
|
115
|
+
log(" boardwalk dev .");
|
|
116
|
+
}
|
|
117
|
+
async function fetchRegistry(baseUrl, fetchImpl) {
|
|
118
|
+
const raw = await fetchText(`${baseUrl}/registry.json`, fetchImpl, "template registry");
|
|
119
|
+
let parsed;
|
|
120
|
+
try {
|
|
121
|
+
parsed = JSON.parse(raw);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
throw new CliError("The template registry is not valid JSON.", registryHint(baseUrl));
|
|
125
|
+
}
|
|
126
|
+
const templates = typeof parsed === "object" && parsed !== null
|
|
127
|
+
? parsed.templates
|
|
128
|
+
: undefined;
|
|
129
|
+
if (!Array.isArray(templates) || !templates.every(isRegistryTemplate)) {
|
|
130
|
+
throw new CliError("The template registry has an unexpected shape.", registryHint(baseUrl));
|
|
131
|
+
}
|
|
132
|
+
return templates;
|
|
133
|
+
}
|
|
134
|
+
async function fetchText(url, fetchImpl, what) {
|
|
135
|
+
let res;
|
|
136
|
+
try {
|
|
137
|
+
res = await fetchImpl(url, { signal: AbortSignal.timeout(20_000) });
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
throw new CliError(`Could not fetch the ${what} (${url}).`, err instanceof Error ? err.message : undefined);
|
|
141
|
+
}
|
|
142
|
+
if (!res.ok) {
|
|
143
|
+
throw new CliError(`Fetching the ${what} failed (${String(res.status)}) at ${url}.`);
|
|
144
|
+
}
|
|
145
|
+
return res.text();
|
|
146
|
+
}
|
|
147
|
+
function registryHint(baseUrl) {
|
|
148
|
+
return `Registry base: ${baseUrl} (override with BOARDWALK_TEMPLATES_URL).`;
|
|
149
|
+
}
|
|
150
|
+
function isRegistryTemplate(value) {
|
|
151
|
+
if (typeof value !== "object" || value === null)
|
|
152
|
+
return false;
|
|
153
|
+
const v = value;
|
|
154
|
+
return (typeof v.name === "string" &&
|
|
155
|
+
typeof v.description === "string" &&
|
|
156
|
+
Array.isArray(v.secrets) &&
|
|
157
|
+
v.secrets.every((s) => typeof s === "string") &&
|
|
158
|
+
Array.isArray(v.files) &&
|
|
159
|
+
v.files.every((f) => typeof f === "string") &&
|
|
160
|
+
v.files.length > 0);
|
|
161
|
+
}
|
|
162
|
+
/** Derive a manifest-legal workflow name from the target directory's basename. */
|
|
163
|
+
export function workflowNameFor(absDir) {
|
|
164
|
+
const base = basename(absDir)
|
|
165
|
+
.toLowerCase()
|
|
166
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
167
|
+
.replace(/^-+|-+$/g, "")
|
|
168
|
+
.slice(0, 100);
|
|
169
|
+
return base.length > 0 ? base : "my-workflow";
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CliConfig } from "../config.js";
|
|
2
|
+
import { type RunSummary } from "../client.js";
|
|
3
|
+
import type { FetchLike } from "../auth/pkce.js";
|
|
4
|
+
export declare function isTerminalStatus(status: string): boolean;
|
|
5
|
+
/** Minimal client surface `pollToTerminal` needs — `BoardwalkClient` satisfies it. */
|
|
6
|
+
export interface RunReader {
|
|
7
|
+
getRun(runId: string): Promise<RunSummary>;
|
|
8
|
+
}
|
|
9
|
+
export interface PollOptions {
|
|
10
|
+
sleep?: (ms: number) => Promise<void>;
|
|
11
|
+
intervalMs?: number;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
onStatus?: (status: string) => void;
|
|
14
|
+
now?: () => number;
|
|
15
|
+
}
|
|
16
|
+
/** Poll a run until it reaches a terminal state (completed/failed/cancelled), or time out. */
|
|
17
|
+
export declare function pollToTerminal(client: RunReader, runId: string, opts?: PollOptions): Promise<RunSummary>;
|
|
18
|
+
export interface RunOptions {
|
|
19
|
+
file: string;
|
|
20
|
+
org?: string | undefined;
|
|
21
|
+
input?: string | undefined;
|
|
22
|
+
token?: string | undefined;
|
|
23
|
+
noWait?: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface RunDeps {
|
|
26
|
+
config: CliConfig;
|
|
27
|
+
fetchImpl?: FetchLike;
|
|
28
|
+
log?: (line: string) => void;
|
|
29
|
+
}
|
|
30
|
+
export declare function runRun(opts: RunOptions, deps: RunDeps): Promise<void>;
|
|
31
|
+
/** Parse `--input '<json>'` to a value; undefined when absent. Throws on malformed JSON. */
|
|
32
|
+
export declare function parseInput(raw: string | undefined): unknown;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// `boardwalk run <file|dir> --org <slug>` — actually run a workflow, for real.
|
|
2
|
+
//
|
|
3
|
+
// Runs on the PLATFORM (where the real worker + real inference live — no mocks): it deploys the
|
|
4
|
+
// current source (via the project link), triggers a run, and polls it to a terminal state. The
|
|
5
|
+
// run's output payload isn't exposed over REST (dashboard-only), so we report the final
|
|
6
|
+
// status/outcome + the run id and point at the dashboard for full output + logs.
|
|
7
|
+
import { CliError } from "../errors.js";
|
|
8
|
+
import { CredentialStore } from "../credentials.js";
|
|
9
|
+
import { resolveToken } from "../auth/resolve.js";
|
|
10
|
+
import { BoardwalkClient } from "../client.js";
|
|
11
|
+
import { deployWithLink, loadProgram } from "../deployment.js";
|
|
12
|
+
const TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled"]);
|
|
13
|
+
const DEFAULT_POLL_INTERVAL_MS = 2_000;
|
|
14
|
+
const DEFAULT_TIMEOUT_MS = 15 * 60_000;
|
|
15
|
+
export function isTerminalStatus(status) {
|
|
16
|
+
return TERMINAL_STATUSES.has(status);
|
|
17
|
+
}
|
|
18
|
+
/** Poll a run until it reaches a terminal state (completed/failed/cancelled), or time out. */
|
|
19
|
+
export async function pollToTerminal(client, runId, opts = {}) {
|
|
20
|
+
const sleep = opts.sleep ??
|
|
21
|
+
((ms) => new Promise((resolve) => {
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
resolve();
|
|
24
|
+
}, ms);
|
|
25
|
+
}));
|
|
26
|
+
const intervalMs = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
27
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
28
|
+
const now = opts.now ?? (() => Date.now());
|
|
29
|
+
const start = now();
|
|
30
|
+
let lastStatus = "";
|
|
31
|
+
for (;;) {
|
|
32
|
+
const run = await client.getRun(runId);
|
|
33
|
+
if (run.status !== lastStatus) {
|
|
34
|
+
opts.onStatus?.(run.status);
|
|
35
|
+
lastStatus = run.status;
|
|
36
|
+
}
|
|
37
|
+
if (isTerminalStatus(run.status))
|
|
38
|
+
return run;
|
|
39
|
+
if (now() - start > timeoutMs) {
|
|
40
|
+
throw new CliError(`Run ${runId} did not finish within ${String(Math.round(timeoutMs / 1000))}s (still ${run.status}).`, "It may still be running — check the run in the dashboard.");
|
|
41
|
+
}
|
|
42
|
+
await sleep(intervalMs);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function runRun(opts, deps) {
|
|
46
|
+
const log = deps.log ??
|
|
47
|
+
((line) => {
|
|
48
|
+
console.log(line);
|
|
49
|
+
});
|
|
50
|
+
const prog = await loadProgram(opts.file);
|
|
51
|
+
const assets = prog.artifact.assetPaths.length;
|
|
52
|
+
log(` built ${prog.entry} (${String(prog.artifact.size)} bytes${assets > 0 ? `, ${String(assets)} asset${assets === 1 ? "" : "s"}` : ""})`);
|
|
53
|
+
const store = CredentialStore.atConfigDir(deps.config.configDir);
|
|
54
|
+
const token = await resolveToken({
|
|
55
|
+
config: deps.config,
|
|
56
|
+
store,
|
|
57
|
+
tokenFlag: opts.token,
|
|
58
|
+
...(deps.fetchImpl !== undefined ? { fetchImpl: deps.fetchImpl } : {}),
|
|
59
|
+
});
|
|
60
|
+
const client = new BoardwalkClient({
|
|
61
|
+
baseUrl: deps.config.apiBaseUrl,
|
|
62
|
+
token,
|
|
63
|
+
...(deps.fetchImpl !== undefined ? { fetchImpl: deps.fetchImpl } : {}),
|
|
64
|
+
});
|
|
65
|
+
const dep = await deployWithLink(client, { orgSlug: opts.org, target: opts.file, prog });
|
|
66
|
+
if (dep.gitignoreUpdated)
|
|
67
|
+
log(" linked → .boardwalk/project.json (added .boardwalk/ to .gitignore)");
|
|
68
|
+
log(`✓ ${dep.outcome} "${prog.name}" version ${String(dep.versionNumber)}`);
|
|
69
|
+
const input = parseInput(opts.input);
|
|
70
|
+
const run = await client.triggerRun(dep.orgSlug, dep.workflowId, input);
|
|
71
|
+
log(`▶ run ${run.id} triggered (${run.status})`);
|
|
72
|
+
if (opts.noWait === true) {
|
|
73
|
+
log(" --no-wait: not polling. Check the run in the dashboard.");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
log(` (cancel anytime: boardwalk cancel ${run.id})`);
|
|
77
|
+
const terminal = await pollToTerminal(client, run.id, {
|
|
78
|
+
onStatus: (status) => {
|
|
79
|
+
log(` status → ${status}`);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
printOutcome(log, terminal);
|
|
83
|
+
if (terminal.status !== "completed") {
|
|
84
|
+
throw new CliError(`Run ${terminal.status}.`, "See the run in the dashboard for logs + output.");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/** Parse `--input '<json>'` to a value; undefined when absent. Throws on malformed JSON. */
|
|
88
|
+
export function parseInput(raw) {
|
|
89
|
+
if (raw === undefined)
|
|
90
|
+
return undefined;
|
|
91
|
+
try {
|
|
92
|
+
return JSON.parse(raw);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
throw new CliError("--input is not valid JSON.");
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function printOutcome(log, run) {
|
|
99
|
+
log("──────── run result ────────");
|
|
100
|
+
log(`run: ${run.id}`);
|
|
101
|
+
log(`status: ${run.status}`);
|
|
102
|
+
log(`outcome: ${run.outcomeStatus ?? "(none)"}`);
|
|
103
|
+
log("output: view the full output + logs in the dashboard");
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/commands/run.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CliConfig } from "../config.js";
|
|
2
|
+
export interface SessionDeps {
|
|
3
|
+
config: CliConfig;
|
|
4
|
+
log?: (line: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface LoginOptions {
|
|
7
|
+
/** Persist this API key (`bwk_…`) instead of running the browser PKCE flow. */
|
|
8
|
+
token?: string | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare function runLogin(deps: SessionDeps, opts?: LoginOptions): Promise<void>;
|
|
11
|
+
export declare function runLogout(deps: SessionDeps): void;
|
|
12
|
+
/** Report whether a stored session exists. (Identity claims live in the JWT; v0 keeps this minimal.) */
|
|
13
|
+
export declare function runWhoami(deps: SessionDeps): void;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// `boardwalk login` / `logout` / `whoami` — session lifecycle.
|
|
2
|
+
import { CliError } from "../errors.js";
|
|
3
|
+
import { CredentialStore } from "../credentials.js";
|
|
4
|
+
import { performLogin } from "../auth/login.js";
|
|
5
|
+
export async function runLogin(deps, opts = {}) {
|
|
6
|
+
const log = deps.log ??
|
|
7
|
+
((line) => {
|
|
8
|
+
console.log(line);
|
|
9
|
+
});
|
|
10
|
+
const store = CredentialStore.atConfigDir(deps.config.configDir);
|
|
11
|
+
// First-class API-key auth: `boardwalk login --token <key>` stores the key like a
|
|
12
|
+
// non-expiring, non-refreshable session, so every command resolves it without re-setting
|
|
13
|
+
// BOARDWALK_API_KEY. (Not validated here — the first request rejects a bad key.) The two
|
|
14
|
+
// non-interactive paths (env BOARDWALK_API_KEY, --token per command) still work too.
|
|
15
|
+
const token = opts.token?.trim();
|
|
16
|
+
if (token !== undefined && token.length > 0) {
|
|
17
|
+
store.putSession({
|
|
18
|
+
accessToken: token,
|
|
19
|
+
refreshToken: null,
|
|
20
|
+
expiresAt: null,
|
|
21
|
+
clientId: null,
|
|
22
|
+
tokenEndpoint: null,
|
|
23
|
+
scope: "api-key",
|
|
24
|
+
});
|
|
25
|
+
log("✓ Stored API key.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const session = await performLogin({ config: deps.config, store, log });
|
|
29
|
+
const expiry = session.expiresAt !== null ? ` (expires ${new Date(session.expiresAt).toISOString()})` : "";
|
|
30
|
+
log(`✓ Logged in.${expiry}`);
|
|
31
|
+
}
|
|
32
|
+
export function runLogout(deps) {
|
|
33
|
+
const log = deps.log ??
|
|
34
|
+
((line) => {
|
|
35
|
+
console.log(line);
|
|
36
|
+
});
|
|
37
|
+
const store = CredentialStore.atConfigDir(deps.config.configDir);
|
|
38
|
+
store.clear();
|
|
39
|
+
log("✓ Logged out — local credentials removed.");
|
|
40
|
+
}
|
|
41
|
+
/** Report whether a stored session exists. (Identity claims live in the JWT; v0 keeps this minimal.) */
|
|
42
|
+
export function runWhoami(deps) {
|
|
43
|
+
const log = deps.log ??
|
|
44
|
+
((line) => {
|
|
45
|
+
console.log(line);
|
|
46
|
+
});
|
|
47
|
+
const store = CredentialStore.atConfigDir(deps.config.configDir);
|
|
48
|
+
const session = store.getSession();
|
|
49
|
+
if (session === null) {
|
|
50
|
+
throw new CliError("Not logged in.", "Run `boardwalk login` (or `boardwalk login --token <key>`).");
|
|
51
|
+
}
|
|
52
|
+
// A stored API key has no token endpoint / refresh token; an OAuth session does.
|
|
53
|
+
const method = session.tokenEndpoint === null ? "API key" : "OAuth session";
|
|
54
|
+
const scope = session.scope ?? "(none)";
|
|
55
|
+
const expiry = session.expiresAt !== null ? new Date(session.expiresAt).toISOString() : "never";
|
|
56
|
+
log(`Logged in via ${method}. scope=${scope} expires=${expiry}`);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/commands/session.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface CliConfig {
|
|
2
|
+
/** REST API base, e.g. https://api.boardwalk.sh (no trailing slash). */
|
|
3
|
+
apiBaseUrl: string;
|
|
4
|
+
/** OAuth issuer origin `boardwalk login` authenticates against (provider-agnostic). */
|
|
5
|
+
issuerUrl: string;
|
|
6
|
+
/** Public OAuth client id for the CLI's OAuth application. Null until configured. */
|
|
7
|
+
oauthClientId: string | null;
|
|
8
|
+
/** Fixed loopback port for the PKCE redirect (must be allowlisted on the OAuth application). */
|
|
9
|
+
loopbackPort: number;
|
|
10
|
+
/** Directory holding the credentials file (XDG config dir by default). */
|
|
11
|
+
configDir: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function loadConfig(env?: NodeJS.ProcessEnv): CliConfig;
|
|
14
|
+
export declare function trimTrailingSlash(url: string): string;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// CLI configuration — resolved from environment variables with sane defaults.
|
|
2
|
+
//
|
|
3
|
+
// Endpoints are NOT hardcoded into logic; they derive from env so the same binary targets prod
|
|
4
|
+
// (the default), a dev stack, or a customer's SELF-HOSTED deployment on their own domain. The
|
|
5
|
+
// identity provider behind `login` is an internal detail — the CLI only ever knows an "issuer URL",
|
|
6
|
+
// never the vendor.
|
|
7
|
+
import envPaths from "env-paths";
|
|
8
|
+
/** Default API host. Self-hosters override with `BOARDWALK_API_DOMAIN=api.their-company.com`. */
|
|
9
|
+
const DEFAULT_API_DOMAIN = "api.boardwalk.sh";
|
|
10
|
+
/**
|
|
11
|
+
* Default OAuth issuer for `login`. Boardwalk self-hosts its OAuth server, so the issuer is the API
|
|
12
|
+
* host itself — the CLI fetches `<issuer>/.well-known/oauth-authorization-server` to discover the
|
|
13
|
+
* authorize page (web app) + token endpoint (api). Override with `BOARDWALK_ISSUER_URL` for dev.
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_ISSUER_URL = `https://${DEFAULT_API_DOMAIN}`;
|
|
16
|
+
/** The first-party CLI's public OAuth client id (registered in the backend's in-code client registry).
|
|
17
|
+
* Shipped as a default so users don't configure it; override with `BOARDWALK_OAUTH_CLIENT_ID`. */
|
|
18
|
+
const DEFAULT_OAUTH_CLIENT_ID = "boardwalk-cli";
|
|
19
|
+
/** A fixed, memorable loopback port (cf. aws/gcloud). Must match the OAuth redirect allowlist. */
|
|
20
|
+
const DEFAULT_LOOPBACK_PORT = 53682;
|
|
21
|
+
export function loadConfig(env = process.env) {
|
|
22
|
+
const paths = envPaths("boardwalk", { suffix: "" });
|
|
23
|
+
return {
|
|
24
|
+
apiBaseUrl: resolveApiBaseUrl(env),
|
|
25
|
+
issuerUrl: trimTrailingSlash(env.BOARDWALK_ISSUER_URL ?? DEFAULT_ISSUER_URL),
|
|
26
|
+
oauthClientId: nonEmpty(env.BOARDWALK_OAUTH_CLIENT_ID) ?? DEFAULT_OAUTH_CLIENT_ID,
|
|
27
|
+
loopbackPort: parsePort(env.BOARDWALK_OAUTH_PORT) ?? DEFAULT_LOOPBACK_PORT,
|
|
28
|
+
configDir: nonEmpty(env.BOARDWALK_CONFIG_DIR) ?? paths.config,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* API base URL, in precedence order:
|
|
33
|
+
* 1. `BOARDWALK_API_URL` — a full URL (escape hatch for local http / non-standard ports)
|
|
34
|
+
* 2. `BOARDWALK_API_DOMAIN` — a hostname → `https://<domain>` (the self-host knob)
|
|
35
|
+
* 3. the default prod domain
|
|
36
|
+
*/
|
|
37
|
+
function resolveApiBaseUrl(env) {
|
|
38
|
+
const fullUrl = nonEmpty(env.BOARDWALK_API_URL);
|
|
39
|
+
if (fullUrl !== null)
|
|
40
|
+
return trimTrailingSlash(fullUrl);
|
|
41
|
+
const domain = nonEmpty(env.BOARDWALK_API_DOMAIN) ?? DEFAULT_API_DOMAIN;
|
|
42
|
+
return `https://${normalizeDomain(domain)}`;
|
|
43
|
+
}
|
|
44
|
+
/** Strip any scheme + trailing slashes from a domain so `https://<domain>` is well-formed. */
|
|
45
|
+
function normalizeDomain(domain) {
|
|
46
|
+
return domain.replace(/^https?:\/\//, "").replace(/\/+$/, "");
|
|
47
|
+
}
|
|
48
|
+
export function trimTrailingSlash(url) {
|
|
49
|
+
return url.replace(/\/+$/, "");
|
|
50
|
+
}
|
|
51
|
+
function nonEmpty(value) {
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
return null;
|
|
54
|
+
const trimmed = value.trim();
|
|
55
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
56
|
+
}
|
|
57
|
+
/** Parse a 1–65535 port from a string, or null when unset/invalid (caller falls back to default). */
|
|
58
|
+
function parsePort(value) {
|
|
59
|
+
if (value === undefined)
|
|
60
|
+
return null;
|
|
61
|
+
const n = Number(value.trim());
|
|
62
|
+
return Number.isInteger(n) && n >= 1 && n <= 65535 ? n : null;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,+FAA+F;AAC/F,8FAA8F;AAC9F,oGAAoG;AACpG,oBAAoB;AAEpB,OAAO,QAAQ,MAAM,WAAW,CAAC;AAejC,iGAAiG;AACjG,MAAM,kBAAkB,GAAG,kBAAkB,CAAC;AAC9C;;;;GAIG;AACH,MAAM,kBAAkB,GAAG,WAAW,kBAAkB,EAAE,CAAC;AAC3D;mGACmG;AACnG,MAAM,uBAAuB,GAAG,eAAe,CAAC;AAChD,kGAAkG;AAClG,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAEpC,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;IACpD,OAAO;QACL,UAAU,EAAE,iBAAiB,CAAC,GAAG,CAAC;QAClC,SAAS,EAAE,iBAAiB,CAAC,GAAG,CAAC,oBAAoB,IAAI,kBAAkB,CAAC;QAC5E,aAAa,EAAE,QAAQ,CAAC,GAAG,CAAC,yBAAyB,CAAC,IAAI,uBAAuB;QACjF,YAAY,EAAE,SAAS,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,qBAAqB;QAC1E,SAAS,EAAE,QAAQ,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,KAAK,CAAC,MAAM;KAC9D,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,GAAsB;IAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;IAChD,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,kBAAkB,CAAC;IACxE,OAAO,WAAW,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9C,CAAC;AAED,8FAA8F;AAC9F,SAAS,eAAe,CAAC,MAAc;IACrC,OAAO,MAAM,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,OAAO,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,QAAQ,CAAC,KAAyB;IACzC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED,qGAAqG;AACrG,SAAS,SAAS,CAAC,KAAyB;IAC1C,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** A logged-in user's OAuth session. `null` fields = the IdP omitted them (e.g. no refresh token). */
|
|
2
|
+
export interface StoredSession {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
refreshToken: string | null;
|
|
5
|
+
/** Absolute access-token expiry, epoch ms; null = unknown (treated as non-expiring). */
|
|
6
|
+
expiresAt: number | null;
|
|
7
|
+
/** The OAuth client id used — needed to refresh. */
|
|
8
|
+
clientId: string | null;
|
|
9
|
+
/** The token endpoint to refresh against. */
|
|
10
|
+
tokenEndpoint: string | null;
|
|
11
|
+
scope: string | null;
|
|
12
|
+
}
|
|
13
|
+
export declare class CredentialStore {
|
|
14
|
+
private readonly filePath;
|
|
15
|
+
constructor(filePath: string);
|
|
16
|
+
static atConfigDir(configDir: string): CredentialStore;
|
|
17
|
+
getSession(): StoredSession | null;
|
|
18
|
+
putSession(session: StoredSession): void;
|
|
19
|
+
/** Remove any stored session (logout). Idempotent — a missing file is fine. */
|
|
20
|
+
clear(): void;
|
|
21
|
+
private read;
|
|
22
|
+
private write;
|
|
23
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// CredentialStore — the on-disk session written by `boardwalk login`, read by every authenticated
|
|
2
|
+
// command.
|
|
3
|
+
//
|
|
4
|
+
// v0 storage: a single JSON file at `<configDir>/credentials.json`, dir mode 0700, file mode 0600
|
|
5
|
+
// (owner-only) — the same posture as the `gh` / `aws` CLIs. AES-at-rest + an OS keychain are a
|
|
6
|
+
// hardening follow-up; the interface here (get/put/clear a session) doesn't change when that lands.
|
|
7
|
+
import { mkdirSync, readFileSync, writeFileSync, rmSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
export class CredentialStore {
|
|
10
|
+
filePath;
|
|
11
|
+
constructor(filePath) {
|
|
12
|
+
this.filePath = filePath;
|
|
13
|
+
}
|
|
14
|
+
static atConfigDir(configDir) {
|
|
15
|
+
return new CredentialStore(join(configDir, "credentials.json"));
|
|
16
|
+
}
|
|
17
|
+
getSession() {
|
|
18
|
+
const session = this.read().session;
|
|
19
|
+
return session !== undefined && isValidSession(session) ? session : null;
|
|
20
|
+
}
|
|
21
|
+
putSession(session) {
|
|
22
|
+
this.write({ session });
|
|
23
|
+
}
|
|
24
|
+
/** Remove any stored session (logout). Idempotent — a missing file is fine. */
|
|
25
|
+
clear() {
|
|
26
|
+
try {
|
|
27
|
+
rmSync(this.filePath, { force: true });
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Best-effort: a missing/locked file shouldn't make logout fail.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
read() {
|
|
34
|
+
let raw;
|
|
35
|
+
try {
|
|
36
|
+
raw = readFileSync(this.filePath, "utf8");
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return {}; // missing/unreadable → no credentials
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
if (typeof parsed === "object" && parsed !== null && "session" in parsed) {
|
|
44
|
+
const session = parsed.session;
|
|
45
|
+
if (isValidSession(session))
|
|
46
|
+
return { session };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// Malformed file → treat as no credentials rather than crashing the command.
|
|
51
|
+
}
|
|
52
|
+
return {};
|
|
53
|
+
}
|
|
54
|
+
write(file) {
|
|
55
|
+
mkdirSync(dirOf(this.filePath), { recursive: true, mode: 0o700 });
|
|
56
|
+
writeFileSync(this.filePath, `${JSON.stringify(file, null, 2)}\n`, { mode: 0o600 });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function dirOf(filePath) {
|
|
60
|
+
const idx = filePath.lastIndexOf("/");
|
|
61
|
+
return idx <= 0 ? "." : filePath.slice(0, idx);
|
|
62
|
+
}
|
|
63
|
+
function isValidSession(value) {
|
|
64
|
+
if (typeof value !== "object" || value === null)
|
|
65
|
+
return false;
|
|
66
|
+
const s = value;
|
|
67
|
+
return typeof s.accessToken === "string" && s.accessToken.length > 0;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type BuiltArtifact } from "./artifact.js";
|
|
2
|
+
import type { BoardwalkClient, WorkflowSummary } from "./client.js";
|
|
3
|
+
export interface PreparedProgram {
|
|
4
|
+
name: string;
|
|
5
|
+
/** Entry module inside the artifact (e.g. `index.mjs`). */
|
|
6
|
+
entry: string;
|
|
7
|
+
/** The built, content-addressed program artifact (tarball + digest + metadata). */
|
|
8
|
+
artifact: BuiltArtifact;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a target path to its deployable artifact: build the program (bundle + assets → tarball,
|
|
12
|
+
* content-addressed) and extract `meta.name` from the bundled entry for the deploy identity.
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadProgram(file: string): Promise<PreparedProgram>;
|
|
15
|
+
export interface DeployPlan {
|
|
16
|
+
action: "create" | "update";
|
|
17
|
+
name: string;
|
|
18
|
+
/** Present only for `update` — the existing workflow id to PUT. */
|
|
19
|
+
workflowId?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Decide create vs update by matching the program name against the org's existing workflows. */
|
|
22
|
+
export declare function planDeploy(existing: readonly WorkflowSummary[], name: string): DeployPlan;
|
|
23
|
+
export interface DeployResultSummary {
|
|
24
|
+
workflowId: string;
|
|
25
|
+
orgSlug: string;
|
|
26
|
+
versionNumber: number;
|
|
27
|
+
/** "created" a new workflow, "updated" the linked one, or "adopted" an existing same-name one. */
|
|
28
|
+
outcome: "created" | "updated" | "adopted";
|
|
29
|
+
/** True when this deploy wrote a `.gitignore` entry for `.boardwalk/`. */
|
|
30
|
+
gitignoreUpdated: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface DeployContext {
|
|
33
|
+
/** Org slug from `--org`; falls back to the link's orgSlug. */
|
|
34
|
+
orgSlug?: string | undefined;
|
|
35
|
+
/** The original target path (used to locate the project dir for the link). */
|
|
36
|
+
target: string;
|
|
37
|
+
prog: PreparedProgram;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Deploy the program, honoring the project link: upload the artifact, then update the linked workflow
|
|
41
|
+
* by id (rename-safe); when unlinked, adopt an existing same-name workflow or create a new one. Always
|
|
42
|
+
* (re)writes the link so the next deploy/run is pinned.
|
|
43
|
+
*/
|
|
44
|
+
export declare function deployWithLink(client: BoardwalkClient, ctx: DeployContext): Promise<DeployResultSummary>;
|