instar 1.3.1237 → 1.3.1239
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/dashboard/index.html +4 -0
- package/dashboard/subscriptions.js +57 -8
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +14 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/EnrollmentWizard.d.ts +8 -2
- package/dist/core/EnrollmentWizard.d.ts.map +1 -1
- package/dist/core/EnrollmentWizard.js +77 -18
- package/dist/core/EnrollmentWizard.js.map +1 -1
- package/dist/core/PendingLoginStore.d.ts +13 -0
- package/dist/core/PendingLoginStore.d.ts.map +1 -1
- package/dist/core/PendingLoginStore.js +20 -0
- package/dist/core/PendingLoginStore.js.map +1 -1
- package/dist/data/standards-guard-index.json +2 -2
- package/dist/data/standards-guard-index.meta.json +3 -3
- package/dist/data/standards-registry.md +37 -37
- package/dist/data/standards-registry.meta.json +2 -2
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +22 -12
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/src/data/standards-guard-index.json +2 -2
- package/src/data/standards-guard-index.meta.json +3 -3
- package/src/data/standards-registry.md +37 -37
- package/src/data/standards-registry.meta.json +2 -2
- package/upgrades/1.3.1238.md +26 -0
- package/upgrades/1.3.1239.md +19 -0
- package/upgrades/side-effects/subscription-relogin-completion-witness.md +125 -0
- package/upgrades/side-effects/subscription-relogin-stale-retry.md +100 -0
package/dashboard/index.html
CHANGED
|
@@ -9857,6 +9857,10 @@
|
|
|
9857
9857
|
__subController = __subModule.createController({
|
|
9858
9858
|
doc: document, els, fetchImpl,
|
|
9859
9859
|
getOperatorSessionToken: () => operatorSessionToken,
|
|
9860
|
+
requestUnlock: () => {
|
|
9861
|
+
document.getElementById('authOverlay').style.display = 'flex';
|
|
9862
|
+
document.getElementById('pinInput').focus();
|
|
9863
|
+
},
|
|
9860
9864
|
});
|
|
9861
9865
|
}
|
|
9862
9866
|
__subController.start();
|
|
@@ -79,6 +79,31 @@ export function effectiveAccountStatus(account) {
|
|
|
79
79
|
return account && account.status;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
const KNOWN_REPAIR_STATES = new Set([
|
|
83
|
+
'suggested', 'approved', 'cli-starting', 'artifact-ready', 'browser-driving',
|
|
84
|
+
'cli-finishing', 'identity-verifying', 'auth-verifying', 'waiting-operator-only',
|
|
85
|
+
'succeeded', 'refused', 'cancelled', 'failed',
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
/** A suggested/failed repair is actionable only while current pool truth still says
|
|
89
|
+
* the account cell needs authentication. Terminal repair rows remain durable audit
|
|
90
|
+
* history, but must never shadow a newer Active observation or offer a retry that
|
|
91
|
+
* the server's admission policy is required to refuse. In-flight states remain
|
|
92
|
+
* visible because pool activation can precede the controller's final ledger close. */
|
|
93
|
+
export function repairAppliesToCurrentState(repair, currentState) {
|
|
94
|
+
if (!repair || !KNOWN_REPAIR_STATES.has(repair.state)) return false;
|
|
95
|
+
if (repair.state === 'suggested' || repair.state === 'failed') {
|
|
96
|
+
return currentState === 'needs-reauth';
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function repairConflictMessage(error) {
|
|
102
|
+
return /^(?:retry|approval)-revalidation-refused:account-not-needs-reauth$/.test(
|
|
103
|
+
typeof error === 'string' ? error : '',
|
|
104
|
+
) ? 'This account is already active. Refreshing its status…' : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
82
107
|
const PROVIDER_WORDS = { anthropic: 'Claude', openai: 'Codex', 'github-copilot': 'Copilot', google: 'Gemini' };
|
|
83
108
|
export function friendlyProvider(provider) {
|
|
84
109
|
return PROVIDER_WORDS[typeof provider === 'string' ? provider : ''] || sanitizeForDisplay(provider, 'label');
|
|
@@ -294,9 +319,12 @@ export function renderAccounts(doc, target, accounts, now = Date.now(), inUseAcc
|
|
|
294
319
|
if (refAge) {
|
|
295
320
|
card.appendChild(el(doc, 'div', 'sub-account-refresh', `Token auto-refreshed ${refAge}`));
|
|
296
321
|
}
|
|
297
|
-
const
|
|
322
|
+
const repairCandidate = Array.isArray(reloginEpisodes)
|
|
298
323
|
? reloginEpisodes.find((episode) => episode && a && episode.accountId === a.id)
|
|
299
324
|
: null;
|
|
325
|
+
const repair = repairAppliesToCurrentState(repairCandidate, effectiveAccountStatus(a))
|
|
326
|
+
? repairCandidate
|
|
327
|
+
: null;
|
|
300
328
|
if (repair) {
|
|
301
329
|
const repairBox = el(doc, 'div', 'sub-account-repair');
|
|
302
330
|
const terminal = ['succeeded', 'refused', 'cancelled', 'failed'].includes(repair.state);
|
|
@@ -855,9 +883,12 @@ export function renderAccountMatrix(doc, target, poolScope, pendingScope, transi
|
|
|
855
883
|
const tr = doc.createElement('tr');
|
|
856
884
|
tr.appendChild(el(doc, 'th', 'sub-matrix-acct', sanitizeForDisplay(row.account.email, 'label')));
|
|
857
885
|
for (const c of row.cells) {
|
|
858
|
-
const
|
|
886
|
+
const repairCandidate = Array.isArray(reloginEpisodes)
|
|
859
887
|
? reloginEpisodes.find((episode) => episode && episode.accountId === c.accountId && episode.machineId === c.machineId)
|
|
860
888
|
: null;
|
|
889
|
+
const repair = repairAppliesToCurrentState(repairCandidate, c.state)
|
|
890
|
+
? repairCandidate
|
|
891
|
+
: null;
|
|
861
892
|
const repairUnavailable = Array.isArray(reloginFailures)
|
|
862
893
|
&& reloginFailures.some((failure) => failure && failure.machineId === c.machineId);
|
|
863
894
|
// just-verified rides a transient highlight on an otherwise-active cell (D4 ceremony).
|
|
@@ -1045,6 +1076,7 @@ export function createController(opts) {
|
|
|
1045
1076
|
schedule = (fn, ms) => setTimeout(fn, ms),
|
|
1046
1077
|
cancel = (id) => clearTimeout(id),
|
|
1047
1078
|
getOperatorSessionToken = () => '',
|
|
1079
|
+
requestUnlock = () => {},
|
|
1048
1080
|
} = opts;
|
|
1049
1081
|
|
|
1050
1082
|
// matrixTransient: client-side last-attempt state per `${accountId}::${machineId}` cell (FD6 —
|
|
@@ -1139,7 +1171,11 @@ export function createController(opts) {
|
|
|
1139
1171
|
const profileId = els.profileProvision.querySelector('[aria-label="Profile name"]')?.value?.trim() || '';
|
|
1140
1172
|
const loginMethod = els.profileProvision.querySelector('[aria-label="Sign-in method"]')?.value || 'session-cookie';
|
|
1141
1173
|
const status = els.profileProvision.querySelector('.sub-profile-status');
|
|
1142
|
-
if (!proof) {
|
|
1174
|
+
if (!proof) {
|
|
1175
|
+
requestUnlock();
|
|
1176
|
+
status.textContent = 'Enter your dashboard PIN, then retry.';
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1143
1179
|
if (!identity || !profileId) { status.textContent = 'Enter the Google account and a profile name.'; return; }
|
|
1144
1180
|
button.setAttribute('disabled', 'disabled');
|
|
1145
1181
|
status.textContent = 'Preparing the private profile…';
|
|
@@ -1152,8 +1188,9 @@ export function createController(opts) {
|
|
|
1152
1188
|
});
|
|
1153
1189
|
const body = await response.json().catch(() => ({}));
|
|
1154
1190
|
if (!response.ok) {
|
|
1191
|
+
if (response.status === 401) requestUnlock();
|
|
1155
1192
|
status.textContent = response.status === 401
|
|
1156
|
-
? '
|
|
1193
|
+
? 'Enter your dashboard PIN, then retry.'
|
|
1157
1194
|
: `Couldn’t create the profile: ${body.error || 'try again'}`;
|
|
1158
1195
|
button.removeAttribute('disabled');
|
|
1159
1196
|
return;
|
|
@@ -1336,7 +1373,8 @@ export function createController(opts) {
|
|
|
1336
1373
|
if (!['approve', 'cancel', 'retry'].includes(action) || !episodeId) return;
|
|
1337
1374
|
const proof = getOperatorSessionToken();
|
|
1338
1375
|
if (!proof) {
|
|
1339
|
-
|
|
1376
|
+
requestUnlock();
|
|
1377
|
+
button.textContent = 'Enter PIN, then try again';
|
|
1340
1378
|
return;
|
|
1341
1379
|
}
|
|
1342
1380
|
button.setAttribute('disabled', 'disabled');
|
|
@@ -1352,8 +1390,13 @@ export function createController(opts) {
|
|
|
1352
1390
|
body: '{}',
|
|
1353
1391
|
});
|
|
1354
1392
|
if (!response.ok) {
|
|
1355
|
-
|
|
1393
|
+
if (response.status === 401) requestUnlock();
|
|
1394
|
+
const body = await response.json().catch(() => ({}));
|
|
1395
|
+
const resolvedConflict = repairConflictMessage(body.error);
|
|
1396
|
+
button.textContent = response.status === 401 ? 'Enter PIN, then try again'
|
|
1397
|
+
: resolvedConflict || 'Couldn’t start — try again';
|
|
1356
1398
|
button.removeAttribute('disabled');
|
|
1399
|
+
if (resolvedConflict) await tick();
|
|
1357
1400
|
return;
|
|
1358
1401
|
}
|
|
1359
1402
|
await tick();
|
|
@@ -1540,7 +1583,8 @@ export function createController(opts) {
|
|
|
1540
1583
|
return;
|
|
1541
1584
|
}
|
|
1542
1585
|
if (!proof) {
|
|
1543
|
-
|
|
1586
|
+
requestUnlock();
|
|
1587
|
+
setCellStatus(cell, 'Enter your dashboard PIN, then tap Repair sign-in once.');
|
|
1544
1588
|
return;
|
|
1545
1589
|
}
|
|
1546
1590
|
btn.setAttribute('disabled', 'disabled');
|
|
@@ -1554,8 +1598,13 @@ export function createController(opts) {
|
|
|
1554
1598
|
});
|
|
1555
1599
|
const body = await response.json().catch(() => ({}));
|
|
1556
1600
|
if (!response.ok) {
|
|
1557
|
-
|
|
1601
|
+
if (response.status === 401) requestUnlock();
|
|
1602
|
+
const resolvedConflict = repairConflictMessage(body.error);
|
|
1603
|
+
setCellStatus(cell, response.status === 401
|
|
1604
|
+
? 'Enter your dashboard PIN, then tap Repair sign-in once.'
|
|
1605
|
+
: resolvedConflict || body.error || 'Couldn’t start the repair — try again.');
|
|
1558
1606
|
btn.removeAttribute('disabled');
|
|
1607
|
+
if (resolvedConflict) await tick();
|
|
1559
1608
|
return;
|
|
1560
1609
|
}
|
|
1561
1610
|
setCellStatus(cell, 'Repair started — Instar is handling the sign-in.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/server.js
CHANGED
|
@@ -9,6 +9,7 @@ import { bindUnknownProducerTelegramSender, sendDeterministicTelegramNotice } fr
|
|
|
9
9
|
* topic message → find/spawn session → inject message → session replies via [telegram:N]
|
|
10
10
|
*/
|
|
11
11
|
import { execFile, execFileSync } from 'node:child_process';
|
|
12
|
+
import { createHmac } from 'node:crypto';
|
|
12
13
|
import fs from 'node:fs';
|
|
13
14
|
import os from 'node:os';
|
|
14
15
|
import path from 'node:path';
|
|
@@ -13542,6 +13543,19 @@ export async function startServer(options) {
|
|
|
13542
13543
|
return null;
|
|
13543
13544
|
}
|
|
13544
13545
|
},
|
|
13546
|
+
// Claude's `.claude.json` is a settings file and commonly predates a
|
|
13547
|
+
// repair, so its mere existence can never prove this login completed.
|
|
13548
|
+
// Capture an HMAC revision of the machine-local keychain blob before the
|
|
13549
|
+
// artifact is issued and require that revision to change. The auth bytes
|
|
13550
|
+
// never leave memory; only the keyed digest enters pending state.
|
|
13551
|
+
authRevisionWitness: async (login) => {
|
|
13552
|
+
if (login.framework !== 'claude-code' || !login.configHome)
|
|
13553
|
+
return null;
|
|
13554
|
+
const raw = await defaultKeychainExec.readService(credSwapService(login.configHome));
|
|
13555
|
+
if (!raw)
|
|
13556
|
+
return null;
|
|
13557
|
+
return createHmac('sha256', config.authToken ?? '').update(raw).digest('hex');
|
|
13558
|
+
},
|
|
13545
13559
|
driveLogin: new FrameworkLoginDriver({
|
|
13546
13560
|
// Capture with `tmux capture-pane -J` so a hard-WRAPPED verification URL is
|
|
13547
13561
|
// joined back into one line (the 2026-06-18 "code=t" truncation bug). Falls
|
|
@@ -13610,7 +13624,6 @@ export async function startServer(options) {
|
|
|
13610
13624
|
.catch(() => { });
|
|
13611
13625
|
enrollmentWizard
|
|
13612
13626
|
.sweepFollowMeCompletions({
|
|
13613
|
-
credentialReady: (l) => !!l.configHome && fs.existsSync(path.join(l.configHome, '.claude.json')),
|
|
13614
13627
|
onValidated: (login, email) => {
|
|
13615
13628
|
// Upsert (D5): a re-auth of an EXISTING pool account updates it back to
|
|
13616
13629
|
// active; only a genuinely-new account is added (add() refuses dup ids).
|