Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ insta status # 登录态 + 已 link 的 project/branch
| `insta secrets list [--branch]` | 仅列出 secret 名 |
| `insta deploy --image <url> [--branch --group --port]` | 部署镜像 |
| `insta manifest [--json]` | agent 可读的环境清单 |
| `insta metrics <db\|compute> [group] [--branch --from --to --step --json]` | 资源指标(compute=Fly;db 受限) |
| `insta logs <db\|compute> [group] [--branch --limit --region --instance --json]` | 运行时日志(compute=Fly;db 受限) |
| `insta events [--branch --limit --json]` | 审计 + agent 事件时间线 |
| `insta approvals list [--status] [--json]` | 治理审批列表 |
| `insta approvals approve <id> [--always]` / `deny <id>` | 批准 / 拒绝(admin) |
Expand All @@ -65,5 +67,5 @@ cd ../platform && DATABASE_URL='postgres://postgres:insta@localhost:55432/insta_
INSTA_API_URL=http://localhost:8899 insta login --email you@x.com --password ...
```

> v1 覆盖平台已实现的能力。`metrics` / `logs`(observability)与 `usage`(billing)待平台 #4/#5
> 完成后补充;OAuth 浏览器登录、`compute add-group/scale/set-domain`、镜像构建后续加入。
> `metrics` / `logs` 已支持(compute 实时;db 受 provider 限制)。`usage`(billing)待平台 #5 完成后补充;
> OAuth 浏览器登录、`compute add-group/scale/set-domain`、镜像构建后续加入。
34 changes: 34 additions & 0 deletions src/commands/metrics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ApiClient, requireProject } from '../api.js'
import { info, printJson } from '../util.js'

function qs(params: Record<string, string | undefined>): string {
const u = new URLSearchParams()
for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== '') u.set(k, String(v))
const s = u.toString()
return s ? `?${s}` : ''
}

// insta metrics <db|compute> [group]
export async function metrics(component: string, group: string | undefined, opts: { branch?: string; from?: string; to?: string; step?: string; json?: boolean }): Promise<void> {
const api = await ApiClient.load()
const p = await requireProject()
const res = await api.request('GET', `/projects/${p.projectId}/metrics${qs({ component, group, branch: opts.branch ?? p.branch, from: opts.from, to: opts.to, step: opts.step })}`)
if (opts.json) return printJson(res)
if (res.note) info(`note: ${res.note}`)
if (!res.series?.length) return info('(no series)')
for (const s of res.series) {
const last = s.points?.[s.points.length - 1]
info(`${s.name}${s.unit ? ` (${s.unit})` : ''}: ${last ? last[1] : 'n/a'} [${s.points?.length ?? 0} points]`)
}
}

// insta logs <db|compute> [group]
export async function logs(component: string, group: string | undefined, opts: { branch?: string; limit?: string; region?: string; instance?: string; json?: boolean }): Promise<void> {
const api = await ApiClient.load()
const p = await requireProject()
const res = await api.request('GET', `/projects/${p.projectId}/logs${qs({ component, group, branch: opts.branch ?? p.branch, limit: opts.limit, region: opts.region, instance: opts.instance })}`)
if (opts.json) return printJson(res)
if (res.note) info(`note: ${res.note}`)
if (!res.lines?.length) return info('(no logs)')
for (const l of res.lines) info(`${l.ts} ${(l.level ?? '').padEnd(5)} ${l.message}`)
}
9 changes: 9 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { deploy } from './commands/deploy.js'
import { manifest } from './commands/manifest.js'
import * as govern from './commands/govern.js'
import * as observe from './commands/observe.js'
import * as obs from './commands/metrics.js'

function onError(e: unknown): never {
if (e instanceof ApiError) die(`${e.message} (HTTP ${e.status})`)
Expand Down Expand Up @@ -66,6 +67,14 @@ program.command('deploy').description('Deploy a container image to a branch comp
// ---- manifest ----
program.command('manifest').description('Print an agent-legible view of the project environments').option('--json').action(guard((o) => manifest(o)))

// ---- observability ----
program.command('metrics <component> [group]').description('Resource metrics (component: db|compute)')
.option('--branch <b>').option('--from <unix>').option('--to <unix>').option('--step <s>').option('--json')
.action(guard((component, group, o) => obs.metrics(component, group, o)))
program.command('logs <component> [group]').description('Runtime logs (component: db|compute)')
.option('--branch <b>').option('--limit <n>').option('--region <r>').option('--instance <i>').option('--json')
.action(guard((component, group, o) => obs.logs(component, group, o)))

// ---- events (audit timeline) ----
program.command('events').description('Show the audit + agent-event timeline').option('--branch <b>').option('--limit <n>').option('--json').action(guard((o) => govern.events(o)))

Expand Down