@grida/fx 0.1.0-alpha.2 → 0.2.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/LICENSE +201 -0
- package/README.md +41 -6
- package/dist/api.d.ts +2 -0
- package/dist/api.js +7 -2
- package/dist/cli.d.ts +32 -5
- package/dist/cli.js +40 -9
- package/dist/control.d.ts +42 -0
- package/dist/control.js +160 -0
- package/dist/documents.d.ts +5 -1
- package/dist/engine.d.ts +4 -3
- package/dist/engine.js +21 -6
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/record.d.ts +107 -0
- package/dist/record.js +246 -0
- package/dist/request.d.ts +4 -0
- package/dist/request.js +14 -3
- package/dist/runs.d.ts +100 -0
- package/dist/runs.js +233 -0
- package/package.json +14 -8
package/dist/request.js
CHANGED
|
@@ -59,6 +59,16 @@ export function planningArgs(request, inputsFile) {
|
|
|
59
59
|
/** The options of `run` alone. */
|
|
60
60
|
export function runArgs(options) {
|
|
61
61
|
const args = [];
|
|
62
|
+
const selected = ["runDir", "name", "resume"];
|
|
63
|
+
if (selected.filter((name) => options[name] !== undefined).length > 1) {
|
|
64
|
+
throw new TypeError("runDir, name and resume are mutually exclusive");
|
|
65
|
+
}
|
|
66
|
+
for (const name of selected) {
|
|
67
|
+
const value = options[name];
|
|
68
|
+
if (value !== undefined && (typeof value !== "string" || value === "" || value.includes("\0"))) {
|
|
69
|
+
throw new TypeError(`${name} is a nonempty string without NUL`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
62
72
|
if (options.live !== undefined && typeof options.live !== "boolean") {
|
|
63
73
|
throw new TypeError(`live is true or false, not ${JSON.stringify(options.live)}`);
|
|
64
74
|
}
|
|
@@ -69,11 +79,12 @@ export function runArgs(options) {
|
|
|
69
79
|
args.push(`--yes-up-to=${amountText("yesUpTo", options.yesUpTo)}`);
|
|
70
80
|
}
|
|
71
81
|
if (options.runDir !== undefined) {
|
|
72
|
-
if (typeof options.runDir !== "string" || options.runDir === "") {
|
|
73
|
-
throw new TypeError("runDir is a folder path");
|
|
74
|
-
}
|
|
75
82
|
args.push(`--run=${options.runDir}`);
|
|
76
83
|
}
|
|
84
|
+
if (options.name !== undefined)
|
|
85
|
+
args.push(`--name=${options.name}`);
|
|
86
|
+
if (options.resume !== undefined)
|
|
87
|
+
args.push(`--resume=${options.resume}`);
|
|
77
88
|
for (const [name, path] of Object.entries(options.deliver ?? {})) {
|
|
78
89
|
if (typeof path !== "string" || name === "" || path === "") {
|
|
79
90
|
throw new TypeError(`deliver.${name} is a path`);
|
package/dist/runs.d.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/** A project's runs through the engine's public command line (spec/store.md §8 and §9): listing
|
|
2
|
+
* them, and removing named ones. The engine finds the project from cwd, selects, locks and
|
|
3
|
+
* removes; this module only reads what it prints. */
|
|
4
|
+
import { type CallOptions, type Exit } from "./engine.js";
|
|
5
|
+
import { FxError } from "./errors.js";
|
|
6
|
+
/** Where a run sits: `allocated` (`<runs>/<id>/<YYYY-MM-DD>-<n>`), `named`, `explicit`
|
|
7
|
+
* (elsewhere in the runs tree) or `external` (outside it, known from the run index or catalog). */
|
|
8
|
+
export type RunPlacement = "allocated" | "named" | "explicit" | "external";
|
|
9
|
+
/** A run's state as the listing reads its log. `empty`: no plan.json at a run's position;
|
|
10
|
+
* `removing`: a removal's tombstone; `missing`: an external folder that is gone. Recorded
|
|
11
|
+
* `unfinished` is not evidence that a process is alive. */
|
|
12
|
+
export type RunState = "planned" | "unfinished" | "succeeded" | "failed" | "incomplete" | "cancelled" | "empty" | "removing" | "missing";
|
|
13
|
+
/** One run of fx-run-list-v1. Wire fields retain lower_snake_case names. */
|
|
14
|
+
export interface RunEntry {
|
|
15
|
+
/** The run folder as the engine printed it: relative to cwd, or absolute. */
|
|
16
|
+
readonly folder: string;
|
|
17
|
+
/** `folder` resolved once against cwd. */
|
|
18
|
+
readonly runDir: string;
|
|
19
|
+
/** The recorded workflow id, or the one its position gives; null when neither is known. */
|
|
20
|
+
readonly workflow: string | null;
|
|
21
|
+
/** The recorded workflow source; null for a folder without a plan. */
|
|
22
|
+
readonly source: string | null;
|
|
23
|
+
/** The run's name: recorded, or given by a named position. */
|
|
24
|
+
readonly name: string | null;
|
|
25
|
+
readonly placement: RunPlacement;
|
|
26
|
+
readonly state: RunState;
|
|
27
|
+
/** The first start's creation time (RFC 3339); null for a folder without a plan. */
|
|
28
|
+
readonly created_at: string | null;
|
|
29
|
+
/** What the run was charged; null before it ended. */
|
|
30
|
+
readonly charged_usd: number | null;
|
|
31
|
+
readonly stand_in: boolean;
|
|
32
|
+
/** Why an incomplete run stopped. */
|
|
33
|
+
readonly stopped?: string;
|
|
34
|
+
}
|
|
35
|
+
/** What removing one run did, or would do in a preview. */
|
|
36
|
+
export interface RunRemovalEntry extends RunEntry {
|
|
37
|
+
/** `would_*`: a preview. `forgotten`: a missing external folder's index and catalog entries
|
|
38
|
+
* removed. `refused`: nothing of this run was removed. `partial`: the run is gone as a run but
|
|
39
|
+
* its tombstone remains; removing it again finishes it. */
|
|
40
|
+
readonly outcome: "would_remove" | "would_forget" | "removed" | "forgotten" | "refused" | "partial";
|
|
41
|
+
/** Present when refused or partial. */
|
|
42
|
+
readonly code?: "active" | "holds_pick" | "changed" | "not_removable" | "io";
|
|
43
|
+
/** Present when refused or partial. */
|
|
44
|
+
readonly message?: string;
|
|
45
|
+
/** Bytes of files whose every link was inside the run folder. */
|
|
46
|
+
readonly freed_bytes: number;
|
|
47
|
+
/** Bytes of files still linked from elsewhere (the store, other runs); not freed. */
|
|
48
|
+
readonly cache_bytes: number;
|
|
49
|
+
}
|
|
50
|
+
/** Why a selection passed a run over. Explicit runs are never passed over, so a removal by
|
|
51
|
+
* this SDK normally reports none. */
|
|
52
|
+
export type RunRemovalSkip = "named" | "explicit" | "external" | "holds_pick" | "young" | "holds_runs";
|
|
53
|
+
/** fx-run-removal-v1: what a removal did, or would do when `applied` is false. */
|
|
54
|
+
export interface RunRemoval {
|
|
55
|
+
/** False for a preview: nothing was removed. */
|
|
56
|
+
readonly applied: boolean;
|
|
57
|
+
readonly runs: readonly RunRemovalEntry[];
|
|
58
|
+
/** How many runs a selection passed over, by reason. */
|
|
59
|
+
readonly skipped: Readonly<Partial<Record<RunRemovalSkip, number>>>;
|
|
60
|
+
readonly freed_bytes: number;
|
|
61
|
+
readonly cache_bytes: number;
|
|
62
|
+
}
|
|
63
|
+
/** The fx-run-removal-v1 error document: a RUN the engine would not resolve. Nothing was removed. */
|
|
64
|
+
export interface RunRemovalFailure {
|
|
65
|
+
readonly kind: "fx-run-removal-v1";
|
|
66
|
+
readonly applied: false;
|
|
67
|
+
readonly error: {
|
|
68
|
+
readonly code: "invalid_target" | "ambiguous_target" | "not_a_run";
|
|
69
|
+
readonly message: string;
|
|
70
|
+
readonly run: string | null;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** A removal the engine refused before removing anything, with its stable code, the RUN it
|
|
74
|
+
* names and the complete error document. Usage mistakes and malformed output reject with
|
|
75
|
+
* ordinary FxError; a refused or partial run does not reject (read each entry's `outcome`). */
|
|
76
|
+
export declare class RunRemovalError extends FxError {
|
|
77
|
+
readonly code: RunRemovalFailure["error"]["code"];
|
|
78
|
+
readonly run: string | null;
|
|
79
|
+
readonly result: RunRemovalFailure;
|
|
80
|
+
constructor(result: RunRemovalFailure, done: Exit);
|
|
81
|
+
}
|
|
82
|
+
/** Options of listRuns. */
|
|
83
|
+
export interface ListRunsOptions extends CallOptions {
|
|
84
|
+
/** Only the runs of this workflow id. */
|
|
85
|
+
readonly workflow?: string;
|
|
86
|
+
}
|
|
87
|
+
/** Options of removeRuns. */
|
|
88
|
+
export interface RemoveRunsOptions extends CallOptions {
|
|
89
|
+
/** Report what would be removed and remove nothing (default: false). */
|
|
90
|
+
readonly preview?: boolean;
|
|
91
|
+
}
|
|
92
|
+
/** The runs of the project found from cwd, newest first. Reads only: no lock, no writes.
|
|
93
|
+
* Folders the engine could not read are left out. */
|
|
94
|
+
export declare function listRuns(options?: ListRunsOptions): Promise<RunEntry[]>;
|
|
95
|
+
/** Remove exactly the named runs: run folders (relative to cwd) or `WORKFLOW_ID/NAME` of named
|
|
96
|
+
* runs; select them with {@link listRuns} first. Every RUN is resolved before anything is removed,
|
|
97
|
+
* and one that is not a run rejects with {@link RunRemovalError}. A run that is active, or the last
|
|
98
|
+
* holding a pick, is refused without rejecting: read each entry's `outcome`. Aborting a removal
|
|
99
|
+
* may leave a run `partial`; removing it again finishes it. */
|
|
100
|
+
export declare function removeRuns(runs: readonly string[], options?: RemoveRunsOptions): Promise<RunRemoval>;
|
package/dist/runs.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/** A project's runs through the engine's public command line (spec/store.md §8 and §9): listing
|
|
2
|
+
* them, and removing named ones. The engine finds the project from cwd, selects, locks and
|
|
3
|
+
* removes; this module only reads what it prints. */
|
|
4
|
+
import { realpathSync } from "node:fs";
|
|
5
|
+
import { resolve } from "node:path";
|
|
6
|
+
import { call, failure, USAGE_OR_ERROR } from "./engine.js";
|
|
7
|
+
import { FxError } from "./errors.js";
|
|
8
|
+
/** A removal the engine refused before removing anything, with its stable code, the RUN it
|
|
9
|
+
* names and the complete error document. Usage mistakes and malformed output reject with
|
|
10
|
+
* ordinary FxError; a refused or partial run does not reject (read each entry's `outcome`). */
|
|
11
|
+
export class RunRemovalError extends FxError {
|
|
12
|
+
code;
|
|
13
|
+
run;
|
|
14
|
+
result;
|
|
15
|
+
constructor(result, done) {
|
|
16
|
+
super(result.error.message, {
|
|
17
|
+
exitCode: done.status,
|
|
18
|
+
signal: done.signal,
|
|
19
|
+
stdout: done.stdout,
|
|
20
|
+
stderr: done.stderr,
|
|
21
|
+
args: done.args,
|
|
22
|
+
});
|
|
23
|
+
this.name = "RunRemovalError";
|
|
24
|
+
this.code = result.error.code;
|
|
25
|
+
this.run = result.error.run;
|
|
26
|
+
this.result = result;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const LIST_KIND = "fx-run-list-v1";
|
|
30
|
+
const REMOVAL_KIND = "fx-run-removal-v1";
|
|
31
|
+
const PLACEMENTS = ["allocated", "named", "explicit", "external"];
|
|
32
|
+
const STATES = [
|
|
33
|
+
"planned", "unfinished", "succeeded", "failed", "incomplete", "cancelled", "empty", "removing", "missing",
|
|
34
|
+
];
|
|
35
|
+
const PREVIEW_OUTCOMES = ["would_remove", "would_forget", "refused"];
|
|
36
|
+
const APPLIED_OUTCOMES = ["removed", "forgotten", "refused", "partial"];
|
|
37
|
+
const FAILED_OUTCOMES = ["refused", "partial"];
|
|
38
|
+
const RUN_CODES = ["active", "holds_pick", "changed", "not_removable", "io"];
|
|
39
|
+
const REMOVAL_SKIPS = ["named", "explicit", "external", "holds_pick", "young", "holds_runs"];
|
|
40
|
+
const ERROR_CODES = ["invalid_target", "ambiguous_target", "not_a_run"];
|
|
41
|
+
function object(value) {
|
|
42
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
43
|
+
}
|
|
44
|
+
function nullableText(value) {
|
|
45
|
+
return value === null || typeof value === "string";
|
|
46
|
+
}
|
|
47
|
+
function count(value) {
|
|
48
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
49
|
+
}
|
|
50
|
+
function entry(value, cwd) {
|
|
51
|
+
if (!object(value))
|
|
52
|
+
throw new Error("run is not an object");
|
|
53
|
+
const { folder, workflow, source, name, placement, state, created_at, charged_usd, stand_in, stopped } = value;
|
|
54
|
+
if (typeof folder !== "string" || folder === "" || folder.includes("\0"))
|
|
55
|
+
throw new Error("invalid folder");
|
|
56
|
+
if (!nullableText(workflow) || !nullableText(source) || !nullableText(name) || !nullableText(created_at)) {
|
|
57
|
+
throw new Error("invalid run text field");
|
|
58
|
+
}
|
|
59
|
+
if (!PLACEMENTS.includes(placement) || !STATES.includes(state)) {
|
|
60
|
+
throw new Error("unknown placement or state");
|
|
61
|
+
}
|
|
62
|
+
if (charged_usd !== null && (typeof charged_usd !== "number" || !Number.isFinite(charged_usd))) {
|
|
63
|
+
throw new Error("invalid charged_usd");
|
|
64
|
+
}
|
|
65
|
+
if (typeof stand_in !== "boolean" || (stopped !== undefined && typeof stopped !== "string")) {
|
|
66
|
+
throw new Error("invalid stand_in or stopped");
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
folder,
|
|
70
|
+
runDir: resolve(cwd, folder),
|
|
71
|
+
workflow,
|
|
72
|
+
source,
|
|
73
|
+
name,
|
|
74
|
+
placement: placement,
|
|
75
|
+
state: state,
|
|
76
|
+
created_at,
|
|
77
|
+
charged_usd,
|
|
78
|
+
stand_in,
|
|
79
|
+
...(stopped !== undefined ? { stopped } : {}),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function parseList(done, cwd) {
|
|
83
|
+
if (done.status !== 0)
|
|
84
|
+
throw new Error("unexpected status");
|
|
85
|
+
const value = JSON.parse(done.stdout);
|
|
86
|
+
if (!object(value) || value.kind !== LIST_KIND || !Array.isArray(value.runs) || !Array.isArray(value.skipped)) {
|
|
87
|
+
throw new Error("incompatible result");
|
|
88
|
+
}
|
|
89
|
+
// What could not be read is not returned, so its entries are not read: a future code must
|
|
90
|
+
// not break listing.
|
|
91
|
+
return value.runs.map((run) => entry(run, cwd));
|
|
92
|
+
}
|
|
93
|
+
function removalEntry(value, cwd, applied) {
|
|
94
|
+
const run = entry(value, cwd);
|
|
95
|
+
const { outcome, code, message, freed_bytes, cache_bytes } = value;
|
|
96
|
+
if (!(applied ? APPLIED_OUTCOMES : PREVIEW_OUTCOMES).includes(outcome)) {
|
|
97
|
+
throw new Error("unknown or mismatched outcome");
|
|
98
|
+
}
|
|
99
|
+
if (code !== undefined && !RUN_CODES.includes(code))
|
|
100
|
+
throw new Error("unknown code");
|
|
101
|
+
if (message !== undefined && typeof message !== "string")
|
|
102
|
+
throw new Error("invalid message");
|
|
103
|
+
if (FAILED_OUTCOMES.includes(outcome) && (code === undefined || message === undefined)) {
|
|
104
|
+
throw new Error("refusal lacks code or message");
|
|
105
|
+
}
|
|
106
|
+
if (!count(freed_bytes) || !count(cache_bytes))
|
|
107
|
+
throw new Error("invalid byte counts");
|
|
108
|
+
return {
|
|
109
|
+
...run,
|
|
110
|
+
outcome: outcome,
|
|
111
|
+
...(code !== undefined ? { code: code } : {}),
|
|
112
|
+
...(message !== undefined ? { message } : {}),
|
|
113
|
+
freed_bytes,
|
|
114
|
+
cache_bytes,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/** The error document of exit status 2, or null when stdout holds none. */
|
|
118
|
+
function removalFailure(done) {
|
|
119
|
+
let value;
|
|
120
|
+
try {
|
|
121
|
+
value = JSON.parse(done.stdout);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
if (!object(value) || value.kind !== REMOVAL_KIND || value.applied !== false || !object(value.error)) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const { code, message, run } = value.error;
|
|
130
|
+
if (!ERROR_CODES.includes(code) || typeof message !== "string" || !nullableText(run)) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return { kind: REMOVAL_KIND, applied: false, error: { code: code, message, run } };
|
|
134
|
+
}
|
|
135
|
+
function parseRemoval(done, cwd, preview) {
|
|
136
|
+
const value = JSON.parse(done.stdout);
|
|
137
|
+
if (!object(value) || value.kind !== REMOVAL_KIND || "error" in value || value.applied !== !preview) {
|
|
138
|
+
throw new Error("incompatible result");
|
|
139
|
+
}
|
|
140
|
+
const { runs, skipped, freed_bytes, cache_bytes } = value;
|
|
141
|
+
if (!Array.isArray(runs) || !object(skipped) || !count(freed_bytes) || !count(cache_bytes)) {
|
|
142
|
+
throw new Error("missing or invalid fields");
|
|
143
|
+
}
|
|
144
|
+
for (const [reason, passed] of Object.entries(skipped)) {
|
|
145
|
+
if (!REMOVAL_SKIPS.includes(reason) || !count(passed) || passed < 1)
|
|
146
|
+
throw new Error("invalid skipped");
|
|
147
|
+
}
|
|
148
|
+
const entries = runs.map((run) => removalEntry(run, cwd, !preview));
|
|
149
|
+
const failed = entries.some((run) => FAILED_OUTCOMES.includes(run.outcome));
|
|
150
|
+
if (done.status !== (failed ? 1 : 0))
|
|
151
|
+
throw new Error("mismatched status");
|
|
152
|
+
return {
|
|
153
|
+
applied: !preview,
|
|
154
|
+
runs: entries,
|
|
155
|
+
skipped: { ...skipped },
|
|
156
|
+
freed_bytes,
|
|
157
|
+
cache_bytes,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function callOptions(options, cwd) {
|
|
161
|
+
return {
|
|
162
|
+
cwd,
|
|
163
|
+
...(options.env !== undefined ? { env: options.env } : {}),
|
|
164
|
+
...(options.signal !== undefined ? { signal: options.signal } : {}),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/** The runs of the project found from cwd, newest first. Reads only: no lock, no writes.
|
|
168
|
+
* Folders the engine could not read are left out. */
|
|
169
|
+
export async function listRuns(options = {}) {
|
|
170
|
+
const args = ["runs", "list"];
|
|
171
|
+
if (options.workflow !== undefined) {
|
|
172
|
+
if (typeof options.workflow !== "string" || options.workflow === "" || options.workflow.includes("\0")) {
|
|
173
|
+
throw new TypeError("workflow is a nonempty workflow id without NUL");
|
|
174
|
+
}
|
|
175
|
+
args.push(`--workflow=${options.workflow}`);
|
|
176
|
+
}
|
|
177
|
+
const cwd = base(options.cwd);
|
|
178
|
+
const done = await call([...args, "--json"], callOptions(options, cwd));
|
|
179
|
+
try {
|
|
180
|
+
return parseList(done, cwd);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
throw failure("runs list", done);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/** The working directory as the engine sees it: its real path, since the engine prints folders
|
|
187
|
+
* relative to its canonical working directory (resolved lexically when it cannot be read). */
|
|
188
|
+
function base(cwd) {
|
|
189
|
+
const given = resolve(cwd ?? process.cwd());
|
|
190
|
+
try {
|
|
191
|
+
return realpathSync(given);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return given;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
/** Remove exactly the named runs: run folders (relative to cwd) or `WORKFLOW_ID/NAME` of named
|
|
198
|
+
* runs; select them with {@link listRuns} first. Every RUN is resolved before anything is removed,
|
|
199
|
+
* and one that is not a run rejects with {@link RunRemovalError}. A run that is active, or the last
|
|
200
|
+
* holding a pick, is refused without rejecting: read each entry's `outcome`. Aborting a removal
|
|
201
|
+
* may leave a run `partial`; removing it again finishes it. */
|
|
202
|
+
export async function removeRuns(runs, options = {}) {
|
|
203
|
+
if (!Array.isArray(runs) || runs.length === 0
|
|
204
|
+
|| !runs.every((run) => typeof run === "string" && run !== "" && !run.includes("\0"))) {
|
|
205
|
+
throw new TypeError("runs is a nonempty array of nonempty run folders or WORKFLOW_ID/NAME, without NUL");
|
|
206
|
+
}
|
|
207
|
+
if (options.preview !== undefined && typeof options.preview !== "boolean") {
|
|
208
|
+
throw new TypeError("preview is true or false");
|
|
209
|
+
}
|
|
210
|
+
const preview = options.preview === true;
|
|
211
|
+
const cwd = base(options.cwd);
|
|
212
|
+
const done = await call(["runs", "remove", ...(preview ? [] : ["--yes"]), "--json", "--", ...runs], callOptions(options, cwd));
|
|
213
|
+
if (done.status === USAGE_OR_ERROR) {
|
|
214
|
+
const refused = removalFailure(done);
|
|
215
|
+
if (refused !== null)
|
|
216
|
+
throw new RunRemovalError(refused, done);
|
|
217
|
+
throw failure("runs remove", done);
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
return parseRemoval(done, cwd, preview);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
if (preview)
|
|
224
|
+
throw failure("runs remove", done);
|
|
225
|
+
const details = {
|
|
226
|
+
exitCode: done.status, signal: done.signal, stdout: done.stdout, stderr: done.stderr, args: done.args,
|
|
227
|
+
};
|
|
228
|
+
const what = done.status === 0 || done.status === 1
|
|
229
|
+
? "grida-fx runs remove printed a result that could not be read"
|
|
230
|
+
: failure("runs remove", done).message;
|
|
231
|
+
throw new FxError(`${what}; the removal may already have taken effect`, details);
|
|
232
|
+
}
|
|
233
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grida/fx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Grida FX for JavaScript: the grida-fx command, and an SDK to build workflows and drive the engine.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"grida",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"assets",
|
|
11
11
|
"pipeline"
|
|
12
12
|
],
|
|
13
|
-
"homepage": "https://
|
|
13
|
+
"homepage": "https://grida.co/fx",
|
|
14
|
+
"license": "Apache-2.0",
|
|
14
15
|
"bugs": "https://github.com/gridaco/fx/issues",
|
|
15
16
|
"repository": {
|
|
16
17
|
"type": "git",
|
|
@@ -31,22 +32,27 @@
|
|
|
31
32
|
"types": "./dist/index.d.ts",
|
|
32
33
|
"default": "./dist/index.js"
|
|
33
34
|
},
|
|
35
|
+
"./cli": {
|
|
36
|
+
"types": "./dist/cli.d.ts",
|
|
37
|
+
"default": "./dist/cli.js"
|
|
38
|
+
},
|
|
34
39
|
"./package.json": "./package.json"
|
|
35
40
|
},
|
|
36
41
|
"files": [
|
|
37
42
|
"bin/grida-fx.js",
|
|
38
43
|
"dist",
|
|
39
|
-
"README.md"
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE"
|
|
40
46
|
],
|
|
41
47
|
"sideEffects": false,
|
|
42
48
|
"optionalDependencies": {
|
|
43
|
-
"@grida/fx-darwin-arm64": "0.
|
|
44
|
-
"@grida/fx-darwin-x64": "0.
|
|
45
|
-
"@grida/fx-linux-arm64-gnu": "0.
|
|
46
|
-
"@grida/fx-linux-x64-gnu": "0.
|
|
49
|
+
"@grida/fx-darwin-arm64": "0.2.0",
|
|
50
|
+
"@grida/fx-darwin-x64": "0.2.0",
|
|
51
|
+
"@grida/fx-linux-arm64-gnu": "0.2.0",
|
|
52
|
+
"@grida/fx-linux-x64-gnu": "0.2.0"
|
|
47
53
|
},
|
|
48
54
|
"publishConfig": {
|
|
49
55
|
"access": "public",
|
|
50
|
-
"tag": "
|
|
56
|
+
"tag": "latest"
|
|
51
57
|
}
|
|
52
58
|
}
|