rhachet-roles-bhuild 0.21.12 → 0.21.13

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 (27) hide show
  1. package/dist/contract/cli/outputRadioResult.d.ts +6 -1
  2. package/dist/contract/cli/outputRadioResult.js +12 -2
  3. package/dist/contract/cli/outputRadioResult.js.map +1 -1
  4. package/dist/contract/cli/radioTaskPush.js +25 -0
  5. package/dist/contract/cli/radioTaskPush.js.map +1 -1
  6. package/dist/domain.objects/RadioPermissions.d.ts +37 -0
  7. package/dist/domain.objects/RadioPermissions.js +7 -0
  8. package/dist/domain.objects/RadioPermissions.js.map +1 -0
  9. package/dist/domain.operations/radio/permission/computeRadioUsagePermissionDecision.d.ts +11 -0
  10. package/dist/domain.operations/radio/permission/computeRadioUsagePermissionDecision.js +46 -0
  11. package/dist/domain.operations/radio/permission/computeRadioUsagePermissionDecision.js.map +1 -0
  12. package/dist/domain.operations/radio/permission/extractOrgFromRepo.d.ts +7 -0
  13. package/dist/domain.operations/radio/permission/extractOrgFromRepo.js +29 -0
  14. package/dist/domain.operations/radio/permission/extractOrgFromRepo.js.map +1 -0
  15. package/dist/domain.operations/radio/permission/getAllRadioUsagePermissions.d.ts +14 -0
  16. package/dist/domain.operations/radio/permission/getAllRadioUsagePermissions.js +114 -0
  17. package/dist/domain.operations/radio/permission/getAllRadioUsagePermissions.js.map +1 -0
  18. package/dist/domain.operations/radio/permission/getOneRadioUsagePermissionDecision.d.ts +11 -0
  19. package/dist/domain.operations/radio/permission/getOneRadioUsagePermissionDecision.js +25 -0
  20. package/dist/domain.operations/radio/permission/getOneRadioUsagePermissionDecision.js.map +1 -0
  21. package/dist/domain.roles/dispatcher/skills/radio.uses.global.sh +133 -0
  22. package/dist/domain.roles/dispatcher/skills/radio.uses.local.sh +172 -0
  23. package/dist/domain.roles/dispatcher/skills/radio.uses.operations.sh +139 -0
  24. package/dist/domain.roles/dispatcher/skills/radio.uses.org.sh +219 -0
  25. package/dist/domain.roles/dispatcher/skills/radio.uses.output.sh +92 -0
  26. package/dist/domain.roles/dispatcher/skills/radio.uses.sh +59 -0
  27. package/package.json +3 -3
@@ -1,7 +1,12 @@
1
1
  /**
2
- * .what = write formatted output to stdout
2
+ * .what = write formatted output to stdout or stderr
3
3
  * .why = cli communicator for radio task results
4
4
  */
5
5
  export declare const outputRadioResult: (input: {
6
6
  message: string;
7
+ isError?: boolean;
8
+ hint?: {
9
+ ask: string;
10
+ command: string;
11
+ };
7
12
  }) => void;
@@ -2,11 +2,21 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.outputRadioResult = void 0;
4
4
  /**
5
- * .what = write formatted output to stdout
5
+ * .what = write formatted output to stdout or stderr
6
6
  * .why = cli communicator for radio task results
7
7
  */
8
8
  const outputRadioResult = (input) => {
9
- console.log(input.message);
9
+ if (input.isError) {
10
+ console.error(input.message);
11
+ if (input.hint) {
12
+ console.error('');
13
+ console.error(input.hint.ask);
14
+ console.error(` $ ${input.hint.command}`);
15
+ }
16
+ }
17
+ else {
18
+ console.log(input.message);
19
+ }
10
20
  };
11
21
  exports.outputRadioResult = outputRadioResult;
12
22
  //# sourceMappingURL=outputRadioResult.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"outputRadioResult.js","sourceRoot":"","sources":["../../../src/contract/cli/outputRadioResult.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACI,MAAM,iBAAiB,GAAG,CAAC,KAA0B,EAAQ,EAAE;IACpE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC,CAAC;AAFW,QAAA,iBAAiB,qBAE5B"}
1
+ {"version":3,"file":"outputRadioResult.js","sourceRoot":"","sources":["../../../src/contract/cli/outputRadioResult.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACI,MAAM,iBAAiB,GAAG,CAAC,KAOjC,EAAQ,EAAE;IACT,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YAClB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9B,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC,CAAC;AAlBW,QAAA,iBAAiB,qBAkB5B"}
@@ -28,6 +28,7 @@ const asPushTaskFromArgs_1 = require("../../domain.operations/radio/cli/asPushTa
28
28
  const asTaskDetailOutput_1 = require("../../domain.operations/radio/cli/asTaskDetailOutput");
29
29
  const getOneRadioContextFromCliArgs_1 = require("../../domain.operations/radio/cli/getOneRadioContextFromCliArgs");
30
30
  const getOneRadioTaskRepoFromCliArg_1 = require("../../domain.operations/radio/cli/getOneRadioTaskRepoFromCliArg");
31
+ const getOneRadioUsagePermissionDecision_1 = require("../../domain.operations/radio/permission/getOneRadioUsagePermissionDecision");
31
32
  const radioTaskPush_1 = require("../../domain.operations/radio/task/push/radioTaskPush");
32
33
  const cli_1 = require("../../infra/cli");
33
34
  const shx_1 = require("../../infra/shell/shx");
@@ -73,6 +74,30 @@ const cliRadioTaskPush = async () => {
73
74
  hint: 'provide --into owner/repo or run from within a git repository',
74
75
  },
75
76
  });
77
+ // check permission to push to this repo
78
+ const permission = await (0, getOneRadioUsagePermissionDecision_1.getOneRadioUsagePermissionDecision)({
79
+ targetRepo: `${repo.owner}/${repo.name}`,
80
+ sourceCwd: process.cwd(),
81
+ });
82
+ if (!permission.allowed) {
83
+ // generate hint based on block level
84
+ const hintCommand = (() => {
85
+ if (permission.level === 'global')
86
+ return 'npx rhachet run --skill radio.uses --global allow';
87
+ if (permission.level === 'org')
88
+ return `npx rhachet run --skill radio.uses --org ${repo.owner} allow`;
89
+ return 'npx rhachet run --skill radio.uses allow';
90
+ })();
91
+ (0, outputRadioResult_1.outputRadioResult)({
92
+ message: `radio.task.push blocked: ${permission.reason}`,
93
+ isError: true,
94
+ hint: {
95
+ ask: 'ask your human to grant:',
96
+ command: hintCommand,
97
+ },
98
+ });
99
+ process.exit(2);
100
+ }
76
101
  // validate and transform task args
