@withone/cli 1.43.9 → 1.44.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.
package/README.md CHANGED
@@ -144,11 +144,21 @@ In a monorepo, the project root is the nearest ancestor with `.one/`, `.git`, or
144
144
 
145
145
  If you've already set up, `one init` shows your current status for the active scope and lets you update your key, install to more agents, or reconfigure.
146
146
 
147
+ **Agent-driven setup (no prompts).** Pass `--auth` and `one init` runs end-to-end without any terminal interaction — handy when an AI agent is onboarding you. It saves the key, auto-installs the One skill, and skips the connect step (`one add` later).
148
+
149
+ ```bash
150
+ one init --auth browser # opens a login window; you authenticate, the window closes — done
151
+ one init --auth manual --api-key sk_live_... # headless / CI, no browser
152
+ ```
153
+
147
154
  | Flag | What it does |
148
155
  |------|-------------|
149
156
  | `-y` | Skip confirmations |
150
157
  | `-g` | Non-interactive: write the One config globally (`~/.one/config.json`) |
151
158
  | `-p` | Non-interactive: write the One config for this project (`~/.one/projects/<slug>/config.json`) |
159
+ | `--auth <browser\|manual>` | Run setup with **no prompts**. `browser` opens a login window; `manual` uses `--api-key`. Scope from `-g`/`-p` (default global). |
160
+ | `--api-key <key>` | API key for `--auth manual` (`sk_live_…` / `sk_test_…`) |
161
+ | `--openai-key <key>` | Optional OpenAI key for `one mem` semantic search |
152
162
 
153
163
  ### `one add <platform>`
154
164
 
