@agentlink.sh/cli 0.10.1 → 0.10.2

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.
@@ -181,7 +181,7 @@ async function ensureAccessToken(nonInteractive, projectDir) {
181
181
  }
182
182
  if (creds.oauth.refresh_token) {
183
183
  try {
184
- const { refreshOAuthToken } = await import("./oauth-J4X62CY4.js");
184
+ const { refreshOAuthToken } = await import("./oauth-VTSNCG7U.js");
185
185
  const tokens = await refreshOAuthToken(creds.oauth.refresh_token);
186
186
  const updated = {
187
187
  ...creds,
@@ -230,7 +230,7 @@ async function ensureAccessToken(nonInteractive, projectDir) {
230
230
  ]
231
231
  });
232
232
  if (method === "oauth") {
233
- const { oauthLogin } = await import("./oauth-J4X62CY4.js");
233
+ const { oauthLogin } = await import("./oauth-VTSNCG7U.js");
234
234
  const tokens = await oauthLogin();
235
235
  process.env.SUPABASE_ACCESS_TOKEN = tokens.access_token;
236
236
  saveCredentials({
@@ -20,7 +20,7 @@ import {
20
20
  updateAuthConfig,
21
21
  updatePostgrestConfig,
22
22
  waitForProjectReady
23
- } from "./chunk-DOWH47I5.js";
23
+ } from "./chunk-2CNK2BQY.js";
24
24
  import "./chunk-G3HVUCWY.js";
25
25
  export {
26
26
  createProject,
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ import {
26
26
  updatePostgrestConfig,
27
27
  validateProjectName,
28
28
  waitForProjectReady
29
- } from "./chunk-DOWH47I5.js";
29
+ } from "./chunk-2CNK2BQY.js";
30
30
  import {
31
31
  SKILLS_VERSION,
32
32
  SUPPORTED_SUPABASE_CLI,
@@ -2361,7 +2361,7 @@ async function connectExistingProject() {
2361
2361
  };
2362
2362
  }
2363
2363
  async function createNewProject(envName, opts) {
2364
- const { listOrganizations: listOrganizations2, listRegions: listRegions2 } = await import("./cloud-N7CILHLD.js");
2364
+ const { listOrganizations: listOrganizations2, listRegions: listRegions2 } = await import("./cloud-2AWCJAG5.js");
2365
2365
  const orgs = await listOrganizations2();
2366
2366
  if (orgs.length === 0) {
2367
2367
  throw new Error("No Supabase organizations found. Create one at https://supabase.com/dashboard.");
@@ -3664,12 +3664,14 @@ async function scaffold(options) {
3664
3664
  "pgdelta binary not found. Run `npm install` in the CLI package first."
3665
3665
  );
3666
3666
  }
3667
- if (checkCommand("psql")) {
3668
- spinner.text = "Checking prerequisites \u2014 PostgreSQL client installed";
3669
- } else {
3670
- throw new Error(
3671
- "psql is required but not installed.\n macOS: brew install libpq && brew link --force libpq\n Ubuntu: sudo apt install postgresql-client\n See: https://www.postgresql.org/download/"
3672
- );
3667
+ if (!options.cloud) {
3668
+ if (checkCommand("psql")) {
3669
+ spinner.text = "Checking prerequisites \u2014 PostgreSQL client installed";
3670
+ } else {
3671
+ throw new Error(
3672
+ "psql is required for local mode but not installed.\n macOS: brew install libpq && brew link --force libpq\n Ubuntu: sudo apt install postgresql-client\n Or remove --local to use Supabase Cloud (no psql needed).\n See: https://www.postgresql.org/download/"
3673
+ );
3674
+ }
3673
3675
  }
3674
3676
  });
3675
3677
  console.log();
@@ -4630,7 +4632,23 @@ Run without --resume to start fresh.`
4630
4632
  const isLoggedIn = authStatus.includes('"loggedIn": true') || authStatus.includes('"loggedIn":true');
4631
4633
  if (!isLoggedIn) {
4632
4634
  console.log(amber(" Claude Code requires authentication. Opening login..."));
4633
- execSync("claude auth login", { stdio: "inherit" });
4635
+ try {
4636
+ execSync("claude auth login", { stdio: "inherit" });
4637
+ } catch {
4638
+ throw new Error(
4639
+ `Claude Code login failed.
4640
+ Run ${dim("claude auth login")} manually, then retry with:
4641
+ ${dim("npx @agentlink.sh/cli@latest")}`
4642
+ );
4643
+ }
4644
+ const recheck = await runCommand("claude auth status").catch(() => "");
4645
+ if (!recheck.includes('"loggedIn": true') && !recheck.includes('"loggedIn":true')) {
4646
+ throw new Error(
4647
+ `Claude Code authentication did not complete.
4648
+ Run ${dim("claude auth login")} manually, then retry with:
4649
+ ${dim("npx @agentlink.sh/cli@latest")}`
4650
+ );
4651
+ }
4634
4652
  }
4635
4653
  const hasSupabase = fs15.existsSync(path15.join(process.cwd(), "supabase", "config.toml"));
4636
4654
  const hasPackageJson = fs15.existsSync(path15.join(process.cwd(), "package.json"));
@@ -4881,8 +4899,8 @@ program.command("info [name]").description("Show documentation about AgentLink c
4881
4899
  });
4882
4900
  program.command("login").description("Authenticate with Supabase via OAuth (opens browser)").action(async () => {
4883
4901
  try {
4884
- const { oauthLogin } = await import("./oauth-J4X62CY4.js");
4885
- const { saveCredentials: saveCredentials2, loadCredentials: loadCredentials2, credentialsPath: credentialsPath2 } = await import("./cloud-N7CILHLD.js");
4902
+ const { oauthLogin } = await import("./oauth-VTSNCG7U.js");
4903
+ const { saveCredentials: saveCredentials2, loadCredentials: loadCredentials2, credentialsPath: credentialsPath2 } = await import("./cloud-2AWCJAG5.js");
4886
4904
  console.log();
4887
4905
  const tokens = await oauthLogin();
4888
4906
  saveCredentials2({
@@ -4,6 +4,7 @@ import {
4
4
  OAUTH_CLIENT_SECRET,
5
5
  amber,
6
6
  blue,
7
+ bold,
7
8
  dim
8
9
  } from "./chunk-G3HVUCWY.js";
9
10
 
@@ -12,11 +13,21 @@ import { createHash, randomBytes } from "crypto";
12
13
  import { createServer } from "http";
13
14
  import { URL } from "url";
14
15
  import { execSync } from "child_process";
16
+ import { select } from "@inquirer/prompts";
17
+ var theme = {
18
+ prefix: { idle: blue("?"), done: blue("\u2714") },
19
+ style: {
20
+ answer: (text) => amber(text),
21
+ message: (text) => bold(text),
22
+ highlight: (text) => blue(text),
23
+ key: (text) => blue(bold(`<${text}>`))
24
+ }
25
+ };
15
26
  var AUTHORIZE_URL = "https://api.supabase.com/v1/oauth/authorize";
16
27
  var TOKEN_URL = "https://api.supabase.com/v1/oauth/token";
17
28
  var PORT_RANGE_START = 54320;
18
29
  var PORT_RANGE_END = 54330;
19
- var TIMEOUT_MS = 12e4;
30
+ var CHECK_IN_MS = 3e4;
20
31
  function generateCodeVerifier() {
21
32
  return randomBytes(64).toString("base64url");
22
33
  }
@@ -87,25 +98,15 @@ function openBrowser(url) {
87
98
  console.log(` ${dim(url)}`);
88
99
  }
89
100
  }
90
- async function oauthLogin() {
91
- const codeVerifier = generateCodeVerifier();
92
- const codeChallenge = generateCodeChallenge(codeVerifier);
93
- const state = randomBytes(16).toString("hex");
94
- const port = await findOpenPort();
95
- const redirectUri = `http://localhost:${port}/callback`;
96
- const authorizeUrl = new URL(AUTHORIZE_URL);
97
- authorizeUrl.searchParams.set("client_id", OAUTH_CLIENT_ID);
98
- authorizeUrl.searchParams.set("redirect_uri", redirectUri);
99
- authorizeUrl.searchParams.set("response_type", "code");
100
- authorizeUrl.searchParams.set("code_challenge", codeChallenge);
101
- authorizeUrl.searchParams.set("code_challenge_method", "S256");
102
- authorizeUrl.searchParams.set("state", state);
103
- const code = await new Promise((resolve, reject) => {
104
- const timeout = setTimeout(() => {
105
- server.close();
106
- reject(new Error("OAuth login timed out. Try again."));
107
- }, TIMEOUT_MS);
108
- const server = createServer((req, res) => {
101
+ function waitForCallback(port, state, timeoutMs) {
102
+ let server;
103
+ let timer;
104
+ const promise = new Promise((resolve) => {
105
+ timer = setTimeout(() => {
106
+ server?.close();
107
+ resolve(null);
108
+ }, timeoutMs);
109
+ server = createServer((req, res) => {
109
110
  const url = new URL(req.url, `http://localhost:${port}`);
110
111
  if (url.pathname !== "/callback") {
111
112
  res.writeHead(404);
@@ -117,43 +118,93 @@ async function oauthLogin() {
117
118
  const desc = url.searchParams.get("error_description") ?? error;
118
119
  res.writeHead(400, { "Content-Type": "text/html" });
119
120
  res.end(ERROR_HTML(desc));
120
- clearTimeout(timeout);
121
+ clearTimeout(timer);
121
122
  server.close();
122
- reject(new Error(`OAuth error: ${desc}`));
123
+ console.log(` ${amber("\u25B2")} OAuth error: ${desc}`);
124
+ resolve(null);
123
125
  return;
124
126
  }
125
127
  const returnedState = url.searchParams.get("state");
126
128
  if (returnedState !== state) {
127
129
  res.writeHead(400, { "Content-Type": "text/html" });
128
130
  res.end(ERROR_HTML("State mismatch \u2014 possible CSRF attack."));
129
- clearTimeout(timeout);
131
+ clearTimeout(timer);
130
132
  server.close();
131
- reject(new Error("OAuth state mismatch."));
133
+ resolve(null);
132
134
  return;
133
135
  }
134
136
  const authCode = url.searchParams.get("code");
135
137
  if (!authCode) {
136
138
  res.writeHead(400, { "Content-Type": "text/html" });
137
139
  res.end(ERROR_HTML("No authorization code received."));
138
- clearTimeout(timeout);
140
+ clearTimeout(timer);
139
141
  server.close();
140
- reject(new Error("No authorization code in callback."));
142
+ resolve(null);
141
143
  return;
142
144
  }
143
145
  res.writeHead(200, { "Content-Type": "text/html" });
144
146
  res.end(SUCCESS_HTML);
145
- clearTimeout(timeout);
147
+ clearTimeout(timer);
146
148
  server.close();
147
149
  resolve(authCode);
148
150
  });
149
- server.listen(port, "127.0.0.1", () => {
150
- console.log(` ${blue("\u25CF")} Opening browser for Supabase login...`);
151
- openBrowser(authorizeUrl.toString());
152
- console.log(` ${dim("Waiting for authorization...")}`);
153
- console.log();
154
- });
151
+ server.listen(port, "127.0.0.1");
155
152
  });
156
- return exchangeCode(code, redirectUri, codeVerifier);
153
+ return {
154
+ promise,
155
+ cleanup: () => {
156
+ clearTimeout(timer);
157
+ server?.close();
158
+ }
159
+ };
160
+ }
161
+ async function oauthLogin() {
162
+ while (true) {
163
+ const codeVerifier = generateCodeVerifier();
164
+ const codeChallenge = generateCodeChallenge(codeVerifier);
165
+ const state = randomBytes(16).toString("hex");
166
+ const port = await findOpenPort();
167
+ const redirectUri = `http://localhost:${port}/callback`;
168
+ const authorizeUrl = new URL(AUTHORIZE_URL);
169
+ authorizeUrl.searchParams.set("client_id", OAUTH_CLIENT_ID);
170
+ authorizeUrl.searchParams.set("redirect_uri", redirectUri);
171
+ authorizeUrl.searchParams.set("response_type", "code");
172
+ authorizeUrl.searchParams.set("code_challenge", codeChallenge);
173
+ authorizeUrl.searchParams.set("code_challenge_method", "S256");
174
+ authorizeUrl.searchParams.set("state", state);
175
+ console.log(` ${blue("\u25CF")} Opening browser for Supabase login...`);
176
+ openBrowser(authorizeUrl.toString());
177
+ console.log(` ${dim("Waiting for authorization...")}`);
178
+ console.log();
179
+ let code = null;
180
+ const first = waitForCallback(port, state, CHECK_IN_MS);
181
+ code = await first.promise;
182
+ if (code) {
183
+ return exchangeCode(code, redirectUri, codeVerifier);
184
+ }
185
+ while (true) {
186
+ const action = await select({
187
+ message: "No response yet. What would you like to do?",
188
+ theme,
189
+ choices: [
190
+ { name: "Keep waiting (30s more)", value: "wait" },
191
+ { name: "Retry (open browser again)", value: "retry" },
192
+ { name: "Cancel", value: "cancel" }
193
+ ]
194
+ });
195
+ if (action === "cancel") {
196
+ throw new Error("OAuth login cancelled.");
197
+ }
198
+ if (action === "retry") {
199
+ break;
200
+ }
201
+ const next = waitForCallback(port, state, CHECK_IN_MS);
202
+ code = await next.promise;
203
+ if (code) {
204
+ return exchangeCode(code, redirectUri, codeVerifier);
205
+ }
206
+ }
207
+ }
157
208
  }
158
209
  async function exchangeCode(code, redirectUri, codeVerifier) {
159
210
  const res = await fetch(TOKEN_URL, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentlink.sh/cli",
3
- "version": "0.10.1",
3
+ "version": "0.10.2",
4
4
  "description": "CLI for scaffolding Supabase apps with AI agents",
5
5
  "bin": {
6
6
  "agentlink": "dist/index.js"