@appfleet-cli/cli 0.1.2 → 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 against AppFleet Cloud." },
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." },
3431
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 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 against AppFleet Cloud and hosted session metadata." },
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." },
3455
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 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"],
@@ -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
@@ -8173,7 +8285,7 @@ async function runCloudSessionCommand(argv, options = {}) {
8173
8285
  createdAt: createdAt3,
8174
8286
  result: authResult
8175
8287
  });
8176
- const localAuthToken = authResult.authToken ?? authResult.sessionId;
8288
+ const localAuthToken = authResult.sessionId ?? authResult.authToken;
8177
8289
  if (localAuthToken) {
8178
8290
  await writeAuthToken(authTokenPath, localAuthToken);
8179
8291
  }
@@ -8793,7 +8905,7 @@ function parseCloudSessionCommand(argv, env) {
8793
8905
  action,
8794
8906
  workspaceId: readFlag4(normalizedArgv, "--workspace") ?? "workspace_local",
8795
8907
  email: readFlag4(normalizedArgv, "--email"),
8796
- productionCloud: cloudProductionFlag(normalizedArgv, env),
8908
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
8797
8909
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
8798
8910
  apiBaseUrl: readFlag4(normalizedArgv, "--api-base-url"),
8799
8911
  outputFormat
@@ -8803,7 +8915,7 @@ function parseCloudSessionCommand(argv, env) {
8803
8915
  return {
8804
8916
  namespace,
8805
8917
  action,
8806
- productionCloud: cloudProductionFlag(normalizedArgv, env),
8918
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
8807
8919
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
8808
8920
  apiBaseUrl: readFlag4(normalizedArgv, "--api-base-url"),
8809
8921
  outputFormat
@@ -9111,16 +9223,28 @@ function createZeroSecretBoundary() {
9111
9223
  storesSecretFragments: false
9112
9224
  };
9113
9225
  }
9114
- function cloudProductionFlag(argv, env) {
9226
+ function cloudProductionFlag(argv, env, defaultProduction = false) {
9227
+ if (hasFlag2(argv, "--local-test")) {
9228
+ return false;
9229
+ }
9115
9230
  if (hasFlag2(argv, "--production-cloud")) {
9116
9231
  return true;
9117
9232
  }
9118
- 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;
9119
9237
  }
9120
9238
  function cloudProductionSource(argv, env) {
9239
+ if (hasFlag2(argv, "--local-test")) {
9240
+ return "flag";
9241
+ }
9121
9242
  if (hasFlag2(argv, "--production-cloud")) {
9122
9243
  return "flag";
9123
9244
  }
9245
+ if (env.APPFLEET_CLOUD_MODE === "local-test") {
9246
+ return "env";
9247
+ }
9124
9248
  if (productionEnvEnabled(env)) {
9125
9249
  return "env";
9126
9250
  }
@@ -11459,7 +11583,7 @@ function commandError(error) {
11459
11583
  childStarted: false
11460
11584
  };
11461
11585
  }
11462
- if (import.meta.url === `file://${process.argv[1]}`) {
11586
+ if (import.meta.url.endsWith("/local-vault.ts") && import.meta.url === `file://${process.argv[1]}`) {
11463
11587
  const result = await runLocalVaultCommand(process.argv.slice(2));
11464
11588
  process.stdout.write(result.stdout);
11465
11589
  process.stderr.write(result.stderr);
@@ -11886,6 +12010,7 @@ var namespaces = [
11886
12010
  "vault",
11887
12011
  "secrets"
11888
12012
  ];
12013
+ var DEFAULT_PRODUCTION_API_BASE_URL2 = "https://appfleet.xyz";
11889
12014
  async function runAppFleetCli(argv) {
11890
12015
  const normalizedArgv = argv[0] === "--" ? argv.slice(1) : argv;
11891
12016
  if (shouldShowHelp(normalizedArgv)) {
@@ -11899,6 +12024,61 @@ async function runAppFleetCli(argv) {
11899
12024
  return 0;
11900
12025
  }
11901
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
+ }
11902
12082
  if (namespace === "init") {
11903
12083
  const result = await runFirstRunSetup(normalizedArgv.slice(1));
11904
12084
  process.stdout.write(result.stdout);
@@ -11919,6 +12099,20 @@ async function runAppFleetCli(argv) {
11919
12099
  process.stderr.write(result.stderr);
11920
12100
  return result.exitCode;
11921
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
+ }
11922
12116
  if (namespace === "projects") {
11923
12117
  if (normalizedArgv[1] === "doctor" && normalizedArgv.includes("--upload")) {
11924
12118
  return runDoctorAndUpload(normalizedArgv);
@@ -11989,7 +12183,7 @@ async function runAppFleetCli(argv) {
11989
12183
  }
11990
12184
  function createAppFleetProgram() {
11991
12185
  const program2 = new Command();
11992
- 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 });
11993
12187
  const initDoc = findCliCommandDoc("appfleet", "init");
11994
12188
  if (initDoc) {
11995
12189
  program2.addCommand(commandFromDoc(initDoc));
@@ -11998,6 +12192,26 @@ function createAppFleetProgram() {
11998
12192
  if (linkDoc) {
11999
12193
  program2.addCommand(commandFromDoc(linkDoc));
12000
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
+ }
12001
12215
  for (const namespace of namespaces) {
12002
12216
  const namespaceCommand = new Command(namespace).description(
12003
12217
  namespaceDescription(namespace)
@@ -12020,6 +12234,139 @@ function cloudCommandOptions() {
12020
12234
  env: process.env
12021
12235
  };
12022
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
+ }
12023
12370
  async function runDoctorAndUpload(argv) {
12024
12371
  const doctorArgs = argv.filter((argument) => argument !== "--upload");
12025
12372
  if (!doctorArgs.includes("--json")) doctorArgs.push("--json");
@@ -12196,9 +12543,9 @@ function namespaceDescription(namespace) {
12196
12543
  case "projects":
12197
12544
  return "Remember projects, summarize recovery status, and run safe diagnostics.";
12198
12545
  case "auth":
12199
- return "Manage local-test hosted auth session metadata.";
12546
+ return "Manage AppFleet Cloud login sessions.";
12200
12547
  case "cloud":
12201
- return "Prepare cloud-safe metadata sync envelopes for V1 scaffold flows.";
12548
+ return "Sync cloud-safe metadata with AppFleet Cloud.";
12202
12549
  case "providers":
12203
12550
  return "Inspect provider integration contracts and fail-closed discovery scaffolds.";
12204
12551
  case "ops":
@@ -73,7 +73,7 @@ export async function runCloudSessionCommand(argv, options = {}) {
73
73
  createdAt,
74
74
  result: authResult,
75
75
  });
76
- const localAuthToken = authResult.authToken ?? authResult.sessionId;
76
+ const localAuthToken = authResult.sessionId ?? authResult.authToken;
77
77
  if (localAuthToken) {
78
78
  await writeAuthToken(authTokenPath, localAuthToken);
79
79
  }
@@ -707,7 +707,7 @@ function parseCloudSessionCommand(argv, env) {
707
707
  action,
708
708
  workspaceId: readFlag(normalizedArgv, "--workspace") ?? "workspace_local",
709
709
  email: readFlag(normalizedArgv, "--email"),
710
- productionCloud: cloudProductionFlag(normalizedArgv, env),
710
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
711
711
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
712
712
  apiBaseUrl: readFlag(normalizedArgv, "--api-base-url"),
713
713
  outputFormat,
@@ -717,7 +717,7 @@ function parseCloudSessionCommand(argv, env) {
717
717
  return {
718
718
  namespace,
719
719
  action,
720
- productionCloud: cloudProductionFlag(normalizedArgv, env),
720
+ productionCloud: cloudProductionFlag(normalizedArgv, env, true),
721
721
  productionCloudSource: cloudProductionSource(normalizedArgv, env),
722
722
  apiBaseUrl: readFlag(normalizedArgv, "--api-base-url"),
723
723
  outputFormat,
@@ -1015,17 +1015,30 @@ function createZeroSecretBoundary() {
1015
1015
  storesSecretFragments: false,
1016
1016
  };
1017
1017
  }
1018
- function cloudProductionFlag(argv, env) {
1018
+ function cloudProductionFlag(argv, env, defaultProduction = false) {
1019
+ if (hasFlag(argv, "--local-test")) {
1020
+ return false;
1021
+ }
1019
1022
  if (hasFlag(argv, "--production-cloud")) {
1020
1023
  return true;
1021
1024
  }
1025
+ if (env.APPFLEET_CLOUD_MODE === "local-test") {
1026
+ return false;
1027
+ }
1022
1028
  return (env.APPFLEET_PRODUCTION_CLOUD_ENABLED === "true" ||
1023
- env.APPFLEET_CLOUD_MODE === "production");
1029
+ env.APPFLEET_CLOUD_MODE === "production" ||
1030
+ defaultProduction);
1024
1031
  }
1025
1032
  function cloudProductionSource(argv, env) {
1033
+ if (hasFlag(argv, "--local-test")) {
1034
+ return "flag";
1035
+ }
1026
1036
  if (hasFlag(argv, "--production-cloud")) {
1027
1037
  return "flag";
1028
1038
  }
1039
+ if (env.APPFLEET_CLOUD_MODE === "local-test") {
1040
+ return "env";
1041
+ }
1029
1042
  if (productionEnvEnabled(env)) {
1030
1043
  return "env";
1031
1044
  }
@@ -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 against AppFleet Cloud." },
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." },
56
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 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 against AppFleet Cloud and hosted session metadata." },
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." },
80
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 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"],
@@ -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.2",
3
+ "version": "0.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {