@ifc-lite/cli 0.38.2 → 0.39.1
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 +2 -0
- package/dist/commands/flow-ai.d.ts +20 -0
- package/dist/commands/flow-ai.d.ts.map +1 -0
- package/dist/commands/flow-ai.js +35 -0
- package/dist/commands/flow-ai.js.map +1 -0
- package/dist/commands/flow-host.d.ts +2 -1
- package/dist/commands/flow-host.d.ts.map +1 -1
- package/dist/commands/flow-host.js +7 -1
- package/dist/commands/flow-host.js.map +1 -1
- package/dist/commands/flow-review.d.ts +28 -0
- package/dist/commands/flow-review.d.ts.map +1 -0
- package/dist/commands/flow-review.js +154 -0
- package/dist/commands/flow-review.js.map +1 -0
- package/dist/commands/flow-tracking.d.ts +4 -0
- package/dist/commands/flow-tracking.d.ts.map +1 -1
- package/dist/commands/flow-tracking.js +25 -4
- package/dist/commands/flow-tracking.js.map +1 -1
- package/dist/commands/flow.d.ts.map +1 -1
- package/dist/commands/flow.js +124 -27
- package/dist/commands/flow.js.map +1 -1
- package/dist/commands/ids-audit-lint.d.ts +3 -0
- package/dist/commands/ids-audit-lint.d.ts.map +1 -0
- package/dist/commands/ids-audit-lint.js +129 -0
- package/dist/commands/ids-audit-lint.js.map +1 -0
- package/dist/commands/ids-diff.d.ts +2 -0
- package/dist/commands/ids-diff.d.ts.map +1 -0
- package/dist/commands/ids-diff.js +61 -0
- package/dist/commands/ids-diff.js.map +1 -0
- package/dist/commands/ids-explain.d.ts +2 -0
- package/dist/commands/ids-explain.d.ts.map +1 -0
- package/dist/commands/ids-explain.js +53 -0
- package/dist/commands/ids-explain.js.map +1 -0
- package/dist/commands/ids-fmt.d.ts +2 -0
- package/dist/commands/ids-fmt.d.ts.map +1 -0
- package/dist/commands/ids-fmt.js +54 -0
- package/dist/commands/ids-fmt.js.map +1 -0
- package/dist/commands/ids-subcommand.d.ts +31 -0
- package/dist/commands/ids-subcommand.d.ts.map +1 -0
- package/dist/commands/ids-subcommand.js +101 -0
- package/dist/commands/ids-subcommand.js.map +1 -0
- package/dist/commands/ids.d.ts.map +1 -1
- package/dist/commands/ids.js +25 -2
- package/dist/commands/ids.js.map +1 -1
- package/dist/commands/info.js +11 -11
- package/dist/commands/info.js.map +1 -1
- package/dist/commands/semantic-file.d.ts +2 -0
- package/dist/commands/semantic-file.d.ts.map +1 -0
- package/dist/commands/semantic-file.js +24 -0
- package/dist/commands/semantic-file.js.map +1 -0
- package/dist/commands/semantic-serve.d.ts +2 -0
- package/dist/commands/semantic-serve.d.ts.map +1 -0
- package/dist/commands/semantic-serve.js +48 -0
- package/dist/commands/semantic-serve.js.map +1 -0
- package/dist/commands/semantic.d.ts +3 -0
- package/dist/commands/semantic.d.ts.map +1 -0
- package/dist/commands/semantic.js +130 -0
- package/dist/commands/semantic.js.map +1 -0
- package/dist/geometry-report.d.ts.map +1 -1
- package/dist/geometry-report.js +2 -1
- package/dist/geometry-report.js.map +1 -1
- package/dist/headless-backend-store-authoring.d.ts +4 -4
- package/dist/headless-backend-store-authoring.d.ts.map +1 -1
- package/dist/headless-backend-store-authoring.js +7 -5
- package/dist/headless-backend-store-authoring.js.map +1 -1
- package/dist/headless-backend.d.ts.map +1 -1
- package/dist/headless-backend.js +1 -57
- package/dist/headless-backend.js.map +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +13 -2
- package/dist/help.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/out-flag-commands.js +1 -1
- package/dist/out-flag-commands.js.map +1 -1
- package/dist/output.d.ts +11 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +13 -0
- package/dist/output.js.map +1 -1
- package/package.json +25 -22
package/README.md
CHANGED
|
@@ -100,3 +100,5 @@ Determinism: the same model plus the same op sequence yields byte-identical rewa
|
|
|
100
100
|
## License
|
|
101
101
|
|
|
102
102
|
MPL-2.0
|
|
103
|
+
|
|
104
|
+
Semantic datasets use the same shared profile and provider engine as the viewer. Run `ifc-lite semantic --help` for `validate`, `query`, `assets`, and authenticated HTTPS `serve`; see the [headless semantic guide](https://ifclite.dev/docs/guide/semantic-headless/).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The CLI's AI service for `ai.*` Flow nodes (#6923).
|
|
3
|
+
*
|
|
4
|
+
* Opt-in through the environment, never through the graph:
|
|
5
|
+
*
|
|
6
|
+
* IFC_LITE_AI_MODEL model id sent to the provider (required)
|
|
7
|
+
* IFC_LITE_AI_API_KEY bearer key (required; never logged, never in a checkpoint)
|
|
8
|
+
* IFC_LITE_AI_BASE_URL OpenAI-compatible endpoint, default https://openrouter.ai/api/v1
|
|
9
|
+
*
|
|
10
|
+
* Without both required variables the host does not list the `ai` feature,
|
|
11
|
+
* so a graph with AI nodes fails availability before anything runs. Every
|
|
12
|
+
* request goes through the shared `@ifc-lite/ai` core against ONE root budget
|
|
13
|
+
* for the whole run, restored from the checkpoint on resume so a pause never
|
|
14
|
+
* resets what was spent.
|
|
15
|
+
*/
|
|
16
|
+
import { type RootBudget } from '@ifc-lite/ai';
|
|
17
|
+
import { type FlowAiConfig } from '@ifc-lite/ai/chat-completions';
|
|
18
|
+
import { type FlowAiService } from '@ifc-lite/flow-nodes/ai';
|
|
19
|
+
export declare function createCliAiService(config: FlowAiConfig, budget?: RootBudget, transport?: import("@ifc-lite/ai").AiTransport<string>): FlowAiService;
|
|
20
|
+
//# sourceMappingURL=flow-ai.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-ai.d.ts","sourceRoot":"","sources":["../../src/commands/flow-ai.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAqC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC5F,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAK7E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,GAAE,UAA6C,EAAE,SAAS,6CAAmC,GAAG,aAAa,CAU3K"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* The CLI's AI service for `ai.*` Flow nodes (#6923).
|
|
6
|
+
*
|
|
7
|
+
* Opt-in through the environment, never through the graph:
|
|
8
|
+
*
|
|
9
|
+
* IFC_LITE_AI_MODEL model id sent to the provider (required)
|
|
10
|
+
* IFC_LITE_AI_API_KEY bearer key (required; never logged, never in a checkpoint)
|
|
11
|
+
* IFC_LITE_AI_BASE_URL OpenAI-compatible endpoint, default https://openrouter.ai/api/v1
|
|
12
|
+
*
|
|
13
|
+
* Without both required variables the host does not list the `ai` feature,
|
|
14
|
+
* so a graph with AI nodes fails availability before anything runs. Every
|
|
15
|
+
* request goes through the shared `@ifc-lite/ai` core against ONE root budget
|
|
16
|
+
* for the whole run, restored from the checkpoint on resume so a pause never
|
|
17
|
+
* resets what was spent.
|
|
18
|
+
*/
|
|
19
|
+
import { createRootBudget, runModelRequest } from '@ifc-lite/ai';
|
|
20
|
+
import { chatCompletionsTransport } from '@ifc-lite/ai/chat-completions';
|
|
21
|
+
import { FLOW_AI_BUDGET } from '@ifc-lite/flow-nodes/ai';
|
|
22
|
+
const ROUTE_CEILING = 8_192;
|
|
23
|
+
const TIMEOUT_MS = 120_000;
|
|
24
|
+
export function createCliAiService(config, budget = createRootBudget(FLOW_AI_BUDGET), transport = chatCompletionsTransport(config)) {
|
|
25
|
+
return {
|
|
26
|
+
model: config.model,
|
|
27
|
+
request: (call) => runModelRequest({
|
|
28
|
+
model: config.model, route: 'cli', transport, budget, routeCeiling: ROUTE_CEILING, timeoutMs: TIMEOUT_MS,
|
|
29
|
+
prepareInput: () => JSON.stringify({ messages: [call.prompt], system: call.system, outputSchema: call.outputSchema }),
|
|
30
|
+
messages: [call.prompt], system: call.system, promptVersion: call.promptVersion, maxOutputTokens: call.maxOutputTokens, signal: call.signal,
|
|
31
|
+
outputSchema: call.outputSchema,
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=flow-ai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-ai.js","sourceRoot":"","sources":["../../src/commands/flow-ai.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAmB,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAqB,MAAM,+BAA+B,CAAC;AAC5F,OAAO,EAAE,cAAc,EAAsB,MAAM,yBAAyB,CAAC;AAE7E,MAAM,aAAa,GAAG,KAAK,CAAC;AAC5B,MAAM,UAAU,GAAG,OAAO,CAAC;AAE3B,MAAM,UAAU,kBAAkB,CAAC,MAAoB,EAAE,SAAqB,gBAAgB,CAAC,cAAc,CAAC,EAAE,SAAS,GAAG,wBAAwB,CAAC,MAAM,CAAC;IAC1J,OAAO;QACL,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC;YACjC,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU;YACxG,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;YACrH,QAAQ,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,IAAI,CAAC,aAAa,EAAE,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM;YAC3I,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC;KACH,CAAC;AACJ,CAAC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Capability } from '@ifc-lite/extensions';
|
|
11
11
|
import type { FlowHost } from '@ifc-lite/flow-nodes';
|
|
12
|
+
import type { FlowAiService } from '@ifc-lite/flow-nodes/ai';
|
|
12
13
|
import type { IfcDataStore } from '@ifc-lite/parser';
|
|
13
14
|
import { type BimContext } from '@ifc-lite/sdk';
|
|
14
15
|
import { HeadlessBackend } from '../headless-backend.js';
|
|
@@ -22,5 +23,5 @@ export interface CliFlowSession {
|
|
|
22
23
|
/** The model the graph is working on now: the command-line one, or the last one it opened. */
|
|
23
24
|
active(): HeadlessModel;
|
|
24
25
|
}
|
|
25
|
-
export declare function createCliFlowSession(initial: HeadlessModel, networkGrants: readonly Capability[]): CliFlowSession;
|
|
26
|
+
export declare function createCliFlowSession(initial: HeadlessModel, networkGrants: readonly Capability[], ai?: FlowAiService): CliFlowSession;
|
|
26
27
|
//# sourceMappingURL=flow-host.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow-host.d.ts","sourceRoot":"","sources":["../../src/commands/flow-host.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"flow-host.d.ts","sourceRoot":"","sources":["../../src/commands/flow-host.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAErD,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,8FAA8F;IAC9F,MAAM,IAAI,aAAa,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,UAAU,EAAE,EAAE,EAAE,CAAC,EAAE,aAAa,GAAG,cAAc,CA0BrI"}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
2
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
3
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
import { createDataAccessor } from '@ifc-lite/ids/bridge';
|
|
4
5
|
import { createBimContext } from '@ifc-lite/sdk';
|
|
5
6
|
import { HeadlessBackend } from '../headless-backend.js';
|
|
6
7
|
import { parseIfcBytes } from '../loader.js';
|
|
7
|
-
export function createCliFlowSession(initial, networkGrants) {
|
|
8
|
+
export function createCliFlowSession(initial, networkGrants, ai) {
|
|
8
9
|
let active = initial;
|
|
9
10
|
const host = {
|
|
10
11
|
get bim() { return active.bim; },
|
|
12
|
+
// Present only when the environment names a provider (`flow-ai.ts`).
|
|
13
|
+
...(ai ? { ai } : {}),
|
|
11
14
|
// `networkGrants` is always the graph's own declared capabilities: a
|
|
12
15
|
// trusted local run is still not trusted to reach a host the graph never
|
|
13
16
|
// declared (see `FlowHost.networkGrants`).
|
|
@@ -17,6 +20,9 @@ export function createCliFlowSession(initial, networkGrants) {
|
|
|
17
20
|
// csv-match.ts index builder, which needs the raw entity table + mutation
|
|
18
21
|
// view rather than per-ref BimContext accessors (see host.ts's TableAccess).
|
|
19
22
|
tables: (modelId) => active.backend.tableAccess(modelId),
|
|
23
|
+
// `ids.validate` reads through the same mutation view, so it sees what an
|
|
24
|
+
// earlier node in the run wrote.
|
|
25
|
+
idsAccessor: () => createDataAccessor(active.store, undefined, active.backend.tableAccess().mutationView),
|
|
20
26
|
async openModel(bytes, name) {
|
|
21
27
|
const store = await parseIfcBytes(bytes, name);
|
|
22
28
|
const backend = new HeadlessBackend(store, name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow-host.js","sourceRoot":"","sources":["../../src/commands/flow-host.ts"],"names":[],"mappings":"AAAA;;+DAE+D;
|
|
1
|
+
{"version":3,"file":"flow-host.js","sourceRoot":"","sources":["../../src/commands/flow-host.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAgB/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAmB,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAc7C,MAAM,UAAU,oBAAoB,CAAC,OAAsB,EAAE,aAAoC,EAAE,EAAkB;IACnH,IAAI,MAAM,GAAG,OAAO,CAAC;IACrB,MAAM,IAAI,GAAa;QACrB,IAAI,GAAG,KAAK,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QAChC,qEAAqE;QACrE,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrB,qEAAqE;QACrE,yEAAyE;QACzE,2CAA2C;QAC3C,aAAa;QACb,IAAI,cAAc,KAAK,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC;QACzE,2EAA2E;QAC3E,0EAA0E;QAC1E,6EAA6E;QAC7E,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,CAAC;QACxD,0EAA0E;QAC1E,iCAAiC;QACjC,WAAW,EAAE,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,YAAY,CAAC;QACzG,KAAK,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI;YACzB,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YAC/C,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;YACjD,MAAM,GAAG,EAAE,GAAG,EAAE,gBAAgB,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;YAChE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC;QAC1D,CAAC;KACF,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC;AACxC,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type FlowDocument, type RunResult } from '@ifc-lite/flow';
|
|
2
|
+
import { createCheckpoint, graphDigest, type FlowCheckpoint } from '@ifc-lite/flow/checkpoint';
|
|
3
|
+
import { FileCheckpointStore } from '@ifc-lite/flow/checkpoint-file';
|
|
4
|
+
/** Exit code of a run that stopped at a review checkpoint: not a failure, not done. */
|
|
5
|
+
export declare const PAUSED_FOR_REVIEW = 3;
|
|
6
|
+
/** Build the exact portable proposal before persisting model or tracking effects. */
|
|
7
|
+
export declare function preparePause(input: Parameters<typeof createCheckpoint>[0], redaction: ReadonlyMap<string, string>): FlowCheckpoint;
|
|
8
|
+
/** Refuse an existing destination before a resume consumes its approval. */
|
|
9
|
+
export declare function checkPauseDestination(path: string): Promise<void>;
|
|
10
|
+
/** Save the already-validated exact proposal, with atomic collision refusal. */
|
|
11
|
+
export declare function savePause(path: string, checkpoint: FlowCheckpoint): Promise<FlowCheckpoint>;
|
|
12
|
+
export declare function reportPause(checkpoint: FlowCheckpoint, path: string): void;
|
|
13
|
+
export declare function reviewCommand(args: string[], path: string | undefined, json: boolean, approve: string | undefined): Promise<void>;
|
|
14
|
+
/** Ids of the nodes a checkpoint restores. */
|
|
15
|
+
export declare function checkpointNodes(path: string): Promise<string[]>;
|
|
16
|
+
export interface Resume {
|
|
17
|
+
readonly store: FileCheckpointStore;
|
|
18
|
+
readonly checkpoint: FlowCheckpoint;
|
|
19
|
+
readonly owner: string;
|
|
20
|
+
}
|
|
21
|
+
/** Claim a reviewed checkpoint for this process, after recovering an abandoned claim. */
|
|
22
|
+
export declare function claimForResume(path: string, doc: FlowDocument, inputs: Record<string, unknown>, sourceDigest: string, registry: NonNullable<Parameters<typeof graphDigest>[2]>, requiresAiBudget?: boolean): Promise<Resume>;
|
|
23
|
+
export declare function finishResume(resume: Resume, result: RunResult): Promise<FlowCheckpoint>;
|
|
24
|
+
/** A failed persistence step must not turn a consumed claim into completed. */
|
|
25
|
+
export declare function failResume(resume: Resume, message: string): Promise<FlowCheckpoint>;
|
|
26
|
+
/** #7039: checkpoint writes must not alias model outputs or another journal. */
|
|
27
|
+
export declare function checkDistinctDestinations(paths: readonly string[]): Promise<void>;
|
|
28
|
+
//# sourceMappingURL=flow-review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-review.d.ts","sourceRoot":"","sources":["../../src/commands/flow-review.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,SAAS,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EACmD,gBAAgB,EAAoB,WAAW,EAChD,KAAK,cAAc,EAC3E,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AAErE,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAcnC,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,cAAc,CAMlI;AAED,4EAA4E;AAC5E,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,gFAAgF;AAChF,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAIjG;AAED,wBAAgB,WAAW,CAAC,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAG1E;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBvI;AAED,8CAA8C;AAC9C,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAMrE;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,yFAAyF;AACzF,wBAAsB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,gBAAgB,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAehO;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,cAAc,CAAC,CAM7F;AAED,+EAA+E;AAC/E,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAEzF;AAED,gFAAgF;AAChF,wBAAsB,yBAAyB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBvF"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
4
|
+
/**
|
|
5
|
+
* `ifc-lite flow` review checkpoints (#6923): pause, review, resume.
|
|
6
|
+
*
|
|
7
|
+
* flow run <graph> <model> --checkpoint cp.json [--out paused.ifc] exits 3 when it paused for review
|
|
8
|
+
* flow review <cp.json> [--approve <proposal digest> | --reject] [--json]
|
|
9
|
+
* flow resume <graph> <model> --checkpoint cp.json [--out done.ifc] [--next-checkpoint cp2.json]
|
|
10
|
+
*
|
|
11
|
+
* The approval names the proposal digest the reviewer was shown, so an
|
|
12
|
+
* approval cannot apply to a proposal it did not see. A resume claims the
|
|
13
|
+
* checkpoint once (compare-and-swap on the file), checks that the graph,
|
|
14
|
+
* Player inputs and the model it starts from are the ones the pause
|
|
15
|
+
* recorded, runs only what was paused (completed nodes are restored, never
|
|
16
|
+
* re-executed), and records completed or partially committed.
|
|
17
|
+
*/
|
|
18
|
+
import { randomUUID } from 'node:crypto';
|
|
19
|
+
import { restoreRootBudget } from '@ifc-lite/ai';
|
|
20
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
21
|
+
import { dirname, basename, join, resolve } from 'node:path';
|
|
22
|
+
import { approveCheckpoint, checkpointProposal, claimCheckpoint, createCheckpoint, finishCheckpoint, graphDigest, recoverCheckpoint, rejectCheckpoint, updateCheckpoint } from '@ifc-lite/flow/checkpoint';
|
|
23
|
+
import { redactDeep } from '@ifc-lite/flow-nodes';
|
|
24
|
+
import { fatal, printJson } from '../output.js';
|
|
25
|
+
import { FileCheckpointStore } from '@ifc-lite/flow/checkpoint-file';
|
|
26
|
+
/** Exit code of a run that stopped at a review checkpoint: not a failure, not done. */
|
|
27
|
+
export const PAUSED_FOR_REVIEW = 3;
|
|
28
|
+
const LEASE_MS = 10 * 60_000;
|
|
29
|
+
function describe(checkpoint) {
|
|
30
|
+
const lines = [`checkpoint ${checkpoint.id} (${checkpoint.state}) for "${checkpoint.graphName}"`, ` proposal digest ${checkpoint.proposalDigest}`];
|
|
31
|
+
for (const [nodeId, ports] of checkpointProposal(checkpoint)) {
|
|
32
|
+
for (const [port, data] of ports) {
|
|
33
|
+
const value = data.kind === 'item' ? data.value : data.kind === 'list' ? data.items : Object.fromEntries(data.branches);
|
|
34
|
+
lines.push(` ${nodeId}.${port}: ${JSON.stringify(value)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return lines;
|
|
38
|
+
}
|
|
39
|
+
/** Build the exact portable proposal before persisting model or tracking effects. */
|
|
40
|
+
export function preparePause(input, redaction) {
|
|
41
|
+
const checkpoint = createCheckpoint(input);
|
|
42
|
+
if (JSON.stringify(checkpoint.outputs) !== JSON.stringify(redactDeep(checkpoint.outputs, redaction))) {
|
|
43
|
+
throw new Error('the paused outputs contain a secret; no checkpoint can be saved or displayed');
|
|
44
|
+
}
|
|
45
|
+
return checkpoint;
|
|
46
|
+
}
|
|
47
|
+
/** Refuse an existing destination before a resume consumes its approval. */
|
|
48
|
+
export async function checkPauseDestination(path) {
|
|
49
|
+
if (await stat(path).then(() => true, () => false))
|
|
50
|
+
throw new Error(`${path} already exists; a checkpoint is never overwritten`);
|
|
51
|
+
}
|
|
52
|
+
/** Save the already-validated exact proposal, with atomic collision refusal. */
|
|
53
|
+
export async function savePause(path, checkpoint) {
|
|
54
|
+
await checkPauseDestination(path);
|
|
55
|
+
if (!(await new FileCheckpointStore(path).write(checkpoint, null)))
|
|
56
|
+
throw new Error(`${path} is being written by another process`);
|
|
57
|
+
return checkpoint;
|
|
58
|
+
}
|
|
59
|
+
export function reportPause(checkpoint, path) {
|
|
60
|
+
for (const line of describe(checkpoint))
|
|
61
|
+
process.stdout.write(`${line}\n`);
|
|
62
|
+
process.stdout.write(`paused for review: approve with\n ifc-lite flow review ${path} --approve ${checkpoint.proposalDigest}\n`);
|
|
63
|
+
}
|
|
64
|
+
export async function reviewCommand(args, path, json, approve) {
|
|
65
|
+
if (!path)
|
|
66
|
+
fatal('Usage: ifc-lite flow review <checkpoint.json> [--approve <proposal digest> | --reject] [--json]');
|
|
67
|
+
const store = new FileCheckpointStore(path);
|
|
68
|
+
const { checkpoint } = await store.load();
|
|
69
|
+
const reject = args.includes('--reject');
|
|
70
|
+
if (approve !== undefined && reject)
|
|
71
|
+
fatal('pass either --approve or --reject');
|
|
72
|
+
let next = checkpoint;
|
|
73
|
+
try {
|
|
74
|
+
if (approve !== undefined)
|
|
75
|
+
next = await updateCheckpoint(store, checkpoint.id, (c) => approveCheckpoint(c, approve));
|
|
76
|
+
else if (reject)
|
|
77
|
+
next = await updateCheckpoint(store, checkpoint.id, (c) => rejectCheckpoint(c));
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
fatal(error.message);
|
|
81
|
+
}
|
|
82
|
+
if (json)
|
|
83
|
+
return printJson({ id: next.id, state: next.state, proposalDigest: next.proposalDigest, reviewNodes: next.reviewNodes,
|
|
84
|
+
proposal: Object.fromEntries([...checkpointProposal(next).keys()].map(node => [node, next.outputs[node]])) });
|
|
85
|
+
for (const line of describe(next))
|
|
86
|
+
process.stdout.write(`${line}\n`);
|
|
87
|
+
}
|
|
88
|
+
/** Ids of the nodes a checkpoint restores. */
|
|
89
|
+
export async function checkpointNodes(path) {
|
|
90
|
+
try {
|
|
91
|
+
return Object.keys((await new FileCheckpointStore(path).load()).checkpoint.outputs);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
fatal(`cannot read checkpoint ${path}: ${error.message}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/** Claim a reviewed checkpoint for this process, after recovering an abandoned claim. */
|
|
98
|
+
export async function claimForResume(path, doc, inputs, sourceDigest, registry, requiresAiBudget = false) {
|
|
99
|
+
const store = new FileCheckpointStore(path);
|
|
100
|
+
const owner = `cli:${process.pid}:${randomUUID()}`;
|
|
101
|
+
try {
|
|
102
|
+
const { checkpoint } = await store.load();
|
|
103
|
+
if (checkpoint.graphId !== doc.id)
|
|
104
|
+
fatal(`${path} belongs to graph "${checkpoint.graphId}", not "${doc.id}"`);
|
|
105
|
+
if (recoverCheckpoint(checkpoint))
|
|
106
|
+
await updateCheckpoint(store, checkpoint.id, (c) => recoverCheckpoint(c) ?? c);
|
|
107
|
+
const claimed = await updateCheckpoint(store, checkpoint.id, (c) => {
|
|
108
|
+
if (requiresAiBudget && !restoreRootBudget(c.budget))
|
|
109
|
+
throw new Error('resuming downstream AI nodes requires the original root budget receipt; no new allowance can be granted');
|
|
110
|
+
return claimCheckpoint(c, { owner, graphDigest: graphDigest(doc, inputs, registry), sourceDigest, leaseMs: LEASE_MS });
|
|
111
|
+
});
|
|
112
|
+
return { store, checkpoint: claimed, owner };
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
fatal(error.message);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export async function finishResume(resume, result) {
|
|
119
|
+
const failed = result.log.find((l) => l.level === 'error');
|
|
120
|
+
return updateCheckpoint(resume.store, resume.checkpoint.id, (c) => finishCheckpoint(c, resume.owner, {
|
|
121
|
+
ok: result.ok,
|
|
122
|
+
...(failed ? { message: `${failed.nodeId}: ${failed.message}` } : result.review.length ? { message: `paused again at ${result.review.join(', ')}` } : {}),
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
/** A failed persistence step must not turn a consumed claim into completed. */
|
|
126
|
+
export async function failResume(resume, message) {
|
|
127
|
+
return updateCheckpoint(resume.store, resume.checkpoint.id, c => finishCheckpoint(c, resume.owner, { ok: false, message }));
|
|
128
|
+
}
|
|
129
|
+
/** #7039: checkpoint writes must not alias model outputs or another journal. */
|
|
130
|
+
export async function checkDistinctDestinations(paths) {
|
|
131
|
+
const names = new Set();
|
|
132
|
+
const files = new Set();
|
|
133
|
+
for (const path of paths) {
|
|
134
|
+
const absolute = resolve(path);
|
|
135
|
+
const canonical = await realpath(absolute).catch(async (error) => {
|
|
136
|
+
if (error.code !== 'ENOENT')
|
|
137
|
+
throw error;
|
|
138
|
+
return join(await realpath(dirname(absolute)), basename(absolute));
|
|
139
|
+
});
|
|
140
|
+
const info = await stat(absolute).catch((error) => {
|
|
141
|
+
if (error.code !== 'ENOENT')
|
|
142
|
+
throw error;
|
|
143
|
+
return null;
|
|
144
|
+
});
|
|
145
|
+
const identity = info ? `${info.dev}:${info.ino}` : undefined;
|
|
146
|
+
if (names.has(canonical) || (identity !== undefined && files.has(identity))) {
|
|
147
|
+
throw new Error('checkpoint, next checkpoint, output and tracking destinations must be distinct');
|
|
148
|
+
}
|
|
149
|
+
names.add(canonical);
|
|
150
|
+
if (identity !== undefined)
|
|
151
|
+
files.add(identity);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=flow-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-review.js","sourceRoot":"","sources":["../../src/commands/flow-review.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,10 +4,14 @@ export declare class FileTrackingStore implements TrackingStore {
|
|
|
4
4
|
readonly pinnedTo: string;
|
|
5
5
|
private sets;
|
|
6
6
|
private dirty;
|
|
7
|
+
private exists;
|
|
8
|
+
private writePath;
|
|
7
9
|
/** Pin recorded in the file that was loaded, when any. */
|
|
8
10
|
loadedPin: string | undefined;
|
|
9
11
|
constructor(path: string, pinnedTo: string);
|
|
10
12
|
static open(path: string, pinnedTo: string): Promise<FileTrackingStore>;
|
|
13
|
+
/** Bind review to the effective sidecar and its canonical destination, including planned writes. */
|
|
14
|
+
fingerprint(): string;
|
|
11
15
|
load(trackingKey: string): TrackedSet | undefined;
|
|
12
16
|
save(set: TrackedSet): void;
|
|
13
17
|
keys(): readonly string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow-tracking.d.ts","sourceRoot":"","sources":["../../src/commands/flow-tracking.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"flow-tracking.d.ts","sourceRoot":"","sources":["../../src/commands/flow-tracking.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAqD,KAAK,UAAU,EAAwB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE9I,qBAAa,iBAAkB,YAAW,aAAa;IAQzC,QAAQ,CAAC,IAAI,EAAE,MAAM;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAP5D,OAAO,CAAC,IAAI,CAAkC;IAC9C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,0DAA0D;IAC1D,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;gBAET,IAAI,EAAE,MAAM,EAAW,QAAQ,EAAE,MAAM;WAE/C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA6B7E,oGAAoG;IACpG,WAAW,IAAI,MAAM;IAMrB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS;IAIjD,IAAI,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI;IAK3B,IAAI,IAAI,SAAS,MAAM,EAAE;IAIzB,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAMjC,4EAA4E;IACtE,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;CAShC;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE7D"}
|
|
@@ -12,30 +12,43 @@
|
|
|
12
12
|
* and a tracked element that is gone is re-created with a warning. The
|
|
13
13
|
* viewer, which keeps one model loaded, applies the strict pin check.
|
|
14
14
|
*/
|
|
15
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
16
|
-
import {
|
|
15
|
+
import { readFile, writeFile, realpath } from 'node:fs/promises';
|
|
16
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
17
|
+
import { digest, TRACKING_SIDECAR_VERSION, trackedSetsFrom } from '@ifc-lite/flow';
|
|
17
18
|
export class FileTrackingStore {
|
|
18
19
|
path;
|
|
19
20
|
pinnedTo;
|
|
20
21
|
sets = {};
|
|
21
22
|
dirty = false;
|
|
23
|
+
exists = false;
|
|
24
|
+
writePath;
|
|
22
25
|
/** Pin recorded in the file that was loaded, when any. */
|
|
23
26
|
loadedPin;
|
|
24
27
|
constructor(path, pinnedTo) {
|
|
25
28
|
this.path = path;
|
|
26
29
|
this.pinnedTo = pinnedTo;
|
|
30
|
+
this.writePath = resolve(path);
|
|
27
31
|
}
|
|
28
32
|
static async open(path, pinnedTo) {
|
|
29
33
|
const store = new FileTrackingStore(path, pinnedTo);
|
|
34
|
+
try {
|
|
35
|
+
store.writePath = await realpath(store.writePath);
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (error.code !== 'ENOENT')
|
|
39
|
+
throw error;
|
|
40
|
+
store.writePath = join(await realpath(dirname(store.writePath)), basename(store.writePath));
|
|
41
|
+
}
|
|
30
42
|
let text;
|
|
31
43
|
try {
|
|
32
|
-
text = await readFile(
|
|
44
|
+
text = await readFile(store.writePath, 'utf-8');
|
|
33
45
|
}
|
|
34
46
|
catch (err) {
|
|
35
47
|
if (err.code !== 'ENOENT')
|
|
36
48
|
throw err;
|
|
37
49
|
}
|
|
38
50
|
if (text !== undefined) {
|
|
51
|
+
store.exists = true;
|
|
39
52
|
const parsed = JSON.parse(text);
|
|
40
53
|
if (parsed.version !== TRACKING_SIDECAR_VERSION)
|
|
41
54
|
throw new Error(`${path}: unsupported tracking sidecar version ${String(parsed.version)}`);
|
|
@@ -53,6 +66,12 @@ export class FileTrackingStore {
|
|
|
53
66
|
}
|
|
54
67
|
return store;
|
|
55
68
|
}
|
|
69
|
+
/** Bind review to the effective sidecar and its canonical destination, including planned writes. */
|
|
70
|
+
fingerprint() {
|
|
71
|
+
const sidecar = !this.exists && !this.dirty ? null : { version: TRACKING_SIDECAR_VERSION,
|
|
72
|
+
pinnedTo: this.dirty ? this.pinnedTo : this.loadedPin ?? this.pinnedTo, sets: this.sets };
|
|
73
|
+
return digest({ path: this.writePath, sidecar });
|
|
74
|
+
}
|
|
56
75
|
load(trackingKey) {
|
|
57
76
|
return this.sets[trackingKey];
|
|
58
77
|
}
|
|
@@ -74,8 +93,10 @@ export class FileTrackingStore {
|
|
|
74
93
|
if (!this.dirty)
|
|
75
94
|
return false;
|
|
76
95
|
const sidecar = { version: TRACKING_SIDECAR_VERSION, pinnedTo: this.pinnedTo, sets: this.sets };
|
|
77
|
-
await writeFile(this.
|
|
96
|
+
await writeFile(this.writePath, `${JSON.stringify(sidecar, null, 2)}\n`, 'utf-8');
|
|
78
97
|
this.dirty = false;
|
|
98
|
+
this.exists = true;
|
|
99
|
+
this.loadedPin = this.pinnedTo;
|
|
79
100
|
return true;
|
|
80
101
|
}
|
|
81
102
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow-tracking.js","sourceRoot":"","sources":["../../src/commands/flow-tracking.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"flow-tracking.js","sourceRoot":"","sources":["../../src/commands/flow-tracking.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,MAAM,EAAE,wBAAwB,EAAE,eAAe,EAA6D,MAAM,gBAAgB,CAAC;AAE9I,MAAM,OAAO,iBAAiB;IAQP;IAAuB;IAPpC,IAAI,GAA+B,EAAE,CAAC;IACtC,KAAK,GAAG,KAAK,CAAC;IACd,MAAM,GAAG,KAAK,CAAC;IACf,SAAS,CAAS;IAC1B,0DAA0D;IAC1D,SAAS,CAAqB;IAE9B,YAAqB,IAAY,EAAW,QAAgB;QAAvC,SAAI,GAAJ,IAAI,CAAQ;QAAW,aAAQ,GAAR,QAAQ,CAAQ;QAAI,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAAC,CAAC;IAEjG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,QAAgB;QAC9C,MAAM,KAAK,GAAG,IAAI,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACpD,IAAI,CAAC;YAAC,KAAK,CAAC,SAAS,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAAC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACxE,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACpE,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,MAAM,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9F,CAAC;QACD,IAAI,IAAwB,CAAC;QAC7B,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QAClD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,GAAG,CAAC;QAClE,CAAC;QACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;YACpB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA6B,CAAC;YAC5D,IAAI,MAAM,CAAC,OAAO,KAAK,wBAAwB;gBAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,0CAA0C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5I,uEAAuE;YACvE,4EAA4E;YAC5E,MAAM,IAAI,GAAG,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1C,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,wCAAwC,CAAC,CAAC;YAC5E,mEAAmE;YACnE,uEAAuE;YACvE,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,sCAAsC,CAAC,CAAC;YACxG,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;YAClB,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,QAAQ,CAAC;QACpC,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oGAAoG;IACpG,WAAW;QACT,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,wBAAwB;YACtF,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5F,OAAO,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,IAAI,CAAC,WAAmB;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,CAAC,GAAe;QAClB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,GAAG,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,IAAI;QACF,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,MAAM,CAAC,WAAmB;QACxB,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO;QACxC,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC9B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,4EAA4E;IAC5E,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QAC9B,MAAM,OAAO,GAAoB,EAAE,OAAO,EAAE,wBAAwB,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACjH,MAAM,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAClF,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC/B,OAAO,IAAI,CAAC;IACd,CAAC;CACF;AAED,2EAA2E;AAC3E,MAAM,UAAU,mBAAmB,CAAC,SAAiB;IACnD,OAAO,SAAS,CAAC,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC,GAAG,gBAAgB,CAAC;AACvE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/commands/flow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"flow.d.ts","sourceRoot":"","sources":["../../src/commands/flow.ts"],"names":[],"mappings":"AA6KA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAoL/D"}
|