@agentspan/agentspan 0.0.3
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/README.md +227 -0
- package/build.sh +35 -0
- package/cli.js +28 -0
- package/client/client.go +365 -0
- package/cmd/agent.go +120 -0
- package/cmd/compile.go +37 -0
- package/cmd/configure.go +48 -0
- package/cmd/delete.go +44 -0
- package/cmd/doctor.go +486 -0
- package/cmd/execution.go +155 -0
- package/cmd/get.go +40 -0
- package/cmd/helpers.go +21 -0
- package/cmd/init.go +83 -0
- package/cmd/list.go +55 -0
- package/cmd/respond.go +56 -0
- package/cmd/root.go +44 -0
- package/cmd/run.go +116 -0
- package/cmd/server.go +446 -0
- package/cmd/server_unix.go +36 -0
- package/cmd/server_windows.go +37 -0
- package/cmd/status.go +72 -0
- package/cmd/stream.go +32 -0
- package/cmd/update.go +91 -0
- package/config/config.go +78 -0
- package/dist/agentspan_darwin_amd64 +0 -0
- package/dist/agentspan_darwin_arm64 +0 -0
- package/dist/agentspan_linux_amd64 +0 -0
- package/dist/agentspan_linux_arm64 +0 -0
- package/dist/agentspan_windows_amd64.exe +0 -0
- package/dist/agentspan_windows_arm64.exe +0 -0
- package/examples/multi-agent.yaml +22 -0
- package/examples/simple-agent.yaml +7 -0
- package/go.mod +17 -0
- package/go.sum +24 -0
- package/install.js +122 -0
- package/install.sh +104 -0
- package/internal/progress/bar.go +121 -0
- package/main.go +10 -0
- package/package.json +43 -0
package/config/config.go
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Copyright (c) 2025 AgentSpan
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
3
|
+
|
|
4
|
+
package config
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"encoding/json"
|
|
8
|
+
"os"
|
|
9
|
+
"path/filepath"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
type Config struct {
|
|
13
|
+
ServerURL string `json:"server_url"`
|
|
14
|
+
AuthKey string `json:"auth_key,omitempty"`
|
|
15
|
+
AuthSecret string `json:"auth_secret,omitempty"`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
func DefaultConfig() *Config {
|
|
19
|
+
return &Config{
|
|
20
|
+
ServerURL: "http://localhost:8080",
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func ConfigDir() string {
|
|
25
|
+
home, _ := os.UserHomeDir()
|
|
26
|
+
return filepath.Join(home, ".agentspan")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
func configPath() string {
|
|
30
|
+
return filepath.Join(ConfigDir(), "config.json")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
func Load() *Config {
|
|
34
|
+
cfg := DefaultConfig()
|
|
35
|
+
|
|
36
|
+
// Env vars override
|
|
37
|
+
if url := os.Getenv("AGENT_SERVER_URL"); url != "" {
|
|
38
|
+
cfg.ServerURL = url
|
|
39
|
+
}
|
|
40
|
+
if key := os.Getenv("CONDUCTOR_AUTH_KEY"); key != "" {
|
|
41
|
+
cfg.AuthKey = key
|
|
42
|
+
}
|
|
43
|
+
if secret := os.Getenv("CONDUCTOR_AUTH_SECRET"); secret != "" {
|
|
44
|
+
cfg.AuthSecret = secret
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// File overrides (env vars take precedence)
|
|
48
|
+
data, err := os.ReadFile(configPath())
|
|
49
|
+
if err != nil {
|
|
50
|
+
return cfg
|
|
51
|
+
}
|
|
52
|
+
var fileCfg Config
|
|
53
|
+
if json.Unmarshal(data, &fileCfg) == nil {
|
|
54
|
+
if cfg.ServerURL == "http://localhost:8080" && fileCfg.ServerURL != "" {
|
|
55
|
+
cfg.ServerURL = fileCfg.ServerURL
|
|
56
|
+
}
|
|
57
|
+
if cfg.AuthKey == "" && fileCfg.AuthKey != "" {
|
|
58
|
+
cfg.AuthKey = fileCfg.AuthKey
|
|
59
|
+
}
|
|
60
|
+
if cfg.AuthSecret == "" && fileCfg.AuthSecret != "" {
|
|
61
|
+
cfg.AuthSecret = fileCfg.AuthSecret
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return cfg
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
func Save(cfg *Config) error {
|
|
69
|
+
dir := ConfigDir()
|
|
70
|
+
if err := os.MkdirAll(dir, 0o700); err != nil {
|
|
71
|
+
return err
|
|
72
|
+
}
|
|
73
|
+
data, err := json.MarshalIndent(cfg, "", " ")
|
|
74
|
+
if err != nil {
|
|
75
|
+
return err
|
|
76
|
+
}
|
|
77
|
+
return os.WriteFile(configPath(), data, 0o600)
|
|
78
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: research-team
|
|
2
|
+
description: A multi-agent research team
|
|
3
|
+
model: openai/gpt-4o
|
|
4
|
+
instructions: Route user requests to the appropriate specialist agent.
|
|
5
|
+
strategy: handoff
|
|
6
|
+
agents:
|
|
7
|
+
- name: researcher
|
|
8
|
+
description: Finds and summarizes information
|
|
9
|
+
model: openai/gpt-4o
|
|
10
|
+
instructions: |
|
|
11
|
+
You are a research specialist. Find relevant information and provide
|
|
12
|
+
well-sourced summaries.
|
|
13
|
+
tools:
|
|
14
|
+
- name: web_search
|
|
15
|
+
type: worker
|
|
16
|
+
- name: writer
|
|
17
|
+
description: Writes polished content
|
|
18
|
+
model: openai/gpt-4o
|
|
19
|
+
instructions: |
|
|
20
|
+
You are a writing specialist. Take research findings and produce
|
|
21
|
+
clear, well-structured content.
|
|
22
|
+
tools: []
|
package/go.mod
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module github.com/agentspan/agentspan/cli
|
|
2
|
+
|
|
3
|
+
go 1.25.6
|
|
4
|
+
|
|
5
|
+
require (
|
|
6
|
+
github.com/fatih/color v1.18.0
|
|
7
|
+
github.com/spf13/cobra v1.10.2
|
|
8
|
+
gopkg.in/yaml.v3 v3.0.1
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
require (
|
|
12
|
+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
13
|
+
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
14
|
+
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
15
|
+
github.com/spf13/pflag v1.0.9 // indirect
|
|
16
|
+
golang.org/x/sys v0.25.0 // indirect
|
|
17
|
+
)
|
package/go.sum
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
|
2
|
+
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
|
3
|
+
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
|
4
|
+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
|
5
|
+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
6
|
+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
7
|
+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
8
|
+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
9
|
+
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
10
|
+
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
11
|
+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
|
12
|
+
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
|
|
13
|
+
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
|
14
|
+
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
|
|
15
|
+
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
16
|
+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
|
17
|
+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
18
|
+
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
19
|
+
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
|
20
|
+
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
21
|
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
22
|
+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
23
|
+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
24
|
+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
package/install.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Copyright (c) 2025 AgentSpan
|
|
4
|
+
// Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
const https = require('https');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
const S3_BUCKET = 'https://agentspan.s3.us-east-2.amazonaws.com';
|
|
12
|
+
const BINARY_NAME = 'agentspan';
|
|
13
|
+
|
|
14
|
+
// Detect platform and architecture
|
|
15
|
+
function getPlatform() {
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
const arch = process.arch;
|
|
18
|
+
|
|
19
|
+
const platformMap = {
|
|
20
|
+
darwin: 'darwin',
|
|
21
|
+
linux: 'linux',
|
|
22
|
+
win32: 'windows'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const archMap = {
|
|
26
|
+
x64: 'amd64',
|
|
27
|
+
arm64: 'arm64'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
if (!platformMap[platform]) {
|
|
31
|
+
console.error(`Unsupported platform: ${platform}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!archMap[arch]) {
|
|
36
|
+
console.error(`Unsupported architecture: ${arch}`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
os: platformMap[platform],
|
|
42
|
+
arch: archMap[arch],
|
|
43
|
+
isWindows: platform === 'win32'
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Download binary following redirects
|
|
48
|
+
function downloadBinary(url, dest) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const makeRequest = (requestUrl) => {
|
|
51
|
+
const mod = requestUrl.startsWith('https') ? https : require('http');
|
|
52
|
+
mod.get(requestUrl, { headers: { 'User-Agent': 'agentspan-npm-installer' } }, (response) => {
|
|
53
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
54
|
+
makeRequest(response.headers.location);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (response.statusCode !== 200) {
|
|
59
|
+
reject(new Error(`Failed to download: ${response.statusCode}`));
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const file = fs.createWriteStream(dest);
|
|
64
|
+
response.pipe(file);
|
|
65
|
+
|
|
66
|
+
file.on('finish', () => {
|
|
67
|
+
file.close();
|
|
68
|
+
resolve();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
file.on('error', (err) => {
|
|
72
|
+
fs.unlink(dest, () => {});
|
|
73
|
+
reject(err);
|
|
74
|
+
});
|
|
75
|
+
}).on('error', (err) => {
|
|
76
|
+
fs.unlink(dest, () => {});
|
|
77
|
+
reject(err);
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
makeRequest(url);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function install() {
|
|
86
|
+
try {
|
|
87
|
+
console.log('Installing AgentSpan CLI...');
|
|
88
|
+
|
|
89
|
+
const { os, arch, isWindows } = getPlatform();
|
|
90
|
+
console.log(`Platform: ${os} ${arch}`);
|
|
91
|
+
|
|
92
|
+
const binaryName = isWindows ? `${BINARY_NAME}.exe` : BINARY_NAME;
|
|
93
|
+
const downloadName = isWindows ? `${BINARY_NAME}_${os}_${arch}.exe` : `${BINARY_NAME}_${os}_${arch}`;
|
|
94
|
+
const downloadUrl = `${S3_BUCKET}/cli/latest/${downloadName}`;
|
|
95
|
+
|
|
96
|
+
console.log(`Downloading from: ${downloadUrl}`);
|
|
97
|
+
|
|
98
|
+
// Create bin directory
|
|
99
|
+
const binDir = path.join(__dirname, 'bin');
|
|
100
|
+
if (!fs.existsSync(binDir)) {
|
|
101
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Download binary
|
|
105
|
+
const binaryPath = path.join(binDir, binaryName);
|
|
106
|
+
await downloadBinary(downloadUrl, binaryPath);
|
|
107
|
+
|
|
108
|
+
// Make executable (Unix-like systems)
|
|
109
|
+
if (!isWindows) {
|
|
110
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.log('Installation successful!');
|
|
114
|
+
console.log(`Binary installed at: ${binaryPath}`);
|
|
115
|
+
console.log(`\nRun 'agentspan --help' to get started.`);
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.error('Installation failed:', error.message);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
install();
|
package/install.sh
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
S3_BUCKET="https://agentspan.s3.us-east-2.amazonaws.com"
|
|
5
|
+
BINARY_NAME="agentspan"
|
|
6
|
+
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
|
|
7
|
+
|
|
8
|
+
# Colors for output
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
NC='\033[0m' # No Color
|
|
13
|
+
|
|
14
|
+
detect_os() {
|
|
15
|
+
OS="$(uname -s)"
|
|
16
|
+
case "$OS" in
|
|
17
|
+
Linux*) OS='linux';;
|
|
18
|
+
Darwin*) OS='darwin';;
|
|
19
|
+
CYGWIN*) OS='windows';;
|
|
20
|
+
MINGW*) OS='windows';;
|
|
21
|
+
*)
|
|
22
|
+
echo "${RED}Unsupported operating system: $OS${NC}"
|
|
23
|
+
exit 1
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
detect_arch() {
|
|
29
|
+
ARCH="$(uname -m)"
|
|
30
|
+
case "$ARCH" in
|
|
31
|
+
x86_64) ARCH='amd64';;
|
|
32
|
+
amd64) ARCH='amd64';;
|
|
33
|
+
arm64) ARCH='arm64';;
|
|
34
|
+
aarch64) ARCH='arm64';;
|
|
35
|
+
*)
|
|
36
|
+
echo "${RED}Unsupported architecture: $ARCH${NC}"
|
|
37
|
+
exit 1
|
|
38
|
+
;;
|
|
39
|
+
esac
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
install_binary() {
|
|
43
|
+
DOWNLOAD_URL="${S3_BUCKET}/cli/latest/${BINARY_NAME}_${OS}_${ARCH}"
|
|
44
|
+
|
|
45
|
+
if [ "$OS" = "windows" ]; then
|
|
46
|
+
DOWNLOAD_URL="${DOWNLOAD_URL}.exe"
|
|
47
|
+
BINARY_NAME="${BINARY_NAME}.exe"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
echo "${YELLOW}Downloading from: $DOWNLOAD_URL${NC}"
|
|
51
|
+
|
|
52
|
+
TMP_DIR=$(mktemp -d)
|
|
53
|
+
TMP_FILE="$TMP_DIR/$BINARY_NAME"
|
|
54
|
+
|
|
55
|
+
if ! curl -fsSL "$DOWNLOAD_URL" -o "$TMP_FILE"; then
|
|
56
|
+
echo "${RED}Failed to download binary${NC}"
|
|
57
|
+
rm -rf "$TMP_DIR"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
chmod +x "$TMP_FILE"
|
|
62
|
+
|
|
63
|
+
if [ -w "$INSTALL_DIR" ]; then
|
|
64
|
+
mv "$TMP_FILE" "$INSTALL_DIR/$BINARY_NAME"
|
|
65
|
+
echo "${GREEN}Installed $BINARY_NAME to $INSTALL_DIR${NC}"
|
|
66
|
+
else
|
|
67
|
+
echo "${YELLOW}$INSTALL_DIR is not writable. Attempting to use sudo...${NC}"
|
|
68
|
+
sudo mv "$TMP_FILE" "$INSTALL_DIR/$BINARY_NAME"
|
|
69
|
+
echo "${GREEN}Installed $BINARY_NAME to $INSTALL_DIR (with sudo)${NC}"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
rm -rf "$TMP_DIR"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
verify_installation() {
|
|
76
|
+
if command -v $BINARY_NAME >/dev/null 2>&1; then
|
|
77
|
+
VERSION_OUTPUT=$($BINARY_NAME version 2>&1 || true)
|
|
78
|
+
echo "${GREEN}Installation successful!${NC}"
|
|
79
|
+
echo "${GREEN} Version: $VERSION_OUTPUT${NC}"
|
|
80
|
+
echo ""
|
|
81
|
+
echo "Run '${BINARY_NAME} --help' to get started."
|
|
82
|
+
else
|
|
83
|
+
echo "${YELLOW}Binary installed but not found in PATH${NC}"
|
|
84
|
+
echo "You may need to add $INSTALL_DIR to your PATH:"
|
|
85
|
+
echo " export PATH=\"$INSTALL_DIR:\$PATH\""
|
|
86
|
+
fi
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main() {
|
|
90
|
+
echo "${GREEN}Installing AgentSpan CLI...${NC}"
|
|
91
|
+
echo ""
|
|
92
|
+
|
|
93
|
+
detect_os
|
|
94
|
+
detect_arch
|
|
95
|
+
|
|
96
|
+
echo "Detected OS: $OS"
|
|
97
|
+
echo "Detected Architecture: $ARCH"
|
|
98
|
+
echo ""
|
|
99
|
+
|
|
100
|
+
install_binary
|
|
101
|
+
verify_installation
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
main
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Copyright (c) 2025 AgentSpan
|
|
2
|
+
// Licensed under the MIT License. See LICENSE file in the project root for details.
|
|
3
|
+
|
|
4
|
+
package progress
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"fmt"
|
|
8
|
+
"io"
|
|
9
|
+
"os"
|
|
10
|
+
"sync"
|
|
11
|
+
"time"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
const (
|
|
15
|
+
barWidth = 30
|
|
16
|
+
filledChar = "█"
|
|
17
|
+
emptyChar = "░"
|
|
18
|
+
renderInterval = 100 * time.Millisecond
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// ProgressBar tracks and renders download progress to stderr.
|
|
22
|
+
type ProgressBar struct {
|
|
23
|
+
total int64
|
|
24
|
+
current int64
|
|
25
|
+
desc string
|
|
26
|
+
mu sync.Mutex
|
|
27
|
+
lastRender time.Time
|
|
28
|
+
w io.Writer
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// NewProgressBar creates a new progress bar. If total <= 0, it renders without a bar.
|
|
32
|
+
func NewProgressBar(total int64, desc string) *ProgressBar {
|
|
33
|
+
return &ProgressBar{
|
|
34
|
+
total: total,
|
|
35
|
+
desc: desc,
|
|
36
|
+
w: os.Stderr,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Add advances the progress bar by n bytes.
|
|
41
|
+
func (p *ProgressBar) Add(n int64) {
|
|
42
|
+
p.mu.Lock()
|
|
43
|
+
defer p.mu.Unlock()
|
|
44
|
+
p.current += n
|
|
45
|
+
now := time.Now()
|
|
46
|
+
if now.Sub(p.lastRender) >= renderInterval {
|
|
47
|
+
p.render()
|
|
48
|
+
p.lastRender = now
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Finish renders the final state with a newline.
|
|
53
|
+
func (p *ProgressBar) Finish() {
|
|
54
|
+
p.mu.Lock()
|
|
55
|
+
defer p.mu.Unlock()
|
|
56
|
+
p.render()
|
|
57
|
+
fmt.Fprintln(p.w)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func (p *ProgressBar) render() {
|
|
61
|
+
if p.total > 0 {
|
|
62
|
+
pct := float64(p.current) / float64(p.total)
|
|
63
|
+
if pct > 1 {
|
|
64
|
+
pct = 1
|
|
65
|
+
}
|
|
66
|
+
filled := int(pct * float64(barWidth))
|
|
67
|
+
bar := ""
|
|
68
|
+
for i := 0; i < barWidth; i++ {
|
|
69
|
+
if i < filled {
|
|
70
|
+
bar += filledChar
|
|
71
|
+
} else {
|
|
72
|
+
bar += emptyChar
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
fmt.Fprintf(p.w, "\r%s [%s] %3.0f%% %s/%s",
|
|
76
|
+
p.desc, bar, pct*100,
|
|
77
|
+
FormatBytes(p.current), FormatBytes(p.total))
|
|
78
|
+
} else {
|
|
79
|
+
fmt.Fprintf(p.w, "\r%s %s downloaded", p.desc, FormatBytes(p.current))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Reader wraps an io.Reader and reports progress on each Read.
|
|
84
|
+
type Reader struct {
|
|
85
|
+
reader io.Reader
|
|
86
|
+
bar *ProgressBar
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// NewReader wraps r with a progress bar. desc is shown as a label.
|
|
90
|
+
// If total <= 0, a simple byte counter is shown instead of a bar.
|
|
91
|
+
func NewReader(r io.Reader, total int64, desc string) (*Reader, *ProgressBar) {
|
|
92
|
+
bar := NewProgressBar(total, desc)
|
|
93
|
+
return &Reader{reader: r, bar: bar}, bar
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func (r *Reader) Read(p []byte) (int, error) {
|
|
97
|
+
n, err := r.reader.Read(p)
|
|
98
|
+
if n > 0 {
|
|
99
|
+
r.bar.Add(int64(n))
|
|
100
|
+
}
|
|
101
|
+
return n, err
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// FormatBytes returns a human-readable byte string (e.g. "12.3 MB").
|
|
105
|
+
func FormatBytes(n int64) string {
|
|
106
|
+
const (
|
|
107
|
+
kb = 1024
|
|
108
|
+
mb = 1024 * kb
|
|
109
|
+
gb = 1024 * mb
|
|
110
|
+
)
|
|
111
|
+
switch {
|
|
112
|
+
case n >= gb:
|
|
113
|
+
return fmt.Sprintf("%.1f GB", float64(n)/float64(gb))
|
|
114
|
+
case n >= mb:
|
|
115
|
+
return fmt.Sprintf("%.1f MB", float64(n)/float64(mb))
|
|
116
|
+
case n >= kb:
|
|
117
|
+
return fmt.Sprintf("%.1f KB", float64(n)/float64(kb))
|
|
118
|
+
default:
|
|
119
|
+
return fmt.Sprintf("%d B", n)
|
|
120
|
+
}
|
|
121
|
+
}
|
package/main.go
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentspan/agentspan",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "CLI for AgentSpan - Build, run, and manage AI agents",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentspan": "./cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"postinstall": "node install.js"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/agentspan/agentspan.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"agentspan",
|
|
18
|
+
"agent",
|
|
19
|
+
"ai",
|
|
20
|
+
"llm",
|
|
21
|
+
"orchestration",
|
|
22
|
+
"workflow",
|
|
23
|
+
"cli"
|
|
24
|
+
],
|
|
25
|
+
"author": "AgentSpan",
|
|
26
|
+
"license": "Apache-2.0",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/agentspan/agentspan/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/agentspan/agentspan#readme",
|
|
31
|
+
"os": [
|
|
32
|
+
"darwin",
|
|
33
|
+
"linux",
|
|
34
|
+
"win32"
|
|
35
|
+
],
|
|
36
|
+
"cpu": [
|
|
37
|
+
"x64",
|
|
38
|
+
"arm64"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=14"
|
|
42
|
+
}
|
|
43
|
+
}
|