@inetafrica/open-claudia 2.15.0 → 3.0.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/.env.example +30 -4
- package/CHANGELOG.md +12 -0
- package/README.md +87 -66
- package/bin/agent.js +44 -18
- package/bin/cli.js +30 -28
- package/bin/dream.js +5 -11
- package/bin/loopback-client.js +29 -5
- package/bin/schedule.js +19 -5
- package/bin/tool.js +23 -5
- package/bot-agent.js +5 -2350
- package/bot.js +66 -3
- package/channels/telegram/adapter.js +6 -1
- package/channels/voice/adapter.js +1 -0
- package/channels/voice/manage.js +43 -5
- package/config-dir.js +3 -11
- package/core/actions.js +149 -40
- package/core/approvals.js +136 -29
- package/core/auth-flow.js +103 -19
- package/core/config-dir.js +19 -0
- package/core/config.js +115 -69
- package/core/doctor.js +111 -57
- package/core/dream.js +219 -62
- package/core/enforcer.js +0 -0
- package/core/entities.js +2 -1
- package/core/fsutil.js +21 -4
- package/core/handlers.js +503 -217
- package/core/ideas.js +2 -1
- package/core/jobs.js +589 -72
- package/core/lessons.js +3 -2
- package/core/loopback.js +42 -6
- package/core/memory-mutation-queue.js +241 -0
- package/core/migration-backup.js +1060 -0
- package/core/pack-review.js +295 -88
- package/core/packs.js +4 -3
- package/core/persona.js +2 -1
- package/core/process-tree.js +19 -2
- package/core/provider-migration.js +39 -0
- package/core/provider-status.js +80 -0
- package/core/providers/claude-events.js +117 -0
- package/core/providers/claude-hook.js +114 -0
- package/core/providers/claude.js +296 -0
- package/core/providers/codex-events.js +121 -0
- package/core/providers/codex-hook.js +280 -0
- package/core/providers/codex.js +286 -0
- package/core/providers/contract.js +153 -0
- package/core/providers/env.js +148 -0
- package/core/providers/events.js +170 -0
- package/core/providers/hook-command.js +22 -0
- package/core/providers/index.js +240 -0
- package/core/providers/utility-policy.js +269 -0
- package/core/recall/classic.js +2 -2
- package/core/recall/discoverer.js +71 -22
- package/core/recall/metrics.js +27 -1
- package/core/recall/tuning.js +4 -1
- package/core/recall/warm-walker.js +151 -108
- package/core/recall-filter.js +86 -61
- package/core/router.js +79 -7
- package/core/run-context.js +185 -0
- package/core/runner.js +1414 -1290
- package/core/scheduler.js +515 -210
- package/core/side-chat.js +346 -0
- package/core/state.js +1084 -97
- package/core/subagent.js +72 -98
- package/core/system-prompt.js +462 -279
- package/core/tool-guard.js +73 -39
- package/core/tools.js +22 -5
- package/core/transcripts.js +30 -1
- package/core/usage-log.js +19 -4
- package/core/utility-agent.js +654 -0
- package/core/web-auth.js +29 -13
- package/docs/CHANNEL_DESIGN.md +181 -0
- package/docs/MULTI_CHANNEL_PLAN.md +254 -0
- package/docs/PROVIDER_MIGRATION.md +67 -0
- package/health.js +50 -39
- package/package.json +48 -4
- package/setup.js +198 -38
- package/soul.md +39 -0
- package/test-ability-extraction.js +5 -0
- package/test-approval-async.js +63 -4
- package/test-cursor-removal.js +334 -0
- package/test-enforcer.js +70 -27
- package/test-fixtures/current-provider-parity.json +132 -0
- package/test-fixtures/fake-agent-cli.js +477 -0
- package/test-fixtures/migrations/claude-only/jobs.json +3 -0
- package/test-fixtures/migrations/claude-only/sessions.json +5 -0
- package/test-fixtures/migrations/claude-only/state.json +5 -0
- package/test-fixtures/migrations/cursor-selected/crons.json +3 -0
- package/test-fixtures/migrations/cursor-selected/jobs.json +3 -0
- package/test-fixtures/migrations/cursor-selected/sessions.json +5 -0
- package/test-fixtures/migrations/cursor-selected/state.json +6 -0
- package/test-fixtures/migrations/dual-provider/jobs.json +3 -0
- package/test-fixtures/migrations/dual-provider/sessions.json +7 -0
- package/test-fixtures/migrations/dual-provider/state.json +10 -0
- package/test-fixtures/migrations/malformed-partial/jobs.json +1 -0
- package/test-fixtures/migrations/malformed-partial/sessions.json +1 -0
- package/test-fixtures/migrations/malformed-partial/sessions.json.bak +3 -0
- package/test-fixtures/migrations/malformed-partial/state.json +1 -0
- package/test-fixtures/migrations/malformed-partial/state.json.bak +5 -0
- package/test-fixtures/migrations/missing-project/jobs.json +3 -0
- package/test-fixtures/migrations/missing-project/sessions.json +3 -0
- package/test-fixtures/migrations/missing-project/state.json +4 -0
- package/test-fixtures/migrations/multi-user/jobs.json +4 -0
- package/test-fixtures/migrations/multi-user/sessions.json +4 -0
- package/test-fixtures/migrations/multi-user/state.json +6 -0
- package/test-fixtures/provider-event-samples.json +17 -0
- package/test-learning-e2e.js +5 -0
- package/test-memory-mutation-queue.js +191 -0
- package/test-provider-boot-matrix.js +399 -0
- package/test-provider-bootstrap.js +158 -0
- package/test-provider-capabilities.js +232 -0
- package/test-provider-claude.js +206 -0
- package/test-provider-codex.js +228 -0
- package/test-provider-compaction.js +371 -0
- package/test-provider-core-prompt.js +264 -0
- package/test-provider-coupling-audit.js +90 -0
- package/test-provider-dream.js +312 -0
- package/test-provider-enforcer.js +252 -0
- package/test-provider-env-isolation.js +150 -0
- package/test-provider-events.js +141 -0
- package/test-provider-fixture.js +332 -0
- package/test-provider-gateway.js +508 -0
- package/test-provider-language.js +89 -0
- package/test-provider-migration-backup.js +424 -0
- package/test-provider-pack-review.js +436 -0
- package/test-provider-parity-e2e.js +537 -0
- package/test-provider-prompt-parity.js +89 -0
- package/test-provider-recall.js +271 -0
- package/test-provider-registry.js +251 -0
- package/test-provider-resume-parity.js +41 -0
- package/test-provider-run-lifecycle.js +349 -0
- package/test-provider-runner.js +271 -0
- package/test-provider-scheduler.js +689 -0
- package/test-provider-session-commands.js +185 -0
- package/test-provider-session-history.js +205 -0
- package/test-provider-side-chat.js +337 -0
- package/test-provider-state-migration.js +316 -0
- package/test-provider-stream-decoder.js +69 -0
- package/test-provider-subagent.js +228 -0
- package/test-provider-tool-hooks.js +360 -0
- package/test-recall-discoverer.js +1 -0
- package/test-recall-engine.js +3 -3
- package/test-recall-evolution.js +18 -0
- package/test-runner-watchdog-static.js +16 -8
- package/test-single-runtime.js +35 -0
- package/test-telegram-poll-recovery.js +56 -0
- package/test-tool-guard.js +56 -0
- package/test-tools.js +19 -0
- package/test-unified-identity.js +3 -1
- package/test-usage-accounting.js +92 -20
- package/test-utility-provider-policy.js +486 -0
- package/web.js +130 -35
package/health.js
CHANGED
|
@@ -3,6 +3,7 @@ const fs = require("fs");
|
|
|
3
3
|
const path = require("path");
|
|
4
4
|
const https = require("https");
|
|
5
5
|
const CONFIG_DIR = require("./config-dir");
|
|
6
|
+
const { inspectProviderConfiguration } = require("./core/provider-status");
|
|
6
7
|
|
|
7
8
|
const ENV_FILE = path.join(CONFIG_DIR, ".env");
|
|
8
9
|
const AUTH_FILE = path.join(CONFIG_DIR, "auth.json");
|
|
@@ -12,7 +13,6 @@ const REQUIRED_ENV_KEYS = [
|
|
|
12
13
|
"TELEGRAM_BOT_TOKEN",
|
|
13
14
|
"TELEGRAM_CHAT_ID",
|
|
14
15
|
"WORKSPACE",
|
|
15
|
-
"CLAUDE_PATH",
|
|
16
16
|
];
|
|
17
17
|
|
|
18
18
|
// Optional but recommended
|
|
@@ -60,24 +60,21 @@ function binaryExists(binPath) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
|
-
* Test
|
|
63
|
+
* Test Claude authentication using the provider's local auth-status command.
|
|
64
|
+
* This compatibility export deliberately never sends a model prompt.
|
|
64
65
|
*/
|
|
65
|
-
function testClaudeAuth(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
// Other errors (like timeout) are OK - Claude is accessible
|
|
79
|
-
return { ok: true };
|
|
80
|
-
}
|
|
66
|
+
function testClaudeAuth(_claudePath, options = {}) {
|
|
67
|
+
const summary = inspectProviderConfiguration({
|
|
68
|
+
registry: options.registry,
|
|
69
|
+
probeAuth: true,
|
|
70
|
+
});
|
|
71
|
+
const claude = summary.providers.find((provider) => provider.id === "claude");
|
|
72
|
+
if (claude?.auth.state === "authenticated") return { ok: true };
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
reason: claude?.auth.state || "not_checked",
|
|
76
|
+
message: claude?.recovery || "Claude Code authentication could not be confirmed.",
|
|
77
|
+
};
|
|
81
78
|
}
|
|
82
79
|
|
|
83
80
|
/**
|
|
@@ -228,12 +225,12 @@ function checkAuthFile() {
|
|
|
228
225
|
/**
|
|
229
226
|
* Run all health checks
|
|
230
227
|
* @param {Object} options - Options
|
|
231
|
-
* @param {boolean} options.quick - Skip
|
|
228
|
+
* @param {boolean} options.quick - Skip auth and external channel checks
|
|
232
229
|
* @param {boolean} options.fix - Attempt to fix issues (create directories)
|
|
233
230
|
* @returns {Promise<Object>} Health check results
|
|
234
231
|
*/
|
|
235
232
|
async function runHealthChecks(options = {}) {
|
|
236
|
-
const { quick = false, fix = false } = options;
|
|
233
|
+
const { quick = false, fix = false, skipNetwork = false } = options;
|
|
237
234
|
const results = {
|
|
238
235
|
ok: true,
|
|
239
236
|
checks: {},
|
|
@@ -267,27 +264,41 @@ async function runHealthChecks(options = {}) {
|
|
|
267
264
|
results.checks.env = { ok: true };
|
|
268
265
|
}
|
|
269
266
|
|
|
270
|
-
// 3.
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
267
|
+
// 3. Provider availability, compatibility, and authentication. Provider
|
|
268
|
+
// adapters use only local --version/help/auth-status commands here.
|
|
269
|
+
const providerSummary = inspectProviderConfiguration({
|
|
270
|
+
registry: options.registry,
|
|
271
|
+
probeAuth: !quick,
|
|
272
|
+
});
|
|
273
|
+
results.providerSummary = providerSummary;
|
|
274
|
+
for (const provider of providerSummary.providers) {
|
|
275
|
+
const compatible = provider.availability.state === "available"
|
|
276
|
+
&& provider.compatibility.state === "compatible";
|
|
277
|
+
const authenticated = quick || provider.auth.state === "authenticated";
|
|
278
|
+
const ok = compatible && authenticated;
|
|
279
|
+
const key = `provider_${provider.id}`;
|
|
280
|
+
results.checks[key] = {
|
|
281
|
+
ok,
|
|
282
|
+
warn: !ok,
|
|
283
|
+
availability: provider.availability.state,
|
|
284
|
+
compatibility: provider.compatibility.state,
|
|
285
|
+
auth: provider.auth.state,
|
|
286
|
+
message: provider.recovery,
|
|
287
|
+
};
|
|
288
|
+
if (!ok && provider.recovery) results.warnings.push(`${provider.label}: ${provider.recovery.split("\n")[0]}`);
|
|
289
|
+
}
|
|
290
|
+
results.checks.default_provider = {
|
|
291
|
+
ok: !!providerSummary.defaultProvider.providerId,
|
|
292
|
+
warn: !providerSummary.defaultProvider.providerId,
|
|
293
|
+
providerId: providerSummary.defaultProvider.providerId,
|
|
294
|
+
message: providerSummary.defaultProvider.reason,
|
|
295
|
+
};
|
|
296
|
+
if (!providerSummary.defaultProvider.providerId) {
|
|
297
|
+
results.warnings.push(`${providerSummary.defaultProvider.reason}. Model turns are unavailable until a provider is configured.`);
|
|
287
298
|
}
|
|
288
299
|
|
|
289
|
-
//
|
|
290
|
-
if (!quick && env.TELEGRAM_BOT_TOKEN) {
|
|
300
|
+
// 4. Check Telegram token (external network; never part of provider health).
|
|
301
|
+
if (!quick && !skipNetwork && env.TELEGRAM_BOT_TOKEN) {
|
|
291
302
|
results.checks.telegram = await testTelegramToken(env.TELEGRAM_BOT_TOKEN);
|
|
292
303
|
if (!results.checks.telegram.ok) {
|
|
293
304
|
results.ok = false;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inetafrica/open-claudia",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "An always-on, provider-agnostic coding-agent harness for Claude Code and OpenAI Codex via chat",
|
|
5
5
|
"main": "bot.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"open-claudia": "./bin/cli.js"
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"setup": "node setup.js",
|
|
11
11
|
"start": "node bot.js",
|
|
12
|
-
"
|
|
12
|
+
"pretest": "node test-provider-fixture.js && node test-provider-core-prompt.js && node test-provider-prompt-parity.js && node test-provider-env-isolation.js && node test-provider-resume-parity.js && node test-provider-bootstrap.js && node test-provider-registry.js && node test-provider-stream-decoder.js && node test-provider-events.js && node test-provider-claude.js && node test-provider-codex.js && node test-provider-runner.js && node test-provider-run-lifecycle.js && node test-provider-gateway.js && node test-provider-side-chat.js && node test-single-runtime.js && node test-provider-migration-backup.js && node test-provider-state-migration.js && node test-provider-session-history.js && node test-provider-session-commands.js && node test-provider-compaction.js && node test-provider-scheduler.js && node test-cursor-removal.js && node test-utility-provider-policy.js && node test-provider-subagent.js && node test-provider-recall.js && node test-provider-pack-review.js && node test-memory-mutation-queue.js && node test-provider-dream.js && node test-provider-enforcer.js && node test-provider-tool-hooks.js && node test-provider-boot-matrix.js && node test-provider-capabilities.js && node test-provider-language.js && node test-provider-coupling-audit.js && node test-provider-parity-e2e.js",
|
|
13
|
+
"test": "OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node -e \"require('./vault'); console.log('OK')\" && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-runner-watchdog-static.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-usage-accounting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-engine.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-discoverer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-project-transcripts-smoke.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-abilities.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-extraction.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-couse.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-transfer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-tiers.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-ability-merge-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-learning-e2e.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-pack-nesting.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-journal-dedupe.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-read-signal.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tools.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-graph.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-guard.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tooling-mode.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-tool-manifest.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-evolution.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-approval-async.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-unified-identity.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-queue-routing.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-packs.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-speaker-persona.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-persona-pipeline.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-recall-relationship-gate.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-relationship.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-enforcer.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-identity-prune.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-kazee-message-id.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-delivery-contract.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-run-lock.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-web-sessions.js && OPEN_CLAUDIA_TEST=1 WORKSPACE=/tmp/open-claudia-test CLAUDE_PATH=./test-fixtures/fake-agent-cli.js FAKE_AGENT_KIND=claude AGENT_ENV_PASSTHROUGH=FAKE_AGENT_KIND node test-telegram-poll-recovery.js"
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
16
|
"bot.js",
|
|
@@ -30,6 +31,8 @@
|
|
|
30
31
|
".env.example",
|
|
31
32
|
"README.md",
|
|
32
33
|
"CHANGELOG.md",
|
|
34
|
+
"soul.md",
|
|
35
|
+
"docs/",
|
|
33
36
|
"test-runner-watchdog-static.js",
|
|
34
37
|
"test-usage-accounting.js",
|
|
35
38
|
"test-recall-engine.js",
|
|
@@ -64,10 +67,51 @@
|
|
|
64
67
|
"test-kazee-message-id.js",
|
|
65
68
|
"test-delivery-contract.js",
|
|
66
69
|
"test-run-lock.js",
|
|
67
|
-
"test-web-sessions.js"
|
|
70
|
+
"test-web-sessions.js",
|
|
71
|
+
"test-provider-fixture.js",
|
|
72
|
+
"test-provider-core-prompt.js",
|
|
73
|
+
"test-provider-prompt-parity.js",
|
|
74
|
+
"test-provider-env-isolation.js",
|
|
75
|
+
"test-provider-resume-parity.js",
|
|
76
|
+
"test-provider-bootstrap.js",
|
|
77
|
+
"test-provider-registry.js",
|
|
78
|
+
"test-provider-stream-decoder.js",
|
|
79
|
+
"test-provider-events.js",
|
|
80
|
+
"test-provider-claude.js",
|
|
81
|
+
"test-provider-codex.js",
|
|
82
|
+
"test-provider-runner.js",
|
|
83
|
+
"test-provider-run-lifecycle.js",
|
|
84
|
+
"test-provider-gateway.js",
|
|
85
|
+
"test-provider-side-chat.js",
|
|
86
|
+
"test-single-runtime.js",
|
|
87
|
+
"test-provider-migration-backup.js",
|
|
88
|
+
"test-provider-state-migration.js",
|
|
89
|
+
"test-provider-session-history.js",
|
|
90
|
+
"test-provider-session-commands.js",
|
|
91
|
+
"test-provider-compaction.js",
|
|
92
|
+
"test-provider-scheduler.js",
|
|
93
|
+
"test-cursor-removal.js",
|
|
94
|
+
"test-utility-provider-policy.js",
|
|
95
|
+
"test-provider-subagent.js",
|
|
96
|
+
"test-provider-recall.js",
|
|
97
|
+
"test-provider-pack-review.js",
|
|
98
|
+
"test-memory-mutation-queue.js",
|
|
99
|
+
"test-provider-dream.js",
|
|
100
|
+
"test-provider-enforcer.js",
|
|
101
|
+
"test-provider-tool-hooks.js",
|
|
102
|
+
"test-provider-boot-matrix.js",
|
|
103
|
+
"test-provider-capabilities.js",
|
|
104
|
+
"test-provider-language.js",
|
|
105
|
+
"test-provider-coupling-audit.js",
|
|
106
|
+
"test-provider-parity-e2e.js",
|
|
107
|
+
"test-telegram-poll-recovery.js",
|
|
108
|
+
"test-fixtures/"
|
|
68
109
|
],
|
|
69
110
|
"keywords": [
|
|
70
111
|
"claude",
|
|
112
|
+
"codex",
|
|
113
|
+
"coding-agent",
|
|
114
|
+
"provider-agnostic",
|
|
71
115
|
"telegram",
|
|
72
116
|
"ai",
|
|
73
117
|
"assistant",
|
package/setup.js
CHANGED
|
@@ -8,6 +8,12 @@ const crypto = require("crypto");
|
|
|
8
8
|
const { execSync } = require("child_process");
|
|
9
9
|
const Vault = require("./vault");
|
|
10
10
|
const CONFIG_DIR = require("./config-dir");
|
|
11
|
+
const { ensureConfigDir } = require("./core/config-dir");
|
|
12
|
+
const { atomicWriteFileSync } = require("./core/fsutil");
|
|
13
|
+
const { inspectProviderConfiguration } = require("./core/provider-status");
|
|
14
|
+
const { ProviderRegistry } = require("./core/providers");
|
|
15
|
+
const { createClaudeProvider } = require("./core/providers/claude");
|
|
16
|
+
const { createCodexProvider } = require("./core/providers/codex");
|
|
11
17
|
|
|
12
18
|
const ENV_FILE = path.join(CONFIG_DIR, ".env");
|
|
13
19
|
const VAULT_FILE = path.join(CONFIG_DIR, "vault.enc");
|
|
@@ -16,8 +22,12 @@ const CRONS_FILE = path.join(CONFIG_DIR, "crons.json");
|
|
|
16
22
|
const AUTH_FILE = path.join(CONFIG_DIR, "auth.json");
|
|
17
23
|
const SETUP_STATE_FILE = path.join(CONFIG_DIR, ".setup-state.json");
|
|
18
24
|
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
let rl = null;
|
|
26
|
+
function promptInterface() {
|
|
27
|
+
if (!rl) rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
28
|
+
return rl;
|
|
29
|
+
}
|
|
30
|
+
const ask = (q) => new Promise((r) => promptInterface().question(q, r));
|
|
21
31
|
|
|
22
32
|
// ── Auth file helpers ──────────────────────────────────────────────
|
|
23
33
|
|
|
@@ -29,7 +39,7 @@ function loadAuth() {
|
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
function saveAuth(auth) {
|
|
32
|
-
|
|
42
|
+
atomicWriteFileSync(AUTH_FILE, JSON.stringify(auth, null, 2), { backup: true, mode: 0o600 });
|
|
33
43
|
}
|
|
34
44
|
|
|
35
45
|
// ── Setup state helpers ────────────────────────────────────────────
|
|
@@ -42,13 +52,25 @@ function loadSetupState() {
|
|
|
42
52
|
}
|
|
43
53
|
|
|
44
54
|
function saveSetupState(state) {
|
|
45
|
-
|
|
55
|
+
atomicWriteFileSync(SETUP_STATE_FILE, JSON.stringify(state, null, 2), { backup: true, mode: 0o600 });
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
function clearSetupState() {
|
|
49
59
|
if (fs.existsSync(SETUP_STATE_FILE)) fs.unlinkSync(SETUP_STATE_FILE);
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
function upgradeSetupState(value) {
|
|
63
|
+
const state = value && typeof value === "object"
|
|
64
|
+
? { ...value, completedSteps: [...(value.completedSteps || [])], data: { ...(value.data || {}) } }
|
|
65
|
+
: { completedSteps: [], data: {} };
|
|
66
|
+
if (state.completedSteps.includes("prerequisites")
|
|
67
|
+
&& (!Object.prototype.hasOwnProperty.call(state.data, "codexPath")
|
|
68
|
+
|| !Object.prototype.hasOwnProperty.call(state.data, "defaultProvider"))) {
|
|
69
|
+
state.completedSteps = state.completedSteps.filter((step) => step !== "prerequisites");
|
|
70
|
+
}
|
|
71
|
+
return state;
|
|
72
|
+
}
|
|
73
|
+
|
|
52
74
|
// ── Telegram helpers ───────────────────────────────────────────────
|
|
53
75
|
|
|
54
76
|
function telegramGet(token, method, params = "") {
|
|
@@ -147,9 +169,56 @@ function findBinary(name) {
|
|
|
147
169
|
}
|
|
148
170
|
|
|
149
171
|
function findClaude() { return findBinary("claude"); }
|
|
172
|
+
function findCodex() { return findBinary("codex"); }
|
|
150
173
|
function findWhisper() { return findBinary("whisper-cli"); }
|
|
151
174
|
function findFfmpeg() { return findBinary("ffmpeg"); }
|
|
152
175
|
|
|
176
|
+
function inspectProviderSetup(options = {}) {
|
|
177
|
+
if (!options.paths) return inspectProviderConfiguration(options);
|
|
178
|
+
const resolved = {};
|
|
179
|
+
for (const providerId of ["claude", "codex"]) {
|
|
180
|
+
const value = String(options.paths[providerId] || "").trim();
|
|
181
|
+
if (!value) {
|
|
182
|
+
resolved[providerId] = null;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
fs.accessSync(value, fs.constants.X_OK);
|
|
187
|
+
resolved[providerId] = path.resolve(value);
|
|
188
|
+
} catch (_) {
|
|
189
|
+
resolved[providerId] = null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const registry = new ProviderRegistry({ defaultProvider: options.defaultProvider || null });
|
|
193
|
+
registry.registerProvider(createClaudeProvider({ resolveExecutable: () => resolved.claude }));
|
|
194
|
+
registry.registerProvider(createCodexProvider({ resolveExecutable: () => resolved.codex }));
|
|
195
|
+
return inspectProviderConfiguration({
|
|
196
|
+
...options,
|
|
197
|
+
registry,
|
|
198
|
+
executableStatus(providerId) {
|
|
199
|
+
const configured = String(options.paths[providerId] || "").trim();
|
|
200
|
+
if (resolved[providerId]) {
|
|
201
|
+
return { state: "available", executable: resolved[providerId], configured: true, reason: null };
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
state: configured ? "missing" : "unconfigured",
|
|
205
|
+
executable: null,
|
|
206
|
+
configured: !!configured,
|
|
207
|
+
reason: configured
|
|
208
|
+
? `Configured ${providerId} executable was not found`
|
|
209
|
+
: `${providerId} is not configured or installed`,
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function providerStatusLine(provider) {
|
|
216
|
+
const availability = provider.availability.state;
|
|
217
|
+
if (availability !== "available") return `${provider.label}: ${availability}`;
|
|
218
|
+
const version = provider.compatibility.version ? ` ${provider.compatibility.version}` : "";
|
|
219
|
+
return `${provider.label}: ${provider.compatibility.state}${version}; auth ${provider.auth.state}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
153
222
|
function findWhisperModel() {
|
|
154
223
|
const candidates = [
|
|
155
224
|
"/opt/homebrew/share/whisper-cpp/models/ggml-base.en.bin",
|
|
@@ -285,7 +354,7 @@ async function setupDaemon(platform) {
|
|
|
285
354
|
const serviceName = "claude-telegram-bot";
|
|
286
355
|
const serviceFile = `/etc/systemd/system/${serviceName}.service`;
|
|
287
356
|
const unit = `[Unit]
|
|
288
|
-
Description=
|
|
357
|
+
Description=Open Claudia Provider Bot
|
|
289
358
|
After=network.target
|
|
290
359
|
|
|
291
360
|
[Service]
|
|
@@ -483,7 +552,7 @@ function updateEnvKey(key, value) {
|
|
|
483
552
|
return line;
|
|
484
553
|
});
|
|
485
554
|
if (!found) updated.push(`${key}=${value}`);
|
|
486
|
-
|
|
555
|
+
atomicWriteFileSync(ENV_FILE, updated.join("\n"), { backup: true, mode: 0o600 });
|
|
487
556
|
}
|
|
488
557
|
|
|
489
558
|
// ── Main setup (resumable) ─────────────────────────────────────────
|
|
@@ -497,6 +566,7 @@ const { validateKazee } = require("./core/kazee-probe");
|
|
|
497
566
|
const KAZEE_DEFAULT_URL = "https://chat.inet.africa";
|
|
498
567
|
|
|
499
568
|
async function main() {
|
|
569
|
+
ensureConfigDir(CONFIG_DIR);
|
|
500
570
|
// Check if this is an auth subcommand
|
|
501
571
|
const args = process.argv.slice(2);
|
|
502
572
|
if (args.includes("--auth") || args.includes("auth")) {
|
|
@@ -504,10 +574,12 @@ async function main() {
|
|
|
504
574
|
return;
|
|
505
575
|
}
|
|
506
576
|
|
|
507
|
-
console.log("\n
|
|
577
|
+
console.log("\n Open Claudia — Setup\n");
|
|
508
578
|
|
|
509
579
|
// Load or create state
|
|
510
|
-
|
|
580
|
+
// Resume files from the Claude-only wizard must pass through the provider
|
|
581
|
+
// step once before the new config keys can be serialized safely.
|
|
582
|
+
let state = upgradeSetupState(loadSetupState());
|
|
511
583
|
const resuming = state.completedSteps.length > 0;
|
|
512
584
|
|
|
513
585
|
if (resuming) {
|
|
@@ -525,27 +597,71 @@ async function main() {
|
|
|
525
597
|
if (!state.completedSteps.includes("prerequisites")) {
|
|
526
598
|
console.log("Checking prerequisites...\n");
|
|
527
599
|
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
console.log(
|
|
531
|
-
console.log(
|
|
532
|
-
process.exit(1);
|
|
600
|
+
const providerReport = inspectProviderSetup();
|
|
601
|
+
for (const provider of providerReport.providers) {
|
|
602
|
+
console.log(` ${providerStatusLine(provider)}`);
|
|
603
|
+
if (provider.recovery) console.log(` Recovery: ${provider.recovery.split("\n")[0]}`);
|
|
533
604
|
}
|
|
534
|
-
console.log(
|
|
605
|
+
console.log("");
|
|
535
606
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
607
|
+
const autoProviders = providerReport.providers
|
|
608
|
+
.filter((provider) => provider.availability.state === "available"
|
|
609
|
+
&& provider.compatibility.state === "compatible")
|
|
610
|
+
.map((provider) => provider.id);
|
|
611
|
+
const choice = (await ask(" Providers to configure (auto/claude/codex/both/none) [auto]: "))
|
|
612
|
+
.trim().toLowerCase() || "auto";
|
|
613
|
+
const wanted = choice === "none" ? []
|
|
614
|
+
: choice === "both" ? ["claude", "codex"]
|
|
615
|
+
: choice === "claude" || choice === "codex" ? [choice]
|
|
616
|
+
: autoProviders;
|
|
617
|
+
const detectedById = Object.fromEntries(providerReport.providers.map((provider) => [
|
|
618
|
+
provider.id,
|
|
619
|
+
provider.availability.executable || null,
|
|
620
|
+
]));
|
|
621
|
+
const providerPaths = { claude: null, codex: null };
|
|
622
|
+
for (const providerId of wanted) {
|
|
623
|
+
let detected = detectedById[providerId]
|
|
624
|
+
|| state.data?.[providerId === "claude" ? "claudePath" : "codexPath"]
|
|
625
|
+
|| (providerId === "claude" ? findClaude() : findCodex());
|
|
626
|
+
if (!detected) {
|
|
627
|
+
const envKey = providerId === "claude" ? "CLAUDE_PATH" : "CODEX_PATH";
|
|
628
|
+
const entered = (await ask(` ${envKey} (blank to leave unconfigured): `)).trim();
|
|
629
|
+
if (entered) {
|
|
630
|
+
try {
|
|
631
|
+
fs.accessSync(entered, fs.constants.X_OK);
|
|
632
|
+
detected = path.resolve(entered);
|
|
633
|
+
} catch (_) {
|
|
634
|
+
console.log(` ${entered} is not executable; ${providerId} remains unconfigured.`);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
providerPaths[providerId] = detected || null;
|
|
639
|
+
}
|
|
640
|
+
const finalProviderReport = inspectProviderSetup({ paths: providerPaths });
|
|
641
|
+
for (const provider of finalProviderReport.providers) {
|
|
642
|
+
if (provider.availability.state === "available" && provider.compatibility.state !== "compatible") {
|
|
643
|
+
console.log(` ${providerStatusLine(provider)}`);
|
|
644
|
+
if (provider.recovery) console.log(` Recovery: ${provider.recovery.split("\n")[0]}`);
|
|
547
645
|
}
|
|
548
|
-
|
|
646
|
+
}
|
|
647
|
+
const configuredProviders = finalProviderReport.providers
|
|
648
|
+
.filter((provider) => provider.availability.state === "available"
|
|
649
|
+
&& provider.compatibility.state === "compatible")
|
|
650
|
+
.map((provider) => provider.id);
|
|
651
|
+
let defaultProvider = configuredProviders[0] || null;
|
|
652
|
+
if (configuredProviders.length > 1) {
|
|
653
|
+
const reportedDefault = providerReport.defaultProvider.providerId;
|
|
654
|
+
const suggestedDefault = configuredProviders.includes(reportedDefault)
|
|
655
|
+
? reportedDefault
|
|
656
|
+
: configuredProviders[0];
|
|
657
|
+
const answer = (await ask(` Default provider (claude/codex) [${suggestedDefault}]: `))
|
|
658
|
+
.trim().toLowerCase();
|
|
659
|
+
const proposed = answer || suggestedDefault;
|
|
660
|
+
defaultProvider = configuredProviders.includes(proposed) ? proposed : configuredProviders[0];
|
|
661
|
+
}
|
|
662
|
+
if (!configuredProviders.length) {
|
|
663
|
+
console.log(" No model provider configured. Setup, doctor, health, and the web UI will still work;");
|
|
664
|
+
console.log(" model turns will return NO_AVAILABLE_PROVIDER until Claude Code or OpenAI Codex is configured.\n");
|
|
549
665
|
}
|
|
550
666
|
|
|
551
667
|
const platform = detectPlatform();
|
|
@@ -592,7 +708,9 @@ async function main() {
|
|
|
592
708
|
}
|
|
593
709
|
}
|
|
594
710
|
|
|
595
|
-
state.data.claudePath =
|
|
711
|
+
state.data.claudePath = providerPaths.claude || "";
|
|
712
|
+
state.data.codexPath = providerPaths.codex || "";
|
|
713
|
+
state.data.defaultProvider = defaultProvider || "";
|
|
596
714
|
state.data.platform = platform;
|
|
597
715
|
state.data.ffmpegPath = ffmpegPath || "";
|
|
598
716
|
state.data.whisperPath = whisperPath || "";
|
|
@@ -648,7 +766,7 @@ async function main() {
|
|
|
648
766
|
|
|
649
767
|
// Send confirmation
|
|
650
768
|
console.log(" Sending test message...");
|
|
651
|
-
const sent = await sendMessage(token, userInfo.chatId, "Setup verified! Your
|
|
769
|
+
const sent = await sendMessage(token, userInfo.chatId, "Setup verified! Your Open Claudia bot is connected.");
|
|
652
770
|
if (sent) {
|
|
653
771
|
console.log(" Test message sent! Check your Telegram.\n");
|
|
654
772
|
} else {
|
|
@@ -747,6 +865,15 @@ async function main() {
|
|
|
747
865
|
`TELEGRAM_CHAT_ID=${d.chatId}`,
|
|
748
866
|
`WORKSPACE=${d.workspace}`,
|
|
749
867
|
`CLAUDE_PATH=${d.claudePath}`,
|
|
868
|
+
`CODEX_PATH=${d.codexPath}`,
|
|
869
|
+
`DEFAULT_PROVIDER=${d.defaultProvider}`,
|
|
870
|
+
"UTILITY_PROVIDER=active",
|
|
871
|
+
"PROVIDER_FALLBACKS=",
|
|
872
|
+
"DREAM_PROVIDER=",
|
|
873
|
+
"MEMORY_PROVIDER=",
|
|
874
|
+
"RECALL_PROVIDER=",
|
|
875
|
+
"ENFORCER_PROVIDER=",
|
|
876
|
+
"SUBAGENT_PROVIDER=",
|
|
750
877
|
`WHISPER_CLI=${d.whisperPath}`,
|
|
751
878
|
`WHISPER_MODEL=${d.whisperModel}`,
|
|
752
879
|
`FFMPEG=${d.ffmpegPath}`,
|
|
@@ -763,9 +890,30 @@ async function main() {
|
|
|
763
890
|
envLines.push(`KAZEE_BOT_USER_ID=${d.kazee.botUserId || ""}`);
|
|
764
891
|
envLines.push(`KAZEE_OWNER_USER_ID=${d.kazee.ownerUserId || ""}`);
|
|
765
892
|
}
|
|
766
|
-
const
|
|
767
|
-
|
|
768
|
-
|
|
893
|
+
const existingEnv = fs.existsSync(ENV_FILE)
|
|
894
|
+
? fs.readFileSync(ENV_FILE, "utf8").split(/\r?\n/).reduce((result, line) => {
|
|
895
|
+
const index = line.indexOf("=");
|
|
896
|
+
if (index > 0) result[line.slice(0, index).trim()] = line.slice(index + 1).trim();
|
|
897
|
+
return result;
|
|
898
|
+
}, {})
|
|
899
|
+
: {};
|
|
900
|
+
const mergedEnv = { ...existingEnv };
|
|
901
|
+
const preserveExistingPolicy = new Set([
|
|
902
|
+
"UTILITY_PROVIDER", "PROVIDER_FALLBACKS", "DREAM_PROVIDER", "MEMORY_PROVIDER",
|
|
903
|
+
"RECALL_PROVIDER", "ENFORCER_PROVIDER", "SUBAGENT_PROVIDER",
|
|
904
|
+
]);
|
|
905
|
+
for (const line of envLines) {
|
|
906
|
+
const index = line.indexOf("=");
|
|
907
|
+
const key = line.slice(0, index);
|
|
908
|
+
const value = line.slice(index + 1);
|
|
909
|
+
// A resumed/second setup must never erase the other provider's existing
|
|
910
|
+
// credential/config merely because its prompt was left blank.
|
|
911
|
+
if (preserveExistingPolicy.has(key) && Object.prototype.hasOwnProperty.call(existingEnv, key)) continue;
|
|
912
|
+
if (!value && Object.prototype.hasOwnProperty.call(existingEnv, key)) continue;
|
|
913
|
+
mergedEnv[key] = value;
|
|
914
|
+
}
|
|
915
|
+
const env = Object.entries(mergedEnv).map(([key, value]) => `${key}=${value}`).join("\n");
|
|
916
|
+
atomicWriteFileSync(ENV_FILE, env, { backup: true, mode: 0o600 });
|
|
769
917
|
console.log(` Config saved: ${ENV_FILE}\n`);
|
|
770
918
|
|
|
771
919
|
if (!fs.existsSync(CRONS_FILE)) fs.writeFileSync(CRONS_FILE, "[]");
|
|
@@ -794,13 +942,25 @@ async function main() {
|
|
|
794
942
|
// Done — clean up state file
|
|
795
943
|
clearSetupState();
|
|
796
944
|
console.log("\n Setup complete! Start chatting with your bot in Telegram.\n");
|
|
797
|
-
rl.close();
|
|
945
|
+
if (rl) rl.close();
|
|
798
946
|
}
|
|
799
947
|
|
|
800
948
|
// Allow running auth directly: node setup.js auth
|
|
801
|
-
module.exports = {
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
949
|
+
module.exports = {
|
|
950
|
+
runAuth,
|
|
951
|
+
loadAuth,
|
|
952
|
+
saveAuth,
|
|
953
|
+
AUTH_FILE,
|
|
954
|
+
inspectProviderSetup,
|
|
955
|
+
providerStatusLine,
|
|
956
|
+
upgradeSetupState,
|
|
957
|
+
main,
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
if (require.main === module) {
|
|
961
|
+
main().catch((e) => {
|
|
962
|
+
if (rl) rl.close();
|
|
963
|
+
console.error(e);
|
|
964
|
+
process.exit(1);
|
|
965
|
+
});
|
|
966
|
+
}
|
package/soul.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Soul
|
|
2
|
+
|
|
3
|
+
You are Sumeet's personal AI assistant: Open Claudia running the selected coding-agent provider, available 24/7 through his configured chat channels.
|
|
4
|
+
|
|
5
|
+
## Identity
|
|
6
|
+
- Name: Open Claudia
|
|
7
|
+
- Tone: Direct, concise, technical. Like a sharp senior engineer who's also a good friend.
|
|
8
|
+
- Never over-explain. Sumeet is technical — he gets it.
|
|
9
|
+
- Mobile-first: keep messages short. Send files for anything long.
|
|
10
|
+
|
|
11
|
+
## About Sumeet
|
|
12
|
+
- Full-stack developer / founder
|
|
13
|
+
- Works across many projects in ~/Workspace
|
|
14
|
+
- Tech stack: Node.js, React/Next.js, MongoDB, Kubernetes, Docker
|
|
15
|
+
- Uses Cursor IDE and Claude Code CLI daily
|
|
16
|
+
- Communicates via Telegram on mobile when away from desk
|
|
17
|
+
|
|
18
|
+
## Key Projects
|
|
19
|
+
- **crm** — Kazee CRM platform (monorepo: crm-backend, frontend, helpdesk-frontend, spaces-backend, spaces-frontend, workflow-kanban). MongoDB, Node.js backend, Next.js frontends.
|
|
20
|
+
- **metriq / metriq-api** — Metrics/analytics platform
|
|
21
|
+
- **ticket-central** — Ticketing system
|
|
22
|
+
- **hr-hub** — HR management
|
|
23
|
+
- **kazee-connect-subscription-service** — Subscription/billing service
|
|
24
|
+
- **k8s / kubernetes-applications / kazee-infratructure** — Kubernetes deployment configs
|
|
25
|
+
- **janus-devops / janus-webrtc** — WebRTC infrastructure
|
|
26
|
+
|
|
27
|
+
## Working Style
|
|
28
|
+
- Prefers action over discussion. Do the thing, then explain what you did.
|
|
29
|
+
- Likes plans for big changes, but hates over-planning small ones.
|
|
30
|
+
- Appreciates when you proactively flag issues or suggest improvements.
|
|
31
|
+
- Send files directly via Telegram when output is long.
|
|
32
|
+
|
|
33
|
+
## What You Can Do
|
|
34
|
+
- Write, edit, and refactor code across all projects
|
|
35
|
+
- Run commands, tests, builds, deployments
|
|
36
|
+
- Send files, images, code snippets directly to Telegram
|
|
37
|
+
- Run scheduled checks and report results
|
|
38
|
+
- Monitor git status, PRs, deployments
|
|
39
|
+
- Help with architecture decisions and code reviews
|
|
@@ -86,6 +86,11 @@ const reused = review.applyAction({
|
|
|
86
86
|
});
|
|
87
87
|
assert.strictEqual(reused.appliedTo, "chat-mobile", "update records the cross-project reuse");
|
|
88
88
|
assert.deepStrictEqual(packs.readPack("mobile-app-deploy").applied_on, ["spaces", "chat-mobile"], "applied_on grew");
|
|
89
|
+
const abilityFile = path.join(process.env.PACKS_DIR, "mobile-app-deploy", "PACK.md");
|
|
90
|
+
assert.ok(fs.existsSync(`${abilityFile}.bak`), "reviewer writes retain the previous complete pack backup");
|
|
91
|
+
assert.strictEqual(fs.statSync(abilityFile).mode & 0o777, 0o600, "reviewer memory files stay owner-only");
|
|
92
|
+
assert.deepStrictEqual(fs.readdirSync(path.dirname(abilityFile)).filter((name) => name.endsWith(".tmp")), [],
|
|
93
|
+
"atomic reviewer writes leave no partial temporary file");
|
|
89
94
|
|
|
90
95
|
graph.syncFromCorpus(packs, entStub);
|
|
91
96
|
edges = graph.allEdges();
|