mlclaw 0.1.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.
Files changed (47) hide show
  1. package/.agents/skills/mlclaw/SKILL.md +214 -0
  2. package/.agents/skills/mlclaw/agents/openai.yaml +4 -0
  3. package/.gitattributes +35 -0
  4. package/Dockerfile +45 -0
  5. package/LICENSE +21 -0
  6. package/README.md +206 -0
  7. package/assets/mlclaw.svg +143 -0
  8. package/dist/hf-state-sync.js +9532 -0
  9. package/dist/mlclaw-space-runtime.js +5010 -0
  10. package/dist/mlclaw.mjs +16502 -0
  11. package/entrypoint.sh +87 -0
  12. package/mlclaw.ps1 +108 -0
  13. package/mlclaw.sh +117 -0
  14. package/openclaw.default.json +67 -0
  15. package/package.json +66 -0
  16. package/scripts/configure-huggingface-model.mjs +86 -0
  17. package/scripts/configure-telegram.mjs +55 -0
  18. package/scripts/report-telegram-probe.mjs +18 -0
  19. package/space/README.md +42 -0
  20. package/src/hf-bucket-client/client.ts +217 -0
  21. package/src/hf-state-sync/archive.ts +137 -0
  22. package/src/hf-state-sync/cli.ts +92 -0
  23. package/src/hf-state-sync/hub.ts +81 -0
  24. package/src/hf-state-sync/manifest.ts +67 -0
  25. package/src/hf-state-sync/paths.ts +73 -0
  26. package/src/hf-state-sync/restore.ts +109 -0
  27. package/src/hf-state-sync/snapshot.ts +133 -0
  28. package/src/hf-state-sync/sqlite.ts +57 -0
  29. package/src/hf-state-sync/supervise.ts +256 -0
  30. package/src/vendor/hfjs-xet/error.ts +52 -0
  31. package/src/vendor/hfjs-xet/types/public.ts +207 -0
  32. package/src/vendor/hfjs-xet/utils/ChunkCache.ts +102 -0
  33. package/src/vendor/hfjs-xet/utils/RangeList.ts +182 -0
  34. package/src/vendor/hfjs-xet/utils/SplicedBlob.ts +249 -0
  35. package/src/vendor/hfjs-xet/utils/XetBlob.ts +732 -0
  36. package/src/vendor/hfjs-xet/utils/checkCredentials.ts +21 -0
  37. package/src/vendor/hfjs-xet/utils/combineUint8Arrays.ts +13 -0
  38. package/src/vendor/hfjs-xet/utils/createXorbs.ts +782 -0
  39. package/src/vendor/hfjs-xet/utils/shardParser.ts +152 -0
  40. package/src/vendor/hfjs-xet/utils/sum.ts +9 -0
  41. package/src/vendor/hfjs-xet/utils/uploadShards.ts +443 -0
  42. package/src/vendor/hfjs-xet/utils/xetWriteToken.ts +101 -0
  43. package/src/vendor/hfjs-xet/vendor/lz4js/index.ts +540 -0
  44. package/src/vendor/hfjs-xet/vendor/lz4js/util.ts +57 -0
  45. package/src/vendor/hfjs-xet/vendor/lz4js/xxh32.ts +99 -0
  46. package/src/vendor/hfjs-xet/vendor/type-fest/basic.ts +34 -0
  47. package/tsconfig.json +16 -0
