opencode-muse 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/session/registry.js +67 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Add the published package to your project-level `opencode.jsonc` (or
|
|
|
16
16
|
```jsonc
|
|
17
17
|
{
|
|
18
18
|
"$schema": "https://opencode.ai/config.json",
|
|
19
|
-
"plugins": ["opencode-muse@0.1.
|
|
19
|
+
"plugins": ["opencode-muse@0.1.1"],
|
|
20
20
|
}
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -117,5 +117,6 @@ setup placeholder rather than advertising a model that will fail on use.
|
|
|
117
117
|
|
|
118
118
|
## Related projects
|
|
119
119
|
|
|
120
|
-
- [opencode-muse-auth](https://github.com/TheStreamCode/opencode-muse-auth)
|
|
121
|
-
- [
|
|
120
|
+
- [opencode-muse-auth](https://github.com/TheStreamCode/opencode-muse-auth) is another credential-extraction/direct-API approach; see its [issue #12](https://github.com/TheStreamCode/opencode-muse-auth/issues/12) for its discussion of subscription-term concerns.
|
|
121
|
+
- [opencode-muse-code](https://github.com/swalker326/opencode-muse-code) is an unofficial direct-API alternative. It reads the Muse CLI login, requests an API key from Meta, and sends model requests to Meta's API from OpenCode instead of routing inference through `muse serve` and MSP. Its subscription eligibility is separate from this plugin's CLI-mediated approach and has not been established by this project.
|
|
122
|
+
- [Official Muse Code documentation](https://dev.meta.ai/docs/quickstart#agent-setup) describes using Muse models in OpenCode through the Meta API.
|
package/dist/session/registry.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import { closeSync, chmodSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { closeSync, chmodSync, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readlinkSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, isAbsolute, join,
|
|
4
|
+
import { dirname, isAbsolute, join, posix, win32 } from "node:path";
|
|
5
5
|
import { RegistryCorruptError, RegistryStorageError, RegistryValidationError, RegistryWorkspaceMismatchError, } from "../errors.js";
|
|
6
6
|
const REGISTRY_VERSION = 1;
|
|
7
7
|
const MAX_ENTRY_BYTES = 1_048_576;
|
|
@@ -9,29 +9,82 @@ const HASH_PATTERN = /^[a-f0-9]{64}$/;
|
|
|
9
9
|
const UUID_V7_PATTERN = /^[a-f0-9]{8}-[a-f0-9]{4}-7[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/;
|
|
10
10
|
const IDENTIFIER_PATTERN = /^[A-Za-z0-9._:@+_-]{1,512}$/;
|
|
11
11
|
const METHOD_PATTERN = /^[a-z][A-Za-z0-9]*(?:\/[A-Za-z][A-Za-z0-9]*)+$/;
|
|
12
|
+
const MAX_PATH_SYMLINKS = 40;
|
|
13
|
+
/** Resolve existing path components while retaining a normalized not-yet-created suffix. */
|
|
14
|
+
function canonicalPath(path, platform) {
|
|
15
|
+
const pathApi = platform === "win32" ? win32 : posix;
|
|
16
|
+
if (platform !== process.platform)
|
|
17
|
+
return pathApi.resolve(path);
|
|
18
|
+
const absolute = pathApi.resolve(path);
|
|
19
|
+
const root = pathApi.parse(absolute).root;
|
|
20
|
+
let current = root;
|
|
21
|
+
let remaining = absolute.slice(root.length).split(pathApi.sep).filter(Boolean);
|
|
22
|
+
let followedSymlinks = 0;
|
|
23
|
+
while (remaining.length > 0) {
|
|
24
|
+
const [component, ...rest] = remaining;
|
|
25
|
+
const candidate = pathApi.join(current, component);
|
|
26
|
+
let stats;
|
|
27
|
+
try {
|
|
28
|
+
stats = lstatSync(candidate);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
// A missing component ends filesystem traversal; normalize the remaining suffix lexically.
|
|
32
|
+
if (isMissingFile(error))
|
|
33
|
+
return pathApi.resolve(current, ...remaining);
|
|
34
|
+
throw new RegistryValidationError();
|
|
35
|
+
}
|
|
36
|
+
if (stats.isSymbolicLink()) {
|
|
37
|
+
followedSymlinks += 1;
|
|
38
|
+
if (followedSymlinks > MAX_PATH_SYMLINKS)
|
|
39
|
+
throw new RegistryValidationError();
|
|
40
|
+
let target;
|
|
41
|
+
try {
|
|
42
|
+
target = readlinkSync(candidate);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
throw new RegistryValidationError();
|
|
46
|
+
}
|
|
47
|
+
const absoluteTarget = pathApi.isAbsolute(target)
|
|
48
|
+
? pathApi.resolve(target)
|
|
49
|
+
: pathApi.resolve(current, target);
|
|
50
|
+
const targetRoot = pathApi.parse(absoluteTarget).root;
|
|
51
|
+
current = targetRoot;
|
|
52
|
+
remaining = [
|
|
53
|
+
...absoluteTarget.slice(targetRoot.length).split(pathApi.sep).filter(Boolean),
|
|
54
|
+
...rest,
|
|
55
|
+
];
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
current = realpathSync(candidate);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw new RegistryValidationError();
|
|
63
|
+
}
|
|
64
|
+
remaining = rest;
|
|
65
|
+
}
|
|
66
|
+
return pathApi.resolve(current);
|
|
67
|
+
}
|
|
12
68
|
/** Refuse a caller-selected state root inside Muse's CLI-owned config directory. */
|
|
13
69
|
export function assertSafeStateDirectory(path, options = {}) {
|
|
14
70
|
const env = options.env ?? process.env;
|
|
15
71
|
const homeDirectory = options.homeDirectory ?? homedir();
|
|
16
72
|
const platform = options.platform ?? process.platform;
|
|
17
73
|
const windows = platform === "win32";
|
|
18
|
-
const
|
|
19
|
-
const pathResolve = windows ? win32.resolve : resolve;
|
|
20
|
-
const pathRelative = windows ? win32.relative : relative;
|
|
21
|
-
const pathSeparator = windows ? win32.sep : sep;
|
|
22
|
-
const pathIsAbsolute = windows ? win32.isAbsolute : isAbsolute;
|
|
74
|
+
const pathApi = windows ? win32 : posix;
|
|
23
75
|
const configuredHome = env.XDG_CONFIG_HOME;
|
|
24
|
-
const configHome = requiredString(configuredHome) &&
|
|
76
|
+
const configHome = requiredString(configuredHome) && pathApi.isAbsolute(configuredHome)
|
|
25
77
|
? configuredHome
|
|
26
|
-
:
|
|
27
|
-
const museConfigDirectory =
|
|
28
|
-
const
|
|
78
|
+
: pathApi.join(homeDirectory, ".config");
|
|
79
|
+
const museConfigDirectory = canonicalPath(pathApi.join(configHome, "muse"), platform);
|
|
80
|
+
const canonicalStateDirectory = canonicalPath(path, platform);
|
|
81
|
+
const relativePath = pathApi.relative(museConfigDirectory, canonicalStateDirectory);
|
|
29
82
|
const outsideMuseConfig = relativePath === ".."
|
|
30
|
-
|| relativePath.startsWith(`..${
|
|
31
|
-
||
|
|
83
|
+
|| relativePath.startsWith(`..${pathApi.sep}`)
|
|
84
|
+
|| pathApi.isAbsolute(relativePath);
|
|
32
85
|
if (!outsideMuseConfig)
|
|
33
86
|
throw new RegistryValidationError();
|
|
34
|
-
return
|
|
87
|
+
return canonicalStateDirectory;
|
|
35
88
|
}
|
|
36
89
|
function isRecord(value) {
|
|
37
90
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|