qaify-playwright-reporter 0.10.2 → 0.11.0

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 CHANGED
@@ -66,7 +66,8 @@ Nama bin lama `playwright-reporter` tetap tersedia sebagai alias.
66
66
  Backend tidak memanggil AI (`QA_AI_MODE=actions`, default). Kegagalan yang tidak dikenali aturan diantrekan, lalu job `analyze` di [`qa.yml`](../.github/workflows/qa.yml) menjalankan Claude Code dengan langganan kamu:
67
67
 
68
68
  1. Di komputer yang sudah login Claude Code: `claude setup-token` → salin token (berlaku 1 tahun).
69
- 2. Simpan sebagai secret `CLAUDE_CODE_OAUTH_TOKEN` di repo yang diuji.
69
+ 2. Simpan token itu **sekali** di Qaify → Setelan → Agen AI → "Token langganan Claude", lalu di setiap repo nyalakan "Pakai setelan Agen AI akun saya" (Otomatisasi & AI). Aplikasi audit memakai akun pembuatnya. Workflow ≥ 0.11 mengambilnya lewat `qaify ai-env --oauth` hanya saat tugas berjalan, jadi secret per repo tidak perlu.
70
+ Cara lama tetap jalan: secret `CLAUDE_CODE_OAUTH_TOKEN` di repo yang diuji. Bila keduanya ada, token dari Qaify yang dipakai.
70
71
  3. Opsional: variable `QA_FACTORY_AI_MODEL` (default `sonnet`; `opus` lebih teliti tapi lebih cepat menghabiskan kuota).
71
72
 
72
73
  `claude -p` dijalankan di folder kosong dengan semua tool dimatikan, menerima antrean lewat stdin, dan mengembalikan JSON sesuai schema. `ai-submit --finalize` menerapkan hasilnya; kegagalan yang tidak terjawab (login gagal, kuota habis, timeout, PR dari fork tanpa secret) menjadi "belum jelas".
package/dist/ai-env.d.ts CHANGED
@@ -15,6 +15,8 @@ export interface AiEnvOptions {
15
15
  githubRunId?: number;
16
16
  envFile: string;
17
17
  errorOut?: string;
18
+ /** workflow memuat env file SETELAH membuang secret repo → token langganan Claude dari Qaify boleh diterima */
19
+ oauth?: boolean;
18
20
  url?: string;
19
21
  token?: string;
20
22
  fetch?: typeof fetch;
package/dist/ai-env.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // `qaify ai-env`: tepat sebelum langkah AI, tanya Qaify mesin + model + kredensial agen untuk tugas ini.
2
2
  // langganan (default / backend lama) → engine=claude_code, tanpa env file (secret CLAUDE_CODE_OAUTH_TOKEN dipakai)
3
+ // langganan + token disimpan di Qaify → (hanya dengan --oauth) env file berisi CLAUDE_CODE_OAUTH_TOKEN akun,
4
+ // menggantikan secret per repo; step Claude membuang secret lalu memuatnya
3
5
  // Claude Code + API key Anthropic → env file berisi ANTHROPIC_API_KEY (step Claude memuatnya & membuang secret OAuth)
4
6
  // Qaify Agent (OpenAI-compatible) → env file berisi QAIFY_AI_*; dibaca & dihapus `qaify agent`, tidak masuk env bash
5
7
  // agen tidak bisa dipakai (key dihapus / budget habis) → engine=none + hasil gagal berbentuk claude.json di --error-out
@@ -9,6 +11,7 @@ import { dirname } from 'node:path';
9
11
  /** Id model yang aman masuk ke --model / output step (cermin AgentSettings::MODEL_PATTERN backend). */
10
12
  export const MODEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/@+-]{0,99}$/;
11
13
  const ENV_NAME = /^(ANTHROPIC_[A-Z_]+|QAIFY_AI_[A-Z_]+)$/;
