icoa-cli 2.19.3 → 2.19.4

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.
@@ -36,7 +36,7 @@ export async function handleChatMessage(input) {
36
36
  chatActive = false;
37
37
  chatSession = null;
38
38
  // Anonymous stats
39
- fetch('https://practice.icoa2026.au:9090/api/icoa/demo-stats', {
39
+ fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
40
40
  method: 'POST',
41
41
  headers: { 'Content-Type': 'application/json' },
42
42
  body: JSON.stringify({ type: 'ai4ctf', tokensUsed: chatTokensUsed, timestamp: new Date().toISOString() }),
@@ -73,7 +73,7 @@ export async function handleCtf4aiMessage(input) {
73
73
  if (input === 'exit' || input === 'back' || input === 'quit') {
74
74
  ctf4aiActive = false;
75
75
  ctf4aiSession = null;
76
- fetch('https://practice.icoa2026.au:9090/api/icoa/demo-stats', {
76
+ fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
77
77
  method: 'POST',
78
78
  headers: { 'Content-Type': 'application/json' },
79
79
  body: JSON.stringify({ type: 'ctf4ai', solved: false, tokensUsed: ctf4aiTokens, timestamp: new Date().toISOString() }),
@@ -90,7 +90,7 @@ export async function handleCtf4aiMessage(input) {
90
90
  console.log(chalk.white(' The AI held its ground this time. 💪'));
91
91
  ctf4aiActive = false;
92
92
  ctf4aiSession = null;
93
- fetch('https://practice.icoa2026.au:9090/api/icoa/demo-stats', {
93
+ fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
94
94
  method: 'POST',
95
95
  headers: { 'Content-Type': 'application/json' },
96
96
  body: JSON.stringify({ type: 'ctf4ai', solved: false, tokensUsed: ctf4aiTokens, timestamp: new Date().toISOString() }),
@@ -121,7 +121,7 @@ export async function handleCtf4aiMessage(input) {
121
121
  console.log(chalk.gray(' This is prompt injection — a real AI vulnerability.'));
122
122
  ctf4aiActive = false;
123
123
  ctf4aiSession = null;
124
- fetch('https://practice.icoa2026.au:9090/api/icoa/demo-stats', {
124
+ fetch('https://practice.icoa2026.au/api/icoa/demo-stats', {
125
125
  method: 'POST',
126
126
  headers: { 'Content-Type': 'application/json' },
127
127
  body: JSON.stringify({ type: 'ctf4ai', solved: true, tokensUsed: ctf4aiTokens, timestamp: new Date().toISOString() }),
@@ -811,7 +811,7 @@ export function registerExamCommand(program) {
811
811
  console.log();
812
812
  drawProgress(0, 'Validating token...');
813
813
  try {
814
- const res = await fetch(`${serverUrl}:9090/api/icoa/exam-token`, {
814
+ const res = await fetch(`${serverUrl}/api/icoa/exam-token`, {
815
815
  method: 'POST',
816
816
  headers: { 'Content-Type': 'application/json' },
817
817
  body: JSON.stringify({ token: code.trim() }),
@@ -137,22 +137,34 @@ export class CTFdClient {
137
137
  return destPath;
138
138
  }
139
139
  async getTokenViaIcoaApi(username, password) {
140
- // Try ICOA token API (port 9090) first
140
+ const body = JSON.stringify({ name: username, password });
141
+ const headers = { 'Content-Type': 'application/json' };
142
+ // Try via nginx proxy first (same origin, no port)
143
+ try {
144
+ const res = await fetch(`${this.baseUrl}/api/icoa/token`, {
145
+ method: 'POST', headers, body,
146
+ signal: AbortSignal.timeout(5000),
147
+ });
148
+ if (res.ok) {
149
+ const json = await res.json();
150
+ if (json.success && json.data?.token)
151
+ return json.data.token;
152
+ }
153
+ }
154
+ catch { /* proxy not available */ }
155
+ // Fallback: direct port 9090
141
156
  try {
142
157
  const res = await fetch(`${this.baseUrl}:9090/api/icoa/token`, {
143
- method: 'POST',
144
- headers: { 'Content-Type': 'application/json' },
145
- body: JSON.stringify({ name: username, password }),
158
+ method: 'POST', headers, body,
146
159
  signal: AbortSignal.timeout(5000),
147
160
  });
148
161
  if (res.ok) {
149
162
  const json = await res.json();
150
- if (json.success && json.data?.token) {
163
+ if (json.success && json.data?.token)
151
164
  return json.data.token;
152
- }
153
165
  }
154
166
  }
155
- catch { /* API not available, try standard flow */ }
167
+ catch { /* API not available */ }
156
168
  return null;
157
169
  }
158
170
  async loginWithCredentials(username, password) {
@@ -4,6 +4,7 @@ export declare class ExamClient {
4
4
  private token;
5
5
  constructor(baseUrl: string, token: string);
6
6
  private request;
7
+ private _fetch;
7
8
  getExams(): Promise<ExamListItem[]>;
8
9
  startExam(examId: string): Promise<{
9
10
  session: ExamSession;
@@ -6,7 +6,23 @@ export class ExamClient {
6
6
  this.token = token;
7
7
  }
8
8
  async request(method, path, body) {
9
- const url = `${this.baseUrl}:9090/api/icoa/exams${path}`;
9
+ // Try nginx proxy first, fallback to direct port
10
+ const urls = [
11
+ `${this.baseUrl}/api/icoa/exams${path}`,
12
+ `${this.baseUrl}:9090/api/icoa/exams${path}`,
13
+ ];
14
+ let lastError = null;
15
+ for (const url of urls) {
16
+ try {
17
+ return await this._fetch(method, url, body);
18
+ }
19
+ catch (e) {
20
+ lastError = e;
21
+ }
22
+ }
23
+ throw lastError || new Error('Exam API unreachable');
24
+ }
25
+ async _fetch(method, url, body) {
10
26
  const res = await fetch(url, {
11
27
  method,
12
28
  headers: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icoa-cli",
3
- "version": "2.19.3",
3
+ "version": "2.19.4",
4
4
  "description": "ICOA CLI — The world's first CLI-native CTF competition terminal",
5
5
  "type": "module",
6
6
  "bin": {