@oussema_mili/test-pkg-123 1.1.22
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.
Potentially problematic release.
This version of @oussema_mili/test-pkg-123 might be problematic. Click here for more details.
- package/LICENSE +29 -0
- package/README.md +220 -0
- package/auth-callback.html +97 -0
- package/auth.js +276 -0
- package/cli-commands.js +1923 -0
- package/containerManager.js +304 -0
- package/daemon/agentRunner.js +429 -0
- package/daemon/daemonEntry.js +64 -0
- package/daemon/daemonManager.js +271 -0
- package/daemon/logManager.js +227 -0
- package/dist/styles.css +504 -0
- package/docker-actions/apps.js +3938 -0
- package/docker-actions/config-transformer.js +380 -0
- package/docker-actions/containers.js +355 -0
- package/docker-actions/general.js +171 -0
- package/docker-actions/images.js +1128 -0
- package/docker-actions/logs.js +224 -0
- package/docker-actions/metrics.js +270 -0
- package/docker-actions/registry.js +1100 -0
- package/docker-actions/setup-tasks.js +859 -0
- package/docker-actions/terminal.js +247 -0
- package/docker-actions/volumes.js +696 -0
- package/helper-functions.js +193 -0
- package/index.html +83 -0
- package/index.js +341 -0
- package/package.json +82 -0
- package/postcss.config.mjs +5 -0
- package/scripts/release.sh +212 -0
- package/setup/setupWizard.js +403 -0
- package/store/agentSessionStore.js +51 -0
- package/store/agentStore.js +113 -0
- package/store/configStore.js +171 -0
- package/store/daemonStore.js +217 -0
- package/store/deviceCredentialStore.js +107 -0
- package/store/npmTokenStore.js +65 -0
- package/store/registryStore.js +329 -0
- package/store/setupState.js +147 -0
- package/styles.css +1 -0
- package/utils/appLogger.js +223 -0
- package/utils/deviceInfo.js +98 -0
- package/utils/ecrAuth.js +225 -0
- package/utils/encryption.js +112 -0
- package/utils/envSetup.js +44 -0
- package/utils/errorHandler.js +327 -0
- package/utils/portUtils.js +59 -0
- package/utils/prerequisites.js +323 -0
- package/utils/prompts.js +318 -0
- package/utils/ssl-certificates.js +256 -0
- package/websocket-server.js +415 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Fenwave Agent Release Script
|
|
4
|
+
# Usage: ./scripts/release.sh [patch|minor|major|x.x.x]
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# Colors
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
BLUE='\033[0;34m'
|
|
13
|
+
NC='\033[0m' # No Color
|
|
14
|
+
|
|
15
|
+
# Functions
|
|
16
|
+
log_info() { echo -e "${BLUE}ℹ ${NC}$1"; }
|
|
17
|
+
log_success() { echo -e "${GREEN}✓ ${NC}$1"; }
|
|
18
|
+
log_warning() { echo -e "${YELLOW}⚠ ${NC}$1"; }
|
|
19
|
+
log_error() { echo -e "${RED}✗ ${NC}$1"; exit 1; }
|
|
20
|
+
|
|
21
|
+
# Check prerequisites
|
|
22
|
+
check_prerequisites() {
|
|
23
|
+
log_info "Checking prerequisites..."
|
|
24
|
+
|
|
25
|
+
command -v node >/dev/null 2>&1 || log_error "Node.js is required"
|
|
26
|
+
command -v npm >/dev/null 2>&1 || log_error "npm is required"
|
|
27
|
+
command -v gh >/dev/null 2>&1 || log_error "GitHub CLI (gh) is required"
|
|
28
|
+
command -v git >/dev/null 2>&1 || log_error "git is required"
|
|
29
|
+
|
|
30
|
+
# Check if logged into npm
|
|
31
|
+
npm whoami >/dev/null 2>&1 || log_error "Not logged into npm. Run 'npm login' first"
|
|
32
|
+
|
|
33
|
+
# Check if logged into GitHub CLI
|
|
34
|
+
gh auth status >/dev/null 2>&1 || log_error "Not logged into GitHub CLI. Run 'gh auth login' first"
|
|
35
|
+
|
|
36
|
+
# Check for uncommitted changes
|
|
37
|
+
if [[ -n $(git status --porcelain) ]]; then
|
|
38
|
+
log_error "Working directory has uncommitted changes. Commit or stash them first"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Check we're on main branch
|
|
42
|
+
CURRENT_BRANCH=$(git branch --show-current)
|
|
43
|
+
if [[ "$CURRENT_BRANCH" != "main" ]]; then
|
|
44
|
+
log_warning "Not on main branch (currently on: $CURRENT_BRANCH)"
|
|
45
|
+
read -p "Continue anyway? (y/N) " -n 1 -r
|
|
46
|
+
echo
|
|
47
|
+
[[ ! $REPLY =~ ^[Yy]$ ]] && exit 1
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
# Pull latest changes
|
|
51
|
+
log_info "Pulling latest changes..."
|
|
52
|
+
git pull origin "$CURRENT_BRANCH"
|
|
53
|
+
|
|
54
|
+
log_success "Prerequisites check passed"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Get version argument
|
|
58
|
+
get_version() {
|
|
59
|
+
VERSION_ARG=$1
|
|
60
|
+
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
|
61
|
+
|
|
62
|
+
if [[ -z "$VERSION_ARG" ]]; then
|
|
63
|
+
echo "" >&2
|
|
64
|
+
echo "Current version: $CURRENT_VERSION" >&2
|
|
65
|
+
echo "" >&2
|
|
66
|
+
echo "Select version bump:" >&2
|
|
67
|
+
echo " 1) patch (x.x.X) - Bug fixes" >&2
|
|
68
|
+
echo " 2) minor (x.X.0) - New features" >&2
|
|
69
|
+
echo " 3) major (X.0.0) - Breaking changes" >&2
|
|
70
|
+
echo " 4) custom - Enter specific version" >&2
|
|
71
|
+
echo "" >&2
|
|
72
|
+
read -p "Choice [1-4]: " choice
|
|
73
|
+
|
|
74
|
+
case $choice in
|
|
75
|
+
1) VERSION_ARG="patch" ;;
|
|
76
|
+
2) VERSION_ARG="minor" ;;
|
|
77
|
+
3) VERSION_ARG="major" ;;
|
|
78
|
+
4) read -p "Enter version (e.g., 1.2.3): " VERSION_ARG ;;
|
|
79
|
+
*) log_error "Invalid choice" ;;
|
|
80
|
+
esac
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
echo "$VERSION_ARG"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Bump version
|
|
87
|
+
bump_version() {
|
|
88
|
+
local VERSION_TYPE=$1
|
|
89
|
+
|
|
90
|
+
log_info "Bumping version ($VERSION_TYPE)..." >&2
|
|
91
|
+
|
|
92
|
+
if [[ "$VERSION_TYPE" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
93
|
+
# Specific version provided
|
|
94
|
+
npm version "$VERSION_TYPE" --no-git-tag-version >/dev/null
|
|
95
|
+
else
|
|
96
|
+
# patch, minor, or major
|
|
97
|
+
npm version "$VERSION_TYPE" --no-git-tag-version >/dev/null
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
NEW_VERSION=$(node -p "require('./package.json').version")
|
|
101
|
+
log_success "Version bumped to $NEW_VERSION" >&2
|
|
102
|
+
|
|
103
|
+
echo "$NEW_VERSION"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Commit and tag
|
|
107
|
+
commit_and_tag() {
|
|
108
|
+
local VERSION=$1
|
|
109
|
+
|
|
110
|
+
log_info "Committing version bump..."
|
|
111
|
+
|
|
112
|
+
git add package.json package-lock.json 2>/dev/null || git add package.json
|
|
113
|
+
git commit -m "chore: release v$VERSION"
|
|
114
|
+
|
|
115
|
+
log_info "Creating git tag v$VERSION..."
|
|
116
|
+
git tag -a "v$VERSION" -m "Release v$VERSION"
|
|
117
|
+
|
|
118
|
+
log_success "Committed and tagged"
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# Push to remote
|
|
122
|
+
push_to_remote() {
|
|
123
|
+
local VERSION=$1
|
|
124
|
+
|
|
125
|
+
log_info "Pushing to remote..."
|
|
126
|
+
|
|
127
|
+
git push origin "$(git branch --show-current)"
|
|
128
|
+
git push origin "v$VERSION"
|
|
129
|
+
|
|
130
|
+
log_success "Pushed to remote"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# Create GitHub release
|
|
134
|
+
create_github_release() {
|
|
135
|
+
local VERSION=$1
|
|
136
|
+
|
|
137
|
+
log_info "Creating GitHub release..."
|
|
138
|
+
|
|
139
|
+
# Get commits since last tag
|
|
140
|
+
LAST_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
|
|
141
|
+
|
|
142
|
+
if [[ -n "$LAST_TAG" ]]; then
|
|
143
|
+
CHANGES=$(git log "$LAST_TAG"..HEAD --pretty=format:"- %s" --no-merges | grep -v "chore: release")
|
|
144
|
+
else
|
|
145
|
+
CHANGES="- Initial release"
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
# Create release notes
|
|
149
|
+
RELEASE_NOTES="## What's Changed
|
|
150
|
+
|
|
151
|
+
$CHANGES
|
|
152
|
+
|
|
153
|
+
**Full Changelog**: https://github.com/$(gh repo view --json nameWithOwner -q .nameWithOwner)/compare/${LAST_TAG:-v0.0.0}...v$VERSION"
|
|
154
|
+
|
|
155
|
+
gh release create "v$VERSION" \
|
|
156
|
+
--title "v$VERSION" \
|
|
157
|
+
--notes "$RELEASE_NOTES"
|
|
158
|
+
|
|
159
|
+
log_success "GitHub release created"
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
# Publish to npm
|
|
163
|
+
publish_to_npm() {
|
|
164
|
+
log_info "Publishing to npm..."
|
|
165
|
+
|
|
166
|
+
# Check if OTP is needed
|
|
167
|
+
read -p "Enter npm OTP (leave empty if not using 2FA): " OTP
|
|
168
|
+
|
|
169
|
+
if [[ -n "$OTP" ]]; then
|
|
170
|
+
npm publish --access public --otp="$OTP"
|
|
171
|
+
else
|
|
172
|
+
npm publish --access public
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
log_success "Published to npm"
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Main
|
|
179
|
+
main() {
|
|
180
|
+
echo ""
|
|
181
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
182
|
+
echo " Fenwave Agent Release Script"
|
|
183
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
184
|
+
echo ""
|
|
185
|
+
|
|
186
|
+
check_prerequisites
|
|
187
|
+
|
|
188
|
+
VERSION_ARG=$(get_version "$1")
|
|
189
|
+
NEW_VERSION=$(bump_version "$VERSION_ARG")
|
|
190
|
+
|
|
191
|
+
echo ""
|
|
192
|
+
log_info "Ready to release v$NEW_VERSION"
|
|
193
|
+
read -p "Continue with release? (y/N) " -n 1 -r
|
|
194
|
+
echo ""
|
|
195
|
+
[[ ! $REPLY =~ ^[Yy]$ ]] && { log_warning "Release cancelled"; exit 0; }
|
|
196
|
+
|
|
197
|
+
commit_and_tag "$NEW_VERSION"
|
|
198
|
+
push_to_remote "$NEW_VERSION"
|
|
199
|
+
create_github_release "$NEW_VERSION"
|
|
200
|
+
publish_to_npm
|
|
201
|
+
|
|
202
|
+
echo ""
|
|
203
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
204
|
+
echo -e "${GREEN} Release v$NEW_VERSION completed successfully!${NC}"
|
|
205
|
+
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
206
|
+
echo ""
|
|
207
|
+
echo " npm: https://www.npmjs.com/package/@fenwave/agent"
|
|
208
|
+
echo " GitHub: $(gh repo view --json url -q .url)/releases/tag/v$NEW_VERSION"
|
|
209
|
+
echo ""
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
main "$@"
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { execSync } from "child_process";
|
|
3
|
+
import axios from "axios";
|
|
4
|
+
import {
|
|
5
|
+
checkPrerequisites,
|
|
6
|
+
displayPrerequisites,
|
|
7
|
+
getMissingPrerequisites,
|
|
8
|
+
} from "../utils/prerequisites.js";
|
|
9
|
+
import {
|
|
10
|
+
promptRegistrationToken,
|
|
11
|
+
promptConfirmation,
|
|
12
|
+
displayProgress,
|
|
13
|
+
displaySuccess,
|
|
14
|
+
displayError,
|
|
15
|
+
displayWarning,
|
|
16
|
+
displayInfo,
|
|
17
|
+
displayHeader,
|
|
18
|
+
displayStep,
|
|
19
|
+
displayKeyValue,
|
|
20
|
+
} from "../utils/prompts.js";
|
|
21
|
+
import {
|
|
22
|
+
handleError,
|
|
23
|
+
createError,
|
|
24
|
+
ErrorCode,
|
|
25
|
+
isUserCancellation,
|
|
26
|
+
} from "../utils/errorHandler.js";
|
|
27
|
+
import { getDeviceMetadata, displayDeviceInfo } from "../utils/deviceInfo.js";
|
|
28
|
+
import {
|
|
29
|
+
saveDeviceCredential,
|
|
30
|
+
isDeviceRegistered,
|
|
31
|
+
} from "../store/deviceCredentialStore.js";
|
|
32
|
+
import {
|
|
33
|
+
saveSetupProgress,
|
|
34
|
+
clearSetupState,
|
|
35
|
+
SetupStep,
|
|
36
|
+
isSetupInProgress,
|
|
37
|
+
} from "../store/setupState.js";
|
|
38
|
+
import { initializeConfig } from "../store/configStore.js";
|
|
39
|
+
|
|
40
|
+
const TOTAL_STEPS = 5; // Reduced - no ECR auth or NPM config needed (public package and GHCR)
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Setup Wizard Orchestrator
|
|
44
|
+
* Guides user through complete agent setup
|
|
45
|
+
*/
|
|
46
|
+
export class SetupWizard {
|
|
47
|
+
constructor(options = {}) {
|
|
48
|
+
this.backendUrl = options.backendUrl || "http://localhost:7007";
|
|
49
|
+
this.frontendUrl = options.frontendUrl || "http://localhost:3000";
|
|
50
|
+
this.skipPrerequisites = options.skipPrerequisites || false;
|
|
51
|
+
this.registrationToken = options.token || null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Run the complete setup wizard
|
|
56
|
+
*/
|
|
57
|
+
async run() {
|
|
58
|
+
try {
|
|
59
|
+
// Welcome message
|
|
60
|
+
this.displayWelcome();
|
|
61
|
+
|
|
62
|
+
// Initialize and save agent configuration
|
|
63
|
+
const agentConfig = {
|
|
64
|
+
backendUrl: this.backendUrl,
|
|
65
|
+
frontendUrl: this.frontendUrl,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
initializeConfig(agentConfig);
|
|
69
|
+
|
|
70
|
+
displayInfo("Agent configuration initialized:");
|
|
71
|
+
displayKeyValue({
|
|
72
|
+
"Backend URL": this.backendUrl,
|
|
73
|
+
"Frontend URL": this.frontendUrl,
|
|
74
|
+
});
|
|
75
|
+
console.log("");
|
|
76
|
+
|
|
77
|
+
// Check if already registered
|
|
78
|
+
if (isDeviceRegistered()) {
|
|
79
|
+
displayWarning("Device is already registered!");
|
|
80
|
+
const shouldContinue = await promptConfirmation(
|
|
81
|
+
"Do you want to re-register (this will replace existing credentials)?",
|
|
82
|
+
false
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (!shouldContinue) {
|
|
86
|
+
displayInfo(
|
|
87
|
+
'Setup cancelled. Use "fenwave status" to view current registration.'
|
|
88
|
+
);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check for interrupted setup
|
|
94
|
+
if (isSetupInProgress()) {
|
|
95
|
+
const resume = await promptConfirmation(
|
|
96
|
+
"Found interrupted setup. Do you want to resume?",
|
|
97
|
+
true
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!resume) {
|
|
101
|
+
clearSetupState();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Step 1: Prerequisites Check
|
|
106
|
+
await this.checkPrerequisitesStep();
|
|
107
|
+
|
|
108
|
+
// Step 2: Device Registration
|
|
109
|
+
const registrationData = await this.registrationStep();
|
|
110
|
+
|
|
111
|
+
// Step 3: Pull DevApp Image (GHCR is public, no auth required)
|
|
112
|
+
await this.pullImageStep(registrationData);
|
|
113
|
+
|
|
114
|
+
// Step 4: Start Agent (optional)
|
|
115
|
+
await this.startAgentStep();
|
|
116
|
+
|
|
117
|
+
// Step 5: Complete
|
|
118
|
+
this.displayComplete(registrationData);
|
|
119
|
+
|
|
120
|
+
// Clear setup state
|
|
121
|
+
clearSetupState();
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (isUserCancellation(error)) {
|
|
124
|
+
displayWarning("\nSetup cancelled by user.");
|
|
125
|
+
displayInfo('Run "fenwave init" again to resume setup.\n');
|
|
126
|
+
} else {
|
|
127
|
+
handleError(error, "Setup Wizard");
|
|
128
|
+
}
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Display welcome message
|
|
135
|
+
*/
|
|
136
|
+
displayWelcome() {
|
|
137
|
+
displayHeader("Fenwave Dev Agent Setup Wizard");
|
|
138
|
+
console.log(
|
|
139
|
+
chalk.gray(
|
|
140
|
+
" This wizard will guide you through setting up the Fenwave Dev Agent.\n"
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
console.log(
|
|
144
|
+
chalk.gray(" You can interrupt the setup anytime and resume later.\n")
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Step 1: Check prerequisites
|
|
150
|
+
*/
|
|
151
|
+
async checkPrerequisitesStep() {
|
|
152
|
+
displayStep(1, TOTAL_STEPS, "Checking Prerequisites");
|
|
153
|
+
|
|
154
|
+
if (this.skipPrerequisites) {
|
|
155
|
+
displayInfo("Skipping prerequisites check (--skip-prerequisites flag)");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const checkResults = await checkPrerequisites({
|
|
160
|
+
backendUrl: this.backendUrl,
|
|
161
|
+
});
|
|
162
|
+
const allPassed = displayPrerequisites(checkResults);
|
|
163
|
+
|
|
164
|
+
if (!allPassed) {
|
|
165
|
+
const missing = getMissingPrerequisites(checkResults);
|
|
166
|
+
|
|
167
|
+
console.log(chalk.yellow("\n⚠️ Missing prerequisites:\n"));
|
|
168
|
+
missing.forEach((item) => {
|
|
169
|
+
console.log(chalk.red(` ❌ ${item.name}: ${item.reason}`));
|
|
170
|
+
console.log(chalk.gray(` Action: ${item.action}\n`));
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
const shouldContinue = await promptConfirmation(
|
|
174
|
+
"Some prerequisites are missing. Continue anyway?",
|
|
175
|
+
false
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
if (!shouldContinue) {
|
|
179
|
+
throw createError(
|
|
180
|
+
ErrorCode.OPERATION_CANCELLED,
|
|
181
|
+
"Prerequisites check failed"
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
saveSetupProgress(SetupStep.PREREQUISITES, { allPassed });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Step 2: Device Registration
|
|
191
|
+
*/
|
|
192
|
+
async registrationStep() {
|
|
193
|
+
displayStep(2, TOTAL_STEPS, "Device Registration");
|
|
194
|
+
|
|
195
|
+
// Get registration token
|
|
196
|
+
if (!this.registrationToken) {
|
|
197
|
+
this.registrationToken = await promptRegistrationToken(this.backendUrl);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Collect device information
|
|
201
|
+
const deviceMetadata = await getDeviceMetadata();
|
|
202
|
+
displayInfo("Collected device information:");
|
|
203
|
+
displayDeviceInfo(deviceMetadata);
|
|
204
|
+
|
|
205
|
+
// Register device with backend
|
|
206
|
+
const registrationData = await displayProgress(
|
|
207
|
+
"Registering device with Fenwave...",
|
|
208
|
+
this.registerDevice(deviceMetadata)
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// Save device credentials
|
|
212
|
+
saveDeviceCredential({
|
|
213
|
+
deviceId: registrationData.deviceId,
|
|
214
|
+
deviceCredential: registrationData.deviceCredential,
|
|
215
|
+
userEntityRef: registrationData.userEntityRef,
|
|
216
|
+
deviceName: deviceMetadata.deviceName,
|
|
217
|
+
platform: deviceMetadata.platform,
|
|
218
|
+
agentVersion: deviceMetadata.agentVersion,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
displaySuccess(`Device registered: ${registrationData.deviceId}`);
|
|
222
|
+
console.log(chalk.gray(` User: ${registrationData.userEntityRef}\n`));
|
|
223
|
+
|
|
224
|
+
saveSetupProgress(SetupStep.REGISTRATION, {
|
|
225
|
+
deviceId: registrationData.deviceId,
|
|
226
|
+
deviceName: deviceMetadata.deviceName,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
return registrationData;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Register device with backend
|
|
234
|
+
*/
|
|
235
|
+
async registerDevice(deviceMetadata) {
|
|
236
|
+
try {
|
|
237
|
+
const response = await axios.post(
|
|
238
|
+
`${this.backendUrl}/api/agent-cli/register`,
|
|
239
|
+
{
|
|
240
|
+
installToken: this.registrationToken,
|
|
241
|
+
deviceInfo: {
|
|
242
|
+
deviceName: deviceMetadata.deviceName,
|
|
243
|
+
platform: deviceMetadata.platform,
|
|
244
|
+
osVersion: deviceMetadata.osVersion,
|
|
245
|
+
agentVersion: deviceMetadata.agentVersion,
|
|
246
|
+
metadata: deviceMetadata.metadata,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{ timeout: 10000 }
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
deviceId: response.data.deviceId,
|
|
254
|
+
deviceCredential: response.data.deviceCredential,
|
|
255
|
+
registryConfig: response.data.registryConfig,
|
|
256
|
+
userEntityRef: response.data.userEntityRef || "unknown",
|
|
257
|
+
};
|
|
258
|
+
} catch (error) {
|
|
259
|
+
if (error.response?.status === 401) {
|
|
260
|
+
throw createError(
|
|
261
|
+
ErrorCode.INVALID_TOKEN,
|
|
262
|
+
"Invalid or expired registration token"
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (error.response?.status === 429) {
|
|
266
|
+
throw createError(
|
|
267
|
+
ErrorCode.RATE_LIMIT_EXCEEDED,
|
|
268
|
+
"Too many registration attempts"
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Step 3: Pull Fenwave DevApp Image (GHCR is public, no auth required)
|
|
277
|
+
*/
|
|
278
|
+
async pullImageStep(registrationData) {
|
|
279
|
+
displayStep(3, TOTAL_STEPS, "Pull Fenwave DevApp Image");
|
|
280
|
+
|
|
281
|
+
const shouldPull = await promptConfirmation(
|
|
282
|
+
"Pull Fenwave DevApp Docker image?",
|
|
283
|
+
true
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
if (!shouldPull) {
|
|
287
|
+
displayInfo("Skipping image pull");
|
|
288
|
+
saveSetupProgress(SetupStep.PULL_IMAGE, { skipped: true });
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const { dockerImage } = registrationData.registryConfig;
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
displayInfo(`Pulling image: ${dockerImage}`);
|
|
296
|
+
displayWarning("This may take a few minutes...");
|
|
297
|
+
|
|
298
|
+
await this.pullDockerImage(dockerImage);
|
|
299
|
+
|
|
300
|
+
displaySuccess("Docker image pulled successfully");
|
|
301
|
+
saveSetupProgress(SetupStep.PULL_IMAGE, { dockerImage });
|
|
302
|
+
} catch (error) {
|
|
303
|
+
displayError("Failed to pull Docker image");
|
|
304
|
+
|
|
305
|
+
const shouldContinue = await promptConfirmation(
|
|
306
|
+
"Continue without pulling image?",
|
|
307
|
+
true
|
|
308
|
+
);
|
|
309
|
+
if (!shouldContinue) {
|
|
310
|
+
throw createError(
|
|
311
|
+
ErrorCode.DOCKER_PULL_FAILED,
|
|
312
|
+
"Docker image pull failed"
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
saveSetupProgress(SetupStep.PULL_IMAGE, { failed: true });
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Pull Docker image
|
|
322
|
+
*/
|
|
323
|
+
async pullDockerImage(imageName) {
|
|
324
|
+
return new Promise((resolve, reject) => {
|
|
325
|
+
try {
|
|
326
|
+
execSync(`docker pull ${imageName}`, {
|
|
327
|
+
stdio: "inherit",
|
|
328
|
+
});
|
|
329
|
+
resolve();
|
|
330
|
+
} catch (error) {
|
|
331
|
+
reject(error);
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Step 4: Start Agent (optional)
|
|
338
|
+
*/
|
|
339
|
+
async startAgentStep() {
|
|
340
|
+
displayStep(4, TOTAL_STEPS, "Start Agent Service");
|
|
341
|
+
|
|
342
|
+
const shouldStart = await promptConfirmation("Start the agent now?", true);
|
|
343
|
+
|
|
344
|
+
if (!shouldStart) {
|
|
345
|
+
displayInfo("You can start the agent later with: fenwave login");
|
|
346
|
+
saveSetupProgress(SetupStep.START_AGENT, { started: false });
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
displayInfo("Starting agent requires authentication...");
|
|
351
|
+
displayInfo("Please run: fenwave login");
|
|
352
|
+
|
|
353
|
+
saveSetupProgress(SetupStep.START_AGENT, { started: false });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Display completion message
|
|
358
|
+
*/
|
|
359
|
+
displayComplete(registrationData) {
|
|
360
|
+
displayStep(5, TOTAL_STEPS, "Setup Complete! 🎉");
|
|
361
|
+
|
|
362
|
+
console.log("");
|
|
363
|
+
displayHeader("Setup Summary");
|
|
364
|
+
|
|
365
|
+
displayKeyValue({
|
|
366
|
+
"Device ID": registrationData.deviceId,
|
|
367
|
+
"Device Name": registrationData.deviceName || "N/A",
|
|
368
|
+
Platform: registrationData.platform || "N/A",
|
|
369
|
+
User: registrationData.userEntityRef || "N/A",
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
console.log("");
|
|
373
|
+
console.log(chalk.green.bold("✅ Setup completed successfully!\n"));
|
|
374
|
+
|
|
375
|
+
console.log(chalk.bold("Next Steps:\n"));
|
|
376
|
+
console.log(chalk.gray(" 1. Start the agent:"));
|
|
377
|
+
console.log(chalk.cyan(" $ fenwave login\n"));
|
|
378
|
+
console.log(chalk.gray(" 2. Check agent status:"));
|
|
379
|
+
console.log(chalk.cyan(" $ fenwave status\n"));
|
|
380
|
+
console.log(chalk.gray(" 3. View agent information:"));
|
|
381
|
+
console.log(chalk.cyan(" $ fenwave info\n"));
|
|
382
|
+
|
|
383
|
+
console.log(chalk.bold("Documentation:\n"));
|
|
384
|
+
console.log(
|
|
385
|
+
chalk.gray(" • View all commands: ") + chalk.cyan("fenwave --help")
|
|
386
|
+
);
|
|
387
|
+
console.log(
|
|
388
|
+
chalk.gray(" • Setup guide: ") + chalk.cyan("fenwave setup-guide")
|
|
389
|
+
);
|
|
390
|
+
console.log("");
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Run setup wizard
|
|
396
|
+
*
|
|
397
|
+
* @param {Object} options - Wizard options
|
|
398
|
+
* @returns {Promise<void>}
|
|
399
|
+
*/
|
|
400
|
+
export async function runSetupWizard(options = {}) {
|
|
401
|
+
const wizard = new SetupWizard(options);
|
|
402
|
+
await wizard.run();
|
|
403
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Session Store
|
|
3
|
+
* Stores agent session information in memory
|
|
4
|
+
* This is a separate module to avoid circular dependencies
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Store agent session info (will be set when server starts)
|
|
8
|
+
let agentSessionInfo = {
|
|
9
|
+
agentId: null,
|
|
10
|
+
userEntityRef: null,
|
|
11
|
+
sessionExpiresAt: null,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Set agent session info
|
|
16
|
+
* @param {object} sessionInfo - Session information
|
|
17
|
+
*/
|
|
18
|
+
function setAgentSessionInfo(sessionInfo) {
|
|
19
|
+
agentSessionInfo.agentId = sessionInfo.agentId || null;
|
|
20
|
+
agentSessionInfo.userEntityRef = sessionInfo.userEntityRef || null;
|
|
21
|
+
agentSessionInfo.sessionExpiresAt = sessionInfo.sessionExpiresAt || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get agent session info
|
|
26
|
+
* @returns {object} Agent session info
|
|
27
|
+
*/
|
|
28
|
+
function getAgentSessionInfo() {
|
|
29
|
+
return agentSessionInfo;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Clear agent session info
|
|
34
|
+
*/
|
|
35
|
+
function clearAgentSessionInfo() {
|
|
36
|
+
agentSessionInfo.agentId = null;
|
|
37
|
+
agentSessionInfo.userEntityRef = null;
|
|
38
|
+
agentSessionInfo.sessionExpiresAt = null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default {
|
|
42
|
+
setAgentSessionInfo,
|
|
43
|
+
getAgentSessionInfo,
|
|
44
|
+
clearAgentSessionInfo,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
setAgentSessionInfo,
|
|
49
|
+
getAgentSessionInfo,
|
|
50
|
+
clearAgentSessionInfo,
|
|
51
|
+
};
|