Skip to content

Commit 55f5789

Browse files
committed
fix(auth): address grok review feedback
1 parent d3238f4 commit 55f5789

10 files changed

Lines changed: 215 additions & 71 deletions

File tree

packages/api/src/http.ts

Lines changed: 36 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Chunk, Duration, Effect, Ref } from "effect"
1+
import { Chunk, Duration, Effect, Match, Ref } from "effect"
22
import * as Stream from "effect/Stream"
33
import type { PlatformError } from "@effect/platform/Error"
44
import type * as HttpBody from "@effect/platform/HttpBody"
@@ -426,60 +426,43 @@ const readProjectSkillUpdateRequest = () => HttpServerRequest.schemaBodyJson(Pro
426426
const readActiveProjectTerminalSessionRequest = () =>
427427
HttpServerRequest.schemaBodyJson(ActiveProjectTerminalSessionRequestSchema)
428428

429-
const skillScopeFromId = (scopeId: string): ProjectSkillScope | null => {
430-
switch (scopeId) {
431-
case "skills":
432-
return "skills"
433-
case "agents-skills":
434-
return "agents/skills"
435-
case "agents-dot-skills":
436-
return "agents/.skills"
437-
case "claude-skills":
438-
return "claude/skills"
439-
case "codex-skills":
440-
return "codex/skills"
441-
case "gemini-skills":
442-
return "gemini/skills"
443-
case "grok-skills":
444-
return "grok/skills"
445-
default:
446-
return null
447-
}
448-
}
429+
const projectSkillScope = (scope: ProjectSkillScope): ProjectSkillScope => scope
430+
431+
const skillScopeFromId = (scopeId: string): ProjectSkillScope | null =>
432+
Match.value(scopeId).pipe(
433+
Match.when("skills", () => projectSkillScope("skills")),
434+
Match.when("agents-skills", () => projectSkillScope("agents/skills")),
435+
Match.when("agents-dot-skills", () => projectSkillScope("agents/.skills")),
436+
Match.when("claude-skills", () => projectSkillScope("claude/skills")),
437+
Match.when("codex-skills", () => projectSkillScope("codex/skills")),
438+
Match.when("gemini-skills", () => projectSkillScope("gemini/skills")),
439+
Match.when("grok-skills", () => projectSkillScope("grok/skills")),
440+
Match.orElse(() => null)
441+
)
449442

450-
export const skillScopeToId = (scope: ProjectSkillScope): string => {
451-
switch (scope) {
452-
case "skills":
453-
return "skills"
454-
case "agents/skills":
455-
return "agents-skills"
456-
case "agents/.skills":
457-
return "agents-dot-skills"
458-
case "claude/skills":
459-
return "claude-skills"
460-
case "codex/skills":
461-
return "codex-skills"
462-
case "gemini/skills":
463-
return "gemini-skills"
464-
case "grok/skills":
465-
return "grok-skills"
466-
}
467-
}
443+
export const skillScopeToId = (scope: ProjectSkillScope): string =>
444+
Match.value(scope).pipe(
445+
Match.when("skills", () => "skills"),
446+
Match.when("agents/skills", () => "agents-skills"),
447+
Match.when("agents/.skills", () => "agents-dot-skills"),
448+
Match.when("claude/skills", () => "claude-skills"),
449+
Match.when("codex/skills", () => "codex-skills"),
450+
Match.when("gemini/skills", () => "gemini-skills"),
451+
Match.when("grok/skills", () => "grok-skills"),
452+
Match.exhaustive
453+
)
468454

469-
const skillScopeFromBody = (scope: string): ProjectSkillScope | null => {
470-
switch (scope) {
471-
case "skills":
472-
case "agents/skills":
473-
case "agents/.skills":
474-
case "claude/skills":
475-
case "codex/skills":
476-
case "gemini/skills":
477-
case "grok/skills":
478-
return scope as ProjectSkillScope
479-
default:
480-
return null
481-
}
482-
}
455+
const skillScopeFromBody = (scope: string): ProjectSkillScope | null =>
456+
Match.value(scope).pipe(
457+
Match.when("skills", () => projectSkillScope("skills")),
458+
Match.when("agents/skills", () => projectSkillScope("agents/skills")),
459+
Match.when("agents/.skills", () => projectSkillScope("agents/.skills")),
460+
Match.when("claude/skills", () => projectSkillScope("claude/skills")),
461+
Match.when("codex/skills", () => projectSkillScope("codex/skills")),
462+
Match.when("gemini/skills", () => projectSkillScope("gemini/skills")),
463+
Match.when("grok/skills", () => projectSkillScope("grok/skills")),
464+
Match.orElse(() => null)
465+
)
483466
const readProjectPortForwardRequest = () => HttpServerRequest.schemaBodyJson(ProjectPortForwardRequestSchema)
484467
const readProjectDatabaseProfileRequest = () => HttpServerRequest.schemaBodyJson(ProjectDatabaseProfileRequestSchema)
485468
const readStateInitRequest = () => HttpServerRequest.schemaBodyJson(StateInitRequestSchema)

packages/api/src/services/project-auth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ const grokUserSettingsCredentialMarkers: ReadonlyArray<RegExp> = [
241241
/"accessToken"\s*:\s*"[^"]+"/u,
242242
/"refreshToken"\s*:\s*"[^"]+"/u,
243243
/"authToken"\s*:\s*"[^"]+"/u,
244-
/"oauth"\s*:/u
244+
/"oauth"\s*:\s*\{[^}]*"(?:apiKey|accessToken|access_token|authToken|refreshToken|refresh_token|token)"\s*:\s*"[^"]+"/su
245245
]
246246

