@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.
Files changed (3) hide show
  1. package/README.md +26 -0
  2. package/dist/foh.js +181 -14
  3. 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.0";
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
- const flags = missing.map((key) => "--" + key.replace(/([A-Z])/g, "-$1").toLowerCase()).join(", ");
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 import_child_process = require("child_process");
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, import_child_process.spawn)(process.execPath, [process.argv[1], ...spawnArgs], {
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 import_child_process2 = require("child_process");
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, import_child_process2.spawn)(
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, import_child_process2.spawn)(
36398
+ const child = (0, import_child_process3.spawn)(
36232
36399
  "corepack",
36233
36400
  ["pnpm", "cli:install:global"],
36234
36401
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@f-o-h/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "FOH CLI - AI-operator provisioning tool for Front Of House",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {