artemys 0.3.1 → 0.3.3
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/CHANGELOG.md +37 -0
- package/README.md +14 -20
- package/dist/cli/config.d.ts +21 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +97 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +310 -106
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +3 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +177 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/coffeeshop/client.d.ts +5 -10
- package/dist/coffeeshop/client.d.ts.map +1 -1
- package/dist/coffeeshop/client.js +43 -15
- package/dist/coffeeshop/client.js.map +1 -1
- package/dist/coffeeshop/index.d.ts +1 -1
- package/dist/coffeeshop/index.d.ts.map +1 -1
- package/dist/coffeeshop/index.js +1 -1
- package/dist/coffeeshop/index.js.map +1 -1
- package/dist/coffeeshop/schemas.d.ts +22 -16
- package/dist/coffeeshop/schemas.d.ts.map +1 -1
- package/dist/coffeeshop/schemas.js +27 -30
- package/dist/coffeeshop/schemas.js.map +1 -1
- package/dist/discovery/agent-card.schema.d.ts +4 -2
- package/dist/discovery/agent-card.schema.d.ts.map +1 -1
- package/dist/discovery/agent-card.schema.js +11 -3
- package/dist/discovery/agent-card.schema.js.map +1 -1
- package/dist/discovery/index.d.ts +2 -1
- package/dist/discovery/index.d.ts.map +1 -1
- package/dist/discovery/index.js +2 -1
- package/dist/discovery/index.js.map +1 -1
- package/dist/discovery/reserved-handles.d.ts +2 -0
- package/dist/discovery/reserved-handles.d.ts.map +1 -0
- package/dist/discovery/reserved-handles.js +18 -0
- package/dist/discovery/reserved-handles.js.map +1 -0
- package/dist/integrations/job-boards/http.d.ts +4 -1
- package/dist/integrations/job-boards/http.d.ts.map +1 -1
- package/dist/integrations/job-boards/http.js +4 -3
- package/dist/integrations/job-boards/http.js.map +1 -1
- package/dist/integrations/job-boards/jsonld.d.ts.map +1 -1
- package/dist/integrations/job-boards/jsonld.js +28 -0
- package/dist/integrations/job-boards/jsonld.js.map +1 -1
- package/dist/integrations/job-boards/remotive.d.ts.map +1 -1
- package/dist/integrations/job-boards/remotive.js +6 -35
- package/dist/integrations/job-boards/remotive.js.map +1 -1
- package/dist/mcp-server/index.d.ts +1 -1
- package/dist/mcp-server/index.d.ts.map +1 -1
- package/dist/mcp-server/index.js +1 -1
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/mcp-server/persistence.d.ts +0 -4
- package/dist/mcp-server/persistence.d.ts.map +1 -1
- package/dist/mcp-server/persistence.js +25 -30
- package/dist/mcp-server/persistence.js.map +1 -1
- package/dist/mcp-server/prompts.d.ts +20 -0
- package/dist/mcp-server/prompts.d.ts.map +1 -0
- package/dist/mcp-server/prompts.js +44 -0
- package/dist/mcp-server/prompts.js.map +1 -0
- package/dist/mcp-server/resources.d.ts +2 -1
- package/dist/mcp-server/resources.d.ts.map +1 -1
- package/dist/mcp-server/resources.js +15 -0
- package/dist/mcp-server/resources.js.map +1 -1
- package/dist/mcp-server/server.d.ts +3 -0
- package/dist/mcp-server/server.d.ts.map +1 -1
- package/dist/mcp-server/server.js +40 -3
- package/dist/mcp-server/server.js.map +1 -1
- package/dist/mcp-server/state.d.ts +0 -1
- package/dist/mcp-server/state.d.ts.map +1 -1
- package/dist/mcp-server/state.js +0 -4
- package/dist/mcp-server/state.js.map +1 -1
- package/dist/mcp-server/tools/common.d.ts +1 -219
- package/dist/mcp-server/tools/common.d.ts.map +1 -1
- package/dist/mcp-server/tools/common.js +1 -3
- package/dist/mcp-server/tools/common.js.map +1 -1
- package/dist/mcp-server/tools/index.d.ts.map +1 -1
- package/dist/mcp-server/tools/index.js +2 -0
- package/dist/mcp-server/tools/index.js.map +1 -1
- package/dist/mcp-server/tools/messaging.d.ts +11 -0
- package/dist/mcp-server/tools/messaging.d.ts.map +1 -1
- package/dist/mcp-server/tools/messaging.js +14 -4
- package/dist/mcp-server/tools/messaging.js.map +1 -1
- package/dist/mcp-server/tools/protocol.js.map +1 -1
- package/dist/mcp-server/tools/resume.d.ts +3 -0
- package/dist/mcp-server/tools/resume.d.ts.map +1 -0
- package/dist/mcp-server/tools/resume.js +61 -0
- package/dist/mcp-server/tools/resume.js.map +1 -0
- package/dist/mcp-server/tools/talent.d.ts +25 -0
- package/dist/mcp-server/tools/talent.d.ts.map +1 -1
- package/dist/mcp-server/tools/talent.js +123 -43
- package/dist/mcp-server/tools/talent.js.map +1 -1
- package/dist/protocol/envelope.schema.d.ts +1 -1
- package/dist/protocol/envelope.schema.d.ts.map +1 -1
- package/dist/protocol/envelope.schema.js +2 -1
- package/dist/protocol/envelope.schema.js.map +1 -1
- package/dist/protocol/index.d.ts +1 -2
- package/dist/protocol/index.d.ts.map +1 -1
- package/dist/protocol/index.js +1 -2
- package/dist/protocol/index.js.map +1 -1
- package/dist/protocol/talent.schema.d.ts +115 -0
- package/dist/protocol/talent.schema.d.ts.map +1 -1
- package/dist/protocol/talent.schema.js +61 -3
- package/dist/protocol/talent.schema.js.map +1 -1
- package/dist/resume/index.d.ts +4 -0
- package/dist/resume/index.d.ts.map +1 -0
- package/dist/resume/index.js +4 -0
- package/dist/resume/index.js.map +1 -0
- package/dist/resume/parse-resume.d.ts +8 -0
- package/dist/resume/parse-resume.d.ts.map +1 -0
- package/dist/resume/parse-resume.js +124 -0
- package/dist/resume/parse-resume.js.map +1 -0
- package/dist/resume/providers/anthropic.d.ts +2 -0
- package/dist/resume/providers/anthropic.d.ts.map +1 -0
- package/dist/resume/providers/anthropic.js +26 -0
- package/dist/resume/providers/anthropic.js.map +1 -0
- package/dist/resume/providers/google.d.ts +2 -0
- package/dist/resume/providers/google.d.ts.map +1 -0
- package/dist/resume/providers/google.js +24 -0
- package/dist/resume/providers/google.js.map +1 -0
- package/dist/resume/providers/openai.d.ts +2 -0
- package/dist/resume/providers/openai.d.ts.map +1 -0
- package/dist/resume/providers/openai.js +25 -0
- package/dist/resume/providers/openai.js.map +1 -0
- package/dist/resume/to-card.d.ts +7 -0
- package/dist/resume/to-card.d.ts.map +1 -0
- package/dist/resume/to-card.js +63 -0
- package/dist/resume/to-card.js.map +1 -0
- package/package.json +10 -1
- package/skills/artemys-talent/SKILL.md +258 -0
- package/skills/artemys-talent/references/TOOLS.md +479 -0
- package/skills/artemys-talent/scripts/setup.sh +88 -0
- package/dist/discovery/intro-decision.schema.d.ts +0 -17
- package/dist/discovery/intro-decision.schema.d.ts.map +0 -1
- package/dist/discovery/intro-decision.schema.js +0 -11
- package/dist/discovery/intro-decision.schema.js.map +0 -1
- package/dist/discovery/intro-request.schema.d.ts +0 -11
- package/dist/discovery/intro-request.schema.d.ts.map +0 -1
- package/dist/discovery/intro-request.schema.js +0 -12
- package/dist/discovery/intro-request.schema.js.map +0 -1
- package/dist/protocol/transport.schema.d.ts +0 -27
- package/dist/protocol/transport.schema.d.ts.map +0 -1
- package/dist/protocol/transport.schema.js +0 -18
- package/dist/protocol/transport.schema.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import process from "node:process";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { CoffeeShopClient } from "../coffeeshop/index.js";
|
|
6
6
|
import { AgentCardSchema, DiscoveryQuerySchema, } from "../discovery/index.js";
|
|
7
|
-
import { ConversationTracker, ProfileStore, SqliteMcpPersistence, createPersistentConversationTracker, createPersistentProfileStore, startArtemysMcpServer, } from "../mcp-server/index.js";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const DEFAULT_CONVERSATION_TTL_MS = 24 * 60 * 60 * 1000;
|
|
7
|
+
import { ConversationTracker, DEFAULT_CONVERSATION_TTL_MS, DEFAULT_MAX_TRACKED_CONVERSATIONS, ProfileStore, SqliteMcpPersistence, createPersistentConversationTracker, createPersistentProfileStore, startArtemysMcpServer, } from "../mcp-server/index.js";
|
|
8
|
+
import { CandidateSnapshotSchema } from "../protocol/index.js";
|
|
9
|
+
import { checkInbox, respondToMessage } from "../mcp-server/tools/messaging.js";
|
|
10
|
+
import { searchOpportunities, expressInterest, updateProfile, getMyApplications } from "../mcp-server/tools/talent.js";
|
|
11
|
+
import { getConfigPath, getFlagString, loadConfig, resolveAgentCardPath, resolveApiKey, resolveCoffeeShopUrl, saveConfig, } from "./config.js";
|
|
13
12
|
function parseArgs(argv) {
|
|
14
13
|
const positionals = [];
|
|
15
14
|
const flags = {};
|
|
@@ -44,10 +43,6 @@ function parseArgs(argv) {
|
|
|
44
43
|
}
|
|
45
44
|
return { positionals, flags };
|
|
46
45
|
}
|
|
47
|
-
function getFlagString(flags, key) {
|
|
48
|
-
const value = flags[key];
|
|
49
|
-
return typeof value === "string" ? value : undefined;
|
|
50
|
-
}
|
|
51
46
|
function getFlagBoolean(flags, key) {
|
|
52
47
|
const value = flags[key];
|
|
53
48
|
if (typeof value === "boolean") {
|
|
@@ -85,39 +80,51 @@ function getPersistOption(flags) {
|
|
|
85
80
|
}
|
|
86
81
|
return undefined;
|
|
87
82
|
}
|
|
88
|
-
function
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.
|
|
96
|
-
|
|
97
|
-
return items.length > 0 ? items : undefined;
|
|
83
|
+
async function outputJson(data, outputPath) {
|
|
84
|
+
const json = JSON.stringify(data, null, 2);
|
|
85
|
+
if (outputPath) {
|
|
86
|
+
await writeFile(outputPath, json + "\n", "utf-8");
|
|
87
|
+
console.log(`Written to ${outputPath}`);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
console.log(json);
|
|
91
|
+
}
|
|
98
92
|
}
|
|
99
93
|
function printHelp() {
|
|
100
94
|
console.log(`
|
|
101
95
|
artemys - Protocol + Coffee Shop + MCP Server SDK CLI
|
|
102
96
|
|
|
103
97
|
Commands:
|
|
98
|
+
start Initialize agent identity and register with Coffee Shop
|
|
99
|
+
whoami Show current agent identity and hub status
|
|
100
|
+
doctor Run diagnostics on agent setup
|
|
104
101
|
mcp-server Start Artemys MCP server
|
|
105
102
|
talent Candidate talent workflow commands
|
|
106
103
|
register Register an agent card with Coffee Shop
|
|
107
104
|
discover Discover agents via Coffee Shop
|
|
108
105
|
rotate-key Rotate Coffee Shop API key
|
|
106
|
+
parse-resume Parse a resume file (PDF or text) into structured JSON via LLM
|
|
107
|
+
to-card Convert a FullResume JSON to an anonymous CandidateCard
|
|
109
108
|
version Show CLI version
|
|
110
109
|
help Show this help message
|
|
111
110
|
|
|
112
111
|
Examples:
|
|
113
|
-
artemys
|
|
112
|
+
artemys start
|
|
113
|
+
artemys start --display-name "My Agent" --role candidate --handle my-agent
|
|
114
|
+
artemys whoami
|
|
115
|
+
artemys doctor
|
|
116
|
+
artemys mcp-server
|
|
114
117
|
artemys mcp-server --agent-card ./agent-card.json --persist
|
|
118
|
+
artemys mcp-server --transport sse --mcp-token my-secret
|
|
115
119
|
artemys register --agent-card ./agent-card.json
|
|
116
|
-
artemys discover --requester-agent-id agent
|
|
117
|
-
artemys talent search --
|
|
118
|
-
artemys talent apply --
|
|
119
|
-
artemys talent profile --
|
|
120
|
-
artemys
|
|
120
|
+
artemys discover --requester-agent-id @my-agent --role talent_agent
|
|
121
|
+
artemys talent search --skills TypeScript,React --remote --location "San Francisco"
|
|
122
|
+
artemys talent apply --job-id job-123 --persist
|
|
123
|
+
artemys talent profile --profile-file ./candidate-profile.json --persist
|
|
124
|
+
artemys talent respond --message-id msg-456 --content '{"text":"Sounds great"}'
|
|
125
|
+
artemys rotate-key --agent-id @my-agent --api-key old-key
|
|
126
|
+
artemys parse-resume ./resume.pdf --provider anthropic --api-key sk-... # or set ANTHROPIC_API_KEY
|
|
127
|
+
artemys to-card ./resume.json --output ./card.json
|
|
121
128
|
`);
|
|
122
129
|
}
|
|
123
130
|
function printTalentHelp() {
|
|
@@ -125,20 +132,26 @@ function printTalentHelp() {
|
|
|
125
132
|
artemys talent - Candidate talent workflow commands
|
|
126
133
|
|
|
127
134
|
Subcommands:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
135
|
+
whoami Show current candidate profile summary
|
|
136
|
+
search Search for matching job opportunities
|
|
137
|
+
apply Submit an application for a job posting
|
|
138
|
+
applications List your submitted applications
|
|
139
|
+
status Check inbox messages
|
|
140
|
+
respond Reply to a message in your inbox
|
|
141
|
+
profile Update stored candidate profile
|
|
142
|
+
help Show this help message
|
|
133
143
|
|
|
134
144
|
Examples:
|
|
135
|
-
artemys talent
|
|
145
|
+
artemys talent whoami --agent-card ./agent-card.json --persist
|
|
146
|
+
artemys talent search --agent-card ./agent-card.json --skills TypeScript,React --remote --limit 20
|
|
136
147
|
artemys talent apply --agent-card ./agent-card.json --job-id job-123 --match-reasoning "Strong backend fit"
|
|
148
|
+
artemys talent applications --status pending --agent-card ./agent-card.json
|
|
137
149
|
artemys talent status --agent-card ./agent-card.json --unread-only --persist
|
|
150
|
+
artemys talent respond --agent-card ./agent-card.json --message-id msg-456 --content '{"text":"Interested, available next week"}'
|
|
138
151
|
artemys talent profile --agent-card ./agent-card.json --profile-file ./candidate-profile.json --sync-agent-card --persist ~/.artemys/state.db
|
|
139
152
|
|
|
140
153
|
Common flags:
|
|
141
|
-
--agent-card <path>
|
|
154
|
+
--agent-card <path> Path to agent card (optional if 'artemys start' was run)
|
|
142
155
|
--coffeeshop-url <url> Override Coffee Shop base URL
|
|
143
156
|
--persist [path] Persist profile + conversations to SQLite (default: ~/.artemys/state.db)
|
|
144
157
|
`);
|
|
@@ -148,13 +161,21 @@ async function readAgentCardFromPath(pathValue) {
|
|
|
148
161
|
const parsed = JSON.parse(raw);
|
|
149
162
|
return AgentCardSchema.parse(parsed);
|
|
150
163
|
}
|
|
164
|
+
async function resolveAndReadAgentCard(flags) {
|
|
165
|
+
const cardPath = resolveAgentCardPath(flags);
|
|
166
|
+
if (!cardPath) {
|
|
167
|
+
throw new Error("No agent card found. Run 'artemys start' to get started, or use --agent-card <path>.");
|
|
168
|
+
}
|
|
169
|
+
return readAgentCardFromPath(cardPath);
|
|
170
|
+
}
|
|
151
171
|
function buildNetworkDeps(args, agentCard) {
|
|
152
|
-
const
|
|
153
|
-
const
|
|
154
|
-
|
|
172
|
+
const config = loadConfig();
|
|
173
|
+
const coffeeShopUrl = resolveCoffeeShopUrl(args.flags, config);
|
|
174
|
+
const apiKey = resolveApiKey(config);
|
|
175
|
+
const hasCoffeeShopApiKey = typeof apiKey === "string" && apiKey.length > 0;
|
|
155
176
|
const coffeeShopClient = new CoffeeShopClient({
|
|
156
177
|
baseUrl: coffeeShopUrl,
|
|
157
|
-
apiKey
|
|
178
|
+
apiKey,
|
|
158
179
|
agentId: agentCard.agent_id,
|
|
159
180
|
});
|
|
160
181
|
return {
|
|
@@ -188,48 +209,100 @@ function buildTalentState(flags) {
|
|
|
188
209
|
},
|
|
189
210
|
};
|
|
190
211
|
}
|
|
191
|
-
function
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
};
|
|
198
|
-
registerTalentTools(mockServer, deps);
|
|
199
|
-
registerMessagingTools(mockServer, deps);
|
|
200
|
-
return handlers;
|
|
201
|
-
}
|
|
202
|
-
function extractToolResponseData(response) {
|
|
203
|
-
const structuredData = response.structuredContent?.data;
|
|
204
|
-
if (structuredData !== undefined) {
|
|
205
|
-
return structuredData;
|
|
206
|
-
}
|
|
207
|
-
const textContent = response.content[0]?.text;
|
|
208
|
-
if (typeof textContent !== "string" || textContent.length === 0) {
|
|
209
|
-
return null;
|
|
212
|
+
async function whoamiCommand(args) {
|
|
213
|
+
const config = loadConfig();
|
|
214
|
+
if (!config) {
|
|
215
|
+
console.error("Not initialized. Run 'artemys start'.");
|
|
216
|
+
process.exitCode = 1;
|
|
217
|
+
return;
|
|
210
218
|
}
|
|
219
|
+
const agentCard = await resolveAndReadAgentCard(args.flags);
|
|
220
|
+
const { coffeeShopClient, hasCoffeeShopApiKey } = buildNetworkDeps(args, agentCard);
|
|
221
|
+
let hubStatus = "unknown";
|
|
211
222
|
try {
|
|
212
|
-
|
|
223
|
+
const remote = await coffeeShopClient.getCard(agentCard.agent_id);
|
|
224
|
+
hubStatus = remote ? "connected" : "not found";
|
|
213
225
|
}
|
|
214
226
|
catch {
|
|
215
|
-
|
|
227
|
+
hubStatus = "unreachable";
|
|
216
228
|
}
|
|
229
|
+
const cardPath = resolveAgentCardPath(args.flags);
|
|
230
|
+
const configPath = getConfigPath();
|
|
231
|
+
console.log(`
|
|
232
|
+
Agent Identity
|
|
233
|
+
Handle: ${agentCard.agent_id}
|
|
234
|
+
Display name: ${agentCard.display_name}
|
|
235
|
+
Role: ${agentCard.role}
|
|
236
|
+
Coffee Shop: ${hubStatus}
|
|
237
|
+
API key: ${hasCoffeeShopApiKey ? "configured" : "not set"}
|
|
238
|
+
Agent card: ${cardPath ?? "not found"}
|
|
239
|
+
Config: ${configPath}
|
|
240
|
+
`);
|
|
217
241
|
}
|
|
218
|
-
async function
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
242
|
+
async function doctorCommand(args) {
|
|
243
|
+
const checks = [];
|
|
244
|
+
// 1. Node.js version
|
|
245
|
+
const major = Number.parseInt(process.version.slice(1).split(".")[0], 10);
|
|
246
|
+
checks.push({
|
|
247
|
+
label: "Node.js >= 22",
|
|
248
|
+
ok: major >= 22,
|
|
249
|
+
detail: process.version,
|
|
250
|
+
});
|
|
251
|
+
// 2. Config exists
|
|
252
|
+
const config = loadConfig();
|
|
253
|
+
checks.push({
|
|
254
|
+
label: "Config exists",
|
|
255
|
+
ok: config !== null,
|
|
256
|
+
detail: config ? getConfigPath() : "Run 'artemys start'",
|
|
257
|
+
});
|
|
258
|
+
// 3. Agent card
|
|
259
|
+
let agentCard = null;
|
|
260
|
+
try {
|
|
261
|
+
agentCard = await resolveAndReadAgentCard(args.flags);
|
|
262
|
+
checks.push({ label: "Agent card valid", ok: true });
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
checks.push({
|
|
266
|
+
label: "Agent card valid",
|
|
267
|
+
ok: false,
|
|
268
|
+
detail: err instanceof Error ? err.message : "Failed to read",
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
// 4 & 5. Coffee Shop reachable + API key valid (run in parallel)
|
|
272
|
+
if (config) {
|
|
273
|
+
const coffeeShopUrl = resolveCoffeeShopUrl(args.flags, config);
|
|
274
|
+
const healthCheck = fetch(`${coffeeShopUrl}/health`, {
|
|
275
|
+
signal: AbortSignal.timeout(5_000),
|
|
276
|
+
})
|
|
277
|
+
.then((response) => ({ ok: response.ok, detail: coffeeShopUrl }))
|
|
278
|
+
.catch(() => ({ ok: false, detail: coffeeShopUrl }));
|
|
279
|
+
const apiKeyCheck = agentCard
|
|
280
|
+
? buildNetworkDeps(args, agentCard)
|
|
281
|
+
.coffeeShopClient.getCard(agentCard.agent_id)
|
|
282
|
+
.then((remote) => ({ ok: remote !== null, detail: undefined }))
|
|
283
|
+
.catch(() => ({ ok: false, detail: "getCard failed" }))
|
|
284
|
+
: Promise.resolve({ ok: false, detail: "Skipped (no agent card)" });
|
|
285
|
+
const [healthResult, apiKeyResult] = await Promise.all([healthCheck, apiKeyCheck]);
|
|
286
|
+
checks.push({ label: "Coffee Shop reachable", ...healthResult });
|
|
287
|
+
checks.push({ label: "API key valid", ...apiKeyResult });
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
checks.push({ label: "Coffee Shop reachable", ok: false, detail: "No config" });
|
|
291
|
+
checks.push({ label: "API key valid", ok: false, detail: "Skipped (no config or card)" });
|
|
292
|
+
}
|
|
293
|
+
for (const check of checks) {
|
|
294
|
+
const status = check.ok ? "[OK]" : "[FAIL]";
|
|
295
|
+
const detail = check.detail ? ` (${check.detail})` : "";
|
|
296
|
+
console.log(` ${status} ${check.label}${detail}`);
|
|
297
|
+
}
|
|
298
|
+
const allOk = checks.every((c) => c.ok);
|
|
299
|
+
if (!allOk) {
|
|
300
|
+
process.exitCode = 1;
|
|
222
301
|
}
|
|
223
|
-
const response = await handler(args);
|
|
224
|
-
return extractToolResponseData(response);
|
|
225
302
|
}
|
|
226
303
|
async function registerCommand(args) {
|
|
227
|
-
const
|
|
228
|
-
|
|
229
|
-
throw new Error("Missing required --agent-card <path>");
|
|
230
|
-
}
|
|
231
|
-
const card = await readAgentCardFromPath(cardPath);
|
|
232
|
-
const coffeeShopUrl = getFlagString(args.flags, "coffeeshop-url") ?? DEFAULT_COFFEESHOP_URL;
|
|
304
|
+
const card = await resolveAndReadAgentCard(args.flags);
|
|
305
|
+
const coffeeShopUrl = resolveCoffeeShopUrl(args.flags, loadConfig());
|
|
233
306
|
const client = new CoffeeShopClient({ baseUrl: coffeeShopUrl });
|
|
234
307
|
const result = await client.register(card);
|
|
235
308
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -256,21 +329,22 @@ async function discoverCommand(args) {
|
|
|
256
329
|
...(capabilities ? { capabilities_any: capabilities } : {}),
|
|
257
330
|
...(limitRaw ? { limit: Number.parseInt(limitRaw, 10) } : {}),
|
|
258
331
|
});
|
|
259
|
-
const coffeeShopUrl =
|
|
332
|
+
const coffeeShopUrl = resolveCoffeeShopUrl(args.flags, loadConfig());
|
|
260
333
|
const client = new CoffeeShopClient({ baseUrl: coffeeShopUrl });
|
|
261
334
|
const results = await client.discover(query);
|
|
262
335
|
console.log(JSON.stringify(results, null, 2));
|
|
263
336
|
}
|
|
264
337
|
async function rotateKeyCommand(args) {
|
|
265
|
-
const
|
|
338
|
+
const config = loadConfig();
|
|
339
|
+
const agentId = getFlagString(args.flags, "agent-id") ?? config?.agent_id;
|
|
266
340
|
if (!agentId) {
|
|
267
|
-
throw new Error("Missing required --agent-id <agent-id>");
|
|
341
|
+
throw new Error("Missing required --agent-id <agent-id> (or run 'artemys start')");
|
|
268
342
|
}
|
|
269
|
-
const apiKey = getFlagString(args.flags, "api-key");
|
|
343
|
+
const apiKey = getFlagString(args.flags, "api-key") ?? resolveApiKey(config);
|
|
270
344
|
if (!apiKey) {
|
|
271
|
-
throw new Error("Missing required --api-key <api-key>");
|
|
345
|
+
throw new Error("Missing required --api-key <api-key> (or run 'artemys start')");
|
|
272
346
|
}
|
|
273
|
-
const coffeeShopUrl =
|
|
347
|
+
const coffeeShopUrl = resolveCoffeeShopUrl(args.flags, config);
|
|
274
348
|
const client = new CoffeeShopClient({
|
|
275
349
|
baseUrl: coffeeShopUrl,
|
|
276
350
|
apiKey,
|
|
@@ -278,25 +352,30 @@ async function rotateKeyCommand(args) {
|
|
|
278
352
|
});
|
|
279
353
|
const result = await client.rotateApiKey();
|
|
280
354
|
console.log(JSON.stringify(result, null, 2));
|
|
281
|
-
|
|
355
|
+
// Update config.json if it exists and agent_id matches
|
|
356
|
+
if (config && config.agent_id === agentId) {
|
|
357
|
+
saveConfig({ ...config, api_key: result.api_key });
|
|
358
|
+
console.log("Config updated with new API key.");
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
console.log("Save api_key now. It is only returned at rotation time.");
|
|
362
|
+
}
|
|
282
363
|
}
|
|
283
364
|
async function mcpServerCommand(args) {
|
|
284
|
-
const cardPath = getFlagString(args.flags, "agent-card");
|
|
285
|
-
if (!cardPath) {
|
|
286
|
-
throw new Error("Missing required --agent-card <path>");
|
|
287
|
-
}
|
|
288
365
|
const transport = getFlagString(args.flags, "transport") === "sse" ? "sse" : "stdio";
|
|
289
366
|
const portRaw = getFlagString(args.flags, "port");
|
|
290
367
|
const port = portRaw ? Number.parseInt(portRaw, 10) : 3100;
|
|
291
368
|
const persist = getPersistOption(args.flags);
|
|
292
|
-
const agentCard = await
|
|
369
|
+
const agentCard = await resolveAndReadAgentCard(args.flags);
|
|
293
370
|
const { coffeeShopClient } = buildNetworkDeps(args, agentCard);
|
|
371
|
+
const token = getFlagString(args.flags, "mcp-token") || process.env.ARTEMYS_MCP_TOKEN || undefined;
|
|
294
372
|
const started = await startArtemysMcpServer({
|
|
295
373
|
agentCard,
|
|
296
374
|
coffeeShopClient,
|
|
297
375
|
transport,
|
|
298
376
|
port,
|
|
299
377
|
...(persist ? { persist } : {}),
|
|
378
|
+
...(token ? { token } : {}),
|
|
300
379
|
});
|
|
301
380
|
if (transport === "sse") {
|
|
302
381
|
console.error(`Artemys MCP server listening on http://127.0.0.1:${port}/mcp`);
|
|
@@ -314,25 +393,42 @@ async function talentCommand(args) {
|
|
|
314
393
|
printTalentHelp();
|
|
315
394
|
return;
|
|
316
395
|
}
|
|
317
|
-
const
|
|
318
|
-
if (!cardPath) {
|
|
319
|
-
throw new Error("Missing required --agent-card <path>");
|
|
320
|
-
}
|
|
321
|
-
const agentCard = await readAgentCardFromPath(cardPath);
|
|
396
|
+
const agentCard = await resolveAndReadAgentCard(args.flags);
|
|
322
397
|
const { coffeeShopClient } = buildNetworkDeps(args, agentCard);
|
|
323
398
|
const state = buildTalentState(args.flags);
|
|
324
399
|
try {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
400
|
+
if (subcommand === "whoami") {
|
|
401
|
+
const profile = state.profileStore.getProfile();
|
|
402
|
+
console.log(JSON.stringify({
|
|
403
|
+
agent_id: agentCard.agent_id,
|
|
404
|
+
display_name: agentCard.display_name,
|
|
405
|
+
has_profile: profile !== null,
|
|
406
|
+
profile,
|
|
407
|
+
}, null, 2));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
331
410
|
if (subcommand === "search") {
|
|
411
|
+
const skillsRaw = getFlagString(args.flags, "skills");
|
|
412
|
+
const location = getFlagString(args.flags, "location");
|
|
413
|
+
const remote = getFlagBoolean(args.flags, "remote");
|
|
414
|
+
const minCompRaw = getFlagString(args.flags, "min-compensation");
|
|
415
|
+
const maxCompRaw = getFlagString(args.flags, "max-compensation");
|
|
332
416
|
const limitRaw = getFlagString(args.flags, "limit");
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
417
|
+
const filters = {};
|
|
418
|
+
if (skillsRaw)
|
|
419
|
+
filters.skills = skillsRaw.split(",").map((s) => s.trim());
|
|
420
|
+
if (location)
|
|
421
|
+
filters.location = location;
|
|
422
|
+
if (typeof remote === "boolean")
|
|
423
|
+
filters.remote = remote;
|
|
424
|
+
if (minCompRaw)
|
|
425
|
+
filters.min_compensation = Number(minCompRaw);
|
|
426
|
+
if (maxCompRaw)
|
|
427
|
+
filters.max_compensation = Number(maxCompRaw);
|
|
428
|
+
if (limitRaw)
|
|
429
|
+
filters.limit = Number(limitRaw);
|
|
430
|
+
const hasFilters = Object.keys(filters).length > 0;
|
|
431
|
+
const data = await searchOpportunities(coffeeShopClient, hasFilters ? filters : undefined);
|
|
336
432
|
console.log(JSON.stringify(data, null, 2));
|
|
337
433
|
return;
|
|
338
434
|
}
|
|
@@ -342,21 +438,47 @@ async function talentCommand(args) {
|
|
|
342
438
|
throw new Error("Missing required --job-id <job-id>");
|
|
343
439
|
}
|
|
344
440
|
const matchReasoning = getFlagString(args.flags, "match-reasoning");
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
});
|
|
441
|
+
const storedProfile = state.profileStore.getProfile();
|
|
442
|
+
const snapshot = CandidateSnapshotSchema.parse(storedProfile ?? { display_name: agentCard.display_name });
|
|
443
|
+
const data = await expressInterest(coffeeShopClient, jobId, snapshot, matchReasoning ?? undefined);
|
|
349
444
|
console.log(JSON.stringify(data, null, 2));
|
|
350
445
|
return;
|
|
351
446
|
}
|
|
352
447
|
if (subcommand === "status") {
|
|
353
448
|
const unreadOnly = getFlagBoolean(args.flags, "unread-only");
|
|
354
|
-
const data = await
|
|
355
|
-
|
|
449
|
+
const data = await checkInbox(coffeeShopClient, {
|
|
450
|
+
unreadOnly: typeof unreadOnly === "boolean" ? unreadOnly : false,
|
|
356
451
|
});
|
|
357
452
|
console.log(JSON.stringify(data, null, 2));
|
|
358
453
|
return;
|
|
359
454
|
}
|
|
455
|
+
if (subcommand === "respond") {
|
|
456
|
+
const messageId = getFlagString(args.flags, "message-id");
|
|
457
|
+
if (!messageId) {
|
|
458
|
+
throw new Error("Missing required --message-id <id>");
|
|
459
|
+
}
|
|
460
|
+
const contentRaw = getFlagString(args.flags, "content");
|
|
461
|
+
if (!contentRaw) {
|
|
462
|
+
throw new Error("Missing required --content <json-string>");
|
|
463
|
+
}
|
|
464
|
+
let content;
|
|
465
|
+
try {
|
|
466
|
+
content = JSON.parse(contentRaw);
|
|
467
|
+
if (!content || typeof content !== "object" || Array.isArray(content)) {
|
|
468
|
+
throw new Error("--content must be a JSON object");
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
if (err instanceof SyntaxError) {
|
|
473
|
+
throw new Error("--content must be valid JSON");
|
|
474
|
+
}
|
|
475
|
+
throw err;
|
|
476
|
+
}
|
|
477
|
+
const messageType = getFlagString(args.flags, "message-type");
|
|
478
|
+
const data = await respondToMessage(coffeeShopClient, messageId, content, messageType ?? undefined);
|
|
479
|
+
console.log(JSON.stringify(data, null, 2));
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
360
482
|
if (subcommand === "profile") {
|
|
361
483
|
const profilePath = getFlagString(args.flags, "profile-file");
|
|
362
484
|
if (!profilePath) {
|
|
@@ -368,13 +490,21 @@ async function talentCommand(args) {
|
|
|
368
490
|
throw new Error("Profile file must contain a JSON object");
|
|
369
491
|
}
|
|
370
492
|
const syncAgentCard = getFlagBoolean(args.flags, "sync-agent-card");
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
493
|
+
const snapshot = CandidateSnapshotSchema.parse(parsedProfile);
|
|
494
|
+
const data = await updateProfile(coffeeShopClient, snapshot, {
|
|
495
|
+
profileStore: state.profileStore,
|
|
496
|
+
agentCard,
|
|
497
|
+
syncAgentCard: typeof syncAgentCard === "boolean" ? syncAgentCard : false,
|
|
374
498
|
});
|
|
375
499
|
console.log(JSON.stringify(data, null, 2));
|
|
376
500
|
return;
|
|
377
501
|
}
|
|
502
|
+
if (subcommand === "applications") {
|
|
503
|
+
const status = getFlagString(args.flags, "status");
|
|
504
|
+
const data = await getMyApplications(coffeeShopClient, status ? { status } : undefined);
|
|
505
|
+
console.log(JSON.stringify(data, null, 2));
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
378
508
|
throw new Error(`Unknown talent subcommand: ${subcommand}`);
|
|
379
509
|
}
|
|
380
510
|
finally {
|
|
@@ -398,6 +528,19 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
398
528
|
await versionCommand();
|
|
399
529
|
return;
|
|
400
530
|
}
|
|
531
|
+
if (command === "start" || command === "init") {
|
|
532
|
+
const { initCommand } = await import("./init.js");
|
|
533
|
+
await initCommand(args);
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (command === "whoami") {
|
|
537
|
+
await whoamiCommand(args);
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
if (command === "doctor") {
|
|
541
|
+
await doctorCommand(args);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
401
544
|
if (command === "register") {
|
|
402
545
|
await registerCommand(args);
|
|
403
546
|
return;
|
|
@@ -418,6 +561,67 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
418
561
|
await talentCommand(args);
|
|
419
562
|
return;
|
|
420
563
|
}
|
|
564
|
+
if (command === "parse-resume") {
|
|
565
|
+
const filePath = args.positionals[1];
|
|
566
|
+
if (!filePath) {
|
|
567
|
+
throw new Error("Missing required <file> argument");
|
|
568
|
+
}
|
|
569
|
+
const provider = getFlagString(args.flags, "provider");
|
|
570
|
+
if (!provider) {
|
|
571
|
+
throw new Error("Missing required --provider <provider>");
|
|
572
|
+
}
|
|
573
|
+
const validProviders = ["anthropic", "openai", "google"];
|
|
574
|
+
if (!validProviders.includes(provider)) {
|
|
575
|
+
throw new Error(`Unsupported provider "${provider}". Use: ${validProviders.join(", ")}`);
|
|
576
|
+
}
|
|
577
|
+
const envVarMap = {
|
|
578
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
579
|
+
openai: "OPENAI_API_KEY",
|
|
580
|
+
google: "GOOGLE_AI_API_KEY",
|
|
581
|
+
};
|
|
582
|
+
const envVar = envVarMap[provider];
|
|
583
|
+
const apiKey = getFlagString(args.flags, "api-key")
|
|
584
|
+
|| (envVar ? process.env[envVar] : undefined);
|
|
585
|
+
if (!apiKey) {
|
|
586
|
+
throw new Error(`Missing API key. Set ${envVar} or pass --api-key`);
|
|
587
|
+
}
|
|
588
|
+
const model = getFlagString(args.flags, "model");
|
|
589
|
+
let text;
|
|
590
|
+
if (filePath.toLowerCase().endsWith(".pdf")) {
|
|
591
|
+
const [buffer, { default: pdf }] = await Promise.all([
|
|
592
|
+
readFile(filePath),
|
|
593
|
+
import("pdf-parse"),
|
|
594
|
+
]);
|
|
595
|
+
try {
|
|
596
|
+
text = (await pdf(buffer)).text;
|
|
597
|
+
}
|
|
598
|
+
catch (err) {
|
|
599
|
+
throw new Error(`Failed to parse PDF "${filePath}": ${err instanceof Error ? err.message : String(err)}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
text = await readFile(filePath, "utf-8");
|
|
604
|
+
}
|
|
605
|
+
const { parseResume } = await import("../resume/index.js");
|
|
606
|
+
const result = await parseResume(text, {
|
|
607
|
+
provider: provider,
|
|
608
|
+
apiKey,
|
|
609
|
+
...(model ? { model } : {}),
|
|
610
|
+
});
|
|
611
|
+
await outputJson(result, getFlagString(args.flags, "output"));
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (command === "to-card") {
|
|
615
|
+
const filePath = args.positionals[1];
|
|
616
|
+
if (!filePath) {
|
|
617
|
+
throw new Error("Missing required <file> argument");
|
|
618
|
+
}
|
|
619
|
+
const raw = await readFile(filePath, "utf-8");
|
|
620
|
+
const { toCard, FullResumeSchema } = await import("../resume/index.js");
|
|
621
|
+
const resume = FullResumeSchema.parse(JSON.parse(raw));
|
|
622
|
+
await outputJson(toCard(resume), getFlagString(args.flags, "output"));
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
421
625
|
throw new Error(`Unknown command: ${command}`);
|
|
422
626
|
}
|
|
423
627
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|