247247
const hasGrokUserSettingsCredentials = (

packages/app/src/lib/core/templates/dockerfile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ RUN npm install -g @anthropic-ai/claude-code@latest
119119
RUN claude --version
120120
RUN npm install -g @google/gemini-cli@latest --force
121121
RUN gemini --version
122-
RUN npm install -g grok-dev@latest --force
122+
RUN npm install -g grok-dev@1.1.7
123123
RUN grok --version`
124124

125125
// CHANGE: install RTK as a real command-output optimizer in generated containers.

packages/app/src/lib/usecases/auth-grok-helpers.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { Effect, pipe } from "effect"
88
import type { AuthGrokLoginCommand, AuthGrokLogoutCommand, AuthGrokStatusCommand } from "../core/domain.js"
99
import { defaultTemplateConfig } from "../core/domain.js"
1010
import { runCommandExitCode } from "../shell/command-runner.js"
11-
import type { CommandFailedError } from "../shell/errors.js"
11+
import { CommandFailedError } from "../shell/errors.js"
1212
import { isRegularFile, normalizeAccountLabel } from "./auth-helpers.js"
1313
import { migrateLegacyOrchLayout } from "./auth-sync.js"
1414
import { ensureDockerImage } from "./docker-image.js"
@@ -22,6 +22,7 @@ export const grokImageName = "docker-git-auth-grok:latest"
2222
export const grokImageDir = ".docker-git/.orch/auth/grok/.image"
2323
export const grokContainerHomeDir = "/grok-home"
2424
export const grokCredentialsDir = ".grok"
25+
export const grokDevPackageSpec = "grok-dev@1.1.7"
2526

2627
export type GrokAccountContext = {
2728
readonly accountLabel: string
@@ -60,7 +61,7 @@ RUN apt-get update \
6061
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
6162
&& apt-get install -y --no-install-recommends nodejs \
6263
&& rm -rf /var/lib/apt/lists/*
63-
RUN npm install -g grok-dev@latest --force
64+
RUN npm install -g ${grokDevPackageSpec}
6465
RUN grok --version
6566
`
6667

@@ -177,7 +178,7 @@ const grokUserSettingsCredentialMarkers: ReadonlyArray<RegExp> = [
177178
/"accessToken"\s*:\s*"[^"]+"/u,
178179
/"refreshToken"\s*:\s*"[^"]+"/u,
179180
/"authToken"\s*:\s*"[^"]+"/u,
180-
/"oauth"\s*:/u
181+
/"oauth"\s*:\s*\{[^}]*"(?:apiKey|accessToken|access_token|authToken|refreshToken|refresh_token|token)"\s*:\s*"[^"]+"/su
181182
]
182183

