@nanopm/cli 0.1.0 → 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.js +130 -95
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { Command } from "commander";
|
|
|
5
5
|
|
|
6
6
|
// ../daemon/src/adapters/claude-code/index.ts
|
|
7
7
|
import { execFile, spawn } from "child_process";
|
|
8
|
+
import { randomUUID } from "crypto";
|
|
8
9
|
import { createInterface } from "readline";
|
|
9
10
|
import { writeFile as writeFile2 } from "fs/promises";
|
|
10
11
|
import { join as join3 } from "path";
|
|
@@ -606,6 +607,13 @@ function hashDeviceSecret(secret) {
|
|
|
606
607
|
return createHash("sha256").update(secret).digest("hex");
|
|
607
608
|
}
|
|
608
609
|
|
|
610
|
+
// ../shared/src/harness.ts
|
|
611
|
+
var CLAUDE_CODE = "claude-code";
|
|
612
|
+
function claudeHarness(harnesses) {
|
|
613
|
+
const entries = Array.isArray(harnesses) ? harnesses : [];
|
|
614
|
+
return entries.find((h) => h.id === CLAUDE_CODE) ?? null;
|
|
615
|
+
}
|
|
616
|
+
|
|
609
617
|
// ../shared/src/secrets.ts
|
|
610
618
|
var SECRET_PATTERNS = [
|
|
611
619
|
"**/.env",
|
|
@@ -3785,7 +3793,26 @@ var ClaudeCodeAdapter = class {
|
|
|
3785
3793
|
else args.push("--session-id", spec.sessionId);
|
|
3786
3794
|
return args;
|
|
3787
3795
|
}
|
|
3796
|
+
/**
|
|
3797
|
+
* One session, and one more when the model asked for is newer than this Claude Code: a
|
|
3798
|
+
* skill names `claude-opus-5-5`, a Claude Code older than 2.1.280 refuses it, and the run
|
|
3799
|
+
* used to end there with no roadmap (activation row 36). The second session asks for the
|
|
3800
|
+
* family alias under a fresh id and says so, so the run works on the Opus the machine has.
|
|
3801
|
+
* Any other failure stays a failure.
|
|
3802
|
+
*/
|
|
3788
3803
|
async *run(spec) {
|
|
3804
|
+
for await (const event of this.runOnce(spec)) {
|
|
3805
|
+
const alias = event.type === "failed" ? olderAlias(spec.model, event.message) : null;
|
|
3806
|
+
if (!alias) {
|
|
3807
|
+
yield event;
|
|
3808
|
+
continue;
|
|
3809
|
+
}
|
|
3810
|
+
yield { type: "text", text: `This Claude Code does not know ${spec.model} yet; using ${alias}. \`claude update\` brings the newer model.` };
|
|
3811
|
+
yield* this.runOnce({ ...spec, model: alias, sessionId: randomUUID(), resumeSessionId: void 0 });
|
|
3812
|
+
return;
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
async *runOnce(spec) {
|
|
3789
3816
|
const mcpConfigPath = join3(spec.runDir, "mcp.json");
|
|
3790
3817
|
const mcpServers = Object.fromEntries(
|
|
3791
3818
|
spec.mcp.map((s) => [s.name, { command: s.command, args: s.args, env: s.env }])
|
|
@@ -3832,6 +3859,10 @@ var ClaudeCodeAdapter = class {
|
|
|
3832
3859
|
}
|
|
3833
3860
|
}
|
|
3834
3861
|
};
|
|
3862
|
+
function olderAlias(model, message) {
|
|
3863
|
+
if (!model || !/does not support this model/i.test(message)) return null;
|
|
3864
|
+
return ["opus", "sonnet", "haiku"].find((family) => model.includes(family) && model !== family) ?? null;
|
|
3865
|
+
}
|
|
3835
3866
|
function appendExit(message, code) {
|
|
3836
3867
|
return code === null || code === 0 ? message : `${message} (exit ${code})`;
|
|
3837
3868
|
}
|
|
@@ -5193,7 +5224,7 @@ async function clonelessSkills(skillsDir = findSkillsDir()) {
|
|
|
5193
5224
|
}
|
|
5194
5225
|
|
|
5195
5226
|
// ../daemon/src/problem-mapping-harness.ts
|
|
5196
|
-
import { randomUUID } from "crypto";
|
|
5227
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
5197
5228
|
function mappingHarnessRunner(options) {
|
|
5198
5229
|
return async (input) => {
|
|
5199
5230
|
const skill = await loadSkill(`problem-${input.role}`);
|
|
@@ -5202,7 +5233,7 @@ function mappingHarnessRunner(options) {
|
|
|
5202
5233
|
for await (const event of options.adapter.run({
|
|
5203
5234
|
cwd: options.runDir,
|
|
5204
5235
|
runDir: options.runDir,
|
|
5205
|
-
sessionId:
|
|
5236
|
+
sessionId: randomUUID2(),
|
|
5206
5237
|
systemPrompt: `${skill.body}
|
|
5207
5238
|
|
|
5208
5239
|
Output schema:
|
|
@@ -5493,7 +5524,7 @@ function mappingUsage(checkpoints) {
|
|
|
5493
5524
|
}
|
|
5494
5525
|
|
|
5495
5526
|
// ../daemon/src/persona-job.ts
|
|
5496
|
-
import { randomUUID as
|
|
5527
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5497
5528
|
|
|
5498
5529
|
// ../daemon/src/crew-retry.ts
|
|
5499
5530
|
async function askAgainIfUnanswered(attempt7, say, role7) {
|
|
@@ -5806,7 +5837,7 @@ async function attempt({
|
|
|
5806
5837
|
await onEvent({ type: "text", text: say });
|
|
5807
5838
|
for await (const event of adapter.run({
|
|
5808
5839
|
...spec,
|
|
5809
|
-
sessionId:
|
|
5840
|
+
sessionId: randomUUID3(),
|
|
5810
5841
|
resumeSessionId: void 0,
|
|
5811
5842
|
capabilities: [],
|
|
5812
5843
|
mcp: [],
|
|
@@ -6027,7 +6058,7 @@ async function collect2(client, projectId, cwd, replace) {
|
|
|
6027
6058
|
}
|
|
6028
6059
|
|
|
6029
6060
|
// ../daemon/src/direction-job.ts
|
|
6030
|
-
import { randomUUID as
|
|
6061
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
6031
6062
|
|
|
6032
6063
|
// ../daemon/src/crew-no-downgrade.ts
|
|
6033
6064
|
function keepsTheLine(job, live, confidence) {
|
|
@@ -6222,7 +6253,7 @@ ${renderTests()}` : skill.body;
|
|
|
6222
6253
|
await onEvent({ type: "text", text: say });
|
|
6223
6254
|
for await (const event of adapter.run({
|
|
6224
6255
|
...spec,
|
|
6225
|
-
sessionId:
|
|
6256
|
+
sessionId: randomUUID4(),
|
|
6226
6257
|
resumeSessionId: void 0,
|
|
6227
6258
|
capabilities: [],
|
|
6228
6259
|
mcp: [],
|
|
@@ -6351,7 +6382,7 @@ async function collect3(client, projectId) {
|
|
|
6351
6382
|
}
|
|
6352
6383
|
|
|
6353
6384
|
// ../daemon/src/pitch-job.ts
|
|
6354
|
-
import { randomUUID as
|
|
6385
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
6355
6386
|
async function runPitchJob({ client, job, adapter, spec, onEvent }) {
|
|
6356
6387
|
if (!job.project_id) throw new Error("A pitch needs a project.");
|
|
6357
6388
|
const projectId = job.project_id;
|
|
@@ -6456,7 +6487,7 @@ async function attempt3({
|
|
|
6456
6487
|
await onEvent({ type: "text", text: say });
|
|
6457
6488
|
for await (const event of adapter.run({
|
|
6458
6489
|
...spec,
|
|
6459
|
-
sessionId:
|
|
6490
|
+
sessionId: randomUUID5(),
|
|
6460
6491
|
resumeSessionId: void 0,
|
|
6461
6492
|
capabilities: [],
|
|
6462
6493
|
mcp: [],
|
|
@@ -6568,7 +6599,7 @@ async function collect4(client, projectId) {
|
|
|
6568
6599
|
}
|
|
6569
6600
|
|
|
6570
6601
|
// ../daemon/src/market-job.ts
|
|
6571
|
-
import { randomUUID as
|
|
6602
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
6572
6603
|
|
|
6573
6604
|
// ../daemon/src/web-journal.ts
|
|
6574
6605
|
var HARNESS_WEB_TOOLS = ["WebSearch", "WebFetch"];
|
|
@@ -6809,7 +6840,7 @@ async function attempt4({ adapter, spec, onEvent, role: which, prompt, say }) {
|
|
|
6809
6840
|
await onEvent({ type: "text", text: say });
|
|
6810
6841
|
for await (const event of adapter.run({
|
|
6811
6842
|
...spec,
|
|
6812
|
-
sessionId:
|
|
6843
|
+
sessionId: randomUUID6(),
|
|
6813
6844
|
resumeSessionId: void 0,
|
|
6814
6845
|
capabilities,
|
|
6815
6846
|
mcp: which === "scout" ? spec.mcp : [],
|
|
@@ -6892,7 +6923,7 @@ async function collect5(client, projectId, ask6) {
|
|
|
6892
6923
|
}
|
|
6893
6924
|
|
|
6894
6925
|
// ../daemon/src/needs-job.ts
|
|
6895
|
-
import { randomUUID as
|
|
6926
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
6896
6927
|
var SIGNALS_MAX = 40;
|
|
6897
6928
|
var SIGNAL_CHARS = 3e3;
|
|
6898
6929
|
async function runNeedsJob({ client, job, adapter, spec, onEvent }) {
|
|
@@ -7471,7 +7502,7 @@ async function attempt5({ adapter, spec, onEvent, role: which, schema: shape2, t
|
|
|
7471
7502
|
await onEvent({ type: "text", text: say });
|
|
7472
7503
|
for await (const event of adapter.run({
|
|
7473
7504
|
...spec,
|
|
7474
|
-
sessionId:
|
|
7505
|
+
sessionId: randomUUID7(),
|
|
7475
7506
|
resumeSessionId: void 0,
|
|
7476
7507
|
capabilities: granted,
|
|
7477
7508
|
mcp: granted.length ? spec.mcp : [],
|
|
@@ -7693,7 +7724,7 @@ function opportunitiesOutputSchema(which) {
|
|
|
7693
7724
|
}
|
|
7694
7725
|
|
|
7695
7726
|
// ../daemon/src/crew.ts
|
|
7696
|
-
import { randomUUID as
|
|
7727
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
7697
7728
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
7698
7729
|
import { join as join7 } from "path";
|
|
7699
7730
|
var READ_ONLY = {
|
|
@@ -7720,7 +7751,7 @@ async function attempt6({ adapter, spec, onEvent, skill: skillName, name, schema
|
|
|
7720
7751
|
// A role that may not read the code does not stand in it (docs/nanopm-71-deep-dive-lenses.md).
|
|
7721
7752
|
cwd: grant.includes("read_repo") ? spec.cwd : runDir,
|
|
7722
7753
|
runDir,
|
|
7723
|
-
sessionId:
|
|
7754
|
+
sessionId: randomUUID8(),
|
|
7724
7755
|
resumeSessionId: void 0,
|
|
7725
7756
|
capabilities: [...grant],
|
|
7726
7757
|
mcp: grant.includes("read_data") ? spec.mcp : [],
|
|
@@ -9222,7 +9253,7 @@ async function readRoadmap(client, projectId) {
|
|
|
9222
9253
|
}
|
|
9223
9254
|
|
|
9224
9255
|
// ../daemon/src/reword-job.ts
|
|
9225
|
-
import { randomUUID as
|
|
9256
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
9226
9257
|
async function runRewordJob({ client, job, adapter, spec, onEvent }) {
|
|
9227
9258
|
if (!job.project_id) throw new Error("A rewrite needs a project.");
|
|
9228
9259
|
const asked = RewordParams.safeParse(job.params);
|
|
@@ -9284,7 +9315,7 @@ async function session({ adapter, spec, onEvent, prompt }) {
|
|
|
9284
9315
|
let usage;
|
|
9285
9316
|
for await (const event of adapter.run({
|
|
9286
9317
|
...spec,
|
|
9287
|
-
sessionId:
|
|
9318
|
+
sessionId: randomUUID9(),
|
|
9288
9319
|
resumeSessionId: void 0,
|
|
9289
9320
|
capabilities: [],
|
|
9290
9321
|
mcp: [],
|
|
@@ -9319,7 +9350,7 @@ ${body}
|
|
|
9319
9350
|
}
|
|
9320
9351
|
|
|
9321
9352
|
// ../daemon/src/talk-job.ts
|
|
9322
|
-
import { randomUUID as
|
|
9353
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
9323
9354
|
var ALLOWED = /* @__PURE__ */ new Set([...TALK_TOOLS.map((t) => `mcp__nanopm__${t}`), "StructuredOutput"]);
|
|
9324
9355
|
async function runTalkJob({ client, job, adapter, spec, onEvent }) {
|
|
9325
9356
|
if (!job.project_id) throw new Error("A conversation needs a project.");
|
|
@@ -9351,7 +9382,7 @@ async function runTalkJob({ client, job, adapter, spec, onEvent }) {
|
|
|
9351
9382
|
let usage;
|
|
9352
9383
|
for await (const event of adapter.run({
|
|
9353
9384
|
...spec,
|
|
9354
|
-
sessionId:
|
|
9385
|
+
sessionId: randomUUID10(),
|
|
9355
9386
|
resumeSessionId: void 0,
|
|
9356
9387
|
capabilities: skill.manifest.capabilities,
|
|
9357
9388
|
harnessWeb: false,
|
|
@@ -9388,7 +9419,7 @@ var told = (r) => ({
|
|
|
9388
9419
|
|
|
9389
9420
|
// ../daemon/src/objective-definitions.ts
|
|
9390
9421
|
import { execFile as execFile4 } from "child_process";
|
|
9391
|
-
import { randomUUID as
|
|
9422
|
+
import { randomUUID as randomUUID11 } from "crypto";
|
|
9392
9423
|
import { existsSync as existsSync2 } from "fs";
|
|
9393
9424
|
import { mkdir as mkdir3, readFile as readFile5 } from "fs/promises";
|
|
9394
9425
|
import { join as join8 } from "path";
|
|
@@ -9564,7 +9595,7 @@ async function readDefinitions({ adapter, spec, publish: publish5, journal, hasR
|
|
|
9564
9595
|
for await (const event of adapter.run({
|
|
9565
9596
|
...spec,
|
|
9566
9597
|
runDir,
|
|
9567
|
-
sessionId:
|
|
9598
|
+
sessionId: randomUUID11(),
|
|
9568
9599
|
resumeSessionId: void 0,
|
|
9569
9600
|
capabilities: ["read_repo"],
|
|
9570
9601
|
// No Glob: it read the folder at 15 s a search (Guillaume's suggestions took 75 s,
|
|
@@ -9888,7 +9919,7 @@ function localDate(timezone, at) {
|
|
|
9888
9919
|
}
|
|
9889
9920
|
|
|
9890
9921
|
// ../daemon/src/run-job.ts
|
|
9891
|
-
import { randomUUID as
|
|
9922
|
+
import { randomUUID as randomUUID12 } from "crypto";
|
|
9892
9923
|
import { existsSync as existsSync3 } from "fs";
|
|
9893
9924
|
import { mkdir as mkdir4, readFile as readFile6, rm as rm2 } from "fs/promises";
|
|
9894
9925
|
import { homedir as homedir2, tmpdir } from "os";
|
|
@@ -10333,7 +10364,7 @@ async function execute(job, skill, ctx, events) {
|
|
|
10333
10364
|
const runDir = join9(nanopmHome(), "run", job.id);
|
|
10334
10365
|
await mkdir4(runDir, { recursive: true, mode: 448 });
|
|
10335
10366
|
const resumeSessionId = job.resume_of ? await sessionOf(ctx.client, job.resume_of) : void 0;
|
|
10336
|
-
const sessionId = resumeSessionId ??
|
|
10367
|
+
const sessionId = resumeSessionId ?? randomUUID12();
|
|
10337
10368
|
await ctx.client.from("jobs").update({ harness_session_id: sessionId }).eq("id", job.id);
|
|
10338
10369
|
const adapter = ctx.adapter ?? new ClaudeCodeAdapter();
|
|
10339
10370
|
const [project, searchProvider] = job.project_id ? await Promise.all([loadProject(ctx.client, job.project_id), loadSearchProvider(ctx.client, job.project_id)]) : [null, null];
|
|
@@ -10829,7 +10860,7 @@ async function runDaemon(options = {}) {
|
|
|
10829
10860
|
let daemonId;
|
|
10830
10861
|
try {
|
|
10831
10862
|
auth = await authenticate();
|
|
10832
|
-
daemonId = await registerDaemon(auth, options.version ?? "dev", options.shared);
|
|
10863
|
+
daemonId = await registerDaemon(auth, options.version ?? "dev", { shared: options.shared });
|
|
10833
10864
|
log(`daemon ${daemonId} online as ${auth.userId} on ${hostname2()}`);
|
|
10834
10865
|
for (const reclaimed of await reclaimOwnJobs(auth.client, daemonId)) {
|
|
10835
10866
|
log(`job ${reclaimed} was running when this daemon last stopped; marked interrupted (resume with \`nanopm resume ${reclaimed}\`)`);
|
|
@@ -11137,7 +11168,7 @@ async function killOrphanedHarness(jobId) {
|
|
|
11137
11168
|
}
|
|
11138
11169
|
await rm3(runDir, { recursive: true, force: true });
|
|
11139
11170
|
}
|
|
11140
|
-
async function registerDaemon(auth, version,
|
|
11171
|
+
async function registerDaemon(auth, version, opts = {}) {
|
|
11141
11172
|
const path = join10(nanopmHome(), "daemon.json");
|
|
11142
11173
|
let identity = null;
|
|
11143
11174
|
try {
|
|
@@ -11145,18 +11176,18 @@ async function registerDaemon(auth, version, shared) {
|
|
|
11145
11176
|
} catch {
|
|
11146
11177
|
identity = null;
|
|
11147
11178
|
}
|
|
11148
|
-
const isShared = shared ?? identity?.shared ?? false;
|
|
11149
|
-
const detection = await new ClaudeCodeAdapter().detect();
|
|
11150
|
-
if (detection.authenticated === null && identity?.id) {
|
|
11179
|
+
const isShared = opts.shared ?? identity?.shared ?? false;
|
|
11180
|
+
const detection = opts.claude ?? await new ClaudeCodeAdapter().detect();
|
|
11181
|
+
if (!opts.claude && detection.authenticated === null && identity?.id) {
|
|
11151
11182
|
const { data: existing2 } = await auth.client.from("daemons").select("harnesses").eq("id", identity.id).maybeSingle();
|
|
11152
|
-
const known = existing2?.harnesses
|
|
11183
|
+
const known = claudeHarness(existing2?.harnesses);
|
|
11153
11184
|
if (typeof known?.authenticated === "boolean") detection.authenticated = known.authenticated;
|
|
11154
11185
|
}
|
|
11155
11186
|
const fields = {
|
|
11156
11187
|
hostname: hostname2(),
|
|
11157
11188
|
version,
|
|
11158
|
-
harnesses: [{ id:
|
|
11159
|
-
status: "online",
|
|
11189
|
+
harnesses: [{ id: CLAUDE_CODE, ...detection }],
|
|
11190
|
+
status: opts.claude ? "offline" : "online",
|
|
11160
11191
|
shared: isShared,
|
|
11161
11192
|
last_seen_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
11162
11193
|
};
|
|
@@ -11240,7 +11271,7 @@ function contextFromEnv(env = process.env) {
|
|
|
11240
11271
|
|
|
11241
11272
|
// ../memory-mcp/src/server.ts
|
|
11242
11273
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11243
|
-
import { randomUUID as
|
|
11274
|
+
import { randomUUID as randomUUID13 } from "crypto";
|
|
11244
11275
|
import { setTimeout as sleep2 } from "timers/promises";
|
|
11245
11276
|
import { z as z20 } from "zod";
|
|
11246
11277
|
|
|
@@ -13083,7 +13114,7 @@ async function talkAct(ctx, roundOf, proposed, asked, why) {
|
|
|
13083
13114
|
const [round2, world] = await Promise.all([roundOf, talkWorld(ctx.client, ctx.projectId)]);
|
|
13084
13115
|
const ref = change.kind === "add" ? change.parent : change.kind === "add_solution" ? change.leaf : change.ref;
|
|
13085
13116
|
const target = ref ? currentRow(world, ref)?.ref ?? currentSolution(world, ref)?.ref ?? ref : void 0;
|
|
13086
|
-
const base = { id:
|
|
13117
|
+
const base = { id: randomUUID13(), change, ...movesDown(change, world) ? { down: true } : {}, ...change.kind === "brainstorm" ? { cost: runCost(change.depth) } : {} };
|
|
13087
13118
|
let action;
|
|
13088
13119
|
if (!askedIn(asked, round2.words)) {
|
|
13089
13120
|
action = { ...base, state: "suggested", line: talkLine(change, world, false), ...target ? { target } : {} };
|
|
@@ -13874,6 +13905,59 @@ import { mkdir as mkdir6, readFile as readFile11, rm as rm4, writeFile as writeF
|
|
|
13874
13905
|
import { homedir as homedir3 } from "os";
|
|
13875
13906
|
import { join as join12 } from "path";
|
|
13876
13907
|
import { promisify as promisify7 } from "util";
|
|
13908
|
+
|
|
13909
|
+
// package.json
|
|
13910
|
+
var package_default = {
|
|
13911
|
+
name: "@nanopm/cli",
|
|
13912
|
+
version: "0.1.2",
|
|
13913
|
+
description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
|
|
13914
|
+
type: "module",
|
|
13915
|
+
license: "MIT",
|
|
13916
|
+
bin: {
|
|
13917
|
+
nanopm: "dist/index.js"
|
|
13918
|
+
},
|
|
13919
|
+
files: [
|
|
13920
|
+
"dist",
|
|
13921
|
+
"skills"
|
|
13922
|
+
],
|
|
13923
|
+
engines: {
|
|
13924
|
+
node: ">=20"
|
|
13925
|
+
},
|
|
13926
|
+
scripts: {
|
|
13927
|
+
build: "tsup && rm -rf skills && cp -R ../../skills skills"
|
|
13928
|
+
},
|
|
13929
|
+
dependencies: {
|
|
13930
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
13931
|
+
"@supabase/supabase-js": "^2.116.0",
|
|
13932
|
+
commander: "^15.0.0",
|
|
13933
|
+
postgres: "^3.4.7",
|
|
13934
|
+
zod: "^4.5.4"
|
|
13935
|
+
},
|
|
13936
|
+
repository: {
|
|
13937
|
+
type: "git",
|
|
13938
|
+
url: "git+https://github.com/nmrtn/nanopm2.git"
|
|
13939
|
+
},
|
|
13940
|
+
keywords: [
|
|
13941
|
+
"product-management",
|
|
13942
|
+
"claude-code",
|
|
13943
|
+
"agent",
|
|
13944
|
+
"solopreneur"
|
|
13945
|
+
],
|
|
13946
|
+
devDependencies: {
|
|
13947
|
+
"@nanopm/connectors": "workspace:*",
|
|
13948
|
+
"@nanopm/daemon": "workspace:*",
|
|
13949
|
+
"@nanopm/memory-mcp": "workspace:*",
|
|
13950
|
+
"@nanopm/shared": "workspace:*"
|
|
13951
|
+
},
|
|
13952
|
+
publishConfig: {
|
|
13953
|
+
access: "public"
|
|
13954
|
+
}
|
|
13955
|
+
};
|
|
13956
|
+
|
|
13957
|
+
// src/version.ts
|
|
13958
|
+
var VERSION = package_default.version;
|
|
13959
|
+
|
|
13960
|
+
// src/commands/daemon-service.ts
|
|
13877
13961
|
var execFileAsync7 = promisify7(execFile7);
|
|
13878
13962
|
var LAUNCHD_LABEL = "pm.nano.daemon";
|
|
13879
13963
|
function plistPath() {
|
|
@@ -13919,12 +14003,20 @@ function requireMac() {
|
|
|
13919
14003
|
async function installDaemon(out = (l) => console.log(l), opts = {}) {
|
|
13920
14004
|
requireMac();
|
|
13921
14005
|
if (!await loadCredentials()) throw new Error("not logged in; run `nanopm login` first");
|
|
14006
|
+
const auth = await authenticate();
|
|
14007
|
+
const found = (claude) => registerDaemon(auth, VERSION, { shared: opts.shared, claude }).catch(() => void 0);
|
|
13922
14008
|
const adapter = new ClaudeCodeAdapter();
|
|
13923
14009
|
const detection = await adapter.detect();
|
|
13924
|
-
if (!detection.available)
|
|
14010
|
+
if (!detection.available) {
|
|
14011
|
+
await found(detection);
|
|
14012
|
+
throw new Error("Claude Code is not installed on this machine (`claude` not found on PATH)");
|
|
14013
|
+
}
|
|
13925
14014
|
out(`Claude Code ${detection.version} found; checking its login\u2026`);
|
|
13926
14015
|
const ping = await adapter.ping();
|
|
13927
|
-
if (!ping.ok)
|
|
14016
|
+
if (!ping.ok) {
|
|
14017
|
+
await found({ ...detection, authenticated: false });
|
|
14018
|
+
throw new Error(`Claude Code is installed but not usable headless: ${ping.message}`);
|
|
14019
|
+
}
|
|
13928
14020
|
out("Claude Code answers.");
|
|
13929
14021
|
const web = await adapter.probeWebSearch();
|
|
13930
14022
|
await rememberWebSearch(web.ok);
|
|
@@ -13936,6 +14028,7 @@ async function installDaemon(out = (l) => console.log(l), opts = {}) {
|
|
|
13936
14028
|
await mkdir6(join12(home, "Library", "LaunchAgents"), { recursive: true });
|
|
13937
14029
|
await mkdir6(nanopmHome(), { recursive: true, mode: 448 });
|
|
13938
14030
|
if (opts.shared !== void 0) await rememberShared(opts.shared);
|
|
14031
|
+
await found({ ...detection, authenticated: true });
|
|
13939
14032
|
await writeFile4(plistPath(), renderPlist({ node: process.execPath, bin, home, path, logPath: daemonLogPath(), nanopmHome: nanopmHome() }), { mode: 420 });
|
|
13940
14033
|
const target = `gui/${process.getuid?.() ?? 501}`;
|
|
13941
14034
|
await execFileAsync7("launchctl", ["bootout", target, plistPath()]).catch(() => void 0);
|
|
@@ -13944,15 +14037,6 @@ async function installDaemon(out = (l) => console.log(l), opts = {}) {
|
|
|
13944
14037
|
const shared = opts.shared ?? await localShared();
|
|
13945
14038
|
out(shared ? "This machine is shared with your projects: it also runs scheduled reviews and partners' jobs." : "This machine runs your jobs only. `nanopm daemon install --share` to offer it to your projects.");
|
|
13946
14039
|
if (opts.next !== false) out(`Next, in a repo: nanopm onboard \xB7 or continue at ${webUrl()}/new`);
|
|
13947
|
-
const auth = await authenticate();
|
|
13948
|
-
for (let i = 0; i < 20; i++) {
|
|
13949
|
-
const id = await localDaemonId();
|
|
13950
|
-
if (id) {
|
|
13951
|
-
await auth.client.from("daemons").update({ harnesses: [{ id: "claude-code", available: true, version: detection.version, authenticated: true }] }).eq("id", id);
|
|
13952
|
-
break;
|
|
13953
|
-
}
|
|
13954
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
13955
|
-
}
|
|
13956
14040
|
}
|
|
13957
14041
|
async function uninstallDaemon(out = (l) => console.log(l)) {
|
|
13958
14042
|
requireMac();
|
|
@@ -14222,7 +14306,9 @@ async function login(opts = {}) {
|
|
|
14222
14306
|
await installDaemon(out, { next: false });
|
|
14223
14307
|
} catch (err) {
|
|
14224
14308
|
out(`could not start me in the background: ${err instanceof Error ? err.message : String(err)}`);
|
|
14225
|
-
out("
|
|
14309
|
+
out("");
|
|
14310
|
+
out("Your account is linked, but I cannot work on this machine yet. Fix the line above, then run: nanopm daemon install. The page in your browser moves on by itself.");
|
|
14311
|
+
return;
|
|
14226
14312
|
}
|
|
14227
14313
|
}
|
|
14228
14314
|
out("");
|
|
@@ -15523,57 +15609,6 @@ async function resume(jobId, opts) {
|
|
|
15523
15609
|
return streamJob(auth.client, job.id, { pollMs: 1e3, out, renderer, cancelSignal: opts.cancelSignal, answerer, verbose: verboseWanted() });
|
|
15524
15610
|
}
|
|
15525
15611
|
|
|
15526
|
-
// package.json
|
|
15527
|
-
var package_default = {
|
|
15528
|
-
name: "@nanopm/cli",
|
|
15529
|
-
version: "0.1.0",
|
|
15530
|
-
description: "An autonomous product manager for builders who run several small products. Runs on your machine, drives your own coding harness.",
|
|
15531
|
-
type: "module",
|
|
15532
|
-
license: "MIT",
|
|
15533
|
-
bin: {
|
|
15534
|
-
nanopm: "dist/index.js"
|
|
15535
|
-
},
|
|
15536
|
-
files: [
|
|
15537
|
-
"dist",
|
|
15538
|
-
"skills"
|
|
15539
|
-
],
|
|
15540
|
-
engines: {
|
|
15541
|
-
node: ">=20"
|
|
15542
|
-
},
|
|
15543
|
-
scripts: {
|
|
15544
|
-
build: "tsup && rm -rf skills && cp -R ../../skills skills"
|
|
15545
|
-
},
|
|
15546
|
-
dependencies: {
|
|
15547
|
-
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
15548
|
-
"@supabase/supabase-js": "^2.116.0",
|
|
15549
|
-
commander: "^15.0.0",
|
|
15550
|
-
postgres: "^3.4.7",
|
|
15551
|
-
zod: "^4.5.4"
|
|
15552
|
-
},
|
|
15553
|
-
repository: {
|
|
15554
|
-
type: "git",
|
|
15555
|
-
url: "git+https://github.com/nmrtn/nanopm2.git"
|
|
15556
|
-
},
|
|
15557
|
-
keywords: [
|
|
15558
|
-
"product-management",
|
|
15559
|
-
"claude-code",
|
|
15560
|
-
"agent",
|
|
15561
|
-
"solopreneur"
|
|
15562
|
-
],
|
|
15563
|
-
devDependencies: {
|
|
15564
|
-
"@nanopm/connectors": "workspace:*",
|
|
15565
|
-
"@nanopm/daemon": "workspace:*",
|
|
15566
|
-
"@nanopm/memory-mcp": "workspace:*",
|
|
15567
|
-
"@nanopm/shared": "workspace:*"
|
|
15568
|
-
},
|
|
15569
|
-
publishConfig: {
|
|
15570
|
-
access: "public"
|
|
15571
|
-
}
|
|
15572
|
-
};
|
|
15573
|
-
|
|
15574
|
-
// src/version.ts
|
|
15575
|
-
var VERSION = package_default.version;
|
|
15576
|
-
|
|
15577
15612
|
// src/commands/connect.ts
|
|
15578
15613
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
15579
15614
|
function whereItGoes(color) {
|
package/package.json
CHANGED