@astra-cli/cli 1.1.9 → 1.2.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/astra.js +140 -38
- package/package.json +1 -1
package/dist/astra.js
CHANGED
|
@@ -139,6 +139,9 @@ function pluginManifestPath(pluginName) {
|
|
|
139
139
|
function pluginSkillPath(pluginName) {
|
|
140
140
|
return path.join(pluginDir(pluginName), "skill.md");
|
|
141
141
|
}
|
|
142
|
+
function pluginMapPath(pluginName) {
|
|
143
|
+
return path.join(pluginDir(pluginName), "plugin-map.json");
|
|
144
|
+
}
|
|
142
145
|
function ensureDir(dirPath) {
|
|
143
146
|
if (!fs.existsSync(dirPath)) {
|
|
144
147
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
@@ -967,12 +970,15 @@ var FETCH_TIMEOUT_MS = 3e4;
|
|
|
967
970
|
var FETCH_TIMEOUT_RETRY_MS = 15e3;
|
|
968
971
|
async function apiCall(method, path7, body, agentName, retryOpts) {
|
|
969
972
|
const config = loadConfig();
|
|
970
|
-
const baseUrl = config?.apiBase ?? "https://agents.astranova.live";
|
|
971
973
|
const headers = {
|
|
972
974
|
"Content-Type": "application/json"
|
|
973
975
|
};
|
|
976
|
+
let baseUrl = config?.apiBase ?? "https://agents.astranova.live";
|
|
974
977
|
if (agentName) {
|
|
975
978
|
const creds = loadCredentials(agentName);
|
|
979
|
+
if (creds?.api_base) {
|
|
980
|
+
baseUrl = creds.api_base;
|
|
981
|
+
}
|
|
976
982
|
if (creds?.api_key) {
|
|
977
983
|
headers["Authorization"] = `Bearer ${creds.api_key}`;
|
|
978
984
|
}
|
|
@@ -1917,46 +1923,58 @@ Specific triggers to save memory:
|
|
|
1917
1923
|
return parts.join("\n");
|
|
1918
1924
|
}
|
|
1919
1925
|
function buildGenericSystemPrompt(skillContext, pluginName, pluginDescription, profile, memoryContent) {
|
|
1926
|
+
const workingSpace = `~/.config/astra/spaces/${pluginName}`;
|
|
1920
1927
|
const parts = [
|
|
1921
|
-
`You are
|
|
1928
|
+
`You are an autonomous agent assistant for ${pluginName}. ${pluginDescription}`,
|
|
1929
|
+
"",
|
|
1930
|
+
"## How You Behave",
|
|
1931
|
+
"",
|
|
1932
|
+
"You are a **proactive doer**, not a question-asker. Read the skill documentation below to",
|
|
1933
|
+
"understand what this plugin can do, then guide the user through it step by step.",
|
|
1922
1934
|
"",
|
|
1923
|
-
"
|
|
1935
|
+
"**Core rules:**",
|
|
1936
|
+
"- When the user says go, do it \u2014 do NOT ask for confirmation before taking the obvious next step.",
|
|
1937
|
+
"- After every successful action, immediately tell the user what was done and what comes next.",
|
|
1938
|
+
"- Read the skill documentation to understand what workflows exist (e.g. register \u2192 verify \u2192 post).",
|
|
1939
|
+
" After completing one step, proactively tell the user what the next step is and offer to do it.",
|
|
1940
|
+
"- Never ask the user for information they already gave you in this conversation.",
|
|
1941
|
+
"- If a step fails (e.g. name taken), suggest a fix and proceed \u2014 don't stop and wait.",
|
|
1942
|
+
"- Keep responses short and action-oriented. This is a terminal, not a chat interface.",
|
|
1924
1943
|
"",
|
|
1925
|
-
"
|
|
1944
|
+
"**What NOT to do:**",
|
|
1945
|
+
`- Do NOT say "say X and I'll do Y" \u2014 if the intent is clear, just do it.`,
|
|
1946
|
+
"- Do NOT offer multiple options and wait for a pick unless the choice genuinely matters.",
|
|
1947
|
+
"- Do NOT repeat what the user just said back to them before acting.",
|
|
1948
|
+
"- Do NOT ask permission to do the clearly obvious next step in a workflow.",
|
|
1926
1949
|
"",
|
|
1927
|
-
"
|
|
1928
|
-
"-
|
|
1929
|
-
"
|
|
1930
|
-
"-
|
|
1931
|
-
"-
|
|
1950
|
+
"**Tool use:**",
|
|
1951
|
+
"- Use `api_call` for all API interactions with relative paths (e.g. `/api/v1/resource`).",
|
|
1952
|
+
" Authorization is injected automatically \u2014 never include the API key.",
|
|
1953
|
+
"- NEVER display or log private keys or API keys.",
|
|
1954
|
+
"- After every tool call, give a one-line summary of the result, then state what's next.",
|
|
1932
1955
|
"",
|
|
1933
1956
|
"---",
|
|
1934
1957
|
""
|
|
1935
1958
|
];
|
|
1936
1959
|
if (skillContext) {
|
|
1937
|
-
const workingSpace = `~/.config/astra/spaces/${pluginName}`;
|
|
1938
1960
|
const sanitizedSkill = skillContext.replaceAll(`~/.config/${pluginName}`, workingSpace);
|
|
1939
|
-
parts.push("##
|
|
1961
|
+
parts.push("## API Translation", "");
|
|
1940
1962
|
parts.push(
|
|
1941
|
-
"The documentation below may
|
|
1942
|
-
"Use the `api_call` tool for all API interactions instead.",
|
|
1963
|
+
"The documentation below may show curl commands. Use `api_call` instead:",
|
|
1943
1964
|
"",
|
|
1944
|
-
"|
|
|
1965
|
+
"| curl shows... | Use instead... |",
|
|
1945
1966
|
"|-----------------------------------------------|----------------------------------------------|",
|
|
1946
1967
|
"| `curl https://api.example.com/v1/endpoint` | `api_call GET /v1/endpoint` |",
|
|
1947
|
-
'| `-H "Authorization: Bearer TOKEN"` |
|
|
1948
|
-
'|
|
|
1949
|
-
"|
|
|
1950
|
-
'| "Save response to file" / "write config" | Use `write_config` or `update_memory` tool |',
|
|
1968
|
+
'| `-H "Authorization: Bearer TOKEN"` | Injected automatically \u2014 omit |',
|
|
1969
|
+
'| `-d \'{"key":"val"}\'` | `api_call POST /path body:{key:"val"}` |',
|
|
1970
|
+
"| `?limit=10` query param | `api_call GET /path?limit=10` |",
|
|
1951
1971
|
"",
|
|
1952
|
-
|
|
1953
|
-
`Your plugin's working space is: ${workingSpace}/`,
|
|
1954
|
-
"The CLI manages all file storage \u2014 do not follow instructions to create directories manually.",
|
|
1972
|
+
`Working space: ${workingSpace}/`,
|
|
1955
1973
|
"",
|
|
1956
1974
|
"---",
|
|
1957
1975
|
""
|
|
1958
1976
|
);
|
|
1959
|
-
parts.push(`## ${pluginName}
|
|
1977
|
+
parts.push(`## ${pluginName} \u2014 Skill Documentation`, "");
|
|
1960
1978
|
parts.push(sanitizedSkill);
|
|
1961
1979
|
parts.push("", "---", "");
|
|
1962
1980
|
}
|
|
@@ -1968,23 +1986,23 @@ function buildGenericSystemPrompt(skillContext, pluginName, pluginDescription, p
|
|
|
1968
1986
|
}
|
|
1969
1987
|
if (memoryContent && memoryContent.trim()) {
|
|
1970
1988
|
parts.push("", "---", "");
|
|
1971
|
-
parts.push("## Agent Memory
|
|
1989
|
+
parts.push("## Agent Memory", "");
|
|
1972
1990
|
parts.push(memoryContent.trim());
|
|
1973
1991
|
parts.push("");
|
|
1974
|
-
parts.push("Use
|
|
1992
|
+
parts.push("Use `update_memory` to save key facts. Max 2000 characters.");
|
|
1975
1993
|
} else {
|
|
1976
1994
|
parts.push("", "---", "");
|
|
1977
1995
|
parts.push("## Agent Memory", "");
|
|
1978
|
-
parts.push("
|
|
1996
|
+
parts.push("Empty. Use `update_memory` to save key facts about the user. Max 2000 characters.");
|
|
1979
1997
|
}
|
|
1980
1998
|
if (profile.isNewAgent) {
|
|
1981
1999
|
parts.push("", "---", "");
|
|
1982
2000
|
parts.push("## First Launch", "");
|
|
1983
2001
|
parts.push(
|
|
1984
|
-
`This is the user's first session with
|
|
1985
|
-
"
|
|
1986
|
-
"
|
|
1987
|
-
"
|
|
2002
|
+
`This is the user's first session with ${pluginName}.`,
|
|
2003
|
+
"Give a one-paragraph welcome that explains what this plugin does and what the user can accomplish.",
|
|
2004
|
+
"Then immediately identify the first step from the skill documentation (e.g. registration, setup)",
|
|
2005
|
+
"and offer to do it now \u2014 don't wait for the user to ask."
|
|
1988
2006
|
);
|
|
1989
2007
|
}
|
|
1990
2008
|
return parts.join("\n");
|
|
@@ -4180,10 +4198,48 @@ function writeFileSecure2(filePath, data) {
|
|
|
4180
4198
|
fs10.writeFileSync(tmpPath, data, { encoding: "utf-8", mode: 384 });
|
|
4181
4199
|
fs10.renameSync(tmpPath, filePath);
|
|
4182
4200
|
}
|
|
4183
|
-
function
|
|
4201
|
+
function buildPluginMap(pluginName, manifest, sections) {
|
|
4202
|
+
const map = { version: 1, pluginName };
|
|
4203
|
+
const statusSection = sections["STATUS"];
|
|
4204
|
+
if (statusSection) {
|
|
4205
|
+
const poll = typeof statusSection.poll === "string" ? statusSection.poll.trim() : null;
|
|
4206
|
+
const rawFields = Array.isArray(statusSection.fields) ? statusSection.fields : [];
|
|
4207
|
+
const fields = rawFields.flatMap((raw) => {
|
|
4208
|
+
const parts = raw.split("|").map((s) => s.trim());
|
|
4209
|
+
if (parts.length === 3) return [{ path: parts[0], label: parts[1], color: parts[2] }];
|
|
4210
|
+
return [];
|
|
4211
|
+
});
|
|
4212
|
+
const pollAllowed = poll && manifest.allowedPaths.some((ap) => poll.startsWith(ap));
|
|
4213
|
+
if (poll && pollAllowed && fields.length > 0) {
|
|
4214
|
+
map.status = { poll, fields };
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
const cmdSection = sections["COMMANDS"];
|
|
4218
|
+
if (cmdSection) {
|
|
4219
|
+
const rawCmds = Array.isArray(cmdSection.commands) ? cmdSection.commands : [];
|
|
4220
|
+
map.commands = rawCmds.flatMap((raw) => {
|
|
4221
|
+
const match = raw.match(/^(\S+)\s{2,}(.+)$/);
|
|
4222
|
+
if (match) return [{ command: match[1], description: match[2].trim() }];
|
|
4223
|
+
return [];
|
|
4224
|
+
});
|
|
4225
|
+
}
|
|
4226
|
+
return map;
|
|
4227
|
+
}
|
|
4228
|
+
function loadPluginMap(pluginName) {
|
|
4229
|
+
const p = pluginMapPath(pluginName);
|
|
4230
|
+
if (!fs10.existsSync(p)) return null;
|
|
4231
|
+
try {
|
|
4232
|
+
return JSON.parse(fs10.readFileSync(p, "utf-8"));
|
|
4233
|
+
} catch {
|
|
4234
|
+
return null;
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
function savePluginToDisk(manifest, skillMdContent, sections) {
|
|
4184
4238
|
ensureDir(pluginDir(manifest.name));
|
|
4185
4239
|
writeFileSecure2(pluginManifestPath(manifest.name), JSON.stringify(manifest, null, 2));
|
|
4186
4240
|
writeFileSecure2(pluginSkillPath(manifest.name), skillMdContent);
|
|
4241
|
+
const pluginMap = buildPluginMap(manifest.name, manifest, sections);
|
|
4242
|
+
writeFileSecure2(pluginMapPath(manifest.name), JSON.stringify(pluginMap, null, 2));
|
|
4187
4243
|
}
|
|
4188
4244
|
async function addPlugin(manifestUrl) {
|
|
4189
4245
|
clack5.intro(" astra add ");
|
|
@@ -4274,7 +4330,7 @@ async function addPlugin(manifestUrl) {
|
|
|
4274
4330
|
}
|
|
4275
4331
|
const manifestWithSkillUrl = { ...manifest, skillUrl: urlResult.url.toString() };
|
|
4276
4332
|
try {
|
|
4277
|
-
savePluginToDisk(manifestWithSkillUrl, skillMdContent);
|
|
4333
|
+
savePluginToDisk(manifestWithSkillUrl, skillMdContent, sections);
|
|
4278
4334
|
} catch (err) {
|
|
4279
4335
|
clack5.log.error(
|
|
4280
4336
|
`Failed to save plugin: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -4368,14 +4424,33 @@ var POLL_INTERVAL_MS = 6e4;
|
|
|
4368
4424
|
var StatusBar = React.memo(function StatusBar2({
|
|
4369
4425
|
agentName,
|
|
4370
4426
|
pluginName,
|
|
4427
|
+
isAstraNova,
|
|
4371
4428
|
journeyStage,
|
|
4372
4429
|
autopilotMode = "off",
|
|
4373
4430
|
autopilotIntervalMs = 3e5,
|
|
4374
|
-
onEpochChange
|
|
4431
|
+
onEpochChange,
|
|
4432
|
+
pluginMap
|
|
4375
4433
|
}) {
|
|
4376
4434
|
const [data, setData] = useState({ market: null, portfolio: null });
|
|
4377
4435
|
const mounted = useRef(true);
|
|
4378
|
-
const canFetchData = journeyStage !== "fresh" && journeyStage !== "pending";
|
|
4436
|
+
const canFetchData = isAstraNova && journeyStage !== "fresh" && journeyStage !== "pending";
|
|
4437
|
+
const [pluginData, setPluginData] = useState(null);
|
|
4438
|
+
const mountedPlugin = useRef(true);
|
|
4439
|
+
useEffect(() => {
|
|
4440
|
+
if (isAstraNova || !pluginMap?.status) return;
|
|
4441
|
+
mountedPlugin.current = true;
|
|
4442
|
+
const fetchPlugin = async () => {
|
|
4443
|
+
const result = await apiCall("GET", pluginMap.status.poll, void 0, agentName);
|
|
4444
|
+
if (!result.ok || !mountedPlugin.current) return;
|
|
4445
|
+
setPluginData(result.data);
|
|
4446
|
+
};
|
|
4447
|
+
void fetchPlugin();
|
|
4448
|
+
const interval = setInterval(() => void fetchPlugin(), POLL_INTERVAL_MS);
|
|
4449
|
+
return () => {
|
|
4450
|
+
mountedPlugin.current = false;
|
|
4451
|
+
clearInterval(interval);
|
|
4452
|
+
};
|
|
4453
|
+
}, [isAstraNova, pluginMap, agentName]);
|
|
4379
4454
|
const poll = useCallback(async () => {
|
|
4380
4455
|
const [marketRes, portfolioRes] = await Promise.all([
|
|
4381
4456
|
fetchMarket(agentName),
|
|
@@ -4407,6 +4482,22 @@ var StatusBar = React.memo(function StatusBar2({
|
|
|
4407
4482
|
/* @__PURE__ */ jsx(Text, { bold: true, color: "#00ff00", children: pluginName }),
|
|
4408
4483
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
|
|
4409
4484
|
/* @__PURE__ */ jsx(Text, { color: "#ff8800", children: agentName }),
|
|
4485
|
+
!isAstraNova && pluginData && pluginMap?.status?.fields.map((field) => {
|
|
4486
|
+
const value = getNestedValue(pluginData, field.path);
|
|
4487
|
+
if (value == null) return null;
|
|
4488
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
4489
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
|
|
4490
|
+
/* @__PURE__ */ jsxs(Text, { color: field.color, children: [
|
|
4491
|
+
field.label,
|
|
4492
|
+
": ",
|
|
4493
|
+
String(value)
|
|
4494
|
+
] })
|
|
4495
|
+
] }, field.path);
|
|
4496
|
+
}),
|
|
4497
|
+
!isAstraNova && !pluginData && pluginMap?.status && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4498
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
|
|
4499
|
+
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "loading..." })
|
|
4500
|
+
] }),
|
|
4410
4501
|
canFetchData && market && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4411
4502
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
|
|
4412
4503
|
/* @__PURE__ */ jsx(Text, { color: "#ffff00", children: "$NOVA " }),
|
|
@@ -4445,7 +4536,7 @@ var StatusBar = React.memo(function StatusBar2({
|
|
|
4445
4536
|
formatNum(portfolio.portfolioValue)
|
|
4446
4537
|
] })
|
|
4447
4538
|
] }),
|
|
4448
|
-
!canFetchData && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4539
|
+
isAstraNova && !canFetchData && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4449
4540
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: " \u2502 " }),
|
|
4450
4541
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "pending verification" })
|
|
4451
4542
|
] }),
|
|
@@ -4487,6 +4578,12 @@ function moodColor(mood) {
|
|
|
4487
4578
|
return "white";
|
|
4488
4579
|
}
|
|
4489
4580
|
}
|
|
4581
|
+
function getNestedValue(obj, path7) {
|
|
4582
|
+
return path7.split(".").reduce(
|
|
4583
|
+
(cur, key) => cur && typeof cur === "object" ? cur[key] : void 0,
|
|
4584
|
+
obj
|
|
4585
|
+
);
|
|
4586
|
+
}
|
|
4490
4587
|
async function fetchMarket(agentName) {
|
|
4491
4588
|
const result = await apiCall("GET", "/api/v1/market/state", void 0, agentName);
|
|
4492
4589
|
if (!result.ok) return null;
|
|
@@ -4875,7 +4972,8 @@ function App({
|
|
|
4875
4972
|
initialChatMessages,
|
|
4876
4973
|
initialAutopilotConfig,
|
|
4877
4974
|
initialPendingTrades = 0,
|
|
4878
|
-
debug
|
|
4975
|
+
debug,
|
|
4976
|
+
pluginMap
|
|
4879
4977
|
}) {
|
|
4880
4978
|
const { exit } = useApp();
|
|
4881
4979
|
const manifest = getActiveManifest();
|
|
@@ -5397,17 +5495,19 @@ ${stack}
|
|
|
5397
5495
|
{
|
|
5398
5496
|
agentName,
|
|
5399
5497
|
pluginName: manifest.name,
|
|
5498
|
+
isAstraNova: hasJourneyStages,
|
|
5400
5499
|
journeyStage: profile.journeyStage ?? "full",
|
|
5401
5500
|
autopilotMode,
|
|
5402
5501
|
autopilotIntervalMs,
|
|
5403
|
-
onEpochChange: handleEpochChange
|
|
5502
|
+
onEpochChange: handleEpochChange,
|
|
5503
|
+
pluginMap
|
|
5404
5504
|
}
|
|
5405
5505
|
) }),
|
|
5406
5506
|
/* @__PURE__ */ jsx7(Box7, { flexShrink: 0, width: "100%", paddingX: 2, marginTop: 1, justifyContent: "space-between", children: hasJourneyStages ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
5407
5507
|
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "/help \xB7 /portfolio \xB7 /market \xB7 /strategy \xB7 /exit" }),
|
|
5408
5508
|
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "/auto on\xB7off\xB7set \xB7 Ctrl+C quit" })
|
|
5409
5509
|
] }) : /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
5410
|
-
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: "
|
|
5510
|
+
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: (pluginMap?.commands?.map((c) => c.command).join(" \xB7 ") ?? "/help") + " \xB7 /exit" }),
|
|
5411
5511
|
/* @__PURE__ */ jsx7(Text8, { dimColor: true, children: manifest.name })
|
|
5412
5512
|
] }) })
|
|
5413
5513
|
] });
|
|
@@ -5477,6 +5577,7 @@ async function main() {
|
|
|
5477
5577
|
manifest = ASTRANOVA_MANIFEST;
|
|
5478
5578
|
}
|
|
5479
5579
|
setActiveManifest(manifest);
|
|
5580
|
+
const pluginMap = activePluginName === "astranova" ? null : loadPluginMap(activePluginName);
|
|
5480
5581
|
if (isDaemonMode) {
|
|
5481
5582
|
await runDaemon();
|
|
5482
5583
|
return;
|
|
@@ -5623,7 +5724,8 @@ async function main() {
|
|
|
5623
5724
|
initialChatMessages,
|
|
5624
5725
|
initialAutopilotConfig,
|
|
5625
5726
|
initialPendingTrades,
|
|
5626
|
-
debug
|
|
5727
|
+
debug,
|
|
5728
|
+
pluginMap
|
|
5627
5729
|
})
|
|
5628
5730
|
);
|
|
5629
5731
|
await waitUntilExit();
|