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.
Files changed (76) hide show
  1. package/README.md +22 -13
  2. package/dist/artifact-archive.js +152 -0
  3. package/dist/connectors-cli.js +80 -0
  4. package/dist/credentials.js +4 -1
  5. package/dist/daytona-slice-rehearsal.js +255 -0
  6. package/dist/daytona-slice.js +441 -0
  7. package/dist/egress-allowlist-cli.js +132 -0
  8. package/dist/egress-policy.js +159 -0
  9. package/dist/git-credential.js +131 -0
  10. package/dist/github-setup.js +226 -0
  11. package/dist/help.js +276 -21
  12. package/dist/hosted-bootstrap-rehearsal.js +235 -0
  13. package/dist/hosted-bootstrap.js +365 -0
  14. package/dist/hosted-broker-server.js +220 -0
  15. package/dist/hosted-git-http-server.js +136 -0
  16. package/dist/hosted-harness-claude.js +493 -0
  17. package/dist/hosted-harness-opencode.js +729 -0
  18. package/dist/hosted-harness.js +60 -0
  19. package/dist/hosted-loop.js +477 -0
  20. package/dist/hosted-run-event-sink.js +499 -0
  21. package/dist/hosted-runtime-assets.js +216 -0
  22. package/dist/hosted-session-cli.js +642 -0
  23. package/dist/index.js +945 -258
  24. package/dist/kill-switch-cli.js +89 -0
  25. package/dist/manifests-cli.js +143 -0
  26. package/dist/preview-runtime.js +4 -1
  27. package/dist/sandbox-provider.js +238 -0
  28. package/dist/secret-redaction.js +112 -0
  29. package/dist/session-timeline.js +103 -0
  30. package/dist/setup-prompt.js +4 -2
  31. package/dist/skill-installer.js +9 -1
  32. package/dist/smoke.js +250 -0
  33. package/dist/task-assignment-manifest.js +96 -0
  34. package/dist/vercel-sandbox-provider.js +260 -0
  35. package/dist/workbench-cli.js +493 -0
  36. package/dist/workspace-sync.js +924 -0
  37. package/dist/workspace.js +868 -57
  38. package/package.json +4 -1
  39. package/src/artifact-archive.ts +182 -0
  40. package/src/connectors-cli.ts +115 -0
  41. package/src/credentials.ts +6 -2
  42. package/src/daytona-slice-rehearsal.ts +320 -0
  43. package/src/daytona-slice.ts +598 -0
  44. package/src/egress-allowlist-cli.ts +147 -0
  45. package/src/egress-policy.ts +188 -0
  46. package/src/git-credential.ts +155 -0
  47. package/src/github-setup.ts +366 -0
  48. package/src/help.ts +277 -21
  49. package/src/hosted-bootstrap-rehearsal.ts +312 -0
  50. package/src/hosted-bootstrap.ts +571 -0
  51. package/src/hosted-broker-server.ts +278 -0
  52. package/src/hosted-git-http-server.ts +164 -0
  53. package/src/hosted-harness-claude.ts +621 -0
  54. package/src/hosted-harness-opencode.ts +823 -0
  55. package/src/hosted-harness.ts +124 -0
  56. package/src/hosted-loop.ts +680 -0
  57. package/src/hosted-run-event-sink.ts +632 -0
  58. package/src/hosted-runtime-assets.ts +272 -0
  59. package/src/hosted-session-cli.ts +844 -0
  60. package/src/index.ts +1102 -317
  61. package/src/kill-switch-cli.ts +113 -0
  62. package/src/manifests-cli.ts +168 -0
  63. package/src/preview-runtime.ts +14 -9
  64. package/src/sandbox-provider.ts +440 -0
  65. package/src/secret-redaction.ts +124 -0
  66. package/src/session-timeline.ts +152 -0
  67. package/src/setup-prompt.ts +4 -2
  68. package/src/skill-installer.ts +13 -1
  69. package/src/smoke.ts +342 -0
  70. package/src/task-assignment-manifest.ts +164 -0
  71. package/src/vercel-sandbox-provider.ts +388 -0
  72. package/src/workbench-cli.ts +701 -0
  73. package/src/workspace-sync.ts +1148 -0
  74. package/src/workspace.ts +1051 -64
  75. package/vendor/skills/orizu-cli/SKILL.md +8 -4
  76. 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
+ }
@@ -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(): any {
680
+ function createPreviewSnapshotPlugin(): Plugin {
676
681
  const modules = buildPreviewSnapshotModules()
677
682
  return {
678
683
  name: 'orizu-preview-runtime-snapshot',
679
- setup(build: any) {
680
- build.onResolve({ filter: /^@\/components\// }, (args: any) => {
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: any) => {
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: any) {
950
+ } catch (error: unknown) {
946
951
  server.removeAllListeners('error')
947
- if (error?.code !== 'EADDRINUSE') throw 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<any> {
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
  )