77
102
  const task = (0, asPushTaskFromArgs_1.asPushTaskFromArgs)({
78
103
  repo,
@@ -1 +1 @@
1
- {"version":3,"file":"radioTaskPush.js","sourceRoot":"","sources":["../../../src/contract/cli/radioTaskPush.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AAEH,6BAAwB;AAExB,yEAAsE;AACtE,mEAAgE;AAChE,yEAAsE;AACtE,4FAAyF;AACzF,4FAAyF;AACzF,kHAA+G;AAC/G,kHAA+G;AAC/G,wFAAqF;AACrF,wCAA4C;AAC5C,8CAA2C;AAE3C,2DAAwD;AAExD,uEAAuE;AACvE,SAAS;AACT,uEAAuE;AAEvE,MAAM,YAAY,GAAG,OAAC,CAAC,MAAM,CAAC;IAC5B,KAAK,EAAE,OAAC,CAAC,MAAM,CAAC;QACd,oBAAoB;QACpB,GAAG,EAAE,OAAC,CAAC,UAAU,CAAC,2BAAY,CAAC;QAE/B,uEAAuE;QACvE,kDAAkD;QAClD,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAE3B,6CAA6C;QAC7C,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAE3B,sBAAsB;QACtB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC5B,WAAW,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAClC,MAAM,EAAE,OAAC,CAAC,UAAU,CAAC,iCAAe,CAAC,CAAC,QAAQ,EAAE;QAEhD,6BAA6B;QAC7B,IAAI,EAAE,OAAC,CAAC,UAAU,CAAC,iCAAe,CAAC,CAAC,QAAQ,EAAE;QAE9C,+CAA+C;QAC/C,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC3B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;QAC5B,CAAC,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;KACzB,CAAC;IACF,OAAO,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CACzC,CAAC,CAAC;AAEH,uEAAuE;AACvE,2BAA2B;AAC3B,uEAAuE;AAEhE,MAAM,gBAAgB,GAAG,KAAK,IAAmB,EAAE;IACxD,MAAM,EAAE,KAAK,EAAE,GAAG,IAAA,gBAAU,EAAC,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC;IAEvD,6CAA6C;IAC7C,MAAM,IAAI,GAAG,MAAM,IAAA,6DAA6B,EAAC;QAC/C,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;QACvB,OAAO,EAAE,QAAQ;QACjB,YAAY,EAAE;YACZ,eAAe,EACb,6FAA6F;YAC/F,IAAI,EAAE,+DAA+D;SACtE;KACF,CAAC,CAAC;IAEH,mCAAmC;IACnC,MAAM,IAAI,GAAG,IAAA,uCAAkB,EAAC;QAC9B,IAAI;QACJ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;QACxB,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;QAC1B,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;QACtC,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,IAAI;KAC7B,CAAC,CAAC;IAEH,kDAAkD;IAClD,MAAM,OAAO,GAAG,MAAM,IAAA,6DAA6B,EACjD;QACE,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,IAAI;QACJ,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;KACzB,EACD,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAH,SAAG,EAAE,CAC1B,CAAC;IAEF,+BAA+B;IAC/B,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAa,EAChC;QACE,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE;YACJ,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9C,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;YACjD,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,IAAI,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC;YACnE,GAAG,CAAC,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;SACrD;KACF,EACD,OAAO,CACR,CAAC;IAEF,gBAAgB;IAChB,IAAA,qCAAiB,EAAC;QAChB,OAAO,EAAE,IAAA,uCAAkB,EAAC;YAC1B,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,MAAM,EAAE,IAAI;SACb,CAAC;KACH,CAAC,CAAC;AACL,CAAC,CAAC;AA1DW,QAAA,gBAAgB,oBA0D3B"}
1
+ {"version":3,"file":"radioTaskPush.js","sourceRoot":"","sources":["../../../src/contract/cli/radioTaskPush.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * .what = permission state types for radio.uses controls
3
+ * .why = enables hierarchical permission management (global > org > local)
4
+ */
5
+ /**
6
+ * global circuit breaker state
7
+ *
8
+ * when blocked = true, all radio usage is blocked regardless of org/local settings
9
+ */
10
+ interface RadioGlobalState {
11
+ blocked: boolean;
12
+ }
13
+ /**
14
+ * per-org permission state
15
+ *
16
+ * orgs map includes:
17
+ * - @all: default fallback for orgs not explicitly set
18
+ * - specific org names: e.g., "ehmpathy", "ahbode"
19
+ */
20
+ interface RadioOrgState {
21
+ orgs: Record<string, 'allowed' | 'blocked'>;
22
+ }
23
+ /**
24
+ * local (per-repo) permission state
25
+ */
26
+ interface RadioLocalState {
27
+ state: 'allowed' | 'blocked';
28
+ }
29
+ /**
30
+ * decision result from permission check
31
+ */
32
+ interface RadioPermissionDecision {
33
+ allowed: boolean;
34
+ reason: string;
35
+ level: 'global' | 'org' | 'local' | 'default';
36
+ }
37
+ export type { RadioGlobalState, RadioOrgState, RadioLocalState, RadioPermissionDecision, };
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * .what = permission state types for radio.uses controls
4
+ * .why = enables hierarchical permission management (global > org > local)
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=RadioPermissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RadioPermissions.js","sourceRoot":"","sources":["../../src/domain.objects/RadioPermissions.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
@@ -0,0 +1,11 @@
1
+ import type { RadioGlobalState, RadioLocalState, RadioOrgState, RadioPermissionDecision } from '../../../domain.objects/RadioPermissions';
2
+ /**
3
+ * .what = compute radio usage permission decision from state hierarchy
4
+ * .why = precedence rules: global > local > org > @all > default blocked
5
+ */
6
+ export declare const computeRadioUsagePermissionDecision: (input: {
7
+ global: RadioGlobalState | null;
8
+ org: RadioOrgState | null;
9
+ local: RadioLocalState | null;
10
+ targetOrg: string;
11
+ }) => RadioPermissionDecision;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computeRadioUsagePermissionDecision = void 0;
4
+ /**
5
+ * .what = compute radio usage permission decision from state hierarchy
6
+ * .why = precedence rules: global > local > org > @all > default blocked
7
+ */
8
+ const computeRadioUsagePermissionDecision = (input) => {
9
+ // rule 1: global blocked supersedes all
10
+ if (input.global?.blocked === true) {
11
+ return { allowed: false, reason: 'global blocked', level: 'global' };
12
+ }
13
+ // rule 2: org-specific state takes precedence over local
14
+ const orgState = input.org?.orgs?.[input.targetOrg];
15
+ if (orgState !== undefined) {
16
+ if (orgState === 'blocked') {
17
+ return { allowed: false, reason: 'org blocked', level: 'org' };
18
+ }
19
+ if (orgState === 'allowed') {
20
+ return { allowed: true, reason: 'org allowed', level: 'org' };
21
+ }
22
+ }
23
+ // rule 3: @all fallback for orgs not explicitly set (before local)
24
+ const allState = input.org?.orgs?.['@all'];
25
+ if (allState !== undefined) {
26
+ if (allState === 'blocked') {
27
+ return { allowed: false, reason: '@all blocked', level: 'org' };
28
+ }
29
+ if (allState === 'allowed') {
30
+ return { allowed: true, reason: '@all allowed', level: 'org' };
31
+ }
32
+ }
33
+ // rule 4: local state (only applies if org unset)
34
+ if (input.local !== null) {
35
+ if (input.local.state === 'blocked') {
36
+ return { allowed: false, reason: 'local blocked', level: 'local' };
37
+ }
38
+ if (input.local.state === 'allowed') {
39
+ return { allowed: true, reason: 'local allowed', level: 'local' };
40
+ }
41
+ }
42
+ // rule 5: all unset = blocked (safe default)
43
+ return { allowed: false, reason: 'safe default (unset)', level: 'default' };
44
+ };
45
+ exports.computeRadioUsagePermissionDecision = computeRadioUsagePermissionDecision;
46
+ //# sourceMappingURL=computeRadioUsagePermissionDecision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"computeRadioUsagePermissionDecision.js","sourceRoot":"","sources":["../../../../src/domain.operations/radio/permission/computeRadioUsagePermissionDecision.ts"],"names":[],"mappings":";;;AAOA;;;GAGG;AACI,MAAM,mCAAmC,GAAG,CAAC,KAKnD,EAA2B,EAAE;IAC5B,wCAAwC;IACxC,IAAI,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;QACnC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;IACvE,CAAC;IAED,yDAAyD;IACzD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACjE,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAChE,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,CAAC;IAC3C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAClE,CAAC;QACD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACjE,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,IAAI,KAAK,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACrE,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;QACpE,CAAC;IACH,CAAC;IAED,6CAA6C;IAC7C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC9E,CAAC,CAAC;AA7CW,QAAA,mCAAmC,uCA6C9C"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * .what = extract org from owner/repo format
3
+ * .why = radio permission checks need org name to lookup org-level permissions
4
+ */
5
+ export declare const extractOrgFromRepo: (input: {
6
+ repo: string;
7
+ }) => string;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractOrgFromRepo = void 0;
4
+ const helpful_errors_1 = require("helpful-errors");
5
+ /**
6
+ * .what = extract org from owner/repo format
7
+ * .why = radio permission checks need org name to lookup org-level permissions
8
+ */
9
+ const extractOrgFromRepo = (input) => {
10
+ // validate not empty
11
+ if (!input.repo || input.repo.trim() === '') {
12
+ throw new helpful_errors_1.BadRequestError('repo cannot be empty', {
13
+ repo: input.repo,
14
+ hint: 'expected format: owner/repo',
15
+ });
16
+ }
17
+ // split on slash
18
+ const parts = input.repo.split('/');
19
+ // validate format
20
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
21
+ throw new helpful_errors_1.BadRequestError('invalid repo format', {
22
+ repo: input.repo,
23
+ hint: 'expected format: owner/repo',
24
+ });
25
+ }
26
+ return parts[0];
27
+ };
28
+ exports.extractOrgFromRepo = extractOrgFromRepo;
29
+ //# sourceMappingURL=extractOrgFromRepo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractOrgFromRepo.js","sourceRoot":"","sources":["../../../../src/domain.operations/radio/permission/extractOrgFromRepo.ts"],"names":[],"mappings":";;;AAAA,mDAAiD;AAEjD;;;GAGG;AACI,MAAM,kBAAkB,GAAG,CAAC,KAAuB,EAAU,EAAE;IACpE,qBAAqB;IACrB,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAC5C,MAAM,IAAI,gCAAe,CAAC,sBAAsB,EAAE;YAChD,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,6BAA6B;SACpC,CAAC,CAAC;IACL,CAAC;IAED,iBAAiB;IACjB,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAEpC,kBAAkB;IAClB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,gCAAe,CAAC,qBAAqB,EAAE;YAC/C,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,6BAA6B;SACpC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;AArBW,QAAA,kBAAkB,sBAqB7B"}
@@ -0,0 +1,14 @@
1
+ import type { RadioGlobalState, RadioLocalState, RadioOrgState } from '../../../domain.objects/RadioPermissions';
2
+ /**
3
+ * .what = read all radio usage permission state files
4
+ * .why = communicator for permission check — reads local, global, and org states
5
+ */
6
+ export declare const getAllRadioUsagePermissions: (input: {
7
+ cwd: string;
8
+ }, context?: {
9
+ homeDir?: string;
10
+ }) => Promise<{
11
+ global: RadioGlobalState | null;
12
+ org: RadioOrgState | null;
13
+ local: RadioLocalState | null;
14
+ }>;
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getAllRadioUsagePermissions = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const GLOBAL_STORAGE_PATH = '.rhachet/storage/repo=bhuild/role=dispatcher/.meter';
11
+ const LOCAL_STATE_FILE = '.meter/radio.uses.jsonc';
12
+ const GLOBAL_STATE_FILE = 'radio.uses.global.jsonc';
13
+ const ORG_STATE_FILE = 'radio.uses.org.jsonc';
14
+ /**
15
+ * parse jsonc content — strip line comments then parse as json
16
+ */
17
+ const parseJsonc = (content) => {
18
+ // remove single-line comments (// ...)
19
+ const stripped = content
20
+ .split('\n')
21
+ .map((line) => {
22
+ // find // that's not inside a string
23
+ let inString = false;
24
+ let escapeNext = false;
25
+ for (let i = 0; i < line.length; i++) {
26
+ const char = line[i];
27
+ if (escapeNext) {
28
+ escapeNext = false;
29
+ continue;
30
+ }
31
+ if (char === '\\') {
32
+ escapeNext = true;
33
+ continue;
34
+ }
35
+ if (char === '"' && !escapeNext) {
36
+ inString = !inString;
37
+ continue;
38
+ }
39
+ if (!inString && char === '/' && line[i + 1] === '/') {
40
+ return line.slice(0, i);
41
+ }
42
+ }
43
+ return line;
44
+ })
45
+ .join('\n');
46
+ try {
47
+ return JSON.parse(stripped);
48
+ }
49
+ catch {
50
+ return null;
51
+ }
52
+ };
53
+ /**
54
+ * .what = read all radio usage permission state files
55
+ * .why = communicator for permission check — reads local, global, and org states
56
+ */
57
+ const getAllRadioUsagePermissions = async (input, context = {}) => {
58
+ const homeDir = context.homeDir ?? os_1.default.homedir();
59
+ // read local state
60
+ const localPath = path_1.default.join(input.cwd, LOCAL_STATE_FILE);
61
+ const local = readLocalState(localPath);
62
+ // read global state
63
+ const globalStoragePath = path_1.default.join(homeDir, GLOBAL_STORAGE_PATH);
64
+ const globalPath = path_1.default.join(globalStoragePath, GLOBAL_STATE_FILE);
65
+ const global = readGlobalState(globalPath);
66
+ // read org state
67
+ const orgPath = path_1.default.join(globalStoragePath, ORG_STATE_FILE);
68
+ const org = readOrgState(orgPath);
69
+ return { global, org, local };
70
+ };
71
+ exports.getAllRadioUsagePermissions = getAllRadioUsagePermissions;
72
+ /**
73
+ * read and parse local state file
74
+ */
75
+ const readLocalState = (filePath) => {
76
+ if (!fs_1.default.existsSync(filePath)) {
77
+ return null;
78
+ }
79
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
80
+ const parsed = parseJsonc(content);
81
+ if (!parsed || typeof parsed.state !== 'string') {
82
+ return null;
83
+ }
84
+ return { state: parsed.state };
85
+ };
86
+ /**
87
+ * read and parse global state file
88
+ */
89
+ const readGlobalState = (filePath) => {
90
+ if (!fs_1.default.existsSync(filePath)) {
91
+ return null;
92
+ }
93
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
94
+ const parsed = parseJsonc(content);
95
+ if (!parsed || typeof parsed.blocked !== 'boolean') {
96
+ return null;
97
+ }
98
+ return { blocked: parsed.blocked };
99
+ };
100
+ /**
101
+ * read and parse org state file
102
+ */
103
+ const readOrgState = (filePath) => {
104
+ if (!fs_1.default.existsSync(filePath)) {
105
+ return null;
106
+ }
107
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
108
+ const parsed = parseJsonc(content);
109
+ if (!parsed || typeof parsed.orgs !== 'object') {
110
+ return null;
111
+ }
112
+ return { orgs: parsed.orgs };
113
+ };
114
+ //# sourceMappingURL=getAllRadioUsagePermissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getAllRadioUsagePermissions.js","sourceRoot":"","sources":["../../../../src/domain.operations/radio/permission/getAllRadioUsagePermissions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { RadioPermissionDecision } from '../../../domain.objects/RadioPermissions';
2
+ /**
3
+ * .what = get permission decision for radio usage
4
+ * .why = orchestrates state load + precedence computation for push gate
5
+ */
6
+ export declare const getOneRadioUsagePermissionDecision: (input: {
7
+ targetRepo: string;
8
+ sourceCwd: string | null;
9
+ }, context?: {
10
+ homeDir?: string;
11
+ }) => Promise<RadioPermissionDecision>;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getOneRadioUsagePermissionDecision = void 0;
4
+ const computeRadioUsagePermissionDecision_1 = require("./computeRadioUsagePermissionDecision");
5
+ const extractOrgFromRepo_1 = require("./extractOrgFromRepo");
6
+ const getAllRadioUsagePermissions_1 = require("./getAllRadioUsagePermissions");
7
+ /**
8
+ * .what = get permission decision for radio usage
9
+ * .why = orchestrates state load + precedence computation for push gate
10
+ */
11
+ const getOneRadioUsagePermissionDecision = async (input, context = {}) => {
12
+ // extract target org from repo
13
+ const targetOrg = (0, extractOrgFromRepo_1.extractOrgFromRepo)({ repo: input.targetRepo });
14
+ // load permission states
15
+ const states = await (0, getAllRadioUsagePermissions_1.getAllRadioUsagePermissions)({ cwd: input.sourceCwd ?? process.cwd() }, context);
16
+ // compute decision
17
+ return (0, computeRadioUsagePermissionDecision_1.computeRadioUsagePermissionDecision)({
18
+ global: states.global,
19
+ org: states.org,
20
+ local: states.local,
21
+ targetOrg,
22
+ });
23
+ };
24
+ exports.getOneRadioUsagePermissionDecision = getOneRadioUsagePermissionDecision;
25
+ //# sourceMappingURL=getOneRadioUsagePermissionDecision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getOneRadioUsagePermissionDecision.js","sourceRoot":"","sources":["../../../../src/domain.operations/radio/permission/getOneRadioUsagePermissionDecision.ts"],"names":[],"mappings":";;;AACA,+FAA4F;AAC5F,6DAA0D;AAC1D,+EAA4E;AAE5E;;;GAGG;AACI,MAAM,kCAAkC,GAAG,KAAK,EACrD,KAGC,EACD,UAEI,EAAE,EAC4B,EAAE;IACpC,+BAA+B;IAC/B,MAAM,SAAS,GAAG,IAAA,uCAAkB,EAAC,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC;IAEjE,yBAAyB;IACzB,MAAM,MAAM,GAAG,MAAM,IAAA,yDAA2B,EAC9C,EAAE,GAAG,EAAE,KAAK,CAAC,SAAS,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,EACzC,OAAO,CACR,CAAC;IAEF,mBAAmB;IACnB,OAAO,IAAA,yEAAmC,EAAC;QACzC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,GAAG,EAAE,MAAM,CAAC,GAAG;QACf,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,SAAS;KACV,CAAC,CAAC;AACL,CAAC,CAAC;AAzBW,QAAA,kCAAkC,sCAyB7C"}
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = manage global radio usage blocker
4
+ #
5
+ # .why = humans can pause all radio usage across all repos
6
+ # with a single global circuit breaker
7
+ #
8
+ # usage:
9
+ # radio.uses.global block # block radio globally
10
+ # radio.uses.global allow # lift global blocker
11
+ # radio.uses.global get # check global blocker state
12
+ #
13
+ # guarantee:
14
+ # - global blocker stored at ~/.rhachet/storage/repo=bhuild/role=dispatcher/.meter/
15
+ # - global blocker overrides org and local settings
16
+ ######################################################################
17
+ set -euo pipefail
18
+
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ source "$SCRIPT_DIR/radio.uses.output.sh"
21
+ source "$SCRIPT_DIR/radio.uses.operations.sh"
22
+
23
+ require_git_repo
24
+
25
+ # parse command
26
+ COMMAND=""
27
+
28
+ # first positional arg is command
29
+ if [[ $# -ge 1 && "$1" != --* ]]; then
30
+ COMMAND="$1"
31
+ shift
32
+ fi
33
+
34
+ while [[ $# -gt 0 ]]; do
35
+ case $1 in
36
+ block|allow|get)
37
+ COMMAND="$1"
38
+ shift
39
+ ;;
40
+ --help|-h)
41
+ echo "usage: radio.uses --global block"
42
+ echo " radio.uses --global allow"
43
+ echo " radio.uses --global get"
44
+ echo ""
45
+ echo "commands:"
46
+ echo " block pause all radio usage globally"
47
+ echo " allow lift global blocker, resume org/local behavior"
48
+ echo " get check global blocker state"
49
+ echo ""
50
+ echo "options:"
51
+ echo " --help, -h show this help"
52
+ exit 0
53
+ ;;
54
+ --repo|--role|--skill|--local|--global|--org)
55
+ # rhachet passthrough args - ignore
56
+ shift
57
+ if [[ $# -gt 0 && "$1" != --* && "$1" != -* ]]; then
58
+ shift
59
+ fi
60
+ ;;
61
+ --)
62
+ shift
63
+ ;;
64
+ --*)
65
+ echo "error: unknown option: $1"
66
+ echo "usage: radio.uses --global block|allow|get"
67
+ exit 2
68
+ ;;
69
+ *)
70
+ shift
71
+ ;;
72
+ esac
73
+ done
74
+
75
+ # validate command
76
+ if [[ -z "$COMMAND" ]]; then
77
+ echo "error: command required (block, allow, or get)"
78
+ echo "usage: radio.uses --global block|allow|get"
79
+ exit 2
80
+ fi
81
+
82
+ ######################################################################
83
+ # guard: mutation commands require TTY (human only)
84
+ ######################################################################
85
+ case "$COMMAND" in
86
+ block|allow)
87
+ require_human "$COMMAND --global"
88
+ ;;
89
+ esac
90
+
91
+ case "$COMMAND" in
92
+ block)
93
+ mkdir -p "$GLOBAL_METER_DIR"
94
+ cat > "$GLOBAL_STATE_FILE" << EOF
95
+ {
96
+ "blocked": true
97
+ }
98
+ EOF
99
+
100
+ print_turtle_header "groovy, bond fire time"
101
+ print_tree_start "radio.uses block --global"
102
+ echo " └─ radio blocked globally"
103
+ ;;
104
+
105
+ allow)
106
+ if [[ -f "$GLOBAL_STATE_FILE" ]]; then
107
+ rm -f "$GLOBAL_STATE_FILE"
108
+ fi
109
+
110
+ print_turtle_header "shell yeah, back in the water!"
111
+ print_tree_start "radio.uses allow --global"
112
+ echo " └─ radio resumed globally"
113
+ ;;
114
+
115
+ get)
116
+ print_turtle_header "lets check the global meter..."
117
+ print_tree_start "radio.uses get --global"
118
+
119
+ GLOBAL_BLOCKED=$(read_global_blocked)
120
+
121
+ if [[ "$GLOBAL_BLOCKED" == "true" ]]; then
122
+ echo " └─ global: blocked"
123
+ else
124
+ echo " └─ global: not blocked"
125
+ fi
126
+ ;;
127
+
128
+ *)
129
+ echo "error: unknown command: $COMMAND"
130
+ echo "usage: radio.uses --global block|allow|get"
131
+ exit 2
132
+ ;;
133
+ esac
@@ -0,0 +1,172 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = manage local radio usage permission for this repo
4
+ #
5
+ # .why = humans control whether radio is allowed in this specific
6
+ # repository, with local config overrides global and org
7
+ #
8
+ # usage:
9
+ # radio.uses.local allow # allow radio in this repo
10
+ # radio.uses.local block # block radio in this repo
11
+ # radio.uses.local del # remove local config, defer to org/global
12
+ # radio.uses.local get # check local state
13
+ #
14
+ # guarantee:
15
+ # - state stored in .meter/radio.uses.jsonc
16
+ # - local state overrides org and global settings
17
+ ######################################################################
18
+ set -euo pipefail
19
+
20
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
21
+ source "$SCRIPT_DIR/radio.uses.output.sh"
22
+ source "$SCRIPT_DIR/radio.uses.operations.sh"
23
+
24
+ require_git_repo
25
+ get_local_paths
26
+
27
+ # parse command
28
+ COMMAND=""
29
+
30
+ # first positional arg is command
31
+ if [[ $# -ge 1 && "$1" != --* ]]; then
32
+ COMMAND="$1"
33
+ shift
34
+ fi
35
+
36
+ while [[ $# -gt 0 ]]; do
37
+ case $1 in
38
+ allow|block|del|get)
39
+ COMMAND="$1"
40
+ shift
41
+ ;;
42
+ --help|-h)
43
+ echo "usage: radio.uses allow"
44
+ echo " radio.uses block"
45
+ echo " radio.uses del"
46
+ echo " radio.uses get"
47
+ echo ""
48
+ echo "commands:"
49
+ echo " allow allow radio in this repo"
50
+ echo " block block radio in this repo"
51
+ echo " del remove local config, defer to org/global"
52
+ echo " get check local state"
53
+ echo ""
54
+ echo "options:"
55
+ echo " --help, -h show this help"
56
+ exit 0
57
+ ;;
58
+ --repo|--role|--skill|--local|--global|--org)
59
+ # rhachet passthrough args - ignore
60
+ shift
61
+ if [[ $# -gt 0 && "$1" != --* && "$1" != -* ]]; then
62
+ shift
63
+ fi
64
+ ;;
65
+ --)
66
+ shift
67
+ ;;
68
+ --*)
69
+ echo "error: unknown option: $1"
70
+ echo "usage: radio.uses allow|block|del|get"
71
+ exit 2
72
+ ;;
73
+ *)
74
+ shift
75
+ ;;
76
+ esac
77
+ done
78
+
79
+ # validate command
80
+ if [[ -z "$COMMAND" ]]; then
81
+ echo "error: command required (allow, block, del, or get)"
82
+ echo "usage: radio.uses allow|block|del|get"
83
+ exit 2
84
+ fi
85
+
86
+ ######################################################################
87
+ # guard: mutation commands require TTY (human only)
88
+ ######################################################################
89
+ case "$COMMAND" in
90
+ allow|block|del)
91
+ require_human "$COMMAND"
92
+ ;;
93
+ esac
94
+
95
+ case "$COMMAND" in
96
+ allow)
97
+ findsert_meter_dir "$LOCAL_METER_DIR"
98
+ cat > "$LOCAL_STATE_FILE" << EOF
99
+ {
100
+ "state": "allowed"
101
+ }
102
+ EOF
103
+
104
+ print_turtle_header "shell yeah! radio allowed"
105
+ print_tree_start "radio.uses allow"
106
+ echo " └─ local: allowed"
107
+ ;;
108
+
109
+ block)
110
+ findsert_meter_dir "$LOCAL_METER_DIR"
111
+ cat > "$LOCAL_STATE_FILE" << EOF
112
+ {
113
+ "state": "blocked"
114
+ }
115
+ EOF
116
+
117
+ print_turtle_header "groovy, radio blocked"
118
+ print_tree_start "radio.uses block"
119
+ echo " └─ local: blocked"
120
+ ;;
121
+
122
+ del)
123
+ if [[ -f "$LOCAL_STATE_FILE" ]]; then
124
+ rm -f "$LOCAL_STATE_FILE"
125
+ fi
126
+
127
+ print_turtle_header "righteous, local config cleared"
128
+ print_tree_start "radio.uses del"
129
+ echo " └─ local config removed, defers to org/global"
130
+ ;;
131
+
132
+ get)
133
+ print_turtle_header "lets check the meter..."
134
+ print_tree_start "radio.uses get"
135
+
136
+ LOCAL_STATE=$(read_local_state)
137
+ ORG_STATES=$(read_all_org_states)
138
+ GLOBAL_BLOCKED=$(read_global_blocked)
139
+
140
+ # local state
141
+ if [[ "$LOCAL_STATE" == "unset" ]]; then
142
+ echo " ├─ local: unset"
143
+ else
144
+ echo " ├─ local: $LOCAL_STATE"
145
+ fi
146
+
147
+ # org state (show each configured org)
148
+ if [[ "$ORG_STATES" == "unset" ]]; then
149
+ echo " ├─ org: unset"
150
+ else
151
+ echo " ├─ org:"
152
+ while IFS= read -r org_entry; do
153
+ org_name="${org_entry%%=*}"
154
+ org_state="${org_entry#*=}"
155
+ echo " │ └─ $org_name: $org_state"
156
+ done <<< "$ORG_STATES"
157
+ fi
158
+
159
+ # global state
160
+ if [[ "$GLOBAL_BLOCKED" == "true" ]]; then
161
+ echo " └─ global: blocked"
162
+ else
163
+ echo " └─ global: not blocked"
164
+ fi
165
+ ;;
166
+
167
+ *)
168
+ echo "error: unknown command: $COMMAND"
169
+ echo "usage: radio.uses allow|block|del|get"
170
+ exit 2
171
+ ;;
172
+ esac
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = shared operations for radio.uses skills
4
+ #
5
+ # .why = DRY the common logic for path resolution, state reads, TTY
6
+ # guards across local, global, and org handlers
7
+ #
8
+ # usage:
9
+ # source "$SCRIPT_DIR/radio.uses.operations.sh"
10
+ ######################################################################
11
+
12
+ # global storage paths
13
+ ROLE_REPO="bhuild"
14
+ ROLE_SLUG="dispatcher"
15
+ GLOBAL_METER_DIR="$HOME/.rhachet/storage/repo=$ROLE_REPO/role=$ROLE_SLUG/.meter"
16
+ GLOBAL_STATE_FILE="$GLOBAL_METER_DIR/radio.uses.global.jsonc"
17
+ ORG_STATE_FILE="$GLOBAL_METER_DIR/radio.uses.org.jsonc"
18
+
19
+ # local storage paths (set after git root is resolved)
20
+ get_local_paths() {
21
+ REPO_ROOT=$(git rev-parse --show-toplevel)
22
+ LOCAL_METER_DIR="$REPO_ROOT/.meter"
23
+ LOCAL_STATE_FILE="$LOCAL_METER_DIR/radio.uses.jsonc"
24
+ }
25
+
26
+ # ensure we're in a git repo
27
+ require_git_repo() {
28
+ if ! git rev-parse --git-dir > /dev/null 2>&1; then
29
+ echo "error: not in a git repository"
30
+ exit 2
31
+ fi
32
+ }
33
+
34
+ # guard: mutation commands require TTY (human only)
35
+ # note: __I_AM_HUMAN=true allows integration tests to run mutations
36
+ require_human() {
37
+ local command="$1"
38
+ if [[ ! -t 0 && "${__I_AM_HUMAN:-}" != "true" ]]; then
39
+ print_turtle_header "bummer dude..."
40
+ print_tree_start "radio.uses $command"
41
+ print_tree_error "only humans can run this command"
42
+ exit 2
43
+ fi
44
+ }
45
+
46
+ # read global state (returns "true" or "false" for blocked)
47
+ read_global_blocked() {
48
+ if [[ -f "$GLOBAL_STATE_FILE" ]]; then
49
+ if BLOCKED_VAL=$(jq -r '.blocked // false' "$GLOBAL_STATE_FILE" 2>/dev/null); then
50
+ echo "$BLOCKED_VAL"
51
+ else
52
+ echo "false"
53
+ fi
54
+ else
55
+ echo "false"
56
+ fi
57
+ }
58
+
59
+ # read org state for a specific org (returns "allowed", "blocked", or "unset")
60
+ read_org_state() {
61
+ local org="$1"
62
+ if [[ -f "$ORG_STATE_FILE" ]]; then
63
+ # first check specific org
64
+ if ORG_VAL=$(jq -r ".orgs[\"$org\"] // \"unset\"" "$ORG_STATE_FILE" 2>/dev/null); then
65
+ if [[ "$ORG_VAL" != "unset" && "$ORG_VAL" != "null" ]]; then
66
+ echo "$ORG_VAL"
67
+ return
68
+ fi
69
+ fi
70
+ # fall back to @all
71
+ if ALL_VAL=$(jq -r '.orgs["@all"] // "unset"' "$ORG_STATE_FILE" 2>/dev/null); then
72
+ if [[ "$ALL_VAL" != "unset" && "$ALL_VAL" != "null" ]]; then
73
+ echo "$ALL_VAL"
74
+ return
75
+ fi
76
+ fi
77
+ fi
78
+ echo "unset"
79
+ }
80
+
81
+ # read local state (returns "allowed", "blocked", or "unset")
82
+ read_local_state() {
83
+ get_local_paths
84
+ if [[ -f "$LOCAL_STATE_FILE" ]]; then
85
+ if STATE_VAL=$(jq -r '.state // "unset"' "$LOCAL_STATE_FILE" 2>/dev/null); then
86
+ if [[ "$STATE_VAL" != "unset" && "$STATE_VAL" != "null" ]]; then
87
+ echo "$STATE_VAL"
88
+ return
89
+ fi
90
+ fi
91
+ fi
92
+ echo "unset"
93
+ }
94
+
95
+ # read all org states (returns "unset" or formatted org list)
96
+ read_all_org_states() {
97
+ if [[ -f "$ORG_STATE_FILE" ]]; then
98
+ local orgs
99
+ orgs=$(jq -r '.orgs // {} | to_entries | map("\(.key)=\(.value)") | .[]' "$ORG_STATE_FILE" 2>/dev/null)
100
+ if [[ -n "$orgs" ]]; then
101
+ echo "$orgs"
102
+ return
103
+ fi
104
+ fi
105
+ echo "unset"
106
+ }
107
+
108
+ # findsert .meter directory with .gitignore
109
+ findsert_meter_dir() {
110
+ local dir="$1"
111
+ mkdir -p "$dir"
112
+ if [[ ! -f "$dir/.gitignore" ]]; then
113
+ echo "*" > "$dir/.gitignore"
114
+ fi
115
+ }
116
+
117
+ # strip rhachet passthrough args and collect rest
118
+ strip_rhachet_args() {
119
+ local -n _args_out="$1"
120
+ shift
121
+ _args_out=()
122
+ while [[ $# -gt 0 ]]; do
123
+ case $1 in
124
+ --repo|--role|--skill)
125
+ shift
126
+ if [[ $# -gt 0 && "$1" != --* && "$1" != -* ]]; then
127
+ shift
128
+ fi
129
+ ;;
130
+ --)
131
+ shift
132
+ ;;
133
+ *)
134
+ _args_out+=("$1")
135
+ shift
136
+ ;;
137
+ esac
138
+ done
139
+ }
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = manage radio usage permission per organization
4
+ #
5
+ # .why = humans can allow/block radio for specific orgs (e.g., ehmpathy)
6
+ # or set @all as default for all orgs
7
+ #
8
+ # usage:
9
+ # radio.uses.org allow --org ehmpathy # allow radio for ehmpathy
10
+ # radio.uses.org block --org ahbode # block radio for ahbode
11
+ # radio.uses.org allow --org @all # allow radio for all orgs by default
12
+ # radio.uses.org block --org @all # block radio for all orgs by default
13
+ # radio.uses.org del --org ehmpathy # remove org config, defer to @all
14
+ # radio.uses.org get # show all org configs
15
+ # radio.uses.org get --org ehmpathy # check config for specific org
16
+ #
17
+ # guarantee:
18
+ # - state stored at ~/.rhachet/storage/repo=bhuild/role=dispatcher/.meter/radio.uses.org.jsonc
19
+ # - specific org config overrides @all
20
+ # - org config is overridden by local repo config
21
+ ######################################################################
22
+ set -euo pipefail
23
+
24
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
25
+ source "$SCRIPT_DIR/radio.uses.output.sh"
26
+ source "$SCRIPT_DIR/radio.uses.operations.sh"
27
+
28
+ require_git_repo
29
+
30
+ # parse command and options
31
+ COMMAND=""
32
+ ORG=""
33
+
34
+ # first positional arg is command
35
+ if [[ $# -ge 1 && "$1" != --* ]]; then
36
+ COMMAND="$1"
37
+ shift
38
+ fi
39
+
40
+ while [[ $# -gt 0 ]]; do
41
+ case $1 in
42
+ allow|block|del|get)
43
+ COMMAND="$1"
44
+ shift
45
+ ;;
46
+ --org)
47
+ ORG="$2"
48
+ shift 2
49
+ ;;
50
+ --help|-h)
51
+ echo "usage: radio.uses --org <org> allow"
52
+ echo " radio.uses --org <org> block"
53
+ echo " radio.uses --org <org> del"
54
+ echo " radio.uses --org <org> get"
55
+ echo " radio.uses --org get # show all"
56
+ echo ""
57
+ echo "commands:"
58
+ echo " allow allow radio for specified org"
59
+ echo " block block radio for specified org"
60
+ echo " del remove org config, defer to @all"
61
+ echo " get check org config(s)"
62
+ echo ""
63
+ echo "options:"
64
+ echo " --org <name> org name (e.g., ehmpathy, ahbode, @all)"
65
+ echo " --help, -h show this help"
66
+ echo ""
67
+ echo "examples:"
68
+ echo " radio.uses --org @all block # block all by default"
69
+ echo " radio.uses --org ehmpathy allow # allow ehmpathy"
70
+ exit 0
71
+ ;;
72
+ --repo|--role|--skill|--local|--global)
73
+ # rhachet passthrough args - ignore
74
+ shift
75
+ if [[ $# -gt 0 && "$1" != --* && "$1" != -* ]]; then
76
+ shift
77
+ fi
78
+ ;;
79
+ --)
80
+ shift
81
+ ;;
82
+ --*)
83
+ echo "error: unknown option: $1"
84
+ echo "usage: radio.uses --org <org> allow|block|del|get"
85
+ exit 2
86
+ ;;
87
+ *)
88
+ shift
89
+ ;;
90
+ esac
91
+ done
92
+
93
+ # validate command
94
+ if [[ -z "$COMMAND" ]]; then
95
+ echo "error: command required (allow, block, del, or get)"
96
+ echo "usage: radio.uses --org <org> allow|block|del|get"
97
+ exit 2
98
+ fi
99
+
100
+ ######################################################################
101
+ # guard: mutation commands require TTY (human only)
102
+ ######################################################################
103
+ case "$COMMAND" in
104
+ allow|block|del)
105
+ require_human "--org $ORG $COMMAND"
106
+ ;;
107
+ esac
108
+
109
+ # read current org state file
110
+ read_org_file() {
111
+ if [[ -f "$ORG_STATE_FILE" ]]; then
112
+ cat "$ORG_STATE_FILE"
113
+ else
114
+ echo '{ "orgs": {} }'
115
+ fi
116
+ }
117
+
118
+ # write org state file
119
+ write_org_file() {
120
+ local content="$1"
121
+ mkdir -p "$GLOBAL_METER_DIR"
122
+ echo "$content" > "$ORG_STATE_FILE"
123
+ }
124
+
125
+ case "$COMMAND" in
126
+ allow)
127
+ if [[ -z "$ORG" ]]; then
128
+ echo "error: --org is required for allow"
129
+ echo "usage: radio.uses --org <org> allow"
130
+ exit 2
131
+ fi
132
+
133
+ # upsert org state
134
+ CURRENT=$(read_org_file)
135
+ UPDATED=$(echo "$CURRENT" | jq ".orgs[\"$ORG\"] = \"allowed\"")
136
+ write_org_file "$UPDATED"
137
+
138
+ print_turtle_header "shell yeah! org allowed"
139
+ print_tree_start "radio.uses allow --org $ORG"
140
+ echo " └─ $ORG: allowed"
141
+ ;;
142
+
143
+ block)
144
+ if [[ -z "$ORG" ]]; then
145
+ echo "error: --org is required for block"
146
+ echo "usage: radio.uses --org <org> block"
147
+ exit 2
148
+ fi
149
+
150
+ # upsert org state
151
+ CURRENT=$(read_org_file)
152
+ UPDATED=$(echo "$CURRENT" | jq ".orgs[\"$ORG\"] = \"blocked\"")
153
+ write_org_file "$UPDATED"
154
+
155
+ print_turtle_header "groovy, org blocked"
156
+ print_tree_start "radio.uses block --org $ORG"
157
+ echo " └─ $ORG: blocked"
158
+ ;;
159
+
160
+ del)
161
+ if [[ -z "$ORG" ]]; then
162
+ echo "error: --org is required for del"
163
+ echo "usage: radio.uses --org <org> del"
164
+ exit 2
165
+ fi
166
+
167
+ if [[ -f "$ORG_STATE_FILE" ]]; then
168
+ CURRENT=$(read_org_file)
169
+ UPDATED=$(echo "$CURRENT" | jq "del(.orgs[\"$ORG\"])")
170
+ write_org_file "$UPDATED"
171
+ fi
172
+
173
+ print_turtle_header "righteous, org config cleared"
174
+ print_tree_start "radio.uses del --org $ORG"
175
+ echo " └─ $ORG config removed, defers to @all"
176
+ ;;
177
+
178
+ get)
179
+ print_turtle_header "lets check the org meter..."
180
+
181
+ if [[ -n "$ORG" ]]; then
182
+ # show specific org
183
+ print_tree_start "radio.uses get --org $ORG"
184
+ ORG_VAL=$(read_org_state "$ORG")
185
+ echo " └─ $ORG: $ORG_VAL"
186
+ else
187
+ # show all orgs
188
+ print_tree_start "radio.uses get --org"
189
+ if [[ -f "$ORG_STATE_FILE" ]]; then
190
+ ORGS=$(jq -r '.orgs | to_entries | .[] | "\(.key): \(.value)"' "$ORG_STATE_FILE" 2>/dev/null || echo "")
191
+ if [[ -n "$ORGS" ]]; then
192
+ # print all but last with ├─, last with └─
193
+ LINES=()
194
+ while IFS= read -r line; do
195
+ LINES+=("$line")
196
+ done <<< "$ORGS"
197
+
198
+ for i in "${!LINES[@]}"; do
199
+ if [[ $i -eq $((${#LINES[@]} - 1)) ]]; then
200
+ echo " └─ ${LINES[$i]}"
201
+ else
202
+ echo " ├─ ${LINES[$i]}"
203
+ fi
204
+ done
205
+ else
206
+ echo " └─ no orgs configured"
207
+ fi
208
+ else
209
+ echo " └─ no orgs configured"
210
+ fi
211
+ fi
212
+ ;;
213
+
214
+ *)
215
+ echo "error: unknown command: $COMMAND"
216
+ echo "usage: radio.uses --org <org> allow|block|del|get"
217
+ exit 2
218
+ ;;
219
+ esac
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = turtle vibes output for radio.uses skills
4
+ #
5
+ # .why = consistent, fun output format across all radio.uses commands
6
+ #
7
+ # usage:
8
+ # source output.sh
9
+ # print_turtle_header "cowabunga!"
10
+ # print_tree_start "radio.uses"
11
+ # print_tree_branch "global"
12
+ # print_tree_leaf "blocked" "true"
13
+ ######################################################################
14
+
15
+ # print turtle emoji + phrase
16
+ # usage: print_turtle_header "cowabunga!"
17
+ print_turtle_header() {
18
+ local phrase="$1"
19
+ echo "🐢 $phrase"
20
+ echo ""
21
+ }
22
+
23
+ # print tree root with shell emoji
24
+ # usage: print_tree_start "radio.uses"
25
+ print_tree_start() {
26
+ local command="$1"
27
+ echo "🐚 $command"
28
+ }
29
+
30
+ # print tree branch (has children)
31
+ # usage: print_tree_branch "commit" [is_last]
32
+ print_tree_branch() {
33
+ local label="$1"
34
+ local is_last="${2:-false}"
35
+ if [[ "$is_last" == "true" ]]; then
36
+ echo " └─ $label"
37
+ else
38
+ echo " ├─ $label"
39
+ fi
40
+ }
41
+
42
+ # print tree leaf (no children, with value)
43
+ # usage: print_tree_leaf "header" "fix(api): validate" [prefix] [is_last]
44
+ print_tree_leaf() {
45
+ local key="$1"
46
+ local value="$2"
47
+ local prefix="${3:-│ }"
48
+ local is_last="${4:-false}"
49
+ if [[ "$is_last" == "true" ]]; then
50
+ echo "${prefix}└─ $key: $value"
51
+ else
52
+ echo "${prefix}├─ $key: $value"
53
+ fi
54
+ }
55
+
56
+ # print nested tree leaf (deeper nesting)
57
+ # usage: print_nested_leaf "state" "blocked" [is_last]
58
+ print_nested_leaf() {
59
+ local key="$1"
60
+ local value="$2"
61
+ local is_last="${3:-true}"
62
+ if [[ "$is_last" == "true" ]]; then
63
+ echo " └─ $key: $value"
64
+ else
65
+ echo " ├─ $key: $value"
66
+ fi
67
+ }
68
+
69
+ # print error in tree format
70
+ # usage: print_tree_error "permission denied"
71
+ print_tree_error() {
72
+ local message="$1"
73
+ echo " └─ error: $message"
74
+ }
75
+
76
+ # print instruction block (after tree)
77
+ # usage: print_instruction "ask your human to grant:" " $ radio.uses allow ..."
78
+ print_instruction() {
79
+ local header="$1"
80
+ local command="$2"
81
+ echo ""
82
+ echo "$header"
83
+ echo "$command"
84
+ }
85
+
86
+ # print tip in dim/muted style
87
+ # usage: print_tip "'rhx radio.uses block' revokes all access"
88
+ print_tip() {
89
+ local text="$1"
90
+ # \033[2m = dim, \033[0m = reset
91
+ echo -e " └─ \033[2mtip: $text\033[0m"
92
+ }
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ ######################################################################
3
+ # .what = manage radio usage permissions for dispatcher
4
+ #
5
+ # .why = humans control whether radio can push tasks to repos
6
+ # across three levels: global > org > local
7
+ #
8
+ # usage:
9
+ # radio.uses allow # allow in this repo (local)
10
+ # radio.uses block # block in this repo (local)
11
+ # radio.uses get # check all states
12
+ # radio.uses --global block # block radio globally
13
+ # radio.uses --global allow # lift global blocker
14
+ # radio.uses --org ehmpathy allow # allow radio for ehmpathy org
15
+ # radio.uses --org @all block # block radio for all orgs by default
16
+ #
17
+ # precedence (highest to lowest):
18
+ # 1. global blocked = always blocked
19
+ # 2. local set = local wins
20
+ # 3. org specific = wins over @all
21
+ # 4. @all = default for all orgs
22
+ # 5. unset = blocked (safe default)
23
+ #
24
+ # guarantee:
25
+ # - local state stored in .meter/radio.uses.jsonc
26
+ # - global state stored at ~/.rhachet/storage/repo=bhuild/role=dispatcher/.meter/
27
+ # - org state stored at ~/.rhachet/storage/repo=bhuild/role=dispatcher/.meter/
28
+ ######################################################################
29
+ set -euo pipefail
30
+
31
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
32
+
33
+ # check for --global flag
34
+ GLOBAL_MODE=false
35
+ ORG_MODE=false
36
+ ORG_VALUE=""
37
+ ARGS=()
38
+
39
+ for arg in "$@"; do
40
+ if [[ "$arg" == "--global" ]]; then
41
+ GLOBAL_MODE=true
42
+ elif [[ "$arg" == "--org" ]]; then
43
+ ORG_MODE=true
44
+ elif [[ "$ORG_MODE" == "true" && -z "$ORG_VALUE" && "$arg" != --* ]]; then
45
+ ORG_VALUE="$arg"
46
+ ARGS+=("--org" "$arg")
47
+ else
48
+ ARGS+=("$arg")
49
+ fi
50
+ done
51
+
52
+ # dispatch to appropriate handler
53
+ if [[ "$GLOBAL_MODE" == "true" ]]; then
54
+ exec "$SCRIPT_DIR/radio.uses.global.sh" "${ARGS[@]}"
55
+ elif [[ "$ORG_MODE" == "true" ]]; then
56
+ exec "$SCRIPT_DIR/radio.uses.org.sh" "${ARGS[@]}"
57
+ else
58
+ exec "$SCRIPT_DIR/radio.uses.local.sh" "${ARGS[@]}"
59
+ fi
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "rhachet-roles-bhuild",
3
3
  "author": "ehmpathy",
4
4
  "description": "roles for building resilient systems, via rhachet",
5
- "version": "0.21.12",
5
+ "version": "0.21.13",
6
6
  "repository": "ehmpathy/rhachet-roles-bhuild",
7
7
  "homepage": "https://github.com/ehmpathy/rhachet-roles-bhuild",
8
8
  "keywords": [
@@ -59,7 +59,7 @@
59
59
  "prepare:husky": "husky install && chmod ug+x .husky/*",
60
60
  "prepare": "if [ -e .git ] && [ -z \"${CI:-}\" ]; then npm run prepare:husky && npm run prepare:rhachet; fi",
61
61
  "test:format:biome": "biome format",
62
- "prepare:rhachet": "npm run build && rhachet init --hooks --roles mechanic behaver driver reviewer librarian ergonomist architect"
62
+ "prepare:rhachet": "npm run build && rhachet init --hooks --roles mechanic behaver driver architect ergonomist reviewer dreamer dispatcher"
63
63
  },
64
64
  "dependencies": {
65
65
  "domain-objects": "0.31.9",
@@ -98,7 +98,7 @@
98
98
  "rhachet-brains-xai": "0.3.3",
99
99
  "rhachet-roles-bhrain": "0.27.8",
100
100
  "rhachet-roles-bhuild": "link:.",
101
- "rhachet-roles-ehmpathy": "1.35.8",
101
+ "rhachet-roles-ehmpathy": "1.35.12",
102
102
  "tsc-alias": "1.8.10",
103
103
  "tsx": "4.20.6",
104
104
  "typescript": "5.4.5",