14
+ const OAUTH_NAME = 'CLAUDE_CODE_OAUTH_TOKEN';
12
15
  const SECRET_NAME = /(KEY|TOKEN|SECRET)/;
13
16
  const SUBSCRIPTION = { engine: 'claude_code', auth: 'subscription', model: null, provider: null, hasKey: false, error: null };
14
17
  export async function aiEnv(options) {
@@ -26,7 +29,12 @@ export async function aiEnv(options) {
26
29
  res = await (options.fetch ?? fetch)(`${url}/api/ingest/ai-env`, {
27
30
  method: 'POST',
28
31
  headers: { Accept: 'application/json', 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
29
- body: JSON.stringify({ kind: options.kind, ...(options.id ? { id: Number(options.id) } : {}), ...(options.githubRunId ? { github_run_id: options.githubRunId } : {}) }),
32
+ body: JSON.stringify({
33
+ kind: options.kind,
34
+ ...(options.id ? { id: Number(options.id) } : {}),
35
+ ...(options.githubRunId ? { github_run_id: options.githubRunId } : {}),
36
+ ...(options.oauth ? { oauth: true } : {}),
37
+ }),
30
38
  signal: AbortSignal.timeout(30_000),
31
39
  });
32
40
  }
@@ -51,7 +59,8 @@ export async function aiEnv(options) {
51
59
  }
52
60
  const env = {};
53
61
  for (const [name, value] of Object.entries((data?.env ?? {}))) {
54
- if (!ENV_NAME.test(name) || typeof value !== 'string' || !value)
62
+ const allowed = ENV_NAME.test(name) || (options.oauth === true && name === OAUTH_NAME);
63
+ if (!allowed || typeof value !== 'string' || !value)
55
64
  continue;
56
65
  if (SECRET_NAME.test(name))
57
66
  mask(value);
@@ -67,7 +76,11 @@ export async function aiEnv(options) {
67
76
  await writeFile(options.envFile, Object.entries(env).map(([k, v]) => `${k}=${shellQuote(v)}\n`).join(''), { mode: 0o600 });
68
77
  await chmod(options.envFile, 0o600).catch(() => { });
69
78
  }
70
- const label = engine === 'qaify_agent' ? `Qaify Agent (${data?.provider ?? 'API key'})` : hasKey ? 'Claude Code (API key Anthropic)' : 'Claude Code (langganan)';
79
+ const label = engine === 'qaify_agent'
80
+ ? `Qaify Agent (${data?.provider ?? 'API key'})`
81
+ : env[OAUTH_NAME] ? 'Claude Code (langganan, token dari Qaify)'
82
+ : hasKey ? 'Claude Code (API key Anthropic)'
83
+ : 'Claude Code (langganan, secret repo)';
71
84
  log(`qa-factory: agen AI ${label}${model ? `, model ${model}` : ''}`);
72
85
  return { engine, auth: data?.auth === 'key' ? 'key' : 'subscription', model, provider: data?.provider ?? null, hasKey, error: null };
73
86
  }
package/dist/analyze.d.ts CHANGED
@@ -32,6 +32,8 @@ export interface CliArgs {
32
32
  id: string | undefined;
33
33
  envFile: string | undefined;
34
34
  errorOut: string | undefined;
35
+ /** ai-env: workflow ≥ 0.11 menerima token langganan Claude dari Qaify (bukan secret repo) */
36
+ oauth: boolean;
35
37
  promptFile: string | undefined;
36
38
  instructionsFile: string | undefined;
37
39
  schemaFile: string | undefined;
package/dist/analyze.js CHANGED
@@ -60,6 +60,7 @@ export function parseArgs(argv) {
60
60
  id: value('id'),
61
61
  envFile: value('env-file'),
62
62
  errorOut: value('error-out'),
63
+ oauth: rest.includes('--oauth'),
63
64
  promptFile: value('prompt-file'),
64
65
  instructionsFile: value('instructions-file'),
65
66
  schemaFile: value('schema-file'),
package/dist/cli.js CHANGED
@@ -24,7 +24,7 @@
24
24
  // qaify autofix-schema --out schema.json --instructions instructions.txt
25
25
  // qaify autofix-submit --fix <id> --results claude.json --patch changes.patch --dir <folder kosong>
26
26
  // Agen AI pilihan user (Setelan → Agen AI di Qaify), sebelum langkah AI di semua workflow:
27
- // qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out claude.json]
27
+ // qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out claude.json] [--oauth]
28
28
  // qaify agent --env-file <file> --schema-file schema.json --out claude.json --prompt-file <f>
29
29
  // [--instructions-file <f>] [--input-file <f>] [--tools read,list,grep,write,edit,bash] [--max-turns 80] [--mcp-config mcp.json]
30
30
  import { appendFile, mkdir, readFile, writeFile } from 'node:fs/promises';
@@ -39,27 +39,27 @@ import { init, initRunner, NEXT_STEPS, RUNNER_NEXT_STEPS } from './init.js';
39
39
  import { check } from './check.js';
40
40
  import { aiEnv } from './ai-env.js';
41
41
  import { agentCommand } from './agent/command.js';
42
- const USAGE = `Pemakaian:
43
- qaify init [--force]
44
- qaify check
45
- qaify analyze --conclusion <${CONCLUSIONS.join('|')}>
46
- qaify ai-queue --out <file>
47
- qaify ai-schema --out <file-schema> [--instructions <file>]
48
- qaify ai-submit --results <file> [--finalize]
49
- qaify init-runner [--force]
50
- qaify audit-context --audit <id> --dir <folder>
51
- qaify audit-schema (--out <file-schema> [--instructions <file>] | --prompt)
52
- qaify audit-static --dir <folder> --out <file>
53
- qaify audit-submit --audit <id> --results <file> --evidence <folder> --context <file>
54
- qaify fix-context --fix <id> --dir <folder>
55
- qaify fix-schema --out <file-schema> [--instructions <file>]
56
- qaify fix-deps --dir <folder>
57
- qaify fix-diff --dir <folder> --out <folder>
58
- qaify fix-submit --fix <id> --results <file> --patch <file> --dir <folder>
59
- qaify autofix-context --fix <id> --dir <folder>
60
- qaify autofix-schema --out <file-schema> [--instructions <file>]
61
- qaify autofix-submit --fix <id> --results <file> --patch <file> --dir <folder>
62
- qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out <file>]
42
+ const USAGE = `Pemakaian:
43
+ qaify init [--force]
44
+ qaify check
45
+ qaify analyze --conclusion <${CONCLUSIONS.join('|')}>
46
+ qaify ai-queue --out <file>
47
+ qaify ai-schema --out <file-schema> [--instructions <file>]
48
+ qaify ai-submit --results <file> [--finalize]
49
+ qaify init-runner [--force]
50
+ qaify audit-context --audit <id> --dir <folder>
51
+ qaify audit-schema (--out <file-schema> [--instructions <file>] | --prompt)
52
+ qaify audit-static --dir <folder> --out <file>
53
+ qaify audit-submit --audit <id> --results <file> --evidence <folder> --context <file>
54
+ qaify fix-context --fix <id> --dir <folder>
55
+ qaify fix-schema --out <file-schema> [--instructions <file>]
56
+ qaify fix-deps --dir <folder>
57
+ qaify fix-diff --dir <folder> --out <folder>
58
+ qaify fix-submit --fix <id> --results <file> --patch <file> --dir <folder>
59
+ qaify autofix-context --fix <id> --dir <folder>
60
+ qaify autofix-schema --out <file-schema> [--instructions <file>]
61
+ qaify autofix-submit --fix <id> --results <file> --patch <file> --dir <folder>
62
+ qaify ai-env --kind <analyze|audit|audit_fix|fix> [--id <id>] --env-file <file> [--error-out <file>] [--oauth]
63
63
  qaify agent --env-file <file> --schema-file <file> --out <file> --prompt-file <file> [--instructions-file <file>] [--input-file <file>] [--tools <daftar>] [--max-turns <n>] [--mcp-config <file>]`;
64
64
  const args = parseArgs(process.argv.slice(2));
65
65
  const client = createClient();
@@ -289,6 +289,7 @@ switch (args.command) {
289
289
  githubRunId: run.github_run_id || undefined,
290
290
  envFile: args.envFile,
291
291
  errorOut: args.errorOut,
292
+ oauth: args.oauth,
292
293
  });
293
294
  await writeOutputs({ engine: result.engine, model: result.model ?? '', has_key: String(result.hasKey), provider: result.provider ?? '' });
294
295
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qaify-playwright-reporter",
3
- "version": "0.10.2",
3
+ "version": "0.11.0",
4
4
  "description": "Reporter Playwright untuk QA Factory (Qaify): kirim hasil test dan artefak ke /api/ingest, tanpa pernah menggagalkan test.",
5
5
  "license": "MIT",
6
6
  "author": "ahmdkenz",
@@ -4,9 +4,10 @@
4
4
  # Butuh di repo ini (sama dengan qa.yml):
5
5
  # secret QA_FACTORY_TOKEN token reporter repo ini
6
6
  # variable QA_FACTORY_URL URL API Qaify
7
- # secret CLAUDE_CODE_OAUTH_TOKEN langganan Claude Pro/Max Anda (`claude setup-token`); tidak perlu bila agen
8
- # "Perbaikan test" memakai API key (Qaify → Setelan → Agen AI, admin repo memilih
9
- # "pakai setelan Agen AI akun saya" di Otomatisasi & AI)
7
+ # secret CLAUDE_CODE_OAUTH_TOKEN opsional: langganan Claude Pro/Max Anda (`claude setup-token`); tidak perlu bila
8
+ # token itu disimpan sekali di Qaify (Setelan → Agen AI) atau agen "Perbaikan test"
9
+ # memakai API key — keduanya butuh admin repo memilih "pakai setelan Agen AI akun
10
+ # saya" di Otomatisasi & AI
10
11
  # GitHub App Qaify butuh izin Actions, Contents, dan Pull requests: Read and write di repo ini.
11
12
  # Job "fix" menjalankan agen tanpa token push; job "submit" (VM bersih) meng-clone ulang, menerapkan patch, lalu push.
12
13
  # Nama file harus tetap auto-fix.yml.
@@ -56,7 +57,7 @@ jobs:
56
57
  tools: composer
57
58
 
58
59
  - name: Pasang Qaify CLI
59
- run: npm install -g qaify-playwright-reporter@0.10
60
+ run: npm install -g qaify-playwright-reporter@0.11
60
61
 
61
62
  # token clone sekali pakai (read-only, hanya repo ini) dicabut sebelum agen berjalan
62
63
  - name: Ambil kegagalan & clone repo
@@ -87,7 +88,7 @@ jobs:
87
88
  QA_FACTORY_TOKEN: ${{ secrets.QA_FACTORY_TOKEN }}
88
89
  run: |
89
90
  qaify ai-env --kind fix --id "${{ inputs.fix_attempt_id }}" \
90
- --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json"
91
+ --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json" --oauth
91
92
 
92
93
  # hanya kredensial AI (secret langganan / API key Anthropic dari Qaify); tidak ada token Qaify maupun token push
93
94
  - name: Perbaiki dengan Claude Code
@@ -102,15 +103,16 @@ jobs:
102
103
  run: |
103
104
  # tanpa secret langganan dan tanpa API key: Claude Code pasti "Not logged in"; catat penyebabnya, jangan dipasang
104
105
  if [ ! -f "$RUNNER_TEMP/qaify-ai.env" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
105
- echo '::error::Secret CLAUDE_CODE_OAUTH_TOKEN kosong di repo ini dan engineer tidak memakai API key. Jalankan "claude setup-token" lalu isi secret-nya, atau pakai API key di Qaify (Setelan, Agen AI).'
106
- printf '%s' '{"type":"result","subtype":"error_qaify","is_error":true,"error_code":"ai_not_logged_in","result":"Not logged in (secret CLAUDE_CODE_OAUTH_TOKEN kosong)"}' > ../.qaify/claude.json
106
+ echo '::error::Secret CLAUDE_CODE_OAUTH_TOKEN kosong di repo ini dan engineer tidak memakai API key. Jalankan "claude setup-token" lalu isi secret-nya, atau simpan token itu sekali di Qaify (Setelan, Agen AI) agar berlaku untuk semua repo, atau pakai API key di sana.'
107
+ printf '%s' '{"type":"result","subtype":"error_qaify","is_error":true,"error_code":"ai_not_logged_in","result":"Not logged in (token langganan Claude kosong: tidak ada di Qaify maupun secret repo)"}' > ../.qaify/claude.json
107
108
  exit 1
108
109
  fi
109
110
  npm install -g @anthropic-ai/claude-code
110
- # API key Anthropic dari Qaify menggantikan langganan
111
+ # API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
111
112
  if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
113
+ unset CLAUDE_CODE_OAUTH_TOKEN
112
114
  set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
113
- rm -f "$RUNNER_TEMP/qaify-ai.env"; unset CLAUDE_CODE_OAUTH_TOKEN
115
+ rm -f "$RUNNER_TEMP/qaify-ai.env"
114
116
  fi
115
117
  claude -p "$(cat ../.qaify/prompt.txt)" \
116
118
  --model "$MODEL" \
@@ -160,7 +162,7 @@ jobs:
160
162
  - uses: actions/setup-node@v4
161
163
  with: { node-version: 22 }
162
164
  - name: Pasang Qaify CLI
163
- run: npm install -g qaify-playwright-reporter@0.10
165
+ run: npm install -g qaify-playwright-reporter@0.11
164
166
  - uses: actions/download-artifact@v4
165
167
  continue-on-error: true
166
168
  with:
package/templates/qa.yml CHANGED
@@ -3,7 +3,8 @@
3
3
  # secret QA_FACTORY_TOKEN token reporter dari Pengaturan → Token Reporter
4
4
  # variable QA_FACTORY_URL URL API QA Factory
5
5
  # secret CLAUDE_CODE_OAUTH_TOKEN opsional, analisis AI dengan langganan Claude Pro/Max Anda (`claude setup-token`);
6
- # atau pakai API key sendiri di Qaify → Setelan → Agen AI (Analis)
6
+ # tidak perlu bila token itu disimpan sekali di Qaify → Setelan → Agen AI (atau
7
+ # pakai API key di sana) dan admin repo memilih "pakai setelan Agen AI akun saya"
7
8
  # variable QA_FACTORY_AI_MODEL opsional, default sonnet (langganan tanpa model dari Qaify)
8
9
  # dan devDependency qaify-playwright-reporter (sudah terpasang bila Anda menjalankan `npx qaify init`).
9
10
  # Nama file harus tetap qa.yml.
@@ -34,8 +35,9 @@ jobs:
34
35
 
35
36
  # Status final run & analisis kegagalan. Tetap jalan walau test gagal / di-cancel.
36
37
  # Analisis AI memakai langganan Claude Pro/Max Anda lewat Claude Code (QA Factory tidak memanggil AI):
37
- # claude setup-token → simpan sebagai secret CLAUDE_CODE_OAUTH_TOKEN. Tanpa secret (mis. PR dari fork)
38
- # kegagalan tetap diklasifikasi aturan / "belum jelas". Kuota langganan dipakai bersama pemakaian pribadi.
38
+ # claude setup-token → simpan sekali di Qaify (Setelan → Agen AI) atau sebagai secret CLAUDE_CODE_OAUTH_TOKEN.
39
+ # Tanpa token (mis. PR dari fork) kegagalan tetap diklasifikasi aturan / "belum jelas". Kuota langganan dipakai
40
+ # bersama pemakaian pribadi.
39
41
  analyze:
40
42
  needs: test
41
43
  if: always()
@@ -68,7 +70,7 @@ jobs:
68
70
  id: ai
69
71
  if: steps.queue.outputs.count != '0'
70
72
  continue-on-error: true
71
- run: npx qaify ai-env --kind analyze --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$RUNNER_TEMP/qaf/claude.json"
73
+ run: npx qaify ai-env --kind analyze --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$RUNNER_TEMP/qaf/claude.json" --oauth
72
74
 
73
75
  # folder kosong: .claude/ dan .mcp.json milik repo tidak ikut termuat; semua tool dimatikan
74
76
  - name: Analisis dengan Claude Code
@@ -83,10 +85,11 @@ jobs:
83
85
  MODEL: ${{ steps.ai.outputs.model || vars.QA_FACTORY_AI_MODEL || 'sonnet' }}
84
86
  run: |
85
87
  npm install -g @anthropic-ai/claude-code
86
- # API key Anthropic dari Qaify menggantikan langganan
88
+ # API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
87
89
  if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
90
+ unset CLAUDE_CODE_OAUTH_TOKEN
88
91
  set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
89
- rm -f "$RUNNER_TEMP/qaify-ai.env"; unset CLAUDE_CODE_OAUTH_TOKEN
92
+ rm -f "$RUNNER_TEMP/qaify-ai.env"
90
93
  fi
91
94
  claude -p "Analisis setiap kegagalan test pada input JSON." \
92
95
  --model "$MODEL" \
@@ -4,7 +4,8 @@
4
4
  # secret QA_FACTORY_TOKEN token reporter repo runner (Pengaturan → Token Reporter)
5
5
  # variable QA_FACTORY_URL URL API Qaify
6
6
  # secret CLAUDE_CODE_OAUTH_TOKEN opsional: agen yang memakai langganan Claude Pro/Max (`claude setup-token`);
7
- # tidak perlu bila semua agen memakai API key (Qaify → Setelan → Agen AI)
7
+ # tidak perlu bila token itu sudah disimpan sekali di Qaify (Setelan → Agen AI,
8
+ # berlaku untuk semua repo) atau semua agen memakai API key
8
9
  # variable QA_FACTORY_AUDIT_MODE opsional, `static` / `claude`; hanya dipakai bila Qaify tidak mengirim mode
9
10
  # variable QA_FACTORY_AI_MODEL opsional, default sonnet (langganan tanpa model dari Qaify)
10
11
  # variable QA_FACTORY_MCP_VERSION opsional, versi @playwright/mcp (default latest)
@@ -61,7 +62,7 @@ jobs:
61
62
  tools: composer
62
63
 
63
64
  - name: Pasang Qaify CLI
64
- run: npm install -g qaify-playwright-reporter@0.10 playwright-core@1
65
+ run: npm install -g qaify-playwright-reporter@0.11 playwright-core@1
65
66
 
66
67
  # token clone sekali pakai (read-only, hanya repo FE + BE) dicabut sebelum auditor berjalan
67
68
  - name: Ambil konteks & clone repo FE + BE
@@ -81,7 +82,7 @@ jobs:
81
82
  QA_FACTORY_TOKEN: ${{ secrets.QA_FACTORY_TOKEN }}
82
83
  run: |
83
84
  qaify ai-env --kind audit --id "${{ inputs.audit_id }}" \
84
- --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json"
85
+ --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json" --oauth
85
86
 
86
87
  # pilihan Qaify → variable QA_FACTORY_AUDIT_MODE → claude bila secret ada; AI tanpa langganan / API key → static
87
88
  - name: Tentukan mode audit
@@ -92,7 +93,7 @@ jobs:
92
93
  HAS_KEY: ${{ steps.ai.outputs.has_key }}
93
94
  ENGINE: ${{ steps.ai.outputs.engine || 'claude_code' }}
94
95
  run: |
95
- mode="${REQUESTED:-$([ "$HAS_CLAUDE" = true ] && echo claude || echo static)}"
96
+ mode="${REQUESTED:-$([ "$HAS_CLAUDE" = true ] || [ "$HAS_KEY" = true ] && echo claude || echo static)}"
96
97
  if [ "$mode" != static ] && [ "$mode" != claude ]; then
97
98
  echo "::warning::Mode audit tidak dikenal ($mode); memakai static."
98
99
  mode=static
@@ -101,7 +102,7 @@ jobs:
101
102
  # hasil gagal + penyebabnya sudah ditulis step "Pilih agen AI"
102
103
  mode=failed
103
104
  elif [ "$mode" = claude ] && [ "$HAS_CLAUDE" != true ] && [ "$HAS_KEY" != true ]; then
104
- echo "::warning::Mode AI diminta tetapi secret CLAUDE_CODE_OAUTH_TOKEN kosong dan agen Auditor tidak memakai API key; audit dijalankan tanpa AI."
105
+ echo "::warning::Mode AI diminta tetapi token langganan Claude kosong (tidak disimpan di Qaify, Setelan → Agen AI, maupun secret CLAUDE_CODE_OAUTH_TOKEN repo) dan agen Auditor tidak memakai API key; audit dijalankan tanpa AI."
105
106
  mode=static
106
107
  fi
107
108
  echo "qa-factory: mode audit $mode ($ENGINE)"
@@ -137,10 +138,11 @@ jobs:
137
138
  CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
138
139
  MODEL: ${{ steps.ai.outputs.model || vars.QA_FACTORY_AI_MODEL || 'sonnet' }}
139
140
  run: |
140
- # API key Anthropic dari Qaify menggantikan langganan
141
+ # API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
141
142
  if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
143
+ unset CLAUDE_CODE_OAUTH_TOKEN
142
144
  set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
143
- rm -f "$RUNNER_TEMP/qaify-ai.env"; unset CLAUDE_CODE_OAUTH_TOKEN
145
+ rm -f "$RUNNER_TEMP/qaify-ai.env"
144
146
  fi
145
147
  claude -p "$(cat .qaify/prompt.txt)" \
146
148
  --model "$MODEL" \
@@ -3,8 +3,9 @@
3
3
  # Butuh di repo runner ini (sama dengan qaify-audit.yml):
4
4
  # secret QA_FACTORY_TOKEN token reporter repo runner
5
5
  # variable QA_FACTORY_URL URL API Qaify
6
- # secret CLAUDE_CODE_OAUTH_TOKEN langganan Claude Pro/Max Anda (`claude setup-token`); hanya untuk engineer yang
7
- # memakai langganan (tidak perlu bila semua engineer memakai API key di Qaify)
6
+ # secret CLAUDE_CODE_OAUTH_TOKEN opsional: langganan Claude Pro/Max Anda (`claude setup-token`) untuk engineer yang
7
+ # memakai langganan; tidak perlu bila token itu sudah disimpan sekali di Qaify
8
+ # (Setelan → Agen AI, berlaku untuk semua repo) atau semua engineer memakai API key
8
9
  # GitHub App Qaify butuh izin Contents: Read and write dan Pull requests: Read and write di repo FE/BE.
9
10
  # Engineer (Nanda / Fajar / Pak Yoga) dipilih Qaify per temuan menurut kompleksitas atau pilihan Anda; mesin, model,
10
11
  # dan API key-nya dari Setelan → Agen AI (Claude Code dengan langganan / API key Anthropic, atau Qaify Agent dengan
@@ -59,7 +60,7 @@ jobs:
59
60
  tools: composer
60
61
 
61
62
  - name: Pasang Qaify CLI
62
- run: npm install -g qaify-playwright-reporter@0.10
63
+ run: npm install -g qaify-playwright-reporter@0.11
63
64
 
64
65
  # token clone sekali pakai (read-only, hanya repo target) dicabut sebelum agen berjalan
65
66
  - name: Ambil temuan & clone repo target
@@ -88,7 +89,7 @@ jobs:
88
89
  QA_FACTORY_TOKEN: ${{ secrets.QA_FACTORY_TOKEN }}
89
90
  run: |
90
91
  qaify ai-env --kind audit_fix --id "${{ inputs.fix_id }}" \
91
- --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json"
92
+ --env-file "$RUNNER_TEMP/qaify-ai.env" --error-out "$WORK/.qaify/claude.json" --oauth
92
93
 
93
94
  # hanya kredensial AI (secret langganan / API key Anthropic dari Qaify); tidak ada token Qaify maupun token push
94
95
  - name: Perbaiki dengan Claude Code
@@ -102,15 +103,16 @@ jobs:
102
103
  run: |
103
104
  # tanpa secret langganan dan tanpa API key: Claude Code pasti "Not logged in"; catat penyebabnya, jangan dipasang
104
105
  if [ ! -f "$RUNNER_TEMP/qaify-ai.env" ] && [ -z "$CLAUDE_CODE_OAUTH_TOKEN" ]; then
105
- echo '::error::Secret CLAUDE_CODE_OAUTH_TOKEN kosong di repo runner ini dan engineer tidak memakai API key. Jalankan "claude setup-token" lalu isi secret-nya, atau pakai API key di Qaify (Setelan, Agen AI).'
106
- printf '%s' '{"type":"result","subtype":"error_qaify","is_error":true,"error_code":"ai_not_logged_in","result":"Not logged in (secret CLAUDE_CODE_OAUTH_TOKEN kosong)"}' > ../.qaify/claude.json
106
+ echo '::error::Secret CLAUDE_CODE_OAUTH_TOKEN kosong di repo runner ini dan engineer tidak memakai API key. Jalankan "claude setup-token" lalu isi secret-nya, atau simpan token itu sekali di Qaify (Setelan, Agen AI) agar berlaku untuk semua repo, atau pakai API key di sana.'
107
+ printf '%s' '{"type":"result","subtype":"error_qaify","is_error":true,"error_code":"ai_not_logged_in","result":"Not logged in (token langganan Claude kosong: tidak ada di Qaify maupun secret repo)"}' > ../.qaify/claude.json
107
108
  exit 1
108
109
  fi
109
110
  npm install -g @anthropic-ai/claude-code
110
- # API key Anthropic dari Qaify menggantikan langganan
111
+ # API key Anthropic / token langganan Claude dari Qaify (Setelan → Agen AI) menggantikan secret repo
111
112
  if [ -f "$RUNNER_TEMP/qaify-ai.env" ]; then
113
+ unset CLAUDE_CODE_OAUTH_TOKEN
112
114
  set -a; . "$RUNNER_TEMP/qaify-ai.env"; set +a
113
- rm -f "$RUNNER_TEMP/qaify-ai.env"; unset CLAUDE_CODE_OAUTH_TOKEN
115
+ rm -f "$RUNNER_TEMP/qaify-ai.env"
114
116
  fi
115
117
  claude -p "$(cat ../.qaify/prompt.txt)" \
116
118
  --model "$MODEL" \
@@ -158,7 +160,7 @@ jobs:
158
160
  - uses: actions/setup-node@v4
159
161
  with: { node-version: 22 }
160
162
  - name: Pasang Qaify CLI
161
- run: npm install -g qaify-playwright-reporter@0.10
163
+ run: npm install -g qaify-playwright-reporter@0.11
162
164
  - uses: actions/download-artifact@v4
163
165
  continue-on-error: true
164
166
  with: