wip: guided standalone installation and workspace checks

This commit is contained in:
Codex
2026-09-26 16:41:15 +02:00
parent 0d2e573e0d
commit 67ee52624c
15 changed files with 902 additions and 560 deletions
+62 -2
View File
@@ -40,9 +40,9 @@ When --installation is omitted, tht uses THOTHII_INSTALLATION or discovers one v
descriptor in the current project tree.
Commands:
setup [--configure-only] [--installation-id ID] [--profile local|server]
setup [--complete|--configure-only] [--installation-id ID] [--profile local|server]
[--shell-mode full|embedded] [--shell-default-locale BCP47-TAG] [--shell-adapter omics-portal]
Create or validate the local non-secret installation configuration.
Create, validate, and optionally complete the local installation.
installation migrate --output PATH --session-default PROVIDER/MODEL
--embedding-id PROVIDER/MODEL --embedding-dimensions N
Create a review-only schema-v2 candidate from all three legacy model sources.
@@ -86,6 +86,10 @@ Commands:
Verify a terminal installation, remove stale lifecycle files, and clear maintenance.
pi logs Show the latest 200 sanitized core log lines (bounded; no follow mode).
workspace inspect --workspace ID [--json]
workspace pull [--json]
Pull and activate the configured workspace repository.
workspace test [--json]
Test configured database, Evidence, Qdrant, and embedding connectivity.
workspace evidence consolidate --workspace ID [--json]
workspace evidence refresh --workspace ID [--json]
workspace evidence decide --workspace ID --source-id SHA --revision SHA --decision keep|replace [--json]
@@ -400,6 +404,11 @@ func parseSetupArgs(args []string) (setup.Request, error) {
flag := args[0]
args = args[1:]
switch flag {
case "--complete":
if request.Complete {
return setup.Request{}, errors.New("--complete may be supplied once")
}
request.Complete = true
case "--configure-only":
if request.ConfigureOnly {
return setup.Request{}, errors.New("--configure-only may be supplied once")
@@ -484,6 +493,9 @@ func parseSetupArgs(args []string) (setup.Request, error) {
*target = value
}
}
if request.Complete && request.ConfigureOnly {
return setup.Request{}, errors.New("--complete and --configure-only cannot be combined")
}
return request, nil
}
@@ -499,6 +511,9 @@ func writeRemovalTargets(outputWriter io.Writer, project string, targets []serve
}
func workspaceCommand(ctx context.Context, installation config.Installation, runner compose.Runner, args []string, secretValues []string, stdout, stderr io.Writer) int {
if len(args) > 0 && (args[0] == "pull" || args[0] == "test") {
return workspaceOperatorCommand(ctx, installation, runner, args, secretValues, stdout, stderr)
}
request, err := workspaceops.Parse(args)
if err != nil {
return commandUsageError(stderr, err.Error())
@@ -527,6 +542,51 @@ func workspaceCommand(ctx context.Context, installation config.Installation, run
}
}
func workspaceOperatorCommand(ctx context.Context, installation config.Installation, runner compose.Runner, args []string, secretValues []string, stdout, stderr io.Writer) int {
action := "workspace-" + args[0]
jsonMode := false
for _, arg := range args[1:] {
if arg != "--json" || jsonMode {
return commandUsageError(stderr, "workspace pull/test accepts only --json")
}
jsonMode = true
}
result, err := runner.Run(ctx, installation.ComposeArgs("exec", "-T", "core", "node", "dist/operator-command.js", action), nil)
if err != nil {
return writeResult(result, err, secretValues, stdout, stderr)
}
var payload struct {
Ready bool `json:"ready"`
Status string `json:"status"`
}
if err := json.Unmarshal([]byte(result.Stdout), &payload); err != nil {
fmt.Fprintln(stderr, "tht: workspace operator returned invalid JSON")
return 1
}
if jsonMode {
fmt.Fprintln(stdout, output.Sanitize(result.Stdout, secretValues))
} else {
fmt.Fprintf(stdout, "workspace %s: %s\n", args[0], output.Sanitize(workspaceOperatorSummary(payload), secretValues))
}
if !payload.Ready {
return 1
}
return 0
}
func workspaceOperatorSummary(payload struct {
Ready bool `json:"ready"`
Status string `json:"status"`
}) string {
if payload.Status != "" {
return payload.Status
}
if payload.Ready {
return "ready"
}
return "failed"
}
func workspaceFailure(stderr io.Writer, err error, secretValues []string) int {
message := output.Sanitize(err.Error(), secretValues)
var operationErr *workspaceops.OperationError