wicked-crew 0.10.2 → 0.12.0
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/api/capture.d.ts +2 -2
- package/dist/api/delivery-assurance.d.ts +1 -6
- package/dist/api/delivery-assurance.d.ts.map +1 -1
- package/dist/api/delivery-assurance.js +4 -14
- package/dist/api/delivery-assurance.js.map +1 -1
- package/dist/api/governance-steering.d.ts +2 -2
- package/dist/api/mcp.d.ts +4 -4
- package/dist/api/recording.d.ts +29 -3
- package/dist/api/recording.d.ts.map +1 -1
- package/dist/api/recording.js +188 -0
- package/dist/api/recording.js.map +1 -1
- package/dist/api/roster-standing.d.ts +6 -2
- package/dist/api/roster-standing.d.ts.map +1 -1
- package/dist/api/roster-standing.js +62 -13
- package/dist/api/roster-standing.js.map +1 -1
- package/dist/api/routes.d.ts +115 -112
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +227 -6
- package/dist/api/routes.js.map +1 -1
- package/dist/api/seat-container-login.d.ts +60 -0
- package/dist/api/seat-container-login.d.ts.map +1 -0
- package/dist/api/seat-container-login.js +158 -0
- package/dist/api/seat-container-login.js.map +1 -0
- package/dist/api/seat-probe.d.ts +7 -0
- package/dist/api/seat-probe.d.ts.map +1 -1
- package/dist/api/seat-probe.js +27 -5
- package/dist/api/seat-probe.js.map +1 -1
- package/dist/api/seat-version.d.ts +8 -0
- package/dist/api/seat-version.d.ts.map +1 -1
- package/dist/api/seat-version.js +36 -4
- package/dist/api/seat-version.js.map +1 -1
- package/dist/api/server.d.ts.map +1 -1
- package/dist/api/server.js +4 -0
- package/dist/api/server.js.map +1 -1
- package/dist/api/testing.d.ts +4 -4
- package/dist/appearance/video-palette.d.ts +14 -0
- package/dist/appearance/video-palette.d.ts.map +1 -0
- package/dist/appearance/video-palette.js +75 -0
- package/dist/appearance/video-palette.js.map +1 -0
- package/dist/campaigns/routes.d.ts +2 -2
- package/dist/core/adapter.d.ts.map +1 -1
- package/dist/core/adapter.js +20 -0
- package/dist/core/adapter.js.map +1 -1
- package/dist/core/builtin-catalog.d.ts.map +1 -1
- package/dist/core/builtin-catalog.js +6 -1
- package/dist/core/builtin-catalog.js.map +1 -1
- package/dist/core/engine-roster.d.ts.map +1 -1
- package/dist/core/engine-roster.js +1 -0
- package/dist/core/engine-roster.js.map +1 -1
- package/dist/core/qe-acceptance-setting.d.ts +65 -0
- package/dist/core/qe-acceptance-setting.d.ts.map +1 -0
- package/dist/core/qe-acceptance-setting.js +93 -0
- package/dist/core/qe-acceptance-setting.js.map +1 -0
- package/dist/core/run-identity.d.ts.map +1 -1
- package/dist/core/run-identity.js +3 -2
- package/dist/core/run-identity.js.map +1 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -1
- package/dist/editors/pack-install.d.ts +36 -3
- package/dist/editors/pack-install.d.ts.map +1 -1
- package/dist/editors/pack-install.js +71 -6
- package/dist/editors/pack-install.js.map +1 -1
- package/dist/editors/registry.d.ts +153 -14
- package/dist/editors/registry.d.ts.map +1 -1
- package/dist/editors/registry.js +29 -4
- package/dist/editors/registry.js.map +1 -1
- package/dist/editors/surfaces.d.ts +48 -0
- package/dist/editors/surfaces.d.ts.map +1 -0
- package/dist/editors/surfaces.js +294 -0
- package/dist/editors/surfaces.js.map +1 -0
- package/dist/events/terminals.d.ts +42 -2
- package/dist/events/terminals.d.ts.map +1 -1
- package/dist/events/terminals.js +136 -3
- package/dist/events/terminals.js.map +1 -1
- package/dist/interactive/chat-events.d.ts +4 -3
- package/dist/interactive/chat-events.d.ts.map +1 -1
- package/dist/interactive/chat-events.js +7 -6
- package/dist/interactive/chat-events.js.map +1 -1
- package/dist/interactive/doc-grounding.d.ts.map +1 -1
- package/dist/interactive/doc-grounding.js +7 -1
- package/dist/interactive/doc-grounding.js.map +1 -1
- package/dist/interactive/draft-skill.d.ts +11 -4
- package/dist/interactive/draft-skill.d.ts.map +1 -1
- package/dist/interactive/draft-skill.js +42 -9
- package/dist/interactive/draft-skill.js.map +1 -1
- package/dist/projects/state-home-registry.d.ts.map +1 -1
- package/dist/projects/state-home-registry.js +2 -0
- package/dist/projects/state-home-registry.js.map +1 -1
- package/dist/studio/assets/index-1Ucvzihb.js +770 -0
- package/dist/studio/assets/index-B0u_f6HD.css +32 -0
- package/dist/studio/editors/wicked-deck/editor.json +88 -0
- package/dist/studio/editors/wicked-deck/index.html +230 -0
- package/dist/studio/editors/wicked-doc/editor.json +91 -0
- package/dist/studio/editors/wicked-doc/index.html +161 -0
- package/dist/studio/editors/wicked-page/editor.json +42 -4
- package/dist/studio/editors/wicked-page/index.html +25 -8
- package/dist/studio/editors/wicked-video/editor.json +271 -0
- package/dist/studio/editors/wicked-video/index.html +28 -0
- package/dist/studio/index.html +2 -2
- package/dist/studio/testid-inventory.json +3844 -1364
- package/dist/team/routes.d.ts +2 -2
- package/endpoint-manifest.json +56 -1
- package/package.json +3 -3
- package/dist/studio/assets/index-C6DLorst.css +0 -32
- package/dist/studio/assets/index-CAhuFWeA.js +0 -609
package/dist/api/routes.js
CHANGED
|
@@ -8,6 +8,7 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
8
8
|
import { promises as fsp } from 'node:fs';
|
|
9
9
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { videoPalette } from '../appearance/video-palette.js';
|
|
11
12
|
import { CampaignsUnsupportedError, ChatUnsupportedError, CoreAdapter, ElicitationUnsupportedError, GovernanceReplayUnsupportedError, PlanLaunchUnsupportedError, SteeringUnsupportedError, addonSupportsQeOverride, addonSupportsReducedAssurance, humanGatePhaseIds, settingsFilePath } from '../core/adapter.js';
|
|
12
13
|
import { codeGraphDb, codeGraphErrorStatus, requirementsGraph } from '../core/repoPaths.js';
|
|
13
14
|
import { resolveCursorUnit } from '../core/cursor.js';
|
|
@@ -25,6 +26,7 @@ import { defaultLaunchRoster, markOptIn } from '../core/opt-in-seats.js';
|
|
|
25
26
|
import { chatSeatAdmission } from './seat-standing.js';
|
|
26
27
|
import { seatGovernanceDiagnostics, seatGovernanceWarnings, } from './seat-governance.js';
|
|
27
28
|
import { ROSTER_REFRESH_WAIT_MS, rosterWithStandingFactory } from './roster-standing.js';
|
|
29
|
+
import { SeatContainerStatus, isContainerLoginCli, isContainerSeatFloor, loginDetail, runSeatLogin, seatLoginDepsFor, } from './seat-container-login.js';
|
|
28
30
|
import { ChatTurnIndex } from './chat-turns.js';
|
|
29
31
|
import { ChatScopeIndex, chatScopeDeps, chatScopeStatement, prepareChatScratch, removeChatScratch, namedKindRefusal, resolveChatScope, } from './chat-scope.js';
|
|
30
32
|
import { ASK_TURN_BUDGET_SECS, AskPathIndex, answerStep, isAskTurn } from './ask-paths.js';
|
|
@@ -46,7 +48,8 @@ import { isSteeringAuthorRun, landSteeringProposal } from './steering-landing.js
|
|
|
46
48
|
import { inventoryComplete, parseInventoryClaims } from '../core/inventory-claim.js';
|
|
47
49
|
import { ApproveEditSchema, approveEdited, captureLaunchRoots, editInFlight, FileProposalSchema, registerCaptureRoutes } from './capture.js';
|
|
48
50
|
import { registerTestingRoutes } from './testing.js';
|
|
49
|
-
import { DEMO_PRESET, demoLaunchRoots, registerDemoRoutes, registerWalkthroughRoutes, walkthroughAcceptance } from './recording.js';
|
|
51
|
+
import { DEMO_PRESET, demoLaunchRoots, registerDemoDocumentRoute, registerDemoRoutes, registerWalkthroughRoutes, walkthroughAcceptance } from './recording.js';
|
|
52
|
+
import { projectDocsRoot } from '../interactive/project-root.js';
|
|
50
53
|
import { registerSkillsRoutes } from './skills.js';
|
|
51
54
|
import { registerMcpRoutes } from './mcp.js';
|
|
52
55
|
import { McpPolicies } from '../mcp/policies.js';
|
|
@@ -57,7 +60,8 @@ import { BASE_SKILL_POLICIES, BASE_SKILL_REF_SHAPE, baseSkillRemedy } from '../s
|
|
|
57
60
|
import { REQUIRED_GARDEN_VERSION } from '../skills/plugin-source.js';
|
|
58
61
|
import { engineRosterJson, noEligibleSeatBody, parseNoEligibleSeat } from '../core/engine-roster.js';
|
|
59
62
|
import { launchAssuranceNotice } from '../core/assurance.js';
|
|
60
|
-
import {
|
|
63
|
+
import { launchRequiresQe, mayRequireQe, qeAcceptanceMode, qeSettingSkipReason } from '../core/qe-acceptance-setting.js';
|
|
64
|
+
import { aggregateReceipt, postHocDeliveryAssurance, qeAcceptanceFromView, qeAcceptanceRefusal, receiptOf, qeAcceptanceNotRequired, qeDecisionBypasses, qeDecisionOf, unreadableQeAcceptance, } from './delivery-assurance.js';
|
|
61
65
|
import { ProjectSettingsStore } from '../projects/settings.js';
|
|
62
66
|
import { isHumanOperator } from './watch-routes.js';
|
|
63
67
|
import { boundOrigin, InteractiveBridgePool } from '../interactive/bridge-pool.js';
|
|
@@ -1077,6 +1081,9 @@ runtime = {}) {
|
|
|
1077
1081
|
// spellings where the contract has no name, statusCodes list every code the route answers on
|
|
1078
1082
|
// purpose. Declared on the highest-traffic run-lifecycle routes first; the manifest records
|
|
1079
1083
|
// null / [] for the rest ("where declared", never invented). See src/api/endpoint-manifest.ts.
|
|
1084
|
+
// The daemon's QE acceptance setting as read now ('required' when the settings cannot be read:
|
|
1085
|
+
// fail closed, never off by accident).
|
|
1086
|
+
const qeAcceptanceNow = async () => qeAcceptanceMode(await Promise.resolve().then(() => adapter.getSettings()).catch(() => null));
|
|
1080
1087
|
app.get(`${V}/health`, { config: { manifest: { statusCodes: [200] } } }, async () => {
|
|
1081
1088
|
const ping = await enginePing();
|
|
1082
1089
|
// F-E2E-030: what this deployment can keep. A composer reads `capabilities.deliverGate`
|
|
@@ -1103,6 +1110,9 @@ runtime = {}) {
|
|
|
1103
1110
|
// QE-IN-APP-WORKFLOWS: `LaunchRunBody.skipQeAcceptance` / `forceQeAcceptance` reach an addon
|
|
1104
1111
|
// that carries the QE decision; never an invented capability.
|
|
1105
1112
|
qeAcceptanceOverride: typeof adapter.engineCapabilities === 'function' && addonSupportsQeOverride(),
|
|
1113
|
+
// api-types 0.120.0: the daemon's QE acceptance setting, read per probe ('off' = every launch
|
|
1114
|
+
// that requires QE acceptance skips it, labelled with the setting).
|
|
1115
|
+
qeAcceptance: await qeAcceptanceNow(),
|
|
1106
1116
|
};
|
|
1107
1117
|
// wicked-core#411 / crew#497: the state-home blocker rides the health probe as a WARNING. The
|
|
1108
1118
|
// daemon still SERVES (status stays ok — studio must load and show the blocker) but refuses to
|
|
@@ -1267,7 +1277,9 @@ runtime = {}) {
|
|
|
1267
1277
|
// (built over the same `seatHealth`); a directly-driven route set builds an equivalent one here.
|
|
1268
1278
|
// `POST /chats` seats its defaults through the SAME standing, so the roster and the chat never
|
|
1269
1279
|
// disagree about a seat.
|
|
1270
|
-
const
|
|
1280
|
+
const seatLoginDeps = seatLoginDepsFor(adapter.dbPath, runtime.seatLoginExec);
|
|
1281
|
+
const containerStatus = runtime.rosterWithStanding?.containerStatus ?? new SeatContainerStatus(seatLoginDeps);
|
|
1282
|
+
const rosterWithStanding = runtime.rosterWithStanding ?? rosterWithStandingFactory({ seatHealth, signedIn, containerStatus });
|
|
1271
1283
|
// A3b (DES-seats-page): `?refresh=<key>` re-runs that seat's sign-in check now and waits for it,
|
|
1272
1284
|
// bounded, so the Agents page's "I've signed in: check again" reads the seat's fresh answer, not
|
|
1273
1285
|
// the probe cache's 30-second-old one. A read with no parameter never waits.
|
|
@@ -1819,15 +1831,23 @@ runtime = {}) {
|
|
|
1819
1831
|
// F-E2E-030: only the explicit `'auto'` opts out of the engine's deliver gate. `'human'` and
|
|
1820
1832
|
// an omitted field both leave `autoDeliver` off the input — the gate is the engine's default,
|
|
1821
1833
|
// so the wire never has to say "gate me" to be gated.
|
|
1834
|
+
// The daemon's QE acceptance setting, read ONCE for this launch: the deliverGate check below and
|
|
1835
|
+
// the skip handed to the engine come from the same reading (core/qe-acceptance-setting.ts).
|
|
1836
|
+
// An addon without the QE decision enforces none, so the setting has nothing to skip on it.
|
|
1837
|
+
const qeMode = addonSupportsQeOverride() ? await qeAcceptanceNow() : 'required';
|
|
1838
|
+
const qeRequired = b.deliverGate === 'auto' || qeMode === 'off' ? await launchRequiresQe(adapter, b.workflow, b.projectId) : false;
|
|
1839
|
+
const settingSkip = qeSettingSkipReason(qeMode, { skipQeAcceptanceReason: b.skipQeAcceptance?.reason, forceQeAcceptance: b.forceQeAcceptance }, qeRequired);
|
|
1822
1840
|
if (b.deliverGate === 'auto') {
|
|
1823
1841
|
// wicked-core#850 EX-03: a workflow that requires QE acceptance delivers only through the deliver
|
|
1824
1842
|
// gate, where crew checks the verdict. Unattended delivery would push before anyone could.
|
|
1825
1843
|
// QE-IN-APP-WORKFLOWS: an explicit operator skip leaves no verdict to check, so it may. A name
|
|
1826
1844
|
// the engine resolves to a built-in preset that requires QE acceptance is judged by that preset
|
|
1827
1845
|
// (the engine resolves a preset before any def, so a def of the same name never speaks for it).
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1846
|
+
// The daemon's `qeAcceptance: 'off'` setting skips it the same way (resolved once, above, and
|
|
1847
|
+
// handed to the engine with this launch), unless this launch forces QE acceptance.
|
|
1848
|
+
// Refused on the CONSERVATIVE reading (a built-in QE name or a QE def counts even when a project
|
|
1849
|
+
// preset shadows it now): only an explicit or setting skip lets QE work deliver unattended.
|
|
1850
|
+
if (mayRequireQe(adapter, b.workflow, qeRequired) && b.skipQeAcceptance === undefined && settingSkip === undefined) {
|
|
1831
1851
|
return reply.code(400).send({
|
|
1832
1852
|
error: `workflow ${b.workflow} requires QE acceptance (required_instruments: qe_acceptance), so it delivers only ` +
|
|
1833
1853
|
'through the deliver gate, where the verdict is checked — deliverGate: "auto" cannot apply to it ' +
|
|
@@ -1909,6 +1929,8 @@ runtime = {}) {
|
|
|
1909
1929
|
// when sent, never inferred.
|
|
1910
1930
|
if (b.skipQeAcceptance !== undefined)
|
|
1911
1931
|
input.skipQeAcceptanceReason = b.skipQeAcceptance.reason;
|
|
1932
|
+
else if (settingSkip !== undefined)
|
|
1933
|
+
input.skipQeAcceptanceReason = settingSkip;
|
|
1912
1934
|
if (b.forceQeAcceptance === true)
|
|
1913
1935
|
input.forceQeAcceptance = true;
|
|
1914
1936
|
// DES-L9 / crew#550 — REVISION: `revisesPr` names an OPEN same-repository pull request whose
|
|
@@ -5126,6 +5148,159 @@ runtime = {}) {
|
|
|
5126
5148
|
return reply.code(201).send({ terminalId: termId, cli, action });
|
|
5127
5149
|
});
|
|
5128
5150
|
}
|
|
5151
|
+
const containerLoginOutcomes = new Map();
|
|
5152
|
+
const containerSeat = (cli) => isContainerSeatFloor() && isContainerLoginCli(cli) && rosterWithStanding().some((seat) => seat.key === cli);
|
|
5153
|
+
const loginError = (stderr) => loginDetail(stderr) || 'seat-login failed';
|
|
5154
|
+
// One sign-in/sign-out at a time per seat, and a generation every new sign-in and every sign-out
|
|
5155
|
+
// bumps (agy crew-r1): an abandoned login terminal that exits later never ingests over a newer
|
|
5156
|
+
// sign-in or a sign-out, and an ingest in flight finishes before a sign-out runs.
|
|
5157
|
+
const loginGeneration = new Map();
|
|
5158
|
+
const seatOps = new Map();
|
|
5159
|
+
const serial = (cli, op) => {
|
|
5160
|
+
const next = (seatOps.get(cli) ?? Promise.resolve()).then(op, op);
|
|
5161
|
+
seatOps.set(cli, next.catch(() => undefined));
|
|
5162
|
+
return next;
|
|
5163
|
+
};
|
|
5164
|
+
const bump = (cli) => {
|
|
5165
|
+
const g = (loginGeneration.get(cli) ?? 0) + 1;
|
|
5166
|
+
loginGeneration.set(cli, g);
|
|
5167
|
+
return g;
|
|
5168
|
+
};
|
|
5169
|
+
// The one sign-in per seat, reserved synchronously from the request's first await until its
|
|
5170
|
+
// ingest settles (codex crew-r1/r2: two sessions would share the engine's login session), and
|
|
5171
|
+
// released only by its owner (the generation that reserved it).
|
|
5172
|
+
const openLogins = new Map();
|
|
5173
|
+
const release = (cli, generation) => {
|
|
5174
|
+
if (openLogins.get(cli) === generation)
|
|
5175
|
+
openLogins.delete(cli);
|
|
5176
|
+
};
|
|
5177
|
+
app.post(`${V}/seats/:cli/container-login`, { config: { manifest: { requestType: 'SeatSessionBody', responseType: 'SeatContainerLoginResponse', statusCodes: [201, 400, 404, 502] } } }, async (req, reply) => {
|
|
5178
|
+
const { cli } = req.params;
|
|
5179
|
+
if (!containerSeat(cli))
|
|
5180
|
+
return reply.code(404).send({ error: `container login unavailable for seat: ${cli}` });
|
|
5181
|
+
const parsed = SeatSessionSchema.safeParse(req.body ?? {});
|
|
5182
|
+
if (!parsed.success)
|
|
5183
|
+
return reply.code(400).send(invalidBody(parsed.error, 'Invalid request body'));
|
|
5184
|
+
if (openLogins.has(cli)) {
|
|
5185
|
+
return reply.code(409).send({ error: `a sign-in is already open for ${cli}; finish or close it first` });
|
|
5186
|
+
}
|
|
5187
|
+
// Reserved BEFORE the first await (codex crew-r1): a sign-out that lands meanwhile wins.
|
|
5188
|
+
const generation = bump(cli);
|
|
5189
|
+
openLogins.set(cli, generation);
|
|
5190
|
+
containerLoginOutcomes.delete(cli);
|
|
5191
|
+
const out = await serial(cli, () => runSeatLogin('argv', cli, seatLoginDeps));
|
|
5192
|
+
if (out.rc !== 0) {
|
|
5193
|
+
release(cli, generation);
|
|
5194
|
+
return reply.code(502).send({ error: loginError(out.stderr) });
|
|
5195
|
+
}
|
|
5196
|
+
if (loginGeneration.get(cli) !== generation) {
|
|
5197
|
+
release(cli, generation);
|
|
5198
|
+
return reply.code(409).send({ error: `the ${cli} sign-in was superseded (a sign-out or another sign-in)` });
|
|
5199
|
+
}
|
|
5200
|
+
let argv;
|
|
5201
|
+
try {
|
|
5202
|
+
const value = JSON.parse(out.stdout);
|
|
5203
|
+
if (!Array.isArray(value) || value.length === 0 || !value.every((arg) => typeof arg === 'string' && arg.length > 0)) {
|
|
5204
|
+
throw new Error('seat-login argv must be a non-empty array of non-empty strings');
|
|
5205
|
+
}
|
|
5206
|
+
argv = value;
|
|
5207
|
+
}
|
|
5208
|
+
catch (err) {
|
|
5209
|
+
release(cli, generation);
|
|
5210
|
+
return reply.code(502).send({ error: message(err) });
|
|
5211
|
+
}
|
|
5212
|
+
let termId = null;
|
|
5213
|
+
let completed = false;
|
|
5214
|
+
const exitedEarly = new Set();
|
|
5215
|
+
const ingest = () => serial(cli, async () => {
|
|
5216
|
+
// A newer sign-in or a sign-out superseded this terminal: nothing to ingest or record.
|
|
5217
|
+
if (loginGeneration.get(cli) !== generation)
|
|
5218
|
+
return;
|
|
5219
|
+
// Refusals the OLD credential earned up to now are superseded by this sign-in (codex
|
|
5220
|
+
// crew-r2: a 401 during the interactive login must not outlive it).
|
|
5221
|
+
const cutoff = Date.now();
|
|
5222
|
+
const result = await runSeatLogin('ingest', cli, seatLoginDeps);
|
|
5223
|
+
// The broker may have changed whatever happens next: the next reading is its own.
|
|
5224
|
+
containerStatus.invalidate(cli);
|
|
5225
|
+
rosterWithStanding.reprobe?.(cli);
|
|
5226
|
+
const outcome = { cli, ok: result.rc === 0, at: new Date().toISOString() };
|
|
5227
|
+
if (result.rc !== 0)
|
|
5228
|
+
outcome.error = loginError(result.stderr);
|
|
5229
|
+
else {
|
|
5230
|
+
try {
|
|
5231
|
+
const value = JSON.parse(result.stdout);
|
|
5232
|
+
if (value === null || !Array.isArray(value.files) || !value.files.every((file) => typeof file === 'string') ||
|
|
5233
|
+
!(value.expires_at === null || (typeof value.expires_at === 'number' && Number.isFinite(value.expires_at)))) {
|
|
5234
|
+
throw new Error('invalid seat-login ingest response');
|
|
5235
|
+
}
|
|
5236
|
+
// Only the public ingest fields cross the wire; raw stdout may contain credentials.
|
|
5237
|
+
outcome.files = value.files;
|
|
5238
|
+
outcome.expires_at = value.expires_at;
|
|
5239
|
+
}
|
|
5240
|
+
catch {
|
|
5241
|
+
outcome.ok = false;
|
|
5242
|
+
outcome.error = 'invalid seat-login ingest response';
|
|
5243
|
+
}
|
|
5244
|
+
}
|
|
5245
|
+
if (outcome.ok) {
|
|
5246
|
+
seatHealth.clearAuthFailureBefore(cli, cutoff);
|
|
5247
|
+
seatHealth.clearRecentBench(cli);
|
|
5248
|
+
}
|
|
5249
|
+
// A sign-out (or a newer sign-in) while this ingest ran owns the outcome now.
|
|
5250
|
+
if (loginGeneration.get(cli) === generation)
|
|
5251
|
+
containerLoginOutcomes.set(cli, outcome);
|
|
5252
|
+
}).finally(() => release(cli, generation));
|
|
5253
|
+
const done = () => {
|
|
5254
|
+
if (completed)
|
|
5255
|
+
return;
|
|
5256
|
+
completed = true;
|
|
5257
|
+
stop();
|
|
5258
|
+
void ingest();
|
|
5259
|
+
};
|
|
5260
|
+
// Subscribe before spawn: an immediate exit still needs exactly one ingest.
|
|
5261
|
+
const stop = adapter.onEvent((event) => {
|
|
5262
|
+
if (event.type !== 'terminalExited' || typeof event.id !== 'string')
|
|
5263
|
+
return;
|
|
5264
|
+
if (termId === null)
|
|
5265
|
+
exitedEarly.add(event.id);
|
|
5266
|
+
else if (event.id === termId)
|
|
5267
|
+
done();
|
|
5268
|
+
});
|
|
5269
|
+
try {
|
|
5270
|
+
termId = await adapter.openTerminal(homedir(), argv, parsed.data.cols ?? 100, parsed.data.rows ?? 30, true);
|
|
5271
|
+
}
|
|
5272
|
+
catch (err) {
|
|
5273
|
+
stop();
|
|
5274
|
+
release(cli, generation);
|
|
5275
|
+
return reply.code(400).send({ error: message(err) });
|
|
5276
|
+
}
|
|
5277
|
+
if (exitedEarly.has(termId))
|
|
5278
|
+
done();
|
|
5279
|
+
return reply.code(201).send({ terminalId: termId, cli, action: 'sign_in' });
|
|
5280
|
+
});
|
|
5281
|
+
app.get(`${V}/seats/:cli/container-login`, { config: { manifest: { responseType: 'SeatContainerLoginOutcome', statusCodes: [200, 404] } } }, async (req, reply) => {
|
|
5282
|
+
const { cli } = req.params;
|
|
5283
|
+
const outcome = containerSeat(cli) ? containerLoginOutcomes.get(cli) : undefined;
|
|
5284
|
+
if (outcome === undefined)
|
|
5285
|
+
return reply.code(404).send({ error: `no container login outcome for seat: ${cli}` });
|
|
5286
|
+
return reply.send(outcome);
|
|
5287
|
+
});
|
|
5288
|
+
app.post(`${V}/seats/:cli/container-logout`, { config: { manifest: { responseType: 'SeatContainerLogoutResponse', statusCodes: [200, 404, 502] } } }, async (req, reply) => {
|
|
5289
|
+
const { cli } = req.params;
|
|
5290
|
+
if (!containerSeat(cli))
|
|
5291
|
+
return reply.code(404).send({ error: `container logout unavailable for seat: ${cli}` });
|
|
5292
|
+
bump(cli);
|
|
5293
|
+
const out = await serial(cli, async () => {
|
|
5294
|
+
containerLoginOutcomes.delete(cli);
|
|
5295
|
+
return runSeatLogin('sign-out', cli, seatLoginDeps);
|
|
5296
|
+
});
|
|
5297
|
+
// Whatever the engine managed before failing, the next reading is the broker's (codex crew-r1).
|
|
5298
|
+
containerStatus.invalidate(cli);
|
|
5299
|
+
rosterWithStanding.reprobe?.(cli);
|
|
5300
|
+
if (out.rc !== 0)
|
|
5301
|
+
return reply.code(502).send({ error: loginError(out.stderr) });
|
|
5302
|
+
return reply.send({ cli, action: 'sign_out' });
|
|
5303
|
+
});
|
|
5129
5304
|
// Resize a live terminal's PTY.
|
|
5130
5305
|
app.post(`${V}/terminals/:id/resize`, async (req, reply) => {
|
|
5131
5306
|
const { id } = req.params;
|
|
@@ -5163,6 +5338,9 @@ runtime = {}) {
|
|
|
5163
5338
|
// additive `path` (api-types 0.38.0 `SettingsResponse.path?`; adjudicated §4.6 — `path`, not
|
|
5164
5339
|
// `settings_path`).
|
|
5165
5340
|
app.get(`${V}/settings`, async () => ({ settings: await adapter.getSettings(), path: settingsFilePath() }));
|
|
5341
|
+
// The demo video's palette from the studio skin (operator 2026-10-14): read-only, derived from `studio.appearance`;
|
|
5342
|
+
// garden's demo stage and interactive's re-cuts read it as their default colours.
|
|
5343
|
+
app.get(`${V}/appearance/video-palette`, { config: { manifest: { responseType: 'VideoPaletteResponse', statusCodes: [200] } } }, async () => videoPalette((await adapter.getSettings())['studio.appearance']));
|
|
5166
5344
|
app.put(`${V}/settings`, async (req, reply) => {
|
|
5167
5345
|
const patch = req.body;
|
|
5168
5346
|
if (typeof patch !== 'object' || patch === null || Array.isArray(patch)) {
|
|
@@ -5278,6 +5456,19 @@ runtime = {}) {
|
|
|
5278
5456
|
.send({ error: "deliverDefault must be 'pr' or 'none'" });
|
|
5279
5457
|
}
|
|
5280
5458
|
}
|
|
5459
|
+
// qeAcceptance (api-types 0.120.0): 'required' | 'off' only. 'off' skips QE acceptance on every
|
|
5460
|
+
// launch that requires it (labelled with the setting), so a typo must be a 400, never a dropped key.
|
|
5461
|
+
// Turning it off waives an assurance instrument on every launch after it, so only a PERSON may
|
|
5462
|
+
// change it (the predicate the watch switches use); a worker's bearer gets 403.
|
|
5463
|
+
if (Object.hasOwn(patch, 'qeAcceptance')) {
|
|
5464
|
+
const q = patch.qeAcceptance;
|
|
5465
|
+
if (q !== 'required' && q !== 'off') {
|
|
5466
|
+
return reply.code(400).send({ error: "qeAcceptance must be 'required' or 'off'" });
|
|
5467
|
+
}
|
|
5468
|
+
if (!isHumanOperator(actorOf(req))) {
|
|
5469
|
+
return reply.code(403).send({ error: 'Only a person can change QE acceptance (qeAcceptance); a worker or system actor cannot' });
|
|
5470
|
+
}
|
|
5471
|
+
}
|
|
5281
5472
|
// onboardingAutoCapture (crew#552): whether a completed onboarding chains capture-learnings.
|
|
5282
5473
|
if (Object.hasOwn(patch, 'onboardingAutoCapture') && typeof patch.onboardingAutoCapture !== 'boolean') {
|
|
5283
5474
|
return reply.code(400).send({ error: 'onboardingAutoCapture must be true or false' });
|
|
@@ -5362,6 +5553,7 @@ runtime = {}) {
|
|
|
5362
5553
|
'workerStallEscalateAction',
|
|
5363
5554
|
'workerStallMaxEscalations',
|
|
5364
5555
|
'deliverDefault',
|
|
5556
|
+
'qeAcceptance',
|
|
5365
5557
|
'onboardingAutoCapture',
|
|
5366
5558
|
'deliverIdentityLogin',
|
|
5367
5559
|
'deliverIdentityByRepo',
|
|
@@ -6035,6 +6227,35 @@ runtime = {}) {
|
|
|
6035
6227
|
dropDocLedgerRows: runtime.dropDocLedgerRows ?? (() => ({ ok: true, removed_keys: [] })),
|
|
6036
6228
|
log: (m) => app.log.warn(m),
|
|
6037
6229
|
});
|
|
6230
|
+
// ── A demo run's video as an editable document (the video editor re-cuts it through E1's per-doc API) ──
|
|
6231
|
+
registerDemoDocumentRoute(app, adapter, {
|
|
6232
|
+
bridgeFor: async (projectId) => {
|
|
6233
|
+
const root = await projectDocsRoot(adapter, projectSettings, projectId);
|
|
6234
|
+
if (root === null)
|
|
6235
|
+
return null;
|
|
6236
|
+
const bridge = await interactiveBridges.ensure(root);
|
|
6237
|
+
return async (method, path, body) => {
|
|
6238
|
+
const raw = Buffer.isBuffer(body);
|
|
6239
|
+
const res = await fetch(`http://${bridge.host}:${bridge.port}${path}`, {
|
|
6240
|
+
method,
|
|
6241
|
+
headers: { 'content-type': raw ? 'application/octet-stream' : 'application/json' },
|
|
6242
|
+
body: raw ? new Uint8Array(body) : JSON.stringify(body),
|
|
6243
|
+
// A segment upload or the import's render (ffmpeg) can take minutes on a loaded host.
|
|
6244
|
+
signal: AbortSignal.timeout(600_000),
|
|
6245
|
+
});
|
|
6246
|
+
let parsed = {};
|
|
6247
|
+
try {
|
|
6248
|
+
const j = await res.json();
|
|
6249
|
+
if (j !== null && typeof j === 'object' && !Array.isArray(j))
|
|
6250
|
+
parsed = j;
|
|
6251
|
+
}
|
|
6252
|
+
catch {
|
|
6253
|
+
parsed = {};
|
|
6254
|
+
}
|
|
6255
|
+
return { status: res.status, body: parsed };
|
|
6256
|
+
};
|
|
6257
|
+
},
|
|
6258
|
+
});
|
|
6038
6259
|
// ── Project-attributed docs list (crew#472) ──
|
|
6039
6260
|
// `GET /projects/:id/interactive/api/docs` relays the bridge's list and stamps each row with
|
|
6040
6261
|
// the mount's `projectId` — the attribution the bridge cannot know. Same static-over-wildcard
|