@bongos/core 1.21.96 → 1.21.97
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/.bongos-core.json +39 -34
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +3 -1
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/api/openapi.json +50 -3
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +363 -363
- package/docs/copy-registry.json +435 -435
- package/docs/design/project-startup-direction.md +13 -0
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-readings.json +250 -250
- package/modules/provisioning/github-setup.js +73 -4
- package/modules/provisioning/routes/github-setup.js +43 -1
- package/modules/provisioning/routes/provisioning.js +8 -4
- package/modules/public-landing/public/projects.html +185 -30
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/gds/provision-github-setup.js +29 -9
- package/scripts/gds/provision.js +20 -9
- package/src/module-api.js +1 -1
- package/tests/github_one_trip.mjs +4 -4
- package/tests/github_org_account.mjs +3 -2
- package/tests/github_pick_up.mjs +366 -0
- package/tests/projects_hub_pre_uat.mjs +3 -2
- package/tests/provisioning_github_app_routes.mjs +73 -0
- package/tests/wizard_github_feedback.mjs +95 -29
|
@@ -40,8 +40,18 @@ const SETUP_EVENTS = Object.freeze({
|
|
|
40
40
|
// the organisation the founder chose to make the app on (task 1004651); its detail is
|
|
41
41
|
// the org's login. Written by the mint route; absent = the founder's personal account.
|
|
42
42
|
appOrg: 'github-app-org',
|
|
43
|
+
// the founder pressed "Pick up where it stopped" or "Check again" (task 1004652). Like an
|
|
44
|
+
// install report it re-opens the runner's checking window; it claims nothing itself.
|
|
45
|
+
checkRequested: 'github-app-check-requested',
|
|
43
46
|
});
|
|
44
47
|
|
|
48
|
+
const { effectiveManifestStatus } = require('./manifest-truth');
|
|
49
|
+
|
|
50
|
+
// GitHub's manifest conversion code lasts an hour from GitHub's redirect, and the redirect
|
|
51
|
+
// comes after the link was minted, so an attempt younger than this can still be converted
|
|
52
|
+
// with the code it already holds — no new approval, so no second app (task 1004652).
|
|
53
|
+
const CODE_LIFE_MS = 60 * 60 * 1000;
|
|
54
|
+
|
|
45
55
|
// A GitHub login (1-39 chars, letters/digits and single inner hyphens). The org name the
|
|
46
56
|
// mint route takes from the page passes this before it reaches a GitHub URL, and the
|
|
47
57
|
// recorded one passes it again before it reaches a link. PURE.
|
|
@@ -89,6 +99,7 @@ async function githubSetupFacts(db, instanceId, since) {
|
|
|
89
99
|
const org = find(SETUP_EVENTS.appOrg);
|
|
90
100
|
return {
|
|
91
101
|
installReportedAt: at(SETUP_EVENTS.installReported), installedAt: at(SETUP_EVENTS.installed), deviceFlowAt: at(SETUP_EVENTS.deviceFlow),
|
|
102
|
+
checkRequestedAt: at(SETUP_EVENTS.checkRequested),
|
|
92
103
|
appOrg: org && validGithubLogin(org.detail) ? org.detail : null,
|
|
93
104
|
};
|
|
94
105
|
}
|
|
@@ -117,10 +128,65 @@ function githubChecklist(status, appSlug, facts = {}) {
|
|
|
117
128
|
// device-flow step can still tick itself, so the page keeps polling. PURE.
|
|
118
129
|
function stillVerifying(row, facts = {}, now = Date.now()) {
|
|
119
130
|
const settled = row && (row.resolved_at || row.updated_at);
|
|
120
|
-
const
|
|
131
|
+
const t = (iso) => (iso ? new Date(iso).getTime() : 0);
|
|
132
|
+
const last = Math.max(t(settled), t(facts.installReportedAt), t(facts.checkRequestedAt));
|
|
121
133
|
return last > 0 && now - last < VERIFY_WINDOW_MS;
|
|
122
134
|
}
|
|
123
135
|
|
|
136
|
+
// What "Pick up where it stopped" does for this attempt (task 1004652) — a closed
|
|
137
|
+
// vocabulary, and never "make another app" once GitHub has made one:
|
|
138
|
+
// 'reopen' — the link is still waiting on GitHub: post the SAME attempt again.
|
|
139
|
+
// 'fresh' — nothing was made on GitHub (the link lapsed, or the runner never took
|
|
140
|
+
// it up): a new link is safe.
|
|
141
|
+
// 'convert' — GitHub approved and the code it gave is still young: convert it again.
|
|
142
|
+
// 'check' — the app exists with its keys placed, a step is unverified and the runner
|
|
143
|
+
// has stopped checking: ask it to look again (sign-in, install, device flow).
|
|
144
|
+
// 'operator' — GitHub approved but the app's keys never reached us and its code is
|
|
145
|
+
// spent or too old: only the platform team can finish it.
|
|
146
|
+
// null — nothing to pick up: in flight, still being checked, or all done.
|
|
147
|
+
// PURE.
|
|
148
|
+
function pickUpFor(row, status, checklist, verifying, now = Date.now()) {
|
|
149
|
+
if (!row) return null;
|
|
150
|
+
if (status === 'pending') return 'reopen';
|
|
151
|
+
if (status === 'expired') return 'fresh';
|
|
152
|
+
if (row.app_slug || row.app_id) {
|
|
153
|
+
if (checklist && checklist.connected && checklist.device_flow === 'done') return null;
|
|
154
|
+
return verifying ? null : 'check';
|
|
155
|
+
}
|
|
156
|
+
if (status !== 'error') return null;
|
|
157
|
+
if (!(Number(row.attempts) > 0)) return 'fresh';
|
|
158
|
+
const age = now - new Date(row.created_at || 0).getTime();
|
|
159
|
+
return row.github_code && age < CODE_LIFE_MS ? 'convert' : 'operator';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// May the mint route make a new link for this instance, given its latest attempt (task
|
|
163
|
+
// 1004652)? null = yes. { reuse } = the latest link is still waiting on GitHub, so hand
|
|
164
|
+
// back the SAME state (a second click, a reopened tab, another device) instead of a second
|
|
165
|
+
// attempt. { refuse, message } = GitHub has approved an app for this project already, so a
|
|
166
|
+
// new link would make a second one. PURE.
|
|
167
|
+
function mintGuard(row, now = Date.now()) {
|
|
168
|
+
if (!row) return null;
|
|
169
|
+
const status = effectiveManifestStatus(row, now);
|
|
170
|
+
if (status === 'pending') return { reuse: row.state, expiresAt: new Date(row.expires_at).toISOString() };
|
|
171
|
+
if (status === 'expired' || (status === 'error' && !(Number(row.attempts) > 0) && !row.app_id)) return null;
|
|
172
|
+
if (status === 'code_received' || status === 'converting') {
|
|
173
|
+
return { refuse: 'github_in_progress', message: 'GitHub already approved this project’s app, and the platform is finishing it. The checklist ticks itself.' };
|
|
174
|
+
}
|
|
175
|
+
return { refuse: 'app_exists', message: 'GitHub already made this project’s app, so we won’t make a second one. Pick up where it stopped instead.' };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Put an approved attempt whose conversion stopped back in the runner's queue, with the
|
|
179
|
+
// code it still holds (pickUpFor 'convert'). The attempt count restarts so the runner
|
|
180
|
+
// tries it again. Only an attempt still in that shape moves; false otherwise.
|
|
181
|
+
async function requeueConversion(db, id) {
|
|
182
|
+
const { rows } = await db.query(
|
|
183
|
+
`UPDATE provisioning_oauth_manifests
|
|
184
|
+
SET status = 'code_received', attempts = 0, last_error = NULL, resolved_at = NULL, updated_at = now()
|
|
185
|
+
WHERE id = $1 AND status = 'error' AND app_id IS NULL AND github_code IS NOT NULL
|
|
186
|
+
RETURNING id`, [id]);
|
|
187
|
+
return rows.length > 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
124
190
|
// What the public status poll and the owner's instance read add for this leg: the checklist,
|
|
125
191
|
// the install link (until the install is verified), the repo it belongs on, and whether the
|
|
126
192
|
// runner is still checking. `status` is manifest-truth's EFFECTIVE status. Never the raw row.
|
|
@@ -129,16 +195,19 @@ function stillVerifying(row, facts = {}, now = Date.now()) {
|
|
|
129
195
|
// lands on the setup page, which sends the founder back to their projects.
|
|
130
196
|
// A failed facts read is "nothing verified yet" — the honest 'now' states, never a 500.
|
|
131
197
|
async function githubSetupRead(db, inst, row, status, { now = Date.now(), withState = true } = {}) {
|
|
132
|
-
if (!row) return { checklist: githubChecklist(null, null), install_url: null, repo: targetRepo(inst), verifying: false, app_org: null };
|
|
198
|
+
if (!row) return { checklist: githubChecklist(null, null), install_url: null, repo: targetRepo(inst), verifying: false, app_org: null, pick_up: null };
|
|
133
199
|
let facts = {};
|
|
134
200
|
// through module.exports so a route test can stub the one DB read on the cached module
|
|
135
201
|
try { facts = await module.exports.githubSetupFacts(db, row.instance_id, row.created_at); } catch (_) { facts = {}; }
|
|
136
202
|
const checklist = githubChecklist(status, row.app_slug, facts);
|
|
203
|
+
const verifying = stillVerifying(row, facts, now);
|
|
137
204
|
return {
|
|
138
205
|
checklist,
|
|
139
206
|
install_url: checklist.install === 'done' ? null : installUrlFor({ appSlug: row.app_slug, state: withState ? row.state : null }),
|
|
140
207
|
repo: targetRepo(inst),
|
|
141
|
-
verifying
|
|
208
|
+
verifying,
|
|
209
|
+
// what "Pick up where it stopped" will do (task 1004652); never a secret, never the code
|
|
210
|
+
pick_up: pickUpFor(row, status, checklist, verifying, now),
|
|
142
211
|
// the organisation this attempt makes the app on, or null for the founder's account
|
|
143
212
|
// (task 1004651): the page links the app's settings under the right owner
|
|
144
213
|
app_org: validGithubLogin(facts.appOrg) ? facts.appOrg : null,
|
|
@@ -147,5 +216,5 @@ async function githubSetupRead(db, inst, row, status, { now = Date.now(), withSt
|
|
|
147
216
|
|
|
148
217
|
module.exports = {
|
|
149
218
|
SETUP_EVENTS, VERIFY_WINDOW_MS, installUrlFor, targetRepo, githubSetupFacts, githubChecklist, stillVerifying, githubSetupRead,
|
|
150
|
-
validGithubLogin,
|
|
219
|
+
validGithubLogin, pickUpFor, mintGuard, requeueConversion,
|
|
151
220
|
};
|
|
@@ -91,7 +91,8 @@ module.exports = function buildGithubSetupRouter() {
|
|
|
91
91
|
status,
|
|
92
92
|
failure,
|
|
93
93
|
ready: status === 'done',
|
|
94
|
-
|
|
94
|
+
// a stopped sign-in the runner is still re-checking is not the end (task 1004652)
|
|
95
|
+
terminal: (failed && !setup.verifying) || (setup.checklist.connected && (setup.checklist.device_flow === 'done' || !setup.verifying)),
|
|
95
96
|
domain: inst && inst.domain ? inst.domain : null,
|
|
96
97
|
instance_status: inst ? inst.status : null,
|
|
97
98
|
// The App's public slug (task 1003049) — its GitHub page is public by design.
|
|
@@ -101,6 +102,7 @@ module.exports = function buildGithubSetupRouter() {
|
|
|
101
102
|
repo: setup.repo,
|
|
102
103
|
verifying: setup.verifying,
|
|
103
104
|
app_org: setup.app_org,
|
|
105
|
+
pick_up: setup.pick_up,
|
|
104
106
|
});
|
|
105
107
|
} catch (err) {
|
|
106
108
|
log.error('[provisioning] GET /provisioning/github-app/status', err);
|
|
@@ -184,6 +186,46 @@ module.exports = function buildGithubSetupRouter() {
|
|
|
184
186
|
}
|
|
185
187
|
});
|
|
186
188
|
|
|
189
|
+
// POST /provisioning/instances/:id/github-app/pick-up — "Pick up where it stopped" and
|
|
190
|
+
// "Check again" (task 1004652). It resumes the latest attempt from the step it stopped at,
|
|
191
|
+
// with the app GitHub already made, and never mints a new link: 'convert' puts an approved
|
|
192
|
+
// attempt whose code is still young back in the runner's queue; 'check' records a check
|
|
193
|
+
// request, which re-opens the runner's checking window for sign-in, the install and
|
|
194
|
+
// device flow. Every other pick-up is the page's own ('reopen' and 'fresh' go through the
|
|
195
|
+
// mint route, whose guard reuses or refuses; 'operator' files a blocker), so they are a
|
|
196
|
+
// 409 naming the action. Answers with the fresh read. rank: any authenticated builder
|
|
197
|
+
// (requireBuilder), the project's OWNER only — no Archon override, as with the mint route;
|
|
198
|
+
// anyone else gets the same 404 as a missing project.
|
|
199
|
+
router.post('/provisioning/instances/:id/github-app/pick-up', api.requireBuilder, returnRateLimit, async (req, res) => {
|
|
200
|
+
const id = /^\d{1,12}$/.test(String(req.params.id)) ? Number(req.params.id) : null;
|
|
201
|
+
if (!id) return res.fail('bad_id', 400);
|
|
202
|
+
try {
|
|
203
|
+
const inst = await provisioning.getInstanceById(pool, id);
|
|
204
|
+
if (!ownsLanding(inst, req.builder && req.builder.id)) return res.fail('instance_not_found', 404);
|
|
205
|
+
const row = await provisioning.getLatestOAuthManifestForInstance(pool, id);
|
|
206
|
+
const read = async (r) => githubSetup.githubSetupRead(pool, inst, r, r ? provisioning.publicGithubApp(r).status : null, { withState: false });
|
|
207
|
+
const before = await read(row);
|
|
208
|
+
const action = before.pick_up;
|
|
209
|
+
if (action === 'convert') {
|
|
210
|
+
if (!(await githubSetup.requeueConversion(pool, row.id))) return res.fail('nothing_to_pick_up', { status: 409, message: 'This step has already moved on.' });
|
|
211
|
+
} else if (action === 'check') {
|
|
212
|
+
await provisioning.recordEvent(pool, {
|
|
213
|
+
instanceId: id, ownerBuilderId: req.builder.id, event: githubSetup.SETUP_EVENTS.checkRequested,
|
|
214
|
+
detail: 'the founder asked to pick up the GitHub setup where it stopped', actor: 'api:self',
|
|
215
|
+
});
|
|
216
|
+
} else {
|
|
217
|
+
return res.fail('nothing_to_pick_up', { status: 409, message: 'Nothing to pick up here.', action: action || null });
|
|
218
|
+
}
|
|
219
|
+
const after = await provisioning.getLatestOAuthManifestForInstance(pool, id);
|
|
220
|
+
const pub = provisioning.publicGithubApp(after);
|
|
221
|
+
const setup = await read(after);
|
|
222
|
+
return res.json({ ok: true, action, status: pub.status, failure: pub.failure, app_slug: after.app_slug || null, ...setup });
|
|
223
|
+
} catch (err) {
|
|
224
|
+
log.error('[provisioning] POST /provisioning/instances/:id/github-app/pick-up', err);
|
|
225
|
+
return res.status(500).json({ ok: false, error: 'pick_up_failed' });
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
187
229
|
// GET /provisioning/github-app/resume[?id=<n>] — the project page's "which project am I
|
|
188
230
|
// connecting?" read when its tab remembers nothing (task 1004650): a hand-back on another
|
|
189
231
|
// device, a fresh tab, a cleared browser. With an id: that project, if the caller owns it.
|
|
@@ -1268,12 +1268,16 @@ module.exports = function provisioningRoutes() {
|
|
|
1268
1268
|
return res.fail('org_required', { status: 400, message: 'Name the GitHub organisation to make the app on.' });
|
|
1269
1269
|
}
|
|
1270
1270
|
const account = wantsOrg ? 'org' : 'personal';
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
await provisioning.
|
|
1271
|
+
// never a second app (task 1004652): a link still waiting on GitHub is handed back,
|
|
1272
|
+
// and once GitHub has approved one for this project no new link is made
|
|
1273
|
+
const guard = githubSetup.mintGuard(await provisioning.getLatestOAuthManifestForInstance(pool, id));
|
|
1274
|
+
if (guard && guard.refuse) return res.fail(guard.refuse, { status: 409, message: guard.message });
|
|
1275
|
+
const state = guard ? guard.reuse : crypto.randomBytes(32).toString('hex');
|
|
1276
|
+
const expiresAt = guard ? guard.expiresAt : new Date(Date.now() + MANIFEST_STATE_TTL_MS).toISOString();
|
|
1277
|
+
if (!guard) await provisioning.createOAuthManifest(pool, { instanceId: id, state, requestedBy: req.builder.id, expiresAt });
|
|
1274
1278
|
await provisioning.recordEvent(pool, {
|
|
1275
1279
|
instanceId: id, ownerBuilderId: req.builder.id, event: 'oauth-manifest-start',
|
|
1276
|
-
detail: 'GitHub App Manifest link minted', actor: 'api:self',
|
|
1280
|
+
detail: guard ? 'GitHub App Manifest link reopened (same attempt)' : 'GitHub App Manifest link minted', actor: 'api:self',
|
|
1277
1281
|
}).catch(() => {});
|
|
1278
1282
|
// which account owns this attempt's app, so the page can link its settings there
|
|
1279
1283
|
// from any tab or device (github-setup.js reads it back as app_org)
|
|
@@ -608,6 +608,10 @@ body:has(#view-new:not([hidden])) footer.foot{position:relative;z-index:1;backgr
|
|
|
608
608
|
display:inline-flex;align-items:center;justify-content:center;font-family:var(--font-body);font-size:13px;letter-spacing:0;}
|
|
609
609
|
#panel-done .humanSteps>li.sdone::before{content:"✓";text-transform:none;color:var(--ok);background:color-mix(in srgb,var(--ok) 12%,transparent);border:1px solid var(--ok);}
|
|
610
610
|
#panel-done .humanSteps>li.slocked::before{border:1px solid var(--rule-soft);}
|
|
611
|
+
/* a stopped step (task 1004652, the mockup's "!" dot): what stopped, and the button that fixes it */
|
|
612
|
+
#panel-done .humanSteps>li.sstopped::before{content:"!";color:var(--err);background:color-mix(in srgb,var(--err) 10%,transparent);border:1px solid var(--err);}
|
|
613
|
+
.humanSteps .linklike{display:block;margin-top:10px;min-height:24px;padding:3px 0;background:none;border:0;cursor:pointer;font-family:var(--font-body);font-size:13.5px;color:var(--link-ink);text-decoration:underline;text-underline-offset:3px;text-align:left;}
|
|
614
|
+
.humanSteps .linklike:hover{color:var(--ink);}
|
|
611
615
|
/* the optional step (command-line sign-in, task 1004649): a dashed ring until it ticks */
|
|
612
616
|
#panel-done .humanSteps>li.soptional:not(.sdone)::before{border-style:dashed;}
|
|
613
617
|
#panel-done .humanSteps b{font-size:15px;line-height:24px;}
|
|
@@ -6822,7 +6826,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6822
6826
|
return '<a href="' + (instanceId ? '?view=manage&id=' + encodeURIComponent(instanceId) : '?view=mine') +
|
|
6823
6827
|
'">' + label + '</a>';
|
|
6824
6828
|
}
|
|
6825
|
-
var live = { instStatus: null, addrReady: false, manifest: null, manifestMsg: '', manifestCode: '', manifestFailure: null, appSlug: null, appOrg: null, setup: null, installUrl: null };
|
|
6829
|
+
var live = { instStatus: null, addrReady: false, manifest: null, manifestMsg: '', manifestCode: '', manifestFailure: null, appSlug: null, appOrg: null, setup: null, installUrl: null, pickUp: null, verifying: false, askedPlatform: false };
|
|
6826
6830
|
var planPromises = {}; /* mode|domain → Promise<steps[]> */
|
|
6827
6831
|
var lastStepsHtml = '';
|
|
6828
6832
|
var lastProgHtml = ''; /* same change-only contract, for the progress strip */
|
|
@@ -6926,6 +6930,18 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6926
6930
|
the server verified (live.setup, the status poll's `checklist`). Every value that
|
|
6927
6931
|
reaches the markup goes through esc(). */
|
|
6928
6932
|
function ghStep(key) { return live.setup && live.setup[key]; }
|
|
6933
|
+
/* "Pick up where it stopped" (task 1004652): a stopped step says what stopped in one plain
|
|
6934
|
+
sentence and carries the action that fixes it, with the app GitHub already made, never a
|
|
6935
|
+
second one. live.pickUp is the server's word for what that action does (github-setup.js
|
|
6936
|
+
pickUpFor); live.verifying is whether the runner is still checking. Static words only. */
|
|
6937
|
+
var ghTabTrouble = null; /* 'behind' | 'closed': what this page saw of the GitHub tab it opened */
|
|
6938
|
+
var NO_SECOND_APP = 'Nothing is lost, and no second app will be made.';
|
|
6939
|
+
function pickUpBtn() { return '<button type="button" class="ghbtn" data-action="pick-up">Pick up where it stopped</button>'; }
|
|
6940
|
+
function askPlatformBtn() {
|
|
6941
|
+
if (live.askedPlatform === true) return '<p>Sent. The platform team has it and finishes it from here; there is nothing else for you to do.</p>';
|
|
6942
|
+
return (live.askedPlatform === 'failed' ? '<p>That didn’t send. Try again.</p>' : '') +
|
|
6943
|
+
'<button type="button" class="linklike" data-action="ask-platform">Send it to the platform team</button>';
|
|
6944
|
+
}
|
|
6929
6945
|
function appTitle() { return 'Approve the ' + esc(state.name || state.slug || 'project') + ' app on GitHub'; }
|
|
6930
6946
|
/* the organisation that owns the app, when the founder made it on one (task 1004651) */
|
|
6931
6947
|
function appOrgLogin() {
|
|
@@ -6950,7 +6966,11 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6950
6966
|
/* GitHub made the app and the platform placed its keys: the slug is recorded only
|
|
6951
6967
|
after the placement (the runner), so a known slug outside a failure is the tick —
|
|
6952
6968
|
a fact, whatever the venue says about starting a NEW trip */
|
|
6953
|
-
|
|
6969
|
+
/* …including when a LATER step stopped (sign-in unconfirmed): the app and its keys are
|
|
6970
|
+
real, so this step stays ticked and the stopped one says so (task 1004652) */
|
|
6971
|
+
var appMade = (ghStep('approve') === 'done' || live.appSlug) && live.manifest !== 'expired' &&
|
|
6972
|
+
(live.manifest !== 'error' || live.manifestFailure === 'sign_in_unconfirmed');
|
|
6973
|
+
if (appMade) {
|
|
6954
6974
|
return '<li class="sdone" data-onboard-step="oauth-app"><b>' + appTitle() + '</b><p>GitHub made the app, and its keys are on your project.</p></li>';
|
|
6955
6975
|
}
|
|
6956
6976
|
/* honesty about the venue (owner walk, 2026-08-12): GitHub rejects app
|
|
@@ -6964,24 +6984,24 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6964
6984
|
details (task 1003244). The one refusal a founder can act on — no
|
|
6965
6985
|
address yet — says where to go instead of what the server said. */
|
|
6966
6986
|
var snag, raw = '';
|
|
6967
|
-
/* a failure AFTER GitHub's approval left an app on the founder's account,
|
|
6968
|
-
|
|
6969
|
-
(live.manifestFailure, task 1004572) and
|
|
6970
|
-
|
|
6987
|
+
/* a failure AFTER GitHub's approval left an app on the founder's account, so
|
|
6988
|
+
"nothing was changed" would be false, and a new link would make a second app —
|
|
6989
|
+
the server says how far it got (live.manifestFailure, task 1004572) and what
|
|
6990
|
+
picking it up does (live.pickUp, task 1004652); the words and the action follow */
|
|
6991
|
+
var act = '<button type="button" class="ghbtn" data-action="manifest-start">' + GH_ICON + 'Connect GitHub ↗</button>' + manualOauthDetails();
|
|
6992
|
+
if (live.manifest === 'expired') snag = 'That setup link expired (they last 30 minutes) before GitHub was approved, so nothing was made on GitHub. Start a fresh one.';
|
|
6971
6993
|
else if (live.manifestCode === 'domain_required') snag = 'This project needs an address first — attach one from ' + projectPageLink('its own page') + ', then come back to this step.';
|
|
6972
|
-
else if (live.
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
|
|
6994
|
+
else if (live.pickUp === 'convert') {
|
|
6995
|
+
snag = 'GitHub approved the app, and the platform stopped before it finished collecting the app’s keys. ' + NO_SECOND_APP;
|
|
6996
|
+
act = pickUpBtn();
|
|
6997
|
+
}
|
|
6998
|
+
else if (live.manifestFailure === 'finish_failed') {
|
|
6999
|
+
snag = 'GitHub made the app, but its keys never reached your project, so it can’t be used yet. Making another would leave you with two, so we won’t: the platform team finishes this one. Send it to them from here.';
|
|
7000
|
+
act = askPlatformBtn();
|
|
6977
7001
|
}
|
|
6978
|
-
else if (live.manifestFailure === 'finish_failed') snag = appOrgLogin()
|
|
6979
|
-
? 'GitHub approved the app, but the platform couldn’t finish setting up sign-in on your project. The app GitHub made is still on ' + esc(appOrgLogin()) + ' — delete it first (the organisation’s Settings → Developer settings → GitHub Apps) so you don’t end up with two, then try again.'
|
|
6980
|
-
: 'GitHub approved the app, but the platform couldn’t finish setting up sign-in on your project. The app GitHub made is still on your account — delete it first (GitHub → Settings → Developer settings → GitHub Apps) so you don’t end up with two, then try again.';
|
|
6981
7002
|
else { snag = 'GitHub setup didn’t start. Nothing was changed on GitHub — try again.'; raw = live.manifestMsg; }
|
|
6982
|
-
return '<li data-onboard-step="oauth-app"><b>' + appTitle() + '</b><p>' + snag + '</p>' +
|
|
6983
|
-
(raw ? '<details><summary>Technical details</summary><p>' + esc(raw) + '</p></details>' : '') +
|
|
6984
|
-
'<button type="button" class="ghbtn" data-action="manifest-start">' + GH_ICON + 'Connect GitHub ↗</button>' + manualOauthDetails() + '</li>';
|
|
7003
|
+
return '<li class="sstopped" data-onboard-step="oauth-app"><b>' + appTitle() + '</b><p>' + snag + '</p>' +
|
|
7004
|
+
(raw ? '<details><summary>Technical details</summary><p>' + esc(raw) + '</p></details>' : '') + act + '</li>';
|
|
6985
7005
|
}
|
|
6986
7006
|
if (live.manifest === 'code_received' || live.manifest === 'converting') {
|
|
6987
7007
|
/* GitHub's half of the approval is done — say so, rather than keep telling the
|
|
@@ -6992,9 +7012,19 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6992
7012
|
}
|
|
6993
7013
|
if (live.manifest) { /* pending — GitHub holds the manifest, nobody has approved it yet */
|
|
6994
7014
|
autoOpenArmed = true;
|
|
6995
|
-
|
|
7015
|
+
/* the tab this page opened, as seen from here (task 1004652): behind this window, or
|
|
7016
|
+
closed before the approval. "Reopen GitHub" posts the SAME attempt again (the mint
|
|
7017
|
+
route hands back a link still waiting), so it can never make a second app. */
|
|
7018
|
+
var wait = ghTabTrouble === 'closed'
|
|
7019
|
+
? 'The GitHub tab closed before the app was approved, so nothing was made on GitHub. Reopen it to carry on with this same setup.'
|
|
7020
|
+
: ghTabTrouble === 'behind'
|
|
7021
|
+
? 'Your GitHub tab opened behind this window. Reopen it to bring it to the front; it is this same setup, so no second app will be made.'
|
|
7022
|
+
: 'Waiting on GitHub — approve the app in the tab that opened. No GitHub tab? It may have opened behind this window or in another one. Once approved, GitHub takes you straight on to installing it on your repo, then back here; every step ticks itself.';
|
|
7023
|
+
return '<li data-onboard-step="oauth-app"><b>' + appTitle() + '</b><p>' + wait + '</p>' +
|
|
6996
7024
|
progressMarkup(0, 0, 'Waiting for your approval on GitHub…', '', 'signin') + autoOpenTick() +
|
|
6997
|
-
(
|
|
7025
|
+
(ghTabTrouble
|
|
7026
|
+
? '<button type="button" class="ghbtn" data-action="reopen-github">' + GH_ICON + 'Reopen GitHub ↗</button>'
|
|
7027
|
+
: '<button type="button" class="linklike" data-action="reopen-github">No GitHub tab? Reopen GitHub</button>') + '</li>';
|
|
6998
7028
|
}
|
|
6999
7029
|
return '<li data-onboard-step="oauth-app"><b>' + appTitle() + '</b><p>One trip to GitHub: approve the app, install it on your repo, and you land back here. No keys to copy, and the secret never leaves the server side.</p>' +
|
|
7000
7030
|
'<button type="button" class="ghbtn" data-action="manifest-start">' + GH_ICON + 'Connect GitHub ↗</button>' + manualOauthDetails() + '</li>';
|
|
@@ -7019,7 +7049,11 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7019
7049
|
(appUrl
|
|
7020
7050
|
? 'open <a href="' + appUrl + '" target="_blank" rel="noopener">your app’s settings</a>, check <strong>Enable Device Flow</strong>, and save. '
|
|
7021
7051
|
: 'GitHub → Settings → Developer settings → GitHub Apps → your app → check <strong>Enable Device Flow</strong> → save. ') +
|
|
7022
|
-
'We tick this when GitHub has it on. Until then builders sign in on the web, and terminal sign-in falls back to a web token from the hall’s Settings page.</p
|
|
7052
|
+
'We tick this when GitHub has it on. Until then builders sign in on the web, and terminal sign-in falls back to a web token from the hall’s Settings page.</p>' +
|
|
7053
|
+
/* past the runner's checking window (task 1004652): ask it to look again. The
|
|
7054
|
+
install step carries the same action while it is unverified, so it shows here
|
|
7055
|
+
only once the install has ticked. */
|
|
7056
|
+
(live.pickUp === 'check' && ghStep('install') === 'done' ? '<button type="button" class="linklike" data-action="pick-up">Turned it on? Check again</button>' : '') + '</li>';
|
|
7023
7057
|
}
|
|
7024
7058
|
|
|
7025
7059
|
/* app-install — "Install it on your repo": the ADR 0055 publish credential (task
|
|
@@ -7036,6 +7070,12 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7036
7070
|
return '<li class="slocked" data-onboard-step="app-install">' + title + '<p>Comes straight after the approval, on the same trip: pick your repo and you land back here.</p></li>';
|
|
7037
7071
|
}
|
|
7038
7072
|
var installUrl = live.installUrl || ('https://github.com/apps/' + encodeURIComponent(live.appSlug) + '/installations/new');
|
|
7073
|
+
/* the runner stopped checking and GitHub never confirmed the install (task 1004652):
|
|
7074
|
+
the link opens the SAME app's install page, and the click asks the runner to look again */
|
|
7075
|
+
if (live.pickUp === 'check') {
|
|
7076
|
+
return '<li class="sstopped" data-onboard-step="app-install">' + title + '<p>GitHub stopped before the app was installed on your repo. ' + NO_SECOND_APP + '</p>' +
|
|
7077
|
+
'<a class="ghbtn" href="' + esc(installUrl) + '" target="_blank" rel="noopener" data-action="pick-up">' + GH_ICON + 'Pick up where it stopped ↗</a></li>';
|
|
7078
|
+
}
|
|
7039
7079
|
return '<li data-onboard-step="app-install">' + title + '<p>Your GitHub tab goes here next. We check with GitHub that the app is on your repo, and this step ticks itself. Came back without installing it?</p>' +
|
|
7040
7080
|
'<a class="ghbtn" href="' + esc(installUrl) + '" target="_blank" rel="noopener">' + GH_ICON + 'Install it on your repo ↗</a></li>';
|
|
7041
7081
|
}
|
|
@@ -7050,6 +7090,16 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7050
7090
|
if (!domain) {
|
|
7051
7091
|
return '<li class="slocked" data-onboard-step="first-sign-in">' + title + '<p>Unlocks with an address — then you open it, sign in with GitHub once, and take the owner’s seat (the founding Archon).</p></li>';
|
|
7052
7092
|
}
|
|
7093
|
+
/* the app and its keys are placed, but the project never confirmed sign-in (task
|
|
7094
|
+
1004652): the runner re-asks it with the keys already there, so picking it up never
|
|
7095
|
+
makes a second app — and a project that still will not answer goes to the platform team */
|
|
7096
|
+
if (live.manifest === 'error' && live.manifestFailure === 'sign_in_unconfirmed') {
|
|
7097
|
+
return '<li' + (live.verifying ? '' : ' class="sstopped"') + ' data-onboard-step="first-sign-in">' + title + '<p>' +
|
|
7098
|
+
(live.verifying
|
|
7099
|
+
? 'Your project hasn’t confirmed sign-in yet. We’re asking it again, with the app GitHub already made, and this step ticks itself.'
|
|
7100
|
+
: 'Your project didn’t confirm that sign-in works. ' + NO_SECOND_APP) + '</p>' +
|
|
7101
|
+
(live.pickUp === 'check' ? pickUpBtn() : '') + askPlatformBtn() + '</li>';
|
|
7102
|
+
}
|
|
7053
7103
|
if (live.manifest !== 'done') {
|
|
7054
7104
|
return '<li class="slocked" data-onboard-step="first-sign-in">' + title + '<p>Ticks once the platform has seen sign-in work on <code style="font-family:var(--font-mono)">' + esc(domain) + '</code>.</p></li>';
|
|
7055
7105
|
}
|
|
@@ -7144,6 +7194,13 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7144
7194
|
$('humanSteps').addEventListener('click', function (ev) {
|
|
7145
7195
|
var mBtn = ev.target.closest('[data-action="manifest-start"]');
|
|
7146
7196
|
if (mBtn) { startManifest(mBtn); return; }
|
|
7197
|
+
/* task 1004652: the pick-up link to GitHub's install page keeps its default (it opens) */
|
|
7198
|
+
var pBtn = ev.target.closest('[data-action="pick-up"]');
|
|
7199
|
+
if (pBtn) { pickUpSetup(pBtn); return; }
|
|
7200
|
+
var rBtn = ev.target.closest('[data-action="reopen-github"]');
|
|
7201
|
+
if (rBtn) { reopenGithub(rBtn); return; }
|
|
7202
|
+
var aBtn = ev.target.closest('[data-action="ask-platform"]');
|
|
7203
|
+
if (aBtn) { askPlatform(aBtn); return; }
|
|
7147
7204
|
var t = ev.target.closest('.copy');
|
|
7148
7205
|
if (!t) return;
|
|
7149
7206
|
navigator.clipboard.writeText(t.getAttribute('data-copy')).then(function () {
|
|
@@ -7199,7 +7256,6 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7199
7256
|
var manifestState = null; /* the one-time state token — kept so a parked poll can resume */
|
|
7200
7257
|
var manifestPending = false;
|
|
7201
7258
|
var manifestStartedAt = 0; /* when the founder was sent to GitHub */
|
|
7202
|
-
var manifestStale = false; /* ~90s with no approval → offer to start again */
|
|
7203
7259
|
/* the GitHub hand-back (task 1004548): the callback page returns the founder here
|
|
7204
7260
|
with the attempt's one-time state in the fragment (#ghstate=…), so a checklist
|
|
7205
7261
|
that page opened — not only the tab that started the setup — keeps watching the
|
|
@@ -7244,6 +7300,62 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7244
7300
|
return true;
|
|
7245
7301
|
}
|
|
7246
7302
|
|
|
7303
|
+
/* ── Pick up where it stopped (task 1004652). The server says what the action does
|
|
7304
|
+
(live.pickUp). 'convert' and 'check' are the pick-up route's: it resumes the
|
|
7305
|
+
stopped step with the app GitHub already made, and this page watches again.
|
|
7306
|
+
'reopen' and 'fresh' go through the mint route, which hands back a link still
|
|
7307
|
+
waiting on GitHub and refuses a new one once GitHub has made the app; 'operator'
|
|
7308
|
+
is a blocker filed for the platform team. ── */
|
|
7309
|
+
function pickUpSetup(btn) {
|
|
7310
|
+
if (!instanceId) return;
|
|
7311
|
+
if (live.pickUp === 'reopen' || live.pickUp === 'fresh') { startManifest(btn); return; }
|
|
7312
|
+
if (live.pickUp === 'operator') { askPlatform(btn); return; }
|
|
7313
|
+
var isBtn = btn.tagName === 'BUTTON';
|
|
7314
|
+
if (isBtn) { btn.disabled = true; btn.textContent = 'Picking it up…'; }
|
|
7315
|
+
fetch(API + '/provisioning/instances/' + encodeURIComponent(instanceId) + '/github-app/pick-up', { method: 'POST', credentials: 'same-origin' })
|
|
7316
|
+
.then(function (res) { return res.json().catch(function () { return {}; }); })
|
|
7317
|
+
.then(function (d) {
|
|
7318
|
+
if (d && d.ok) applyPickUp(d);
|
|
7319
|
+
else if (isBtn) { btn.disabled = false; btn.textContent = 'That didn’t go through. Try again'; }
|
|
7320
|
+
watchSetupAgain();
|
|
7321
|
+
})
|
|
7322
|
+
.catch(function () { if (isBtn) { btn.disabled = false; btn.textContent = 'That didn’t go through. Try again'; } });
|
|
7323
|
+
}
|
|
7324
|
+
/* the pick-up route's answer is the fresh read: seed it and redraw at once */
|
|
7325
|
+
function applyPickUp(d) {
|
|
7326
|
+
if (typeof d.status === 'string') { live.manifest = d.status; live.manifestFailure = d.failure || null; rememberManifest(undefined, d.status); }
|
|
7327
|
+
seedSetup(d.checklist, d.install_url, d);
|
|
7328
|
+
lastStepsHtml = '';
|
|
7329
|
+
renderSteps();
|
|
7330
|
+
}
|
|
7331
|
+
/* …and keep watching: the attempt's own poll when this tab knows it, else the instance read */
|
|
7332
|
+
function watchSetupAgain() {
|
|
7333
|
+
if (manifestState) pollManifest(manifestState);
|
|
7334
|
+
else if (!pollTimer && instanceId) startProgress(instanceId);
|
|
7335
|
+
}
|
|
7336
|
+
/* "Reopen GitHub": bring a tab still open to the front, and post the SAME attempt again */
|
|
7337
|
+
function reopenGithub(btn) {
|
|
7338
|
+
if (ghTab && !ghTab.closed) { try { ghTab.focus(); } catch (e) {} }
|
|
7339
|
+
startManifest(btn);
|
|
7340
|
+
}
|
|
7341
|
+
/* the one stop no button on this page can fix (GitHub made the app but its keys never
|
|
7342
|
+
reached the project): filed as a blocker for the platform team, once per attempt */
|
|
7343
|
+
function askPlatform(btn) {
|
|
7344
|
+
if (!instanceId) return;
|
|
7345
|
+
btn.disabled = true; btn.textContent = 'Sending…';
|
|
7346
|
+
fetch(API + '/blockers', {
|
|
7347
|
+
method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' },
|
|
7348
|
+
body: JSON.stringify({
|
|
7349
|
+
title: 'A project’s GitHub setup needs the platform team',
|
|
7350
|
+
body_md: 'Project ' + instanceId + ': the founder pressed “Send it to the platform team” on the GitHub step. GitHub made the app, but the project cannot finish connecting with it (its latest provisioning_oauth_manifests row says why in last_error), and a new link would make a second app. Once it is sorted, mark that attempt expired so the founder can start a fresh link.',
|
|
7351
|
+
source: 'github-setup', source_ref: 'instance-' + instanceId + '-' + (manifestStartedAt || 'latest')
|
|
7352
|
+
})
|
|
7353
|
+
}).then(function (res) {
|
|
7354
|
+
live.askedPlatform = res.ok ? true : 'failed';
|
|
7355
|
+
}).catch(function () { live.askedPlatform = 'failed'; })
|
|
7356
|
+
.then(function () { lastStepsHtml = ''; renderSteps(); });
|
|
7357
|
+
}
|
|
7358
|
+
|
|
7247
7359
|
function startManifest(btn) {
|
|
7248
7360
|
if (!instanceId) return;
|
|
7249
7361
|
/* an organisation that cannot go ahead says why where it is chosen, before any
|
|
@@ -7252,6 +7364,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7252
7364
|
if (!acct.ok) { appPick.explain(); return; }
|
|
7253
7365
|
btn.disabled = true; btn.textContent = 'Preparing GitHub…';
|
|
7254
7366
|
var tab = window.open('about:blank', 'ghAppSetup');
|
|
7367
|
+
if (tab) { try { tab.focus(); } catch (e) {} watchGhTab(tab); }
|
|
7255
7368
|
fetch(API + '/provisioning/instances/' + encodeURIComponent(instanceId) + '/github-app', {
|
|
7256
7369
|
method: 'POST', credentials: 'same-origin',
|
|
7257
7370
|
/* the founder's own account posts exactly what it always did */
|
|
@@ -7275,13 +7388,20 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7275
7388
|
form.submit(); form.remove();
|
|
7276
7389
|
live.manifest = 'pending'; live.manifestMsg = ''; live.manifestFailure = null;
|
|
7277
7390
|
noteAppOrg(d.account === 'org' ? d.org_login : null);
|
|
7278
|
-
manifestStartedAt = Date.now();
|
|
7391
|
+
manifestStartedAt = Date.now();
|
|
7279
7392
|
rememberManifest(d.state, 'pending');
|
|
7280
7393
|
renderSteps();
|
|
7281
7394
|
pollManifest(d.state);
|
|
7282
7395
|
}).catch(function (err) {
|
|
7283
7396
|
if (String(err && err.message) === 'signing in') return;
|
|
7284
7397
|
if (tab) { try { tab.close(); } catch (e) {} }
|
|
7398
|
+
/* GitHub already approved an app for this project (task 1004652): no second one —
|
|
7399
|
+
this page was behind, so it re-learns the truth and draws that step's own action */
|
|
7400
|
+
if (err && (err.code === 'app_exists' || err.code === 'github_in_progress')) {
|
|
7401
|
+
lastStepsHtml = '';
|
|
7402
|
+
if (instanceId) startProgress(instanceId);
|
|
7403
|
+
return;
|
|
7404
|
+
}
|
|
7285
7405
|
live.manifest = 'error'; live.manifestFailure = null; live.manifestMsg = String((err && err.message) || 'could not start');
|
|
7286
7406
|
live.manifestCode = String((err && err.code) || '');
|
|
7287
7407
|
renderSteps();
|
|
@@ -7296,22 +7416,57 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7296
7416
|
}
|
|
7297
7417
|
/* the checklist the server derived (task 1004649) — change-only; true when it moved.
|
|
7298
7418
|
The install link rides only on a state-keyed read; the owner's read keeps the last. */
|
|
7299
|
-
function seedSetup(checklist, installUrl) {
|
|
7419
|
+
function seedSetup(checklist, installUrl, more) {
|
|
7300
7420
|
if (!checklist || typeof checklist !== 'object') return false;
|
|
7421
|
+
/* what picking it up does, and whether the runner is still checking (task 1004652) */
|
|
7422
|
+
var moreMoved = false;
|
|
7423
|
+
if (more && typeof more === 'object') {
|
|
7424
|
+
var pick = typeof more.pick_up === 'string' ? more.pick_up : null, ver = !!more.verifying;
|
|
7425
|
+
moreMoved = pick !== live.pickUp || ver !== live.verifying;
|
|
7426
|
+
live.pickUp = pick; live.verifying = ver;
|
|
7427
|
+
}
|
|
7301
7428
|
var next = JSON.stringify(checklist), was = live.setup ? JSON.stringify(live.setup) : '';
|
|
7302
7429
|
var url = typeof installUrl === 'string' && /^https:\/\/github\.com\/apps\//.test(installUrl) ? installUrl : null;
|
|
7303
7430
|
var urlMoved = !!url && url !== live.installUrl && (!live.installUrl || /[?&]state=/.test(url) || !/[?&]state=/.test(live.installUrl));
|
|
7304
|
-
if (next === was && !urlMoved) return
|
|
7431
|
+
if (next === was && !urlMoved) return moreMoved;
|
|
7305
7432
|
live.setup = checklist;
|
|
7306
7433
|
if (urlMoved) live.installUrl = url;
|
|
7307
7434
|
return true;
|
|
7308
7435
|
}
|
|
7309
7436
|
/* still worth watching after sign-in: the install not verified yet, while the runner checks */
|
|
7310
7437
|
function setupOpen() {
|
|
7311
|
-
return !!(live.setup && !live.setup.connected && live.manifest === 'done');
|
|
7438
|
+
return !!(live.setup && ((!live.setup.connected && live.manifest === 'done') || (live.verifying && live.manifest === 'error')));
|
|
7439
|
+
}
|
|
7440
|
+
/* The GitHub tab this page opened (task 1004652). A tab that opened BEHIND this window
|
|
7441
|
+
(this page kept the focus) or was closed before the approval is noticed within a few
|
|
7442
|
+
seconds — it used to take 90 — and the step offers "Reopen GitHub". */
|
|
7443
|
+
var ghTab = null, ghTabLeft = false, ghTabSince = 0, ghTabTimer = null, ghTabHeard = false;
|
|
7444
|
+
/* PURE: 'closed' · 'behind' (still focused here a few seconds after opening it) · null */
|
|
7445
|
+
function ghTabState(tab, left, since, now, focusedHere) {
|
|
7446
|
+
if (!tab) return null;
|
|
7447
|
+
if (tab.closed) return 'closed';
|
|
7448
|
+
if (!left && focusedHere && now - since > 4000) return 'behind';
|
|
7449
|
+
return null;
|
|
7450
|
+
}
|
|
7451
|
+
function watchGhTab(tab) {
|
|
7452
|
+
if (!ghTabHeard) { /* this page losing the focus = the GitHub tab came to the front */
|
|
7453
|
+
ghTabHeard = true;
|
|
7454
|
+
window.addEventListener('blur', function () { ghTabLeft = true; });
|
|
7455
|
+
document.addEventListener('visibilitychange', function () { if (document.hidden) ghTabLeft = true; });
|
|
7456
|
+
}
|
|
7457
|
+
ghTab = tab; ghTabLeft = false; ghTabSince = Date.now(); ghTabTrouble = null;
|
|
7458
|
+
if (ghTabTimer) clearInterval(ghTabTimer);
|
|
7459
|
+
ghTabTimer = setInterval(noteManifestWait, 1500);
|
|
7312
7460
|
}
|
|
7313
7461
|
function noteManifestWait() {
|
|
7314
|
-
if (live.manifest
|
|
7462
|
+
if (live.manifest !== 'pending') {
|
|
7463
|
+
if (ghTabTimer) { clearInterval(ghTabTimer); ghTabTimer = null; }
|
|
7464
|
+
ghTabTrouble = null;
|
|
7465
|
+
return;
|
|
7466
|
+
}
|
|
7467
|
+
var focused = !document.hidden && (typeof document.hasFocus !== 'function' || document.hasFocus());
|
|
7468
|
+
var next = ghTabState(ghTab, ghTabLeft, ghTabSince, Date.now(), focused);
|
|
7469
|
+
if (next !== ghTabTrouble) { ghTabTrouble = next; renderSteps(); }
|
|
7315
7470
|
}
|
|
7316
7471
|
function pollManifest(stateToken) {
|
|
7317
7472
|
stopManifestPoll();
|
|
@@ -7327,7 +7482,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7327
7482
|
if (myGen !== manifestGen) return;
|
|
7328
7483
|
if (d.app_slug && d.app_slug !== live.appSlug) { live.appSlug = d.app_slug; rememberManifest(undefined, undefined, d.app_slug); renderSteps(); }
|
|
7329
7484
|
/* the server-verified ticks (task 1004649), and whose account the app is on */
|
|
7330
|
-
var setupMoved = seedSetup(d.checklist, d.install_url);
|
|
7485
|
+
var setupMoved = seedSetup(d.checklist, d.install_url, d);
|
|
7331
7486
|
if (noteAppOrg(d.app_org)) setupMoved = true;
|
|
7332
7487
|
if (setupMoved) renderSteps();
|
|
7333
7488
|
var next = d.ready ? 'done' : (d.status || 'pending');
|
|
@@ -7576,7 +7731,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7576
7731
|
}
|
|
7577
7732
|
/* the server-verified checklist rides the same read (task 1004649) */
|
|
7578
7733
|
if (data.github_setup && !manifestPollTimer) {
|
|
7579
|
-
var seedMoved = seedSetup(data.github_setup.checklist, data.github_setup.install_url);
|
|
7734
|
+
var seedMoved = seedSetup(data.github_setup.checklist, data.github_setup.install_url, data.github_setup);
|
|
7580
7735
|
if (noteAppOrg(data.github_setup.app_org)) seedMoved = true;
|
|
7581
7736
|
if (seedMoved) renderSteps();
|
|
7582
7737
|
}
|
|
@@ -7618,8 +7773,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7618
7773
|
stopManifestPoll();
|
|
7619
7774
|
instanceId = null;
|
|
7620
7775
|
watchPending = false; manifestPending = false; manifestState = null;
|
|
7621
|
-
manifestStartedAt = 0;
|
|
7622
|
-
live = { instStatus: null, addrReady: false, manifest: null, manifestMsg: '', manifestCode: '', manifestFailure: null, appSlug: null, appOrg: null, setup: null, installUrl: null };
|
|
7776
|
+
manifestStartedAt = 0; ghTabTrouble = null;
|
|
7777
|
+
live = { instStatus: null, addrReady: false, manifest: null, manifestMsg: '', manifestCode: '', manifestFailure: null, appSlug: null, appOrg: null, setup: null, installUrl: null, pickUp: null, verifying: false, askedPlatform: false };
|
|
7623
7778
|
lastStepsHtml = '';
|
|
7624
7779
|
lastProgHtml = '';
|
|
7625
7780
|
hopReset();
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.97",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.97",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.97",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -9406,5 +9406,11 @@
|
|
|
9406
9406
|
"id": "1004651",
|
|
9407
9407
|
"text": "Founders can now put a new project under a GitHub organisation, not only their own account. When connecting GitHub, they choose \"On my account\" or \"On an organisation\". Their organisations are listed; one they only belong to ("
|
|
9408
9408
|
}
|
|
9409
|
+
],
|
|
9410
|
+
"1.21.97": [
|
|
9411
|
+
{
|
|
9412
|
+
"id": "1004652",
|
|
9413
|
+
"text": "Connecting GitHub no longer makes a second app when something goes wrong. If a step stops, the checklist shows which one, says in one plain sentence that nothing is lost, and gives a \"Pick up where it stopped\" button that carr"
|
|
9414
|
+
}
|
|
9409
9415
|
]
|
|
9410
9416
|
}
|