package/dist/index.js CHANGED
@@ -1046,8 +1046,12 @@ async function loginCommand() {
1046
1046
 
1047
1047
  // src/commands/init.ts
1048
1048
  async function initCommand(options) {
1049
+ if (options.auth) {
1050
+ await nonInteractiveInit(options);
1051
+ return;
1052
+ }
1049
1053
  if (isAgentMode()) {
1050
- error("This command requires interactive input. Run without --agent.");
1054
+ error("This command is interactive. Run without --agent, or pass --auth <browser|manual> for a non-interactive setup.");
1051
1055
  }
1052
1056
  printBanner();
1053
1057
  const scope = await chooseConfigScope(options);
@@ -1062,6 +1066,101 @@ async function initCommand(options) {
1062
1066
  }
1063
1067
  await freshSetup(scope, options);
1064
1068
  }
1069
+ async function nonInteractiveInit(options) {
1070
+ const auth = options.auth;
1071
+ if (auth !== "browser" && auth !== "manual") {
1072
+ error(`Invalid --auth value '${auth}'. Use 'browser' or 'manual'.`);
1073
+ }
1074
+ const scope = options.global ? "global" : options.project ? "project" : "global";
1075
+ let apiKey;
1076
+ let whoami;
1077
+ if (auth === "browser") {
1078
+ const result = await browserLogin();
1079
+ if (!result) {
1080
+ error("Browser login did not complete. Try again: one init --auth browser");
1081
+ }
1082
+ apiKey = result.apiKey;
1083
+ whoami = result.whoami;
1084
+ } else {
1085
+ const key = options.apiKey?.trim();
1086
+ if (!key) {
1087
+ error("--auth manual requires --api-key <sk_live_\u2026 | sk_test_\u2026>.");
1088
+ }
1089
+ if (!key.startsWith("sk_live_") && !key.startsWith("sk_test_")) {
1090
+ error("API key should start with sk_live_ or sk_test_.");
1091
+ }
1092
+ const api = new OneApi(key, getApiBase());
1093
+ let validated;
1094
+ try {
1095
+ validated = await api.validateApiKey();
1096
+ } catch (err) {
1097
+ const msg = err instanceof Error ? err.message : String(err);
1098
+ error(`Could not validate API key: ${msg}`);
1099
+ }
1100
+ if (!validated) {
1101
+ error(`Invalid API key. Get a valid key at ${getApiKeyUrl()}`);
1102
+ }
1103
+ apiKey = key;
1104
+ whoami = validated;
1105
+ }
1106
+ const existing = scope === "project" ? readProjectConfig() : readGlobalConfig();
1107
+ writeConfig(
1108
+ {
1109
+ apiKey,
1110
+ installedAgents: existing?.installedAgents ?? [],
1111
+ createdAt: existing?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1112
+ accessControl: existing?.accessControl,
1113
+ apiBase: existing?.apiBase,
1114
+ cacheTtl: existing?.cacheTtl,
1115
+ whoami
1116
+ },
1117
+ scope
1118
+ );
1119
+ if (options.openaiKey?.trim()) {
1120
+ try {
1121
+ setOpenAiApiKey(options.openaiKey.trim());
1122
+ } catch {
1123
+ }
1124
+ }
1125
+ const primaryIds = SKILL_AGENTS.filter((a) => a.primary).map((a) => a.id);
1126
+ const { installed, failed } = installSkillForAgents(primaryIds);
1127
+ const configPath = scope === "project" ? getProjectConfigPath() : getGlobalConfigPath();
1128
+ if (isAgentMode()) {
1129
+ json({
1130
+ success: true,
1131
+ scope,
1132
+ configPath,
1133
+ auth,
1134
+ account: {
1135
+ user: whoami.user,
1136
+ organization: whoami.organization,
1137
+ project: whoami.project,
1138
+ env: getEnvFromApiKey(apiKey)
1139
+ },
1140
+ skillInstalled: installed,
1141
+ skillFailed: failed
1142
+ });
1143
+ return;
1144
+ }
1145
+ const env = getEnvFromApiKey(apiKey);
1146
+ const contextParts = [];
1147
+ if (whoami.organization) contextParts.push(whoami.organization.name);
1148
+ if (whoami.project) contextParts.push(whoami.project.name);
1149
+ const scopeDisplay = contextParts.length > 0 ? contextParts.join(" / ") : "Personal";
1150
+ const envLabel = env === "test" ? pc2.yellow("test") : pc2.green("live");
1151
+ console.log();
1152
+ console.log(` ${pc2.bold("Setup complete")} ${scopeLabel(scope)}`);
1153
+ console.log(` ${pc2.dim("\u2500".repeat(42))}`);
1154
+ console.log(` ${pc2.dim("Account:")} ${scopeDisplay} ${pc2.dim("\xB7")} ${envLabel}`);
1155
+ console.log(` ${pc2.dim("User:")} ${whoami.user.name} ${pc2.dim(`(${whoami.user.email})`)}`);
1156
+ console.log(` ${pc2.dim("Config:")} ${tildify(configPath)}`);
1157
+ if (installed.length > 0) {
1158
+ console.log(` ${pc2.dim("Skill:")} ${pc2.green("installed")} ${pc2.dim("\xB7 " + installed.join(", "))}`);
1159
+ }
1160
+ console.log();
1161
+ console.log(` ${pc2.dim("Connect a platform later with")} ${pc2.cyan("one add <platform>")}`);
1162
+ printOnboardingPrompt();
1163
+ }
1065
1164
  async function chooseConfigScope(options) {
1066
1165
  if (options.global) return "global";
1067
1166
  if (options.project) return "project";
@@ -6174,17 +6273,23 @@ async function syncModel(api, profile, options) {
6174
6273
  if (lock) lock.release();
6175
6274
  const rawMsg = err instanceof Error ? err.message : String(err);
6176
6275
  const shortMsg = truncate(rawMsg, 500);
6276
+ const httpStatus = err instanceof ApiError ? err.status : void 0;
6277
+ const retryAfter = err instanceof ApiError ? err.retryAfterSeconds : void 0;
6177
6278
  if (pagesProcessed > 0) {
6178
6279
  const resumeErr = new Error(
6179
6280
  `Sync interrupted after page ${pagesProcessed} (${totalRecords} records). Run again to resume. Error: ${shortMsg}`
6180
6281
  );
6181
6282
  resumeErr._recordsSynced = totalRecords;
6182
6283
  resumeErr._pagesProcessed = pagesProcessed;
6284
+ resumeErr._httpStatus = httpStatus;
6285
+ resumeErr._retryAfter = retryAfter;
6183
6286
  throw resumeErr;
6184
6287
  }
6185
6288
  const wrapped = new Error(shortMsg);
6186
6289
  wrapped._recordsSynced = totalRecords;
6187
6290
  wrapped._pagesProcessed = pagesProcessed;
6291
+ wrapped._httpStatus = httpStatus;
6292
+ wrapped._retryAfter = retryAfter;
6188
6293
  throw wrapped;
6189
6294
  } finally {
6190
6295
  process.off("SIGINT", onSigint);
@@ -7663,13 +7768,18 @@ async function syncRunCommand(platform, options) {
7663
7768
  results.push(result);
7664
7769
  } catch (err) {
7665
7770
  const errObj = err;
7771
+ const errorContext = {
7772
+ message: err instanceof Error ? err.message : String(err),
7773
+ ...errObj?._httpStatus !== void 0 ? { httpStatus: errObj._httpStatus } : {},
7774
+ ...errObj?._retryAfter !== void 0 ? { retryAfter: errObj._retryAfter } : {}
7775
+ };
7666
7776
  results.push({
7667
7777
  model: profile.model,
7668
7778
  recordsSynced: errObj?._recordsSynced ?? 0,
7669
7779
  pagesProcessed: errObj?._pagesProcessed ?? 0,
7670
7780
  duration: "0s",
7671
7781
  status: "failed",
7672
- error: err instanceof Error ? err.message : String(err)
7782
+ error: errorContext
7673
7783
  });
7674
7784
  }
7675
7785
  }
@@ -7688,8 +7798,11 @@ async function syncRunCommand(platform, options) {
7688
7798
  const archivedColor = sc.archived > sc.active ? pc9.red : pc9.dim;
7689
7799
  console.log(` memory: ${pc9.green(String(sc.active))} active, ${archivedColor(String(sc.archived))} archived`);
7690
7800
  }
7691
- if ("error" in r && r.error) {
7692
- console.log(` ${pc9.red(r.error)}`);
7801
+ if (r.error) {
7802
+ const errParts = [r.error.message];
7803
+ if (r.error.httpStatus) errParts.push(`HTTP ${r.error.httpStatus}`);
7804
+ if (r.error.retryAfter) errParts.push(`retry after ${r.error.retryAfter}s`);
7805
+ console.log(` ${pc9.red(errParts.join(" \u2014 "))}`);
7693
7806
  }
7694
7807
  }
7695
7808
  }
@@ -9236,6 +9349,17 @@ var GUIDE_OVERVIEW = `# One CLI \u2014 Agent Guide
9236
9349
 
9237
9350
  You can also use \`one login\` / \`one logout\` to manage authentication separately (global or per-directory).
9238
9351
 
9352
+ ### Agent-driven setup (no prompts)
9353
+ To onboard a user without any terminal interaction, pass \`--auth\` to \`one init\`. This disables every prompt, auto-installs the One skill, and skips the connect-a-platform step (run \`one add <platform>\` afterwards).
9354
+
9355
+ \`\`\`bash
9356
+ one init --auth browser # opens a login window; the user authenticates, the CLI saves the key
9357
+ one init --auth browser --project # same, but scoped to this folder (default scope is global)
9358
+ one init --auth manual --api-key sk_live_... # headless / CI \u2014 no browser
9359
+ \`\`\`
9360
+
9361
+ With \`--auth browser\` the user sees a browser window, picks how to authenticate, and the window closes when done \u2014 the agent never blocks on stdin. Add \`--openai-key sk-...\` to enable semantic search in \`one mem\` during setup.
9362
+
9239
9363
  ## The --agent Flag
9240
9364
 
9241
9365
  Always use \`--agent\` for machine-readable JSON output. It disables colors, spinners, and interactive prompts.
@@ -10640,7 +10764,7 @@ program.name("one").option("--agent", "Machine-readable JSON output (no colors,
10640
10764
  Setup:
10641
10765
  one login Authenticate via browser (opens app.withone.ai)
10642
10766
  one logout Clear local credentials
10643
- one init Set up API key and install MCP server
10767
+ one init Set up API key + skill (add --auth browser for no-prompt agent setup)
10644
10768
  one add <platform> Connect a platform via OAuth (e.g. gmail, slack, shopify)
10645
10769
  one connection delete <key> Remove a connection (alias: one connection rm)
10646
10770
  one config Configure access control (permissions, scoping)
@@ -10734,7 +10858,7 @@ program.hook("postAction", async () => {
10734
10858
  if (!isNewerVersion(info.version, current)) return;
10735
10859
  autoUpdate(info.version, info.publishedAt);
10736
10860
  });
10737
- program.command("init").description("Set up One and install MCP to your AI agents (interactive: picks global or project scope)").option("-y, --yes", "Skip confirmations").option("-g, --global", "Write the One config globally (~/.one/config.json) \u2014 skips the scope picker").option("-p, --project", "Write the One config for this project only (~/.one/projects/<slug>/) \u2014 skips the scope picker").action(async (options) => {
10861
+ program.command("init").description("Set up One and install the skill to your AI agents (interactive; pass --auth for a no-prompt setup)").option("-y, --yes", "Skip confirmations").option("-g, --global", "Write the One config globally (~/.one/config.json) \u2014 skips the scope picker").option("-p, --project", "Write the One config for this project only (~/.one/projects/<slug>/) \u2014 skips the scope picker").option("--auth <method>", 'Non-interactive setup (no prompts): "browser" opens a login window, "manual" uses --api-key').option("--api-key <key>", "API key for --auth manual (sk_live_\u2026 or sk_test_\u2026)").option("--openai-key <key>", "Optional OpenAI key for `one mem` semantic search (non-interactive setup)").action(async (options) => {
10738
10862
  await initCommand(options);
10739
10863
  });
10740
10864
  program.command("login").description("Authenticate with One via browser").action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@withone/cli",
3
- "version": "1.43.9",
3
+ "version": "1.44.1",
4
4
  "description": "CLI for managing One",
5
5
  "type": "module",
6
6
  "files": [
@@ -31,7 +31,9 @@ one login # Browser-based login (opens app.withone.ai)
31
31
  one logout # Clear local credentials
32
32
  ```
33
33
 
34
- `one login` opens the browser for OAuth authentication and automatically creates and stores an API key. If already logged in, the user can choose to log in globally or for the current directory. `one logout` shows current session info and confirms before clearing credentials. For CI/CD or headless environments, use `one init` to paste a key manually.
34
+ `one login` opens the browser for OAuth authentication and automatically creates and stores an API key. If already logged in, the user can choose to log in globally or for the current directory. `one logout` shows current session info and confirms before clearing credentials.
35
+
36
+ **Onboarding a user with no prompts:** run `one init --auth browser` — it opens a login window (the user authenticates there), saves the key, and auto-installs this skill, all without blocking on stdin. Add `-g`/`-p` for scope (default global). For CI/CD or headless environments, use `one init --auth manual --api-key sk_live_...`.
35
37
 
36
38
  ## Core Workflow: search -> knowledge -> execute
37
39