jobarbiter 0.3.0 → 0.3.2
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/dist/index.js +172 -43
- package/dist/lib/config.js +1 -1
- package/dist/lib/detect-tools.d.ts +46 -0
- package/dist/lib/detect-tools.js +473 -0
- package/dist/lib/observe.d.ts +52 -0
- package/dist/lib/observe.js +672 -0
- package/dist/lib/onboard.d.ts +13 -0
- package/dist/lib/onboard.js +580 -0
- package/package.json +15 -5
- package/src/index.ts +194 -48
- package/src/lib/config.ts +1 -1
- package/src/lib/detect-tools.ts +526 -0
- package/src/lib/observe.ts +753 -0
- package/src/lib/onboard.ts +694 -0
- package/test/smoke.test.ts +205 -0
- package/vitest.config.ts +8 -0
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import { Command } from "commander";
|
|
|
3
3
|
import { saveConfig, requireConfig, getConfigPath } from "./lib/config.js";
|
|
4
4
|
import { api, apiUnauthenticated, ApiError } from "./lib/api.js";
|
|
5
5
|
import { output, outputList, success, error, setJsonMode } from "./lib/output.js";
|
|
6
|
+
import { runOnboardWizard } from "./lib/onboard.js";
|
|
7
|
+
import { detectAgents, installObservers, removeObservers, getObservationStatus } from "./lib/observe.js";
|
|
6
8
|
const program = new Command();
|
|
7
9
|
program
|
|
8
10
|
.name("jobarbiter")
|
|
@@ -15,57 +17,24 @@ program
|
|
|
15
17
|
setJsonMode(true);
|
|
16
18
|
});
|
|
17
19
|
// ============================================================
|
|
18
|
-
//
|
|
20
|
+
// onboard — Interactive setup wizard (primary entry point)
|
|
19
21
|
// ============================================================
|
|
20
22
|
program
|
|
21
|
-
.command("
|
|
22
|
-
.description("
|
|
23
|
-
.
|
|
24
|
-
.requiredOption("--type <type>", "Account type: worker or employer")
|
|
23
|
+
.command("onboard")
|
|
24
|
+
.description("Interactive setup wizard — the only command you need to get started")
|
|
25
|
+
.option("--force", "Start fresh even if already configured")
|
|
25
26
|
.option("--base-url <url>", "API base URL", "https://jobarbiter-api-production.up.railway.app")
|
|
26
27
|
.action(async (opts) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
process.exit(1);
|
|
32
|
-
}
|
|
33
|
-
// Step 1: Register and request verification code
|
|
34
|
-
const registerData = await apiUnauthenticated(opts.baseUrl, "POST", "/v1/auth/register", {
|
|
35
|
-
email: opts.email,
|
|
36
|
-
userType,
|
|
37
|
-
});
|
|
38
|
-
success(`Verification code sent to ${opts.email} (expires in 15 minutes)`);
|
|
39
|
-
// Step 2: Prompt for verification code
|
|
40
|
-
const code = await promptForCode();
|
|
41
|
-
if (!code) {
|
|
42
|
-
error("Verification cancelled.");
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
// Step 3: Verify the code
|
|
46
|
-
const verifyData = await apiUnauthenticated(opts.baseUrl, "POST", "/v1/auth/verify", {
|
|
47
|
-
email: opts.email,
|
|
48
|
-
code: code.trim(),
|
|
49
|
-
});
|
|
50
|
-
// Step 4: Save config with API key
|
|
51
|
-
saveConfig({
|
|
52
|
-
apiKey: verifyData.apiKey,
|
|
53
|
-
baseUrl: opts.baseUrl,
|
|
54
|
-
userType: userType,
|
|
55
|
-
});
|
|
56
|
-
success(`Email verified! API key saved to ${getConfigPath()}`);
|
|
57
|
-
console.log(` Key: ${verifyData.apiKey.slice(0, 20)}... (save this — shown only once)`);
|
|
58
|
-
output({ id: verifyData.id, email: opts.email, userType });
|
|
59
|
-
}
|
|
60
|
-
catch (e) {
|
|
61
|
-
handleError(e);
|
|
62
|
-
}
|
|
28
|
+
await runOnboardWizard({
|
|
29
|
+
force: opts.force,
|
|
30
|
+
baseUrl: opts.baseUrl,
|
|
31
|
+
});
|
|
63
32
|
});
|
|
64
33
|
// ============================================================
|
|
65
34
|
// verify (standalone verification for existing registrations)
|
|
66
35
|
// ============================================================
|
|
67
36
|
program
|
|
68
|
-
.command("verify")
|
|
37
|
+
.command("verify-email")
|
|
69
38
|
.description("Verify email with code (if registration was interrupted)")
|
|
70
39
|
.requiredOption("--email <email>", "Email address")
|
|
71
40
|
.option("--code <code>", "6-digit verification code")
|
|
@@ -706,7 +675,7 @@ program
|
|
|
706
675
|
// ============================================================
|
|
707
676
|
// verify
|
|
708
677
|
// ============================================================
|
|
709
|
-
const verify = program.command("
|
|
678
|
+
const verify = program.command("identity").description("Identity verification (GitHub, LinkedIn, domain)");
|
|
710
679
|
verify
|
|
711
680
|
.command("linkedin <url>")
|
|
712
681
|
.description("Submit LinkedIn profile for verification")
|
|
@@ -774,6 +743,166 @@ program
|
|
|
774
743
|
}
|
|
775
744
|
});
|
|
776
745
|
// ============================================================
|
|
746
|
+
// observe (manage coding agent observers)
|
|
747
|
+
// ============================================================
|
|
748
|
+
const observe = program.command("observe").description("Manage coding agent proficiency observers");
|
|
749
|
+
observe
|
|
750
|
+
.command("status")
|
|
751
|
+
.description("Show observer status and accumulated data")
|
|
752
|
+
.action(async () => {
|
|
753
|
+
try {
|
|
754
|
+
const agents = detectAgents();
|
|
755
|
+
const status = getObservationStatus();
|
|
756
|
+
const detected = agents.filter((a) => a.installed);
|
|
757
|
+
console.log("\n🔍 Coding Agent Observers\n");
|
|
758
|
+
console.log(" Agents:");
|
|
759
|
+
for (const agent of agents) {
|
|
760
|
+
if (!agent.installed) {
|
|
761
|
+
console.log(` ⬚ ${agent.name} (not installed)`);
|
|
762
|
+
}
|
|
763
|
+
else if (agent.hookInstalled) {
|
|
764
|
+
console.log(` ✅ ${agent.name} (observer active)`);
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
console.log(` ⚠️ ${agent.name} (detected, no observer)`);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
console.log("\n Accumulated Data:");
|
|
771
|
+
console.log(` Sessions observed: ${status.totalSessions}`);
|
|
772
|
+
console.log(` Total tokens: ${status.totalTokens.toLocaleString()}`);
|
|
773
|
+
console.log(` Agents seen: ${status.agents.join(", ") || "none yet"}`);
|
|
774
|
+
console.log(` Last submitted: ${status.lastSubmitted || "never"}`);
|
|
775
|
+
if (status.topTools.length > 0) {
|
|
776
|
+
console.log("\n Top Tools:");
|
|
777
|
+
for (const { tool, count } of status.topTools.slice(0, 5)) {
|
|
778
|
+
console.log(` ${tool}: ${count} uses`);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
console.log(`\n Data file: ~/.config/jobarbiter/observer/observations.json\n`);
|
|
782
|
+
output({
|
|
783
|
+
detectedAgents: agents.map((a) => ({
|
|
784
|
+
id: a.id,
|
|
785
|
+
name: a.name,
|
|
786
|
+
installed: a.installed,
|
|
787
|
+
observerActive: a.hookInstalled,
|
|
788
|
+
})),
|
|
789
|
+
...status,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
catch (e) {
|
|
793
|
+
handleError(e);
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
observe
|
|
797
|
+
.command("install")
|
|
798
|
+
.description("Install observers for detected coding agents")
|
|
799
|
+
.option("--agent <id>", "Install for specific agent (claude-code, cursor, opencode, codex, gemini)")
|
|
800
|
+
.option("--all", "Install for all detected agents")
|
|
801
|
+
.action(async (opts) => {
|
|
802
|
+
try {
|
|
803
|
+
const agents = detectAgents();
|
|
804
|
+
const detected = agents.filter((a) => a.installed);
|
|
805
|
+
if (detected.length === 0) {
|
|
806
|
+
error("No coding agents detected on this system.");
|
|
807
|
+
console.log(" Supported: Claude Code, Cursor, OpenCode, Codex CLI, Gemini CLI");
|
|
808
|
+
process.exit(1);
|
|
809
|
+
}
|
|
810
|
+
let toInstall;
|
|
811
|
+
if (opts.agent) {
|
|
812
|
+
const agent = agents.find((a) => a.id === opts.agent);
|
|
813
|
+
if (!agent) {
|
|
814
|
+
error(`Unknown agent: ${opts.agent}. Available: ${agents.map((a) => a.id).join(", ")}`);
|
|
815
|
+
process.exit(1);
|
|
816
|
+
}
|
|
817
|
+
if (!agent.installed) {
|
|
818
|
+
error(`${agent.name} is not installed on this system.`);
|
|
819
|
+
process.exit(1);
|
|
820
|
+
}
|
|
821
|
+
toInstall = [opts.agent];
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
toInstall = detected.map((a) => a.id);
|
|
825
|
+
}
|
|
826
|
+
const result = installObservers(toInstall);
|
|
827
|
+
for (const name of result.installed) {
|
|
828
|
+
success(`Installed observer for ${name}`);
|
|
829
|
+
}
|
|
830
|
+
for (const name of result.skipped) {
|
|
831
|
+
console.log(` — ${name} (already installed)`);
|
|
832
|
+
}
|
|
833
|
+
for (const { agent: a, error: errMsg } of result.errors) {
|
|
834
|
+
error(`${a}: ${errMsg}`);
|
|
835
|
+
}
|
|
836
|
+
output(result);
|
|
837
|
+
}
|
|
838
|
+
catch (e) {
|
|
839
|
+
handleError(e);
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
observe
|
|
843
|
+
.command("remove")
|
|
844
|
+
.description("Remove observers from coding agents")
|
|
845
|
+
.option("--agent <id>", "Remove from specific agent")
|
|
846
|
+
.option("--all", "Remove from all agents")
|
|
847
|
+
.action(async (opts) => {
|
|
848
|
+
try {
|
|
849
|
+
const agents = detectAgents();
|
|
850
|
+
const withHooks = agents.filter((a) => a.hookInstalled);
|
|
851
|
+
if (withHooks.length === 0) {
|
|
852
|
+
console.log("No observers are currently installed.");
|
|
853
|
+
process.exit(0);
|
|
854
|
+
}
|
|
855
|
+
let toRemove;
|
|
856
|
+
if (opts.agent) {
|
|
857
|
+
toRemove = [opts.agent];
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
toRemove = withHooks.map((a) => a.id);
|
|
861
|
+
}
|
|
862
|
+
const result = removeObservers(toRemove);
|
|
863
|
+
for (const name of result.removed) {
|
|
864
|
+
success(`Removed observer from ${name}`);
|
|
865
|
+
}
|
|
866
|
+
for (const name of result.notFound) {
|
|
867
|
+
console.log(` — ${name} (no observer found)`);
|
|
868
|
+
}
|
|
869
|
+
output(result);
|
|
870
|
+
}
|
|
871
|
+
catch (e) {
|
|
872
|
+
handleError(e);
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
observe
|
|
876
|
+
.command("review")
|
|
877
|
+
.description("Review accumulated observation data before submission")
|
|
878
|
+
.action(async () => {
|
|
879
|
+
try {
|
|
880
|
+
const status = getObservationStatus();
|
|
881
|
+
if (!status.hasData) {
|
|
882
|
+
console.log("\nNo observation data collected yet.");
|
|
883
|
+
console.log("Use your coding agents normally — data accumulates automatically.\n");
|
|
884
|
+
process.exit(0);
|
|
885
|
+
}
|
|
886
|
+
console.log("\n📊 Observation Data Review\n");
|
|
887
|
+
console.log(` Sessions: ${status.totalSessions}`);
|
|
888
|
+
console.log(` Tokens: ${status.totalTokens.toLocaleString()}`);
|
|
889
|
+
console.log(` Agents: ${status.agents.join(", ")}`);
|
|
890
|
+
if (status.topTools.length > 0) {
|
|
891
|
+
console.log("\n Tool Usage:");
|
|
892
|
+
for (const { tool, count } of status.topTools) {
|
|
893
|
+
const bar = "█".repeat(Math.min(count, 30));
|
|
894
|
+
console.log(` ${tool.padEnd(25)} ${bar} ${count}`);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
console.log(`\n This data will be submitted as an attestation when you run:`);
|
|
898
|
+
console.log(` jobarbiter attest --agent observer --capabilities <auto-generated>\n`);
|
|
899
|
+
output(status);
|
|
900
|
+
}
|
|
901
|
+
catch (e) {
|
|
902
|
+
handleError(e);
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
// ============================================================
|
|
777
906
|
// Helpers
|
|
778
907
|
// ============================================================
|
|
779
908
|
function handleError(e) {
|
package/dist/lib/config.js
CHANGED
|
@@ -35,7 +35,7 @@ export function saveConfig(config) {
|
|
|
35
35
|
export function requireConfig() {
|
|
36
36
|
const config = loadConfig();
|
|
37
37
|
if (!config) {
|
|
38
|
-
console.error("Not configured. Run: jobarbiter
|
|
38
|
+
console.error("Not configured. Run: jobarbiter onboard");
|
|
39
39
|
console.error("Or set JOBARBITER_API_KEY environment variable.");
|
|
40
40
|
process.exit(1);
|
|
41
41
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Tool Detection Module
|
|
3
|
+
*
|
|
4
|
+
* Comprehensive detection of AI tools installed on the user's system.
|
|
5
|
+
* Checks binaries in PATH, config directories, VS Code extensions,
|
|
6
|
+
* pip/npm packages, macOS apps, and environment variables.
|
|
7
|
+
*
|
|
8
|
+
* Never logs or stores API key values — only checks for existence.
|
|
9
|
+
*/
|
|
10
|
+
export type ToolCategory = "coding-agent" | "chat" | "orchestration" | "api-provider";
|
|
11
|
+
export interface DetectedTool {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
category: ToolCategory;
|
|
15
|
+
installed: boolean;
|
|
16
|
+
version?: string;
|
|
17
|
+
configDir?: string;
|
|
18
|
+
observerAvailable: boolean;
|
|
19
|
+
observerActive: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Detect all AI tools on the system.
|
|
23
|
+
* Returns a list of tools with installation status, version info,
|
|
24
|
+
* and observer availability.
|
|
25
|
+
*/
|
|
26
|
+
export declare function detectAllTools(): DetectedTool[];
|
|
27
|
+
/**
|
|
28
|
+
* Get only installed tools.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getInstalledTools(): DetectedTool[];
|
|
31
|
+
/**
|
|
32
|
+
* Get tools by category.
|
|
33
|
+
*/
|
|
34
|
+
export declare function getToolsByCategory(category: ToolCategory): DetectedTool[];
|
|
35
|
+
/**
|
|
36
|
+
* Get tools that have observers available.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getObservableTools(): DetectedTool[];
|
|
39
|
+
/**
|
|
40
|
+
* Get tools that need observer installation.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getToolsNeedingObserver(): DetectedTool[];
|
|
43
|
+
/**
|
|
44
|
+
* Format display name for a tool (includes version if available).
|
|
45
|
+
*/
|
|
46
|
+
export declare function formatToolDisplay(tool: DetectedTool): string;
|