@paradigma-inc/flywheel 0.1.104 → 0.1.108

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
@@ -14,6 +14,12 @@ One-shot installer (macOS and Linux):
14
14
  curl -fsSL https://flywheel.paradigma.inc/install | sh
15
15
  ```
16
16
 
17
+ Windows users running their AI coding host from WSL should open the WSL distro,
18
+ install Node.js 20+ and npm inside WSL, and run the same installer there.
19
+ Flywheel then writes WSL-side config, so launch the host from that same WSL
20
+ environment. If browser auth does not open from WSL, copy the printed approval
21
+ URL and setup code into your Windows browser.
22
+
17
23
  The script installs `@paradigma-inc/flywheel@latest` into the managed user
18
24
  prefix `${FLYWHEEL_INSTALL_PREFIX:-$HOME/.local}`, adds a marked PATH block
19
25
  when needed, and then runs guided `flywheel setup` through the installed binary.
@@ -47,9 +53,17 @@ if (($userPath -split ";") -notcontains $prefix) {
47
53
  if (($env:Path -split ";") -notcontains $prefix) {
48
54
  $env:Path = "$prefix;$env:Path"
49
55
  }
50
- & "$prefix\flywheel.cmd" setup --mode cli
56
+
57
+ # MCP host wiring for native Windows hosts
58
+ & "$prefix\flywheel.cmd" setup --mode mcp
51
59
  ```
52
60
 
61
+ Use the PowerShell path when Codex, Claude Code, Cursor, or another AI coding
62
+ host runs as a native Windows process. Use `--mode cli` instead when you only
63
+ want direct `flywheel` binary setup without MCP host wiring. Do not install in
64
+ both WSL and native Windows unless you intentionally want separate Flywheel
65
+ setups.
66
+
53
67
  Configure your AI host (Claude Code, Codex, etc.) to call Flywheel over MCP:
54
68
 
55
69
  ```bash
@@ -57,6 +71,12 @@ Configure your AI host (Claude Code, Codex, etc.) to call Flywheel over MCP:
57
71
  npx --yes @paradigma-inc/flywheel@latest setup --mode mcp --install-skill
58
72
  ```
59
73
 
74
+ In native PowerShell, the equivalent command is:
75
+
76
+ ```powershell
77
+ npx --yes @paradigma-inc/flywheel@latest setup --mode mcp --install-skill
78
+ ```
79
+
60
80
  ## Shell Completion
61
81
 
62
82
  `flywheel completion <shell>` generates shell completion scripts from the public
@@ -200,12 +220,15 @@ npx --yes @paradigma-inc/flywheel setup --mode cli
200
220
 
201
221
  ## Auth Modes And Remote Shells
202
222
 
203
- `setup --auth-mode auto|loopback|device` controls authentication behavior:
223
+ `setup --auth-mode <mode>` controls authentication behavior. Supported values
224
+ are:
204
225
 
205
- - `loopback`: opens `/auth/mcp/setup` and waits for a localhost callback.
226
+ - `auto` (default): uses the active device approval flow.
206
227
  - `device`: opens `/auth/mcp/setup/device`, confirms a setup code in browser,
207
228
  and CLI polls for approval.
208
- - `auto` (default): uses `device` on SSH shells and `loopback` otherwise.
229
+ - `loopback`: retired legacy mode. The parser still accepts it so explicit
230
+ requests receive the deterministic FLY-416 remediation, which tells users to
231
+ use `--auth-mode device` or `--auth-mode auto`.
209
232
 
210
233
  Remote/headless example:
211
234
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paradigma-inc/flywheel",
3
- "version": "0.1.104",
3
+ "version": "0.1.108",
4
4
  "description": "One-command setup for Flywheel MCP hosts",
5
5
  "type": "module",
