@meshy-ai/cli 0.1.3 → 0.3.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/.env.example +8 -2
- package/README.md +342 -108
- package/dist/client/endpoints/animation-catalog.d.ts +37 -0
- package/dist/client/endpoints/animation-catalog.d.ts.map +1 -0
- package/dist/client/endpoints/animation-catalog.js +55 -0
- package/dist/client/endpoints/animation-catalog.js.map +1 -0
- package/dist/client/endpoints/balance.d.ts +5 -0
- package/dist/client/endpoints/balance.d.ts.map +1 -1
- package/dist/client/endpoints/balance.js +5 -1
- package/dist/client/endpoints/balance.js.map +1 -1
- package/dist/client/endpoints/base.d.ts +41 -7
- package/dist/client/endpoints/base.d.ts.map +1 -1
- package/dist/client/endpoints/base.js +61 -40
- package/dist/client/endpoints/base.js.map +1 -1
- package/dist/client/endpoints/showcases.d.ts +31 -0
- package/dist/client/endpoints/showcases.d.ts.map +1 -0
- package/dist/client/endpoints/showcases.js +49 -0
- package/dist/client/endpoints/showcases.js.map +1 -0
- package/dist/client/errors.d.ts +1 -0
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +1 -1
- package/dist/client/errors.js.map +1 -1
- package/dist/client/index.d.ts +47 -38
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +89 -120
- package/dist/client/index.js.map +1 -1
- package/dist/client/resource-registry.d.ts +97 -0
- package/dist/client/resource-registry.d.ts.map +1 -0
- package/dist/client/resource-registry.js +157 -0
- package/dist/client/resource-registry.js.map +1 -0
- package/dist/client/transport.d.ts +99 -0
- package/dist/client/transport.d.ts.map +1 -0
- package/dist/client/transport.js +320 -0
- package/dist/client/transport.js.map +1 -0
- package/dist/client/types.d.ts +28 -221
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/types.js +28 -6
- package/dist/client/types.js.map +1 -1
- package/dist/cmd/animation-catalog.d.ts +26 -0
- package/dist/cmd/animation-catalog.d.ts.map +1 -0
- package/dist/cmd/animation-catalog.js +80 -0
- package/dist/cmd/animation-catalog.js.map +1 -0
- package/dist/cmd/api.d.ts +3 -2
- package/dist/cmd/api.d.ts.map +1 -1
- package/dist/cmd/api.js +22 -8
- package/dist/cmd/api.js.map +1 -1
- package/dist/cmd/auth.d.ts.map +1 -1
- package/dist/cmd/auth.js +5 -0
- package/dist/cmd/auth.js.map +1 -1
- package/dist/cmd/balance.d.ts.map +1 -1
- package/dist/cmd/balance.js +10 -6
- package/dist/cmd/balance.js.map +1 -1
- package/dist/cmd/creative-lab.d.ts +26 -0
- package/dist/cmd/creative-lab.d.ts.map +1 -0
- package/dist/cmd/creative-lab.js +256 -0
- package/dist/cmd/creative-lab.js.map +1 -0
- package/dist/cmd/delete.d.ts.map +1 -1
- package/dist/cmd/delete.js +6 -7
- package/dist/cmd/delete.js.map +1 -1
- package/dist/cmd/doctor.d.ts +23 -0
- package/dist/cmd/doctor.d.ts.map +1 -0
- package/dist/cmd/doctor.js +61 -0
- package/dist/cmd/doctor.js.map +1 -0
- package/dist/cmd/download.d.ts +17 -0
- package/dist/cmd/download.d.ts.map +1 -0
- package/dist/cmd/download.js +436 -0
- package/dist/cmd/download.js.map +1 -0
- package/dist/cmd/inspect.d.ts +63 -0
- package/dist/cmd/inspect.d.ts.map +1 -0
- package/dist/cmd/inspect.js +179 -0
- package/dist/cmd/inspect.js.map +1 -0
- package/dist/cmd/make.d.ts +13 -3
- package/dist/cmd/make.d.ts.map +1 -1
- package/dist/cmd/make.js +240 -36
- package/dist/cmd/make.js.map +1 -1
- package/dist/cmd/mesh.d.ts +13 -0
- package/dist/cmd/mesh.d.ts.map +1 -0
- package/dist/cmd/mesh.js +67 -0
- package/dist/cmd/mesh.js.map +1 -0
- package/dist/cmd/project.d.ts +16 -0
- package/dist/cmd/project.d.ts.map +1 -0
- package/dist/cmd/project.js +136 -0
- package/dist/cmd/project.js.map +1 -0
- package/dist/cmd/resources.d.ts +5 -0
- package/dist/cmd/resources.d.ts.map +1 -1
- package/dist/cmd/resources.js +38 -10
- package/dist/cmd/resources.js.map +1 -1
- package/dist/cmd/showcases.d.ts +10 -0
- package/dist/cmd/showcases.d.ts.map +1 -0
- package/dist/cmd/showcases.js +63 -0
- package/dist/cmd/showcases.js.map +1 -0
- package/dist/cmd/slicer.d.ts +12 -0
- package/dist/cmd/slicer.d.ts.map +1 -0
- package/dist/cmd/slicer.js +46 -0
- package/dist/cmd/slicer.js.map +1 -0
- package/dist/cmd/uv-unwrap.d.ts +16 -0
- package/dist/cmd/uv-unwrap.d.ts.map +1 -0
- package/dist/cmd/uv-unwrap.js +52 -0
- package/dist/cmd/uv-unwrap.js.map +1 -0
- package/dist/index.d.ts +13 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -8
- package/dist/index.js.map +1 -1
- package/dist/internal/artifacts.d.ts +97 -0
- package/dist/internal/artifacts.d.ts.map +1 -0
- package/dist/internal/artifacts.js +280 -0
- package/dist/internal/artifacts.js.map +1 -0
- package/dist/internal/atomic-file.d.ts +39 -0
- package/dist/internal/atomic-file.d.ts.map +1 -0
- package/dist/internal/atomic-file.js +160 -0
- package/dist/internal/atomic-file.js.map +1 -0
- package/dist/internal/command-helpers.d.ts +46 -0
- package/dist/internal/command-helpers.d.ts.map +1 -0
- package/dist/internal/command-helpers.js +61 -0
- package/dist/internal/command-helpers.js.map +1 -0
- package/dist/internal/config.d.ts +48 -5
- package/dist/internal/config.d.ts.map +1 -1
- package/dist/internal/config.js +95 -6
- package/dist/internal/config.js.map +1 -1
- package/dist/internal/context.d.ts +25 -0
- package/dist/internal/context.d.ts.map +1 -0
- package/dist/internal/context.js +37 -0
- package/dist/internal/context.js.map +1 -0
- package/dist/internal/credentials.d.ts +11 -0
- package/dist/internal/credentials.d.ts.map +1 -1
- package/dist/internal/credentials.js +2 -0
- package/dist/internal/credentials.js.map +1 -1
- package/dist/internal/device-state.js.map +1 -1
- package/dist/internal/device.d.ts.map +1 -1
- package/dist/internal/doctor.d.ts +94 -0
- package/dist/internal/doctor.d.ts.map +1 -0
- package/dist/internal/doctor.js +280 -0
- package/dist/internal/doctor.js.map +1 -0
- package/dist/internal/download.d.ts +147 -13
- package/dist/internal/download.d.ts.map +1 -1
- package/dist/internal/download.js +735 -142
- package/dist/internal/download.js.map +1 -1
- package/dist/internal/env-file.d.ts +29 -0
- package/dist/internal/env-file.d.ts.map +1 -0
- package/dist/internal/env-file.js +95 -0
- package/dist/internal/env-file.js.map +1 -0
- package/dist/internal/errors.d.ts +74 -1
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +173 -1
- package/dist/internal/errors.js.map +1 -1
- package/dist/internal/file-input.d.ts +52 -12
- package/dist/internal/file-input.d.ts.map +1 -1
- package/dist/internal/file-input.js +162 -33
- package/dist/internal/file-input.js.map +1 -1
- package/dist/internal/flags.js.map +1 -1
- package/dist/internal/global-options.d.ts +6 -4
- package/dist/internal/global-options.d.ts.map +1 -1
- package/dist/internal/global-options.js +19 -4
- package/dist/internal/global-options.js.map +1 -1
- package/dist/internal/headless.js.map +1 -1
- package/dist/internal/image-models.d.ts.map +1 -1
- package/dist/internal/inspect.d.ts +67 -0
- package/dist/internal/inspect.d.ts.map +1 -0
- package/dist/internal/inspect.js +111 -0
- package/dist/internal/inspect.js.map +1 -0
- package/dist/internal/lock.d.ts +19 -0
- package/dist/internal/lock.d.ts.map +1 -0
- package/dist/internal/lock.js +66 -0
- package/dist/internal/lock.js.map +1 -0
- package/dist/internal/logger.d.ts.map +1 -1
- package/dist/internal/material-links.d.ts +115 -0
- package/dist/internal/material-links.d.ts.map +1 -0
- package/dist/internal/material-links.js +577 -0
- package/dist/internal/material-links.js.map +1 -0
- package/dist/internal/obj-transform.d.ts +115 -0
- package/dist/internal/obj-transform.d.ts.map +1 -0
- package/dist/internal/obj-transform.js +649 -0
- package/dist/internal/obj-transform.js.map +1 -0
- package/dist/internal/operation-store.d.ts +123 -0
- package/dist/internal/operation-store.d.ts.map +1 -0
- package/dist/internal/operation-store.js +229 -0
- package/dist/internal/operation-store.js.map +1 -0
- package/dist/internal/output.d.ts +20 -2
- package/dist/internal/output.d.ts.map +1 -1
- package/dist/internal/output.js +32 -4
- package/dist/internal/output.js.map +1 -1
- package/dist/internal/paths.d.ts +89 -0
- package/dist/internal/paths.d.ts.map +1 -0
- package/dist/internal/paths.js +168 -0
- package/dist/internal/paths.js.map +1 -0
- package/dist/internal/payload.d.ts +15 -0
- package/dist/internal/payload.d.ts.map +1 -1
- package/dist/internal/payload.js +50 -3
- package/dist/internal/payload.js.map +1 -1
- package/dist/internal/poll.d.ts +30 -4
- package/dist/internal/poll.d.ts.map +1 -1
- package/dist/internal/poll.js +99 -16
- package/dist/internal/poll.js.map +1 -1
- package/dist/internal/pricing.d.ts.map +1 -1
- package/dist/internal/project-store.d.ts +197 -0
- package/dist/internal/project-store.d.ts.map +1 -0
- package/dist/internal/project-store.js +467 -0
- package/dist/internal/project-store.js.map +1 -0
- package/dist/internal/result.d.ts +50 -0
- package/dist/internal/result.d.ts.map +1 -0
- package/dist/internal/result.js +41 -0
- package/dist/internal/result.js.map +1 -0
- package/dist/internal/runtime.d.ts +35 -5
- package/dist/internal/runtime.d.ts.map +1 -1
- package/dist/internal/runtime.js +68 -18
- package/dist/internal/runtime.js.map +1 -1
- package/dist/internal/slicers.d.ts +117 -0
- package/dist/internal/slicers.d.ts.map +1 -0
- package/dist/internal/slicers.js +314 -0
- package/dist/internal/slicers.js.map +1 -0
- package/dist/internal/stream.d.ts +58 -0
- package/dist/internal/stream.d.ts.map +1 -0
- package/dist/internal/stream.js +280 -0
- package/dist/internal/stream.js.map +1 -0
- package/dist/internal/task-command.d.ts +128 -11
- package/dist/internal/task-command.d.ts.map +1 -1
- package/dist/internal/task-command.js +991 -111
- package/dist/internal/task-command.js.map +1 -1
- package/dist/internal/task-view.d.ts +56 -0
- package/dist/internal/task-view.d.ts.map +1 -0
- package/dist/internal/task-view.js +95 -0
- package/dist/internal/task-view.js.map +1 -0
- package/dist/internal/update-notifier.d.ts +5 -1
- package/dist/internal/update-notifier.d.ts.map +1 -1
- package/dist/internal/update-notifier.js +7 -3
- package/dist/internal/update-notifier.js.map +1 -1
- package/dist/internal/version.js.map +1 -1
- package/dist/root.d.ts +10 -0
- package/dist/root.d.ts.map +1 -1
- package/dist/root.js +75 -10
- package/dist/root.js.map +1 -1
- package/package.json +11 -10
- package/skills/meshy-cli/SKILL.md +140 -59
- package/dist/client/endpoints/analyze-printability.d.ts +0 -5
- package/dist/client/endpoints/analyze-printability.d.ts.map +0 -1
- package/dist/client/endpoints/analyze-printability.js +0 -7
- package/dist/client/endpoints/analyze-printability.js.map +0 -1
- package/dist/client/endpoints/animate.d.ts +0 -6
- package/dist/client/endpoints/animate.d.ts.map +0 -1
- package/dist/client/endpoints/animate.js +0 -8
- package/dist/client/endpoints/animate.js.map +0 -1
- package/dist/client/endpoints/convert.d.ts +0 -5
- package/dist/client/endpoints/convert.d.ts.map +0 -1
- package/dist/client/endpoints/convert.js +0 -7
- package/dist/client/endpoints/convert.js.map +0 -1
- package/dist/client/endpoints/image-to-3d.d.ts +0 -5
- package/dist/client/endpoints/image-to-3d.d.ts.map +0 -1
- package/dist/client/endpoints/image-to-3d.js +0 -7
- package/dist/client/endpoints/image-to-3d.js.map +0 -1
- package/dist/client/endpoints/image-to-image.d.ts +0 -5
- package/dist/client/endpoints/image-to-image.d.ts.map +0 -1
- package/dist/client/endpoints/image-to-image.js +0 -7
- package/dist/client/endpoints/image-to-image.js.map +0 -1
- package/dist/client/endpoints/multi-color-print.d.ts +0 -5
- package/dist/client/endpoints/multi-color-print.d.ts.map +0 -1
- package/dist/client/endpoints/multi-color-print.js +0 -7
- package/dist/client/endpoints/multi-color-print.js.map +0 -1
- package/dist/client/endpoints/multi-image-to-3d.d.ts +0 -5
- package/dist/client/endpoints/multi-image-to-3d.d.ts.map +0 -1
- package/dist/client/endpoints/multi-image-to-3d.js +0 -7
- package/dist/client/endpoints/multi-image-to-3d.js.map +0 -1
- package/dist/client/endpoints/remesh.d.ts +0 -5
- package/dist/client/endpoints/remesh.d.ts.map +0 -1
- package/dist/client/endpoints/remesh.js +0 -7
- package/dist/client/endpoints/remesh.js.map +0 -1
- package/dist/client/endpoints/repair-printability.d.ts +0 -5
- package/dist/client/endpoints/repair-printability.d.ts.map +0 -1
- package/dist/client/endpoints/repair-printability.js +0 -7
- package/dist/client/endpoints/repair-printability.js.map +0 -1
- package/dist/client/endpoints/resize.d.ts +0 -5
- package/dist/client/endpoints/resize.d.ts.map +0 -1
- package/dist/client/endpoints/resize.js +0 -7
- package/dist/client/endpoints/resize.js.map +0 -1
- package/dist/client/endpoints/retexture.d.ts +0 -5
- package/dist/client/endpoints/retexture.d.ts.map +0 -1
- package/dist/client/endpoints/retexture.js +0 -7
- package/dist/client/endpoints/retexture.js.map +0 -1
- package/dist/client/endpoints/rigging.d.ts +0 -6
- package/dist/client/endpoints/rigging.d.ts.map +0 -1
- package/dist/client/endpoints/rigging.js +0 -8
- package/dist/client/endpoints/rigging.js.map +0 -1
- package/dist/client/endpoints/text-to-3d.d.ts +0 -6
- package/dist/client/endpoints/text-to-3d.d.ts.map +0 -1
- package/dist/client/endpoints/text-to-3d.js +0 -8
- package/dist/client/endpoints/text-to-3d.js.map +0 -1
- package/dist/client/endpoints/text-to-image.d.ts +0 -5
- package/dist/client/endpoints/text-to-image.d.ts.map +0 -1
- package/dist/client/endpoints/text-to-image.js +0 -7
- package/dist/client/endpoints/text-to-image.js.map +0 -1
- package/dist/client/endpoints/text-to-motion.d.ts +0 -6
- package/dist/client/endpoints/text-to-motion.d.ts.map +0 -1
- package/dist/client/endpoints/text-to-motion.js +0 -8
- package/dist/client/endpoints/text-to-motion.js.map +0 -1
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project store — the `meshy_output/` layout the Skills established.
|
|
3
|
+
*
|
|
4
|
+
* <root>/history.json index of projects (rebuildable)
|
|
5
|
+
* <root>/<folder>/metadata.json the facts about one project
|
|
6
|
+
* <root>/<folder>/task_<id>.json task snapshots saved by --project
|
|
7
|
+
* <root>/<folder>/<asset files>
|
|
8
|
+
*
|
|
9
|
+
* metadata.json is the source of truth; history.json is an index derived
|
|
10
|
+
* from it. They are two files, so no cross-file transaction is claimed:
|
|
11
|
+
* `recordTask` commits metadata under the project lock, releases it, then
|
|
12
|
+
* takes the root lock to refresh the index. When the second step fails the
|
|
13
|
+
* caller gets `index: { updated: false }` and `rebuild-index` repairs it.
|
|
14
|
+
* Locks are never nested in the other order.
|
|
15
|
+
*
|
|
16
|
+
* Legacy files written by the Python helper (no schema_version, tasks with
|
|
17
|
+
* only task_id/task_type/stage/files/created_at) are read as v1 and migrated
|
|
18
|
+
* on the first write with a `.bak-<timestamp>` copy; unknown fields survive.
|
|
19
|
+
* The CLI's own download `meta.json` is a third format and never written here.
|
|
20
|
+
*/
|
|
21
|
+
import { type AuthorisedRoot } from "./paths.js";
|
|
22
|
+
export declare const METADATA_SCHEMA_VERSION = 2;
|
|
23
|
+
export declare const HISTORY_VERSION = 1;
|
|
24
|
+
export interface ProjectTaskEntry {
|
|
25
|
+
task_id: string;
|
|
26
|
+
task_type: string | null;
|
|
27
|
+
resource: string | null;
|
|
28
|
+
endpoint: string | null;
|
|
29
|
+
stage: string;
|
|
30
|
+
parent_task_id: string | null;
|
|
31
|
+
status: string | null;
|
|
32
|
+
files: string[];
|
|
33
|
+
task_json: string | null;
|
|
34
|
+
operation_id: string | null;
|
|
35
|
+
created_at: string;
|
|
36
|
+
updated_at: string;
|
|
37
|
+
[extra: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
export interface ProjectMetadata {
|
|
40
|
+
schema_version: number;
|
|
41
|
+
project_name: string;
|
|
42
|
+
folder: string;
|
|
43
|
+
root_task_id: string | null;
|
|
44
|
+
created_at: string;
|
|
45
|
+
updated_at: string;
|
|
46
|
+
tasks: ProjectTaskEntry[];
|
|
47
|
+
[extra: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
export interface HistoryEntry {
|
|
50
|
+
folder: string;
|
|
51
|
+
prompt: string;
|
|
52
|
+
task_type: string;
|
|
53
|
+
root_task_id: string | null;
|
|
54
|
+
created_at: string;
|
|
55
|
+
updated_at: string;
|
|
56
|
+
task_count: number;
|
|
57
|
+
[extra: string]: unknown;
|
|
58
|
+
}
|
|
59
|
+
export interface HistoryFile {
|
|
60
|
+
version: number;
|
|
61
|
+
projects: HistoryEntry[];
|
|
62
|
+
[extra: string]: unknown;
|
|
63
|
+
}
|
|
64
|
+
export interface RecordInput {
|
|
65
|
+
taskId: string;
|
|
66
|
+
stage: string;
|
|
67
|
+
resource?: string | null;
|
|
68
|
+
taskType?: string | null;
|
|
69
|
+
endpoint?: string | null;
|
|
70
|
+
parentTaskId?: string | null;
|
|
71
|
+
status?: string | null;
|
|
72
|
+
files?: string[];
|
|
73
|
+
taskJson?: string | null;
|
|
74
|
+
operationId?: string | null;
|
|
75
|
+
}
|
|
76
|
+
export interface StoreOptions {
|
|
77
|
+
now?: () => Date;
|
|
78
|
+
lockTimeoutMs?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Where a project's history index lives (its parent directory unless the
|
|
82
|
+
* caller named a root) and whether this invocation may write there. With an
|
|
83
|
+
* explicit --workspace the index root must resolve inside it; when it does not
|
|
84
|
+
* (the workspace *is* the project directory), metadata is still recorded and
|
|
85
|
+
* the index is left alone with an explicit reason — nothing is ever written,
|
|
86
|
+
* locked or temp-filed outside the workspace.
|
|
87
|
+
*/
|
|
88
|
+
export declare function indexRootFor(projectDir: string, explicitRoot: string | undefined, workspace: string | AuthorisedRoot | undefined): {
|
|
89
|
+
root: string;
|
|
90
|
+
skipIndex?: string;
|
|
91
|
+
};
|
|
92
|
+
export declare function metadataPath(projectDir: string): string;
|
|
93
|
+
export declare function historyPath(root: string): string;
|
|
94
|
+
/** Folder name: `YYYYMMDD_HHmmss_<slug>_<id-prefix|random>` (legacy shape, collision-safe). */
|
|
95
|
+
export declare function projectFolderName(name: string, taskId: string | null, now: Date, random?: () => string): string;
|
|
96
|
+
/** A metadata/history file path entry must be a plain relative path inside its base. */
|
|
97
|
+
export declare function assertSafeRelativeFile(rel: string, label: string): string;
|
|
98
|
+
/** Normalise any metadata.json (legacy v1 or v2) into the v2 view without touching disk. */
|
|
99
|
+
export declare function normalizeMetadata(raw: unknown, folder: string): {
|
|
100
|
+
metadata: ProjectMetadata;
|
|
101
|
+
legacy: boolean;
|
|
102
|
+
};
|
|
103
|
+
export interface ProjectRead {
|
|
104
|
+
path: string;
|
|
105
|
+
metadata: ProjectMetadata;
|
|
106
|
+
legacy: boolean;
|
|
107
|
+
exists: boolean;
|
|
108
|
+
}
|
|
109
|
+
export declare function readProject(projectDir: string): ProjectRead;
|
|
110
|
+
export interface InitResult {
|
|
111
|
+
root: string;
|
|
112
|
+
project_dir: string;
|
|
113
|
+
folder: string;
|
|
114
|
+
metadata: ProjectMetadata;
|
|
115
|
+
index: {
|
|
116
|
+
updated: boolean;
|
|
117
|
+
error: string | null;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export declare function initProject(root: string, opts?: {
|
|
121
|
+
name?: string;
|
|
122
|
+
taskId?: string | null;
|
|
123
|
+
taskType?: string | null;
|
|
124
|
+
} & StoreOptions): InitResult;
|
|
125
|
+
export interface RecordResult {
|
|
126
|
+
project_dir: string;
|
|
127
|
+
metadata: ProjectMetadata;
|
|
128
|
+
entry: ProjectTaskEntry;
|
|
129
|
+
action: "added" | "merged";
|
|
130
|
+
migrated_from_legacy: boolean;
|
|
131
|
+
index: {
|
|
132
|
+
updated: boolean;
|
|
133
|
+
error: string | null;
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Add or merge a task record. The de-duplication key is (task_id, stage):
|
|
138
|
+
* a repeat merges files (union) and refreshes status/task_json instead of
|
|
139
|
+
* appending a duplicate entry.
|
|
140
|
+
*/
|
|
141
|
+
export declare function recordTask(projectDir: string, input: RecordInput, opts?: StoreOptions & {
|
|
142
|
+
root?: string;
|
|
143
|
+
skipIndex?: string;
|
|
144
|
+
}): RecordResult;
|
|
145
|
+
export interface ListResult {
|
|
146
|
+
root: string;
|
|
147
|
+
history_path: string;
|
|
148
|
+
history_present: boolean;
|
|
149
|
+
projects: Array<HistoryEntry & {
|
|
150
|
+
present: boolean;
|
|
151
|
+
}>;
|
|
152
|
+
unindexed_folders: string[];
|
|
153
|
+
index_dirty: boolean;
|
|
154
|
+
}
|
|
155
|
+
/** Read the index and compare it with the folders that actually carry a metadata.json. */
|
|
156
|
+
export declare function listProjects(root: string): ListResult;
|
|
157
|
+
export interface RebuildResult {
|
|
158
|
+
root: string;
|
|
159
|
+
history_path: string;
|
|
160
|
+
indexed: number;
|
|
161
|
+
skipped: Array<{
|
|
162
|
+
folder: string;
|
|
163
|
+
reason: string;
|
|
164
|
+
}>;
|
|
165
|
+
backup: string | null;
|
|
166
|
+
}
|
|
167
|
+
/** Regenerate history.json from the project folders; a damaged index is backed up, never silently replaced. */
|
|
168
|
+
export declare function rebuildIndex(root: string, opts?: StoreOptions): RebuildResult;
|
|
169
|
+
/** Save a task snapshot as `task_<safe-id>.json` inside the project (overwrites an older snapshot of the same task). */
|
|
170
|
+
export declare function saveTaskSnapshot(projectDir: string, taskId: string, raw: unknown): {
|
|
171
|
+
path: string;
|
|
172
|
+
relative: string;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* A project that was initialised when a command started must still be one when
|
|
176
|
+
* its record is written. metadata.json missing or replaced by something that is
|
|
177
|
+
* not a regular file is a local condition of the project — reported as
|
|
178
|
+
* `local_io`, never as an API "not found" — and the caller says how to redo the
|
|
179
|
+
* record once the project is restored.
|
|
180
|
+
*/
|
|
181
|
+
export declare function assertProjectMetadataPresent(projectDir: string, flag: string): void;
|
|
182
|
+
/**
|
|
183
|
+
* The `meshy project record` invocation that redoes exactly one thing: the
|
|
184
|
+
* metadata entry a command could not write. Assets, task and journal are left
|
|
185
|
+
* alone — the caller runs it once the project directory is repaired. The
|
|
186
|
+
* original write boundary (`--workspace`, resolved to an absolute path) travels
|
|
187
|
+
* with the command: a recovery never reaches further than the invocation that
|
|
188
|
+
* failed, so a workspace equal to the project still leaves the parent's
|
|
189
|
+
* history index alone (`index_dirty`), exactly as the original would have.
|
|
190
|
+
*/
|
|
191
|
+
export declare function projectRecordCommand(projectDir: string, input: RecordInput, opts?: {
|
|
192
|
+
root?: string;
|
|
193
|
+
workspace?: string;
|
|
194
|
+
}): string;
|
|
195
|
+
/** Derive a stage name from a task type (`text-to-3d-preview` → preview, `creative-lab-lamp-build` → build). */
|
|
196
|
+
export declare function stageFromTaskType(type: unknown, fallback: string): string;
|
|
197
|
+
//# sourceMappingURL=project-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-store.d.ts","sourceRoot":"","sources":["../../src/internal/project-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project store — the `meshy_output/` layout the Skills established.
|
|
3
|
+
*
|
|
4
|
+
* <root>/history.json index of projects (rebuildable)
|
|
5
|
+
* <root>/<folder>/metadata.json the facts about one project
|
|
6
|
+
* <root>/<folder>/task_<id>.json task snapshots saved by --project
|
|
7
|
+
* <root>/<folder>/<asset files>
|
|
8
|
+
*
|
|
9
|
+
* metadata.json is the source of truth; history.json is an index derived
|
|
10
|
+
* from it. They are two files, so no cross-file transaction is claimed:
|
|
11
|
+
* `recordTask` commits metadata under the project lock, releases it, then
|
|
12
|
+
* takes the root lock to refresh the index. When the second step fails the
|
|
13
|
+
* caller gets `index: { updated: false }` and `rebuild-index` repairs it.
|
|
14
|
+
* Locks are never nested in the other order.
|
|
15
|
+
*
|
|
16
|
+
* Legacy files written by the Python helper (no schema_version, tasks with
|
|
17
|
+
* only task_id/task_type/stage/files/created_at) are read as v1 and migrated
|
|
18
|
+
* on the first write with a `.bak-<timestamp>` copy; unknown fields survive.
|
|
19
|
+
* The CLI's own download `meta.json` is a third format and never written here.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, statSync, copyFileSync } from "node:fs";
|
|
22
|
+
import { basename, join, relative, resolve as resolvePath, sep } from "node:path";
|
|
23
|
+
import { randomBytes } from "node:crypto";
|
|
24
|
+
import { writeJsonFile } from "./atomic-file.js";
|
|
25
|
+
import { CliError, UsageError } from "./errors.js";
|
|
26
|
+
import { withFileLock } from "./lock.js";
|
|
27
|
+
import { realpathLenient, resolveWithinRoot, safeSegment } from "./paths.js";
|
|
28
|
+
export const METADATA_SCHEMA_VERSION = 2;
|
|
29
|
+
export const HISTORY_VERSION = 1;
|
|
30
|
+
/**
|
|
31
|
+
* Where a project's history index lives (its parent directory unless the
|
|
32
|
+
* caller named a root) and whether this invocation may write there. With an
|
|
33
|
+
* explicit --workspace the index root must resolve inside it; when it does not
|
|
34
|
+
* (the workspace *is* the project directory), metadata is still recorded and
|
|
35
|
+
* the index is left alone with an explicit reason — nothing is ever written,
|
|
36
|
+
* locked or temp-filed outside the workspace.
|
|
37
|
+
*/
|
|
38
|
+
export function indexRootFor(projectDir, explicitRoot, workspace) {
|
|
39
|
+
const root = explicitRoot !== undefined ? resolvePath(explicitRoot) : resolvePath(projectDir, "..");
|
|
40
|
+
if (!workspace)
|
|
41
|
+
return { root };
|
|
42
|
+
const workspacePath = typeof workspace === "string" ? resolvePath(workspace) : workspace.given;
|
|
43
|
+
try {
|
|
44
|
+
resolveWithinRoot(root, workspace, { label: "history root" });
|
|
45
|
+
return { root };
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
return { root, skipIndex: `history root ${root} resolves outside --workspace ${workspacePath}; metadata.json was recorded but history.json was not touched (${err instanceof Error ? err.message : String(err)}) — run \`meshy project rebuild-index --root ${root}\` from a workspace that contains it` };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const ISO = (d) => d.toISOString();
|
|
52
|
+
function projectLock(projectDir) {
|
|
53
|
+
return join(projectDir, ".meshy.lock");
|
|
54
|
+
}
|
|
55
|
+
function rootLock(root) {
|
|
56
|
+
return join(root, ".meshy-history.lock");
|
|
57
|
+
}
|
|
58
|
+
export function metadataPath(projectDir) {
|
|
59
|
+
return join(projectDir, "metadata.json");
|
|
60
|
+
}
|
|
61
|
+
export function historyPath(root) {
|
|
62
|
+
return join(root, "history.json");
|
|
63
|
+
}
|
|
64
|
+
/** Folder name: `YYYYMMDD_HHmmss_<slug>_<id-prefix|random>` (legacy shape, collision-safe). */
|
|
65
|
+
export function projectFolderName(name, taskId, now, random = () => randomBytes(2).toString("hex")) {
|
|
66
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
67
|
+
const stamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
|
|
68
|
+
let slug = name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 30).replace(/-+$/g, "");
|
|
69
|
+
if (!slug)
|
|
70
|
+
slug = "project";
|
|
71
|
+
const suffix = taskId ? safeSegment(taskId).slice(0, 8) : random();
|
|
72
|
+
return safeSegment(`${stamp}_${slug}_${suffix}`, "project");
|
|
73
|
+
}
|
|
74
|
+
/** A metadata/history file path entry must be a plain relative path inside its base. */
|
|
75
|
+
export function assertSafeRelativeFile(rel, label) {
|
|
76
|
+
if (!rel || rel.includes("\0"))
|
|
77
|
+
throw new UsageError(`${label}: empty or invalid path`);
|
|
78
|
+
const normalized = rel.split(/[\\/]/);
|
|
79
|
+
if (normalized.some((s) => s === ".." || s === ""))
|
|
80
|
+
throw new UsageError(`${label}: '${rel}' must be a relative path inside the project (no '..', no absolute path)`);
|
|
81
|
+
if (/^[A-Za-z]:/.test(rel) || rel.startsWith("/") || rel.startsWith("\\"))
|
|
82
|
+
throw new UsageError(`${label}: '${rel}' must be relative to the project directory`);
|
|
83
|
+
return normalized.join("/");
|
|
84
|
+
}
|
|
85
|
+
function readJsonFile(path) {
|
|
86
|
+
let text;
|
|
87
|
+
try {
|
|
88
|
+
text = readFileSync(path, "utf8");
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
if (err.code === "ENOENT")
|
|
92
|
+
return undefined;
|
|
93
|
+
throw new CliError({ code: "local_io", message: `cannot read ${path}: ${err.message}` });
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
return JSON.parse(text);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
throw new CliError({ code: "local_io", message: `${path} is not valid JSON (${err.message}); refusing to overwrite a damaged file — repair or move it first` });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/** Normalise any metadata.json (legacy v1 or v2) into the v2 view without touching disk. */
|
|
103
|
+
export function normalizeMetadata(raw, folder) {
|
|
104
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
105
|
+
throw new CliError({ code: "local_io", message: `metadata.json in ${folder} is not a JSON object` });
|
|
106
|
+
}
|
|
107
|
+
const o = raw;
|
|
108
|
+
const legacy = typeof o["schema_version"] !== "number";
|
|
109
|
+
const tasksRaw = Array.isArray(o["tasks"]) ? o["tasks"] : [];
|
|
110
|
+
const tasks = tasksRaw
|
|
111
|
+
.filter((t) => Boolean(t) && typeof t === "object" && !Array.isArray(t))
|
|
112
|
+
.map((t) => {
|
|
113
|
+
const created = typeof t["created_at"] === "string" ? t["created_at"] : "";
|
|
114
|
+
return {
|
|
115
|
+
...t,
|
|
116
|
+
task_id: String(t["task_id"] ?? ""),
|
|
117
|
+
task_type: typeof t["task_type"] === "string" ? t["task_type"] : null,
|
|
118
|
+
resource: typeof t["resource"] === "string" ? t["resource"] : null,
|
|
119
|
+
endpoint: typeof t["endpoint"] === "string" ? t["endpoint"] : null,
|
|
120
|
+
stage: typeof t["stage"] === "string" ? t["stage"] : "unknown",
|
|
121
|
+
parent_task_id: typeof t["parent_task_id"] === "string" ? t["parent_task_id"] : null,
|
|
122
|
+
status: typeof t["status"] === "string" ? t["status"] : null,
|
|
123
|
+
files: Array.isArray(t["files"]) ? t["files"].filter((f) => typeof f === "string") : [],
|
|
124
|
+
task_json: typeof t["task_json"] === "string" ? t["task_json"] : null,
|
|
125
|
+
operation_id: typeof t["operation_id"] === "string" ? t["operation_id"] : null,
|
|
126
|
+
created_at: created,
|
|
127
|
+
updated_at: typeof t["updated_at"] === "string" ? t["updated_at"] : created,
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
const metadata = {
|
|
131
|
+
...o,
|
|
132
|
+
schema_version: legacy ? METADATA_SCHEMA_VERSION : o["schema_version"],
|
|
133
|
+
project_name: typeof o["project_name"] === "string" ? o["project_name"] : folder,
|
|
134
|
+
folder: typeof o["folder"] === "string" ? o["folder"] : folder,
|
|
135
|
+
root_task_id: typeof o["root_task_id"] === "string" ? o["root_task_id"] : null,
|
|
136
|
+
created_at: typeof o["created_at"] === "string" ? o["created_at"] : "",
|
|
137
|
+
updated_at: typeof o["updated_at"] === "string" ? o["updated_at"] : "",
|
|
138
|
+
tasks,
|
|
139
|
+
};
|
|
140
|
+
return { metadata, legacy };
|
|
141
|
+
}
|
|
142
|
+
export function readProject(projectDir) {
|
|
143
|
+
const dir = resolvePath(projectDir);
|
|
144
|
+
const raw = readJsonFile(metadataPath(dir));
|
|
145
|
+
if (raw === undefined) {
|
|
146
|
+
throw new CliError({ code: "not_found", message: `${dir} has no metadata.json; run \`meshy project init\` or pass an existing project directory` });
|
|
147
|
+
}
|
|
148
|
+
const { metadata, legacy } = normalizeMetadata(raw, basename(dir));
|
|
149
|
+
return { path: dir, metadata, legacy, exists: true };
|
|
150
|
+
}
|
|
151
|
+
function writeMetadata(dir, metadata, wasLegacy, now) {
|
|
152
|
+
const target = metadataPath(dir);
|
|
153
|
+
if (wasLegacy && existsSync(target)) {
|
|
154
|
+
const backup = `${target}.bak-${ISO(now).replace(/[:.]/g, "-")}`;
|
|
155
|
+
copyFileSync(target, backup);
|
|
156
|
+
}
|
|
157
|
+
writeJsonFile(target, metadata, { overwrite: true, mode: 0o644 });
|
|
158
|
+
}
|
|
159
|
+
export function initProject(root, opts = {}) {
|
|
160
|
+
const now = (opts.now ?? (() => new Date()))();
|
|
161
|
+
const rootAbs = resolvePath(root);
|
|
162
|
+
mkdirSync(rootAbs, { recursive: true });
|
|
163
|
+
const name = (opts.name ?? opts.taskType ?? "model").trim() || "model";
|
|
164
|
+
let folder = projectFolderName(name, opts.taskId ?? null, now);
|
|
165
|
+
let dir = join(rootAbs, folder);
|
|
166
|
+
while (existsSync(dir)) {
|
|
167
|
+
folder = safeSegment(`${folder}-${randomBytes(2).toString("hex")}`);
|
|
168
|
+
dir = join(rootAbs, folder);
|
|
169
|
+
}
|
|
170
|
+
resolveWithinRoot(dir, rootAbs, { label: "project directory" });
|
|
171
|
+
mkdirSync(dir, { recursive: false });
|
|
172
|
+
const metadata = {
|
|
173
|
+
schema_version: METADATA_SCHEMA_VERSION,
|
|
174
|
+
project_name: name,
|
|
175
|
+
folder,
|
|
176
|
+
root_task_id: opts.taskId ?? null,
|
|
177
|
+
created_at: ISO(now),
|
|
178
|
+
updated_at: ISO(now),
|
|
179
|
+
tasks: [],
|
|
180
|
+
};
|
|
181
|
+
withFileLock(projectLock(dir), () => writeMetadata(dir, metadata, false, now), { timeoutMs: opts.lockTimeoutMs });
|
|
182
|
+
const index = refreshIndexEntry(rootAbs, dir, metadata, opts);
|
|
183
|
+
return { root: rootAbs, project_dir: dir, folder, metadata, index };
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Add or merge a task record. The de-duplication key is (task_id, stage):
|
|
187
|
+
* a repeat merges files (union) and refreshes status/task_json instead of
|
|
188
|
+
* appending a duplicate entry.
|
|
189
|
+
*/
|
|
190
|
+
export function recordTask(projectDir, input, opts = {}) {
|
|
191
|
+
const now = (opts.now ?? (() => new Date()))();
|
|
192
|
+
const dir = resolvePath(projectDir);
|
|
193
|
+
if (!input.taskId)
|
|
194
|
+
throw new UsageError("--task-id is required");
|
|
195
|
+
if (!input.stage)
|
|
196
|
+
throw new UsageError("--stage is required");
|
|
197
|
+
const files = (input.files ?? []).map((f) => assertSafeRelativeFile(f, "--file"));
|
|
198
|
+
if (input.taskJson)
|
|
199
|
+
assertSafeRelativeFile(input.taskJson, "task_json");
|
|
200
|
+
const result = withFileLock(projectLock(dir), () => {
|
|
201
|
+
const raw = readJsonFile(metadataPath(dir));
|
|
202
|
+
if (raw === undefined) {
|
|
203
|
+
throw new CliError({ code: "not_found", message: `${dir} has no metadata.json; run \`meshy project init\` first` });
|
|
204
|
+
}
|
|
205
|
+
const { metadata, legacy } = normalizeMetadata(raw, basename(dir));
|
|
206
|
+
const existing = metadata.tasks.find((t) => t.task_id === input.taskId && t.stage === input.stage);
|
|
207
|
+
let entry;
|
|
208
|
+
let action;
|
|
209
|
+
if (existing) {
|
|
210
|
+
action = "merged";
|
|
211
|
+
existing.files = [...new Set([...existing.files, ...files])];
|
|
212
|
+
if (input.status !== undefined && input.status !== null)
|
|
213
|
+
existing.status = input.status;
|
|
214
|
+
if (input.taskJson)
|
|
215
|
+
existing.task_json = input.taskJson;
|
|
216
|
+
if (input.resource)
|
|
217
|
+
existing.resource = input.resource;
|
|
218
|
+
if (input.taskType)
|
|
219
|
+
existing.task_type = input.taskType;
|
|
220
|
+
if (input.endpoint)
|
|
221
|
+
existing.endpoint = input.endpoint;
|
|
222
|
+
if (input.parentTaskId)
|
|
223
|
+
existing.parent_task_id = input.parentTaskId;
|
|
224
|
+
if (input.operationId)
|
|
225
|
+
existing.operation_id = input.operationId;
|
|
226
|
+
existing.updated_at = ISO(now);
|
|
227
|
+
entry = existing;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
action = "added";
|
|
231
|
+
entry = {
|
|
232
|
+
task_id: input.taskId,
|
|
233
|
+
task_type: input.taskType ?? input.resource ?? null,
|
|
234
|
+
resource: input.resource ?? null,
|
|
235
|
+
endpoint: input.endpoint ?? null,
|
|
236
|
+
stage: input.stage,
|
|
237
|
+
parent_task_id: input.parentTaskId ?? null,
|
|
238
|
+
status: input.status ?? null,
|
|
239
|
+
files,
|
|
240
|
+
task_json: input.taskJson ?? null,
|
|
241
|
+
operation_id: input.operationId ?? null,
|
|
242
|
+
created_at: ISO(now),
|
|
243
|
+
updated_at: ISO(now),
|
|
244
|
+
};
|
|
245
|
+
metadata.tasks.push(entry);
|
|
246
|
+
}
|
|
247
|
+
if (!metadata.root_task_id)
|
|
248
|
+
metadata.root_task_id = input.taskId;
|
|
249
|
+
metadata.updated_at = ISO(now);
|
|
250
|
+
writeMetadata(dir, metadata, legacy, now);
|
|
251
|
+
return { metadata, entry, action, legacy };
|
|
252
|
+
}, { timeoutMs: opts.lockTimeoutMs });
|
|
253
|
+
const root = opts.root ? resolvePath(opts.root) : resolvePath(dir, "..");
|
|
254
|
+
const index = opts.skipIndex ? { updated: false, error: opts.skipIndex } : refreshIndexEntry(root, dir, result.metadata, opts);
|
|
255
|
+
return { project_dir: dir, metadata: result.metadata, entry: result.entry, action: result.action, migrated_from_legacy: result.legacy, index };
|
|
256
|
+
}
|
|
257
|
+
function historyEntryFor(metadata, folder) {
|
|
258
|
+
const first = metadata.tasks[0];
|
|
259
|
+
return {
|
|
260
|
+
folder,
|
|
261
|
+
prompt: metadata.project_name,
|
|
262
|
+
task_type: first?.task_type ?? first?.resource ?? "",
|
|
263
|
+
root_task_id: metadata.root_task_id,
|
|
264
|
+
created_at: metadata.created_at,
|
|
265
|
+
updated_at: metadata.updated_at,
|
|
266
|
+
task_count: metadata.tasks.length,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
/** Second phase of a record: refresh this project's row in history.json under the root lock. Never throws. */
|
|
270
|
+
function refreshIndexEntry(root, projectDir, metadata, opts) {
|
|
271
|
+
try {
|
|
272
|
+
const folder = basename(projectDir);
|
|
273
|
+
withFileLock(rootLock(root), () => {
|
|
274
|
+
const raw = readJsonFile(historyPath(root));
|
|
275
|
+
let history;
|
|
276
|
+
if (raw === undefined)
|
|
277
|
+
history = { version: HISTORY_VERSION, projects: [] };
|
|
278
|
+
else if (raw && typeof raw === "object" && !Array.isArray(raw) && Array.isArray(raw.projects))
|
|
279
|
+
history = raw;
|
|
280
|
+
else
|
|
281
|
+
throw new CliError({ code: "local_io", message: `${historyPath(root)} is not a history index ({version, projects[]}); refusing to overwrite it` });
|
|
282
|
+
const entry = historyEntryFor(metadata, folder);
|
|
283
|
+
const idx = history.projects.findIndex((p) => p.folder === folder);
|
|
284
|
+
if (idx === -1)
|
|
285
|
+
history.projects.push(entry);
|
|
286
|
+
else
|
|
287
|
+
history.projects[idx] = { ...history.projects[idx], ...entry };
|
|
288
|
+
writeJsonFile(historyPath(root), history, { overwrite: true, mode: 0o644 });
|
|
289
|
+
}, { timeoutMs: opts.lockTimeoutMs });
|
|
290
|
+
return { updated: true, error: null };
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
return { updated: false, error: err instanceof Error ? err.message : String(err) };
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** Read the index and compare it with the folders that actually carry a metadata.json. */
|
|
297
|
+
export function listProjects(root) {
|
|
298
|
+
const rootAbs = resolvePath(root);
|
|
299
|
+
const raw = readJsonFile(historyPath(rootAbs));
|
|
300
|
+
let history = null;
|
|
301
|
+
if (raw !== undefined) {
|
|
302
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw) || !Array.isArray(raw.projects)) {
|
|
303
|
+
throw new CliError({ code: "local_io", message: `${historyPath(rootAbs)} is not a history index; run \`meshy project rebuild-index\` after moving the damaged file aside` });
|
|
304
|
+
}
|
|
305
|
+
history = raw;
|
|
306
|
+
}
|
|
307
|
+
const folders = existsSync(rootAbs)
|
|
308
|
+
? readdirSync(rootAbs).filter((f) => {
|
|
309
|
+
try {
|
|
310
|
+
return statSync(join(rootAbs, f)).isDirectory() && existsSync(metadataPath(join(rootAbs, f)));
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
})
|
|
316
|
+
: [];
|
|
317
|
+
const indexed = new Set();
|
|
318
|
+
const projects = (history?.projects ?? []).map((p) => {
|
|
319
|
+
const safe = typeof p.folder === "string" && !/[\\/]/.test(p.folder) && p.folder !== ".." && p.folder !== ".";
|
|
320
|
+
const present = safe && existsSync(metadataPath(join(rootAbs, p.folder)));
|
|
321
|
+
if (safe)
|
|
322
|
+
indexed.add(p.folder);
|
|
323
|
+
return { ...p, present };
|
|
324
|
+
});
|
|
325
|
+
const unindexed = folders.filter((f) => !indexed.has(f));
|
|
326
|
+
return {
|
|
327
|
+
root: rootAbs,
|
|
328
|
+
history_path: historyPath(rootAbs),
|
|
329
|
+
history_present: history !== null,
|
|
330
|
+
projects,
|
|
331
|
+
unindexed_folders: unindexed,
|
|
332
|
+
index_dirty: unindexed.length > 0 || projects.some((p) => !p.present),
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
/** Regenerate history.json from the project folders; a damaged index is backed up, never silently replaced. */
|
|
336
|
+
export function rebuildIndex(root, opts = {}) {
|
|
337
|
+
const now = (opts.now ?? (() => new Date()))();
|
|
338
|
+
const rootAbs = resolvePath(root);
|
|
339
|
+
if (!existsSync(rootAbs))
|
|
340
|
+
throw new CliError({ code: "not_found", message: `${rootAbs} does not exist` });
|
|
341
|
+
const skipped = [];
|
|
342
|
+
const entries = [];
|
|
343
|
+
const rootReal = realpathLenient(rootAbs);
|
|
344
|
+
for (const f of readdirSync(rootAbs).sort()) {
|
|
345
|
+
const dir = join(rootAbs, f);
|
|
346
|
+
try {
|
|
347
|
+
if (!statSync(dir).isDirectory())
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!existsSync(metadataPath(dir)))
|
|
354
|
+
continue;
|
|
355
|
+
try {
|
|
356
|
+
resolveWithinRoot(dir, rootReal, { label: "project folder", allowSymlinkLeaf: false });
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
skipped.push({ folder: f, reason: err instanceof Error ? err.message : String(err) });
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
362
|
+
try {
|
|
363
|
+
const raw = readJsonFile(metadataPath(dir));
|
|
364
|
+
const { metadata } = normalizeMetadata(raw, f);
|
|
365
|
+
entries.push(historyEntryFor(metadata, f));
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
skipped.push({ folder: f, reason: err instanceof Error ? err.message : String(err) });
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
entries.sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
372
|
+
let backup = null;
|
|
373
|
+
withFileLock(rootLock(rootAbs), () => {
|
|
374
|
+
const hp = historyPath(rootAbs);
|
|
375
|
+
if (existsSync(hp)) {
|
|
376
|
+
backup = `${hp}.bak-${ISO(now).replace(/[:.]/g, "-")}`;
|
|
377
|
+
copyFileSync(hp, backup);
|
|
378
|
+
}
|
|
379
|
+
writeJsonFile(hp, { version: HISTORY_VERSION, projects: entries }, { overwrite: true, mode: 0o644 });
|
|
380
|
+
}, { timeoutMs: opts.lockTimeoutMs });
|
|
381
|
+
return { root: rootAbs, history_path: historyPath(rootAbs), indexed: entries.length, skipped, backup };
|
|
382
|
+
}
|
|
383
|
+
/** Save a task snapshot as `task_<safe-id>.json` inside the project (overwrites an older snapshot of the same task). */
|
|
384
|
+
export function saveTaskSnapshot(projectDir, taskId, raw) {
|
|
385
|
+
const dir = resolvePath(projectDir);
|
|
386
|
+
const name = `task_${safeSegment(taskId, "task")}.json`;
|
|
387
|
+
// Both sides of the relative path come from the same (real-path) frame, so a
|
|
388
|
+
// symlinked temp dir (macOS /var → /private/var) cannot turn "task_x.json"
|
|
389
|
+
// into a ../.. path that the metadata store then rightly refuses.
|
|
390
|
+
const resolved = resolveWithinRoot(join(dir, name), dir, { label: "task snapshot" });
|
|
391
|
+
writeJsonFile(resolved.path, raw, { overwrite: true, mode: 0o600 });
|
|
392
|
+
return { path: resolved.path, relative: relative(resolved.root, resolved.path).split(sep).join("/") };
|
|
393
|
+
}
|
|
394
|
+
/** Quote one argument for a command a human can paste into a shell; plain tokens stay bare. */
|
|
395
|
+
function shellArg(value) {
|
|
396
|
+
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* A project that was initialised when a command started must still be one when
|
|
400
|
+
* its record is written. metadata.json missing or replaced by something that is
|
|
401
|
+
* not a regular file is a local condition of the project — reported as
|
|
402
|
+
* `local_io`, never as an API "not found" — and the caller says how to redo the
|
|
403
|
+
* record once the project is restored.
|
|
404
|
+
*/
|
|
405
|
+
export function assertProjectMetadataPresent(projectDir, flag) {
|
|
406
|
+
const target = metadataPath(projectDir);
|
|
407
|
+
let st;
|
|
408
|
+
try {
|
|
409
|
+
st = lstatSync(target);
|
|
410
|
+
}
|
|
411
|
+
catch (err) {
|
|
412
|
+
const code = err.code;
|
|
413
|
+
if (code === "ENOENT" || code === "ENOTDIR") {
|
|
414
|
+
throw new CliError({
|
|
415
|
+
code: "local_io",
|
|
416
|
+
message: `--project ${flag} has no metadata.json any more (it was an initialised project when this command started)`,
|
|
417
|
+
cause: err,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
throw err;
|
|
421
|
+
}
|
|
422
|
+
if (!st.isFile()) {
|
|
423
|
+
throw new CliError({
|
|
424
|
+
code: "local_io",
|
|
425
|
+
message: `--project ${flag}: metadata.json is not a regular file (${st.isSymbolicLink() ? "a symbolic link" : st.isDirectory() ? "a directory" : "special"}); refusing to write through it`,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* The `meshy project record` invocation that redoes exactly one thing: the
|
|
431
|
+
* metadata entry a command could not write. Assets, task and journal are left
|
|
432
|
+
* alone — the caller runs it once the project directory is repaired. The
|
|
433
|
+
* original write boundary (`--workspace`, resolved to an absolute path) travels
|
|
434
|
+
* with the command: a recovery never reaches further than the invocation that
|
|
435
|
+
* failed, so a workspace equal to the project still leaves the parent's
|
|
436
|
+
* history index alone (`index_dirty`), exactly as the original would have.
|
|
437
|
+
*/
|
|
438
|
+
export function projectRecordCommand(projectDir, input, opts = {}) {
|
|
439
|
+
const parts = ["meshy", "project", "record", "--project", shellArg(projectDir), "--task-id", shellArg(input.taskId), "--stage", shellArg(input.stage)];
|
|
440
|
+
if (input.resource)
|
|
441
|
+
parts.push("--resource", shellArg(input.resource));
|
|
442
|
+
if (input.taskType)
|
|
443
|
+
parts.push("--task-type", shellArg(input.taskType));
|
|
444
|
+
if (input.parentTaskId)
|
|
445
|
+
parts.push("--parent-task-id", shellArg(input.parentTaskId));
|
|
446
|
+
if (input.status)
|
|
447
|
+
parts.push("--status", shellArg(input.status));
|
|
448
|
+
for (const f of input.files ?? [])
|
|
449
|
+
parts.push("--file", shellArg(f));
|
|
450
|
+
if (input.taskJson)
|
|
451
|
+
parts.push("--task-json", shellArg(input.taskJson));
|
|
452
|
+
if (input.operationId)
|
|
453
|
+
parts.push("--operation-id", shellArg(input.operationId));
|
|
454
|
+
if (opts.root)
|
|
455
|
+
parts.push("--root", shellArg(opts.root));
|
|
456
|
+
if (opts.workspace)
|
|
457
|
+
parts.push("--workspace", shellArg(resolvePath(opts.workspace)));
|
|
458
|
+
return parts.join(" ");
|
|
459
|
+
}
|
|
460
|
+
/** Derive a stage name from a task type (`text-to-3d-preview` → preview, `creative-lab-lamp-build` → build). */
|
|
461
|
+
export function stageFromTaskType(type, fallback) {
|
|
462
|
+
if (typeof type !== "string" || !type)
|
|
463
|
+
return fallback;
|
|
464
|
+
const m = /-(preview|refine|prototype|build)$/.exec(type);
|
|
465
|
+
return m ? m[1] : fallback;
|
|
466
|
+
}
|
|
467
|
+
//# sourceMappingURL=project-store.js.map
|