@appfleet-cli/cli 0.1.1 → 0.1.3

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/dist/appfleet.js CHANGED
@@ -3370,10 +3370,58 @@ var program = new Command();
3370
3370
 
3371
3371
  // src/appfleet.ts
3372
3372
  import { realpathSync } from "node:fs";
3373
+ import { readFile as readFile5 } from "node:fs/promises";
3374
+ import { dirname as dirname5, join as join4 } from "node:path";
3373
3375
  import { fileURLToPath } from "node:url";
3374
3376
 
3375
3377
  // src/command-registry.ts
3376
3378
  var cliCommandDocs = [
3379
+ {
3380
+ namespace: "appfleet",
3381
+ name: "login",
3382
+ summary: "Sign in to AppFleet Cloud.",
3383
+ usage: "appfleet login [--workspace <workspace-id>] [--email <email>] [--api-base-url <url>] [--local-test] [--json]",
3384
+ arguments: [],
3385
+ options: [
3386
+ { flags: "--workspace <workspace-id>", description: "Workspace id to sign in to; defaults to workspace_local." },
3387
+ { flags: "--email <email>", description: "Optional non-secret user label for local-test identity metadata." },
3388
+ { flags: "--api-base-url <url>", description: "AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
3389
+ { flags: "--local-test", description: "Create local-test session metadata instead of signing in to AppFleet Cloud." },
3390
+ { flags: "--json", description: "Emit machine-readable JSON." }
3391
+ ],
3392
+ examples: [
3393
+ "appfleet login",
3394
+ "appfleet login --workspace workspace_prod",
3395
+ "appfleet login --json"
3396
+ ],
3397
+ reads: [".appfleet/cloud-session.json if it already exists"],
3398
+ writes: [".appfleet/cloud-session.json hosted redacted session metadata with status and expiry", ".appfleet/cloud-auth-token mode-0600 local credential file"],
3399
+ secretSafety: [
3400
+ "Opens the hosted browser auth flow and stores session metadata only.",
3401
+ "The short-lived hosted session token is isolated in a mode-0600 local credential file and is never printed or included in project memory.",
3402
+ "Does not store OAuth refresh material, session cookies, provider credentials, encrypted credential blobs, key wrappers, command output, secret fragments, or secret values."
3403
+ ]
3404
+ },
3405
+ {
3406
+ namespace: "appfleet",
3407
+ name: "logout",
3408
+ summary: "Sign out of AppFleet Cloud.",
3409
+ usage: "appfleet logout [--api-base-url <url>] [--local-test] [--json]",
3410
+ arguments: [],
3411
+ options: [
3412
+ { flags: "--api-base-url <url>", description: "AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
3413
+ { flags: "--local-test", description: "Remove local-test session metadata instead of revoking a hosted AppFleet Cloud session." },
3414
+ { flags: "--json", description: "Emit machine-readable JSON." }
3415
+ ],
3416
+ examples: ["appfleet logout", "appfleet logout --json"],
3417
+ reads: [".appfleet/cloud-session.json"],
3418
+ writes: [".appfleet/cloud-session.json and .appfleet/cloud-auth-token are removed when present"],
3419
+ secretSafety: [
3420
+ "Revokes hosted session metadata through AppFleet Cloud when available.",
3421
+ "Removes local hosted session metadata and the local credential file without printing credential material.",
3422
+ "Does not read or print OAuth refresh material, session cookies, provider credentials, encrypted credential blobs, key wrappers, command output, or secret fragments."
3423
+ ]
3424
+ },
3377
3425
  {
3378
3426
  namespace: "appfleet",
3379
3427
  name: "link",
@@ -3389,6 +3437,68 @@ var cliCommandDocs = [
3389
3437
  "Never uploads environment values, credential values, command output, key wrappers, or provider payloads."
3390
3438
  ]
3391
3439
  },
3440
+ {
3441
+ namespace: "appfleet",
3442
+ name: "doctor",
3443
+ summary: "Run safe diagnostics for a remembered project, optionally uploading the report.",
3444
+ usage: "appfleet doctor [project] [--upload] [--json]",
3445
+ arguments: ["project: project id, path, URL, or repo fingerprint; defaults to the current project when resolvable."],
3446
+ options: [
3447
+ { flags: "--upload", description: "Upload the safe doctor report to AppFleet Cloud after the local check." },
3448
+ { flags: "--json", description: "Emit machine-readable JSON." }
3449
+ ],
3450
+ examples: [
3451
+ "appfleet doctor appfleet-demo",
3452
+ "appfleet doctor .",
3453
+ "appfleet doctor appfleet-demo --upload"
3454
+ ],
3455
+ reads: [".appfleet/project-memory.json", ".appfleet/cloud-session.json when --upload is used"],
3456
+ writes: [".appfleet/project-memory.json latest safe doctor evidence", ".appfleet/cloud-metadata-sync.json when --upload is used"],
3457
+ secretSafety: [
3458
+ "Runs safe URL and metadata checks only.",
3459
+ "Uploads safe project metadata and doctor evidence only when --upload is passed.",
3460
+ "Never reads .env files or uploads environment values, credential values, command output, key wrappers, or provider payloads."
3461
+ ]
3462
+ },
3463
+ {
3464
+ namespace: "appfleet",
3465
+ name: "workspaces",
3466
+ summary: "List AppFleet Cloud workspaces available to the signed-in user.",
3467
+ usage: "appfleet workspaces [--api-base-url <url>] [--json]",
3468
+ arguments: [],
3469
+ options: [
3470
+ { flags: "--api-base-url <url>", description: "AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
3471
+ { flags: "--json", description: "Emit machine-readable JSON." }
3472
+ ],
3473
+ examples: ["appfleet workspaces", "appfleet workspaces --json"],
3474
+ reads: [".appfleet/cloud-session.json", ".appfleet/cloud-auth-token"],
3475
+ writes: [],
3476
+ secretSafety: [
3477
+ "Uses the active hosted AppFleet session to list workspace metadata.",
3478
+ "Does not print auth tokens, cookies, provider credentials, environment values, or secret fragments."
3479
+ ]
3480
+ },
3481
+ {
3482
+ namespace: "appfleet",
3483
+ name: "workspace",
3484
+ summary: "Show or change the active AppFleet Cloud workspace.",
3485
+ usage: "appfleet workspace current|list|use <workspace-id> [--json]",
3486
+ arguments: [],
3487
+ options: [
3488
+ { flags: "--json", description: "Emit machine-readable JSON." }
3489
+ ],
3490
+ examples: [
3491
+ "appfleet workspace current",
3492
+ "appfleet workspace list",
3493
+ "appfleet workspace use workspace_prod"
3494
+ ],
3495
+ reads: [".appfleet/cloud-session.json", ".appfleet/cloud-auth-token"],
3496
+ writes: [".appfleet/cloud-session.json and .appfleet/cloud-auth-token when using a workspace"],
3497
+ secretSafety: [
3498
+ "Workspace use runs the hosted AppFleet login flow for the selected workspace.",
3499
+ "Stores active workspace session metadata only and never prints auth tokens, cookies, provider credentials, or secret fragments."
3500
+ ]
3501
+ },
3392
3502
  {
3393
3503
  namespace: "appfleet",
3394
3504
  name: "init",
@@ -3421,20 +3531,21 @@ var cliCommandDocs = [
3421
3531
  {
3422
3532
  namespace: "auth",
3423
3533
  name: "login",
3424
- summary: "Create local-test session metadata by default, or hosted session metadata when production cloud is explicitly enabled.",
3425
- usage: "appfleet auth login [--workspace <workspace-id>] [--email <email>] [--production-cloud] [--api-base-url <url>] [--json]",
3534
+ summary: "Sign in to AppFleet Cloud; use --local-test for local scaffold sessions.",
3535
+ usage: "appfleet auth login [--workspace <workspace-id>] [--email <email>] [--api-base-url <url>] [--local-test] [--json]",
3426
3536
  arguments: [],
3427
3537
  options: [
3428
3538
  { flags: "--workspace <workspace-id>", description: "Workspace id for local-test or hosted session metadata." },
3429
3539
  { flags: "--email <email>", description: "Optional non-secret user label for local-test identity or hosted login metadata." },
3430
- { flags: "--production-cloud", description: "Explicitly use hosted auth mode; also requires API/auth env config." },
3431
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
3540
+ { flags: "--local-test", description: "Create local-test session metadata instead of signing in to AppFleet Cloud." },
3541
+ { flags: "--production-cloud", description: "Deprecated compatibility flag; hosted AppFleet Cloud is already the default." },
3542
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
3432
3543
  { flags: "--json", description: "Emit machine-readable JSON." }
3433
3544
  ],
3434
3545
  examples: [
3435
- "appfleet auth login --workspace workspace_test",
3436
- "appfleet auth login --workspace workspace_test --email demo@example.com --json",
3437
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet auth login --production-cloud --workspace workspace_prod --json"
3546
+ "appfleet login",
3547
+ "appfleet auth login --workspace workspace_prod --json",
3548
+ "appfleet auth login --local-test --workspace workspace_test --email demo@example.com --json"
3438
3549
  ],
3439
3550
  reads: [".appfleet/cloud-session.json if it already exists"],
3440
3551
  writes: [".appfleet/cloud-session.json local-test or hosted redacted session metadata with status and expiry"],
@@ -3447,18 +3558,19 @@ var cliCommandDocs = [
3447
3558
  {
3448
3559
  namespace: "auth",
3449
3560
  name: "logout",
3450
- summary: "Log out of local-test session metadata by default, or revoke hosted session metadata when production cloud is explicitly enabled.",
3451
- usage: "appfleet auth logout [--production-cloud] [--api-base-url <url>] [--json]",
3561
+ summary: "Sign out of AppFleet Cloud; use --local-test for local scaffold sessions.",
3562
+ usage: "appfleet auth logout [--api-base-url <url>] [--local-test] [--json]",
3452
3563
  arguments: [],
3453
3564
  options: [
3454
- { flags: "--production-cloud", description: "Explicitly use hosted logout mode; also requires API/auth env config and hosted session metadata." },
3455
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
3565
+ { flags: "--local-test", description: "Remove local-test session metadata instead of revoking a hosted AppFleet Cloud session." },
3566
+ { flags: "--production-cloud", description: "Deprecated compatibility flag; hosted AppFleet Cloud is already the default." },
3567
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
3456
3568
  { flags: "--json", description: "Emit machine-readable JSON." }
3457
3569
  ],
3458
3570
  examples: [
3459
- "appfleet auth logout",
3571
+ "appfleet logout",
3460
3572
  "appfleet auth logout --json",
3461
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet auth logout --production-cloud --json"
3573
+ "appfleet auth logout --local-test --json"
3462
3574
  ],
3463
3575
  reads: [".appfleet/cloud-session.json"],
3464
3576
  writes: [".appfleet/cloud-session.json is removed when present; JSON output includes a safe logged_out/not_found/missing_hosted_session status"],
@@ -3476,23 +3588,23 @@ var cliCommandDocs = [
3476
3588
  arguments: [],
3477
3589
  options: [
3478
3590
  { flags: "--workspace <workspace-id>", description: "Workspace id for the local-test sync store." },
3479
- { flags: "--production-cloud", description: "Explicitly use production cloud mode; also requires API/auth env config." },
3480
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
3591
+ { flags: "--production-cloud", description: "Explicitly use production cloud mode against AppFleet Cloud." },
3592
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
3481
3593
  { flags: "--idempotency-key <key>", description: "Override the production metadata sync idempotency key; retries reuse the same key." },
3482
3594
  { flags: "--json", description: "Emit machine-readable JSON." }
3483
3595
  ],
3484
3596
  examples: [
3485
3597
  "appfleet cloud sync",
3486
3598
  "appfleet cloud sync --workspace workspace_test --json",
3487
- "APPFLEET_PRODUCTION_CLOUD_ENABLED=true APPFLEET_API_BASE_URL=https://appfleet.xyz APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud sync --workspace <workspace-id> --json",
3488
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet cloud sync --production-cloud --workspace <workspace-id> --json"
3599
+ "APPFLEET_PRODUCTION_CLOUD_ENABLED=true APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud sync --workspace <workspace-id> --json",
3600
+ "appfleet cloud sync --production-cloud --workspace <workspace-id> --json"
3489
3601
  ],
3490
3602
  reads: [".appfleet/cloud-session.json", ".appfleet/project-memory.json", ".appfleet/cloud-metadata-sync.json"],
3491
3603
  writes: [".appfleet/cloud-metadata-sync.json", ".appfleet/project-memory.json lastSyncedAt for accepted projects"],
3492
3604
  secretSafety: [
3493
3605
  "Chooses local-test mode by default; production mode requires --production-cloud, APPFLEET_PRODUCTION_CLOUD_ENABLED=true, or APPFLEET_CLOUD_MODE=production.",
3494
- "In production mode, fails closed when APPFLEET_API_BASE_URL/--api-base-url or APPFLEET_CLOUD_AUTH_TOKEN is missing.",
3495
- "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured AppFleet web API base URL.",
3606
+ "In production mode, defaults to https://appfleet.xyz and fails closed when APPFLEET_CLOUD_AUTH_TOKEN is missing.",
3607
+ "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured or default AppFleet web API base URL.",
3496
3608
  "Production metadata sync sends redacted auth reporting plus an idempotency key, workspace id, and project metadata envelopes.",
3497
3609
  "Production metadata sync retries safe transient transport failures with the same idempotency key.",
3498
3610
  "Persists local-test and production sync metadata, project ids, fingerprints, durable queue status, idempotency metadata, and conflict reports only.",
@@ -3508,23 +3620,23 @@ var cliCommandDocs = [
3508
3620
  arguments: [],
3509
3621
  options: [
3510
3622
  { flags: "--workspace <workspace-id>", description: "Workspace id for the local-test sync store." },
3511
- { flags: "--production-cloud", description: "Explicitly use production cloud mode; also requires API/auth env config." },
3512
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
3623
+ { flags: "--production-cloud", description: "Explicitly use production cloud mode against AppFleet Cloud." },
3624
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
3513
3625
  { flags: "--idempotency-key <key>", description: "Override the production metadata sync idempotency key; retries reuse the same key." },
3514
3626
  { flags: "--json", description: "Emit machine-readable JSON." }
3515
3627
  ],
3516
3628
  examples: [
3517
3629
  "appfleet cloud metadata-sync",
3518
3630
  "appfleet cloud metadata-sync --workspace workspace_test --json",
3519
- "APPFLEET_CLOUD_MODE=production APPFLEET_API_BASE_URL=https://appfleet.xyz APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud metadata-sync --workspace <workspace-id> --json",
3520
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet cloud metadata-sync --production-cloud --workspace <workspace-id> --json"
3631
+ "APPFLEET_CLOUD_MODE=production APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud metadata-sync --workspace <workspace-id> --json",
3632
+ "appfleet cloud metadata-sync --production-cloud --workspace <workspace-id> --json"
3521
3633
  ],
3522
3634
  reads: [".appfleet/cloud-session.json", ".appfleet/project-memory.json", ".appfleet/cloud-metadata-sync.json"],
3523
3635
  writes: [".appfleet/cloud-metadata-sync.json", ".appfleet/project-memory.json lastSyncedAt for accepted projects"],
3524
3636
  secretSafety: [
3525
3637
  "Chooses local-test mode by default; production mode requires --production-cloud, APPFLEET_PRODUCTION_CLOUD_ENABLED=true, or APPFLEET_CLOUD_MODE=production.",
3526
- "In production mode, fails closed when APPFLEET_API_BASE_URL/--api-base-url or APPFLEET_CLOUD_AUTH_TOKEN is missing.",
3527
- "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured AppFleet web API base URL.",
3638
+ "In production mode, defaults to https://appfleet.xyz and fails closed when APPFLEET_CLOUD_AUTH_TOKEN is missing.",
3639
+ "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured or default AppFleet web API base URL.",
3528
3640
  "Production metadata sync sends redacted auth reporting plus an idempotency key, workspace id, and project metadata envelopes.",
3529
3641
  "Production metadata sync retries safe transient transport failures with the same idempotency key.",
3530
3642
  "Persists local-test and production sync metadata, project ids, fingerprints, durable queue status, idempotency metadata, and conflict reports only.",
@@ -8093,7 +8205,7 @@ function defaultProjectMemoryStorePath(cwd) {
8093
8205
  function errorMessage2(error) {
8094
8206
  return error instanceof Error ? error.message : String(error);
8095
8207
  }
8096
- if (import.meta.url === `file://${process.argv[1]}`) {
8208
+ if (import.meta.url.endsWith("/project-memory.ts") && import.meta.url === `file://${process.argv[1]}`) {
8097
8209
  const result = await runProjectMemoryCommand(process.argv.slice(2), {
8098
8210
  storePath: process.env.APPFLEET_PROJECT_MEMORY_STORE_PATH,
8099
8211
  currentGitRemoteFingerprint: process.env.APPFLEET_GIT_REMOTE_FINGERPRINT
@@ -8104,6 +8216,7 @@ if (import.meta.url === `file://${process.argv[1]}`) {
8104
8216
  }
8105
8217
 
8106
8218
  // src/cloud-session.ts
8219
+ var DEFAULT_PRODUCTION_API_BASE_URL = "https://appfleet.xyz";
8107
8220
  async function runCloudSessionCommand(argv, options = {}) {
8108
8221
  const now = options.now ?? (() => /* @__PURE__ */ new Date());
8109
8222
  let env = options.env ?? process.env;
@@ -8172,7 +8285,7 @@ async function runCloudSessionCommand(argv, options = {}) {
8172
8285
  createdAt: createdAt3,
8173
8286
  result: authResult
8174
8287
  });
8175
- const localAuthToken = authResult.authToken ?? authResult.sessionId;
8288
+ const localAuthToken = authResult.sessionId ?? authResult.authToken;
8176
8289
  if (localAuthToken) {
8177
8290
  await writeAuthToken(authTokenPath, localAuthToken);
8178
8291
  }
@@ -8792,7 +8905,7 @@ function parseCloudSessionCommand(argv, env) {
8792
8905
  action,
8793
8906
  workspaceId: readFlag4(normalizedArgv, "--workspace") ?? "workspace_local",
8794
8907
  email: readFlag4(normalizedArgv, "--email"),
8795
- productionCloud: cloudProductionFlag(normalizedArgv, env),
8908
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
8796
8909
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
8797
8910
  apiBaseUrl: readFlag4(normalizedArgv, "--api-base-url"),
8798
8911
  outputFormat
@@ -8802,7 +8915,7 @@ function parseCloudSessionCommand(argv, env) {
8802
8915
  return {
8803
8916
  namespace,
8804
8917
  action,
8805
- productionCloud: cloudProductionFlag(normalizedArgv, env),
8918
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
8806
8919
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
8807
8920
  apiBaseUrl: readFlag4(normalizedArgv, "--api-base-url"),
8808
8921
  outputFormat
@@ -9110,23 +9223,35 @@ function createZeroSecretBoundary() {
9110
9223
  storesSecretFragments: false
9111
9224
  };
9112
9225
  }
9113
- function cloudProductionFlag(argv, env) {
9226
+ function cloudProductionFlag(argv, env, defaultProduction = false) {
9227
+ if (hasFlag2(argv, "--local-test")) {
9228
+ return false;
9229
+ }
9114
9230
  if (hasFlag2(argv, "--production-cloud")) {
9115
9231
  return true;
9116
9232
  }
9117
- return env.APPFLEET_PRODUCTION_CLOUD_ENABLED === "true" || env.APPFLEET_CLOUD_MODE === "production";
9233
+ if (env.APPFLEET_CLOUD_MODE === "local-test") {
9234
+ return false;
9235
+ }
9236
+ return env.APPFLEET_PRODUCTION_CLOUD_ENABLED === "true" || env.APPFLEET_CLOUD_MODE === "production" || defaultProduction;
9118
9237
  }
9119
9238
  function cloudProductionSource(argv, env) {
9239
+ if (hasFlag2(argv, "--local-test")) {
9240
+ return "flag";
9241
+ }
9120
9242
  if (hasFlag2(argv, "--production-cloud")) {
9121
9243
  return "flag";
9122
9244
  }
9245
+ if (env.APPFLEET_CLOUD_MODE === "local-test") {
9246
+ return "env";
9247
+ }
9123
9248
  if (productionEnvEnabled(env)) {
9124
9249
  return "env";
9125
9250
  }
9126
9251
  return "default";
9127
9252
  }
9128
9253
  function resolveProductionCloudConfig(parsed, env) {
9129
- const apiBaseUrl = parsed.apiBaseUrl ?? env.APPFLEET_API_BASE_URL;
9254
+ const apiBaseUrl = parsed.apiBaseUrl ?? env.APPFLEET_API_BASE_URL ?? DEFAULT_PRODUCTION_API_BASE_URL;
9130
9255
  return {
9131
9256
  enabled: parsed.productionCloud,
9132
9257
  apiBaseUrl,
@@ -9134,7 +9259,7 @@ function resolveProductionCloudConfig(parsed, env) {
9134
9259
  databaseDsn: env.APPFLEET_DATABASE_URL,
9135
9260
  sources: {
9136
9261
  enabled: parsed.productionCloudSource,
9137
- apiBaseUrl: parsed.apiBaseUrl ? "flag" : env.APPFLEET_API_BASE_URL ? "env" : "missing",
9262
+ apiBaseUrl: parsed.apiBaseUrl ? "flag" : env.APPFLEET_API_BASE_URL ? "env" : "default",
9138
9263
  authToken: env.APPFLEET_CLOUD_AUTH_TOKEN ? env.APPFLEET_CLOUD_AUTH_TOKEN_SOURCE === "device_store" ? "device_store" : "env" : "missing",
9139
9264
  databaseDsn: env.APPFLEET_DATABASE_URL ? "env" : "missing"
9140
9265
  }
@@ -9145,14 +9270,11 @@ function productionEnvEnabled(env) {
9145
9270
  }
9146
9271
  function missingProductionConfig(config) {
9147
9272
  return [
9148
- config.apiBaseUrl ? void 0 : "APPFLEET_API_BASE_URL or --api-base-url",
9149
9273
  config.authToken ? void 0 : "APPFLEET_CLOUD_AUTH_TOKEN"
9150
9274
  ].filter((value) => value !== void 0);
9151
9275
  }
9152
9276
  function missingProductionAuthConfig(config) {
9153
- return [
9154
- config.apiBaseUrl ? void 0 : "APPFLEET_API_BASE_URL or --api-base-url"
9155
- ].filter((value) => value !== void 0);
9277
+ return [];
9156
9278
  }
9157
9279
  function resolveHostedAuthTimeoutMs(env, override) {
9158
9280
  const raw = override ?? Number.parseInt(env.APPFLEET_HOSTED_AUTH_TIMEOUT_MS ?? "", 10);
@@ -11461,7 +11583,7 @@ function commandError(error) {
11461
11583
  childStarted: false
11462
11584
  };
11463
11585
  }
11464
- if (import.meta.url === `file://${process.argv[1]}`) {
11586
+ if (import.meta.url.endsWith("/local-vault.ts") && import.meta.url === `file://${process.argv[1]}`) {
11465
11587
  const result = await runLocalVaultCommand(process.argv.slice(2));
11466
11588
  process.stdout.write(result.stdout);
11467
11589
  process.stderr.write(result.stderr);
@@ -11888,6 +12010,7 @@ var namespaces = [
11888
12010
  "vault",
11889
12011
  "secrets"
11890
12012
  ];
12013
+ var DEFAULT_PRODUCTION_API_BASE_URL2 = "https://appfleet.xyz";
11891
12014
  async function runAppFleetCli(argv) {
11892
12015
  const normalizedArgv = argv[0] === "--" ? argv.slice(1) : argv;
11893
12016
  if (shouldShowHelp(normalizedArgv)) {
@@ -11901,6 +12024,61 @@ async function runAppFleetCli(argv) {
11901
12024
  return 0;
11902
12025
  }
11903
12026
  const namespace = normalizedArgv[0];
12027
+ if (namespace === "login" || namespace === "logout") {
12028
+ const result = await runCloudSessionCommand(
12029
+ ["auth", namespace, ...normalizedArgv.slice(1)],
12030
+ cloudCommandOptions()
12031
+ );
12032
+ process.stdout.write(result.stdout);
12033
+ process.stderr.write(result.stderr);
12034
+ return result.exitCode;
12035
+ }
12036
+ if (namespace === "workspaces") {
12037
+ const result = await runWorkspaceListCommand(normalizedArgv.slice(1));
12038
+ process.stdout.write(result.stdout);
12039
+ process.stderr.write(result.stderr);
12040
+ return result.exitCode;
12041
+ }
12042
+ if (namespace === "workspace") {
12043
+ const action = normalizedArgv[1] ?? "current";
12044
+ if (action === "use") {
12045
+ const workspaceId = normalizedArgv[2] ?? readFlag9(normalizedArgv, "--workspace");
12046
+ if (!workspaceId) {
12047
+ process.stderr.write("AppFleet workspace failed: usage: appfleet workspace use <workspace-id> [--json]\n");
12048
+ return 1;
12049
+ }
12050
+ const result = await runCloudSessionCommand(
12051
+ [
12052
+ "auth",
12053
+ "login",
12054
+ "--workspace",
12055
+ workspaceId,
12056
+ ...normalizedArgv.includes("--json") ? ["--json"] : []
12057
+ ],
12058
+ cloudCommandOptions()
12059
+ );
12060
+ process.stdout.write(result.stdout);
12061
+ process.stderr.write(result.stderr);
12062
+ return result.exitCode;
12063
+ }
12064
+ if (action === "current") {
12065
+ const result = await runWorkspaceCurrentCommand(normalizedArgv.slice(2));
12066
+ process.stdout.write(result.stdout);
12067
+ process.stderr.write(result.stderr);
12068
+ return result.exitCode;
12069
+ }
12070
+ if (action === "list") {
12071
+ const result = await runWorkspaceListCommand(normalizedArgv.slice(2));
12072
+ process.stdout.write(result.stdout);
12073
+ process.stderr.write(result.stderr);
12074
+ return result.exitCode;
12075
+ }
12076
+ process.stderr.write(
12077
+ `AppFleet workspace failed: unknown workspace command "${action}"
12078
+ `
12079
+ );
12080
+ return 1;
12081
+ }
11904
12082
  if (namespace === "init") {
11905
12083
  const result = await runFirstRunSetup(normalizedArgv.slice(1));
11906
12084
  process.stdout.write(result.stdout);
@@ -11921,6 +12099,20 @@ async function runAppFleetCli(argv) {
11921
12099
  process.stderr.write(result.stderr);
11922
12100
  return result.exitCode;
11923
12101
  }
12102
+ if (namespace === "doctor") {
12103
+ const args = ["projects", "doctor", ...normalizedArgv.slice(1)];
12104
+ if (normalizedArgv.includes("--upload")) {
12105
+ return runDoctorAndUpload(args);
12106
+ }
12107
+ const result = await runProjectMemoryCommand(args, {
12108
+ storePath: process.env.APPFLEET_PROJECT_MEMORY_STORE_PATH,
12109
+ currentGitRemoteFingerprint: process.env.APPFLEET_GIT_REMOTE_FINGERPRINT,
12110
+ projectRoot: process.env.INIT_CWD
12111
+ });
12112
+ process.stdout.write(result.stdout);
12113
+ process.stderr.write(result.stderr);
12114
+ return result.exitCode;
12115
+ }
11924
12116
  if (namespace === "projects") {
11925
12117
  if (normalizedArgv[1] === "doctor" && normalizedArgv.includes("--upload")) {
11926
12118
  return runDoctorAndUpload(normalizedArgv);
@@ -11991,7 +12183,7 @@ async function runAppFleetCli(argv) {
11991
12183
  }
11992
12184
  function createAppFleetProgram() {
11993
12185
  const program2 = new Command();
11994
- program2.name("appfleet").description("Local AppFleet recovery cockpit and encrypted secret injection CLI.").showHelpAfterError().configureHelp({ sortSubcommands: true, sortOptions: true });
12186
+ program2.name("appfleet").description("AppFleet Cloud CLI for remembering, diagnosing, and recovering projects.").showHelpAfterError().configureHelp({ sortSubcommands: true, sortOptions: true });
11995
12187
  const initDoc = findCliCommandDoc("appfleet", "init");
11996
12188
  if (initDoc) {
11997
12189
  program2.addCommand(commandFromDoc(initDoc));
@@ -12000,6 +12192,26 @@ function createAppFleetProgram() {
12000
12192
  if (linkDoc) {
12001
12193
  program2.addCommand(commandFromDoc(linkDoc));
12002
12194
  }
12195
+ const loginDoc = findCliCommandDoc("appfleet", "login");
12196
+ if (loginDoc) {
12197
+ program2.addCommand(commandFromDoc(loginDoc));
12198
+ }
12199
+ const logoutDoc = findCliCommandDoc("appfleet", "logout");
12200
+ if (logoutDoc) {
12201
+ program2.addCommand(commandFromDoc(logoutDoc));
12202
+ }
12203
+ const doctorDoc = findCliCommandDoc("appfleet", "doctor");
12204
+ if (doctorDoc) {
12205
+ program2.addCommand(commandFromDoc(doctorDoc));
12206
+ }
12207
+ const workspacesDoc = findCliCommandDoc("appfleet", "workspaces");
12208
+ if (workspacesDoc) {
12209
+ program2.addCommand(commandFromDoc(workspacesDoc));
12210
+ }
12211
+ const workspaceDoc = findCliCommandDoc("appfleet", "workspace");
12212
+ if (workspaceDoc) {
12213
+ program2.addCommand(commandFromDoc(workspaceDoc));
12214
+ }
12003
12215
  for (const namespace of namespaces) {
12004
12216
  const namespaceCommand = new Command(namespace).description(
12005
12217
  namespaceDescription(namespace)
@@ -12022,6 +12234,139 @@ function cloudCommandOptions() {
12022
12234
  env: process.env
12023
12235
  };
12024
12236
  }
12237
+ async function runWorkspaceCurrentCommand(argv) {
12238
+ const outputFormat = argv.includes("--json") ? "json" : "human";
12239
+ const session = await readHostedSession();
12240
+ if (!session) {
12241
+ const document2 = {
12242
+ type: "appfleet_active_workspace_result",
12243
+ version: 1,
12244
+ status: "not_logged_in",
12245
+ activeWorkspaceId: void 0,
12246
+ next: "Run appfleet login."
12247
+ };
12248
+ return {
12249
+ exitCode: 1,
12250
+ stdout: outputFormat === "json" ? `${JSON.stringify(document2, null, 2)}
12251
+ ` : "",
12252
+ stderr: outputFormat === "json" ? "" : "AppFleet workspace failed: not logged in. Run appfleet login.\n"
12253
+ };
12254
+ }
12255
+ const document = {
12256
+ type: "appfleet_active_workspace_result",
12257
+ version: 1,
12258
+ status: "ok",
12259
+ activeWorkspaceId: session.workspaceId,
12260
+ user: session.user,
12261
+ expiresAt: session.expiresAt
12262
+ };
12263
+ return {
12264
+ exitCode: 0,
12265
+ stdout: outputFormat === "json" ? `${JSON.stringify(document, null, 2)}
12266
+ ` : `Active workspace: ${session.workspaceId}
12267
+ `,
12268
+ stderr: ""
12269
+ };
12270
+ }
12271
+ async function runWorkspaceListCommand(argv) {
12272
+ const outputFormat = argv.includes("--json") ? "json" : "human";
12273
+ const session = await readHostedSession();
12274
+ const authToken = await readWorkspaceAuthToken();
12275
+ if (!session || !authToken) {
12276
+ const document2 = {
12277
+ type: "appfleet_workspace_list_result",
12278
+ version: 1,
12279
+ status: "not_logged_in",
12280
+ workspaces: [],
12281
+ next: "Run appfleet login."
12282
+ };
12283
+ return {
12284
+ exitCode: 1,
12285
+ stdout: outputFormat === "json" ? `${JSON.stringify(document2, null, 2)}
12286
+ ` : "",
12287
+ stderr: outputFormat === "json" ? "" : "AppFleet workspaces failed: not logged in. Run appfleet login.\n"
12288
+ };
12289
+ }
12290
+ const apiBaseUrl = readFlag9(argv, "--api-base-url") ?? process.env.APPFLEET_API_BASE_URL ?? DEFAULT_PRODUCTION_API_BASE_URL2;
12291
+ const url = new URL("/api/workspaces", apiBaseUrl);
12292
+ url.searchParams.set("workspaceId", session.workspaceId);
12293
+ const response = await fetch(url, {
12294
+ headers: {
12295
+ Accept: "application/json",
12296
+ "X-AppFleet-Hosted-Session": authToken,
12297
+ "X-AppFleet-Workspace-Id": session.workspaceId
12298
+ }
12299
+ });
12300
+ if (!response.ok) {
12301
+ const document2 = {
12302
+ type: "appfleet_workspace_list_result",
12303
+ version: 1,
12304
+ status: "request_failed",
12305
+ httpStatusCode: response.status,
12306
+ activeWorkspaceId: session.workspaceId,
12307
+ workspaces: []
12308
+ };
12309
+ return {
12310
+ exitCode: 1,
12311
+ stdout: outputFormat === "json" ? `${JSON.stringify(document2, null, 2)}
12312
+ ` : "",
12313
+ stderr: outputFormat === "json" ? "" : `AppFleet workspaces failed: cloud request returned HTTP ${response.status}.
12314
+ `
12315
+ };
12316
+ }
12317
+ const parsed = await response.json();
12318
+ const workspaces = parsed.data?.workspaces ?? [];
12319
+ const document = {
12320
+ type: "appfleet_workspace_list_result",
12321
+ version: 1,
12322
+ status: "ok",
12323
+ activeWorkspaceId: session.workspaceId,
12324
+ workspaces: workspaces.map((workspace) => ({
12325
+ ...workspace,
12326
+ active: workspace.id === session.workspaceId
12327
+ }))
12328
+ };
12329
+ return {
12330
+ exitCode: 0,
12331
+ stdout: outputFormat === "json" ? `${JSON.stringify(document, null, 2)}
12332
+ ` : formatWorkspaceList(document),
12333
+ stderr: ""
12334
+ };
12335
+ }
12336
+ function formatWorkspaceList(document) {
12337
+ if (document.workspaces.length === 0) {
12338
+ return "No workspaces found.\n";
12339
+ }
12340
+ return `${document.workspaces.map(
12341
+ (workspace) => `${workspace.active ? "*" : " "} ${workspace.name} (${workspace.id})`
12342
+ ).join("\n")}
12343
+ `;
12344
+ }
12345
+ async function readHostedSession() {
12346
+ try {
12347
+ const parsed = JSON.parse(await readFile5(defaultCliSessionPath(), "utf8"));
12348
+ if (!parsed.workspaceId) return void 0;
12349
+ return {
12350
+ type: parsed.type,
12351
+ workspaceId: parsed.workspaceId,
12352
+ user: parsed.user,
12353
+ expiresAt: parsed.expiresAt
12354
+ };
12355
+ } catch {
12356
+ return void 0;
12357
+ }
12358
+ }
12359
+ async function readWorkspaceAuthToken() {
12360
+ try {
12361
+ const token = await readFile5(join4(dirname5(defaultCliSessionPath()), "cloud-auth-token"), "utf8");
12362
+ return token.trim() || void 0;
12363
+ } catch {
12364
+ return void 0;
12365
+ }
12366
+ }
12367
+ function defaultCliSessionPath() {
12368
+ return process.env.APPFLEET_CLOUD_SESSION_PATH ?? join4(process.env.INIT_CWD ?? process.cwd(), ".appfleet", "cloud-session.json");
12369
+ }
12025
12370
  async function runDoctorAndUpload(argv) {
12026
12371
  const doctorArgs = argv.filter((argument) => argument !== "--upload");
12027
12372
  if (!doctorArgs.includes("--json")) doctorArgs.push("--json");
@@ -12198,9 +12543,9 @@ function namespaceDescription(namespace) {
12198
12543
  case "projects":
12199
12544
  return "Remember projects, summarize recovery status, and run safe diagnostics.";
12200
12545
  case "auth":
12201
- return "Manage local-test hosted auth session metadata.";
12546
+ return "Manage AppFleet Cloud login sessions.";
12202
12547
  case "cloud":
12203
- return "Prepare cloud-safe metadata sync envelopes for V1 scaffold flows.";
12548
+ return "Sync cloud-safe metadata with AppFleet Cloud.";
12204
12549
  case "providers":
12205
12550
  return "Inspect provider integration contracts and fail-closed discovery scaffolds.";
12206
12551
  case "ops":
@@ -24,7 +24,7 @@ type ProductionCloudConfig = {
24
24
  databaseDsn?: string;
25
25
  sources: {
26
26
  enabled: "flag" | "env" | "default";
27
- apiBaseUrl: "flag" | "env" | "missing";
27
+ apiBaseUrl: "flag" | "env" | "default" | "missing";
28
28
  authToken: "env" | "device_store" | "missing";
29
29
  databaseDsn: "env" | "missing";
30
30
  };
@@ -5,6 +5,7 @@ import { dirname, join } from "node:path";
5
5
  import { spawn } from "node:child_process";
6
6
  import { createAppProjectMemory, createCloudSyncMetadata, createCloudSyncRuntimeConflictState, createLocalMetadataSyncQueueEntry, createProjectMemorySyncRequest, createProjectMemorySyncResult, } from "@appfleet/domain";
7
7
  import { readProjectMemories, writeProjectMemories } from "./project-memory.js";
8
+ const DEFAULT_PRODUCTION_API_BASE_URL = "https://appfleet.xyz";
8
9
  export async function runCloudSessionCommand(argv, options = {}) {
9
10
  const now = options.now ?? (() => new Date());
10
11
  let env = options.env ?? process.env;
@@ -72,7 +73,7 @@ export async function runCloudSessionCommand(argv, options = {}) {
72
73
  createdAt,
73
74
  result: authResult,
74
75
  });
75
- const localAuthToken = authResult.authToken ?? authResult.sessionId;
76
+ const localAuthToken = authResult.sessionId ?? authResult.authToken;
76
77
  if (localAuthToken) {
77
78
  await writeAuthToken(authTokenPath, localAuthToken);
78
79
  }
@@ -706,7 +707,7 @@ function parseCloudSessionCommand(argv, env) {
706
707
  action,
707
708
  workspaceId: readFlag(normalizedArgv, "--workspace") ?? "workspace_local",
708
709
  email: readFlag(normalizedArgv, "--email"),
709
- productionCloud: cloudProductionFlag(normalizedArgv, env),
710
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
710
711
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
711
712
  apiBaseUrl: readFlag(normalizedArgv, "--api-base-url"),
712
713
  outputFormat,
@@ -716,7 +717,7 @@ function parseCloudSessionCommand(argv, env) {
716
717
  return {
717
718
  namespace,
718
719
  action,
719
- productionCloud: cloudProductionFlag(normalizedArgv, env),
720
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
720
721
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
721
722
  apiBaseUrl: readFlag(normalizedArgv, "--api-base-url"),
722
723
  outputFormat,
@@ -1014,24 +1015,37 @@ function createZeroSecretBoundary() {
1014
1015
  storesSecretFragments: false,
1015
1016
  };
1016
1017
  }
1017
- function cloudProductionFlag(argv, env) {
1018
+ function cloudProductionFlag(argv, env, defaultProduction = false) {
1019
+ if (hasFlag(argv, "--local-test")) {
1020
+ return false;
1021
+ }
1018
1022
  if (hasFlag(argv, "--production-cloud")) {
1019
1023
  return true;
1020
1024
  }
1025
+ if (env.APPFLEET_CLOUD_MODE === "local-test") {
1026
+ return false;
1027
+ }
1021
1028
  return (env.APPFLEET_PRODUCTION_CLOUD_ENABLED === "true" ||
1022
- env.APPFLEET_CLOUD_MODE === "production");
1029
+ env.APPFLEET_CLOUD_MODE === "production" ||
1030
+ defaultProduction);
1023
1031
  }
1024
1032
  function cloudProductionSource(argv, env) {
1033
+ if (hasFlag(argv, "--local-test")) {
1034
+ return "flag";
1035
+ }
1025
1036
  if (hasFlag(argv, "--production-cloud")) {
1026
1037
  return "flag";
1027
1038
  }
1039
+ if (env.APPFLEET_CLOUD_MODE === "local-test") {
1040
+ return "env";
1041
+ }
1028
1042
  if (productionEnvEnabled(env)) {
1029
1043
  return "env";
1030
1044
  }
1031
1045
  return "default";
1032
1046
  }
1033
1047
  function resolveProductionCloudConfig(parsed, env) {
1034
- const apiBaseUrl = parsed.apiBaseUrl ?? env.APPFLEET_API_BASE_URL;
1048
+ const apiBaseUrl = parsed.apiBaseUrl ?? env.APPFLEET_API_BASE_URL ?? DEFAULT_PRODUCTION_API_BASE_URL;
1035
1049
  return {
1036
1050
  enabled: parsed.productionCloud,
1037
1051
  apiBaseUrl,
@@ -1043,7 +1057,7 @@ function resolveProductionCloudConfig(parsed, env) {
1043
1057
  ? "flag"
1044
1058
  : env.APPFLEET_API_BASE_URL
1045
1059
  ? "env"
1046
- : "missing",
1060
+ : "default",
1047
1061
  authToken: env.APPFLEET_CLOUD_AUTH_TOKEN
1048
1062
  ? env.APPFLEET_CLOUD_AUTH_TOKEN_SOURCE === "device_store"
1049
1063
  ? "device_store"
@@ -1059,14 +1073,11 @@ function productionEnvEnabled(env) {
1059
1073
  }
1060
1074
  function missingProductionConfig(config) {
1061
1075
  return [
1062
- config.apiBaseUrl ? undefined : "APPFLEET_API_BASE_URL or --api-base-url",
1063
1076
  config.authToken ? undefined : "APPFLEET_CLOUD_AUTH_TOKEN",
1064
1077
  ].filter((value) => value !== undefined);
1065
1078
  }
1066
1079
  function missingProductionAuthConfig(config) {
1067
- return [
1068
- config.apiBaseUrl ? undefined : "APPFLEET_API_BASE_URL or --api-base-url",
1069
- ].filter((value) => value !== undefined);
1080
+ return [];
1070
1081
  }
1071
1082
  function resolveHostedAuthTimeoutMs(env, override) {
1072
1083
  const raw = override ?? Number.parseInt(env.APPFLEET_HOSTED_AUTH_TIMEOUT_MS ?? "", 10);
@@ -1,4 +1,50 @@
1
1
  export const cliCommandDocs = [
2
+ {
3
+ namespace: "appfleet",
4
+ name: "login",
5
+ summary: "Sign in to AppFleet Cloud.",
6
+ usage: "appfleet login [--workspace <workspace-id>] [--email <email>] [--api-base-url <url>] [--local-test] [--json]",
7
+ arguments: [],
8
+ options: [
9
+ { flags: "--workspace <workspace-id>", description: "Workspace id to sign in to; defaults to workspace_local." },
10
+ { flags: "--email <email>", description: "Optional non-secret user label for local-test identity metadata." },
11
+ { flags: "--api-base-url <url>", description: "AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
12
+ { flags: "--local-test", description: "Create local-test session metadata instead of signing in to AppFleet Cloud." },
13
+ { flags: "--json", description: "Emit machine-readable JSON." },
14
+ ],
15
+ examples: [
16
+ "appfleet login",
17
+ "appfleet login --workspace workspace_prod",
18
+ "appfleet login --json",
19
+ ],
20
+ reads: [".appfleet/cloud-session.json if it already exists"],
21
+ writes: [".appfleet/cloud-session.json hosted redacted session metadata with status and expiry", ".appfleet/cloud-auth-token mode-0600 local credential file"],
22
+ secretSafety: [
23
+ "Opens the hosted browser auth flow and stores session metadata only.",
24
+ "The short-lived hosted session token is isolated in a mode-0600 local credential file and is never printed or included in project memory.",
25
+ "Does not store OAuth refresh material, session cookies, provider credentials, encrypted credential blobs, key wrappers, command output, secret fragments, or secret values.",
26
+ ],
27
+ },
28
+ {
29
+ namespace: "appfleet",
30
+ name: "logout",
31
+ summary: "Sign out of AppFleet Cloud.",
32
+ usage: "appfleet logout [--api-base-url <url>] [--local-test] [--json]",
33
+ arguments: [],
34
+ options: [
35
+ { flags: "--api-base-url <url>", description: "AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
36
+ { flags: "--local-test", description: "Remove local-test session metadata instead of revoking a hosted AppFleet Cloud session." },
37
+ { flags: "--json", description: "Emit machine-readable JSON." },
38
+ ],
39
+ examples: ["appfleet logout", "appfleet logout --json"],
40
+ reads: [".appfleet/cloud-session.json"],
41
+ writes: [".appfleet/cloud-session.json and .appfleet/cloud-auth-token are removed when present"],
42
+ secretSafety: [
43
+ "Revokes hosted session metadata through AppFleet Cloud when available.",
44
+ "Removes local hosted session metadata and the local credential file without printing credential material.",
45
+ "Does not read or print OAuth refresh material, session cookies, provider credentials, encrypted credential blobs, key wrappers, command output, or secret fragments.",
46
+ ],
47
+ },
2
48
  {
3
49
  namespace: "appfleet",
4
50
  name: "link",
@@ -14,6 +60,68 @@ export const cliCommandDocs = [
14
60
  "Never uploads environment values, credential values, command output, key wrappers, or provider payloads.",
15
61
  ],
16
62
  },
63
+ {
64
+ namespace: "appfleet",
65
+ name: "doctor",
66
+ summary: "Run safe diagnostics for a remembered project, optionally uploading the report.",
67
+ usage: "appfleet doctor [project] [--upload] [--json]",
68
+ arguments: ["project: project id, path, URL, or repo fingerprint; defaults to the current project when resolvable."],
69
+ options: [
70
+ { flags: "--upload", description: "Upload the safe doctor report to AppFleet Cloud after the local check." },
71
+ { flags: "--json", description: "Emit machine-readable JSON." },
72
+ ],
73
+ examples: [
74
+ "appfleet doctor appfleet-demo",
75
+ "appfleet doctor .",
76
+ "appfleet doctor appfleet-demo --upload",
77
+ ],
78
+ reads: [".appfleet/project-memory.json", ".appfleet/cloud-session.json when --upload is used"],
79
+ writes: [".appfleet/project-memory.json latest safe doctor evidence", ".appfleet/cloud-metadata-sync.json when --upload is used"],
80
+ secretSafety: [
81
+ "Runs safe URL and metadata checks only.",
82
+ "Uploads safe project metadata and doctor evidence only when --upload is passed.",
83
+ "Never reads .env files or uploads environment values, credential values, command output, key wrappers, or provider payloads.",
84
+ ],
85
+ },
86
+ {
87
+ namespace: "appfleet",
88
+ name: "workspaces",
89
+ summary: "List AppFleet Cloud workspaces available to the signed-in user.",
90
+ usage: "appfleet workspaces [--api-base-url <url>] [--json]",
91
+ arguments: [],
92
+ options: [
93
+ { flags: "--api-base-url <url>", description: "AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
94
+ { flags: "--json", description: "Emit machine-readable JSON." },
95
+ ],
96
+ examples: ["appfleet workspaces", "appfleet workspaces --json"],
97
+ reads: [".appfleet/cloud-session.json", ".appfleet/cloud-auth-token"],
98
+ writes: [],
99
+ secretSafety: [
100
+ "Uses the active hosted AppFleet session to list workspace metadata.",
101
+ "Does not print auth tokens, cookies, provider credentials, environment values, or secret fragments.",
102
+ ],
103
+ },
104
+ {
105
+ namespace: "appfleet",
106
+ name: "workspace",
107
+ summary: "Show or change the active AppFleet Cloud workspace.",
108
+ usage: "appfleet workspace current|list|use <workspace-id> [--json]",
109
+ arguments: [],
110
+ options: [
111
+ { flags: "--json", description: "Emit machine-readable JSON." },
112
+ ],
113
+ examples: [
114
+ "appfleet workspace current",
115
+ "appfleet workspace list",
116
+ "appfleet workspace use workspace_prod",
117
+ ],
118
+ reads: [".appfleet/cloud-session.json", ".appfleet/cloud-auth-token"],
119
+ writes: [".appfleet/cloud-session.json and .appfleet/cloud-auth-token when using a workspace"],
120
+ secretSafety: [
121
+ "Workspace use runs the hosted AppFleet login flow for the selected workspace.",
122
+ "Stores active workspace session metadata only and never prints auth tokens, cookies, provider credentials, or secret fragments.",
123
+ ],
124
+ },
17
125
  {
18
126
  namespace: "appfleet",
19
127
  name: "init",
@@ -46,20 +154,21 @@ export const cliCommandDocs = [
46
154
  {
47
155
  namespace: "auth",
48
156
  name: "login",
49
- summary: "Create local-test session metadata by default, or hosted session metadata when production cloud is explicitly enabled.",
50
- usage: "appfleet auth login [--workspace <workspace-id>] [--email <email>] [--production-cloud] [--api-base-url <url>] [--json]",
157
+ summary: "Sign in to AppFleet Cloud; use --local-test for local scaffold sessions.",
158
+ usage: "appfleet auth login [--workspace <workspace-id>] [--email <email>] [--api-base-url <url>] [--local-test] [--json]",
51
159
  arguments: [],
52
160
  options: [
53
161
  { flags: "--workspace <workspace-id>", description: "Workspace id for local-test or hosted session metadata." },
54
162
  { flags: "--email <email>", description: "Optional non-secret user label for local-test identity or hosted login metadata." },
55
- { flags: "--production-cloud", description: "Explicitly use hosted auth mode; also requires API/auth env config." },
56
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
163
+ { flags: "--local-test", description: "Create local-test session metadata instead of signing in to AppFleet Cloud." },
164
+ { flags: "--production-cloud", description: "Deprecated compatibility flag; hosted AppFleet Cloud is already the default." },
165
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
57
166
  { flags: "--json", description: "Emit machine-readable JSON." },
58
167
  ],
59
168
  examples: [
60
- "appfleet auth login --workspace workspace_test",
61
- "appfleet auth login --workspace workspace_test --email demo@example.com --json",
62
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet auth login --production-cloud --workspace workspace_prod --json",
169
+ "appfleet login",
170
+ "appfleet auth login --workspace workspace_prod --json",
171
+ "appfleet auth login --local-test --workspace workspace_test --email demo@example.com --json",
63
172
  ],
64
173
  reads: [".appfleet/cloud-session.json if it already exists"],
65
174
  writes: [".appfleet/cloud-session.json local-test or hosted redacted session metadata with status and expiry"],
@@ -72,18 +181,19 @@ export const cliCommandDocs = [
72
181
  {
73
182
  namespace: "auth",
74
183
  name: "logout",
75
- summary: "Log out of local-test session metadata by default, or revoke hosted session metadata when production cloud is explicitly enabled.",
76
- usage: "appfleet auth logout [--production-cloud] [--api-base-url <url>] [--json]",
184
+ summary: "Sign out of AppFleet Cloud; use --local-test for local scaffold sessions.",
185
+ usage: "appfleet auth logout [--api-base-url <url>] [--local-test] [--json]",
77
186
  arguments: [],
78
187
  options: [
79
- { flags: "--production-cloud", description: "Explicitly use hosted logout mode; also requires API/auth env config and hosted session metadata." },
80
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
188
+ { flags: "--local-test", description: "Remove local-test session metadata instead of revoking a hosted AppFleet Cloud session." },
189
+ { flags: "--production-cloud", description: "Deprecated compatibility flag; hosted AppFleet Cloud is already the default." },
190
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
81
191
  { flags: "--json", description: "Emit machine-readable JSON." },
82
192
  ],
83
193
  examples: [
84
- "appfleet auth logout",
194
+ "appfleet logout",
85
195
  "appfleet auth logout --json",
86
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet auth logout --production-cloud --json",
196
+ "appfleet auth logout --local-test --json",
87
197
  ],
88
198
  reads: [".appfleet/cloud-session.json"],
89
199
  writes: [".appfleet/cloud-session.json is removed when present; JSON output includes a safe logged_out/not_found/missing_hosted_session status"],
@@ -101,23 +211,23 @@ export const cliCommandDocs = [
101
211
  arguments: [],
102
212
  options: [
103
213
  { flags: "--workspace <workspace-id>", description: "Workspace id for the local-test sync store." },
104
- { flags: "--production-cloud", description: "Explicitly use production cloud mode; also requires API/auth env config." },
105
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
214
+ { flags: "--production-cloud", description: "Explicitly use production cloud mode against AppFleet Cloud." },
215
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
106
216
  { flags: "--idempotency-key <key>", description: "Override the production metadata sync idempotency key; retries reuse the same key." },
107
217
  { flags: "--json", description: "Emit machine-readable JSON." },
108
218
  ],
109
219
  examples: [
110
220
  "appfleet cloud sync",
111
221
  "appfleet cloud sync --workspace workspace_test --json",
112
- "APPFLEET_PRODUCTION_CLOUD_ENABLED=true APPFLEET_API_BASE_URL=https://appfleet.xyz APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud sync --workspace <workspace-id> --json",
113
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet cloud sync --production-cloud --workspace <workspace-id> --json",
222
+ "APPFLEET_PRODUCTION_CLOUD_ENABLED=true APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud sync --workspace <workspace-id> --json",
223
+ "appfleet cloud sync --production-cloud --workspace <workspace-id> --json",
114
224
  ],
115
225
  reads: [".appfleet/cloud-session.json", ".appfleet/project-memory.json", ".appfleet/cloud-metadata-sync.json"],
116
226
  writes: [".appfleet/cloud-metadata-sync.json", ".appfleet/project-memory.json lastSyncedAt for accepted projects"],
117
227
  secretSafety: [
118
228
  "Chooses local-test mode by default; production mode requires --production-cloud, APPFLEET_PRODUCTION_CLOUD_ENABLED=true, or APPFLEET_CLOUD_MODE=production.",
119
- "In production mode, fails closed when APPFLEET_API_BASE_URL/--api-base-url or APPFLEET_CLOUD_AUTH_TOKEN is missing.",
120
- "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured AppFleet web API base URL.",
229
+ "In production mode, defaults to https://appfleet.xyz and fails closed when APPFLEET_CLOUD_AUTH_TOKEN is missing.",
230
+ "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured or default AppFleet web API base URL.",
121
231
  "Production metadata sync sends redacted auth reporting plus an idempotency key, workspace id, and project metadata envelopes.",
122
232
  "Production metadata sync retries safe transient transport failures with the same idempotency key.",
123
233
  "Persists local-test and production sync metadata, project ids, fingerprints, durable queue status, idempotency metadata, and conflict reports only.",
@@ -133,23 +243,23 @@ export const cliCommandDocs = [
133
243
  arguments: [],
134
244
  options: [
135
245
  { flags: "--workspace <workspace-id>", description: "Workspace id for the local-test sync store." },
136
- { flags: "--production-cloud", description: "Explicitly use production cloud mode; also requires API/auth env config." },
137
- { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL." },
246
+ { flags: "--production-cloud", description: "Explicitly use production cloud mode against AppFleet Cloud." },
247
+ { flags: "--api-base-url <url>", description: "Production AppFleet API base URL; overrides APPFLEET_API_BASE_URL and the default https://appfleet.xyz." },
138
248
  { flags: "--idempotency-key <key>", description: "Override the production metadata sync idempotency key; retries reuse the same key." },
139
249
  { flags: "--json", description: "Emit machine-readable JSON." },
140
250
  ],
141
251
  examples: [
142
252
  "appfleet cloud metadata-sync",
143
253
  "appfleet cloud metadata-sync --workspace workspace_test --json",
144
- "APPFLEET_CLOUD_MODE=production APPFLEET_API_BASE_URL=https://appfleet.xyz APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud metadata-sync --workspace <workspace-id> --json",
145
- "APPFLEET_API_BASE_URL=https://appfleet.xyz appfleet cloud metadata-sync --production-cloud --workspace <workspace-id> --json",
254
+ "APPFLEET_CLOUD_MODE=production APPFLEET_CLOUD_AUTH_TOKEN=<redacted> appfleet cloud metadata-sync --workspace <workspace-id> --json",
255
+ "appfleet cloud metadata-sync --production-cloud --workspace <workspace-id> --json",
146
256
  ],
147
257
  reads: [".appfleet/cloud-session.json", ".appfleet/project-memory.json", ".appfleet/cloud-metadata-sync.json"],
148
258
  writes: [".appfleet/cloud-metadata-sync.json", ".appfleet/project-memory.json lastSyncedAt for accepted projects"],
149
259
  secretSafety: [
150
260
  "Chooses local-test mode by default; production mode requires --production-cloud, APPFLEET_PRODUCTION_CLOUD_ENABLED=true, or APPFLEET_CLOUD_MODE=production.",
151
- "In production mode, fails closed when APPFLEET_API_BASE_URL/--api-base-url or APPFLEET_CLOUD_AUTH_TOKEN is missing.",
152
- "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured AppFleet web API base URL.",
261
+ "In production mode, defaults to https://appfleet.xyz and fails closed when APPFLEET_CLOUD_AUTH_TOKEN is missing.",
262
+ "Production metadata sync posts to /api/workspaces/<workspace-id>/metadata-sync on the configured or default AppFleet web API base URL.",
153
263
  "Production metadata sync sends redacted auth reporting plus an idempotency key, workspace id, and project metadata envelopes.",
154
264
  "Production metadata sync retries safe transient transport failures with the same idempotency key.",
155
265
  "Persists local-test and production sync metadata, project ids, fingerprints, durable queue status, idempotency metadata, and conflict reports only.",
@@ -1161,7 +1161,8 @@ function commandError(error) {
1161
1161
  childStarted: false,
1162
1162
  };
1163
1163
  }
1164
- if (import.meta.url === `file://${process.argv[1]}`) {
1164
+ if (import.meta.url.endsWith("/local-vault.ts") &&
1165
+ import.meta.url === `file://${process.argv[1]}`) {
1165
1166
  const result = await runLocalVaultCommand(process.argv.slice(2));
1166
1167
  process.stdout.write(result.stdout);
1167
1168
  process.stderr.write(result.stderr);
@@ -1518,7 +1518,8 @@ function defaultProjectMemoryStorePath(cwd) {
1518
1518
  function errorMessage(error) {
1519
1519
  return error instanceof Error ? error.message : String(error);
1520
1520
  }
1521
- if (import.meta.url === `file://${process.argv[1]}`) {
1521
+ if (import.meta.url.endsWith("/project-memory.ts") &&
1522
+ import.meta.url === `file://${process.argv[1]}`) {
1522
1523
  const result = await runProjectMemoryCommand(process.argv.slice(2), {
1523
1524
  storePath: process.env.APPFLEET_PROJECT_MEMORY_STORE_PATH,
1524
1525
  currentGitRemoteFingerprint: process.env.APPFLEET_GIT_REMOTE_FINGERPRINT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appfleet-cli/cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {