@zq-silk/yui 1.1.2 → 2.1.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/ARCHITECTURE.md +2 -1
- package/ARCHITECTURE.zh-CN.md +2 -1
- package/README.md +2 -0
- package/dist/cli/commandCatalog.js +344 -324
- package/dist/cli/commandDiscovery.js +69 -0
- package/dist/cli/completion.js +22 -227
- package/dist/cli/dynamicCompletion.js +12 -6
- package/dist/cli/invocationAuthority.js +2 -2
- package/dist/cli/invocationRouter.js +18 -7
- package/dist/cli/managedDiagnostics.js +2 -1
- package/dist/cli/updatePorts.js +0 -5
- package/dist/cli.js +184 -130
- package/dist/commands/capabilityCommands.js +5 -5
- package/dist/commands/controllerCommands.js +2 -1
- package/dist/commands/globalRoleCommands.js +86 -44
- package/dist/commands/grantCommands.js +48 -8
- package/dist/commands/taskCommands.js +157 -196
- package/dist/commands/taskContextCommand.js +29 -6
- package/dist/commands/taskFactCommands.js +10 -69
- package/dist/commands/taskInputCommands.js +26 -35
- package/dist/commands/taskPublicationCommands.js +2 -35
- package/dist/commands/taskPublicationVerifyCommand.js +1 -2
- package/dist/commands/taskUpstreamCommands.js +2 -1
- package/dist/context/runContextPack.js +8 -29
- package/dist/context/sessionBootstrapManifest.js +5 -101
- package/dist/context/taskContext.js +104 -41
- package/dist/controller/clientRuntime.js +16 -8
- package/dist/controller/fileSchedulerStoreAdapter.js +24 -0
- package/dist/controller/runtime.js +6 -0
- package/dist/core/controllerClient.js +46 -21
- package/dist/core/protocol.js +12 -7
- package/dist/doctor/doctor.js +4 -3
- package/dist/errors/cliError.js +4 -4
- package/dist/errors/cliFailure.js +213 -0
- package/dist/executor/fileRoleLaunchPlanner.js +1 -4
- package/dist/grant/capabilityGrant.js +13 -0
- package/dist/grant/taskAuthorization.js +127 -0
- package/dist/kernel/accessAssessment.js +1 -0
- package/dist/kernel/builtinCapabilities.js +88 -9
- package/dist/kernel/capabilityRegistry.js +64 -19
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/nativeAgent/agent.js +173 -0
- package/dist/nativeAgent/contracts.js +1 -0
- package/dist/nativeAgent/demo.js +29 -0
- package/dist/nativeAgent/index.js +3 -0
- package/dist/nativeAgent/mockProvider.js +43 -0
- package/dist/nativeAgent/textTools.js +156 -0
- package/dist/nativeAgent/validation.js +95 -0
- package/dist/output/boundedRead.js +118 -0
- package/dist/plugins/pluginService.js +98 -8
- package/dist/release/releaseWorkflowEngine.js +8 -1
- package/dist/resources/projectResourceService.js +23 -1
- package/dist/runtime/managedCaller.js +2 -2
- package/dist/runtime/managedIdentity.js +6 -0
- package/dist/runtime/runtimeCoherence.js +10 -8
- package/dist/storage/sqliteSchema.js +10 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/taskStore.js +3 -0
- package/dist/surface/surfaceContributions.js +3 -0
- package/dist/task/leaderArchive.js +111 -0
- package/dist/task/leaderArchiveAuthority.js +24 -0
- package/dist/task/taskAuthority.js +5 -10
- package/dist/tmux/commandExecutor.js +7 -5
- package/dist/web/assets/assetManifest.js +38 -22
- package/dist/web/assets/client/api.js +115 -0
- package/dist/web/assets/client/app.js +555 -863
- package/dist/web/assets/client/components.js +323 -897
- package/dist/web/assets/client/detail.js +333 -0
- package/dist/web/assets/client/dock.js +225 -0
- package/dist/web/assets/client/dom.js +55 -3
- package/dist/web/assets/client/evidence.js +172 -0
- package/dist/web/assets/client/format.js +25 -14
- package/dist/web/assets/client/forms.js +230 -0
- package/dist/web/assets/client/i18n.js +1020 -791
- package/dist/web/assets/client/overview.js +115 -0
- package/dist/web/assets/client/records.js +63 -0
- package/dist/web/assets/client/sections.js +313 -0
- package/dist/web/assets/client/sidebar.js +117 -0
- package/dist/web/assets/client/theme.js +61 -22
- package/dist/web/assets/icons.js +39 -0
- package/dist/web/assets/shell.js +124 -118
- package/dist/web/assets/styles/base.js +41 -0
- package/dist/web/assets/styles/components.js +167 -0
- package/dist/web/assets/styles/layout.js +49 -77
- package/dist/web/assets/styles/markdown.js +16 -24
- package/dist/web/assets/styles/responsive.js +33 -44
- package/dist/web/assets/styles/tokens.js +65 -89
- package/dist/web/assets/styles/views.js +289 -0
- package/dist/web/webServer.js +2 -1
- package/docs/architecture/README.md +1 -0
- package/docs/architecture/README.zh-CN.md +1 -0
- package/docs/cli-information-contract.md +107 -0
- package/docs/cli-information-contract.zh-CN.md +81 -0
- package/docs/managed-turn-and-session-runtime.md +9 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +6 -0
- package/docs/plugin-sdk.md +7 -3
- package/docs/plugin-sdk.zh-CN.md +5 -2
- package/docs/release-workflow.md +42 -9
- package/docs/release-workflow.zh-CN.md +30 -7
- package/docs/storage-baseline.md +5 -0
- package/docs/testing/verification-levels.md +2 -2
- package/i18n/README.zh-CN.md +2 -0
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +8 -1
- package/skills/yui-leader/references/authorization.md +64 -0
- package/skills/yui-leader/references/execution.md +18 -4
- package/skills/yui-leader/references/task-plugins.md +4 -2
- package/skills/yui-operator/SKILL.md +9 -2
- package/skills/yui-runtime/SKILL.md +77 -9
- package/skills/yui-runtime/references/publication.md +3 -1
- package/dist/web/assets/client/taskSummary.js +0 -350
- package/dist/web/assets/client/taskSurface.js +0 -616
- package/dist/web/assets/client/view.js +0 -608
- package/dist/web/assets/styles/cards.js +0 -247
- package/dist/web/assets/styles/widgets.js +0 -168
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createTaskEvent } from "../event/taskEvent.js";
|
|
3
|
+
import { runTaskCommand } from "../commands/taskCommands.js";
|
|
4
|
+
import { archiveExecutionChecks, archiveSettlementChecks } from "./archivePreflight.js";
|
|
5
|
+
import { admitLeaderArchive, assertLeaderArchiveAdmission } from "./leaderArchiveAuthority.js";
|
|
6
|
+
import { assertTaskRemoteDeliveryIntegrated, createTaskRemoteDeliveryProof } from "./remoteDeliveryService.js";
|
|
7
|
+
import { selectNewTaskRoleSession } from "../executor/agentExecutor.js";
|
|
8
|
+
/** A single foreground Controller operation survives stopping its requesting
|
|
9
|
+
* Session. It has no retry worker: uncertain receipts are inspected, not
|
|
10
|
+
* replayed. All existing settlement, delivery and cleanup checks remain. */
|
|
11
|
+
export async function archiveLeaderTask(store, coordinator, taskId, environment, request) {
|
|
12
|
+
const inputDigest = createHash("sha256").update(JSON.stringify(request)).digest("hex");
|
|
13
|
+
const previous = store.listEvents(taskId).find(event => event.type === "task.leader-archive-started" && event.payload.requestId === request.requestId);
|
|
14
|
+
if (previous) {
|
|
15
|
+
if (previous.payload.inputDigest !== inputDigest)
|
|
16
|
+
throw new Error("Archive request id names different input.");
|
|
17
|
+
const result = store.listEvents(taskId).find(event => event.type === "task.leader-archive-result" && event.payload.requestId === request.requestId);
|
|
18
|
+
return { taskId, requestId: request.requestId, status: result?.payload.status ?? "unknown",
|
|
19
|
+
detail: result?.payload.detail, replayed: true };
|
|
20
|
+
}
|
|
21
|
+
const admission = admitLeaderArchive(store, taskId, environment, request.sourceMessage, request.purpose);
|
|
22
|
+
const task = store.getTask(taskId);
|
|
23
|
+
const settlement = archiveSettlementChecks(store, task);
|
|
24
|
+
if (settlement.length)
|
|
25
|
+
throw new Error(`Archive is unsettled: ${JSON.stringify(settlement)}`);
|
|
26
|
+
const session = store.getTaskRoleSessionSet(taskId, "leader");
|
|
27
|
+
const ownInput = session?.providerBinding?.run;
|
|
28
|
+
// Only the requesting no-Run Leader may be stopped as part of this action.
|
|
29
|
+
// Another Run, Job, unknown input, or then handoff is not archive authority.
|
|
30
|
+
const ownResources = `role:${taskId}/leader`;
|
|
31
|
+
const execution = archiveExecutionChecks(store, taskId).filter(check => !(check.resource === ownResources
|
|
32
|
+
&& ownInput?.runId === undefined && ownInput?.status === "accepted"
|
|
33
|
+
&& ["unresolved-provider-input", "unresolved-mailbox"].includes(check.reason)));
|
|
34
|
+
if (execution.length)
|
|
35
|
+
throw new Error(`Archive execution is unresolved: ${JSON.stringify(execution)}`);
|
|
36
|
+
if (store.listMessages(taskId).some(message => message.interruptThen?.targetNativeSessionId === admission.source.nativeSessionId
|
|
37
|
+
&& message.continuation === undefined && message.interruptThen.notDeliveredReason === undefined)) {
|
|
38
|
+
throw new Error("Settle the original input handoff before archive.");
|
|
39
|
+
}
|
|
40
|
+
let candidate = null;
|
|
41
|
+
const workspace = store.getTaskWorkspace(taskId);
|
|
42
|
+
if (task.projectBindings.length && workspace) {
|
|
43
|
+
const snapshot = await coordinator.preparer.snapshotDirectTaskMain(workspace, task.projectBindings.map(binding => binding.projectId));
|
|
44
|
+
candidate = { schemaVersion: 1, projects: snapshot.projects.map(project => ({
|
|
45
|
+
projectId: project.projectId, commit: project.headCommit
|
|
46
|
+
})) };
|
|
47
|
+
}
|
|
48
|
+
const proof = createTaskRemoteDeliveryProof(store, task, candidate);
|
|
49
|
+
assertTaskRemoteDeliveryIntegrated(proof.delivery);
|
|
50
|
+
for (const owned of store.listManagedWorkspaces(taskId)) {
|
|
51
|
+
const disposition = owned.owner.type === "work-item"
|
|
52
|
+
&& store.getWorkItem(taskId, owned.owner.workItemId)?.status === "retired" ? "abandoned" : "integrated";
|
|
53
|
+
const checks = await coordinator.preparer.inspectWorkspaceCleanup(owned, disposition);
|
|
54
|
+
if (checks.some(check => !(owned.owner.type === "task" && check.reason === "git-worktree-registrations"))) {
|
|
55
|
+
throw new Error(`Archive workspace is not clean/removable: ${JSON.stringify(checks)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Reauthenticate after asynchronous inspection, before the first effect.
|
|
59
|
+
admitLeaderArchive(store, taskId, environment, request.sourceMessage, request.purpose);
|
|
60
|
+
assertLeaderArchiveAdmission(store, taskId, admission);
|
|
61
|
+
const record = (type, payload) => store.transaction(tx => tx.saveEvent(taskId, createTaskEvent(tx.nextEventId(taskId), taskId, type, { requestId: request.requestId, ...payload }, new Date())));
|
|
62
|
+
const started = store.transaction(tx => {
|
|
63
|
+
assertLeaderArchiveAdmission(tx, taskId, admission);
|
|
64
|
+
const events = tx.listEvents(taskId);
|
|
65
|
+
const prior = events.find(event => event.type === "task.leader-archive-started"
|
|
66
|
+
&& event.payload.requestId === request.requestId);
|
|
67
|
+
if (prior) {
|
|
68
|
+
if (prior.payload.inputDigest !== inputDigest)
|
|
69
|
+
throw new Error("Archive request id names different input.");
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const unsettled = events.find(event => event.type === "task.leader-archive-started"
|
|
73
|
+
&& !events.some(result => result.type === "task.leader-archive-result"
|
|
74
|
+
&& result.payload.requestId === event.payload.requestId));
|
|
75
|
+
if (unsettled)
|
|
76
|
+
throw new Error(`Inspect unresolved archive ${unsettled.payload.requestId} before another operation.`);
|
|
77
|
+
tx.saveEvent(taskId, createTaskEvent(tx.nextEventId(taskId), taskId, "task.leader-archive-started", { requestId: request.requestId, inputDigest, authorizationSource: JSON.stringify(admission.source) }, new Date()));
|
|
78
|
+
return true;
|
|
79
|
+
});
|
|
80
|
+
if (!started)
|
|
81
|
+
return { taskId, requestId: request.requestId, status: "unknown", replayed: true };
|
|
82
|
+
try {
|
|
83
|
+
await coordinator.runtime.stopTaskRoleSessions(taskId, ["leader"]);
|
|
84
|
+
store.transaction(tx => {
|
|
85
|
+
assertLeaderArchiveAdmission(tx, taskId, admission);
|
|
86
|
+
const stopped = tx.getTaskRoleSessionSet(taskId, "leader");
|
|
87
|
+
const current = stopped?.sessions[stopped.activeAgentId];
|
|
88
|
+
if (!stopped || current?.nativeSessionId !== admission.source.nativeSessionId
|
|
89
|
+
|| current.status !== "ended" || current.endReason !== "stopped") {
|
|
90
|
+
throw new Error("Leader Session changed before archive runtime retirement.");
|
|
91
|
+
}
|
|
92
|
+
// Retain the exact stopped caller in history and clear its resumable
|
|
93
|
+
// binding. Generic workspace cleanup must not stop that caller again:
|
|
94
|
+
// the first verified stop already released its physical owner records.
|
|
95
|
+
tx.saveTaskRoleSessionSet(selectNewTaskRoleSession(stopped, stopped.activeAgentId, new Date()));
|
|
96
|
+
});
|
|
97
|
+
const cleanup = await coordinator.cleanupTaskForArchive(taskId, "integrated");
|
|
98
|
+
if (cleanup.status !== "removed")
|
|
99
|
+
throw new Error(cleanup.error ?? `Archive cleanup ${cleanup.status}.`);
|
|
100
|
+
runTaskCommand(["archive", taskId, "--integrated"], store, {
|
|
101
|
+
environment, archiveLeaderAdmission: admission, archiveRemoteDeliveryProof: proof
|
|
102
|
+
});
|
|
103
|
+
record("task.leader-archive-result", { status: "archived" });
|
|
104
|
+
return { taskId, requestId: request.requestId, status: "archived" };
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
record("task.leader-archive-result", { status: store.getTask(taskId)?.status === "archived" ? "archived" : "failed",
|
|
108
|
+
detail: error instanceof Error ? error.message : String(error) });
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { taskAuthorizationSource } from "../grant/taskAuthorization.js";
|
|
2
|
+
const admissions = new WeakSet();
|
|
3
|
+
const terminalIdentity = (store, taskId) => {
|
|
4
|
+
const task = store.getTask(taskId);
|
|
5
|
+
if (!task || !["completed", "cancelled"].includes(task.status))
|
|
6
|
+
throw new Error("Ordinary Leader archive requires a terminal Task.");
|
|
7
|
+
return JSON.stringify({
|
|
8
|
+
status: task.status, completedAt: task.completedAt, completionSummary: task.completionSummary,
|
|
9
|
+
retiredAt: task.retiredAt, retirementSummary: task.retirementSummary,
|
|
10
|
+
projectBindings: task.projectBindings
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export function admitLeaderArchive(store, taskId, environment, messageId, purpose) {
|
|
14
|
+
const source = taskAuthorizationSource(store, taskId, environment, messageId, purpose);
|
|
15
|
+
const admission = Object.freeze({ taskId, source, terminalIdentity: terminalIdentity(store, taskId) });
|
|
16
|
+
admissions.add(admission);
|
|
17
|
+
return admission;
|
|
18
|
+
}
|
|
19
|
+
export function assertLeaderArchiveAdmission(store, taskId, admission) {
|
|
20
|
+
if (!admissions.has(admission) || admission.taskId !== taskId
|
|
21
|
+
|| terminalIdentity(store, taskId) !== admission.terminalIdentity) {
|
|
22
|
+
throw new Error("Archive admission is untrusted or its terminal Task changed.");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { hasManagedIdentity } from "../runtime/managedIdentity.js";
|
|
1
2
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
-
import {
|
|
3
|
+
import { currentManagedRuntime } from "../runtime/managedCaller.js";
|
|
3
4
|
const LEADER_ROLE = "leader";
|
|
4
5
|
export function taskActor(environment, taskId) {
|
|
5
6
|
const env = environment ?? {};
|
|
@@ -16,9 +17,7 @@ export function taskActor(environment, taskId) {
|
|
|
16
17
|
return "operator";
|
|
17
18
|
throw usageError("A managed global Session may perform this action only as Operator.");
|
|
18
19
|
}
|
|
19
|
-
if (env
|
|
20
|
-
|| env.YUI_AGENT_ID !== undefined
|
|
21
|
-
|| env[MANAGED_NATIVE_SESSION_ENV] !== undefined) {
|
|
20
|
+
if (hasManagedIdentity(env)) {
|
|
22
21
|
throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
|
|
23
22
|
}
|
|
24
23
|
return "user";
|
|
@@ -86,9 +85,7 @@ export function projectActor(environment) {
|
|
|
86
85
|
return "operator";
|
|
87
86
|
throw usageError("A managed global Session may manage Project Knowledge only as Operator.");
|
|
88
87
|
}
|
|
89
|
-
if (env
|
|
90
|
-
|| env.YUI_AGENT_ID !== undefined
|
|
91
|
-
|| env[MANAGED_NATIVE_SESSION_ENV] !== undefined) {
|
|
88
|
+
if (hasManagedIdentity(env)) {
|
|
92
89
|
throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
|
|
93
90
|
}
|
|
94
91
|
return "user";
|
|
@@ -124,9 +121,7 @@ export function resolveJobCaller(environment, taskId) {
|
|
|
124
121
|
nativeSessionId: env.CODEX_THREAD_ID ?? env.YUI_NATIVE_SESSION_ID
|
|
125
122
|
};
|
|
126
123
|
}
|
|
127
|
-
if (env
|
|
128
|
-
|| env.YUI_AGENT_ID !== undefined
|
|
129
|
-
|| env[MANAGED_NATIVE_SESSION_ENV] !== undefined) {
|
|
124
|
+
if (hasManagedIdentity(env)) {
|
|
130
125
|
throw usageError("Managed Agent identity is incomplete; refusing to infer user authority.");
|
|
131
126
|
}
|
|
132
127
|
return { scope: "user" };
|
|
@@ -4,8 +4,8 @@ export class CommandExecutionError extends Error {
|
|
|
4
4
|
code;
|
|
5
5
|
exitStatus;
|
|
6
6
|
stderr;
|
|
7
|
-
constructor(code, exitStatus, stderr = "") {
|
|
8
|
-
super(commandExecutionMessage(code));
|
|
7
|
+
constructor(code, exitStatus, stderr = "", options) {
|
|
8
|
+
super(commandExecutionMessage(code), options);
|
|
9
9
|
this.code = code;
|
|
10
10
|
this.exitStatus = exitStatus;
|
|
11
11
|
this.stderr = stderr;
|
|
@@ -102,15 +102,17 @@ export class NodeCommandExecutor {
|
|
|
102
102
|
}
|
|
103
103
|
function stableCommandExecutionError(error) {
|
|
104
104
|
const details = errorDetails(error);
|
|
105
|
+
const cause = typeof error === "object" && error !== null && "error" in error
|
|
106
|
+
&& error.error instanceof Error ? { cause: error.error } : undefined;
|
|
105
107
|
if (details.code === "ENOENT") {
|
|
106
|
-
return new CommandExecutionError("COMMAND_NOT_FOUND", details.exitStatus, details.stderr);
|
|
108
|
+
return new CommandExecutionError("COMMAND_NOT_FOUND", details.exitStatus, details.stderr, cause);
|
|
107
109
|
}
|
|
108
110
|
if (details.code === "ETIMEDOUT"
|
|
109
111
|
|| details.signal === "SIGTERM"
|
|
110
112
|
|| details.signal === "SIGKILL") {
|
|
111
|
-
return new CommandExecutionError("COMMAND_TIMED_OUT", details.exitStatus, details.stderr);
|
|
113
|
+
return new CommandExecutionError("COMMAND_TIMED_OUT", details.exitStatus, details.stderr, cause);
|
|
112
114
|
}
|
|
113
|
-
return new CommandExecutionError("COMMAND_FAILED", details.exitStatus, details.stderr);
|
|
115
|
+
return new CommandExecutionError("COMMAND_FAILED", details.exitStatus, details.stderr, cause);
|
|
114
116
|
}
|
|
115
117
|
function errorDetails(error) {
|
|
116
118
|
if (typeof error !== "object" || error === null)
|
|
@@ -1,43 +1,59 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { API_SCRIPT } from "./client/api.js";
|
|
3
4
|
import { APP_SCRIPT } from "./client/app.js";
|
|
4
5
|
import { COMPONENTS_SCRIPT } from "./client/components.js";
|
|
6
|
+
import { DETAIL_SCRIPT } from "./client/detail.js";
|
|
7
|
+
import { DOCK_SCRIPT } from "./client/dock.js";
|
|
5
8
|
import { DOM_SCRIPT } from "./client/dom.js";
|
|
9
|
+
import { EVIDENCE_SCRIPT } from "./client/evidence.js";
|
|
6
10
|
import { FORMAT_SCRIPT } from "./client/format.js";
|
|
11
|
+
import { FORMS_SCRIPT } from "./client/forms.js";
|
|
7
12
|
import { I18N_SCRIPT } from "./client/i18n.js";
|
|
8
13
|
import { MARKDOWN_SCRIPT } from "./client/markdown.js";
|
|
14
|
+
import { OVERVIEW_SCRIPT } from "./client/overview.js";
|
|
15
|
+
import { RECORDS_SCRIPT } from "./client/records.js";
|
|
16
|
+
import { SECTIONS_SCRIPT } from "./client/sections.js";
|
|
17
|
+
import { SIDEBAR_SCRIPT } from "./client/sidebar.js";
|
|
9
18
|
import { THEME_SCRIPT } from "./client/theme.js";
|
|
10
|
-
import { VIEW_SCRIPT } from "./client/view.js";
|
|
11
|
-
import { TASK_SURFACE_SCRIPT } from "./client/taskSurface.js";
|
|
12
|
-
import { TASK_SUMMARY_SCRIPT } from "./client/taskSummary.js";
|
|
13
19
|
import { DASHBOARD_HTML } from "./shell.js";
|
|
14
20
|
import { FONT_FACE_STYLES } from "./fonts.js";
|
|
15
21
|
import { FONT_WOFF2_BASE64 } from "./fontData.js";
|
|
16
|
-
import {
|
|
22
|
+
import { BASE_STYLES } from "./styles/base.js";
|
|
23
|
+
import { COMPONENT_STYLES } from "./styles/components.js";
|
|
17
24
|
import { LAYOUT_STYLES } from "./styles/layout.js";
|
|
18
25
|
import { MARKDOWN_STYLES } from "./styles/markdown.js";
|
|
19
26
|
import { RESPONSIVE_STYLES } from "./styles/responsive.js";
|
|
20
27
|
import { TOKEN_STYLES } from "./styles/tokens.js";
|
|
21
|
-
import {
|
|
28
|
+
import { VIEW_STYLES } from "./styles/views.js";
|
|
22
29
|
const require = createRequire(import.meta.url);
|
|
30
|
+
const CSS = "text/css; charset=utf-8";
|
|
31
|
+
const JS = "text/javascript; charset=utf-8";
|
|
23
32
|
export const WEB_ASSETS = Object.freeze({
|
|
24
|
-
"/assets/css/
|
|
25
|
-
"/assets/css/
|
|
26
|
-
"/assets/css/
|
|
27
|
-
"/assets/css/
|
|
28
|
-
"/assets/css/
|
|
29
|
-
"/assets/css/
|
|
30
|
-
"/assets/css/
|
|
31
|
-
"/assets/
|
|
32
|
-
"/assets/js/
|
|
33
|
-
"/assets/js/
|
|
34
|
-
"/assets/js/
|
|
35
|
-
"/assets/js/
|
|
36
|
-
"/assets/js/
|
|
37
|
-
"/assets/js/
|
|
38
|
-
"/assets/js/
|
|
39
|
-
"/assets/js/
|
|
40
|
-
"/assets/
|
|
33
|
+
"/assets/css/fonts.css": { contentType: CSS, body: FONT_FACE_STYLES },
|
|
34
|
+
"/assets/css/tokens.css": { contentType: CSS, body: TOKEN_STYLES },
|
|
35
|
+
"/assets/css/base.css": { contentType: CSS, body: BASE_STYLES },
|
|
36
|
+
"/assets/css/layout.css": { contentType: CSS, body: LAYOUT_STYLES },
|
|
37
|
+
"/assets/css/components.css": { contentType: CSS, body: COMPONENT_STYLES },
|
|
38
|
+
"/assets/css/views.css": { contentType: CSS, body: VIEW_STYLES },
|
|
39
|
+
"/assets/css/markdown.css": { contentType: CSS, body: MARKDOWN_STYLES },
|
|
40
|
+
"/assets/css/responsive.css": { contentType: CSS, body: RESPONSIVE_STYLES },
|
|
41
|
+
"/assets/js/dom.js": { contentType: JS, body: DOM_SCRIPT },
|
|
42
|
+
"/assets/js/format.js": { contentType: JS, body: FORMAT_SCRIPT },
|
|
43
|
+
"/assets/js/i18n.js": { contentType: JS, body: I18N_SCRIPT },
|
|
44
|
+
"/assets/js/markdown.js": { contentType: JS, body: MARKDOWN_SCRIPT },
|
|
45
|
+
"/assets/js/theme.js": { contentType: JS, body: THEME_SCRIPT },
|
|
46
|
+
"/assets/js/api.js": { contentType: JS, body: API_SCRIPT },
|
|
47
|
+
"/assets/js/components.js": { contentType: JS, body: COMPONENTS_SCRIPT },
|
|
48
|
+
"/assets/js/records.js": { contentType: JS, body: RECORDS_SCRIPT },
|
|
49
|
+
"/assets/js/forms.js": { contentType: JS, body: FORMS_SCRIPT },
|
|
50
|
+
"/assets/js/evidence.js": { contentType: JS, body: EVIDENCE_SCRIPT },
|
|
51
|
+
"/assets/js/sidebar.js": { contentType: JS, body: SIDEBAR_SCRIPT },
|
|
52
|
+
"/assets/js/overview.js": { contentType: JS, body: OVERVIEW_SCRIPT },
|
|
53
|
+
"/assets/js/sections.js": { contentType: JS, body: SECTIONS_SCRIPT },
|
|
54
|
+
"/assets/js/detail.js": { contentType: JS, body: DETAIL_SCRIPT },
|
|
55
|
+
"/assets/js/dock.js": { contentType: JS, body: DOCK_SCRIPT },
|
|
56
|
+
"/assets/app.js": { contentType: JS, body: APP_SCRIPT },
|
|
41
57
|
...fontAssets(),
|
|
42
58
|
"/assets/vendor/xterm.mjs": vendorAsset("@xterm/xterm/lib/xterm.mjs", "text/javascript; charset=utf-8"),
|
|
43
59
|
"/assets/vendor/addon-fit.mjs": vendorAsset("@xterm/addon-fit/lib/addon-fit.mjs", "text/javascript; charset=utf-8"),
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
export const API_SCRIPT = String.raw `
|
|
2
|
+
// Loopback API client. Every request carries the page token; a write is keyed
|
|
3
|
+
// so an unresolved submission can never be silently replayed from this page.
|
|
4
|
+
const token = document.querySelector('meta[name="yui-web-token"]').content;
|
|
5
|
+
const unresolved = new Set();
|
|
6
|
+
|
|
7
|
+
export function pageToken() { return token; }
|
|
8
|
+
|
|
9
|
+
export async function requestJson(path, options) {
|
|
10
|
+
const response = await fetch(path, {
|
|
11
|
+
...options,
|
|
12
|
+
headers: {
|
|
13
|
+
accept: "application/json",
|
|
14
|
+
"x-yui-web-token": token,
|
|
15
|
+
...(options && options.headers ? options.headers : {})
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
let message = "HTTP " + response.status;
|
|
20
|
+
let disposition = "unknown";
|
|
21
|
+
try {
|
|
22
|
+
const body = await response.json();
|
|
23
|
+
if (body && body.error) message = body.error;
|
|
24
|
+
if (body && body.disposition === "not-submitted") disposition = "not-submitted";
|
|
25
|
+
} catch {}
|
|
26
|
+
throw Object.assign(new Error(message), { disposition, status: response.status });
|
|
27
|
+
}
|
|
28
|
+
return response.json();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function postJson(path, body, signal) {
|
|
32
|
+
return requestJson(path, {
|
|
33
|
+
method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body),
|
|
34
|
+
...(signal ? { signal } : {})
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// A key stays blocked until the server either answers or proves the request
|
|
39
|
+
// was not submitted; an unknown outcome must be inspected, not resent.
|
|
40
|
+
export async function submitMutation(key, path, body) {
|
|
41
|
+
if (unresolved.has(key)) {
|
|
42
|
+
throw Object.assign(new Error("An earlier submission is unresolved; read current facts first."), { disposition: "blocked" });
|
|
43
|
+
}
|
|
44
|
+
unresolved.add(key);
|
|
45
|
+
try {
|
|
46
|
+
const receipt = await postJson(path, body);
|
|
47
|
+
unresolved.delete(key);
|
|
48
|
+
return receipt;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error.disposition === "not-submitted") unresolved.delete(key);
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function releaseMutation(key) { unresolved.delete(key); }
|
|
56
|
+
|
|
57
|
+
const task = function (taskId) { return "/api/tasks/" + encodeURIComponent(taskId); };
|
|
58
|
+
|
|
59
|
+
export const api = {
|
|
60
|
+
dashboard: function (query) { return requestJson("/api/dashboard?" + query); },
|
|
61
|
+
pageSessions: function (query) {
|
|
62
|
+
return requestJson("/api/dashboard/sessions?" + query, { signal: AbortSignal.timeout(5000) });
|
|
63
|
+
},
|
|
64
|
+
context: function (taskId) { return requestJson(task(taskId) + "/context"); },
|
|
65
|
+
observation: function (taskId) { return requestJson(task(taskId), { signal: AbortSignal.timeout(1500) }); },
|
|
66
|
+
inspect: async function (taskId, ref) {
|
|
67
|
+
const query = new URLSearchParams({ store: ref.store, ref: ref.refId });
|
|
68
|
+
if (ref.digest) query.set("digest", ref.digest);
|
|
69
|
+
let result = await requestJson(task(taskId) + "/inspect?" + query);
|
|
70
|
+
if (!result.contentPage) return result;
|
|
71
|
+
const source = result.contentPage.source;
|
|
72
|
+
const digest = result.contentPage.digest;
|
|
73
|
+
let text = "";
|
|
74
|
+
while (true) {
|
|
75
|
+
const page = result.contentPage;
|
|
76
|
+
if (!page || page.source !== source || page.digest !== digest || page.encoding !== "json"
|
|
77
|
+
|| page.offset !== text.length || typeof page.text !== "string" || !page.text.length) {
|
|
78
|
+
throw new Error("Original read changed or returned an invalid page; refresh its Context reference.");
|
|
79
|
+
}
|
|
80
|
+
text += page.text;
|
|
81
|
+
if (page.complete) return JSON.parse(text);
|
|
82
|
+
if (!page.nextCursor) throw new Error("Original read is incomplete; refresh its Context reference.");
|
|
83
|
+
query.set("cursor", page.nextCursor);
|
|
84
|
+
result = await requestJson(task(taskId) + "/inspect?" + query);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
artifacts: function (taskId) { return requestJson(task(taskId) + "/artifacts"); },
|
|
88
|
+
artifact: function (taskId, path, commit) {
|
|
89
|
+
return requestJson(task(taskId) + "/artifacts?" + new URLSearchParams({ path, commit }));
|
|
90
|
+
},
|
|
91
|
+
evidence: function (taskId) { return requestJson(task(taskId) + "/evidence"); },
|
|
92
|
+
panels: function (taskId) { return requestJson(task(taskId) + "/panels", { signal: AbortSignal.timeout(3000) }); },
|
|
93
|
+
readPanel: function (taskId, ref, input) {
|
|
94
|
+
return postJson(task(taskId) + "/panels", { ref, input }, AbortSignal.timeout(3000));
|
|
95
|
+
},
|
|
96
|
+
sendMessage: function (taskId, body, requestId, intent) {
|
|
97
|
+
return submitMutation(taskId + "/messages", task(taskId) + "/messages",
|
|
98
|
+
{ body, requestId, ...(intent === undefined ? {} : { intent }) });
|
|
99
|
+
},
|
|
100
|
+
control: function (taskId, payload) {
|
|
101
|
+
return submitMutation(taskId + "/control/" + payload.requestId, task(taskId) + "/control", payload);
|
|
102
|
+
},
|
|
103
|
+
updateTask: function (taskId, patch, requestId) {
|
|
104
|
+
return submitMutation(taskId + "/metadata", task(taskId) + "/metadata", { patch, requestId });
|
|
105
|
+
},
|
|
106
|
+
answerInput: function (taskId, inputId, answer) {
|
|
107
|
+
return submitMutation(taskId + "/input/" + inputId,
|
|
108
|
+
task(taskId) + "/inputs/" + encodeURIComponent(inputId) + "/answer", answer);
|
|
109
|
+
},
|
|
110
|
+
globalState: function (role) { return requestJson("/api/roles/" + encodeURIComponent(role) + "/control"); },
|
|
111
|
+
globalControl: function (role, input) {
|
|
112
|
+
return submitMutation("global/" + role, "/api/roles/" + encodeURIComponent(role) + "/control", input);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
`;
|