@intercom/cli 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/index.js +225 -43
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
View it from the CLI with `intercom changelog`.
|
|
9
9
|
|
|
10
|
+
## [0.9.0] - 2026-07-03
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `fin attributes upload <file>` creates or updates Fin custom attributes from a YAML file. Attributes were previously read-only (`list`). The file can be a list of attributes, a single attribute, or the full `type: attribute` / `attributes:` server shape — the CLI normalizes it and sets the routing type for you. Each attribute needs a dotted `name` (e.g. `user.order_status`) and a data `type` (`string`, `integer`, `decimal`, `float`, `boolean`, `list`, `date`, `object`, or `array`).
|
|
15
|
+
- `fin procedures get <id> [output] --with-simulations` downloads a procedure's simulations alongside it. With the flag, `[output]` is a directory (default: current directory) and the CLI writes both `procedure.yaml` and `simulations.yaml`. Without it, `[output]` is a single file for the procedure YAML (previously `get` could only print to stdout).
|
|
16
|
+
- `fin simulations upload <file>` re-uploads a simulation set (the `simulations.yaml` written by `fin procedures get --with-simulations`, or `fin simulations list` output), completing the export/edit/re-import round-trip for a procedure's tests.
|
|
17
|
+
|
|
18
|
+
## [0.8.3] - 2026-06-18
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- `fin workflows upload` now works with workflow YAML that doesn't declare a top-level `type:`. The upload endpoint is shared with procedures and routes by that field, so a workflow file without `type: workflow` was being validated as a procedure (`object at root is missing required properties: name, trigger_description, procedure`). The CLI now sets `type: workflow` for you when it's absent.
|
|
23
|
+
- `fin workflows enable` and `fin workflows disable` no longer fail with `Resource not found`. They re-upload the workflow after changing its state, and the re-uploaded YAML was missing `type: workflow`, so the server looked for a procedure with that id and 404'd. The state-change upload now carries `type: workflow`.
|
|
24
|
+
- `fin workflows get <id>` now prints the workflow as YAML (the format `upload` expects) instead of a lossy JSON summary, and accepts an optional output file: `intercom fin workflows get 42 workflow.yaml`. `--json` still returns parsed JSON.
|
|
25
|
+
|
|
10
26
|
## [0.8.2] - 2026-06-17
|
|
11
27
|
|
|
12
28
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -2569,8 +2569,8 @@ Examples:
|
|
|
2569
2569
|
import { writeFile as writeFile3 } from "fs/promises";
|
|
2570
2570
|
|
|
2571
2571
|
// src/commands/fin/procedures.ts
|
|
2572
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
2573
|
-
import { basename } from "path";
|
|
2572
|
+
import { readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
2573
|
+
import { basename, join as join3 } from "path";
|
|
2574
2574
|
import AdmZip from "adm-zip";
|
|
2575
2575
|
function registerProceduresCommand(fin) {
|
|
2576
2576
|
const procedures = fin.command("procedures").description("Manage Fin SDK procedures");
|
|
@@ -2591,7 +2591,21 @@ function registerProceduresCommand(fin) {
|
|
|
2591
2591
|
return table({ columns: ["ID", "NAME"], rows });
|
|
2592
2592
|
}, items);
|
|
2593
2593
|
});
|
|
2594
|
-
procedures.command("get <id>").description("Download a procedure by ID
|
|
2594
|
+
procedures.command("get <id> [output]").description("Download a procedure by ID as YAML (writes to [output], or stdout)").option("--with-simulations", "Also download the procedure's simulations").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
2595
|
+
"after",
|
|
2596
|
+
`
|
|
2597
|
+
Examples:
|
|
2598
|
+
$ intercom fin procedures get proc-1 # print YAML to stdout
|
|
2599
|
+
$ intercom fin procedures get proc-1 procedure.yaml # write YAML to a file
|
|
2600
|
+
$ intercom fin procedures get proc-1 ./out --with-simulations
|
|
2601
|
+
# writes ./out/procedure.yaml and ./out/simulations.yaml
|
|
2602
|
+
|
|
2603
|
+
With --with-simulations, [output] is treated as a directory (default: current
|
|
2604
|
+
directory). Without it, [output] is a single file for the procedure YAML.
|
|
2605
|
+
|
|
2606
|
+
Re-import the pieces with \`intercom fin procedures upload <procedure.yaml>\`
|
|
2607
|
+
and \`intercom fin simulations upload <simulations.yaml>\`.`
|
|
2608
|
+
).action(async function(id, output) {
|
|
2595
2609
|
const opts = this.optsWithGlobals();
|
|
2596
2610
|
const client = await resolveClient(opts);
|
|
2597
2611
|
const buffer = await client.getBuffer(`/fin-sdk/procedures/${id}/download`);
|
|
@@ -2600,7 +2614,28 @@ function registerProceduresCommand(fin) {
|
|
|
2600
2614
|
if (!entry) {
|
|
2601
2615
|
throw new Error("procedure.yaml not found in downloaded ZIP");
|
|
2602
2616
|
}
|
|
2603
|
-
|
|
2617
|
+
const procedureYaml = zip.readAsText(entry);
|
|
2618
|
+
let simulationsYaml;
|
|
2619
|
+
if (opts.withSimulations) {
|
|
2620
|
+
simulationsYaml = await client.getText(`/fin-sdk/procedures/${id}/simulations`);
|
|
2621
|
+
}
|
|
2622
|
+
if (opts.withSimulations) {
|
|
2623
|
+
const dir = output ?? ".";
|
|
2624
|
+
await mkdir2(dir, { recursive: true });
|
|
2625
|
+
const procedurePath = join3(dir, "procedure.yaml");
|
|
2626
|
+
const simulationsPath = join3(dir, "simulations.yaml");
|
|
2627
|
+
await atomicWriteFile(procedurePath, procedureYaml, 420);
|
|
2628
|
+
await atomicWriteFile(simulationsPath, simulationsYaml ?? "", 420);
|
|
2629
|
+
console.error(`Wrote procedure ${id} to ${procedurePath}`);
|
|
2630
|
+
console.error(`Wrote simulations to ${simulationsPath}`);
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
if (output) {
|
|
2634
|
+
await atomicWriteFile(output, procedureYaml, 420);
|
|
2635
|
+
console.error(`Wrote procedure ${id} to ${output}`);
|
|
2636
|
+
} else {
|
|
2637
|
+
console.log(procedureYaml);
|
|
2638
|
+
}
|
|
2604
2639
|
});
|
|
2605
2640
|
procedures.command("upload <file>").description("Upload a procedure file to Fin SDK").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").action(async function(filePath) {
|
|
2606
2641
|
const opts = this.optsWithGlobals();
|
|
@@ -2766,6 +2801,29 @@ Optional fields: state (live or paused, default: paused), audiences`
|
|
|
2766
2801
|
}
|
|
2767
2802
|
|
|
2768
2803
|
// src/commands/fin/attributes.ts
|
|
2804
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
2805
|
+
import { basename as basename2 } from "path";
|
|
2806
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
2807
|
+
var ATTRIBUTE_TYPE = "attribute";
|
|
2808
|
+
function buildAttributesUploadBody(raw) {
|
|
2809
|
+
let parsed;
|
|
2810
|
+
try {
|
|
2811
|
+
parsed = parseYaml(raw);
|
|
2812
|
+
} catch {
|
|
2813
|
+
return raw;
|
|
2814
|
+
}
|
|
2815
|
+
if (Array.isArray(parsed)) {
|
|
2816
|
+
return stringifyYaml({ type: ATTRIBUTE_TYPE, attributes: parsed });
|
|
2817
|
+
}
|
|
2818
|
+
if (parsed == null || typeof parsed !== "object") {
|
|
2819
|
+
return raw;
|
|
2820
|
+
}
|
|
2821
|
+
const obj = parsed;
|
|
2822
|
+
if (Array.isArray(obj.attributes)) {
|
|
2823
|
+
return stringifyYaml({ ...obj, type: ATTRIBUTE_TYPE });
|
|
2824
|
+
}
|
|
2825
|
+
return stringifyYaml({ type: ATTRIBUTE_TYPE, attributes: [obj] });
|
|
2826
|
+
}
|
|
2769
2827
|
function registerAttributesCommand(fin) {
|
|
2770
2828
|
const attributes = fin.command("attributes").description("Manage Fin SDK attributes");
|
|
2771
2829
|
attributes.command("list").description("List Fin SDK attributes").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
@@ -2785,6 +2843,49 @@ function registerAttributesCommand(fin) {
|
|
|
2785
2843
|
return table({ columns: ["NAME", "TYPE", "DESCRIPTION"], rows });
|
|
2786
2844
|
}, items);
|
|
2787
2845
|
});
|
|
2846
|
+
attributes.command("upload <file>").description("Upload attributes from a YAML file (creates or updates)").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
2847
|
+
"after",
|
|
2848
|
+
`
|
|
2849
|
+
The file holds one or more attributes. The CLI wraps them in the bulk shape the
|
|
2850
|
+
server expects and sets the routing type for you, so any of these work:
|
|
2851
|
+
|
|
2852
|
+
# a list of attributes
|
|
2853
|
+
- name: user.order_status # name must contain a dot
|
|
2854
|
+
type: string # data type: string, integer, decimal, float,
|
|
2855
|
+
description: Order state # boolean, list, date, object, or array
|
|
2856
|
+
|
|
2857
|
+
# ...or a single attribute
|
|
2858
|
+
name: company.tier
|
|
2859
|
+
type: string
|
|
2860
|
+
|
|
2861
|
+
# ...or the full server shape
|
|
2862
|
+
type: attribute
|
|
2863
|
+
attributes:
|
|
2864
|
+
- name: user.order_status
|
|
2865
|
+
type: string
|
|
2866
|
+
|
|
2867
|
+
Examples:
|
|
2868
|
+
$ intercom fin attributes upload ./attributes.yaml
|
|
2869
|
+
$ intercom fin attributes upload ./attributes.yaml --json`
|
|
2870
|
+
).action(async function(filePath) {
|
|
2871
|
+
const opts = this.optsWithGlobals();
|
|
2872
|
+
const client = await resolveClient(opts);
|
|
2873
|
+
const fileContent = await readFile3(filePath, "utf8");
|
|
2874
|
+
const yamlBody = buildAttributesUploadBody(fileContent);
|
|
2875
|
+
const formData = new FormData();
|
|
2876
|
+
const blob = new Blob([yamlBody], { type: "application/x-yaml" });
|
|
2877
|
+
formData.append("file_data", blob, basename2(filePath));
|
|
2878
|
+
const result = await client.postFormData("/fin-sdk/upload", formData);
|
|
2879
|
+
render(
|
|
2880
|
+
result,
|
|
2881
|
+
opts,
|
|
2882
|
+
() => detail({
|
|
2883
|
+
"ID": result.id != null ? String(result.id) : void 0,
|
|
2884
|
+
"Type": result.type != null ? String(result.type) : void 0,
|
|
2885
|
+
"Message": result.message != null ? String(result.message) : void 0
|
|
2886
|
+
})
|
|
2887
|
+
);
|
|
2888
|
+
});
|
|
2788
2889
|
}
|
|
2789
2890
|
|
|
2790
2891
|
// src/commands/fin/audiences.ts
|
|
@@ -2809,11 +2910,29 @@ function registerAudiencesCommand(fin) {
|
|
|
2809
2910
|
}
|
|
2810
2911
|
|
|
2811
2912
|
// src/commands/fin/workflows.ts
|
|
2812
|
-
import { readFile as
|
|
2813
|
-
import { basename as
|
|
2814
|
-
import { parse as
|
|
2913
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
2914
|
+
import { basename as basename3 } from "path";
|
|
2915
|
+
import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
|
|
2815
2916
|
var STATE_LIVE = "live";
|
|
2816
2917
|
var STATE_PAUSED = "paused";
|
|
2918
|
+
var WORKFLOW_TYPE = "workflow";
|
|
2919
|
+
function ensureWorkflowType(raw) {
|
|
2920
|
+
let parsed;
|
|
2921
|
+
try {
|
|
2922
|
+
parsed = parseYaml2(raw);
|
|
2923
|
+
} catch {
|
|
2924
|
+
return raw;
|
|
2925
|
+
}
|
|
2926
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
2927
|
+
return raw;
|
|
2928
|
+
}
|
|
2929
|
+
const obj = parsed;
|
|
2930
|
+
if (obj.type != null && obj.type !== "") {
|
|
2931
|
+
return raw;
|
|
2932
|
+
}
|
|
2933
|
+
obj.type = WORKFLOW_TYPE;
|
|
2934
|
+
return stringifyYaml2(obj);
|
|
2935
|
+
}
|
|
2817
2936
|
function registerWorkflowsCommand(fin) {
|
|
2818
2937
|
const workflows = fin.command("workflows").description("Manage Fin SDK workflows");
|
|
2819
2938
|
workflows.command("list").description("List Fin SDK workflows").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function() {
|
|
@@ -2834,22 +2953,31 @@ function registerWorkflowsCommand(fin) {
|
|
|
2834
2953
|
return table({ columns: ["ID", "TITLE", "STATE", "TRIGGER"], rows });
|
|
2835
2954
|
}, items);
|
|
2836
2955
|
});
|
|
2837
|
-
workflows.command("get <id>").description("Get a workflow by ID").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").
|
|
2956
|
+
workflows.command("get <id> [output]").description("Get a workflow by ID as YAML (writes to [output] file, or stdout)").option("--json", "Output as parsed JSON instead of YAML").option("--jq <expr>", "Filter parsed-JSON output with a jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
2957
|
+
"after",
|
|
2958
|
+
`
|
|
2959
|
+
Examples:
|
|
2960
|
+
$ intercom fin workflows get 42 workflow.yaml # write YAML to a file
|
|
2961
|
+
$ intercom fin workflows get 42 > workflow.yaml # or redirect stdout
|
|
2962
|
+
$ intercom fin workflows get 42 --json | jq .state
|
|
2963
|
+
|
|
2964
|
+
The YAML it prints is ready to edit and re-upload with
|
|
2965
|
+
\`intercom fin workflows upload\`.`
|
|
2966
|
+
).action(async function(id, output) {
|
|
2838
2967
|
const opts = this.optsWithGlobals();
|
|
2839
2968
|
const client = await resolveClient(opts);
|
|
2840
2969
|
const text = await client.getText(`/fin-sdk/workflows/${id}`);
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
parsed,
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
);
|
|
2970
|
+
if (opts.json || opts.jq || opts.agent) {
|
|
2971
|
+
const parsed = parseYaml2(text);
|
|
2972
|
+
render(parsed, opts, () => formatJson(parsed));
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
if (output) {
|
|
2976
|
+
await atomicWriteFile(output, text, 420);
|
|
2977
|
+
console.error(`Wrote workflow ${id} to ${output}`);
|
|
2978
|
+
} else {
|
|
2979
|
+
console.log(text.replace(/\n+$/, ""));
|
|
2980
|
+
}
|
|
2853
2981
|
});
|
|
2854
2982
|
workflows.command("upload <file>").description('Upload a workflow YAML file (creates if id is "new", updates if numeric)').option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
2855
2983
|
"after",
|
|
@@ -2865,10 +2993,11 @@ The YAML file's id field determines whether this creates or updates:
|
|
|
2865
2993
|
).action(async function(filePath) {
|
|
2866
2994
|
const opts = this.optsWithGlobals();
|
|
2867
2995
|
const client = await resolveClient(opts);
|
|
2868
|
-
const fileContent = await
|
|
2996
|
+
const fileContent = await readFile4(filePath, "utf8");
|
|
2997
|
+
const yamlBody = ensureWorkflowType(fileContent);
|
|
2869
2998
|
const formData = new FormData();
|
|
2870
|
-
const blob = new Blob([
|
|
2871
|
-
formData.append("file_data", blob,
|
|
2999
|
+
const blob = new Blob([yamlBody], { type: "application/x-yaml" });
|
|
3000
|
+
formData.append("file_data", blob, basename3(filePath));
|
|
2872
3001
|
const result = await client.postFormData("/fin-sdk/upload", formData);
|
|
2873
3002
|
render(
|
|
2874
3003
|
result,
|
|
@@ -2897,12 +3026,13 @@ overwritten (the server does not expose a concurrency token).`
|
|
|
2897
3026
|
const text = await client.getText(`/fin-sdk/workflows/${id}`);
|
|
2898
3027
|
let parsed;
|
|
2899
3028
|
try {
|
|
2900
|
-
parsed =
|
|
3029
|
+
parsed = parseYaml2(text);
|
|
2901
3030
|
} catch (err) {
|
|
2902
3031
|
throw new Error(`server returned unparseable YAML for workflow ${id}: ${err.message}`);
|
|
2903
3032
|
}
|
|
3033
|
+
parsed.type = WORKFLOW_TYPE;
|
|
2904
3034
|
parsed.state = STATE_LIVE;
|
|
2905
|
-
const yamlBody =
|
|
3035
|
+
const yamlBody = stringifyYaml2(parsed);
|
|
2906
3036
|
const formData = new FormData();
|
|
2907
3037
|
const blob = new Blob([yamlBody], { type: "application/x-yaml" });
|
|
2908
3038
|
formData.append("file_data", blob, `workflow-${id}.yaml`);
|
|
@@ -2933,12 +3063,13 @@ overwritten (the server does not expose a concurrency token).`
|
|
|
2933
3063
|
const text = await client.getText(`/fin-sdk/workflows/${id}`);
|
|
2934
3064
|
let parsed;
|
|
2935
3065
|
try {
|
|
2936
|
-
parsed =
|
|
3066
|
+
parsed = parseYaml2(text);
|
|
2937
3067
|
} catch (err) {
|
|
2938
3068
|
throw new Error(`server returned unparseable YAML for workflow ${id}: ${err.message}`);
|
|
2939
3069
|
}
|
|
3070
|
+
parsed.type = WORKFLOW_TYPE;
|
|
2940
3071
|
parsed.state = STATE_PAUSED;
|
|
2941
|
-
const yamlBody =
|
|
3072
|
+
const yamlBody = stringifyYaml2(parsed);
|
|
2942
3073
|
const formData = new FormData();
|
|
2943
3074
|
const blob = new Blob([yamlBody], { type: "application/x-yaml" });
|
|
2944
3075
|
formData.append("file_data", blob, `workflow-${id}.yaml`);
|
|
@@ -2956,6 +3087,27 @@ overwritten (the server does not expose a concurrency token).`
|
|
|
2956
3087
|
}
|
|
2957
3088
|
|
|
2958
3089
|
// src/commands/fin/simulations.ts
|
|
3090
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
3091
|
+
import { basename as basename4 } from "path";
|
|
3092
|
+
import { parse as parseYaml3, stringify as stringifyYaml3 } from "yaml";
|
|
3093
|
+
var SIMULATION_SET_TYPE = "simulation_set";
|
|
3094
|
+
function ensureSimulationSetType(raw) {
|
|
3095
|
+
let parsed;
|
|
3096
|
+
try {
|
|
3097
|
+
parsed = parseYaml3(raw);
|
|
3098
|
+
} catch {
|
|
3099
|
+
return raw;
|
|
3100
|
+
}
|
|
3101
|
+
if (parsed == null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
3102
|
+
return raw;
|
|
3103
|
+
}
|
|
3104
|
+
const obj = parsed;
|
|
3105
|
+
if (obj.type != null && obj.type !== "") {
|
|
3106
|
+
return raw;
|
|
3107
|
+
}
|
|
3108
|
+
obj.type = SIMULATION_SET_TYPE;
|
|
3109
|
+
return stringifyYaml3(obj);
|
|
3110
|
+
}
|
|
2959
3111
|
function registerSimulationsCommand(fin) {
|
|
2960
3112
|
const simulations = fin.command("simulations").description("Manage Fin SDK simulations");
|
|
2961
3113
|
simulations.command("list <procedure_id>").description("List simulations for a procedure (displays YAML)").option("--verbose", "Show HTTP request/response details").action(async function(procedureId) {
|
|
@@ -2964,6 +3116,36 @@ function registerSimulationsCommand(fin) {
|
|
|
2964
3116
|
const text = await client.getText(`/fin-sdk/procedures/${procedureId}/simulations`);
|
|
2965
3117
|
console.log(text);
|
|
2966
3118
|
});
|
|
3119
|
+
simulations.command("upload <file>").description("Upload a simulation set YAML file for its procedure").option("--json", "Output as JSON").option("--jq <expr>", "Filter JSON output with jq expression").option("--verbose", "Show HTTP request/response details").addHelpText(
|
|
3120
|
+
"after",
|
|
3121
|
+
`
|
|
3122
|
+
The file is a simulation set carrying its own procedure_id, as produced by
|
|
3123
|
+
\`intercom fin procedures get <id> ./out --with-simulations\` (writes
|
|
3124
|
+
./out/simulations.yaml) or \`intercom fin simulations list <procedure_id>\`.
|
|
3125
|
+
This completes the export/edit/re-import round-trip for a procedure's tests.
|
|
3126
|
+
|
|
3127
|
+
Example:
|
|
3128
|
+
$ intercom fin simulations upload ./out/simulations.yaml`
|
|
3129
|
+
).action(async function(filePath) {
|
|
3130
|
+
const opts = this.optsWithGlobals();
|
|
3131
|
+
const client = await resolveClient(opts);
|
|
3132
|
+
const fileContent = await readFile5(filePath, "utf8");
|
|
3133
|
+
const yamlBody = ensureSimulationSetType(fileContent);
|
|
3134
|
+
const formData = new FormData();
|
|
3135
|
+
const blob = new Blob([yamlBody], { type: "application/x-yaml" });
|
|
3136
|
+
formData.append("file_data", blob, basename4(filePath));
|
|
3137
|
+
const result = await client.postFormData("/fin-sdk/upload", formData);
|
|
3138
|
+
render(
|
|
3139
|
+
result,
|
|
3140
|
+
opts,
|
|
3141
|
+
() => detail({
|
|
3142
|
+
"ID": result.id != null ? String(result.id) : void 0,
|
|
3143
|
+
"Procedure ID": result.procedure_id != null ? String(result.procedure_id) : void 0,
|
|
3144
|
+
"Type": result.type != null ? String(result.type) : void 0,
|
|
3145
|
+
"Message": result.message != null ? String(result.message) : void 0
|
|
3146
|
+
})
|
|
3147
|
+
);
|
|
3148
|
+
});
|
|
2967
3149
|
simulations.command("run <procedure_id> [test_id]").description("Run one or all simulations for a procedure").option("--json", "Output as JSON").option("--verbose", "Show HTTP request/response details").action(async function(procedureId, testId) {
|
|
2968
3150
|
const opts = this.optsWithGlobals();
|
|
2969
3151
|
const client = await resolveClient(opts);
|
|
@@ -3193,8 +3375,8 @@ Fin is live on workspace ${appId}`);
|
|
|
3193
3375
|
}
|
|
3194
3376
|
|
|
3195
3377
|
// src/commands/fin/monitors.ts
|
|
3196
|
-
import { mkdir as
|
|
3197
|
-
import { join as
|
|
3378
|
+
import { mkdir as mkdir3, writeFile as writeFile2 } from "fs/promises";
|
|
3379
|
+
import { join as join4, relative, dirname } from "path";
|
|
3198
3380
|
import { stringify } from "yaml";
|
|
3199
3381
|
async function resolveMonitorId(client, nameOrId) {
|
|
3200
3382
|
if (/^\d+$/.test(nameOrId)) {
|
|
@@ -3275,14 +3457,14 @@ Examples:
|
|
|
3275
3457
|
);
|
|
3276
3458
|
return;
|
|
3277
3459
|
}
|
|
3278
|
-
const outputPath = opts.output ??
|
|
3460
|
+
const outputPath = opts.output ?? join4(
|
|
3279
3461
|
process.cwd(),
|
|
3280
3462
|
"src",
|
|
3281
3463
|
"qa",
|
|
3282
3464
|
"bulk-actions",
|
|
3283
3465
|
"flagged-conversations.yaml"
|
|
3284
3466
|
);
|
|
3285
|
-
await
|
|
3467
|
+
await mkdir3(dirname(outputPath), { recursive: true });
|
|
3286
3468
|
await writeFile2(outputPath, buildWorkingSetYaml(result, since), "utf-8");
|
|
3287
3469
|
const relPath = relative(process.cwd(), outputPath);
|
|
3288
3470
|
console.log(
|
|
@@ -3300,8 +3482,8 @@ Examples:
|
|
|
3300
3482
|
}
|
|
3301
3483
|
|
|
3302
3484
|
// src/commands/fin/conversation.ts
|
|
3303
|
-
import { readFile as
|
|
3304
|
-
import { join as
|
|
3485
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
3486
|
+
import { join as join5 } from "path";
|
|
3305
3487
|
import { parse } from "yaml";
|
|
3306
3488
|
var PLACEHOLDER_MESSAGE = "Your message here \u2014 this will be sent to all conversations listed below.";
|
|
3307
3489
|
function validateWorkingSet(ws) {
|
|
@@ -3351,7 +3533,7 @@ function registerConversationCommand(fin) {
|
|
|
3351
3533
|
).option(
|
|
3352
3534
|
"--file <path>",
|
|
3353
3535
|
"Path to the working set YAML file",
|
|
3354
|
-
|
|
3536
|
+
join5(process.cwd(), "src", "qa", "bulk-actions", "flagged-conversations.yaml")
|
|
3355
3537
|
).option("--dry-run", "Preview actions without sending").option("--yes", "Skip confirmation prompt (required in TTY mode)").option(
|
|
3356
3538
|
"--json",
|
|
3357
3539
|
"Output result as JSON"
|
|
@@ -3365,7 +3547,7 @@ Examples:
|
|
|
3365
3547
|
$ fin conversation bulk-send --json`
|
|
3366
3548
|
).action(async function() {
|
|
3367
3549
|
const opts = this.optsWithGlobals();
|
|
3368
|
-
const filePath = opts.file ??
|
|
3550
|
+
const filePath = opts.file ?? join5(
|
|
3369
3551
|
process.cwd(),
|
|
3370
3552
|
"src",
|
|
3371
3553
|
"qa",
|
|
@@ -3374,7 +3556,7 @@ Examples:
|
|
|
3374
3556
|
);
|
|
3375
3557
|
let raw;
|
|
3376
3558
|
try {
|
|
3377
|
-
raw = await
|
|
3559
|
+
raw = await readFile6(filePath, "utf-8");
|
|
3378
3560
|
} catch (err) {
|
|
3379
3561
|
const e = err;
|
|
3380
3562
|
if (e.code === "ENOENT") {
|
|
@@ -3535,7 +3717,7 @@ Examples:
|
|
|
3535
3717
|
|
|
3536
3718
|
// src/commands/setup.ts
|
|
3537
3719
|
import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
|
|
3538
|
-
import { join as
|
|
3720
|
+
import { join as join6, basename as basename5 } from "path";
|
|
3539
3721
|
function isGreenfield(opts) {
|
|
3540
3722
|
return !!(opts.email && opts.name && (process.env.INTERCOM_PASSWORD || opts.passwordStdin));
|
|
3541
3723
|
}
|
|
@@ -3593,8 +3775,8 @@ function buildPlan(opts) {
|
|
|
3593
3775
|
}
|
|
3594
3776
|
if (mdFiles.length > 0) {
|
|
3595
3777
|
const articleCommands = mdFiles.map((file) => {
|
|
3596
|
-
const title =
|
|
3597
|
-
return `intercom articles create -f title="${title}" -F body=@${
|
|
3778
|
+
const title = basename5(file, ".md").replace(/-/g, " ").replace(/\b\w/g, (c2) => c2.toUpperCase());
|
|
3779
|
+
return `intercom articles create -f title="${title}" -F body=@${join6(opts.articlesFrom, file)} -f author_id=$(intercom me --json | jq -r .id)`;
|
|
3598
3780
|
});
|
|
3599
3781
|
steps.push({
|
|
3600
3782
|
step: stepNum++,
|
|
@@ -3660,8 +3842,8 @@ async function runPostAuthSteps(client, opts, results, appId) {
|
|
|
3660
3842
|
let created = 0;
|
|
3661
3843
|
let skipped = 0;
|
|
3662
3844
|
for (const file of mdFiles) {
|
|
3663
|
-
const title =
|
|
3664
|
-
const body = readFileSync4(
|
|
3845
|
+
const title = basename5(file, ".md").replace(/-/g, " ").replace(/\b\w/g, (c2) => c2.toUpperCase());
|
|
3846
|
+
const body = readFileSync4(join6(opts.articlesFrom, file), "utf-8");
|
|
3665
3847
|
if (existingTitles.has(title)) {
|
|
3666
3848
|
skipped++;
|
|
3667
3849
|
} else {
|
|
@@ -4138,7 +4320,7 @@ function registerPricingCommand(program) {
|
|
|
4138
4320
|
}
|
|
4139
4321
|
|
|
4140
4322
|
// src/commands/config.ts
|
|
4141
|
-
import { join as
|
|
4323
|
+
import { join as join7 } from "path";
|
|
4142
4324
|
var SUPPORTED_KEYS = ["default_workspace", "timeout"];
|
|
4143
4325
|
function configDir() {
|
|
4144
4326
|
return process.env.INTERCOM_CONFIG_DIR ?? defaultConfigDir();
|
|
@@ -4227,7 +4409,7 @@ function registerConfigCommand(program) {
|
|
|
4227
4409
|
console.log(`Set ${key} = ${value}`);
|
|
4228
4410
|
});
|
|
4229
4411
|
cmd.command("path").description("Show configuration file path").action(async function() {
|
|
4230
|
-
console.log(
|
|
4412
|
+
console.log(join7(configDir(), "config.json"));
|
|
4231
4413
|
});
|
|
4232
4414
|
}
|
|
4233
4415
|
|