@indigoai-us/hq-cli 5.279.0 → 5.281.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.
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bash
2
+ # ontology-source-worklist.sh — list a declared source's unprocessed items with
3
+ # each item's resolved audience. The ontology worker's process-source skill
4
+ # reads this list, extracts candidates from each item, and marks it done.
5
+ # Specs: source-yaml-spec.md, ontology-local-spec.md (core/knowledge/public/hq-core/)
6
+ #
7
+ # Usage:
8
+ # ontology-source-worklist.sh --company <co> --channel <ch> [--limit N] [--force]
9
+ # ontology-source-worklist.sh --company <co> --channel <ch> --mark-done <item-file>
10
+ #
11
+ # Output: one JSON object per line:
12
+ # {"file":"sources/meetings/x.md","audience_key":"…","audience":["prs_…","a@x.com"]}
13
+ # {"file":"sources/meetings/y.md","skip":"no-audience"}
14
+ # Audience by audience_rule:
15
+ # attendees / thread / channel — the item's `audience:` or `attendees:` list if
16
+ # it has one, else the direct read grants on the item's vault key
17
+ # (hq files acl), which is how cloud ingestion records who was privy.
18
+ # explicit — the item's own `audience:` frontmatter only.
19
+ # company — "company".
20
+ # An item with no resolvable audience is emitted as a skip, never as company.
21
+ # Exit 0; 2 bad usage / invalid source.yaml; 4 run: cloud source without --force.
22
+ set -uo pipefail
23
+ HQ="${HQ_BIN:-hq}"
24
+ root="${HQ_ROOT:-$(cd "$(dirname "$0")/../.." && pwd)}"
25
+ here="$(cd "$(dirname "$0")" && pwd)"
26
+ co="" ch="" limit=50 force=0 done_item=""
27
+ while [ $# -gt 0 ]; do
28
+ case "$1" in
29
+ --company) co="$2"; shift 2 ;; --channel) ch="$2"; shift 2 ;; --limit) limit="$2"; shift 2 ;;
30
+ --force) force=1; shift ;; --mark-done) done_item="$2"; shift 2 ;;
31
+ *) echo "unknown flag $1" >&2; exit 2 ;;
32
+ esac
33
+ done
34
+ [ -n "$co" ] && [ -n "$ch" ] || { echo "usage: ontology-source-worklist.sh --company <co> --channel <ch> [--limit N] [--force] [--mark-done <file>]" >&2; exit 2; }
35
+ D="$root/companies/$co/sources/$ch"; Y="$D/source.yaml"; ledger="$D/.processed"
36
+ bash "$here/source-yaml-validate.sh" "$Y" >/dev/null || { bash "$here/source-yaml-validate.sh" "$Y" >&2; exit 2; }
37
+
38
+ if [ -n "$done_item" ]; then echo "$(basename "$done_item")" >> "$ledger"; exit 0; fi
39
+
40
+ get() { awk -v k="$1" '$0 ~ "^"k":" { sub("^"k":[[:space:]]*", ""); gsub(/^["\x27]|["\x27]$/, ""); print; exit }' "$Y"; }
41
+ run="$(get run)"; rule="$(get audience_rule)"
42
+ if [ "$run" = cloud ] && [ "$force" = 0 ]; then echo "assigned to cloud agent (run: cloud); pass --force to run locally" >&2; exit 4; fi
43
+
44
+ item_list() { # frontmatter list field: inline [a, b] or block "- a"
45
+ awk -v k="$2" '
46
+ BEGIN{fm=0}
47
+ /^---$/ { fm++; if (fm==2) exit; next }
48
+ fm==1 && $0 ~ "^"k":" { v=$0; sub("^"k":[[:space:]]*", "", v)
49
+ if (v ~ /^\[/) { gsub(/[\[\]"\x27]/, "", v); n=split(v, a, ","); for(i=1;i<=n;i++){gsub(/^[ \t]+|[ \t]+$/, "", a[i]); if(a[i]!="") print a[i]}; exit }
50
+ inlist=1; next }
51
+ fm==1 && inlist && /^[[:space:]]*-[[:space:]]/ { v=$0; sub(/^[[:space:]]*-[[:space:]]*/, "", v); gsub(/["\x27]/, "", v); print v; next }
52
+ fm==1 && inlist { exit }
53
+ ' "$1"
54
+ }
55
+
56
+ n=0
57
+ for f in "$D"/*.md; do
58
+ [ -f "$f" ] || continue
59
+ b="$(basename "$f")"
60
+ [ -f "$ledger" ] && grep -qxF "$b" "$ledger" && continue
61
+ [ "$n" -ge "$limit" ] && break
62
+ n=$((n+1))
63
+ rel="sources/$ch/$b"
64
+ aud=""
65
+ case "$rule" in
66
+ company) aud="company" ;;
67
+ explicit) aud="$(item_list "$f" audience | paste -sd, -)" ;;
68
+ *)
69
+ aud="$(item_list "$f" audience | paste -sd, -)"
70
+ [ -n "$aud" ] || aud="$(item_list "$f" attendees | grep -E '@|^(prs|agt)_' | paste -sd, -)"
71
+ if [ -z "$aud" ]; then
72
+ aud="$("$HQ" files --company "$co" acl "$rel" --json 2>/dev/null \
73
+ | jq -r '[.direct[]? | select(.granteeType=="person" or .granteeType=="email" or .granteeType=="agent") | select(.permission=="read" or .permission=="write" or .permission=="admin") | .granteeId] | unique | join(",")' 2>/dev/null)"
74
+ fi ;;
75
+ esac
76
+ if [ -z "$aud" ]; then jq -cn --arg f "$rel" '{file:$f, skip:"no-audience"}'; continue; fi
77
+ key="$(HQ_ROOT="$root" bash "$here/ontology-candidate.sh" key "$aud")"
78
+ jq -cn --arg f "$rel" --arg k "$key" --arg a "$aud" '{file:$f, audience_key:$k, audience:($a|split(","))}'
79
+ done
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env bash
2
+ # source-yaml-validate.sh — validate a sources/{channel}/source.yaml.
3
+ # Spec: core/knowledge/public/hq-core/source-yaml-spec.md
4
+ # Usage: source-yaml-validate.sh <path/to/source.yaml>
5
+ # Exit 0 valid; 1 invalid (each violation printed); 2 bad usage / missing file.
6
+ set -uo pipefail
7
+ f="${1:-}"
8
+ [ -n "$f" ] || { echo "usage: source-yaml-validate.sh <source.yaml>" >&2; exit 2; }
9
+ [ -f "$f" ] || { echo "source-yaml-validate: no such file $f" >&2; exit 2; }
10
+
11
+ get() {
12
+ awk -v k="$1" '$0 ~ "^"k":" { sub("^"k":[[:space:]]*", ""); sub("[[:space:]]+#.*$", ""); gsub(/^["\x27]|["\x27]$/, ""); print; exit }' "$f"
13
+ }
14
+ v=0
15
+ bad() { echo "$f: $*"; v=$((v+1)); }
16
+ oneof() { case " $3 " in *" $2 "*) ;; *) bad "$1 must be one of: $3 (got '${2:-<missing>}')" ;; esac; }
17
+
18
+ channel="$(get channel)"; kind="$(get kind)"; arrives="$(get arrives)"; pull="$(get pull_command)"
19
+ aud="$(get audience_rule)"; proc="$(get processor)"; run="$(get run)"; sched="$(get schedule)"
20
+
21
+ [ -n "$channel" ] || bad "channel is required"
22
+ folder="$(basename "$(dirname "$f")")"
23
+ [ -z "$channel" ] || [ "$channel" = "$folder" ] || bad "channel '$channel' must equal its folder name '$folder'"
24
+ oneof kind "$kind" "meeting email slack doc custom"
25
+ oneof arrives "$arrives" "drop pull cloud"
26
+ [ "$arrives" != pull ] || [ -n "$pull" ] || bad "pull_command is required when arrives: pull"
27
+ [ -n "$aud" ] || bad "audience_rule is required (attendees|thread|channel|company|explicit); it is never defaulted"
28
+ [ -z "$aud" ] || oneof audience_rule "$aud" "attendees thread channel company explicit"
29
+ [ -n "$proc" ] || bad "processor is required (<worker>/<skill>)"
30
+ [ -z "$proc" ] || printf '%s' "$proc" | grep -Eq '^[a-z0-9-]+/[a-z0-9-]+$' || bad "processor must look like <worker>/<skill> (got '$proc')"
31
+ oneof run "$run" "local cloud"
32
+ if [ -z "$sched" ]; then bad "schedule is required (cron or on-close)"
33
+ elif [ "$sched" != on-close ] && [ "$(printf '%s' "$sched" | awk '{print NF}')" -ne 5 ]; then bad "schedule must be a 5-field cron or on-close (got '$sched')"; fi
34
+
35
+ [ "$v" -eq 0 ] && { echo "$f: ok"; exit 0; }
36
+ exit 1
@@ -1623,14 +1623,14 @@ export declare const COMMAND_CATALOG: readonly [{
1623
1623
  readonly subcommands: readonly [];
1624
1624
  }, {
1625
1625
  readonly name: "outbound";
1626
- readonly description: "List grants a source-company group holds on other companies";
1626
+ readonly description: "List grants a source-company group holds on other companies; requires --group <groupId>.";
1627
1627
  readonly aliases: readonly [];
1628
1628
  readonly hidden: false;
1629
1629
  readonly usage: "[options]";
1630
1630
  readonly arguments: readonly [];
1631
1631
  readonly options: readonly [{
1632
1632
  readonly flags: "--group <groupId>";
1633
- readonly description: "Group id to inspect";
1633
+ readonly description: "Group id to inspect (required)";
1634
1634
  }];
1635
1635
  readonly subcommands: readonly [];
1636
1636
  }, {
@@ -5205,6 +5205,84 @@ export declare const COMMAND_CATALOG: readonly [{
5205
5205
  readonly description: "HQ installation to operate on";
5206
5206
  }];
5207
5207
  readonly subcommands: readonly [];
5208
+ }, {
5209
+ readonly name: "audience-grant";
5210
+ readonly description: "Grant scoped ontology audiences and verify each grant";
5211
+ readonly aliases: readonly [];
5212
+ readonly hidden: false;
5213
+ readonly usage: "[args...]";
5214
+ readonly arguments: readonly [{
5215
+ readonly name: "args";
5216
+ readonly required: false;
5217
+ readonly variadic: true;
5218
+ }];
5219
+ readonly options: readonly [];
5220
+ readonly subcommands: readonly [];
5221
+ }, {
5222
+ readonly name: "brief";
5223
+ readonly description: "Render the local ontology company brief";
5224
+ readonly aliases: readonly [];
5225
+ readonly hidden: false;
5226
+ readonly usage: "[args...]";
5227
+ readonly arguments: readonly [{
5228
+ readonly name: "args";
5229
+ readonly required: false;
5230
+ readonly variadic: true;
5231
+ }];
5232
+ readonly options: readonly [];
5233
+ readonly subcommands: readonly [];
5234
+ }, {
5235
+ readonly name: "candidate";
5236
+ readonly description: "Write a deterministic ontology candidate";
5237
+ readonly aliases: readonly [];
5238
+ readonly hidden: false;
5239
+ readonly usage: "[args...]";
5240
+ readonly arguments: readonly [{
5241
+ readonly name: "args";
5242
+ readonly required: false;
5243
+ readonly variadic: true;
5244
+ }];
5245
+ readonly options: readonly [];
5246
+ readonly subcommands: readonly [];
5247
+ }, {
5248
+ readonly name: "garden";
5249
+ readonly description: "Promote local ontology candidates";
5250
+ readonly aliases: readonly [];
5251
+ readonly hidden: false;
5252
+ readonly usage: "[args...]";
5253
+ readonly arguments: readonly [{
5254
+ readonly name: "args";
5255
+ readonly required: false;
5256
+ readonly variadic: true;
5257
+ }];
5258
+ readonly options: readonly [];
5259
+ readonly subcommands: readonly [];
5260
+ }, {
5261
+ readonly name: "source-worklist";
5262
+ readonly description: "List unprocessed source items and their audience";
5263
+ readonly aliases: readonly [];
5264
+ readonly hidden: false;
5265
+ readonly usage: "[args...]";
5266
+ readonly arguments: readonly [{
5267
+ readonly name: "args";
5268
+ readonly required: false;
5269
+ readonly variadic: true;
5270
+ }];
5271
+ readonly options: readonly [];
5272
+ readonly subcommands: readonly [];
5273
+ }, {
5274
+ readonly name: "source-yaml-validate";
5275
+ readonly description: "Validate a source.yaml file";
5276
+ readonly aliases: readonly [];
5277
+ readonly hidden: false;
5278
+ readonly usage: "[args...]";
5279
+ readonly arguments: readonly [{
5280
+ readonly name: "args";
5281
+ readonly required: false;
5282
+ readonly variadic: true;
5283
+ }];
5284
+ readonly options: readonly [];
5285
+ readonly subcommands: readonly [];
5208
5286
  }];
5209
5287
  }, {
5210
5288
  readonly name: "checkpoint-gate-setting";
@@ -2070,7 +2070,7 @@ export const COMMAND_CATALOG = [
2070
2070
  },
2071
2071
  {
2072
2072
  "name": "outbound",
2073
- "description": "List grants a source-company group holds on other companies",
2073
+ "description": "List grants a source-company group holds on other companies; requires --group <groupId>.",
2074
2074
  "aliases": [],
2075
2075
  "hidden": false,
2076
2076
  "usage": "[options]",
@@ -2078,7 +2078,7 @@ export const COMMAND_CATALOG = [
2078
2078
  "options": [
2079
2079
  {
2080
2080
  "flags": "--group <groupId>",
2081
- "description": "Group id to inspect"
2081
+ "description": "Group id to inspect (required)"
2082
2082
  }
2083
2083
  ],
2084
2084
  "subcommands": []
@@ -6729,6 +6729,102 @@ export const COMMAND_CATALOG = [
6729
6729
  }
6730
6730
  ],
6731
6731
  "subcommands": []
6732
+ },
6733
+ {
6734
+ "name": "audience-grant",
6735
+ "description": "Grant scoped ontology audiences and verify each grant",
6736
+ "aliases": [],
6737
+ "hidden": false,
6738
+ "usage": "[args...]",
6739
+ "arguments": [
6740
+ {
6741
+ "name": "args",
6742
+ "required": false,
6743
+ "variadic": true
6744
+ }
6745
+ ],
6746
+ "options": [],
6747
+ "subcommands": []
6748
+ },
6749
+ {
6750
+ "name": "brief",
6751
+ "description": "Render the local ontology company brief",
6752
+ "aliases": [],
6753
+ "hidden": false,
6754
+ "usage": "[args...]",
6755
+ "arguments": [
6756
+ {
6757
+ "name": "args",
6758
+ "required": false,
6759
+ "variadic": true
6760
+ }
6761
+ ],
6762
+ "options": [],
6763
+ "subcommands": []
6764
+ },
6765
+ {
6766
+ "name": "candidate",
6767
+ "description": "Write a deterministic ontology candidate",
6768
+ "aliases": [],
6769
+ "hidden": false,
6770
+ "usage": "[args...]",
6771
+ "arguments": [
6772
+ {
6773
+ "name": "args",
6774
+ "required": false,
6775
+ "variadic": true
6776
+ }
6777
+ ],
6778
+ "options": [],
6779
+ "subcommands": []
6780
+ },
6781
+ {
6782
+ "name": "garden",
6783
+ "description": "Promote local ontology candidates",
6784
+ "aliases": [],
6785
+ "hidden": false,
6786
+ "usage": "[args...]",
6787
+ "arguments": [
6788
+ {
6789
+ "name": "args",
6790
+ "required": false,
6791
+ "variadic": true
6792
+ }
6793
+ ],
6794
+ "options": [],
6795
+ "subcommands": []
6796
+ },
6797
+ {
6798
+ "name": "source-worklist",
6799
+ "description": "List unprocessed source items and their audience",
6800
+ "aliases": [],
6801
+ "hidden": false,
6802
+ "usage": "[args...]",
6803
+ "arguments": [
6804
+ {
6805
+ "name": "args",
6806
+ "required": false,
6807
+ "variadic": true
6808
+ }
6809
+ ],
6810
+ "options": [],
6811
+ "subcommands": []
6812
+ },
6813
+ {
6814
+ "name": "source-yaml-validate",
6815
+ "description": "Validate a source.yaml file",
6816
+ "aliases": [],
6817
+ "hidden": false,
6818
+ "usage": "[args...]",
6819
+ "arguments": [
6820
+ {
6821
+ "name": "args",
6822
+ "required": false,
6823
+ "variadic": true
6824
+ }
6825
+ ],
6826
+ "options": [],
6827
+ "subcommands": []
6732
6828
  }
6733
6829
  ]
6734
6830
  },
@@ -36,6 +36,44 @@ function decodeIdTokenClaims(idToken) {
36
36
  return undefined;
37
37
  }
38
38
  }
39
+ function formatAuthExpiry(expiresAt, now = Date.now()) {
40
+ const expiresAtMs = typeof expiresAt === "number" ? expiresAt : Date.parse(expiresAt);
41
+ const expiresAtDate = new Date(expiresAtMs);
42
+ if (!Number.isFinite(expiresAtMs) || Number.isNaN(expiresAtDate.getTime())) {
43
+ return String(expiresAt);
44
+ }
45
+ const remainingMs = expiresAtMs - now;
46
+ const absoluteMs = Math.abs(remainingMs);
47
+ let unit;
48
+ let unitMs;
49
+ if (absoluteMs >= 365 * 24 * 60 * 60 * 1_000) {
50
+ unit = "year";
51
+ unitMs = 365 * 24 * 60 * 60 * 1_000;
52
+ }
53
+ else if (absoluteMs >= 30 * 24 * 60 * 60 * 1_000) {
54
+ unit = "month";
55
+ unitMs = 30 * 24 * 60 * 60 * 1_000;
56
+ }
57
+ else if (absoluteMs >= 24 * 60 * 60 * 1_000) {
58
+ unit = "day";
59
+ unitMs = 24 * 60 * 60 * 1_000;
60
+ }
61
+ else if (absoluteMs >= 60 * 60 * 1_000) {
62
+ unit = "hour";
63
+ unitMs = 60 * 60 * 1_000;
64
+ }
65
+ else if (absoluteMs >= 60 * 1_000) {
66
+ unit = "minute";
67
+ unitMs = 60 * 1_000;
68
+ }
69
+ else {
70
+ unit = "second";
71
+ unitMs = 1_000;
72
+ }
73
+ const relative = new Intl.RelativeTimeFormat("en", { numeric: "always" })
74
+ .format(Math.round(remainingMs / unitMs), unit);
75
+ return expiresAtDate.toISOString() + " (" + relative + ")";
76
+ }
39
77
  function peekIdToken(idToken) {
40
78
  const claims = decodeIdTokenClaims(idToken);
41
79
  return {
@@ -201,8 +239,8 @@ export function registerAuthCommands(program) {
201
239
  const expiring = isExpiring(cached);
202
240
  const label = who ? `${who} — ` : "";
203
241
  console.log(expiring
204
- ? chalk.yellow(`${label}HQ session cached but expiring (expiresAt=${cached.expiresAt})`)
205
- : chalk.green(`${label}HQ session valid (expiresAt=${cached.expiresAt})`));
242
+ ? chalk.yellow(`${label}HQ session cached but expiring (expiresAt=${formatAuthExpiry(cached.expiresAt)})`)
243
+ : chalk.green(`${label}HQ session valid (expiresAt=${formatAuthExpiry(cached.expiresAt)})`));
206
244
  console.log(chalk.dim(`token source: ${tokenSource}`));
207
245
  });
208
246
  }
@@ -0,0 +1,16 @@
1
+ /** Native TypeScript implementation of the local ontology scaffold scripts. */
2
+ import { Command } from "commander";
3
+ export interface ScriptResult {
4
+ code: number;
5
+ stdout: string;
6
+ stderr: string;
7
+ }
8
+ export declare function preferenceValue(root: string, company: string, field: string): ScriptResult;
9
+ export declare function sourceYamlValidate(file: string | undefined): ScriptResult;
10
+ export declare function candidateCommand(args: string[], root: string): ScriptResult;
11
+ export declare function briefCommand(args: string[], defaultRoot: string): ScriptResult;
12
+ export declare function gardenCommand(args: string[], defaultRoot: string): ScriptResult;
13
+ export declare function audienceGrant(args: string[], root: string): ScriptResult;
14
+ export declare function worklistCommand(args: string[], root: string): ScriptResult;
15
+ export declare function registerCoreOntologyNativeCommands(ontology: Command, rootFor: () => string): void;
16
+ //# sourceMappingURL=core-ontology-native.d.ts.map