@galinum/cli 0.1.0 → 0.2.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.
Files changed (46) hide show
  1. package/README.md +8 -10
  2. package/dist/agent.js +40 -0
  3. package/dist/agent.js.map +1 -0
  4. package/dist/api.js +36 -0
  5. package/dist/api.js.map +1 -0
  6. package/dist/args.js +32 -0
  7. package/dist/args.js.map +1 -0
  8. package/dist/bin.js +7 -0
  9. package/dist/bin.js.map +1 -0
  10. package/{src → dist}/cli.js +31 -32
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands/fetch.js +27 -0
  13. package/dist/commands/fetch.js.map +1 -0
  14. package/dist/commands/identify.js +23 -0
  15. package/dist/commands/identify.js.map +1 -0
  16. package/dist/commands/listen.js +39 -0
  17. package/dist/commands/listen.js.map +1 -0
  18. package/dist/commands/status.js +31 -0
  19. package/dist/commands/status.js.map +1 -0
  20. package/dist/commands/subscribe.js +39 -0
  21. package/dist/commands/subscribe.js.map +1 -0
  22. package/dist/commands/subscriptions.js +12 -0
  23. package/dist/commands/subscriptions.js.map +1 -0
  24. package/dist/commands/unsubscribe.js +17 -0
  25. package/dist/commands/unsubscribe.js.map +1 -0
  26. package/dist/config.js +48 -0
  27. package/dist/config.js.map +1 -0
  28. package/{src → dist}/constants.js +1 -1
  29. package/dist/constants.js.map +1 -0
  30. package/dist/messages.js +38 -0
  31. package/dist/messages.js.map +1 -0
  32. package/dist/types.js +2 -0
  33. package/dist/types.js.map +1 -0
  34. package/package.json +11 -6
  35. package/bin/galinum.js +0 -7
  36. package/src/agent.js +0 -49
  37. package/src/api.js +0 -34
  38. package/src/args.js +0 -35
  39. package/src/commands/fetch.js +0 -25
  40. package/src/commands/identify.js +0 -30
  41. package/src/commands/listen.js +0 -37
  42. package/src/commands/subscribe.js +0 -47
  43. package/src/commands/subscriptions.js +0 -12
  44. package/src/commands/unsubscribe.js +0 -17
  45. package/src/config.js +0 -46
  46. package/src/messages.js +0 -40
package/README.md CHANGED
@@ -16,7 +16,7 @@ Agents typically have no inbox. They run, they exit, and any message that arrive
16
16
  npm install -g @galinum/cli
17
17
  ```
18
18
 
19
- Requires Node.js 20 or later. Zero runtime dependencies.
19
+ Requires Node.js 20 or later. Written in TypeScript; zero runtime dependencies.
20
20
 
21
21
  ## Quick start
22
22
 
@@ -41,19 +41,18 @@ galinum listen
41
41
  Create and store this agent's Galinum identity.
42
42
 
43
43
  ```bash
44
- galinum identify --name <name> [--harness <value>] [--email <email>] [--reset]
44
+ galinum identify --name <name> [--harness <value>] [--reset]
45
45
  ```
46
46
 
47
47
  | Flag | Meaning |
48
48
  | --- | --- |
49
49
  | `--name` | Human-readable agent name. |
50
- | `--harness` | The runtime this agent runs in (`claude-code`, `codex`, `custom`, …). |
51
- | `--email` | Optional contact email. |
50
+ | `--harness` | The runtime this agent runs in (`claude-code`, `codex`, `openclaw`, `hermes`, `custom`, …). |
52
51
  | `--reset` | Replace an existing stored identity. |
53
52
 
54
53
  ### `subscribe`
55
54
 
56
- Subscribe to an organization's inbox using an invite code.
55
+ Subscribe to a project's inbox using an invite code.
57
56
 
58
57
  ```bash
59
58
  galinum subscribe <invite-code>
@@ -74,7 +73,7 @@ galinum subscriptions
74
73
  Remove a local subscription.
75
74
 
76
75
  ```bash
77
- galinum unsubscribe <organization-slug>
76
+ galinum unsubscribe <project-slug>
78
77
  ```
79
78
 
80
79
  ### `fetch`
@@ -82,7 +81,7 @@ galinum unsubscribe <organization-slug>
82
81
  Fetch pending messages once. Messages are acknowledged after they are printed.
83
82
 
84
83
  ```bash
85
- galinum fetch [organization-slug]
84
+ galinum fetch [project-slug]
86
85
  ```
87
86
 
88
87
  ### `listen`
@@ -90,7 +89,7 @@ galinum fetch [organization-slug]
90
89
  Continuously poll for new messages.
91
90
 
92
91
  ```bash
93
- galinum listen [organization-slug] [--interval-seconds 30]
92
+ galinum listen [project-slug] [--interval-seconds 30]
94
93
  ```
95
94
 
96
95
  `Ctrl+C` stops cleanly.
@@ -112,12 +111,11 @@ The CLI stores state in `~/.galinum/config.json` (file mode `0600`, directory mo
112
111
  "id": "agt_…",
113
112
  "name": "my-agent",
114
113
  "harness": "codex",
115
- "contact_email": null,
116
114
  "key": "<bearer key>"
117
115
  },
