happy-coder 0.1.1 → 0.1.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.cjs +13 -8
- package/dist/index.mjs +11 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5,7 +5,7 @@ var fs = require('node:fs');
|
|
|
5
5
|
var promises = require('node:fs/promises');
|
|
6
6
|
var node_crypto = require('node:crypto');
|
|
7
7
|
var tweetnacl = require('tweetnacl');
|
|
8
|
-
var
|
|
8
|
+
var os = require('node:os');
|
|
9
9
|
var node_path = require('node:path');
|
|
10
10
|
var chalk = require('chalk');
|
|
11
11
|
var node_events = require('node:events');
|
|
@@ -74,14 +74,14 @@ function authChallenge(secret) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
async function getOrCreateSecretKey() {
|
|
77
|
-
const keyPath = node_path.join(
|
|
77
|
+
const keyPath = node_path.join(os.homedir(), ".handy", "access.key");
|
|
78
78
|
if (fs.existsSync(keyPath)) {
|
|
79
79
|
const keyBase642 = fs.readFileSync(keyPath, "utf8").trim();
|
|
80
80
|
return new Uint8Array(Buffer.from(keyBase642, "base64"));
|
|
81
81
|
}
|
|
82
82
|
const secret = getRandomBytes(32);
|
|
83
83
|
const keyBase64 = encodeBase64(secret);
|
|
84
|
-
fs.mkdirSync(node_path.join(
|
|
84
|
+
fs.mkdirSync(node_path.join(os.homedir(), ".handy"), { recursive: true });
|
|
85
85
|
fs.writeFileSync(keyPath, keyBase64);
|
|
86
86
|
await promises.chmod(keyPath, 384);
|
|
87
87
|
return secret;
|
|
@@ -291,11 +291,11 @@ class ApiClient {
|
|
|
291
291
|
/**
|
|
292
292
|
* Create a new session or load existing one with the given tag
|
|
293
293
|
*/
|
|
294
|
-
async getOrCreateSession(
|
|
294
|
+
async getOrCreateSession(opts) {
|
|
295
295
|
try {
|
|
296
296
|
const response = await axios.post(
|
|
297
297
|
`https://handy-api.korshakov.org/v1/sessions`,
|
|
298
|
-
{ tag },
|
|
298
|
+
{ tag: opts.tag, metadata: encodeBase64(encrypt(opts.metadata, this.secret)) },
|
|
299
299
|
{
|
|
300
300
|
headers: {
|
|
301
301
|
"Authorization": `Bearer ${this.token}`,
|
|
@@ -303,7 +303,7 @@ class ApiClient {
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
);
|
|
306
|
-
logger.info(`Session created/loaded: ${response.data.session.id} (tag: ${tag})`);
|
|
306
|
+
logger.info(`Session created/loaded: ${response.data.session.id} (tag: ${opts.tag})`);
|
|
307
307
|
return response.data;
|
|
308
308
|
} catch (error) {
|
|
309
309
|
logger.error("Failed to get or create session:", error);
|
|
@@ -468,6 +468,7 @@ function startClaudeLoop(opts, session) {
|
|
|
468
468
|
let exiting = false;
|
|
469
469
|
const messageQueue = [];
|
|
470
470
|
let messageResolve = null;
|
|
471
|
+
let sessionId;
|
|
471
472
|
let promise = (async () => {
|
|
472
473
|
session.onUserMessage((message) => {
|
|
473
474
|
messageQueue.push(message);
|
|
@@ -484,7 +485,8 @@ function startClaudeLoop(opts, session) {
|
|
|
484
485
|
command: message.content.text,
|
|
485
486
|
workingDirectory: opts.path,
|
|
486
487
|
model: opts.model,
|
|
487
|
-
permissionMode: opts.permissionMode
|
|
488
|
+
permissionMode: opts.permissionMode,
|
|
489
|
+
sessionId
|
|
488
490
|
})) {
|
|
489
491
|
if (output.type === "exit") {
|
|
490
492
|
if (output.code !== 0 || output.code === void 0) {
|
|
@@ -505,6 +507,9 @@ function startClaudeLoop(opts, session) {
|
|
|
505
507
|
type: "output"
|
|
506
508
|
});
|
|
507
509
|
}
|
|
510
|
+
if (output.type === "json" && output.data.type === "system" && output.data.subtype === "init") {
|
|
511
|
+
sessionId = output.data.sessionId;
|
|
512
|
+
}
|
|
508
513
|
}
|
|
509
514
|
}
|
|
510
515
|
}
|
|
@@ -532,7 +537,7 @@ async function start(options = {}) {
|
|
|
532
537
|
const token = await authGetToken(secret);
|
|
533
538
|
logger.info("Authenticated with handy server");
|
|
534
539
|
const api = new ApiClient(token, secret);
|
|
535
|
-
const response = await api.getOrCreateSession(sessionTag);
|
|
540
|
+
const response = await api.getOrCreateSession({ tag: sessionTag, metadata: { path: workingDirectory, host: os.hostname() } });
|
|
536
541
|
logger.info(`Session created: ${response.session.id}`);
|
|
537
542
|
const handyUrl = generateAppUrl(secret);
|
|
538
543
|
displayQRCode(handyUrl);
|
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { existsSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
|
4
4
|
import { chmod } from 'node:fs/promises';
|
|
5
5
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
6
6
|
import tweetnacl from 'tweetnacl';
|
|
7
|
-
import { homedir } from 'node:os';
|
|
7
|
+
import os, { homedir } from 'node:os';
|
|
8
8
|
import { join, basename } from 'node:path';
|
|
9
9
|
import chalk from 'chalk';
|
|
10
10
|
import { EventEmitter } from 'node:events';
|
|
@@ -271,11 +271,11 @@ class ApiClient {
|
|
|
271
271
|
/**
|
|
272
272
|
* Create a new session or load existing one with the given tag
|
|
273
273
|
*/
|
|
274
|
-
async getOrCreateSession(
|
|
274
|
+
async getOrCreateSession(opts) {
|
|
275
275
|
try {
|
|
276
276
|
const response = await axios.post(
|
|
277
277
|
`https://handy-api.korshakov.org/v1/sessions`,
|
|
278
|
-
{ tag },
|
|
278
|
+
{ tag: opts.tag, metadata: encodeBase64(encrypt(opts.metadata, this.secret)) },
|
|
279
279
|
{
|
|
280
280
|
headers: {
|
|
281
281
|
"Authorization": `Bearer ${this.token}`,
|
|
@@ -283,7 +283,7 @@ class ApiClient {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
);
|
|
286
|
-
logger.info(`Session created/loaded: ${response.data.session.id} (tag: ${tag})`);
|
|
286
|
+
logger.info(`Session created/loaded: ${response.data.session.id} (tag: ${opts.tag})`);
|
|
287
287
|
return response.data;
|
|
288
288
|
} catch (error) {
|
|
289
289
|
logger.error("Failed to get or create session:", error);
|
|
@@ -448,6 +448,7 @@ function startClaudeLoop(opts, session) {
|
|
|
448
448
|
let exiting = false;
|
|
449
449
|
const messageQueue = [];
|
|
450
450
|
let messageResolve = null;
|
|
451
|
+
let sessionId;
|
|
451
452
|
let promise = (async () => {
|
|
452
453
|
session.onUserMessage((message) => {
|
|
453
454
|
messageQueue.push(message);
|
|
@@ -464,7 +465,8 @@ function startClaudeLoop(opts, session) {
|
|
|
464
465
|
command: message.content.text,
|
|
465
466
|
workingDirectory: opts.path,
|
|
466
467
|
model: opts.model,
|
|
467
|
-
permissionMode: opts.permissionMode
|
|
468
|
+
permissionMode: opts.permissionMode,
|
|
469
|
+
sessionId
|
|
468
470
|
})) {
|
|
469
471
|
if (output.type === "exit") {
|
|
470
472
|
if (output.code !== 0 || output.code === void 0) {
|
|
@@ -485,6 +487,9 @@ function startClaudeLoop(opts, session) {
|
|
|
485
487
|
type: "output"
|
|
486
488
|
});
|
|
487
489
|
}
|
|
490
|
+
if (output.type === "json" && output.data.type === "system" && output.data.subtype === "init") {
|
|
491
|
+
sessionId = output.data.sessionId;
|
|
492
|
+
}
|
|
488
493
|
}
|
|
489
494
|
}
|
|
490
495
|
}
|
|
@@ -512,7 +517,7 @@ async function start(options = {}) {
|
|
|
512
517
|
const token = await authGetToken(secret);
|
|
513
518
|
logger.info("Authenticated with handy server");
|
|
514
519
|
const api = new ApiClient(token, secret);
|
|
515
|
-
const response = await api.getOrCreateSession(sessionTag);
|
|
520
|
+
const response = await api.getOrCreateSession({ tag: sessionTag, metadata: { path: workingDirectory, host: os.hostname() } });
|
|
516
521
|
logger.info(`Session created: ${response.session.id}`);
|
|
517
522
|
const handyUrl = generateAppUrl(secret);
|
|
518
523
|
displayQRCode(handyUrl);
|