183184
const hasGrokUserSettingsCredentials = (content: string): boolean =>
@@ -209,7 +210,11 @@ export const prepareGrokCredentialsDir = (
209210
command: "docker",
210211
args: ["run", "--rm", "-v", `${accountPath}:/target`, "alpine", "rm", "-rf", "/target/.grok"]
211212
}),
212-
Effect.asVoid
213+
Effect.flatMap((exitCode) =>
214+
exitCode === 0
215+
? Effect.void
216+
: Effect.fail(new CommandFailedError({ command: "docker", exitCode }))
217+
)
213218
)
214219

215220
yield* _(

packages/app/src/lib/usecases/auth-grok.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { PlatformError } from "@effect/platform/Error"
22
import { Effect } from "effect"
33

44
import type { AuthGrokLoginCommand } from "../core/domain.js"
5-
import type { AuthError, CommandFailedError } from "../shell/errors.js"
5+
import { AuthError, type CommandFailedError } from "../shell/errors.js"
66
import {
77
grokApiKeyPath,
88
grokContainerHomeDir,
@@ -30,11 +30,14 @@ import { autoSyncState } from "./state-repo.js"
3030
export const authGrokLogin = (
3131
command: AuthGrokLoginCommand,
3232
apiKey: string
33-
): Effect.Effect<void, PlatformError | CommandFailedError, GrokRuntime> => {
33+
): Effect.Effect<void, AuthError | PlatformError | CommandFailedError, GrokRuntime> => {
34+
const trimmedApiKey = apiKey.trim()
35+
if (trimmedApiKey.length === 0) {
36+
return Effect.fail(new AuthError({ message: "Grok API key must not be empty" }))
37+
}
3438
const accountLabel = normalizeAccountLabel(command.label, "default")
3539
return withGrokAuth(command, ({ accountPath, fs }) =>
3640
Effect.gen(function*(_) {
37-
const trimmedApiKey = apiKey.trim()
3841
const apiKeyFilePath = grokApiKeyPath(accountPath)
3942
yield* _(fs.writeFileString(apiKeyFilePath, `${trimmedApiKey}\n`))
4043
yield* _(fs.chmod(apiKeyFilePath, 0o600))

packages/lib/src/core/templates/dockerfile.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ RUN npm install -g @anthropic-ai/claude-code@latest
119119
RUN claude --version
120120
RUN npm install -g @google/gemini-cli@latest --force
121121
RUN gemini --version
122-
RUN npm install -g grok-dev@latest --force
122+
RUN npm install -g grok-dev@1.1.7
123123
RUN grok --version`
124124

125125
// CHANGE: install RTK as a real command-output optimizer in generated containers.

packages/lib/src/usecases/auth-grok-helpers.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { Effect, pipe } from "effect"
77
import type { AuthGrokLoginCommand, AuthGrokLogoutCommand, AuthGrokStatusCommand } from "../core/domain.js"
88
import { defaultTemplateConfig } from "../core/domain.js"
99
import { runCommandExitCode } from "../shell/command-runner.js"
10-
import type { CommandFailedError } from "../shell/errors.js"
10+
import { CommandFailedError } from "../shell/errors.js"
1111
import { isRegularFile, normalizeAccountLabel } from "./auth-helpers.js"
1212
import { migrateLegacyOrchLayout } from "./auth-sync.js"
1313
import { ensureDockerImage } from "./docker-image.js"
@@ -21,6 +21,7 @@ export const grokImageName = "docker-git-auth-grok:latest"
2121
export const grokImageDir = ".docker-git/.orch/auth/grok/.image"
2222
export const grokContainerHomeDir = "/grok-home"
2323
export const grokCredentialsDir = ".grok"
24+
export const grokDevPackageSpec = "grok-dev@1.1.7"
2425

2526
export type GrokAccountContext = {
2627
readonly accountLabel: string
@@ -59,7 +60,7 @@ RUN apt-get update \
5960
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
6061
&& apt-get install -y --no-install-recommends nodejs \
6162
&& rm -rf /var/lib/apt/lists/*
62-
RUN npm install -g grok-dev@latest --force
63+
RUN npm install -g ${grokDevPackageSpec}
6364
RUN grok --version
6465
`
6566

@@ -176,7 +177,7 @@ const grokUserSettingsCredentialMarkers: ReadonlyArray<RegExp> = [
176177
/"accessToken"\s*:\s*"[^"]+"/u,
177178
/"refreshToken"\s*:\s*"[^"]+"/u,
178179
/"authToken"\s*:\s*"[^"]+"/u,
179-
/"oauth"\s*:/u
180+
/"oauth"\s*:\s*\{[^}]*"(?:apiKey|accessToken|access_token|authToken|refreshToken|refresh_token|token)"\s*:\s*"[^"]+"/su
180181
]
181182

182183
const hasGrokUserSettingsCredentials = (content: string): boolean =>
@@ -208,7 +209,11 @@ export const prepareGrokCredentialsDir = (
208209
command: "docker",
209210
args: ["run", "--rm", "-v", `${accountPath}:/target`, "alpine", "rm", "-rf", "/target/.grok"]
210211
}),
211-
Effect.asVoid
212+
Effect.flatMap((exitCode) =>
213+
exitCode === 0
214+
? Effect.void
215+
: Effect.fail(new CommandFailedError({ command: "docker", exitCode }))
216+
)
212217
)
213218