6
6
  "files": [
package/src/cli.mjs CHANGED
@@ -1369,7 +1369,8 @@ Configure the Flywheel CLI, MCP host entries, and bundled skills without changin
1369
1369
  .optionsGroup("Advanced:")
1370
1370
  .option(
1371
1371
  "--auth-mode <mode>",
1372
- "API-key mint flow: auto | loopback | device (default: auto)",
1372
+ "Device approval flow; default auto. " +
1373
+ "Explicit loopback is retired and returns FLY-416 guidance",
1373
1374
  normalizeAuthMode,
1374
1375
  "auto",
1375
1376
  )
@@ -1,6 +1,6 @@
1
- import { access, mkdir, readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
1
+ import { access, readFile } from "node:fs/promises";
3
2
  import { load as loadYaml, dump as dumpYaml } from "js-yaml";
3
+ import { secureFileMode, writeSecureFile } from "./secure-write.mjs";
4
4
 
5
5
  function stripJsonComments(text) {
6
6
  let result = "";
@@ -117,11 +117,7 @@ export async function readJsonConfig(filePath) {
117
117
  }
118
118
 
119
119
  export async function writeJsonConfig(filePath, config) {
120
- await mkdir(path.dirname(filePath), { recursive: true });
121
- await writeFile(filePath, `${JSON.stringify(config, null, 2)}\n`, {
122
- encoding: "utf8",
123
- mode: 0o600,
124
- });
120
+ await writeSecureFile(filePath, `${JSON.stringify(config, null, 2)}\n`);
125
121
  }
126
122
 
127
123
  export async function readYamlConfig(filePath) {
@@ -146,11 +142,7 @@ export async function readYamlConfig(filePath) {
146
142
  }
147
143
 
148
144
  export async function writeYamlConfig(filePath, config) {
149
- await mkdir(path.dirname(filePath), { recursive: true });
150
- await writeFile(filePath, dumpYaml(config, { noRefs: true }), {
151
- encoding: "utf8",
152
- mode: 0o600,
153
- });
145
+ await writeSecureFile(filePath, dumpYaml(config, { noRefs: true }));
154
146
  }
155
147
 
156
148
  export async function resolveMcpPath(candidates) {
@@ -479,6 +471,7 @@ export async function appendTomlServer(
479
471
  // header when the caller explicitly toggled auth mode via `--oauth` or
480
472
  // `--api-key`.
481
473
  if (!enforceAuthMode || existingEntry.authMode === newAuthMode) {
474
+ await secureFileMode(filePath);
482
475
  return { alreadyExists: true };
483
476
  }
484
477
  }
@@ -515,11 +508,7 @@ export async function appendTomlServer(
515
508
  : existing.length > 0
516
509
  ? "\n"
517
510
  : "";
518
- await mkdir(path.dirname(filePath), { recursive: true });
519
- await writeFile(filePath, `${existing}${separator}${block}`, {
520
- encoding: "utf8",
521
- mode: 0o600,
522
- });
511
+ await writeSecureFile(filePath, `${existing}${separator}${block}`);
523
512
  return { alreadyExists: false };
524
513
  }
525
514
 
@@ -582,7 +571,6 @@ export async function removeCodexTomlServer({ filePath, serverName }) {
582
571
  });
583
572
  if (!removed) return { changed: false };
584
573
 
585
- await mkdir(path.dirname(filePath), { recursive: true });
586
- await writeFile(filePath, stripped, { encoding: "utf8", mode: 0o600 });
574
+ await writeSecureFile(filePath, stripped);
587
575
  return { changed: true };
588
576
  }
@@ -296,7 +296,8 @@ export const USAGE_EXTRAS = [
296
296
  name: "auth-mode",
297
297
  type: "string",
298
298
  usage: "--auth-mode <MODE>",
299
- description: "API-key mint flow: auto | loopback | device",
299
+ description:
300
+ "Device approval flow; retired legacy mode returns FLY-416 guidance; default auto",
300
301
  default: "auto",
301
302
  choices: ["auto", "loopback", "device"],
302
303
  },
@@ -1,6 +1,7 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
1
+ import { readFile } from "node:fs/promises";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
+ import { writeSecureFile } from "../secure-write.mjs";
4
5
 
5
6
  const RUNTIME_BASE_URL_SUFFIX = "/api";
6
7
 
@@ -76,9 +77,8 @@ export async function writeRuntimeConfig({
76
77
  }
77
78
 
78
79
  const baseUrl = normalizeRuntimeBaseUrl(origin);
79
- await mkdir(path.dirname(configPath), { recursive: true });
80
80
  const existing = await readExistingRuntimeConfig(configPath);
81
81
  const merged = { ...existing, baseUrl, apiKey: trimmedKey };
82
- await writeFile(configPath, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
82
+ await writeSecureFile(configPath, `${JSON.stringify(merged, null, 2)}\n`);
83
83
  return { configPath, baseUrl };
84
84
  }
@@ -1,9 +1,9 @@
1
1
  export declare const PRODUCT_TELEMETRY_CONTRACT_VERSION: "2026-05-18.2";
2
- export declare const PRODUCT_TELEMETRY_EVENT_NAMES: readonly ["api_client_operation_recorded", "api_route_behavior_recorded", "cli_command_behavior_recorded", "credits_checkout_started", "mcp_tool_behavior_recorded", "message_sent", "node_created", "node_link_disabled", "node_link_enabled", "node_public_disabled", "node_public_enabled", "product_demand_signal_recorded", "server_workflow_behavior_recorded", "subscription_checkout_started", "user_session_started", "web_app_opened", "web_artifact_payload_batch", "web_artifact_payload_failed", "web_artifact_payload_loaded", "web_artifact_payload_retried", "web_artifact_section_opened", "web_artifact_unrenderable_seen", "web_asset_deleted", "web_asset_note_saved", "web_asset_operation_failed", "web_asset_panel_opened", "web_asset_uploaded", "web_collaboration_action_completed", "web_collaboration_action_failed", "web_graph_layout_changed", "web_graph_load_failed", "web_graph_loaded", "web_graph_mutation_completed", "web_graph_mutation_failed", "web_graph_mutation_started", "web_graph_node_focused", "web_graph_opened", "web_graph_partial_notice_shown", "web_graph_search_changed", "web_graph_search_empty", "web_node_closed", "web_node_edit_cancelled", "web_node_edit_failed", "web_node_edit_saved", "web_node_edit_started", "web_node_hydration_completed", "web_node_hydration_failed", "web_node_lease_acquired", "web_node_lease_failed", "web_node_lease_heartbeat_failed", "web_node_lease_released", "web_node_opened", "web_node_stage_committed", "web_node_stage_created", "web_node_stage_discarded", "web_node_stage_failed", "web_node_stage_rebased", "web_settings_opened", "web_temp_node_commit_completed", "web_temp_node_commit_failed"];
2
+ export declare const PRODUCT_TELEMETRY_EVENT_NAMES: readonly ["api_client_operation_recorded", "api_route_behavior_recorded", "cli_command_behavior_recorded", "credits_checkout_started", "mcp_tool_behavior_recorded", "message_sent", "node_created", "node_link_disabled", "node_link_enabled", "node_public_disabled", "node_public_enabled", "product_demand_signal_recorded", "server_workflow_behavior_recorded", "subscription_checkout_started", "user_session_started", "web_app_opened", "web_artifact_payload_batch", "web_artifact_payload_failed", "web_artifact_payload_loaded", "web_artifact_payload_retried", "web_artifact_section_opened", "web_artifact_unrenderable_seen", "web_asset_deleted", "web_asset_note_saved", "web_asset_operation_failed", "web_asset_panel_opened", "web_asset_uploaded", "web_collaboration_action_completed", "web_collaboration_action_failed", "web_graph_filter_panel_closed", "web_graph_filter_panel_opened", "web_graph_filter_visibility_changed", "web_graph_layout_changed", "web_graph_load_failed", "web_graph_loaded", "web_graph_mutation_completed", "web_graph_mutation_failed", "web_graph_mutation_started", "web_graph_node_focused", "web_graph_opened", "web_graph_partial_notice_shown", "web_graph_search_changed", "web_graph_search_empty", "web_node_closed", "web_node_edit_cancelled", "web_node_edit_failed", "web_node_edit_saved", "web_node_edit_started", "web_node_hydration_completed", "web_node_hydration_failed", "web_node_lease_acquired", "web_node_lease_failed", "web_node_lease_heartbeat_failed", "web_node_lease_released", "web_node_opened", "web_node_stage_committed", "web_node_stage_created", "web_node_stage_discarded", "web_node_stage_failed", "web_node_stage_rebased", "web_settings_opened", "web_temp_node_commit_completed", "web_temp_node_commit_failed"];
3
3
  export type ProductTelemetryEventName = (typeof PRODUCT_TELEMETRY_EVENT_NAMES)[number];
4
4
  export declare const PRODUCT_TELEMETRY_SURFACES: readonly ["api", "api_client", "cli", "mcp", "server", "webui"];
5
5
  export type ProductTelemetrySurface = (typeof PRODUCT_TELEMETRY_SURFACES)[number];
6
- export declare const WEBUI_PRODUCT_TELEMETRY_EVENT_NAMES: readonly ["web_app_opened", "web_artifact_payload_batch", "web_artifact_payload_failed", "web_artifact_payload_loaded", "web_artifact_payload_retried", "web_artifact_section_opened", "web_artifact_unrenderable_seen", "web_asset_deleted", "web_asset_note_saved", "web_asset_operation_failed", "web_asset_panel_opened", "web_asset_uploaded", "web_collaboration_action_completed", "web_collaboration_action_failed", "web_graph_layout_changed", "web_graph_load_failed", "web_graph_loaded", "web_graph_mutation_completed", "web_graph_mutation_failed", "web_graph_mutation_started", "web_graph_node_focused", "web_graph_opened", "web_graph_partial_notice_shown", "web_graph_search_changed", "web_graph_search_empty", "web_node_closed", "web_node_edit_cancelled", "web_node_edit_failed", "web_node_edit_saved", "web_node_edit_started", "web_node_hydration_completed", "web_node_hydration_failed", "web_node_lease_acquired", "web_node_lease_failed", "web_node_lease_heartbeat_failed", "web_node_lease_released", "web_node_opened", "web_node_stage_committed", "web_node_stage_created", "web_node_stage_discarded", "web_node_stage_failed", "web_node_stage_rebased", "web_settings_opened", "web_temp_node_commit_completed", "web_temp_node_commit_failed"];
6
+ export declare const WEBUI_PRODUCT_TELEMETRY_EVENT_NAMES: readonly ["web_app_opened", "web_artifact_payload_batch", "web_artifact_payload_failed", "web_artifact_payload_loaded", "web_artifact_payload_retried", "web_artifact_section_opened", "web_artifact_unrenderable_seen", "web_asset_deleted", "web_asset_note_saved", "web_asset_operation_failed", "web_asset_panel_opened", "web_asset_uploaded", "web_collaboration_action_completed", "web_collaboration_action_failed", "web_graph_filter_panel_closed", "web_graph_filter_panel_opened", "web_graph_filter_visibility_changed", "web_graph_layout_changed", "web_graph_load_failed", "web_graph_loaded", "web_graph_mutation_completed", "web_graph_mutation_failed", "web_graph_mutation_started", "web_graph_node_focused", "web_graph_opened", "web_graph_partial_notice_shown", "web_graph_search_changed", "web_graph_search_empty", "web_node_closed", "web_node_edit_cancelled", "web_node_edit_failed", "web_node_edit_saved", "web_node_edit_started", "web_node_hydration_completed", "web_node_hydration_failed", "web_node_lease_acquired", "web_node_lease_failed", "web_node_lease_heartbeat_failed", "web_node_lease_released", "web_node_opened", "web_node_stage_committed", "web_node_stage_created", "web_node_stage_discarded", "web_node_stage_failed", "web_node_stage_rebased", "web_settings_opened", "web_temp_node_commit_completed", "web_temp_node_commit_failed"];
7
7
  export type WebuiProductTelemetryEventName = (typeof WEBUI_PRODUCT_TELEMETRY_EVENT_NAMES)[number];
8
8
  export declare const WEBUI_NODE_WORKFLOW_FAMILIES: readonly ["artifacts", "assets", "bulk_graph_actions", "canvas", "commit", "context_menu", "graph_layout", "hooks", "lease", "minimap", "node_focus", "node_hydration", "node_open", "node_selection", "node_view_editing", "partial_graph_preview", "radial_view", "share_policy", "staged_edit", "tags", "timeline_layout"];
9
9
  export type WebuiNodeWorkflowFamily = (typeof WEBUI_NODE_WORKFLOW_FAMILIES)[number];