@uluops/setup 0.6.4 → 0.6.5
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/dist/steps/auth.js +7 -3
- package/dist/steps/mcp.d.ts +12 -0
- package/dist/steps/mcp.js +26 -7
- package/dist/steps/shell.js +4 -3
- package/package.json +1 -1
package/dist/steps/auth.js
CHANGED
|
@@ -87,7 +87,10 @@ async function readCredentialsFile() {
|
|
|
87
87
|
return defaultProfile?.apiKey ?? defaultProfile?.api_key;
|
|
88
88
|
}
|
|
89
89
|
async function validateKey(apiKey) {
|
|
90
|
-
|
|
90
|
+
// Self-identity lives in ops-uluops-api (`/api/v1/auth/me`), not the
|
|
91
|
+
// registry-api users namespace. Registry-api `/users/:id` Zod-validates the
|
|
92
|
+
// id as UUID, so /users/me returns 400 with `id: ["Invalid uuid"]`.
|
|
93
|
+
const url = "https://api.uluops.ai/api/v1/auth/me";
|
|
91
94
|
try {
|
|
92
95
|
const res = await fetch(url, {
|
|
93
96
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
@@ -99,8 +102,9 @@ async function validateKey(apiKey) {
|
|
|
99
102
|
if (!res.ok) {
|
|
100
103
|
throw new Error(`API returned ${res.status}. Try --skip-validation to continue offline.`);
|
|
101
104
|
}
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
// ops-uluops-api wraps user payloads as { data: { email, ... } }
|
|
106
|
+
const body = (await res.json());
|
|
107
|
+
return { email: body.data?.email ?? null };
|
|
104
108
|
}
|
|
105
109
|
catch (err) {
|
|
106
110
|
// fetch() throws TypeError for network failures (ENOTFOUND, ECONNREFUSED).
|
package/dist/steps/mcp.d.ts
CHANGED
|
@@ -8,3 +8,15 @@ export interface McpResult {
|
|
|
8
8
|
export declare function installMcp(profile: HarnessProfile, apiKey: string, scope: "global" | "local", dryRun: boolean): Promise<McpResult>;
|
|
9
9
|
/** Remove UluOps MCP server entries from the harness config. */
|
|
10
10
|
export declare function uninstallMcp(profile: HarnessProfile, configPath: string): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Append `entry` to a .gitignore file, creating it if missing.
|
|
13
|
+
*
|
|
14
|
+
* Discriminates ENOENT (file does not exist → create fresh) from other read
|
|
15
|
+
* errors (permission denied, I/O error, EISDIR → warn and skip). The previous
|
|
16
|
+
* implementation caught everything and wrote a single-line file, silently
|
|
17
|
+
* clobbering user content on any read failure.
|
|
18
|
+
*
|
|
19
|
+
* `reader` is injected for testing the error-discrimination behavior; defaults
|
|
20
|
+
* to the real fs reader.
|
|
21
|
+
*/
|
|
22
|
+
export declare function ensureGitignoreEntry(gitignorePath: string, entry: string, reader?: (path: string) => Promise<string>): Promise<void>;
|
package/dist/steps/mcp.js
CHANGED
|
@@ -38,20 +38,39 @@ async function backupConfig(harnessName, configPath) {
|
|
|
38
38
|
}
|
|
39
39
|
async function addToGitignore(localConfigFilename) {
|
|
40
40
|
const root = await findProjectRoot();
|
|
41
|
-
const gitignorePath = join(root, ".gitignore");
|
|
42
41
|
try {
|
|
43
42
|
await access(join(root, ".git"));
|
|
44
43
|
}
|
|
45
44
|
catch {
|
|
46
45
|
return;
|
|
47
46
|
}
|
|
47
|
+
await ensureGitignoreEntry(join(root, ".gitignore"), localConfigFilename);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Append `entry` to a .gitignore file, creating it if missing.
|
|
51
|
+
*
|
|
52
|
+
* Discriminates ENOENT (file does not exist → create fresh) from other read
|
|
53
|
+
* errors (permission denied, I/O error, EISDIR → warn and skip). The previous
|
|
54
|
+
* implementation caught everything and wrote a single-line file, silently
|
|
55
|
+
* clobbering user content on any read failure.
|
|
56
|
+
*
|
|
57
|
+
* `reader` is injected for testing the error-discrimination behavior; defaults
|
|
58
|
+
* to the real fs reader.
|
|
59
|
+
*/
|
|
60
|
+
export async function ensureGitignoreEntry(gitignorePath, entry, reader = (p) => readFile(p, "utf-8")) {
|
|
61
|
+
let content;
|
|
48
62
|
try {
|
|
49
|
-
|
|
50
|
-
if (content.includes(localConfigFilename))
|
|
51
|
-
return;
|
|
52
|
-
await atomicWrite(gitignorePath, content.trimEnd() + `\n${localConfigFilename}\n`);
|
|
63
|
+
content = await reader(gitignorePath);
|
|
53
64
|
}
|
|
54
|
-
catch {
|
|
55
|
-
|
|
65
|
+
catch (err) {
|
|
66
|
+
if (err.code === "ENOENT") {
|
|
67
|
+
await atomicWrite(gitignorePath, `${entry}\n`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
console.warn(`Warning: could not read ${gitignorePath} (${err.message}). Skipping .gitignore update.`);
|
|
71
|
+
return;
|
|
56
72
|
}
|
|
73
|
+
if (content.includes(entry))
|
|
74
|
+
return;
|
|
75
|
+
await atomicWrite(gitignorePath, content.trimEnd() + `\n${entry}\n`);
|
|
57
76
|
}
|
package/dist/steps/shell.js
CHANGED
|
@@ -24,12 +24,13 @@ export async function writeShellExport(profilePath, apiKey, dryRun) {
|
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
const startIdx = content.indexOf(FENCE_START);
|
|
27
|
-
const endIdx = content.
|
|
27
|
+
const endIdx = content.lastIndexOf(FENCE_END);
|
|
28
28
|
if (!dryRun) {
|
|
29
29
|
await backupProfile(profilePath);
|
|
30
30
|
}
|
|
31
31
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
32
|
-
// Replace existing fenced block
|
|
32
|
+
// Replace existing fenced block — using lastIndexOf for FENCE_END collapses
|
|
33
|
+
// any duplicate blocks left by earlier buggy installs into a single new block
|
|
33
34
|
const before = content.slice(0, startIdx);
|
|
34
35
|
const after = content.slice(endIdx + FENCE_END.length);
|
|
35
36
|
if (!dryRun) {
|
|
@@ -52,7 +53,7 @@ export async function removeShellExport(profilePath) {
|
|
|
52
53
|
return;
|
|
53
54
|
}
|
|
54
55
|
const startIdx = content.indexOf(FENCE_START);
|
|
55
|
-
const endIdx = content.
|
|
56
|
+
const endIdx = content.lastIndexOf(FENCE_END);
|
|
56
57
|
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
57
58
|
await backupProfile(profilePath);
|
|
58
59
|
const before = content.slice(0, startIdx);
|