@holon-run/agentinbox 1.0.0-beta.3 → 1.0.0-beta.4

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/README.md CHANGED
@@ -85,7 +85,7 @@ database.
85
85
  Requires:
86
86
 
87
87
  - Node.js 20 or newer
88
- - `uxc` 0.15.0 or newer if you want to use GitHub or Feishu adapters:
88
+ - `uxc` 0.15.3 or newer if you want to use GitHub or Feishu adapters:
89
89
  https://github.com/holon-run/uxc
90
90
 
91
91
  Install globally:
@@ -44,7 +44,9 @@ class AdapterRegistry {
44
44
  remoteModuleRegistry;
45
45
  constructor(store, appendSourceEvent, options) {
46
46
  this.homeDir = options?.homeDir ?? (0, paths_1.resolveAgentInboxHome)(process.env);
47
- this.remoteModuleRegistry = options?.remoteModuleRegistry ?? new remote_modules_1.RemoteSourceModuleRegistry();
47
+ this.remoteModuleRegistry = options?.remoteModuleRegistry ?? new remote_modules_1.RemoteSourceModuleRegistry({
48
+ githubCallClient: options?.githubCallClient,
49
+ });
48
50
  this.remoteSource = new remote_1.RemoteSourceRuntime(store, appendSourceEvent, {
49
51
  homeDir: this.homeDir,
50
52
  client: options?.remoteSourceClient,
@@ -140,6 +142,31 @@ class AdapterRegistry {
140
142
  }
141
143
  return this.remoteSource.expandSubscriptionShortcut(source, input);
142
144
  }
145
+ async listFollowTemplates(source) {
146
+ if (source.sourceType === "local_event") {
147
+ return [];
148
+ }
149
+ return this.remoteSource.listFollowTemplates(source);
150
+ }
151
+ async expandFollowTemplate(source, input) {
152
+ if (source.sourceType === "local_event") {
153
+ return null;
154
+ }
155
+ return this.remoteSource.expandFollowTemplate(source, input);
156
+ }
157
+ followPreviewRefsForProviderOrKind(providerOrKind, config) {
158
+ const trimmed = providerOrKind.trim();
159
+ if (trimmed.length === 0) {
160
+ throw new Error("follow requires a providerOrKind");
161
+ }
162
+ if (isExplicitFollowPreviewRef(trimmed)) {
163
+ return [trimmed];
164
+ }
165
+ const candidates = hasRemoteSourceModulePath(config)
166
+ ? ["remote_source", ...(0, remote_modules_1.builtinRemoteSourceTypes)()]
167
+ : (0, remote_modules_1.builtinRemoteSourceTypes)();
168
+ return Array.from(new Set(candidates));
169
+ }
143
170
  async deriveInlinePreview(source, item) {
144
171
  if (source.sourceType === "local_event") {
145
172
  return null;
@@ -152,6 +179,15 @@ class AdapterRegistry {
152
179
  }
153
180
  return this.remoteSource.deriveNotificationGrouping(source, item);
154
181
  }
182
+ async getSourceRuntime(source) {
183
+ if (source.sourceType === "local_event") {
184
+ return null;
185
+ }
186
+ return this.remoteSource.getSourceRuntime(source);
187
+ }
188
+ async listSourceRuntimes(sources) {
189
+ return this.remoteSource.listSourceRuntimes(sources);
190
+ }
155
191
  async summarizeDigestThread(source, items, grouping) {
156
192
  if (source.sourceType === "local_event") {
157
193
  return null;
@@ -194,6 +230,18 @@ class AdapterRegistry {
194
230
  }
195
231
  }
196
232
  exports.AdapterRegistry = AdapterRegistry;
233
+ function isExplicitFollowPreviewRef(value) {
234
+ return (value === "local_event" ||
235
+ value === "remote_source" ||
236
+ value === "github_repo" ||
237
+ value === "github_repo_ci" ||
238
+ value === "feishu_bot" ||
239
+ value.startsWith("remote:"));
240
+ }
241
+ function hasRemoteSourceModulePath(config) {
242
+ const modulePath = config?.modulePath;
243
+ return typeof modulePath === "string" && modulePath.trim().length > 0;
244
+ }
197
245
  function syntheticBuiltinSource(sourceType) {
198
246
  return {
199
247
  sourceId: `builtin:${sourceType}`,
package/dist/src/cli.js CHANGED
@@ -182,6 +182,47 @@ async function main() {
182
182
  await printRemote(client, `/streams/${encodeURIComponent(streamRef)}/schema`, undefined, "GET");
183
183
  return;
184
184
  }
185
+ if (command === "follow") {
186
+ const args = normalized.slice(1);
187
+ const positionals = positionalArgs(args, [
188
+ "--agent-id",
189
+ "--args-json",
190
+ "--arg",
191
+ "--config-json",
192
+ "--config-ref",
193
+ "--start-policy",
194
+ "--start-offset",
195
+ "--start-time",
196
+ ]);
197
+ const [providerOrKind, template] = positionals;
198
+ if (!providerOrKind || !template || positionals[2]) {
199
+ throw new Error("usage: agentinbox follow <providerOrKind> <template> [--agent-id ID] [--args-json JSON | --arg KEY=VALUE ...] [--config-json JSON] [--config-ref REF] [--start-policy POLICY] [--start-offset N] [--start-time ISO8601]");
200
+ }
201
+ const argsJson = takeFlagValue(normalized, "--args-json");
202
+ const hasArgFlags = hasFlag(normalized, "--arg");
203
+ if (argsJson != null && hasArgFlags) {
204
+ throw new Error("follow accepts either --args-json or repeated --arg flags, but not both");
205
+ }
206
+ const selection = await selectAgentForCommand(client, {
207
+ explicitAgentId: takeFlagValue(normalized, "--agent-id"),
208
+ autoRegister: true,
209
+ });
210
+ const response = await requestRemote(client, "/follow", {
211
+ agentId: selection.agentId,
212
+ providerOrKind,
213
+ template,
214
+ templateArgs: argsJson != null
215
+ ? (0, util_1.parseJsonArg)(argsJson, "--args-json")
216
+ : readFollowTemplateArgs(normalized),
217
+ configRef: takeFlagValue(normalized, "--config-ref") ?? undefined,
218
+ config: (0, util_1.parseJsonArg)(takeFlagValue(normalized, "--config-json")),
219
+ startPolicy: takeFlagValue(normalized, "--start-policy") ?? undefined,
220
+ startOffset: parseOptionalNumber(takeFlagValue(normalized, "--start-offset")),
221
+ startTime: takeFlagValue(normalized, "--start-time") ?? undefined,
222
+ });
223
+ console.log((0, util_1.jsonResponse)(withCommandMetadata(response.data, selection)));
224
+ return;
225
+ }
185
226
  if (command === "stream" && normalized[1] === "poll") {
186
227
  const streamId = normalized[2];
187
228
  if (!streamId) {
@@ -1065,6 +1106,50 @@ function readSubscriptionFilter(args) {
1065
1106
  }
1066
1107
  return {};
1067
1108
  }
1109
+ function readFollowTemplateArgs(args) {
1110
+ const parsed = {};
1111
+ for (let index = 0; index < args.length; index += 1) {
1112
+ if (args[index] !== "--arg") {
1113
+ continue;
1114
+ }
1115
+ const raw = args[index + 1];
1116
+ if (!raw || raw.startsWith("--")) {
1117
+ throw new Error("flag --arg requires KEY=VALUE");
1118
+ }
1119
+ const separator = raw.indexOf("=");
1120
+ if (separator <= 0) {
1121
+ throw new Error("flag --arg requires KEY=VALUE");
1122
+ }
1123
+ const key = raw.slice(0, separator).trim();
1124
+ const rawValue = raw.slice(separator + 1);
1125
+ if (key.length === 0) {
1126
+ throw new Error("flag --arg requires a non-empty key");
1127
+ }
1128
+ parsed[key] = parseFollowTemplateArgValue(rawValue);
1129
+ index += 1;
1130
+ }
1131
+ return parsed;
1132
+ }
1133
+ function parseFollowTemplateArgValue(raw) {
1134
+ if (raw === "true") {
1135
+ return true;
1136
+ }
1137
+ if (raw === "false") {
1138
+ return false;
1139
+ }
1140
+ if (/^-?\d+(?:\.\d+)?$/.test(raw)) {
1141
+ return Number(raw);
1142
+ }
1143
+ if ((raw.startsWith("[") && raw.endsWith("]")) || (raw.startsWith("{") && raw.endsWith("}")) || raw === "null") {
1144
+ try {
1145
+ return JSON.parse(raw);
1146
+ }
1147
+ catch {
1148
+ return raw;
1149
+ }
1150
+ }
1151
+ return raw;
1152
+ }
1068
1153
  function withCommandMetadata(data, selection) {
1069
1154
  return {
1070
1155
  ...data,
@@ -1142,6 +1227,7 @@ Usage:
1142
1227
  Commands:
1143
1228
  serve
1144
1229
  daemon
1230
+ follow
1145
1231
  source
1146
1232
  agent
1147
1233
  timer
@@ -1187,6 +1273,11 @@ Usage:
1187
1273
  agentinbox stream schema preview <sourceKind|sourceType> [--config-json JSON] [--config-ref REF]
1188
1274
  agentinbox stream poll <streamId>
1189
1275
  agentinbox stream event <streamId> --native-id ID --event EVENT [--occurred-at ISO8601] [--metadata-json JSON] [--payload-json JSON]
1276
+ `,
1277
+ follow: `agentinbox follow
1278
+
1279
+ Usage:
1280
+ agentinbox follow <providerOrKind> <template> [--agent-id ID] [--args-json JSON | --arg KEY=VALUE ...] [--config-json JSON] [--config-ref REF] [--start-policy POLICY] [--start-offset N] [--start-time ISO8601]
1190
1281
  `,
1191
1282
  source: `agentinbox source
1192
1283
 
package/dist/src/http.js CHANGED
@@ -29,6 +29,10 @@ const errorResponseSchema = {
29
29
  error: { type: "string" },
30
30
  },
31
31
  };
32
+ const subscriptionStartPolicySchema = {
33
+ type: "string",
34
+ enum: ["latest", "earliest", "at_offset", "at_time"],
35
+ };
32
36
  const subscriptionCollectionSchema = {
33
37
  type: "object",
34
38
  required: ["subscriptions"],
@@ -286,7 +290,7 @@ function buildFastifyServer(service) {
286
290
  },
287
291
  },
288
292
  },
289
- }, async () => ({ sources: service.listSources() }));
293
+ }, async () => ({ sources: await service.listSourceViews() }));
290
294
  app.post("/sources", {
291
295
  schema: {
292
296
  tags: ["sources"],
@@ -464,6 +468,48 @@ function buildFastifyServer(service) {
464
468
  },
465
469
  },
466
470
  }, async (request) => service.previewSourceSchema(request.body));
471
+ app.post("/follow", {
472
+ schema: {
473
+ tags: ["subscriptions"],
474
+ body: {
475
+ type: "object",
476
+ additionalProperties: false,
477
+ required: ["agentId", "providerOrKind", "template"],
478
+ properties: {
479
+ agentId: { type: "string", minLength: 1 },
480
+ providerOrKind: { type: "string", minLength: 1 },
481
+ template: { type: "string", minLength: 1 },
482
+ templateArgs: jsonObjectSchema,
483
+ configRef: { anyOf: [{ type: "string" }, { type: "null" }] },
484
+ config: jsonObjectSchema,
485
+ startPolicy: subscriptionStartPolicySchema,
486
+ startOffset: { type: "integer" },
487
+ startTime: { type: "string" },
488
+ },
489
+ },
490
+ response: {
491
+ 200: jsonObjectSchema,
492
+ 400: errorResponseSchema,
493
+ },
494
+ },
495
+ }, async (request) => {
496
+ const body = request.body;
497
+ return service.follow({
498
+ agentId: String(body.agentId),
499
+ providerOrKind: String(body.providerOrKind),
500
+ template: String(body.template),
501
+ templateArgs: body.templateArgs && typeof body.templateArgs === "object"
502
+ ? body.templateArgs
503
+ : undefined,
504
+ configRef: optionalString(body.configRef) ?? null,
505
+ config: body.config && typeof body.config === "object"
506
+ ? body.config
507
+ : undefined,
508
+ startPolicy: optionalString(body.startPolicy),
509
+ startOffset: typeof body.startOffset === "number" ? body.startOffset : undefined,
510
+ startTime: optionalString(body.startTime) ?? undefined,
511
+ });
512
+ });
467
513
  app.post("/streams/:streamId/poll", {
468
514
  schema: {
469
515
  tags: ["sources"],
@@ -1083,7 +1129,7 @@ function buildFastifyServer(service) {
1083
1129
  filter: jsonObjectSchema,
1084
1130
  trackedResourceRef: { type: "string" },
1085
1131
  cleanupPolicy: jsonObjectSchema,
1086
- startPolicy: { type: "string" },
1132
+ startPolicy: subscriptionStartPolicySchema,
1087
1133
  startOffset: { type: "integer" },
1088
1134
  startTime: { type: "string" },
1089
1135
  },
@@ -1204,7 +1250,7 @@ function buildFastifyServer(service) {
1204
1250
  additionalProperties: false,
1205
1251
  required: ["startPolicy"],
1206
1252
  properties: {
1207
- startPolicy: { type: "string", minLength: 1 },
1253
+ startPolicy: subscriptionStartPolicySchema,
1208
1254
  startOffset: { type: "integer" },
1209
1255
  startTime: { type: "string" },
1210
1256
  },
@@ -1690,6 +1736,10 @@ function isBadRequestError(message) {
1690
1736
  message.startsWith("cleanupPolicy ") ||
1691
1737
  message.startsWith("trackedResourceRef ") ||
1692
1738
  message.startsWith("preview failed: ") ||
1739
+ message.startsWith("follow preview failed: ") ||
1740
+ message.startsWith("follow template ") ||
1741
+ message.startsWith("unknown follow template ") ||
1742
+ message.startsWith("follow requires a providerOrKind") ||
1693
1743
  message.startsWith("unknown source kind or type for preview") ||
1694
1744
  message.startsWith("preview source kind ") ||
1695
1745
  message.startsWith("subscription add shortcut") ||