insta 0.0.71 → 0.0.72
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/agent.js +1 -1
- package/dist/commands/github.js +10 -33
- package/package.json +1 -1
package/dist/agent.js
CHANGED
|
@@ -66,7 +66,7 @@ export async function loadAgentSession(apiUrl, projectId, cwd = process.cwd()) {
|
|
|
66
66
|
// scope.projectId. A miss fails HERE, naming the route, instead of on the platform as a
|
|
67
67
|
// "for a different project" 403 whose setup hint cannot help — keep this list in step with the
|
|
68
68
|
// account-level paths in src/commands/.
|
|
69
|
-
export const ACCOUNT_ROUTES = new Set(['agent', 'auth', 'me', 'orgs', 'regions', 'templates', 'tokens'
|
|
69
|
+
export const ACCOUNT_ROUTES = new Set(['agent', 'auth', 'me', 'orgs', 'regions', 'templates', 'tokens']);
|
|
70
70
|
export async function agentHeaders(api, method, path, rawBody, scope = {}) {
|
|
71
71
|
if (!mode)
|
|
72
72
|
return {};
|
package/dist/commands/github.js
CHANGED
|
@@ -81,8 +81,8 @@ const sleepSeconds = (s) => new Promise((r) => setTimeout(r, s * 1000));
|
|
|
81
81
|
const pollDelay = (s) => Math.min(Math.max(s, 1), 60);
|
|
82
82
|
// GitHub shows the person a code to type; the platform holds the device code and finishes the exchange,
|
|
83
83
|
// so nothing secret passes through the CLI.
|
|
84
|
-
export async function authorizeTerminal(api,
|
|
85
|
-
const start = await api.request('POST',
|
|
84
|
+
export async function authorizeTerminal(api, wait = sleepSeconds) {
|
|
85
|
+
const start = await api.request('POST', '/me/github/device', {});
|
|
86
86
|
const deadline = Date.parse(start.expiresAt);
|
|
87
87
|
// A NaN deadline makes every comparison false, which reads as an instant expiry — or, inverted, as a
|
|
88
88
|
// loop with no way out. Fail on it rather than guess which.
|
|
@@ -100,7 +100,7 @@ export async function authorizeTerminal(api, orgId, wait = sleepSeconds) {
|
|
|
100
100
|
await wait(interval);
|
|
101
101
|
let answer;
|
|
102
102
|
try {
|
|
103
|
-
answer = await api.request('POST',
|
|
103
|
+
answer = await api.request('POST', '/me/github/device/poll', { state: start.state });
|
|
104
104
|
}
|
|
105
105
|
catch (e) {
|
|
106
106
|
// A dropped link, or the per-IP limiter this cadence already tripped on the login flow, must not
|
|
@@ -120,41 +120,18 @@ export async function authorizeTerminal(api, orgId, wait = sleepSeconds) {
|
|
|
120
120
|
}
|
|
121
121
|
throw new Error('the GitHub authorization expired before it was confirmed — run the command again');
|
|
122
122
|
}
|
|
123
|
-
//
|
|
124
|
-
// failure is real, and a device flow cannot fix it.
|
|
125
|
-
const needsAuthorization = (e) => e instanceof ApiError && e.status === 400 && /not linked|no longer accepted/i.test(e.message);
|
|
126
|
-
// The repositories THIS caller's GitHub account can reach — the same question the platform asks again
|
|
127
|
-
// when the connect lands, so a repo missing here would be refused there anyway.
|
|
128
|
-
// Someone has to read the code and type it at GitHub. A terminal qualifies, and so does agent mode —
|
|
129
|
-
// an agent relays the URL to the person driving it — but --json and a bare pipe have no reader, and a
|
|
130
|
-
// ten-minute wait there is a stall where the old code failed with something to act on.
|
|
123
|
+
// Device authorization needs a person to read the code; JSON and bare pipes must fail promptly.
|
|
131
124
|
export function canAuthorizeHere(opts = {}) {
|
|
132
125
|
if (opts.json)
|
|
133
126
|
return false;
|
|
134
127
|
return !!agentMode() || !!process.stderr.isTTY;
|
|
135
128
|
}
|
|
136
|
-
export async function findCallerRepo(api,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
repos = (await api.request('POST', `/orgs/${encodeURIComponent(orgId)}/github/repos`, {})).repos ?? [];
|
|
142
|
-
}
|
|
143
|
-
catch (e) {
|
|
144
|
-
// Two 403s carry an action; a third kind would be guessed at, so it is rethrown as the platform put it.
|
|
145
|
-
if (e instanceof ApiError && e.status === 403 && /unclassified_agent_action/.test(e.message)) {
|
|
146
|
-
throw new Error('this backend does not let an agent authorize GitHub yet — connect the repository from the console, or pass --public for a public repository');
|
|
147
|
-
}
|
|
148
|
-
if (e instanceof ApiError && e.status === 403 && /requires admin/i.test(e.message)) {
|
|
149
|
-
throw new Error('connecting a repository needs the org admin role — ask an admin to connect it, or pass --public for a public repository');
|
|
150
|
-
}
|
|
151
|
-
if (!needsAuthorization(e))
|
|
152
|
-
throw e;
|
|
153
|
-
if (!canAuthorize) {
|
|
154
|
-
throw new Error('this GitHub account is not authorized for InstaCloud yet, and nothing here can read the code GitHub shows — run `insta compute connect-repo` from a terminal, connect the repository from the console, or pass --public for a public repository');
|
|
155
|
-
}
|
|
156
|
-
repos = await authorize(api, orgId);
|
|
129
|
+
export async function findCallerRepo(api, ref, authorize = authorizeTerminal, canAuthorize = canAuthorizeHere()) {
|
|
130
|
+
const mine = await api.request('GET', '/me/github/repos');
|
|
131
|
+
if (!mine.linked && !canAuthorize) {
|
|
132
|
+
throw new Error('this GitHub account is not authorized for InstaCloud yet, and nothing here can read the code GitHub shows — run `insta compute connect-repo` from a terminal, connect the repository from the console, or pass --public for a public repository');
|
|
157
133
|
}
|
|
134
|
+
const repos = mine.linked ? mine.repos : await authorize(api);
|
|
158
135
|
const whole = (n) => n !== null && n !== '' && Number.isInteger(Number(n)) && Number(n) > 0;
|
|
159
136
|
const hit = repos.find((r) => r.owner.toLowerCase() === ref.owner.toLowerCase() && r.repo.toLowerCase() === ref.repo.toLowerCase());
|
|
160
137
|
if (hit && whole(hit.installationId) && whole(hit.id))
|
|
@@ -187,7 +164,7 @@ export async function computeConnectRepo(rawRef, serviceName, opts) {
|
|
|
187
164
|
const svc = await targetService(api, p.projectId, opts.branch ?? p.branch, serviceName);
|
|
188
165
|
const src = opts.public
|
|
189
166
|
? { source: 'public', ...ref }
|
|
190
|
-
: { source: 'app', ...(await findCallerRepo(api,
|
|
167
|
+
: { source: 'app', ...(await findCallerRepo(api, ref, authorizeTerminal, canAuthorizeHere(opts))), ...ref };
|
|
191
168
|
// Detection must scan the branch that will be built: the build refuses commands that differ from what it detects there.
|
|
192
169
|
const detected = await api.request('POST', `/projects/${p.projectId}/github/detect`, { ...src, ...(opts.repoBranch ? { ref: opts.repoBranch } : {}) });
|
|
193
170
|
const candidate = pickCandidate(detected.services, opts.rootDir);
|