arkaos 4.33.0 → 4.35.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/THE-ARKAOS-GUIDE.md +1 -1
- package/VERSION +1 -1
- package/bin/arka-menubar.py +407 -0
- package/config/hooks/session-start.sh +1 -1
- package/config/install-profiles.json +75 -0
- package/config/statusline.sh +6 -1
- package/core/hooks/session_start.py +69 -28
- package/core/runtime/opencode.py +113 -0
- package/core/runtime/registry.py +6 -1
- package/harness/codex/AGENTS.md +1 -1
- package/harness/copilot/copilot-instructions.md +1 -1
- package/harness/cursor/rules/arkaos.mdc +2 -2
- package/harness/gemini/GEMINI.md +1 -1
- package/harness/opencode/AGENTS.md +1 -1
- package/harness/opencode/agents/arka-architect-gabriel.md +12 -0
- package/harness/opencode/agents/arka-brand-director-valentina.md +12 -0
- package/harness/opencode/agents/arka-cfo-helena.md +12 -0
- package/harness/opencode/agents/arka-chief-of-staff-afonso.md +12 -0
- package/harness/opencode/agents/arka-community-strategist-beatriz.md +12 -0
- package/harness/opencode/agents/arka-content-strategist-rafael.md +12 -0
- package/harness/opencode/agents/arka-conversion-strategist-ines.md +12 -0
- package/harness/opencode/agents/arka-coo-sofia.md +12 -0
- package/harness/opencode/agents/arka-copy-director-eduardo.md +12 -0
- package/harness/opencode/agents/arka-cqo-marta.md +12 -0
- package/harness/opencode/agents/arka-cto-marco.md +12 -0
- package/harness/opencode/agents/arka-design-ops-lead-iris.md +12 -0
- package/harness/opencode/agents/arka-ecom-director-ricardo.md +12 -0
- package/harness/opencode/agents/arka-knowledge-director-clara.md +12 -0
- package/harness/opencode/agents/arka-leadership-director-rodrigo.md +12 -0
- package/harness/opencode/agents/arka-marketing-director-luna.md +12 -0
- package/harness/opencode/agents/arka-ops-lead-daniel.md +12 -0
- package/harness/opencode/agents/arka-pm-director-carolina.md +12 -0
- package/harness/opencode/agents/arka-revops-lead-vicente.md +12 -0
- package/harness/opencode/agents/arka-saas-strategist-tiago.md +12 -0
- package/harness/opencode/agents/arka-sales-director-miguel.md +12 -0
- package/harness/opencode/agents/arka-strategy-director-tomas.md +12 -0
- package/harness/opencode/agents/arka-tech-director-francisca.md +12 -0
- package/harness/opencode/agents/arka-tech-lead-paulo.md +12 -0
- package/harness/opencode/agents/arka-video-producer-simao.md +12 -0
- package/harness/opencode/commands/arka-brand.md +9 -0
- package/harness/opencode/commands/arka-community.md +9 -0
- package/harness/opencode/commands/arka-content.md +9 -0
- package/harness/opencode/commands/arka-dev.md +9 -0
- package/harness/opencode/commands/arka-ecom.md +9 -0
- package/harness/opencode/commands/arka-fin.md +9 -0
- package/harness/opencode/commands/arka-kb.md +9 -0
- package/harness/opencode/commands/arka-landing.md +9 -0
- package/harness/opencode/commands/arka-lead.md +9 -0
- package/harness/opencode/commands/arka-mkt.md +9 -0
- package/harness/opencode/commands/arka-ops.md +9 -0
- package/harness/opencode/commands/arka-org.md +9 -0
- package/harness/opencode/commands/arka-pm.md +9 -0
- package/harness/opencode/commands/arka-saas.md +9 -0
- package/harness/opencode/commands/arka-sales.md +9 -0
- package/harness/opencode/commands/arka-strat.md +9 -0
- package/harness/opencode/opencode.json +16 -0
- package/harness/zed/.rules +1 -1
- package/installer/adapters/opencode.js +118 -0
- package/installer/autoupdate.js +249 -0
- package/installer/cli.js +38 -1
- package/installer/core-snapshot.js +5 -1
- package/installer/detect-runtime.js +18 -0
- package/installer/doctor.js +213 -2
- package/installer/index.js +182 -85
- package/installer/menubar.js +262 -0
- package/installer/product-stats.js +93 -0
- package/installer/profile.js +75 -0
- package/installer/prompts.js +303 -41
- package/installer/services.js +487 -0
- package/installer/ui.js +190 -0
- package/installer/update.js +219 -91
- package/knowledge/skills-manifest.json +1 -1
- package/package.json +6 -1
- package/pyproject.toml +1 -1
- package/scripts/auto-update.sh +188 -0
- package/scripts/harness_gen.py +98 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /lead department (Rodrigo)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] lead -> Rodrigo
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /lead department, orchestrated by Rodrigo. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /mkt department (Luna)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] mkt -> Luna
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /mkt department, orchestrated by Luna. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /ops department (Daniel)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] ops -> Daniel
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /ops department, orchestrated by Daniel. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /org department (Sofia)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] org -> Sofia
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /org department, orchestrated by Sofia. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /pm department (Carolina)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] pm -> Carolina
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /pm department, orchestrated by Carolina. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /saas department (Tiago)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] saas -> Tiago
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /saas department, orchestrated by Tiago. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /sales department (Miguel)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] sales -> Miguel
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /sales department, orchestrated by Miguel. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "ArkaOS /strat department (Tomas)"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
[arka:routing] strat -> Tomas
|
|
6
|
+
|
|
7
|
+
Handle the following as the ArkaOS /strat department, orchestrated by Tomas. Follow the AGENTS.md contract (plan approval, evidence over narration, quality gate; generated by scripts/harness_gen.py):
|
|
8
|
+
|
|
9
|
+
$ARGUMENTS
|
package/harness/zed/.rules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ArkaOS — The Operating System for AI Agent Teams
|
|
2
2
|
|
|
3
|
-
> v4.
|
|
3
|
+
> v4.35.0 — 89 agents, 17 departments, 331 skills. Generated by `scripts/harness_gen.py`; do not edit.
|
|
4
4
|
|
|
5
5
|
You are operating within ArkaOS. Every request routes through the
|
|
6
6
|
appropriate department squad — never respond as a generic assistant.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { listBundleFiles, readBundleFile } from "../harness-bundle.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* OpenCode adapter (Foundation PR-6). Deploys the generated harness
|
|
7
|
+
* bundle onto OpenCode's native surfaces (opencode.ai/docs):
|
|
8
|
+
*
|
|
9
|
+
* AGENTS.md instructions contract (same body as codex)
|
|
10
|
+
* agents/*.md curated agent cut (C-suite + squad leads)
|
|
11
|
+
* commands/*.md one /arka-<dept> router command per department
|
|
12
|
+
* opencode.json arka-tools MCP — merged NON-destructively
|
|
13
|
+
*
|
|
14
|
+
* Merge contract: user keys ALWAYS win. An existing `$schema`, any
|
|
15
|
+
* user-defined `mcp` server, or a user-modified `arka-tools` entry is
|
|
16
|
+
* never overwritten; a corrupt opencode.json is never clobbered (we
|
|
17
|
+
* skip with a warning instead — config-seed.js posture).
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Pure, exported for tests. Returns:
|
|
22
|
+
* { config, changed } merged object + whether anything was added
|
|
23
|
+
* null leave the file alone (corrupt input)
|
|
24
|
+
*/
|
|
25
|
+
export function mergeOpencodeConfig(existingText, fragmentText) {
|
|
26
|
+
let fragment;
|
|
27
|
+
try {
|
|
28
|
+
fragment = JSON.parse(fragmentText);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
let existing = {};
|
|
33
|
+
const trimmed = (existingText ?? "").trim();
|
|
34
|
+
if (trimmed !== "") {
|
|
35
|
+
try {
|
|
36
|
+
existing = JSON.parse(trimmed);
|
|
37
|
+
} catch {
|
|
38
|
+
return null; // corrupt user config — never clobber
|
|
39
|
+
}
|
|
40
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const merged = { ...existing };
|
|
45
|
+
let changed = false;
|
|
46
|
+
if (!merged.$schema && fragment.$schema) {
|
|
47
|
+
merged.$schema = fragment.$schema;
|
|
48
|
+
changed = true;
|
|
49
|
+
}
|
|
50
|
+
const userMcp =
|
|
51
|
+
merged.mcp && typeof merged.mcp === "object" && !Array.isArray(merged.mcp)
|
|
52
|
+
? merged.mcp
|
|
53
|
+
: {};
|
|
54
|
+
const mcp = { ...userMcp };
|
|
55
|
+
for (const [name, server] of Object.entries(fragment.mcp || {})) {
|
|
56
|
+
if (!(name in mcp)) {
|
|
57
|
+
mcp[name] = server;
|
|
58
|
+
changed = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (changed && Object.keys(mcp).length > 0) merged.mcp = mcp;
|
|
62
|
+
return { config: merged, changed };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default {
|
|
66
|
+
configureHooks(config) {
|
|
67
|
+
// OpenCode has no shell-hook chain; "configure" means deploying the
|
|
68
|
+
// instruction/agent/command surfaces + the MCP config merge.
|
|
69
|
+
const agentsMd = readBundleFile("opencode", "AGENTS.md");
|
|
70
|
+
if (!agentsMd) {
|
|
71
|
+
console.warn(
|
|
72
|
+
" OpenCode bundle missing (harness/opencode/) — " +
|
|
73
|
+
"run scripts/harness_gen.py; skipping."
|
|
74
|
+
);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
mkdirSync(config.configDir, { recursive: true });
|
|
78
|
+
writeFileSync(join(config.configDir, "AGENTS.md"), agentsMd);
|
|
79
|
+
|
|
80
|
+
let deployed = 0;
|
|
81
|
+
for (const sub of ["agents", "commands"]) {
|
|
82
|
+
const names = listBundleFiles("opencode", sub);
|
|
83
|
+
if (names.length === 0) continue;
|
|
84
|
+
mkdirSync(join(config.configDir, sub), { recursive: true });
|
|
85
|
+
for (const name of names) {
|
|
86
|
+
const content = readBundleFile("opencode", `${sub}/${name}`);
|
|
87
|
+
if (content === null) continue;
|
|
88
|
+
writeFileSync(join(config.configDir, sub, name), content);
|
|
89
|
+
deployed += 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const fragment = readBundleFile("opencode", "opencode.json");
|
|
94
|
+
if (fragment) {
|
|
95
|
+
const existingText = existsSync(config.settingsFile)
|
|
96
|
+
? readFileSync(config.settingsFile, "utf-8")
|
|
97
|
+
: "";
|
|
98
|
+
const result = mergeOpencodeConfig(existingText, fragment);
|
|
99
|
+
if (result === null) {
|
|
100
|
+
console.warn(
|
|
101
|
+
" opencode.json unreadable — left untouched " +
|
|
102
|
+
"(the merge is never destructive)."
|
|
103
|
+
);
|
|
104
|
+
} else if (result.changed) {
|
|
105
|
+
writeFileSync(
|
|
106
|
+
config.settingsFile,
|
|
107
|
+
JSON.stringify(result.config, null, 2) + "\n"
|
|
108
|
+
);
|
|
109
|
+
console.log(
|
|
110
|
+
" opencode.json updated (arka-tools MCP registered, user keys preserved)."
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
console.log(
|
|
115
|
+
` OpenCode configured (AGENTS.md + ${deployed} agent/command files).`
|
|
116
|
+
);
|
|
117
|
+
},
|
|
118
|
+
};
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background auto-update daemon for ArkaOS (Foundation PR-1).
|
|
3
|
+
*
|
|
4
|
+
* Users were found running v2 while v4.33 was live — the update path
|
|
5
|
+
* (`npx arkaos@latest update`) only fires when the user remembers to run
|
|
6
|
+
* it. This installs a per-OS scheduled unit that runs
|
|
7
|
+
* `scripts/auto-update.sh` daily (and at load): the script checks the npm
|
|
8
|
+
* registry, applies the core update headlessly, and notifies the user.
|
|
9
|
+
* Project sync stays supervised — the next Claude session picks it up via
|
|
10
|
+
* [arka:update-available].
|
|
11
|
+
*
|
|
12
|
+
* Opt-in by default on install/update (`ensureDefaultEnabled`), with a
|
|
13
|
+
* persisted opt-out marker so `npx arkaos autoupdate disable` survives
|
|
14
|
+
* future updates. ESM, Node + Bun, no interactive prompts. Unit
|
|
15
|
+
* generation (`unitFor`) is pure so it can be unit-tested without
|
|
16
|
+
* touching the OS (autostart.js precedent).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { execSync } from "node:child_process";
|
|
20
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { homedir } from "node:os";
|
|
22
|
+
import { dirname, join } from "node:path";
|
|
23
|
+
import { fileURLToPath } from "node:url";
|
|
24
|
+
|
|
25
|
+
export const AUTOUPDATE_LABEL = "io.wizardingcode.arkaos.updater";
|
|
26
|
+
const SERVICE_NAME = "arkaos-updater.service";
|
|
27
|
+
const TIMER_NAME = "arkaos-updater.timer";
|
|
28
|
+
|
|
29
|
+
function defaultRepoRoot() {
|
|
30
|
+
return join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Pure: the opt-out marker path — written by disable(), honored by
|
|
34
|
+
* ensureDefaultEnabled() and by scripts/auto-update.sh itself. */
|
|
35
|
+
export function optoutPath(home = homedir()) {
|
|
36
|
+
return join(home, ".arkaos", "autoupdate.optout");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Anchor the daemon at the stable ~/.arkaos/lib snapshot, never at the
|
|
40
|
+
* package root when it can be an npx cache: `npm cache clean` purges
|
|
41
|
+
* that location at any time, and a default-on daemon anchored there
|
|
42
|
+
* dies silently — with no surviving daemon to self-heal (QG blocker,
|
|
43
|
+
* Foundation PR-1). core-snapshot.js ships scripts/ into the snapshot;
|
|
44
|
+
* repoRoot is the fallback for dev checkouts before the first deploy. */
|
|
45
|
+
export function stableRoot({ home = homedir(), repoRoot = defaultRepoRoot() } = {}) {
|
|
46
|
+
const lib = join(home, ".arkaos", "lib");
|
|
47
|
+
return existsSync(join(lib, "scripts", "auto-update.sh")) ? lib : repoRoot;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Pure: the scheduled unit(s) for a given platform. Throws on
|
|
51
|
+
* unsupported OS. Returns { kind, files: [{ path, content }] } —
|
|
52
|
+
* systemd needs a service + timer pair, launchd a single plist. */
|
|
53
|
+
export function unitFor(os, { repoRoot, home }) {
|
|
54
|
+
const script = `${repoRoot}/scripts/auto-update.sh`;
|
|
55
|
+
const log = `${home}/.arkaos/logs/auto-update-daemon.log`;
|
|
56
|
+
|
|
57
|
+
if (os === "darwin") {
|
|
58
|
+
// launchd runs with a minimal PATH (/usr/bin:/bin:...) — node/npx
|
|
59
|
+
// live in /opt/homebrew/bin or /usr/local/bin, so the plist must
|
|
60
|
+
// carry a usable PATH or the daemon dies on `command -v npx`.
|
|
61
|
+
// StartCalendarInterval fires daily; RunAtLoad covers machines that
|
|
62
|
+
// were asleep/off at that hour (login catches up).
|
|
63
|
+
const content = `<?xml version="1.0" encoding="UTF-8"?>
|
|
64
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
65
|
+
<plist version="1.0">
|
|
66
|
+
<dict>
|
|
67
|
+
<key>Label</key>
|
|
68
|
+
<string>${AUTOUPDATE_LABEL}</string>
|
|
69
|
+
<key>ProgramArguments</key>
|
|
70
|
+
<array>
|
|
71
|
+
<string>/bin/bash</string>
|
|
72
|
+
<string>${script}</string>
|
|
73
|
+
</array>
|
|
74
|
+
<key>EnvironmentVariables</key>
|
|
75
|
+
<dict>
|
|
76
|
+
<key>PATH</key>
|
|
77
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
78
|
+
</dict>
|
|
79
|
+
<key>StartCalendarInterval</key>
|
|
80
|
+
<dict>
|
|
81
|
+
<key>Hour</key>
|
|
82
|
+
<integer>10</integer>
|
|
83
|
+
<key>Minute</key>
|
|
84
|
+
<integer>30</integer>
|
|
85
|
+
</dict>
|
|
86
|
+
<key>RunAtLoad</key>
|
|
87
|
+
<true/>
|
|
88
|
+
<key>StandardOutPath</key>
|
|
89
|
+
<string>${log}</string>
|
|
90
|
+
<key>StandardErrorPath</key>
|
|
91
|
+
<string>${log}</string>
|
|
92
|
+
</dict>
|
|
93
|
+
</plist>
|
|
94
|
+
`;
|
|
95
|
+
return {
|
|
96
|
+
kind: "launchd",
|
|
97
|
+
files: [
|
|
98
|
+
{
|
|
99
|
+
path: join(home, "Library", "LaunchAgents", `${AUTOUPDATE_LABEL}.plist`),
|
|
100
|
+
content,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (os === "linux") {
|
|
107
|
+
const service = `[Unit]
|
|
108
|
+
Description=ArkaOS auto-update (npm registry watch + headless core update)
|
|
109
|
+
After=network-online.target
|
|
110
|
+
|
|
111
|
+
[Service]
|
|
112
|
+
Type=oneshot
|
|
113
|
+
ExecStart=/bin/bash ${script}
|
|
114
|
+
StandardOutput=append:${log}
|
|
115
|
+
StandardError=append:${log}
|
|
116
|
+
`;
|
|
117
|
+
// Persistent=true replays a missed daily run at next boot — the
|
|
118
|
+
// laptop-that-was-off case that left users on v2.
|
|
119
|
+
const timer = `[Unit]
|
|
120
|
+
Description=Daily ArkaOS auto-update check
|
|
121
|
+
|
|
122
|
+
[Timer]
|
|
123
|
+
OnCalendar=daily
|
|
124
|
+
Persistent=true
|
|
125
|
+
|
|
126
|
+
[Install]
|
|
127
|
+
WantedBy=timers.target
|
|
128
|
+
`;
|
|
129
|
+
const unitDir = join(home, ".config", "systemd", "user");
|
|
130
|
+
return {
|
|
131
|
+
kind: "systemd",
|
|
132
|
+
files: [
|
|
133
|
+
{ path: join(unitDir, SERVICE_NAME), content: service },
|
|
134
|
+
{ path: join(unitDir, TIMER_NAME), content: timer },
|
|
135
|
+
],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
throw new Error(`unsupported platform for autoupdate: ${os}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function _silent(cmd) {
|
|
143
|
+
try {
|
|
144
|
+
execSync(cmd, { stdio: "pipe" });
|
|
145
|
+
return true;
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function enable({ repoRoot = defaultRepoRoot() } = {}) {
|
|
152
|
+
const root = stableRoot({ repoRoot });
|
|
153
|
+
const unit = unitFor(process.platform, { repoRoot: root, home: homedir() });
|
|
154
|
+
mkdirSync(join(homedir(), ".arkaos", "logs"), { recursive: true });
|
|
155
|
+
for (const f of unit.files) {
|
|
156
|
+
mkdirSync(dirname(f.path), { recursive: true });
|
|
157
|
+
writeFileSync(f.path, f.content, "utf8");
|
|
158
|
+
}
|
|
159
|
+
if (existsSync(optoutPath())) rmSync(optoutPath());
|
|
160
|
+
const mainPath = unit.files[0].path;
|
|
161
|
+
if (unit.kind === "launchd") {
|
|
162
|
+
_silent(`launchctl unload "${mainPath}"`);
|
|
163
|
+
if (!_silent(`launchctl load -w "${mainPath}"`)) {
|
|
164
|
+
return { ok: false, path: mainPath,
|
|
165
|
+
message: "Plist written but launchctl load failed — it will still run at next login." };
|
|
166
|
+
}
|
|
167
|
+
} else if (unit.kind === "systemd") {
|
|
168
|
+
_silent("systemctl --user daemon-reload");
|
|
169
|
+
if (!_silent(`systemctl --user enable --now ${TIMER_NAME}`)) {
|
|
170
|
+
return { ok: false, path: mainPath,
|
|
171
|
+
message: "Timer written but systemctl enable failed — check 'systemctl --user' availability." };
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { ok: true, path: mainPath,
|
|
175
|
+
message: "Auto-update enabled. ArkaOS checks npm daily and updates itself." };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function disable() {
|
|
179
|
+
const unit = unitFor(process.platform, { repoRoot: defaultRepoRoot(), home: homedir() });
|
|
180
|
+
const mainPath = unit.files[0].path;
|
|
181
|
+
if (unit.kind === "launchd") _silent(`launchctl unload "${mainPath}"`);
|
|
182
|
+
else if (unit.kind === "systemd") _silent(`systemctl --user disable --now ${TIMER_NAME}`);
|
|
183
|
+
for (const f of unit.files) if (existsSync(f.path)) rmSync(f.path);
|
|
184
|
+
// Persisted opt-out: future `npx arkaos update` runs must not re-enable.
|
|
185
|
+
mkdirSync(join(homedir(), ".arkaos"), { recursive: true });
|
|
186
|
+
writeFileSync(optoutPath(), new Date().toISOString(), "utf8");
|
|
187
|
+
return { ok: true, path: mainPath, message: "Auto-update disabled (opt-out persisted)." };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function status() {
|
|
191
|
+
let unit;
|
|
192
|
+
try {
|
|
193
|
+
unit = unitFor(process.platform, { repoRoot: defaultRepoRoot(), home: homedir() });
|
|
194
|
+
} catch (err) {
|
|
195
|
+
return { installed: false, supported: false, optout: false, message: err.message };
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
installed: unit.files.every((f) => existsSync(f.path)),
|
|
199
|
+
supported: true,
|
|
200
|
+
optout: existsSync(optoutPath()),
|
|
201
|
+
kind: unit.kind,
|
|
202
|
+
path: unit.files[0].path,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Default-on wiring for install/update flows: enable unless the user
|
|
207
|
+
* opted out or the platform is unsupported. Never throws. */
|
|
208
|
+
export function ensureDefaultEnabled({ repoRoot = defaultRepoRoot() } = {}) {
|
|
209
|
+
const s = status();
|
|
210
|
+
if (!s.supported) return { action: "unsupported" };
|
|
211
|
+
if (s.optout) return { action: "optout" };
|
|
212
|
+
if (s.installed) {
|
|
213
|
+
// Refresh unit content in place (paths/schedule may change between
|
|
214
|
+
// versions) but do not force a reload storm on every update.
|
|
215
|
+
try {
|
|
216
|
+
const root = stableRoot({ repoRoot });
|
|
217
|
+
const unit = unitFor(process.platform, { repoRoot: root, home: homedir() });
|
|
218
|
+
for (const f of unit.files) writeFileSync(f.path, f.content, "utf8");
|
|
219
|
+
} catch {}
|
|
220
|
+
return { action: "already-enabled" };
|
|
221
|
+
}
|
|
222
|
+
const r = enable({ repoRoot });
|
|
223
|
+
return { action: r.ok ? "enabled" : "partial", message: r.message };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export async function autoupdate(args = []) {
|
|
227
|
+
const action = (args[0] || "status").toLowerCase();
|
|
228
|
+
if (action === "enable") {
|
|
229
|
+
const r = enable();
|
|
230
|
+
console.log(` ${r.ok ? "✓" : "⚠"} ${r.message}\n ${r.path}`);
|
|
231
|
+
} else if (action === "disable") {
|
|
232
|
+
const r = disable();
|
|
233
|
+
console.log(` ✓ ${r.message}`);
|
|
234
|
+
} else if (action === "run") {
|
|
235
|
+
// Foreground one-shot check — same script (and anchor) the daemon runs.
|
|
236
|
+
const script = join(stableRoot(), "scripts", "auto-update.sh");
|
|
237
|
+
try {
|
|
238
|
+
execSync(`/bin/bash "${script}" --force`, { stdio: "inherit" });
|
|
239
|
+
} catch { process.exitCode = 1; }
|
|
240
|
+
} else if (action === "status") {
|
|
241
|
+
const s = status();
|
|
242
|
+
if (!s.supported) console.log(` Auto-update not supported here: ${s.message}`);
|
|
243
|
+
else if (s.optout) console.log(` Auto-update: DISABLED by user opt-out\n (re-enable: npx arkaos autoupdate enable)`);
|
|
244
|
+
else console.log(` Auto-update: ${s.installed ? "ENABLED" : "disabled"} (${s.kind})\n ${s.path}`);
|
|
245
|
+
} else {
|
|
246
|
+
console.error(` Unknown autoupdate action: ${action}. Use enable | disable | status | run.`);
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
}
|
|
249
|
+
}
|
package/installer/cli.js
CHANGED
|
@@ -21,6 +21,10 @@ const { values, positionals } = parseArgs({
|
|
|
21
21
|
force: { type: "boolean", short: "f" },
|
|
22
22
|
"no-system": { type: "boolean" },
|
|
23
23
|
"with-ollama": { type: "boolean" },
|
|
24
|
+
// Foundation PR-3 — `npx arkaos install --profile <essential|complete|local-ai>`.
|
|
25
|
+
// Declared so it lands in `values.profile` instead of a free
|
|
26
|
+
// positional under strict:false (the documented --fix lesson).
|
|
27
|
+
profile: { type: "string" },
|
|
24
28
|
// PR3.5 v3.74.1 — declared so `npx arkaos doctor --fix` lands in
|
|
25
29
|
// `values.fix` rather than as a free positional under strict:false.
|
|
26
30
|
// Eliminates the dead-branch fallback flagged by Marta in PR2's QG.
|
|
@@ -57,12 +61,15 @@ ArkaOS v${VERSION} — The Operating System for AI Agent Teams
|
|
|
57
61
|
Usage:
|
|
58
62
|
npx arkaos install Install ArkaOS in current environment
|
|
59
63
|
npx arkaos install --runtime <runtime> Install for specific runtime
|
|
64
|
+
npx arkaos install --profile <profile> Pick the install profile non-interactively
|
|
60
65
|
npx arkaos init Initialize project config (.arkaos.json)
|
|
61
66
|
npx arkaos update Update to latest version
|
|
62
67
|
npx arkaos migrate Migrate from v1 to v2
|
|
63
68
|
npx arkaos migrate-user-data Move user data (~/.claude/skills/arka/ → ~/.arkaos/)
|
|
64
69
|
npx arkaos dashboard Start monitoring dashboard
|
|
65
70
|
npx arkaos autostart <enable|disable|status> Start dashboard on boot
|
|
71
|
+
npx arkaos autoupdate <enable|disable|status|run> Keep ArkaOS updated automatically (daily check + notification)
|
|
72
|
+
npx arkaos menubar <enable|disable|status> Menu bar launcher (macOS — ▲ quick actions)
|
|
66
73
|
npx arkaos keys Manage API keys (OpenAI, fal.ai, etc.)
|
|
67
74
|
npx arkaos models Model Fabric: which model runs each role
|
|
68
75
|
npx arkaos models set <role> <provider>/<model> Re-route a role
|
|
@@ -76,6 +83,9 @@ Options:
|
|
|
76
83
|
-r, --runtime <name> Target runtime: claude-code, codex, gemini, cursor
|
|
77
84
|
-p, --path <dir> Installation directory (default: auto-detect)
|
|
78
85
|
-f, --force Force reinstall, overwriting existing files
|
|
86
|
+
--profile <name> Install profile: essential (venv, hooks, skills,
|
|
87
|
+
dashboard, MCPs), complete (+ litellm[proxy], ffmpeg),
|
|
88
|
+
local-ai (+ Ollama, local execution model)
|
|
79
89
|
-v, --version Show version
|
|
80
90
|
-h, --help Show this help
|
|
81
91
|
|
|
@@ -99,7 +109,20 @@ Examples:
|
|
|
99
109
|
|
|
100
110
|
async function main() {
|
|
101
111
|
switch (command) {
|
|
102
|
-
case "install":
|
|
112
|
+
case "install": {
|
|
113
|
+
// Validate --profile up front (before runtime detection) so a
|
|
114
|
+
// typo fails fast instead of silently falling back to the wizard.
|
|
115
|
+
let profileFlag;
|
|
116
|
+
if (values.profile !== undefined) {
|
|
117
|
+
const { normalizeProfileFlag, INSTALL_PROFILES } = await import("./profile.js");
|
|
118
|
+
profileFlag = normalizeProfileFlag(values.profile);
|
|
119
|
+
if (!profileFlag) {
|
|
120
|
+
console.error(
|
|
121
|
+
`Invalid --profile "${values.profile}". Valid values: ${INSTALL_PROFILES.join(", ")}`
|
|
122
|
+
);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
103
126
|
const runtime = values.runtime || await detectRuntime();
|
|
104
127
|
await install({
|
|
105
128
|
runtime,
|
|
@@ -107,8 +130,10 @@ async function main() {
|
|
|
107
130
|
force: values.force,
|
|
108
131
|
skipSystem: values["no-system"],
|
|
109
132
|
withOllama: values["with-ollama"],
|
|
133
|
+
profileFlag,
|
|
110
134
|
});
|
|
111
135
|
break;
|
|
136
|
+
}
|
|
112
137
|
|
|
113
138
|
case "init": {
|
|
114
139
|
const { init } = await import("./init.js");
|
|
@@ -134,6 +159,18 @@ async function main() {
|
|
|
134
159
|
break;
|
|
135
160
|
}
|
|
136
161
|
|
|
162
|
+
case "autoupdate": {
|
|
163
|
+
const { autoupdate } = await import("./autoupdate.js");
|
|
164
|
+
await autoupdate(positionals.slice(1));
|
|
165
|
+
break;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
case "menubar": {
|
|
169
|
+
const { menubar } = await import("./menubar.js");
|
|
170
|
+
await menubar(positionals.slice(1));
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
|
|
137
174
|
case "uninstall":
|
|
138
175
|
const { uninstall } = await import("./uninstall.js");
|
|
139
176
|
await uninstall();
|
|
@@ -35,7 +35,11 @@ import { join, basename } from "node:path";
|
|
|
35
35
|
// resolve_arkaos_root() checks, so a crash mid-deploy on a fresh install
|
|
36
36
|
// leaves a snapshot that fails validation instead of a core-only root
|
|
37
37
|
// that validates but lacks config/departments/knowledge.
|
|
38
|
-
|
|
38
|
+
// scripts/ joined in Foundation PR-1: the auto-update daemon's launchd/
|
|
39
|
+
// systemd unit anchors at the snapshot (a unit pointing into the npx
|
|
40
|
+
// cache dies silently on `npm cache clean` — QG blocker). Under 0.5MB
|
|
41
|
+
// after the __pycache__ filter.
|
|
42
|
+
const SNAPSHOT_DIRS = ["config", "departments", "knowledge", "scripts", "core"];
|
|
39
43
|
|
|
40
44
|
export function deployCoreSnapshot(arkaosRoot, installDir) {
|
|
41
45
|
if (!existsSync(join(arkaosRoot, "core", "sync", "__init__.py"))) return false;
|
|
@@ -69,6 +69,24 @@ const RUNTIMES = [
|
|
|
69
69
|
skillsDir: () => join(homedir(), ".cursor", "skills"),
|
|
70
70
|
settingsFile: () => join(homedir(), ".cursor", "settings.json"),
|
|
71
71
|
},
|
|
72
|
+
{
|
|
73
|
+
// Foundation PR-6 — OpenCode first-class. Listed AFTER the existing
|
|
74
|
+
// runtimes so multi-runtime machines keep their current default
|
|
75
|
+
// (detection picks the first match).
|
|
76
|
+
id: "opencode",
|
|
77
|
+
name: "OpenCode",
|
|
78
|
+
detect: () => {
|
|
79
|
+
try {
|
|
80
|
+
execSync("opencode --version", { stdio: "pipe" });
|
|
81
|
+
return true;
|
|
82
|
+
} catch {
|
|
83
|
+
return existsSync(join(homedir(), ".config", "opencode"));
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
configDir: () => join(homedir(), ".config", "opencode"),
|
|
87
|
+
skillsDir: () => join(homedir(), ".config", "opencode", "commands"),
|
|
88
|
+
settingsFile: () => join(homedir(), ".config", "opencode", "opencode.json"),
|
|
89
|
+
},
|
|
72
90
|
];
|
|
73
91
|
|
|
74
92
|
export async function detectRuntime() {
|