jobarbiter 0.3.8 → 0.3.9

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.
@@ -435,9 +435,11 @@ async function runToolDetectionStep(prompt, config) {
435
435
  console.log(` communication clarity, iteration patterns, tool fluency\n`);
436
436
  console.log(` This is what makes your profile ${c.bold("verified")} and ${c.bold("valuable")}.`);
437
437
  console.log(` Your observer regularly records and reports your proven AI proficiency.\n`);
438
- console.log(` ${c.bold("Privacy:")} ${c.highlight("No code, prompts, or content leave your machine.")}`);
439
- console.log(` Only derived proficiency signals (scores, patterns, dimensions)`);
440
- console.log(` are submitted never raw session data.\n`);
438
+ console.log(` ${c.bold("Privacy:")} Analysis runs ${c.highlight("locally by your own AI agent.")}`);
439
+ console.log(` Your agent reads your sessions to evaluate proficiency, but`);
440
+ console.log(` ${c.bold("only derived scores and signals are submitted to JobArbiter.")}`);
441
+ console.log(` Raw session data — prompts, responses, code — never leaves`);
442
+ console.log(` your machine and is never stored on our servers.\n`);
441
443
  console.log(c.dim(` Data stored locally: ~/.config/jobarbiter/observer/observations.json`));
442
444
  console.log(c.dim(` Review anytime: jobarbiter observe review\n`));
443
445
  const observerNames = needsObserver.map((t) => t.name).join(", ");
@@ -458,11 +460,12 @@ async function runToolDetectionStep(prompt, config) {
458
460
  if (result.installed.length > 0) {
459
461
  console.log(`\n ${sym.check} ${c.success(`${result.installed.length} observer${result.installed.length > 1 ? "s" : ""} installed!`)}`);
460
462
  console.log(`\n ${c.bold("What happens now?")}`);
461
- console.log(` Observers run automatically each time you use your AI tools.`);
462
- console.log(` Just work normally — every Claude Code session, every Gemini`);
463
- console.log(` conversation, every Codex task builds your profile.\n`);
464
- console.log(` ${sym.arrow} ${c.highlight("Your next AI session is your first proficiency data point.")}`);
465
- console.log(c.dim(` Tip: The more you use your tools, the richer your profile becomes.\n`));
463
+ console.log(` Observers activate each time you use your AI tools.`);
464
+ console.log(` Just work normally — every session builds your profile.\n`);
465
+ console.log(` ${sym.arrow} ${c.highlight("Your next AI session will begin your proficiency analysis.")}`);
466
+ console.log(` Your own AI agent performs the analysis locally evaluating`);
467
+ console.log(` how you work, not just how much. Only derived scores are`);
468
+ console.log(` submitted to JobArbiter. Raw session data never leaves your machine.\n`);
466
469
  }
467
470
  }
468
471
  else {
@@ -502,10 +505,11 @@ async function runConnectAIAccountsStep(prompt) {
502
505
  console.log(` ${sym.bullet} Model preferences ${c.dim("(which models you reach for)")}`);
503
506
  console.log(` ${sym.bullet} Session frequency and consistency over time`);
504
507
  console.log(` ${sym.bullet} API spend patterns ${c.dim("(demonstrates serious usage)")}\n`);
505
- console.log(` ${c.bold("What we NEVER access:")}`);
506
- console.log(` ${sym.bullet} Your conversation content`);
507
- console.log(` ${sym.bullet} Prompts or responses`);
508
- console.log(` ${sym.bullet} Any proprietary or sensitive data\n`);
508
+ console.log(` ${c.bold("How your data stays safe:")}`);
509
+ console.log(` ${sym.bullet} Analysis runs ${c.bold("locally on your machine")} by your own AI agent`);
510
+ console.log(` ${sym.bullet} Only derived proficiency scores leave your machine — never raw`);
511
+ console.log(` session data, prompts, responses, or conversation content`);
512
+ console.log(` ${sym.bullet} ${c.bold("Nothing is stored on JobArbiter's servers")} except your scores\n`);
509
513
  // Check for already connected providers
510
514
  const existingProviders = loadProviderKeys();
511
515
  if (existingProviders.length > 0) {
@@ -521,27 +525,31 @@ async function runConnectAIAccountsStep(prompt) {
521
525
  console.log(` ${c.bold("Available connections:")}\n`);
522
526
  console.log(` ${c.highlight("1.")} Anthropic API key — Pull Claude usage stats`);
523
527
  console.log(` ${c.highlight("2.")} OpenAI API key — Pull GPT/ChatGPT usage stats`);
524
- console.log(` ${c.highlight("3.")} Skip for now\n`);
528
+ console.log(` ${c.highlight("3.")} Google AI API key — Pull Gemini usage stats`);
529
+ console.log(` ${c.highlight("4.")} Skip for now\n`);
525
530
  console.log(c.dim(` You can connect accounts later with 'jobarbiter tokens connect'\n`));
526
- const choice = await prompt.question(` Your choice ${c.dim("[1/2/3]")}: `);
527
- if (choice === "3" || choice.toLowerCase() === "skip" || choice === "") {
531
+ const choice = await prompt.question(` Your choice ${c.dim("[1/2/3/4]")}: `);
532
+ if (choice === "4" || choice.toLowerCase() === "skip" || choice === "") {
528
533
  console.log(`\n${c.dim(" Skipped — you can connect providers later with 'jobarbiter tokens connect'")}\n`);
529
534
  continueConnecting = false;
530
535
  }
531
536
  else if (choice === "1") {
532
537
  await connectProvider(prompt, "anthropic", "Anthropic");
533
- // Ask if they want to connect another
534
538
  continueConnecting = await prompt.confirm(`\n Connect another provider?`, false);
535
539
  console.log();
536
540
  }
537
541
  else if (choice === "2") {
538
542
  await connectProvider(prompt, "openai", "OpenAI");
539
- // Ask if they want to connect another
543
+ continueConnecting = await prompt.confirm(`\n Connect another provider?`, false);
544
+ console.log();
545
+ }
546
+ else if (choice === "3") {
547
+ await connectProvider(prompt, "google", "Google AI");
540
548
  continueConnecting = await prompt.confirm(`\n Connect another provider?`, false);
541
549
  console.log();
542
550
  }
543
551
  else {
544
- console.log(c.error(" Please enter 1, 2, or 3"));
552
+ console.log(c.error(" Please enter 1, 2, 3, or 4"));
545
553
  }
546
554
  }
547
555
  }
@@ -573,9 +581,10 @@ async function connectProvider(prompt, providerId, providerName) {
573
581
  function showWorkerCompletion(state) {
574
582
  console.log(`${sym.done} ${c.bold("Step 7/7 — You're In!")}\n`);
575
583
  console.log(`Your profile is live. Here's how it builds:\n`);
576
- console.log(` 💡 ${c.bold("Just use your AI tools normally.")} Every session is observed,`);
577
- console.log(` analyzed, and builds your verified proficiency profile.`);
578
- console.log(` Your next AI session is your first data point.\n`);
584
+ console.log(` 💡 ${c.bold("Your next AI session will begin your proficiency analysis.")}`);
585
+ console.log(` Just use your tools normally. Your own AI agent analyzes each`);
586
+ console.log(` session locally evaluating how you work with AI, not just`);
587
+ console.log(` how much. Only proficiency scores are sent to JobArbiter.\n`);
579
588
  console.log(` 📊 Your proficiency profile builds automatically from:`);
580
589
  console.log(` ${c.bold("How you use AI (qualitative):")}`);
581
590
  console.log(` ${sym.bullet} Orchestration complexity ${c.dim("— single prompts vs multi-agent pipelines")}`);
@@ -35,6 +35,10 @@ export declare function validateAnthropicKey(apiKey: string): Promise<Validation
35
35
  * Returns validation result with usage summary if available.
36
36
  */
37
37
  export declare function validateOpenAIKey(apiKey: string): Promise<ValidationResult>;
38
+ /**
39
+ * Validate a Google AI API key by making a test API call.
40
+ */
41
+ export declare function validateGoogleKey(apiKey: string): Promise<ValidationResult>;
38
42
  /**
39
43
  * Get list of supported providers.
40
44
  */
@@ -133,6 +133,30 @@ export async function validateOpenAIKey(apiKey) {
133
133
  return { valid: false, error: "Unknown error" };
134
134
  }
135
135
  }
136
+ /**
137
+ * Validate a Google AI API key by making a test API call.
138
+ */
139
+ export async function validateGoogleKey(apiKey) {
140
+ try {
141
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`, { method: "GET" });
142
+ if (response.status === 400 || response.status === 401 || response.status === 403) {
143
+ return { valid: false, error: "Invalid API key" };
144
+ }
145
+ if (!response.ok) {
146
+ return { valid: false, error: `API error: ${response.status}` };
147
+ }
148
+ return {
149
+ valid: true,
150
+ summary: "API key validated",
151
+ };
152
+ }
153
+ catch (err) {
154
+ if (err instanceof Error) {
155
+ return { valid: false, error: `Connection error: ${err.message}` };
156
+ }
157
+ return { valid: false, error: "Unknown error" };
158
+ }
159
+ }
136
160
  /**
137
161
  * Get list of supported providers.
138
162
  */
@@ -140,6 +164,7 @@ export function getSupportedProviders() {
140
164
  return [
141
165
  { id: "anthropic", name: "Anthropic" },
142
166
  { id: "openai", name: "OpenAI" },
167
+ { id: "google", name: "Google AI" },
143
168
  ];
144
169
  }
145
170
  /**
@@ -151,6 +176,8 @@ export async function validateProviderKey(provider, apiKey) {
151
176
  return validateAnthropicKey(apiKey);
152
177
  case "openai":
153
178
  return validateOpenAIKey(apiKey);
179
+ case "google":
180
+ return validateGoogleKey(apiKey);
154
181
  default:
155
182
  return { valid: false, error: `Unknown provider: ${provider}` };
156
183
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobarbiter",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "CLI for JobArbiter — the first AI Proficiency Marketplace",
5
5
  "type": "module",
6
6
  "bin": {
@@ -529,9 +529,11 @@ async function runToolDetectionStep(
529
529
  console.log(` communication clarity, iteration patterns, tool fluency\n`);
530
530
  console.log(` This is what makes your profile ${c.bold("verified")} and ${c.bold("valuable")}.`);
531
531
  console.log(` Your observer regularly records and reports your proven AI proficiency.\n`);
532
- console.log(` ${c.bold("Privacy:")} ${c.highlight("No code, prompts, or content leave your machine.")}`);
533
- console.log(` Only derived proficiency signals (scores, patterns, dimensions)`);
534
- console.log(` are submitted never raw session data.\n`);
532
+ console.log(` ${c.bold("Privacy:")} Analysis runs ${c.highlight("locally by your own AI agent.")}`);
533
+ console.log(` Your agent reads your sessions to evaluate proficiency, but`);
534
+ console.log(` ${c.bold("only derived scores and signals are submitted to JobArbiter.")}`);
535
+ console.log(` Raw session data — prompts, responses, code — never leaves`);
536
+ console.log(` your machine and is never stored on our servers.\n`);
535
537
  console.log(c.dim(` Data stored locally: ~/.config/jobarbiter/observer/observations.json`));
536
538
  console.log(c.dim(` Review anytime: jobarbiter observe review\n`));
537
539
 
@@ -558,11 +560,12 @@ async function runToolDetectionStep(
558
560
  if (result.installed.length > 0) {
559
561
  console.log(`\n ${sym.check} ${c.success(`${result.installed.length} observer${result.installed.length > 1 ? "s" : ""} installed!`)}`);
560
562
  console.log(`\n ${c.bold("What happens now?")}`);
561
- console.log(` Observers run automatically each time you use your AI tools.`);
562
- console.log(` Just work normally — every Claude Code session, every Gemini`);
563
- console.log(` conversation, every Codex task builds your profile.\n`);
564
- console.log(` ${sym.arrow} ${c.highlight("Your next AI session is your first proficiency data point.")}`);
565
- console.log(c.dim(` Tip: The more you use your tools, the richer your profile becomes.\n`));
563
+ console.log(` Observers activate each time you use your AI tools.`);
564
+ console.log(` Just work normally — every session builds your profile.\n`);
565
+ console.log(` ${sym.arrow} ${c.highlight("Your next AI session will begin your proficiency analysis.")}`);
566
+ console.log(` Your own AI agent performs the analysis locally evaluating`);
567
+ console.log(` how you work, not just how much. Only derived scores are`);
568
+ console.log(` submitted to JobArbiter. Raw session data never leaves your machine.\n`);
566
569
  }
567
570
  } else {
568
571
  console.log(c.dim("\n Skipped — you can install observers later with 'jobarbiter observe install'.\n"));
@@ -606,10 +609,11 @@ async function runConnectAIAccountsStep(prompt: Prompt): Promise<void> {
606
609
  console.log(` ${sym.bullet} Session frequency and consistency over time`);
607
610
  console.log(` ${sym.bullet} API spend patterns ${c.dim("(demonstrates serious usage)")}\n`);
608
611
 
609
- console.log(` ${c.bold("What we NEVER access:")}`);
610
- console.log(` ${sym.bullet} Your conversation content`);
611
- console.log(` ${sym.bullet} Prompts or responses`);
612
- console.log(` ${sym.bullet} Any proprietary or sensitive data\n`);
612
+ console.log(` ${c.bold("How your data stays safe:")}`);
613
+ console.log(` ${sym.bullet} Analysis runs ${c.bold("locally on your machine")} by your own AI agent`);
614
+ console.log(` ${sym.bullet} Only derived proficiency scores leave your machine — never raw`);
615
+ console.log(` session data, prompts, responses, or conversation content`);
616
+ console.log(` ${sym.bullet} ${c.bold("Nothing is stored on JobArbiter's servers")} except your scores\n`);
613
617
 
614
618
  // Check for already connected providers
615
619
  const existingProviders = loadProviderKeys();
@@ -628,26 +632,29 @@ async function runConnectAIAccountsStep(prompt: Prompt): Promise<void> {
628
632
  console.log(` ${c.bold("Available connections:")}\n`);
629
633
  console.log(` ${c.highlight("1.")} Anthropic API key — Pull Claude usage stats`);
630
634
  console.log(` ${c.highlight("2.")} OpenAI API key — Pull GPT/ChatGPT usage stats`);
631
- console.log(` ${c.highlight("3.")} Skip for now\n`);
635
+ console.log(` ${c.highlight("3.")} Google AI API key — Pull Gemini usage stats`);
636
+ console.log(` ${c.highlight("4.")} Skip for now\n`);
632
637
  console.log(c.dim(` You can connect accounts later with 'jobarbiter tokens connect'\n`));
633
638
 
634
- const choice = await prompt.question(` Your choice ${c.dim("[1/2/3]")}: `);
639
+ const choice = await prompt.question(` Your choice ${c.dim("[1/2/3/4]")}: `);
635
640
 
636
- if (choice === "3" || choice.toLowerCase() === "skip" || choice === "") {
641
+ if (choice === "4" || choice.toLowerCase() === "skip" || choice === "") {
637
642
  console.log(`\n${c.dim(" Skipped — you can connect providers later with 'jobarbiter tokens connect'")}\n`);
638
643
  continueConnecting = false;
639
644
  } else if (choice === "1") {
640
645
  await connectProvider(prompt, "anthropic", "Anthropic");
641
- // Ask if they want to connect another
642
646
  continueConnecting = await prompt.confirm(`\n Connect another provider?`, false);
643
647
  console.log();
644
648
  } else if (choice === "2") {
645
649
  await connectProvider(prompt, "openai", "OpenAI");
646
- // Ask if they want to connect another
650
+ continueConnecting = await prompt.confirm(`\n Connect another provider?`, false);
651
+ console.log();
652
+ } else if (choice === "3") {
653
+ await connectProvider(prompt, "google", "Google AI");
647
654
  continueConnecting = await prompt.confirm(`\n Connect another provider?`, false);
648
655
  console.log();
649
656
  } else {
650
- console.log(c.error(" Please enter 1, 2, or 3"));
657
+ console.log(c.error(" Please enter 1, 2, 3, or 4"));
651
658
  }
652
659
  }
653
660
  }
@@ -685,9 +692,10 @@ async function connectProvider(prompt: Prompt, providerId: string, providerName:
685
692
  function showWorkerCompletion(state: OnboardState): void {
686
693
  console.log(`${sym.done} ${c.bold("Step 7/7 — You're In!")}\n`);
687
694
  console.log(`Your profile is live. Here's how it builds:\n`);
688
- console.log(` 💡 ${c.bold("Just use your AI tools normally.")} Every session is observed,`);
689
- console.log(` analyzed, and builds your verified proficiency profile.`);
690
- console.log(` Your next AI session is your first data point.\n`);
695
+ console.log(` 💡 ${c.bold("Your next AI session will begin your proficiency analysis.")}`);
696
+ console.log(` Just use your tools normally. Your own AI agent analyzes each`);
697
+ console.log(` session locally evaluating how you work with AI, not just`);
698
+ console.log(` how much. Only proficiency scores are sent to JobArbiter.\n`);
691
699
 
692
700
  console.log(` 📊 Your proficiency profile builds automatically from:`);
693
701
  console.log(` ${c.bold("How you use AI (qualitative):")}`);
@@ -180,6 +180,36 @@ export async function validateOpenAIKey(apiKey: string): Promise<ValidationResul
180
180
  }
181
181
  }
182
182
 
183
+ /**
184
+ * Validate a Google AI API key by making a test API call.
185
+ */
186
+ export async function validateGoogleKey(apiKey: string): Promise<ValidationResult> {
187
+ try {
188
+ const response = await fetch(
189
+ `https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,
190
+ { method: "GET" },
191
+ );
192
+
193
+ if (response.status === 400 || response.status === 401 || response.status === 403) {
194
+ return { valid: false, error: "Invalid API key" };
195
+ }
196
+
197
+ if (!response.ok) {
198
+ return { valid: false, error: `API error: ${response.status}` };
199
+ }
200
+
201
+ return {
202
+ valid: true,
203
+ summary: "API key validated",
204
+ };
205
+ } catch (err) {
206
+ if (err instanceof Error) {
207
+ return { valid: false, error: `Connection error: ${err.message}` };
208
+ }
209
+ return { valid: false, error: "Unknown error" };
210
+ }
211
+ }
212
+
183
213
  /**
184
214
  * Get list of supported providers.
185
215
  */
@@ -187,6 +217,7 @@ export function getSupportedProviders(): Array<{ id: string; name: string }> {
187
217
  return [
188
218
  { id: "anthropic", name: "Anthropic" },
189
219
  { id: "openai", name: "OpenAI" },
220
+ { id: "google", name: "Google AI" },
190
221
  ];
191
222
  }
192
223
 
@@ -199,6 +230,8 @@ export async function validateProviderKey(provider: string, apiKey: string): Pro
199
230
  return validateAnthropicKey(apiKey);
200
231
  case "openai":
201
232
  return validateOpenAIKey(apiKey);
233
+ case "google":
234
+ return validateGoogleKey(apiKey);
202
235
  default:
203
236
  return { valid: false, error: `Unknown provider: ${provider}` };
204
237
  }