yarramate 0.23.0 → 1.0.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/README.md +13 -4
- package/catalogues/core-enrichment.yaml +79 -55
- package/dist/adapter-mapping.js +6 -1
- package/dist/adapters/likec4-cli.js +36 -7
- package/dist/adapters/visual/client.d.ts +2 -2
- package/dist/adapters/visual/client.js +79 -10
- package/dist/adapters/visual/protocol-contract.d.ts +124 -10
- package/dist/adapters/visual/protocol-contract.js +12 -1
- package/dist/adapters/visual/protocol.d.ts +63 -0
- package/dist/adapters/visual/protocol.js +96 -5
- package/dist/adapters/visual/session-server.d.ts +1 -6
- package/dist/adapters/visual/session-server.js +243 -143
- package/dist/adapters/visual/session-store.d.ts +9 -0
- package/dist/adapters/visual/session-store.js +14 -5
- package/dist/adapters/visual/view-identity.d.ts +135 -0
- package/dist/adapters/visual/view-identity.js +200 -0
- package/dist/adapters/visual/wire.d.ts +19 -4
- package/dist/adapters/visual/workspace-model.d.ts +116 -0
- package/dist/adapters/visual/workspace-model.js +212 -0
- package/dist/adapters/visual-cli.d.ts +1 -1
- package/dist/adapters/visual-cli.js +30 -21
- package/dist/apply-cli.d.ts +2 -0
- package/dist/apply-cli.js +83 -0
- package/dist/apply-command.d.ts +84 -3
- package/dist/apply-command.js +218 -109
- package/dist/archimate-relationships.generated.d.ts +22 -0
- package/dist/archimate-relationships.generated.js +156 -0
- package/dist/ask-command.js +18 -6
- package/dist/attestation-staleness.js +2 -2
- package/dist/changed.js +1 -1
- package/dist/check-command.js +13 -5
- package/dist/cli-support.d.ts +1 -1
- package/dist/cli-support.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/compiler.d.ts +31 -3
- package/dist/compiler.js +321 -81
- package/dist/concept-drafting.d.ts +27 -0
- package/dist/concept-drafting.js +75 -0
- package/dist/core-contract.js +6 -1
- package/dist/deletion-drafting.d.ts +52 -0
- package/dist/deletion-drafting.js +100 -0
- package/dist/evidence.js +6 -1
- package/dist/graph-projection.d.ts +14 -0
- package/dist/graph-projection.js +4 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +8 -1
- package/dist/interrogate-command.js +63 -17
- package/dist/kind-label.d.ts +4 -3
- package/dist/kind-label.js +4 -3
- package/dist/nesting.d.ts +15 -0
- package/dist/nesting.js +2 -0
- package/dist/notation/archimate.d.ts +1 -1
- package/dist/notation/archimate.js +108 -9
- package/dist/profile.d.ts +9 -12
- package/dist/profile.js +17 -26
- package/dist/projection.d.ts +62 -3
- package/dist/projection.js +127 -43
- package/dist/relationship-drafting.d.ts +51 -0
- package/dist/relationship-drafting.js +92 -0
- package/dist/relationship-matrix.d.ts +14 -0
- package/dist/relationship-matrix.js +74 -0
- package/dist/shipped-profile.d.ts +1 -1
- package/dist/shipped-profile.js +19 -5
- package/dist/source-store.d.ts +95 -0
- package/dist/source-store.js +170 -0
- package/dist/subject-references.js +1 -1
- package/dist/visual-app/assets/index-BLcpI8WN.js +394 -0
- package/dist/visual-app/assets/index-DLutIWES.css +1 -0
- package/dist/visual-app/index.html +2 -2
- package/dist/visual-app-lib/editor.d.ts +1 -0
- package/dist/visual-app-lib/editor.js +90968 -0
- package/dist/visual-app-lib/styles.css +2 -0
- package/dist/visual-app-lib/types/adapter-mapping.d.ts +40 -0
- package/dist/visual-app-lib/types/adapters/visual/protocol-contract.d.ts +455 -0
- package/dist/visual-app-lib/types/adapters/visual/view-identity.d.ts +135 -0
- package/dist/visual-app-lib/types/adapters/visual/wire.d.ts +141 -0
- package/dist/visual-app-lib/types/adapters/visual/workspace-model.d.ts +116 -0
- package/dist/visual-app-lib/types/apply-command.d.ts +93 -0
- package/dist/visual-app-lib/types/archimate-relationships.generated.d.ts +22 -0
- package/dist/visual-app-lib/types/brief.d.ts +5 -0
- package/dist/visual-app-lib/types/compiler.d.ts +155 -0
- package/dist/visual-app-lib/types/concept-drafting.d.ts +27 -0
- package/dist/visual-app-lib/types/deletion-drafting.d.ts +52 -0
- package/dist/visual-app-lib/types/evidence.d.ts +62 -0
- package/dist/visual-app-lib/types/graph-claims.d.ts +18 -0
- package/dist/visual-app-lib/types/graph-projection.d.ts +77 -0
- package/dist/visual-app-lib/types/kind-label.d.ts +9 -0
- package/dist/visual-app-lib/types/nesting.d.ts +15 -0
- package/dist/visual-app-lib/types/notation/archimate.d.ts +71 -0
- package/dist/visual-app-lib/types/operations.d.ts +157 -0
- package/dist/visual-app-lib/types/profile.d.ts +47 -0
- package/dist/visual-app-lib/types/projection.d.ts +113 -0
- package/dist/visual-app-lib/types/relationship-drafting.d.ts +51 -0
- package/dist/visual-app-lib/types/relationship-matrix.d.ts +14 -0
- package/dist/visual-app-lib/types/shipped-profile.d.ts +2 -0
- package/dist/visual-app-lib/types/source-document.d.ts +36 -0
- package/dist/visual-app-lib/types/source-store.d.ts +95 -0
- package/dist/visual-app-lib/types/subject-references.d.ts +92 -0
- package/dist/visual-app-lib/types/visual-app/App.d.ts +15 -0
- package/dist/visual-app-lib/types/visual-app/badges.d.ts +7 -0
- package/dist/visual-app-lib/types/visual-app/changeset-tray.d.ts +106 -0
- package/dist/visual-app-lib/types/visual-app/confirm-dialog.d.ts +14 -0
- package/dist/visual-app-lib/types/visual-app/connection-panel.d.ts +23 -0
- package/dist/visual-app-lib/types/visual-app/context-menu-model.d.ts +187 -0
- package/dist/visual-app-lib/types/visual-app/context-menu.d.ts +24 -0
- package/dist/visual-app-lib/types/visual-app/describe-query.d.ts +9 -0
- package/dist/visual-app-lib/types/visual-app/editor-host.d.ts +70 -0
- package/dist/visual-app-lib/types/visual-app/faults.d.ts +26 -0
- package/dist/visual-app-lib/types/visual-app/graph-canvas.d.ts +73 -0
- package/dist/visual-app-lib/types/visual-app/kind-icons.d.ts +3 -0
- package/dist/visual-app-lib/types/visual-app/local-host.d.ts +38 -0
- package/dist/visual-app-lib/types/visual-app/mount.d.ts +56 -0
- package/dist/visual-app-lib/types/visual-app/prompt-dialog.d.ts +24 -0
- package/dist/visual-app-lib/types/visual-app/query-fields.d.ts +69 -0
- package/dist/visual-app-lib/types/visual-app/query-panel.d.ts +142 -0
- package/dist/visual-app-lib/types/visual-app/quick-filter.d.ts +11 -0
- package/dist/visual-app-lib/types/visual-app/relationship-kind-options.d.ts +50 -0
- package/dist/visual-app-lib/types/visual-app/save-view.d.ts +86 -0
- package/dist/visual-app-lib/types/visual-app/section-stack.d.ts +59 -0
- package/dist/visual-app-lib/types/visual-app/session-client.d.ts +38 -0
- package/dist/visual-app-lib/types/visual-app/state.d.ts +328 -0
- package/dist/visual-app-lib/types/visual-app/subject-draft-panel.d.ts +22 -0
- package/dist/visual-app-lib/types/visual-app/subject-form.d.ts +42 -0
- package/dist/visual-app-lib/types/visual-app/view-tree-model.d.ts +122 -0
- package/dist/visual-app-lib/types/visual-app/view-tree.d.ts +32 -0
- package/dist/visual-app-lib/types/visual-app/workspace-state.d.ts +268 -0
- package/dist/visual-app-lib/types/workspace.d.ts +29 -0
- package/dist/workspace.js +7 -1
- package/docs/CONSUMING-YARRAMATE.md +51 -2
- package/package.json +9 -2
- package/schema/yarramate-adapter-mapping.schema.json +1 -1
- package/schema/yarramate-ask-result.schema.json +24 -3
- package/schema/yarramate-check-result.schema.json +9 -0
- package/schema/yarramate-diagnostic-result.schema.json +9 -0
- package/schema/yarramate-document.schema.json +6 -0
- package/schema/yarramate-evidence-report.schema.json +1 -1
- package/schema/yarramate-evidence.schema.json +1 -1
- package/schema/yarramate-graph-v2.schema.json +34 -9
- package/schema/yarramate-likec4-generated-project-v2.schema.json +1 -1
- package/schema/yarramate-likec4-generated-project.schema.json +1 -1
- package/schema/yarramate-likec4-project.schema.json +1 -1
- package/schema/yarramate-projection-result.schema.json +3 -7
- package/schema/yarramate-projection.schema.json +16 -16
- package/schema/yarramate-reconciliation-report.schema.json +2 -2
- package/schema/yarramate-rtm.schema.json +1 -1
- package/schema/yarramate-state-comparison.schema.json +1 -1
- package/schema/yarramate-visual-diagnostic-result.schema.json +9 -0
- package/schema/yarramate-visual-event.schema.json +40 -41
- package/schema/yarramate-visual-graph.schema.json +8 -0
- package/schema/yarramate-visual-handoff.schema.json +3 -3
- package/schema/yarramate-visual-session-descriptor.schema.json +6 -6
- package/schema/yarramate-visual-session-started.schema.json +10 -9
- package/schema/yarramate-visual-status.schema.json +3 -3
- package/skills/yarramate-architecture/SKILL.md +3 -3
- package/skills/yarramate-architecture/references/modelling-patterns.md +23 -44
- package/skills/yarramate-architecture/references/native-authoring.md +32 -49
- package/skills/yarramate-architecture/references/visual-conversations.md +13 -10
- package/dist/visual-app/assets/index-CRd1Khma.js +0 -354
- package/dist/visual-app/assets/index-tZSHlU-a.css +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { stringify } from "yaml";
|
|
2
|
+
import { loadProjection } from "../../projection.js";
|
|
3
|
+
import { withDiagnosticSubjects } from "../../compiler.js";
|
|
4
|
+
import { posixDirectoryOf } from "../../apply-command.js";
|
|
5
|
+
import { projectGraphForCanvas } from "../../graph-projection.js";
|
|
6
|
+
import { DEFAULT_PROJECTION_DIRECTORY } from "./view-identity.js";
|
|
7
|
+
import { evaluateProjection, explainProjection } from "../../projection.js";
|
|
8
|
+
import { kindLabelOf } from "../../kind-label.js";
|
|
9
|
+
/**
|
|
10
|
+
* What a workspace looks like to the editor, however the editor is being run
|
|
11
|
+
* (#252).
|
|
12
|
+
*
|
|
13
|
+
* These were the session server's, and the session server is one host now: an
|
|
14
|
+
* embedder mounting the editor over its own store computes the same model, the
|
|
15
|
+
* same vocabulary and the same view counts, and two definitions of any of them
|
|
16
|
+
* would be two answers to the same question. So the arithmetic lives here and
|
|
17
|
+
* the orchestration - which bytes to read, which digests to pin, what a layout
|
|
18
|
+
* sidecar says - stays with whoever owns those.
|
|
19
|
+
*
|
|
20
|
+
* Browser-safe: no `node:`, no filesystem, no session. Everything is a pure
|
|
21
|
+
* function of a compile result.
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* The kinds a browser may offer, each with the core kind it descends from.
|
|
25
|
+
*
|
|
26
|
+
* `[0]` is the nearest declared ancestor, the same reading
|
|
27
|
+
* `projectGraphForCanvas` gives an authored subject its `coreKindLabel`. A kind
|
|
28
|
+
* with no lineage IS a core kind and stands for itself. Without the core label
|
|
29
|
+
* an editor can read a palette but cannot judge it: the ArchiMate table is
|
|
30
|
+
* keyed on core kinds, so offering an extension kind unchecked puts a `YM404`
|
|
31
|
+
* one click away.
|
|
32
|
+
*/
|
|
33
|
+
export const kindOptionsOf = (lineages) => [...lineages.keys()].map((id) => ({
|
|
34
|
+
id,
|
|
35
|
+
label: kindLabelOf(id),
|
|
36
|
+
coreLabel: kindLabelOf(lineages.get(id)?.[0] ?? id),
|
|
37
|
+
}));
|
|
38
|
+
/**
|
|
39
|
+
* How many SUBJECTS a query matches, which is not the size of its match set.
|
|
40
|
+
*
|
|
41
|
+
* A `SemanticGraph`'s subjects are concepts and relationships together, so the
|
|
42
|
+
* match set returns both — right for narrowing a canvas that draws edges as
|
|
43
|
+
* well as nodes, and wrong for a number sitting beside a view called its
|
|
44
|
+
* subject count. A view over three components with two relationships between
|
|
45
|
+
* them would read as five, and the reviewer counting boxes would find three.
|
|
46
|
+
*/
|
|
47
|
+
export const conceptCountOf = (graph, query, profileContext) => evaluateProjection(graph, adHoc(query), profileContext).subjects.filter(({ type }) => type === "concept").length;
|
|
48
|
+
/**
|
|
49
|
+
* Rebuilds the shared editor workspace from one successful compile.
|
|
50
|
+
*
|
|
51
|
+
* The caller owns metadata which cannot be inferred from a graph (authority,
|
|
52
|
+
* source revisions, layouts, and initial view); this helper owns all derived
|
|
53
|
+
* canvas, vocabulary, and view-count arithmetic.
|
|
54
|
+
*/
|
|
55
|
+
export const renderedWorkspaceOf = (compiled, views, metadata) => {
|
|
56
|
+
const refreshedViews = views.map((view) => ({
|
|
57
|
+
...view,
|
|
58
|
+
subjectCount: conceptCountOf(compiled.graph, view.query, compiled.profileContext),
|
|
59
|
+
}));
|
|
60
|
+
return {
|
|
61
|
+
model: {
|
|
62
|
+
...metadata,
|
|
63
|
+
graph: projectGraphForCanvas(compiled.graph, compiled.profileContext),
|
|
64
|
+
vocabulary: {
|
|
65
|
+
conceptKinds: kindOptionsOf(compiled.profileContext.conceptKindLineages),
|
|
66
|
+
relationshipKinds: kindOptionsOf(compiled.profileContext.relationshipKindLineages),
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
views: refreshedViews,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/** Every subject a query draws, concepts and relationships alike. */
|
|
73
|
+
export const matchedIdsOf = (graph, query, profileContext) => evaluateProjection(graph, adHoc(query), profileContext).subjects.map(({ id }) => id);
|
|
74
|
+
/** Every concept a query dropped, and the facet that dropped it (#248). */
|
|
75
|
+
export const exclusionsOf = (graph, query, profileContext) => explainProjection(graph, adHoc(query), profileContext);
|
|
76
|
+
/**
|
|
77
|
+
* A query on its own is not a projection, and every evaluator here wants one.
|
|
78
|
+
* The id is a placeholder that never reaches a document.
|
|
79
|
+
*/
|
|
80
|
+
const adHoc = (query) => ({
|
|
81
|
+
format: "yarramate/projection/v1",
|
|
82
|
+
id: "ad-hoc",
|
|
83
|
+
version: "0",
|
|
84
|
+
query,
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* One saved view, as the rail reads it. `subjectCount` is the caller's,
|
|
88
|
+
* because counting needs a compiled graph and a session builds its first list
|
|
89
|
+
* before it has one.
|
|
90
|
+
*/
|
|
91
|
+
export const viewSummaryOf = (projection, path, subjectCount = 0) => ({
|
|
92
|
+
id: projection.id,
|
|
93
|
+
title: projection.presentation?.title ?? projection.id,
|
|
94
|
+
description: projection.presentation?.description ?? "",
|
|
95
|
+
query: projection.query,
|
|
96
|
+
presentation: projection.presentation,
|
|
97
|
+
path,
|
|
98
|
+
subjectCount,
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* Applies a landed view batch to a host-owned list.
|
|
102
|
+
*
|
|
103
|
+
* Writes are read back through the callback, so a malformed or otherwise
|
|
104
|
+
* unreadable landed document cannot become a fabricated summary.
|
|
105
|
+
*/
|
|
106
|
+
export const adoptLandedViews = (views, operations, readSummary) => {
|
|
107
|
+
const next = [...views];
|
|
108
|
+
for (const operation of operations) {
|
|
109
|
+
const at = next.findIndex((view) => view.path === operation.path);
|
|
110
|
+
if (operation.op === "delete-view") {
|
|
111
|
+
if (at !== -1)
|
|
112
|
+
next.splice(at, 1);
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const summary = readSummary(operation.path);
|
|
116
|
+
if (summary === undefined)
|
|
117
|
+
continue;
|
|
118
|
+
if (at === -1)
|
|
119
|
+
next.push(summary);
|
|
120
|
+
else
|
|
121
|
+
next[at] = summary;
|
|
122
|
+
}
|
|
123
|
+
return next;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* The document a diagnostic the runtime minted itself points at. Not a file:
|
|
127
|
+
* these are about the session rather than about anything the workspace holds.
|
|
128
|
+
*/
|
|
129
|
+
export const VISUAL_SERVER_DOCUMENT = "visual-session-server";
|
|
130
|
+
export const published = (diagnostics, sources) => withDiagnosticSubjects(diagnostics, sources);
|
|
131
|
+
export const serverDiagnostic = (code, message, pointer = "/") => ({
|
|
132
|
+
severity: "error",
|
|
133
|
+
code,
|
|
134
|
+
message,
|
|
135
|
+
path: VISUAL_SERVER_DOCUMENT,
|
|
136
|
+
pointer,
|
|
137
|
+
line: 1,
|
|
138
|
+
column: 1,
|
|
139
|
+
});
|
|
140
|
+
/**
|
|
141
|
+
* Whether the workspace would load a projection written at this path.
|
|
142
|
+
*
|
|
143
|
+
* This is a DIRECTORY check, not a pattern match, and deliberately so: ADR
|
|
144
|
+
* 0100 recorded that the manifest's pattern dialect has never been named - it
|
|
145
|
+
* is whatever `globSync` happens to support, undocumented and untested past
|
|
146
|
+
* one wildcard - so a matcher written here would be inventing the dialect
|
|
147
|
+
* rather than honouring it. A directory that already holds a projection the
|
|
148
|
+
* manifest resolved is a directory the manifest demonstrably reaches.
|
|
149
|
+
*
|
|
150
|
+
* A workspace with no projections at all has nothing to demonstrate, so the
|
|
151
|
+
* default directory is allowed: refusing there would make the first view in a
|
|
152
|
+
* fresh workspace impossible to create.
|
|
153
|
+
*/
|
|
154
|
+
export const projectionDirectoryIsCovered = (path, workspace) => {
|
|
155
|
+
if (workspace.projections.length === 0) {
|
|
156
|
+
return posixDirectoryOf(path) === DEFAULT_PROJECTION_DIRECTORY;
|
|
157
|
+
}
|
|
158
|
+
const covered = new Set(workspace.projections.map(posixDirectoryOf));
|
|
159
|
+
return covered.has(posixDirectoryOf(path));
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Turns staged view operations into pending writes, refusing before anything
|
|
163
|
+
* is written (ADR 0103).
|
|
164
|
+
*
|
|
165
|
+
* A `write-view` is validated through the same `loadProjection` the CLI's own
|
|
166
|
+
* projection writes go through, so a document the schema would reject never
|
|
167
|
+
* reaches the store. A `delete-view` names a revision, which is what makes a
|
|
168
|
+
* removal refusable rather than a silent success on a file someone else
|
|
169
|
+
* already changed.
|
|
170
|
+
*/
|
|
171
|
+
export const planViewWrites = (operations, workspace, store) => {
|
|
172
|
+
const writes = [];
|
|
173
|
+
const diagnostics = [];
|
|
174
|
+
for (const operation of operations) {
|
|
175
|
+
const held = store.read(operation.path);
|
|
176
|
+
if (operation.op === "delete-view") {
|
|
177
|
+
if (held === undefined) {
|
|
178
|
+
diagnostics.push(serverDiagnostic("YMVS314", `View "${operation.path}" is already gone`));
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
writes.push({
|
|
182
|
+
path: operation.path,
|
|
183
|
+
source: null,
|
|
184
|
+
expected: held.revision,
|
|
185
|
+
});
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (held === undefined &&
|
|
189
|
+
!projectionDirectoryIsCovered(operation.path, workspace)) {
|
|
190
|
+
// A projection no pattern reaches is a file the workspace never loads,
|
|
191
|
+
// which is worse than a refusal because nothing later says so (ADR 0043).
|
|
192
|
+
diagnostics.push(serverDiagnostic("YMVS315", `The workspace manifest covers no projection in "${posixDirectoryOf(operation.path)}", so a view saved there would never load`));
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
const source = stringify(operation.projection);
|
|
196
|
+
const loaded = loadProjection({ path: operation.path, source });
|
|
197
|
+
if (!loaded.ok) {
|
|
198
|
+
// The composed document is the only source these are about, so they are
|
|
199
|
+
// published against it rather than against the workspace.
|
|
200
|
+
diagnostics.push(...published(loaded.diagnostics, [{ path: operation.path, source }]));
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
writes.push({
|
|
204
|
+
path: operation.path,
|
|
205
|
+
source,
|
|
206
|
+
expected: held?.revision ?? null,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
return diagnostics.length > 0
|
|
210
|
+
? { ok: false, diagnostics }
|
|
211
|
+
: { ok: true, writes };
|
|
212
|
+
};
|
|
@@ -30,7 +30,7 @@ export interface VisualStartIo {
|
|
|
30
30
|
* Serves one visual session in the foreground.
|
|
31
31
|
*
|
|
32
32
|
* The request is validated before any filesystem or network effect, so a bad
|
|
33
|
-
* document costs nothing. Exactly one public `visual-session-started/
|
|
33
|
+
* document costs nothing. Exactly one public `visual-session-started/v2` line is
|
|
34
34
|
* published before the command blocks — it names the private descriptor but
|
|
35
35
|
* never carries the agent capability, which lives only in that mode 0600 file.
|
|
36
36
|
* The command then holds the session open until the runtime closes it, whether
|
|
@@ -10,11 +10,14 @@ export const visualUsage = 'Usage:\n' +
|
|
|
10
10
|
' yarramate-visual request [--view <id>] [--title <text>]\n' +
|
|
11
11
|
' [--description <text>] [--chat]\n' +
|
|
12
12
|
' yarramate-visual start <request.json>\n' +
|
|
13
|
-
' yarramate-visual wait <descriptor
|
|
14
|
-
' yarramate-visual respond <descriptor
|
|
15
|
-
' yarramate-visual status <descriptor
|
|
16
|
-
' yarramate-visual recover <descriptor
|
|
17
|
-
' yarramate-visual stop <descriptor
|
|
13
|
+
' yarramate-visual wait <descriptor-uri> [--after <sequence>]\n' +
|
|
14
|
+
' yarramate-visual respond <descriptor-uri> <response.json>\n' +
|
|
15
|
+
' yarramate-visual status <descriptor-uri>\n' +
|
|
16
|
+
' yarramate-visual recover <descriptor-uri> [--transcript]\n' +
|
|
17
|
+
' yarramate-visual stop <descriptor-uri> [--transcript]\n' +
|
|
18
|
+
'\n' +
|
|
19
|
+
'<descriptor-uri> is the `descriptorPath` file: URI `start` published,\n' +
|
|
20
|
+
'copied back verbatim. It is never resolved against the working directory.\n' +
|
|
18
21
|
' yarramate-visual --version\n';
|
|
19
22
|
/**
|
|
20
23
|
* Where a repository keeps its live sessions, one directory per session. It sits
|
|
@@ -126,11 +129,11 @@ const requestCliCommand = (rest, cwd) => {
|
|
|
126
129
|
return refusalResult(built.diagnostics);
|
|
127
130
|
return { exitCode: 0, stdout: documentLine(built.request), stderr: '' };
|
|
128
131
|
};
|
|
129
|
-
const waitCliCommand = async (
|
|
132
|
+
const waitCliCommand = async (descriptorUri, rest) => {
|
|
130
133
|
const after = sequenceFlag(rest);
|
|
131
134
|
if (after === undefined)
|
|
132
135
|
return usageResult;
|
|
133
|
-
const descriptor = await readVisualSessionDescriptor(
|
|
136
|
+
const descriptor = await readVisualSessionDescriptor(descriptorUri);
|
|
134
137
|
if (!descriptor.ok)
|
|
135
138
|
return refusalResult(descriptor.diagnostics);
|
|
136
139
|
const delivery = await waitForVisualEvent(descriptor.value, after);
|
|
@@ -144,13 +147,16 @@ const waitCliCommand = async (descriptorPath, rest, cwd) => {
|
|
|
144
147
|
stderr: '',
|
|
145
148
|
};
|
|
146
149
|
};
|
|
147
|
-
const respondCliCommand = async (
|
|
150
|
+
const respondCliCommand = async (descriptorUri, rest, cwd) => {
|
|
148
151
|
const [responsePath, ...trailing] = rest;
|
|
149
152
|
if (responsePath === undefined || trailing.length > 0)
|
|
150
153
|
return usageResult;
|
|
151
|
-
const descriptor = await readVisualSessionDescriptor(
|
|
154
|
+
const descriptor = await readVisualSessionDescriptor(descriptorUri);
|
|
152
155
|
if (!descriptor.ok)
|
|
153
156
|
return refusalResult(descriptor.diagnostics);
|
|
157
|
+
// The response document is the agent's own file, named however the agent
|
|
158
|
+
// likes: it is not a protocol path field, so it stays a native path
|
|
159
|
+
// resolved against `cwd`.
|
|
154
160
|
const document = await readVisualJsonDocument(responsePath, cwd);
|
|
155
161
|
if (!document.ok)
|
|
156
162
|
return refusalResult(document.diagnostics);
|
|
@@ -164,10 +170,10 @@ const respondCliCommand = async (descriptorPath, rest, cwd) => {
|
|
|
164
170
|
return refusalResult(accepted.diagnostics);
|
|
165
171
|
return { exitCode: 0, stdout: documentLine(accepted.value), stderr: '' };
|
|
166
172
|
};
|
|
167
|
-
const statusCliCommand = async (
|
|
173
|
+
const statusCliCommand = async (descriptorUri, rest) => {
|
|
168
174
|
if (rest.length > 0)
|
|
169
175
|
return usageResult;
|
|
170
|
-
const descriptor = await readVisualSessionDescriptor(
|
|
176
|
+
const descriptor = await readVisualSessionDescriptor(descriptorUri);
|
|
171
177
|
if (!descriptor.ok)
|
|
172
178
|
return refusalResult(descriptor.diagnostics);
|
|
173
179
|
const status = await fetchVisualStatus(descriptor.value);
|
|
@@ -175,11 +181,11 @@ const statusCliCommand = async (descriptorPath, rest, cwd) => {
|
|
|
175
181
|
return refusalResult(status.diagnostics);
|
|
176
182
|
return { exitCode: 0, stdout: documentLine(status.value), stderr: '' };
|
|
177
183
|
};
|
|
178
|
-
const recoverCliCommand = async (
|
|
184
|
+
const recoverCliCommand = async (descriptorUri, rest) => {
|
|
179
185
|
const includeTranscript = transcriptFlag(rest);
|
|
180
186
|
if (includeTranscript === undefined)
|
|
181
187
|
return usageResult;
|
|
182
|
-
const descriptor = await readVisualSessionDescriptor(
|
|
188
|
+
const descriptor = await readVisualSessionDescriptor(descriptorUri);
|
|
183
189
|
if (!descriptor.ok)
|
|
184
190
|
return refusalResult(descriptor.diagnostics);
|
|
185
191
|
const handoff = await recoverVisualSessionClient(descriptor.value, includeTranscript);
|
|
@@ -201,13 +207,13 @@ const recoverCliCommand = async (descriptorPath, rest, cwd) => {
|
|
|
201
207
|
* descriptor that did not parse, and a corrupt, redirected, or foreign
|
|
202
208
|
* descriptor fails exactly as before.
|
|
203
209
|
*/
|
|
204
|
-
const stopCliCommand = async (
|
|
210
|
+
const stopCliCommand = async (descriptorUri, rest) => {
|
|
205
211
|
const includeTranscript = transcriptFlag(rest);
|
|
206
212
|
if (includeTranscript === undefined)
|
|
207
213
|
return usageResult;
|
|
208
|
-
const descriptor = await readVisualSessionDescriptor(
|
|
214
|
+
const descriptor = await readVisualSessionDescriptor(descriptorUri);
|
|
209
215
|
if (!descriptor.ok) {
|
|
210
|
-
return (await visualSessionAlreadyStopped(
|
|
216
|
+
return (await visualSessionAlreadyStopped(descriptorUri))
|
|
211
217
|
? { exitCode: 0, stdout: '', stderr: '' }
|
|
212
218
|
: refusalResult(descriptor.diagnostics);
|
|
213
219
|
}
|
|
@@ -236,17 +242,20 @@ export async function runVisualClientCli(args, cwd = process.cwd()) {
|
|
|
236
242
|
return requestCliCommand(args.slice(1), cwd);
|
|
237
243
|
if (descriptor === undefined)
|
|
238
244
|
return usageResult;
|
|
245
|
+
// `cwd` reaches only the commands that still read a native path of their
|
|
246
|
+
// own. The descriptor argument is a `file:` URI and is never resolved
|
|
247
|
+
// against anything.
|
|
239
248
|
switch (command) {
|
|
240
249
|
case 'wait':
|
|
241
|
-
return waitCliCommand(descriptor, rest
|
|
250
|
+
return waitCliCommand(descriptor, rest);
|
|
242
251
|
case 'respond':
|
|
243
252
|
return respondCliCommand(descriptor, rest, cwd);
|
|
244
253
|
case 'status':
|
|
245
|
-
return statusCliCommand(descriptor, rest
|
|
254
|
+
return statusCliCommand(descriptor, rest);
|
|
246
255
|
case 'recover':
|
|
247
|
-
return recoverCliCommand(descriptor, rest
|
|
256
|
+
return recoverCliCommand(descriptor, rest);
|
|
248
257
|
case 'stop':
|
|
249
|
-
return stopCliCommand(descriptor, rest
|
|
258
|
+
return stopCliCommand(descriptor, rest);
|
|
250
259
|
default:
|
|
251
260
|
return usageResult;
|
|
252
261
|
}
|
|
@@ -259,7 +268,7 @@ const processIo = {
|
|
|
259
268
|
* Serves one visual session in the foreground.
|
|
260
269
|
*
|
|
261
270
|
* The request is validated before any filesystem or network effect, so a bad
|
|
262
|
-
* document costs nothing. Exactly one public `visual-session-started/
|
|
271
|
+
* document costs nothing. Exactly one public `visual-session-started/v2` line is
|
|
263
272
|
* published before the command blocks — it names the private descriptor but
|
|
264
273
|
* never carries the agent capability, which lives only in that mode 0600 file.
|
|
265
274
|
* The command then holds the session open until the runtime closes it, whether
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `apply` command, and the only part of applying a batch that touches a
|
|
3
|
+
* filesystem.
|
|
4
|
+
*
|
|
5
|
+
* It lives apart from `./apply-command.js` for the reason `./nesting.js` and
|
|
6
|
+
* `./kind-label.js` state for their own values: the engine half is reachable
|
|
7
|
+
* from a browser now (#252), and a top-level `node:fs` import is a bundle
|
|
8
|
+
* failure whether or not the function that uses it is ever called. Core reads
|
|
9
|
+
* nothing (ADR 0100) - this is the caller that reads for it.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync } from 'node:fs';
|
|
12
|
+
import { resolve } from 'node:path';
|
|
13
|
+
import { parseDocument } from 'yaml';
|
|
14
|
+
import { landOperations, posixDirectoryOf } from './apply-command.js';
|
|
15
|
+
import { createFileSystemStore } from './source-store.js';
|
|
16
|
+
import { loadWorkspaceManifest } from './workspace.js';
|
|
17
|
+
import { diagnosticJson, humanDiagnostics, usage } from './cli-support.js';
|
|
18
|
+
export function runApplyCommand(options, cwd) {
|
|
19
|
+
const json = options.includes('--json');
|
|
20
|
+
const rest = options.filter((option) => option !== '--json');
|
|
21
|
+
const [operationsPath, workspacePath] = rest;
|
|
22
|
+
if (rest.length !== 2 ||
|
|
23
|
+
operationsPath === undefined ||
|
|
24
|
+
workspacePath === undefined ||
|
|
25
|
+
rest.some((option) => option.startsWith('-'))) {
|
|
26
|
+
return { exitCode: 2, stdout: '', stderr: usage };
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const manifestSource = readFileSync(resolve(cwd, workspacePath), 'utf8');
|
|
30
|
+
if (parseDocument(manifestSource).get('format') !== 'yarramate/workspace/v1') {
|
|
31
|
+
return {
|
|
32
|
+
exitCode: 2,
|
|
33
|
+
stdout: '',
|
|
34
|
+
stderr: 'apply requires an explicit workspace manifest (yarramate/workspace/v1)\n',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const operationsSource = readFileSync(resolve(cwd, operationsPath), 'utf8');
|
|
38
|
+
const loadedWorkspace = loadWorkspaceManifest({ path: workspacePath, source: manifestSource }, cwd);
|
|
39
|
+
if (!loadedWorkspace.ok) {
|
|
40
|
+
return {
|
|
41
|
+
exitCode: 1,
|
|
42
|
+
stdout: json
|
|
43
|
+
? diagnosticJson(loadedWorkspace.diagnostics)
|
|
44
|
+
: humanDiagnostics(loadedWorkspace.diagnostics),
|
|
45
|
+
stderr: '',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const outcome = landOperations(createFileSystemStore(cwd), {
|
|
49
|
+
workspace: loadedWorkspace.workspace,
|
|
50
|
+
operations: { path: operationsPath, source: operationsSource },
|
|
51
|
+
manifestDirectory: posixDirectoryOf(workspacePath),
|
|
52
|
+
});
|
|
53
|
+
if (!outcome.ok) {
|
|
54
|
+
return {
|
|
55
|
+
exitCode: 1,
|
|
56
|
+
stdout: json
|
|
57
|
+
? diagnosticJson(outcome.diagnostics)
|
|
58
|
+
: humanDiagnostics(outcome.diagnostics),
|
|
59
|
+
stderr: '',
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const { result } = outcome;
|
|
63
|
+
if (json) {
|
|
64
|
+
return {
|
|
65
|
+
exitCode: 0,
|
|
66
|
+
stdout: `${JSON.stringify(result, null, 2)}\n`,
|
|
67
|
+
stderr: '',
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Every counter, summed by iteration rather than by hand, so a new
|
|
71
|
+
// operation kind cannot silently report zero work.
|
|
72
|
+
const applied = Object.values(result.applied).reduce((total, count) => total + count, 0);
|
|
73
|
+
return {
|
|
74
|
+
exitCode: 0,
|
|
75
|
+
stdout: `Applied ${applied} operation${applied === 1 ? '' : 's'} to ${result.documents.join(', ')}\n`,
|
|
76
|
+
stderr: '',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
+
return { exitCode: 2, stdout: '', stderr: `${message}\n` };
|
|
82
|
+
}
|
|
83
|
+
}
|
package/dist/apply-command.d.ts
CHANGED
|
@@ -1,12 +1,93 @@
|
|
|
1
|
-
import { type CliResult } from './cli-support.js';
|
|
2
1
|
import { type Diagnostic, type WorkspaceSource } from './compiler.js';
|
|
2
|
+
import type { ResolvedWorkspace } from './workspace.js';
|
|
3
|
+
import { type PendingWrite, type SourceStore, type WriteConflict } from './source-store.js';
|
|
4
|
+
/**
|
|
5
|
+
* The directory part of a workspace path, in the `/`-separated terms a
|
|
6
|
+
* manifest is written in rather than the platform's.
|
|
7
|
+
*
|
|
8
|
+
* Both separators by hand rather than `node:path`'s `sep`: this module is
|
|
9
|
+
* reachable from a browser (#252), where there is no platform separator to
|
|
10
|
+
* ask about, and a manifest path is `/`-separated wherever it is read.
|
|
11
|
+
*/
|
|
12
|
+
export declare const posixDirectoryOf: (path: string) => string;
|
|
3
13
|
import type { YarramateApplyResult } from './operations.js';
|
|
4
14
|
export type ApplyOutcome = {
|
|
5
15
|
readonly ok: true;
|
|
16
|
+
/**
|
|
17
|
+
* The documents this batch changed, for the caller to write. Core
|
|
18
|
+
* returns them rather than writing them, so the store's comparison is
|
|
19
|
+
* the last thing that happens before bytes land (ADR 0100).
|
|
20
|
+
*/
|
|
21
|
+
readonly sources: readonly WorkspaceSource[];
|
|
6
22
|
readonly result: YarramateApplyResult;
|
|
7
23
|
} | {
|
|
8
24
|
readonly ok: false;
|
|
9
25
|
readonly diagnostics: readonly Diagnostic[];
|
|
10
26
|
};
|
|
11
|
-
|
|
12
|
-
|
|
27
|
+
/**
|
|
28
|
+
* Everything `applyOperations` is allowed to know. The workspace is already
|
|
29
|
+
* resolved and the sources are already read, because Core does not reach for
|
|
30
|
+
* either (ADR 0100).
|
|
31
|
+
*/
|
|
32
|
+
export interface ApplyInput {
|
|
33
|
+
/** The workspace this batch is addressed to, already resolved. */
|
|
34
|
+
readonly workspace: ResolvedWorkspace;
|
|
35
|
+
/** Every source that workspace resolves to, keyed by its manifest path. */
|
|
36
|
+
readonly sources: readonly WorkspaceSource[];
|
|
37
|
+
/** The operations document itself. */
|
|
38
|
+
readonly operations: WorkspaceSource;
|
|
39
|
+
/**
|
|
40
|
+
* Where the manifest sits relative to the paths in `workspace`, so an
|
|
41
|
+
* operation may address a document the way the manifest names it as well as
|
|
42
|
+
* the way the workspace lists it (#216). String arithmetic, not a lookup.
|
|
43
|
+
*/
|
|
44
|
+
readonly manifestDirectory: string;
|
|
45
|
+
}
|
|
46
|
+
export declare const applyOperations: (input: ApplyInput) => ApplyOutcome;
|
|
47
|
+
export type PlannedOperations = {
|
|
48
|
+
readonly ok: true;
|
|
49
|
+
readonly outcome: ApplyOutcome & {
|
|
50
|
+
readonly ok: true;
|
|
51
|
+
};
|
|
52
|
+
/** What this batch would write, unwritten. Empty when it changes nothing. */
|
|
53
|
+
readonly writes: readonly PendingWrite[];
|
|
54
|
+
} | {
|
|
55
|
+
readonly ok: false;
|
|
56
|
+
readonly outcome: ApplyOutcome & {
|
|
57
|
+
readonly ok: false;
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Reads a workspace through a store and applies a batch, stopping short of
|
|
62
|
+
* writing (ADR 0100).
|
|
63
|
+
*
|
|
64
|
+
* Separated from {@link landOperations} because a caller may have writes of
|
|
65
|
+
* its own to land in the same batch: the visual runtime commits a projection
|
|
66
|
+
* beside the model it belongs to, and one `writeAll` is what makes the two
|
|
67
|
+
* arrive together or not at all (ADR 0103). A caller with nothing to add wants
|
|
68
|
+
* `landOperations` and should not see this.
|
|
69
|
+
*/
|
|
70
|
+
export declare const planOperations: (store: SourceStore, input: {
|
|
71
|
+
readonly workspace: ResolvedWorkspace;
|
|
72
|
+
readonly operations: WorkspaceSource;
|
|
73
|
+
readonly manifestDirectory: string;
|
|
74
|
+
}) => PlannedOperations;
|
|
75
|
+
/**
|
|
76
|
+
* The diagnostics a refused `writeAll` becomes, in the workspace range
|
|
77
|
+
* (ADR 0100). Shared so a caller that merges writes of its own into the batch
|
|
78
|
+
* reports a conflict the same way the CLI does.
|
|
79
|
+
*/
|
|
80
|
+
export declare const writeConflictDiagnostics: (conflicts: readonly WriteConflict[], operationsPath: string) => readonly Diagnostic[];
|
|
81
|
+
/**
|
|
82
|
+
* Reads a workspace through a store, applies a batch, and writes what changed
|
|
83
|
+
* back only if every document still holds what was read (ADR 0100).
|
|
84
|
+
*
|
|
85
|
+
* This is the composition Core deliberately does not perform: `applyOperations`
|
|
86
|
+
* is a pure function, and the comparison that decides whether a batch lands
|
|
87
|
+
* belongs to the store.
|
|
88
|
+
*/
|
|
89
|
+
export declare const landOperations: (store: SourceStore, input: {
|
|
90
|
+
readonly workspace: ResolvedWorkspace;
|
|
91
|
+
readonly operations: WorkspaceSource;
|
|
92
|
+
readonly manifestDirectory: string;
|
|
93
|
+
}) => ApplyOutcome;
|