jobarbiter 0.3.2 → 0.3.3

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.
@@ -2,6 +2,8 @@ export interface Config {
2
2
  apiKey: string;
3
3
  baseUrl: string;
4
4
  userType: "worker" | "employer" | "seeker" | "poster";
5
+ onboardingComplete?: boolean;
6
+ onboardingStep?: number;
5
7
  }
6
8
  export declare function getConfigPath(): string;
7
9
  export declare function loadConfig(): Config | null;
@@ -92,13 +92,45 @@ class Prompt {
92
92
  // ── Main Wizard ────────────────────────────────────────────────────────
93
93
  export async function runOnboardWizard(opts) {
94
94
  const baseUrl = opts.baseUrl || "https://jobarbiter-api-production.up.railway.app";
95
- // Check for existing config
95
+ // Check for existing config — resume if onboarding incomplete
96
96
  const existingConfig = loadConfig();
97
97
  if (existingConfig && !opts.force) {
98
- console.log(`\n${sym.warning} ${c.warning("You already have a JobArbiter account configured.")}`);
99
- console.log(`\n Run ${c.highlight("jobarbiter status")} to check your account.`);
100
- console.log(` Run ${c.highlight("jobarbiter onboard --force")} to start fresh.\n`);
101
- process.exit(0);
98
+ if (existingConfig.onboardingComplete) {
99
+ console.log(`\n${sym.check} ${c.success("You're already onboarded!")}`);
100
+ console.log(`\n Run ${c.highlight("jobarbiter status")} to check your account.`);
101
+ console.log(` Run ${c.highlight("jobarbiter onboard --force")} to start fresh.\n`);
102
+ process.exit(0);
103
+ }
104
+ // Onboarding incomplete — resume
105
+ const resumeStep = (existingConfig.onboardingStep ?? 1) + 1;
106
+ console.log(`\n${sym.rocket} ${c.bold("Resuming onboarding")} from step ${resumeStep}/6\n`);
107
+ console.log(c.dim(` Account: ${existingConfig.userType} | API key configured`));
108
+ console.log(c.dim(` Run ${c.highlight("jobarbiter onboard --force")} to start over.\n`));
109
+ const prompt = new Prompt();
110
+ const state = {
111
+ baseUrl,
112
+ apiKey: existingConfig.apiKey,
113
+ userType: existingConfig.userType,
114
+ userId: "",
115
+ email: "",
116
+ };
117
+ try {
118
+ if (existingConfig.userType === "worker" || existingConfig.userType === "seeker") {
119
+ await runWorkerFlow(prompt, state, resumeStep);
120
+ }
121
+ else {
122
+ await runEmployerFlow(prompt, state);
123
+ }
124
+ prompt.close();
125
+ }
126
+ catch (err) {
127
+ prompt.close();
128
+ if (err instanceof Error) {
129
+ console.log(`\n${sym.cross} ${c.error(err.message)}`);
130
+ }
131
+ process.exit(1);
132
+ }
133
+ return;
102
134
  }
103
135
  const prompt = new Prompt();
104
136
  const state = { baseUrl };
@@ -112,11 +144,13 @@ export async function runOnboardWizard(opts) {
112
144
  state.email = email;
113
145
  state.apiKey = apiKey;
114
146
  state.userId = userId;
115
- // Save config immediately after verification
147
+ // Save config immediately after verification (with step progress)
116
148
  saveConfig({
117
149
  apiKey,
118
150
  baseUrl,
119
151
  userType,
152
+ onboardingStep: 1,
153
+ onboardingComplete: false,
120
154
  });
121
155
  if (userType === "worker") {
122
156
  await runWorkerFlow(prompt, state);
@@ -237,79 +271,95 @@ async function handleEmailVerification(prompt, baseUrl, userType) {
237
271
  return { email, apiKey, userId };
238
272
  }
239
273
  // ── Worker Flow ────────────────────────────────────────────────────────
240
- async function runWorkerFlow(prompt, state) {
274
+ async function runWorkerFlow(prompt, state, startStep = 2) {
241
275
  const config = {
242
276
  apiKey: state.apiKey,
243
277
  baseUrl: state.baseUrl,
244
278
  userType: "worker",
245
279
  };
280
+ const saveProgress = (step) => {
281
+ saveConfig({ ...config, onboardingStep: step });
282
+ };
246
283
  // Step 2: Auto-detect AI Tools
247
- const detectedToolsResult = await runToolDetectionStep(prompt, config);
248
- state.tools = detectedToolsResult.tools;
249
- // Step 3: Domains
250
- console.log(`${sym.target} ${c.bold("Step 3/6 — Your Domains")}\n`);
251
- console.log(`What domains do you work in? ${c.dim("(comma-separated)")}`);
252
- console.log(c.dim("Examples: full-stack dev, data engineering, trading, content creation\n"));
253
- const domainsInput = await prompt.question(`${sym.arrow} `);
254
- const domains = domainsInput.split(",").map(s => s.trim()).filter(Boolean);
255
- state.domains = domains;
256
- // Create/update profile
257
- console.log(c.dim("\nSaving profile..."));
258
- try {
259
- await api(config, "POST", "/v1/profile", {
260
- domains,
261
- tools: {
262
- primary: state.tools,
263
- },
264
- });
265
- console.log(`${sym.check} Profile saved\n`);
266
- }
267
- catch (err) {
268
- console.log(`${sym.warning} ${c.warning("Could not save profile details — you can update later with 'jobarbiter profile create'")}\n`);
284
+ if (startStep <= 2) {
285
+ const detectedToolsResult = await runToolDetectionStep(prompt, config);
286
+ state.tools = detectedToolsResult.tools;
287
+ saveProgress(2);
269
288
  }
270
- // Step 4: Connect GitHub (optional)
271
- console.log(`${sym.link} ${c.bold("Step 4/6 — Connect GitHub")} ${c.dim("(optional)")}\n`);
272
- console.log(`Connecting your GitHub lets us analyze your AI-assisted work patterns.`);
273
- console.log(`This significantly boosts your proficiency score.\n`);
274
- const githubUsername = await prompt.question(`GitHub username ${c.dim("(press Enter to skip)")}: `);
275
- if (githubUsername) {
276
- console.log(c.dim("\nConnecting GitHub..."));
289
+ // Step 3: Domains
290
+ if (startStep <= 3) {
291
+ console.log(`${sym.target} ${c.bold("Step 3/6 Your Domains")}\n`);
292
+ console.log(`What domains do you work in? ${c.dim("(comma-separated)")}`);
293
+ console.log(c.dim("Examples: full-stack dev, data engineering, trading, content creation\n"));
294
+ const domainsInput = await prompt.question(`${sym.arrow} `);
295
+ const domains = domainsInput.split(",").map(s => s.trim()).filter(Boolean);
296
+ state.domains = domains;
297
+ // Create/update profile
298
+ console.log(c.dim("\nSaving profile..."));
277
299
  try {
278
- await api(config, "POST", "/v1/attestations/git/connect", {
279
- provider: "github",
280
- username: githubUsername,
300
+ await api(config, "POST", "/v1/profile", {
301
+ domains,
302
+ tools: {
303
+ primary: state.tools,
304
+ },
281
305
  });
282
- console.log(`${sym.check} GitHub connected: ${c.highlight(githubUsername)}\n`);
283
- state.githubUsername = githubUsername;
306
+ console.log(`${sym.check} Profile saved\n`);
284
307
  }
285
308
  catch (err) {
286
- console.log(`${sym.warning} ${c.warning("Could not connect GitHub — you can try later with 'jobarbiter git connect'")}\n`);
309
+ console.log(`${sym.warning} ${c.warning("Could not save profile details — you can update later with 'jobarbiter profile create'")}\n`);
287
310
  }
311
+ saveProgress(3);
288
312
  }
289
- else {
290
- console.log(`${c.dim("Skipped you can connect later with 'jobarbiter git connect'")}\n`);
313
+ // Step 4: Connect GitHub (optional)
314
+ if (startStep <= 4) {
315
+ console.log(`${sym.link} ${c.bold("Step 4/6 — Connect GitHub")} ${c.dim("(optional)")}\n`);
316
+ console.log(`Connecting your GitHub lets us analyze your AI-assisted work patterns.`);
317
+ console.log(`This significantly boosts your proficiency score.\n`);
318
+ const githubUsername = await prompt.question(`GitHub username ${c.dim("(press Enter to skip)")}: `);
319
+ if (githubUsername) {
320
+ console.log(c.dim("\nConnecting GitHub..."));
321
+ try {
322
+ await api(config, "POST", "/v1/attestations/git/connect", {
323
+ provider: "github",
324
+ username: githubUsername,
325
+ });
326
+ console.log(`${sym.check} GitHub connected: ${c.highlight(githubUsername)}\n`);
327
+ state.githubUsername = githubUsername;
328
+ }
329
+ catch (err) {
330
+ console.log(`${sym.warning} ${c.warning("Could not connect GitHub — you can try later with 'jobarbiter git connect'")}\n`);
331
+ }
332
+ }
333
+ else {
334
+ console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter git connect'")}\n`);
335
+ }
336
+ saveProgress(4);
291
337
  }
292
338
  // Step 5: Connect LinkedIn (optional)
293
- console.log(`${sym.link} ${c.bold("Step 5/6 — Connect LinkedIn")} ${c.dim("(optional)")}\n`);
294
- console.log(`Your LinkedIn profile strengthens identity verification.`);
295
- console.log(c.dim("We never post on your behalf or access your connections.\n"));
296
- const linkedinUrl = await prompt.question(`LinkedIn URL ${c.dim("(press Enter to skip)")}: `);
297
- if (linkedinUrl) {
298
- console.log(c.dim("\nSubmitting for verification..."));
299
- try {
300
- await api(config, "POST", "/v1/verification/linkedin", {
301
- linkedinUrl: linkedinUrl.trim(),
302
- });
303
- console.log(`${sym.check} LinkedIn submitted for verification\n`);
339
+ if (startStep <= 5) {
340
+ console.log(`${sym.link} ${c.bold("Step 5/6 Connect LinkedIn")} ${c.dim("(optional)")}\n`);
341
+ console.log(`Your LinkedIn profile strengthens identity verification.`);
342
+ console.log(c.dim("We never post on your behalf or access your connections.\n"));
343
+ const linkedinUrl = await prompt.question(`LinkedIn URL ${c.dim("(press Enter to skip)")}: `);
344
+ if (linkedinUrl) {
345
+ console.log(c.dim("\nSubmitting for verification..."));
346
+ try {
347
+ await api(config, "POST", "/v1/verification/linkedin", {
348
+ linkedinUrl: linkedinUrl.trim(),
349
+ });
350
+ console.log(`${sym.check} LinkedIn submitted for verification\n`);
351
+ }
352
+ catch (err) {
353
+ console.log(`${sym.warning} ${c.warning("Could not submit LinkedIn — you can try later with 'jobarbiter identity linkedin <url>'")}\n`);
354
+ }
304
355
  }
305
- catch (err) {
306
- console.log(`${sym.warning} ${c.warning("Could not submit LinkedIn — you can try later with 'jobarbiter identity linkedin <url>'")}\n`);
356
+ else {
357
+ console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter identity linkedin <url>'")}\n`);
307
358
  }
308
- }
309
- else {
310
- console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter identity linkedin <url>'")}\n`);
359
+ saveProgress(5);
311
360
  }
312
361
  // Step 6: Done!
362
+ saveConfig({ ...config, onboardingComplete: true, onboardingStep: 6 });
313
363
  showWorkerCompletion(state);
314
364
  }
315
365
  // ── Tool Detection Step ────────────────────────────────────────────────
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jobarbiter",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "CLI for JobArbiter — the first AI Proficiency Marketplace",
5
5
  "type": "module",
6
6
  "bin": {
package/src/lib/config.ts CHANGED
@@ -6,6 +6,8 @@ export interface Config {
6
6
  apiKey: string;
7
7
  baseUrl: string;
8
8
  userType: "worker" | "employer" | "seeker" | "poster";
9
+ onboardingComplete?: boolean;
10
+ onboardingStep?: number; // last completed step (1-6)
9
11
  }
10
12
 
11
13
  const CONFIG_DIR = join(homedir(), ".config", "jobarbiter");
@@ -139,13 +139,44 @@ interface OnboardState {
139
139
  export async function runOnboardWizard(opts: { force?: boolean; baseUrl?: string }): Promise<void> {
140
140
  const baseUrl = opts.baseUrl || "https://jobarbiter-api-production.up.railway.app";
141
141
 
142
- // Check for existing config
142
+ // Check for existing config — resume if onboarding incomplete
143
143
  const existingConfig = loadConfig();
144
144
  if (existingConfig && !opts.force) {
145
- console.log(`\n${sym.warning} ${c.warning("You already have a JobArbiter account configured.")}`);
146
- console.log(`\n Run ${c.highlight("jobarbiter status")} to check your account.`);
147
- console.log(` Run ${c.highlight("jobarbiter onboard --force")} to start fresh.\n`);
148
- process.exit(0);
145
+ if (existingConfig.onboardingComplete) {
146
+ console.log(`\n${sym.check} ${c.success("You're already onboarded!")}`);
147
+ console.log(`\n Run ${c.highlight("jobarbiter status")} to check your account.`);
148
+ console.log(` Run ${c.highlight("jobarbiter onboard --force")} to start fresh.\n`);
149
+ process.exit(0);
150
+ }
151
+ // Onboarding incomplete — resume
152
+ const resumeStep = (existingConfig.onboardingStep ?? 1) + 1;
153
+ console.log(`\n${sym.rocket} ${c.bold("Resuming onboarding")} from step ${resumeStep}/6\n`);
154
+ console.log(c.dim(` Account: ${existingConfig.userType} | API key configured`));
155
+ console.log(c.dim(` Run ${c.highlight("jobarbiter onboard --force")} to start over.\n`));
156
+
157
+ const prompt = new Prompt();
158
+ const state: Partial<OnboardState> = {
159
+ baseUrl,
160
+ apiKey: existingConfig.apiKey,
161
+ userType: existingConfig.userType as "worker" | "employer",
162
+ userId: "",
163
+ email: "",
164
+ };
165
+ try {
166
+ if (existingConfig.userType === "worker" || existingConfig.userType === "seeker") {
167
+ await runWorkerFlow(prompt, state as OnboardState, resumeStep);
168
+ } else {
169
+ await runEmployerFlow(prompt, state as OnboardState);
170
+ }
171
+ prompt.close();
172
+ } catch (err) {
173
+ prompt.close();
174
+ if (err instanceof Error) {
175
+ console.log(`\n${sym.cross} ${c.error(err.message)}`);
176
+ }
177
+ process.exit(1);
178
+ }
179
+ return;
149
180
  }
150
181
 
151
182
  const prompt = new Prompt();
@@ -163,11 +194,13 @@ export async function runOnboardWizard(opts: { force?: boolean; baseUrl?: string
163
194
  state.apiKey = apiKey;
164
195
  state.userId = userId;
165
196
 
166
- // Save config immediately after verification
197
+ // Save config immediately after verification (with step progress)
167
198
  saveConfig({
168
199
  apiKey,
169
200
  baseUrl,
170
201
  userType,
202
+ onboardingStep: 1,
203
+ onboardingComplete: false,
171
204
  });
172
205
 
173
206
  if (userType === "worker") {
@@ -306,85 +339,102 @@ async function handleEmailVerification(
306
339
 
307
340
  // ── Worker Flow ────────────────────────────────────────────────────────
308
341
 
309
- async function runWorkerFlow(prompt: Prompt, state: OnboardState): Promise<void> {
342
+ async function runWorkerFlow(prompt: Prompt, state: OnboardState, startStep = 2): Promise<void> {
310
343
  const config: Config = {
311
344
  apiKey: state.apiKey,
312
345
  baseUrl: state.baseUrl,
313
346
  userType: "worker",
314
347
  };
315
348
 
349
+ const saveProgress = (step: number) => {
350
+ saveConfig({ ...config, onboardingStep: step });
351
+ };
352
+
316
353
  // Step 2: Auto-detect AI Tools
317
- const detectedToolsResult = await runToolDetectionStep(prompt, config);
318
- state.tools = detectedToolsResult.tools;
354
+ if (startStep <= 2) {
355
+ const detectedToolsResult = await runToolDetectionStep(prompt, config);
356
+ state.tools = detectedToolsResult.tools;
357
+ saveProgress(2);
358
+ }
319
359
 
320
360
  // Step 3: Domains
321
- console.log(`${sym.target} ${c.bold("Step 3/6 — Your Domains")}\n`);
322
- console.log(`What domains do you work in? ${c.dim("(comma-separated)")}`);
323
- console.log(c.dim("Examples: full-stack dev, data engineering, trading, content creation\n"));
324
- const domainsInput = await prompt.question(`${sym.arrow} `);
325
- const domains = domainsInput.split(",").map(s => s.trim()).filter(Boolean);
326
- state.domains = domains;
361
+ if (startStep <= 3) {
362
+ console.log(`${sym.target} ${c.bold("Step 3/6 — Your Domains")}\n`);
363
+ console.log(`What domains do you work in? ${c.dim("(comma-separated)")}`);
364
+ console.log(c.dim("Examples: full-stack dev, data engineering, trading, content creation\n"));
365
+ const domainsInput = await prompt.question(`${sym.arrow} `);
366
+ const domains = domainsInput.split(",").map(s => s.trim()).filter(Boolean);
367
+ state.domains = domains;
327
368
 
328
- // Create/update profile
329
- console.log(c.dim("\nSaving profile..."));
369
+ // Create/update profile
370
+ console.log(c.dim("\nSaving profile..."));
330
371
 
331
- try {
332
- await api(config, "POST", "/v1/profile", {
333
- domains,
334
- tools: {
335
- primary: state.tools,
336
- },
337
- });
338
- console.log(`${sym.check} Profile saved\n`);
339
- } catch (err) {
340
- console.log(`${sym.warning} ${c.warning("Could not save profile details — you can update later with 'jobarbiter profile create'")}\n`);
372
+ try {
373
+ await api(config, "POST", "/v1/profile", {
374
+ domains,
375
+ tools: {
376
+ primary: state.tools,
377
+ },
378
+ });
379
+ console.log(`${sym.check} Profile saved\n`);
380
+ } catch (err) {
381
+ console.log(`${sym.warning} ${c.warning("Could not save profile details — you can update later with 'jobarbiter profile create'")}\n`);
382
+ }
383
+ saveProgress(3);
341
384
  }
342
385
 
343
386
  // Step 4: Connect GitHub (optional)
344
- console.log(`${sym.link} ${c.bold("Step 4/6 — Connect GitHub")} ${c.dim("(optional)")}\n`);
345
- console.log(`Connecting your GitHub lets us analyze your AI-assisted work patterns.`);
346
- console.log(`This significantly boosts your proficiency score.\n`);
387
+ if (startStep <= 4) {
388
+ console.log(`${sym.link} ${c.bold("Step 4/6 Connect GitHub")} ${c.dim("(optional)")}\n`);
389
+ console.log(`Connecting your GitHub lets us analyze your AI-assisted work patterns.`);
390
+ console.log(`This significantly boosts your proficiency score.\n`);
347
391
 
348
- const githubUsername = await prompt.question(`GitHub username ${c.dim("(press Enter to skip)")}: `);
349
-
350
- if (githubUsername) {
351
- console.log(c.dim("\nConnecting GitHub..."));
352
- try {
353
- await api(config, "POST", "/v1/attestations/git/connect", {
354
- provider: "github",
355
- username: githubUsername,
356
- });
357
- console.log(`${sym.check} GitHub connected: ${c.highlight(githubUsername)}\n`);
358
- state.githubUsername = githubUsername;
359
- } catch (err) {
360
- console.log(`${sym.warning} ${c.warning("Could not connect GitHub — you can try later with 'jobarbiter git connect'")}\n`);
392
+ const githubUsername = await prompt.question(`GitHub username ${c.dim("(press Enter to skip)")}: `);
393
+
394
+ if (githubUsername) {
395
+ console.log(c.dim("\nConnecting GitHub..."));
396
+ try {
397
+ await api(config, "POST", "/v1/attestations/git/connect", {
398
+ provider: "github",
399
+ username: githubUsername,
400
+ });
401
+ console.log(`${sym.check} GitHub connected: ${c.highlight(githubUsername)}\n`);
402
+ state.githubUsername = githubUsername;
403
+ } catch (err) {
404
+ console.log(`${sym.warning} ${c.warning("Could not connect GitHub — you can try later with 'jobarbiter git connect'")}\n`);
405
+ }
406
+ } else {
407
+ console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter git connect'")}\n`);
361
408
  }
362
- } else {
363
- console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter git connect'")}\n`);
409
+ saveProgress(4);
364
410
  }
365
411
 
366
412
  // Step 5: Connect LinkedIn (optional)
367
- console.log(`${sym.link} ${c.bold("Step 5/6 — Connect LinkedIn")} ${c.dim("(optional)")}\n`);
368
- console.log(`Your LinkedIn profile strengthens identity verification.`);
369
- console.log(c.dim("We never post on your behalf or access your connections.\n"));
413
+ if (startStep <= 5) {
414
+ console.log(`${sym.link} ${c.bold("Step 5/6 Connect LinkedIn")} ${c.dim("(optional)")}\n`);
415
+ console.log(`Your LinkedIn profile strengthens identity verification.`);
416
+ console.log(c.dim("We never post on your behalf or access your connections.\n"));
370
417
 
371
- const linkedinUrl = await prompt.question(`LinkedIn URL ${c.dim("(press Enter to skip)")}: `);
372
-
373
- if (linkedinUrl) {
374
- console.log(c.dim("\nSubmitting for verification..."));
375
- try {
376
- await api(config, "POST", "/v1/verification/linkedin", {
377
- linkedinUrl: linkedinUrl.trim(),
378
- });
379
- console.log(`${sym.check} LinkedIn submitted for verification\n`);
380
- } catch (err) {
381
- console.log(`${sym.warning} ${c.warning("Could not submit LinkedIn — you can try later with 'jobarbiter identity linkedin <url>'")}\n`);
418
+ const linkedinUrl = await prompt.question(`LinkedIn URL ${c.dim("(press Enter to skip)")}: `);
419
+
420
+ if (linkedinUrl) {
421
+ console.log(c.dim("\nSubmitting for verification..."));
422
+ try {
423
+ await api(config, "POST", "/v1/verification/linkedin", {
424
+ linkedinUrl: linkedinUrl.trim(),
425
+ });
426
+ console.log(`${sym.check} LinkedIn submitted for verification\n`);
427
+ } catch (err) {
428
+ console.log(`${sym.warning} ${c.warning("Could not submit LinkedIn — you can try later with 'jobarbiter identity linkedin <url>'")}\n`);
429
+ }
430
+ } else {
431
+ console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter identity linkedin <url>'")}\n`);
382
432
  }
383
- } else {
384
- console.log(`${c.dim("Skipped — you can connect later with 'jobarbiter identity linkedin <url>'")}\n`);
433
+ saveProgress(5);
385
434
  }
386
435
 
387
436
  // Step 6: Done!
437
+ saveConfig({ ...config, onboardingComplete: true, onboardingStep: 6 });
388
438
  showWorkerCompletion(state);
389
439
  }
390
440