viveworker 0.1.11 → 0.2.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 +27 -8
- package/package.json +7 -2
- package/scripts/viveworker-bridge.mjs +794 -43
- package/scripts/viveworker-claude-hook.mjs +839 -0
- package/scripts/viveworker.mjs +71 -0
- package/web/app.css +171 -0
- package/web/app.js +523 -59
- package/web/i18n.js +96 -58
package/scripts/viveworker.mjs
CHANGED
|
@@ -285,6 +285,73 @@ async function runSetup(cliOptions) {
|
|
|
285
285
|
console.log(t(locale, "cli.setup.qrCaDownload"));
|
|
286
286
|
await printQrCode(caDownloadIpUrl);
|
|
287
287
|
}
|
|
288
|
+
|
|
289
|
+
const claudeSettingsPath = resolvePath(
|
|
290
|
+
cliOptions.claudeSettingsFile || path.join(os.homedir(), ".claude", "settings.json")
|
|
291
|
+
);
|
|
292
|
+
const claudeHomeExists = await fileExists(path.dirname(claudeSettingsPath));
|
|
293
|
+
if (cliOptions.claudeSettingsFile || claudeHomeExists) {
|
|
294
|
+
await installClaudeHooks({
|
|
295
|
+
envFile,
|
|
296
|
+
claudeSettingsFile: cliOptions.claudeSettingsFile,
|
|
297
|
+
sessionSecret,
|
|
298
|
+
});
|
|
299
|
+
} else {
|
|
300
|
+
console.log("");
|
|
301
|
+
console.log(t(locale, "cli.setup.claudeHooksSkipped"));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function installClaudeHooks({ envFile, claudeSettingsFile, sessionSecret }) {
|
|
306
|
+
const hookScript = path.join(packageRoot, "scripts", "viveworker-claude-hook.mjs");
|
|
307
|
+
const nodePath = process.execPath;
|
|
308
|
+
const settingsFile = resolvePath(
|
|
309
|
+
claudeSettingsFile || path.join(os.homedir(), ".claude", "settings.json")
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const hookCommand = `'${nodePath}' '${hookScript}' --env-file '${envFile}'`;
|
|
313
|
+
|
|
314
|
+
let settings = {};
|
|
315
|
+
try {
|
|
316
|
+
const raw = await fs.readFile(settingsFile, "utf8");
|
|
317
|
+
settings = JSON.parse(raw);
|
|
318
|
+
} catch {
|
|
319
|
+
// File missing or invalid — start fresh
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!settings.hooks) {
|
|
323
|
+
settings.hooks = {};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const hookEntry = (timeout) => ({
|
|
327
|
+
hooks: [{ type: "command", command: hookCommand, timeout }],
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const fileToolMatcher = "Write|Edit|MultiEdit";
|
|
331
|
+
const approvalMatcher = "Bash|WebFetch|WebSearch|Write|Edit|MultiEdit";
|
|
332
|
+
const interactiveMatcher = "ExitPlanMode|AskUserQuestion";
|
|
333
|
+
|
|
334
|
+
settings.hooks.UserPromptSubmit = [hookEntry(15)];
|
|
335
|
+
settings.hooks.Notification = [hookEntry(15)];
|
|
336
|
+
settings.hooks.Stop = [hookEntry(15)];
|
|
337
|
+
settings.hooks.PermissionRequest = [{ ...hookEntry(900), matcher: approvalMatcher }];
|
|
338
|
+
settings.hooks.PreToolUse = [
|
|
339
|
+
{ ...hookEntry(30), matcher: fileToolMatcher },
|
|
340
|
+
{ ...hookEntry(900), matcher: interactiveMatcher },
|
|
341
|
+
];
|
|
342
|
+
settings.hooks.PostToolUse = [
|
|
343
|
+
{ ...hookEntry(30), matcher: `Read|${approvalMatcher}|${interactiveMatcher}` },
|
|
344
|
+
];
|
|
345
|
+
settings.hooks.PostToolUseFailure = [
|
|
346
|
+
{ ...hookEntry(15), matcher: `${approvalMatcher}|${interactiveMatcher}` },
|
|
347
|
+
];
|
|
348
|
+
settings.hooks.SessionEnd = [hookEntry(15)];
|
|
349
|
+
|
|
350
|
+
await fs.mkdir(path.dirname(settingsFile), { recursive: true });
|
|
351
|
+
await fs.writeFile(settingsFile, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
352
|
+
|
|
353
|
+
console.log("");
|
|
354
|
+
console.log(`Claude hooks installed: ${settingsFile}`);
|
|
288
355
|
}
|
|
289
356
|
|
|
290
357
|
async function runStart(cliOptions) {
|
|
@@ -533,6 +600,7 @@ function parseArgs(argv) {
|
|
|
533
600
|
vapidPrivateKey: "",
|
|
534
601
|
locale: "",
|
|
535
602
|
mkcertTrustStores: "",
|
|
603
|
+
claudeSettingsFile: "",
|
|
536
604
|
};
|
|
537
605
|
|
|
538
606
|
if (argv[0] && !argv[0].startsWith("-")) {
|
|
@@ -611,6 +679,9 @@ function parseArgs(argv) {
|
|
|
611
679
|
index += 1;
|
|
612
680
|
} else if (arg === "--pair") {
|
|
613
681
|
parsed.pair = true;
|
|
682
|
+
} else if (arg === "--claude-settings-file") {
|
|
683
|
+
parsed.claudeSettingsFile = next;
|
|
684
|
+
index += 1;
|
|
614
685
|
} else if (arg === "--help" || arg === "-h") {
|
|
615
686
|
parsed.command = "help";
|
|
616
687
|
} else {
|
package/web/app.css
CHANGED
|
@@ -3286,3 +3286,174 @@ button:disabled {
|
|
|
3286
3286
|
padding: 0.4rem 0.46rem;
|
|
3287
3287
|
}
|
|
3288
3288
|
}
|
|
3289
|
+
|
|
3290
|
+
/* Action button busy/loading state */
|
|
3291
|
+
button[data-action-url].is-loading {
|
|
3292
|
+
display: inline-flex;
|
|
3293
|
+
align-items: center;
|
|
3294
|
+
justify-content: center;
|
|
3295
|
+
gap: 0.5rem;
|
|
3296
|
+
cursor: wait;
|
|
3297
|
+
opacity: 0.9;
|
|
3298
|
+
}
|
|
3299
|
+
button[data-action-url][aria-busy="true"]:not(.is-loading) {
|
|
3300
|
+
opacity: 0.5;
|
|
3301
|
+
cursor: not-allowed;
|
|
3302
|
+
}
|
|
3303
|
+
.action-spinner {
|
|
3304
|
+
display: inline-block;
|
|
3305
|
+
width: 0.95em;
|
|
3306
|
+
height: 0.95em;
|
|
3307
|
+
border-radius: 50%;
|
|
3308
|
+
border: 2px solid currentColor;
|
|
3309
|
+
border-right-color: transparent;
|
|
3310
|
+
animation: action-spinner-rotate 0.7s linear infinite;
|
|
3311
|
+
flex-shrink: 0;
|
|
3312
|
+
}
|
|
3313
|
+
@keyframes action-spinner-rotate {
|
|
3314
|
+
to { transform: rotate(360deg); }
|
|
3315
|
+
}
|
|
3316
|
+
|
|
3317
|
+
.provider-badge {
|
|
3318
|
+
display: inline-flex;
|
|
3319
|
+
align-items: center;
|
|
3320
|
+
gap: 0.3rem;
|
|
3321
|
+
padding: 0.1rem 0.45rem 0.1rem 0.3rem;
|
|
3322
|
+
font-size: 0.7rem;
|
|
3323
|
+
font-weight: 600;
|
|
3324
|
+
line-height: 1.4;
|
|
3325
|
+
border-radius: 999px;
|
|
3326
|
+
border: 1px solid currentColor;
|
|
3327
|
+
white-space: nowrap;
|
|
3328
|
+
}
|
|
3329
|
+
.provider-badge__icon {
|
|
3330
|
+
display: inline-flex;
|
|
3331
|
+
align-items: center;
|
|
3332
|
+
justify-content: center;
|
|
3333
|
+
width: 1.05em;
|
|
3334
|
+
height: 1.05em;
|
|
3335
|
+
border-radius: 50%;
|
|
3336
|
+
background: currentColor;
|
|
3337
|
+
color: #0b0f14;
|
|
3338
|
+
font-size: 0.65em;
|
|
3339
|
+
font-weight: 800;
|
|
3340
|
+
font-family: var(--font-ui);
|
|
3341
|
+
}
|
|
3342
|
+
.provider-badge--codex {
|
|
3343
|
+
color: #6aa9ff;
|
|
3344
|
+
background: rgba(106, 169, 255, 0.08);
|
|
3345
|
+
}
|
|
3346
|
+
.provider-badge--claude {
|
|
3347
|
+
color: #f5a45a;
|
|
3348
|
+
background: rgba(245, 164, 90, 0.1);
|
|
3349
|
+
}
|
|
3350
|
+
|
|
3351
|
+
.provider-filter {
|
|
3352
|
+
display: inline-flex;
|
|
3353
|
+
align-items: center;
|
|
3354
|
+
gap: 0.25rem;
|
|
3355
|
+
padding: 0.2rem;
|
|
3356
|
+
margin: 0.5rem 0;
|
|
3357
|
+
background: rgba(255, 255, 255, 0.04);
|
|
3358
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
3359
|
+
border-radius: 999px;
|
|
3360
|
+
width: fit-content;
|
|
3361
|
+
}
|
|
3362
|
+
.provider-filter__button {
|
|
3363
|
+
appearance: none;
|
|
3364
|
+
background: transparent;
|
|
3365
|
+
color: var(--text-muted, #9aa6b2);
|
|
3366
|
+
border: 0;
|
|
3367
|
+
padding: 0.3rem 0.85rem;
|
|
3368
|
+
font-size: 0.78rem;
|
|
3369
|
+
font-weight: 600;
|
|
3370
|
+
border-radius: 999px;
|
|
3371
|
+
cursor: pointer;
|
|
3372
|
+
transition: background 0.15s ease, color 0.15s ease;
|
|
3373
|
+
}
|
|
3374
|
+
.provider-filter__button:hover {
|
|
3375
|
+
color: #fff;
|
|
3376
|
+
}
|
|
3377
|
+
.provider-filter__button.is-active {
|
|
3378
|
+
background: rgba(255, 255, 255, 0.12);
|
|
3379
|
+
color: #fff;
|
|
3380
|
+
}
|
|
3381
|
+
|
|
3382
|
+
.claude-question-item {
|
|
3383
|
+
display: flex;
|
|
3384
|
+
flex-direction: column;
|
|
3385
|
+
gap: 0.5rem;
|
|
3386
|
+
padding: 0.75rem 0;
|
|
3387
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
3388
|
+
}
|
|
3389
|
+
.claude-question-item:last-of-type {
|
|
3390
|
+
border-bottom: none;
|
|
3391
|
+
}
|
|
3392
|
+
.claude-question-text {
|
|
3393
|
+
margin: 0 0 0.25rem 0;
|
|
3394
|
+
font-size: 1rem;
|
|
3395
|
+
line-height: 1.4;
|
|
3396
|
+
}
|
|
3397
|
+
.claude-question-options {
|
|
3398
|
+
display: flex;
|
|
3399
|
+
flex-direction: column;
|
|
3400
|
+
gap: 0.4rem;
|
|
3401
|
+
}
|
|
3402
|
+
.claude-question-option {
|
|
3403
|
+
display: flex;
|
|
3404
|
+
align-items: flex-start;
|
|
3405
|
+
gap: 0.55rem;
|
|
3406
|
+
padding: 0.6rem 0.75rem;
|
|
3407
|
+
background: rgba(255, 255, 255, 0.04);
|
|
3408
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
3409
|
+
border-radius: 0.6rem;
|
|
3410
|
+
cursor: pointer;
|
|
3411
|
+
flex-wrap: wrap;
|
|
3412
|
+
}
|
|
3413
|
+
.claude-question-option:hover {
|
|
3414
|
+
background: rgba(255, 255, 255, 0.07);
|
|
3415
|
+
}
|
|
3416
|
+
.claude-question-option input[type="radio"],
|
|
3417
|
+
.claude-question-option input[type="checkbox"] {
|
|
3418
|
+
margin-top: 0.2rem;
|
|
3419
|
+
flex-shrink: 0;
|
|
3420
|
+
}
|
|
3421
|
+
.claude-question-option__label {
|
|
3422
|
+
flex: 1 1 auto;
|
|
3423
|
+
min-width: 0;
|
|
3424
|
+
font-weight: 600;
|
|
3425
|
+
}
|
|
3426
|
+
.claude-question-option__desc {
|
|
3427
|
+
flex: 1 1 100%;
|
|
3428
|
+
margin-left: 1.5rem;
|
|
3429
|
+
font-size: 0.85rem;
|
|
3430
|
+
opacity: 0.75;
|
|
3431
|
+
line-height: 1.35;
|
|
3432
|
+
}
|
|
3433
|
+
.claude-question-note {
|
|
3434
|
+
width: 100%;
|
|
3435
|
+
box-sizing: border-box;
|
|
3436
|
+
margin-top: 0.25rem;
|
|
3437
|
+
padding: 0.5rem 0.6rem;
|
|
3438
|
+
background: rgba(0, 0, 0, 0.35);
|
|
3439
|
+
color: inherit;
|
|
3440
|
+
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
3441
|
+
border-radius: 0.5rem;
|
|
3442
|
+
font: inherit;
|
|
3443
|
+
resize: vertical;
|
|
3444
|
+
}
|
|
3445
|
+
.claude-question-actions {
|
|
3446
|
+
display: flex;
|
|
3447
|
+
justify-content: flex-end;
|
|
3448
|
+
margin-top: 0.75rem;
|
|
3449
|
+
}
|
|
3450
|
+
.claude-question-notice {
|
|
3451
|
+
margin: 0.5rem 0 0 0;
|
|
3452
|
+
font-size: 0.85rem;
|
|
3453
|
+
opacity: 0.8;
|
|
3454
|
+
}
|
|
3455
|
+
.claude-question-error {
|
|
3456
|
+
margin: 0.5rem 0 0 0;
|
|
3457
|
+
font-size: 0.85rem;
|
|
3458
|
+
color: #ff8a8a;
|
|
3459
|
+
}
|