@stacksjs/defaults 0.70.366 → 0.70.368
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/ai/skills/stacks-browse/SKILL.md +9 -1
- package/ai/skills/stacks-dashboard/SKILL.md +49 -1
- package/ai/skills/stacks-deploy/SKILL.md +18 -0
- package/ai/skills/stacks-desktop/SKILL.md +11 -2
- package/ai/skills/stacks-stx/SKILL.md +25 -0
- package/app/Actions/Dashboard/Deployments/CreateDeployment.ts +64 -44
- package/app/Actions/Dashboard/Deployments/CreateDeploymentRollback.ts +26 -0
- package/app/Actions/Dashboard/Deployments/PreviewDeployment.ts +39 -0
- package/app/Actions/Dashboard/Deployments/PreviewDeploymentRollback.ts +22 -0
- package/app/Actions/Dashboard/Deployments/deployment-input.test.ts +12 -2
- package/app/Actions/Dashboard/Deployments/deployment-input.ts +5 -3
- package/app/Actions/Dashboard/Deployments/deployment-preview.test.ts +29 -0
- package/app/Actions/Dashboard/Deployments/deployment-preview.ts +41 -0
- package/app/Actions/Dashboard/Deployments/deployment-rollback.test.ts +16 -0
- package/app/Actions/Dashboard/Deployments/deployment-rollback.ts +89 -0
- package/app/Actions/Dashboard/Operations/AuditIndexAction.ts +25 -0
- package/app/Actions/Dashboard/Operations/ChangeApprovalAction.ts +28 -0
- package/app/Actions/Dashboard/Operations/ChangeIndexAction.ts +32 -0
- package/app/Actions/Dashboard/Operations/IncidentIndexAction.ts +32 -0
- package/app/Actions/Dashboard/Operations/IncidentUpdateAction.ts +34 -0
- package/app/Actions/Dashboard/Operations/MigrationApplyAction.ts +30 -0
- package/app/Actions/Dashboard/Operations/MigrationIndexAction.ts +24 -0
- package/app/Actions/Dashboard/Operations/MigrationReconcileAction.ts +23 -0
- package/app/Actions/Dashboard/Operations/RecoveryDestinationStoreAction.ts +91 -0
- package/app/Actions/Dashboard/Operations/RecoveryDestinationTestAction.ts +34 -0
- package/app/Actions/Dashboard/Operations/RecoveryIndexAction.ts +58 -0
- package/app/Actions/Dashboard/Operations/RecoveryPolicyStoreAction.ts +90 -0
- package/app/Actions/Dashboard/Operations/RecoveryProtectAction.ts +32 -0
- package/app/Actions/Dashboard/Operations/RecoveryRestoreAction.ts +72 -0
- package/app/Actions/Dashboard/Operations/RecoveryRetentionAction.ts +17 -0
- package/app/Actions/Dashboard/Operations/RecoveryRunAction.ts +25 -0
- package/app/Actions/Dashboard/Operations/RecoveryVerifyAction.ts +24 -0
- package/app/Actions/Dashboard/Operations/SchedulerIndexAction.ts +34 -0
- package/app/Actions/Dashboard/Operations/SchedulerRunAction.ts +33 -0
- package/app/Actions/Dashboard/Operations/SchedulerToggleAction.ts +36 -0
- package/app/Actions/Dashboard/Operations/control-plane.ts +150 -0
- package/app/Actions/Dashboard/Operations/migration-operations.ts +80 -0
- package/app/Actions/Dashboard/Operations/operations-runtime.ts +11 -0
- package/app/Actions/Dashboard/Operations/recovery-input.test.ts +22 -0
- package/app/Actions/Dashboard/Operations/recovery-input.ts +33 -0
- package/app/Actions/Dashboard/Operations/recovery-runtime.ts +99 -0
- package/app/Actions/Dashboard/Operations/scheduler-operations.test.ts +23 -0
- package/app/Actions/Dashboard/Operations/scheduler-operations.ts +138 -0
- package/functions/deployments.ts +18 -0
- package/functions/operations.ts +361 -0
- package/ide/vscode/package.json +1 -1
- package/package.json +1 -1
- package/resources/components/Dashboard/Ci/CiDashboard.stx +4 -4
- package/resources/components/Dashboard/Deployments/DeploymentList.stx +147 -14
- package/resources/components/Dashboard/Deployments/DeploymentPreviewDialog.stx +118 -0
- package/resources/components/Dashboard/GlobalSearch.stx +87 -91
- package/resources/components/Dashboard/Operations/AuditDashboard.stx +16 -0
- package/resources/components/Dashboard/Operations/ChangeDashboard.stx +66 -0
- package/resources/components/Dashboard/Operations/IncidentDashboard.stx +39 -0
- package/resources/components/Dashboard/Operations/MigrationDashboard.stx +146 -0
- package/resources/components/Dashboard/Operations/OperationHistory.stx +61 -0
- package/resources/components/Dashboard/Operations/OperationsNavigation.stx +32 -0
- package/resources/components/Dashboard/Operations/RecoveryDashboard.stx +229 -0
- package/resources/components/Dashboard/Operations/RecoveryDestinationDialog.stx +126 -0
- package/resources/components/Dashboard/Operations/RecoveryJobList.stx +32 -0
- package/resources/components/Dashboard/Operations/RecoveryPointTable.stx +82 -0
- package/resources/components/Dashboard/Operations/RecoveryPolicyDialog.stx +155 -0
- package/resources/components/Dashboard/Operations/RecoveryRestoreDialog.stx +90 -0
- package/resources/components/Dashboard/Operations/SchedulerDashboard.stx +144 -0
- package/resources/components/Dashboard/Operations/SchedulerTaskTable.stx +81 -0
- package/resources/components/Dashboard/Settings/SettingsDashboard.stx +45 -3
- package/resources/components/Dashboard/UI/Modal.stx +33 -8
- package/resources/functions/dashboard/sidebar.ts +1 -0
- package/routes/dashboard-api.ts +23 -0
- package/routes/dashboard.ts +1 -0
- package/vcs/github/workflows/ci.yml +3 -3
- package/vcs/github/workflows/release.yml +1 -1
- package/views/dashboard/operations/audit.stx +5 -0
- package/views/dashboard/operations/changes.stx +5 -0
- package/views/dashboard/operations/incidents.stx +5 -0
- package/views/dashboard/operations/migrations.stx +8 -0
- package/views/dashboard/operations/recovery.stx +8 -0
- package/views/dashboard/operations/scheduler.stx +8 -0
- package/views/dashboard/stores/ci.ts +2 -2
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import process from 'node:process'
|
|
3
|
+
|
|
4
|
+
export interface SchedulerTask {
|
|
5
|
+
name: string
|
|
6
|
+
pattern: string
|
|
7
|
+
timezone: string
|
|
8
|
+
nextRun: string | null
|
|
9
|
+
enabled: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface BuddyResult {
|
|
13
|
+
stdout: string
|
|
14
|
+
stderr: string
|
|
15
|
+
exitCode: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SchedulerRegistryPayload {
|
|
19
|
+
jobs?: unknown
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class SchedulerOperationError extends Error {
|
|
23
|
+
constructor(message: string) {
|
|
24
|
+
super(message)
|
|
25
|
+
this.name = 'SchedulerOperationError'
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function runBuddy(args: string[], timeoutMs = 30_000): Promise<BuddyResult> {
|
|
30
|
+
const subprocess = Bun.spawn([join(process.cwd(), 'buddy'), ...args], {
|
|
31
|
+
cwd: process.cwd(),
|
|
32
|
+
env: { ...process.env, NO_COLOR: '1' },
|
|
33
|
+
stdout: 'pipe',
|
|
34
|
+
stderr: 'pipe',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
let timeout: ReturnType<typeof setTimeout> | undefined
|
|
38
|
+
const timedOut = new Promise<never>((_resolve, reject) => {
|
|
39
|
+
timeout = setTimeout(() => {
|
|
40
|
+
subprocess.kill()
|
|
41
|
+
reject(new SchedulerOperationError(`buddy ${args[0]} timed out.`))
|
|
42
|
+
}, timeoutMs)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const [stdout, stderr, exitCode] = await Promise.race([
|
|
47
|
+
Promise.all([
|
|
48
|
+
new Response(subprocess.stdout).text(),
|
|
49
|
+
new Response(subprocess.stderr).text(),
|
|
50
|
+
subprocess.exited,
|
|
51
|
+
]),
|
|
52
|
+
timedOut,
|
|
53
|
+
])
|
|
54
|
+
return { stdout, stderr, exitCode }
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
if (timeout)
|
|
58
|
+
clearTimeout(timeout)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function parseSchedulerRegistry(output: string): SchedulerTask[] {
|
|
63
|
+
const marker = 'STACKS_SCHEDULE_JSON='
|
|
64
|
+
const line = output.split(/\r?\n/).find(entry => entry.startsWith(marker))
|
|
65
|
+
if (!line)
|
|
66
|
+
throw new SchedulerOperationError('The scheduler registry did not return machine-readable output.')
|
|
67
|
+
|
|
68
|
+
let payload: SchedulerRegistryPayload
|
|
69
|
+
try {
|
|
70
|
+
payload = JSON.parse(line.slice(marker.length)) as SchedulerRegistryPayload
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new SchedulerOperationError('The scheduler registry returned invalid JSON.')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!Array.isArray(payload.jobs))
|
|
77
|
+
throw new SchedulerOperationError('The scheduler registry response did not include a task list.')
|
|
78
|
+
|
|
79
|
+
return payload.jobs.map((value) => {
|
|
80
|
+
if (!value || typeof value !== 'object')
|
|
81
|
+
throw new SchedulerOperationError('The scheduler registry returned an invalid task.')
|
|
82
|
+
const job = value as Record<string, unknown>
|
|
83
|
+
if (typeof job.name !== 'string' || !job.name.trim())
|
|
84
|
+
throw new SchedulerOperationError('A scheduled task is missing its name.')
|
|
85
|
+
return {
|
|
86
|
+
name: job.name,
|
|
87
|
+
pattern: typeof job.pattern === 'string' ? job.pattern : '',
|
|
88
|
+
timezone: typeof job.timezone === 'string' ? job.timezone : 'UTC',
|
|
89
|
+
nextRun: typeof job.nextRun === 'string' ? job.nextRun : null,
|
|
90
|
+
enabled: job.enabled !== false,
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export async function listSchedulerTasks(): Promise<SchedulerTask[]> {
|
|
96
|
+
const result = await runBuddy(['schedule:list', '--json'])
|
|
97
|
+
if (result.exitCode !== 0)
|
|
98
|
+
throw new SchedulerOperationError(result.stderr.trim() || 'The scheduler registry could not be loaded.')
|
|
99
|
+
return parseSchedulerRegistry(result.stdout)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function requireSchedulerTask(name: string): Promise<SchedulerTask> {
|
|
103
|
+
const normalized = name.trim()
|
|
104
|
+
const task = (await listSchedulerTasks()).find(entry => entry.name === normalized)
|
|
105
|
+
if (!task)
|
|
106
|
+
throw new SchedulerOperationError(`Scheduled task "${normalized}" was not found.`)
|
|
107
|
+
return task
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function runScheduledTask(name: string): Promise<{ message: string, task: string }> {
|
|
111
|
+
const task = await requireSchedulerTask(name)
|
|
112
|
+
if (!task.enabled)
|
|
113
|
+
throw new SchedulerOperationError(`Scheduled task "${task.name}" is paused.`)
|
|
114
|
+
const result = await runBuddy(['schedule:run-one', task.name], 5 * 60_000)
|
|
115
|
+
if (result.exitCode !== 0)
|
|
116
|
+
throw new SchedulerOperationError(result.stderr.trim() || `Scheduled task "${task.name}" failed.`)
|
|
117
|
+
return { message: `Scheduled task ${task.name} completed.`, task: task.name }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function setScheduledTaskEnabled(name: string, enabled: boolean): Promise<{ enabled: boolean, message: string, task: string }> {
|
|
121
|
+
const task = await requireSchedulerTask(name)
|
|
122
|
+
if (task.enabled === enabled) {
|
|
123
|
+
return {
|
|
124
|
+
enabled,
|
|
125
|
+
message: `Scheduled task ${task.name} is already ${enabled ? 'active' : 'paused'}.`,
|
|
126
|
+
task: task.name,
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const result = await runBuddy([enabled ? 'schedule:enable' : 'schedule:disable', task.name])
|
|
131
|
+
if (result.exitCode !== 0)
|
|
132
|
+
throw new SchedulerOperationError(result.stderr.trim() || `Scheduled task "${task.name}" could not be updated.`)
|
|
133
|
+
return {
|
|
134
|
+
enabled,
|
|
135
|
+
message: `Scheduled task ${task.name} ${enabled ? 'resumed' : 'paused'}.`,
|
|
136
|
+
task: task.name,
|
|
137
|
+
}
|
|
138
|
+
}
|
package/functions/deployments.ts
CHANGED
|
@@ -35,6 +35,16 @@ export interface DeploymentTerminal {
|
|
|
35
35
|
exists: boolean
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export interface DeploymentRollbackPlan {
|
|
39
|
+
revision: string
|
|
40
|
+
environment: string
|
|
41
|
+
site: string | null
|
|
42
|
+
release: string | null
|
|
43
|
+
target: string
|
|
44
|
+
output: string
|
|
45
|
+
warnings: string[]
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
export interface DeployScriptUpdate {
|
|
39
49
|
success: boolean
|
|
40
50
|
path: string
|
|
@@ -142,3 +152,11 @@ export async function updateDeployScript(content: string): Promise<DeployScriptU
|
|
|
142
152
|
export async function fetchDeploymentTerminal(): Promise<DeploymentTerminal> {
|
|
143
153
|
return dashboardApi<DeploymentTerminal>('/api/dashboard/deployments/terminal')
|
|
144
154
|
}
|
|
155
|
+
|
|
156
|
+
export function previewDeploymentRollback(input: { environment: string, site?: string, release?: string }): Promise<{ success: boolean, plan: DeploymentRollbackPlan }> {
|
|
157
|
+
return dashboardApi('/api/dashboard/deployments/rollback/preview', { method: 'POST', body: input })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function runDeploymentRollback(input: { environment: string, site?: string, release?: string, revision: string, confirmation: string }): Promise<{ success: boolean, message: string }> {
|
|
161
|
+
return dashboardApi('/api/dashboard/deployments/rollback', { method: 'POST', body: input })
|
|
162
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { dashboardApi } from './dashboard-api'
|
|
2
|
+
|
|
3
|
+
export interface SchedulerTask {
|
|
4
|
+
name: string
|
|
5
|
+
pattern: string
|
|
6
|
+
timezone: string
|
|
7
|
+
nextRun: string | null
|
|
8
|
+
enabled: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface OperatorOperation {
|
|
12
|
+
id: string
|
|
13
|
+
actorName: string
|
|
14
|
+
kind: string
|
|
15
|
+
state: 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'timed_out'
|
|
16
|
+
input: Record<string, unknown>
|
|
17
|
+
output: Record<string, unknown>
|
|
18
|
+
error?: string
|
|
19
|
+
startedAt?: string
|
|
20
|
+
finishedAt?: string
|
|
21
|
+
createdAt: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SchedulerOperationsResponse {
|
|
25
|
+
tasks: SchedulerTask[]
|
|
26
|
+
operations: OperatorOperation[]
|
|
27
|
+
summary: {
|
|
28
|
+
total: number
|
|
29
|
+
active: number
|
|
30
|
+
paused: number
|
|
31
|
+
nextRun: string | null
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SchedulerMutationResponse {
|
|
36
|
+
success: boolean
|
|
37
|
+
message: string
|
|
38
|
+
task: string
|
|
39
|
+
enabled?: boolean
|
|
40
|
+
operation: OperatorOperation
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function fetchSchedulerOperations(): Promise<SchedulerOperationsResponse> {
|
|
44
|
+
return dashboardApi<SchedulerOperationsResponse>('/api/dashboard/operations/scheduler')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function runSchedulerTask(name: string): Promise<SchedulerMutationResponse> {
|
|
48
|
+
return dashboardApi<SchedulerMutationResponse>(`/api/dashboard/operations/scheduler/${encodeURIComponent(name)}/run`, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function updateSchedulerTask(name: string, enabled: boolean): Promise<SchedulerMutationResponse> {
|
|
54
|
+
return dashboardApi<SchedulerMutationResponse>(`/api/dashboard/operations/scheduler/${encodeURIComponent(name)}`, {
|
|
55
|
+
method: 'PATCH',
|
|
56
|
+
body: { enabled },
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type BackupResourceKind = 'managed_database' | 'logical_database' | 'volume' | 'files' | 'control_plane' | 'infrastructure'
|
|
61
|
+
|
|
62
|
+
export interface RecoveryDestination {
|
|
63
|
+
id: string
|
|
64
|
+
name: string
|
|
65
|
+
provider: 'aws_s3' | 's3_compatible' | 'aws_backup'
|
|
66
|
+
bucket?: string
|
|
67
|
+
region?: string
|
|
68
|
+
status: 'untested' | 'healthy' | 'failing' | 'disabled'
|
|
69
|
+
credentialsConfigured: boolean
|
|
70
|
+
clientEncryptionConfigured: boolean
|
|
71
|
+
lastTestedAt?: string
|
|
72
|
+
lastError?: string
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface RecoveryPolicy {
|
|
76
|
+
id: string
|
|
77
|
+
destinationId: string
|
|
78
|
+
name: string
|
|
79
|
+
resourceKind: BackupResourceKind
|
|
80
|
+
schedule: string
|
|
81
|
+
timezone: string
|
|
82
|
+
expectedRpoMinutes: number
|
|
83
|
+
expectedRtoMinutes: number
|
|
84
|
+
enabled: boolean
|
|
85
|
+
nextRunAt?: string
|
|
86
|
+
lastRunAt?: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface RecoveryPoint {
|
|
90
|
+
id: string
|
|
91
|
+
policyId?: string
|
|
92
|
+
kind: BackupResourceKind
|
|
93
|
+
pointInTime: string
|
|
94
|
+
sizeBytes: number
|
|
95
|
+
status: 'pending' | 'available' | 'failed' | 'deleting' | 'deleted'
|
|
96
|
+
verificationState: 'unverified' | 'verifying' | 'verified' | 'corrupt' | 'failed'
|
|
97
|
+
verifiedAt?: string
|
|
98
|
+
held: boolean
|
|
99
|
+
pinned: boolean
|
|
100
|
+
expiresAt?: string
|
|
101
|
+
lockedUntil?: string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface RecoveryJob {
|
|
105
|
+
id: string
|
|
106
|
+
policyId?: string
|
|
107
|
+
recoveryPointId?: string
|
|
108
|
+
operationId?: string
|
|
109
|
+
kind: 'backup' | 'restore' | 'verify' | 'drill' | 'cleanup'
|
|
110
|
+
status: 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'cleanup_required'
|
|
111
|
+
progress: Record<string, unknown>
|
|
112
|
+
error?: string
|
|
113
|
+
startedAt?: string
|
|
114
|
+
finishedAt?: string
|
|
115
|
+
createdAt: string
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface RecoveryCoverage {
|
|
119
|
+
policy: RecoveryPolicy
|
|
120
|
+
lastRecoveryPoint?: RecoveryPoint
|
|
121
|
+
missedRpo: boolean
|
|
122
|
+
unverified: number
|
|
123
|
+
destinationHealthy: boolean
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface RecoveryResource {
|
|
127
|
+
id: string
|
|
128
|
+
kind: string
|
|
129
|
+
name: string
|
|
130
|
+
slug: string
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface RecoveryDataService {
|
|
134
|
+
id: string
|
|
135
|
+
name: string
|
|
136
|
+
engine: string
|
|
137
|
+
provider: string
|
|
138
|
+
status: string
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface RecoveryOperationsResponse {
|
|
142
|
+
environment: { id: string, name: string, slug: string }
|
|
143
|
+
coverage: RecoveryCoverage[]
|
|
144
|
+
destinations: RecoveryDestination[]
|
|
145
|
+
policies: RecoveryPolicy[]
|
|
146
|
+
recoveryPoints: RecoveryPoint[]
|
|
147
|
+
jobs: RecoveryJob[]
|
|
148
|
+
resources: RecoveryResource[]
|
|
149
|
+
dataServices: RecoveryDataService[]
|
|
150
|
+
summary: {
|
|
151
|
+
policies: number
|
|
152
|
+
protected: number
|
|
153
|
+
missedRpo: number
|
|
154
|
+
unverified: number
|
|
155
|
+
queuedJobs: number
|
|
156
|
+
latestPoint: string | null
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface RecoveryDestinationInput {
|
|
161
|
+
name: string
|
|
162
|
+
provider: RecoveryDestination['provider']
|
|
163
|
+
bucket?: string
|
|
164
|
+
endpoint?: string
|
|
165
|
+
prefix?: string
|
|
166
|
+
region?: string
|
|
167
|
+
allowPrivate?: boolean
|
|
168
|
+
forcePathStyle?: boolean
|
|
169
|
+
encryption: 'provider' | 'client_side' | 'both'
|
|
170
|
+
encryptionKey?: string
|
|
171
|
+
lockDays?: number
|
|
172
|
+
credentials?: { accessKeyId?: string, secretAccessKey?: string, sessionToken?: string }
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface RecoveryPolicyInput {
|
|
176
|
+
name: string
|
|
177
|
+
destinationId: string
|
|
178
|
+
resourceKind: BackupResourceKind
|
|
179
|
+
resourceId?: string
|
|
180
|
+
dataServiceId?: string
|
|
181
|
+
includePatterns: string[]
|
|
182
|
+
excludePatterns: string[]
|
|
183
|
+
schedule: string
|
|
184
|
+
timezone: string
|
|
185
|
+
keepLast: number
|
|
186
|
+
expireAfterDays: number
|
|
187
|
+
compression: 'none' | 'gzip' | 'zstd'
|
|
188
|
+
expectedRpoMinutes: number
|
|
189
|
+
expectedRtoMinutes: number
|
|
190
|
+
enabled: boolean
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface RestorePlanInput {
|
|
194
|
+
mode: 'isolated' | 'in_place'
|
|
195
|
+
targetName: string
|
|
196
|
+
target?: Record<string, unknown>
|
|
197
|
+
drill?: boolean
|
|
198
|
+
execute?: boolean
|
|
199
|
+
confirm?: string
|
|
200
|
+
downtimeAcknowledged?: boolean
|
|
201
|
+
safetyBackupId?: string
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function fetchRecoveryOperations(): Promise<RecoveryOperationsResponse> {
|
|
205
|
+
return dashboardApi<RecoveryOperationsResponse>('/api/dashboard/operations/recovery')
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export function createRecoveryDestination(input: RecoveryDestinationInput): Promise<{ success: boolean, destination: RecoveryDestination }> {
|
|
209
|
+
return dashboardApi('/api/dashboard/operations/recovery/destinations', { method: 'POST', body: input })
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function testRecoveryDestination(id: string): Promise<{ success: boolean, message: string, destination: RecoveryDestination }> {
|
|
213
|
+
return dashboardApi(`/api/dashboard/operations/recovery/destinations/${encodeURIComponent(id)}/test`, { method: 'POST' })
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function createRecoveryPolicy(input: RecoveryPolicyInput): Promise<{ success: boolean, policy: RecoveryPolicy }> {
|
|
217
|
+
return dashboardApi('/api/dashboard/operations/recovery/policies', { method: 'POST', body: input })
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function runRecoveryPolicy(id: string): Promise<{ success: boolean, message: string, job: RecoveryJob }> {
|
|
221
|
+
return dashboardApi(`/api/dashboard/operations/recovery/policies/${encodeURIComponent(id)}/run`, { method: 'POST' })
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function verifyRecoveryPoint(id: string): Promise<{ success: boolean, message: string, job: RecoveryJob }> {
|
|
225
|
+
return dashboardApi(`/api/dashboard/operations/recovery/points/${encodeURIComponent(id)}/verify`, { method: 'POST' })
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function planRecoveryRestore(id: string, input: RestorePlanInput): Promise<{ success: boolean, plan: { mode: string, target: Record<string, unknown>, warnings: string[] }, productionExecutionCreated: boolean }> {
|
|
229
|
+
return dashboardApi(`/api/dashboard/operations/recovery/points/${encodeURIComponent(id)}/restore`, { method: 'POST', body: input })
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function protectRecoveryPoint(id: string, input: { pinned?: boolean, held?: boolean }): Promise<{ success: boolean, recoveryPoint: RecoveryPoint }> {
|
|
233
|
+
return dashboardApi(`/api/dashboard/operations/recovery/points/${encodeURIComponent(id)}/protection`, { method: 'PATCH', body: input })
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function runRecoveryRetention(): Promise<{ success: boolean, message: string, jobs: RecoveryJob[] }> {
|
|
237
|
+
return dashboardApi('/api/dashboard/operations/recovery/retention', { method: 'POST' })
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export interface MigrationPlanOperation {
|
|
241
|
+
kind: string
|
|
242
|
+
table: string
|
|
243
|
+
column?: string
|
|
244
|
+
sql?: string
|
|
245
|
+
destructive: boolean
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export interface MigrationLedgerEntry {
|
|
249
|
+
file: string
|
|
250
|
+
status: string
|
|
251
|
+
recorded: boolean
|
|
252
|
+
effects: Array<{ kind: string, table?: string, name: string }>
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface MigrationOperationsResponse {
|
|
256
|
+
environment: string
|
|
257
|
+
dialect: string
|
|
258
|
+
operations: MigrationPlanOperation[]
|
|
259
|
+
revision: string
|
|
260
|
+
applied: number
|
|
261
|
+
ledger: {
|
|
262
|
+
supported: boolean
|
|
263
|
+
drift: boolean
|
|
264
|
+
entries: MigrationLedgerEntry[]
|
|
265
|
+
orphans: Array<{ migration: string, renamedTo?: string }>
|
|
266
|
+
counts: Record<string, number>
|
|
267
|
+
}
|
|
268
|
+
reconciliation: Record<string, unknown>
|
|
269
|
+
operatorOperations: OperatorOperation[]
|
|
270
|
+
summary: { pending: number, destructive: number, drift: boolean, ledgerIssues: number }
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function fetchMigrationOperations(): Promise<MigrationOperationsResponse> {
|
|
274
|
+
return dashboardApi('/api/dashboard/operations/migrations')
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function applyMigrationOperations(revision: string, confirmation: string): Promise<{ success: boolean, message: string, operation: OperatorOperation }> {
|
|
278
|
+
return dashboardApi('/api/dashboard/operations/migrations/apply', { method: 'POST', body: { revision, confirmation } })
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function reconcileMigrationLedger(revision: string, confirmation: string): Promise<{ success: boolean, result: Record<string, unknown>, operation: OperatorOperation }> {
|
|
282
|
+
return dashboardApi('/api/dashboard/operations/migrations/reconcile', { method: 'POST', body: { revision, confirmation } })
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export interface ReleaseApprovalItem {
|
|
286
|
+
id: string
|
|
287
|
+
sourceSha?: string
|
|
288
|
+
kind: string
|
|
289
|
+
strategy: string
|
|
290
|
+
createdAt: string
|
|
291
|
+
approvals: Array<{ decision: string, comment?: string, createdAt: string }>
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export interface ChangeOperationsResponse {
|
|
295
|
+
environment?: { id: string, name: string, slug: string }
|
|
296
|
+
migrations: MigrationOperationsResponse
|
|
297
|
+
pendingReleases: ReleaseApprovalItem[]
|
|
298
|
+
activeOperations: OperatorOperation[]
|
|
299
|
+
recentOperations: OperatorOperation[]
|
|
300
|
+
summary: { migrationChanges: number, destructiveChanges: number, approvals: number, activeOperations: number }
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function fetchChangeOperations(): Promise<ChangeOperationsResponse> {
|
|
304
|
+
return dashboardApi('/api/dashboard/operations/changes')
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export function decideRelease(id: string, decision: 'approved' | 'rejected', comment = ''): Promise<{ success: boolean }> {
|
|
308
|
+
return dashboardApi(`/api/dashboard/operations/changes/releases/${encodeURIComponent(id)}/decision`, { method: 'POST', body: { decision, comment } })
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export interface IncidentAlert {
|
|
312
|
+
id: string
|
|
313
|
+
state: string
|
|
314
|
+
severity: string
|
|
315
|
+
title: string
|
|
316
|
+
occurrenceCount: number
|
|
317
|
+
ownerName?: string
|
|
318
|
+
acknowledgedAt?: string
|
|
319
|
+
silencedUntil?: string
|
|
320
|
+
firstSeenAt: string
|
|
321
|
+
lastSeenAt: string
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export interface IncidentOperationsResponse {
|
|
325
|
+
alerts: IncidentAlert[]
|
|
326
|
+
rules: Array<{ id: string, name: string, severity: string, enabled: boolean }>
|
|
327
|
+
healthChecks: Array<{ id: string, name: string, kind: string, target: string, enabled: boolean }>
|
|
328
|
+
channels: Array<{ id: string, name: string, kind: string, status: string }>
|
|
329
|
+
deliveries: Array<Record<string, unknown>>
|
|
330
|
+
operations: OperatorOperation[]
|
|
331
|
+
summary: { firing: number, critical: number, acknowledged: number, rules: number }
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export function fetchIncidentOperations(): Promise<IncidentOperationsResponse> {
|
|
335
|
+
return dashboardApi('/api/dashboard/operations/incidents')
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function updateIncident(id: string, input: { action: 'acknowledge' | 'assign_self' | 'unassign' | 'silence', until?: string }): Promise<{ success: boolean, alert: IncidentAlert }> {
|
|
339
|
+
return dashboardApi(`/api/dashboard/operations/incidents/${encodeURIComponent(id)}`, { method: 'PATCH', body: input })
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export interface AuditEvent {
|
|
343
|
+
id: string
|
|
344
|
+
sequence: number
|
|
345
|
+
operationId?: string
|
|
346
|
+
actorName: string
|
|
347
|
+
type: string
|
|
348
|
+
level: string
|
|
349
|
+
payload: unknown
|
|
350
|
+
createdAt: string
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export interface AuditOperationsResponse {
|
|
354
|
+
events: AuditEvent[]
|
|
355
|
+
operations: OperatorOperation[]
|
|
356
|
+
summary: { events: number, operators: number, failures: number, latestSequence: number }
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function fetchAuditOperations(): Promise<AuditOperationsResponse> {
|
|
360
|
+
return dashboardApi('/api/dashboard/operations/audit')
|
|
361
|
+
}
|
package/ide/vscode/package.json
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.368",
|
|
6
6
|
"description": "The complete managed Stacks application scaffold, including runtime defaults, AI guidance, editor metadata, and npm-backed project support files.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"license": "MIT",
|
|
@@ -263,13 +263,13 @@
|
|
|
263
263
|
<span class="text-xs text-zinc-400" :text="repo.commitAuthor"></span>
|
|
264
264
|
</div>
|
|
265
265
|
|
|
266
|
-
<div :if="repo.
|
|
266
|
+
<div :if="repo.buddyBotPRs > 0 || repo.actionsPRs > 0" class="mt-2 pt-2 border-t border-zinc-200/60 dark:border-zinc-800">
|
|
267
267
|
<div class="mb-1 text-xs text-zinc-400">Pull Requests</div>
|
|
268
268
|
<div class="flex flex-wrap gap-x-3 gap-y-0.5">
|
|
269
|
-
<a :if="repo.
|
|
270
|
-
|
|
269
|
+
<a :if="repo.buddyBotPRs > 0" :href="repo.buddyBotPRsUrl" target="_blank" rel="noreferrer" @click.stop class="no-underline text-xs text-zinc-500 dark:text-zinc-400 hover:underline">
|
|
270
|
+
buddy-bot: <span :text="repo.buddyBotPRs"></span>
|
|
271
271
|
</a>
|
|
272
|
-
<a :if="repo.actionsPRs > 0" :href="repo.actionsPRsUrl" target="_blank" @click.stop class="no-underline text-xs text-zinc-500 dark:text-zinc-400 hover:underline">
|
|
272
|
+
<a :if="repo.actionsPRs > 0" :href="repo.actionsPRsUrl" target="_blank" rel="noreferrer" @click.stop class="no-underline text-xs text-zinc-500 dark:text-zinc-400 hover:underline">
|
|
273
273
|
github-actions: <span :text="repo.actionsPRs"></span>
|
|
274
274
|
</a>
|
|
275
275
|
</div>
|