@f-o-h/cli 0.1.0 → 0.1.2
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 +26 -0
- package/dist/foh.js +181 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ AI-operator provisioning CLI for Front Of House.
|
|
|
4
4
|
|
|
5
5
|
Public mirror: https://github.com/iiko38/front-of-house-cli
|
|
6
6
|
|
|
7
|
+
Current published baseline: `@f-o-h/cli@0.1.2`
|
|
8
|
+
|
|
7
9
|
This mirror is a generated release artifact. The private product monorepo is not
|
|
8
10
|
published here, and no open-source license is granted unless stated separately.
|
|
9
11
|
|
|
@@ -20,13 +22,37 @@ npm install -g @f-o-h/cli
|
|
|
20
22
|
foh --help
|
|
21
23
|
```
|
|
22
24
|
|
|
25
|
+
Verify the package version:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @f-o-h/cli --version
|
|
29
|
+
```
|
|
30
|
+
|
|
23
31
|
## First Run
|
|
24
32
|
|
|
25
33
|
```bash
|
|
34
|
+
foh auth signup --web
|
|
35
|
+
foh auth login --web
|
|
26
36
|
foh auth login
|
|
27
37
|
foh org list
|
|
28
38
|
foh org use --org <org-id>
|
|
29
39
|
foh setup
|
|
30
40
|
```
|
|
31
41
|
|
|
42
|
+
For AI agents and text-only terminals:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
foh auth signup --web --json
|
|
46
|
+
foh auth login --web --json
|
|
47
|
+
foh auth login --email "$FOH_EMAIL" --password "$FOH_PASSWORD" --json
|
|
48
|
+
foh org list --json
|
|
49
|
+
foh org use --org <org-id> --json
|
|
50
|
+
foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
`auth signup --web` opens the console signup page when possible and always
|
|
54
|
+
prints the fallback URL. `auth login --web` does the same for sign-in. The CLI
|
|
55
|
+
still requires explicit credential auth until device-code browser-token
|
|
56
|
+
exchange is available.
|
|
57
|
+
|
|
32
58
|
The CLI defaults to the production API at `https://api.frontofhouse.okii.uk`.
|
package/dist/foh.js
CHANGED
|
@@ -10136,7 +10136,121 @@ async function promptSecret(label) {
|
|
|
10136
10136
|
});
|
|
10137
10137
|
}
|
|
10138
10138
|
|
|
10139
|
+
// src/lib/console-url.ts
|
|
10140
|
+
var DEFAULT_FOH_CONSOLE_URL = "https://app.frontofhouse.okii.uk";
|
|
10141
|
+
function normalizeBaseUrl(value) {
|
|
10142
|
+
const normalized = String(value ?? "").trim().replace(/\/+$/, "");
|
|
10143
|
+
return normalized || DEFAULT_FOH_CONSOLE_URL;
|
|
10144
|
+
}
|
|
10145
|
+
function resolveConsoleBaseUrl(cliOverride) {
|
|
10146
|
+
return normalizeBaseUrl(cliOverride ?? process.env.FOH_CONSOLE_URL);
|
|
10147
|
+
}
|
|
10148
|
+
function buildConsoleSignInUrl(consoleUrl) {
|
|
10149
|
+
return `${normalizeBaseUrl(consoleUrl)}/sign-in?redirect=%2Fdashboard`;
|
|
10150
|
+
}
|
|
10151
|
+
function buildConsoleSignUpUrl(consoleUrl) {
|
|
10152
|
+
return `${normalizeBaseUrl(consoleUrl)}/sign-up?redirect=%2Fdashboard`;
|
|
10153
|
+
}
|
|
10154
|
+
function buildCliAuthFallbackCommands() {
|
|
10155
|
+
return [
|
|
10156
|
+
"foh auth login --email <email> --password <password> --json",
|
|
10157
|
+
"foh org list --json",
|
|
10158
|
+
"foh org use --org <org-id> --json"
|
|
10159
|
+
];
|
|
10160
|
+
}
|
|
10161
|
+
function buildCliAuthFallbackInstructions(signInUrl) {
|
|
10162
|
+
return {
|
|
10163
|
+
human: [
|
|
10164
|
+
`Open ${signInUrl}`,
|
|
10165
|
+
"Sign in to Front Of House.",
|
|
10166
|
+
"Return to the terminal and authenticate the CLI with email/password until browser-token exchange is available."
|
|
10167
|
+
],
|
|
10168
|
+
ai_agent: [
|
|
10169
|
+
"Show the sign_in_url to the user if browser opening is unavailable.",
|
|
10170
|
+
"Ask the user for FOH email/password or an approved service-token path.",
|
|
10171
|
+
"Run the explicit CLI auth commands in next_commands.",
|
|
10172
|
+
"Never scrape browser cookies or local storage."
|
|
10173
|
+
]
|
|
10174
|
+
};
|
|
10175
|
+
}
|
|
10176
|
+
function buildCliSignupFallbackInstructions(signUpUrl) {
|
|
10177
|
+
return {
|
|
10178
|
+
human: [
|
|
10179
|
+
`Open ${signUpUrl}`,
|
|
10180
|
+
"Create a Front Of House account.",
|
|
10181
|
+
"Confirm your email if prompted.",
|
|
10182
|
+
"Return to the terminal and run `foh auth login --web --json` or credential login."
|
|
10183
|
+
],
|
|
10184
|
+
ai_agent: [
|
|
10185
|
+
"Show the sign_up_url to the user if browser opening is unavailable.",
|
|
10186
|
+
"Ask the user to complete signup and email confirmation in the browser.",
|
|
10187
|
+
"After the user confirms signup, run `foh auth login --web --json`.",
|
|
10188
|
+
"Never ask the user to paste browser cookies or local storage."
|
|
10189
|
+
]
|
|
10190
|
+
};
|
|
10191
|
+
}
|
|
10192
|
+
|
|
10193
|
+
// src/lib/open-url.ts
|
|
10194
|
+
var import_child_process = require("child_process");
|
|
10195
|
+
function openUrl(url2) {
|
|
10196
|
+
const platform = process.platform;
|
|
10197
|
+
const command = platform === "win32" ? "cmd" : platform === "darwin" ? "open" : "xdg-open";
|
|
10198
|
+
const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
|
|
10199
|
+
try {
|
|
10200
|
+
const child = (0, import_child_process.spawn)(command, args, {
|
|
10201
|
+
detached: true,
|
|
10202
|
+
stdio: "ignore",
|
|
10203
|
+
shell: false,
|
|
10204
|
+
windowsHide: true
|
|
10205
|
+
});
|
|
10206
|
+
child.unref();
|
|
10207
|
+
return { attempted: true, command };
|
|
10208
|
+
} catch (error2) {
|
|
10209
|
+
return {
|
|
10210
|
+
attempted: false,
|
|
10211
|
+
command,
|
|
10212
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
10213
|
+
};
|
|
10214
|
+
}
|
|
10215
|
+
}
|
|
10216
|
+
|
|
10139
10217
|
// src/commands/auth.ts
|
|
10218
|
+
function emitBrowserAuthLink(opts) {
|
|
10219
|
+
const consoleUrl = resolveConsoleBaseUrl(opts.consoleUrl);
|
|
10220
|
+
const signInUrl = buildConsoleSignInUrl(consoleUrl);
|
|
10221
|
+
const openResult = openUrl(signInUrl);
|
|
10222
|
+
const nextCommands = buildCliAuthFallbackCommands();
|
|
10223
|
+
format({
|
|
10224
|
+
status: openResult.attempted ? "browser_auth_link_opened" : "browser_auth_link",
|
|
10225
|
+
sign_in_url: signInUrl,
|
|
10226
|
+
opened: openResult.attempted,
|
|
10227
|
+
opener_command: openResult.command,
|
|
10228
|
+
opener_error: openResult.error ?? null,
|
|
10229
|
+
cli_auth_required: true,
|
|
10230
|
+
note: "Browser sign-in opens the console. Until device-code auth is implemented, authenticate this CLI with the explicit credential command after sign-in.",
|
|
10231
|
+
next_commands: nextCommands,
|
|
10232
|
+
text_fallback: buildCliAuthFallbackInstructions(signInUrl)
|
|
10233
|
+
}, { json: opts.json ?? false });
|
|
10234
|
+
}
|
|
10235
|
+
function emitBrowserSignupLink(opts) {
|
|
10236
|
+
const consoleUrl = resolveConsoleBaseUrl(opts.consoleUrl);
|
|
10237
|
+
const signUpUrl = buildConsoleSignUpUrl(consoleUrl);
|
|
10238
|
+
const openResult = openUrl(signUpUrl);
|
|
10239
|
+
format({
|
|
10240
|
+
status: openResult.attempted ? "browser_signup_link_opened" : "browser_signup_link",
|
|
10241
|
+
sign_up_url: signUpUrl,
|
|
10242
|
+
opened: openResult.attempted,
|
|
10243
|
+
opener_command: openResult.command,
|
|
10244
|
+
opener_error: openResult.error ?? null,
|
|
10245
|
+
cli_auth_required_after_signup: true,
|
|
10246
|
+
note: "Signup is completed in the FOH console. After account creation and email confirmation, return to the CLI and authenticate.",
|
|
10247
|
+
next_commands: [
|
|
10248
|
+
"foh auth login --web --json",
|
|
10249
|
+
...buildCliAuthFallbackCommands()
|
|
10250
|
+
],
|
|
10251
|
+
text_fallback: buildCliSignupFallbackInstructions(signUpUrl)
|
|
10252
|
+
}, { json: opts.json ?? false });
|
|
10253
|
+
}
|
|
10140
10254
|
async function resolveLoginInputs(opts) {
|
|
10141
10255
|
let email3 = String(opts.email ?? "").trim();
|
|
10142
10256
|
let password = String(opts.password ?? "").trim();
|
|
@@ -10195,7 +10309,14 @@ async function maybeSelectDefaultOrg(orgs, jsonMode) {
|
|
|
10195
10309
|
}
|
|
10196
10310
|
function registerAuth(program3) {
|
|
10197
10311
|
const auth = program3.command("auth").description("Manage CLI authentication");
|
|
10198
|
-
auth.command("login").description("Authenticate with the FOH API and store a token locally").option("--email <email>", "FOH account email").option("--password <password>", "FOH account password").option("--wizard", "Run guided login wizard prompts").option("--api-url <url>", "Internal API base URL override (operators only)").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
10312
|
+
auth.command("login").description("Authenticate with the FOH API and store a token locally").option("--email <email>", "FOH account email").option("--password <password>", "FOH account password").option("--web", "Open browser sign-in and print text fallback commands").option("--browser", "Alias for --web").option("--wizard", "Run guided login wizard prompts").option("--console-url <url>", "Console sign-in URL override").option("--api-url <url>", "Internal API base URL override (operators only)").option("--json", "Output as JSON").action(async (opts) => withCommandErrorHandling(async () => {
|
|
10313
|
+
if ((opts.web || opts.browser) && !opts.email && !opts.password) {
|
|
10314
|
+
emitBrowserAuthLink({
|
|
10315
|
+
consoleUrl: opts.consoleUrl,
|
|
10316
|
+
json: opts.json
|
|
10317
|
+
});
|
|
10318
|
+
return;
|
|
10319
|
+
}
|
|
10199
10320
|
const apiUrl = resolveApiBaseUrl(opts.apiUrl);
|
|
10200
10321
|
const login = await resolveLoginInputs({
|
|
10201
10322
|
email: opts.email,
|
|
@@ -10264,6 +10385,12 @@ function registerAuth(program3) {
|
|
|
10264
10385
|
}
|
|
10265
10386
|
format(output, { json: opts.json ?? false });
|
|
10266
10387
|
}));
|
|
10388
|
+
auth.command("signup").description("Open account signup and print text fallback commands").option("--web", "Open browser signup and print text fallback commands", true).option("--browser", "Alias for --web").option("--console-url <url>", "Console signup URL override").option("--json", "Output as JSON").action((opts) => {
|
|
10389
|
+
emitBrowserSignupLink({
|
|
10390
|
+
consoleUrl: opts.consoleUrl,
|
|
10391
|
+
json: opts.json
|
|
10392
|
+
});
|
|
10393
|
+
});
|
|
10267
10394
|
auth.command("logout").description("Remove stored credentials").option("--json", "Output as JSON").action((opts) => {
|
|
10268
10395
|
clearCredentials();
|
|
10269
10396
|
format({ status: "logged_out" }, { json: opts.json ?? false });
|
|
@@ -32101,7 +32228,7 @@ var StdioServerTransport = class {
|
|
|
32101
32228
|
};
|
|
32102
32229
|
|
|
32103
32230
|
// src/lib/cli-version.ts
|
|
32104
|
-
var CLI_VERSION = "0.1.
|
|
32231
|
+
var CLI_VERSION = "0.1.2";
|
|
32105
32232
|
|
|
32106
32233
|
// src/commands/mcp-serve.ts
|
|
32107
32234
|
var DEFAULT_TIMEOUT_MS = 12e4;
|
|
@@ -33606,8 +33733,53 @@ function timedStepResult(result, startedAtIso, startedAtMs) {
|
|
|
33606
33733
|
duration_ms: Math.max(0, Date.now() - startedAtMs)
|
|
33607
33734
|
};
|
|
33608
33735
|
}
|
|
33736
|
+
function optionNameToFlag(key) {
|
|
33737
|
+
return "--" + key.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
33738
|
+
}
|
|
33739
|
+
function buildMissingOptionsPlan(missing, opts) {
|
|
33740
|
+
const missingFlags = missing.map(optionNameToFlag);
|
|
33741
|
+
const signInUrl = buildConsoleSignInUrl(resolveConsoleBaseUrl(opts.consoleUrl));
|
|
33742
|
+
return {
|
|
33743
|
+
status: "blocked",
|
|
33744
|
+
code: "setup_required_options_missing",
|
|
33745
|
+
missing_options: missingFlags,
|
|
33746
|
+
reason: "setup requires an authenticated org, an agent template, and an agent name before it can mutate customer resources",
|
|
33747
|
+
sign_in_url: signInUrl,
|
|
33748
|
+
next_commands: [
|
|
33749
|
+
"foh auth signup --web --json",
|
|
33750
|
+
"foh auth login --web --json",
|
|
33751
|
+
...buildCliAuthFallbackCommands(),
|
|
33752
|
+
"foh templates list --json",
|
|
33753
|
+
'foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --widget-domains <domain> --report-out setup-report.json --json'
|
|
33754
|
+
],
|
|
33755
|
+
text_fallback: buildCliAuthFallbackInstructions(signInUrl),
|
|
33756
|
+
ai_agent_instruction: [
|
|
33757
|
+
"Do not guess org IDs, template IDs, or customer domains.",
|
|
33758
|
+
"If no browser is available, print sign_in_url and ask the user to sign in.",
|
|
33759
|
+
"After auth, discover orgs and templates with the listed commands.",
|
|
33760
|
+
"Rerun setup only after all missing_options are resolved."
|
|
33761
|
+
]
|
|
33762
|
+
};
|
|
33763
|
+
}
|
|
33764
|
+
function emitMissingOptionsPlan(missing, opts) {
|
|
33765
|
+
const plan = buildMissingOptionsPlan(missing, { consoleUrl: opts.consoleUrl });
|
|
33766
|
+
if (resolveJsonMode({ json: opts.json })) {
|
|
33767
|
+
format(plan, { json: true });
|
|
33768
|
+
return;
|
|
33769
|
+
}
|
|
33770
|
+
const flags = missing.map(optionNameToFlag).join(", ");
|
|
33771
|
+
process.stderr.write(`error: required options missing: ${flags}
|
|
33772
|
+
`);
|
|
33773
|
+
process.stderr.write(` Sign in: ${plan.sign_in_url}
|
|
33774
|
+
`);
|
|
33775
|
+
process.stderr.write(" Remediation:\n");
|
|
33776
|
+
for (const command of plan.next_commands) {
|
|
33777
|
+
process.stderr.write(` ${command}
|
|
33778
|
+
`);
|
|
33779
|
+
}
|
|
33780
|
+
}
|
|
33609
33781
|
function registerSetup(program3) {
|
|
33610
|
-
program3.command("setup").description("Fully provision a new agency customer in one command").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--agent-template <id>", "Agent template ID (e.g. viewing-request)").option("--agent-name <name>", "Name for the new agent").option("--phone-country <cc>", "Phone number country code", "GB").option("--phone-area-code <code>", "Phone area code preference").option("--widget-domains <domains>", "Comma-separated widget domain allowlist").option("--voice-provider <p>", "TTS provider: openai, azure, twilio").option("--voice-id <id>", "Voice ID").option("--skip-compliance", "Skip compliance submission and wait").option("--skip-voice", "Skip voice configuration").option("--skip-tests", "Skip smoke tests").option("--cert-mode <m>", "Simulation cert mode: quick, full, stress", "quick").option("--cert-adaptive-runs <n>", "Adaptive run count for certification loop", "30").option("--cert-max-improvement-rounds <n>", "Max instruction improvement rounds in cert loop (0-5)", "1").option("--resume-from <step>", `Resume from a setup step (${SETUP_STEP_ORDER.join(", ")})`).option("--report-out <path>", "Optional path to write signed setup run report JSON").option("--dry-run", "Print all steps that would run without making any API calls").option("--api-url <url>", "API base URL override").option("--json", "Output as JSON").action(async (opts) => {
|
|
33782
|
+
program3.command("setup").description("Fully provision a new agency customer in one command").option("--org <id>", "Org ID (default: stored org from foh org use)").option("--agent-template <id>", "Agent template ID (e.g. viewing-request)").option("--agent-name <name>", "Name for the new agent").option("--phone-country <cc>", "Phone number country code", "GB").option("--phone-area-code <code>", "Phone area code preference").option("--widget-domains <domains>", "Comma-separated widget domain allowlist").option("--voice-provider <p>", "TTS provider: openai, azure, twilio").option("--voice-id <id>", "Voice ID").option("--skip-compliance", "Skip compliance submission and wait").option("--skip-voice", "Skip voice configuration").option("--skip-tests", "Skip smoke tests").option("--cert-mode <m>", "Simulation cert mode: quick, full, stress", "quick").option("--cert-adaptive-runs <n>", "Adaptive run count for certification loop", "30").option("--cert-max-improvement-rounds <n>", "Max instruction improvement rounds in cert loop (0-5)", "1").option("--resume-from <step>", `Resume from a setup step (${SETUP_STEP_ORDER.join(", ")})`).option("--report-out <path>", "Optional path to write signed setup run report JSON").option("--dry-run", "Print all steps that would run without making any API calls").option("--api-url <url>", "API base URL override").option("--console-url <url>", "Console sign-in URL override").option("--json", "Output as JSON").action(async (opts) => {
|
|
33611
33783
|
if (!opts.org) {
|
|
33612
33784
|
try {
|
|
33613
33785
|
opts.org = loadCredentials(opts.apiUrl).orgId;
|
|
@@ -33616,12 +33788,7 @@ function registerSetup(program3) {
|
|
|
33616
33788
|
}
|
|
33617
33789
|
const missing = ["org", "agentTemplate", "agentName"].filter((key) => !opts[key]);
|
|
33618
33790
|
if (missing.length) {
|
|
33619
|
-
|
|
33620
|
-
process.stderr.write(`error: required options missing: ${flags}
|
|
33621
|
-
`);
|
|
33622
|
-
if (missing.includes("org")) {
|
|
33623
|
-
process.stderr.write(" Remediation: Run: foh org use --org <id> to set a default, or pass --org <id>\n");
|
|
33624
|
-
}
|
|
33791
|
+
emitMissingOptionsPlan(missing, { json: opts.json, consoleUrl: opts.consoleUrl });
|
|
33625
33792
|
markCommandFailed(1);
|
|
33626
33793
|
return;
|
|
33627
33794
|
}
|
|
@@ -35775,7 +35942,7 @@ function getHomeQuickActions(state) {
|
|
|
35775
35942
|
}
|
|
35776
35943
|
|
|
35777
35944
|
// src/commands/home-interaction.ts
|
|
35778
|
-
var
|
|
35945
|
+
var import_child_process2 = require("child_process");
|
|
35779
35946
|
var import_readline2 = require("readline");
|
|
35780
35947
|
|
|
35781
35948
|
// src/tui/keypress.ts
|
|
@@ -35830,7 +35997,7 @@ async function runSelf(args, apiUrlOverride) {
|
|
|
35830
35997
|
spawnArgs.push("--api-url", apiUrlOverride);
|
|
35831
35998
|
}
|
|
35832
35999
|
return await new Promise((resolve5, reject) => {
|
|
35833
|
-
const child = (0,
|
|
36000
|
+
const child = (0, import_child_process2.spawn)(process.execPath, [process.argv[1], ...spawnArgs], {
|
|
35834
36001
|
stdio: "inherit",
|
|
35835
36002
|
env: {
|
|
35836
36003
|
...process.env,
|
|
@@ -36131,7 +36298,7 @@ function maybeDefaultToHome(argv = process.argv) {
|
|
|
36131
36298
|
// src/lib/update.ts
|
|
36132
36299
|
var import_fs6 = require("fs");
|
|
36133
36300
|
var import_path5 = require("path");
|
|
36134
|
-
var
|
|
36301
|
+
var import_child_process3 = require("child_process");
|
|
36135
36302
|
var import_crypto5 = require("crypto");
|
|
36136
36303
|
function parseSemver(version2) {
|
|
36137
36304
|
const trimmed = String(version2 ?? "").trim();
|
|
@@ -36218,7 +36385,7 @@ async function applyRepoUpdate(repoRoot) {
|
|
|
36218
36385
|
const scriptPath = (0, import_path5.join)(repoRoot, "scripts", "Install-FohCli.ps1");
|
|
36219
36386
|
if (process.platform === "win32") {
|
|
36220
36387
|
return await new Promise((resolve5, reject) => {
|
|
36221
|
-
const child = (0,
|
|
36388
|
+
const child = (0, import_child_process3.spawn)(
|
|
36222
36389
|
"powershell",
|
|
36223
36390
|
["-ExecutionPolicy", "Bypass", "-File", scriptPath],
|
|
36224
36391
|
{ stdio: "inherit" }
|
|
@@ -36228,7 +36395,7 @@ async function applyRepoUpdate(repoRoot) {
|
|
|
36228
36395
|
});
|
|
36229
36396
|
}
|
|
36230
36397
|
return await new Promise((resolve5, reject) => {
|
|
36231
|
-
const child = (0,
|
|
36398
|
+
const child = (0, import_child_process3.spawn)(
|
|
36232
36399
|
"corepack",
|
|
36233
36400
|
["pnpm", "cli:install:global"],
|
|
36234
36401
|
{
|