@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-
|
|
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-
|
|
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({
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
updatePostgrestConfig,
|
|
27
27
|
validateProjectName,
|
|
28
28
|
waitForProjectReady
|
|
29
|
-
} from "./chunk-
|
|
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-
|
|
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 (
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
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
|
-
|
|
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-
|
|
4885
|
-
const { saveCredentials: saveCredentials2, loadCredentials: loadCredentials2, credentialsPath: credentialsPath2 } = await import("./cloud-
|
|
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
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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(
|
|
121
|
+
clearTimeout(timer);
|
|
121
122
|
server.close();
|
|
122
|
-
|
|
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(
|
|
131
|
+
clearTimeout(timer);
|
|
130
132
|
server.close();
|
|
131
|
-
|
|
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(
|
|
140
|
+
clearTimeout(timer);
|
|
139
141
|
server.close();
|
|
140
|
-
|
|
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(
|
|
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
|
|
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, {
|