omgbase 0.11.1 → 0.13.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/build-info.json +2 -2
- package/dist/src/cmd/_mutate.d.ts +51 -16
- package/dist/src/cmd/_mutate.js +133 -52
- package/dist/src/cmd/docs.js +0 -0
- package/dist/src/cmd/edit.js +7 -7
- package/dist/src/cmd/help.js +1 -1
- package/dist/src/cmd/mcp.js +16 -4
- package/dist/src/cmd/mutate.d.ts +1 -0
- package/dist/src/cmd/mutate.js +114 -65
- package/dist/src/cmd/node.js +7 -7
- package/dist/src/cmd/retarget.js +27 -24
- package/dist/src/cmd/source.js +4 -2
- package/dist/src/cmd/sync.js +12 -6
- package/dist/src/cmd/update.js +51 -40
- package/dist/src/commands.js +2 -1
- package/dist/src/context.js +3 -2
- package/dist/src/dispatch.js +9 -4
- package/dist/src/seams.d.ts +3 -0
- package/dist/src/seams.js +39 -3
- package/dist/src/shell/refs.js +3 -10
- package/package.json +3 -3
package/dist/build-info.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type EffectTarget, type Plan, type Receipt, type Sugar } from "@omgbase/core";
|
|
2
2
|
import type { Cli } from "../context.js";
|
|
3
3
|
import type { RepoRow } from "@omgbase/core";
|
|
4
4
|
import type { Workspace } from "@omgbase/core";
|
|
@@ -25,30 +25,65 @@ export declare function readStdin(): string;
|
|
|
25
25
|
export declare function readIdsFromStdin(): string[];
|
|
26
26
|
/** Expand a block-list argument list, substituting stdin ids for a bare "-". */
|
|
27
27
|
export declare function expandBlockArgs(args: string[]): string[];
|
|
28
|
-
export
|
|
28
|
+
export declare function defaultActor(): string;
|
|
29
|
+
export interface RunOpts {
|
|
29
30
|
reason?: string;
|
|
30
31
|
actor?: string;
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* cli.flags.
|
|
36
|
-
*
|
|
34
|
+
* Run a builder's effects as one request (`applySugar`: plan + execute under the
|
|
35
|
+
* writer flock, the sugar's reason unless `--reason` overrides it) and render the
|
|
36
|
+
* outcome. Honors cli.flags.dryRun — the Plan: per-file unified diffs to stdout,
|
|
37
|
+
* nothing minted, nothing committed (§3.6) — and cli.flags.mode (--json emits
|
|
38
|
+
* the Receipt, or the Plan on a dry run, verbatim). An empty effect list is
|
|
39
|
+
* ` nothing to do`.
|
|
37
40
|
*/
|
|
38
|
-
export declare function
|
|
41
|
+
export declare function runSugar(cli: Cli, ws: Workspace, repo: RepoRow, sugar: Sugar, opts?: RunOpts): number;
|
|
39
42
|
/**
|
|
40
|
-
* Remote peer of
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Remote peer of runSugar: call the sugar tool that mirrors this command
|
|
44
|
+
* (blocks_insert, blocks_check, …) against the `--server` engine — the server
|
|
45
|
+
* resolves refs + pins CAS + applies — and render the returned Receipt with the
|
|
46
|
+
* SAME renderers as local. The global --dry-run is the `plan` tool over the
|
|
47
|
+
* effects the verb would build (`effects` composes them from the refs as given:
|
|
48
|
+
* an id names its row, anything else a document path — the server resolves);
|
|
49
|
+
* no tool takes `dry_run` since surface 4.0.
|
|
45
50
|
*/
|
|
46
|
-
export declare function
|
|
51
|
+
export declare function runSugarRemote(cli: Cli, tool: string, toolArgs: Record<string, unknown>, effects: () => unknown[] | Promise<unknown[]>): Promise<number>;
|
|
52
|
+
/** A ref as an effect target for a remote `plan`: a `b_` id is the block, a `d_` id the document, anything else a document path. */
|
|
53
|
+
export declare function targetOf(ref: string): EffectTarget;
|
|
54
|
+
/**
|
|
55
|
+
* `apply` given `{ effects }`: plan + execute as one transaction
|
|
56
|
+
* (`applyEffects`, spec/mutate §11.7) under the writer flock, rendered as a
|
|
57
|
+
* Receipt. `--dry-run` plans only and renders the Plan (§3.6: planning runs
|
|
58
|
+
* under the symbolic minter and the ids it reports are `$new.n`). An empty
|
|
59
|
+
* list is `nothing to do`.
|
|
60
|
+
*/
|
|
61
|
+
export declare function runEffects(cli: Cli, ws: Workspace, repo: RepoRow, effects: unknown[], opts?: RunOpts): number;
|
|
62
|
+
/** Remote peer of `runEffects`: the `apply` tool with `effects` (the Receipt), or on a dry run the `plan` tool (the Plan). */
|
|
63
|
+
export declare function runEffectsRemote(cli: Cli, effects: unknown[], reason?: string): Promise<number>;
|
|
64
|
+
/** `execute`: commit exactly a Plan (the plan form, spec/mutate §11.7 — admission, then the one transaction), rendered as a Receipt. */
|
|
65
|
+
export declare function runExecutePlan(cli: Cli, ws: Workspace, repo: RepoRow, plan: unknown, opts?: RunOpts): number;
|
|
66
|
+
/** Remote peer of `runExecutePlan`: the `execute` tool's plan form. */
|
|
67
|
+
export declare function runExecutePlanRemote(cli: Cli, plan: unknown, reason?: string): Promise<number>;
|
|
68
|
+
/** A Plan, dry-run style: `--json` the Plan verbatim (what `execute` takes back); human the §3.6 note and the per-file diffs of its preview. */
|
|
69
|
+
export declare function renderPlan(cli: Cli, plan: Plan): number;
|
|
70
|
+
/**
|
|
71
|
+
* A Receipt (any write tool's result since 4.0 — a `links_*` result carries its
|
|
72
|
+
* `hits`/`pairs` beside the receipt's keys): `--json` verbatim; human the
|
|
73
|
+
* commit confirmation of §4 (one revision per touched document) and, on
|
|
74
|
+
* stdout, the ids the write names — the plan's `preview.results[].ids` with
|
|
75
|
+
* every `$new.n` realized from `new_ids` when the plan is at hand with its
|
|
76
|
+
* preview (a local write, any `execute`); else (a plan echoed without
|
|
77
|
+
* `preview`, a remote write) every id the plan minted, the `new_ids` values in
|
|
78
|
+
* order — which includes the discarded mints of a re-parse (`spec/mutate` §10),
|
|
79
|
+
* ids that name no row.
|
|
80
|
+
*/
|
|
81
|
+
export declare function renderReceipt(cli: Cli, receipt: Receipt, plan?: unknown): number;
|
|
82
|
+
export declare function receiptIds(receipt: Receipt, plan: unknown): string[];
|
|
47
83
|
/**
|
|
48
84
|
* The dry-run rendering every write verb shares (spec/cli §3.6): the note on
|
|
49
|
-
* stderr, then per changed file
|
|
50
|
-
* (`spec/surface` §3) and an empty line.
|
|
51
|
-
* `DocOpResult.diffs` here; block verbs their `ApplyResult.diffs`.
|
|
85
|
+
* stderr, then per changed file of the Plan's `preview.diffs` the path, the
|
|
86
|
+
* unified diff of before → after (`spec/surface` §3) and an empty line.
|
|
52
87
|
*/
|
|
53
88
|
export declare function renderDiffs(cli: Cli, diffs: Record<string, {
|
|
54
89
|
before: string;
|
package/dist/src/cmd/_mutate.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { readFileSync, readSync } from "node:fs";
|
|
2
2
|
import { userInfo } from "node:os";
|
|
3
|
-
import {
|
|
3
|
+
import { applyEffectsDetailed, applySugar, executePlan, isValidId, substituteSymbols, unifiedDiff, surfacePlan, surfaceReceipt } from "@omgbase/core";
|
|
4
4
|
import { CliUsageError, EXIT_OK, EXIT_ERROR } from "../output.js";
|
|
5
5
|
import { remoteCall } from "./_remote.js";
|
|
6
|
-
// Shared plumbing for every write command (
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
6
|
+
// Shared plumbing for every write command (spec/cli §3.6, §4; ADR-021's
|
|
7
|
+
// retirement release, cli 4.0). The sugar verbs build their effects with the
|
|
8
|
+
// shared builders of `@omgbase/core` (`mutate/sugar.ts` — the same table the
|
|
9
|
+
// MCP tools use), then hand them here: this sets origin.actor (human:$USER by
|
|
10
|
+
// default, override --actor), runs under the writer flock (omgbaseDir),
|
|
11
|
+
// threads the global --dry-run as a `plan` (render the preview's diffs, mint
|
|
12
|
+
// nothing, commit nothing), and renders the Receipt or the Plan uniformly.
|
|
13
|
+
// Typed conflicts are rendered by the caller's catch via the output contract;
|
|
14
|
+
// we let the planner throw.
|
|
11
15
|
/** Read markdown content from -m, -f (file, or "-" = stdin), or bare stdin. */
|
|
12
16
|
export function readContent(values, allowStdin = true) {
|
|
13
17
|
if (values.m !== undefined)
|
|
@@ -108,7 +112,7 @@ export function expandBlockArgs(args) {
|
|
|
108
112
|
}
|
|
109
113
|
return out;
|
|
110
114
|
}
|
|
111
|
-
function defaultActor() {
|
|
115
|
+
export function defaultActor() {
|
|
112
116
|
try {
|
|
113
117
|
return `human:${userInfo().username}`;
|
|
114
118
|
}
|
|
@@ -117,61 +121,149 @@ function defaultActor() {
|
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* cli.flags.
|
|
123
|
-
*
|
|
124
|
+
* Run a builder's effects as one request (`applySugar`: plan + execute under the
|
|
125
|
+
* writer flock, the sugar's reason unless `--reason` overrides it) and render the
|
|
126
|
+
* outcome. Honors cli.flags.dryRun — the Plan: per-file unified diffs to stdout,
|
|
127
|
+
* nothing minted, nothing committed (§3.6) — and cli.flags.mode (--json emits
|
|
128
|
+
* the Receipt, or the Plan on a dry run, verbatim). An empty effect list is
|
|
129
|
+
* ` nothing to do`.
|
|
124
130
|
*/
|
|
125
|
-
export function
|
|
126
|
-
if (
|
|
131
|
+
export function runSugar(cli, ws, repo, sugar, opts = {}) {
|
|
132
|
+
if (sugar.effects.length === 0) {
|
|
127
133
|
cli.io.err(cli.style.dim(" nothing to do"));
|
|
128
134
|
return EXIT_OK;
|
|
129
135
|
}
|
|
130
|
-
const
|
|
136
|
+
const { plan, receipt } = applySugar(ws.store, {
|
|
131
137
|
repoId: repo.repoId,
|
|
132
138
|
...(repo.rootPath ? { rootPath: repo.rootPath } : {}),
|
|
133
|
-
ops,
|
|
134
|
-
origin: { actor: opts.actor ?? defaultActor(), ...(opts.reason ? { reason: opts.reason } : {}) },
|
|
135
139
|
omgbaseDir: ws.omgbaseDir,
|
|
140
|
+
actor: opts.actor ?? defaultActor(),
|
|
141
|
+
...(opts.reason ? { reason: opts.reason } : {}),
|
|
136
142
|
...(cli.flags.dryRun ? { dryRun: true } : {}),
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
143
|
+
}, sugar);
|
|
144
|
+
if (!receipt)
|
|
145
|
+
return renderPlan(cli, surfacePlan(plan));
|
|
146
|
+
return renderReceipt(cli, surfaceReceipt(receipt), plan);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Remote peer of runSugar: call the sugar tool that mirrors this command
|
|
150
|
+
* (blocks_insert, blocks_check, …) against the `--server` engine — the server
|
|
151
|
+
* resolves refs + pins CAS + applies — and render the returned Receipt with the
|
|
152
|
+
* SAME renderers as local. The global --dry-run is the `plan` tool over the
|
|
153
|
+
* effects the verb would build (`effects` composes them from the refs as given:
|
|
154
|
+
* an id names its row, anything else a document path — the server resolves);
|
|
155
|
+
* no tool takes `dry_run` since surface 4.0.
|
|
156
|
+
*/
|
|
157
|
+
export async function runSugarRemote(cli, tool, toolArgs, effects) {
|
|
158
|
+
if (cli.flags.dryRun)
|
|
159
|
+
return renderPlan(cli, await remoteCall(cli, "plan", { effects: await effects() }));
|
|
160
|
+
return renderReceipt(cli, await remoteCall(cli, tool, toolArgs));
|
|
161
|
+
}
|
|
162
|
+
/** A ref as an effect target for a remote `plan`: a `b_` id is the block, a `d_` id the document, anything else a document path. */
|
|
163
|
+
export function targetOf(ref) {
|
|
164
|
+
if (isValidId(ref, "b"))
|
|
165
|
+
return { kind: "block", id: ref };
|
|
166
|
+
if (isValidId(ref, "d"))
|
|
167
|
+
return { kind: "document", id: ref };
|
|
168
|
+
return { kind: "document", path: ref };
|
|
142
169
|
}
|
|
170
|
+
// ---- the Effect layer (ADR-021; spec/cli 3.2 `apply -f { effects }`, `execute`) ----
|
|
143
171
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
172
|
+
* `apply` given `{ effects }`: plan + execute as one transaction
|
|
173
|
+
* (`applyEffects`, spec/mutate §11.7) under the writer flock, rendered as a
|
|
174
|
+
* Receipt. `--dry-run` plans only and renders the Plan (§3.6: planning runs
|
|
175
|
+
* under the symbolic minter and the ids it reports are `$new.n`). An empty
|
|
176
|
+
* list is `nothing to do`.
|
|
149
177
|
*/
|
|
150
|
-
export
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
178
|
+
export function runEffects(cli, ws, repo, effects, opts = {}) {
|
|
179
|
+
if (effects.length === 0) {
|
|
180
|
+
cli.io.err(cli.style.dim(" nothing to do"));
|
|
181
|
+
return EXIT_OK;
|
|
182
|
+
}
|
|
183
|
+
const req = {
|
|
184
|
+
repoId: repo.repoId,
|
|
185
|
+
...(repo.rootPath ? { rootPath: repo.rootPath } : {}),
|
|
186
|
+
effects,
|
|
187
|
+
origin: { actor: opts.actor ?? defaultActor(), ...(opts.reason ? { reason: opts.reason } : {}) },
|
|
188
|
+
omgbaseDir: ws.omgbaseDir,
|
|
189
|
+
};
|
|
190
|
+
const { plan, receipt } = applyEffectsDetailed(ws.store, { ...req, ...(cli.flags.dryRun ? { dryRun: true } : {}) });
|
|
191
|
+
if (!receipt)
|
|
192
|
+
return renderPlan(cli, surfacePlan(plan));
|
|
193
|
+
return renderReceipt(cli, surfaceReceipt(receipt), plan);
|
|
194
|
+
}
|
|
195
|
+
/** Remote peer of `runEffects`: the `apply` tool with `effects` (the Receipt), or on a dry run the `plan` tool (the Plan). */
|
|
196
|
+
export async function runEffectsRemote(cli, effects, reason) {
|
|
197
|
+
if (cli.flags.dryRun)
|
|
198
|
+
return renderPlan(cli, await remoteCall(cli, "plan", { effects }));
|
|
199
|
+
return renderReceipt(cli, await remoteCall(cli, "apply", { effects, ...(reason ? { reason } : {}) }));
|
|
200
|
+
}
|
|
201
|
+
/** `execute`: commit exactly a Plan (the plan form, spec/mutate §11.7 — admission, then the one transaction), rendered as a Receipt. */
|
|
202
|
+
export function runExecutePlan(cli, ws, repo, plan, opts = {}) {
|
|
203
|
+
const receipt = executePlan(ws.store, {
|
|
204
|
+
repoId: repo.repoId,
|
|
205
|
+
...(repo.rootPath ? { rootPath: repo.rootPath } : {}),
|
|
206
|
+
plan,
|
|
207
|
+
origin: { actor: opts.actor ?? defaultActor(), ...(opts.reason ? { reason: opts.reason } : {}) },
|
|
208
|
+
omgbaseDir: ws.omgbaseDir,
|
|
154
209
|
});
|
|
155
|
-
return
|
|
210
|
+
return renderReceipt(cli, surfaceReceipt(receipt), plan);
|
|
156
211
|
}
|
|
157
|
-
/**
|
|
158
|
-
function
|
|
159
|
-
cli
|
|
212
|
+
/** Remote peer of `runExecutePlan`: the `execute` tool's plan form. */
|
|
213
|
+
export async function runExecutePlanRemote(cli, plan, reason) {
|
|
214
|
+
return renderReceipt(cli, await remoteCall(cli, "execute", { plan, ...(reason ? { reason } : {}) }), plan);
|
|
215
|
+
}
|
|
216
|
+
/** A Plan, dry-run style: `--json` the Plan verbatim (what `execute` takes back); human the §3.6 note and the per-file diffs of its preview. */
|
|
217
|
+
export function renderPlan(cli, plan) {
|
|
218
|
+
cli.capture?.(plan);
|
|
160
219
|
if (cli.flags.mode !== "human") {
|
|
161
|
-
cli.io.out(JSON.stringify(
|
|
220
|
+
cli.io.out(JSON.stringify(plan));
|
|
162
221
|
return EXIT_OK;
|
|
163
222
|
}
|
|
164
|
-
|
|
223
|
+
renderDiffs(cli, plan.preview?.diffs ?? {});
|
|
224
|
+
return EXIT_OK;
|
|
165
225
|
}
|
|
166
|
-
|
|
167
|
-
|
|
226
|
+
/**
|
|
227
|
+
* A Receipt (any write tool's result since 4.0 — a `links_*` result carries its
|
|
228
|
+
* `hits`/`pairs` beside the receipt's keys): `--json` verbatim; human the
|
|
229
|
+
* commit confirmation of §4 (one revision per touched document) and, on
|
|
230
|
+
* stdout, the ids the write names — the plan's `preview.results[].ids` with
|
|
231
|
+
* every `$new.n` realized from `new_ids` when the plan is at hand with its
|
|
232
|
+
* preview (a local write, any `execute`); else (a plan echoed without
|
|
233
|
+
* `preview`, a remote write) every id the plan minted, the `new_ids` values in
|
|
234
|
+
* order — which includes the discarded mints of a re-parse (`spec/mutate` §10),
|
|
235
|
+
* ids that name no row.
|
|
236
|
+
*/
|
|
237
|
+
export function renderReceipt(cli, receipt, plan) {
|
|
238
|
+
const ids = receiptIds(receipt, plan);
|
|
239
|
+
cli.capture?.({ ...receipt, ids }); // shell: the ids the write names are the frame
|
|
240
|
+
if (cli.flags.mode !== "human") {
|
|
241
|
+
cli.io.out(JSON.stringify(receipt));
|
|
242
|
+
return EXIT_OK;
|
|
243
|
+
}
|
|
244
|
+
const { render, style, io } = cli;
|
|
245
|
+
const n = receipt.revisions.length;
|
|
246
|
+
io.err(style.dim(` ${style.ok(render.g.ok)} committed · ${n} document${n === 1 ? "" : "s"} touched`));
|
|
247
|
+
for (const id of ids)
|
|
248
|
+
io.out(id);
|
|
168
249
|
return EXIT_OK;
|
|
169
250
|
}
|
|
251
|
+
export function receiptIds(receipt, plan) {
|
|
252
|
+
const results = plan?.preview?.results;
|
|
253
|
+
if (!Array.isArray(results))
|
|
254
|
+
return Object.values(receipt.new_ids);
|
|
255
|
+
const ids = [];
|
|
256
|
+
for (const r of results)
|
|
257
|
+
if (Array.isArray(r.ids))
|
|
258
|
+
for (const id of r.ids)
|
|
259
|
+
if (typeof id === "string")
|
|
260
|
+
ids.push(id);
|
|
261
|
+
return substituteSymbols(ids, receipt.new_ids);
|
|
262
|
+
}
|
|
170
263
|
/**
|
|
171
264
|
* The dry-run rendering every write verb shares (spec/cli §3.6): the note on
|
|
172
|
-
* stderr, then per changed file
|
|
173
|
-
* (`spec/surface` §3) and an empty line.
|
|
174
|
-
* `DocOpResult.diffs` here; block verbs their `ApplyResult.diffs`.
|
|
265
|
+
* stderr, then per changed file of the Plan's `preview.diffs` the path, the
|
|
266
|
+
* unified diff of before → after (`spec/surface` §3) and an empty line.
|
|
175
267
|
*/
|
|
176
268
|
export function renderDiffs(cli, diffs) {
|
|
177
269
|
const { style, io } = cli;
|
|
@@ -194,16 +286,5 @@ export function unifiedDiffLines(cli, before, after) {
|
|
|
194
286
|
return style.dim(line);
|
|
195
287
|
});
|
|
196
288
|
}
|
|
197
|
-
function renderCommitted(cli, result) {
|
|
198
|
-
const { render, style, io } = cli;
|
|
199
|
-
const g = render.g;
|
|
200
|
-
const n = result.revisions.length;
|
|
201
|
-
io.err(style.dim(` ${style.ok(g.ok)} committed · ${n} document${n === 1 ? "" : "s"} touched`));
|
|
202
|
-
// stdout stays useful for pipes: the ids minted/affected, one per line.
|
|
203
|
-
const ids = result.results.flatMap((r) => r.ids);
|
|
204
|
-
for (const id of ids)
|
|
205
|
-
io.out(id);
|
|
206
|
-
return EXIT_OK;
|
|
207
|
-
}
|
|
208
289
|
export { EXIT_OK, EXIT_ERROR };
|
|
209
290
|
//# sourceMappingURL=_mutate.js.map
|
package/dist/src/cmd/docs.js
CHANGED
|
Binary file
|
package/dist/src/cmd/edit.js
CHANGED
|
@@ -3,13 +3,14 @@ import { spawnSync } from "node:child_process";
|
|
|
3
3
|
import { writeFileSync, readFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { join } from "node:path";
|
|
6
|
-
import { resolveRef, nodesGet } from "@omgbase/core";
|
|
6
|
+
import { resolveRef, nodesGet, updateBlockEffects } from "@omgbase/core";
|
|
7
7
|
import { CliUsageError, EngineErrorLike, EXIT_OK, renderHelp } from "../output.js";
|
|
8
|
-
import {
|
|
8
|
+
import { runSugar } from "./_mutate.js";
|
|
9
9
|
// `omg edit <block>` (11 §5.6, §5.7): read the block's raw markdown → open it in
|
|
10
|
-
// $EDITOR →
|
|
11
|
-
//
|
|
12
|
-
//
|
|
10
|
+
// $EDITOR → `set markdown` with the PRE-READ hash pinned as `expect.content_hash`
|
|
11
|
+
// (the `blocks_update` builder; cli 4.0). A mid-edit change by someone else
|
|
12
|
+
// lands as a clean stale_expectation (with current truth), never a lost write.
|
|
13
|
+
// The human structural-edit loop.
|
|
13
14
|
function rawHashOf(ws, blockId) {
|
|
14
15
|
const row = ws.store.db
|
|
15
16
|
.prepare("SELECT lower(hex(raw_hash)) h FROM blocks WHERE block_id = ? AND deleted_commit IS NULL")
|
|
@@ -63,8 +64,7 @@ function runEdit(cli, args) {
|
|
|
63
64
|
cli.io.err(cli.style.dim(" no changes"));
|
|
64
65
|
return EXIT_OK;
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
return runOps(cli, ws, repo, [op], values.actor ? { actor: values.actor } : {});
|
|
67
|
+
return runSugar(cli, ws, repo, updateBlockEffects(blockId, { markdown: after, ...(preHash ? { expect: { content_hash: preHash } } : {}) }), values.actor ? { actor: values.actor } : {});
|
|
68
68
|
}
|
|
69
69
|
finally {
|
|
70
70
|
rmSync(dir, { recursive: true, force: true });
|
package/dist/src/cmd/help.js
CHANGED
|
@@ -7,7 +7,7 @@ const GROUPS = [
|
|
|
7
7
|
{ title: "orient & read", names: ["status", "ls", "outline", "cat", "show", "find"] },
|
|
8
8
|
{ title: "query", names: ["query", "run"] },
|
|
9
9
|
{ title: "history & links", names: ["log", "hist", "diff", "links"] },
|
|
10
|
-
{ title: "mutate", names: ["apply", "insert", "update", "edit", "move", "rm", "done", "append", "retarget", "split", "merge", "node"] },
|
|
10
|
+
{ title: "mutate", names: ["apply", "execute", "insert", "update", "edit", "move", "rm", "done", "append", "retarget", "split", "merge", "node"] },
|
|
11
11
|
{ title: "documents", names: ["new", "mv", "meta"] },
|
|
12
12
|
{ title: "session", names: ["shell"] },
|
|
13
13
|
{ title: "sync & serve", names: ["sync", "mcp"] },
|
package/dist/src/cmd/mcp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseArgs } from "node:util";
|
|
2
|
-
import { serveStdio, Watcher, WatchLease, watchLeaseLive, freshnessSweep, awaitReady, WATCH_READY_PATIENCE_MS, EmbedDrainer, EngineError } from "@omgbase/core";
|
|
2
|
+
import { serveStdio, Watcher, WatchLease, watchLeaseLive, freshnessSweep, withWriterLock, awaitReady, WATCH_READY_PATIENCE_MS, EmbedDrainer, EngineError } from "@omgbase/core";
|
|
3
3
|
import { EngineErrorLike, EXIT_OK, renderHelp } from "../output.js";
|
|
4
|
-
import { installSpecSeams } from "../seams.js";
|
|
4
|
+
import { installSpecSeams, WRITER_LOCK_TIMEOUT_ENV } from "../seams.js";
|
|
5
5
|
import { loadEmbedding } from "./_embed.js";
|
|
6
6
|
import { openRepoSource } from "./_source.js";
|
|
7
7
|
import { hostInfo } from "../build.js";
|
|
@@ -26,6 +26,9 @@ function announceSpecSeams(cli) {
|
|
|
26
26
|
if (seams.clock !== null) {
|
|
27
27
|
cli.io.err(cli.style.dim(`[mcp] spec seam: clock pinned to ${seams.clock} (OMGBASE_SPEC_CLOCK) — conformance run, not for production`));
|
|
28
28
|
}
|
|
29
|
+
if (seams.writerLockTimeoutMs !== null) {
|
|
30
|
+
cli.io.err(cli.style.dim(`[mcp] test seam: writer lock waits ${seams.writerLockTimeoutMs} ms (${WRITER_LOCK_TIMEOUT_ENV}) — not for production`));
|
|
31
|
+
}
|
|
29
32
|
}
|
|
30
33
|
async function runMcp(cli, args) {
|
|
31
34
|
const { values } = parseArgs({
|
|
@@ -115,7 +118,11 @@ async function runMcp(cli, args) {
|
|
|
115
118
|
// is primed is caught by the sweep, one after it by the feed.
|
|
116
119
|
source = await openRepoSource(ws.store, repo);
|
|
117
120
|
if (source) {
|
|
121
|
+
// Every checkpoint and the priming sweep run under the workspace writer
|
|
122
|
+
// lock (spec/sync §7), as the Rust watcher's do: a verb in another
|
|
123
|
+
// process waits for the ingest instead of racing its file-CAS.
|
|
118
124
|
watcher = new Watcher(ws.store, repo.repoId, source, {
|
|
125
|
+
omgbaseDir: ws.omgbaseDir,
|
|
119
126
|
onCheckpoint: (r) => {
|
|
120
127
|
if (r.ingested.length > 0 || r.deleted.length > 0) {
|
|
121
128
|
cli.io.err(cli.style.dim(`[watch] checkpoint: +${r.ingested.length} -${r.deleted.length}`));
|
|
@@ -129,8 +136,9 @@ async function runMcp(cli, args) {
|
|
|
129
136
|
if (!(await awaitReady(watcher.ready(), WATCH_READY_PATIENCE_MS))) {
|
|
130
137
|
cli.io.err(cli.style.dim(`[watch] the fs adapter did not report ready within ${WATCH_READY_PATIENCE_MS / 1000}s — proceeding as if ready`));
|
|
131
138
|
}
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
const root = repo.rootPath;
|
|
140
|
+
if (root)
|
|
141
|
+
withWriterLock(ws.omgbaseDir, () => freshnessSweep(ws.store, repo.repoId, root)); // prime: anything that landed before the feed was ready
|
|
134
142
|
cli.io.err(cli.style.dim(`[mcp] serving ${repo.slug} on stdio · watcher live`));
|
|
135
143
|
}
|
|
136
144
|
else {
|
|
@@ -161,9 +169,13 @@ async function runMcp(cli, args) {
|
|
|
161
169
|
: failed
|
|
162
170
|
? (_text) => Promise.reject(failed)
|
|
163
171
|
: undefined;
|
|
172
|
+
// The workspace dir makes every write tool take the writer lock for its whole
|
|
173
|
+
// call (spec/sync §7) — `omg` verbs, the Rust `omgbase mcp` and the watcher
|
|
174
|
+
// all lock the same file, so the server must too.
|
|
164
175
|
const handle = await serveStdio({
|
|
165
176
|
store: ws.store,
|
|
166
177
|
repoId: repo.repoId,
|
|
178
|
+
omgbaseDir: ws.omgbaseDir,
|
|
167
179
|
...(repo.rootPath ? { rootPath: repo.rootPath } : {}),
|
|
168
180
|
...(drainer ? { onMutation: () => drainer.schedule() } : {}),
|
|
169
181
|
...(embedQuery ? { embedQuery } : {}),
|
package/dist/src/cmd/mutate.d.ts
CHANGED