- add finalize-task-routing skill with policy and test scripts - add execution target selection scripts and tests for orchestrate-agent-task-loop - add quota probe adapter and CLI status for node app - update plan, finalize-task-routing, and orchestration skill files - add agent-task archive for runtime target selector - update stream-evidence-gate-core milestone
107 lines
3 KiB
Go
107 lines
3 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"iop/apps/node/internal/adapters/cli/status"
|
|
"iop/packages/go/config"
|
|
)
|
|
|
|
var quotaCheckUsage = status.CheckUsage
|
|
|
|
func quotaProbeCmd() *cobra.Command {
|
|
var target string
|
|
var command string
|
|
var requiredCaps []string
|
|
var checkedAt string
|
|
c := &cobra.Command{
|
|
Use: "quota-probe",
|
|
Short: "Emit normalized CLI quota evidence for the dispatcher",
|
|
Hidden: true,
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
target = strings.TrimSpace(target)
|
|
command = strings.TrimSpace(command)
|
|
if target == "" {
|
|
return fmt.Errorf("--target must be non-empty")
|
|
}
|
|
if command == "" {
|
|
return fmt.Errorf("--command must be non-empty")
|
|
}
|
|
caps, err := normalizeRequiredCaps(requiredCaps)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
at, err := parseQuotaCheckedAt(checkedAt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
adapter := quotaAdapter(command)
|
|
usage, checkErr := quotaCheckUsage(cmd.Context(), target, config.CLIProfileConf{Command: command})
|
|
snapshot := status.NormalizeQuotaSnapshot(adapter, target, caps, at, usage, checkErr)
|
|
return json.NewEncoder(cmd.OutOrStdout()).Encode(snapshot)
|
|
},
|
|
}
|
|
c.Flags().StringVar(&target, "target", "", "canonical execution target")
|
|
c.Flags().StringVar(&command, "command", "", "CLI command used for usage checking")
|
|
c.Flags().StringSliceVar(&requiredCaps, "required-cap", nil, "required cap: overall or model:<name> (repeatable)")
|
|
c.Flags().StringVar(&checkedAt, "checked-at", "", "RFC3339 observation time (defaults to now)")
|
|
_ = c.MarkFlagRequired("target")
|
|
_ = c.MarkFlagRequired("command")
|
|
return c
|
|
}
|
|
|
|
func normalizeRequiredCaps(values []string) ([]string, error) {
|
|
if len(values) == 0 {
|
|
return nil, fmt.Errorf("at least one --required-cap is required")
|
|
}
|
|
caps := make([]string, 0, len(values))
|
|
seen := make(map[string]struct{}, len(values))
|
|
for _, value := range values {
|
|
cap := strings.TrimSpace(value)
|
|
if cap != "overall" {
|
|
model, ok := strings.CutPrefix(cap, "model:")
|
|
if !ok || strings.TrimSpace(model) == "" {
|
|
return nil, fmt.Errorf("invalid required cap %q; use overall or model:<name>", value)
|
|
}
|
|
cap = "model:" + strings.TrimSpace(model)
|
|
}
|
|
key := strings.ToLower(cap)
|
|
if _, exists := seen[key]; exists {
|
|
return nil, fmt.Errorf("duplicate required cap %q", value)
|
|
}
|
|
seen[key] = struct{}{}
|
|
caps = append(caps, cap)
|
|
}
|
|
return caps, nil
|
|
}
|
|
|
|
func parseQuotaCheckedAt(value string) (time.Time, error) {
|
|
if value == "" {
|
|
return time.Now().UTC(), nil
|
|
}
|
|
parsed, err := time.Parse(time.RFC3339, value)
|
|
if err != nil {
|
|
return time.Time{}, fmt.Errorf("invalid --checked-at: %w", err)
|
|
}
|
|
return parsed.UTC(), nil
|
|
}
|
|
|
|
func quotaAdapter(command string) string {
|
|
switch strings.ToLower(filepath.Base(command)) {
|
|
case "agy", "antigravity":
|
|
return "agy"
|
|
case "claude":
|
|
return "claude"
|
|
case "codex":
|
|
return "codex"
|
|
default:
|
|
return filepath.Base(command)
|
|
}
|
|
}
|