@f-o-h/cli 0.1.0 → 0.1.1

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 +23 -0
  2. package/dist/foh.js +136 -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.1`
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,34 @@ 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 login --web
26
35
  foh auth login
27
36
  foh org list
28
37
  foh org use --org <org-id>
29
38
  foh setup
30
39
  ```
31
40
 
41
+ For AI agents and text-only terminals:
42
+
43
+ ```bash
44
+ foh auth login --web --json
45
+ foh auth login --email "$FOH_EMAIL" --password "$FOH_PASSWORD" --json
46
+ foh org list --json
47
+ foh org use --org <org-id> --json
48
+ foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --json
49
+ ```
50
+
51
+ `auth login --web` opens the console sign-in page when possible and always
52
+ prints the fallback URL. The CLI still requires explicit credential auth until
53
+ device-code browser-token exchange is available.
54
+
32
55
  The CLI defaults to the production API at `https://api.frontofhouse.okii.uk`.
package/dist/foh.js CHANGED
@@ -10136,7 +10136,83 @@ 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?source=cli`;
10150
+ }
10151
+ function buildCliAuthFallbackCommands() {
10152
+ return [
10153
+ "foh auth login --email <email> --password <password> --json",
10154
+ "foh org list --json",
10155
+ "foh org use --org <org-id> --json"
10156
+ ];
10157
+ }
10158
+ function buildCliAuthFallbackInstructions(signInUrl) {
10159
+ return {
10160
+ human: [
10161
+ `Open ${signInUrl}`,
10162
+ "Sign in to Front Of House.",
10163
+ "Return to the terminal and authenticate the CLI with email/password until browser-token exchange is available."
10164
+ ],
10165
+ ai_agent: [
10166
+ "Show the sign_in_url to the user if browser opening is unavailable.",
10167
+ "Ask the user for FOH email/password or an approved service-token path.",
10168
+ "Run the explicit CLI auth commands in next_commands.",
10169
+ "Never scrape browser cookies or local storage."
10170
+ ]
10171
+ };
10172
+ }
10173
+
10174
+ // src/lib/open-url.ts
10175
+ var import_child_process = require("child_process");
10176
+ function openUrl(url2) {
10177
+ const platform = process.platform;
10178
+ const command = platform === "win32" ? "cmd" : platform === "darwin" ? "open" : "xdg-open";
10179
+ const args = platform === "win32" ? ["/c", "start", "", url2] : [url2];
10180
+ try {
10181
+ const child = (0, import_child_process.spawn)(command, args, {
10182
+ detached: true,
10183
+ stdio: "ignore",
10184
+ shell: false,
10185
+ windowsHide: true
10186
+ });
10187
+ child.unref();
10188
+ return { attempted: true, command };
10189
+ } catch (error2) {
10190
+ return {
10191
+ attempted: false,
10192
+ command,
10193
+ error: error2 instanceof Error ? error2.message : String(error2)
10194
+ };
10195
+ }
10196
+ }
10197
+
10139
10198
  // src/commands/auth.ts
10199
+ function emitBrowserAuthLink(opts) {
10200
+ const consoleUrl = resolveConsoleBaseUrl(opts.consoleUrl);
10201
+ const signInUrl = buildConsoleSignInUrl(consoleUrl);
10202
+ const openResult = openUrl(signInUrl);
10203
+ const nextCommands = buildCliAuthFallbackCommands();
10204
+ format({
10205
+ status: openResult.attempted ? "browser_auth_link_opened" : "browser_auth_link",
10206
+ sign_in_url: signInUrl,
10207
+ opened: openResult.attempted,
10208
+ opener_command: openResult.command,
10209
+ opener_error: openResult.error ?? null,
10210
+ cli_auth_required: true,
10211
+ note: "Browser sign-in opens the console. Until device-code auth is implemented, authenticate this CLI with the explicit credential command after sign-in.",
10212
+ next_commands: nextCommands,
10213
+ text_fallback: buildCliAuthFallbackInstructions(signInUrl)
10214
+ }, { json: opts.json ?? false });
10215
+ }
10140
10216
  async function resolveLoginInputs(opts) {
10141
10217
  let email3 = String(opts.email ?? "").trim();
10142
10218
  let password = String(opts.password ?? "").trim();
@@ -10195,7 +10271,14 @@ async function maybeSelectDefaultOrg(orgs, jsonMode) {
10195
10271
  }
10196
10272
  function registerAuth(program3) {
10197
10273
  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 () => {
10274
+ 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 () => {
10275
+ if ((opts.web || opts.browser) && !opts.email && !opts.password) {
10276
+ emitBrowserAuthLink({
10277
+ consoleUrl: opts.consoleUrl,
10278
+ json: opts.json
10279
+ });
10280
+ return;
10281
+ }
10199
10282
  const apiUrl = resolveApiBaseUrl(opts.apiUrl);
10200
10283
  const login = await resolveLoginInputs({
10201
10284
  email: opts.email,
@@ -32101,7 +32184,7 @@ var StdioServerTransport = class {
32101
32184
  };
32102
32185
 
32103
32186
  // src/lib/cli-version.ts
32104
- var CLI_VERSION = "0.1.0";
32187
+ var CLI_VERSION = "0.1.1";
32105
32188
 
32106
32189
  // src/commands/mcp-serve.ts
32107
32190
  var DEFAULT_TIMEOUT_MS = 12e4;
@@ -33606,8 +33689,52 @@ function timedStepResult(result, startedAtIso, startedAtMs) {
33606
33689
  duration_ms: Math.max(0, Date.now() - startedAtMs)
33607
33690
  };
33608
33691
  }
33692
+ function optionNameToFlag(key) {
33693
+ return "--" + key.replace(/([A-Z])/g, "-$1").toLowerCase();
33694
+ }
33695
+ function buildMissingOptionsPlan(missing, opts) {
33696
+ const missingFlags = missing.map(optionNameToFlag);
33697
+ const signInUrl = buildConsoleSignInUrl(resolveConsoleBaseUrl(opts.consoleUrl));
33698
+ return {
33699
+ status: "blocked",
33700
+ code: "setup_required_options_missing",
33701
+ missing_options: missingFlags,
33702
+ reason: "setup requires an authenticated org, an agent template, and an agent name before it can mutate customer resources",
33703
+ sign_in_url: signInUrl,
33704
+ next_commands: [
33705
+ "foh auth login --web --json",
33706
+ ...buildCliAuthFallbackCommands(),
33707
+ "foh templates list --json",
33708
+ 'foh setup --org <org-id> --agent-template <template-id> --agent-name "Demo Agent" --widget-domains <domain> --report-out setup-report.json --json'
33709
+ ],
33710
+ text_fallback: buildCliAuthFallbackInstructions(signInUrl),
33711
+ ai_agent_instruction: [
33712
+ "Do not guess org IDs, template IDs, or customer domains.",
33713
+ "If no browser is available, print sign_in_url and ask the user to sign in.",
33714
+ "After auth, discover orgs and templates with the listed commands.",
33715
+ "Rerun setup only after all missing_options are resolved."
33716
+ ]
33717
+ };
33718
+ }
33719
+ function emitMissingOptionsPlan(missing, opts) {
33720
+ const plan = buildMissingOptionsPlan(missing, { consoleUrl: opts.consoleUrl });
33721
+ if (resolveJsonMode({ json: opts.json })) {
33722
+ format(plan, { json: true });
33723
+ return;
33724
+ }
33725
+ const flags = missing.map(optionNameToFlag).join(", ");
33726
+ process.stderr.write(`error: required options missing: ${flags}
33727
+ `);
33728
+ process.stderr.write(` Sign in: ${plan.sign_in_url}
33729
+ `);
33730
+ process.stderr.write(" Remediation:\n");
33731
+ for (const command of plan.next_commands) {
33732
+ process.stderr.write(` ${command}
33733
+ `);
33734
+ }
33735
+ }
33609
33736
  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) => {
33737
+ 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
33738
  if (!opts.org) {
33612
33739
  try {
33613
33740
  opts.org = loadCredentials(opts.apiUrl).orgId;
@@ -33616,12 +33743,7 @@ function registerSetup(program3) {
33616
33743
  }
33617
33744
  const missing = ["org", "agentTemplate", "agentName"].filter((key) => !opts[key]);
33618
33745
  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
- }
33746
+ emitMissingOptionsPlan(missing, { json: opts.json, consoleUrl: opts.consoleUrl });
33625
33747
  markCommandFailed(1);
33626
33748
  return;
33627
33749
  }
@@ -35775,7 +35897,7 @@ function getHomeQuickActions(state) {
35775
35897
  }
35776
35898
 
35777
35899
  // src/commands/home-interaction.ts
35778
- var import_child_process = require("child_process");
35900
+ var import_child_process2 = require("child_process");
35779
35901
  var import_readline2 = require("readline");
35780
35902
 
35781
35903
  // src/tui/keypress.ts
@@ -35830,7 +35952,7 @@ async function runSelf(args, apiUrlOverride) {
35830
35952
  spawnArgs.push("--api-url", apiUrlOverride);
35831
35953
  }
35832
35954
  return await new Promise((resolve5, reject) => {
35833
- const child = (0, import_child_process.spawn)(process.execPath, [process.argv[1], ...spawnArgs], {
35955
+ const child = (0, import_child_process2.spawn)(process.execPath, [process.argv[1], ...spawnArgs], {
35834
35956
  stdio: "inherit",
35835
35957
  env: {
35836
35958
  ...process.env,
@@ -36131,7 +36253,7 @@ function maybeDefaultToHome(argv = process.argv) {
36131
36253
  // src/lib/update.ts
36132
36254
  var import_fs6 = require("fs");
36133
36255
  var import_path5 = require("path");
36134
- var import_child_process2 = require("child_process");
36256
+ var import_child_process3 = require("child_process");
36135
36257
  var import_crypto5 = require("crypto");
36136
36258
  function parseSemver(version2) {
36137
36259
  const trimmed = String(version2 ?? "").trim();
@@ -36218,7 +36340,7 @@ async function applyRepoUpdate(repoRoot) {
36218
36340
  const scriptPath = (0, import_path5.join)(repoRoot, "scripts", "Install-FohCli.ps1");
36219
36341
  if (process.platform === "win32") {
36220
36342
  return await new Promise((resolve5, reject) => {
36221
- const child = (0, import_child_process2.spawn)(
36343
+ const child = (0, import_child_process3.spawn)(
36222
36344
  "powershell",
36223
36345
  ["-ExecutionPolicy", "Bypass", "-File", scriptPath],
36224
36346
  { stdio: "inherit" }
@@ -36228,7 +36350,7 @@ async function applyRepoUpdate(repoRoot) {
36228
36350
  });
36229
36351
  }
36230
36352
  return await new Promise((resolve5, reject) => {
36231
- const child = (0, import_child_process2.spawn)(
36353
+ const child = (0, import_child_process3.spawn)(
36232
36354
  "corepack",
36233
36355
  ["pnpm", "cli:install:global"],
36234
36356
  {
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.1",
4
4
  "description": "FOH CLI - AI-operator provisioning tool for Front Of House",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {