mirror of
https://github.com/stablyai/orca.git
synced 2026-10-17 00:03:08 +00:00
Here is a summary of how the sandbox behaves on your macOS system: ### ⚙️ How it Works When `--sandbox` is enabled (either via the launch flag or the `enableTerminalSandbox` setting in your `settings.json`), terminal commands run inside a lightweight containment boundary: - **macOS Native Isolation**: It utilizes macOS's native `sandbox-exec` utility to restrict system calls, network sockets, and directory access. - **Secure File Boundaries**: File system writes are locked down to designated safe zones (such as your designated workspace or scratch directory). Access to critical system paths, private user data, and external network resources is restricted. --- ### 🛡️ Active Permissions for this Session In this current session, the permission model is configured as follows: | Action / Resource | Permission Status | Details / Paths | | :--- | :--- | :--- | | **Command Execution** | ✅ **Allowed** | Terminal command execution is enabled. | | **File Reads (Allowed)** | ✅ **Allowed** | `/scratch`, `/browser_recordings`, `/html_artifacts`, `/knowledge`, `/worktrees`, `/skills`, `/builtin` | | **File Writes (Allowed)**| ✅ **Allowed** | `/scratch`, `/browser_recordings`, `/html_artifacts`, `/knowledge`, `/worktrees` | | **Sensitive Files** | ⚠️ **Ask** | `.env`, `.npmrc`, `.vscode`, `.git-credentials`, etc. | | **Root/App Settings** | 🚫 **Denied** | Direct modifications to `/config` and main `.gemini` configurations | --- ### 🔧 Configuration and Management * **Persistent Settings**: To enable sandboxing by default for all future sessions, configure the `enableTerminalSandbox` setting in your `~/.gemini/antigravity-cli/settings.json`: ```json { "enableTerminalSandbox": true } ``` * **Dynamic Adjustments**: Within an active CLI (`agy`) session, you can run the `/permissions` slash command to view or modify your autonomy and sandboxing levels on the fly. > [!NOTE] > Running in sandbox mode provides an excellent balance of autonomy and security, allowing me to execute build commands, run test scripts, and manage project files safely without risk to your primary host environment. Please let me know if you would like me to set up a new project workspace or run any specific tasks within this session!
198 lines
5.8 KiB
TypeScript
198 lines
5.8 KiB
TypeScript
import type {
|
|
CheckStatus,
|
|
GitHubAssignableUser,
|
|
GitHubPRCheckSummary,
|
|
GitHubPRMergeMethod,
|
|
GitHubPRMergeMethodSettings,
|
|
GitHubPRReviewSummary,
|
|
GitHubRepositoryIdentity,
|
|
PRCheckDetail,
|
|
PRMergeableState,
|
|
PRReviewDecision,
|
|
PRState
|
|
} from '../../../src/shared/types'
|
|
import type { HostedReviewProvider } from '../../../src/shared/hosted-review'
|
|
|
|
// Primitive + enum value readers shared by the github.* PR parsers. Each narrows
|
|
// `unknown` defensively (never throws) so RPC payloads can be parsed safely.
|
|
|
|
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return typeof value === 'object' && value !== null
|
|
}
|
|
|
|
export function readString(value: unknown): string | undefined {
|
|
return typeof value === 'string' ? value : undefined
|
|
}
|
|
|
|
export function readNumber(value: unknown): number | undefined {
|
|
return typeof value === 'number' && Number.isFinite(value) ? value : undefined
|
|
}
|
|
|
|
export function readBoolean(value: unknown): boolean | undefined {
|
|
return typeof value === 'boolean' ? value : undefined
|
|
}
|
|
|
|
export function readStringArray(value: unknown): string[] {
|
|
if (!Array.isArray(value)) {
|
|
return []
|
|
}
|
|
return value.flatMap((entry): string[] => {
|
|
const str = readString(entry)
|
|
return str === undefined ? [] : [str]
|
|
})
|
|
}
|
|
|
|
export function readProvider(value: unknown): HostedReviewProvider | undefined {
|
|
return value === 'github' ||
|
|
value === 'gitlab' ||
|
|
value === 'bitbucket' ||
|
|
value === 'azure-devops' ||
|
|
value === 'gitea' ||
|
|
value === 'unsupported'
|
|
? value
|
|
: undefined
|
|
}
|
|
|
|
export function readPRState(value: unknown): PRState | null {
|
|
return value === 'open' || value === 'closed' || value === 'merged' || value === 'draft'
|
|
? value
|
|
: null
|
|
}
|
|
|
|
export function readCheckStatus(value: unknown): CheckStatus {
|
|
return value === 'pending' || value === 'success' || value === 'failure' || value === 'neutral'
|
|
? value
|
|
: 'pending'
|
|
}
|
|
|
|
export function readMergeableState(value: unknown): PRMergeableState | undefined {
|
|
return value === 'MERGEABLE' || value === 'CONFLICTING' || value === 'UNKNOWN' ? value : undefined
|
|
}
|
|
|
|
export function readReviewDecision(value: unknown): PRReviewDecision | null | undefined {
|
|
if (value === null) {
|
|
return null
|
|
}
|
|
return value === 'APPROVED' || value === 'CHANGES_REQUESTED' || value === 'REVIEW_REQUIRED'
|
|
? value
|
|
: undefined
|
|
}
|
|
|
|
export function readCheckRunStatus(value: unknown): PRCheckDetail['status'] | null {
|
|
return value === 'queued' || value === 'in_progress' || value === 'completed' ? value : null
|
|
}
|
|
|
|
export function readCheckRunConclusion(value: unknown): PRCheckDetail['conclusion'] {
|
|
return value === 'success' ||
|
|
value === 'failure' ||
|
|
value === 'cancelled' ||
|
|
value === 'timed_out' ||
|
|
value === 'neutral' ||
|
|
value === 'skipped' ||
|
|
value === 'pending'
|
|
? value
|
|
: null
|
|
}
|
|
|
|
export function readAssignableUser(value: unknown): GitHubAssignableUser | null {
|
|
if (!isRecord(value)) {
|
|
return null
|
|
}
|
|
const login = readString(value.login)
|
|
if (login === undefined) {
|
|
return null
|
|
}
|
|
return {
|
|
login,
|
|
name: readString(value.name) ?? null,
|
|
avatarUrl: readString(value.avatarUrl) ?? ''
|
|
}
|
|
}
|
|
|
|
export function readAssignableUserArray(value: unknown): GitHubAssignableUser[] {
|
|
if (!Array.isArray(value)) {
|
|
return []
|
|
}
|
|
return value.flatMap((entry): GitHubAssignableUser[] => {
|
|
const parsed = readAssignableUser(entry)
|
|
return parsed ? [parsed] : []
|
|
})
|
|
}
|
|
|
|
export function readReviewSummary(value: unknown): GitHubPRReviewSummary | null {
|
|
if (!isRecord(value)) {
|
|
return null
|
|
}
|
|
// Desktop maps latestReviews to top-level `login`. Raw `gh pr view --json`
|
|
// keeps nested `author.login` — accept both so mobile never drops reviewers.
|
|
const nestedAuthor = isRecord(value.author) ? value.author : null
|
|
const login =
|
|
readString(value.login) ?? (nestedAuthor ? readString(nestedAuthor.login) : undefined)
|
|
if (login === undefined) {
|
|
return null
|
|
}
|
|
const avatarUrl =
|
|
readString(value.avatarUrl) ??
|
|
(nestedAuthor
|
|
? (readString(nestedAuthor.avatarUrl) ?? readString(nestedAuthor.avatar_url) ?? null)
|
|
: null)
|
|
return {
|
|
login,
|
|
state: readString(value.state) ?? null,
|
|
avatarUrl
|
|
}
|
|
}
|
|
|
|
export function readRepoIdentity(value: unknown): GitHubRepositoryIdentity | undefined {
|
|
if (!isRecord(value)) {
|
|
return undefined
|
|
}
|
|
const owner = readString(value.owner)
|
|
const repo = readString(value.repo)
|
|
// Empty owner/repo is malformed, not a valid identity — drop it before it reaches prRepo parsing.
|
|
if (!owner || !repo) {
|
|
return undefined
|
|
}
|
|
return { owner, repo }
|
|
}
|
|
|
|
function readMergeMethod(value: unknown): GitHubPRMergeMethod | undefined {
|
|
return value === 'merge' || value === 'squash' || value === 'rebase' ? value : undefined
|
|
}
|
|
|
|
export function readMergeMethodSettings(value: unknown): GitHubPRMergeMethodSettings | undefined {
|
|
if (!isRecord(value)) {
|
|
return undefined
|
|
}
|
|
const defaultMethod = readMergeMethod(value.defaultMethod)
|
|
if (defaultMethod === undefined || !isRecord(value.allowedMethods)) {
|
|
return undefined
|
|
}
|
|
const allowed = value.allowedMethods
|
|
return {
|
|
defaultMethod,
|
|
allowedMethods: {
|
|
merge: readBoolean(allowed.merge) ?? false,
|
|
squash: readBoolean(allowed.squash) ?? false,
|
|
rebase: readBoolean(allowed.rebase) ?? false
|
|
}
|
|
}
|
|
}
|
|
|
|
export function readCheckSummary(value: unknown): GitHubPRCheckSummary | undefined {
|
|
if (!isRecord(value)) {
|
|
return undefined
|
|
}
|
|
const state = value.state
|
|
if (state !== 'success' && state !== 'failure' && state !== 'pending' && state !== 'none') {
|
|
return undefined
|
|
}
|
|
return {
|
|
state,
|
|
total: readNumber(value.total) ?? 0,
|
|
passed: readNumber(value.passed) ?? 0,
|
|
failed: readNumber(value.failed) ?? 0,
|
|
pending: readNumber(value.pending) ?? 0
|
|
}
|
|
}
|