niahere 0.2.6 → 0.2.8
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 +2 -2
- package/bin/nia +0 -2
- package/defaults/self/identity.md +2 -2
- package/package.json +1 -1
- package/skills/nia-image/SKILL.md +3 -3
- package/skills/nia-image/scripts/generate_image.py +2 -2
- package/src/cli/channels.ts +2 -5
- package/src/cli/status.ts +7 -2
- package/src/commands/init.ts +19 -12
- package/src/core/scheduler.ts +6 -0
package/README.md
CHANGED
|
@@ -67,8 +67,8 @@ All config and data lives in `~/.niahere/`:
|
|
|
67
67
|
soul.md — how the agent works
|
|
68
68
|
memory.md — persistent learnings (read/written on demand, not loaded into context)
|
|
69
69
|
images/
|
|
70
|
-
reference.
|
|
71
|
-
profile.
|
|
70
|
+
reference.webp — visual identity reference image
|
|
71
|
+
profile.webp — profile picture for Telegram/Slack
|
|
72
72
|
tmp/
|
|
73
73
|
nia.pid, daemon.log, cron-state.json, cron-audit.jsonl
|
|
74
74
|
```
|
package/bin/nia
CHANGED
|
@@ -19,9 +19,7 @@ if [ -z "$BUN_CMD" ]; then
|
|
|
19
19
|
echo "Bun install failed. Install manually: curl -fsSL https://bun.sh/install | bash"
|
|
20
20
|
exit 1
|
|
21
21
|
fi
|
|
22
|
-
echo ""
|
|
23
22
|
echo "Bun installed. Continuing..."
|
|
24
|
-
echo "(Open a new terminal for bun to be in PATH permanently)"
|
|
25
23
|
echo ""
|
|
26
24
|
else
|
|
27
25
|
echo ""
|
|
@@ -31,8 +31,8 @@ You're curious. You like understanding *why* things work, not just *that* they w
|
|
|
31
31
|
|
|
32
32
|
To generate or update your visual identity, use the `nia-image` skill.
|
|
33
33
|
|
|
34
|
-
- **Profile picture**: `~/.niahere/images/profile.
|
|
35
|
-
- **Reference image**: `~/.niahere/images/reference.
|
|
34
|
+
- **Profile picture**: `~/.niahere/images/profile.webp`
|
|
35
|
+
- **Reference image**: `~/.niahere/images/reference.webp`
|
|
36
36
|
|
|
37
37
|
## What You Don't Do
|
|
38
38
|
|
package/package.json
CHANGED
|
@@ -21,13 +21,13 @@ Generate photorealistic images of Nia with consistent identity across different
|
|
|
21
21
|
## Assets
|
|
22
22
|
|
|
23
23
|
The script looks for references in this order:
|
|
24
|
-
1. `~/.niahere/images/reference.
|
|
24
|
+
1. `~/.niahere/images/reference.webp` — user's custom reference (takes priority)
|
|
25
25
|
2. `assets/nia-reference.webp` — default shipped with niahere
|
|
26
26
|
|
|
27
27
|
| Location | Purpose |
|
|
28
28
|
|----------|---------|
|
|
29
|
-
| `~/.niahere/images/reference.
|
|
30
|
-
| `~/.niahere/images/profile.
|
|
29
|
+
| `~/.niahere/images/reference.webp` | User's reference image |
|
|
30
|
+
| `~/.niahere/images/profile.webp` | User's profile picture (for Telegram/Slack) |
|
|
31
31
|
| `~/.niahere/images/` | Output directory for new generations |
|
|
32
32
|
| `assets/nia-reference.webp` | Default reference (fallback) |
|
|
33
33
|
| `assets/nia-profile.webp` | Default profile picture (fallback) |
|
|
@@ -24,7 +24,7 @@ NIA_CONFIG = NIA_HOME / "config.yaml"
|
|
|
24
24
|
DEFAULT_MODEL = "gemini-3.1-flash-image-preview"
|
|
25
25
|
PRO_MODEL = "gemini-3-pro-image-preview"
|
|
26
26
|
BASIC_MODEL = "gemini-2.5-flash-image"
|
|
27
|
-
USER_REFERENCE = str(NIA_HOME / "images" / "reference.
|
|
27
|
+
USER_REFERENCE = str(NIA_HOME / "images" / "reference.webp")
|
|
28
28
|
DEFAULT_REFERENCE = str(PROJECT_ROOT / "assets" / "nia-reference.webp")
|
|
29
29
|
DEFAULT_OUTPUT = str(NIA_HOME / "images")
|
|
30
30
|
DEFAULT_PROMPT = (
|
|
@@ -194,7 +194,7 @@ def main() -> None:
|
|
|
194
194
|
f"or add gemini_api_key to {NIA_CONFIG}."
|
|
195
195
|
)
|
|
196
196
|
|
|
197
|
-
# Resolve reference image: user's ~/.niahere/images/reference.
|
|
197
|
+
# Resolve reference image: user's ~/.niahere/images/reference.webp > skill default > none
|
|
198
198
|
ref_path: str | None = None
|
|
199
199
|
if not args.no_reference:
|
|
200
200
|
if args.reference:
|
package/src/cli/channels.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getConfig, updateRawConfig } from "../utils/config";
|
|
2
|
-
import { withDb } from "../db/connection";
|
|
3
2
|
import { getPaths } from "../utils/paths";
|
|
4
3
|
import { errMsg } from "../utils/errors";
|
|
5
4
|
import { fail } from "../utils/cli";
|
|
@@ -21,10 +20,8 @@ export async function sendCommand(): Promise<void> {
|
|
|
21
20
|
const { sendMessage } = await import("../mcp/tools");
|
|
22
21
|
|
|
23
22
|
try {
|
|
24
|
-
await
|
|
25
|
-
|
|
26
|
-
console.log(result);
|
|
27
|
-
});
|
|
23
|
+
const result = await sendMessage(message, channel);
|
|
24
|
+
console.log(result);
|
|
28
25
|
} catch (err) {
|
|
29
26
|
fail(`Failed to send: ${errMsg(err)}`);
|
|
30
27
|
}
|
package/src/cli/status.ts
CHANGED
|
@@ -228,7 +228,10 @@ export async function statusCommand(argv: string[] = []): Promise<void> {
|
|
|
228
228
|
a.name.localeCompare(b.name),
|
|
229
229
|
);
|
|
230
230
|
|
|
231
|
-
|
|
231
|
+
// Hide completed one-shot jobs
|
|
232
|
+
const visibleJobs = sortedJobs.filter((j) => !(j.scheduleType === "once" && !j.enabled && j.lastRunAt));
|
|
233
|
+
|
|
234
|
+
for (const job of visibleJobs) {
|
|
232
235
|
const stateInfo = state[job.name];
|
|
233
236
|
const status = stateInfo?.status ?? (job.lastRunAt ? "ok" : "never");
|
|
234
237
|
const lastRun = stateInfo?.lastRun ?? job.lastRunAt ?? null;
|
|
@@ -291,8 +294,10 @@ export async function statusCommand(argv: string[] = []): Promise<void> {
|
|
|
291
294
|
const icon = info.status === "ok" ? "\u2713" : info.status === "error" ? "\u2717" : "\u2217";
|
|
292
295
|
console.log(` ${icon} ${name}: ${info.status} (last: ${last}, ${info.duration_ms}ms)`);
|
|
293
296
|
}
|
|
297
|
+
} else if (dbError) {
|
|
298
|
+
console.log(`\nJobs: database unavailable (${errMsg(dbError)})`);
|
|
294
299
|
} else {
|
|
295
|
-
console.log("\nJobs:
|
|
300
|
+
console.log("\nJobs: none");
|
|
296
301
|
}
|
|
297
302
|
}
|
|
298
303
|
|
package/src/commands/init.ts
CHANGED
|
@@ -52,8 +52,12 @@ async function offerBeadsShellExport(rl: readline.Interface, beadsDir: string):
|
|
|
52
52
|
if (answer.toLowerCase() !== "y") return;
|
|
53
53
|
|
|
54
54
|
appendFileSync(rcFile, `\n# Beads global task DB\n${exportLine}\n`);
|
|
55
|
+
// Apply to current process so it takes effect immediately
|
|
56
|
+
const parts = exportLine.replace("$HOME", homedir()).split("=");
|
|
57
|
+
if (parts.length === 2) {
|
|
58
|
+
process.env[parts[0].replace("export ", "")] = parts[1].replace(/"/g, "");
|
|
59
|
+
}
|
|
55
60
|
console.log(` \u2713 added BEADS_DIR to ${rcFile.replace(homedir(), "~")}`);
|
|
56
|
-
console.log(` Run 'source ${rcFile.replace(homedir(), "~")}' or open a new terminal.`);
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
function loadTemplate(name: string, vars: Record<string, string> = {}): string {
|
|
@@ -144,6 +148,7 @@ export async function runInit(): Promise<void> {
|
|
|
144
148
|
if (telegramToken) {
|
|
145
149
|
const openInput = await ask(rl, "Allow anyone to message? (y/n)", "n");
|
|
146
150
|
telegramOpen = openInput.toLowerCase() === "y";
|
|
151
|
+
console.log(" Tip: Send a message to your bot to activate outbound messaging.");
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
154
|
}
|
|
@@ -181,7 +186,8 @@ export async function runInit(): Promise<void> {
|
|
|
181
186
|
const createUrl = `https://api.slack.com/apps?new_app=1&manifest_json=${encodeURIComponent(manifest)}`;
|
|
182
187
|
|
|
183
188
|
console.log("\n Opening Slack app creation page...");
|
|
184
|
-
|
|
189
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
190
|
+
Bun.spawn([openCmd, createUrl], { stdio: ["ignore", "ignore", "ignore"] });
|
|
185
191
|
console.log(" 1. Click 'Create' to create the app");
|
|
186
192
|
console.log(" 2. Go to 'OAuth & Permissions' → Install to workspace → copy Bot Token (xoxb-...)");
|
|
187
193
|
console.log(" 3. Go to 'Basic Information' → 'App-Level Tokens' → create one with connections:write → copy (xapp-...)\n");
|
|
@@ -299,7 +305,7 @@ export async function runInit(): Promise<void> {
|
|
|
299
305
|
// Visual identity
|
|
300
306
|
const imagesDir = `${home}/images`;
|
|
301
307
|
mkdirSync(imagesDir, { recursive: true });
|
|
302
|
-
const hasUserReference = existsSync(`${imagesDir}/reference.
|
|
308
|
+
const hasUserReference = existsSync(`${imagesDir}/reference.webp`);
|
|
303
309
|
const hasDefaultReference = existsSync(`${SKILL_ASSETS_DIR}/nia-reference.webp`);
|
|
304
310
|
|
|
305
311
|
if (geminiApiKey && !hasUserReference) {
|
|
@@ -317,23 +323,23 @@ export async function runInit(): Promise<void> {
|
|
|
317
323
|
"--api-key", geminiApiKey,
|
|
318
324
|
"--aspect-ratio", "9:16",
|
|
319
325
|
"--prompt", prompt,
|
|
320
|
-
"--output", `${imagesDir}/reference.
|
|
326
|
+
"--output", `${imagesDir}/reference.webp`,
|
|
321
327
|
], { stdout: "pipe", stderr: "pipe" });
|
|
322
328
|
const exitCode = await proc.exited;
|
|
323
329
|
if (exitCode === 0) {
|
|
324
|
-
console.log(` \u2713 generated reference image at ${imagesDir}/reference.
|
|
330
|
+
console.log(` \u2713 generated reference image at ${imagesDir}/reference.webp`);
|
|
325
331
|
// Also generate a profile picture
|
|
326
332
|
console.log(" Generating profile picture...");
|
|
327
333
|
const profileProc = Bun.spawn([
|
|
328
334
|
"python3", GENERATE_SCRIPT,
|
|
329
|
-
"--reference", `${imagesDir}/reference.
|
|
335
|
+
"--reference", `${imagesDir}/reference.webp`,
|
|
330
336
|
"--api-key", geminiApiKey,
|
|
331
337
|
"--aspect-ratio", "1:1",
|
|
332
338
|
"--prompt", `Photorealistic close-up portrait of the same person from the reference. Warm slight smile, direct eye contact, soft ambient side lighting, creamy bokeh background, 85mm f/1.8, shallow depth of field. Same face, same style, natural skin texture, DSLR quality, hyper-detailed.`,
|
|
333
|
-
"--output", `${imagesDir}/profile.
|
|
339
|
+
"--output", `${imagesDir}/profile.webp`,
|
|
334
340
|
], { stdout: "pipe", stderr: "pipe" });
|
|
335
341
|
if (await profileProc.exited === 0) {
|
|
336
|
-
console.log(` \u2713 generated profile picture at ${imagesDir}/profile.
|
|
342
|
+
console.log(` \u2713 generated profile picture at ${imagesDir}/profile.webp`);
|
|
337
343
|
}
|
|
338
344
|
} else {
|
|
339
345
|
const stderr = await new Response(proc.stderr).text();
|
|
@@ -342,10 +348,10 @@ export async function runInit(): Promise<void> {
|
|
|
342
348
|
} else if (hasDefaultReference) {
|
|
343
349
|
// No description — copy defaults
|
|
344
350
|
const { copyFileSync } = await import("fs");
|
|
345
|
-
copyFileSync(`${SKILL_ASSETS_DIR}/nia-reference.webp`, `${imagesDir}/reference.
|
|
351
|
+
copyFileSync(`${SKILL_ASSETS_DIR}/nia-reference.webp`, `${imagesDir}/reference.webp`);
|
|
346
352
|
console.log(` \u2713 copied default reference image`);
|
|
347
353
|
if (existsSync(`${SKILL_ASSETS_DIR}/nia-profile.webp`)) {
|
|
348
|
-
copyFileSync(`${SKILL_ASSETS_DIR}/nia-profile.webp`, `${imagesDir}/profile.
|
|
354
|
+
copyFileSync(`${SKILL_ASSETS_DIR}/nia-profile.webp`, `${imagesDir}/profile.webp`);
|
|
349
355
|
console.log(` \u2713 copied default profile picture`);
|
|
350
356
|
}
|
|
351
357
|
}
|
|
@@ -400,6 +406,7 @@ export async function runInit(): Promise<void> {
|
|
|
400
406
|
const vars = { agentName, ownerName, ownerRole, ownerLocation, ownerInterests };
|
|
401
407
|
const selfFile = (name: string) => `${paths.selfDir}/${name}`;
|
|
402
408
|
|
|
409
|
+
// Identity and owner — always write (template-driven, not user-customized)
|
|
403
410
|
writeFileSync(selfFile("identity.md"), loadTemplate("identity.md", vars));
|
|
404
411
|
console.log(` \u2713 wrote ${selfFile("identity.md")}`);
|
|
405
412
|
|
|
@@ -411,8 +418,8 @@ export async function runInit(): Promise<void> {
|
|
|
411
418
|
}
|
|
412
419
|
|
|
413
420
|
// Soul and memory — only create if missing (user may have customized)
|
|
414
|
-
writeIfMissing(selfFile("soul.md"), loadTemplate("soul.md"), selfFile("soul.md"));
|
|
415
|
-
writeIfMissing(selfFile("memory.md"), loadTemplate("memory.md"), selfFile("memory.md"));
|
|
421
|
+
writeIfMissing(selfFile("soul.md"), loadTemplate("soul.md", vars), selfFile("soul.md"));
|
|
422
|
+
writeIfMissing(selfFile("memory.md"), loadTemplate("memory.md", vars), selfFile("memory.md"));
|
|
416
423
|
|
|
417
424
|
resetConfig();
|
|
418
425
|
|
package/src/core/scheduler.ts
CHANGED
|
@@ -99,6 +99,12 @@ async function tick(): Promise<void> {
|
|
|
99
99
|
await Job.markRun(job.name, nextRun).catch((err) => {
|
|
100
100
|
log.error({ err, job: job.name }, "scheduler: failed to update next_run_at");
|
|
101
101
|
});
|
|
102
|
+
|
|
103
|
+
// Auto-disable one-shot jobs after execution
|
|
104
|
+
if (job.scheduleType === "once") {
|
|
105
|
+
await Job.update(job.name, { enabled: false }).catch(() => {});
|
|
106
|
+
log.info({ job: job.name }, "scheduler: one-shot job completed, auto-disabled");
|
|
107
|
+
}
|
|
102
108
|
}
|
|
103
109
|
}
|
|
104
110
|
|