@assethub/cli 0.1.6 → 0.1.8
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 +289 -5
- package/dist/canvas.d.ts +27 -0
- package/dist/canvas.d.ts.map +1 -0
- package/dist/canvas.js +82 -0
- package/dist/canvasAssetImport.d.ts +45 -0
- package/dist/canvasAssetImport.d.ts.map +1 -0
- package/dist/canvasAssetImport.js +154 -0
- package/dist/canvasComparison.d.ts +21 -0
- package/dist/canvasComparison.d.ts.map +1 -0
- package/dist/canvasComparison.js +58 -0
- package/dist/execution.d.ts +175 -0
- package/dist/execution.d.ts.map +1 -0
- package/dist/execution.js +234 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1567 -189
- package/dist/projectSources.d.ts +65 -0
- package/dist/projectSources.d.ts.map +1 -0
- package/dist/projectSources.js +369 -0
- package/dist/projectSourcesWorkbook.d.ts +3 -0
- package/dist/projectSourcesWorkbook.d.ts.map +1 -0
- package/dist/projectSourcesWorkbook.js +143 -0
- package/dist/runsUpload/artifactGraphModel.d.ts +48 -0
- package/dist/runsUpload/artifactGraphModel.d.ts.map +1 -0
- package/dist/runsUpload/artifactGraphModel.js +127 -0
- package/dist/runsUpload/blobRefs.d.ts +13 -0
- package/dist/runsUpload/blobRefs.d.ts.map +1 -0
- package/dist/runsUpload/blobRefs.js +73 -0
- package/dist/runsUpload/buildRunUpload.d.ts +63 -0
- package/dist/runsUpload/buildRunUpload.d.ts.map +1 -0
- package/dist/runsUpload/buildRunUpload.js +428 -0
- package/dist/runsUpload/canonicalJson.d.ts +5 -0
- package/dist/runsUpload/canonicalJson.d.ts.map +1 -0
- package/dist/runsUpload/canonicalJson.js +108 -0
- package/dist/runsUpload/controlEndpoints.d.ts +63 -0
- package/dist/runsUpload/controlEndpoints.d.ts.map +1 -0
- package/dist/runsUpload/controlEndpoints.js +64 -0
- package/dist/runsUpload/graphFolder.d.ts +25 -0
- package/dist/runsUpload/graphFolder.d.ts.map +1 -0
- package/dist/runsUpload/graphFolder.js +147 -0
- package/dist/runsUpload/runUploadError.d.ts +23 -0
- package/dist/runsUpload/runUploadError.d.ts.map +1 -0
- package/dist/runsUpload/runUploadError.js +24 -0
- package/dist/runsUpload/uploadRun.d.ts +43 -0
- package/dist/runsUpload/uploadRun.d.ts.map +1 -0
- package/dist/runsUpload/uploadRun.js +217 -0
- package/package.json +11 -5
package/README.md
CHANGED
|
@@ -2,6 +2,258 @@
|
|
|
2
2
|
|
|
3
3
|
Command line interface for AssetHub.
|
|
4
4
|
|
|
5
|
+
## Agent generation with canvas history
|
|
6
|
+
|
|
7
|
+
This release uses the Internal `api_canvas_execution` gate. Run `assethub capabilities`
|
|
8
|
+
first. Image, mesh, parts split, and production analyze/execute/automation refuse to dispatch if canvas history is unavailable.
|
|
9
|
+
The current public API remains compatible with clients that omit `executionContext`.
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
assethub image generate --prompt "small stylized robot" --agent my-agent --wait > image.json
|
|
13
|
+
CANVAS_ID=$(jq -r '.execution.canvas.id' image.json)
|
|
14
|
+
IMAGE_ASSET_ID=$(jq -r '.execution.outputs[0].assetId' image.json)
|
|
15
|
+
IMAGE_RUN_ID=$(jq -r '.execution.runId' image.json)
|
|
16
|
+
assethub mesh generate --source-id "$IMAGE_ASSET_ID" --canvas "$CANVAS_ID" \
|
|
17
|
+
--derived-from-run "$IMAGE_RUN_ID" --agent my-agent --wait > mesh.json
|
|
18
|
+
assethub graph show --canvas "$CANVAS_ID"
|
|
19
|
+
assethub runs list --canvas "$CANVAS_ID"
|
|
20
|
+
assethub canvas open "$CANVAS_ID"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Without `--canvas`, the first generation creates one canvas per real working
|
|
24
|
+
directory, API origin and organization. `canvas use <id>` changes the selection.
|
|
25
|
+
An inaccessible selected canvas is an error; the CLI does not silently make another.
|
|
26
|
+
`canvas open` prints JSON and opens a browser only in a terminal.
|
|
27
|
+
|
|
28
|
+
Each generation returns `execution` with the canvas URL, run/job IDs, stable output
|
|
29
|
+
asset IDs, history status, and credit usage when available. History persists while
|
|
30
|
+
all browsers are closed. Uploaded images are promoted to owned durable assets.
|
|
31
|
+
Use `--input-json @request.json` for a complete request, or the existing file/stdin/
|
|
32
|
+
source flags. A `source create` or `files import` JSON response can be passed to
|
|
33
|
+
`--source-json @source.json`; generated asset IDs can be passed to `--source-id`.
|
|
34
|
+
|
|
35
|
+
The CLI saves each request before dispatch under `~/.assethub/state/operations`
|
|
36
|
+
(or `ASSETHUB_CLI_STATE_DIR`). Those files contain inputs, never API keys. Keep the
|
|
37
|
+
state directory private. `--operation-id <uuid>` supplies an operation identity;
|
|
38
|
+
retries and `runs resume <operation-id>` reuse its identical body and key. A new
|
|
39
|
+
candidate needs a new operation ID. `runs watch <run-id>` only reads status.
|
|
40
|
+
|
|
41
|
+
stdout contains final JSON; stderr contains progress. Exit codes are 0 for accepted/
|
|
42
|
+
completed, 1 for terminal failure/partial, 2 for input/auth/capability/budget errors,
|
|
43
|
+
3 for timeout/history pending/needs review, and 130 for Ctrl-C. Timeout and Ctrl-C
|
|
44
|
+
leave server execution running and return known identities.
|
|
45
|
+
|
|
46
|
+
External agents can evaluate downloaded artifacts with their own tools and store
|
|
47
|
+
an assessment without starting another model call:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
assethub evaluations submit --canvas "$CANVAS_ID" --artifact "$IMAGE_ASSET_ID" \
|
|
51
|
+
--report ./evaluation.json --agent my-agent --require-pass
|
|
52
|
+
assethub evaluations list --canvas "$CANVAS_ID"
|
|
53
|
+
assethub graph lineage --canvas "$CANVAS_ID" --artifact "$IMAGE_ASSET_ID"
|
|
54
|
+
assethub graph export --canvas "$CANVAS_ID" --out ./graph.json
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`evaluation.json` requires the strict report fields `schemaVersion`,
|
|
58
|
+
`rubric`, `verdict: pass|fail|needs_review`, `criteria`, `referenceAssetIds`, and
|
|
59
|
+
`evidenceAssetIds` for comparable reports. The server records `agent_submission`
|
|
60
|
+
and authenticated actor provenance. This never counts as human approval.
|
|
61
|
+
`--require-pass` returns 1 for fail and 3 for missing/needs-review verdicts.
|
|
62
|
+
|
|
63
|
+
For example, an agent that has not inspected the output yet can submit:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"schemaVersion": "assethub.evaluation-submission.v1",
|
|
68
|
+
"rubric": {"id": "asset-quality", "version": "1"},
|
|
69
|
+
"verdict": "needs_review",
|
|
70
|
+
"criteria": [
|
|
71
|
+
{
|
|
72
|
+
"id": "visual-quality",
|
|
73
|
+
"verdict": "needs_review",
|
|
74
|
+
"reason": "Artifact inspection is pending."
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
"referenceAssetIds": [],
|
|
78
|
+
"evidenceAssetIds": []
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Replace the verdict and reason with the actual assessment after inspecting the
|
|
83
|
+
artifact; reference and evidence IDs must belong to the authenticated organization.
|
|
84
|
+
|
|
85
|
+
Existing parts commands use the same canvas and durable operation storage:
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
assethub parts split --source-id "$IMAGE_ASSET_ID" --canvas "$CANVAS_ID" --part-extractor V1.5 --wait
|
|
89
|
+
# Select only the parts to execute; --all-ready selects all ready tasks.
|
|
90
|
+
assethub parts split --source-id "$IMAGE_ASSET_ID" --canvas "$CANVAS_ID" --part-extractor V1.5 --all-ready --wait
|
|
91
|
+
# Internal V3.6.1 runs the complete graph split through one analyze receipt.
|
|
92
|
+
assethub production agents
|
|
93
|
+
assethub parts split --source-id "$IMAGE_ASSET_ID" --canvas "$CANVAS_ID" --part-extractor V3.6.1 --all-ready --wait
|
|
94
|
+
assethub production automation --canvas "$CANVAS_ID" --input-json @batch.json --wait
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`parts split` keeps separate analysis and selected-part execution phases. Each phase
|
|
98
|
+
has its own saved operation ID, with the execution linked to the analysis run.
|
|
99
|
+
For Internal `V3.6.1` (`V3.6.1 Primary Images First`), analysis runs the complete
|
|
100
|
+
Artifact Graph split. `--all-ready` waits for that one receipt and does not start a
|
|
101
|
+
second production execution. Resume an interrupted graph split with
|
|
102
|
+
`runs resume <operation-id>`; `--task-id`, `--mission-id`,
|
|
103
|
+
`--part-extraction-mode`, and classic `--order-id` continuation are unsupported.
|
|
104
|
+
Automation runs the existing complete batch pipeline, including mesh generation;
|
|
105
|
+
its `images[]` share the selected canvas. `batch.json` uses the existing automation
|
|
106
|
+
request shape, including `agentVersion`, `images` (owned `imageAssetId`, `uploadId`,
|
|
107
|
+
or an `imageUrl` to import), `config`, and optional `maxCostCredits`.
|
|
108
|
+
An uncertain Trigger acknowledgement remains recoverable as `needs_review` with
|
|
109
|
+
pending history; retrying the same operation never silently launches another run.
|
|
110
|
+
|
|
111
|
+
`evaluate list` discovers server evaluators. Server evaluation dispatch is currently
|
|
112
|
+
unavailable. Graph exports are bounded to 100 runs/evaluations and 1,000 nodes;
|
|
113
|
+
truncated exports require `--allow-truncated`. Other commands, including production run/intervene, rig and retopology,
|
|
114
|
+
retain their existing API behavior and do not yet create these receipts.
|
|
115
|
+
`autopilot --demo` is an experimental demo requiring the AssetHub monorepo and its unpublished development dependency `@assethub/autopilot-core`. Standalone project workflow commands do not require or install it. Autonomous budgeted improvement is a later stage.
|
|
116
|
+
|
|
117
|
+
## Storyboard project workflow
|
|
118
|
+
|
|
119
|
+
These API-key commands work from the standalone CLI. Canvas/context commands use
|
|
120
|
+
`api_canvas_execution`; native moodboards additionally require an internal actor
|
|
121
|
+
with `moodboard_style_reference_enabled`. Select a key belonging to the intended
|
|
122
|
+
workspace before uploading source material. API keys are never part of context.
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
# No API key is needed for local extraction. Optional dependencies are reported.
|
|
126
|
+
assethub project ingest ./client-sources --out-dir ./extracted --exclude-dir generated
|
|
127
|
+
assethub canvas create --name "Storyboard scene study" > canvas.json
|
|
128
|
+
CANVAS_ID=$(jq -r .id canvas.json)
|
|
129
|
+
assethub canvas import --canvas "$CANVAS_ID" --file ./extracted/storyboard.png > storyboard.json
|
|
130
|
+
assethub canvas import --canvas "$CANVAS_ID" --file ./character-face.png > character.json
|
|
131
|
+
assethub canvas import --canvas "$CANVAS_ID" --file ./primary-style.png > style.json
|
|
132
|
+
|
|
133
|
+
# Supply prompt + source text + imageAssetIds in analysis-request.json.
|
|
134
|
+
# Keep facts, uncertain identities and proposed backgrounds separate.
|
|
135
|
+
assethub language vision --input-json @analysis-request.json \
|
|
136
|
+
--operation-id 61e3a99c-30b7-4f41-8d57-2789ccdc7c92 --out analysis.json
|
|
137
|
+
|
|
138
|
+
# Prepare context.json from the inspected source facts and analysis.
|
|
139
|
+
assethub canvas context put --canvas "$CANVAS_ID" --file ./context.json --if-version 0
|
|
140
|
+
assethub canvas context get --canvas "$CANVAS_ID" --out ./recovered-context.json
|
|
141
|
+
assethub moodboard create --input-json @moodboard.json > board.json
|
|
142
|
+
BOARD_ID=$(jq -r .id board.json)
|
|
143
|
+
assethub moodboard analyze "$BOARD_ID" --wait > revision.json
|
|
144
|
+
REVISION_ID=$(jq -r .id revision.json)
|
|
145
|
+
assethub image generate --canvas "$CANVAS_ID" --context "$CANVAS_ID" \
|
|
146
|
+
--moodboard-revision "$REVISION_ID" --prompt "Reconstruct this storyboard as a 3D scene" \
|
|
147
|
+
--wait --download --out-dir ./generated > generated.json
|
|
148
|
+
SOURCE_ID=$(jq -r .assetId storyboard.json)
|
|
149
|
+
OUTPUT_ID=$(jq -r '.execution.outputs[0].assetId' generated.json)
|
|
150
|
+
assethub canvas compare --canvas "$CANVAS_ID" --source-id "$SOURCE_ID" \
|
|
151
|
+
--asset-id "$OUTPUT_ID" --out ./comparison.html
|
|
152
|
+
assethub evaluations submit --canvas "$CANVAS_ID" --artifact "$OUTPUT_ID" \
|
|
153
|
+
--report ./review.json --agent my-agent
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`project ingest` extracts PDF text/pages, workbook cells and embedded images,
|
|
157
|
+
representative video frames, and image/text sources. It writes `summary.json`,
|
|
158
|
+
`text.md` and `images.json` with source paths, hashes and extraction metadata.
|
|
159
|
+
Dependencies (Poppler, ffmpeg and Python 3) are detected, never installed. Partial
|
|
160
|
+
extraction exits 3 and reports missing dependencies; filenames do not establish
|
|
161
|
+
whether a picture is a storyboard or a 3D reference. Original files stay unchanged.
|
|
162
|
+
|
|
163
|
+
A context document is an extensible JSON object:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{
|
|
167
|
+
"schemaVersion": 1,
|
|
168
|
+
"title": "EP013 s150",
|
|
169
|
+
"instructions": {
|
|
170
|
+
"must": ["Preserve the original character face and pose"],
|
|
171
|
+
"avoid": ["Generic replacement characters"]
|
|
172
|
+
},
|
|
173
|
+
"sources": [
|
|
174
|
+
{
|
|
175
|
+
"key": "storyboard",
|
|
176
|
+
"remoteAssetId": "<owned-image-uuid>",
|
|
177
|
+
"generationUse": "required"
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"generationPlan": {"requiredSourceKeys": ["storyboard"]},
|
|
181
|
+
"sourceFacts": [
|
|
182
|
+
"Facts established by the original storyboard and story text"
|
|
183
|
+
],
|
|
184
|
+
"inferences": [
|
|
185
|
+
{"proposal": "Background inferred from story", "status": "proposed"}
|
|
186
|
+
],
|
|
187
|
+
"reviewState": {"status": "awaiting_creator_review"}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
`context get --out` writes the document for editing and reuse; stdout contains its
|
|
192
|
+
version/hash envelope. Updating changed content requires `--if-version N` from the
|
|
193
|
+
last read. Image generation pins the current version by default; `--context-version`
|
|
194
|
+
and repeated `--context-source` explicitly select a version and source keys. Required
|
|
195
|
+
references, labelled instructions and source facts are added by the server, and their
|
|
196
|
+
IDs/version/hash are recorded on the run. Retry an uncertain generation with
|
|
197
|
+
`runs resume <operation-id>` to preserve the original pinned inputs.
|
|
198
|
+
|
|
199
|
+
`moodboard.json` contains `name`, `assetIds` (maximum 12),
|
|
200
|
+
`representativeAssetIds` (maximum 3, all included in assetIds), and optional
|
|
201
|
+
`userNote`. `update <board-id> --input-json @file` creates a new immutable revision;
|
|
202
|
+
`get`, `list`, and `archive` manage existing boards. Analysis uses the existing
|
|
203
|
+
8-credit vision plan for a new attempt, resumes an active job, and reuses a ready
|
|
204
|
+
revision. The combined generation limit is **6 content and style images**: five identity/storyboard references leave room
|
|
205
|
+
for one style representative.
|
|
206
|
+
|
|
207
|
+
Image import persists upload identity before registration and recovers the same
|
|
208
|
+
asset after an uncertain response; `--upload-id` resumes a known completed upload.
|
|
209
|
+
Identical moodboard creation input uses the same operation identity; supply a fresh
|
|
210
|
+
`--operation-id` when intentionally creating another identical board. Language
|
|
211
|
+
commands require a UUID operation ID: reuse it with the same input after a timeout.
|
|
212
|
+
|
|
213
|
+
Comparisons are portable HTML with embedded image bytes and a SHA-256 provenance
|
|
214
|
+
sidecar. They remain `awaiting_creator_review`; an agent evaluation is not creator
|
|
215
|
+
approval. Native project assets and generation history are saved in AssetHub.
|
|
216
|
+
`canvas layout` appends native image nodes and source/output edges through the
|
|
217
|
+
authoritative canvas room (Internal rollout). It preserves matching existing nodes
|
|
218
|
+
and rejects identity collisions. `canvas import` alone preserves an asset without
|
|
219
|
+
changing layout. Use the owned asset IDs returned by import or generation, including `image_...` IDs.
|
|
220
|
+
For example, create `layout.json` with those IDs:
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{
|
|
224
|
+
"images": [
|
|
225
|
+
{
|
|
226
|
+
"assetId": "<storyboard-asset-id>",
|
|
227
|
+
"role": "source",
|
|
228
|
+
"x": 0,
|
|
229
|
+
"y": 0,
|
|
230
|
+
"name": "Original storyboard"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"assetId": "<generated-asset-id>",
|
|
234
|
+
"role": "output",
|
|
235
|
+
"x": 600,
|
|
236
|
+
"y": 0,
|
|
237
|
+
"name": "Generated candidate — review pending"
|
|
238
|
+
}
|
|
239
|
+
],
|
|
240
|
+
"connections": [
|
|
241
|
+
{
|
|
242
|
+
"sourceAssetId": "<storyboard-asset-id>",
|
|
243
|
+
"targetAssetId": "<generated-asset-id>"
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
```sh
|
|
250
|
+
assethub canvas layout --canvas "$CANVAS_ID" --input-json @layout.json
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Legacy `workspace --internal` commands and the experimental autopilot demo are
|
|
254
|
+
not available in the standalone distribution. Use the authenticated public
|
|
255
|
+
`canvas`, `moodboard`, and `canvas context` commands described above.
|
|
256
|
+
|
|
5
257
|
```sh
|
|
6
258
|
export ASSETHUB_API_KEY=ah_live_xxx
|
|
7
259
|
|
|
@@ -13,8 +265,39 @@ assethub mesh generate --file ./input.png --wait
|
|
|
13
265
|
assethub jobs watch job_123 --download --out-dir ./out/job
|
|
14
266
|
assethub parts split --file ./input.png --part-extractor "V1.5" --wait --all-ready --download --out-dir ./out/parts
|
|
15
267
|
assethub parts compare --file ./input.png --preprocess-prompt "clean white background, centered product photo" --part-extractor "V1.5" --wait --all-ready
|
|
268
|
+
assethub runs upload ./out/pluffy-run-2026-07-30 --dry-run
|
|
16
269
|
```
|
|
17
270
|
|
|
271
|
+
Uploading a local artifact-graph run:
|
|
272
|
+
|
|
273
|
+
```sh
|
|
274
|
+
# Validate the run folder without sending anything.
|
|
275
|
+
assethub runs upload ./out/pluffy-run-2026-07-30 --dry-run
|
|
276
|
+
|
|
277
|
+
# Upload it. Blobs go first, one at a time, then the snapshot.
|
|
278
|
+
assethub runs upload ./out/pluffy-run-2026-07-30 --tag pluffy --description "V3 humanoid"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`<path>` is an `ag.graph-folder.v1` folder — the directory holding
|
|
282
|
+
`manifest.json`, `nodes.jsonl`, `edges.jsonl` and `blobs/`. The wire format is
|
|
283
|
+
`ag.registry.v1`, the same document the artifact-graph registry accepts, so one
|
|
284
|
+
folder can be published to either destination without conversion.
|
|
285
|
+
|
|
286
|
+
- `--dry-run` runs every local check and prints what _would_ be sent. A folder
|
|
287
|
+
whose manifest no longer matches its own `nodes.jsonl`, whose blob table
|
|
288
|
+
disagrees with the graph, or which exceeds the per-request size ceilings is
|
|
289
|
+
rejected here rather than halfway through an upload.
|
|
290
|
+
- Uploads are resumable. Each blob is checked against the server before it is
|
|
291
|
+
sent, and blob storage is content-addressed, so re-running the identical
|
|
292
|
+
command after a failure sends only what is still missing.
|
|
293
|
+
- `--graph-id` / `--stream-id` / `--rev` override the identity derived from the
|
|
294
|
+
folder name and `manifest.source`. Re-uploading the same revision with
|
|
295
|
+
different content is refused; bump `--rev` to publish a new revision.
|
|
296
|
+
- `--skip-register` skips the graph-registration call for a graph that already
|
|
297
|
+
exists.
|
|
298
|
+
- Requires an internal account whose org has the Production Control
|
|
299
|
+
entitlement; other keys get "cannot upload runs to Production Control".
|
|
300
|
+
|
|
18
301
|
Configuration:
|
|
19
302
|
|
|
20
303
|
- `ASSETHUB_API_KEY`: optional when a profile was saved with `auth login`.
|
|
@@ -79,12 +362,13 @@ is preferred. Use `--all-ready` rather than explicit `--task-id` values when
|
|
|
79
362
|
preprocessing is enabled, because the generated-image split creates a separate
|
|
80
363
|
production order.
|
|
81
364
|
|
|
82
|
-
Image generation
|
|
83
|
-
|
|
365
|
+
Image generation uses the `v2` endpoint with mandatory canvas history.
|
|
366
|
+
`--api-version v1` is rejected; run `assethub capabilities` to check availability.
|
|
84
367
|
|
|
85
|
-
Part extraction uses the same
|
|
86
|
-
`
|
|
87
|
-
|
|
368
|
+
Part extraction uses the same names shown in the AssetHub product: `V1.5`,
|
|
369
|
+
`V2.0 alpha`, `V2.1 alpha`, and the Internal-only `V3.6.1` alias for
|
|
370
|
+
`V3.6.1 Primary Images First`. Retired names are no longer accepted by the CLI.
|
|
371
|
+
Internal agent IDs are intentionally not part of the CLI interface.
|
|
88
372
|
|
|
89
373
|
The CLI only uses AssetHub's public API surface and is designed so this package
|
|
90
374
|
can be published independently from the private application monorepo.
|
package/dist/canvas.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Canvas } from '@assethub/api-client';
|
|
2
|
+
type CanvasScope = {
|
|
3
|
+
stateDir: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
ownerId: string;
|
|
7
|
+
};
|
|
8
|
+
type CanvasClient = {
|
|
9
|
+
v2: {
|
|
10
|
+
createCanvas: (body: {
|
|
11
|
+
name: string;
|
|
12
|
+
}, options: {
|
|
13
|
+
idempotencyKey: string;
|
|
14
|
+
}) => Promise<Canvas>;
|
|
15
|
+
getCanvas: (id: number) => Promise<Canvas>;
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export declare const writeState: (path: string, value: unknown) => Promise<void>;
|
|
19
|
+
export declare const readState: (path: string) => Promise<unknown | undefined>;
|
|
20
|
+
export declare const saveCanvasSelection: (scope: CanvasScope, canvasId: number) => Promise<void>;
|
|
21
|
+
export declare const resolveCanvasSelection: (options: CanvasScope & {
|
|
22
|
+
client: CanvasClient;
|
|
23
|
+
canvasId?: number;
|
|
24
|
+
create?: boolean;
|
|
25
|
+
}) => Promise<Canvas>;
|
|
26
|
+
export {};
|
|
27
|
+
//# sourceMappingURL=canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../src/canvas.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,sBAAsB,CAAA;AAEhD,KAAK,WAAW,GAAG;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AACD,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE;QACF,YAAY,EAAE,CACZ,IAAI,EAAE;YAAC,IAAI,EAAE,MAAM,CAAA;SAAC,EACpB,OAAO,EAAE;YAAC,cAAc,EAAE,MAAM,CAAA;SAAC,KAC9B,OAAO,CAAC,MAAM,CAAC,CAAA;QACpB,SAAS,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;KAC3C,CAAA;CACF,CAAA;AAED,eAAO,MAAM,UAAU,GACrB,MAAM,MAAM,EACZ,OAAO,OAAO,KACb,OAAO,CAAC,IAAI,CAWd,CAAA;AAED,eAAO,MAAM,SAAS,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,OAAO,GAAG,SAAS,CAazE,CAAA;AAiBD,eAAO,MAAM,mBAAmB,GAC9B,OAAO,WAAW,EAClB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAOd,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,SAAS,WAAW,GAAG;IACrB,MAAM,EAAE,YAAY,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,KACA,OAAO,CAAC,MAAM,CA8ChB,CAAA"}
|
package/dist/canvas.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, readFile, realpath, rename, unlink, writeFile, } from 'node:fs/promises';
|
|
3
|
+
import { basename, dirname, join } from 'node:path';
|
|
4
|
+
export const writeState = async (path, value) => {
|
|
5
|
+
await mkdir(dirname(path), { recursive: true, mode: 0o700 });
|
|
6
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
7
|
+
try {
|
|
8
|
+
await writeFile(temporary, JSON.stringify(value), { mode: 0o600, flag: 'wx' });
|
|
9
|
+
await rename(temporary, path);
|
|
10
|
+
}
|
|
11
|
+
finally {
|
|
12
|
+
await unlink(temporary).catch(error => {
|
|
13
|
+
if (error.code !== 'ENOENT')
|
|
14
|
+
throw error;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
export const readState = async (path) => {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(await readFile(path, 'utf8'));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error &&
|
|
24
|
+
typeof error === 'object' &&
|
|
25
|
+
'code' in error &&
|
|
26
|
+
error.code === 'ENOENT')
|
|
27
|
+
return undefined;
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const scopeHash = async (scope) => createHash('sha256')
|
|
32
|
+
.update(JSON.stringify([
|
|
33
|
+
scope.baseUrl.replace(/\/+$/, ''),
|
|
34
|
+
scope.ownerId,
|
|
35
|
+
await realpath(scope.cwd),
|
|
36
|
+
]))
|
|
37
|
+
.digest('hex');
|
|
38
|
+
/** A stable operation identity lets the server deduplicate even a crashed first invocation. */
|
|
39
|
+
const creationId = (hash) => `${hash.slice(0, 8)}-${hash.slice(8, 12)}-4${hash.slice(13, 16)}-8${hash.slice(17, 20)}-${hash.slice(20, 32)}`;
|
|
40
|
+
export const saveCanvasSelection = async (scope, canvasId) => {
|
|
41
|
+
if (!Number.isSafeInteger(canvasId) || canvasId <= 0)
|
|
42
|
+
throw new Error('Canvas ID must be a positive integer');
|
|
43
|
+
await writeState(join(scope.stateDir, 'canvases', `${await scopeHash(scope)}.json`), { canvasId });
|
|
44
|
+
};
|
|
45
|
+
export const resolveCanvasSelection = async (options) => {
|
|
46
|
+
const { client } = options;
|
|
47
|
+
let canvasId = options.canvasId;
|
|
48
|
+
const hash = await scopeHash(options);
|
|
49
|
+
if (canvasId == null) {
|
|
50
|
+
const stored = await readState(join(options.stateDir, 'canvases', `${hash}.json`));
|
|
51
|
+
if (stored != null) {
|
|
52
|
+
if (typeof stored !== 'object' ||
|
|
53
|
+
!('canvasId' in stored) ||
|
|
54
|
+
!Number.isSafeInteger(stored.canvasId)) {
|
|
55
|
+
throw new Error('Invalid saved canvas selection; select one with canvas use <id>');
|
|
56
|
+
}
|
|
57
|
+
canvasId = Number(stored.canvasId);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (canvasId != null) {
|
|
61
|
+
if (!Number.isSafeInteger(canvasId) || canvasId <= 0)
|
|
62
|
+
throw new Error('Canvas ID must be a positive integer');
|
|
63
|
+
const canvas = await client.v2.getCanvas(canvasId);
|
|
64
|
+
if (canvas.ownerId !== options.ownerId)
|
|
65
|
+
throw new Error('Canvas belongs to a different organization');
|
|
66
|
+
return canvas;
|
|
67
|
+
}
|
|
68
|
+
if (options.create === false)
|
|
69
|
+
throw new Error('No canvas selected; use --canvas <id> or canvas use <id>');
|
|
70
|
+
// Check local persistence before starting a remote mutation. The same scope
|
|
71
|
+
// always posts the same name and key, including after a lost response.
|
|
72
|
+
const directory = join(options.stateDir, 'canvases');
|
|
73
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
74
|
+
const probe = join(directory, `${hash}.${randomUUID()}.probe`);
|
|
75
|
+
await writeFile(probe, '', { mode: 0o600, flag: 'wx' });
|
|
76
|
+
await unlink(probe);
|
|
77
|
+
const canvas = await client.v2.createCanvas({ name: `CLI ${basename(await realpath(options.cwd))} ${hash.slice(0, 8)}` }, { idempotencyKey: creationId(hash) });
|
|
78
|
+
if (canvas.ownerId !== options.ownerId)
|
|
79
|
+
throw new Error('Created canvas belongs to a different organization');
|
|
80
|
+
await saveCanvasSelection(options, canvas.id);
|
|
81
|
+
return canvas;
|
|
82
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Source } from '@assethub/api-client';
|
|
2
|
+
type DurableSource = Extract<Source, {
|
|
3
|
+
uploadId: string;
|
|
4
|
+
} | {
|
|
5
|
+
resourceId: string;
|
|
6
|
+
}>;
|
|
7
|
+
type ImportedAsset = {
|
|
8
|
+
assetId: string;
|
|
9
|
+
mediaType: 'image';
|
|
10
|
+
canvasId: number;
|
|
11
|
+
};
|
|
12
|
+
type ImportClient = {
|
|
13
|
+
v2: {
|
|
14
|
+
uploadFile: (input: {
|
|
15
|
+
file: Blob;
|
|
16
|
+
fileName: string;
|
|
17
|
+
mediaType: 'image';
|
|
18
|
+
}) => Promise<{
|
|
19
|
+
uploadId?: string;
|
|
20
|
+
}>;
|
|
21
|
+
importCanvasAsset: (canvasId: number, input: {
|
|
22
|
+
source: DurableSource;
|
|
23
|
+
name?: string;
|
|
24
|
+
}) => Promise<ImportedAsset>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
type ImportOptions = {
|
|
28
|
+
client: ImportClient;
|
|
29
|
+
baseUrl: string;
|
|
30
|
+
ownerId: string;
|
|
31
|
+
canvasId: number;
|
|
32
|
+
stateDir: string;
|
|
33
|
+
filePath?: string;
|
|
34
|
+
contentType?: string;
|
|
35
|
+
source?: DurableSource;
|
|
36
|
+
name?: string;
|
|
37
|
+
};
|
|
38
|
+
/** Keep staging/import receipts separate from paid generation operation state. */
|
|
39
|
+
export declare function importCanvasAssetWithState(options: ImportOptions): Promise<ImportedAsset & {
|
|
40
|
+
statePath: string;
|
|
41
|
+
sha256?: string;
|
|
42
|
+
uploadId?: string;
|
|
43
|
+
}>;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=canvasAssetImport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasAssetImport.d.ts","sourceRoot":"","sources":["../src/canvasAssetImport.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,sBAAsB,CAAA;AAGhD,KAAK,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,GAAG;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC,CAAC,CAAA;AAC/E,KAAK,aAAa,GAAG;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,CAAA;AAC5E,KAAK,YAAY,GAAG;IAClB,EAAE,EAAE;QACF,UAAU,EAAE,CAAC,KAAK,EAAE;YAClB,IAAI,EAAE,IAAI,CAAA;YACV,QAAQ,EAAE,MAAM,CAAA;YAChB,SAAS,EAAE,OAAO,CAAA;SACnB,KAAK,OAAO,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAC,CAAC,CAAA;QAClC,iBAAiB,EAAE,CACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE;YAAC,MAAM,EAAE,aAAa,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAC,KAC1C,OAAO,CAAC,aAAa,CAAC,CAAA;KAC5B,CAAA;CACF,CAAA;AAaD,KAAK,aAAa,GAAG;IACnB,MAAM,EAAE,YAAY,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,aAAa,CAAA;IACtB,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAcD,kFAAkF;AAClF,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,aAAa,GACrB,OAAO,CACR,aAAa,GAAG;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAC,CACxE,CAwJA"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile, unlink } from 'node:fs/promises';
|
|
3
|
+
import { basename, dirname, extname, join, resolve } from 'node:path';
|
|
4
|
+
import { readState, writeState } from './canvas.js';
|
|
5
|
+
const imageContentTypes = {
|
|
6
|
+
'.png': 'image/png',
|
|
7
|
+
'.jpg': 'image/jpeg',
|
|
8
|
+
'.jpeg': 'image/jpeg',
|
|
9
|
+
'.webp': 'image/webp',
|
|
10
|
+
'.gif': 'image/gif',
|
|
11
|
+
'.avif': 'image/avif',
|
|
12
|
+
'.bmp': 'image/bmp',
|
|
13
|
+
'.tif': 'image/tiff',
|
|
14
|
+
'.tiff': 'image/tiff',
|
|
15
|
+
};
|
|
16
|
+
/** Keep staging/import receipts separate from paid generation operation state. */
|
|
17
|
+
export async function importCanvasAssetWithState(options) {
|
|
18
|
+
if (!Number.isSafeInteger(options.canvasId) ||
|
|
19
|
+
options.canvasId <= 0 ||
|
|
20
|
+
!options.ownerId)
|
|
21
|
+
throw new Error('An owned canvas is required for image import');
|
|
22
|
+
if (Boolean(options.filePath) === Boolean(options.source))
|
|
23
|
+
throw new Error('Provide exactly one file, upload ID, or resource ID');
|
|
24
|
+
const name = options.name?.trim();
|
|
25
|
+
if (name !== undefined && (!name || name.length > 200))
|
|
26
|
+
throw new Error('Import name must be between 1 and 200 characters');
|
|
27
|
+
const bytes = options.filePath
|
|
28
|
+
? await readFile(resolve(options.filePath))
|
|
29
|
+
: undefined;
|
|
30
|
+
const sha256 = bytes
|
|
31
|
+
? createHash('sha256').update(bytes).digest('hex')
|
|
32
|
+
: undefined;
|
|
33
|
+
const sourceIdentity = sha256
|
|
34
|
+
? `sha256:${sha256}`
|
|
35
|
+
: options.source?.uploadId
|
|
36
|
+
? `upload:${options.source.uploadId}`
|
|
37
|
+
: options.source?.resourceId
|
|
38
|
+
? `resource:${options.source.resourceId}`
|
|
39
|
+
: undefined;
|
|
40
|
+
if (!sourceIdentity)
|
|
41
|
+
throw new Error('Import source ID is required');
|
|
42
|
+
const scope = {
|
|
43
|
+
baseUrl: options.baseUrl.replace(/\/+$/, ''),
|
|
44
|
+
ownerId: options.ownerId,
|
|
45
|
+
canvasId: options.canvasId,
|
|
46
|
+
sourceIdentity,
|
|
47
|
+
};
|
|
48
|
+
const key = createHash('sha256').update(JSON.stringify(scope)).digest('hex');
|
|
49
|
+
const statePath = join(options.stateDir, 'canvas-imports', `${key}.json`);
|
|
50
|
+
const readReceipt = async () => {
|
|
51
|
+
const value = await readState(statePath);
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (!value ||
|
|
55
|
+
typeof value !== 'object' ||
|
|
56
|
+
!('schemaVersion' in value) ||
|
|
57
|
+
value.schemaVersion !== 'assethub.canvas-import.v1' ||
|
|
58
|
+
!('baseUrl' in value) ||
|
|
59
|
+
value.baseUrl !== scope.baseUrl ||
|
|
60
|
+
!('ownerId' in value) ||
|
|
61
|
+
value.ownerId !== scope.ownerId ||
|
|
62
|
+
!('canvasId' in value) ||
|
|
63
|
+
value.canvasId !== scope.canvasId ||
|
|
64
|
+
!('sourceIdentity' in value) ||
|
|
65
|
+
value.sourceIdentity !== sourceIdentity)
|
|
66
|
+
throw new Error(`Invalid or differently scoped import receipt: ${statePath}`);
|
|
67
|
+
const saved = value;
|
|
68
|
+
if ((saved.uploadId !== undefined &&
|
|
69
|
+
(typeof saved.uploadId !== 'string' || !saved.uploadId)) ||
|
|
70
|
+
(saved.resourceId !== undefined &&
|
|
71
|
+
(typeof saved.resourceId !== 'string' || !saved.resourceId)) ||
|
|
72
|
+
(sha256 !== undefined && saved.sha256 !== sha256))
|
|
73
|
+
throw new Error(`Invalid import source in receipt: ${statePath}`);
|
|
74
|
+
if (name !== undefined && saved.name !== name)
|
|
75
|
+
throw new Error(`This source has an import receipt with a different name. Reuse its original name: ${statePath}`);
|
|
76
|
+
return saved;
|
|
77
|
+
};
|
|
78
|
+
let saved = await readReceipt();
|
|
79
|
+
if (!saved?.uploadId && !saved?.resourceId) {
|
|
80
|
+
await mkdir(dirname(statePath), { recursive: true, mode: 0o700 });
|
|
81
|
+
const lockPath = `${statePath}.upload-lock`;
|
|
82
|
+
const lock = await open(lockPath, 'wx', 0o600).catch(error => {
|
|
83
|
+
if (error &&
|
|
84
|
+
typeof error === 'object' &&
|
|
85
|
+
'code' in error &&
|
|
86
|
+
error.code === 'EEXIST')
|
|
87
|
+
throw new Error(`An upload for this source is already in progress. Retry the same command after it finishes. If its process was terminated, verify the PID in ${lockPath} is stopped before removing that lock.`);
|
|
88
|
+
throw error;
|
|
89
|
+
});
|
|
90
|
+
try {
|
|
91
|
+
await lock.writeFile(JSON.stringify({ pid: process.pid }));
|
|
92
|
+
// Another process may have completed its upload between our initial read and lock acquisition.
|
|
93
|
+
saved = await readReceipt();
|
|
94
|
+
if (!saved?.uploadId && !saved?.resourceId) {
|
|
95
|
+
saved ??= {
|
|
96
|
+
schemaVersion: 'assethub.canvas-import.v1',
|
|
97
|
+
...scope,
|
|
98
|
+
...(sha256 ? { sha256 } : {}),
|
|
99
|
+
...(name === undefined ? {} : { name }),
|
|
100
|
+
};
|
|
101
|
+
await writeState(statePath, saved);
|
|
102
|
+
if (bytes && options.filePath) {
|
|
103
|
+
const uploaded = await options.client.v2.uploadFile({
|
|
104
|
+
file: new Blob([bytes], {
|
|
105
|
+
type: options.contentType ??
|
|
106
|
+
imageContentTypes[extname(options.filePath).toLowerCase()] ??
|
|
107
|
+
'application/octet-stream',
|
|
108
|
+
}),
|
|
109
|
+
fileName: basename(options.filePath),
|
|
110
|
+
mediaType: 'image',
|
|
111
|
+
});
|
|
112
|
+
if (!uploaded.uploadId)
|
|
113
|
+
throw new Error('Server did not return a durable upload ID');
|
|
114
|
+
saved.uploadId = uploaded.uploadId;
|
|
115
|
+
}
|
|
116
|
+
else if (options.source?.uploadId)
|
|
117
|
+
saved.uploadId = options.source.uploadId;
|
|
118
|
+
else if (options.source?.resourceId)
|
|
119
|
+
saved.resourceId = options.source.resourceId;
|
|
120
|
+
// Never call durable import before its upload identity has been persisted.
|
|
121
|
+
await writeState(statePath, saved);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
await lock.close();
|
|
126
|
+
await unlink(lockPath);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const source = saved?.uploadId
|
|
130
|
+
? { uploadId: saved.uploadId }
|
|
131
|
+
: saved?.resourceId
|
|
132
|
+
? { resourceId: saved.resourceId }
|
|
133
|
+
: (() => {
|
|
134
|
+
throw new Error('Import receipt has no durable source');
|
|
135
|
+
})();
|
|
136
|
+
// Revalidate on the server even after a successful cached result. Promotion
|
|
137
|
+
// recognizes this same upload after staging cleanup and never creates a new asset.
|
|
138
|
+
const result = await options.client.v2.importCanvasAsset(options.canvasId, {
|
|
139
|
+
source,
|
|
140
|
+
name: saved.name,
|
|
141
|
+
});
|
|
142
|
+
if (result.canvasId !== options.canvasId ||
|
|
143
|
+
result.mediaType !== 'image' ||
|
|
144
|
+
!result.assetId ||
|
|
145
|
+
(saved.result && saved.result.assetId !== result.assetId))
|
|
146
|
+
throw new Error(`Server returned an inconsistent imported asset; inspect ${statePath}`);
|
|
147
|
+
await writeState(statePath, { ...saved, result });
|
|
148
|
+
return {
|
|
149
|
+
...result,
|
|
150
|
+
statePath,
|
|
151
|
+
...(sha256 ? { sha256 } : {}),
|
|
152
|
+
...(saved.uploadId ? { uploadId: saved.uploadId } : {}),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AssetHubClient } from '@assethub/api-client';
|
|
2
|
+
/** A portable review artifact; downloading uses signed URLs, never the API key. */
|
|
3
|
+
export declare function writeCanvasComparison(input: {
|
|
4
|
+
client: AssetHubClient;
|
|
5
|
+
canvasId: number;
|
|
6
|
+
sourceId: string;
|
|
7
|
+
assetId: string;
|
|
8
|
+
out: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
canvasId: number;
|
|
12
|
+
canvasUrl: string;
|
|
13
|
+
reviewStatus: string;
|
|
14
|
+
images: {
|
|
15
|
+
assetId: string;
|
|
16
|
+
sha256: string;
|
|
17
|
+
}[];
|
|
18
|
+
path: string;
|
|
19
|
+
provenancePath: string;
|
|
20
|
+
}>;
|
|
21
|
+
//# sourceMappingURL=canvasComparison.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvasComparison.d.ts","sourceRoot":"","sources":["../src/canvasComparison.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,sBAAsB,CAAA;AAWxD,mFAAmF;AACnF,wBAAsB,qBAAqB,CAAC,KAAK,EAAE;IACjD,MAAM,EAAE,cAAc,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;;;;;;;;;;GAuDA"}
|