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
19 changes: 19 additions & 0 deletions .github/workflows/sync-commands.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: πŸ”„ Sync commands to ws-meta
on:
push:
branches:
- main
paths:
- commands.yaml

jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: πŸ”„ Sync to ws-meta
uses: kloudkit/ws-meta/.github/actions/sync@main
with:
token: ${{ secrets.KLOUD_BOT_ORG_PAT }}
files: |
commands.yaml
1 change: 1 addition & 0 deletions cmd/clip/clip.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ import (
var ClipCmd = &cobra.Command{
Use: "clip",
Short: "Interact with the native clipboard",
Long: "Reach the browser clipboard from the terminal over the workspace IPC socket.",
}
1 change: 1 addition & 0 deletions cmd/clip/paste.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
var pasteCmd = &cobra.Command{
Use: "paste",
Short: "Paste clipboard content",
Long: "Read the browser clipboard over the workspace IPC socket and write it to stdout β€” redirect it to a file or pipe it onward. Pairs with the pbcopy/xclip/xsel shims for terminal clipboard access.",
RunE: func(cmd *cobra.Command, args []string) error {
return clipboard.Paste(cmd.OutOrStdout())
},
Expand Down
31 changes: 31 additions & 0 deletions cmd/commands_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cmd_test

import (
"os"
"path/filepath"
"testing"

"github.com/kloudkit/ws-cli/cmd"
"github.com/kloudkit/ws-cli/internals/docs"
"gotest.tools/v3/assert"
)

func TestCommandsManifestMatchesCommittedFile(t *testing.T) {
want, err := os.ReadFile(filepath.Join("..", "commands.yaml"))
assert.NilError(t, err)

got, err := docs.Serialize(cmd.RootCmd())
assert.NilError(t, err)

assert.Equal(t, string(got), string(want))
}

func TestSerializeIsDeterministic(t *testing.T) {
first, err := docs.Serialize(cmd.RootCmd())
assert.NilError(t, err)

second, err := docs.Serialize(cmd.RootCmd())
assert.NilError(t, err)

assert.Equal(t, string(first), string(second))
}
1 change: 1 addition & 0 deletions cmd/editor/diagnostics.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
var diagnosticsCmd = &cobra.Command{
Use: "diagnostics",
Short: "Show language diagnostics for the workspace (or a single file)",
Long: "Pull language-server diagnostics (errors, warnings) from the editor over the IPC socket, across the whole workspace or a single file with --uri. Styled table by default, --raw for the JSON.",
RunE: func(cmd *cobra.Command, args []string) error {
uri, _ := cmd.Flags().GetString("uri")

Expand Down
1 change: 1 addition & 0 deletions cmd/editor/editor.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ var errNoEditorOverSSH = errors.New(
var EditorCmd = &cobra.Command{
Use: "editor",
Short: "Inspect and drive the active editor session",
Long: "Query and control the running VS Code / code-server window over the workspace IPC socket β€” list open tabs, read diagnostics and the current selection, or open a file. Blocked over SSH, where there is no browser editor to reach.",
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
if env.IsSSHSession() {
return errNoEditorOverSSH
Expand Down
1 change: 1 addition & 0 deletions cmd/editor/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
var listCmd = &cobra.Command{
Use: "list",
Short: "List the currently open editor tabs",
Long: "List the editor's open tabs over the IPC socket, with each tab's path, language, and active or dirty state.",
RunE: func(cmd *cobra.Command, args []string) error {
body, err := editoripc.FetchEditors()
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions cmd/editor/open.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var openCmd = &cobra.Command{
Use: "open <file>",
Short: "Open a file in the editor",
Long: "Open a file in the running editor window over the workspace IPC socket β€” a tab in the current window by default, a separate one with --new-window, jumping to a range with --selection. Fails fast over SSH, where there is no browser editor to open into.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
newWindow, _ := cmd.Flags().GetBool("new-window")
Expand Down
1 change: 1 addition & 0 deletions cmd/editor/selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
var selectionCmd = &cobra.Command{
Use: "selection",
Short: "Show the active editor's current selection",
Long: "Report the active editor's current selection β€” file, range, and selected text β€” over the IPC socket. Empty when nothing is selected.",
RunE: func(cmd *cobra.Command, args []string) error {
body, err := editoripc.FetchSelection()
if err != nil {
Expand Down
1 change: 1 addition & 0 deletions cmd/feature/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
var FeatureCmd = &cobra.Command{
Use: "feature",
Short: "Install additional pre-configured features",
Long: "Install and inspect optional workspace features β€” Ansible playbooks that add tools on top of the base image. Ships a curated set; --root points at your own under ~/.ws/features.d.",
}

func featureDirs(cmd *cobra.Command) []string {
Expand Down
1 change: 1 addition & 0 deletions cmd/feature/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
var infoCmd = &cobra.Command{
Use: "info <name>",
Short: "Show detailed information about a feature",
Long: "Show a feature's description and the variables it accepts, so you know what --opt values install will take.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
featureName := args[0]
Expand Down
1 change: 1 addition & 0 deletions cmd/feature/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var skippableSections = []struct {
var installCmd = &cobra.Command{
Use: "install",
Short: "Install additional pre-configured features",
Long: "Run a feature's playbook to install it. Pass variables with --opt KEY=VAL, and skip parts you do not want with --skip-extensions, --skip-completion, or --skip-repository.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
featureName := args[0]
Expand Down
1 change: 1 addition & 0 deletions cmd/feature/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
var listCmd = &cobra.Command{
Use: "list",
Short: "List available features that can be installed",
Long: "List the features you can install, marking where each comes from β€” the shipped set, a workspace override, or your own ~/.ws/features.d.",
Aliases: []string{"ls"},
RunE: func(cmd *cobra.Command, args []string) error {
result, err := features.ListFeatures(featureDirs(cmd))
Expand Down
1 change: 1 addition & 0 deletions cmd/feature/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var storeCmd = &cobra.Command{
Use: "store",
Short: "List packages available in the feature store",
Long: "List the artifacts published to the feature store (WS_FEATURES_STORE_URL) β€” the offline mirror features install from when the network is locked down.",
RunE: func(cmd *cobra.Command, args []string) error {
storeURL, _ := config.Resolve("features", "store_url")
if storeURL == "" {
Expand Down
1 change: 1 addition & 0 deletions cmd/info/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func showEnvironment(writer io.Writer) {
var envCmd = &cobra.Command{
Use: "env",
Short: "Display effective workspace environment variables",
Long: "Print every WS_* variable in effect, sorted β€” the resolved environment the workspace booted with.",
RunE: func(cmd *cobra.Command, args []string) error {
showEnvironment(cmd.OutOrStdout())
return nil
Expand Down
1 change: 1 addition & 0 deletions cmd/info/extensions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var extensionsCmd = &cobra.Command{
Use: "extensions",
Short: "Display installed extensions",
Long: "List the installed VS Code extensions with their versions.",
RunE: func(cmd *cobra.Command, args []string) error {
extensions, _ := config.GetExtensions()

Expand Down
2 changes: 2 additions & 0 deletions cmd/info/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ func showVersion(writer io.Writer) {
var InfoCmd = &cobra.Command{
Use: "info",
Short: "Display workspace information",
Long: "Report facts about the running workspace β€” version, effective environment, installed extensions, live resource metrics, and uptime.",
}

var showVersionCmd = &cobra.Command{
Use: "version",
Short: "Display installed workspace version",
Long: "Print the workspace version. --all expands to the full table β€” workspace, ws-cli, and VS Code.",
Run: func(cmd *cobra.Command, args []string) {
if all, _ := cmd.Flags().GetBool("all"); all {
showVersion(cmd.OutOrStdout())
Expand Down
1 change: 1 addition & 0 deletions cmd/info/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var metricsCmd = &cobra.Command{
Use: "metrics",
Short: "Display workspace metrics",
Long: "Show live resource usage β€” CPU, memory, disk, and file descriptors, plus GPU with --gpu.",
RunE: func(cmd *cobra.Command, args []string) error {
includeGPU, _ := cmd.Flags().GetBool("gpu")

Expand Down
1 change: 1 addition & 0 deletions cmd/info/uptime.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var uptimeCmd = &cobra.Command{
Use: "uptime",
Short: "Display the workspace uptime",
Long: "Show when the workspace session started and how long it has been running.",
RunE: func(cmd *cobra.Command, args []string) error {
started, running, err := config.GetSessionInfo()

Expand Down
3 changes: 3 additions & 0 deletions cmd/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
var LogCmd = &cobra.Command{
Use: "log",
Short: "Log messages",
Long: "Emit styled, level-tagged log lines β€” the same formatting the startup scripts use. --pipe runs each line of piped input through the logger.",
}

var debugCmd = createCommand("debug", "debugging")
Expand All @@ -19,6 +20,7 @@ var warnCmd = createCommand("warn", "warning")
var stampCmd = &cobra.Command{
Use: "stamp",
Short: "Log the current timestamp",
Long: "Print just the current timestamp in the workspace log style β€” handy for marking phases in a startup log.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
withPipe, _ := cmd.Flags().GetBool("pipe")
Expand All @@ -36,6 +38,7 @@ func createCommand(short, long string) *cobra.Command {
cmd := &cobra.Command{
Use: fmt.Sprintf("%s message", short),
Short: fmt.Sprintf("Log %s messages", long),
Long: fmt.Sprintf("Emit a log line at %s level in the workspace style β€” the same formatting the startup scripts use. --indent nests it under a preceding line, --stamp prefixes a timestamp.", short),
Args: validate,
RunE: execute(short),
}
Expand Down
1 change: 1 addition & 0 deletions cmd/logs/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var validLogTargets = []string{"main", "metrics", "docker", "auth_proxy", "cloud
var LogsCmd = &cobra.Command{
Use: "logs",
Short: "Retrieve workspace logs",
Long: "Read a workspace daemon's log β€” the main log by default, or --target metrics|docker|auth_proxy|cloudflared. Filter by --level, limit with --tail, or stream live with --follow.",
Args: cobra.NoArgs,
RunE: execute,
}
Expand Down
5 changes: 5 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
var rootCmd = &cobra.Command{
Use: "ws-cli",
Short: "⚑ CLI companion to charge the workspace batteries",
Long: "The workspace command-line companion. Groups helpers for inspecting the running workspace, managing settings and secrets, driving the editor, and serving local assets β€” most are called by the startup scripts, all are yours in the terminal.",
Version: "v" + info.Version,
Aliases: []string{"ws"},
SilenceErrors: true,
Expand All @@ -32,6 +33,10 @@ var rootCmd = &cobra.Command{
},
}

func RootCmd() *cobra.Command {
return rootCmd
}

func Execute() {
ctx := context.Background()

Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/decrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
var decryptCmd = &cobra.Command{
Use: "decrypt <encrypted|->",
Short: "Decrypt an encrypted value",
Long: "Decrypt a value produced by encrypt, under the master key. Reads from the argument or stdin (-); writes the plaintext to stdout, or a file with --output.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg := getOutputConfig(cmd)
Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/encrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var encryptCmd = &cobra.Command{
Use: "encrypt <plaintext|->",
Short: "Encrypt a plaintext value",
Long: "Encrypt a value under the master key. Reads the plaintext from the argument or stdin (-); writes the ciphertext to stdout, or a file with --output.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
cfg := getOutputConfig(cmd)
Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "github.com/spf13/cobra"
var generateCmd = &cobra.Command{
Use: "generate",
Short: "Generate master keys or login password hashes",
Long: "Generate the credentials the workspace needs β€” a master key for secrets, or a login password hash for the server.",
}

func init() {
Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
var loginCmd = &cobra.Command{
Use: "login",
Short: "Generate a workspace password hash for authentication",
Long: "Prompt for a password and print its hash for the workspace server login (WS_AUTH_PASSWORD_HASHED). Store the hash, never the password.",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cfg := getOutputConfig(cmd)
Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/master.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
var masterCmd = &cobra.Command{
Use: "master",
Short: "Generate a cryptographically secure master key",
Long: "Generate a random master key, printed base64-encoded β€” the key encrypt, decrypt, and the seed engine use. --length sets the byte size (default 32).",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
cfg := getOutputConfig(cmd)
Expand Down
1 change: 1 addition & 0 deletions cmd/secrets/secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
var SecretsCmd = &cobra.Command{
Use: "secrets",
Short: "Manage encryption and decryption of secrets",
Long: "Encrypt and decrypt values under a master key, and generate the keys themselves. Encrypted values are what the seed engine's secrets: map stores and decrypts at boot.",
}

func init() {
Expand Down
1 change: 1 addition & 0 deletions cmd/seed/apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var applyCmd = &cobra.Command{
Use: "apply [dest...]",
Short: "Project seed content onto the filesystem",
Long: "Apply the seed source to the filesystem β€” mirror bare files and run the .seed.yaml operations. Writes only where the destination is missing unless --force; pass destinations to limit the run to those paths.",
SilenceUsage: true,
RunE: runApply,
}
Expand Down
1 change: 1 addition & 0 deletions cmd/seed/ls.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
var lsCmd = &cobra.Command{
Use: "ls",
Short: "List seed destinations and their behaviors",
Long: "List what apply would write β€” each destination with its operation and whether it carries a secret or a template β€” without touching the filesystem.",
RunE: runLs,
}

Expand Down
1 change: 1 addition & 0 deletions cmd/seed/rotate.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
var rotateCmd = &cobra.Command{
Use: "rotate",
Short: "Re-encrypt managed secrets under a new master key",
Long: "Re-encrypt every managed secret from the old master key (--master) to a new one (--new-master), in place. All-or-nothing: it verifies every secret decrypts before writing anything.",
SilenceUsage: true,
RunE: runRotate,
}
Expand Down
1 change: 1 addition & 0 deletions cmd/seed/seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
var SeedCmd = &cobra.Command{
Use: "seed",
Short: "Project declarative content onto the filesystem",
Long: "Copy files and apply small edits from a seed source onto the filesystem at boot. Bare files mirror verbatim; a .seed.yaml manifest overlays behavior β€” copy, merge, append β€” and decrypts secrets under the master key. Point --source at a mounted volume to seed a container from durable storage.",
}

func init() {
Expand Down
1 change: 1 addition & 0 deletions cmd/serve/current.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
var currentCmd = &cobra.Command{
Use: "current",
Short: "Serve current directory as a static site",
Long: "Serve the current directory over HTTP as a static site β€” a quick way to preview built files.",
RunE: func(cmd *cobra.Command, args []string) error {
port, _ := cmd.Flags().GetInt("port")
bind, _ := cmd.Flags().GetString("bind")
Expand Down
1 change: 1 addition & 0 deletions cmd/serve/font.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
var fontCmd = &cobra.Command{
Use: "font",
Short: "Serve fonts for local download",
Long: "Serve the installed fonts over HTTP so a browser can fetch them for local install.",
RunE: func(cmd *cobra.Command, args []string) error {
port, _ := cmd.Flags().GetInt("port")
bind, _ := cmd.Flags().GetString("bind")
Expand Down
1 change: 1 addition & 0 deletions cmd/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "github.com/spf13/cobra"
var ServeCmd = &cobra.Command{
Use: "serve",
Short: "Serve internal assets",
Long: "Run a small HTTP server for local assets β€” fonts or the current directory β€” on --port (default 38080).",
}

func init() {
Expand Down
1 change: 1 addition & 0 deletions cmd/show/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var osExit = os.Exit
var envCmd = &cobra.Command{
Use: "env <KEY>",
Short: "Display the resolved value of a workspace environment variable",
Long: "Resolve a setting by its dotted key (server.port) and print it with its source and description. --value emits just the value for scripts, --as bool|int|list validates the shape, --check tests whether it is set so a startup script can guard on it.",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
dotted := args[0]
Expand Down
8 changes: 5 additions & 3 deletions cmd/show/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import (
var ipCmd = &cobra.Command{
Use: "ip",
Short: "Display IP addresses",
Long: "Print the workspace's IP addresses β€” the internal container address or the node it runs on.",
}

func makeIPCmd(use, short, title string, getter func() (string, error)) *cobra.Command {
func makeIPCmd(use, short, long, title string, getter func() (string, error)) *cobra.Command {
return &cobra.Command{
Use: use,
Short: short,
Long: long,
RunE: func(cmd *cobra.Command, args []string) error {
ip, err := getter()
if err != nil {
Expand All @@ -36,8 +38,8 @@ func makeIPCmd(use, short, title string, getter func() (string, error)) *cobra.C

func init() {
ipCmd.AddCommand(
makeIPCmd("internal", "Display the internal IP address", "Internal IP Address", net.GetInternalIP),
makeIPCmd("node", "Display the node/host IP address", "Node IP Address", net.GetNodeIP),
makeIPCmd("internal", "Display the internal IP address", "Print the workspace container's internal IP address.", "Internal IP Address", net.GetInternalIP),
makeIPCmd("node", "Display the node/host IP address", "Print the IP address of the node hosting the workspace.", "Node IP Address", net.GetNodeIP),
)

ShowCmd.AddCommand(ipCmd)
Expand Down
2 changes: 1 addition & 1 deletion cmd/show/ip_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

func _runIPCmd(t *testing.T, title string, getter func() (string, error)) (stdout, stderr string, err error) {
t.Helper()
cmd := makeIPCmd("ip", "Display an IP address", title, getter)
cmd := makeIPCmd("ip", "Display an IP address", "", title, getter)

var outBuf, errBuf bytes.Buffer
cmd.SetOut(&outBuf)
Expand Down
Loading
Loading