214219
yield* _(

packages/lib/src/usecases/auth-grok.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { PlatformError } from "@effect/platform/Error"
22
import { Effect } from "effect"
33

44
import type { AuthGrokLoginCommand } from "../core/domain.js"
5-
import type { AuthError, CommandFailedError } from "../shell/errors.js"
5+
import { AuthError, type CommandFailedError } from "../shell/errors.js"
66
import {
77
grokApiKeyPath,
88
grokContainerHomeDir,
@@ -30,11 +30,14 @@ import { autoSyncState } from "./state-repo.js"
3030
export const authGrokLogin = (
3131
command: AuthGrokLoginCommand,
3232
apiKey: string
33-
): Effect.Effect<void, PlatformError | CommandFailedError, GrokRuntime> => {
33+
): Effect.Effect<void, AuthError | PlatformError | CommandFailedError, GrokRuntime> => {
34+
const trimmedApiKey = apiKey.trim()
35+
if (trimmedApiKey.length === 0) {
36+
return Effect.fail(new AuthError({ message: "Grok API key must not be empty" }))
37+
}
3438
const accountLabel = normalizeAccountLabel(command.label, "default")
3539
return withGrokAuth(command, ({ accountPath, fs }) =>
3640
Effect.gen(function*(_) {
37-
const trimmedApiKey = apiKey.trim()
3841
const apiKeyFilePath = grokApiKeyPath(accountPath)
3942
yield* _(fs.writeFileString(apiKeyFilePath, `${trimmedApiKey}\n`))
4043
yield* _(fs.chmod(apiKeyFilePath, 0o600))

packages/lib/tests/core/templates.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,11 @@ describe("renderDockerfile", () => {
219219
const dockerfile = renderDockerfile(makeTemplateConfig())
220220

221221
expectContainsAll(dockerfile, [
222-
"npm install -g grok-dev@latest --force",
222+
"npm install -g grok-dev@",
223223
"grok --version"
224224
])
225+
expect(dockerfile).not.toContain("grok-dev@latest")
226+
expect(dockerfile).not.toContain("npm install -g grok-dev@latest --force")
225227
expect(dockerfile).not.toContain("grok --version >/dev/null || true")
226228
})
227229
})

0 commit comments

Comments
 (0)