orizu 0.4.0 → 0.5.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.
- package/README.md +22 -13
- package/dist/artifact-archive.js +152 -0
- package/dist/connectors-cli.js +80 -0
- package/dist/credentials.js +4 -1
- package/dist/daytona-slice-rehearsal.js +255 -0
- package/dist/daytona-slice.js +441 -0
- package/dist/egress-allowlist-cli.js +132 -0
- package/dist/egress-policy.js +159 -0
- package/dist/git-credential.js +131 -0
- package/dist/github-setup.js +226 -0
- package/dist/help.js +276 -21
- package/dist/hosted-bootstrap-rehearsal.js +235 -0
- package/dist/hosted-bootstrap.js +365 -0
- package/dist/hosted-broker-server.js +220 -0
- package/dist/hosted-git-http-server.js +136 -0
- package/dist/hosted-harness-claude.js +493 -0
- package/dist/hosted-harness-opencode.js +729 -0
- package/dist/hosted-harness.js +60 -0
- package/dist/hosted-loop.js +477 -0
- package/dist/hosted-run-event-sink.js +499 -0
- package/dist/hosted-runtime-assets.js +216 -0
- package/dist/hosted-session-cli.js +642 -0
- package/dist/index.js +945 -258
- package/dist/kill-switch-cli.js +89 -0
- package/dist/manifests-cli.js +143 -0
- package/dist/preview-runtime.js +4 -1
- package/dist/sandbox-provider.js +238 -0
- package/dist/secret-redaction.js +112 -0
- package/dist/session-timeline.js +103 -0
- package/dist/setup-prompt.js +4 -2
- package/dist/skill-installer.js +9 -1
- package/dist/smoke.js +250 -0
- package/dist/task-assignment-manifest.js +96 -0
- package/dist/vercel-sandbox-provider.js +260 -0
- package/dist/workbench-cli.js +493 -0
- package/dist/workspace-sync.js +924 -0
- package/dist/workspace.js +868 -57
- package/package.json +4 -1
- package/src/artifact-archive.ts +182 -0
- package/src/connectors-cli.ts +115 -0
- package/src/credentials.ts +6 -2
- package/src/daytona-slice-rehearsal.ts +320 -0
- package/src/daytona-slice.ts +598 -0
- package/src/egress-allowlist-cli.ts +147 -0
- package/src/egress-policy.ts +188 -0
- package/src/git-credential.ts +155 -0
- package/src/github-setup.ts +366 -0
- package/src/help.ts +277 -21
- package/src/hosted-bootstrap-rehearsal.ts +312 -0
- package/src/hosted-bootstrap.ts +571 -0
- package/src/hosted-broker-server.ts +278 -0
- package/src/hosted-git-http-server.ts +164 -0
- package/src/hosted-harness-claude.ts +621 -0
- package/src/hosted-harness-opencode.ts +823 -0
- package/src/hosted-harness.ts +124 -0
- package/src/hosted-loop.ts +680 -0
- package/src/hosted-run-event-sink.ts +632 -0
- package/src/hosted-runtime-assets.ts +272 -0
- package/src/hosted-session-cli.ts +844 -0
- package/src/index.ts +1102 -317
- package/src/kill-switch-cli.ts +113 -0
- package/src/manifests-cli.ts +168 -0
- package/src/preview-runtime.ts +14 -9
- package/src/sandbox-provider.ts +440 -0
- package/src/secret-redaction.ts +124 -0
- package/src/session-timeline.ts +152 -0
- package/src/setup-prompt.ts +4 -2
- package/src/skill-installer.ts +13 -1
- package/src/smoke.ts +342 -0
- package/src/task-assignment-manifest.ts +164 -0
- package/src/vercel-sandbox-provider.ts +388 -0
- package/src/workbench-cli.ts +701 -0
- package/src/workspace-sync.ts +1148 -0
- package/src/workspace.ts +1051 -64
- package/vendor/skills/orizu-cli/SKILL.md +8 -4
- package/vendor/skills/orizu-cli/references/cli-reference.md +12 -4
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-team agent kill switch CLI (ALI-1007 / G6):
|
|
3
|
+
* orizu team kill-agents <team> [--reason <text>]
|
|
4
|
+
* orizu team release-agents <team>
|
|
5
|
+
*
|
|
6
|
+
* `kill-agents` ENGAGES the switch — ends every active session of the team and
|
|
7
|
+
* revokes its agent tokens "in seconds"; `release-agents` re-enables minting
|
|
8
|
+
* (killed sessions stay ended). Human ADMIN only: the server rejects agent
|
|
9
|
+
* bearers and non-admins. Pure command logic with an injected fetcher so the
|
|
10
|
+
* entry point owns argument parsing and human/JSON output.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { authedFetch } from './http.js'
|
|
14
|
+
|
|
15
|
+
export type CliFetcher = (path: string, init?: RequestInit) => Promise<Response>
|
|
16
|
+
|
|
17
|
+
export interface KillSwitchCommandIo {
|
|
18
|
+
json: boolean
|
|
19
|
+
print: (line: string) => void
|
|
20
|
+
printErr?: (line: string) => void
|
|
21
|
+
fetcher?: CliFetcher
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function responseMessage(response: Response): Promise<string> {
|
|
25
|
+
try {
|
|
26
|
+
const data = (await response.clone().json()) as Record<string, unknown>
|
|
27
|
+
const error = typeof data.error === 'string' ? data.error : null
|
|
28
|
+
return error || response.statusText || String(response.status)
|
|
29
|
+
} catch {
|
|
30
|
+
return response.statusText || String(response.status)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function fetcherFrom(io: KillSwitchCommandIo): CliFetcher {
|
|
35
|
+
return io.fetcher ?? authedFetch
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function argValue(args: readonly string[], flag: string): string | null {
|
|
39
|
+
const index = args.indexOf(flag)
|
|
40
|
+
if (index === -1 || index + 1 >= args.length || args[index + 1].startsWith('--')) {
|
|
41
|
+
return null
|
|
42
|
+
}
|
|
43
|
+
return args[index + 1]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function firstPositional(args: readonly string[]): string | null {
|
|
47
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
48
|
+
const arg = args[i]
|
|
49
|
+
if (arg.startsWith('--')) {
|
|
50
|
+
if (argValue(args, arg) !== null) {
|
|
51
|
+
i += 1
|
|
52
|
+
}
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
return arg
|
|
56
|
+
}
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function emit(io: KillSwitchCommandIo, payload: Record<string, unknown>, human: string): void {
|
|
61
|
+
io.print(io.json ? JSON.stringify(payload) : human)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function printErr(io: KillSwitchCommandIo, message: string): void {
|
|
65
|
+
;(io.printErr ?? io.print)(message)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function killSwitchCommand(args: string[], io: KillSwitchCommandIo): Promise<number> {
|
|
69
|
+
const subcommand = args[0]
|
|
70
|
+
|
|
71
|
+
if (subcommand !== 'kill-agents' && subcommand !== 'release-agents') {
|
|
72
|
+
printErr(io, 'Usage: orizu team <kill-agents|release-agents> <team> [--reason <text>] [--json]')
|
|
73
|
+
return 1
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const teamSlug = firstPositional(args.slice(1))
|
|
77
|
+
if (!teamSlug) {
|
|
78
|
+
printErr(io, `Usage: orizu team ${subcommand} <team>${subcommand === 'kill-agents' ? ' [--reason <text>]' : ''} [--json]`)
|
|
79
|
+
return 1
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const path = `/api/cli/teams/${encodeURIComponent(teamSlug)}/agent-kill-switch`
|
|
83
|
+
|
|
84
|
+
if (subcommand === 'kill-agents') {
|
|
85
|
+
const reason = argValue(args, '--reason')
|
|
86
|
+
const response = await fetcherFrom(io)(path, {
|
|
87
|
+
method: 'POST',
|
|
88
|
+
headers: { 'Content-Type': 'application/json' },
|
|
89
|
+
body: JSON.stringify(reason ? { reason } : {}),
|
|
90
|
+
})
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
printErr(io, `kill-agents failed (${response.status}): ${await responseMessage(response)}`)
|
|
93
|
+
return 1
|
|
94
|
+
}
|
|
95
|
+
const data = (await response.json()) as Record<string, unknown>
|
|
96
|
+
emit(
|
|
97
|
+
io,
|
|
98
|
+
data,
|
|
99
|
+
`agent kill switch ENGAGED for ${teamSlug} ` +
|
|
100
|
+
`(${data.sessionsEnded ?? 0} sessions ended, ${data.tokensRevoked ?? 0} tokens revoked)`
|
|
101
|
+
)
|
|
102
|
+
return 0
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const response = await fetcherFrom(io)(path, { method: 'DELETE' })
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
printErr(io, `release-agents failed (${response.status}): ${await responseMessage(response)}`)
|
|
108
|
+
return 1
|
|
109
|
+
}
|
|
110
|
+
const data = (await response.json()) as Record<string, unknown>
|
|
111
|
+
emit(io, data, `agent kill switch RELEASED for ${teamSlug} (killed sessions stay ended)`)
|
|
112
|
+
return 0
|
|
113
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion manifests CLI (ALI-907): list | show | approve | reject | apply.
|
|
3
|
+
* Pure command logic with an injected fetcher and project resolver; the entry
|
|
4
|
+
* point owns argument parsing and human/JSON output. apply is idempotent on the
|
|
5
|
+
* server — re-applying returns the stored outcome.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { authedFetch } from './http.js'
|
|
9
|
+
|
|
10
|
+
export type CliFetcher = (path: string, init?: RequestInit) => Promise<Response>
|
|
11
|
+
|
|
12
|
+
export interface ManifestsCommandIo {
|
|
13
|
+
json: boolean
|
|
14
|
+
print: (line: string) => void
|
|
15
|
+
fetcher?: CliFetcher
|
|
16
|
+
resolveProjectSlug?: (arg: string | null) => Promise<string>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const MANIFEST_ACTIONS = new Set(['approve', 'reject', 'apply'])
|
|
20
|
+
|
|
21
|
+
async function responseMessage(response: Response): Promise<string> {
|
|
22
|
+
try {
|
|
23
|
+
const data = (await response.clone().json()) as Record<string, unknown>
|
|
24
|
+
const error = typeof data.error === 'string' ? data.error : null
|
|
25
|
+
const message = typeof data.message === 'string' ? data.message : null
|
|
26
|
+
return error || message || response.statusText || String(response.status)
|
|
27
|
+
} catch {
|
|
28
|
+
return response.statusText || String(response.status)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function requireOk(response: Response, action: string): Promise<Record<string, unknown>> {
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`${action} failed (${response.status}): ${await responseMessage(response)}`)
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return (await response.json()) as Record<string, unknown>
|
|
38
|
+
} catch {
|
|
39
|
+
return {}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function fetcherFrom(io: ManifestsCommandIo): CliFetcher {
|
|
44
|
+
return io.fetcher ?? authedFetch
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function argValue(args: string[], flag: string): string | null {
|
|
48
|
+
const index = args.indexOf(flag)
|
|
49
|
+
if (index === -1 || index + 1 >= args.length || args[index + 1].startsWith('--')) {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
return args[index + 1]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function positionalArgs(args: string[]): string[] {
|
|
56
|
+
const values: string[] = []
|
|
57
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
58
|
+
const arg = args[i]
|
|
59
|
+
if (arg.startsWith('--')) {
|
|
60
|
+
if (argValue(args, arg) !== null) {
|
|
61
|
+
i += 1
|
|
62
|
+
}
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
values.push(arg)
|
|
66
|
+
}
|
|
67
|
+
return values
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function requireProjectSlug(io: ManifestsCommandIo, args: string[]): Promise<string> {
|
|
71
|
+
if (!io.resolveProjectSlug) {
|
|
72
|
+
throw new Error('Project resolver unavailable')
|
|
73
|
+
}
|
|
74
|
+
return io.resolveProjectSlug(argValue(args, '--project'))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function emit(io: ManifestsCommandIo, payload: Record<string, unknown>, human: string) {
|
|
78
|
+
io.print(io.json ? JSON.stringify(payload) : human)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function manifestLine(manifest: Record<string, unknown>): string {
|
|
82
|
+
const id = typeof manifest.id === 'string' ? manifest.id : '(unknown)'
|
|
83
|
+
const status = typeof manifest.status === 'string' ? manifest.status : '(unknown)'
|
|
84
|
+
const actionType = typeof manifest.actionType === 'string' ? manifest.actionType : '(unknown)'
|
|
85
|
+
return `${id} ${status} ${actionType}`
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function asRecord(value: unknown): Record<string, unknown> {
|
|
89
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function num(value: unknown): number {
|
|
93
|
+
return typeof value === 'number' ? value : 0
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// repo_merge manifests (ALI-972) render the compare summary from stored evidence
|
|
97
|
+
// — no live GitHub call — so a reviewer can approve informed without a PR.
|
|
98
|
+
function repoMergeShow(manifest: Record<string, unknown>): string {
|
|
99
|
+
const proposed = asRecord(manifest.proposedState)
|
|
100
|
+
const compare = asRecord(asRecord(manifest.evidence).compare)
|
|
101
|
+
const branch = typeof proposed.branch === 'string' ? proposed.branch : '(branch)'
|
|
102
|
+
const defaultBranch = typeof proposed.defaultBranch === 'string' ? proposed.defaultBranch : '(default)'
|
|
103
|
+
const files = num(compare.filesChanged || proposed.filesChanged)
|
|
104
|
+
const additions = num(compare.additions ?? proposed.additions)
|
|
105
|
+
const deletions = num(compare.deletions ?? proposed.deletions)
|
|
106
|
+
const lines = [
|
|
107
|
+
manifestLine(manifest),
|
|
108
|
+
` ${branch} -> ${defaultBranch}`,
|
|
109
|
+
` ${files} files, +${additions}/-${deletions}`,
|
|
110
|
+
]
|
|
111
|
+
const compareFiles = Array.isArray(compare.files) ? (compare.files as Record<string, unknown>[]) : []
|
|
112
|
+
for (const file of compareFiles.slice(0, 100)) {
|
|
113
|
+
const status = typeof file.status === 'string' ? file.status : 'modified'
|
|
114
|
+
const filename = typeof file.filename === 'string' ? file.filename : '(file)'
|
|
115
|
+
lines.push(` ${status} ${filename} (+${num(file.additions)}/-${num(file.deletions)})`)
|
|
116
|
+
}
|
|
117
|
+
return lines.join('\n')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function manifestsCommand(args: string[], io: ManifestsCommandIo): Promise<number> {
|
|
121
|
+
const positional = positionalArgs(args)
|
|
122
|
+
const subcommand = positional[0]
|
|
123
|
+
|
|
124
|
+
if (subcommand === 'list') {
|
|
125
|
+
const projectSlug = await requireProjectSlug(io, args)
|
|
126
|
+
const status = argValue(args, '--status')
|
|
127
|
+
const query = status
|
|
128
|
+
? `?project=${encodeURIComponent(projectSlug)}&status=${encodeURIComponent(status)}`
|
|
129
|
+
: `?project=${encodeURIComponent(projectSlug)}`
|
|
130
|
+
const response = await fetcherFrom(io)(`/api/cli/promotion-manifests${query}`)
|
|
131
|
+
const data = await requireOk(response, 'Manifest list')
|
|
132
|
+
const manifests = Array.isArray(data.manifests) ? (data.manifests as Record<string, unknown>[]) : []
|
|
133
|
+
emit(io, { manifests }, ['promotion manifests', ...manifests.map(manifestLine)].join('\n'))
|
|
134
|
+
return 0
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (subcommand === 'show') {
|
|
138
|
+
const id = positional[1]
|
|
139
|
+
if (!id) {
|
|
140
|
+
throw new Error('Usage: orizu manifests show <id> [--json]')
|
|
141
|
+
}
|
|
142
|
+
const response = await fetcherFrom(io)(`/api/cli/promotion-manifests/${encodeURIComponent(id)}`)
|
|
143
|
+
const data = await requireOk(response, 'Manifest show')
|
|
144
|
+
const manifest = (data.manifest ?? {}) as Record<string, unknown>
|
|
145
|
+
const human = manifest.actionType === 'repo_merge' ? repoMergeShow(manifest) : manifestLine(manifest)
|
|
146
|
+
emit(io, { manifest }, human)
|
|
147
|
+
return 0
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (subcommand && MANIFEST_ACTIONS.has(subcommand)) {
|
|
151
|
+
const id = positional[1]
|
|
152
|
+
if (!id) {
|
|
153
|
+
throw new Error(`Usage: orizu manifests ${subcommand} <id> [--json]`)
|
|
154
|
+
}
|
|
155
|
+
const response = await fetcherFrom(io)(`/api/cli/promotion-manifests/${encodeURIComponent(id)}`, {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: { 'Content-Type': 'application/json' },
|
|
158
|
+
body: JSON.stringify({ action: subcommand }),
|
|
159
|
+
})
|
|
160
|
+
const data = await requireOk(response, `Manifest ${subcommand}`)
|
|
161
|
+
const manifest = (data.manifest ?? {}) as Record<string, unknown>
|
|
162
|
+
emit(io, { manifest }, manifestLine(manifest))
|
|
163
|
+
return 0
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
io.print('Usage: orizu manifests <list|show|approve|reject|apply> ... [--json]')
|
|
167
|
+
return 1
|
|
168
|
+
}
|
package/src/preview-runtime.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { tmpdir } from 'os'
|
|
12
12
|
import { basename, dirname, isAbsolute, join, relative, resolve } from 'path'
|
|
13
13
|
import { fileURLToPath, pathToFileURL } from 'url'
|
|
14
|
+
import type { Plugin } from 'esbuild'
|
|
14
15
|
|
|
15
16
|
export const PREVIEW_RUNTIME_REGISTRY_VERSION = 1
|
|
16
17
|
|
|
@@ -112,6 +113,10 @@ interface PreviewPage {
|
|
|
112
113
|
off?: (event: string, listener: () => void) => unknown
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
function isNodeError(error: unknown): error is NodeJS.ErrnoException {
|
|
117
|
+
return error instanceof Error
|
|
118
|
+
}
|
|
119
|
+
|
|
115
120
|
const SUPPORTED_SCHEMA_KEYS = new Set(['type', 'required', 'properties', 'items', 'enum'])
|
|
116
121
|
|
|
117
122
|
export function validatePreviewInputs(options: AppPreviewOptions): Issue[] {
|
|
@@ -672,18 +677,18 @@ function buildSnapshotTailwindCss() {
|
|
|
672
677
|
`
|
|
673
678
|
}
|
|
674
679
|
|
|
675
|
-
function createPreviewSnapshotPlugin():
|
|
680
|
+
function createPreviewSnapshotPlugin(): Plugin {
|
|
676
681
|
const modules = buildPreviewSnapshotModules()
|
|
677
682
|
return {
|
|
678
683
|
name: 'orizu-preview-runtime-snapshot',
|
|
679
|
-
setup(build
|
|
680
|
-
build.onResolve({ filter: /^@\/components\// }, (args
|
|
684
|
+
setup(build) {
|
|
685
|
+
build.onResolve({ filter: /^@\/components\// }, (args) => {
|
|
681
686
|
if (modules[args.path]) {
|
|
682
687
|
return { path: args.path, namespace: 'orizu-preview-snapshot' }
|
|
683
688
|
}
|
|
684
689
|
return null
|
|
685
690
|
})
|
|
686
|
-
build.onLoad({ filter: /.*/, namespace: 'orizu-preview-snapshot' }, (args
|
|
691
|
+
build.onLoad({ filter: /.*/, namespace: 'orizu-preview-snapshot' }, (args) => {
|
|
687
692
|
const contents = modules[args.path]
|
|
688
693
|
if (!contents) {
|
|
689
694
|
return null
|
|
@@ -942,19 +947,19 @@ async function listenOnAvailablePort(server: ReturnType<typeof createServer>): P
|
|
|
942
947
|
})
|
|
943
948
|
})
|
|
944
949
|
return port
|
|
945
|
-
} catch (error:
|
|
950
|
+
} catch (error: unknown) {
|
|
946
951
|
server.removeAllListeners('error')
|
|
947
|
-
if (error
|
|
952
|
+
if (!isNodeError(error) || error.code !== 'EADDRINUSE') throw error
|
|
948
953
|
}
|
|
949
954
|
}
|
|
950
955
|
throw new Error('Unable to find an available localhost port for app preview.')
|
|
951
956
|
}
|
|
952
957
|
|
|
953
|
-
async function loadPlaywright(): Promise<
|
|
958
|
+
async function loadPlaywright(): Promise<PreviewPlaywright> {
|
|
954
959
|
const fromTest = await import('@playwright/test').catch(() => null)
|
|
955
|
-
if (fromTest?.chromium) return fromTest
|
|
960
|
+
if (fromTest?.chromium) return fromTest as unknown as PreviewPlaywright
|
|
956
961
|
const fromPlaywright = await import('playwright').catch(() => null)
|
|
957
|
-
if (fromPlaywright?.chromium) return fromPlaywright
|
|
962
|
+
if (fromPlaywright?.chromium) return fromPlaywright as unknown as PreviewPlaywright
|
|
958
963
|
throw new Error(
|
|
959
964
|
'Local preview requires Playwright. Install project dependencies with `bun install`, then rerun `orizu apps preview`.'
|
|
960
965
|
)
|