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.
- package/dist/contract/cli/outputRadioResult.d.ts +6 -1
- package/dist/contract/cli/outputRadioResult.js +12 -2
- package/dist/contract/cli/outputRadioResult.js.map +1 -1
- package/dist/contract/cli/radioTaskPush.js +25 -0
- package/dist/contract/cli/radioTaskPush.js.map +1 -1
- package/dist/domain.objects/RadioPermissions.d.ts +37 -0
- package/dist/domain.objects/RadioPermissions.js +7 -0
- package/dist/domain.objects/RadioPermissions.js.map +1 -0
- package/dist/domain.operations/radio/permission/computeRadioUsagePermissionDecision.d.ts +11 -0
- package/dist/domain.operations/radio/permission/computeRadioUsagePermissionDecision.js +46 -0
- package/dist/domain.operations/radio/permission/computeRadioUsagePermissionDecision.js.map +1 -0
- package/dist/domain.operations/radio/permission/extractOrgFromRepo.d.ts +7 -0
- package/dist/domain.operations/radio/permission/extractOrgFromRepo.js +29 -0
- package/dist/domain.operations/radio/permission/extractOrgFromRepo.js.map +1 -0
- package/dist/domain.operations/radio/permission/getAllRadioUsagePermissions.d.ts +14 -0
- package/dist/domain.operations/radio/permission/getAllRadioUsagePermissions.js +114 -0
- package/dist/domain.operations/radio/permission/getAllRadioUsagePermissions.js.map +1 -0
- package/dist/domain.operations/radio/permission/getOneRadioUsagePermissionDecision.d.ts +11 -0
- package/dist/domain.operations/radio/permission/getOneRadioUsagePermissionDecision.js +25 -0
- package/dist/domain.operations/radio/permission/getOneRadioUsagePermissionDecision.js.map +1 -0
- package/dist/domain.roles/dispatcher/skills/radio.uses.global.sh +133 -0
- package/dist/domain.roles/dispatcher/skills/radio.uses.local.sh +172 -0
- package/dist/domain.roles/dispatcher/skills/radio.uses.operations.sh +139 -0
- package/dist/domain.roles/dispatcher/skills/radio.uses.org.sh +219 -0
- package/dist/domain.roles/dispatcher/skills/radio.uses.output.sh +92 -0
- package/dist/domain.roles/dispatcher/skills/radio.uses.sh +59 -0
- 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
|
-
|
|
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,
|
|
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":"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
|
|
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,mIAAgI;AAChI,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,wCAAwC;IACxC,MAAM,UAAU,GAAG,MAAM,IAAA,uEAAkC,EAAC;QAC1D,UAAU,EAAE,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE;QACxC,SAAS,EAAE,OAAO,CAAC,GAAG,EAAE;KACzB,CAAC,CAAC;IACH,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;QACxB,qCAAqC;QACrC,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE;YACxB,IAAI,UAAU,CAAC,KAAK,KAAK,QAAQ;gBAC/B,OAAO,mDAAmD,CAAC;YAC7D,IAAI,UAAU,CAAC,KAAK,KAAK,KAAK;gBAC5B,OAAO,4CAA4C,IAAI,CAAC,KAAK,QAAQ,CAAC;YACxE,OAAO,0CAA0C,CAAC;QACpD,CAAC,CAAC,EAAE,CAAC;QAEL,IAAA,qCAAiB,EAAC;YAChB,OAAO,EAAE,4BAA4B,UAAU,CAAC,MAAM,EAAE;YACxD,OAAO,EAAE,IAAI;YACb,IAAI,EAAE;gBACJ,GAAG,EAAE,0BAA0B;gBAC/B,OAAO,EAAE,WAAW;aACrB;SACF,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,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;AApFW,QAAA,gBAAgB,oBAoF3B"}
|
|
@@ -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,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.
|
|
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
|
|
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.
|
|
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",
|