118
116
  "subscriptions": [
119
117
  {
120
- "org": "acme",
118
+ "project": "acme",
121
119
  "baseUrl": "https://galinum.com",
122
120
  "inboxId": "ibx_…",
123
121
  "key": "<bearer key>"
package/dist/agent.js ADDED
@@ -0,0 +1,40 @@
1
+ import { normalizeBaseUrl, requestJson } from "./api.js";
2
+ import { saveConfig } from "./config.js";
3
+ export function agentPayload(args) {
4
+ return {
5
+ name: typeof args.name === "string" ? args.name : "agent",
6
+ harness: typeof args.harness === "string" ? args.harness : null,
7
+ };
8
+ }
9
+ export function sameAgentMetadata(agent, payload) {
10
+ return (agent.name === payload.name &&
11
+ (agent.harness ?? null) === payload.harness);
12
+ }
13
+ export async function createAgent(args, payload) {
14
+ const baseUrl = normalizeBaseUrl(args["base-url"]);
15
+ const data = (await requestJson(`${baseUrl}/api/agents/identify`, {
16
+ method: "POST",
17
+ body: JSON.stringify(payload),
18
+ }));
19
+ if (!data?.id || !data?.name || !data?.key) {
20
+ throw new Error("Server response missing agent identity");
21
+ }
22
+ return {
23
+ id: data.id,
24
+ name: data.name,
25
+ harness: data.harness ?? null,
26
+ key: data.key,
27
+ };
28
+ }
29
+ export async function ensureIdentified(args, config) {
30
+ if (config.agent?.key)
31
+ return config.agent;
32
+ if (config.agent) {
33
+ throw new Error(`Stored identity ${config.agent.id} has no agent key. Re-run \`galinum identify --reset\` to create a new identity.`);
34
+ }
35
+ config.agent = await createAgent(args, agentPayload({ ...args, name: "agent", _: args._ }));
36
+ await saveConfig(args, config);
37
+ console.log(`Identified as ${config.agent.id}.`);
38
+ return config.agent;
39
+ }
40
+ //# sourceMappingURL=agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,UAAU,YAAY,CAAC,IAAgB;IAC3C,OAAO;QACL,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO;QACzD,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;KAChE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAoB,EAAE,OAAwB;IAC9E,OAAO,CACL,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI;QAC3B,CAAC,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,OAAO,CAAC,OAAO,CAC5C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,IAAgB,EAChB,OAAwB;IAExB,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,WAAW,CAAC,GAAG,OAAO,sBAAsB,EAAE;QAChE,MAAM,EAAE,MAAM;QACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAkC,CAAC;IAErC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,IAAI;QAC7B,GAAG,EAAE,IAAI,CAAC,GAAG;KACd,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,IAAgB,EAChB,MAAc;IAEd,IAAI,MAAM,CAAC,KAAK,EAAE,GAAG;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC;IAC3C,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,mBAAmB,MAAM,CAAC,KAAK,CAAC,EAAE,kFAAkF,CACrH,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,YAAY,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5F,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC"}
package/dist/api.js ADDED
@@ -0,0 +1,36 @@
1
+ import { DEFAULT_BASE_URL } from "./constants.js";
2
+ export class GalinumRequestError extends Error {
3
+ status;
4
+ data;
5
+ constructor(status, data) {
6
+ const detail = data && typeof data === "object" && "error" in data
7
+ ? `: ${String(data.error)}`
8
+ : "";
9
+ super(`Galinum request failed (${status})${detail}`);
10
+ this.status = status;
11
+ this.data = data;
12
+ }
13
+ }
14
+ export function normalizeBaseUrl(value) {
15
+ const raw = typeof value === "string" && value.length > 0 ? value : DEFAULT_BASE_URL;
16
+ const url = new URL(raw);
17
+ url.pathname = "";
18
+ url.search = "";
19
+ url.hash = "";
20
+ return url.toString().replace(/\/$/, "");
21
+ }
22
+ export async function requestJson(url, options = {}) {
23
+ const res = await fetch(url, {
24
+ ...options,
25
+ headers: {
26
+ "Content-Type": "application/json",
27
+ ...(options.headers ?? {}),
28
+ },
29
+ });
30
+ const data = (await res.json().catch(() => null));
31
+ if (!res.ok) {
32
+ throw new GalinumRequestError(res.status, data);
33
+ }
34
+ return data;
35
+ }
36
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACnC,MAAM,CAAS;IACf,IAAI,CAAU;IAEvB,YAAY,MAAc,EAAE,IAAa;QACvC,MAAM,MAAM,GACV,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI;YACjD,CAAC,CAAC,KAAK,MAAM,CAAE,IAA2B,CAAC,KAAK,CAAC,EAAE;YACnD,CAAC,CAAC,EAAE,CAAC;QACT,KAAK,CAAC,2BAA2B,MAAM,IAAI,MAAM,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,CAAC;IACrF,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACzB,GAAG,CAAC,QAAQ,GAAG,EAAE,CAAC;IAClB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC;IAChB,GAAG,CAAC,IAAI,GAAG,EAAE,CAAC;IACd,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,GAAW,EAAE,UAAuB,EAAE;IACtE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,GAAG,OAAO;QACV,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,GAAG,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;SAC3B;KACF,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAY,CAAC;IAC7D,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
package/dist/args.js ADDED
@@ -0,0 +1,32 @@
1
+ import path from "node:path";
2
+ import { DEFAULT_CONFIG_PATH } from "./constants.js";
3
+ export function parseArgs(argv) {
4
+ const args = { _: [] };
5
+ for (let i = 0; i < argv.length; i += 1) {
6
+ const arg = argv[i];
7
+ if (!arg.startsWith("--")) {
8
+ args._.push(arg);
9
+ continue;
10
+ }
11
+ const key = arg.slice(2);
12
+ const next = argv[i + 1];
13
+ if (!next || next.startsWith("--")) {
14
+ args[key] = true;
15
+ continue;
16
+ }
17
+ args[key] = next;
18
+ i += 1;
19
+ }
20
+ return args;
21
+ }
22
+ export function invocationName() {
23
+ const argv1 = process.argv[1] ?? "";
24
+ const base = path.basename(argv1);
25
+ if (base === "galinum" || base === "galinum-cli")
26
+ return base;
27
+ return `node ${argv1}`;
28
+ }
29
+ export function configPath(args) {
30
+ return typeof args.config === "string" ? args.config : DEFAULT_CONFIG_PATH;
31
+ }
32
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGrD,MAAM,UAAU,SAAS,CAAC,IAAc;IACtC,MAAM,IAAI,GAAe,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC;IACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QACrB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACjB,SAAS;QACX,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;YACjB,SAAS;QACX,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;QACjB,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,aAAa;QAAE,OAAO,IAAI,CAAC;IAC9D,OAAO,QAAQ,KAAK,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAgB;IACzC,OAAO,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC;AAC7E,CAAC"}
package/dist/bin.js ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "./cli.js";
3
+ run(process.argv.slice(2)).catch((err) => {
4
+ console.error(err instanceof Error ? err.message : String(err));
5
+ process.exitCode = 1;
6
+ });
7
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAChD,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -5,55 +5,54 @@ import { listSubscriptions } from "./commands/subscriptions.js";
5
5
  import { unsubscribe } from "./commands/unsubscribe.js";
6
6
  import { fetchMessages } from "./commands/fetch.js";
7
7
  import { listen } from "./commands/listen.js";
8
-
8
+ import { status } from "./commands/status.js";
9
9
  function usage() {
10
- const bin = invocationName();
11
- console.log(`Usage:
12
- ${bin} identify --name name [--harness value] [--email email] [--reset]
10
+ const bin = invocationName();
11
+ console.log(`Usage:
12
+ ${bin} identify --name name [--harness value] [--reset]
13
13
  ${bin} subscribe <invite-code>
14
14
  ${bin} subscriptions
15
- ${bin} unsubscribe <organization-slug>
16
- ${bin} fetch [organization-slug]
17
- ${bin} listen [organization-slug] [--interval-seconds 30]
15
+ ${bin} unsubscribe <project-slug>
16
+ ${bin} fetch [project-slug]
17
+ ${bin} listen [project-slug] [--interval-seconds 30]
18
+ ${bin} status
18
19
 
19
20
  Commands:
20
21
  identify Create and store this agent's Galinum identity.
21
- subscribe Subscribe to an organization using an invite code.
22
+ subscribe Subscribe to a project using an invite code.
22
23
  subscriptions List local subscriptions.
23
24
  unsubscribe Remove a local subscription.
24
25
  fetch Fetch pending messages once (all subscriptions, or one).
25
26
  listen Repeatedly fetch new messages on an interval.
27
+ status Show the stored agent identity and subscriptions.
26
28
 
27
29
  Global flags:
28
30
  --config <path> Override config location (default: ~/.galinum/config.json).
29
31
  --base-url <url> Override Galinum API base URL.
30
32
  `);
31
33
  }
32
-
33
34
  const COMMANDS = {
34
- identify,
35
- subscribe,
36
- subscriptions: listSubscriptions,
37
- unsubscribe,
38
- fetch: fetchMessages,
39
- listen,
35
+ identify,
36
+ subscribe,
37
+ subscriptions: listSubscriptions,
38
+ unsubscribe,
39
+ fetch: fetchMessages,
40
+ listen,
41
+ status,
40
42
  };
41
-
42
43
  export async function run(argv) {
43
- const args = parseArgs(argv);
44
- const command = args._[0];
45
-
46
- if (!command || command === "help" || args.help) {
47
- usage();
48
- return;
49
- }
50
-
51
- const handler = COMMANDS[command];
52
- if (!handler) {
53
- usage();
54
- process.exitCode = 1;
55
- return;
56
- }
57
-
58
- await handler(args);
44
+ const args = parseArgs(argv);
45
+ const command = args._[0];
46
+ if (!command || command === "help" || args.help) {
47
+ usage();
48
+ return;
49
+ }
50
+ const handler = COMMANDS[command];
51
+ if (!handler) {
52
+ usage();
53
+ process.exitCode = 1;
54
+ return;
55
+ }
56
+ await handler(args);
59
57
  }
58
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAG9C,SAAS,KAAK;IACZ,MAAM,GAAG,GAAG,cAAc,EAAE,CAAC;IAC7B,OAAO,CAAC,GAAG,CAAC;IACV,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;IACH,GAAG;;;;;;;;;;;;;;CAcN,CAAC,CAAC;AACH,CAAC;AAID,MAAM,QAAQ,GAAmC;IAC/C,QAAQ;IACR,SAAS;IACT,aAAa,EAAE,iBAAiB;IAChC,WAAW;IACX,KAAK,EAAE,aAAa;IACpB,MAAM;IACN,MAAM;CACP,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,GAAG,CAAC,IAAc;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1B,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAChD,KAAK,EAAE,CAAC;QACR,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAClC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,KAAK,EAAE,CAAC;QACR,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { loadConfig } from "../config.js";
2
+ import { fetchSubscription } from "../messages.js";
3
+ export async function fetchMessages(args) {
4
+ const projectSlug = args._[1];
5
+ const quiet = args.quiet === true;
6
+ const config = await loadConfig(args);
7
+ if (config.subscriptions.length === 0) {
8
+ if (!quiet) {
9
+ console.log("No subscriptions. Use `galinum subscribe <invite-code>` first.");
10
+ }
11
+ return 0;
12
+ }
13
+ const targets = projectSlug
14
+ ? config.subscriptions.filter((s) => s.project === projectSlug)
15
+ : config.subscriptions;
16
+ if (targets.length === 0) {
17
+ if (!quiet)
18
+ console.log(`Not subscribed to "${projectSlug}".`);
19
+ return 0;
20
+ }
21
+ let total = 0;
22
+ for (const sub of targets) {
23
+ total += await fetchSubscription(sub, quiet);
24
+ }
25
+ return total;
26
+ }
27
+ //# sourceMappingURL=fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/commands/fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGnD,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAgB;IAClD,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IAEtC,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,gEAAgE,CAAC,CAAC;QAChF,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,OAAO,GAAG,WAAW;QACzB,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,WAAW,CAAC;QAC/D,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;IACzB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,WAAW,IAAI,CAAC,CAAC;QAC/D,OAAO,CAAC,CAAC;IACX,CAAC;IACD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,KAAK,IAAI,MAAM,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { configPath } from "../args.js";
2
+ import { loadConfig, saveConfig } from "../config.js";
3
+ import { agentPayload, createAgent, sameAgentMetadata } from "../agent.js";
4
+ export async function identify(args) {
5
+ const config = await loadConfig(args);
6
+ const payload = agentPayload(args);
7
+ if (config.agent && !args.reset) {
8
+ if (!config.agent.key) {
9
+ throw new Error(`Stored identity ${config.agent.id} predates agent auth and has no key. Re-run with --reset to create a new identity.`);
10
+ }
11
+ if (sameAgentMetadata(config.agent, payload)) {
12
+ console.log(`Already identified as ${config.agent.id}.`);
13
+ return config.agent;
14
+ }
15
+ throw new Error(`Already identified as ${config.agent.id}. To update metadata, edit ${configPath(args)} or re-run with --reset.`);
16
+ }
17
+ config.agent = await createAgent(args, payload);
18
+ const file = await saveConfig(args, config);
19
+ console.log(`Identified as ${config.agent.id}.`);
20
+ console.log(`Config: ${file}`);
21
+ return config.agent;
22
+ }
23
+ //# sourceMappingURL=identify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identify.js","sourceRoot":"","sources":["../../src/commands/identify.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG3E,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAgB;IAC7C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAEnC,IAAI,MAAM,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,mBAAmB,MAAM,CAAC,KAAK,CAAC,EAAE,oFAAoF,CACvH,CAAC;QACJ,CAAC;QACD,IAAI,iBAAiB,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;YAC7C,OAAO,CAAC,GAAG,CAAC,yBAAyB,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;YACzD,OAAO,MAAM,CAAC,KAAK,CAAC;QACtB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,yBAAyB,MAAM,CAAC,KAAK,CAAC,EAAE,8BAA8B,UAAU,CAAC,IAAI,CAAC,0BAA0B,CACjH,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,KAAK,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,KAAK,CAAC;AACtB,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { DEFAULT_POLL_INTERVAL_MS } from "../constants.js";
2
+ import { fetchMessages } from "./fetch.js";
3
+ function sleep(ms, signal) {
4
+ return new Promise((resolve) => {
5
+ const timer = setTimeout(resolve, ms);
6
+ if (signal) {
7
+ const onAbort = () => {
8
+ clearTimeout(timer);
9
+ resolve();
10
+ };
11
+ signal.addEventListener("abort", onAbort, { once: true });
12
+ }
13
+ });
14
+ }
15
+ export async function listen(args) {
16
+ const raw = args["interval-seconds"];
17
+ const intervalSeconds = typeof raw === "string" ? Number(raw) : 30;
18
+ const intervalMs = Number.isFinite(intervalSeconds)
19
+ ? Math.max(5, intervalSeconds) * 1000
20
+ : DEFAULT_POLL_INTERVAL_MS;
21
+ console.log("Listening for Galinum messages.");
22
+ console.log("Press Ctrl+C to stop.");
23
+ const controller = new AbortController();
24
+ const stop = () => controller.abort();
25
+ process.once("SIGINT", stop);
26
+ process.once("SIGTERM", stop);
27
+ const innerArgs = {
28
+ ...args,
29
+ _: ["fetch", ...(args._[1] ? [args._[1]] : [])],
30
+ quiet: true,
31
+ };
32
+ while (!controller.signal.aborted) {
33
+ await fetchMessages(innerArgs);
34
+ if (controller.signal.aborted)
35
+ break;
36
+ await sleep(intervalMs, controller.signal);
37
+ }
38
+ }
39
+ //# sourceMappingURL=listen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.js","sourceRoot":"","sources":["../../src/commands/listen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAG3C,SAAS,KAAK,CAAC,EAAU,EAAE,MAAoB;IAC7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACtC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;YACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAgB;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACrC,MAAM,eAAe,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACnE,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;QACjD,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,eAAe,CAAC,GAAG,IAAI;QACrC,CAAC,CAAC,wBAAwB,CAAC;IAE7B,OAAO,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;IAErC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAE9B,MAAM,SAAS,GAAe;QAC5B,GAAG,IAAI;QACP,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/C,KAAK,EAAE,IAAI;KACZ,CAAC;IACF,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QAClC,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM;QACrC,MAAM,KAAK,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC"}
@@ -0,0 +1,31 @@
1
+ import { configPath } from "../args.js";
2
+ import { loadConfig } from "../config.js";
3
+ function padLabel(label, width = 9) {
4
+ return label.length >= width ? `${label} ` : label.padEnd(width);
5
+ }
6
+ export async function status(args) {
7
+ const file = configPath(args);
8
+ const config = await loadConfig(args);
9
+ console.log(`Config: ${file}`);
10
+ console.log("");
11
+ if (!config.agent) {
12
+ console.log("Agent identity: none. Run `galinum identify --name <name>` to create one.");
13
+ }
14
+ else {
15
+ console.log("Agent identity:");
16
+ console.log(` ${padLabel("id")}${config.agent.id}`);
17
+ console.log(` ${padLabel("name")}${config.agent.name}`);
18
+ console.log(` ${padLabel("harness")}${config.agent.harness ?? "(none)"}`);
19
+ console.log(` ${padLabel("key")}${config.agent.key ? "present" : "missing"}`);
20
+ }
21
+ console.log("");
22
+ if (config.subscriptions.length === 0) {
23
+ console.log("Subscriptions: none. Run `galinum subscribe <invite-code>` to add one.");
24
+ return;
25
+ }
26
+ console.log(`Subscriptions (${config.subscriptions.length}):`);
27
+ for (const sub of config.subscriptions) {
28
+ console.log(` ${sub.project}\t${sub.inboxId}\t${sub.baseUrl}`);
29
+ }
30
+ }
31
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,SAAS,QAAQ,CAAC,KAAa,EAAE,KAAK,GAAG,CAAC;IACxC,OAAO,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAgB;IAC3C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IAEtC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,2EAA2E,CAAC,CAAC;IAC3F,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,QAAQ,EAAE,CAAC,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,KAAK,QAAQ,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;IACjF,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,wEAAwE,CAAC,CAAC;QACtF,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,CAAC;IAC/D,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { INVITE_CODE_REGEX } from "../constants.js";
2
+ import { normalizeBaseUrl, requestJson } from "../api.js";
3
+ import { loadConfig, saveConfig } from "../config.js";
4
+ import { ensureIdentified } from "../agent.js";
5
+ export async function subscribe(args) {
6
+ const inviteCode = args._[1];
7
+ if (!inviteCode) {
8
+ throw new Error("Invite code is required. Usage: galinum subscribe <invite-code>");
9
+ }
10
+ if (!INVITE_CODE_REGEX.test(inviteCode)) {
11
+ throw new Error(`Invalid invite code "${inviteCode}". Expected format: inv_<16-128 url-safe chars>.`);
12
+ }
13
+ const baseUrl = normalizeBaseUrl(args["base-url"]);
14
+ const config = await loadConfig(args);
15
+ const agent = await ensureIdentified(args, config);
16
+ const data = (await requestJson(`${baseUrl}/api/agent-inboxes`, {
17
+ method: "POST",
18
+ headers: { Authorization: `Bearer ${agent.key}` },
19
+ body: JSON.stringify({ invite_code: inviteCode }),
20
+ }));
21
+ const project = data?.project?.id;
22
+ if (!project || !data?.id || !data?.key) {
23
+ throw new Error("Server response missing inbox credentials");
24
+ }
25
+ const existing = config.subscriptions.findIndex((s) => s.project === project);
26
+ if (existing !== -1)
27
+ config.subscriptions.splice(existing, 1);
28
+ config.subscriptions.push({
29
+ project,
30
+ baseUrl,
31
+ inboxId: data.id,
32
+ key: data.key,
33
+ });
34
+ const file = await saveConfig(args, config);
35
+ console.log(`Subscribed to ${project}.`);
36
+ console.log(`Inbox ID: ${data.id}`);
37
+ console.log(`Config: ${file}`);
38
+ }
39
+ //# sourceMappingURL=subscribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscribe.js","sourceRoot":"","sources":["../../src/commands/subscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAS/C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAgB;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,iEAAiE,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,wBAAwB,UAAU,kDAAkD,CACrF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAEnD,MAAM,IAAI,GAAG,CAAC,MAAM,WAAW,CAAC,GAAG,OAAO,oBAAoB,EAAE;QAC9D,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,CAAC,GAAG,EAAE,EAAE;QACjD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KAClD,CAAC,CAA6B,CAAC;IAEhC,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;IAClC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAC9E,IAAI,QAAQ,KAAK,CAAC,CAAC;QAAE,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IAE9D,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC;QACxB,OAAO;QACP,OAAO;QACP,OAAO,EAAE,IAAI,CAAC,EAAE;QAChB,GAAG,EAAE,IAAI,CAAC,GAAG;KACd,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAE5C,OAAO,CAAC,GAAG,CAAC,iBAAiB,OAAO,GAAG,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { loadConfig } from "../config.js";
2
+ export async function listSubscriptions(args) {
3
+ const config = await loadConfig(args);
4
+ if (config.subscriptions.length === 0) {
5
+ console.log("No subscriptions. Use `galinum subscribe <invite-code>` to add one.");
6
+ return;
7
+ }
8
+ for (const sub of config.subscriptions) {
9
+ console.log(`${sub.project}\t${sub.inboxId}\t${sub.baseUrl}`);
10
+ }
11
+ }
12
+ //# sourceMappingURL=subscriptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscriptions.js","sourceRoot":"","sources":["../../src/commands/subscriptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,IAAgB;IACtD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,qEAAqE,CAAC,CAAC;QACnF,OAAO;IACT,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAChE,CAAC;AACH,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { loadConfig, saveConfig } from "../config.js";
2
+ export async function unsubscribe(args) {
3
+ const projectSlug = args._[1];
4
+ if (!projectSlug) {
5
+ throw new Error("Project slug is required. Usage: galinum unsubscribe <slug>");
6
+ }
7
+ const config = await loadConfig(args);
8
+ const idx = config.subscriptions.findIndex((s) => s.project === projectSlug);
9
+ if (idx === -1) {
10
+ console.log(`Not subscribed to "${projectSlug}".`);
11
+ return;
12
+ }
13
+ config.subscriptions.splice(idx, 1);
14
+ await saveConfig(args, config);
15
+ console.log(`Unsubscribed from ${projectSlug}.`);
16
+ }
17
+ //# sourceMappingURL=unsubscribe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unsubscribe.js","sourceRoot":"","sources":["../../src/commands/unsubscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAGtD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAgB;IAChD,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC;IAC7E,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,sBAAsB,WAAW,IAAI,CAAC,CAAC;QACnD,OAAO;IACT,CAAC;IACD,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;IACpC,MAAM,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,CAAC,GAAG,CAAC,qBAAqB,WAAW,GAAG,CAAC,CAAC;AACnD,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,48 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { configPath } from "./args.js";
4
+ function emptyConfig() {
5
+ return { subscriptions: [] };
6
+ }
7
+ function normalizeAgent(agent) {
8
+ if (!agent || typeof agent !== "object")
9
+ return null;
10
+ const a = agent;
11
+ if (typeof a.id !== "string" || typeof a.name !== "string")
12
+ return null;
13
+ return {
14
+ id: a.id,
15
+ name: a.name,
16
+ harness: typeof a.harness === "string" ? a.harness : null,
17
+ key: typeof a.key === "string" ? a.key : null,
18
+ };
19
+ }
20
+ function normalizeConfig(parsed) {
21
+ const p = (parsed && typeof parsed === "object" ? parsed : {});
22
+ const subscriptions = Array.isArray(p.subscriptions)
23
+ ? p.subscriptions
24
+ : [];
25
+ const config = { subscriptions };
26
+ const agent = normalizeAgent(p.agent);
27
+ if (agent)
28
+ config.agent = agent;
29
+ return config;
30
+ }
31
+ export async function loadConfig(args) {
32
+ const file = configPath(args);
33
+ try {
34
+ const parsed = JSON.parse(await readFile(file, "utf8"));
35
+ return normalizeConfig(parsed);
36
+ }
37
+ catch {
38
+ return emptyConfig();
39
+ }
40
+ }
41
+ export async function saveConfig(args, config) {
42
+ const file = configPath(args);
43
+ // Restrict permissions — config holds bearer tokens.
44
+ await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
45
+ await writeFile(file, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
46
+ return file;
47
+ }
48
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC,SAAS,WAAW;IAClB,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrD,MAAM,CAAC,GAAG,KAAgC,CAAC;IAC3C,IAAI,OAAO,CAAC,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,OAAO,EAAE,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;QACzD,GAAG,EAAE,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI;KAC9C,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,MAAe;IACtC,MAAM,CAAC,GAAG,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;IAC1F,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAC;QAClD,CAAC,CAAE,CAAC,CAAC,aAAgC;QACrC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,MAAM,GAAW,EAAE,aAAa,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC;IAChC,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAgB;IAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACxD,OAAO,eAAe,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,WAAW,EAAE,CAAC;IACvB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAgB,EAAE,MAAc;IAC/D,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,qDAAqD;IACrD,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAClE,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/E,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import os from "node:os";
2
2
  import path from "node:path";
3
-
4
3
  export const DEFAULT_BASE_URL = "https://galinum.com";
5
4
  export const DEFAULT_CONFIG_PATH = path.join(os.homedir(), ".galinum", "config.json");
6
5
  export const DEFAULT_POLL_INTERVAL_MS = 30_000;
7
6
  export const INVITE_CODE_REGEX = /^inv_[A-Za-z0-9_-]{16,128}$/;
7
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;AACtD,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;AACtF,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAC/C,MAAM,CAAC,MAAM,iBAAiB,GAAG,6BAA6B,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { requestJson } from "./api.js";
2
+ export function printMessage(projectSlug, message) {
3
+ console.log("");
4
+ console.log(`[${projectSlug}] [${message.created_at}] ${message.title}`);
5
+ console.log(`Type: ${message.type}`);
6
+ console.log(message.body);
7
+ if (message.payload !== null && message.payload !== undefined) {
8
+ console.log("Payload:");
9
+ console.log(JSON.stringify(message.payload, null, 2));
10
+ }
11
+ }
12
+ async function ackMessages(subscription, messageIds) {
13
+ if (messageIds.length === 0)
14
+ return;
15
+ const url = `${subscription.baseUrl}/api/agent-inbox/messages?inbox_id=${encodeURIComponent(subscription.inboxId)}`;
16
+ await requestJson(url, {
17
+ method: "POST",
18
+ headers: { Authorization: `Bearer ${subscription.key}` },
19
+ body: JSON.stringify({ message_ids: messageIds }),
20
+ });
21
+ }
22
+ export async function fetchSubscription(subscription, quiet) {
23
+ const url = `${subscription.baseUrl}/api/agent-inbox/messages?inbox_id=${encodeURIComponent(subscription.inboxId)}`;
24
+ const data = (await requestJson(url, {
25
+ headers: { Authorization: `Bearer ${subscription.key}` },
26
+ }));
27
+ const messages = Array.isArray(data?.messages) ? data.messages : [];
28
+ if (messages.length === 0) {
29
+ if (!quiet)
30
+ console.log(`[${subscription.project}] No pending messages.`);
31
+ return 0;
32
+ }
33
+ for (const message of messages)
34
+ printMessage(subscription.project, message);
35
+ await ackMessages(subscription, messages.map((m) => m.id));
36
+ return messages.length;
37
+ }
38
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,MAAM,UAAU,YAAY,CAAC,WAAmB,EAAE,OAAqB;IACrE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,IAAI,WAAW,MAAM,OAAO,CAAC,UAAU,KAAK,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,SAAS,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,OAAO,CAAC,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,YAA0B,EAAE,UAAoB;IACzE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IACpC,MAAM,GAAG,GAAG,GAAG,YAAY,CAAC,OAAO,sCAAsC,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;IACpH,MAAM,WAAW,CAAC,GAAG,EAAE;QACrB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,YAAY,CAAC,GAAG,EAAE,EAAE;QACxD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KAClD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,YAA0B,EAC1B,KAAc;IAEd,MAAM,GAAG,GAAG,GAAG,YAAY,CAAC,OAAO,sCAAsC,kBAAkB,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;IACpH,MAAM,IAAI,GAAG,CAAC,MAAM,WAAW,CAAC,GAAG,EAAE;QACnC,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,YAAY,CAAC,GAAG,EAAE,EAAE;KACzD,CAAC,CAAyC,CAAC;IAE5C,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACpE,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,YAAY,CAAC,OAAO,wBAAwB,CAAC,CAAC;QAC1E,OAAO,CAAC,CAAC;IACX,CAAC;IACD,KAAK,MAAM,OAAO,IAAI,QAAQ;QAAE,YAAY,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5E,MAAM,WAAW,CACf,YAAY,EACZ,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAC1B,CAAC;IACF,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzB,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@galinum/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Universal agent inbox CLI — receive durable messages from any product that integrates with the Galinum SDK, in any agent harness.",
5
5
  "keywords": [
6
6
  "agents",
@@ -24,11 +24,10 @@
24
24
  },
25
25
  "type": "module",
26
26
  "bin": {
27
- "galinum": "./bin/galinum.js"
27
+ "galinum": "./dist/bin.js"
28
28
  },
29
29
  "files": [
30
- "bin",
31
- "src",
30
+ "dist",
32
31
  "LICENSE",
33
32
  "README.md"
34
33
  ],
@@ -36,8 +35,14 @@
36
35
  "node": ">=20"
37
36
  },
38
37
  "scripts": {
39
- "test": "node --test test/*.test.js",
40
- "start": "node bin/galinum.js"
38
+ "build": "tsc && chmod +x dist/bin.js",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "tsc -p tsconfig.test.json && node --test dist-test/test/*.test.js",
41
+ "prepublishOnly": "npm run test && npm run build"
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.6.0",
45
+ "@types/node": "^20.0.0"
41
46
  },
42
47
  "publishConfig": {
43
48
  "access": "public"
package/bin/galinum.js DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env node
2
- import { run } from "../src/cli.js";
3
-
4
- run(process.argv.slice(2)).catch((err) => {
5
- console.error(err instanceof Error ? err.message : String(err));
6
- process.exitCode = 1;
7
- });
package/src/agent.js DELETED
@@ -1,49 +0,0 @@
1
- import { normalizeBaseUrl, requestJson } from "./api.js";
2
- import { saveConfig } from "./config.js";
3
-
4
- export function agentPayload(args) {
5
- return {
6
- name: args.name || "agent",
7
- harness: args.harness || null,
8
- contact_email: args.email || null,
9
- };
10
- }
11
-
12
- export function sameAgentMetadata(agent, payload) {
13
- return (
14
- agent.name === payload.name &&
15
- (agent.harness ?? null) === payload.harness &&
16
- (agent.contact_email ?? null) === payload.contact_email
17
- );
18
- }
19
-
20
- export async function createAgent(args, payload) {
21
- const baseUrl = normalizeBaseUrl(args["base-url"]);
22
- const data = await requestJson(`${baseUrl}/api/agents/identify`, {
23
- method: "POST",
24
- body: JSON.stringify(payload),
25
- });
26
- if (!data?.id || !data?.name || !data?.key) {
27
- throw new Error("Server response missing agent identity");
28
- }
29
- return {
30
- id: data.id,
31
- name: data.name,
32
- harness: data.harness ?? null,
33
- contact_email: data.contact_email ?? null,
34
- key: data.key,
35
- };
36
- }
37
-
38
- export async function ensureIdentified(args, config) {
39
- if (config.agent?.key) return config.agent;
40
- if (config.agent) {
41
- throw new Error(
42
- `Stored identity ${config.agent.id} has no agent key. Re-run \`galinum identify --reset\` to create a new identity.`,
43
- );
44
- }
45
- config.agent = await createAgent(args, agentPayload({ ...args, name: "agent" }));
46
- await saveConfig(args, config);
47
- console.log(`Identified as ${config.agent.id}.`);
48
- return config.agent;
49
- }
package/src/api.js DELETED
@@ -1,34 +0,0 @@
1
- import { DEFAULT_BASE_URL } from "./constants.js";
2
-
3
- export class GalinumRequestError extends Error {
4
- constructor(status, data) {
5
- const detail = data?.error ? `: ${data.error}` : "";
6
- super(`Galinum request failed (${status})${detail}`);
7
- this.status = status;
8
- this.data = data;
9
- }
10
- }
11
-
12
- export function normalizeBaseUrl(value) {
13
- const url = new URL(value || DEFAULT_BASE_URL);
14
- url.pathname = "";
15
- url.search = "";
16
- url.hash = "";
17
- return url.toString().replace(/\/$/, "");
18
- }
19
-
20
- export async function requestJson(url, options = {}) {
21
- const res = await fetch(url, {
22
- ...options,
23
- headers: {
24
- "Content-Type": "application/json",
25
- ...(options.headers ?? {}),
26
- },
27
- });
28
-
29
- const data = await res.json().catch(() => null);
30
- if (!res.ok) {
31
- throw new GalinumRequestError(res.status, data);
32
- }
33
- return data;
34
- }
package/src/args.js DELETED
@@ -1,35 +0,0 @@
1
- import path from "node:path";
2
- import { DEFAULT_CONFIG_PATH } from "./constants.js";
3
-
4
- export function parseArgs(argv) {
5
- const args = { _: [] };
6
- for (let i = 0; i < argv.length; i += 1) {
7
- const arg = argv[i];
8
- if (!arg.startsWith("--")) {
9
- args._.push(arg);
10
- continue;
11
- }
12
-
13
- const key = arg.slice(2);
14
- const next = argv[i + 1];
15
- if (!next || next.startsWith("--")) {
16
- args[key] = true;
17
- continue;
18
- }
19
-
20
- args[key] = next;
21
- i += 1;
22
- }
23
- return args;
24
- }
25
-
26
- export function invocationName() {
27
- const argv1 = process.argv[1] ?? "";
28
- const base = path.basename(argv1);
29
- if (base === "galinum" || base === "galinum-cli") return base;
30
- return `node ${argv1}`;
31
- }
32
-
33
- export function configPath(args) {
34
- return typeof args.config === "string" ? args.config : DEFAULT_CONFIG_PATH;
35
- }
@@ -1,25 +0,0 @@
1
- import { loadConfig } from "../config.js";
2
- import { fetchSubscription } from "../messages.js";
3
-
4
- export async function fetchMessages(args) {
5
- const orgSlug = args._[1];
6
- const config = await loadConfig(args);
7
- if (config.subscriptions.length === 0) {
8
- if (!args.quiet) {
9
- console.log("No subscriptions. Use `galinum subscribe <invite-code>` first.");
10
- }
11
- return 0;
12
- }
13
- const targets = orgSlug
14
- ? config.subscriptions.filter((s) => s.org === orgSlug)
15
- : config.subscriptions;
16
- if (targets.length === 0) {
17
- if (!args.quiet) console.log(`Not subscribed to "${orgSlug}".`);
18
- return 0;
19
- }
20
- let total = 0;
21
- for (const sub of targets) {
22
- total += await fetchSubscription(sub, args.quiet);
23
- }
24
- return total;
25
- }
@@ -1,30 +0,0 @@
1
- import { configPath } from "../args.js";
2
- import { loadConfig, saveConfig } from "../config.js";
3
- import { agentPayload, createAgent, sameAgentMetadata } from "../agent.js";
4
-
5
- export async function identify(args) {
6
- const config = await loadConfig(args);
7
- const payload = agentPayload(args);
8
-
9
- if (config.agent && !args.reset) {
10
- if (!config.agent.key) {
11
- throw new Error(
12
- `Stored identity ${config.agent.id} predates agent auth and has no key. Re-run with --reset to create a new identity.`,
13
- );
14
- }
15
- if (sameAgentMetadata(config.agent, payload)) {
16
- console.log(`Already identified as ${config.agent.id}.`);
17
- return config.agent;
18
- }
19
- throw new Error(
20
- `Already identified as ${config.agent.id}. To update metadata, edit ${configPath(args)} or re-run with --reset.`,
21
- );
22
- }
23
-
24
- config.agent = await createAgent(args, payload);
25
- const file = await saveConfig(args, config);
26
-
27
- console.log(`Identified as ${config.agent.id}.`);
28
- console.log(`Config: ${file}`);
29
- return config.agent;
30
- }
@@ -1,37 +0,0 @@
1
- import { DEFAULT_POLL_INTERVAL_MS } from "../constants.js";
2
- import { fetchMessages } from "./fetch.js";
3
-
4
- function sleep(ms, signal) {
5
- return new Promise((resolve) => {
6
- const timer = setTimeout(resolve, ms);
7
- if (signal) {
8
- const onAbort = () => {
9
- clearTimeout(timer);
10
- resolve();
11
- };
12
- signal.addEventListener("abort", onAbort, { once: true });
13
- }
14
- });
15
- }
16
-
17
- export async function listen(args) {
18
- const intervalSeconds = Number(args["interval-seconds"] ?? "30");
19
- const intervalMs = Number.isFinite(intervalSeconds)
20
- ? Math.max(5, intervalSeconds) * 1000
21
- : DEFAULT_POLL_INTERVAL_MS;
22
-
23
- console.log("Listening for Galinum messages.");
24
- console.log("Press Ctrl+C to stop.");
25
-
26
- const controller = new AbortController();
27
- const stop = () => controller.abort();
28
- process.once("SIGINT", stop);
29
- process.once("SIGTERM", stop);
30
-
31
- const innerArgs = { ...args, _: ["fetch", ...(args._[1] ? [args._[1]] : [])], quiet: true };
32
- while (!controller.signal.aborted) {
33
- await fetchMessages(innerArgs);
34
- if (controller.signal.aborted) break;
35
- await sleep(intervalMs, controller.signal);
36
- }
37
- }
@@ -1,47 +0,0 @@
1
- import { INVITE_CODE_REGEX } from "../constants.js";
2
- import { normalizeBaseUrl, requestJson } from "../api.js";
3
- import { loadConfig, saveConfig } from "../config.js";
4
- import { ensureIdentified } from "../agent.js";
5
-
6
- export async function subscribe(args) {
7
- const inviteCode = args._[1];
8
- if (!inviteCode) {
9
- throw new Error("Invite code is required. Usage: galinum subscribe <invite-code>");
10
- }
11
- if (!INVITE_CODE_REGEX.test(inviteCode)) {
12
- throw new Error(
13
- `Invalid invite code "${inviteCode}". Expected format: inv_<16-128 url-safe chars>.`,
14
- );
15
- }
16
-
17
- const baseUrl = normalizeBaseUrl(args["base-url"]);
18
- const config = await loadConfig(args);
19
- const agent = await ensureIdentified(args, config);
20
-
21
- const data = await requestJson(`${baseUrl}/api/agent-inboxes`, {
22
- method: "POST",
23
- headers: { Authorization: `Bearer ${agent.key}` },
24
- body: JSON.stringify({ invite_code: inviteCode }),
25
- });
26
-
27
- const org = data.organization?.id;
28
- if (!org || !data.id || !data.key) {
29
- throw new Error("Server response missing inbox credentials");
30
- }
31
-
32
- const existing = config.subscriptions.findIndex((s) => s.org === org);
33
- if (existing !== -1) config.subscriptions.splice(existing, 1);
34
-
35
- config.subscriptions.push({
36
- org,
37
- baseUrl,
38
- inboxId: data.id,
39
- key: data.key,
40
- });
41
-
42
- const file = await saveConfig(args, config);
43
-
44
- console.log(`Subscribed to ${org}.`);
45
- console.log(`Inbox ID: ${data.id}`);
46
- console.log(`Config: ${file}`);
47
- }
@@ -1,12 +0,0 @@
1
- import { loadConfig } from "../config.js";
2
-
3
- export async function listSubscriptions(args) {
4
- const config = await loadConfig(args);
5
- if (config.subscriptions.length === 0) {
6
- console.log("No subscriptions. Use `galinum subscribe <invite-code>` to add one.");
7
- return;
8
- }
9
- for (const sub of config.subscriptions) {
10
- console.log(`${sub.org}\t${sub.inboxId}\t${sub.baseUrl}`);
11
- }
12
- }
@@ -1,17 +0,0 @@
1
- import { loadConfig, saveConfig } from "../config.js";
2
-
3
- export async function unsubscribe(args) {
4
- const orgSlug = args._[1];
5
- if (!orgSlug) {
6
- throw new Error("Organization slug is required. Usage: galinum unsubscribe <slug>");
7
- }
8
- const config = await loadConfig(args);
9
- const idx = config.subscriptions.findIndex((s) => s.org === orgSlug);
10
- if (idx === -1) {
11
- console.log(`Not subscribed to "${orgSlug}".`);
12
- return;
13
- }
14
- config.subscriptions.splice(idx, 1);
15
- await saveConfig(args, config);
16
- console.log(`Unsubscribed from ${orgSlug}.`);
17
- }
package/src/config.js DELETED
@@ -1,46 +0,0 @@
1
- import { mkdir, readFile, writeFile } from "node:fs/promises";
2
- import path from "node:path";
3
- import { configPath } from "./args.js";
4
-
5
- function emptyConfig() {
6
- return { subscriptions: [] };
7
- }
8
-
9
- function normalizeAgent(agent) {
10
- if (!agent || typeof agent !== "object") return null;
11
- if (typeof agent.id !== "string" || typeof agent.name !== "string") return null;
12
- return {
13
- id: agent.id,
14
- name: agent.name,
15
- harness: typeof agent.harness === "string" ? agent.harness : null,
16
- contact_email: typeof agent.contact_email === "string" ? agent.contact_email : null,
17
- key: typeof agent.key === "string" ? agent.key : null,
18
- };
19
- }
20
-
21
- function normalizeConfig(parsed) {
22
- const config = {
23
- subscriptions: Array.isArray(parsed?.subscriptions) ? parsed.subscriptions : [],
24
- };
25
- const agent = normalizeAgent(parsed?.agent);
26
- if (agent) config.agent = agent;
27
- return config;
28
- }
29
-
30
- export async function loadConfig(args) {
31
- const file = configPath(args);
32
- try {
33
- const parsed = JSON.parse(await readFile(file, "utf8"));
34
- return normalizeConfig(parsed);
35
- } catch {
36
- return emptyConfig();
37
- }
38
- }
39
-
40
- export async function saveConfig(args, config) {
41
- const file = configPath(args);
42
- // Restrict permissions — config holds bearer tokens.
43
- await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
44
- await writeFile(file, `${JSON.stringify(config, null, 2)}\n`, { mode: 0o600 });
45
- return file;
46
- }
package/src/messages.js DELETED
@@ -1,40 +0,0 @@
1
- import { requestJson } from "./api.js";
2
-
3
- export function printMessage(orgSlug, message) {
4
- console.log("");
5
- console.log(`[${orgSlug}] [${message.created_at}] ${message.title}`);
6
- console.log(`Type: ${message.type}`);
7
- console.log(message.body);
8
- if (message.payload !== null && message.payload !== undefined) {
9
- console.log("Payload:");
10
- console.log(JSON.stringify(message.payload, null, 2));
11
- }
12
- }
13
-
14
- async function ackMessages(subscription, messageIds) {
15
- if (messageIds.length === 0) return;
16
- const url = `${subscription.baseUrl}/api/agent-inbox/messages?inbox_id=${encodeURIComponent(subscription.inboxId)}`;
17
- await requestJson(url, {
18
- method: "POST",
19
- headers: { Authorization: `Bearer ${subscription.key}` },
20
- body: JSON.stringify({ message_ids: messageIds }),
21
- });
22
- }
23
-
24
- export async function fetchSubscription(subscription, quiet) {
25
- const url = `${subscription.baseUrl}/api/agent-inbox/messages?inbox_id=${encodeURIComponent(subscription.inboxId)}`;
26
- const data = await requestJson(url, {
27
- headers: { Authorization: `Bearer ${subscription.key}` },
28
- });
29
- const messages = Array.isArray(data.messages) ? data.messages : [];
30
- if (messages.length === 0) {
31
- if (!quiet) console.log(`[${subscription.org}] No pending messages.`);
32
- return 0;
33
- }
34
- for (const message of messages) printMessage(subscription.org, message);
35
- await ackMessages(
36
- subscription,
37
- messages.map((m) => m.id),
38
- );
39
- return messages.length;
40
- }