@anythingai/cli 0.0.3 → 0.1.0

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/js/bin.mjs CHANGED
@@ -3,7 +3,7 @@ import { createRequire } from "node:module";
3
3
  import { hideBin } from "yargs/helpers";
4
4
  import yargs from "yargs";
5
5
  import { exec, execSync, spawn } from "node:child_process";
6
- import fs, { existsSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
6
+ import fs, { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
7
7
  import path, { basename, dirname, extname, join, resolve } from "node:path";
8
8
  import Conf from "conf";
9
9
  import { err, ok } from "neverthrow";
@@ -3037,6 +3037,18 @@ function exitCodeFromHttpStatus(status) {
3037
3037
  if (status === 422 || status === 400) return EXIT_INVALID_ARGS;
3038
3038
  return EXIT_ERROR;
3039
3039
  }
3040
+ function errorCodeFromExitCode(exitCode) {
3041
+ switch (exitCode) {
3042
+ case EXIT_INVALID_ARGS: return "INVALID_ARGUMENTS";
3043
+ case EXIT_NOT_FOUND: return "NOT_FOUND";
3044
+ case EXIT_AUTH_FAILURE: return "AUTH_FAILURE";
3045
+ case EXIT_CONFLICT: return "CONFLICT";
3046
+ case EXIT_TIMEOUT: return "TIMEOUT";
3047
+ case EXIT_SERVER_ERROR: return "SERVER_ERROR";
3048
+ case EXIT_RATE_LIMITED: return "RATE_LIMITED";
3049
+ default: return "ERROR";
3050
+ }
3051
+ }
3040
3052
  function errorCodeFromHttpStatus(status) {
3041
3053
  if (status === null) return "NETWORK_ERROR";
3042
3054
  if (status === 401) return "AUTH_FAILURE";
@@ -3053,7 +3065,7 @@ function errorCodeFromHttpStatus(status) {
3053
3065
 
3054
3066
  //#endregion
3055
3067
  //#region package.json
3056
- var version = "0.0.3";
3068
+ var version = "0.1.0";
3057
3069
 
3058
3070
  //#endregion
3059
3071
  //#region generated/core/bodySerializer.gen.ts
@@ -4315,14 +4327,23 @@ const USER_AGENT = `anything-cli/${version}`;
4315
4327
  const ErrorWithMessageSchema = z.object({ message: z.string().min(1) });
4316
4328
  const ErrorWithErrorStringSchema = z.object({ error: z.string().min(1) });
4317
4329
  const ErrorWithNestedMessageSchema = z.object({ error: z.object({ message: z.string() }) });
4330
+ const HTML_DOCUMENT = /<!doctype html|<html[\s>]/i;
4331
+ function summarizeErrorString(raw, status) {
4332
+ const trimmed = raw.trim();
4333
+ if (!HTML_DOCUMENT.test(trimmed)) return trimmed;
4334
+ const pre = trimmed.match(/<pre[^>]*>([\s\S]*?)<\/pre>/i)?.[1];
4335
+ const title = trimmed.match(/<title[^>]*>([\s\S]*?)<\/title>/i)?.[1];
4336
+ const summary = (pre ?? title)?.replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim();
4337
+ return summary && summary.length > 0 ? `API error (${status}): ${summary}` : `API error (${status})`;
4338
+ }
4318
4339
  function extractErrorMessage(status, details) {
4319
4340
  const withMessage = ErrorWithMessageSchema.safeParse(details);
4320
- if (withMessage.success) return withMessage.data.message;
4341
+ if (withMessage.success) return summarizeErrorString(withMessage.data.message, status);
4321
4342
  const withErrorString = ErrorWithErrorStringSchema.safeParse(details);
4322
- if (withErrorString.success) return withErrorString.data.error;
4343
+ if (withErrorString.success) return summarizeErrorString(withErrorString.data.error, status);
4323
4344
  const withNested = ErrorWithNestedMessageSchema.safeParse(details);
4324
- if (withNested.success) return withNested.data.error.message;
4325
- if (typeof details === "string" && details.length > 0) return details;
4345
+ if (withNested.success) return summarizeErrorString(withNested.data.error.message, status);
4346
+ if (typeof details === "string" && details.length > 0) return summarizeErrorString(details, status);
4326
4347
  return `API error (${status})`;
4327
4348
  }
4328
4349
  function parseRetryAfter(response) {
@@ -4365,23 +4386,41 @@ const RollbackResponseSchema = z.object({
4365
4386
  status: z.string()
4366
4387
  }).optional()
4367
4388
  });
4389
+ function wrapFetchWithTimeout(ms) {
4390
+ return (input, init) => {
4391
+ const signal = AbortSignal.timeout(ms);
4392
+ if (input instanceof Request) return globalThis.fetch(new Request(input, { signal }));
4393
+ return globalThis.fetch(input, {
4394
+ ...init,
4395
+ signal
4396
+ });
4397
+ };
4398
+ }
4368
4399
  var AnythingApiClient = class {
4369
4400
  client;
4370
- constructor(config) {
4401
+ timeoutMs;
4402
+ constructor(config, options) {
4403
+ const apiUrl = config.apiUrl.replace(/\/$/, "");
4404
+ this.timeoutMs = options?.timeoutMs ?? null;
4371
4405
  this.client = createClient$1(createConfig({
4372
- baseUrl: config.apiUrl.replace(/\/$/, ""),
4406
+ baseUrl: apiUrl,
4373
4407
  headers: {
4374
4408
  authorization: buildAuthHeader$1(config.apiKey),
4375
4409
  "user-agent": USER_AGENT
4376
- }
4410
+ },
4411
+ ...this.timeoutMs !== null ? { fetch: wrapFetchWithTimeout(this.timeoutMs) } : {}
4377
4412
  }));
4378
4413
  }
4379
4414
  toResult(result) {
4380
4415
  const { data, error, response } = result;
4381
- if (!response) return err({
4382
- message: `Network error: ${error instanceof Error ? error.message : String(error)}`,
4383
- status: null
4384
- });
4416
+ if (!response) {
4417
+ const isTimeout = error instanceof DOMException && error.name === "TimeoutError";
4418
+ const timeoutSuffix = this.timeoutMs !== null ? ` after ${Math.round(this.timeoutMs / 1e3)}s` : "";
4419
+ return err({
4420
+ message: isTimeout ? `Request timed out${timeoutSuffix}. Use --timeout to increase.` : `Network error: ${error instanceof Error ? error.message : String(error)}`,
4421
+ status: isTimeout ? 408 : null
4422
+ });
4423
+ }
4385
4424
  if (!response.ok) {
4386
4425
  const retryAfter = parseRetryAfter(response);
4387
4426
  return err({
@@ -4810,10 +4849,6 @@ function printApiError(error) {
4810
4849
  if (withErrors.success) for (const e of withErrors.data.errors) console.error(` ${typeof e === "string" ? e : e.message}`);
4811
4850
  }
4812
4851
  function outputSuccess({ argv, command, data, primaryId, startTime }) {
4813
- if (argv.quiet) {
4814
- if (primaryId) console.log(primaryId);
4815
- return true;
4816
- }
4817
4852
  if (argv.json) {
4818
4853
  const envelope = {
4819
4854
  ok: true,
@@ -4824,6 +4859,10 @@ function outputSuccess({ argv, command, data, primaryId, startTime }) {
4824
4859
  console.log(JSON.stringify(envelope, null, 2));
4825
4860
  return true;
4826
4861
  }
4862
+ if (argv.quiet) {
4863
+ if (primaryId) console.log(primaryId);
4864
+ return true;
4865
+ }
4827
4866
  return false;
4828
4867
  }
4829
4868
  const DetailsWithCodeSchema = z.object({ code: z.string() });
@@ -4836,7 +4875,7 @@ function outputError({ argv, command, error, hint, exitCode }) {
4836
4875
  const details = "details" in error ? error.details : void 0;
4837
4876
  const retryAfter = "retryAfter" in error && typeof error.retryAfter === "number" ? error.retryAfter : null;
4838
4877
  process.exitCode = exitCode ?? exitCodeFromHttpStatus(status);
4839
- const code = extractErrorCode(details) ?? errorCodeFromHttpStatus(status);
4878
+ const code = extractErrorCode(details) ?? (status === null && exitCode !== void 0 ? errorCodeFromExitCode(exitCode) : errorCodeFromHttpStatus(status));
4840
4879
  if (argv.json || argv.quiet) {
4841
4880
  console.error(JSON.stringify({
4842
4881
  ok: false,
@@ -4871,7 +4910,6 @@ function outputValidationError({ argv, command, message, hint }) {
4871
4910
  });
4872
4911
  }
4873
4912
  function outputDryRun({ argv, command, plannedActions }) {
4874
- if (argv.quiet) return;
4875
4913
  if (argv.json) {
4876
4914
  console.log(JSON.stringify({
4877
4915
  ok: true,
@@ -4881,6 +4919,7 @@ function outputDryRun({ argv, command, plannedActions }) {
4881
4919
  }, null, 2));
4882
4920
  return;
4883
4921
  }
4922
+ if (argv.quiet) return;
4884
4923
  printSuccess("Dry run — no changes made.");
4885
4924
  for (const action of plannedActions) {
4886
4925
  printLabel("Action", action.action);
@@ -4958,6 +4997,19 @@ const upload = {
4958
4997
  });
4959
4998
  return;
4960
4999
  }
5000
+ if (argv["dry-run"]) {
5001
+ outputDryRun({
5002
+ argv,
5003
+ command,
5004
+ plannedActions: [{
5005
+ action: "upload_asset",
5006
+ projectGroupId: argv.projectId,
5007
+ file: filePath,
5008
+ ...argv.name ? { name: argv.name } : {}
5009
+ }]
5010
+ });
5011
+ return;
5012
+ }
4961
5013
  const config = resolveConfig({
4962
5014
  dev: argv.dev,
4963
5015
  apiUrl: argv.apiUrl
@@ -5097,6 +5149,18 @@ const remove$1 = {
5097
5149
  }),
5098
5150
  handler: async (argv) => {
5099
5151
  const command = "assets remove";
5152
+ if (argv["dry-run"]) {
5153
+ outputDryRun({
5154
+ argv,
5155
+ command,
5156
+ plannedActions: [{
5157
+ action: "remove_asset",
5158
+ projectGroupId: argv.projectId,
5159
+ assetId: argv.assetId
5160
+ }]
5161
+ });
5162
+ return;
5163
+ }
5100
5164
  const config = resolveConfig({
5101
5165
  dev: argv.dev,
5102
5166
  apiUrl: argv.apiUrl
@@ -5406,13 +5470,31 @@ const login = {
5406
5470
  }).replace(/\/$/, "");
5407
5471
  const nonInteractive = isNonInteractive(argv);
5408
5472
  if (argv["api-key"]) {
5473
+ const apiKey = argv["api-key"];
5474
+ const probe = await new AnythingApiClient({
5475
+ apiKey,
5476
+ apiUrl
5477
+ }).getMe();
5478
+ if (probe.isErr()) {
5479
+ outputError({
5480
+ argv,
5481
+ command: "auth login",
5482
+ error: {
5483
+ message: `API key was rejected: ${probe.error.message}`,
5484
+ status: probe.error.status
5485
+ },
5486
+ hint: "Double-check the key and that it matches this API URL. Your existing login was left untouched.",
5487
+ exitCode: 4
5488
+ });
5489
+ return;
5490
+ }
5409
5491
  saveConfig({
5410
- apiKey: argv["api-key"],
5492
+ apiKey,
5411
5493
  apiUrl
5412
5494
  });
5413
5495
  const org = await selectAndSetOrg({
5414
5496
  config: {
5415
- apiKey: argv["api-key"],
5497
+ apiKey,
5416
5498
  apiUrl
5417
5499
  },
5418
5500
  json: argv.json,
@@ -5686,6 +5768,9 @@ const authCommand = {
5686
5768
  //#endregion
5687
5769
  //#region src/commands/database-connect.ts
5688
5770
  const COMMAND$25 = "databases connect";
5771
+ function maskConnectionString(connectionString) {
5772
+ return connectionString.replace(/(\/\/[^:/?#@]+:)[^@/?#]*(@)/, "$1****$2");
5773
+ }
5689
5774
  const databaseConnectCommand = {
5690
5775
  command: "connect <databaseId>",
5691
5776
  describe: "Print the connection string for a database",
@@ -5693,7 +5778,11 @@ const databaseConnectCommand = {
5693
5778
  type: "string",
5694
5779
  demandOption: true,
5695
5780
  describe: "The database ID"
5696
- }).example("anything databases connect db_123", "Print the connection string"),
5781
+ }).option("mask", {
5782
+ type: "boolean",
5783
+ default: false,
5784
+ describe: "Redact the password in the connection string"
5785
+ }).example("anything databases connect db_123", "Print the connection string").example("anything databases connect db_123 --mask", "Print the connection string with the password redacted"),
5697
5786
  handler: async (argv) => {
5698
5787
  const config = resolveConfig({
5699
5788
  dev: argv.dev,
@@ -5720,13 +5809,17 @@ const databaseConnectCommand = {
5720
5809
  });
5721
5810
  return;
5722
5811
  }
5812
+ const connectionString = argv.mask ? maskConnectionString(result.value.connectionString) : result.value.connectionString;
5723
5813
  if (outputSuccess({
5724
5814
  argv,
5725
5815
  command: COMMAND$25,
5726
- data: result.value,
5727
- primaryId: result.value.connectionString
5816
+ data: {
5817
+ ...result.value,
5818
+ connectionString
5819
+ },
5820
+ primaryId: connectionString
5728
5821
  })) return;
5729
- console.log(result.value.connectionString);
5822
+ console.log(connectionString);
5730
5823
  }
5731
5824
  };
5732
5825
 
@@ -5791,6 +5884,19 @@ const databaseCreateCommand = {
5791
5884
  describe: "Database name"
5792
5885
  }).example("anything databases create --project pg_123", "Create a database for a project"),
5793
5886
  handler: async (argv) => {
5887
+ const name = argv.name?.trim();
5888
+ if (!name) {
5889
+ outputError({
5890
+ argv,
5891
+ command: COMMAND$24,
5892
+ error: {
5893
+ message: "Database name is required. Pass --name <name>.",
5894
+ status: null
5895
+ },
5896
+ exitCode: 2
5897
+ });
5898
+ return;
5899
+ }
5794
5900
  const config = resolveConfig({
5795
5901
  dev: argv.dev,
5796
5902
  apiUrl: argv.apiUrl
@@ -5831,8 +5937,8 @@ const databaseCreateCommand = {
5831
5937
  plannedActions: [{
5832
5938
  action: "create_database",
5833
5939
  org: orgResult.orgId,
5834
- ...argv.project ? { project: argv.project } : {},
5835
- ...argv.name ? { name: argv.name } : {}
5940
+ name,
5941
+ ...argv.project ? { project: argv.project } : {}
5836
5942
  }]
5837
5943
  });
5838
5944
  return;
@@ -5840,7 +5946,7 @@ const databaseCreateCommand = {
5840
5946
  const result = await new AnythingApiClient(config.value).createDatabase({
5841
5947
  organizationId: orgResult.orgId,
5842
5948
  projectGroupId: argv.project ?? null,
5843
- name: argv.name ?? null
5949
+ name
5844
5950
  });
5845
5951
  if (result.isErr()) {
5846
5952
  outputError({
@@ -6712,155 +6818,383 @@ const domainsCommand = {
6712
6818
  };
6713
6819
 
6714
6820
  //#endregion
6715
- //#region src/commands/introspect.ts
6716
- const commandTree = [
6717
- {
6718
- name: "auth login",
6719
- flags: [{
6720
- name: "--api-key",
6721
- type: "string",
6722
- required: false
6723
- }, {
6724
- name: "--dev",
6725
- type: "boolean",
6726
- required: false
6727
- }],
6728
- idempotent: true,
6729
- destructive: false
6730
- },
6731
- {
6732
- name: "auth logout",
6733
- flags: [],
6734
- idempotent: true,
6735
- destructive: true
6736
- },
6737
- {
6738
- name: "auth status",
6739
- flags: [],
6740
- idempotent: true,
6741
- destructive: false
6742
- },
6743
- {
6744
- name: "user",
6745
- flags: [],
6746
- idempotent: true,
6747
- destructive: false
6748
- },
6749
- {
6750
- name: "orgs list",
6751
- flags: [],
6752
- idempotent: true,
6753
- destructive: false
6754
- },
6755
- {
6756
- name: "orgs get",
6757
- flags: [{
6758
- name: "<organizationId>",
6759
- type: "string",
6760
- required: true
6761
- }],
6762
- idempotent: true,
6763
- destructive: false
6764
- },
6765
- {
6766
- name: "orgs set",
6767
- flags: [{
6768
- name: "<org-id>",
6769
- type: "string",
6770
- required: true
6771
- }],
6772
- idempotent: true,
6773
- destructive: false
6774
- },
6775
- {
6776
- name: "orgs unset",
6777
- flags: [],
6778
- idempotent: true,
6779
- destructive: false
6780
- },
6781
- {
6782
- name: "orgs members",
6783
- flags: [{
6784
- name: "<organizationId>",
6785
- type: "string",
6786
- required: true
6787
- }],
6788
- idempotent: true,
6789
- destructive: false
6790
- },
6791
- {
6792
- name: "projects list",
6793
- flags: [
6794
- {
6795
- name: "--org",
6796
- type: "string",
6797
- required: false
6798
- },
6799
- {
6800
- name: "--search",
6801
- type: "string",
6802
- required: false
6803
- },
6804
- {
6805
- name: "--limit",
6806
- type: "number",
6807
- required: false
6808
- }
6809
- ],
6810
- idempotent: true,
6811
- destructive: false
6812
- },
6813
- {
6814
- name: "projects create",
6815
- flags: [
6816
- {
6817
- name: "--prompt",
6818
- type: "string",
6819
- required: true
6820
- },
6821
- {
6822
- name: "--org",
6823
- type: "string",
6824
- required: false
6825
- },
6826
- {
6827
- name: "--name",
6828
- type: "string",
6829
- required: false
6830
- }
6831
- ],
6832
- idempotent: false,
6833
- destructive: false
6834
- },
6835
- {
6836
- name: "projects get",
6837
- flags: [{
6838
- name: "<projectId>",
6839
- type: "string",
6840
- required: true
6841
- }],
6842
- idempotent: true,
6843
- destructive: false
6844
- },
6845
- {
6846
- name: "projects generate",
6847
- flags: [
6848
- {
6849
- name: "<projectId>",
6850
- type: "string",
6851
- required: true
6852
- },
6853
- {
6854
- name: "--prompt",
6855
- type: "string",
6856
- required: true
6857
- },
6858
- {
6859
- name: "--thread",
6860
- type: "string",
6861
- required: false
6821
+ //#region src/commands/submit.ts
6822
+ const POLL_INTERVAL_MS$1 = 2e3;
6823
+ const POLL_TIMEOUT_MS = 6e4;
6824
+ const STORE_CHOICES = ["app-store", "play-store"];
6825
+ async function waitForSubmission({ client, projectGroupId, submission }) {
6826
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
6827
+ let current = submission;
6828
+ while (current.status === "PENDING" && Date.now() < deadline) {
6829
+ const latestResult = await client.getProjectSubmission({
6830
+ projectGroupId,
6831
+ submissionId: current.id
6832
+ });
6833
+ if (latestResult.isErr()) return latestResult.map((value) => value.submission);
6834
+ current = latestResult.value.submission;
6835
+ if (current.status !== "PENDING") return latestResult.map((value) => value.submission);
6836
+ await setTimeout$1(POLL_INTERVAL_MS$1);
6837
+ }
6838
+ return ok(current);
6839
+ }
6840
+ function printSubmissionResult({ argv, command, pendingMessage, submission }) {
6841
+ const { projectGroupId: projectId, ...submissionRest } = submission;
6842
+ const submissionOut = {
6843
+ projectId,
6844
+ ...submissionRest
6845
+ };
6846
+ if (submission.status === "FAILED") {
6847
+ printError(submission.errorMessage ?? "Submission failed.");
6848
+ process.exitCode = 1;
6849
+ return;
6850
+ }
6851
+ if (submission.status === "CREATED") {
6852
+ if (outputSuccess({
6853
+ argv,
6854
+ command,
6855
+ data: {
6856
+ success: true,
6857
+ submission: submissionOut
6862
6858
  },
6863
- {
6859
+ primaryId: submission.id
6860
+ })) return;
6861
+ printSuccess("App Store submission is ready.");
6862
+ printLabel("Submission ID", submission.id);
6863
+ printLabel("Launch URL", submission.launchUrl);
6864
+ printLabel("Auth URL", submission.authUrl);
6865
+ printLabel("Expires At", submission.expiresAt);
6866
+ return;
6867
+ }
6868
+ if (outputSuccess({
6869
+ argv,
6870
+ command,
6871
+ data: {
6872
+ success: true,
6873
+ submission
6874
+ },
6875
+ primaryId: submission.id
6876
+ })) return;
6877
+ printSuccess(pendingMessage);
6878
+ printLabel("Submission ID", submission.id);
6879
+ printLabel("Status", submission.status);
6880
+ printLabel("Next step", "Expo is still preparing the launch flow. Run the command again in a moment.");
6881
+ }
6882
+ const submitStatusCommand = {
6883
+ command: "status <projectId> <submissionId>",
6884
+ describe: "Check the status of an App Store submission",
6885
+ builder: (yargs) => yargs.positional("projectId", {
6886
+ type: "string",
6887
+ demandOption: true,
6888
+ describe: "The project ID"
6889
+ }).positional("submissionId", {
6890
+ type: "string",
6891
+ demandOption: true,
6892
+ describe: "The submission ID"
6893
+ }).example("anything projects submit status <project-id> <submission-id>", "Check a submission later").example("anything projects submit status <project-id> <submission-id> --json", "Return the submission status in JSON"),
6894
+ handler: async (argv) => {
6895
+ const command = "projects submit status";
6896
+ const config = resolveConfig({
6897
+ dev: argv.dev,
6898
+ apiUrl: argv.apiUrl
6899
+ });
6900
+ if (config.isErr()) {
6901
+ outputError({
6902
+ argv,
6903
+ command,
6904
+ error: {
6905
+ message: config.error.message,
6906
+ status: null
6907
+ },
6908
+ exitCode: 4
6909
+ });
6910
+ return;
6911
+ }
6912
+ const result = await new AnythingApiClient(config.value).getProjectSubmission({
6913
+ projectGroupId: argv.projectId,
6914
+ submissionId: argv.submissionId
6915
+ });
6916
+ if (result.isErr()) {
6917
+ outputError({
6918
+ argv,
6919
+ command,
6920
+ error: result.error
6921
+ });
6922
+ return;
6923
+ }
6924
+ printSubmissionResult({
6925
+ argv,
6926
+ command,
6927
+ pendingMessage: "Submission is still preparing.",
6928
+ submission: result.value.submission
6929
+ });
6930
+ }
6931
+ };
6932
+ const submitCommand = {
6933
+ command: "submit <projectId>",
6934
+ describe: "Start an App Store submission for an app",
6935
+ builder: (yargs) => yargs.command(submitStatusCommand).positional("projectId", {
6936
+ type: "string",
6937
+ demandOption: true,
6938
+ describe: "The project ID"
6939
+ }).option("store", {
6940
+ type: "string",
6941
+ choices: STORE_CHOICES,
6942
+ describe: "Submission target store"
6943
+ }).example("anything projects submit <project-id> --store app-store", "Start an App Store submission").example("anything projects submit <project-id> --store play-store", "Start a Play Store submission").example("anything projects submit <project-id> --store app-store --json", "Return the submission session in JSON").example("anything projects submit status <project-id> <submission-id>", "Check a submission later"),
6944
+ handler: async (argv) => {
6945
+ const command = "projects submit";
6946
+ if (argv.store === void 0) {
6947
+ outputError({
6948
+ argv,
6949
+ command,
6950
+ error: {
6951
+ message: "Missing required argument: store",
6952
+ status: null
6953
+ },
6954
+ exitCode: 2
6955
+ });
6956
+ return;
6957
+ }
6958
+ const store = STORE_CHOICES.find((s) => s === argv.store);
6959
+ if (!store) {
6960
+ outputError({
6961
+ argv,
6962
+ command,
6963
+ error: {
6964
+ message: `Unsupported store: ${argv.store}`,
6965
+ status: null
6966
+ },
6967
+ exitCode: 2
6968
+ });
6969
+ return;
6970
+ }
6971
+ if (argv["dry-run"]) {
6972
+ outputDryRun({
6973
+ argv,
6974
+ command,
6975
+ plannedActions: [{
6976
+ action: "submit_project",
6977
+ projectGroupId: argv.projectId,
6978
+ store
6979
+ }]
6980
+ });
6981
+ return;
6982
+ }
6983
+ const config = resolveConfig({
6984
+ dev: argv.dev,
6985
+ apiUrl: argv.apiUrl
6986
+ });
6987
+ if (config.isErr()) {
6988
+ outputError({
6989
+ argv,
6990
+ command,
6991
+ error: {
6992
+ message: config.error.message,
6993
+ status: null
6994
+ },
6995
+ exitCode: 4
6996
+ });
6997
+ return;
6998
+ }
6999
+ const client = new AnythingApiClient(config.value);
7000
+ const startResult = await client.submitProject({
7001
+ projectGroupId: argv.projectId,
7002
+ store
7003
+ });
7004
+ if (startResult.isErr()) {
7005
+ outputError({
7006
+ argv,
7007
+ command,
7008
+ error: startResult.error
7009
+ });
7010
+ return;
7011
+ }
7012
+ const finalResult = await waitForSubmission({
7013
+ client,
7014
+ projectGroupId: argv.projectId,
7015
+ submission: startResult.value.submission
7016
+ });
7017
+ if (finalResult.isErr()) {
7018
+ outputError({
7019
+ argv,
7020
+ command,
7021
+ error: finalResult.error
7022
+ });
7023
+ return;
7024
+ }
7025
+ printSubmissionResult({
7026
+ argv,
7027
+ command,
7028
+ pendingMessage: "Submission started.",
7029
+ submission: finalResult.value
7030
+ });
7031
+ }
7032
+ };
7033
+
7034
+ //#endregion
7035
+ //#region src/commands/introspect.ts
7036
+ const commandTree = [
7037
+ {
7038
+ name: "auth login",
7039
+ flags: [{
7040
+ name: "--api-key",
7041
+ type: "string",
7042
+ required: false
7043
+ }, {
7044
+ name: "--dev",
7045
+ type: "boolean",
7046
+ required: false
7047
+ }],
7048
+ idempotent: true,
7049
+ destructive: false
7050
+ },
7051
+ {
7052
+ name: "auth logout",
7053
+ flags: [],
7054
+ idempotent: true,
7055
+ destructive: true
7056
+ },
7057
+ {
7058
+ name: "auth status",
7059
+ flags: [],
7060
+ idempotent: true,
7061
+ destructive: false
7062
+ },
7063
+ {
7064
+ name: "user",
7065
+ flags: [{
7066
+ name: "--brief",
7067
+ type: "boolean",
7068
+ required: false
7069
+ }],
7070
+ idempotent: true,
7071
+ destructive: false
7072
+ },
7073
+ {
7074
+ name: "orgs list",
7075
+ flags: [],
7076
+ idempotent: true,
7077
+ destructive: false
7078
+ },
7079
+ {
7080
+ name: "orgs get",
7081
+ flags: [{
7082
+ name: "<organizationId>",
7083
+ type: "string",
7084
+ required: true
7085
+ }],
7086
+ idempotent: true,
7087
+ destructive: false
7088
+ },
7089
+ {
7090
+ name: "orgs set",
7091
+ flags: [{
7092
+ name: "<org-id>",
7093
+ type: "string",
7094
+ required: true
7095
+ }],
7096
+ idempotent: true,
7097
+ destructive: false
7098
+ },
7099
+ {
7100
+ name: "orgs unset",
7101
+ flags: [],
7102
+ idempotent: true,
7103
+ destructive: false
7104
+ },
7105
+ {
7106
+ name: "orgs members",
7107
+ flags: [{
7108
+ name: "<organizationId>",
7109
+ type: "string",
7110
+ required: true
7111
+ }],
7112
+ idempotent: true,
7113
+ destructive: false
7114
+ },
7115
+ {
7116
+ name: "projects list",
7117
+ flags: [
7118
+ {
7119
+ name: "--org",
7120
+ type: "string",
7121
+ required: false
7122
+ },
7123
+ {
7124
+ name: "--search",
7125
+ type: "string",
7126
+ required: false
7127
+ },
7128
+ {
7129
+ name: "--limit",
7130
+ type: "number",
7131
+ required: false
7132
+ }
7133
+ ],
7134
+ idempotent: true,
7135
+ destructive: false
7136
+ },
7137
+ {
7138
+ name: "projects create",
7139
+ flags: [
7140
+ {
7141
+ name: "--prompt",
7142
+ type: "string",
7143
+ required: true
7144
+ },
7145
+ {
7146
+ name: "--org",
7147
+ type: "string",
7148
+ required: false
7149
+ },
7150
+ {
7151
+ name: "--name",
7152
+ type: "string",
7153
+ required: false
7154
+ },
7155
+ {
7156
+ name: "--wait",
7157
+ type: "boolean",
7158
+ required: false
7159
+ },
7160
+ {
7161
+ name: "--no-wait",
7162
+ type: "boolean",
7163
+ required: false
7164
+ }
7165
+ ],
7166
+ idempotent: false,
7167
+ destructive: false
7168
+ },
7169
+ {
7170
+ name: "projects get",
7171
+ flags: [{
7172
+ name: "<projectId>",
7173
+ type: "string",
7174
+ required: true
7175
+ }],
7176
+ idempotent: true,
7177
+ destructive: false
7178
+ },
7179
+ {
7180
+ name: "projects generate",
7181
+ flags: [
7182
+ {
7183
+ name: "<projectId>",
7184
+ type: "string",
7185
+ required: true
7186
+ },
7187
+ {
7188
+ name: "--prompt",
7189
+ type: "string",
7190
+ required: true
7191
+ },
7192
+ {
7193
+ name: "--thread",
7194
+ type: "string",
7195
+ required: false
7196
+ },
7197
+ {
6864
7198
  name: "--new-thread",
6865
7199
  type: "boolean",
6866
7200
  required: false
@@ -6931,7 +7265,7 @@ const commandTree = [
6931
7265
  name: "--store",
6932
7266
  type: "string",
6933
7267
  required: true,
6934
- choices: ["app-store"]
7268
+ choices: [...STORE_CHOICES]
6935
7269
  }],
6936
7270
  idempotent: false,
6937
7271
  destructive: false
@@ -7171,7 +7505,12 @@ const commandTree = [
7171
7505
  {
7172
7506
  name: "--env",
7173
7507
  type: "string",
7174
- required: false
7508
+ required: false,
7509
+ choices: [
7510
+ "development",
7511
+ "preview",
7512
+ "production"
7513
+ ]
7175
7514
  }
7176
7515
  ],
7177
7516
  idempotent: false,
@@ -7291,7 +7630,7 @@ const commandTree = [
7291
7630
  {
7292
7631
  name: "--name",
7293
7632
  type: "string",
7294
- required: false
7633
+ required: true
7295
7634
  }
7296
7635
  ],
7297
7636
  idempotent: false,
@@ -7317,6 +7656,10 @@ const commandTree = [
7317
7656
  name: "<databaseId>",
7318
7657
  type: "string",
7319
7658
  required: true
7659
+ }, {
7660
+ name: "--mask",
7661
+ type: "boolean",
7662
+ required: false
7320
7663
  }],
7321
7664
  idempotent: true,
7322
7665
  destructive: false
@@ -7473,7 +7816,13 @@ const commandTree = [
7473
7816
  {
7474
7817
  name: "--role",
7475
7818
  type: "string",
7476
- required: false
7819
+ required: false,
7820
+ choices: [
7821
+ "owner",
7822
+ "admin",
7823
+ "editor",
7824
+ "viewer"
7825
+ ]
7477
7826
  }
7478
7827
  ],
7479
7828
  idempotent: false,
@@ -7512,7 +7861,13 @@ const commandTree = [
7512
7861
  {
7513
7862
  name: "<role>",
7514
7863
  type: "string",
7515
- required: true
7864
+ required: true,
7865
+ choices: [
7866
+ "owner",
7867
+ "admin",
7868
+ "editor",
7869
+ "viewer"
7870
+ ]
7516
7871
  },
7517
7872
  {
7518
7873
  name: "--org",
@@ -7703,7 +8058,10 @@ function readLinkedProject() {
7703
8058
  try {
7704
8059
  const raw = readFileSync(filePath, "utf8");
7705
8060
  const data = JSON.parse(raw);
7706
- if (data.orgId && data.projectId) return data;
8061
+ if (typeof data.projectId === "string" && data.projectId.length > 0) return {
8062
+ orgId: typeof data.orgId === "string" && data.orgId.length > 0 ? data.orgId : null,
8063
+ projectId: data.projectId
8064
+ };
7707
8065
  return null;
7708
8066
  } catch {
7709
8067
  return null;
@@ -7756,7 +8114,7 @@ const linkCommand = {
7756
8114
  });
7757
8115
  return;
7758
8116
  }
7759
- const orgId = getStoredOrgId() ?? "";
8117
+ const orgId = getStoredOrgId() ?? null;
7760
8118
  const dir = join(process.cwd(), ANYTHING_DIR$1);
7761
8119
  mkdirSync(dir, { recursive: true });
7762
8120
  writeFileSync(join(dir, PROJECT_FILE), JSON.stringify({
@@ -7785,19 +8143,23 @@ const unlinkCommand = {
7785
8143
  handler: (argv) => {
7786
8144
  const command = "unlink";
7787
8145
  const dir = join(process.cwd(), ANYTHING_DIR$1);
7788
- if (!existsSync(dir)) {
8146
+ const projectFile = getProjectFilePath();
8147
+ if (!existsSync(projectFile)) {
7789
8148
  outputError({
7790
8149
  argv,
7791
8150
  command,
7792
8151
  error: {
7793
- message: "No .anything/ directory found in current directory.",
8152
+ message: "No linked project found in current directory.",
7794
8153
  status: null
7795
8154
  },
7796
8155
  exitCode: 3
7797
8156
  });
7798
8157
  return;
7799
8158
  }
7800
- rmSync(dir, { recursive: true });
8159
+ rmSync(projectFile);
8160
+ try {
8161
+ if (existsSync(dir) && readdirSync(dir).length === 0) rmSync(dir, { recursive: true });
8162
+ } catch {}
7801
8163
  if (outputSuccess({
7802
8164
  argv,
7803
8165
  command,
@@ -7999,12 +8361,19 @@ const llmContextCommand = {
7999
8361
 
8000
8362
  //#endregion
8001
8363
  //#region src/commands/members.ts
8364
+ const MEMBER_ROLE_CHOICES = [
8365
+ "owner",
8366
+ "admin",
8367
+ "editor",
8368
+ "viewer"
8369
+ ];
8370
+ const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
8002
8371
  const membersListCommand = {
8003
8372
  command: "list",
8004
8373
  describe: "List organization members and pending invites",
8005
8374
  builder: (yargs) => yargs.option("org", {
8006
8375
  type: "string",
8007
- describe: "Organization ID"
8376
+ describe: "Organization ID (defaults to the active org)"
8008
8377
  }).example("anything members list --org org_123", "List members"),
8009
8378
  handler: async (argv) => {
8010
8379
  const command = "members list";
@@ -8093,13 +8462,26 @@ const membersInviteCommand = {
8093
8462
  describe: "Email address to invite"
8094
8463
  }).option("org", {
8095
8464
  type: "string",
8096
- describe: "Organization ID"
8465
+ describe: "Organization ID (defaults to the active org)"
8097
8466
  }).option("role", {
8098
8467
  type: "string",
8099
- describe: "Role to assign (e.g. admin, member)"
8468
+ choices: MEMBER_ROLE_CHOICES,
8469
+ describe: "Role to assign: owner, admin, editor, or viewer"
8100
8470
  }).example("anything members invite user@example.com --org org_123", "Invite a member"),
8101
8471
  handler: async (argv) => {
8102
8472
  const command = "members invite";
8473
+ if (!EMAIL_PATTERN.test(argv.email)) {
8474
+ outputError({
8475
+ argv,
8476
+ command,
8477
+ error: {
8478
+ message: `Invalid email address: "${argv.email}".`,
8479
+ status: null
8480
+ },
8481
+ exitCode: 2
8482
+ });
8483
+ return;
8484
+ }
8103
8485
  const config = resolveConfig({
8104
8486
  dev: argv.dev,
8105
8487
  apiUrl: argv.apiUrl
@@ -8179,7 +8561,7 @@ const membersRemoveCommand = {
8179
8561
  describe: "Email address of the member to remove"
8180
8562
  }).option("org", {
8181
8563
  type: "string",
8182
- describe: "Organization ID"
8564
+ describe: "Organization ID (defaults to the active org)"
8183
8565
  }).option("yes", {
8184
8566
  type: "boolean",
8185
8567
  default: false,
@@ -8277,10 +8659,11 @@ const membersRoleCommand = {
8277
8659
  }).positional("role", {
8278
8660
  type: "string",
8279
8661
  demandOption: true,
8280
- describe: "New role (e.g. admin, member)"
8662
+ choices: MEMBER_ROLE_CHOICES,
8663
+ describe: "New role: owner, admin, editor, or viewer"
8281
8664
  }).option("org", {
8282
8665
  type: "string",
8283
- describe: "Organization ID"
8666
+ describe: "Organization ID (defaults to the active org)"
8284
8667
  }).example("anything members role user@example.com admin --org org_123", "Change role to admin"),
8285
8668
  handler: async (argv) => {
8286
8669
  const command = "members role";
@@ -8317,6 +8700,19 @@ const membersRoleCommand = {
8317
8700
  });
8318
8701
  return;
8319
8702
  }
8703
+ if (argv["dry-run"]) {
8704
+ outputDryRun({
8705
+ argv,
8706
+ command,
8707
+ plannedActions: [{
8708
+ action: "update_member_role",
8709
+ org: orgResult.orgId,
8710
+ email: argv.email,
8711
+ role: argv.role
8712
+ }]
8713
+ });
8714
+ return;
8715
+ }
8320
8716
  const result = await new AnythingApiClient(config.value).updateMemberRole({
8321
8717
  organizationId: orgResult.orgId,
8322
8718
  email: argv.email,
@@ -8625,20 +9021,28 @@ const orgsCommand = {
8625
9021
 
8626
9022
  //#endregion
8627
9023
  //#region src/stream.ts
9024
+ function resultErrorMessage(error) {
9025
+ if (error && typeof error === "object" && "message" in error && typeof error.message === "string") return error.message;
9026
+ return "Operation failed";
9027
+ }
8628
9028
  function createEmitter(argv) {
8629
9029
  if (argv.quiet) return (_event) => {};
8630
9030
  if (argv.json) return (event) => {
8631
9031
  printNdjson(event);
8632
9032
  };
8633
9033
  return (event) => {
8634
- if (event.type === "progress") printStreamMarker(event.status === "complete" ? "done" : "progress", event.message ?? `${event.step}...`);
9034
+ if (event.type === "progress") {
9035
+ printStreamMarker(event.status === "complete" ? "done" : "progress", event.message ?? `${event.step}...`);
9036
+ return;
9037
+ }
9038
+ if (event.type === "result" && !event.ok) printError(`[error] ${resultErrorMessage(event.error)}`);
8635
9039
  };
8636
9040
  }
8637
9041
 
8638
9042
  //#endregion
8639
9043
  //#region src/watch-generation.ts
8640
9044
  const TIMEOUT_MS = 300 * 1e3;
8641
- const POLL_INTERVAL_MS$1 = 3e3;
9045
+ const POLL_INTERVAL_MS = 3e3;
8642
9046
  const TERMINAL_SUCCESS_STATUSES = new Set(["COMPLETED", "VALID"]);
8643
9047
  const TERMINAL_FAILURE_STATUSES = new Set([
8644
9048
  "INVALID",
@@ -8762,7 +9166,7 @@ async function pollUntilTerminal({ client, projectGroupId, emit, sleepFn = sleep
8762
9166
  while (Date.now() < deadlineMs) {
8763
9167
  const result = await client.getProjectStatus({ projectGroupId });
8764
9168
  if (result.isErr()) {
8765
- await sleepFn(POLL_INTERVAL_MS$1);
9169
+ await sleepFn(POLL_INTERVAL_MS);
8766
9170
  continue;
8767
9171
  }
8768
9172
  const { status } = result.value;
@@ -8786,7 +9190,7 @@ async function pollUntilTerminal({ client, projectGroupId, emit, sleepFn = sleep
8786
9190
  });
8787
9191
  return false;
8788
9192
  }
8789
- await sleepFn(POLL_INTERVAL_MS$1);
9193
+ await sleepFn(POLL_INTERVAL_MS);
8790
9194
  }
8791
9195
  emit({
8792
9196
  type: "result",
@@ -8845,7 +9249,11 @@ const createCommand = {
8845
9249
  }).option("name", {
8846
9250
  type: "string",
8847
9251
  describe: "App name (optional)"
8848
- }).example("anything projects create --prompt \"Build a todo app\"", "Create an app using your saved organization").example("anything projects create --org org_1 --prompt \"Build a todo app\"", "Create an app in a specific organization").example("cat prompt.txt | anything projects create", "Create an app from stdin"),
9252
+ }).option("wait", {
9253
+ type: "boolean",
9254
+ default: true,
9255
+ describe: "Wait for generation to complete (use --no-wait to skip)"
9256
+ }).example("anything projects create --prompt \"Build a todo app\"", "Create an app using your saved organization").example("anything projects create --org org_1 --prompt \"Build a todo app\"", "Create an app in a specific organization").example("cat prompt.txt | anything projects create", "Create an app from stdin").example("anything projects create --prompt \"Build a todo app\" --no-wait --json", "Create an app and return immediately without streaming"),
8849
9257
  handler: async (argv) => {
8850
9258
  const config = resolveConfig({
8851
9259
  dev: argv.dev,
@@ -8934,6 +9342,25 @@ const createCommand = {
8934
9342
  status: "complete",
8935
9343
  message: "Project created"
8936
9344
  });
9345
+ if (!argv.wait) {
9346
+ if (argv.quiet) {
9347
+ console.log(result.value.projectGroupId);
9348
+ return;
9349
+ }
9350
+ if (argv.json) {
9351
+ printNdjson({
9352
+ type: "result",
9353
+ ok: true,
9354
+ data: result.value
9355
+ });
9356
+ return;
9357
+ }
9358
+ printStreamMarker("done", `project ${result.value.projectGroupId} created`);
9359
+ printSuccess("App created! Generation enqueued.");
9360
+ printLabel("Project Group ID", result.value.projectGroupId);
9361
+ printLabel("Revision ID", result.value.revisionId);
9362
+ return;
9363
+ }
8937
9364
  emit({
8938
9365
  type: "progress",
8939
9366
  step: "generating",
@@ -9055,7 +9482,10 @@ const publishStatusCommand = {
9055
9482
  }).example("anything projects publish status <project-id> <deployment-id>", "Check a deployment later").example("anything projects publish status <project-id> <deployment-id> --json", "Return deployment status in JSON"),
9056
9483
  handler: async (argv) => {
9057
9484
  const command = "projects publish status";
9058
- const config = resolveConfig({ dev: argv.dev });
9485
+ const config = resolveConfig({
9486
+ dev: argv.dev,
9487
+ apiUrl: argv.apiUrl
9488
+ });
9059
9489
  if (config.isErr()) {
9060
9490
  outputError({
9061
9491
  argv,
@@ -9079,14 +9509,23 @@ const publishStatusCommand = {
9079
9509
  }
9080
9510
  const { deployment } = result.value;
9081
9511
  if (deployment.status === "FAILED") {
9082
- process.exitCode = 1;
9083
- if (outputSuccess({
9512
+ const failure = getDeploymentError({
9513
+ result: {
9514
+ outcome: "completed",
9515
+ deployment
9516
+ },
9517
+ statusCommand: `anything deployments logs ${argv.deploymentId}`
9518
+ });
9519
+ outputError({
9084
9520
  argv,
9085
9521
  command,
9086
- data: { deployment },
9087
- primaryId: deployment.id
9088
- })) return;
9089
- printError(deployment.buildLogs ?? "Deployment failed (no logs available).");
9522
+ error: {
9523
+ message: failure?.message ?? "Deployment failed (no logs available).",
9524
+ status: null
9525
+ },
9526
+ hint: `Inspect the full build logs with: anything deployments logs ${argv.deploymentId}`,
9527
+ exitCode: failure?.exitCode ?? 1
9528
+ });
9090
9529
  return;
9091
9530
  }
9092
9531
  if (outputSuccess({
@@ -9338,7 +9777,7 @@ const list$1 = {
9338
9777
  describe: "The project ID"
9339
9778
  }).example("anything projects files list <id>", "List the files available in a project"),
9340
9779
  handler: async (argv) => {
9341
- const command = "files list";
9780
+ const command = "projects files list";
9342
9781
  const config = resolveConfig({
9343
9782
  dev: argv.dev,
9344
9783
  apiUrl: argv.apiUrl
@@ -9393,7 +9832,7 @@ const get$1 = {
9393
9832
  describe: "The file path"
9394
9833
  }).example("anything projects files get <id> app/page.tsx", "Print a file to stdout"),
9395
9834
  handler: async (argv) => {
9396
- const command = "files get";
9835
+ const command = "projects files get";
9397
9836
  const config = resolveConfig({
9398
9837
  dev: argv.dev,
9399
9838
  apiUrl: argv.apiUrl
@@ -9731,7 +10170,10 @@ const infoCommand = {
9731
10170
  printLabel("Published URL", latestPublishedUrl);
9732
10171
  printLabel("Created", new Date(info.createdAt).toLocaleString());
9733
10172
  printLabel("Updated", new Date(info.updatedAt).toLocaleString());
9734
- for (const url of info.publishedUrls) printLabel("Published URL", url);
10173
+ for (const url of info.publishedUrls) {
10174
+ if (url === latestPublishedUrl) continue;
10175
+ printLabel("Other Published URL", url);
10176
+ }
9735
10177
  if (info.files.length > 0) {
9736
10178
  console.log();
9737
10179
  printTable({
@@ -10006,6 +10448,96 @@ const logsCommand = {
10006
10448
  }
10007
10449
  };
10008
10450
 
10451
+ //#endregion
10452
+ //#region src/format-assistant-message.ts
10453
+ const NAMED_ENTITIES = {
10454
+ "&quot;": "\"",
10455
+ "&apos;": "'",
10456
+ "&#39;": "'",
10457
+ "&lt;": "<",
10458
+ "&gt;": ">",
10459
+ "&nbsp;": " "
10460
+ };
10461
+ function decodeEntities(input) {
10462
+ return input.replace(/&(?:quot|apos|#39|lt|gt|nbsp);/g, (m) => NAMED_ENTITIES[m] ?? m).replace(/&amp;/g, "&");
10463
+ }
10464
+ function stripTags(input) {
10465
+ return input.replace(/<[^>]+>/g, "");
10466
+ }
10467
+ function parseAttrs(attrString) {
10468
+ const attrs = {};
10469
+ const re = /([a-zA-Z][\w-]*)="([^"]*)"/g;
10470
+ let match;
10471
+ while ((match = re.exec(attrString)) !== null) {
10472
+ const [, key, value] = match;
10473
+ if (key !== void 0 && value !== void 0) attrs[key] = value;
10474
+ }
10475
+ return attrs;
10476
+ }
10477
+ function firstLine(text) {
10478
+ return (text.split("\n").map((l) => l.trim()).find(Boolean) ?? "").replace(/\*\*/g, "");
10479
+ }
10480
+ function indent(text) {
10481
+ return text.split("\n").map((l) => ` ${l}`).join("\n");
10482
+ }
10483
+ function parseTodos(raw) {
10484
+ if (!raw) return [];
10485
+ try {
10486
+ const parsed = JSON.parse(decodeEntities(raw));
10487
+ if (!Array.isArray(parsed)) return [];
10488
+ return parsed.filter((t) => !!t && typeof t === "object" && typeof t.content === "string").map((t) => ({
10489
+ content: t.content,
10490
+ status: typeof t.status === "string" ? t.status : "pending"
10491
+ }));
10492
+ } catch {
10493
+ return [];
10494
+ }
10495
+ }
10496
+ function renderBlock(attrs) {
10497
+ const uiType = attrs.uiType ?? "block";
10498
+ const text = decodeEntities(attrs.text ?? "").trim();
10499
+ const subtext = decodeEntities(attrs.subtext ?? "").trim();
10500
+ if (uiType === "todo-list") {
10501
+ const lines = [`[plan] ${text || "Plan"}${subtext ? ` — ${subtext}` : ""}`];
10502
+ for (const todo of parseTodos(attrs.todos)) {
10503
+ const box = todo.status === "completed" ? "[x]" : todo.status === "in_progress" ? "[~]" : "[ ]";
10504
+ lines.push(` ${box} ${todo.content}`);
10505
+ }
10506
+ return lines.join("\n");
10507
+ }
10508
+ if (uiType === "thinking") {
10509
+ const summary = firstLine(subtext) || text;
10510
+ return summary ? `[thinking] ${summary}` : null;
10511
+ }
10512
+ if (uiType === "database") {
10513
+ const code = decodeEntities(attrs.code ?? "").trim();
10514
+ const head = `[database] ${text || "change"}`;
10515
+ return code ? `${head}\n${indent(code)}` : head;
10516
+ }
10517
+ const parts = [text, subtext].filter(Boolean);
10518
+ if (parts.length === 0) return null;
10519
+ return `[${uiType}] ${parts.join(" — ")}`;
10520
+ }
10521
+ function formatAssistantMessage(raw) {
10522
+ const blockRe = /<file-based-block\b([^>]*)>([\s\S]*?)<\/file-based-block>/g;
10523
+ const segments = [];
10524
+ let lastIndex = 0;
10525
+ let hadBlock = false;
10526
+ let match;
10527
+ while ((match = blockRe.exec(raw)) !== null) {
10528
+ hadBlock = true;
10529
+ const between = stripTags(raw.slice(lastIndex, match.index)).trim();
10530
+ if (between) segments.push(decodeEntities(between));
10531
+ const rendered = renderBlock(parseAttrs(match[1] ?? ""));
10532
+ if (rendered) segments.push(rendered);
10533
+ lastIndex = blockRe.lastIndex;
10534
+ }
10535
+ if (!hadBlock) return decodeEntities(stripTags(raw)).trim();
10536
+ const tail = stripTags(raw.slice(lastIndex)).trim();
10537
+ if (tail) segments.push(decodeEntities(tail));
10538
+ return segments.filter(Boolean).join("\n");
10539
+ }
10540
+
10009
10541
  //#endregion
10010
10542
  //#region src/commands/messages.ts
10011
10543
  const COMMAND$9 = "projects messages";
@@ -10014,7 +10546,13 @@ function formatMessage(msg) {
10014
10546
  const status = msg.status === "VALID" ? styleText("green", msg.status) : msg.status === "BUILDING" ? styleText("yellow", msg.status) : styleText("red", msg.status);
10015
10547
  console.log(styleText("dim", `─── ${time} ─── ${status} ───`));
10016
10548
  if (msg.userMessage) console.log(styleText("bold", "You: ") + msg.userMessage);
10017
- if (msg.assistantMessage) console.log(styleText("bold", "Assistant: ") + msg.assistantMessage);
10549
+ if (msg.assistantMessage) {
10550
+ const text = formatAssistantMessage(msg.assistantMessage);
10551
+ if (text) {
10552
+ console.log(styleText("bold", "Assistant:"));
10553
+ console.log(text);
10554
+ }
10555
+ }
10018
10556
  console.log();
10019
10557
  }
10020
10558
  const messagesCommand = {
@@ -10061,12 +10599,15 @@ const messagesCommand = {
10061
10599
  });
10062
10600
  return;
10063
10601
  }
10602
+ const { messages } = result.value;
10064
10603
  if (outputSuccess({
10065
10604
  argv,
10066
10605
  command: COMMAND$9,
10067
- data: result.value
10606
+ data: { messages: messages.map((msg) => ({
10607
+ ...msg,
10608
+ assistantText: msg.assistantMessage ? formatAssistantMessage(msg.assistantMessage) : null
10609
+ })) }
10068
10610
  })) return;
10069
- const { messages } = result.value;
10070
10611
  if (messages.length === 0) {
10071
10612
  console.log("No messages found.");
10072
10613
  return;
@@ -10223,6 +10764,11 @@ const renameCommand = {
10223
10764
 
10224
10765
  //#endregion
10225
10766
  //#region src/commands/secrets.ts
10767
+ const secretEnvironmentChoices = [
10768
+ "development",
10769
+ "preview",
10770
+ "production"
10771
+ ];
10226
10772
  const add = {
10227
10773
  command: "add <projectId>",
10228
10774
  describe: "Add a secret to an app",
@@ -10239,10 +10785,11 @@ const add = {
10239
10785
  describe: "Secret value. If omitted, reads stdin when piped."
10240
10786
  }).option("env", {
10241
10787
  type: "string",
10242
- describe: "Environment (development, preview, production)"
10788
+ choices: secretEnvironmentChoices,
10789
+ describe: "Target environment for the secret"
10243
10790
  }).example("anything projects secrets add <id> --name KEY --value \"secret\"", "Add a secret from an inline value").example("cat secret.txt | anything projects secrets add <id> --name KEY", "Read a secret value from stdin"),
10244
10791
  handler: async (argv) => {
10245
- const command = "secrets add";
10792
+ const command = "projects secrets add";
10246
10793
  if (argv["dry-run"]) {
10247
10794
  outputDryRun({
10248
10795
  argv,
@@ -10251,7 +10798,7 @@ const add = {
10251
10798
  action: "add_secret",
10252
10799
  projectGroupId: argv.projectId,
10253
10800
  name: argv.name,
10254
- ...argv.env ? { environment: argv.env } : {}
10801
+ ...argv.env ? { environment: argv.env.toUpperCase() } : {}
10255
10802
  }]
10256
10803
  });
10257
10804
  return;
@@ -10293,7 +10840,7 @@ const add = {
10293
10840
  projectGroupId: argv.projectId,
10294
10841
  displayName: argv.name,
10295
10842
  value: valueResult.value,
10296
- environment: argv.env ?? null
10843
+ environment: argv.env ? argv.env.toUpperCase() : null
10297
10844
  });
10298
10845
  if (result.isErr()) {
10299
10846
  outputError({
@@ -10327,18 +10874,34 @@ const remove = {
10327
10874
  type: "string",
10328
10875
  demandOption: true,
10329
10876
  describe: "The secret ID to remove"
10330
- }).example("anything projects secrets remove <project-id> <secret-id> --dry-run", "Preview a secret removal").example("anything projects secrets remove <project-id> <secret-id>", "Delete a secret"),
10877
+ }).option("yes", {
10878
+ type: "boolean",
10879
+ default: false,
10880
+ describe: "Skip confirmation"
10881
+ }).example("anything projects secrets remove <project-id> <secret-id> --dry-run", "Preview a secret removal").example("anything projects secrets remove <project-id> <secret-id> --yes", "Delete a secret"),
10331
10882
  handler: async (argv) => {
10332
- const command = "secrets remove";
10883
+ const command = "projects secrets remove";
10333
10884
  if (argv["dry-run"]) {
10334
10885
  outputDryRun({
10335
10886
  argv,
10336
10887
  command,
10337
- plannedActions: [{
10338
- action: "remove_secret",
10339
- projectGroupId: argv.projectId,
10340
- secretId: argv.secretId
10341
- }]
10888
+ plannedActions: [{
10889
+ action: "remove_secret",
10890
+ projectGroupId: argv.projectId,
10891
+ secretId: argv.secretId
10892
+ }]
10893
+ });
10894
+ return;
10895
+ }
10896
+ if (!argv.yes) {
10897
+ outputError({
10898
+ argv,
10899
+ command,
10900
+ error: {
10901
+ message: "Removing a secret is destructive. Pass --yes to confirm.",
10902
+ status: null
10903
+ },
10904
+ exitCode: 2
10342
10905
  });
10343
10906
  return;
10344
10907
  }
@@ -10388,7 +10951,7 @@ const list = {
10388
10951
  describe: "The project ID"
10389
10952
  }).example("anything projects secrets list <project-id>", "List secrets for an app"),
10390
10953
  handler: async (argv) => {
10391
- const command = "secrets list";
10954
+ const command = "projects secrets list";
10392
10955
  const config = resolveConfig({
10393
10956
  dev: argv.dev,
10394
10957
  apiUrl: argv.apiUrl
@@ -10515,272 +11078,41 @@ function parseSecrets({ secretArgs, environment }) {
10515
11078
  environment
10516
11079
  });
10517
11080
  }
10518
- return {
10519
- ok: true,
10520
- value: secrets
10521
- };
10522
- }
10523
- function resolveSecretEnvironment(environment) {
10524
- switch (environment) {
10525
- case "development":
10526
- case "preview":
10527
- case "production": return environment;
10528
- default: return null;
10529
- }
10530
- }
10531
- const get = {
10532
- command: "get [projectId]",
10533
- describe: "Read project auth settings",
10534
- builder: (yargs) => yargs.positional("projectId", {
10535
- type: "string",
10536
- describe: "The project ID. Falls back to the current project set via `anything projects set`."
10537
- }).example("anything projects settings auth get <project-id>", "Inspect auth provider state for a project").example("anything projects settings auth get --json", "Return auth settings for the current project in JSON"),
10538
- handler: async (argv) => {
10539
- const command = "settings auth get";
10540
- const projectGroupId = resolveProjectGroupId(argv.projectId);
10541
- if (projectGroupId === null) {
10542
- outputError({
10543
- argv,
10544
- command,
10545
- error: {
10546
- message: "No project specified. Pass a project ID or run `anything projects set <project-id>`.",
10547
- status: null
10548
- },
10549
- exitCode: 2
10550
- });
10551
- return;
10552
- }
10553
- const config = resolveConfig({
10554
- dev: argv.dev,
10555
- apiUrl: argv.apiUrl
10556
- });
10557
- if (config.isErr()) {
10558
- outputError({
10559
- argv,
10560
- command,
10561
- error: {
10562
- message: config.error.message,
10563
- status: null
10564
- },
10565
- exitCode: 4
10566
- });
10567
- return;
10568
- }
10569
- const result = await new AnythingApiClient(config.value).getProjectAuthSettings({ projectGroupId });
10570
- if (result.isErr()) {
10571
- outputError({
10572
- argv,
10573
- command,
10574
- error: result.error
10575
- });
10576
- return;
10577
- }
10578
- if (outputSuccess({
10579
- argv,
10580
- command,
10581
- data: result.value
10582
- })) return;
10583
- printAuthSettings(result.value);
10584
- }
10585
- };
10586
- const set = {
10587
- command: "set [projectId]",
10588
- describe: "Update project auth settings for a provider",
10589
- builder: (yargs) => yargs.positional("projectId", {
10590
- type: "string",
10591
- describe: "The project ID. Falls back to the current project set via `anything projects set`."
10592
- }).option("provider", {
10593
- choices: authProviderChoices,
10594
- demandOption: true,
10595
- describe: "The auth provider to update"
10596
- }).option("enabled", {
10597
- type: "boolean",
10598
- demandOption: true,
10599
- describe: "Whether to enable the provider. Use --no-enabled to disable it."
10600
- }).option("secret", {
10601
- type: "string",
10602
- array: true,
10603
- describe: "Provider secret in ENV_KEY=VALUE format. Repeat for multiple secrets."
10604
- }).option("env", {
10605
- choices: authEnvironmentChoices,
10606
- default: "production",
10607
- describe: "Environment for secrets passed via --secret"
10608
- }).example("anything projects settings auth set <project-id> --provider google --enabled --secret GOOGLE_CLIENT_ID=abc --secret GOOGLE_CLIENT_SECRET=def", "Enable Google auth and store provider secrets").example("anything projects settings auth set <project-id> --provider email --no-enabled", "Disable email auth"),
10609
- handler: async (argv) => {
10610
- const command = "settings auth set";
10611
- const projectGroupId = resolveProjectGroupId(argv.projectId);
10612
- if (projectGroupId === null) {
10613
- outputError({
10614
- argv,
10615
- command,
10616
- error: {
10617
- message: "No project specified. Pass a project ID or run `anything projects set <project-id>`.",
10618
- status: null
10619
- },
10620
- exitCode: 2
10621
- });
10622
- return;
10623
- }
10624
- const secretEnvironment = resolveSecretEnvironment(argv.env);
10625
- if (secretEnvironment === null) {
10626
- outputError({
10627
- argv,
10628
- command,
10629
- error: {
10630
- message: `Invalid --env value "${argv.env}".`,
10631
- status: null
10632
- },
10633
- exitCode: 2
10634
- });
10635
- return;
10636
- }
10637
- const secretsResult = parseSecrets({
10638
- secretArgs: argv.secret ?? [],
10639
- environment: secretEnvironment
10640
- });
10641
- if (!secretsResult.ok) {
10642
- outputError({
10643
- argv,
10644
- command,
10645
- error: {
10646
- message: secretsResult.error,
10647
- status: null
10648
- },
10649
- exitCode: 2
10650
- });
10651
- return;
10652
- }
10653
- const config = resolveConfig({
10654
- dev: argv.dev,
10655
- apiUrl: argv.apiUrl
10656
- });
10657
- if (config.isErr()) {
10658
- outputError({
10659
- argv,
10660
- command,
10661
- error: {
10662
- message: config.error.message,
10663
- status: null
10664
- },
10665
- exitCode: 4
10666
- });
10667
- return;
10668
- }
10669
- const result = await new AnythingApiClient(config.value).updateProjectAuthSettings({
10670
- projectGroupId,
10671
- provider: argv.provider,
10672
- enabled: argv.enabled,
10673
- secrets: secretsResult.value,
10674
- enableForAllModules: false
10675
- });
10676
- if (result.isErr()) {
10677
- outputError({
10678
- argv,
10679
- command,
10680
- error: result.error
10681
- });
10682
- return;
10683
- }
10684
- if (outputSuccess({
10685
- argv,
10686
- command,
10687
- data: result.value
10688
- })) return;
10689
- printSuccess("Auth settings updated.");
10690
- printAuthSettings(result.value);
10691
- }
10692
- };
10693
- const authSettingsCommand = {
10694
- command: "auth <command>",
10695
- describe: "Manage auth-related project settings",
10696
- builder: (yargs) => yargs.command(get).command(set).demandCommand(),
10697
- handler: () => {}
10698
- };
10699
-
10700
- //#endregion
10701
- //#region src/commands/settings.ts
10702
- const settingsCommand = {
10703
- command: "settings <command>",
10704
- describe: "Inspect and update project settings",
10705
- builder: (yargs) => yargs.command(authSettingsCommand).example("anything projects settings auth get <project-id>", "Inspect project auth settings").demandCommand(),
10706
- handler: () => {}
10707
- };
10708
-
10709
- //#endregion
10710
- //#region src/commands/submit.ts
10711
- const POLL_INTERVAL_MS = 2e3;
10712
- const POLL_TIMEOUT_MS = 6e4;
10713
- async function waitForSubmission({ client, projectGroupId, submission }) {
10714
- const deadline = Date.now() + POLL_TIMEOUT_MS;
10715
- let current = submission;
10716
- while (current.status === "PENDING" && Date.now() < deadline) {
10717
- const latestResult = await client.getProjectSubmission({
10718
- projectGroupId,
10719
- submissionId: current.id
10720
- });
10721
- if (latestResult.isErr()) return latestResult.map((value) => value.submission);
10722
- current = latestResult.value.submission;
10723
- if (current.status !== "PENDING") return latestResult.map((value) => value.submission);
10724
- await setTimeout$1(POLL_INTERVAL_MS);
10725
- }
10726
- return ok(current);
10727
- }
10728
- function printSubmissionResult({ argv, command, pendingMessage, submission }) {
10729
- const { projectGroupId: projectId, ...submissionRest } = submission;
10730
- const submissionOut = {
10731
- projectId,
10732
- ...submissionRest
10733
- };
10734
- if (submission.status === "FAILED") {
10735
- printError(submission.errorMessage ?? "Submission failed.");
10736
- process.exitCode = 1;
10737
- return;
10738
- }
10739
- if (submission.status === "CREATED") {
10740
- if (outputSuccess({
10741
- argv,
10742
- command,
10743
- data: {
10744
- success: true,
10745
- submission: submissionOut
10746
- },
10747
- primaryId: submission.id
10748
- })) return;
10749
- printSuccess("App Store submission is ready.");
10750
- printLabel("Submission ID", submission.id);
10751
- printLabel("Launch URL", submission.launchUrl);
10752
- printLabel("Auth URL", submission.authUrl);
10753
- printLabel("Expires At", submission.expiresAt);
10754
- return;
10755
- }
10756
- if (outputSuccess({
10757
- argv,
10758
- command,
10759
- data: {
10760
- success: true,
10761
- submission
10762
- },
10763
- primaryId: submission.id
10764
- })) return;
10765
- printSuccess(pendingMessage);
10766
- printLabel("Submission ID", submission.id);
10767
- printLabel("Status", submission.status);
10768
- printLabel("Next step", "Expo is still preparing the launch flow. Run the command again in a moment.");
11081
+ return {
11082
+ ok: true,
11083
+ value: secrets
11084
+ };
10769
11085
  }
10770
- const submitStatusCommand = {
10771
- command: "status <projectId> <submissionId>",
10772
- describe: "Check the status of an App Store submission",
11086
+ function resolveSecretEnvironment(environment) {
11087
+ switch (environment) {
11088
+ case "development":
11089
+ case "preview":
11090
+ case "production": return environment;
11091
+ default: return null;
11092
+ }
11093
+ }
11094
+ const get = {
11095
+ command: "get [projectId]",
11096
+ describe: "Read project auth settings",
10773
11097
  builder: (yargs) => yargs.positional("projectId", {
10774
11098
  type: "string",
10775
- demandOption: true,
10776
- describe: "The project ID"
10777
- }).positional("submissionId", {
10778
- type: "string",
10779
- demandOption: true,
10780
- describe: "The submission ID"
10781
- }).example("anything projects submit status <project-id> <submission-id>", "Check a submission later").example("anything projects submit status <project-id> <submission-id> --json", "Return the submission status in JSON"),
11099
+ describe: "The project ID. Falls back to the current project set via `anything projects set`."
11100
+ }).example("anything projects settings auth get <project-id>", "Inspect auth provider state for a project").example("anything projects settings auth get --json", "Return auth settings for the current project in JSON"),
10782
11101
  handler: async (argv) => {
10783
- const command = "projects submit status";
11102
+ const command = "projects settings auth get";
11103
+ const projectGroupId = resolveProjectGroupId(argv.projectId);
11104
+ if (projectGroupId === null) {
11105
+ outputError({
11106
+ argv,
11107
+ command,
11108
+ error: {
11109
+ message: "No project specified. Pass a project ID or run `anything projects set <project-id>`.",
11110
+ status: null
11111
+ },
11112
+ exitCode: 2
11113
+ });
11114
+ return;
11115
+ }
10784
11116
  const config = resolveConfig({
10785
11117
  dev: argv.dev,
10786
11118
  apiUrl: argv.apiUrl
@@ -10797,10 +11129,7 @@ const submitStatusCommand = {
10797
11129
  });
10798
11130
  return;
10799
11131
  }
10800
- const result = await new AnythingApiClient(config.value).getProjectSubmission({
10801
- projectGroupId: argv.projectId,
10802
- submissionId: argv.submissionId
10803
- });
11132
+ const result = await new AnythingApiClient(config.value).getProjectAuthSettings({ projectGroupId });
10804
11133
  if (result.isErr()) {
10805
11134
  outputError({
10806
11135
  argv,
@@ -10809,102 +11138,151 @@ const submitStatusCommand = {
10809
11138
  });
10810
11139
  return;
10811
11140
  }
10812
- printSubmissionResult({
11141
+ if (outputSuccess({
10813
11142
  argv,
10814
11143
  command,
10815
- pendingMessage: "Submission is still preparing.",
10816
- submission: result.value.submission
10817
- });
11144
+ data: result.value
11145
+ })) return;
11146
+ printAuthSettings(result.value);
10818
11147
  }
10819
11148
  };
10820
- const submitCommand = {
10821
- command: "submit <projectId>",
10822
- describe: "Start an App Store submission for an app",
10823
- builder: (yargs) => yargs.command(submitStatusCommand).positional("projectId", {
11149
+ const set = {
11150
+ command: "set [projectId]",
11151
+ describe: "Update project auth settings for a provider",
11152
+ builder: (yargs) => yargs.positional("projectId", {
10824
11153
  type: "string",
11154
+ describe: "The project ID. Falls back to the current project set via `anything projects set`."
11155
+ }).option("provider", {
11156
+ choices: authProviderChoices,
10825
11157
  demandOption: true,
10826
- describe: "The project ID"
10827
- }).option("store", {
11158
+ describe: "The auth provider to update"
11159
+ }).option("enabled", {
11160
+ type: "boolean",
11161
+ demandOption: true,
11162
+ describe: "Whether to enable the provider. Use --no-enabled to disable it."
11163
+ }).option("secret", {
10828
11164
  type: "string",
10829
- choices: ["app-store"],
10830
- describe: "Submission target store"
10831
- }).example("anything projects submit <project-id> --store app-store", "Start an App Store submission").example("anything projects submit <project-id> --store app-store --json", "Return the submission session in JSON").example("anything projects submit status <project-id> <submission-id>", "Check a submission later"),
11165
+ array: true,
11166
+ describe: "Provider secret in ENV_KEY=VALUE format. Repeat for multiple secrets."
11167
+ }).option("env", {
11168
+ choices: authEnvironmentChoices,
11169
+ default: "production",
11170
+ describe: "Environment for secrets passed via --secret"
11171
+ }).example("anything projects settings auth set <project-id> --provider google --enabled --secret GOOGLE_CLIENT_ID=abc --secret GOOGLE_CLIENT_SECRET=def", "Enable Google auth and store provider secrets").example("anything projects settings auth set <project-id> --provider email --no-enabled", "Disable email auth"),
10832
11172
  handler: async (argv) => {
10833
- const command = "projects submit";
10834
- if (argv.store === void 0) {
11173
+ const command = "projects settings auth set";
11174
+ const projectGroupId = resolveProjectGroupId(argv.projectId);
11175
+ if (projectGroupId === null) {
10835
11176
  outputError({
10836
11177
  argv,
10837
11178
  command,
10838
11179
  error: {
10839
- message: "Missing required argument: store",
11180
+ message: "No project specified. Pass a project ID or run `anything projects set <project-id>`.",
10840
11181
  status: null
10841
11182
  },
10842
11183
  exitCode: 2
10843
11184
  });
10844
11185
  return;
10845
11186
  }
10846
- if (argv.store !== "app-store") {
11187
+ const secretEnvironment = resolveSecretEnvironment(argv.env);
11188
+ if (secretEnvironment === null) {
10847
11189
  outputError({
10848
11190
  argv,
10849
11191
  command,
10850
11192
  error: {
10851
- message: "Unsupported store.",
11193
+ message: `Invalid --env value "${argv.env}".`,
10852
11194
  status: null
10853
11195
  },
10854
11196
  exitCode: 2
10855
11197
  });
10856
11198
  return;
10857
11199
  }
10858
- const config = resolveConfig({
10859
- dev: argv.dev,
10860
- apiUrl: argv.apiUrl
11200
+ const secretsResult = parseSecrets({
11201
+ secretArgs: argv.secret ?? [],
11202
+ environment: secretEnvironment
10861
11203
  });
10862
- if (config.isErr()) {
11204
+ if (!secretsResult.ok) {
10863
11205
  outputError({
10864
11206
  argv,
10865
11207
  command,
10866
11208
  error: {
10867
- message: config.error.message,
11209
+ message: secretsResult.error,
10868
11210
  status: null
10869
11211
  },
10870
- exitCode: 4
11212
+ exitCode: 2
10871
11213
  });
10872
11214
  return;
10873
11215
  }
10874
- const client = new AnythingApiClient(config.value);
10875
- const startResult = await client.submitProject({
10876
- projectGroupId: argv.projectId,
10877
- store: argv.store
11216
+ if (argv["dry-run"]) {
11217
+ outputDryRun({
11218
+ argv,
11219
+ command,
11220
+ plannedActions: [{
11221
+ action: "update_auth_settings",
11222
+ projectGroupId,
11223
+ provider: argv.provider,
11224
+ enabled: argv.enabled,
11225
+ environment: secretEnvironment,
11226
+ secretKeys: secretsResult.value.map((secret) => secret.envKey)
11227
+ }]
11228
+ });
11229
+ return;
11230
+ }
11231
+ const config = resolveConfig({
11232
+ dev: argv.dev,
11233
+ apiUrl: argv.apiUrl
10878
11234
  });
10879
- if (startResult.isErr()) {
11235
+ if (config.isErr()) {
10880
11236
  outputError({
10881
11237
  argv,
10882
11238
  command,
10883
- error: startResult.error
11239
+ error: {
11240
+ message: config.error.message,
11241
+ status: null
11242
+ },
11243
+ exitCode: 4
10884
11244
  });
10885
11245
  return;
10886
11246
  }
10887
- const finalResult = await waitForSubmission({
10888
- client,
10889
- projectGroupId: argv.projectId,
10890
- submission: startResult.value.submission
11247
+ const result = await new AnythingApiClient(config.value).updateProjectAuthSettings({
11248
+ projectGroupId,
11249
+ provider: argv.provider,
11250
+ enabled: argv.enabled,
11251
+ secrets: secretsResult.value,
11252
+ enableForAllModules: false
10891
11253
  });
10892
- if (finalResult.isErr()) {
11254
+ if (result.isErr()) {
10893
11255
  outputError({
10894
11256
  argv,
10895
11257
  command,
10896
- error: finalResult.error
11258
+ error: result.error
10897
11259
  });
10898
11260
  return;
10899
11261
  }
10900
- printSubmissionResult({
11262
+ if (outputSuccess({
10901
11263
  argv,
10902
11264
  command,
10903
- pendingMessage: "Submission started.",
10904
- submission: finalResult.value
10905
- });
11265
+ data: result.value
11266
+ })) return;
11267
+ printSuccess("Auth settings updated.");
11268
+ printAuthSettings(result.value);
10906
11269
  }
10907
11270
  };
11271
+ const authSettingsCommand = {
11272
+ command: "auth <command>",
11273
+ describe: "Manage auth-related project settings",
11274
+ builder: (yargs) => yargs.command(get).command(set).demandCommand(),
11275
+ handler: () => {}
11276
+ };
11277
+
11278
+ //#endregion
11279
+ //#region src/commands/settings.ts
11280
+ const settingsCommand = {
11281
+ command: "settings <command>",
11282
+ describe: "Inspect and update project settings",
11283
+ builder: (yargs) => yargs.command(authSettingsCommand).example("anything projects settings auth get <project-id>", "Inspect project auth settings").demandCommand(),
11284
+ handler: () => {}
11285
+ };
10908
11286
 
10909
11287
  //#endregion
10910
11288
  //#region src/commands/unpublish.ts
@@ -10922,6 +11300,17 @@ const unpublishCommand = {
10922
11300
  describe: "Confirm unpublishing without prompting"
10923
11301
  }).example("anything projects unpublish <project-id> --yes", "Remove a published app from production").example("anything projects unpublish <project-id> --json --yes", "Unpublish an app and capture the result in JSON"),
10924
11302
  handler: async (argv) => {
11303
+ if (argv["dry-run"]) {
11304
+ outputDryRun({
11305
+ argv,
11306
+ command: COMMAND$6,
11307
+ plannedActions: [{
11308
+ action: "unpublish_project",
11309
+ projectGroupId: argv.projectId
11310
+ }]
11311
+ });
11312
+ return;
11313
+ }
10925
11314
  if (!argv.yes && !isNonInteractive(argv)) {
10926
11315
  outputError({
10927
11316
  argv,
@@ -10934,17 +11323,6 @@ const unpublishCommand = {
10934
11323
  });
10935
11324
  return;
10936
11325
  }
10937
- if (argv["dry-run"]) {
10938
- outputDryRun({
10939
- argv,
10940
- command: COMMAND$6,
10941
- plannedActions: [{
10942
- action: "unpublish_project",
10943
- projectGroupId: argv.projectId
10944
- }]
10945
- });
10946
- return;
10947
- }
10948
11326
  const config = resolveConfig({
10949
11327
  dev: argv.dev,
10950
11328
  apiUrl: argv.apiUrl
@@ -11002,6 +11380,17 @@ const deleteProjectCommand = {
11002
11380
  describe: "Confirm deletion without prompting"
11003
11381
  }).example("anything projects delete <project-id> --yes", "Delete a project permanently").example("anything projects delete <project-id> --yes --json", "Delete a project and capture the result in JSON"),
11004
11382
  handler: async (argv) => {
11383
+ if (argv["dry-run"]) {
11384
+ outputDryRun({
11385
+ argv,
11386
+ command: COMMAND$5,
11387
+ plannedActions: [{
11388
+ action: "delete_project",
11389
+ projectGroupId: argv.projectId
11390
+ }]
11391
+ });
11392
+ return;
11393
+ }
11005
11394
  if (!argv.yes && !isNonInteractive(argv)) {
11006
11395
  outputError({
11007
11396
  argv,
@@ -11014,17 +11403,6 @@ const deleteProjectCommand = {
11014
11403
  });
11015
11404
  return;
11016
11405
  }
11017
- if (argv["dry-run"]) {
11018
- outputDryRun({
11019
- argv,
11020
- command: COMMAND$5,
11021
- plannedActions: [{
11022
- action: "delete_project",
11023
- projectGroupId: argv.projectId
11024
- }]
11025
- });
11026
- return;
11027
- }
11028
11406
  const config = resolveConfig({
11029
11407
  dev: argv.dev,
11030
11408
  apiUrl: argv.apiUrl
@@ -11044,10 +11422,12 @@ const deleteProjectCommand = {
11044
11422
  const startTime = performance.now();
11045
11423
  const result = await new AnythingApiClient(config.value).deleteProject({ projectGroupId: argv.projectId });
11046
11424
  if (result.isErr()) {
11425
+ const hint = result.error.status === 404 ? "If the project ID is correct, project deletion may not be enabled for your account." : void 0;
11047
11426
  outputError({
11048
11427
  argv,
11049
11428
  command: COMMAND$5,
11050
- error: result.error
11429
+ error: result.error,
11430
+ hint
11051
11431
  });
11052
11432
  return;
11053
11433
  }
@@ -11153,11 +11533,16 @@ const projectsSetCommand = {
11153
11533
  return;
11154
11534
  }
11155
11535
  setStoredProjectGroupId(result.value.id);
11156
- if (argv.json) printJson({
11157
- projectId: result.value.id,
11158
- name: result.value.name
11159
- });
11160
- else printSuccess(`Current project set to ${result.value.name} (${result.value.id})`);
11536
+ if (outputSuccess({
11537
+ argv,
11538
+ command: "projects set",
11539
+ data: {
11540
+ projectId: result.value.id,
11541
+ name: result.value.name
11542
+ },
11543
+ primaryId: result.value.id
11544
+ })) return;
11545
+ printSuccess(`Current project set to ${result.value.name} (${result.value.id})`);
11161
11546
  }
11162
11547
  };
11163
11548
  const projectsUnsetCommand = {
@@ -11166,8 +11551,12 @@ const projectsUnsetCommand = {
11166
11551
  builder: (yargs) => yargs.example("anything projects unset", "Clear the saved project"),
11167
11552
  handler: (argv) => {
11168
11553
  clearStoredProjectGroupId();
11169
- if (argv.json) printJson({ projectId: null });
11170
- else printSuccess("Current project cleared.");
11554
+ if (outputSuccess({
11555
+ argv,
11556
+ command: "projects unset",
11557
+ data: { projectId: null }
11558
+ })) return;
11559
+ printSuccess("Current project cleared.");
11171
11560
  }
11172
11561
  };
11173
11562
  const projectsCommand = {
@@ -11181,7 +11570,8 @@ const projectsCommand = {
11181
11570
  //#region src/commands/pull.ts
11182
11571
  const ANYTHING_DIR = ".anything";
11183
11572
  const FILES_DIR = "files";
11184
- async function pullFiles({ client, projectGroupId }) {
11573
+ const DEFAULT_TIMEOUT_SECONDS = 60;
11574
+ async function pullFiles({ client, projectGroupId, argv }) {
11185
11575
  const listResult = await client.listProjectFiles({ projectGroupId });
11186
11576
  if (listResult.isErr()) {
11187
11577
  printApiError(listResult.error);
@@ -11190,6 +11580,7 @@ async function pullFiles({ client, projectGroupId }) {
11190
11580
  }
11191
11581
  const filesDir = join(process.cwd(), ANYTHING_DIR, FILES_DIR);
11192
11582
  if (!existsSync(filesDir)) mkdirSync(filesDir, { recursive: true });
11583
+ const totalFiles = listResult.value.files.length;
11193
11584
  const pulled = [];
11194
11585
  for (const file of listResult.value.files) {
11195
11586
  const fileResult = await client.getProjectFile({
@@ -11205,6 +11596,12 @@ async function pullFiles({ client, projectGroupId }) {
11205
11596
  if (!existsSync(destDir)) mkdirSync(destDir, { recursive: true });
11206
11597
  writeFileSync(destPath, fileResult.value.file.content);
11207
11598
  pulled.push(file.path);
11599
+ if (argv.json) printNdjson({
11600
+ type: "progress",
11601
+ file: file.path,
11602
+ completed: pulled.length,
11603
+ total: totalFiles
11604
+ });
11208
11605
  }
11209
11606
  return pulled;
11210
11607
  }
@@ -11231,8 +11628,13 @@ const pullCommand = {
11231
11628
  }).option("env", {
11232
11629
  type: "boolean",
11233
11630
  describe: "Write secret names to .env.example only"
11631
+ }).option("timeout", {
11632
+ type: "number",
11633
+ default: DEFAULT_TIMEOUT_SECONDS,
11634
+ describe: "Request timeout in seconds"
11234
11635
  }).example("anything pull", "Pull files + secret names").example("anything pull --files", "Pull project files only").example("anything pull --env", "Write secret names to .env.example"),
11235
11636
  handler: async (argv) => {
11637
+ const startTime = performance.now();
11236
11638
  const linked = readLinkedProject();
11237
11639
  if (!linked) {
11238
11640
  printError("No linked project. Run `anything link <project-id>` first.");
@@ -11248,7 +11650,8 @@ const pullCommand = {
11248
11650
  process.exitCode = 1;
11249
11651
  return;
11250
11652
  }
11251
- const client = new AnythingApiClient(config.value);
11653
+ const timeoutSeconds = argv.timeout ?? DEFAULT_TIMEOUT_SECONDS;
11654
+ const client = new AnythingApiClient(config.value, { timeoutMs: timeoutSeconds * 1e3 });
11252
11655
  const pullBoth = !argv.files && !argv.env;
11253
11656
  const projectResult = await client.getProject({ projectGroupId: linked.projectId });
11254
11657
  if (projectResult.isErr()) {
@@ -11261,7 +11664,8 @@ const pullCommand = {
11261
11664
  if (pullBoth || argv.files) {
11262
11665
  pulledFiles = await pullFiles({
11263
11666
  client,
11264
- projectGroupId: linked.projectId
11667
+ projectGroupId: linked.projectId,
11668
+ argv
11265
11669
  });
11266
11670
  if (pulledFiles === null) return;
11267
11671
  }
@@ -11276,6 +11680,7 @@ const pullCommand = {
11276
11680
  if (outputSuccess({
11277
11681
  argv,
11278
11682
  command: "pull",
11683
+ startTime,
11279
11684
  data: {
11280
11685
  projectId: linked.projectId,
11281
11686
  projectName: projectResult.value.name,
@@ -11296,9 +11701,9 @@ const pullCommand = {
11296
11701
  //#region src/commands/ship.ts
11297
11702
  const COMMAND$4 = "ship";
11298
11703
  const shipCommand = {
11299
- command: "ship",
11704
+ command: "ship [prompt]",
11300
11705
  describe: "Create or update + generate + publish in one shot",
11301
- builder: (yargs) => yargs.option("prompt", {
11706
+ builder: (yargs) => yargs.positional("prompt", {
11302
11707
  type: "string",
11303
11708
  describe: "What to build or change. Reads stdin if omitted."
11304
11709
  }).option("project", {
@@ -11321,7 +11726,7 @@ const shipCommand = {
11321
11726
  type: "boolean",
11322
11727
  default: true,
11323
11728
  describe: "Wait for deployment to complete (use --no-wait to skip)"
11324
- }).example("anything ship --prompt \"Build a todo app with auth\"", "Create, generate, and publish a new app").example("anything ship --project pg_123 --prompt \"Add a settings page\"", "Iterate on existing app and publish").example("anything ship --project pg_123 --skip-publish --prompt \"Add dark mode\"", "Generate changes without publishing").example("anything ship --no-wait --prompt \"Build a landing page\"", "Ship without waiting for deployment"),
11729
+ }).example("anything ship \"Build a markdown viewer to review blog posts\"", "Create, generate, and publish a new app").example("anything ship --project pg_123 \"Add a settings page\"", "Iterate on existing app and publish").example("anything ship --project pg_123 --skip-publish \"Add dark mode\"", "Generate changes without publishing").example("anything ship --no-wait \"Build a landing page\"", "Ship without waiting for deployment"),
11325
11730
  handler: async (argv) => {
11326
11731
  const promptResult = await resolveTextInput({
11327
11732
  value: argv.prompt ?? null,
@@ -11575,13 +11980,14 @@ const skillCommand = {
11575
11980
  }),
11576
11981
  handler: async (argv) => {
11577
11982
  if (argv.path) {
11578
- if (argv.json) {
11579
- printJson({
11983
+ if (outputSuccess({
11984
+ argv,
11985
+ command: "skill",
11986
+ data: {
11580
11987
  name: "anything-cli",
11581
11988
  path: skillRootPath
11582
- });
11583
- return;
11584
- }
11989
+ }
11990
+ })) return;
11585
11991
  console.log(skillRootPath);
11586
11992
  return;
11587
11993
  }
@@ -11594,15 +12000,16 @@ const skillCommand = {
11594
12000
  process.exitCode = 1;
11595
12001
  return;
11596
12002
  }
11597
- if (argv.json) {
11598
- printJson({
12003
+ if (outputSuccess({
12004
+ argv,
12005
+ command: "skill",
12006
+ data: {
11599
12007
  name: "anything-cli",
11600
12008
  path: skillRootPath,
11601
12009
  file: relativePath,
11602
12010
  content
11603
- });
11604
- return;
11605
- }
12011
+ }
12012
+ })) return;
11606
12013
  process.stdout.write(content);
11607
12014
  }
11608
12015
  };
@@ -12090,6 +12497,11 @@ const userCommand = {
12090
12497
  command: "user",
12091
12498
  aliases: ["whoami"],
12092
12499
  describe: "Show current user and organization context",
12500
+ builder: (yargs) => yargs.option("brief", {
12501
+ type: "boolean",
12502
+ default: false,
12503
+ describe: "Show only identity essentials (id, name, email, active org)"
12504
+ }).example("anything user --brief", "Show just the current identity").example("anything whoami --brief --json", "Identity essentials as JSON"),
12093
12505
  handler: async (argv) => {
12094
12506
  const config = resolveConfig({
12095
12507
  dev: argv.dev,
@@ -12128,10 +12540,29 @@ const userCommand = {
12128
12540
  defaultOrganizationHasCredits: defaultOrganization !== null ? BigInt(defaultOrganization.creditBalance) > 0n : null
12129
12541
  };
12130
12542
  const activeOrg = activeOrgId ? result.value.organizations.find((o) => o.id === activeOrgId) ?? null : null;
12131
- if (argv.quiet) {
12543
+ if (argv.quiet && !argv.json) {
12132
12544
  console.log(result.value.id);
12133
12545
  return;
12134
12546
  }
12547
+ if (argv.brief) {
12548
+ const briefData = {
12549
+ id: result.value.id,
12550
+ name: result.value.name ?? null,
12551
+ email: result.value.email ?? null,
12552
+ activeOrgId: activeOrgId ?? null,
12553
+ activeOrgName: activeOrg?.name ?? null
12554
+ };
12555
+ if (outputSuccess({
12556
+ argv,
12557
+ command: COMMAND,
12558
+ data: briefData
12559
+ })) return;
12560
+ printLabel("Name", briefData.name);
12561
+ printLabel("Email", briefData.email);
12562
+ printLabel("User ID", briefData.id);
12563
+ printLabel("Active Org", activeOrg ? `${activeOrg.name} (${activeOrgId})` : null);
12564
+ return;
12565
+ }
12135
12566
  if (outputSuccess({
12136
12567
  argv,
12137
12568
  command: COMMAND,