apiblaze 0.1.17 → 0.1.19

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.
Files changed (2) hide show
  1. package/dist/index.js +297 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -51,6 +51,14 @@ function saveCredentials(creds) {
51
51
  fs.writeFileSync(CREDENTIALS_PATH, JSON.stringify(creds, null, 2), "utf-8");
52
52
  fs.chmodSync(CREDENTIALS_PATH, 384);
53
53
  }
54
+ function clearCredentials() {
55
+ try {
56
+ fs.unlinkSync(CREDENTIALS_PATH);
57
+ return true;
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
54
62
  function loadCredentials() {
55
63
  try {
56
64
  const raw = fs.readFileSync(CREDENTIALS_PATH, "utf-8");
@@ -85,13 +93,32 @@ var init_auth = __esm({
85
93
  var api_exports = {};
86
94
  __export(api_exports, {
87
95
  checkProxyName: () => checkProxyName,
96
+ claimProxy: () => claimProxy,
88
97
  createProxy: () => createProxy,
98
+ createProxyAnonymous: () => createProxyAnonymous,
89
99
  deleteDevTunnel: () => deleteDevTunnel,
90
100
  getLocalhostTargets: () => getLocalhostTargets,
91
101
  getProjects: () => getProjects,
92
102
  getTeams: () => getTeams,
93
103
  putDevTunnel: () => putDevTunnel
94
104
  });
105
+ async function createProxyAnonymous(body) {
106
+ const res = await fetch(`${PUBLIC_API_BASE}/proxy`, {
107
+ method: "POST",
108
+ headers: { "Content-Type": "application/json" },
109
+ body: JSON.stringify(body)
110
+ });
111
+ if (!res.ok) {
112
+ let message = res.statusText;
113
+ try {
114
+ const b = await res.json();
115
+ message = b.details ?? b.error ?? message;
116
+ } catch {
117
+ }
118
+ throw new ApiError(res.status, message);
119
+ }
120
+ return res.json();
121
+ }
95
122
  async function apiFetch(path2, options = {}) {
96
123
  const token = getAccessToken();
97
124
  const url = `${DASHBOARD_BASE}${path2}`;
@@ -141,6 +168,12 @@ async function createProxy(payload) {
141
168
  body: JSON.stringify(payload)
142
169
  });
143
170
  }
171
+ async function claimProxy(claimCode, teamId) {
172
+ return apiFetch("/api/cli/claim", {
173
+ method: "POST",
174
+ body: JSON.stringify({ claimCode, team_id: teamId })
175
+ });
176
+ }
144
177
  async function putDevTunnel(payload) {
145
178
  return apiFetch("/api/cli/dev-tunnel", {
146
179
  method: "PUT",
@@ -153,22 +186,23 @@ async function deleteDevTunnel(restore) {
153
186
  body: JSON.stringify({ restore })
154
187
  });
155
188
  }
156
- var DASHBOARD_BASE;
189
+ var DASHBOARD_BASE, PUBLIC_API_BASE;
157
190
  var init_api = __esm({
158
191
  "src/lib/api.ts"() {
159
192
  "use strict";
160
193
  init_auth();
161
194
  init_types();
162
195
  DASHBOARD_BASE = "https://dashboard.apiblaze.com";
196
+ PUBLIC_API_BASE = "https://api.apiblaze.com";
163
197
  }
164
198
  });
165
199
 
166
200
  // src/index.ts
167
201
  var import_commander = require("commander");
168
- var import_chalk7 = __toESM(require("chalk"));
202
+ var import_chalk9 = __toESM(require("chalk"));
169
203
 
170
204
  // package.json
171
- var version = "0.1.17";
205
+ var version = "0.1.19";
172
206
 
173
207
  // src/index.ts
174
208
  init_types();
@@ -649,6 +683,7 @@ ${projects.length} project${projects.length === 1 ? "" : "s"}`));
649
683
  }
650
684
 
651
685
  // src/commands/create.ts
686
+ var import_fs = __toESM(require("fs"));
652
687
  var import_chalk5 = __toESM(require("chalk"));
653
688
  var import_ora4 = __toESM(require("ora"));
654
689
  init_auth();
@@ -684,7 +719,8 @@ var VALID_AUTH = ["api_key", "none", "oauth"];
684
719
  async function runCreate(opts = {}) {
685
720
  const creds = loadCredentials();
686
721
  if (!creds) {
687
- fail("Not logged in. Run `apiblaze login` first.");
722
+ await runAnonymousCreate(opts);
723
+ return;
688
724
  }
689
725
  const interactive = !!process.stdin.isTTY && !opts.json;
690
726
  const auth = (opts.auth ?? "api_key").toLowerCase();
@@ -826,8 +862,123 @@ async function runCreate(opts = {}) {
826
862
  }
827
863
  console.log();
828
864
  }
865
+ async function runAnonymousCreate(opts) {
866
+ const interactive = !!process.stdin.isTTY && !opts.json;
867
+ if (!opts.json) {
868
+ console.log(import_chalk5.default.bold("\nCreate an API proxy"));
869
+ console.log(import_chalk5.default.dim("Not logged in \u2014 creating an anonymous proxy. You can claim it to your account within 30 days.\n"));
870
+ }
871
+ let body = {};
872
+ if (opts.config) {
873
+ let raw = "";
874
+ try {
875
+ raw = import_fs.default.readFileSync(opts.config, "utf8");
876
+ } catch {
877
+ fail(`Cannot read --config file: ${opts.config}`);
878
+ }
879
+ let parsed;
880
+ try {
881
+ parsed = JSON.parse(raw);
882
+ } catch {
883
+ fail(`--config is not valid JSON: ${opts.config}`);
884
+ }
885
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) fail("--config must be a JSON object.");
886
+ body = parsed;
887
+ }
888
+ let name = opts.name !== void 0 ? normalizeName(opts.name) : typeof body.name === "string" ? body.name : void 0;
889
+ if (opts.name !== void 0 && name.length < 3) {
890
+ fail("Proxy name must be at least 3 characters (letters and digits only).");
891
+ }
892
+ if (opts.target && !isHttpUrl(opts.target)) fail("--target must be a valid http(s) URL.");
893
+ let target = opts.target?.trim() || (typeof body.target === "string" ? body.target : "") || (typeof body.target_url === "string" ? body.target_url : "");
894
+ const hasOtherSource = !!(body.openapi || body.github);
895
+ if (!target && !hasOtherSource) {
896
+ if (interactive) {
897
+ const { default: inquirer2 } = await import("inquirer");
898
+ if (name === void 0) {
899
+ const { rawName } = await inquirer2.prompt([{
900
+ type: "input",
901
+ name: "rawName",
902
+ message: "Proxy name (leave blank to auto-generate):",
903
+ transformer: (v) => normalizeName(v)
904
+ }]);
905
+ const n = normalizeName(rawName);
906
+ name = n.length >= 3 ? n : void 0;
907
+ }
908
+ for (; ; ) {
909
+ const { url } = await inquirer2.prompt([{
910
+ type: "input",
911
+ name: "url",
912
+ message: "Target URL to forward requests to (e.g. https://httpbin.org):"
913
+ }]);
914
+ if (!isHttpUrl(url)) {
915
+ console.log(import_chalk5.default.yellow(" Enter a valid http(s) URL.\n"));
916
+ continue;
917
+ }
918
+ target = url.trim();
919
+ break;
920
+ }
921
+ } else {
922
+ fail("A source is required: pass --target, or target/openapi/github in --config.");
923
+ }
924
+ }
925
+ if (target) {
926
+ body.target = target;
927
+ body.target_url = target;
928
+ }
929
+ if (name) {
930
+ body.name = name;
931
+ body.subdomain = name;
932
+ }
933
+ if (opts.subdomain) body.subdomain = normalizeName(opts.subdomain);
934
+ if (opts.tenant) body.tenant = normalizeName(opts.tenant);
935
+ if (opts.product) body.product_slug = normalizeName(opts.product);
936
+ if (opts.displayName) body.display_name = opts.displayName;
937
+ if (opts.auth && opts.auth !== "api_key") body.auth_type = opts.auth;
938
+ const spinner = !opts.json ? (0, import_ora4.default)("Creating proxy...").start() : null;
939
+ let result;
940
+ try {
941
+ result = await createProxyAnonymous(body);
942
+ spinner?.succeed(import_chalk5.default.green("Proxy created!"));
943
+ } catch (err) {
944
+ spinner?.fail("Failed to create proxy.");
945
+ throw err;
946
+ }
947
+ const version2 = result.api_version || "1.0.0";
948
+ const keys = result.api_keys ?? {};
949
+ const apiKey = result.apiKey ?? keys.prod ?? Object.values(keys)[0];
950
+ const prodEndpoint = (result.endpoints || []).find((e) => e.endsWith("/prod")) || (result.endpoints || [])[0];
951
+ if (opts.json) {
952
+ process.stdout.write(JSON.stringify({
953
+ project_id: result.project_id,
954
+ api_version: version2,
955
+ endpoints: result.endpoints,
956
+ api_key: apiKey,
957
+ api_keys: keys,
958
+ claim_url: result.claim_url,
959
+ anonymous: true
960
+ }) + "\n");
961
+ return;
962
+ }
963
+ console.log();
964
+ if (prodEndpoint) console.log(` ${import_chalk5.default.dim("Proxy URL: ")} ${import_chalk5.default.bold(prodEndpoint)}`);
965
+ if (result.portal) console.log(` ${import_chalk5.default.dim("Dev portal:")} ${import_chalk5.default.bold(result.portal)}`);
966
+ if (apiKey) {
967
+ console.log();
968
+ console.log(` ${import_chalk5.default.dim("API key:")}`);
969
+ console.log(` ${import_chalk5.default.bold.green(apiKey)}`);
970
+ console.log(import_chalk5.default.dim("\n Save this now \u2014 send it as the X-API-Key header. It may not be shown again."));
971
+ }
972
+ if (result.claim_url) {
973
+ console.log();
974
+ console.log(` ${import_chalk5.default.yellow("\u26A0 Anonymous proxy \u2014 claim it to your account within 30 days or it expires:")}`);
975
+ console.log(` ${import_chalk5.default.bold(result.claim_url)}`);
976
+ console.log(import_chalk5.default.dim(" (or run `apiblaze login`, then re-run `apiblaze create` to create it under your account)"));
977
+ }
978
+ console.log();
979
+ }
829
980
 
830
- // src/commands/team.ts
981
+ // src/commands/claim.ts
831
982
  var import_chalk6 = __toESM(require("chalk"));
832
983
  init_auth();
833
984
  init_api();
@@ -835,26 +986,112 @@ function fail2(message) {
835
986
  console.error(import_chalk6.default.red(`Error: ${message}`));
836
987
  process.exit(1);
837
988
  }
838
- async function runTeam(arg) {
989
+ async function runClaim(claimCodeArg, opts) {
839
990
  const creds = loadCredentials();
840
991
  if (!creds) {
841
- fail2("Not logged in. Run `apiblaze login` first.");
992
+ fail2("Not logged in. Run `apiblaze login` first, then claim.");
993
+ }
994
+ let claimCode = (claimCodeArg ?? "").trim();
995
+ if (!claimCode && process.stdin.isTTY) {
996
+ const { default: inquirer2 } = await import("inquirer");
997
+ const { code } = await inquirer2.prompt([{
998
+ type: "input",
999
+ name: "code",
1000
+ message: "Claim code (from the create output / claim URL):"
1001
+ }]);
1002
+ claimCode = (code ?? "").trim();
1003
+ }
1004
+ if (!claimCode) {
1005
+ fail2("A claim code is required: `apiblaze claim <code>`.");
842
1006
  }
843
1007
  const teams = await getTeams().catch(() => []);
844
1008
  if (teams.length === 0) {
845
1009
  fail2("No teams found for your account.");
846
1010
  }
1011
+ let teamId;
1012
+ if (opts.team) {
1013
+ const match = teams.find(
1014
+ (t) => t.teamId === opts.team || t.name.toLowerCase() === opts.team.toLowerCase()
1015
+ );
1016
+ if (!match) {
1017
+ fail2(`Team "${opts.team}" not found. Available: ${teams.map((t) => t.name).join(", ")}.`);
1018
+ }
1019
+ teamId = match.teamId;
1020
+ } else if (creds.teamId && teams.some((t) => t.teamId === creds.teamId)) {
1021
+ teamId = creds.teamId;
1022
+ } else if (teams.length === 1) {
1023
+ teamId = teams[0].teamId;
1024
+ } else if (process.stdin.isTTY) {
1025
+ const { default: inquirer2 } = await import("inquirer");
1026
+ const { picked } = await inquirer2.prompt([{
1027
+ type: "list",
1028
+ name: "picked",
1029
+ message: "Claim into which team?",
1030
+ choices: teams.map((t) => ({ name: t.name, value: t.teamId }))
1031
+ }]);
1032
+ teamId = picked;
1033
+ } else {
1034
+ fail2(`Specify a team: \`apiblaze claim ${claimCode} --team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
1035
+ }
1036
+ const teamName = teams.find((t) => t.teamId === teamId)?.name ?? teamId;
1037
+ const result = await claimProxy(claimCode, teamId);
1038
+ if (opts.json) {
1039
+ console.log(JSON.stringify(result, null, 2));
1040
+ return;
1041
+ }
1042
+ console.log(import_chalk6.default.green(`
1043
+ \u2714 Claimed into ${import_chalk6.default.bold(teamName)}.`));
1044
+ if (result.project_id) console.log(` Project: ${import_chalk6.default.bold(result.project_id)}`);
1045
+ if (result.endpoints?.length) {
1046
+ console.log(" Endpoints:");
1047
+ for (const e of result.endpoints) console.log(` ${e}`);
1048
+ }
1049
+ console.log(`
1050
+ Manage it at ${import_chalk6.default.cyan("https://dashboard.apiblaze.com/dashboard")}`);
1051
+ }
1052
+
1053
+ // src/commands/logout.ts
1054
+ var import_chalk7 = __toESM(require("chalk"));
1055
+ init_auth();
1056
+ async function runLogout() {
1057
+ const creds = loadCredentials();
1058
+ const removed = clearCredentials();
1059
+ if (!removed) {
1060
+ console.log(import_chalk7.default.yellow("You were not logged in."));
1061
+ return;
1062
+ }
1063
+ const who = creds?.githubHandle ?? creds?.email;
1064
+ console.log(import_chalk7.default.green(`\u2714 Logged out${who ? ` (${who})` : ""}.`));
1065
+ }
1066
+
1067
+ // src/commands/team.ts
1068
+ var import_chalk8 = __toESM(require("chalk"));
1069
+ init_auth();
1070
+ init_api();
1071
+ function fail3(message) {
1072
+ console.error(import_chalk8.default.red(`Error: ${message}`));
1073
+ process.exit(1);
1074
+ }
1075
+ async function runTeam(arg) {
1076
+ const creds = loadCredentials();
1077
+ if (!creds) {
1078
+ fail3("Not logged in. Run `apiblaze login` first.");
1079
+ }
1080
+ const teams = await getTeams().catch(() => []);
1081
+ if (teams.length === 0) {
1082
+ fail3("No teams found for your account.");
1083
+ }
847
1084
  let chosen;
848
1085
  if (arg) {
849
1086
  chosen = teams.find(
850
1087
  (t) => t.teamId === arg || t.name.toLowerCase() === arg.toLowerCase()
851
1088
  );
852
1089
  if (!chosen) {
853
- fail2(`Team "${arg}" not found. Available: ${teams.map((t) => t.name).join(", ")}.`);
1090
+ fail3(`Team "${arg}" not found. Available: ${teams.map((t) => t.name).join(", ")}.`);
854
1091
  }
855
1092
  } else if (teams.length === 1) {
856
1093
  chosen = teams[0];
857
- console.log(`${import_chalk6.default.cyan("\u2192")} You only have one team: ${import_chalk6.default.bold(chosen.name)}`);
1094
+ console.log(`${import_chalk8.default.cyan("\u2192")} You only have one team: ${import_chalk8.default.bold(chosen.name)}`);
858
1095
  } else if (process.stdin.isTTY) {
859
1096
  const { default: inquirer2 } = await import("inquirer");
860
1097
  const { picked } = await inquirer2.prompt([{
@@ -866,16 +1103,16 @@ async function runTeam(arg) {
866
1103
  }]);
867
1104
  chosen = teams.find((t) => t.teamId === picked);
868
1105
  } else {
869
- fail2(`Specify a team: \`apiblaze team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
1106
+ fail3(`Specify a team: \`apiblaze team <name|id>\`. Available: ${teams.map((t) => t.name).join(", ")}.`);
870
1107
  }
871
1108
  saveCredentials({ ...creds, teamId: chosen.teamId, teamName: chosen.name });
872
- console.log(import_chalk6.default.green(`
873
- \u2714 Active team: ${import_chalk6.default.bold(chosen.name)}`));
1109
+ console.log(import_chalk8.default.green(`
1110
+ \u2714 Active team: ${import_chalk8.default.bold(chosen.name)}`));
874
1111
  }
875
1112
 
876
1113
  // src/index.ts
877
1114
  var program = new import_commander.Command();
878
- program.name("apiblaze").description("APIblaze dev tunnel CLI").version(version);
1115
+ program.name("apiblaze").description("APIblaze CLI \u2014 create & manage API proxies and run dev tunnels").version(version);
879
1116
  program.command("login").description("Authenticate with APIblaze").action(async () => {
880
1117
  try {
881
1118
  await runLogin();
@@ -884,7 +1121,7 @@ program.command("login").description("Authenticate with APIblaze").action(async
884
1121
  process.exit(1);
885
1122
  }
886
1123
  });
887
- program.command("create").description("Create a new API proxy").option("--name <name>", "Proxy name (becomes <name>.apiblaze.com)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
1124
+ program.command("create").description("Create a new API proxy (no login needed \u2014 without auth it creates an anonymous proxy and prints a claim URL)").option("--name <name>", "Proxy name (becomes <name>.apiblaze.com)").option("--target <url>", "Target URL to forward requests to").option("--team <id|name>", "Team to create under (defaults to your active team)").option("--auth <type>", "Auth type: api_key | none | oauth", "api_key").option("--tenant <slug>", "Tenant slug (anonymous create; generated if omitted)").option("--product <slug>", "Product slug (anonymous create; defaults to the proxy name)").option("--display-name <name>", "Human-friendly display name").option("--subdomain <slug>", "Explicit subdomain (defaults to --name)").option("--config <file>", "JSON file with the full request body (anonymous create): requests_auth, login providers + client/server token types, scopes, callback URLs, etc. See apiblaze_anonymous.yaml. Flags override its fields.").option("-y, --yes", "Skip the confirmation prompt").option("--json", "Output machine-readable JSON (non-interactive)").action(async (opts) => {
888
1125
  try {
889
1126
  await runCreate(opts);
890
1127
  } catch (err) {
@@ -892,6 +1129,22 @@ program.command("create").description("Create a new API proxy").option("--name <
892
1129
  process.exit(1);
893
1130
  }
894
1131
  });
1132
+ program.command("logout").description("Sign out \u2014 remove stored credentials from this machine").action(async () => {
1133
+ try {
1134
+ await runLogout();
1135
+ } catch (err) {
1136
+ printError(err);
1137
+ process.exit(1);
1138
+ }
1139
+ });
1140
+ program.command("claim").description("Claim an anonymously-created proxy into one of your teams (requires login)").argument("[code]", "Claim code from `create` output / claim URL (prompted if omitted)").option("--team <id|name>", "Team to claim into (defaults to your active team)").option("--json", "Output machine-readable JSON (non-interactive)").action(async (code, opts) => {
1141
+ try {
1142
+ await runClaim(code, opts);
1143
+ } catch (err) {
1144
+ printError(err);
1145
+ process.exit(1);
1146
+ }
1147
+ });
895
1148
  program.command("team").description("Switch the active team").argument("[team]", "Team name or id to switch to (omit to choose interactively)").action(async (team) => {
896
1149
  try {
897
1150
  await runTeam(team);
@@ -912,7 +1165,7 @@ program.command("dev").description("Start a dev tunnel for your localhost projec
912
1165
  try {
913
1166
  const resolved = parseInt(port ?? opts.port, 10);
914
1167
  if (Number.isNaN(resolved)) {
915
- console.error(import_chalk7.default.red(`Invalid port: ${port ?? opts.port}`));
1168
+ console.error(import_chalk9.default.red(`Invalid port: ${port ?? opts.port}`));
916
1169
  process.exit(1);
917
1170
  }
918
1171
  await runDev({ port: resolved });
@@ -921,15 +1174,41 @@ program.command("dev").description("Start a dev tunnel for your localhost projec
921
1174
  process.exit(1);
922
1175
  }
923
1176
  });
1177
+ program.addHelpText(
1178
+ "after",
1179
+ `
1180
+ Examples:
1181
+ # No account needed \u2014 create an anonymous proxy, get a claim URL:
1182
+ $ npx apiblaze create --target https://api.example.com
1183
+
1184
+ # Non-interactive (CI / scripts):
1185
+ $ npx apiblaze create --target https://api.example.com --name myapi --json
1186
+
1187
+ # Claim that anonymous proxy into your team (after signing in):
1188
+ $ npx apiblaze login
1189
+ $ npx apiblaze claim G7QN-62JB-VN3D-RQNR-F6EZ
1190
+ $ npx apiblaze claim G7QN-62JB-VN3D-RQNR-F6EZ --team my-team
1191
+
1192
+ # Sign in, then create under your team:
1193
+ $ npx apiblaze login
1194
+ $ npx apiblaze create --name myapi --target https://api.example.com --auth api_key
1195
+
1196
+ # Manage:
1197
+ $ npx apiblaze projects
1198
+ $ npx apiblaze team
1199
+ $ npx apiblaze dev 3000
1200
+ $ npx apiblaze logout
1201
+ `
1202
+ );
924
1203
  function printError(err) {
925
1204
  if (err instanceof ApiError) {
926
- console.error(import_chalk7.default.red(`
1205
+ console.error(import_chalk9.default.red(`
927
1206
  API error (${err.status}): ${err.message}`));
928
1207
  } else if (err instanceof Error) {
929
- console.error(import_chalk7.default.red(`
1208
+ console.error(import_chalk9.default.red(`
930
1209
  Error: ${err.message}`));
931
1210
  } else {
932
- console.error(import_chalk7.default.red("\nUnknown error"));
1211
+ console.error(import_chalk9.default.red("\nUnknown error"));
933
1212
  }
934
1213
  }
935
1214
  program.parse(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apiblaze",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "description": "Dev tunnel CLI for APIblaze — route localhost projects through your APIblaze endpoints",
5
5
  "keywords": [
6
6
  "apiblaze",