@@ -0,0 +1,207 @@
1
+ // @ts-nocheck -- vendored upstream code, kept verbatim; our strict tsconfig does not apply.
2
+ // Vendored from huggingface/huggingface.js@f8fdf6be (packages/hub/src/types/public.ts), MIT License.
3
+ // Delete this directory when bucket support is upstreamed to @huggingface/hub.
4
+ import type { PipelineType } from "@huggingface/tasks";
5
+
6
+ export type RepoType = "space" | "dataset" | "model" | "bucket" | "kernel";
7
+
8
+ export interface RepoId {
9
+ name: string;
10
+ type: RepoType;
11
+ }
12
+
13
+ export type RepoFullName =
14
+ | string
15
+ | `spaces/${string}`
16
+ | `datasets/${string}`
17
+ | `buckets/${string}`
18
+ | `kernels/${string}`;
19
+
20
+ export type RepoDesignation = RepoId | RepoFullName;
21
+
22
+ /**
23
+ * A {@link RepoDesignation} narrowed to bucket repos.
24
+ *
25
+ * Used by APIs that only operate on buckets (e.g. {@link copyFile}, {@link copyFiles},
26
+ * {@link copyFolder}).
27
+ */
28
+ export type BucketDesignation = { type: "bucket"; name: string } | `buckets/${string}`;
29
+
30
+ /** Actually `hf_${string}`, but for convenience, using the string type */
31
+ export type AccessToken = string;
32
+
33
+ /**
34
+ * @deprecated Use `AccessToken` instead. Pass { accessToken: "hf_..." } instead of { credentials: { accessToken: "hf_..." } }
35
+ */
36
+ export interface Credentials {
37
+ accessToken: AccessToken;
38
+ }
39
+
40
+ export type CredentialsParams =
41
+ | {
42
+ accessToken?: undefined;
43
+ /**
44
+ * @deprecated Use `accessToken` instead
45
+ */
46
+ credentials: Credentials;
47
+ }
48
+ | {
49
+ accessToken: AccessToken;
50
+ /**
51
+ * @deprecated Use `accessToken` instead
52
+ */
53
+ credentials?: undefined;
54
+ };
55
+
56
+ export type SpaceHardwareFlavor =
57
+ | "cpu-basic"
58
+ | "cpu-upgrade"
59
+ | "cpu-performance"
60
+ | "cpu-xl"
61
+ | "sprx8"
62
+ | "zero-a10g"
63
+ | "inf2x6"
64
+ | "t4-small"
65
+ | "t4-medium"
66
+ | "l4x1"
67
+ | "l4x4"
68
+ | "l40sx1"
69
+ | "l40sx4"
70
+ | "l40sx8"
71
+ | "a10g-small"
72
+ | "a10g-large"
73
+ | "a10g-largex2"
74
+ | "a10g-largex4"
75
+ | "a100-large"
76
+ | "a100x4"
77
+ | "a100x8";
78
+
79
+ export type SpaceSdk = "streamlit" | "gradio" | "docker" | "static";
80
+
81
+ export type SpaceStage =
82
+ | "NO_APP_FILE"
83
+ | "CONFIG_ERROR"
84
+ | "BUILDING"
85
+ | "BUILD_ERROR"
86
+ | "RUNNING"
87
+ | "RUNNING_BUILDING"
88
+ | "RUNTIME_ERROR"
89
+ | "DELETING"
90
+ | "PAUSED"
91
+ | "SLEEPING";
92
+
93
+ export type AccessTokenRole = "admin" | "write" | "contributor" | "read";
94
+
95
+ export type AuthType = "access_token" | "app_token" | "app_token_as_user";
96
+
97
+ export type { PipelineType };
98
+
99
+ export interface SpaceRuntime {
100
+ stage: SpaceStage;
101
+ sdk?: SpaceSdk;
102
+ sdkVersion?: string;
103
+ errorMessage?: string;
104
+ hardware?: {
105
+ current: SpaceHardwareFlavor | null;
106
+ currentPrettyName?: string;
107
+ requested: SpaceHardwareFlavor | null;
108
+ requestedPrettyName?: string;
109
+ };
110
+ /** when calling /spaces, those props are only fetched if ?full=true */
111
+ resources?: SpaceResourceConfig;
112
+ /** in seconds */
113
+ gcTimeout?: number | null;
114
+ }
115
+
116
+ export interface SpaceResourceRequirement {
117
+ cpu?: string;
118
+ memory?: string;
119
+ gpu?: string;
120
+ gpuModel?: string;
121
+ ephemeral?: string;
122
+ }
123
+
124
+ export interface SpaceResourceConfig {
125
+ requests: SpaceResourceRequirement;
126
+ limits: SpaceResourceRequirement;
127
+ replicas?: number;
128
+ throttled?: boolean;
129
+ is_custom?: boolean;
130
+ }
131
+
132
+ export type License =
133
+ | "apache-2.0"
134
+ | "mit"
135
+ | "openrail"
136
+ | "bigscience-openrail-m"
137
+ | "creativeml-openrail-m"
138
+ | "bigscience-bloom-rail-1.0"
139
+ | "bigcode-openrail-m"
140
+ | "afl-3.0"
141
+ | "artistic-2.0"
142
+ | "bsl-1.0"
143
+ | "bsd"
144
+ | "bsd-2-clause"
145
+ | "bsd-3-clause"
146
+ | "bsd-3-clause-clear"
147
+ | "c-uda"
148
+ | "cc"
149
+ | "cc0-1.0"
150
+ | "cc-by-2.0"
151
+ | "cc-by-2.5"
152
+ | "cc-by-3.0"
153
+ | "cc-by-4.0"
154
+ | "cc-by-sa-3.0"
155
+ | "cc-by-sa-4.0"
156
+ | "cc-by-nc-2.0"
157
+ | "cc-by-nc-3.0"
158
+ | "cc-by-nc-4.0"
159
+ | "cc-by-nd-4.0"
160
+ | "cc-by-nc-nd-3.0"
161
+ | "cc-by-nc-nd-4.0"
162
+ | "cc-by-nc-sa-2.0"
163
+ | "cc-by-nc-sa-3.0"
164
+ | "cc-by-nc-sa-4.0"
165
+ | "cdla-sharing-1.0"
166
+ | "cdla-permissive-1.0"
167
+ | "cdla-permissive-2.0"
168
+ | "wtfpl"
169
+ | "ecl-2.0"
170
+ | "epl-1.0"
171
+ | "epl-2.0"
172
+ | "etalab-2.0"
173
+ | "eupl-1.1"
174
+ | "agpl-3.0"
175
+ | "gfdl"
176
+ | "gpl"
177
+ | "gpl-2.0"
178
+ | "gpl-3.0"
179
+ | "lgpl"
180
+ | "lgpl-2.1"
181
+ | "lgpl-3.0"
182
+ | "isc"
183
+ | "lppl-1.3c"
184
+ | "ms-pl"
185
+ | "mpl-2.0"
186
+ | "odc-by"
187
+ | "odbl"
188
+ | "openrail++"
189
+ | "osl-3.0"
190
+ | "postgresql"
191
+ | "ofl-1.1"
192
+ | "ncsa"
193
+ | "unlicense"
194
+ | "zlib"
195
+ | "pddl"
196
+ | "lgpl-lr"
197
+ | "deepfloyd-if-license"
198
+ | "llama2"
199
+ | "llama3"
200
+ | "llama3.1"
201
+ | "llama3.2"
202
+ | "llama3.3"
203
+ | "gemma"
204
+ | "apple-ascl"
205
+ | "apple-amlr"
206
+ | "unknown"
207
+ | "other";
@@ -0,0 +1,102 @@
1
+ // @ts-nocheck -- vendored upstream code, kept verbatim; our strict tsconfig does not apply.
2
+ // Vendored from huggingface/huggingface.js@f8fdf6be (packages/hub/src/utils/ChunkCache.ts), MIT License.
3
+ // Delete this directory when bucket support is upstreamed to @huggingface/hub.
4
+ const CHUNK_CACHE_INITIAL_SIZE = 10_000;
5
+ const CHUNK_CACHE_GROW_FACTOR = 1.5;
6
+ const CHUNK_CACHE_MAX_SIZE = 1_000_000;
7
+
8
+ export class ChunkCache {
9
+ index = 0;
10
+ // Index >= 0 means local xorb, < 0 means remote xorb
11
+ xorbIndices: Int32Array;
12
+ // Max 8K chunks per xorb, less than 64K uint16_t
13
+ chunkIndices: Uint16Array;
14
+ map = new Map<string, number>(); // hash -> chunkCacheIndex. Less overhead that way, empty object is 60+B and empty array is 40+B
15
+ hmacs = new Set<string>(); // todo : remove old hmacs
16
+ maxSize: number;
17
+
18
+ constructor(maxSize: number = CHUNK_CACHE_MAX_SIZE) {
19
+ if (maxSize < 1) {
20
+ throw new Error("maxSize must be at least 1");
21
+ }
22
+ this.maxSize = maxSize;
23
+ this.xorbIndices = new Int32Array(Math.min(CHUNK_CACHE_INITIAL_SIZE, maxSize));
24
+ this.chunkIndices = new Uint16Array(Math.min(CHUNK_CACHE_INITIAL_SIZE, maxSize));
25
+ }
26
+
27
+ addChunkToCache(hash: string, xorbIndex: number, chunkIndex: number, hmac: string | null): void {
28
+ if (this.map.has(hash)) {
29
+ // Happens when we receive an existing chunk from remote dedup info (eg duplicate chunk in shard? Or shards with same hmac key
30
+ // sharing chunks/xorbs)
31
+
32
+ // processing this chunk again would desync the cache, as `this.map.size` would not increase, as opposed to `this.index`
33
+
34
+ // We could readd/remove it to "refresh it"
35
+ return;
36
+ }
37
+ if (this.map.values().next().value === this.index) {
38
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
39
+ this.map.delete(this.map.keys().next().value!);
40
+ }
41
+ this.map.set(hash, this.index);
42
+ if (hmac !== null) {
43
+ this.hmacs.add(hmac);
44
+ }
45
+
46
+ if (this.index >= this.xorbIndices.length) {
47
+ // todo: switch to resize() with modern browsers
48
+ const oldXorbIndices = this.xorbIndices;
49
+ const oldChunkIndices = this.chunkIndices;
50
+ this.xorbIndices = new Int32Array(Math.min(this.xorbIndices.length * CHUNK_CACHE_GROW_FACTOR, this.maxSize));
51
+ this.chunkIndices = new Uint16Array(Math.min(this.chunkIndices.length * CHUNK_CACHE_GROW_FACTOR, this.maxSize));
52
+ this.xorbIndices.set(oldXorbIndices);
53
+ this.chunkIndices.set(oldChunkIndices);
54
+ }
55
+
56
+ this.xorbIndices[this.index] = xorbIndex;
57
+ this.chunkIndices[this.index] = chunkIndex;
58
+ this.index = (this.index + 1) % this.maxSize;
59
+ }
60
+
61
+ getChunk(
62
+ hash: string,
63
+ /**
64
+ * Set to null if you only want to check against locally created chunks, or the hash is already a hmac
65
+ */
66
+ hmacFunction: ((hash: string, key: string) => string) | null,
67
+ ):
68
+ | {
69
+ xorbIndex: number;
70
+ chunkIndex: number;
71
+ }
72
+ | undefined {
73
+ let index = this.map.get(hash);
74
+ if (index === undefined && hmacFunction !== null) {
75
+ for (const hmac of this.hmacs) {
76
+ index = this.map.get(hmacFunction(hash, hmac));
77
+ if (index !== undefined) {
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ if (index === undefined) {
83
+ return undefined;
84
+ }
85
+ return {
86
+ xorbIndex: this.xorbIndices[index],
87
+ chunkIndex: this.chunkIndices[index],
88
+ };
89
+ }
90
+
91
+ updateChunkIndex(hash: string, chunkIndex: number): void {
92
+ const index = this.map.get(hash);
93
+ if (index === undefined) {
94
+ throw new Error(`Chunk not found in cache: ${hash}`);
95
+ }
96
+ this.chunkIndices[index] = chunkIndex;
97
+ }
98
+
99
+ removeChunkFromCache(hash: string): void {
100
+ this.map.delete(hash);
101
+ }
102
+ }
@@ -0,0 +1,182 @@
1
+ // @ts-nocheck -- vendored upstream code, kept verbatim; our strict tsconfig does not apply.
2
+ // Vendored from huggingface/huggingface.js@f8fdf6be (packages/hub/src/utils/RangeList.ts), MIT License.
3
+ // Delete this directory when bucket support is upstreamed to @huggingface/hub.
4
+ /**
5
+ * Code generated with this prompt by Cursor:
6
+ *
7
+ * I want to build a class to manage ranges
8
+ *
9
+ * I can add ranges to it with a start& an end (both integer, end > start). It should store those ranges efficiently.
10
+ *
11
+ * When several ranges overlap, eg [1, 100] and [30, 50], I want the class to split the range into non-overlapping ranges, and add a "ref counter" to the ranges. For example, [1, 30], [30, 50] * 2, [50, 100]
12
+ *
13
+ * I also want to be able to remove ranges, it will decrease the ref counter or remove the range altogether. I can only remove ranges at existing boundaries. For example, with the [1, 30], [30, 50] * 2, [50, 100] configuration
14
+ *
15
+ * - removing [1, 100] => the only range remaning is [30, 50]
16
+ * - removing [2, 50] => error, because "2' is not a boundary
17
+ * - removing [30, 50] => [1, 30], [30, 50], [50, 100] (do not "merge" the ranges back together)
18
+ *
19
+ * I want to be able to associate data to each range. And I want to be able to get the ranges inside boundaries. For example , with [1, 30], [30, 50] * 2, [50, 100] configuration
20
+ *
21
+ * - getting [30, 100] => I receive [30, 50] * 2, [50, 100], and I can get / modify the data associated to each range by accessing their data prop. Note the "*2" is just the ref counter, there is onlly one range object for the interval returned
22
+ * - getting [2, 50] => I get [30, 50] * 2
23
+ *
24
+ * ----
25
+ *
26
+ * Could optimize with binary search, but the ranges we want to handle are not that many.
27
+ */
28
+ interface Range<T> {
29
+ start: number;
30
+ end: number;
31
+ refCount: number;
32
+ data: T | null;
33
+ }
34
+
35
+ export class RangeList<T> {
36
+ private ranges: Range<T>[] = [];
37
+
38
+ /**
39
+ * Add a range to the list. If it overlaps with existing ranges,
40
+ * it will split them and increment reference counts accordingly.
41
+ */
42
+ add(start: number, end: number): void {
43
+ if (end <= start) {
44
+ throw new TypeError("End must be greater than start");
45
+ }
46
+
47
+ // Find all ranges that overlap with the new range
48
+ const overlappingRanges: { index: number; range: Range<T> }[] = [];
49
+ for (let i = 0; i < this.ranges.length; i++) {
50
+ const range = this.ranges[i];
51
+ if (start < range.end && end > range.start) {
52
+ overlappingRanges.push({ index: i, range });
53
+ }
54
+ if (range.data !== null) {
55
+ throw new Error("Overlapping range already has data");
56
+ }
57
+ }
58
+
59
+ if (overlappingRanges.length === 0) {
60
+ // No overlaps, just add the new range
61
+ this.ranges.push({ start, end, refCount: 1, data: null });
62
+ this.ranges.sort((a, b) => a.start - b.start);
63
+ return;
64
+ }
65
+
66
+ // Handle overlaps by splitting ranges
67
+ const newRanges: Range<T>[] = [];
68
+ let currentPos = start;
69
+
70
+ for (let i = 0; i < overlappingRanges.length; i++) {
71
+ const { range } = overlappingRanges[i];
72
+
73
+ // Add range before overlap if exists
74
+ if (currentPos < range.start) {
75
+ newRanges.push({
76
+ start: currentPos,
77
+ end: range.start,
78
+ refCount: 1,
79
+ data: null,
80
+ });
81
+ } else if (range.start < currentPos) {
82
+ newRanges.push({
83
+ start: range.start,
84
+ end: currentPos,
85
+ refCount: range.refCount,
86
+ data: null,
87
+ });
88
+ }
89
+
90
+ // Add overlapping part with increased ref count
91
+ newRanges.push({
92
+ start: Math.max(currentPos, range.start),
93
+ end: Math.min(end, range.end),
94
+ refCount: range.refCount + 1,
95
+ data: null,
96
+ });
97
+
98
+ // Add remaining part of existing range if exists
99
+ if (range.end > end) {
100
+ newRanges.push({
101
+ start: end,
102
+ end: range.end,
103
+ refCount: range.refCount,
104
+ data: null,
105
+ });
106
+ }
107
+
108
+ currentPos = Math.max(currentPos, range.end);
109
+ }
110
+
111
+ // Add remaining part after last overlap if exists
112
+ if (currentPos < end) {
113
+ newRanges.push({
114
+ start: currentPos,
115
+ end,
116
+ refCount: 1,
117
+ data: null,
118
+ });
119
+ }
120
+
121
+ // Remove old overlapping ranges and insert new ones
122
+ const firstIndex = overlappingRanges[0].index;
123
+ const lastIndex = overlappingRanges[overlappingRanges.length - 1].index;
124
+ this.ranges.splice(firstIndex, lastIndex - firstIndex + 1, ...newRanges);
125
+ this.ranges.sort((a, b) => a.start - b.start);
126
+ }
127
+
128
+ /**
129
+ * Remove a range from the list. The range must start and end at existing boundaries.
130
+ */
131
+ remove(start: number, end: number): void {
132
+ if (end <= start) {
133
+ throw new TypeError("End must be greater than start");
134
+ }
135
+
136
+ // Find ranges that need to be modified
137
+ const affectedRanges: { index: number; range: Range<T> }[] = [];
138
+ for (let i = 0; i < this.ranges.length; i++) {
139
+ const range = this.ranges[i];
140
+ if (start < range.end && end > range.start) {
141
+ affectedRanges.push({ index: i, range });
142
+ }
143
+ }
144
+
145
+ if (affectedRanges.length === 0) {
146
+ throw new Error("No ranges found to remove");
147
+ }
148
+
149
+ // Verify boundaries match
150
+ if (start !== affectedRanges[0].range.start || end !== affectedRanges[affectedRanges.length - 1].range.end) {
151
+ throw new Error("Range boundaries must match existing boundaries");
152
+ }
153
+
154
+ // Todo: also check if there's a gap in the middle but it should not happen with our usage
155
+
156
+ for (let i = 0; i < affectedRanges.length; i++) {
157
+ const { range } = affectedRanges[i];
158
+
159
+ range.refCount--;
160
+ }
161
+
162
+ this.ranges = this.ranges.filter((range) => range.refCount > 0);
163
+ }
164
+
165
+ /**
166
+ * Get all ranges within the specified boundaries.
167
+ */
168
+ getRanges(start: number, end: number): Range<T>[] {
169
+ if (end <= start) {
170
+ throw new TypeError("End must be greater than start");
171
+ }
172
+
173
+ return this.ranges.filter((range) => start < range.end && end > range.start);
174
+ }
175
+
176
+ /**
177
+ * Get all ranges in the list
178
+ */
179
+ getAllRanges(): Range<T>[] {
180
+ return [...this.ranges];
181
+ }
182
+ }